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