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