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