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