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