]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/hdb/hdb-ldap.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / lib / hdb / hdb-ldap.c
1 /*
2  * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd.
3  * Copyright (c) 2004, Andrew Bartlett.
4  * Copyright (c) 2003 - 2007, Kungliga Tekniska Högskolan.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * 3. Neither the name of PADL Software  nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include "hdb_locl.h"
36
37 RCSID("$Id: hdb-ldap.c 22071 2007-11-14 20:04:50Z lha $");
38
39 #ifdef OPENLDAP
40
41 #include <lber.h>
42 #include <ldap.h>
43 #include <sys/un.h>
44 #include <hex.h>
45
46 static krb5_error_code LDAP__connect(krb5_context context, HDB *);
47 static krb5_error_code LDAP_close(krb5_context context, HDB *);
48
49 static krb5_error_code
50 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
51                    hdb_entry_ex * ent);
52
53 static const char *default_structural_object = "account";
54 static char *structural_object;
55 static krb5_boolean samba_forwardable;
56
57 struct hdbldapdb {
58     LDAP *h_lp;
59     int   h_msgid;
60     char *h_base;
61     char *h_url;
62     char *h_createbase;
63 };
64
65 #define HDB2LDAP(db) (((struct hdbldapdb *)(db)->hdb_db)->h_lp)
66 #define HDB2MSGID(db) (((struct hdbldapdb *)(db)->hdb_db)->h_msgid)
67 #define HDBSETMSGID(db,msgid) \
68         do { ((struct hdbldapdb *)(db)->hdb_db)->h_msgid = msgid; } while(0)
69 #define HDB2BASE(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_base)
70 #define HDB2URL(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_url)
71 #define HDB2CREATE(db) (((struct hdbldapdb *)(db)->hdb_db)->h_createbase)
72
73 /*
74  *
75  */
76
77 static char * krb5kdcentry_attrs[] = { 
78     "cn",
79     "createTimestamp",
80     "creatorsName",
81     "krb5EncryptionType",
82     "krb5KDCFlags",
83     "krb5Key",
84     "krb5KeyVersionNumber",
85     "krb5MaxLife",
86     "krb5MaxRenew",
87     "krb5PasswordEnd",
88     "krb5PrincipalName",
89     "krb5PrincipalRealm",
90     "krb5ValidEnd",
91     "krb5ValidStart",
92     "modifiersName",
93     "modifyTimestamp",
94     "objectClass",
95     "sambaAcctFlags",
96     "sambaKickoffTime",
97     "sambaNTPassword",
98     "sambaPwdLastSet",
99     "sambaPwdMustChange",
100     "uid",
101     NULL
102 };
103
104 static char *krb5principal_attrs[] = {
105     "cn",
106     "createTimestamp",
107     "creatorsName",
108     "krb5PrincipalName",
109     "krb5PrincipalRealm",
110     "modifiersName",
111     "modifyTimestamp",
112     "objectClass",
113     "uid",
114     NULL
115 };
116
117 static int
118 LDAP_no_size_limit(krb5_context context, LDAP *lp)
119 {
120     int ret, limit = LDAP_NO_LIMIT;
121
122     ret = ldap_set_option(lp, LDAP_OPT_SIZELIMIT, (const void *)&limit);
123     if (ret != LDAP_SUCCESS) {
124         krb5_set_error_string(context, "ldap_set_option: %s",
125                               ldap_err2string(ret));
126         return HDB_ERR_BADVERSION;
127     }
128     return 0;
129 }
130
131 static int
132 check_ldap(krb5_context context, HDB *db, int ret)
133 {
134     switch (ret) {
135     case LDAP_SUCCESS:
136         return 0;
137     case LDAP_SERVER_DOWN:
138         LDAP_close(context, db);
139         return 1;
140     default:
141         return 1;
142     }
143 }
144
145 static krb5_error_code
146 LDAP__setmod(LDAPMod *** modlist, int modop, const char *attribute,
147              int *pIndex)
148 {
149     int cMods;
150
151     if (*modlist == NULL) {
152         *modlist = (LDAPMod **)ber_memcalloc(1, sizeof(LDAPMod *));
153         if (*modlist == NULL)
154             return ENOMEM;
155     }
156
157     for (cMods = 0; (*modlist)[cMods] != NULL; cMods++) {
158         if ((*modlist)[cMods]->mod_op == modop &&
159             strcasecmp((*modlist)[cMods]->mod_type, attribute) == 0) {
160             break;
161         }
162     }
163
164     *pIndex = cMods;
165
166     if ((*modlist)[cMods] == NULL) {
167         LDAPMod *mod;
168
169         *modlist = (LDAPMod **)ber_memrealloc(*modlist,
170                                               (cMods + 2) * sizeof(LDAPMod *));
171         if (*modlist == NULL)
172             return ENOMEM;
173
174         (*modlist)[cMods] = (LDAPMod *)ber_memalloc(sizeof(LDAPMod));
175         if ((*modlist)[cMods] == NULL)
176             return ENOMEM;
177
178         mod = (*modlist)[cMods];
179         mod->mod_op = modop;
180         mod->mod_type = ber_strdup(attribute);
181         if (mod->mod_type == NULL) {
182             ber_memfree(mod);
183             (*modlist)[cMods] = NULL;
184             return ENOMEM;
185         }
186
187         if (modop & LDAP_MOD_BVALUES) {
188             mod->mod_bvalues = NULL;
189         } else {
190             mod->mod_values = NULL;
191         }
192
193         (*modlist)[cMods + 1] = NULL;
194     }
195
196     return 0;
197 }
198
199 static krb5_error_code
200 LDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute,
201                 unsigned char *value, size_t len)
202 {
203     krb5_error_code ret;
204     int cMods, i = 0;
205
206     ret = LDAP__setmod(modlist, modop | LDAP_MOD_BVALUES, attribute, &cMods);
207     if (ret)
208         return ret;
209
210     if (value != NULL) {
211         struct berval **bv;
212
213         bv = (*modlist)[cMods]->mod_bvalues;
214         if (bv != NULL) {
215             for (i = 0; bv[i] != NULL; i++)
216                 ;
217             bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
218         } else
219             bv = ber_memalloc(2 * sizeof(*bv));
220         if (bv == NULL)
221             return ENOMEM;
222
223         (*modlist)[cMods]->mod_bvalues = bv;
224
225         bv[i] = ber_memalloc(sizeof(*bv));;
226         if (bv[i] == NULL)
227             return ENOMEM;
228
229         bv[i]->bv_val = (void *)value;
230         bv[i]->bv_len = len;
231
232         bv[i + 1] = NULL;
233     }
234
235     return 0;
236 }
237
238 static krb5_error_code
239 LDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute,
240             const char *value)
241 {
242     int cMods, i = 0;
243     krb5_error_code ret;
244
245     ret = LDAP__setmod(modlist, modop, attribute, &cMods);
246     if (ret)
247         return ret;
248
249     if (value != NULL) {
250         char **bv;
251
252         bv = (*modlist)[cMods]->mod_values;
253         if (bv != NULL) {
254             for (i = 0; bv[i] != NULL; i++)
255                 ;
256             bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));
257         } else
258             bv = ber_memalloc(2 * sizeof(*bv));
259         if (bv == NULL)
260             return ENOMEM;
261
262         (*modlist)[cMods]->mod_values = bv;
263
264         bv[i] = ber_strdup(value);
265         if (bv[i] == NULL)
266             return ENOMEM;
267
268         bv[i + 1] = NULL;
269     }
270
271     return 0;
272 }
273
274 static krb5_error_code
275 LDAP_addmod_generalized_time(LDAPMod *** mods, int modop,
276                              const char *attribute, KerberosTime * time)
277 {
278     char buf[22];
279     struct tm *tm;
280
281     /* XXX not threadsafe */
282     tm = gmtime(time);
283     strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm);
284
285     return LDAP_addmod(mods, modop, attribute, buf);
286 }
287
288 static krb5_error_code
289 LDAP_addmod_integer(krb5_context context,
290                     LDAPMod *** mods, int modop,
291                     const char *attribute, unsigned long l)
292 {
293     krb5_error_code ret;
294     char *buf;
295
296     ret = asprintf(&buf, "%ld", l);
297     if (ret < 0) {
298         krb5_set_error_string(context, "asprintf: out of memory:");
299         return ret;
300     }
301     ret = LDAP_addmod(mods, modop, attribute, buf);
302     free (buf);
303     return ret;
304 }
305
306 static krb5_error_code
307 LDAP_get_string_value(HDB * db, LDAPMessage * entry,
308                       const char *attribute, char **ptr)
309 {
310     char **vals;
311     int ret;
312
313     vals = ldap_get_values(HDB2LDAP(db), entry, (char *) attribute);
314     if (vals == NULL) {
315         *ptr = NULL;
316         return HDB_ERR_NOENTRY;
317     }
318
319     *ptr = strdup(vals[0]);
320     if (*ptr == NULL)
321         ret = ENOMEM;
322     else
323         ret = 0;
324
325     ldap_value_free(vals);
326
327     return ret;
328 }
329
330 static krb5_error_code
331 LDAP_get_integer_value(HDB * db, LDAPMessage * entry,
332                        const char *attribute, int *ptr)
333 {
334     char **vals;
335
336     vals = ldap_get_values(HDB2LDAP(db), entry, (char *) attribute);
337     if (vals == NULL)
338         return HDB_ERR_NOENTRY;
339
340     *ptr = atoi(vals[0]);
341     ldap_value_free(vals);
342     return 0;
343 }
344
345 static krb5_error_code
346 LDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry,
347                                 const char *attribute, KerberosTime * kt)
348 {
349     char *tmp, *gentime;
350     struct tm tm;
351     int ret;
352
353     *kt = 0;
354
355     ret = LDAP_get_string_value(db, entry, attribute, &gentime);
356     if (ret)
357         return ret;
358
359     tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
360     if (tmp == NULL) {
361         free(gentime);
362         return HDB_ERR_NOENTRY;
363     }
364
365     free(gentime);
366
367     *kt = timegm(&tm);
368
369     return 0;
370 }
371
372 static krb5_error_code
373 LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent,
374                 LDAPMessage * msg, LDAPMod *** pmods)
375 {
376     krb5_error_code ret;
377     krb5_boolean is_new_entry;
378     char *tmp = NULL;
379     LDAPMod **mods = NULL;
380     hdb_entry_ex orig;
381     unsigned long oflags, nflags;
382     int i;
383
384     krb5_boolean is_samba_account = FALSE;
385     krb5_boolean is_account = FALSE;
386     krb5_boolean is_heimdal_entry = FALSE;
387     krb5_boolean is_heimdal_principal = FALSE;
388
389     char **values;
390
391     *pmods = NULL;
392
393     if (msg != NULL) {
394
395         ret = LDAP_message2entry(context, db, msg, &orig);
396         if (ret)
397             goto out;
398
399         is_new_entry = FALSE;
400             
401         values = ldap_get_values(HDB2LDAP(db), msg, "objectClass");
402         if (values) {
403             int num_objectclasses = ldap_count_values(values);
404             for (i=0; i < num_objectclasses; i++) {
405                 if (strcasecmp(values[i], "sambaSamAccount") == 0) {
406                     is_samba_account = TRUE;
407                 } else if (strcasecmp(values[i], structural_object) == 0) {
408                     is_account = TRUE;
409                 } else if (strcasecmp(values[i], "krb5Principal") == 0) {
410                     is_heimdal_principal = TRUE;
411                 } else if (strcasecmp(values[i], "krb5KDCEntry") == 0) {
412                     is_heimdal_entry = TRUE;
413                 }
414             }
415             ldap_value_free(values);
416         }
417
418         /*
419          * If this is just a "account" entry and no other objectclass
420          * is hanging on this entry, it's really a new entry.
421          */
422         if (is_samba_account == FALSE && is_heimdal_principal == FALSE && 
423             is_heimdal_entry == FALSE) {
424             if (is_account == TRUE) {
425                 is_new_entry = TRUE;
426             } else {
427                 ret = HDB_ERR_NOENTRY;
428                 goto out;
429             }
430         }
431     } else
432         is_new_entry = TRUE;
433
434     if (is_new_entry) {
435
436         /* to make it perfectly obvious we're depending on
437          * orig being intiialized to zero */
438         memset(&orig, 0, sizeof(orig));
439
440         ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top");
441         if (ret)
442             goto out;
443         
444         /* account is the structural object class */
445         if (is_account == FALSE) {
446             ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", 
447                               structural_object);
448             is_account = TRUE;
449             if (ret)
450                 goto out;
451         }
452
453         ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5Principal");
454         is_heimdal_principal = TRUE;
455         if (ret)
456             goto out;
457
458         ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5KDCEntry");
459         is_heimdal_entry = TRUE;
460         if (ret)
461             goto out;
462     }
463
464     if (is_new_entry || 
465         krb5_principal_compare(context, ent->entry.principal, orig.entry.principal)
466         == FALSE)
467     {
468         if (is_heimdal_principal || is_heimdal_entry) {
469
470             ret = krb5_unparse_name(context, ent->entry.principal, &tmp);
471             if (ret)
472                 goto out;
473
474             ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE,
475                               "krb5PrincipalName", tmp);
476             if (ret) {
477                 free(tmp);
478                 goto out;
479             }
480             free(tmp);
481         }
482
483         if (is_account || is_samba_account) {
484             ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp);
485             if (ret)
486                 goto out;
487             ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp);
488             if (ret) {
489                 free(tmp);
490                 goto out;
491             }
492             free(tmp);
493         }
494     }
495
496     if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) {
497         ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
498                             "krb5KeyVersionNumber", 
499                             ent->entry.kvno);
500         if (ret)
501             goto out;
502     }
503
504     if (is_heimdal_entry && ent->entry.valid_start) {
505         if (orig.entry.valid_end == NULL
506             || (*(ent->entry.valid_start) != *(orig.entry.valid_start))) {
507             ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
508                                                "krb5ValidStart",
509                                                ent->entry.valid_start);
510             if (ret)
511                 goto out;
512         }
513     }
514
515     if (ent->entry.valid_end) {
516         if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) {
517             if (is_heimdal_entry) { 
518                 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
519                                                    "krb5ValidEnd",
520                                                    ent->entry.valid_end);
521                 if (ret)
522                     goto out;
523             }
524             if (is_samba_account) {
525                 ret = LDAP_addmod_integer(context, &mods,  LDAP_MOD_REPLACE,
526                                           "sambaKickoffTime", 
527                                           *(ent->entry.valid_end));
528                 if (ret)
529                     goto out;
530             }
531         }
532     }
533
534     if (ent->entry.pw_end) {
535         if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) {
536             if (is_heimdal_entry) {
537                 ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,
538                                                    "krb5PasswordEnd",
539                                                    ent->entry.pw_end);
540                 if (ret)
541                     goto out;
542             }
543
544             if (is_samba_account) {
545                 ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
546                                           "sambaPwdMustChange", 
547                                           *(ent->entry.pw_end));
548                 if (ret)
549                     goto out;
550             }
551         }
552     }
553
554
555 #if 0 /* we we have last_pw_change */
556     if (is_samba_account && ent->entry.last_pw_change) {
557         if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) {
558             ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
559                                       "sambaPwdLastSet", 
560                                       *(ent->entry.last_pw_change));
561             if (ret)
562                 goto out;
563         }
564     }
565 #endif
566
567     if (is_heimdal_entry && ent->entry.max_life) {
568         if (orig.entry.max_life == NULL
569             || (*(ent->entry.max_life) != *(orig.entry.max_life))) {
570
571             ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
572                                       "krb5MaxLife", 
573                                       *(ent->entry.max_life));
574             if (ret)
575                 goto out;
576         }
577     }
578
579     if (is_heimdal_entry && ent->entry.max_renew) {
580         if (orig.entry.max_renew == NULL
581             || (*(ent->entry.max_renew) != *(orig.entry.max_renew))) {
582
583             ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
584                                       "krb5MaxRenew",
585                                       *(ent->entry.max_renew));
586             if (ret)
587                 goto out;
588         }
589     }
590
591     oflags = HDBFlags2int(orig.entry.flags);
592     nflags = HDBFlags2int(ent->entry.flags);
593
594     if (is_heimdal_entry && oflags != nflags) {
595
596         ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,
597                                   "krb5KDCFlags",
598                                   nflags);
599         if (ret)
600             goto out;
601     }
602
603     /* Remove keys if they exists, and then replace keys. */
604     if (!is_new_entry && orig.entry.keys.len > 0) {
605         values = ldap_get_values(HDB2LDAP(db), msg, "krb5Key");
606         if (values) {
607             ldap_value_free(values);
608
609             ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL);
610             if (ret)
611                 goto out;
612         }
613     }
614
615     for (i = 0; i < ent->entry.keys.len; i++) {
616
617         if (is_samba_account
618             && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
619             char *ntHexPassword;
620             char *nt;
621                     
622             /* the key might have been 'sealed', but samba passwords
623                are clear in the directory */
624             ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]);
625             if (ret)
626                 goto out;
627                     
628             nt = ent->entry.keys.val[i].key.keyvalue.data;
629             /* store in ntPassword, not krb5key */
630             ret = hex_encode(nt, 16, &ntHexPassword);
631             if (ret < 0) {
632                 krb5_set_error_string(context, "hdb-ldap: failed to "
633                                       "hex encode key");
634                 ret = ENOMEM;
635                 goto out;
636             }
637             ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "sambaNTPassword", 
638                               ntHexPassword);
639             free(ntHexPassword);
640             if (ret)
641                 goto out;
642                     
643             /* have to kill the LM passwod if it exists */
644             values = ldap_get_values(HDB2LDAP(db), msg, "sambaLMPassword");
645             if (values) {
646                 ldap_value_free(values);
647                 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE,
648                                   "sambaLMPassword", NULL);
649                 if (ret)
650                     goto out;
651             }
652                     
653         } else if (is_heimdal_entry) {
654             unsigned char *buf;
655             size_t len, buf_size;
656
657             ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret);
658             if (ret)
659                 goto out;
660             if(buf_size != len)
661                 krb5_abortx(context, "internal error in ASN.1 encoder");
662
663             /* addmod_len _owns_ the key, doesn't need to copy it */
664             ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len);
665             if (ret)
666                 goto out;
667         }
668     }
669
670     if (ent->entry.etypes) {
671         int add_krb5EncryptionType = 0;
672
673         /* 
674          * Only add/modify krb5EncryptionType if it's a new heimdal
675          * entry or krb5EncryptionType already exists on the entry.
676          */
677
678         if (!is_new_entry) {
679             values = ldap_get_values(HDB2LDAP(db), msg, "krb5EncryptionType");
680             if (values) {
681                 ldap_value_free(values);
682                 ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType",
683                                   NULL);
684                 if (ret)
685                     goto out;
686                 add_krb5EncryptionType = 1;
687             }
688         } else if (is_heimdal_entry)
689             add_krb5EncryptionType = 1;
690
691         if (add_krb5EncryptionType) {
692             for (i = 0; i < ent->entry.etypes->len; i++) {
693                 if (is_samba_account && 
694                     ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5)
695                 {
696                     ;
697                 } else if (is_heimdal_entry) {
698                     ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD,
699                                               "krb5EncryptionType",
700                                               ent->entry.etypes->val[i]);
701                     if (ret)
702                         goto out;
703                 }
704             }
705         }
706     }
707
708     /* for clarity */
709     ret = 0;
710
711  out:
712
713     if (ret == 0)
714         *pmods = mods;
715     else if (mods != NULL) {
716         ldap_mods_free(mods, 1);
717         *pmods = NULL;
718     }
719
720     if (msg)
721         hdb_free_entry(context, &orig);
722
723     return ret;
724 }
725
726 static krb5_error_code
727 LDAP_dn2principal(krb5_context context, HDB * db, const char *dn,
728                   krb5_principal * principal)
729 {
730     krb5_error_code ret;
731     int rc;
732     const char *filter = "(objectClass=krb5Principal)";
733     char **values;
734     LDAPMessage *res = NULL, *e;
735
736     ret = LDAP_no_size_limit(context, HDB2LDAP(db));
737     if (ret)
738         goto out;
739
740     rc = ldap_search_s(HDB2LDAP(db), dn, LDAP_SCOPE_SUBTREE,
741                        filter, krb5principal_attrs,
742                        0, &res);
743     if (check_ldap(context, db, rc)) {
744         krb5_set_error_string(context, "ldap_search_s: filter: %s error: %s",
745                               filter, ldap_err2string(rc));
746         ret = HDB_ERR_NOENTRY;
747         goto out;
748     }
749
750     e = ldap_first_entry(HDB2LDAP(db), res);
751     if (e == NULL) {
752         ret = HDB_ERR_NOENTRY;
753         goto out;
754     }
755
756     values = ldap_get_values(HDB2LDAP(db), e, "krb5PrincipalName");
757     if (values == NULL) {
758         ret = HDB_ERR_NOENTRY;
759         goto out;
760     }
761
762     ret = krb5_parse_name(context, values[0], principal);
763     ldap_value_free(values);
764
765   out:
766     if (res)
767         ldap_msgfree(res);
768
769     return ret;
770 }
771
772 static krb5_error_code
773 LDAP__lookup_princ(krb5_context context,
774                    HDB *db,
775                    const char *princname,
776                    const char *userid,
777                    LDAPMessage **msg)
778 {
779     krb5_error_code ret;
780     int rc;
781     char *filter = NULL;
782
783     ret = LDAP__connect(context, db);
784     if (ret)
785         return ret;
786
787     rc = asprintf(&filter,
788                   "(&(objectClass=krb5Principal)(krb5PrincipalName=%s))",
789                   princname);
790     if (rc < 0) {
791         krb5_set_error_string(context, "asprintf: out of memory");
792         ret = ENOMEM;
793         goto out;
794     }
795
796     ret = LDAP_no_size_limit(context, HDB2LDAP(db));
797     if (ret)
798         goto out;
799
800     rc = ldap_search_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE, filter, 
801                        krb5kdcentry_attrs, 0, msg);
802     if (check_ldap(context, db, rc)) {
803         krb5_set_error_string(context, "ldap_search_s: filter: %s - error: %s",
804                               filter, ldap_err2string(rc));
805         ret = HDB_ERR_NOENTRY;
806         goto out;
807     }
808
809     if (userid && ldap_count_entries(HDB2LDAP(db), *msg) == 0) {
810         free(filter);
811         filter = NULL;
812         ldap_msgfree(*msg);
813         *msg = NULL;
814         
815         rc = asprintf(&filter,
816             "(&(|(objectClass=sambaSamAccount)(objectClass=%s))(uid=%s))",
817                       structural_object, userid);
818         if (rc < 0) {
819             krb5_set_error_string(context, "asprintf: out of memory");
820             ret = ENOMEM;
821             goto out;
822         }
823             
824         ret = LDAP_no_size_limit(context, HDB2LDAP(db));
825         if (ret)
826             goto out;
827
828         rc = ldap_search_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE, 
829                            filter, krb5kdcentry_attrs, 0, msg);
830         if (check_ldap(context, db, rc)) {
831             krb5_set_error_string(context, 
832                                   "ldap_search_s: filter: %s error: %s",
833                                   filter, ldap_err2string(rc));
834             ret = HDB_ERR_NOENTRY;
835             goto out;
836         }
837     }
838
839     ret = 0;
840
841   out:
842     if (filter)
843         free(filter);
844
845     return ret;
846 }
847
848 static krb5_error_code
849 LDAP_principal2message(krb5_context context, HDB * db,
850                        krb5_const_principal princ, LDAPMessage ** msg)
851 {
852     char *name, *name_short = NULL;
853     krb5_error_code ret;
854     krb5_realm *r, *r0;
855
856     *msg = NULL;
857
858     ret = krb5_unparse_name(context, princ, &name);
859     if (ret)
860         return ret;
861
862     ret = krb5_get_default_realms(context, &r0);
863     if(ret) {
864         free(name);
865         return ret;
866     }
867     for (r = r0; *r != NULL; r++) {
868         if(strcmp(krb5_principal_get_realm(context, princ), *r) == 0) {
869             ret = krb5_unparse_name_short(context, princ, &name_short);
870             if (ret) {
871                 krb5_free_host_realm(context, r0);
872                 free(name);
873                 return ret;
874             }
875             break;
876         }
877     }
878     krb5_free_host_realm(context, r0);
879
880     ret = LDAP__lookup_princ(context, db, name, name_short, msg);
881     free(name);
882     free(name_short);
883
884     return ret;
885 }
886
887 /*
888  * Construct an hdb_entry from a directory entry.
889  */
890 static krb5_error_code
891 LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
892                    hdb_entry_ex * ent)
893 {
894     char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL;
895     char *samba_acct_flags = NULL;
896     unsigned long tmp;
897     struct berval **keys;
898     char **values;
899     int tmp_time, i, ret, have_arcfour = 0;
900
901     memset(ent, 0, sizeof(*ent));
902     ent->entry.flags = int2HDBFlags(0);
903
904     ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name);
905     if (ret == 0) {
906         ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
907         if (ret)
908             goto out;
909     } else {
910         ret = LDAP_get_string_value(db, msg, "uid",
911                                     &unparsed_name);
912         if (ret == 0) {
913             ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);
914             if (ret)
915                 goto out;
916         } else {
917             krb5_set_error_string(context, "hdb-ldap: ldap entry missing"
918                                   "principal name");
919             return HDB_ERR_NOENTRY;
920         }
921     }
922
923     {
924         int integer;
925         ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber",
926                                      &integer);
927         if (ret)
928             ent->entry.kvno = 0;
929         else
930             ent->entry.kvno = integer;
931     }
932
933     keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");
934     if (keys != NULL) {
935         int i;
936         size_t l;
937
938         ent->entry.keys.len = ldap_count_values_len(keys);
939         ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key));
940         if (ent->entry.keys.val == NULL) {
941             krb5_set_error_string(context, "calloc: out of memory");
942             ret = ENOMEM;
943             goto out;
944         }
945         for (i = 0; i < ent->entry.keys.len; i++) {
946             decode_Key((unsigned char *) keys[i]->bv_val,
947                        (size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l);
948         }
949         ber_bvecfree(keys);
950     } else {
951 #if 1
952         /*
953          * This violates the ASN1 but it allows a principal to
954          * be related to a general directory entry without creating
955          * the keys. Hopefully it's OK.
956          */
957         ent->entry.keys.len = 0;
958         ent->entry.keys.val = NULL;
959 #else
960         ret = HDB_ERR_NOENTRY;
961         goto out;
962 #endif
963     }
964
965     values = ldap_get_values(HDB2LDAP(db), msg, "krb5EncryptionType");
966     if (values != NULL) {
967         int i;
968
969         ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
970         if (ent->entry.etypes == NULL) {
971             krb5_set_error_string(context, "malloc: out of memory");
972             ret = ENOMEM;
973             goto out;
974         }
975         ent->entry.etypes->len = ldap_count_values(values);
976         ent->entry.etypes->val = calloc(ent->entry.etypes->len, sizeof(int));
977         if (ent->entry.etypes->val == NULL) {
978             krb5_set_error_string(context, "malloc: out of memory");
979             ret = ENOMEM;
980             goto out;
981         }
982         for (i = 0; i < ent->entry.etypes->len; i++) {
983             ent->entry.etypes->val[i] = atoi(values[i]);
984         }
985         ldap_value_free(values);
986     }
987
988     for (i = 0; i < ent->entry.keys.len; i++) {
989         if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {
990             have_arcfour = 1;
991             break;
992         }
993     }
994
995     /* manually construct the NT (type 23) key */
996     ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN);
997     if (ret == 0 && have_arcfour == 0) {
998         unsigned *etypes;
999         Key *keys;
1000         int i;
1001
1002         keys = realloc(ent->entry.keys.val,
1003                        (ent->entry.keys.len + 1) * sizeof(ent->entry.keys.val[0]));
1004         if (keys == NULL) {
1005             free(ntPasswordIN);
1006             krb5_set_error_string(context, "malloc: out of memory");
1007             ret = ENOMEM;
1008             goto out;
1009         }
1010         ent->entry.keys.val = keys;
1011         memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key));
1012         ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5;
1013         ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16);
1014         if (ret) {
1015             krb5_set_error_string(context, "malloc: out of memory");
1016             free(ntPasswordIN);
1017             ret = ENOMEM;
1018             goto out;
1019         }
1020         ret = hex_decode(ntPasswordIN,
1021                          ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16);
1022         ent->entry.keys.len++;
1023
1024         if (ent->entry.etypes == NULL) {
1025             ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
1026             if (ent->entry.etypes == NULL) {
1027                 krb5_set_error_string(context, "malloc: out of memory");
1028                 ret = ENOMEM;
1029                 goto out;
1030             }
1031             ent->entry.etypes->val = NULL;
1032             ent->entry.etypes->len = 0;
1033         }
1034
1035         for (i = 0; i < ent->entry.etypes->len; i++)
1036             if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5)
1037                 break;
1038         /* If there is no ARCFOUR enctype, add one */
1039         if (i == ent->entry.etypes->len) {
1040             etypes = realloc(ent->entry.etypes->val, 
1041                              (ent->entry.etypes->len + 1) * 
1042                              sizeof(ent->entry.etypes->val[0]));
1043             if (etypes == NULL) {
1044                 krb5_set_error_string(context, "malloc: out of memory");
1045                 ret = ENOMEM;
1046                 goto out;                           
1047             }
1048             ent->entry.etypes->val = etypes;
1049             ent->entry.etypes->val[ent->entry.etypes->len] = 
1050                 ETYPE_ARCFOUR_HMAC_MD5;
1051             ent->entry.etypes->len++;
1052         }
1053     }
1054
1055     ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp",
1056                                           &ent->entry.created_by.time);
1057     if (ret)
1058         ent->entry.created_by.time = time(NULL);
1059
1060     ent->entry.created_by.principal = NULL;
1061
1062     ret = LDAP_get_string_value(db, msg, "creatorsName", &dn);
1063     if (ret == 0) {
1064         if (LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal)
1065             != 0) {
1066             ent->entry.created_by.principal = NULL;
1067         }
1068         free(dn);
1069     }
1070
1071     ent->entry.modified_by = (Event *) malloc(sizeof(Event));
1072     if (ent->entry.modified_by == NULL) {
1073         krb5_set_error_string(context, "malloc: out of memory");
1074         ret = ENOMEM;
1075         goto out;
1076     }
1077     ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp",
1078                                           &ent->entry.modified_by->time);
1079     if (ret == 0) {
1080         ret = LDAP_get_string_value(db, msg, "modifiersName", &dn);
1081         if (LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal))
1082             ent->entry.modified_by->principal = NULL;
1083         free(dn);
1084     } else {
1085         free(ent->entry.modified_by);
1086         ent->entry.modified_by = NULL;
1087     }
1088
1089     ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start));
1090     if (ent->entry.valid_start == NULL) {
1091         krb5_set_error_string(context, "malloc: out of memory");
1092         ret = ENOMEM;
1093         goto out;
1094     }
1095     ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart",
1096                                           ent->entry.valid_start);
1097     if (ret) {
1098         /* OPTIONAL */
1099         free(ent->entry.valid_start);
1100         ent->entry.valid_start = NULL;
1101     }
1102     
1103     ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1104     if (ent->entry.valid_end == NULL) {
1105         krb5_set_error_string(context, "malloc: out of memory");
1106         ret = ENOMEM;
1107         goto out;
1108     }
1109     ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd",
1110                                           ent->entry.valid_end);
1111     if (ret) {
1112         /* OPTIONAL */
1113         free(ent->entry.valid_end);
1114         ent->entry.valid_end = NULL;
1115     }
1116
1117     ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time);
1118     if (ret == 0) {
1119         if (ent->entry.valid_end == NULL) {
1120             ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));
1121             if (ent->entry.valid_end == NULL) {
1122                 krb5_set_error_string(context, "malloc: out of memory");
1123                 ret = ENOMEM;
1124                 goto out;
1125             }
1126         }
1127         *ent->entry.valid_end = tmp_time;
1128     }
1129
1130     ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1131     if (ent->entry.pw_end == NULL) {
1132         krb5_set_error_string(context, "malloc: out of memory");
1133         ret = ENOMEM;
1134         goto out;
1135     }
1136     ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd",
1137                                           ent->entry.pw_end);
1138     if (ret) {
1139         /* OPTIONAL */
1140         free(ent->entry.pw_end);
1141         ent->entry.pw_end = NULL;
1142     }
1143
1144     ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time);
1145     if (ret == 0) {
1146         if (ent->entry.pw_end == NULL) {
1147             ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
1148             if (ent->entry.pw_end == NULL) {
1149                 krb5_set_error_string(context, "malloc: out of memory");
1150                 ret = ENOMEM;
1151                 goto out;
1152             }
1153         }
1154         *ent->entry.pw_end = tmp_time;
1155     }
1156
1157     /* OPTIONAL */
1158     ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);
1159     if (ret == 0)
1160         hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time);
1161
1162     {
1163         int max_life;
1164
1165         ent->entry.max_life = malloc(sizeof(*ent->entry.max_life));
1166         if (ent->entry.max_life == NULL) {
1167             krb5_set_error_string(context, "malloc: out of memory");
1168             ret = ENOMEM;
1169             goto out;
1170         }
1171         ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life);
1172         if (ret) {
1173             free(ent->entry.max_life);
1174             ent->entry.max_life = NULL;
1175         } else
1176             *ent->entry.max_life = max_life;
1177     }
1178
1179     {
1180         int max_renew;
1181
1182         ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew));
1183         if (ent->entry.max_renew == NULL) {
1184             krb5_set_error_string(context, "malloc: out of memory");
1185             ret = ENOMEM;
1186             goto out;
1187         }
1188         ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew);
1189         if (ret) {
1190             free(ent->entry.max_renew);
1191             ent->entry.max_renew = NULL;
1192         } else
1193             *ent->entry.max_renew = max_renew;
1194     }
1195
1196     values = ldap_get_values(HDB2LDAP(db), msg, "krb5KDCFlags");
1197     if (values != NULL) {
1198         errno = 0;
1199         tmp = strtoul(values[0], (char **) NULL, 10);
1200         if (tmp == ULONG_MAX && errno == ERANGE) {
1201             krb5_set_error_string(context, "strtoul: could not convert flag");
1202             ret = ERANGE;
1203             goto out;
1204         }
1205     } else {
1206         tmp = 0;
1207     }
1208
1209     ent->entry.flags = int2HDBFlags(tmp);
1210
1211     /* Try and find Samba flags to put into the mix */
1212     ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags);
1213     if (ret == 0) {
1214         /* parse the [UXW...] string:
1215                
1216            'N'    No password    
1217            'D'    Disabled       
1218            'H'    Homedir required       
1219            'T'    Temp account.  
1220            'U'    User account (normal)          
1221            'M'    MNS logon user account - what is this ?        
1222            'W'    Workstation account    
1223            'S'    Server account         
1224            'L'    Locked account         
1225            'X'    No Xpiry on password   
1226            'I'    Interdomain trust account      
1227             
1228         */       
1229             
1230         int i;
1231         int flags_len = strlen(samba_acct_flags);
1232
1233         if (flags_len < 2)
1234             goto out2;
1235
1236         if (samba_acct_flags[0] != '[' 
1237             || samba_acct_flags[flags_len - 1] != ']') 
1238             goto out2;
1239
1240         /* Allow forwarding */
1241         if (samba_forwardable)
1242             ent->entry.flags.forwardable = TRUE;
1243
1244         for (i=0; i < flags_len; i++) {
1245             switch (samba_acct_flags[i]) {
1246             case ' ':
1247             case '[':
1248             case ']':
1249                 break;
1250             case 'N':
1251                 /* how to handle no password in kerberos? */
1252                 break;
1253             case 'D':
1254                 ent->entry.flags.invalid = TRUE;
1255                 break;
1256             case 'H':
1257                 break;
1258             case 'T':
1259                 /* temp duplicate */
1260                 ent->entry.flags.invalid = TRUE;
1261                 break;
1262             case 'U':
1263                 ent->entry.flags.client = TRUE;
1264                 break;
1265             case 'M':
1266                 break;
1267             case 'W':
1268             case 'S':
1269                 ent->entry.flags.server = TRUE;
1270                 ent->entry.flags.client = TRUE;
1271                 break;
1272             case 'L':
1273                 ent->entry.flags.invalid = TRUE;
1274                 break;
1275             case 'X':
1276                 if (ent->entry.pw_end) {
1277                     free(ent->entry.pw_end);
1278                     ent->entry.pw_end = NULL;
1279                 }
1280                 break;
1281             case 'I':
1282                 ent->entry.flags.server = TRUE;
1283                 ent->entry.flags.client = TRUE;
1284                 break;
1285             }
1286         }
1287     out2:
1288         free(samba_acct_flags);
1289     }
1290
1291     ret = 0;
1292
1293 out:
1294     if (unparsed_name)
1295         free(unparsed_name);
1296
1297     if (ret)
1298         hdb_free_entry(context, ent);
1299
1300     return ret;
1301 }
1302
1303 static krb5_error_code
1304 LDAP_close(krb5_context context, HDB * db)
1305 {
1306     if (HDB2LDAP(db)) {
1307         ldap_unbind_ext(HDB2LDAP(db), NULL, NULL);
1308         ((struct hdbldapdb *)db->hdb_db)->h_lp = NULL;
1309     }
1310     
1311     return 0;
1312 }
1313
1314 static krb5_error_code
1315 LDAP_lock(krb5_context context, HDB * db, int operation)
1316 {
1317     return 0;
1318 }
1319
1320 static krb5_error_code
1321 LDAP_unlock(krb5_context context, HDB * db)
1322 {
1323     return 0;
1324 }
1325
1326 static krb5_error_code
1327 LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry)
1328 {
1329     int msgid, rc, parserc;
1330     krb5_error_code ret;
1331     LDAPMessage *e;
1332
1333     msgid = HDB2MSGID(db);
1334     if (msgid < 0)
1335         return HDB_ERR_NOENTRY;
1336
1337     do {
1338         rc = ldap_result(HDB2LDAP(db), msgid, LDAP_MSG_ONE, NULL, &e);
1339         switch (rc) {
1340         case LDAP_RES_SEARCH_REFERENCE:
1341             ldap_msgfree(e);
1342             ret = 0;
1343             break;
1344         case LDAP_RES_SEARCH_ENTRY:
1345             /* We have an entry. Parse it. */
1346             ret = LDAP_message2entry(context, db, e, entry);
1347             ldap_msgfree(e);
1348             break;
1349         case LDAP_RES_SEARCH_RESULT:
1350             /* We're probably at the end of the results. If not, abandon. */
1351             parserc =
1352                 ldap_parse_result(HDB2LDAP(db), e, NULL, NULL, NULL,
1353                                   NULL, NULL, 1);
1354             if (parserc != LDAP_SUCCESS
1355                 && parserc != LDAP_MORE_RESULTS_TO_RETURN) {
1356                 krb5_set_error_string(context, "ldap_parse_result: %s",
1357                                       ldap_err2string(parserc));
1358                 ldap_abandon(HDB2LDAP(db), msgid);
1359             }
1360             ret = HDB_ERR_NOENTRY;
1361             HDBSETMSGID(db, -1);
1362             break;
1363         case LDAP_SERVER_DOWN:
1364             ldap_msgfree(e);
1365             LDAP_close(context, db);
1366             HDBSETMSGID(db, -1);
1367             ret = ENETDOWN;
1368             break;
1369         default:
1370             /* Some unspecified error (timeout?). Abandon. */
1371             ldap_msgfree(e);
1372             ldap_abandon(HDB2LDAP(db), msgid);
1373             ret = HDB_ERR_NOENTRY;
1374             HDBSETMSGID(db, -1);
1375             break;
1376         }
1377     } while (rc == LDAP_RES_SEARCH_REFERENCE);
1378
1379     if (ret == 0) {
1380         if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1381             ret = hdb_unseal_keys(context, db, &entry->entry);
1382             if (ret)
1383                 hdb_free_entry(context, entry);
1384         }
1385     }
1386
1387     return ret;
1388 }
1389
1390 static krb5_error_code
1391 LDAP_firstkey(krb5_context context, HDB *db, unsigned flags,
1392               hdb_entry_ex *entry)
1393 {
1394     krb5_error_code ret;
1395     int msgid;
1396
1397     ret = LDAP__connect(context, db);
1398     if (ret)
1399         return ret;
1400
1401     ret = LDAP_no_size_limit(context, HDB2LDAP(db));
1402     if (ret)
1403         return ret;
1404
1405     msgid = ldap_search(HDB2LDAP(db), HDB2BASE(db),
1406                         LDAP_SCOPE_SUBTREE,
1407                         "(|(objectClass=krb5Principal)(objectClass=sambaSamAccount))",
1408                         krb5kdcentry_attrs, 0);
1409     if (msgid < 0)
1410         return HDB_ERR_NOENTRY;
1411
1412     HDBSETMSGID(db, msgid);
1413
1414     return LDAP_seq(context, db, flags, entry);
1415 }
1416
1417 static krb5_error_code
1418 LDAP_nextkey(krb5_context context, HDB * db, unsigned flags,
1419              hdb_entry_ex * entry)
1420 {
1421     return LDAP_seq(context, db, flags, entry);
1422 }
1423
1424 static krb5_error_code
1425 LDAP__connect(krb5_context context, HDB * db)
1426 {
1427     int rc, version = LDAP_VERSION3;
1428     /*
1429      * Empty credentials to do a SASL bind with LDAP. Note that empty
1430      * different from NULL credentials. If you provide NULL
1431      * credentials instead of empty credentials you will get a SASL
1432      * bind in progress message.
1433      */
1434     struct berval bv = { 0, "" };
1435
1436     if (HDB2LDAP(db)) {
1437         /* connection has been opened. ping server. */
1438         struct sockaddr_un addr;
1439         socklen_t len = sizeof(addr);
1440         int sd;
1441
1442         if (ldap_get_option(HDB2LDAP(db), LDAP_OPT_DESC, &sd) == 0 &&
1443             getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
1444             /* the other end has died. reopen. */
1445             LDAP_close(context, db);
1446         }
1447     }
1448
1449     if (HDB2LDAP(db) != NULL) /* server is UP */
1450         return 0;
1451
1452     rc = ldap_initialize(&((struct hdbldapdb *)db->hdb_db)->h_lp, HDB2URL(db));
1453     if (rc != LDAP_SUCCESS) {
1454         krb5_set_error_string(context, "ldap_initialize: %s", 
1455                               ldap_err2string(rc));
1456         return HDB_ERR_NOENTRY;
1457     }
1458
1459     rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_PROTOCOL_VERSION,
1460                          (const void *)&version);
1461     if (rc != LDAP_SUCCESS) {
1462         krb5_set_error_string(context, "ldap_set_option: %s",
1463                               ldap_err2string(rc));
1464         LDAP_close(context, db);
1465         return HDB_ERR_BADVERSION;
1466     }
1467
1468     rc = ldap_sasl_bind_s(HDB2LDAP(db), NULL, "EXTERNAL", &bv,
1469                           NULL, NULL, NULL);
1470     if (rc != LDAP_SUCCESS) {
1471         krb5_set_error_string(context, "ldap_sasl_bind_s: %s",
1472                               ldap_err2string(rc));
1473         LDAP_close(context, db);
1474         return HDB_ERR_BADVERSION;
1475     }
1476
1477     return 0;
1478 }
1479
1480 static krb5_error_code
1481 LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode)
1482 {
1483     /* Not the right place for this. */
1484 #ifdef HAVE_SIGACTION
1485     struct sigaction sa;
1486
1487     sa.sa_flags = 0;
1488     sa.sa_handler = SIG_IGN;
1489     sigemptyset(&sa.sa_mask);
1490
1491     sigaction(SIGPIPE, &sa, NULL);
1492 #else
1493     signal(SIGPIPE, SIG_IGN);
1494 #endif /* HAVE_SIGACTION */
1495
1496     return LDAP__connect(context, db);
1497 }
1498
1499 static krb5_error_code
1500 LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal,
1501            unsigned flags, hdb_entry_ex * entry)
1502 {
1503     LDAPMessage *msg, *e;
1504     krb5_error_code ret;
1505
1506     ret = LDAP_principal2message(context, db, principal, &msg);
1507     if (ret)
1508         return ret;
1509
1510     e = ldap_first_entry(HDB2LDAP(db), msg);
1511     if (e == NULL) {
1512         ret = HDB_ERR_NOENTRY;
1513         goto out;
1514     }
1515
1516     ret = LDAP_message2entry(context, db, e, entry);
1517     if (ret == 0) {
1518         if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
1519             ret = hdb_unseal_keys(context, db, &entry->entry);
1520             if (ret)
1521                 hdb_free_entry(context, entry);
1522         }
1523     }
1524
1525   out:
1526     ldap_msgfree(msg);
1527
1528     return ret;
1529 }
1530
1531 static krb5_error_code
1532 LDAP_store(krb5_context context, HDB * db, unsigned flags,
1533            hdb_entry_ex * entry)
1534 {
1535     LDAPMod **mods = NULL;
1536     krb5_error_code ret;
1537     const char *errfn;
1538     int rc;
1539     LDAPMessage *msg = NULL, *e = NULL;
1540     char *dn = NULL, *name = NULL;
1541
1542     ret = LDAP_principal2message(context, db, entry->entry.principal, &msg);
1543     if (ret == 0)
1544         e = ldap_first_entry(HDB2LDAP(db), msg);
1545
1546     ret = krb5_unparse_name(context, entry->entry.principal, &name);
1547     if (ret) {
1548         free(name);
1549         return ret;
1550     }
1551
1552     ret = hdb_seal_keys(context, db, &entry->entry);
1553     if (ret)
1554         goto out;
1555
1556     /* turn new entry into LDAPMod array */
1557     ret = LDAP_entry2mods(context, db, entry, e, &mods);
1558     if (ret)
1559         goto out;
1560
1561     if (e == NULL) {
1562         ret = asprintf(&dn, "krb5PrincipalName=%s,%s", name, HDB2CREATE(db));
1563         if (ret < 0) {
1564             krb5_set_error_string(context, "asprintf: out of memory");
1565             ret = ENOMEM;
1566             goto out;
1567         }
1568     } else if (flags & HDB_F_REPLACE) {
1569         /* Entry exists, and we're allowed to replace it. */
1570         dn = ldap_get_dn(HDB2LDAP(db), e);
1571     } else {
1572         /* Entry exists, but we're not allowed to replace it. Bail. */
1573         ret = HDB_ERR_EXISTS;
1574         goto out;
1575     }
1576
1577     /* write entry into directory */
1578     if (e == NULL) {
1579         /* didn't exist before */
1580         rc = ldap_add_s(HDB2LDAP(db), dn, mods);
1581         errfn = "ldap_add_s";
1582     } else {
1583         /* already existed, send deltas only */
1584         rc = ldap_modify_s(HDB2LDAP(db), dn, mods);
1585         errfn = "ldap_modify_s";
1586     }
1587
1588     if (check_ldap(context, db, rc)) {
1589         char *ld_error = NULL;
1590         ldap_get_option(HDB2LDAP(db), LDAP_OPT_ERROR_STRING,
1591                         &ld_error);
1592         krb5_set_error_string(context, "%s: %s (DN=%s) %s: %s", 
1593                               errfn, name, dn, ldap_err2string(rc), ld_error);
1594         ret = HDB_ERR_CANT_LOCK_DB;
1595     } else
1596         ret = 0;
1597
1598   out:
1599     /* free stuff */
1600     if (dn)
1601         free(dn);
1602     if (msg)
1603         ldap_msgfree(msg);
1604     if (mods)
1605         ldap_mods_free(mods, 1);
1606     if (name)
1607         free(name);
1608
1609     return ret;
1610 }
1611
1612 static krb5_error_code
1613 LDAP_remove(krb5_context context, HDB *db, krb5_const_principal principal)
1614 {
1615     krb5_error_code ret;
1616     LDAPMessage *msg, *e;
1617     char *dn = NULL;
1618     int rc, limit = LDAP_NO_LIMIT;
1619
1620     ret = LDAP_principal2message(context, db, principal, &msg);
1621     if (ret)
1622         goto out;
1623
1624     e = ldap_first_entry(HDB2LDAP(db), msg);
1625     if (e == NULL) {
1626         ret = HDB_ERR_NOENTRY;
1627         goto out;
1628     }
1629
1630     dn = ldap_get_dn(HDB2LDAP(db), e);
1631     if (dn == NULL) {
1632         ret = HDB_ERR_NOENTRY;
1633         goto out;
1634     }
1635
1636     rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_SIZELIMIT, (const void *)&limit);
1637     if (rc != LDAP_SUCCESS) {
1638         krb5_set_error_string(context, "ldap_set_option: %s",
1639                               ldap_err2string(rc));
1640         ret = HDB_ERR_BADVERSION;
1641         goto out;
1642     }
1643
1644     rc = ldap_delete_s(HDB2LDAP(db), dn);
1645     if (check_ldap(context, db, rc)) {
1646         krb5_set_error_string(context, "ldap_delete_s: %s", 
1647                               ldap_err2string(rc));
1648         ret = HDB_ERR_CANT_LOCK_DB;
1649     } else
1650         ret = 0;
1651
1652   out:
1653     if (dn != NULL)
1654         free(dn);
1655     if (msg != NULL)
1656         ldap_msgfree(msg);
1657
1658     return ret;
1659 }
1660
1661 static krb5_error_code
1662 LDAP_destroy(krb5_context context, HDB * db)
1663 {
1664     krb5_error_code ret;
1665
1666     LDAP_close(context, db);
1667
1668     ret = hdb_clear_master_key(context, db);
1669     if (HDB2BASE(db))
1670         free(HDB2BASE(db));
1671     if (HDB2CREATE(db))
1672         free(HDB2CREATE(db));
1673     if (HDB2URL(db))
1674         free(HDB2URL(db));
1675     if (db->hdb_name)
1676         free(db->hdb_name);
1677     free(db->hdb_db);
1678     free(db);
1679
1680     return ret;
1681 }
1682
1683 krb5_error_code
1684 hdb_ldap_common(krb5_context context,
1685                 HDB ** db,
1686                 const char *search_base,
1687                 const char *url)
1688 {
1689     struct hdbldapdb *h;
1690     const char *create_base = NULL;
1691
1692     if (search_base == NULL && search_base[0] == '\0') {
1693         krb5_set_error_string(context, "ldap search base not configured");
1694         return ENOMEM; /* XXX */
1695     }
1696
1697     if (structural_object == NULL) {
1698         const char *p;
1699
1700         p = krb5_config_get_string(context, NULL, "kdc", 
1701                                    "hdb-ldap-structural-object", NULL);
1702         if (p == NULL)
1703             p = default_structural_object;
1704         structural_object = strdup(p);
1705         if (structural_object == NULL) {
1706             krb5_set_error_string(context, "malloc: out of memory");
1707             return ENOMEM;
1708         }
1709     }
1710
1711     samba_forwardable = 
1712         krb5_config_get_bool_default(context, NULL, TRUE,
1713                                      "kdc", "hdb-samba-forwardable", NULL);
1714
1715     *db = calloc(1, sizeof(**db));
1716     if (*db == NULL) {
1717         krb5_set_error_string(context, "malloc: out of memory");
1718         return ENOMEM;
1719     }
1720     memset(*db, 0, sizeof(**db));
1721
1722     h = calloc(1, sizeof(*h));
1723     if (h == NULL) {
1724         krb5_set_error_string(context, "malloc: out of memory");
1725         free(*db);
1726         *db = NULL;
1727         return ENOMEM;
1728     }
1729     (*db)->hdb_db = h;
1730
1731     /* XXX */
1732     if (asprintf(&(*db)->hdb_name, "ldap:%s", search_base) == -1) {
1733         LDAP_destroy(context, *db);
1734         krb5_set_error_string(context, "strdup: out of memory");
1735         *db = NULL;
1736         return ENOMEM;
1737     }
1738
1739     h->h_url = strdup(url);
1740     h->h_base = strdup(search_base);
1741     if (h->h_url == NULL || h->h_base == NULL) {
1742         LDAP_destroy(context, *db);
1743         krb5_set_error_string(context, "strdup: out of memory");
1744         *db = NULL;
1745         return ENOMEM;
1746     }
1747
1748     create_base = krb5_config_get_string(context, NULL, "kdc", 
1749                                          "hdb-ldap-create-base", NULL);
1750     if (create_base == NULL)
1751         create_base = h->h_base;
1752
1753     h->h_createbase = strdup(create_base);
1754     if (h->h_createbase == NULL) {
1755         LDAP_destroy(context, *db);
1756         krb5_set_error_string(context, "strdup: out of memory");
1757         *db = NULL;
1758         return ENOMEM;
1759     }
1760
1761     (*db)->hdb_master_key_set = 0;
1762     (*db)->hdb_openp = 0;
1763     (*db)->hdb_open = LDAP_open;
1764     (*db)->hdb_close = LDAP_close;
1765     (*db)->hdb_fetch = LDAP_fetch;
1766     (*db)->hdb_store = LDAP_store;
1767     (*db)->hdb_remove = LDAP_remove;
1768     (*db)->hdb_firstkey = LDAP_firstkey;
1769     (*db)->hdb_nextkey = LDAP_nextkey;
1770     (*db)->hdb_lock = LDAP_lock;
1771     (*db)->hdb_unlock = LDAP_unlock;
1772     (*db)->hdb_rename = NULL;
1773     (*db)->hdb__get = NULL;
1774     (*db)->hdb__put = NULL;
1775     (*db)->hdb__del = NULL;
1776     (*db)->hdb_destroy = LDAP_destroy;
1777
1778     return 0;
1779 }
1780
1781 krb5_error_code
1782 hdb_ldap_create(krb5_context context, HDB ** db, const char *arg)
1783 {
1784     return hdb_ldap_common(context, db, arg, "ldapi:///");
1785 }
1786
1787 krb5_error_code
1788 hdb_ldapi_create(krb5_context context, HDB ** db, const char *arg)
1789 {
1790     krb5_error_code ret;
1791     char *search_base, *p;
1792
1793     asprintf(&p, "ldapi:%s", arg);
1794     if (p == NULL) {
1795         krb5_set_error_string(context, "out of memory");
1796         *db = NULL;
1797         return ENOMEM;
1798     }
1799     search_base = strchr(p + strlen("ldapi://"), ':');
1800     if (search_base == NULL) {
1801         krb5_set_error_string(context, "search base missing");
1802         *db = NULL;
1803         return HDB_ERR_BADVERSION;
1804     }
1805     *search_base = '\0';
1806     search_base++;
1807
1808     ret = hdb_ldap_common(context, db, search_base, p);
1809     free(p);
1810     return ret;
1811 }
1812
1813 #ifdef OPENLDAP_MODULE
1814
1815 struct hdb_so_method hdb_ldap_interface = {
1816     HDB_INTERFACE_VERSION,
1817     "ldap",
1818     hdb_ldap_create
1819 };
1820
1821 struct hdb_so_method hdb_ldapi_interface = {
1822     HDB_INTERFACE_VERSION,
1823     "ldapi",
1824     hdb_ldapi_create
1825 };
1826
1827 #endif
1828
1829 #endif                          /* OPENLDAP */