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