xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/rbac/rbacuser.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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 *
rbac_alloc_user()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
rbac_read_user_cb(Operation * op,SlapReply * rs)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
rbac_bind_cb(Operation * op,SlapReply * rs)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
rbac_authenticate_user(Operation * op,rbac_user_t * userp)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
isvalidusername(struct berval * bv)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 *
rbac_read_user(Operation * op,rbac_req_t * reqp)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
rbac_user_temporal_constraint(rbac_user_t * userp)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 *
rbac_user_role_constraints(BerVarray values)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
rbac_free_user(rbac_user_t * userp)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