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