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