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