Home | History | Annotate | Line # | Download | only in rbac
      1 /*	$NetBSD: rbacuser.c,v 1.2 2021/08/14 16:14:53 christos Exp $	*/
      2 
      3 /* rbacuser.c - RBAC users */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  *
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in the file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 /* ACKNOWLEDGEMENTS:
     19  */
     20 
     21 #include <sys/cdefs.h>
     22 __RCSID("$NetBSD: rbacuser.c,v 1.2 2021/08/14 16:14:53 christos Exp $");
     23 
     24 #include "portable.h"
     25 
     26 #include <stdio.h>
     27 
     28 #include <ac/string.h>
     29 
     30 #include "slap.h"
     31 #include "slap-config.h"
     32 #include "lutil.h"
     33 
     34 #include "rbac.h"
     35 
     36 static int ppolicy_cid = -1;
     37 
     38 static rbac_user_t *
     39 rbac_alloc_user()
     40 {
     41 	rbac_user_t *userp = ch_calloc( 1, sizeof(rbac_user_t) );
     42 
     43 	BER_BVZERO( &userp->tenantid );
     44 	BER_BVZERO( &userp->uid );
     45 	BER_BVZERO( &userp->dn );
     46 	BER_BVZERO( &userp->password );
     47 	BER_BVZERO( &userp->constraints );
     48 	BER_BVZERO( &userp->msg );
     49 	userp->roles = NULL;
     50 	userp->role_constraints = NULL;
     51 
     52 	return userp;
     53 }
     54 
     55 static int
     56 rbac_read_user_cb( Operation *op, SlapReply *rs )
     57 {
     58 	rbac_callback_info_t *cbp = op->o_callback->sc_private;
     59 	rbac_ad_t *user_ads;
     60 	rbac_user_t *userp = NULL;
     61 	int rc = 0, i;
     62 
     63 	Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb\n" );
     64 
     65 	if ( rs->sr_type != REP_SEARCH ) {
     66 		Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb: "
     67 				"sr_type != REP_SEARCH\n" );
     68 		return 0;
     69 	}
     70 
     71 	assert( cbp );
     72 
     73 	user_ads = cbp->tenantp->schema->user_ads;
     74 
     75 	userp = rbac_alloc_user();
     76 	if ( !userp ) {
     77 		Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb: "
     78 				"rbac_alloc_user failed\n" );
     79 
     80 		goto done;
     81 	}
     82 
     83 	ber_dupbv( &userp->dn, &rs->sr_entry->e_name );
     84 
     85 	Debug( LDAP_DEBUG_ANY, "DEBUG rbac_read_user_cb (%s): "
     86 			"rc (%d)\n",
     87 			userp->dn.bv_val, rc );
     88 
     89 	for ( i = 0; !BER_BVISNULL( &user_ads[i].attr ); i++ ) {
     90 		Attribute *attr = NULL;
     91 
     92 		attr = attr_find( rs->sr_entry->e_attrs, *user_ads[i].ad );
     93 		if ( attr != NULL ) {
     94 			switch ( user_ads[i].type ) {
     95 				case RBAC_ROLE_ASSIGNMENT:
     96 					ber_bvarray_dup_x( &userp->roles, attr->a_nvals, NULL );
     97 					break;
     98 				case RBAC_ROLE_CONSTRAINTS:
     99 					ber_bvarray_dup_x(
    100 							&userp->role_constraints, attr->a_nvals, NULL );
    101 					break;
    102 				case RBAC_USER_CONSTRAINTS:
    103 					ber_dupbv_x( &userp->constraints, &attr->a_nvals[0], NULL );
    104 					break;
    105 				case RBAC_UID:
    106 					ber_dupbv_x( &userp->uid, &attr->a_nvals[0], NULL );
    107 					break;
    108 				default:
    109 					break;
    110 			}
    111 		}
    112 	}
    113 
    114 done:;
    115 	cbp->private = userp;
    116 
    117 	return 0;
    118 }
    119 
    120 static int
    121 rbac_bind_cb( Operation *op, SlapReply *rs )
    122 {
    123 	rbac_user_t *ui = op->o_callback->sc_private;
    124 
    125 	LDAPControl *ctrl = ldap_control_find(
    126 			LDAP_CONTROL_PASSWORDPOLICYRESPONSE, rs->sr_ctrls, NULL );
    127 	if ( ctrl ) {
    128 		LDAP *ld;
    129 		ber_int_t expire, grace;
    130 		LDAPPasswordPolicyError error;
    131 
    132 		ldap_create( &ld );
    133 		if ( ld ) {
    134 			int rc = ldap_parse_passwordpolicy_control(
    135 					ld, ctrl, &expire, &grace, &error );
    136 			if ( rc == LDAP_SUCCESS ) {
    137 				ui->authz = RBAC_PASSWORD_GOOD;
    138 				if ( grace > 0 ) {
    139 					//ui->msg.bv_len = sprintf(ui->msg.bv_val,
    140 					//		"Password expired; %d grace logins remaining",
    141 					//		grace);
    142 					ui->authz = RBAC_BIND_NEW_AUTHTOK_REQD;
    143 				} else if ( error != PP_noError ) {
    144 					ber_str2bv( ldap_passwordpolicy_err2txt( error ), 0, 0,
    145 							&ui->msg );
    146 
    147 					switch ( error ) {
    148 						case PP_passwordExpired:
    149 							ui->authz = RBAC_PASSWORD_EXPIRATION_WARNING;
    150 
    151 							if ( expire >= 0 ) {
    152 								char *unit = "seconds";
    153 								if ( expire > 60 ) {
    154 									expire /= 60;
    155 									unit = "minutes";
    156 								}
    157 								if ( expire > 60 ) {
    158 									expire /= 60;
    159 									unit = "hours";
    160 								}
    161 								if ( expire > 24 ) {
    162 									expire /= 24;
    163 									unit = "days";
    164 								}
    165 #if 0 /* Who warns about expiration so far in advance? */
    166 								if (expire > 7) {
    167 									expire /= 7;
    168 									unit = "weeks";
    169 								}
    170 								if (expire > 4) {
    171 									expire /= 4;
    172 									unit = "months";
    173 								}
    174 								if (expire > 12) {
    175 									expire /= 12;
    176 									unit = "years";
    177 								}
    178 #endif
    179 							}
    180 
    181 							//rs->sr_err = ;
    182 							break;
    183 						case PP_accountLocked:
    184 							ui->authz = RBAC_ACCOUNT_LOCKED;
    185 							//rs->sr_err = ;
    186 							break;
    187 						case PP_changeAfterReset:
    188 							ui->authz = RBAC_CHANGE_AFTER_RESET;
    189 							rs->sr_err = LDAP_SUCCESS;
    190 							break;
    191 						case PP_passwordModNotAllowed:
    192 							ui->authz = RBAC_NO_MODIFICATIONS;
    193 							//rs->sr_err = ;
    194 							break;
    195 						case PP_mustSupplyOldPassword:
    196 							ui->authz = RBAC_MUST_SUPPLY_OLD;
    197 							//rs->sr_err = ;
    198 							break;
    199 						case PP_insufficientPasswordQuality:
    200 							ui->authz = RBAC_INSUFFICIENT_QUALITY;
    201 							//rs->sr_err = ;
    202 							break;
    203 						case PP_passwordTooShort:
    204 							ui->authz = RBAC_PASSWORD_TOO_SHORT;
    205 							//rs->sr_err = ;
    206 							break;
    207 						case PP_passwordTooYoung:
    208 							ui->authz = RBAC_PASSWORD_TOO_YOUNG;
    209 							//rs->sr_err = ;
    210 							break;
    211 						case PP_passwordInHistory:
    212 							ui->authz = RBAC_HISTORY_VIOLATION;
    213 							//rs->sr_err = ;
    214 							break;
    215 						case PP_noError:
    216 						default:
    217 							// do nothing
    218 							//ui->authz = RBAC_PASSWORD_GOOD;
    219 							rs->sr_err = LDAP_SUCCESS;
    220 							break;
    221 					}
    222 
    223 //					switch (error) {
    224 //					case PP_passwordExpired:
    225 						/* report this during authz */
    226 //						rs->sr_err = LDAP_SUCCESS;
    227 						/* fallthru */
    228 //					case PP_changeAfterReset:
    229 //						ui->authz = RBAC_BIND_NEW_AUTHTOK_REQD;
    230 //					}
    231 				}
    232 			}
    233 			ldap_unbind_ext( ld, NULL, NULL );
    234 		}
    235 	}
    236 
    237 	return 0;
    238 }
    239 
    240 /* exported user functions */
    241 int
    242 rbac_authenticate_user( Operation *op, rbac_user_t *userp )
    243 {
    244 	int rc = LDAP_SUCCESS;
    245 	slap_callback cb = { 0 };
    246 	SlapReply rs2 = { REP_RESULT };
    247 	Operation op2 = *op;
    248 	LDAPControl *sctrls[4];
    249 	LDAPControl sctrl[3];
    250 	int nsctrls = 0;
    251 	LDAPControl c;
    252 	struct berval ber_bvnull = BER_BVNULL;
    253 	struct berval dn, ndn;
    254 
    255 	rc = dnPrettyNormal( 0, &userp->dn, &dn, &ndn, NULL );
    256 	if ( rc != LDAP_SUCCESS ) {
    257 		goto done;
    258 	}
    259 
    260 	cb.sc_response = rbac_bind_cb;
    261 	cb.sc_private = userp;
    262 	op2.o_callback = &cb;
    263 	op2.o_dn = ber_bvnull;
    264 	op2.o_ndn = ber_bvnull;
    265 	op2.o_tag = LDAP_REQ_BIND;
    266 	op2.o_protocol = LDAP_VERSION3;
    267 	op2.orb_method = LDAP_AUTH_SIMPLE;
    268 	op2.orb_cred = userp->password;
    269 	op2.o_req_dn = dn;
    270 	op2.o_req_ndn = ndn;
    271 
    272 	// loading the ldap pw policy controls loaded into here, added by smm:
    273 	c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
    274 	c.ldctl_value.bv_val = NULL;
    275 	c.ldctl_value.bv_len = 0;
    276 	c.ldctl_iscritical = 0;
    277 	sctrl[nsctrls] = c;
    278 	sctrls[nsctrls] = &sctrl[nsctrls];
    279 	sctrls[++nsctrls] = NULL;
    280 	op2.o_ctrls = sctrls;
    281 
    282 	if ( ppolicy_cid < 0 ) {
    283 		rc = slap_find_control_id( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
    284 				&ppolicy_cid );
    285 		if ( rc != LDAP_SUCCESS ) {
    286 			goto done;
    287 		}
    288 	}
    289 	// smm - need to set the control flag too:
    290 	op2.o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_CRITICAL;
    291 
    292 	slap_op_time( &op2.o_time, &op2.o_tincr );
    293 	op2.o_bd = frontendDB;
    294 	rc = op2.o_bd->be_bind( &op2, &rs2 );
    295 	if ( userp->authz > 0 ) {
    296 		Debug( LDAP_DEBUG_ANY, "rbac_authenticate_user (%s): "
    297 				"password policy violation (%d)\n",
    298 				userp->dn.bv_val ? userp->dn.bv_val : "NULL", userp->authz );
    299 	}
    300 
    301 done:;
    302 	ch_free( dn.bv_val );
    303 	ch_free( ndn.bv_val );
    304 
    305 	Debug( LDAP_DEBUG_ANY, "rbac_authenticate_user (%s): "
    306 			"rc (%d)\n",
    307 			userp->dn.bv_val ? userp->dn.bv_val : "NULL", rc );
    308 	return rc;
    309 }
    310 
    311 /*
    312 	isvalidusername(): from OpenLDAP ~/contrib/slapd-modules/nssov/passwd.c
    313 	Checks to see if the specified name is a valid user name.
    314 
    315     This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name
    316 	 and 3.276 Portable Filename Character Set):
    317 	 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426
    318 	 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
    319 
    320 	 The standard defines user names valid if they contain characters from
    321 	 the set [A-Za-z0-9._-] where the hyphen should not be used as first
    322 	 character. As an extension this test allows the dolar '$' sign as the last
    323 	 character to support Samba special accounts.
    324 */
    325 static int
    326 isvalidusername( struct berval *bv )
    327 {
    328 	int i;
    329 	char *name = bv->bv_val;
    330 	if ( (name == NULL) || ( name[0] == '\0' ) ) return 0;
    331 	/* check first character */
    332 	if ( !( ( name[0] >= 'A' && name[0] <= 'Z' ) ||
    333 				 ( name[0] >= 'a' && name[0] <= 'z' ) ||
    334 				 ( name[0] >= '0' && name[0] <= '9' ) || name[0] == '.' ||
    335 				 name[0] == '_' ) )
    336 		return 0;
    337 	/* check other characters */
    338 	for ( i = 1; i < bv->bv_len; i++ ) {
    339 		if ( name[i] == '$' ) {
    340 			/* if the char is $ we require it to be the last char */
    341 			if ( name[i + 1] != '\0' ) return 0;
    342 		} else if ( !( ( name[i] >= 'A' && name[i] <= 'Z' ) ||
    343 							( name[i] >= 'a' && name[i] <= 'z' ) ||
    344 							( name[i] >= '0' && name[i] <= '9' ) ||
    345 							name[i] == '.' || name[i] == '_' ||
    346 							name[i] == '-' ) )
    347 			return 0;
    348 	}
    349 	/* no test failed so it must be good */
    350 	return -1;
    351 }
    352 
    353 rbac_user_t *
    354 rbac_read_user( Operation *op, rbac_req_t *reqp )
    355 {
    356 	int rc = LDAP_SUCCESS;
    357 	tenant_info_t *tenantp = rbac_tid2tenant( &reqp->tenantid );
    358 	rbac_user_t *userp = NULL;
    359 	char fbuf[RBAC_BUFLEN];
    360 	struct berval filter = { sizeof(fbuf), fbuf };
    361 	SlapReply rs2 = { REP_RESULT };
    362 	Operation op2 = *op;
    363 	slap_callback cb = { 0 };
    364 	rbac_callback_info_t rbac_cb;
    365 
    366 	if ( !tenantp ) {
    367 		Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
    368 				"missing tenant information\n" );
    369 		rc = LDAP_UNWILLING_TO_PERFORM;
    370 		goto done;
    371 	}
    372 
    373 	/* uid is a pre-requisite for reading the user information */
    374 	if ( BER_BVISNULL( &reqp->uid ) ) {
    375 		Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
    376 				"missing uid, unable to read user entry\n" );
    377 		rc = LDAP_UNWILLING_TO_PERFORM;
    378 		goto done;
    379 	}
    380 
    381 	if ( !isvalidusername( &reqp->uid ) ) {
    382 		Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
    383 				"invalid user id\n" );
    384 		rc = LDAP_NO_SUCH_OBJECT;
    385 		goto done;
    386 	}
    387 
    388 	rbac_cb.tenantp = tenantp;
    389 	rbac_cb.private = NULL;
    390 
    391 	memset( fbuf, 0, sizeof(fbuf) );
    392 	strcpy( fbuf, "uid=" );
    393 	strncat( fbuf, reqp->uid.bv_val, reqp->uid.bv_len );
    394 	filter.bv_val = fbuf;
    395 	filter.bv_len = strlen( fbuf );
    396 
    397 	if ( rc != LDAP_SUCCESS ) {
    398 		Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
    399 				"invalid DN syntax\n" );
    400 		goto done;
    401 	}
    402 
    403 	cb.sc_private = &rbac_cb;
    404 	cb.sc_response = rbac_read_user_cb;
    405 	op2.o_callback = &cb;
    406 	op2.o_tag = LDAP_REQ_SEARCH;
    407 	op2.o_dn = tenantp->admin;
    408 	op2.o_ndn = tenantp->admin;
    409 	op2.o_req_dn = tenantp->users_basedn;
    410 	op2.o_req_ndn = tenantp->users_basedn;
    411 	op2.ors_filterstr = filter;
    412 	op2.ors_filter = str2filter_x( &op2, filter.bv_val );
    413 	op2.ors_scope = LDAP_SCOPE_SUBTREE;
    414 	op2.ors_attrs = tenantp->schema->user_attrs;
    415 	op2.ors_tlimit = SLAP_NO_LIMIT;
    416 	op2.ors_slimit = SLAP_NO_LIMIT;
    417 	op2.ors_attrsonly = 0;
    418 	op2.o_bd = frontendDB;
    419 	op2.ors_limit = NULL;
    420 	rc = op2.o_bd->be_search( &op2, &rs2 );
    421 	filter_free_x( &op2, op2.ors_filter, 1 );
    422 
    423 done:;
    424 	if ( rc == LDAP_SUCCESS && rbac_cb.private ) {
    425 		userp = (rbac_user_t *)rbac_cb.private;
    426 		if ( !BER_BVISNULL( &reqp->authtok ) )
    427 			ber_dupbv( &userp->password, &reqp->authtok );
    428 		rbac_cb.private = NULL;
    429 		return userp;
    430 	} else {
    431 		userp = (rbac_user_t *)rbac_cb.private;
    432 		rbac_free_user( userp );
    433 		return NULL;
    434 	}
    435 }
    436 
    437 /* evaluate temporal constraints for the user */
    438 int
    439 rbac_user_temporal_constraint( rbac_user_t *userp )
    440 {
    441 	int rc = LDAP_SUCCESS;
    442 	rbac_constraint_t *cp = NULL;
    443 
    444 	if ( BER_BVISNULL( &userp->constraints ) ) {
    445 		/* no temporal constraint */
    446 		goto done;
    447 	}
    448 
    449 	cp = rbac_bv2constraint( &userp->constraints );
    450 	if ( !cp ) {
    451 		Debug( LDAP_DEBUG_ANY, "rbac_user_temporal_constraint: "
    452 				"invalid user constraint \n" );
    453 		rc = LDAP_OTHER;
    454 		goto done;
    455 	}
    456 
    457 	rc = rbac_check_time_constraint( cp );
    458 
    459 done:;
    460 	rbac_free_constraint( cp );
    461 
    462 	return rc;
    463 }
    464 
    465 /*
    466 rbac_constraint_t *
    467 rbac_user_role_constraintsx(rbac_user_t *userp)
    468 {
    469 	rbac_constraint_t *tmp, *cp = NULL;
    470 	int i = 0;
    471 
    472 	if (!userp || !userp->role_constraints)
    473 		goto done;
    474 
    475 	while (!BER_BVISNULL(&userp->role_constraints[i])) {
    476 		tmp = rbac_bv2constraint(&userp->role_constraints[i++]);
    477 		if (tmp) {
    478 			if (!cp) {
    479 				cp = tmp;
    480 			} else {
    481 				tmp->next = cp;
    482 				cp = tmp;
    483 			}
    484 		}
    485 	}
    486 
    487 done:;
    488 	return cp;
    489 }
    490 */
    491 
    492 rbac_constraint_t *
    493 rbac_user_role_constraints( BerVarray values )
    494 {
    495 	rbac_constraint_t *curr, *head = NULL;
    496 	int i = 0;
    497 
    498 	if ( values ) {
    499 		while ( !BER_BVISNULL( &values[i] ) ) {
    500 			curr = rbac_bv2constraint( &values[i++] );
    501 			if ( curr ) {
    502 				curr->next = head;
    503 				head = curr;
    504 			}
    505 		}
    506 	}
    507 
    508 	return head;
    509 }
    510 
    511 /*
    512 
    513 void main() {
    514    item * curr, * head;
    515    int i;
    516 
    517    head = NULL;
    518 
    519    for(i=1;i<=10;i++) {
    520       curr = (item *)malloc(sizeof(item));
    521       curr->val = i;
    522       curr->next  = head;
    523       head = curr;
    524    }
    525 
    526    curr = head;
    527 
    528    while(curr) {
    529       printf("%d\n", curr->val);
    530       curr = curr->next ;
    531    }
    532 }
    533 
    534  */
    535 
    536 /*
    537  *
    538 rbac_user_role_constraints2(BerVarray values)
    539 {
    540 	rbac_constraint_t *tmp, *cp = NULL;
    541 	int i = 0;
    542 
    543 	if (!values)
    544 		goto done;
    545 
    546 	while (!BER_BVISNULL(&values[i])) {
    547 		tmp = rbac_bv2constraint(&values[i++]);
    548 		if (tmp) {
    549 			if (!cp) {
    550 				cp = tmp;
    551 			} else {
    552 				tmp->next = cp;
    553 				cp = tmp;
    554 				//cp->next = tmp;
    555 				//cp = tmp->next;
    556 
    557 			}
    558 		}
    559 	}
    560 
    561 done:;
    562 	return cp;
    563 }
    564 
    565 
    566 rbac_user_role_constraints3(rbac_constraint_t *values)
    567 {
    568 	rbac_constraint_t *tmp, *cp = NULL;
    569 	int i = 0;
    570 
    571 	if (!values)
    572 		goto done;
    573 
    574 	while (!BER_BVISNULL(values[i])) {
    575 		tmp = rbac_bv2constraint(&values[i++]);
    576 		if (tmp) {
    577 			if (!cp) {
    578 				cp = tmp;
    579 			} else {
    580 				tmp->next = cp;
    581 				cp = tmp;
    582 			}
    583 		}
    584 	}
    585 
    586 done:;
    587 	return cp;
    588 }
    589 */
    590 
    591 void
    592 rbac_free_user( rbac_user_t *userp )
    593 {
    594 	if ( !userp ) return;
    595 
    596 	if ( !BER_BVISNULL( &userp->tenantid ) ) {
    597 		ber_memfree( userp->tenantid.bv_val );
    598 	}
    599 
    600 	if ( !BER_BVISNULL( &userp->uid ) ) {
    601 		ber_memfree( userp->uid.bv_val );
    602 	}
    603 
    604 	if ( !BER_BVISNULL( &userp->dn ) ) {
    605 		ber_memfree( userp->dn.bv_val );
    606 	}
    607 
    608 	if ( !BER_BVISNULL( &userp->constraints ) ) {
    609 		ber_memfree( userp->constraints.bv_val );
    610 	}
    611 
    612 	if ( !BER_BVISNULL( &userp->password ) ) {
    613 		ber_memfree( userp->password.bv_val );
    614 	}
    615 
    616 	if ( !BER_BVISNULL( &userp->msg ) ) {
    617 		ber_memfree( userp->msg.bv_val );
    618 	}
    619 
    620 	if ( userp->roles ) ber_bvarray_free( userp->roles );
    621 
    622 	if ( userp->role_constraints ) ber_bvarray_free( userp->role_constraints );
    623 
    624 	ch_free( userp );
    625 }
    626