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