1/*
2 * lib/kdb/kdb_ldap/ldap_misc.c
3 *
4 * Copyright (c) 2004-2005, Novell, Inc.
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 are met:
9 *
10 *   * Redistributions of source code must retain the above copyright notice,
11 *       this list of conditions and the following disclaimer.
12 *   * Redistributions in binary form must reproduce the above copyright
13 *       notice, this list of conditions and the following disclaimer in the
14 *       documentation and/or other materials provided with the distribution.
15 *   * The copyright holder's name is not used to endorse or promote products
16 *       derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30/*
31 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
32 * Use is subject to license terms.
33 */
34#include <string.h>
35#include <time.h>
36#include <k5-platform.h>
37#include "ldap_main.h"
38#include "ldap_err.h"
39#include "ldap_principal.h"
40#include "princ_xdr.h"
41#include "ldap_pwd_policy.h"
42#include <libintl.h>
43
44#ifdef NEED_STRPTIME_PROTO
45extern char *strptime (const char *, const char *, struct tm *);
46#endif
47
48static krb5_error_code
49remove_overlapping_subtrees(char **listin, char **listop, int *subtcount,
50			    int sscope);
51
52/* Linux (GNU Libc) provides a length-limited variant of strdup.
53   But all the world's not Linux.  */
54#undef strndup
55#define strndup my_strndup
56#ifdef HAVE_LDAP_STR2DN
57static char *my_strndup (const char *input, size_t limit)
58{
59    size_t len = strlen(input);
60    char *result;
61    if (len > limit) {
62	result = malloc(1 + limit);
63	if (result != NULL) {
64	    memcpy(result, input, limit);
65	    result[limit] = 0;
66	}
67	return result;
68    } else
69	return strdup(input);
70}
71#endif
72
73/* Get integer or string values from the config section, falling back
74   to the default section, then to hard-coded values.  */
75static errcode_t
76prof_get_integer_def(krb5_context ctx, const char *conf_section,
77		     const char *name, int dfl, krb5_ui_4 *out)
78{
79    errcode_t err;
80    int out_temp = 0;
81
82    err = profile_get_integer (ctx->profile,
83			       KDB_MODULE_SECTION, conf_section, name,
84			       0, &out_temp);
85    if (err) {
86	krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
87				name, error_message(err));
88	return err;
89    }
90    if (out_temp != 0) {
91	*out = out_temp;
92	return 0;
93    }
94    err = profile_get_integer (ctx->profile,
95			       KDB_MODULE_DEF_SECTION, name, 0,
96			       dfl, &out_temp);
97    if (err) {
98	krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
99				name, error_message(err));
100	return err;
101    }
102    *out = out_temp;
103    return 0;
104}
105
106/* We don't have non-null defaults in any of our calls, so don't
107   bother with the extra argument.  */
108static errcode_t
109prof_get_string_def(krb5_context ctx, const char *conf_section,
110		    const char *name, char **out)
111{
112    errcode_t err;
113
114    err = profile_get_string (ctx->profile,
115			      KDB_MODULE_SECTION, conf_section, name,
116			      0, out);
117    if (err) {
118	krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
119				name, error_message(err));
120	return err;
121    }
122    if (*out != 0)
123	return 0;
124    err = profile_get_string (ctx->profile,
125			      KDB_MODULE_DEF_SECTION, name, 0,
126			      0, out);
127    if (err) {
128	krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
129				name, error_message(err));
130	return err;
131    }
132    return 0;
133}
134
135
136
137/*
138 * This function reads the parameters from the krb5.conf file. The
139 * parameters read here are DAL-LDAP specific attributes. Some of
140 * these are ldap_server ....
141 */
142krb5_error_code
143krb5_ldap_read_server_params(context, conf_section, srv_type)
144    krb5_context               context;
145    char                       *conf_section;
146    int                        srv_type;
147{
148    char                        *tempval=NULL, *save_ptr=NULL;
149    const char                  *delims="\t\n\f\v\r ,";
150    krb5_error_code             st=0;
151    kdb5_dal_handle             *dal_handle=NULL;
152    krb5_ldap_context           *ldap_context=NULL;
153    krb5_ldap_server_info       ***server_info=NULL;
154
155    dal_handle = (kdb5_dal_handle *) context->db_context;
156    ldap_context = (krb5_ldap_context *) dal_handle->db_context;
157
158    /* copy the conf_section into ldap_context for later use */
159    if (conf_section) {
160	ldap_context->conf_section = strdup (conf_section);
161	if (ldap_context->conf_section == NULL) {
162	    st = ENOMEM;
163	    goto cleanup;
164	}
165    }
166
167    /* initialize the mutexs and condition variable */
168    /* this portion logically doesn't fit here should be moved appropriately */
169
170    /* this mutex is used in ldap reconnection pool */
171    if (k5_mutex_init(&(ldap_context->hndl_lock)) != 0) {
172	st = KRB5_KDB_SERVER_INTERNAL_ERR;
173#if 0
174	st = -1;
175	krb5_ldap_dal_err_funcp(context, krb5_err_have_str, st,
176				"k5_mutex_init failed");
177#endif
178	goto cleanup;
179    }
180
181    /*
182     * If max_server_conns is not set read it from database module
183     * section of conf file this parameter defines maximum ldap
184     * connections per ldap server.
185     */
186    if (ldap_context->max_server_conns == 0) {
187	st = prof_get_integer_def (context, conf_section,
188				   "ldap_conns_per_server",
189				   DEFAULT_CONNS_PER_SERVER,
190				   &ldap_context->max_server_conns);
191	if (st)
192	    goto cleanup;
193    }
194
195    if (ldap_context->max_server_conns < 2) {
196	st = EINVAL;
197	krb5_set_error_message (context, st,
198				gettext("Minimum connections required per server is 2"));
199	goto cleanup;
200    }
201
202    /*
203     * If the bind dn is not set read it from the database module
204     * section of conf file this paramter is populated by one of the
205     * KDC, ADMIN or PASSWD dn to be used to connect to LDAP
206     * server.  The srv_type decides which dn to read.
207     */
208    if (ldap_context->bind_dn == NULL) {
209	char *name = 0;
210	if (srv_type == KRB5_KDB_SRV_TYPE_KDC)
211	    name = "ldap_kdc_dn";
212	else if (srv_type == KRB5_KDB_SRV_TYPE_ADMIN)
213	    name = "ldap_kadmind_dn";
214	else if (srv_type == KRB5_KDB_SRV_TYPE_PASSWD)
215	    name = "ldap_kpasswdd_dn";
216
217	if (name) {
218	    st = prof_get_string_def (context, conf_section, name,
219				      &ldap_context->bind_dn);
220	    if (st)
221		goto cleanup;
222	}
223    }
224
225    /*
226     * Read service_password_file parameter from database module
227     * section of conf file this file contains stashed passwords of
228     * the KDC, ADMIN and PASSWD dns.
229     */
230    if (ldap_context->service_password_file == NULL) {
231	/*
232	 * Solaris Kerberos: providing a default.
233	 */
234	st = profile_get_string (context->profile, KDB_MODULE_SECTION,
235				   conf_section,
236				  "ldap_service_password_file",
237				  NULL,
238				  &ldap_context->service_password_file);
239
240	if (st)
241	    goto cleanup;
242
243	if (ldap_context->service_password_file == NULL) {
244	    st = profile_get_string (context->profile, KDB_MODULE_DEF_SECTION,
245				      "ldap_service_password_file",
246				      NULL,
247				      DEF_SERVICE_PASSWD_FILE,
248				      &ldap_context->service_password_file);
249	    if (st)
250		goto cleanup;
251	}
252    }
253
254/*
255 * Solaris Kerberos: we must use root_certificate_file
256 *
257 * Note, I've changed the ldap_root_certificate_file config parameter to
258 * ldap_cert_path which is more appropriate for that parameter.
259 */
260/* #ifdef HAVE_EDIRECTORY */
261    /*
262     * If root certificate file is not set read it from database
263     * module section of conf file this is the trusted root
264     * certificate of the Directory.
265     */
266    if (ldap_context->root_certificate_file == NULL) {
267	st = prof_get_string_def (context, conf_section,
268				  "ldap_cert_path",
269				  &ldap_context->root_certificate_file);
270	if (st)
271	    goto cleanup;
272    }
273/* #endif */
274
275    /*
276     * If the ldap server parameter is not set read the list of ldap
277     * servers from the database module section of the conf file.
278     */
279
280    if (ldap_context->server_info_list == NULL) {
281	unsigned int ele=0;
282
283	server_info = &(ldap_context->server_info_list);
284	*server_info = (krb5_ldap_server_info **) calloc (SERV_COUNT+1,
285							  sizeof (krb5_ldap_server_info *));
286
287	if (*server_info == NULL) {
288	    st = ENOMEM;
289	    goto cleanup;
290	}
291
292	if ((st=profile_get_string(context->profile, KDB_MODULE_SECTION, conf_section,
293				   "ldap_servers", NULL, &tempval)) != 0) {
294	    krb5_set_error_message (context, st, gettext("Error reading 'ldap_servers' attribute"));
295	    goto cleanup;
296	}
297
298	if (tempval == NULL) {
299
300	    (*server_info)[ele] = (krb5_ldap_server_info *)calloc(1,
301								  sizeof(krb5_ldap_server_info));
302
303	    if ((*server_info)[ele] == NULL) {
304		st = ENOMEM;
305		goto cleanup;
306	    }
307	    (*server_info)[ele]->server_name = strdup("ldapi://");
308	    if ((*server_info)[ele]->server_name == NULL) {
309		st = ENOMEM;
310		goto cleanup;
311	    }
312	    (*server_info)[ele]->server_status = NOTSET;
313	} else {
314	    char *item=NULL;
315
316	    item = strtok_r(tempval,delims,&save_ptr);
317	    while (item != NULL && ele<SERV_COUNT) {
318		(*server_info)[ele] = (krb5_ldap_server_info *)calloc(1,
319								      sizeof(krb5_ldap_server_info));
320		if ((*server_info)[ele] == NULL) {
321		    st = ENOMEM;
322		    goto cleanup;
323		}
324		(*server_info)[ele]->server_name = strdup(item);
325		if ((*server_info)[ele]->server_name == NULL) {
326		    st = ENOMEM;
327		    goto cleanup;
328		}
329
330		(*server_info)[ele]->server_status = NOTSET;
331		item = strtok_r(NULL,delims,&save_ptr);
332		++ele;
333	    }
334	    profile_release_string(tempval);
335	}
336    }
337
338cleanup:
339    return(st);
340}
341
342/*
343 * This function frees the krb5_ldap_context structure members.
344 */
345
346krb5_error_code
347krb5_ldap_free_server_params(ldap_context)
348    krb5_ldap_context           *ldap_context;
349{
350    int                         i=0;
351    krb5_ldap_server_handle     *ldap_server_handle=NULL, *next_ldap_server_handle=NULL;
352
353    if (ldap_context == NULL)
354	return 0;
355
356    /* Free all ldap servers list and the ldap handles associated with
357       the ldap server.  */
358    if (ldap_context->server_info_list) {
359	while (ldap_context->server_info_list[i]) {
360	    if (ldap_context->server_info_list[i]->server_name) {
361		free (ldap_context->server_info_list[i]->server_name);
362	    }
363#ifdef HAVE_EDIRECTORY
364	    if (ldap_context->server_info_list[i]->root_certificate_file) {
365		free (ldap_context->server_info_list[i]->root_certificate_file);
366	    }
367#endif
368	    if (ldap_context->server_info_list[i]->ldap_server_handles) {
369		ldap_server_handle = ldap_context->server_info_list[i]->ldap_server_handles;
370		while (ldap_server_handle) {
371		    ldap_unbind_ext_s(ldap_server_handle->ldap_handle, NULL, NULL);
372		    ldap_server_handle->ldap_handle = NULL;
373		    next_ldap_server_handle = ldap_server_handle->next;
374		    krb5_xfree(ldap_server_handle);
375		    ldap_server_handle = next_ldap_server_handle;
376		}
377	    }
378	    krb5_xfree(ldap_context->server_info_list[i]);
379	    i++;
380	}
381	krb5_xfree(ldap_context->server_info_list);
382    }
383
384    if (ldap_context->conf_section != NULL) {
385	krb5_xfree(ldap_context->conf_section);
386	ldap_context->conf_section = NULL;
387    }
388
389    if (ldap_context->bind_dn != NULL) {
390	krb5_xfree(ldap_context->bind_dn);
391	ldap_context->bind_dn = NULL;
392    }
393
394    if (ldap_context->bind_pwd != NULL) {
395	krb5_xfree(ldap_context->bind_pwd);
396	ldap_context->bind_pwd = NULL;
397    }
398
399    if (ldap_context->service_password_file != NULL) {
400	krb5_xfree(ldap_context->service_password_file);
401	ldap_context->service_password_file = NULL;
402    }
403
404/* Solaris Kerberos */
405/* #ifdef HAVE_EDIRECTORY */
406    if (ldap_context->root_certificate_file != NULL) {
407	krb5_xfree(ldap_context->root_certificate_file);
408	ldap_context->root_certificate_file = NULL;
409    }
410/* #endif */
411
412    if (ldap_context->service_cert_path != NULL) {
413	krb5_xfree(ldap_context->service_cert_path);
414	ldap_context->service_cert_path = NULL;
415    }
416
417    if (ldap_context->service_cert_pass != NULL) {
418	krb5_xfree(ldap_context->service_cert_pass);
419	ldap_context->service_cert_pass = NULL;
420    }
421
422    if (ldap_context->certificates) {
423	i=0;
424	while (ldap_context->certificates[i] != NULL) {
425	    krb5_xfree(ldap_context->certificates[i]->certificate);
426	    krb5_xfree(ldap_context->certificates[i]);
427	    ++i;
428	}
429	krb5_xfree(ldap_context->certificates);
430    }
431
432    k5_mutex_destroy(&ldap_context->hndl_lock);
433
434    krb5_xfree(ldap_context);
435    return(0);
436}
437
438
439/*
440 * check to see if the principal belongs to the default realm.
441 * The default realm is present in the krb5_ldap_context structure.
442 * The principal has a realm portion. This realm portion is compared with the default realm
443 * to check whether the principal belong to the default realm.
444 * Return 0 if principal belongs to default realm else 1.
445 */
446
447krb5_error_code
448is_principal_in_realm(ldap_context, searchfor)
449    krb5_ldap_context          *ldap_context;
450    krb5_const_principal       searchfor;
451{
452    size_t                      defrealmlen=0;
453    char                        *defrealm=NULL;
454
455#define FIND_MAX(a,b) ((a) > (b) ? (a) : (b))
456
457    defrealmlen = strlen(ldap_context->lrparams->realm_name);
458    defrealm = ldap_context->lrparams->realm_name;
459
460    /*
461     * Care should be taken for inter-realm principals as the default
462     * realm can exist in the realm part of the principal name or can
463     * also exist in the second portion of the name part.  However, if
464     * the default realm exist in the second part of the principal
465     * portion, then the first portion of the principal name SHOULD be
466     * "krbtgt".  All this check is done in the immediate block.
467     */
468    if (searchfor->length == 2)
469	if ((strncasecmp(searchfor->data[0].data, "krbtgt",
470			 FIND_MAX(searchfor->data[0].length, strlen("krbtgt"))) == 0) &&
471	    (strncasecmp(searchfor->data[1].data, defrealm,
472			 FIND_MAX(searchfor->data[1].length, defrealmlen)) == 0))
473	    return 0;
474
475    /* first check the length, if they are not equal, then they are not same */
476    if (strlen(defrealm) != searchfor->realm.length)
477	return 1;
478
479    /* if the length is equal, check for the contents */
480    if (strncmp(defrealm, searchfor->realm.data,
481		searchfor->realm.length) != 0)
482	return 1;
483    /* if we are here, then the realm portions match, return 0 */
484    return 0;
485}
486
487
488/*
489 * Deduce the subtree information from the context. A realm can have
490 * multiple subtrees.
491 * 1. the Realm container
492 * 2. the actual subtrees associated with the Realm
493 *
494 * However, there are some conditions to be considered to deduce the
495 * actual subtree/s associated with the realm.  The conditions are as
496 * follows:
497 * 1. If the subtree information of the Realm is [Root] or NULL (that
498 *    is internal a [Root]) then the realm has only one subtree
499 *    i.e [Root], i.e. whole of the tree.
500 * 2. If the subtree information of the Realm is missing/absent, then the
501 *    realm has only one, i.e., the Realm container.  NOTE: In all cases
502 *    Realm container SHOULD be the one among the subtrees or the only
503 *    one subtree.
504 * 3. The subtree information of the realm is overlapping the realm
505 *    container of the realm, then the realm has only one subtree and
506 *    it is the subtree information associated with the realm.
507 */
508krb5_error_code
509krb5_get_subtree_info(ldap_context, subtreearr, ntree)
510    krb5_ldap_context           *ldap_context;
511    char                        ***subtreearr;
512    unsigned int                *ntree;
513{
514    int                         st=0, i=0, subtreecount=0;
515    int				ncount=0, search_scope=0;
516    char                        **subtree=NULL, *realm_cont_dn=NULL;
517    char                        **subtarr=NULL;
518    char                        *containerref=NULL;
519    char 			**newsubtree=NULL;
520
521    containerref = ldap_context->lrparams->containerref;
522    subtree = ldap_context->lrparams->subtree;
523    realm_cont_dn = ldap_context->lrparams->realmdn;
524    subtreecount = ldap_context->lrparams->subtreecount;
525    search_scope = ldap_context->lrparams->search_scope;
526
527    subtarr = (char **) malloc(sizeof(char *) * (subtreecount + 1 /*realm dn*/ + 1 /*containerref*/ + 1));
528    if (subtarr == NULL) {
529	st = ENOMEM;
530	goto cleanup;
531    }
532    memset(subtarr, 0, (sizeof(char *) * (subtreecount+1+1+1)));
533
534    /* get the complete subtree list */
535    for (i=0; i<subtreecount && subtree[i]!=NULL; i++) {
536	subtarr[i] = strdup(subtree[i]);
537	if (subtarr[i] == NULL) {
538	    st = ENOMEM;
539	    goto cleanup;
540	}
541    }
542
543    subtarr[i] = strdup(realm_cont_dn);
544    if (subtarr[i++] == NULL) {
545	st = ENOMEM;
546	goto cleanup;
547    }
548
549    if (containerref != NULL) {
550	subtarr[i] = strdup(containerref);
551	if (subtarr[i++] == NULL) {
552	    st = ENOMEM;
553	    goto cleanup;
554	}
555    }
556
557    ncount = i;
558    newsubtree = (char **) malloc(sizeof(char *) * (ncount + 1));
559    if (newsubtree == NULL) {
560        st = ENOMEM;
561        goto cleanup;
562    }
563    memset(newsubtree, 0, (sizeof(char *) * (ncount+1)));
564    if ((st = remove_overlapping_subtrees(subtarr, newsubtree, &ncount,
565		search_scope)) != 0) {
566        goto cleanup;
567    }
568
569    *ntree = ncount;
570    *subtreearr = newsubtree;
571
572cleanup:
573    if (subtarr != NULL) {
574	for (i=0; subtarr[i] != NULL; i++)
575	    free(subtarr[i]);
576	free(subtarr);
577    }
578
579    if (st != 0) {
580        if (newsubtree != NULL) {
581	    for (i=0; newsubtree[i] != NULL; i++)
582	        free(newsubtree[i]);
583	    free(newsubtree);
584        }
585    }
586    return st;
587}
588
589/*
590 * This function appends the content with a type into the tl_data
591 * structure.  Based on the type the length of the content is either
592 * pre-defined or computed from the content.  Returns 0 in case of
593 * success and 1 if the type associated with the content is undefined.
594 */
595
596krb5_error_code
597store_tl_data(tl_data, tl_type, value)
598    krb5_tl_data                *tl_data;
599    int                         tl_type;
600    void                        *value;
601{
602    unsigned int                currlen=0, tldatalen=0;
603    unsigned char               *curr=NULL;
604    void                        *reallocptr=NULL;
605
606    tl_data->tl_data_type = KDB_TL_USER_INFO;
607    switch (tl_type) {
608    case KDB_TL_PRINCCOUNT:
609    case KDB_TL_PRINCTYPE:
610    case KDB_TL_MASK:
611    {
612	int *iptr = (int *)value;
613	int ivalue = *iptr;
614
615	currlen = tl_data->tl_data_length;
616	tl_data->tl_data_length += 1 + 2 + 2;
617	/* allocate required memory */
618	reallocptr = tl_data->tl_data_contents;
619	tl_data->tl_data_contents = realloc(tl_data->tl_data_contents,
620					    tl_data->tl_data_length);
621	if (tl_data->tl_data_contents == NULL) {
622	    if (reallocptr)
623		free (reallocptr);
624	    return ENOMEM;
625	}
626	curr = (tl_data->tl_data_contents + currlen);
627
628	/* store the tl_type value */
629	memset(curr, tl_type, 1);
630	curr += 1;
631	/* store the content length */
632	tldatalen = 2;
633	STORE16_INT(curr, tldatalen);
634	curr += 2;
635	/* store the content */
636	STORE16_INT(curr, ivalue);
637	curr += 2;
638	break;
639    }
640
641    case KDB_TL_USERDN:
642    case KDB_TL_LINKDN:
643    {
644	char *cptr = (char *)value;
645
646	currlen = tl_data->tl_data_length;
647	tl_data->tl_data_length += 1 + 2 + strlen(cptr);
648	/* allocate required memory */
649	reallocptr = tl_data->tl_data_contents;
650	tl_data->tl_data_contents = realloc(tl_data->tl_data_contents,
651					    tl_data->tl_data_length);
652	if (tl_data->tl_data_contents == NULL) {
653	    if (reallocptr)
654		free (reallocptr);
655	    return ENOMEM;
656	}
657	curr = (tl_data->tl_data_contents + currlen);
658
659	/* store the tl_type value */
660	memset(curr, tl_type, 1);
661	curr += 1;
662	/* store the content length */
663	tldatalen = strlen(cptr);
664	STORE16_INT(curr, tldatalen);
665	curr += 2;
666	/* store the content */
667	memcpy(curr, cptr, tldatalen);
668	curr += tldatalen;
669	break;
670    }
671
672    default:
673	return 1;
674
675    }
676    return 0;
677}
678
679/*
680 * This function scans the tl_data structure to get the value of a
681 * type defined by the tl_type (second parameter).  The tl_data
682 * structure has all the data in the tl_data_contents member.  The
683 * format of the tl_data_contents is as follows.  The first byte
684 * defines the type of the content that follows.  The next 2 bytes
685 * define the size n (in terms of bytes) of the content that
686 * follows.  The next n bytes define the content itself.
687 */
688
689krb5_error_code
690decode_tl_data(tl_data, tl_type, data)
691    krb5_tl_data                *tl_data;
692    int                         tl_type;
693    void                        **data;
694{
695    int                         subtype=0, i=0, limit=10;
696    unsigned int                sublen=0;
697    unsigned char               *curr=NULL;
698    int                         *intptr=NULL;
699    long                        *longptr=NULL;
700    char                        *DN=NULL, **DNarr=NULL;
701    krb5_error_code             st=-1;
702
703    *data = NULL;
704
705    curr = tl_data->tl_data_contents;
706    while (curr < (tl_data->tl_data_contents + tl_data->tl_data_length)) {
707
708	/* get the type of the content */
709	subtype = (int) curr[0];
710	/* forward by 1 byte*/
711	curr += 1;
712
713	if (subtype == tl_type) {
714	    switch (subtype) {
715
716	    case KDB_TL_PRINCCOUNT:
717	    case KDB_TL_PRINCTYPE:
718	    case KDB_TL_MASK:
719		/* get the length of the content */
720		UNSTORE16_INT(curr, sublen);
721		/* forward by 2 bytes */
722		curr += 2;
723		/* get the actual content */
724		if (sublen == 2) {
725		    /* intptr = malloc(sublen);	  */
726		    intptr = malloc(sizeof(krb5_int32));
727		    if (intptr == NULL)
728			return ENOMEM;
729		    memset(intptr, 0, sublen);
730		    UNSTORE16_INT(curr, (*intptr));
731		    *data = intptr;
732		} else {
733		    longptr = malloc(sublen);
734		    if (longptr == NULL)
735			return ENOMEM;
736		    memset(longptr, 0, sublen);
737		    UNSTORE32_INT(curr, (*longptr));
738		    *data = longptr;
739		}
740		curr += sublen;
741		st = 0;
742		return st;
743		/*LINTED*/
744		break;
745
746	    case KDB_TL_CONTAINERDN:
747	    case KDB_TL_USERDN:
748		/* get the length of the content */
749		UNSTORE16_INT(curr, sublen);
750		/* forward by 2 bytes */
751		curr += 2;
752		DN = malloc (sublen + 1);
753		if (DN == NULL)
754		    return ENOMEM;
755		memcpy(DN, curr, sublen);
756		DN[sublen] = 0;
757		*data = DN;
758		curr += sublen;
759		st = 0;
760		return st;
761		/*LINTED*/
762		break;
763
764	    case KDB_TL_LINKDN:
765		if (DNarr == NULL) {
766		    DNarr = calloc(limit, sizeof(char *));
767		    if (DNarr == NULL)
768			return ENOMEM;
769		}
770		if (i == limit-1) {
771		    limit *= 2;
772		    DNarr = realloc(DNarr, sizeof(char *) * (limit));
773		    if (DNarr == NULL)
774			return ENOMEM;
775		}
776
777		/* get the length of the content */
778		UNSTORE16_INT(curr, sublen);
779		/* forward by 2 bytes */
780		curr += 2;
781		DNarr[i] = malloc (sublen + 1);
782		if (DNarr[i] == NULL) {
783		    int j=0;
784		    for (; j<i; j++)
785			free(DNarr[j]);
786		    free(DNarr);
787		    return ENOMEM;
788		}
789		memcpy(DNarr[i], curr, sublen);
790		DNarr[i][sublen] = 0;
791		++i;
792		curr += sublen;
793		*data = DNarr;
794		st=0;
795		break;
796	    }
797	} else {
798	    /* move to the current content block */
799	    UNSTORE16_INT(curr, sublen);
800	    curr += 2 + sublen;
801	}
802    }
803    return st;
804}
805
806/*
807 * wrapper routines for decode_tl_data
808 */
809static krb5_error_code
810krb5_get_int_from_tl_data(context, entries, type, intval)
811    krb5_context                context;
812    krb5_db_entry               *entries;
813    int                         type;
814    int                         *intval;
815{
816    krb5_error_code             st=0;
817    krb5_tl_data                tl_data;
818    void                        *voidptr=NULL;
819    int                         *intptr=NULL;
820
821    tl_data.tl_data_type = KDB_TL_USER_INFO;
822    if (((st=krb5_dbe_lookup_tl_data(context, entries, &tl_data)) != 0) || tl_data.tl_data_length == 0)
823	goto cleanup;
824
825    if (decode_tl_data(&tl_data, type, &voidptr) == 0) {
826	intptr = (int *) voidptr;
827	*intval = *intptr;
828	free(intptr);
829    }
830
831cleanup:
832    return st;
833}
834
835/*
836 * Get the mask representing the attributes set on the directory
837 * object (user, policy ...).
838 */
839krb5_error_code
840krb5_get_attributes_mask(context, entries, mask)
841    krb5_context                context;
842    krb5_db_entry               *entries;
843    unsigned int                *mask;
844{
845    return krb5_get_int_from_tl_data(context, entries, KDB_TL_MASK,
846	(int *)mask);
847}
848
849krb5_error_code
850krb5_get_princ_type(context, entries, ptype)
851    krb5_context                context;
852    krb5_db_entry               *entries;
853    int                         *ptype;
854{
855    return krb5_get_int_from_tl_data(context, entries, KDB_TL_PRINCTYPE, ptype);
856}
857
858krb5_error_code
859krb5_get_princ_count(context, entries, pcount)
860    krb5_context                context;
861    krb5_db_entry               *entries;
862    int                         *pcount;
863{
864    return krb5_get_int_from_tl_data(context, entries, KDB_TL_PRINCCOUNT, pcount);
865}
866
867krb5_error_code
868krb5_get_linkdn(context, entries, link_dn)
869    krb5_context                context;
870    krb5_db_entry               *entries;
871    char                        ***link_dn;
872{
873    krb5_error_code             st=0;
874    krb5_tl_data                tl_data;
875    void                        *voidptr=NULL;
876
877    *link_dn = NULL;
878    tl_data.tl_data_type = KDB_TL_USER_INFO;
879    if (((st=krb5_dbe_lookup_tl_data(context, entries, &tl_data)) != 0) || tl_data.tl_data_length == 0)
880	goto cleanup;
881
882    if (decode_tl_data(&tl_data, KDB_TL_LINKDN, &voidptr) == 0) {
883	*link_dn = (char **) voidptr;
884    }
885
886cleanup:
887    return st;
888}
889
890static krb5_error_code
891krb5_get_str_from_tl_data(context, entries, type, strval)
892    krb5_context                context;
893    krb5_db_entry               *entries;
894    int                         type;
895    char                        **strval;
896{
897    krb5_error_code             st=0;
898    krb5_tl_data                tl_data;
899    void                        *voidptr=NULL;
900
901    if (type != KDB_TL_USERDN && type != KDB_TL_CONTAINERDN) {
902	st = EINVAL;
903	goto cleanup;
904    }
905
906    tl_data.tl_data_type = KDB_TL_USER_INFO;
907    if (((st=krb5_dbe_lookup_tl_data(context, entries, &tl_data)) != 0) || tl_data.tl_data_length == 0)
908	goto cleanup;
909
910    if (decode_tl_data(&tl_data, type, &voidptr) == 0) {
911	*strval = (char *) voidptr;
912    }
913
914cleanup:
915    return st;
916}
917
918krb5_error_code
919krb5_get_userdn(context, entries, userdn)
920    krb5_context                context;
921    krb5_db_entry               *entries;
922    char                        **userdn;
923{
924    *userdn = NULL;
925    return krb5_get_str_from_tl_data(context, entries, KDB_TL_USERDN, userdn);
926}
927
928krb5_error_code
929krb5_get_containerdn(context, entries, containerdn)
930    krb5_context                context;
931    krb5_db_entry               *entries;
932    char                        **containerdn;
933{
934    *containerdn = NULL;
935    return krb5_get_str_from_tl_data(context, entries, KDB_TL_CONTAINERDN, containerdn);
936}
937
938/*
939 * This function reads the attribute values (if the attribute is
940 * non-null) from the dn.  The read attribute values is compared
941 * aganist the attrvalues passed to the function and a bit mask is set
942 * for all the matching attributes (attributes existing in both list).
943 * The bit to be set is selected such that the index of the attribute
944 * in the attrvalues parameter is the position of the bit.  For ex:
945 * the first element in the attrvalues is present in both list shall
946 * set the LSB of the bit mask.
947 *
948 * In case if either the attribute or the attrvalues parameter to the
949 * function is NULL, then the existence of the object is considered
950 * and appropriate status is returned back.
951 */
952
953krb5_error_code
954checkattributevalue (ld, dn, attribute, attrvalues, mask)
955    LDAP                        *ld;
956    char                        *dn;
957    char                        *attribute;
958    char                        **attrvalues;
959    int                         *mask;
960{
961    int                         st=0, one=1;
962    char                        **values=NULL, *attributes[2] = {NULL};
963    LDAPMessage                 *result=NULL, *entry=NULL;
964
965    if (strlen(dn) == 0) {
966	st = set_ldap_error(0, LDAP_NO_SUCH_OBJECT, OP_SEARCH);
967	return st;
968    }
969
970    attributes[0] = attribute;
971
972    /* read the attribute values from the dn */
973    if ((st = ldap_search_ext_s(ld,
974				dn,
975				LDAP_SCOPE_BASE,
976				0,
977				attributes,
978				0,
979				NULL,
980				NULL,
981				&timelimit,
982				LDAP_NO_LIMIT,
983				&result)) != LDAP_SUCCESS) {
984	st = set_ldap_error(0, st, OP_SEARCH);
985	return st;
986    }
987
988    /*
989     * If the attribute/attrvalues is NULL, then check for the
990     * existence of the object alone.
991     */
992    if (attribute == NULL || attrvalues == NULL)
993	goto cleanup;
994
995    /* reset the bit mask */
996    *mask = 0;
997
998    if ((entry=ldap_first_entry(ld, result)) != NULL) {
999	/* read the attribute values */
1000	if ((values=ldap_get_values(ld, entry, attribute)) != NULL) {
1001	    int i,j;
1002
1003	    /*
1004	     * Compare the read attribute values with the attrvalues
1005	     * array and set the appropriate bit mask.
1006	     */
1007	    for (j=0; attrvalues[j]; ++j) {
1008		for (i=0; values[i]; ++i) {
1009		    if (strcasecmp(values[i], attrvalues[j]) == 0) {
1010			*mask |= (one<<j);
1011			break;
1012		    }
1013		}
1014	    }
1015	    ldap_value_free(values);
1016	}
1017    }
1018
1019cleanup:
1020    ldap_msgfree(result);
1021    return st;
1022}
1023
1024
1025/*
1026 * This function updates a single attribute with a single value of a
1027 * specified dn.  This function is mainly used to update
1028 * krbRealmReferences, krbKdcServers, krbAdminServers... when KDC,
1029 * ADMIN, PASSWD servers are associated with some realms or vice
1030 * versa.
1031 */
1032
1033krb5_error_code
1034updateAttribute (ld, dn, attribute, value)
1035    LDAP                        *ld;
1036    char                        *dn;
1037    char                        *attribute;
1038    char                        *value;
1039{
1040    int                         st=0;
1041    LDAPMod                     modAttr, *mods[2]={NULL};
1042    char                        *values[2]={NULL};
1043
1044    values[0] = value;
1045
1046    /* data to update the {attr,attrval} combination */
1047    memset(&modAttr, 0, sizeof(modAttr));
1048    modAttr.mod_type = attribute;
1049    modAttr.mod_op = LDAP_MOD_ADD;
1050    modAttr.mod_values = values;
1051    mods[0] = &modAttr;
1052
1053    /* ldap modify operation */
1054    st = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1055
1056    /* if the {attr,attrval} combination is already present return a success
1057     * LDAP_ALREADY_EXISTS is for single-valued attribute
1058     * LDAP_TYPE_OR_VALUE_EXISTS is for multi-valued attribute
1059     */
1060    if (st == LDAP_ALREADY_EXISTS || st == LDAP_TYPE_OR_VALUE_EXISTS)
1061	st = 0;
1062
1063    if (st != 0) {
1064	st = set_ldap_error (0, st, OP_MOD);
1065    }
1066
1067    return st;
1068}
1069
1070/*
1071 * This function deletes a single attribute with a single value of a
1072 * specified dn.  This function is mainly used to delete
1073 * krbRealmReferences, krbKdcServers, krbAdminServers... when KDC,
1074 * ADMIN, PASSWD servers are disassociated with some realms or vice
1075 * versa.
1076 */
1077
1078krb5_error_code
1079deleteAttribute (ld, dn, attribute, value)
1080    LDAP                        *ld;
1081    char                        *dn;
1082    char                        *attribute;
1083    char                        *value;
1084{
1085    krb5_error_code             st=0;
1086    LDAPMod                     modAttr, *mods[2]={NULL};
1087    char                        *values[2]={NULL};
1088
1089    values[0] = value;
1090
1091    /* data to delete the {attr,attrval} combination */
1092    memset(&modAttr, 0, sizeof(modAttr));
1093    modAttr.mod_type = attribute;
1094    modAttr.mod_op = LDAP_MOD_DELETE;
1095    modAttr.mod_values = values;
1096    mods[0] = &modAttr;
1097
1098    /* ldap modify operation */
1099    st = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1100
1101    /* if either the attribute or the attribute value is missing return a success */
1102    if (st == LDAP_NO_SUCH_ATTRIBUTE || st == LDAP_UNDEFINED_TYPE)
1103	st = 0;
1104
1105    if (st != 0) {
1106	st = set_ldap_error (0, st, OP_MOD);
1107    }
1108
1109    return st;
1110}
1111
1112
1113/*
1114 * This function takes in 2 string arrays, compares them to remove the
1115 * matching entries.  The first array is the original list and the
1116 * second array is the modified list.  Removing the matching entries
1117 * will result in a reduced array, where the left over first array
1118 * elements are the deleted entries and the left over second array
1119 * elements are the added entries.  These additions and deletions has
1120 * resulted in the modified second array.
1121 */
1122
1123krb5_error_code
1124disjoint_members(src, dest)
1125    char                        **src;
1126    char                        **dest;
1127{
1128    int                         i=0, j=0, slen=0, dlen=0;
1129
1130    /* validate the input parameters */
1131    if (src == NULL || dest == NULL)
1132	return 0;
1133
1134    /* compute the first array length */
1135    for (i=0;src[i]; ++i)
1136	;
1137
1138    /* return if the length is 0 */
1139    if (i==0)
1140	return 0;
1141
1142    /* index of the last element and also the length of the array */
1143    slen = i-1;
1144
1145    /* compute the second array length */
1146    for (i=0;dest[i]; ++i)
1147	;
1148
1149    /* return if the length is 0 */
1150    if (i==0)
1151	return 0;
1152
1153    /* index of the last element and also the length of the array */
1154    dlen = i-1;
1155
1156    /* check for the similar elements and delete them from both the arrays */
1157    for (i=0; src[i]; ++i) {
1158
1159	for (j=0; dest[j]; ++j) {
1160
1161	    /* if the element are same */
1162	    if (strcasecmp(src[i], dest[j]) == 0) {
1163		/*
1164		 * If the matched element is in the middle, then copy
1165		 * the last element to the matched index.
1166		 */
1167		if (i != slen) {
1168		    free (src[i]);
1169		    src[i] = src[slen];
1170		    src[slen] = NULL;
1171		} else {
1172		    /*
1173		     * If the matched element is the last, free it and
1174		     * set it to NULL.
1175		     */
1176		    free (src[i]);
1177		    src[i] = NULL;
1178		}
1179		/* reduce the array length by 1 */
1180		slen -= 1;
1181
1182		/* repeat the same processing for the second array too */
1183		if (j != dlen) {
1184		    free(dest[j]);
1185		    dest[j] = dest[dlen];
1186		    dest[dlen] = NULL;
1187		} else {
1188		    free(dest[j]);
1189		    dest[j] = NULL;
1190		}
1191		dlen -=1;
1192
1193		/*
1194		 * The source array is reduced by 1, so reduce the
1195		 * index variable used for source array by 1.  No need
1196		 * to adjust the second array index variable as it is
1197		 * reset while entering the inner loop.
1198		 */
1199		i -= 1;
1200		break;
1201	    }
1202	}
1203    }
1204    return 0;
1205}
1206
1207/*
1208 * This function replicates the contents of the src array for later
1209 * use. Mostly the contents of the src array is obtained from a
1210 * ldap_search operation and the contents are required for later use.
1211 */
1212
1213krb5_error_code
1214copy_arrays(src, dest, count)
1215    char                        **src;
1216    char                        ***dest;
1217    int                         count;
1218{
1219    krb5_error_code             st=0;
1220    int                         i=0;
1221
1222    /* validate the input parameters */
1223    if (src == NULL || dest == NULL)
1224	return 0;
1225
1226    /* allocate memory for the dest array */
1227    *dest = (char **) calloc((unsigned) count+1, sizeof(char *));
1228    if (*dest == NULL) {
1229	st = ENOMEM;
1230	goto cleanup;
1231    }
1232
1233    /* copy the members from src to dest array. */
1234    for (i=0; i < count && src[i] != NULL; ++i) {
1235	(*dest)[i] = strdup(src[i]);
1236	if ((*dest)[i] == NULL) {
1237	    st = ENOMEM;
1238	    goto cleanup;
1239	}
1240    }
1241
1242cleanup:
1243    /* in case of error free up everything and return */
1244    if (st != 0) {
1245	if (*dest != NULL) {
1246	    for (i=0; (*dest)[i]; ++i) {
1247		free ((*dest)[i]);
1248		(*dest)[i] = NULL;
1249	    }
1250	    free (*dest);
1251	    *dest = NULL;
1252	}
1253    }
1254    return st;
1255}
1256
1257static krb5_error_code
1258getepochtime(strtime, epochtime)
1259    char              *strtime;
1260    krb5_timestamp    *epochtime;
1261{
1262    struct tm           tme;
1263
1264    memset(&tme, 0, sizeof(tme));
1265    if (strptime(strtime, DATE_FORMAT, &tme) == NULL) {
1266	*epochtime = 0;
1267	return EINVAL;
1268    }
1269
1270    *epochtime = krb5int_gmt_mktime(&tme);
1271
1272    return 0;
1273}
1274
1275/*
1276 * krb5_ldap_get_value() - get the integer value of the attribute
1277 * Returns, 0 if the attribute is present, 1 if the attribute is missing.
1278 * The retval is 0 if the attribute is missing.
1279 */
1280
1281krb5_error_code
1282krb5_ldap_get_value(ld, ent, attribute, retval)
1283    LDAP                        *ld;
1284    LDAPMessage                 *ent;
1285    char                        *attribute;
1286    int                         *retval;
1287{
1288    char                           **values=NULL;
1289
1290    *retval = 0;
1291    values=ldap_get_values(ld, ent, attribute);
1292    if (values != NULL) {
1293	if (values[0] != NULL)
1294	    *retval = atoi(values[0]);
1295	ldap_value_free(values);
1296	return 0;
1297    }
1298    return 1;
1299}
1300
1301/*
1302 * krb5_ldap_get_string() - Returns the first string of the
1303 * attribute.  Intended to
1304 *
1305 *
1306 */
1307krb5_error_code
1308krb5_ldap_get_string(ld, ent, attribute, retstr, attr_present)
1309    LDAP                        *ld;
1310    LDAPMessage                 *ent;
1311    char                        *attribute;
1312    char                        **retstr;
1313    krb5_boolean                *attr_present;
1314{
1315    char                           **values=NULL;
1316    krb5_error_code                st=0;
1317
1318    *retstr = NULL;
1319    if (attr_present != NULL)
1320	*attr_present = FALSE;
1321
1322    values=ldap_get_values(ld, ent, attribute);
1323    if (values != NULL) {
1324	if (values[0] != NULL) {
1325	    if (attr_present!= NULL)
1326		*attr_present = TRUE;
1327	    *retstr = strdup(values[0]);
1328	    if (*retstr == NULL)
1329		st = ENOMEM;
1330	}
1331	ldap_value_free(values);
1332    }
1333    return st;
1334}
1335
1336/*
1337 * krb5_ldap_get_strings() - Returns all the values
1338 * of the attribute.
1339 */
1340krb5_error_code
1341krb5_ldap_get_strings(ld, ent, attribute, retarr, attr_present)
1342    LDAP                        *ld;
1343    LDAPMessage                 *ent;
1344    char                        *attribute;
1345    char                        ***retarr;
1346    krb5_boolean                *attr_present;
1347{
1348    char                        **values=NULL;
1349    krb5_error_code             st=0;
1350    unsigned int                i=0, count=0;
1351
1352    *retarr = NULL;
1353    if (attr_present != NULL)
1354	*attr_present = FALSE;
1355
1356    values=ldap_get_values(ld, ent, attribute);
1357    if (values != NULL) {
1358	if (attr_present != NULL)
1359	    *attr_present = TRUE;
1360
1361	count = ldap_count_values(values);
1362	*retarr  = (char **) calloc(count+1, sizeof(char *));
1363	if (*retarr == NULL) {
1364	    st = ENOMEM;
1365	    return st;
1366	}
1367	for (i=0; i< count; ++i) {
1368	    (*retarr)[i] = strdup(values[i]);
1369	    if ((*retarr)[i] == NULL) {
1370		st = ENOMEM;
1371		goto cleanup;
1372	    }
1373	}
1374	ldap_value_free(values);
1375    }
1376
1377cleanup:
1378    if (st != 0) {
1379	if (*retarr != NULL) {
1380	    for (i=0; i< count; ++i)
1381		if ((*retarr)[i] != NULL)
1382		    free ((*retarr)[i]);
1383	    free (*retarr);
1384	}
1385    }
1386    return st;
1387}
1388
1389krb5_error_code
1390krb5_ldap_get_time(ld, ent, attribute, rettime, attr_present)
1391    LDAP                        *ld;
1392    LDAPMessage                 *ent;
1393    char                        *attribute;
1394    krb5_timestamp              *rettime;
1395    krb5_boolean                *attr_present;
1396{
1397    char                         **values=NULL;
1398    krb5_error_code              st=0;
1399
1400    *rettime = 0;
1401    *attr_present = FALSE;
1402
1403    values=ldap_get_values(ld, ent, attribute);
1404    if (values != NULL) {
1405	if (values[0] != NULL) {
1406	    *attr_present = TRUE;
1407	    st = getepochtime(values[0], rettime);
1408	}
1409	ldap_value_free(values);
1410    }
1411    return st;
1412}
1413
1414/*
1415 * Function to allocate, set the values of LDAPMod structure. The
1416 * LDAPMod structure is then added to the array at the ind
1417 */
1418
1419krb5_error_code
1420krb5_add_member(mods, count)
1421    LDAPMod          ***mods;
1422    int              *count;
1423{
1424    int i=0;
1425    LDAPMod **lmods=NULL;
1426
1427    if ((*mods) != NULL) {
1428	for (;(*mods)[i] != NULL; ++i)
1429	    ;
1430    }
1431    lmods = (LDAPMod **) realloc((*mods), (2+i) * sizeof(LDAPMod *));
1432    if (lmods == NULL)
1433	return ENOMEM;
1434
1435    *mods = lmods;
1436    (*mods)[i+1] = NULL;
1437    (*mods)[i] = (LDAPMod *) calloc(1, sizeof (LDAPMod));
1438    if ((*mods)[i] == NULL) {
1439	free(lmods);
1440	*mods = NULL;
1441	return ENOMEM;
1442    }
1443    *count = i;
1444    return 0;
1445}
1446
1447krb5_error_code
1448krb5_add_str_mem_ldap_mod(mods, attribute, op, values)
1449    LDAPMod  ***mods;
1450    char     *attribute;
1451    int      op;
1452    char     **values;
1453
1454{
1455    int i=0, j=0;
1456    krb5_error_code   st=0;
1457
1458    if ((st=krb5_add_member(mods, &i)) != 0)
1459	return st;
1460
1461    (*mods)[i]->mod_type = strdup(attribute);
1462    if ((*mods)[i]->mod_type == NULL)
1463	return ENOMEM;
1464    (*mods)[i]->mod_op = op;
1465
1466    (*mods)[i]->mod_values = NULL;
1467
1468    if (values != NULL) {
1469	for (j=0; values[j] != NULL; ++j)
1470	    ;
1471	(*mods)[i]->mod_values = malloc (sizeof(char *) * (j+1));
1472	if ((*mods)[i]->mod_values == NULL) {
1473	    free((*mods)[i]->mod_type);
1474	    (*mods)[i]->mod_type = NULL;
1475	    return ENOMEM;
1476	}
1477
1478	for (j=0; values[j] != NULL; ++j) {
1479	    (*mods)[i]->mod_values[j] = strdup(values[j]);
1480	    if ((*mods)[i]->mod_values[j] == NULL){
1481		int k=0;
1482		for (; k<j; k++) {
1483		    free((*mods)[i]->mod_values[k]);
1484		    (*mods)[i]->mod_values[k] = NULL;
1485		}
1486		return ENOMEM;
1487	    }
1488	}
1489	(*mods)[i]->mod_values[j] = NULL;
1490    }
1491    return 0;
1492}
1493
1494krb5_error_code
1495krb5_add_ber_mem_ldap_mod(mods, attribute, op, ber_values)
1496    LDAPMod  ***mods;
1497    char     *attribute;
1498    int      op;
1499    struct berval **ber_values;
1500
1501{
1502    int i=0, j=0;
1503    krb5_error_code   st=0;
1504
1505    if ((st=krb5_add_member(mods, &i)) != 0)
1506	return st;
1507
1508    (*mods)[i]->mod_type = strdup(attribute);
1509    if ((*mods)[i]->mod_type == NULL)
1510	return ENOMEM;
1511    (*mods)[i]->mod_op = op;
1512
1513    for (j=0; ber_values[j] != NULL; ++j)
1514	;
1515    (*mods)[i]->mod_bvalues = malloc (sizeof(struct berval *) * (j+1));
1516    if ((*mods)[i]->mod_bvalues == NULL)
1517	return ENOMEM;
1518
1519    for (j=0; ber_values[j] != NULL; ++j) {
1520	(*mods)[i]->mod_bvalues[j] = calloc(1, sizeof(struct berval));
1521	if ((*mods)[i]->mod_bvalues[j] == NULL)
1522	    return ENOMEM;
1523
1524	(*mods)[i]->mod_bvalues[j]->bv_len = ber_values[j]->bv_len;
1525	(*mods)[i]->mod_bvalues[j]->bv_val = malloc((*mods)[i]->mod_bvalues[j]->bv_len);
1526	if ((*mods)[i]->mod_bvalues[j]->bv_val == NULL)
1527	    return ENOMEM;
1528
1529	memcpy((*mods)[i]->mod_bvalues[j]->bv_val, ber_values[j]->bv_val,
1530	       ber_values[j]->bv_len);
1531    }
1532    (*mods)[i]->mod_bvalues[j] = NULL;
1533    return 0;
1534}
1535
1536static inline char *
1537format_d (int val)
1538{
1539    char tmpbuf[2+3*sizeof(val)];
1540    sprintf(tmpbuf, "%d", val);
1541    return strdup(tmpbuf);
1542}
1543
1544krb5_error_code
1545krb5_add_int_arr_mem_ldap_mod(mods, attribute, op, value)
1546    LDAPMod  ***mods;
1547    char     *attribute;
1548    int      op;
1549    int      *value;
1550
1551{
1552    int i=0, j=0;
1553    krb5_error_code   st=0;
1554
1555    if ((st=krb5_add_member(mods, &i)) != 0)
1556	return st;
1557
1558    (*mods)[i]->mod_type = strdup(attribute);
1559    if ((*mods)[i]->mod_type == NULL)
1560	return ENOMEM;
1561    (*mods)[i]->mod_op = op;
1562
1563    for (j=0; value[j] != -1; ++j)
1564	;
1565
1566    (*mods)[i]->mod_values = malloc(sizeof(char *) * (j+1));
1567
1568    for (j=0; value[j] != -1; ++j) {
1569	if (((*mods)[i]->mod_values[j] = format_d(value[j])) == NULL)
1570	    return ENOMEM;
1571    }
1572    (*mods)[i]->mod_values[j] = NULL;
1573    return 0;
1574}
1575
1576krb5_error_code
1577krb5_add_int_mem_ldap_mod(mods, attribute, op, value)
1578    LDAPMod  ***mods;
1579    char     *attribute;
1580    int      op;
1581    int      value;
1582
1583{
1584    int i=0;
1585    krb5_error_code      st=0;
1586
1587    if ((st=krb5_add_member(mods, &i)) != 0)
1588	return st;
1589
1590    (*mods)[i]->mod_type = strdup(attribute);
1591    if ((*mods)[i]->mod_type == NULL)
1592	return ENOMEM;
1593
1594    (*mods)[i]->mod_op = op;
1595    (*mods)[i]->mod_values = calloc (2, sizeof(char *));
1596    if (((*mods)[i]->mod_values[0] = format_d(value)) == NULL)
1597	return ENOMEM;
1598    return 0;
1599}
1600
1601/*ARGSUSED*/
1602krb5_error_code
1603krb5_ldap_set_option(krb5_context kcontext, int option, void *value)
1604{
1605    krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1606    krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1607    return status;
1608}
1609
1610/*ARGSUSED*/
1611krb5_error_code
1612krb5_ldap_lock(krb5_context kcontext, int mode)
1613{
1614    krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1615    krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1616    return status;
1617}
1618
1619krb5_error_code
1620krb5_ldap_unlock(krb5_context kcontext)
1621{
1622    krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1623    krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1624    return status;
1625}
1626
1627/*ARGSUSED*/
1628krb5_error_code
1629krb5_ldap_supported_realms(krb5_context kcontext, char **realms)
1630{
1631    krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1632    krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1633    return status;
1634}
1635
1636/*ARGSUSED*/
1637krb5_error_code
1638krb5_ldap_free_supported_realms(krb5_context kcontext, char **realms)
1639{
1640    krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1641    krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1642    return status;
1643}
1644
1645const char *
1646krb5_ldap_errcode_2_string(krb5_context kcontext, long err_code)
1647{
1648    return krb5_get_error_message(kcontext, err_code);
1649}
1650
1651void
1652krb5_ldap_release_errcode_string(krb5_context kcontext, const char *msg)
1653{
1654    krb5_free_error_message(kcontext, msg);
1655}
1656
1657
1658/*
1659 * Get the number of times an object has been referred to in a realm. this is
1660 * needed to find out if deleting the attribute will cause dangling links.
1661 *
1662 * An LDAP handle may be optionally specified to prevent race condition - there
1663 * are a limited number of LDAP handles.
1664 */
1665krb5_error_code
1666krb5_ldap_get_reference_count (krb5_context context, char *dn, char *refattr,
1667			       int *count, LDAP *ld)
1668{
1669    int                         st = 0, tempst = 0, gothandle = 0;
1670    unsigned int		i, ntrees;
1671    char                        *refcntattr[2];
1672    char                        *filter = NULL;
1673    char                        **subtree = NULL, *ptr = NULL;
1674    kdb5_dal_handle             *dal_handle = NULL;
1675    krb5_ldap_context           *ldap_context = NULL;
1676    krb5_ldap_server_handle     *ldap_server_handle = NULL;
1677    LDAPMessage                 *result = NULL;
1678
1679
1680    if (dn == NULL || refattr == NULL) {
1681	st = EINVAL;
1682	goto cleanup;
1683    }
1684
1685    SETUP_CONTEXT();
1686    if (ld == NULL) {
1687	GET_HANDLE();
1688	gothandle = 1;
1689    }
1690
1691    refcntattr [0] = refattr;
1692    refcntattr [1] = NULL;
1693
1694    ptr = ldap_filter_correct (dn);
1695    if (ptr == NULL) {
1696	st = ENOMEM;
1697	goto cleanup;
1698    }
1699
1700    filter = (char *) malloc (strlen (refattr) + strlen (ptr) + 2);
1701    if (filter == NULL) {
1702	st = ENOMEM;
1703	goto cleanup;
1704    }
1705
1706    /*LINTED*/
1707    sprintf (filter, "%s=%s", refattr, ptr);
1708
1709    if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees)) != 0)
1710	goto cleanup;
1711
1712    for (i = 0, *count = 0; i < ntrees; i++) {
1713	int n;
1714
1715	LDAP_SEARCH(subtree[i],
1716		    LDAP_SCOPE_SUBTREE,
1717		    filter,
1718		    refcntattr);
1719	n = ldap_count_entries (ld, result);
1720	if (n == -1) {
1721	    int ret, errcode = 0;
1722	    ret = ldap_parse_result (ld, result, &errcode, NULL, NULL, NULL, NULL, 0);
1723	    if (ret != LDAP_SUCCESS)
1724		errcode = ret;
1725	    st = translate_ldap_error (errcode, OP_SEARCH);
1726	    goto cleanup;
1727	}
1728
1729	ldap_msgfree(result);
1730	result = NULL;
1731
1732	*count += n;
1733    }
1734
1735cleanup:
1736    if (filter != NULL)
1737	free (filter);
1738
1739    if (result != NULL)
1740	ldap_msgfree (result);
1741
1742    if (subtree != NULL) {
1743	for (i = 0; i < ntrees; i++)
1744	    free (subtree[i]);
1745	free (subtree);
1746    }
1747
1748    if (ptr != NULL)
1749	free (ptr);
1750
1751    if (gothandle == 1)
1752	krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1753
1754    return st;
1755}
1756
1757/*
1758 * For now, policy objects are expected to be directly under the realm
1759 * container.
1760 */
1761krb5_error_code krb5_ldap_policydn_to_name (context, policy_dn, name)
1762    krb5_context                context;
1763    char                        *policy_dn;
1764    char                        **name;
1765{
1766    int len1, len2;
1767    krb5_error_code             st = 0;
1768    kdb5_dal_handle             *dal_handle=NULL;
1769    krb5_ldap_context           *ldap_context=NULL;
1770
1771    SETUP_CONTEXT();
1772
1773    if (ldap_context->lrparams->realmdn == NULL) {
1774	st = EINVAL;
1775	goto cleanup;
1776    }
1777
1778    len1 = strlen (ldap_context->lrparams->realmdn);
1779    len2 = strlen (policy_dn);
1780    if (len1 == 0 || len2 == 0 || len1 > len2) {
1781	st = EINVAL;
1782	goto cleanup;
1783    }
1784
1785    if (strcmp (ldap_context->lrparams->realmdn, policy_dn + (len2 - len1)) != 0) {
1786	st = EINVAL;
1787	goto cleanup;
1788    }
1789
1790#if defined HAVE_LDAP_STR2DN
1791    {
1792	char *rdn;
1793	LDAPDN dn;
1794	rdn = strndup(policy_dn, len2 - len1 - 1); /* 1 character for ',' */
1795
1796	if (ldap_str2dn (rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC) != 0) {
1797	    st = EINVAL;
1798	    goto cleanup;
1799	}
1800	if (dn[0] == NULL || dn[1] != NULL)
1801	    st = EINVAL;
1802	else if (strcasecmp (dn[0][0]->la_attr.bv_val, "cn") != 0)
1803	    st = EINVAL;
1804	else {
1805	    *name = strndup(dn[0][0]->la_value.bv_val, dn[0][0]->la_value.bv_len);
1806	    if (*name == NULL)
1807		st = EINVAL;
1808	}
1809
1810	ldap_memfree (dn);
1811    }
1812#elif defined HAVE_LDAP_EXPLODE_DN
1813    {
1814	char **parsed_dn;
1815
1816	/* 1 = return DN components without type prefix */
1817	parsed_dn = ldap_explode_dn(policy_dn, 1);
1818	if (parsed_dn == NULL) {
1819	    st = EINVAL;
1820	} else {
1821	    *name = strdup(parsed_dn[0]);
1822	    if (*name == NULL)
1823		st = EINVAL;
1824
1825	    ldap_value_free(parsed_dn);
1826	}
1827    }
1828#else
1829    st = EINVAL;
1830#endif
1831
1832cleanup:
1833    return st;
1834}
1835
1836krb5_error_code krb5_ldap_name_to_policydn (context, name, policy_dn)
1837    krb5_context                context;
1838    char                        *name;
1839    char                        **policy_dn;
1840{
1841    int                         len;
1842    char                        *ptr = NULL;
1843    krb5_error_code             st = 0;
1844    kdb5_dal_handle             *dal_handle=NULL;
1845    krb5_ldap_context           *ldap_context=NULL;
1846
1847    *policy_dn = NULL;
1848
1849    /* validate the input parameters */
1850    if (name == NULL) {
1851	st = EINVAL;
1852	goto cleanup;
1853    }
1854
1855    /* Used for removing policy reference from an object */
1856    if (name[0] == '\0') {
1857	if ((*policy_dn = strdup ("")) == NULL)
1858	    st = ENOMEM;
1859	goto cleanup;
1860    }
1861
1862    SETUP_CONTEXT();
1863
1864    if (ldap_context->lrparams->realmdn == NULL) {
1865	st = EINVAL;
1866	goto cleanup;
1867    }
1868    len = strlen (ldap_context->lrparams->realmdn);
1869
1870    ptr = ldap_filter_correct (name);
1871    if (ptr == NULL) {
1872	st = ENOMEM;
1873	goto cleanup;
1874    }
1875    len += strlen (ptr);
1876
1877    len += sizeof ("cn=") + 3;
1878
1879    *policy_dn = (char *) malloc (len);
1880    if (*policy_dn == NULL) {
1881	st = ENOMEM;
1882	goto cleanup;
1883    }
1884
1885    /*LINTED*/
1886    sprintf (*policy_dn, "cn=%s,%s", ptr, ldap_context->lrparams->realmdn);
1887
1888cleanup:
1889    if (ptr != NULL)
1890	free (ptr);
1891    return st;
1892}
1893
1894/* remove overlapping and repeated subtree entries from the list of subtrees */
1895static krb5_error_code
1896remove_overlapping_subtrees(char **listin, char **listop, int *subtcount, int sscope)
1897{
1898    int     slen=0, k=0, j=0, lendiff=0;
1899    int     count = *subtcount;
1900    char    **subtree = listop;
1901
1902    slen = count-1;
1903    for (k=0; k<=slen && listin[k]!=NULL ; k++) {
1904	for (j=k+1; j<=slen && listin[j]!=NULL ;j++) {
1905	    lendiff = strlen(listin[k]) - strlen(listin[j]);
1906	    if (sscope == 2) {
1907		if ((lendiff > 0) && (strcasecmp((listin[k])+lendiff, listin[j])==0)) {
1908		    if (k != slen) {
1909			free(listin[k]);
1910			listin[k] = listin[slen];
1911			listin[slen] = NULL;
1912		    } else {
1913			free(listin[k]);
1914			listin[k] = NULL;
1915		    }
1916		    slen-=1;
1917		    k-=1;
1918		    break;
1919		} else if ((lendiff < 0) && (strcasecmp((listin[j])+abs(lendiff), listin[k])==0)) {
1920		    if (j != slen) {
1921			free(listin[j]);
1922			listin[j] = listin[slen];
1923			listin[slen]=NULL;
1924		    } else {
1925			free(listin[j]);
1926			listin[j] = NULL;
1927		    }
1928		    slen-=1;
1929		    j-=1;
1930		}
1931	    }
1932	    if ((lendiff == 0) && (strcasecmp(listin[j], listin[k])==0)) {
1933		if (j != slen) {
1934		    free(listin[j]);
1935		    listin[j] = listin[slen];
1936		    listin[slen]=NULL;
1937		} else {
1938		    free(listin[j]);
1939		    listin[j] = NULL;
1940		}
1941		slen -=1;
1942		j-=1;
1943	    }
1944	}
1945    }
1946    *subtcount=slen+1;
1947    for (k=0; k<*subtcount && listin[k]!=NULL; k++) {
1948	subtree[k] = strdup(listin[k]);
1949	if (subtree[k] == NULL) {
1950	    return ENOMEM;
1951	}
1952    }
1953    return 0;
1954}
1955
1956/*
1957 * Fill out a krb5_db_entry princ entry struct given a LDAP message containing
1958 * the results of a principal search of the directory.
1959 */
1960krb5_error_code
1961populate_krb5_db_entry (krb5_context context,
1962			krb5_ldap_context *ldap_context,
1963			LDAP *ld,
1964			LDAPMessage *ent,
1965			krb5_const_principal princ,
1966			krb5_db_entry *entry)
1967{
1968    krb5_error_code st = 0;
1969    unsigned int    mask = 0;
1970    krb5_boolean    attr_present = FALSE;
1971    char            **values = NULL, *policydn = NULL, *pwdpolicydn = NULL;
1972    char            *polname = NULL, *tktpolname = NULL;
1973    struct berval   **bvalues = NULL;
1974    krb5_tl_data    userinfo_tl_data = {0};
1975    /* Solaris Kerberos: added next line to fix memleak */
1976    krb5_tl_data    kadm_tl_data = {NULL};
1977    char            **link_references = NULL;
1978    char *DN = NULL;
1979
1980    if (princ == NULL) {
1981	st = EINVAL;
1982	goto cleanup;
1983    } else {
1984	if ((st=krb5_copy_principal(context, princ, &(entry->princ))) != 0)
1985	    goto cleanup;
1986    }
1987    /* get the associated directory user information */
1988    if ((values = ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
1989	int i, pcount=0, kerberos_principal_object_type=0;
1990	char *user;
1991
1992	if ((st=krb5_unparse_name(context, princ, &user)) != 0)
1993	    goto cleanup;
1994
1995	for (i=0; values[i] != NULL; ++i) {
1996	    if (strcasecmp(values[i], user) == 0) {
1997		pcount = ldap_count_values(values);
1998		break;
1999	    }
2000	}
2001	ldap_value_free(values);
2002	free(user);
2003
2004	if ((DN = ldap_get_dn(ld, ent)) == NULL) {
2005	    ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &st);
2006	    st = set_ldap_error(context, st, 0);
2007	    goto cleanup;
2008	}
2009
2010	if ((values=ldap_get_values(ld, ent, "objectclass")) != NULL) {
2011	    for (i=0; values[i] != NULL; ++i)
2012		if (strcasecmp(values[i], "krbprincipal") == 0) {
2013		    kerberos_principal_object_type = KDB_STANDALONE_PRINCIPAL_OBJECT;
2014		    if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE,
2015				&kerberos_principal_object_type)) != 0)
2016			goto cleanup;
2017		    break;
2018		}
2019	    ldap_value_free(values);
2020	}
2021
2022	/* add principalcount, DN and principaltype user information to tl_data */
2023	if (((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount)) != 0) ||
2024	    ((st=store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, DN)) != 0))
2025	    goto cleanup;
2026    }
2027
2028    /* read all the kerberos attributes */
2029
2030    /* KRBLASTSUCCESSFULAUTH */
2031    if ((st=krb5_ldap_get_time(ld, ent, "krbLastSuccessfulAuth",
2032		&(entry->last_success), &attr_present)) != 0)
2033	goto cleanup;
2034    if (attr_present == TRUE)
2035	mask |= KDB_LAST_SUCCESS_ATTR;
2036
2037    /* KRBLASTFAILEDAUTH */
2038    if ((st=krb5_ldap_get_time(ld, ent, "krbLastFailedAuth",
2039		&(entry->last_failed), &attr_present)) != 0)
2040	goto cleanup;
2041    if (attr_present == TRUE)
2042	mask |= KDB_LAST_FAILED_ATTR;
2043
2044    /* KRBLOGINFAILEDCOUNT */
2045    if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount",
2046	    /* Solaris kerberos: need the cast */
2047	    (int *)&(entry->fail_auth_count)) == 0)
2048	mask |= KDB_FAIL_AUTH_COUNT_ATTR;
2049
2050    /* KRBMAXTICKETLIFE */
2051    if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &(entry->max_life)) == 0)
2052	mask |= KDB_MAX_LIFE_ATTR;
2053
2054    /* KRBMAXRENEWABLEAGE */
2055    if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage",
2056	    &(entry->max_renewable_life)) == 0)
2057	mask |= KDB_MAX_RLIFE_ATTR;
2058
2059    /* KRBTICKETFLAGS */
2060    if (krb5_ldap_get_value(ld, ent, "krbticketflags", &(entry->attributes)) == 0)
2061	mask |= KDB_TKT_FLAGS_ATTR;
2062
2063    /* PRINCIPAL EXPIRATION TIME */
2064    if ((st=krb5_ldap_get_time(ld, ent, "krbprincipalexpiration", &(entry->expiration),
2065		&attr_present)) != 0)
2066	goto cleanup;
2067    if (attr_present == TRUE)
2068	mask |= KDB_PRINC_EXPIRE_TIME_ATTR;
2069
2070    /* PASSWORD EXPIRATION TIME */
2071    if ((st=krb5_ldap_get_time(ld, ent, "krbpasswordexpiration", &(entry->pw_expiration),
2072		&attr_present)) != 0)
2073	goto cleanup;
2074    if (attr_present == TRUE)
2075	mask |= KDB_PWD_EXPIRE_TIME_ATTR;
2076
2077    /* KRBPOLICYREFERENCE */
2078
2079    if ((st=krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn,
2080		&attr_present)) != 0)
2081	goto cleanup;
2082    if (attr_present == TRUE) {
2083	mask |= KDB_POL_REF_ATTR;
2084	/* Ensure that the policy is inside the realm container */
2085	if ((st = krb5_ldap_policydn_to_name (context, policydn, &tktpolname)) != 0)
2086	    goto cleanup;
2087    }
2088
2089    /* KRBPWDPOLICYREFERENCE */
2090    if ((st=krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn,
2091		&attr_present)) != 0)
2092	goto cleanup;
2093    if (attr_present == TRUE) {
2094	/* Solaris Kerberos: changed this to fix memleak */
2095	/* krb5_tl_data  kadm_tl_data; */
2096
2097	mask |= KDB_PWD_POL_REF_ATTR;
2098
2099	/* Ensure that the policy is inside the realm container */
2100	if ((st = krb5_ldap_policydn_to_name (context, pwdpolicydn, &polname)) != 0)
2101	    goto cleanup;
2102
2103	/* Solaris Kerberos: adding support for key history in LDAP KDB */
2104	if ((st = krb5_update_tl_kadm_data(polname, &kadm_tl_data, entry->tl_data)) != 0) {
2105	    goto cleanup;
2106	}
2107	krb5_dbe_update_tl_data(context, entry, &kadm_tl_data);
2108    }
2109
2110    /* KRBSECRETKEY */
2111    if ((bvalues=ldap_get_values_len(ld, ent, "krbprincipalkey")) != NULL) {
2112	mask |= KDB_SECRET_KEY_ATTR;
2113	if ((st=krb5_decode_krbsecretkey(context, entry, bvalues)) != 0)
2114	    goto cleanup;
2115    }
2116
2117    /* LAST PASSWORD CHANGE */
2118    {
2119	krb5_timestamp lstpwdchng=0;
2120	if ((st=krb5_ldap_get_time(ld, ent, "krbLastPwdChange",
2121		    &lstpwdchng, &attr_present)) != 0)
2122	    goto cleanup;
2123	if (attr_present == TRUE) {
2124	    if ((st=krb5_dbe_update_last_pwd_change(context, entry,
2125			lstpwdchng)))
2126		goto cleanup;
2127	    mask |= KDB_LAST_PWD_CHANGE_ATTR;
2128	}
2129    }
2130
2131    /* KRBOBJECTREFERENCES */
2132    {
2133	int i=0;
2134
2135	if ((st = krb5_ldap_get_strings(ld, ent, "krbobjectreferences",
2136		    &link_references, &attr_present)) != 0)
2137	    goto cleanup;
2138	if (link_references != NULL) {
2139	    for (i=0; link_references[i] != NULL; ++i) {
2140		if ((st = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN,
2141			    link_references[i])) != 0)
2142		    goto cleanup;
2143	    }
2144	}
2145    }
2146
2147    /* Set tl_data */
2148    {
2149	int i;
2150	struct berval **ber_tl_data = NULL;
2151	krb5_tl_data *ptr = NULL;
2152
2153	if ((ber_tl_data = ldap_get_values_len (ld, ent, "krbExtraData")) != NULL) {
2154	    for (i = 0; ber_tl_data[i] != NULL; i++) {
2155		if ((st = berval2tl_data (ber_tl_data[i], &ptr)) != 0)
2156		    break;
2157		if ((st = krb5_dbe_update_tl_data(context, entry, ptr)) != 0)
2158		    break;
2159		/* Solaris kerberos: fix memory leak */
2160		if (ptr) {
2161		    if (ptr->tl_data_contents)
2162			free(ptr->tl_data_contents);
2163		    free(ptr);
2164		    ptr = NULL;
2165		}
2166	    }
2167	    ldap_value_free_len (ber_tl_data);
2168	    if (st != 0)
2169		goto cleanup;
2170	    mask |= KDB_EXTRA_DATA_ATTR;
2171	}
2172    }
2173
2174    /* update the mask of attributes present on the directory object to the tl_data */
2175    if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask)) != 0)
2176	goto cleanup;
2177    if ((st=krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data)) != 0)
2178	goto cleanup;
2179
2180#ifdef HAVE_EDIRECTORY
2181    {
2182	krb5_timestamp              expiretime=0;
2183	char                        *is_login_disabled=NULL;
2184
2185	/* LOGIN EXPIRATION TIME */
2186	if ((st=krb5_ldap_get_time(ld, ent, "loginexpirationtime", &expiretime,
2187		    &attr_present)) != 0)
2188	    goto cleanup;
2189
2190	if (attr_present == TRUE) {
2191	    if ((mask & KDB_PRINC_EXPIRE_TIME_ATTR) == 1) {
2192		if (expiretime < entry->expiration)
2193		    entry->expiration = expiretime;
2194	    } else {
2195		entry->expiration = expiretime;
2196	    }
2197	}
2198
2199	/* LOGIN DISABLED */
2200	if ((st=krb5_ldap_get_string(ld, ent, "logindisabled", &is_login_disabled,
2201		    &attr_present)) != 0)
2202	    goto cleanup;
2203	if (attr_present == TRUE) {
2204	    if (strcasecmp(is_login_disabled, "TRUE")== 0)
2205		entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
2206	    free (is_login_disabled);
2207	}
2208    }
2209#endif
2210
2211    if ((st=krb5_read_tkt_policy (context, ldap_context, entry, tktpolname)) !=0)
2212	goto cleanup;
2213
2214    /* We already know that the policy is inside the realm container. */
2215    if (polname) {
2216	osa_policy_ent_t   pwdpol;
2217	int                cnt=0;
2218	krb5_timestamp     last_pw_changed;
2219	krb5_ui_4          pw_max_life;
2220
2221	memset(&pwdpol, 0, sizeof(pwdpol));
2222
2223	if ((st=krb5_ldap_get_password_policy(context, polname, &pwdpol, &cnt)) != 0)
2224	    goto cleanup;
2225	pw_max_life = pwdpol->pw_max_life;
2226	/* Solaris Kerberos: fix memory leak */
2227	krb5_ldap_free_password_policy(context, pwdpol);
2228
2229	if (pw_max_life > 0) {
2230	    if ((st=krb5_dbe_lookup_last_pwd_change(context, entry, &last_pw_changed)) != 0)
2231		goto cleanup;
2232
2233	    if ((mask & KDB_PWD_EXPIRE_TIME_ATTR) == 1) {
2234		if ((last_pw_changed + pw_max_life) < entry->pw_expiration)
2235		    entry->pw_expiration = last_pw_changed + pw_max_life;
2236	    } else
2237		entry->pw_expiration = last_pw_changed + pw_max_life;
2238	}
2239    }
2240    /* XXX so krb5_encode_princ_contents() will be happy */
2241    entry->len = KRB5_KDB_V1_BASE_LENGTH;
2242
2243cleanup:
2244
2245    if (DN != NULL)
2246	ldap_memfree(DN);
2247
2248    if (userinfo_tl_data.tl_data_contents != NULL)
2249	free(userinfo_tl_data.tl_data_contents);
2250
2251    /* Solaris Kerberos: added this to fix memleak */
2252    if (kadm_tl_data.tl_data_contents != NULL)
2253	free(kadm_tl_data.tl_data_contents);
2254
2255    if (pwdpolicydn != NULL)
2256	free(pwdpolicydn);
2257
2258    if (polname != NULL)
2259	free(polname);
2260
2261    if (tktpolname != NULL)
2262	free (tktpolname);
2263
2264    if (policydn != NULL)
2265	free(policydn);
2266
2267    if (link_references) {
2268        int i;
2269        for (i=0; link_references[i] != NULL; ++i)
2270            free (link_references[i]);
2271        free (link_references);
2272    }
2273
2274    return (st);
2275}
2276
2277/*
2278 * Solaris libldap does not provide the following functions which are in
2279 * OpenLDAP.  Note, Solaris Kerberos added the use_SSL to do a SSL init.  Also
2280 * added errstr to return specific error if it isn't NULL.  Yes, this is ugly
2281 * and no, the errstr should not be free()'ed.
2282 */
2283#ifndef HAVE_LDAP_INITIALIZE
2284int
2285ldap_initialize(LDAP **ldp, char *url, int use_SSL, char **errstr)
2286{
2287    int rc = LDAP_SUCCESS;
2288    LDAP *ld = NULL;
2289    LDAPURLDesc *ludp = NULL;
2290
2291    /* For now, we don't use any DN that may be provided.  And on
2292       Solaris (based on Mozilla's LDAP client code), we need the
2293       _nodn form to parse "ldap://host" without a trailing slash.
2294
2295       Also, this version won't handle an input string which contains
2296       multiple URLs, unlike the OpenLDAP ldap_initialize.  See
2297       https://bugzilla.mozilla.org/show_bug.cgi?id=353336#c1 .  */
2298
2299    /* to avoid reinit and leaking handles, *ldp must be NULL */
2300    if (*ldp != NULL)
2301	return LDAP_SUCCESS;
2302
2303#ifdef HAVE_LDAP_URL_PARSE_NODN
2304    rc = ldap_url_parse_nodn(url, &ludp);
2305#else
2306    rc = ldap_url_parse(url, &ludp);
2307#endif
2308    if (rc == 0) {
2309	if (use_SSL == SSL_ON)
2310	    ld = ldapssl_init(ludp->lud_host, ludp->lud_port, 1);
2311	else
2312	    ld = ldap_init(ludp->lud_host, ludp->lud_port);
2313
2314	if (ld != NULL)
2315	    *ldp = ld;
2316	else {
2317	    if (errstr != NULL)
2318		*errstr = strerror(errno);
2319	    rc = LDAP_OPERATIONS_ERROR;
2320	}
2321
2322	ldap_free_urldesc(ludp);
2323    } else {
2324	/* report error from ldap url parsing */
2325	if (errstr != NULL)
2326	    *errstr = ldap_err2string(rc);
2327	/* convert to generic LDAP error */
2328	rc = LDAP_OPERATIONS_ERROR;
2329    }
2330    return rc;
2331}
2332#endif /* HAVE_LDAP_INITIALIZE */
2333
2334#ifndef HAVE_LDAP_UNBIND_EXT_S
2335int
2336ldap_unbind_ext_s(LDAP *ld, LDAPControl **sctrls, LDAPControl **cctrls)
2337{
2338    return ldap_unbind_ext(ld, sctrls, cctrls);
2339}
2340#endif /* HAVE_LDAP_UNBIND_EXT_S */
2341