1 /* $NetBSD: smbk5pwd.c,v 1.3 2021/08/14 16:14:53 christos Exp $ */ 2 3 /* smbk5pwd.c - Overlay for managing Samba and Heimdal passwords */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2004-2021 The OpenLDAP Foundation. 8 * Portions Copyright 2004-2005 by Howard Chu, Symas Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted only as authorized by the OpenLDAP 13 * Public License. 14 * 15 * A copy of this license is available in the file LICENSE in the 16 * top-level directory of the distribution or, alternatively, at 17 * <http://www.OpenLDAP.org/license.html>. 18 */ 19 /* ACKNOWLEDGEMENTS: 20 * Support for table-driven configuration added by Pierangelo Masarati. 21 * Support for sambaPwdMustChange and sambaPwdCanChange added by Marco D'Ettorre. 22 * Support for shadowLastChange added by SATOH Fumiyasu @ OSS Technology, Inc. 23 */ 24 25 #include <portable.h> 26 27 #ifndef SLAPD_OVER_SMBK5PWD 28 #define SLAPD_OVER_SMBK5PWD SLAPD_MOD_DYNAMIC 29 #endif 30 31 #ifdef SLAPD_OVER_SMBK5PWD 32 33 #include <slap.h> 34 #include <ac/errno.h> 35 #include <ac/string.h> 36 37 #include "slap-config.h" 38 39 #ifdef DO_KRB5 40 #include <lber.h> 41 #include <lber_pvt.h> 42 #include <lutil.h> 43 44 /* make ASN1_MALLOC_ENCODE use our allocator */ 45 #define malloc ch_malloc 46 47 #include <krb5.h> 48 #include <kadm5/admin.h> 49 #include <hdb.h> 50 51 #ifndef HDB_INTERFACE_VERSION 52 #define HDB_MASTER_KEY_SET master_key_set 53 #else 54 #define HDB_MASTER_KEY_SET hdb_master_key_set 55 #endif 56 57 static krb5_context context; 58 static void *kadm_context; 59 static kadm5_config_params conf; 60 static HDB *db; 61 62 static AttributeDescription *ad_krb5Key; 63 static AttributeDescription *ad_krb5KeyVersionNumber; 64 static AttributeDescription *ad_krb5PrincipalName; 65 static AttributeDescription *ad_krb5ValidEnd; 66 static ObjectClass *oc_krb5KDCEntry; 67 #endif 68 69 #ifdef DO_SAMBA 70 #ifdef HAVE_GNUTLS 71 #include <nettle/md4.h> 72 #elif HAVE_OPENSSL 73 #include <openssl/md4.h> 74 #else 75 #error Unsupported crypto backend. 76 #endif 77 #include "ldap_utf8.h" 78 79 static AttributeDescription *ad_sambaNTPassword; 80 static AttributeDescription *ad_sambaPwdLastSet; 81 static AttributeDescription *ad_sambaPwdMustChange; 82 static AttributeDescription *ad_sambaPwdCanChange; 83 static ObjectClass *oc_sambaSamAccount; 84 #endif 85 86 #ifdef DO_SHADOW 87 static AttributeDescription *ad_shadowLastChange; 88 static ObjectClass *oc_shadowAccount; 89 #endif 90 91 /* Per-instance configuration information */ 92 typedef struct smbk5pwd_t { 93 unsigned mode; 94 #define SMBK5PWD_F_KRB5 (0x1U) 95 #define SMBK5PWD_F_SAMBA (0x2U) 96 #define SMBK5PWD_F_SHADOW (0x4U) 97 98 #define SMBK5PWD_DO_KRB5(pi) ((pi)->mode & SMBK5PWD_F_KRB5) 99 #define SMBK5PWD_DO_SAMBA(pi) ((pi)->mode & SMBK5PWD_F_SAMBA) 100 #define SMBK5PWD_DO_SHADOW(pi) ((pi)->mode & SMBK5PWD_F_SHADOW) 101 102 #ifdef DO_KRB5 103 /* nothing yet */ 104 #endif 105 106 #ifdef DO_SAMBA 107 /* How many seconds before forcing a password change? */ 108 time_t smb_must_change; 109 /* How many seconds after allowing a password change? */ 110 time_t smb_can_change; 111 #endif 112 113 #ifdef DO_SHADOW 114 /* nothing yet */ 115 #endif 116 } smbk5pwd_t; 117 118 static const unsigned SMBK5PWD_F_ALL = 119 0 120 #ifdef DO_KRB5 121 | SMBK5PWD_F_KRB5 122 #endif 123 #ifdef DO_SAMBA 124 | SMBK5PWD_F_SAMBA 125 #endif 126 #ifdef DO_SHADOW 127 | SMBK5PWD_F_SHADOW 128 #endif 129 ; 130 131 static int smbk5pwd_modules_init( smbk5pwd_t *pi ); 132 133 #ifdef DO_SAMBA 134 static const char hex[] = "0123456789abcdef"; 135 136 #define MAX_PWLEN 256 137 #define HASHLEN 16 138 139 static void hexify( 140 const char in[HASHLEN], 141 struct berval *out 142 ) 143 { 144 int i; 145 char *a; 146 unsigned char *b; 147 148 out->bv_val = ch_malloc(HASHLEN*2 + 1); 149 out->bv_len = HASHLEN*2; 150 151 a = out->bv_val; 152 b = (unsigned char *)in; 153 for (i=0; i<HASHLEN; i++) { 154 *a++ = hex[*b >> 4]; 155 *a++ = hex[*b++ & 0x0f]; 156 } 157 *a++ = '\0'; 158 } 159 160 static void nthash( 161 struct berval *passwd, 162 struct berval *hash 163 ) 164 { 165 /* Windows currently only allows 14 character passwords, but 166 * may support up to 256 in the future. We assume this means 167 * 256 UCS2 characters, not 256 bytes... 168 */ 169 char hbuf[HASHLEN]; 170 #ifdef HAVE_OPENSSL 171 MD4_CTX ctx; 172 #elif defined(HAVE_GNUTLS) 173 struct md4_ctx ctx; 174 #endif 175 176 if (passwd->bv_len > MAX_PWLEN*2) 177 passwd->bv_len = MAX_PWLEN*2; 178 179 #ifdef HAVE_OPENSSL 180 MD4_Init( &ctx ); 181 MD4_Update( &ctx, passwd->bv_val, passwd->bv_len ); 182 MD4_Final( (unsigned char *)hbuf, &ctx ); 183 #elif defined(HAVE_GNUTLS) 184 md4_init( &ctx ); 185 md4_update( &ctx, passwd->bv_len, (unsigned char *)passwd->bv_val ); 186 md4_digest( &ctx, sizeof(hbuf), (unsigned char *)hbuf ); 187 #endif 188 189 hexify( hbuf, hash ); 190 } 191 #endif /* DO_SAMBA */ 192 193 #ifdef DO_KRB5 194 195 static int smbk5pwd_op_cleanup( 196 Operation *op, 197 SlapReply *rs ) 198 { 199 slap_callback *cb; 200 201 /* clear out the current key */ 202 ldap_pvt_thread_pool_setkey( op->o_threadctx, smbk5pwd_op_cleanup, 203 NULL, 0, NULL, NULL ); 204 205 /* free the callback */ 206 cb = op->o_callback; 207 op->o_callback = cb->sc_next; 208 op->o_tmpfree( cb, op->o_tmpmemctx ); 209 return 0; 210 } 211 212 static int smbk5pwd_op_bind( 213 Operation *op, 214 SlapReply *rs ) 215 { 216 /* If this is a simple Bind, stash the Op pointer so our chk 217 * function can find it. Set a cleanup callback to clear it 218 * out when the Bind completes. 219 */ 220 if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) { 221 slap_callback *cb; 222 ldap_pvt_thread_pool_setkey( op->o_threadctx, 223 smbk5pwd_op_cleanup, op, 0, NULL, NULL ); 224 cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx ); 225 cb->sc_cleanup = smbk5pwd_op_cleanup; 226 cb->sc_next = op->o_callback; 227 op->o_callback = cb; 228 } 229 return SLAP_CB_CONTINUE; 230 } 231 232 static LUTIL_PASSWD_CHK_FUNC k5key_chk; 233 static LUTIL_PASSWD_HASH_FUNC k5key_hash; 234 static const struct berval k5key_scheme = BER_BVC("{K5KEY}"); 235 236 /* This password scheme stores no data in the userPassword attribute 237 * other than the scheme name. It assumes the invoking entry is a 238 * krb5KDCentry and compares the passed-in credentials against the 239 * krb5Key attribute. The krb5Key may be multi-valued, but they are 240 * simply multiple keytypes generated from the same input string, so 241 * only the first value needs to be compared here. 242 * 243 * Since the lutil_passwd API doesn't pass the Entry object in, we 244 * have to fetch it ourselves in order to get access to the other 245 * attributes. We accomplish this with the help of the overlay's Bind 246 * function, which stores the current Operation pointer in thread-specific 247 * storage so we can retrieve it here. The Operation provides all 248 * the necessary context for us to get Entry from the database. 249 */ 250 static int k5key_chk( 251 const struct berval *sc, 252 const struct berval *passwd, 253 const struct berval *cred, 254 const char **text ) 255 { 256 void *ctx, *op_tmp; 257 Operation *op; 258 int rc; 259 Entry *e; 260 Attribute *a; 261 krb5_error_code ret; 262 krb5_keyblock key; 263 krb5_salt salt; 264 hdb_entry ent; 265 266 /* Find our thread context, find our Operation */ 267 ctx = ldap_pvt_thread_pool_context(); 268 269 if ( ldap_pvt_thread_pool_getkey( ctx, smbk5pwd_op_cleanup, &op_tmp, NULL ) 270 || !op_tmp ) 271 return LUTIL_PASSWD_ERR; 272 op = op_tmp; 273 274 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 275 if ( rc != LDAP_SUCCESS ) return LUTIL_PASSWD_ERR; 276 277 rc = LUTIL_PASSWD_ERR; 278 do { 279 size_t l; 280 Key ekey = {0}; 281 282 a = attr_find( e->e_attrs, ad_krb5PrincipalName ); 283 if (!a ) break; 284 285 memset( &ent, 0, sizeof(ent) ); 286 ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal); 287 if ( ret ) break; 288 289 a = attr_find( e->e_attrs, ad_krb5ValidEnd ); 290 if (a) { 291 struct lutil_tm tm; 292 struct lutil_timet tt; 293 if ( lutil_parsetime( a->a_vals[0].bv_val, &tm ) == 0 && 294 lutil_tm2time( &tm, &tt ) == 0 && tt.tt_sec < op->o_time ) { 295 /* Account is expired */ 296 rc = LUTIL_PASSWD_ERR; 297 break; 298 } 299 } 300 301 krb5_get_pw_salt( context, ent.principal, &salt ); 302 krb5_free_principal( context, ent.principal ); 303 304 a = attr_find( e->e_attrs, ad_krb5Key ); 305 if ( !a ) break; 306 307 ent.keys.len = 1; 308 ent.keys.val = &ekey; 309 decode_Key((unsigned char *) a->a_vals[0].bv_val, 310 (size_t) a->a_vals[0].bv_len, &ent.keys.val[0], &l); 311 if ( db->HDB_MASTER_KEY_SET ) 312 hdb_unseal_keys( context, db, &ent ); 313 314 krb5_string_to_key_salt( context, ekey.key.keytype, cred->bv_val, 315 salt, &key ); 316 317 krb5_free_salt( context, salt ); 318 319 if ( memcmp( ekey.key.keyvalue.data, key.keyvalue.data, 320 key.keyvalue.length ) == 0 ) rc = LUTIL_PASSWD_OK; 321 322 krb5_free_keyblock_contents( context, &key ); 323 krb5_free_keyblock_contents( context, &ekey.key ); 324 325 } while(0); 326 be_entry_release_r( op, e ); 327 return rc; 328 } 329 330 static int k5key_hash( 331 const struct berval *scheme, 332 const struct berval *passwd, 333 struct berval *hash, 334 const char **text ) 335 { 336 ber_dupbv( hash, (struct berval *)&k5key_scheme ); 337 return LUTIL_PASSWD_OK; 338 } 339 #endif /* DO_KRB5 */ 340 341 static int smbk5pwd_exop_passwd( 342 Operation *op, 343 SlapReply *rs ) 344 { 345 int rc; 346 req_pwdexop_s *qpw = &op->oq_pwdexop; 347 Entry *e; 348 Modifications *ml; 349 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 350 smbk5pwd_t *pi = on->on_bi.bi_private; 351 char term; 352 353 /* Not the operation we expected, pass it on... */ 354 if ( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) ) { 355 return SLAP_CB_CONTINUE; 356 } 357 358 op->o_bd->bd_info = (BackendInfo *)on->on_info; 359 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 360 if ( rc != LDAP_SUCCESS ) return rc; 361 362 term = qpw->rs_new.bv_val[qpw->rs_new.bv_len]; 363 qpw->rs_new.bv_val[qpw->rs_new.bv_len] = '\0'; 364 365 #ifdef DO_KRB5 366 /* Kerberos stuff */ 367 do { 368 krb5_error_code ret; 369 hdb_entry ent; 370 struct berval *keys; 371 size_t nkeys; 372 int kvno, i; 373 Attribute *a; 374 375 if ( !SMBK5PWD_DO_KRB5( pi ) ) break; 376 377 if ( !is_entry_objectclass(e, oc_krb5KDCEntry, 0 ) ) break; 378 379 a = attr_find( e->e_attrs, ad_krb5PrincipalName ); 380 if ( !a ) break; 381 382 memset( &ent, 0, sizeof(ent) ); 383 ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal); 384 if ( ret ) break; 385 386 a = attr_find( e->e_attrs, ad_krb5KeyVersionNumber ); 387 kvno = 0; 388 if ( a ) { 389 if ( lutil_atoi( &kvno, a->a_vals[0].bv_val ) != 0 ) { 390 Debug( LDAP_DEBUG_ANY, "%s smbk5pwd EXOP: " 391 "dn=\"%s\" unable to parse krb5KeyVersionNumber=\"%s\"\n", 392 op->o_log_prefix, e->e_name.bv_val, a->a_vals[0].bv_val ); 393 } 394 395 } else { 396 /* shouldn't happen, this is a required attr */ 397 Debug( LDAP_DEBUG_ANY, "%s smbk5pwd EXOP: " 398 "dn=\"%s\" missing krb5KeyVersionNumber\n", 399 op->o_log_prefix, e->e_name.bv_val ); 400 } 401 402 ret = hdb_generate_key_set_password(context, ent.principal, 403 qpw->rs_new.bv_val, &ent.keys.val, &nkeys); 404 ent.keys.len = nkeys; 405 hdb_seal_keys(context, db, &ent); 406 krb5_free_principal( context, ent.principal ); 407 408 keys = ch_malloc( (ent.keys.len + 1) * sizeof(struct berval)); 409 410 for (i = 0; i < ent.keys.len; i++) { 411 unsigned char *buf; 412 size_t len; 413 414 ASN1_MALLOC_ENCODE(Key, buf, len, &ent.keys.val[i], &len, ret); 415 if (ret != 0) 416 break; 417 418 keys[i].bv_val = (char *)buf; 419 keys[i].bv_len = len; 420 } 421 BER_BVZERO( &keys[i] ); 422 423 hdb_free_keys(context, ent.keys.len, ent.keys.val); 424 425 if ( i != ent.keys.len ) { 426 ber_bvarray_free( keys ); 427 break; 428 } 429 430 ml = ch_malloc(sizeof(Modifications)); 431 if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next; 432 ml->sml_next = qpw->rs_mods; 433 qpw->rs_mods = ml; 434 435 ml->sml_desc = ad_krb5Key; 436 ml->sml_op = LDAP_MOD_REPLACE; 437 #ifdef SLAP_MOD_INTERNAL 438 ml->sml_flags = SLAP_MOD_INTERNAL; 439 #endif 440 ml->sml_numvals = i; 441 ml->sml_values = keys; 442 ml->sml_nvalues = NULL; 443 444 ml = ch_malloc(sizeof(Modifications)); 445 ml->sml_next = qpw->rs_mods; 446 qpw->rs_mods = ml; 447 448 ml->sml_desc = ad_krb5KeyVersionNumber; 449 ml->sml_op = LDAP_MOD_REPLACE; 450 #ifdef SLAP_MOD_INTERNAL 451 ml->sml_flags = SLAP_MOD_INTERNAL; 452 #endif 453 ml->sml_numvals = 1; 454 ml->sml_values = ch_malloc( 2 * sizeof(struct berval)); 455 ml->sml_values[0].bv_val = ch_malloc( 64 ); 456 ml->sml_values[0].bv_len = sprintf(ml->sml_values[0].bv_val, 457 "%d", kvno+1 ); 458 BER_BVZERO( &ml->sml_values[1] ); 459 ml->sml_nvalues = NULL; 460 } while ( 0 ); 461 #endif /* DO_KRB5 */ 462 463 #ifdef DO_SAMBA 464 /* Samba stuff */ 465 if ( SMBK5PWD_DO_SAMBA( pi ) && is_entry_objectclass(e, oc_sambaSamAccount, 0 ) ) { 466 struct berval *keys; 467 ber_len_t j,l; 468 wchar_t *wcs, wc; 469 char *c; 470 struct berval pwd; 471 472 /* Expand incoming UTF8 string to UCS4 */ 473 l = ldap_utf8_chars(qpw->rs_new.bv_val); 474 wcs = ch_malloc((l+1) * sizeof(wchar_t)); 475 476 ldap_x_utf8s_to_wcs( wcs, qpw->rs_new.bv_val, l ); 477 478 /* Truncate UCS4 to UCS2 */ 479 c = (char *)wcs; 480 for (j=0; j<l; j++) { 481 wc = wcs[j]; 482 *c++ = wc & 0xff; 483 *c++ = (wc >> 8) & 0xff; 484 } 485 *c++ = 0; 486 pwd.bv_val = (char *)wcs; 487 pwd.bv_len = l * 2; 488 489 ml = ch_malloc(sizeof(Modifications)); 490 if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next; 491 ml->sml_next = qpw->rs_mods; 492 qpw->rs_mods = ml; 493 494 keys = ch_malloc( 2 * sizeof(struct berval) ); 495 BER_BVZERO( &keys[1] ); 496 nthash( &pwd, keys ); 497 498 ml->sml_desc = ad_sambaNTPassword; 499 ml->sml_op = LDAP_MOD_REPLACE; 500 #ifdef SLAP_MOD_INTERNAL 501 ml->sml_flags = SLAP_MOD_INTERNAL; 502 #endif 503 ml->sml_numvals = 1; 504 ml->sml_values = keys; 505 ml->sml_nvalues = NULL; 506 507 ch_free(wcs); 508 509 ml = ch_malloc(sizeof(Modifications)); 510 ml->sml_next = qpw->rs_mods; 511 qpw->rs_mods = ml; 512 513 keys = ch_malloc( 2 * sizeof(struct berval) ); 514 keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) ); 515 keys[0].bv_len = snprintf(keys[0].bv_val, 516 LDAP_PVT_INTTYPE_CHARS(long), 517 "%ld", slap_get_time()); 518 BER_BVZERO( &keys[1] ); 519 520 ml->sml_desc = ad_sambaPwdLastSet; 521 ml->sml_op = LDAP_MOD_REPLACE; 522 #ifdef SLAP_MOD_INTERNAL 523 ml->sml_flags = SLAP_MOD_INTERNAL; 524 #endif 525 ml->sml_numvals = 1; 526 ml->sml_values = keys; 527 ml->sml_nvalues = NULL; 528 529 if (pi->smb_must_change) 530 { 531 ml = ch_malloc(sizeof(Modifications)); 532 ml->sml_next = qpw->rs_mods; 533 qpw->rs_mods = ml; 534 535 keys = ch_malloc( 2 * sizeof(struct berval) ); 536 keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) ); 537 keys[0].bv_len = snprintf(keys[0].bv_val, 538 LDAP_PVT_INTTYPE_CHARS(long), 539 "%ld", slap_get_time() + pi->smb_must_change); 540 BER_BVZERO( &keys[1] ); 541 542 ml->sml_desc = ad_sambaPwdMustChange; 543 ml->sml_op = LDAP_MOD_REPLACE; 544 #ifdef SLAP_MOD_INTERNAL 545 ml->sml_flags = SLAP_MOD_INTERNAL; 546 #endif 547 ml->sml_numvals = 1; 548 ml->sml_values = keys; 549 ml->sml_nvalues = NULL; 550 } 551 552 if (pi->smb_can_change) 553 { 554 ml = ch_malloc(sizeof(Modifications)); 555 ml->sml_next = qpw->rs_mods; 556 qpw->rs_mods = ml; 557 558 keys = ch_malloc( 2 * sizeof(struct berval) ); 559 keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) ); 560 keys[0].bv_len = snprintf(keys[0].bv_val, 561 LDAP_PVT_INTTYPE_CHARS(long), 562 "%ld", slap_get_time() + pi->smb_can_change); 563 BER_BVZERO( &keys[1] ); 564 565 ml->sml_desc = ad_sambaPwdCanChange; 566 ml->sml_op = LDAP_MOD_REPLACE; 567 #ifdef SLAP_MOD_INTERNAL 568 ml->sml_flags = SLAP_MOD_INTERNAL; 569 #endif 570 ml->sml_numvals = 1; 571 ml->sml_values = keys; 572 ml->sml_nvalues = NULL; 573 } 574 } 575 #endif /* DO_SAMBA */ 576 577 #ifdef DO_SHADOW 578 /* shadow stuff */ 579 if ( SMBK5PWD_DO_SHADOW( pi ) && is_entry_objectclass(e, oc_shadowAccount, 0 ) ) { 580 struct berval *keys; 581 582 ml = ch_malloc(sizeof(Modifications)); 583 if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next; 584 ml->sml_next = qpw->rs_mods; 585 qpw->rs_mods = ml; 586 587 keys = ch_malloc( sizeof(struct berval) * 2); 588 BER_BVZERO( &keys[1] ); 589 keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) ); 590 keys[0].bv_len = snprintf(keys[0].bv_val, 591 LDAP_PVT_INTTYPE_CHARS(long), 592 "%ld", (long)(slap_get_time() / (60 * 60 * 24))); 593 594 ml->sml_desc = ad_shadowLastChange; 595 ml->sml_op = LDAP_MOD_REPLACE; 596 #ifdef SLAP_MOD_INTERNAL 597 ml->sml_flags = SLAP_MOD_INTERNAL; 598 #endif 599 ml->sml_numvals = 1; 600 ml->sml_values = keys; 601 ml->sml_nvalues = NULL; 602 } 603 #endif /* DO_SHADOW */ 604 605 be_entry_release_r( op, e ); 606 qpw->rs_new.bv_val[qpw->rs_new.bv_len] = term; 607 608 return SLAP_CB_CONTINUE; 609 } 610 611 static slap_overinst smbk5pwd; 612 613 /* back-config stuff */ 614 enum { 615 PC_SMB_MUST_CHANGE = 1, 616 PC_SMB_CAN_CHANGE, 617 PC_SMB_ENABLE 618 }; 619 620 static ConfigDriver smbk5pwd_cf_func; 621 622 /* 623 * NOTE: uses OID arcs OLcfgCtAt:1 and OLcfgCtOc:1 624 */ 625 626 static ConfigTable smbk5pwd_cfats[] = { 627 { "smbk5pwd-enable", "arg", 628 2, 0, 0, ARG_MAGIC|PC_SMB_ENABLE, smbk5pwd_cf_func, 629 "( OLcfgCtAt:1.1 NAME 'olcSmbK5PwdEnable' " 630 "DESC 'Modules to be enabled' " 631 "EQUALITY caseIgnoreMatch " 632 "SYNTAX OMsDirectoryString )", NULL, NULL }, 633 { "smbk5pwd-must-change", "time", 634 2, 2, 0, ARG_MAGIC|ARG_INT|PC_SMB_MUST_CHANGE, smbk5pwd_cf_func, 635 "( OLcfgCtAt:1.2 NAME 'olcSmbK5PwdMustChange' " 636 "DESC 'Credentials validity interval' " 637 "EQUALITY integerMatch " 638 "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL }, 639 { "smbk5pwd-can-change", "time", 640 2, 2, 0, ARG_MAGIC|ARG_INT|PC_SMB_CAN_CHANGE, smbk5pwd_cf_func, 641 "( OLcfgCtAt:1.3 NAME 'olcSmbK5PwdCanChange' " 642 "DESC 'Credentials minimum validity interval' " 643 "EQUALITY integerMatch " 644 "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL }, 645 646 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 647 }; 648 649 static ConfigOCs smbk5pwd_cfocs[] = { 650 { "( OLcfgCtOc:1.1 " 651 "NAME 'olcSmbK5PwdConfig' " 652 "DESC 'smbk5pwd overlay configuration' " 653 "SUP olcOverlayConfig " 654 "MAY ( " 655 "olcSmbK5PwdEnable " 656 "$ olcSmbK5PwdMustChange " 657 "$ olcSmbK5PwdCanChange " 658 ") )", Cft_Overlay, smbk5pwd_cfats }, 659 660 { NULL, 0, NULL } 661 }; 662 663 /* 664 * add here other functionalities; handle their initialization 665 * as appropriate in smbk5pwd_modules_init(). 666 */ 667 static slap_verbmasks smbk5pwd_modules[] = { 668 { BER_BVC( "krb5" ), SMBK5PWD_F_KRB5 }, 669 { BER_BVC( "samba" ), SMBK5PWD_F_SAMBA }, 670 { BER_BVC( "shadow" ), SMBK5PWD_F_SHADOW }, 671 { BER_BVNULL, -1 } 672 }; 673 674 static int 675 smbk5pwd_cf_func( ConfigArgs *c ) 676 { 677 slap_overinst *on = (slap_overinst *)c->bi; 678 679 int rc = 0; 680 smbk5pwd_t *pi = on->on_bi.bi_private; 681 682 if ( c->op == SLAP_CONFIG_EMIT ) { 683 switch( c->type ) { 684 case PC_SMB_MUST_CHANGE: 685 #ifdef DO_SAMBA 686 c->value_int = pi->smb_must_change; 687 #else /* ! DO_SAMBA */ 688 c->value_int = 0; 689 #endif /* ! DO_SAMBA */ 690 break; 691 692 case PC_SMB_CAN_CHANGE: 693 #ifdef DO_SAMBA 694 c->value_int = pi->smb_can_change; 695 #else /* ! DO_SAMBA */ 696 c->value_int = 0; 697 #endif /* ! DO_SAMBA */ 698 break; 699 700 case PC_SMB_ENABLE: 701 c->rvalue_vals = NULL; 702 if ( pi->mode ) { 703 mask_to_verbs( smbk5pwd_modules, pi->mode, &c->rvalue_vals ); 704 if ( c->rvalue_vals == NULL ) { 705 rc = 1; 706 } 707 } 708 break; 709 710 default: 711 assert( 0 ); 712 rc = 1; 713 } 714 return rc; 715 716 } else if ( c->op == LDAP_MOD_DELETE ) { 717 switch( c->type ) { 718 case PC_SMB_MUST_CHANGE: 719 break; 720 721 case PC_SMB_CAN_CHANGE: 722 break; 723 724 case PC_SMB_ENABLE: 725 if ( !c->line ) { 726 pi->mode = 0; 727 728 } else { 729 int i; 730 731 i = verb_to_mask( c->line, smbk5pwd_modules ); 732 pi->mode &= ~smbk5pwd_modules[i].mask; 733 } 734 break; 735 736 default: 737 assert( 0 ); 738 rc = 1; 739 } 740 return rc; 741 } 742 743 switch( c->type ) { 744 case PC_SMB_MUST_CHANGE: 745 #ifdef DO_SAMBA 746 if ( c->value_int < 0 ) { 747 Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: " 748 "<%s> invalid negative value \"%d\".", 749 c->log, c->argv[ 0 ], c->value_int ); 750 return 1; 751 } 752 pi->smb_must_change = c->value_int; 753 #else /* ! DO_SAMBA */ 754 Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: " 755 "<%s> only meaningful " 756 "when compiled with -DDO_SAMBA.\n", 757 c->log, c->argv[ 0 ] ); 758 return 1; 759 #endif /* ! DO_SAMBA */ 760 break; 761 762 case PC_SMB_CAN_CHANGE: 763 #ifdef DO_SAMBA 764 if ( c->value_int < 0 ) { 765 Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: " 766 "<%s> invalid negative value \"%d\".", 767 c->log, c->argv[ 0 ], c->value_int ); 768 return 1; 769 } 770 pi->smb_can_change = c->value_int; 771 #else /* ! DO_SAMBA */ 772 Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: " 773 "<%s> only meaningful " 774 "when compiled with -DDO_SAMBA.\n", 775 c->log, c->argv[ 0 ] ); 776 return 1; 777 #endif /* ! DO_SAMBA */ 778 break; 779 780 case PC_SMB_ENABLE: { 781 slap_mask_t mode = pi->mode, m = 0; 782 783 rc = verbs_to_mask( c->argc, c->argv, smbk5pwd_modules, &m ); 784 if ( rc > 0 ) { 785 Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: " 786 "<%s> unknown module \"%s\".\n", 787 c->log, c->argv[ 0 ], c->argv[ rc ] ); 788 return 1; 789 } 790 791 /* we can hijack the smbk5pwd_t structure because 792 * from within the configuration, this is the only 793 * active thread. */ 794 pi->mode |= m; 795 796 #ifndef DO_KRB5 797 if ( SMBK5PWD_DO_KRB5( pi ) ) { 798 Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: " 799 "<%s> module \"%s\" only allowed when compiled with -DDO_KRB5.\n", 800 c->log, c->argv[ 0 ], c->argv[ rc ] ); 801 pi->mode = mode; 802 return 1; 803 } 804 #endif /* ! DO_KRB5 */ 805 806 #ifndef DO_SAMBA 807 if ( SMBK5PWD_DO_SAMBA( pi ) ) { 808 Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: " 809 "<%s> module \"%s\" only allowed when compiled with -DDO_SAMBA.\n", 810 c->log, c->argv[ 0 ], c->argv[ rc ] ); 811 pi->mode = mode; 812 return 1; 813 } 814 #endif /* ! DO_SAMBA */ 815 816 #ifndef DO_SHADOW 817 if ( SMBK5PWD_DO_SHADOW( pi ) ) { 818 Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: " 819 "<%s> module \"%s\" only allowed when compiled with -DDO_SHADOW.\n", 820 c->log, c->argv[ 0 ], c->argv[ rc ] ); 821 pi->mode = mode; 822 return 1; 823 } 824 #endif /* ! DO_SHADOW */ 825 826 /* Re-initialize the module, because 827 * the configuration might have changed */ 828 rc = smbk5pwd_modules_init( pi ); 829 if ( rc ) { 830 pi->mode = mode; 831 return 1; 832 } 833 834 } break; 835 836 default: 837 assert( 0 ); 838 return 1; 839 } 840 return rc; 841 } 842 843 static int 844 smbk5pwd_modules_init( smbk5pwd_t *pi ) 845 { 846 static struct { 847 const char *name; 848 AttributeDescription **adp; 849 } 850 #ifdef DO_KRB5 851 krb5_ad[] = { 852 { "krb5Key", &ad_krb5Key }, 853 { "krb5KeyVersionNumber", &ad_krb5KeyVersionNumber }, 854 { "krb5PrincipalName", &ad_krb5PrincipalName }, 855 { "krb5ValidEnd", &ad_krb5ValidEnd }, 856 { NULL } 857 }, 858 #endif /* DO_KRB5 */ 859 #ifdef DO_SAMBA 860 samba_ad[] = { 861 { "sambaNTPassword", &ad_sambaNTPassword }, 862 { "sambaPwdLastSet", &ad_sambaPwdLastSet }, 863 { "sambaPwdMustChange", &ad_sambaPwdMustChange }, 864 { "sambaPwdCanChange", &ad_sambaPwdCanChange }, 865 { NULL } 866 }, 867 #endif /* DO_SAMBA */ 868 #ifdef DO_SHADOW 869 shadow_ad[] = { 870 { "shadowLastChange", &ad_shadowLastChange }, 871 { NULL } 872 }, 873 #endif /* DO_SHADOW */ 874 dummy_ad; 875 876 /* this is to silence the unused var warning */ 877 (void) dummy_ad; 878 879 #ifdef DO_KRB5 880 if ( SMBK5PWD_DO_KRB5( pi ) && oc_krb5KDCEntry == NULL ) { 881 krb5_error_code ret; 882 extern HDB *_kadm5_s_get_db(void *); 883 884 int i, rc; 885 886 /* Make sure all of our necessary schema items are loaded */ 887 oc_krb5KDCEntry = oc_find( "krb5KDCEntry" ); 888 if ( !oc_krb5KDCEntry ) { 889 Debug( LDAP_DEBUG_ANY, "smbk5pwd: " 890 "unable to find \"krb5KDCEntry\" objectClass.\n" ); 891 return -1; 892 } 893 894 for ( i = 0; krb5_ad[ i ].name != NULL; i++ ) { 895 const char *text; 896 897 *(krb5_ad[ i ].adp) = NULL; 898 899 rc = slap_str2ad( krb5_ad[ i ].name, krb5_ad[ i ].adp, &text ); 900 if ( rc != LDAP_SUCCESS ) { 901 Debug( LDAP_DEBUG_ANY, "smbk5pwd: " 902 "unable to find \"%s\" attributeType: %s (%d).\n", 903 krb5_ad[ i ].name, text, rc ); 904 oc_krb5KDCEntry = NULL; 905 return rc; 906 } 907 } 908 909 /* Initialize Kerberos context */ 910 ret = krb5_init_context(&context); 911 if (ret) { 912 Debug( LDAP_DEBUG_ANY, "smbk5pwd: " 913 "unable to initialize krb5 context (%d).\n", 914 ret ); 915 oc_krb5KDCEntry = NULL; 916 return -1; 917 } 918 919 ret = kadm5_s_init_with_password_ctx( context, 920 KADM5_ADMIN_SERVICE, 921 NULL, 922 KADM5_ADMIN_SERVICE, 923 &conf, 0, 0, &kadm_context ); 924 if (ret) { 925 char *err_str, *err_msg = "<unknown error>"; 926 err_str = krb5_get_error_string( context ); 927 if (!err_str) 928 err_msg = (char *)krb5_get_err_text( context, ret ); 929 Debug( LDAP_DEBUG_ANY, "smbk5pwd: " 930 "unable to initialize krb5 admin context: %s (%d).\n", 931 err_str ? err_str : err_msg, ret ); 932 if (err_str) 933 krb5_free_error_string( context, err_str ); 934 krb5_free_context( context ); 935 oc_krb5KDCEntry = NULL; 936 return -1; 937 } 938 939 db = _kadm5_s_get_db( kadm_context ); 940 } 941 #endif /* DO_KRB5 */ 942 943 #ifdef DO_SAMBA 944 if ( SMBK5PWD_DO_SAMBA( pi ) && oc_sambaSamAccount == NULL ) { 945 int i, rc; 946 947 oc_sambaSamAccount = oc_find( "sambaSamAccount" ); 948 if ( !oc_sambaSamAccount ) { 949 Debug( LDAP_DEBUG_ANY, "smbk5pwd: " 950 "unable to find \"sambaSamAccount\" objectClass.\n" ); 951 return -1; 952 } 953 954 for ( i = 0; samba_ad[ i ].name != NULL; i++ ) { 955 const char *text; 956 957 *(samba_ad[ i ].adp) = NULL; 958 959 rc = slap_str2ad( samba_ad[ i ].name, samba_ad[ i ].adp, &text ); 960 if ( rc != LDAP_SUCCESS ) { 961 Debug( LDAP_DEBUG_ANY, "smbk5pwd: " 962 "unable to find \"%s\" attributeType: %s (%d).\n", 963 samba_ad[ i ].name, text, rc ); 964 oc_sambaSamAccount = NULL; 965 return rc; 966 } 967 } 968 } 969 #endif /* DO_SAMBA */ 970 971 #ifdef DO_SHADOW 972 if ( SMBK5PWD_DO_SHADOW( pi ) && oc_shadowAccount == NULL ) { 973 int i, rc; 974 975 oc_shadowAccount = oc_find( "shadowAccount" ); 976 if ( !oc_shadowAccount ) { 977 Debug( LDAP_DEBUG_ANY, "smbk5pwd: " 978 "unable to find \"shadowAccount\" objectClass.\n" ); 979 return -1; 980 } 981 982 for ( i = 0; shadow_ad[ i ].name != NULL; i++ ) { 983 const char *text; 984 985 *(shadow_ad[ i ].adp) = NULL; 986 987 rc = slap_str2ad( shadow_ad[ i ].name, shadow_ad[ i ].adp, &text ); 988 if ( rc != LDAP_SUCCESS ) { 989 Debug( LDAP_DEBUG_ANY, "smbk5pwd: " 990 "unable to find \"%s\" attributeType: %s (%d).\n", 991 shadow_ad[ i ].name, text, rc ); 992 oc_shadowAccount = NULL; 993 return rc; 994 } 995 } 996 } 997 #endif /* DO_SHADOW */ 998 999 return 0; 1000 } 1001 1002 static int 1003 smbk5pwd_db_init(BackendDB *be, ConfigReply *cr) 1004 { 1005 slap_overinst *on = (slap_overinst *)be->bd_info; 1006 smbk5pwd_t *pi; 1007 1008 pi = ch_calloc( 1, sizeof( smbk5pwd_t ) ); 1009 if ( pi == NULL ) { 1010 return 1; 1011 } 1012 on->on_bi.bi_private = (void *)pi; 1013 1014 return 0; 1015 } 1016 1017 static int 1018 smbk5pwd_db_open(BackendDB *be, ConfigReply *cr) 1019 { 1020 slap_overinst *on = (slap_overinst *)be->bd_info; 1021 smbk5pwd_t *pi = (smbk5pwd_t *)on->on_bi.bi_private; 1022 1023 int rc; 1024 1025 if ( pi->mode == 0 ) { 1026 pi->mode = SMBK5PWD_F_ALL; 1027 } 1028 1029 rc = smbk5pwd_modules_init( pi ); 1030 if ( rc ) { 1031 return rc; 1032 } 1033 1034 return 0; 1035 } 1036 1037 static int 1038 smbk5pwd_db_destroy(BackendDB *be, ConfigReply *cr) 1039 { 1040 slap_overinst *on = (slap_overinst *)be->bd_info; 1041 smbk5pwd_t *pi = (smbk5pwd_t *)on->on_bi.bi_private; 1042 1043 if ( pi ) { 1044 ch_free( pi ); 1045 } 1046 1047 return 0; 1048 } 1049 1050 int 1051 smbk5pwd_initialize(void) 1052 { 1053 int rc; 1054 1055 smbk5pwd.on_bi.bi_type = "smbk5pwd"; 1056 1057 smbk5pwd.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; 1058 smbk5pwd.on_bi.bi_db_init = smbk5pwd_db_init; 1059 smbk5pwd.on_bi.bi_db_open = smbk5pwd_db_open; 1060 smbk5pwd.on_bi.bi_db_destroy = smbk5pwd_db_destroy; 1061 1062 smbk5pwd.on_bi.bi_extended = smbk5pwd_exop_passwd; 1063 1064 #ifdef DO_KRB5 1065 smbk5pwd.on_bi.bi_op_bind = smbk5pwd_op_bind; 1066 1067 lutil_passwd_add( (struct berval *)&k5key_scheme, k5key_chk, k5key_hash ); 1068 #endif 1069 1070 smbk5pwd.on_bi.bi_cf_ocs = smbk5pwd_cfocs; 1071 1072 rc = config_register_schema( smbk5pwd_cfats, smbk5pwd_cfocs ); 1073 if ( rc ) { 1074 return rc; 1075 } 1076 1077 return overlay_register( &smbk5pwd ); 1078 } 1079 1080 #if SLAPD_OVER_SMBK5PWD == SLAPD_MOD_DYNAMIC 1081 int init_module(int argc, char *argv[]) { 1082 return smbk5pwd_initialize(); 1083 } 1084 #endif 1085 1086 #endif /* defined(SLAPD_OVER_SMBK5PWD) */ 1087