]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/smbk5pwd/smbk5pwd.c
Add SLAP_MOD_INTERNAL flag on mod ops if it's defined
[openldap] / contrib / slapd-modules / smbk5pwd / smbk5pwd.c
1 /* smbk5pwd.c - Overlay for managing Samba and Heimdal passwords */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 2004-2005 by Howard Chu, Symas Corp.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15
16 #include <portable.h>
17
18 #ifndef SLAPD_OVER_SMBK5PWD
19 #define SLAPD_OVER_SMBK5PWD SLAPD_MOD_DYNAMIC
20 #endif
21
22 #ifdef SLAPD_OVER_SMBK5PWD
23
24 #include <slap.h>
25 #include <ac/errno.h>
26
27 #ifdef DO_KRB5
28 #include <ac/string.h>
29 #include <lber.h>
30 #include <lber_pvt.h>
31 #include <lutil.h>
32
33 /* make ASN1_MALLOC_ENCODE use our allocator */
34 #define malloc  ch_malloc
35
36 #include <krb5.h>
37 #include <kadm5/admin.h>
38 #include <hdb.h>
39
40 static krb5_context context;
41 static void *kadm_context;
42 static kadm5_config_params conf;
43 static HDB *db;
44
45 static AttributeDescription *ad_krb5Key;
46 static AttributeDescription *ad_krb5KeyVersionNumber;
47 static AttributeDescription *ad_krb5PrincipalName;
48 static ObjectClass *oc_krb5KDCEntry;
49 #endif
50
51 #ifdef DO_SAMBA
52 #include <openssl/des.h>
53 #include <openssl/md4.h>
54
55 static AttributeDescription *ad_sambaLMPassword;
56 static AttributeDescription *ad_sambaNTPassword;
57 static AttributeDescription *ad_sambaPwdLastSet;
58 static ObjectClass *oc_sambaSamAccount;
59 #endif
60
61 #if 0
62 static void smbk5pwd_destroy() {
63         kadm5_destroy(kadm_context);
64         krb5_free_context(context);
65 }
66 #endif
67
68 #ifdef DO_SAMBA
69 static const char hex[] = "0123456789abcdef";
70
71 /* From liblutil/passwd.c... */
72 static void lmPasswd_to_key(
73         const unsigned char *lmPasswd,
74         des_cblock *key)
75 {
76         /* make room for parity bits */
77         ((char *)key)[0] = lmPasswd[0];
78         ((char *)key)[1] = ((lmPasswd[0]&0x01)<<7) | (lmPasswd[1]>>1);
79         ((char *)key)[2] = ((lmPasswd[1]&0x03)<<6) | (lmPasswd[2]>>2);
80         ((char *)key)[3] = ((lmPasswd[2]&0x07)<<5) | (lmPasswd[3]>>3);
81         ((char *)key)[4] = ((lmPasswd[3]&0x0F)<<4) | (lmPasswd[4]>>4);
82         ((char *)key)[5] = ((lmPasswd[4]&0x1F)<<3) | (lmPasswd[5]>>5);
83         ((char *)key)[6] = ((lmPasswd[5]&0x3F)<<2) | (lmPasswd[6]>>6);
84         ((char *)key)[7] = ((lmPasswd[6]&0x7F)<<1);
85
86         des_set_odd_parity( key );
87 }
88
89 #define MAX_PWLEN 256
90 #define HASHLEN 16
91
92 static void hexify(
93         const char in[HASHLEN],
94         struct berval *out
95 )
96 {
97         int i;
98         char *a;
99         unsigned char *b;
100
101         out->bv_val = ch_malloc(HASHLEN*2 + 1);
102         out->bv_len = HASHLEN*2;
103
104         a = out->bv_val;
105         b = (unsigned char *)in;
106         for (i=0; i<HASHLEN; i++) {
107                 *a++ = hex[*b >> 4];
108                 *a++ = hex[*b++ & 0x0f];
109         }
110         *a++ = '\0';
111 }
112
113 static void lmhash(
114         struct berval *passwd,
115         struct berval *hash
116 )
117 {
118         char UcasePassword[15];
119         des_cblock key;
120         des_key_schedule schedule;
121         des_cblock StdText = "KGS!@#$%";
122         des_cblock hbuf[2];
123
124         strncpy( UcasePassword, passwd->bv_val, 14 );
125         UcasePassword[14] = '\0';
126         ldap_pvt_str2upper( UcasePassword );
127
128         lmPasswd_to_key( UcasePassword, &key );
129         des_set_key_unchecked( &key, schedule );
130         des_ecb_encrypt( &StdText, &hbuf[0], schedule , DES_ENCRYPT );
131
132         lmPasswd_to_key( &UcasePassword[7], &key );
133         des_set_key_unchecked( &key, schedule );
134         des_ecb_encrypt( &StdText, &hbuf[1], schedule , DES_ENCRYPT );
135
136         hexify( (char *)hbuf, hash );
137 }
138
139 static void nthash(
140         struct berval *passwd,
141         struct berval *hash
142 )
143 {
144         /* Windows currently only allows 14 character passwords, but
145          * may support up to 256 in the future. We assume this means
146          * 256 UCS2 characters, not 256 bytes...
147          */
148         char hbuf[HASHLEN];
149         int i;
150         MD4_CTX ctx;
151
152         if (passwd->bv_len > MAX_PWLEN*2)
153                 passwd->bv_len = MAX_PWLEN*2;
154                 
155         MD4_Init( &ctx );
156         MD4_Update( &ctx, passwd->bv_val, passwd->bv_len );
157         MD4_Final( hbuf, &ctx );
158
159         hexify( hbuf, hash );
160 }
161 #endif /* DO_SAMBA */
162
163 #ifdef DO_KRB5
164
165 static int smbk5pwd_op_cleanup(
166         Operation *op,
167         SlapReply *rs )
168 {
169         slap_callback *cb;
170
171         /* clear out the current key */
172         ldap_pvt_thread_pool_setkey( op->o_threadctx, smbk5pwd_op_cleanup,
173                 NULL, NULL );
174
175         /* free the callback */
176         cb = op->o_callback;
177         op->o_callback = cb->sc_next;
178         op->o_tmpfree( cb, op->o_tmpmemctx );
179         return 0;
180 }
181
182 static int smbk5pwd_op_bind(
183         Operation *op,
184         SlapReply *rs )
185 {
186         /* If this is a simple Bind, stash the Op pointer so our chk
187          * function can find it. Set a cleanup callback to clear it
188          * out when the Bind completes.
189          */
190         if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
191                 slap_callback *cb;
192                 ldap_pvt_thread_pool_setkey( op->o_threadctx, smbk5pwd_op_cleanup, op,
193                         NULL );
194                 cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
195                 cb->sc_cleanup = smbk5pwd_op_cleanup;
196                 cb->sc_next = op->o_callback;
197                 op->o_callback = cb;
198         }
199         return SLAP_CB_CONTINUE;
200 }
201
202 static LUTIL_PASSWD_CHK_FUNC k5key_chk;
203 static LUTIL_PASSWD_HASH_FUNC k5key_hash;
204 static const struct berval k5key_scheme = BER_BVC("{K5KEY}");
205
206 /* This password scheme stores no data in the userPassword attribute
207  * other than the scheme name. It assumes the invoking entry is a
208  * krb5KDCentry and compares the passed-in credentials against the
209  * krb5Key attribute. The krb5Key may be multi-valued, but they are
210  * simply multiple keytypes generated from the same input string, so
211  * only the first value needs to be compared here.
212  *
213  * Since the lutil_passwd API doesn't pass the Entry object in, we
214  * have to fetch it ourselves in order to get access to the other
215  * attributes. We accomplish this with the help of the overlay's Bind
216  * function, which stores the current Operation pointer in thread-specific
217  * storage so we can retrieve it here. The Operation provides all
218  * the necessary context for us to get Entry from the database.
219  */
220 static int k5key_chk(
221         const struct berval *sc,
222         const struct berval *passwd,
223         const struct berval *cred,
224         const char **text )
225 {
226         void *ctx;
227         Operation *op;
228         int rc;
229         Entry *e;
230         Attribute *a;
231     krb5_error_code ret;
232     krb5_keyblock key;
233     krb5_salt salt;
234         hdb_entry ent;
235
236         /* Find our thread context, find our Operation */
237         ctx = ldap_pvt_thread_pool_context();
238
239         if ( ldap_pvt_thread_pool_getkey( ctx, smbk5pwd_op_cleanup, (void **)&op, NULL ) ||
240                 !op )
241                 return LUTIL_PASSWD_ERR;
242
243         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
244         if ( rc != LDAP_SUCCESS ) return LUTIL_PASSWD_ERR;
245
246         rc = LUTIL_PASSWD_ERR;
247         do {
248                 size_t l;
249                 Key ekey = {0};
250
251                 a = attr_find( e->e_attrs, ad_krb5PrincipalName );
252                 if (!a ) break;
253
254                 memset( &ent, 0, sizeof(ent) );
255                 ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
256                 if ( ret ) break;
257                 krb5_get_pw_salt( context, ent.principal, &salt );
258                 krb5_free_principal( context, ent.principal );
259
260                 a = attr_find( e->e_attrs, ad_krb5Key );
261                 if ( !a ) break;
262
263                 ent.keys.len = 1;
264                 ent.keys.val = &ekey;
265                 decode_Key((unsigned char *) a->a_vals[0].bv_val,
266                         (size_t) a->a_vals[0].bv_len, &ent.keys.val[0], &l);
267                 if ( db->master_key_set )
268                         hdb_unseal_keys( context, db, &ent );
269
270                 krb5_string_to_key_salt( context, ekey.key.keytype, cred->bv_val,
271                         salt, &key );
272
273                 krb5_free_salt( context, salt );
274
275                 if ( memcmp( ekey.key.keyvalue.data, key.keyvalue.data,
276                         key.keyvalue.length ) == 0 ) rc = LUTIL_PASSWD_OK;
277
278                 krb5_free_keyblock_contents( context, &key );
279                 krb5_free_keyblock_contents( context, &ekey.key );
280
281         } while(0);
282         be_entry_release_r( op, e );
283         return rc;
284 }
285
286 static int k5key_hash(
287         const struct berval *scheme,
288         const struct berval *passwd,
289         struct berval *hash,
290         const char **text )
291 {
292         ber_dupbv( hash, (struct berval *)&k5key_scheme );
293         return LUTIL_PASSWD_OK;
294 }
295 #endif /* DO_KRB5 */
296
297 static int smbk5pwd_exop_passwd(
298         Operation *op,
299         SlapReply *rs )
300 {
301         int i, rc;
302         req_pwdexop_s *qpw = &op->oq_pwdexop;
303         Entry *e;
304         Attribute *a;
305         Modifications *ml;
306         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
307
308         /* Not the operation we expected, pass it on... */
309         if ( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) ) {
310                 return SLAP_CB_CONTINUE;
311         }
312
313         op->o_bd->bd_info = (BackendInfo *)on->on_info;
314         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
315         if ( rc != LDAP_SUCCESS ) return rc;
316
317 #ifdef DO_KRB5
318         /* Kerberos stuff */
319         do {
320                 krb5_error_code ret;
321                 hdb_entry ent;
322                 struct berval *keys;
323                 int kvno;
324
325                 if ( !is_entry_objectclass(e, oc_krb5KDCEntry, 0 ) ) break;
326
327                 a = attr_find( e->e_attrs, ad_krb5PrincipalName );
328                 if ( !a ) break;
329
330                 memset( &ent, 0, sizeof(ent) );
331                 ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
332                 if ( ret ) break;
333
334                 a = attr_find( e->e_attrs, ad_krb5KeyVersionNumber );
335                 if ( a ) {
336                         kvno = atoi(a->a_vals[0].bv_val);
337                 } else {
338                         /* shouldn't happen, this is a required attr */
339                         kvno = 0;
340                 }
341
342                 ret = _kadm5_set_keys(kadm_context, &ent, qpw->rs_new.bv_val);
343                 hdb_seal_keys(context, db, &ent);
344                 krb5_free_principal( context, ent.principal );
345
346                 keys = ch_malloc( (ent.keys.len + 1) * sizeof(struct berval));
347
348                 for (i = 0; i < ent.keys.len; i++) {
349                         unsigned char *buf;
350                         size_t len;
351
352                         ASN1_MALLOC_ENCODE(Key, buf, len, &ent.keys.val[i], &len, ret);
353                         if (ret != 0)
354                                 break;
355                         
356                         keys[i].bv_val = buf;
357                         keys[i].bv_len = len;
358                 }
359                 keys[i].bv_val = NULL;
360                 keys[i].bv_len = 0;
361
362                 _kadm5_free_keys(kadm_context, ent.keys.len, ent.keys.val);
363
364                 if ( i != ent.keys.len ) {
365                         ber_bvarray_free( keys );
366                         break;
367                 }
368
369                 ml = ch_malloc(sizeof(Modifications));
370                 if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
371                 ml->sml_next = qpw->rs_mods;
372                 qpw->rs_mods = ml;
373
374                 ml->sml_desc = ad_krb5Key;
375                 ml->sml_op = LDAP_MOD_REPLACE;
376 #ifdef SLAP_MOD_INTERNAL
377                 ml->sml_flags = SLAP_MOD_INTERNAL;
378 #endif
379                 ml->sml_values = keys;
380                 ml->sml_nvalues = NULL;
381                 
382                 ml = ch_malloc(sizeof(Modifications));
383                 ml->sml_next = qpw->rs_mods;
384                 qpw->rs_mods = ml;
385                 
386                 ml->sml_desc = ad_krb5KeyVersionNumber;
387                 ml->sml_op = LDAP_MOD_REPLACE;
388 #ifdef SLAP_MOD_INTERNAL
389                 ml->sml_flags = SLAP_MOD_INTERNAL;
390 #endif
391                 ml->sml_values = ch_malloc( 2 * sizeof(struct berval));
392                 ml->sml_values[0].bv_val = ch_malloc( 64 );
393                 ml->sml_values[0].bv_len = sprintf(ml->sml_values[0].bv_val,
394                         "%d", kvno+1 );
395                 ml->sml_values[1].bv_val = NULL;
396                 ml->sml_values[1].bv_len = 0;
397                 ml->sml_nvalues = NULL;
398         } while(0);
399 #endif /* DO_KRB5 */
400
401 #ifdef DO_SAMBA
402         /* Samba stuff */
403         if ( is_entry_objectclass(e, oc_sambaSamAccount, 0 ) ) {
404                 struct berval *keys;
405                 ber_len_t j,l;
406                 wchar_t *wcs, wc;
407                 char *c, *d;
408                 struct berval pwd;
409                 
410                 /* Expand incoming UTF8 string to UCS4 */
411                 l = ldap_utf8_chars(qpw->rs_new.bv_val);
412                 wcs = ch_malloc((l+1) * sizeof(wchar_t));
413
414                 ldap_x_utf8s_to_wcs( wcs, qpw->rs_new.bv_val, l );
415                 
416                 /* Truncate UCS4 to UCS2 */
417                 c = (char *)wcs;
418                 for (j=0; j<l; j++) {
419                         wc = wcs[j];
420                         *c++ = wc & 0xff;
421                         *c++ = (wc >> 8) & 0xff;
422                 }
423                 *c++ = 0;
424                 pwd.bv_val = (char *)wcs;
425                 pwd.bv_len = l * 2;
426
427                 ml = ch_malloc(sizeof(Modifications));
428                 if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
429                 ml->sml_next = qpw->rs_mods;
430                 qpw->rs_mods = ml;
431
432                 keys = ch_malloc( 2 * sizeof(struct berval) );
433                 keys[1].bv_val = NULL;
434                 keys[1].bv_len = 0;
435                 nthash( &pwd, keys );
436                 
437                 ml->sml_desc = ad_sambaNTPassword;
438                 ml->sml_op = LDAP_MOD_REPLACE;
439 #ifdef SLAP_MOD_INTERNAL
440                 ml->sml_flags = SLAP_MOD_INTERNAL;
441 #endif
442                 ml->sml_values = keys;
443                 ml->sml_nvalues = NULL;
444
445                 /* Truncate UCS2 to 8-bit ASCII */
446                 c = pwd.bv_val+1;
447                 d = pwd.bv_val+2;
448                 for (j=1; j<l; j++) {
449                         *c++ = *d++;
450                         d++;
451                 }
452                 pwd.bv_len /= 2;
453                 pwd.bv_val[pwd.bv_len] = '\0';
454
455                 ml = ch_malloc(sizeof(Modifications));
456                 ml->sml_next = qpw->rs_mods;
457                 qpw->rs_mods = ml;
458
459                 keys = ch_malloc( 2 * sizeof(struct berval) );
460                 keys[1].bv_val = NULL;
461                 keys[1].bv_len = 0;
462                 lmhash( &pwd, keys );
463                 
464                 ml->sml_desc = ad_sambaLMPassword;
465                 ml->sml_op = LDAP_MOD_REPLACE;
466 #ifdef SLAP_MOD_INTERNAL
467                 ml->sml_flags = SLAP_MOD_INTERNAL;
468 #endif
469                 ml->sml_values = keys;
470                 ml->sml_nvalues = NULL;
471
472                 ch_free(wcs);
473
474                 ml = ch_malloc(sizeof(Modifications));
475                 ml->sml_next = qpw->rs_mods;
476                 qpw->rs_mods = ml;
477
478                 keys = ch_malloc( 2 * sizeof(struct berval) );
479                 keys[1].bv_val = NULL;
480                 keys[1].bv_len = 0;
481                 keys[0].bv_val = ch_malloc(16);
482                 keys[0].bv_len = sprintf(keys[0].bv_val, "%d",
483                         slap_get_time());
484                 
485                 ml->sml_desc = ad_sambaPwdLastSet;
486                 ml->sml_op = LDAP_MOD_REPLACE;
487 #ifdef SLAP_MOD_INTERNAL
488                 ml->sml_flags = SLAP_MOD_INTERNAL;
489 #endif
490                 ml->sml_values = keys;
491                 ml->sml_nvalues = NULL;
492         }
493 #endif /* DO_SAMBA */
494         be_entry_release_r( op, e );
495
496         return SLAP_CB_CONTINUE;
497 }
498
499 static slap_overinst smbk5pwd;
500
501 int smbk5pwd_init() {
502         int rc;
503         const char *text;
504
505 #ifdef DO_KRB5
506         krb5_error_code ret;
507         extern HDB * _kadm5_s_get_db(void *);
508
509         /* Make sure all of our necessary schema items are loaded */
510         oc_krb5KDCEntry = oc_find("krb5KDCEntry");
511         if ( !oc_krb5KDCEntry ) return -1;
512
513         rc = slap_str2ad( "krb5Key", &ad_krb5Key, &text );
514         if ( rc ) return rc;
515         rc = slap_str2ad( "krb5KeyVersionNumber", &ad_krb5KeyVersionNumber, &text );
516         if ( rc ) return rc;
517         rc = slap_str2ad( "krb5PrincipalName", &ad_krb5PrincipalName, &text );
518         if ( rc ) return rc;
519
520         /* Initialize Kerberos context */
521         ret = krb5_init_context(&context);
522         if (ret) {
523                 return -1;
524         }
525
526         ret = kadm5_s_init_with_password_ctx( context,
527                 KADM5_ADMIN_SERVICE,
528                 NULL,
529                 KADM5_ADMIN_SERVICE,
530                 &conf, 0, 0, &kadm_context );
531         
532         db = _kadm5_s_get_db(kadm_context);
533 #endif /* DO_KRB5 */
534
535 #ifdef DO_SAMBA
536         oc_sambaSamAccount = oc_find("sambaSamAccount");
537         if ( !oc_sambaSamAccount ) return -1;
538
539         rc = slap_str2ad( "sambaLMPassword", &ad_sambaLMPassword, &text );
540         if ( rc ) return rc;
541         rc = slap_str2ad( "sambaNTPassword", &ad_sambaNTPassword, &text );
542         if ( rc ) return rc;
543         rc = slap_str2ad( "sambaPwdLastSet", &ad_sambaPwdLastSet, &text );
544         if ( rc ) return rc;
545 #endif /* DO_SAMBA */
546
547         smbk5pwd.on_bi.bi_type = "smbk5pwd";
548         smbk5pwd.on_bi.bi_extended = smbk5pwd_exop_passwd;
549
550 #ifdef DO_KRB5
551         smbk5pwd.on_bi.bi_op_bind = smbk5pwd_op_bind;
552
553         lutil_passwd_add( (struct berval *)&k5key_scheme, k5key_chk, k5key_hash );
554 #endif
555
556         return overlay_register( &smbk5pwd );
557 }
558
559 #if SLAPD_OVER_SMBK5PWD == SLAPD_MOD_DYNAMIC
560 int init_module(int argc, char *argv[]) {
561         return smbk5pwd_init();
562 }
563 #endif
564
565 #endif /* defined(SLAPD_OVER_SMBK5PWD) */