]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/smbk5pwd/smbk5pwd.c
e82a73e445a4fab9134c1d67469df29ed8f07c0a
[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 chk_k5key;
203 static const struct berval scheme = BER_BVC("{K5KEY}");
204
205 /* This password scheme stores no data in the userPassword attribute
206  * other than the scheme name. It assumes the invoking entry is a
207  * krb5KDCentry and compares the passed-in credentials against the
208  * krb5Key attribute. The krb5Key may be multi-valued, but they are
209  * simply multiple keytypes generated from the same input string, so
210  * only the first value needs to be compared here.
211  *
212  * Since the lutil_passwd API doesn't pass the Entry object in, we
213  * have to fetch it ourselves in order to get access to the other
214  * attributes. We accomplish this with the help of the overlay's Bind
215  * function, which stores the current Operation pointer in thread-specific
216  * storage so we can retrieve it here. The Operation provides all
217  * the necessary context for us to get Entry from the database.
218  */
219 static int chk_k5key(
220         const struct berval *sc,
221         const struct berval *passwd,
222         const struct berval *cred,
223         const char **text )
224 {
225         void *ctx;
226         Operation *op;
227         int rc;
228         Entry *e;
229         Attribute *a;
230     krb5_error_code ret;
231     krb5_keyblock key;
232     krb5_salt salt;
233         hdb_entry ent;
234
235         /* Find our thread context, find our Operation */
236         ctx = ldap_pvt_thread_pool_context();
237
238         if ( ldap_pvt_thread_pool_getkey( ctx, smbk5pwd_op_cleanup, (void **)&op, NULL ) ||
239                 !op )
240                 return LUTIL_PASSWD_ERR;
241
242         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
243         if ( rc != LDAP_SUCCESS ) return LUTIL_PASSWD_ERR;
244
245         rc = LUTIL_PASSWD_ERR;
246         do {
247                 size_t l;
248                 Key ekey = {0};
249
250                 a = attr_find( e->e_attrs, ad_krb5PrincipalName );
251                 if (!a ) break;
252
253                 memset( &ent, 0, sizeof(ent) );
254                 ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
255                 if ( ret ) break;
256                 krb5_get_pw_salt( context, ent.principal, &salt );
257                 krb5_free_principal( context, ent.principal );
258
259                 a = attr_find( e->e_attrs, ad_krb5Key );
260                 if ( !a ) break;
261
262                 ent.keys.len = 1;
263                 ent.keys.val = &ekey;
264                 decode_Key((unsigned char *) a->a_vals[0].bv_val,
265                         (size_t) a->a_vals[0].bv_len, &ent.keys.val[0], &l);
266                 if ( db->master_key_set )
267                         hdb_unseal_keys( context, db, &ent );
268
269                 krb5_string_to_key_salt( context, ekey.key.keytype, cred->bv_val,
270                         salt, &key );
271
272                 krb5_free_salt( context, salt );
273
274                 if ( memcmp( ekey.key.keyvalue.data, key.keyvalue.data,
275                         key.keyvalue.length ) == 0 ) rc = LUTIL_PASSWD_OK;
276
277                 krb5_free_keyblock_contents( context, &key );
278                 krb5_free_keyblock_contents( context, &ekey.key );
279
280         } while(0);
281         be_entry_release_r( op, e );
282         return rc;
283 }
284 #endif /* DO_KRB5 */
285
286 static int smbk5pwd_exop_passwd(
287         Operation *op,
288         SlapReply *rs )
289 {
290         int i, rc;
291         req_pwdexop_s *qpw = &op->oq_pwdexop;
292         Entry *e;
293         Attribute *a;
294         Modifications *ml;
295         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
296
297         /* Not the operation we expected, pass it on... */
298         if ( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) ) {
299                 return SLAP_CB_CONTINUE;
300         }
301
302         op->o_bd->bd_info = (BackendInfo *)on->on_info;
303         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
304         if ( rc != LDAP_SUCCESS ) return rc;
305
306 #ifdef DO_KRB5
307         /* Kerberos stuff */
308         do {
309                 krb5_error_code ret;
310                 hdb_entry ent;
311                 struct berval *keys;
312                 int kvno;
313
314                 if ( !is_entry_objectclass(e, oc_krb5KDCEntry, 0 ) ) break;
315
316                 a = attr_find( e->e_attrs, ad_krb5PrincipalName );
317                 if ( !a ) break;
318
319                 memset( &ent, 0, sizeof(ent) );
320                 ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
321                 if ( ret ) break;
322
323                 a = attr_find( e->e_attrs, ad_krb5KeyVersionNumber );
324                 if ( a ) {
325                         kvno = atoi(a->a_vals[0].bv_val);
326                 } else {
327                         /* shouldn't happen, this is a required attr */
328                         kvno = 0;
329                 }
330
331                 ret = _kadm5_set_keys(kadm_context, &ent, qpw->rs_new.bv_val);
332                 hdb_seal_keys(context, db, &ent);
333                 krb5_free_principal( context, ent.principal );
334
335                 keys = ch_malloc( (ent.keys.len + 1) * sizeof(struct berval));
336
337                 for (i = 0; i < ent.keys.len; i++) {
338                         unsigned char *buf;
339                         size_t len;
340
341                         ASN1_MALLOC_ENCODE(Key, buf, len, &ent.keys.val[i], &len, ret);
342                         if (ret != 0)
343                                 break;
344                         
345                         keys[i].bv_val = buf;
346                         keys[i].bv_len = len;
347                 }
348                 keys[i].bv_val = NULL;
349                 keys[i].bv_len = 0;
350
351                 _kadm5_free_keys(kadm_context, ent.keys.len, ent.keys.val);
352
353                 if ( i != ent.keys.len ) {
354                         ber_bvarray_free( keys );
355                         break;
356                 }
357
358                 ml = ch_malloc(sizeof(Modifications));
359                 if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
360                 ml->sml_next = qpw->rs_mods;
361                 qpw->rs_mods = ml;
362
363                 ml->sml_desc = ad_krb5Key;
364                 ml->sml_op = LDAP_MOD_REPLACE;
365                 ml->sml_values = keys;
366                 ml->sml_nvalues = NULL;
367                 
368                 ml = ch_malloc(sizeof(Modifications));
369                 ml->sml_next = qpw->rs_mods;
370                 qpw->rs_mods = ml;
371                 
372                 ml->sml_desc = ad_krb5KeyVersionNumber;
373                 ml->sml_op = LDAP_MOD_REPLACE;
374                 ml->sml_values = ch_malloc( 2 * sizeof(struct berval));
375                 ml->sml_values[0].bv_val = ch_malloc( 64 );
376                 ml->sml_values[0].bv_len = sprintf(ml->sml_values[0].bv_val,
377                         "%d", kvno+1 );
378                 ml->sml_values[1].bv_val = NULL;
379                 ml->sml_values[1].bv_len = 0;
380                 ml->sml_nvalues = NULL;
381         } while(0);
382 #endif /* DO_KRB5 */
383
384 #ifdef DO_SAMBA
385         /* Samba stuff */
386         if ( is_entry_objectclass(e, oc_sambaSamAccount, 0 ) ) {
387                 struct berval *keys;
388                 ber_len_t j,l;
389                 wchar_t *wcs, wc;
390                 char *c, *d;
391                 struct berval pwd;
392                 
393                 /* Expand incoming UTF8 string to UCS4 */
394                 l = ldap_utf8_chars(qpw->rs_new.bv_val);
395                 wcs = ch_malloc((l+1) * sizeof(wchar_t));
396
397                 ldap_x_utf8s_to_wcs( wcs, qpw->rs_new.bv_val, l );
398                 
399                 /* Truncate UCS4 to UCS2 */
400                 c = (char *)wcs;
401                 for (j=0; j<l; j++) {
402                         wc = wcs[j];
403                         *c++ = wc & 0xff;
404                         *c++ = (wc >> 8) & 0xff;
405                 }
406                 *c++ = 0;
407                 pwd.bv_val = (char *)wcs;
408                 pwd.bv_len = l * 2;
409
410                 ml = ch_malloc(sizeof(Modifications));
411                 if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
412                 ml->sml_next = qpw->rs_mods;
413                 qpw->rs_mods = ml;
414
415                 keys = ch_malloc( 2 * sizeof(struct berval) );
416                 keys[1].bv_val = NULL;
417                 keys[1].bv_len = 0;
418                 nthash( &pwd, keys );
419                 
420                 ml->sml_desc = ad_sambaNTPassword;
421                 ml->sml_op = LDAP_MOD_REPLACE;
422                 ml->sml_values = keys;
423                 ml->sml_nvalues = NULL;
424
425                 /* Truncate UCS2 to 8-bit ASCII */
426                 c = pwd.bv_val+1;
427                 d = pwd.bv_val+2;
428                 for (j=1; j<l; j++) {
429                         *c++ = *d++;
430                         d++;
431                 }
432
433                 ml = ch_malloc(sizeof(Modifications));
434                 ml->sml_next = qpw->rs_mods;
435                 qpw->rs_mods = ml;
436
437                 keys = ch_malloc( 2 * sizeof(struct berval) );
438                 keys[1].bv_val = NULL;
439                 keys[1].bv_len = 0;
440                 lmhash( &pwd, keys );
441                 
442                 ml->sml_desc = ad_sambaLMPassword;
443                 ml->sml_op = LDAP_MOD_REPLACE;
444                 ml->sml_values = keys;
445                 ml->sml_nvalues = NULL;
446
447                 ch_free(wcs);
448
449                 ml = ch_malloc(sizeof(Modifications));
450                 ml->sml_next = qpw->rs_mods;
451                 qpw->rs_mods = ml;
452
453                 keys = ch_malloc( 2 * sizeof(struct berval) );
454                 keys[1].bv_val = NULL;
455                 keys[1].bv_len = 0;
456                 keys[0].bv_val = ch_malloc(16);
457                 keys[0].bv_len = sprintf(keys[0].bv_val, "%d",
458                         slap_get_time());
459                 
460                 ml->sml_desc = ad_sambaPwdLastSet;
461                 ml->sml_op = LDAP_MOD_REPLACE;
462                 ml->sml_values = keys;
463                 ml->sml_nvalues = NULL;
464         }
465 #endif /* DO_SAMBA */
466         be_entry_release_r( op, e );
467
468         return SLAP_CB_CONTINUE;
469 }
470
471 static slap_overinst smbk5pwd;
472
473 int smbk5pwd_init() {
474         int rc;
475         const char *text;
476
477 #ifdef DO_KRB5
478         krb5_error_code ret;
479         extern HDB * _kadm5_s_get_db(void *);
480
481         /* Make sure all of our necessary schema items are loaded */
482         oc_krb5KDCEntry = oc_find("krb5KDCEntry");
483         if ( !oc_krb5KDCEntry ) return -1;
484
485         rc = slap_str2ad( "krb5Key", &ad_krb5Key, &text );
486         if ( rc ) return rc;
487         rc = slap_str2ad( "krb5KeyVersionNumber", &ad_krb5KeyVersionNumber, &text );
488         if ( rc ) return rc;
489         rc = slap_str2ad( "krb5PrincipalName", &ad_krb5PrincipalName, &text );
490         if ( rc ) return rc;
491
492         /* Initialize Kerberos context */
493         ret = krb5_init_context(&context);
494         if (ret) {
495                 return -1;
496         }
497
498         ret = kadm5_s_init_with_password_ctx( context,
499                 KADM5_ADMIN_SERVICE,
500                 NULL,
501                 KADM5_ADMIN_SERVICE,
502                 &conf, 0, 0, &kadm_context );
503         
504         db = _kadm5_s_get_db(kadm_context);
505 #endif /* DO_KRB5 */
506
507 #ifdef DO_SAMBA
508         oc_sambaSamAccount = oc_find("sambaSamAccount");
509         if ( !oc_sambaSamAccount ) return -1;
510
511         rc = slap_str2ad( "sambaLMPassword", &ad_sambaLMPassword, &text );
512         if ( rc ) return rc;
513         rc = slap_str2ad( "sambaNTPassword", &ad_sambaNTPassword, &text );
514         if ( rc ) return rc;
515         rc = slap_str2ad( "sambaPwdLastSet", &ad_sambaPwdLastSet, &text );
516         if ( rc ) return rc;
517 #endif /* DO_SAMBA */
518
519         smbk5pwd.on_bi.bi_type = "smbk5pwd";
520         smbk5pwd.on_bi.bi_extended = smbk5pwd_exop_passwd;
521
522 #ifdef DO_KRB5
523         smbk5pwd.on_bi.bi_op_bind = smbk5pwd_op_bind;
524
525         lutil_passwd_add( (struct berval *)&scheme, chk_k5key, NULL );
526 #endif
527
528         return overlay_register( &smbk5pwd );
529 }
530
531 #if SLAPD_OVER_SMBK5PWD == SLAPD_MOD_DYNAMIC
532 int init_module(int argc, char *argv[]) {
533         return smbk5pwd_init();
534 }
535 #endif
536
537 #endif /* defined(SLAPD_OVER_SMBK5PWD) */