1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
2 /*
3  * lib/kdb/kdb_ldap/ldap_misc.c
4  *
5  * Copyright (c) 2004-2005, Novell, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  *   * Redistributions of source code must retain the above copyright notice,
12  *       this list of conditions and the following disclaimer.
13  *   * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in the
15  *       documentation and/or other materials provided with the distribution.
16  *   * The copyright holder's name is not used to endorse or promote products
17  *       derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*
32  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
33  * Use is subject to license terms.
34  */
35 #include <string.h>
36 #include <time.h>
37 #include <k5-platform.h>
38 #include "ldap_main.h"
39 #include "ldap_err.h"
40 #include "ldap_principal.h"
41 #include "princ_xdr.h"
42 #include "ldap_pwd_policy.h"
43 #include <libintl.h>
44 
45 #ifdef NEED_STRPTIME_PROTO
46 extern char *strptime (const char *, const char *, struct tm *);
47 #endif
48 
49 static krb5_error_code
50 remove_overlapping_subtrees(char **listin, char **listop, int *subtcount,
51 			    int sscope);
52 
53 /* Linux (GNU Libc) provides a length-limited variant of strdup.
54    But all the world's not Linux.  */
55 #undef strndup
56 #define strndup my_strndup
57 #ifdef HAVE_LDAP_STR2DN
58 static char *my_strndup (const char *input, size_t limit)
59 {
60     size_t len = strlen(input);
61     char *result;
62     if (len > limit) {
63 	result = malloc(1 + limit);
64 	if (result != NULL) {
65 	    memcpy(result, input, limit);
66 	    result[limit] = 0;
67 	}
68 	return result;
69     } else
70 	return strdup(input);
71 }
72 #endif
73 
74 /* Get integer or string values from the config section, falling back
75    to the default section, then to hard-coded values.  */
76 static errcode_t
77 prof_get_integer_def(krb5_context ctx, const char *conf_section,
78 		     const char *name, int dfl, krb5_ui_4 *out)
79 {
80     errcode_t err;
81     int out_temp = 0;
82 
83     err = profile_get_integer (ctx->profile,
84 			       KDB_MODULE_SECTION, conf_section, name,
85 			       0, &out_temp);
86     if (err) {
87 	krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
88 				name, error_message(err));
89 	return err;
90     }
91     if (out_temp != 0) {
92 	*out = out_temp;
93 	return 0;
94     }
95     err = profile_get_integer (ctx->profile,
96 			       KDB_MODULE_DEF_SECTION, name, 0,
97 			       dfl, &out_temp);
98     if (err) {
99 	krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
100 				name, error_message(err));
101 	return err;
102     }
103     *out = out_temp;
104     return 0;
105 }
106 
107 /* We don't have non-null defaults in any of our calls, so don't
108    bother with the extra argument.  */
109 static errcode_t
110 prof_get_string_def(krb5_context ctx, const char *conf_section,
111 		    const char *name, char **out)
112 {
113     errcode_t err;
114 
115     err = profile_get_string (ctx->profile,
116 			      KDB_MODULE_SECTION, conf_section, name,
117 			      0, out);
118     if (err) {
119 	krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
120 				name, error_message(err));
121 	return err;
122     }
123     if (*out != 0)
124 	return 0;
125     err = profile_get_string (ctx->profile,
126 			      KDB_MODULE_DEF_SECTION, name, 0,
127 			      0, out);
128     if (err) {
129 	krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
130 				name, error_message(err));
131 	return err;
132     }
133     return 0;
134 }
135 
136 
137 
138 /*
139  * This function reads the parameters from the krb5.conf file. The
140  * parameters read here are DAL-LDAP specific attributes. Some of
141  * these are ldap_server ....
142  */
143 krb5_error_code
144 krb5_ldap_read_server_params(context, conf_section, srv_type)
145     krb5_context               context;
146     char                       *conf_section;
147     int                        srv_type;
148 {
149     char                        *tempval=NULL, *save_ptr=NULL;
150     const char                  *delims="\t\n\f\v\r ,";
151     krb5_error_code             st=0;
152     kdb5_dal_handle             *dal_handle=NULL;
153     krb5_ldap_context           *ldap_context=NULL;
154     krb5_ldap_server_info       ***server_info=NULL;
155 
156     dal_handle = (kdb5_dal_handle *) context->db_context;
157     ldap_context = (krb5_ldap_context *) dal_handle->db_context;
158 
159     /* copy the conf_section into ldap_context for later use */
160     if (conf_section) {
161 	ldap_context->conf_section = strdup (conf_section);
162 	if (ldap_context->conf_section == NULL) {
163 	    st = ENOMEM;
164 	    goto cleanup;
165 	}
166     }
167 
168     /* initialize the mutexs and condition variable */
169     /* this portion logically doesn't fit here should be moved appropriately */
170 
171     /* this mutex is used in ldap reconnection pool */
172     if (k5_mutex_init(&(ldap_context->hndl_lock)) != 0) {
173 	st = KRB5_KDB_SERVER_INTERNAL_ERR;
174 #if 0
175 	st = -1;
176 	krb5_ldap_dal_err_funcp(context, krb5_err_have_str, st,
177 				"k5_mutex_init failed");
178 #endif
179 	goto cleanup;
180     }
181 
182     /*
183      * If max_server_conns is not set read it from database module
184      * section of conf file this parameter defines maximum ldap
185      * connections per ldap server.
186      */
187     if (ldap_context->max_server_conns == 0) {
188 	st = prof_get_integer_def (context, conf_section,
189 				   "ldap_conns_per_server",
190 				   DEFAULT_CONNS_PER_SERVER,
191 				   &ldap_context->max_server_conns);
192 	if (st)
193 	    goto cleanup;
194     }
195 
196     if (ldap_context->max_server_conns < 2) {
197 	st = EINVAL;
198 	krb5_set_error_message (context, st,
199 				gettext("Minimum connections required per server is 2"));
200 	goto cleanup;
201     }
202 
203     /*
204      * If the bind dn is not set read it from the database module
205      * section of conf file this paramter is populated by one of the
206      * KDC, ADMIN or PASSWD dn to be used to connect to LDAP
207      * server.  The srv_type decides which dn to read.
208      */
209     if (ldap_context->bind_dn == NULL) {
210 	char *name = 0;
211 	if (srv_type == KRB5_KDB_SRV_TYPE_KDC)
212 	    name = "ldap_kdc_dn";
213 	else if (srv_type == KRB5_KDB_SRV_TYPE_ADMIN)
214 	    name = "ldap_kadmind_dn";
215 	else if (srv_type == KRB5_KDB_SRV_TYPE_PASSWD)
216 	    name = "ldap_kpasswdd_dn";
217 
218 	if (name) {
219 	    st = prof_get_string_def (context, conf_section, name,
220 				      &ldap_context->bind_dn);
221 	    if (st)
222 		goto cleanup;
223 	}
224     }
225 
226     /*
227      * Read service_password_file parameter from database module
228      * section of conf file this file contains stashed passwords of
229      * the KDC, ADMIN and PASSWD dns.
230      */
231     if (ldap_context->service_password_file == NULL) {
232 	/*
233 	 * Solaris Kerberos: providing a default.
234 	 */
235 	st = profile_get_string (context->profile, KDB_MODULE_SECTION,
236 				   conf_section,
237 				  "ldap_service_password_file",
238 				  NULL,
239 				  &ldap_context->service_password_file);
240 
241 	if (st)
242 	    goto cleanup;
243 
244 	if (ldap_context->service_password_file == NULL) {
245 	    st = profile_get_string (context->profile, KDB_MODULE_DEF_SECTION,
246 				      "ldap_service_password_file",
247 				      NULL,
248 				      DEF_SERVICE_PASSWD_FILE,
249 				      &ldap_context->service_password_file);
250 	    if (st)
251 		goto cleanup;
252 	}
253     }
254 
255 /*
256  * Solaris Kerberos: we must use root_certificate_file
257  *
258  * Note, I've changed the ldap_root_certificate_file config parameter to
259  * ldap_cert_path which is more appropriate for that parameter.
260  */
261 /* #ifdef HAVE_EDIRECTORY */
262     /*
263      * If root certificate file is not set read it from database
264      * module section of conf file this is the trusted root
265      * certificate of the Directory.
266      */
267     if (ldap_context->root_certificate_file == NULL) {
268 	st = prof_get_string_def (context, conf_section,
269 				  "ldap_cert_path",
270 				  &ldap_context->root_certificate_file);
271 	if (st)
272 	    goto cleanup;
273     }
274 /* #endif */
275 
276     /*
277      * If the ldap server parameter is not set read the list of ldap
278      * servers from the database module section of the conf file.
279      */
280 
281     if (ldap_context->server_info_list == NULL) {
282 	unsigned int ele=0;
283 
284 	server_info = &(ldap_context->server_info_list);
285 	*server_info = (krb5_ldap_server_info **) calloc (SERV_COUNT+1,
286 							  sizeof (krb5_ldap_server_info *));
287 
288 	if (*server_info == NULL) {
289 	    st = ENOMEM;
290 	    goto cleanup;
291 	}
292 
293 	if ((st=profile_get_string(context->profile, KDB_MODULE_SECTION, conf_section,
294 				   "ldap_servers", NULL, &tempval)) != 0) {
295 	    krb5_set_error_message (context, st, gettext("Error reading 'ldap_servers' attribute"));
296 	    goto cleanup;
297 	}
298 
299 	if (tempval == NULL) {
300 
301 	    (*server_info)[ele] = (krb5_ldap_server_info *)calloc(1,
302 								  sizeof(krb5_ldap_server_info));
303 
304 	    if ((*server_info)[ele] == NULL) {
305 		st = ENOMEM;
306 		goto cleanup;
307 	    }
308 	    (*server_info)[ele]->server_name = strdup("ldapi://");
309 	    if ((*server_info)[ele]->server_name == NULL) {
310 		st = ENOMEM;
311 		goto cleanup;
312 	    }
313 	    (*server_info)[ele]->server_status = NOTSET;
314 	} else {
315 	    char *item=NULL;
316 
317 	    item = strtok_r(tempval,delims,&save_ptr);
318 	    while (item != NULL && ele<SERV_COUNT) {
319 		(*server_info)[ele] = (krb5_ldap_server_info *)calloc(1,
320 								      sizeof(krb5_ldap_server_info));
321 		if ((*server_info)[ele] == NULL) {
322 		    st = ENOMEM;
323 		    goto cleanup;
324 		}
325 		(*server_info)[ele]->server_name = strdup(item);
326 		if ((*server_info)[ele]->server_name == NULL) {
327 		    st = ENOMEM;
328 		    goto cleanup;
329 		}
330 
331 		(*server_info)[ele]->server_status = NOTSET;
332 		item = strtok_r(NULL,delims,&save_ptr);
333 		++ele;
334 	    }
335 	    profile_release_string(tempval);
336 	}
337     }
338 
339 cleanup:
340     return(st);
341 }
342 
343 /*
344  * This function frees the krb5_ldap_context structure members.
345  */
346 
347 krb5_error_code
348 krb5_ldap_free_server_params(ldap_context)
349     krb5_ldap_context           *ldap_context;
350 {
351     int                         i=0;
352     krb5_ldap_server_handle     *ldap_server_handle=NULL, *next_ldap_server_handle=NULL;
353 
354     if (ldap_context == NULL)
355 	return 0;
356 
357     /* Free all ldap servers list and the ldap handles associated with
358        the ldap server.  */
359     if (ldap_context->server_info_list) {
360 	while (ldap_context->server_info_list[i]) {
361 	    if (ldap_context->server_info_list[i]->server_name) {
362 		free (ldap_context->server_info_list[i]->server_name);
363 	    }
364 #ifdef HAVE_EDIRECTORY
365 	    if (ldap_context->server_info_list[i]->root_certificate_file) {
366 		free (ldap_context->server_info_list[i]->root_certificate_file);
367 	    }
368 #endif
369 	    if (ldap_context->server_info_list[i]->ldap_server_handles) {
370 		ldap_server_handle = ldap_context->server_info_list[i]->ldap_server_handles;
371 		while (ldap_server_handle) {
372 		    ldap_unbind_ext_s(ldap_server_handle->ldap_handle, NULL, NULL);
373 		    ldap_server_handle->ldap_handle = NULL;
374 		    next_ldap_server_handle = ldap_server_handle->next;
375 		    krb5_xfree(ldap_server_handle);
376 		    ldap_server_handle = next_ldap_server_handle;
377 		}
378 	    }
379 	    krb5_xfree(ldap_context->server_info_list[i]);
380 	    i++;
381 	}
382 	krb5_xfree(ldap_context->server_info_list);
383     }
384 
385     if (ldap_context->conf_section != NULL) {
386 	krb5_xfree(ldap_context->conf_section);
387 	ldap_context->conf_section = NULL;
388     }
389 
390     if (ldap_context->bind_dn != NULL) {
391 	krb5_xfree(ldap_context->bind_dn);
392 	ldap_context->bind_dn = NULL;
393     }
394 
395     if (ldap_context->bind_pwd != NULL) {
396 	krb5_xfree(ldap_context->bind_pwd);
397 	ldap_context->bind_pwd = NULL;
398     }
399 
400     if (ldap_context->service_password_file != NULL) {
401 	krb5_xfree(ldap_context->service_password_file);
402 	ldap_context->service_password_file = NULL;
403     }
404 
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 
447 krb5_error_code
448 is_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  */
508 krb5_error_code
509 krb5_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 
572 cleanup:
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 
596 krb5_error_code
597 store_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 
689 krb5_error_code
690 decode_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  */
809 static krb5_error_code
810 krb5_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 
831 cleanup:
832     return st;
833 }
834 
835 /*
836  * Get the mask representing the attributes set on the directory
837  * object (user, policy ...).
838  */
839 krb5_error_code
840 krb5_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 
849 krb5_error_code
850 krb5_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 
858 krb5_error_code
859 krb5_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 
867 krb5_error_code
868 krb5_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 
886 cleanup:
887     return st;
888 }
889 
890 static krb5_error_code
891 krb5_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 
914 cleanup:
915     return st;
916 }
917 
918 krb5_error_code
919 krb5_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 
928 krb5_error_code
929 krb5_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 
953 krb5_error_code
954 checkattributevalue (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 
1019 cleanup:
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 
1033 krb5_error_code
1034 updateAttribute (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 
1078 krb5_error_code
1079 deleteAttribute (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 
1123 krb5_error_code
1124 disjoint_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 
1213 krb5_error_code
1214 copy_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 
1242 cleanup:
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 
1257 static krb5_error_code
1258 getepochtime(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     /* Solaris kerberos: don't have krb5int_gmt_mktime at this point */
1270 #if 0 /************** Begin IFDEF'ed OUT *******************************/
1271     *epochtime = krb5int_gmt_mktime(&tme);
1272 #else
1273     *epochtime = gmt_mktime(&tme);
1274 #endif /**************** END IFDEF'ed OUT *******************************/
1275     return 0;
1276 }
1277 
1278 /*
1279  * krb5_ldap_get_value() - get the integer value of the attribute
1280  * Returns, 0 if the attribute is present, 1 if the attribute is missing.
1281  * The retval is 0 if the attribute is missing.
1282  */
1283 
1284 krb5_error_code
1285 krb5_ldap_get_value(ld, ent, attribute, retval)
1286     LDAP                        *ld;
1287     LDAPMessage                 *ent;
1288     char                        *attribute;
1289     int                         *retval;
1290 {
1291     char                           **values=NULL;
1292 
1293     *retval = 0;
1294     values=ldap_get_values(ld, ent, attribute);
1295     if (values != NULL) {
1296 	if (values[0] != NULL)
1297 	    *retval = atoi(values[0]);
1298 	ldap_value_free(values);
1299 	return 0;
1300     }
1301     return 1;
1302 }
1303 
1304 /*
1305  * krb5_ldap_get_string() - Returns the first string of the
1306  * attribute.  Intended to
1307  *
1308  *
1309  */
1310 krb5_error_code
1311 krb5_ldap_get_string(ld, ent, attribute, retstr, attr_present)
1312     LDAP                        *ld;
1313     LDAPMessage                 *ent;
1314     char                        *attribute;
1315     char                        **retstr;
1316     krb5_boolean                *attr_present;
1317 {
1318     char                           **values=NULL;
1319     krb5_error_code                st=0;
1320 
1321     *retstr = NULL;
1322     if (attr_present != NULL)
1323 	*attr_present = FALSE;
1324 
1325     values=ldap_get_values(ld, ent, attribute);
1326     if (values != NULL) {
1327 	if (values[0] != NULL) {
1328 	    if (attr_present!= NULL)
1329 		*attr_present = TRUE;
1330 	    *retstr = strdup(values[0]);
1331 	    if (*retstr == NULL)
1332 		st = ENOMEM;
1333 	}
1334 	ldap_value_free(values);
1335     }
1336     return st;
1337 }
1338 
1339 /*
1340  * krb5_ldap_get_strings() - Returns all the values
1341  * of the attribute.
1342  */
1343 krb5_error_code
1344 krb5_ldap_get_strings(ld, ent, attribute, retarr, attr_present)
1345     LDAP                        *ld;
1346     LDAPMessage                 *ent;
1347     char                        *attribute;
1348     char                        ***retarr;
1349     krb5_boolean                *attr_present;
1350 {
1351     char                        **values=NULL;
1352     krb5_error_code             st=0;
1353     unsigned int                i=0, count=0;
1354 
1355     *retarr = NULL;
1356     if (attr_present != NULL)
1357 	*attr_present = FALSE;
1358 
1359     values=ldap_get_values(ld, ent, attribute);
1360     if (values != NULL) {
1361 	if (attr_present != NULL)
1362 	    *attr_present = TRUE;
1363 
1364 	count = ldap_count_values(values);
1365 	*retarr  = (char **) calloc(count+1, sizeof(char *));
1366 	if (*retarr == NULL) {
1367 	    st = ENOMEM;
1368 	    return st;
1369 	}
1370 	for (i=0; i< count; ++i) {
1371 	    (*retarr)[i] = strdup(values[i]);
1372 	    if ((*retarr)[i] == NULL) {
1373 		st = ENOMEM;
1374 		goto cleanup;
1375 	    }
1376 	}
1377 	ldap_value_free(values);
1378     }
1379 
1380 cleanup:
1381     if (st != 0) {
1382 	if (*retarr != NULL) {
1383 	    for (i=0; i< count; ++i)
1384 		if ((*retarr)[i] != NULL)
1385 		    free ((*retarr)[i]);
1386 	    free (*retarr);
1387 	}
1388     }
1389     return st;
1390 }
1391 
1392 krb5_error_code
1393 krb5_ldap_get_time(ld, ent, attribute, rettime, attr_present)
1394     LDAP                        *ld;
1395     LDAPMessage                 *ent;
1396     char                        *attribute;
1397     krb5_timestamp              *rettime;
1398     krb5_boolean                *attr_present;
1399 {
1400     char                         **values=NULL;
1401     krb5_error_code              st=0;
1402 
1403     *rettime = 0;
1404     *attr_present = FALSE;
1405 
1406     values=ldap_get_values(ld, ent, attribute);
1407     if (values != NULL) {
1408 	if (values[0] != NULL) {
1409 	    *attr_present = TRUE;
1410 	    st = getepochtime(values[0], rettime);
1411 	}
1412 	ldap_value_free(values);
1413     }
1414     return st;
1415 }
1416 
1417 /*
1418  * Function to allocate, set the values of LDAPMod structure. The
1419  * LDAPMod structure is then added to the array at the ind
1420  */
1421 
1422 krb5_error_code
1423 krb5_add_member(mods, count)
1424     LDAPMod          ***mods;
1425     int              *count;
1426 {
1427     int i=0;
1428     LDAPMod **lmods=NULL;
1429 
1430     if ((*mods) != NULL) {
1431 	for (;(*mods)[i] != NULL; ++i)
1432 	    ;
1433     }
1434     lmods = (LDAPMod **) realloc((*mods), (2+i) * sizeof(LDAPMod *));
1435     if (lmods == NULL)
1436 	return ENOMEM;
1437 
1438     *mods = lmods;
1439     (*mods)[i+1] = NULL;
1440     (*mods)[i] = (LDAPMod *) calloc(1, sizeof (LDAPMod));
1441     if ((*mods)[i] == NULL) {
1442 	free(lmods);
1443 	*mods = NULL;
1444 	return ENOMEM;
1445     }
1446     *count = i;
1447     return 0;
1448 }
1449 
1450 krb5_error_code
1451 krb5_add_str_mem_ldap_mod(mods, attribute, op, values)
1452     LDAPMod  ***mods;
1453     char     *attribute;
1454     int      op;
1455     char     **values;
1456 
1457 {
1458     int i=0, j=0;
1459     krb5_error_code   st=0;
1460 
1461     if ((st=krb5_add_member(mods, &i)) != 0)
1462 	return st;
1463 
1464     (*mods)[i]->mod_type = strdup(attribute);
1465     if ((*mods)[i]->mod_type == NULL)
1466 	return ENOMEM;
1467     (*mods)[i]->mod_op = op;
1468 
1469     (*mods)[i]->mod_values = NULL;
1470 
1471     if (values != NULL) {
1472 	for (j=0; values[j] != NULL; ++j)
1473 	    ;
1474 	(*mods)[i]->mod_values = malloc (sizeof(char *) * (j+1));
1475 	if ((*mods)[i]->mod_values == NULL) {
1476 	    free((*mods)[i]->mod_type);
1477 	    (*mods)[i]->mod_type = NULL;
1478 	    return ENOMEM;
1479 	}
1480 
1481 	for (j=0; values[j] != NULL; ++j) {
1482 	    (*mods)[i]->mod_values[j] = strdup(values[j]);
1483 	    if ((*mods)[i]->mod_values[j] == NULL){
1484 		int k=0;
1485 		for (; k<j; k++) {
1486 		    free((*mods)[i]->mod_values[k]);
1487 		    (*mods)[i]->mod_values[k] = NULL;
1488 		}
1489 		return ENOMEM;
1490 	    }
1491 	}
1492 	(*mods)[i]->mod_values[j] = NULL;
1493     }
1494     return 0;
1495 }
1496 
1497 krb5_error_code
1498 krb5_add_ber_mem_ldap_mod(mods, attribute, op, ber_values)
1499     LDAPMod  ***mods;
1500     char     *attribute;
1501     int      op;
1502     struct berval **ber_values;
1503 
1504 {
1505     int i=0, j=0;
1506     krb5_error_code   st=0;
1507 
1508     if ((st=krb5_add_member(mods, &i)) != 0)
1509 	return st;
1510 
1511     (*mods)[i]->mod_type = strdup(attribute);
1512     if ((*mods)[i]->mod_type == NULL)
1513 	return ENOMEM;
1514     (*mods)[i]->mod_op = op;
1515 
1516     for (j=0; ber_values[j] != NULL; ++j)
1517 	;
1518     (*mods)[i]->mod_bvalues = malloc (sizeof(struct berval *) * (j+1));
1519     if ((*mods)[i]->mod_bvalues == NULL)
1520 	return ENOMEM;
1521 
1522     for (j=0; ber_values[j] != NULL; ++j) {
1523 	(*mods)[i]->mod_bvalues[j] = calloc(1, sizeof(struct berval));
1524 	if ((*mods)[i]->mod_bvalues[j] == NULL)
1525 	    return ENOMEM;
1526 
1527 	(*mods)[i]->mod_bvalues[j]->bv_len = ber_values[j]->bv_len;
1528 	(*mods)[i]->mod_bvalues[j]->bv_val = malloc((*mods)[i]->mod_bvalues[j]->bv_len);
1529 	if ((*mods)[i]->mod_bvalues[j]->bv_val == NULL)
1530 	    return ENOMEM;
1531 
1532 	memcpy((*mods)[i]->mod_bvalues[j]->bv_val, ber_values[j]->bv_val,
1533 	       ber_values[j]->bv_len);
1534     }
1535     (*mods)[i]->mod_bvalues[j] = NULL;
1536     return 0;
1537 }
1538 
1539 static inline char *
1540 format_d (int val)
1541 {
1542     char tmpbuf[2+3*sizeof(val)];
1543     sprintf(tmpbuf, "%d", val);
1544     return strdup(tmpbuf);
1545 }
1546 
1547 krb5_error_code
1548 krb5_add_int_arr_mem_ldap_mod(mods, attribute, op, value)
1549     LDAPMod  ***mods;
1550     char     *attribute;
1551     int      op;
1552     int      *value;
1553 
1554 {
1555     int i=0, j=0;
1556     krb5_error_code   st=0;
1557 
1558     if ((st=krb5_add_member(mods, &i)) != 0)
1559 	return st;
1560 
1561     (*mods)[i]->mod_type = strdup(attribute);
1562     if ((*mods)[i]->mod_type == NULL)
1563 	return ENOMEM;
1564     (*mods)[i]->mod_op = op;
1565 
1566     for (j=0; value[j] != -1; ++j)
1567 	;
1568 
1569     (*mods)[i]->mod_values = malloc(sizeof(char *) * (j+1));
1570 
1571     for (j=0; value[j] != -1; ++j) {
1572 	if (((*mods)[i]->mod_values[j] = format_d(value[j])) == NULL)
1573 	    return ENOMEM;
1574     }
1575     (*mods)[i]->mod_values[j] = NULL;
1576     return 0;
1577 }
1578 
1579 krb5_error_code
1580 krb5_add_int_mem_ldap_mod(mods, attribute, op, value)
1581     LDAPMod  ***mods;
1582     char     *attribute;
1583     int      op;
1584     int      value;
1585 
1586 {
1587     int i=0;
1588     krb5_error_code      st=0;
1589 
1590     if ((st=krb5_add_member(mods, &i)) != 0)
1591 	return st;
1592 
1593     (*mods)[i]->mod_type = strdup(attribute);
1594     if ((*mods)[i]->mod_type == NULL)
1595 	return ENOMEM;
1596 
1597     (*mods)[i]->mod_op = op;
1598     (*mods)[i]->mod_values = calloc (2, sizeof(char *));
1599     if (((*mods)[i]->mod_values[0] = format_d(value)) == NULL)
1600 	return ENOMEM;
1601     return 0;
1602 }
1603 
1604 /*ARGSUSED*/
1605 krb5_error_code
1606 krb5_ldap_set_option(krb5_context kcontext, int option, void *value)
1607 {
1608     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1609     krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1610     return status;
1611 }
1612 
1613 /*ARGSUSED*/
1614 krb5_error_code
1615 krb5_ldap_lock(krb5_context kcontext, int mode)
1616 {
1617     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1618     krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1619     return status;
1620 }
1621 
1622 krb5_error_code
1623 krb5_ldap_unlock(krb5_context kcontext)
1624 {
1625     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1626     krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1627     return status;
1628 }
1629 
1630 /*ARGSUSED*/
1631 krb5_error_code
1632 krb5_ldap_supported_realms(krb5_context kcontext, char **realms)
1633 {
1634     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1635     krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1636     return status;
1637 }
1638 
1639 /*ARGSUSED*/
1640 krb5_error_code
1641 krb5_ldap_free_supported_realms(krb5_context kcontext, char **realms)
1642 {
1643     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1644     krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1645     return status;
1646 }
1647 
1648 const char *
1649 krb5_ldap_errcode_2_string(krb5_context kcontext, long err_code)
1650 {
1651     return krb5_get_error_message(kcontext, err_code);
1652 }
1653 
1654 void
1655 krb5_ldap_release_errcode_string(krb5_context kcontext, const char *msg)
1656 {
1657     krb5_free_error_message(kcontext, msg);
1658 }
1659 
1660 
1661 /*
1662  * Get the number of times an object has been referred to in a realm. this is
1663  * needed to find out if deleting the attribute will cause dangling links.
1664  *
1665  * An LDAP handle may be optionally specified to prevent race condition - there
1666  * are a limited number of LDAP handles.
1667  */
1668 krb5_error_code
1669 krb5_ldap_get_reference_count (krb5_context context, char *dn, char *refattr,
1670 			       int *count, LDAP *ld)
1671 {
1672     int                         st = 0, tempst = 0, gothandle = 0;
1673     unsigned int		i, ntrees;
1674     char                        *refcntattr[2];
1675     char                        *filter = NULL;
1676     char                        **subtree = NULL, *ptr = NULL;
1677     kdb5_dal_handle             *dal_handle = NULL;
1678     krb5_ldap_context           *ldap_context = NULL;
1679     krb5_ldap_server_handle     *ldap_server_handle = NULL;
1680     LDAPMessage                 *result = NULL;
1681 
1682 
1683     if (dn == NULL || refattr == NULL) {
1684 	st = EINVAL;
1685 	goto cleanup;
1686     }
1687 
1688     SETUP_CONTEXT();
1689     if (ld == NULL) {
1690 	GET_HANDLE();
1691 	gothandle = 1;
1692     }
1693 
1694     refcntattr [0] = refattr;
1695     refcntattr [1] = NULL;
1696 
1697     ptr = ldap_filter_correct (dn);
1698     if (ptr == NULL) {
1699 	st = ENOMEM;
1700 	goto cleanup;
1701     }
1702 
1703     filter = (char *) malloc (strlen (refattr) + strlen (ptr) + 2);
1704     if (filter == NULL) {
1705 	st = ENOMEM;
1706 	goto cleanup;
1707     }
1708 
1709     /*LINTED*/
1710     sprintf (filter, "%s=%s", refattr, ptr);
1711 
1712     if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees)) != 0)
1713 	goto cleanup;
1714 
1715     for (i = 0, *count = 0; i < ntrees; i++) {
1716 	int n;
1717 
1718 	LDAP_SEARCH(subtree[i],
1719 		    LDAP_SCOPE_SUBTREE,
1720 		    filter,
1721 		    refcntattr);
1722 	n = ldap_count_entries (ld, result);
1723 	if (n == -1) {
1724 	    int ret, errcode = 0;
1725 	    ret = ldap_parse_result (ld, result, &errcode, NULL, NULL, NULL, NULL, 0);
1726 	    if (ret != LDAP_SUCCESS)
1727 		errcode = ret;
1728 	    st = translate_ldap_error (errcode, OP_SEARCH);
1729 	    goto cleanup;
1730 	}
1731 
1732 	ldap_msgfree(result);
1733 	result = NULL;
1734 
1735 	*count += n;
1736     }
1737 
1738 cleanup:
1739     if (filter != NULL)
1740 	free (filter);
1741 
1742     if (result != NULL)
1743 	ldap_msgfree (result);
1744 
1745     if (subtree != NULL) {
1746 	for (i = 0; i < ntrees; i++)
1747 	    free (subtree[i]);
1748 	free (subtree);
1749     }
1750 
1751     if (ptr != NULL)
1752 	free (ptr);
1753 
1754     if (gothandle == 1)
1755 	krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1756 
1757     return st;
1758 }
1759 
1760 /*
1761  * For now, policy objects are expected to be directly under the realm
1762  * container.
1763  */
1764 krb5_error_code krb5_ldap_policydn_to_name (context, policy_dn, name)
1765     krb5_context                context;
1766     char                        *policy_dn;
1767     char                        **name;
1768 {
1769     int len1, len2;
1770     krb5_error_code             st = 0;
1771     kdb5_dal_handle             *dal_handle=NULL;
1772     krb5_ldap_context           *ldap_context=NULL;
1773 
1774     SETUP_CONTEXT();
1775 
1776     if (ldap_context->lrparams->realmdn == NULL) {
1777 	st = EINVAL;
1778 	goto cleanup;
1779     }
1780 
1781     len1 = strlen (ldap_context->lrparams->realmdn);
1782     len2 = strlen (policy_dn);
1783     if (len1 == 0 || len2 == 0 || len1 > len2) {
1784 	st = EINVAL;
1785 	goto cleanup;
1786     }
1787 
1788     if (strcmp (ldap_context->lrparams->realmdn, policy_dn + (len2 - len1)) != 0) {
1789 	st = EINVAL;
1790 	goto cleanup;
1791     }
1792 
1793 #if defined HAVE_LDAP_STR2DN
1794     {
1795 	char *rdn;
1796 	LDAPDN dn;
1797 	rdn = strndup(policy_dn, len2 - len1 - 1); /* 1 character for ',' */
1798 
1799 	if (ldap_str2dn (rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC) != 0) {
1800 	    st = EINVAL;
1801 	    goto cleanup;
1802 	}
1803 	if (dn[0] == NULL || dn[1] != NULL)
1804 	    st = EINVAL;
1805 	else if (strcasecmp (dn[0][0]->la_attr.bv_val, "cn") != 0)
1806 	    st = EINVAL;
1807 	else {
1808 	    *name = strndup(dn[0][0]->la_value.bv_val, dn[0][0]->la_value.bv_len);
1809 	    if (*name == NULL)
1810 		st = EINVAL;
1811 	}
1812 
1813 	ldap_memfree (dn);
1814     }
1815 #elif defined HAVE_LDAP_EXPLODE_DN
1816     {
1817 	char **parsed_dn;
1818 
1819 	/* 1 = return DN components without type prefix */
1820 	parsed_dn = ldap_explode_dn(policy_dn, 1);
1821 	if (parsed_dn == NULL) {
1822 	    st = EINVAL;
1823 	} else {
1824 	    *name = strdup(parsed_dn[0]);
1825 	    if (*name == NULL)
1826 		st = EINVAL;
1827 
1828 	    ldap_value_free(parsed_dn);
1829 	}
1830     }
1831 #else
1832     st = EINVAL;
1833 #endif
1834 
1835 cleanup:
1836     return st;
1837 }
1838 
1839 krb5_error_code krb5_ldap_name_to_policydn (context, name, policy_dn)
1840     krb5_context                context;
1841     char                        *name;
1842     char                        **policy_dn;
1843 {
1844     int                         len;
1845     char                        *ptr = NULL;
1846     krb5_error_code             st = 0;
1847     kdb5_dal_handle             *dal_handle=NULL;
1848     krb5_ldap_context           *ldap_context=NULL;
1849 
1850     *policy_dn = NULL;
1851 
1852     /* validate the input parameters */
1853     if (name == NULL) {
1854 	st = EINVAL;
1855 	goto cleanup;
1856     }
1857 
1858     /* Used for removing policy reference from an object */
1859     if (name[0] == '\0') {
1860 	if ((*policy_dn = strdup ("")) == NULL)
1861 	    st = ENOMEM;
1862 	goto cleanup;
1863     }
1864 
1865     SETUP_CONTEXT();
1866 
1867     if (ldap_context->lrparams->realmdn == NULL) {
1868 	st = EINVAL;
1869 	goto cleanup;
1870     }
1871     len = strlen (ldap_context->lrparams->realmdn);
1872 
1873     ptr = ldap_filter_correct (name);
1874     if (ptr == NULL) {
1875 	st = ENOMEM;
1876 	goto cleanup;
1877     }
1878     len += strlen (ptr);
1879 
1880     len += sizeof ("cn=") + 3;
1881 
1882     *policy_dn = (char *) malloc (len);
1883     if (*policy_dn == NULL) {
1884 	st = ENOMEM;
1885 	goto cleanup;
1886     }
1887 
1888     /*LINTED*/
1889     sprintf (*policy_dn, "cn=%s,%s", ptr, ldap_context->lrparams->realmdn);
1890 
1891 cleanup:
1892     if (ptr != NULL)
1893 	free (ptr);
1894     return st;
1895 }
1896 
1897 /* remove overlapping and repeated subtree entries from the list of subtrees */
1898 static krb5_error_code
1899 remove_overlapping_subtrees(char **listin, char **listop, int *subtcount, int sscope)
1900 {
1901     int     slen=0, k=0, j=0, lendiff=0;
1902     int     count = *subtcount;
1903     char    **subtree = listop;
1904 
1905     slen = count-1;
1906     for (k=0; k<=slen && listin[k]!=NULL ; k++) {
1907 	for (j=k+1; j<=slen && listin[j]!=NULL ;j++) {
1908 	    lendiff = strlen(listin[k]) - strlen(listin[j]);
1909 	    if (sscope == 2) {
1910 		if ((lendiff > 0) && (strcasecmp((listin[k])+lendiff, listin[j])==0)) {
1911 		    if (k != slen) {
1912 			free(listin[k]);
1913 			listin[k] = listin[slen];
1914 			listin[slen] = NULL;
1915 		    } else {
1916 			free(listin[k]);
1917 			listin[k] = NULL;
1918 		    }
1919 		    slen-=1;
1920 		    k-=1;
1921 		    break;
1922 		} else if ((lendiff < 0) && (strcasecmp((listin[j])+abs(lendiff), listin[k])==0)) {
1923 		    if (j != slen) {
1924 			free(listin[j]);
1925 			listin[j] = listin[slen];
1926 			listin[slen]=NULL;
1927 		    } else {
1928 			free(listin[j]);
1929 			listin[j] = NULL;
1930 		    }
1931 		    slen-=1;
1932 		    j-=1;
1933 		}
1934 	    }
1935 	    if ((lendiff == 0) && (strcasecmp(listin[j], listin[k])==0)) {
1936 		if (j != slen) {
1937 		    free(listin[j]);
1938 		    listin[j] = listin[slen];
1939 		    listin[slen]=NULL;
1940 		} else {
1941 		    free(listin[j]);
1942 		    listin[j] = NULL;
1943 		}
1944 		slen -=1;
1945 		j-=1;
1946 	    }
1947 	}
1948     }
1949     *subtcount=slen+1;
1950     for (k=0; k<*subtcount && listin[k]!=NULL; k++) {
1951 	subtree[k] = strdup(listin[k]);
1952 	if (subtree[k] == NULL) {
1953 	    return ENOMEM;
1954 	}
1955     }
1956     return 0;
1957 }
1958 
1959 /*
1960  * Fill out a krb5_db_entry princ entry struct given a LDAP message containing
1961  * the results of a principal search of the directory.
1962  */
1963 krb5_error_code
1964 populate_krb5_db_entry (krb5_context context,
1965 			krb5_ldap_context *ldap_context,
1966 			LDAP *ld,
1967 			LDAPMessage *ent,
1968 			krb5_const_principal princ,
1969 			krb5_db_entry *entry)
1970 {
1971     krb5_error_code st = 0;
1972     unsigned int    mask = 0;
1973     krb5_boolean    attr_present = FALSE;
1974     char            **values = NULL, *policydn = NULL, *pwdpolicydn = NULL;
1975     char            *polname = NULL, *tktpolname = NULL;
1976     struct berval   **bvalues = NULL;
1977     krb5_tl_data    userinfo_tl_data = {0};
1978     /* Solaris Kerberos: added next line to fix memleak */
1979     krb5_tl_data    kadm_tl_data = {NULL};
1980     char            **link_references = NULL;
1981     char *DN = NULL;
1982 
1983     if (princ == NULL) {
1984 	st = EINVAL;
1985 	goto cleanup;
1986     } else {
1987 	if ((st=krb5_copy_principal(context, princ, &(entry->princ))) != 0)
1988 	    goto cleanup;
1989     }
1990     /* get the associated directory user information */
1991     if ((values = ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
1992 	int i, pcount=0, kerberos_principal_object_type=0;
1993 	char *user;
1994 
1995 	if ((st=krb5_unparse_name(context, princ, &user)) != 0)
1996 	    goto cleanup;
1997 
1998 	for (i=0; values[i] != NULL; ++i) {
1999 	    if (strcasecmp(values[i], user) == 0) {
2000 		pcount = ldap_count_values(values);
2001 		break;
2002 	    }
2003 	}
2004 	ldap_value_free(values);
2005 	free(user);
2006 
2007 	if ((DN = ldap_get_dn(ld, ent)) == NULL) {
2008 	    ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &st);
2009 	    st = set_ldap_error(context, st, 0);
2010 	    goto cleanup;
2011 	}
2012 
2013 	if ((values=ldap_get_values(ld, ent, "objectclass")) != NULL) {
2014 	    for (i=0; values[i] != NULL; ++i)
2015 		if (strcasecmp(values[i], "krbprincipal") == 0) {
2016 		    kerberos_principal_object_type = KDB_STANDALONE_PRINCIPAL_OBJECT;
2017 		    if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE,
2018 				&kerberos_principal_object_type)) != 0)
2019 			goto cleanup;
2020 		    break;
2021 		}
2022 	    ldap_value_free(values);
2023 	}
2024 
2025 	/* add principalcount, DN and principaltype user information to tl_data */
2026 	if (((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount)) != 0) ||
2027 	    ((st=store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, DN)) != 0))
2028 	    goto cleanup;
2029     }
2030 
2031     /* read all the kerberos attributes */
2032 
2033     /* KRBLASTSUCCESSFULAUTH */
2034     if ((st=krb5_ldap_get_time(ld, ent, "krbLastSuccessfulAuth",
2035 		&(entry->last_success), &attr_present)) != 0)
2036 	goto cleanup;
2037     if (attr_present == TRUE)
2038 	mask |= KDB_LAST_SUCCESS_ATTR;
2039 
2040     /* KRBLASTFAILEDAUTH */
2041     if ((st=krb5_ldap_get_time(ld, ent, "krbLastFailedAuth",
2042 		&(entry->last_failed), &attr_present)) != 0)
2043 	goto cleanup;
2044     if (attr_present == TRUE)
2045 	mask |= KDB_LAST_FAILED_ATTR;
2046 
2047     /* KRBLOGINFAILEDCOUNT */
2048     if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount",
2049 	    /* Solaris kerberos: need the cast */
2050 	    (int *)&(entry->fail_auth_count)) == 0)
2051 	mask |= KDB_FAIL_AUTH_COUNT_ATTR;
2052 
2053     /* KRBMAXTICKETLIFE */
2054     if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &(entry->max_life)) == 0)
2055 	mask |= KDB_MAX_LIFE_ATTR;
2056 
2057     /* KRBMAXRENEWABLEAGE */
2058     if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage",
2059 	    &(entry->max_renewable_life)) == 0)
2060 	mask |= KDB_MAX_RLIFE_ATTR;
2061 
2062     /* KRBTICKETFLAGS */
2063     if (krb5_ldap_get_value(ld, ent, "krbticketflags", &(entry->attributes)) == 0)
2064 	mask |= KDB_TKT_FLAGS_ATTR;
2065 
2066     /* PRINCIPAL EXPIRATION TIME */
2067     if ((st=krb5_ldap_get_time(ld, ent, "krbprincipalexpiration", &(entry->expiration),
2068 		&attr_present)) != 0)
2069 	goto cleanup;
2070     if (attr_present == TRUE)
2071 	mask |= KDB_PRINC_EXPIRE_TIME_ATTR;
2072 
2073     /* PASSWORD EXPIRATION TIME */
2074     if ((st=krb5_ldap_get_time(ld, ent, "krbpasswordexpiration", &(entry->pw_expiration),
2075 		&attr_present)) != 0)
2076 	goto cleanup;
2077     if (attr_present == TRUE)
2078 	mask |= KDB_PWD_EXPIRE_TIME_ATTR;
2079 
2080     /* KRBPOLICYREFERENCE */
2081 
2082     if ((st=krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn,
2083 		&attr_present)) != 0)
2084 	goto cleanup;
2085     if (attr_present == TRUE) {
2086 	mask |= KDB_POL_REF_ATTR;
2087 	/* Ensure that the policy is inside the realm container */
2088 	if ((st = krb5_ldap_policydn_to_name (context, policydn, &tktpolname)) != 0)
2089 	    goto cleanup;
2090     }
2091 
2092     /* KRBPWDPOLICYREFERENCE */
2093     if ((st=krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn,
2094 		&attr_present)) != 0)
2095 	goto cleanup;
2096     if (attr_present == TRUE) {
2097 	/* Solaris Kerberos: changed this to fix memleak */
2098 	/* krb5_tl_data  kadm_tl_data; */
2099 
2100 	mask |= KDB_PWD_POL_REF_ATTR;
2101 
2102 	/* Ensure that the policy is inside the realm container */
2103 	if ((st = krb5_ldap_policydn_to_name (context, pwdpolicydn, &polname)) != 0)
2104 	    goto cleanup;
2105 
2106 	if ((st = krb5_update_tl_kadm_data(polname, &kadm_tl_data)) != 0) {
2107 	    goto cleanup;
2108 	}
2109 	krb5_dbe_update_tl_data(context, entry, &kadm_tl_data);
2110     }
2111 
2112     /* KRBSECRETKEY */
2113     if ((bvalues=ldap_get_values_len(ld, ent, "krbprincipalkey")) != NULL) {
2114 	mask |= KDB_SECRET_KEY_ATTR;
2115 	if ((st=krb5_decode_krbsecretkey(context, entry, bvalues)) != 0)
2116 	    goto cleanup;
2117     }
2118 
2119     /* LAST PASSWORD CHANGE */
2120     {
2121 	krb5_timestamp lstpwdchng=0;
2122 	if ((st=krb5_ldap_get_time(ld, ent, "krbLastPwdChange",
2123 		    &lstpwdchng, &attr_present)) != 0)
2124 	    goto cleanup;
2125 	if (attr_present == TRUE) {
2126 	    if ((st=krb5_dbe_update_last_pwd_change(context, entry,
2127 			lstpwdchng)))
2128 		goto cleanup;
2129 	    mask |= KDB_LAST_PWD_CHANGE_ATTR;
2130 	}
2131     }
2132 
2133     /* KRBOBJECTREFERENCES */
2134     {
2135 	int i=0;
2136 
2137 	if ((st = krb5_ldap_get_strings(ld, ent, "krbobjectreferences",
2138 		    &link_references, &attr_present)) != 0)
2139 	    goto cleanup;
2140 	if (link_references != NULL) {
2141 	    for (i=0; link_references[i] != NULL; ++i) {
2142 		if ((st = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN,
2143 			    link_references[i])) != 0)
2144 		    goto cleanup;
2145 	    }
2146 	}
2147     }
2148 
2149     /* Set tl_data */
2150     {
2151 	int i;
2152 	struct berval **ber_tl_data = NULL;
2153 	krb5_tl_data *ptr = NULL;
2154 
2155 	if ((ber_tl_data = ldap_get_values_len (ld, ent, "krbExtraData")) != NULL) {
2156 	    for (i = 0; ber_tl_data[i] != NULL; i++) {
2157 		if ((st = berval2tl_data (ber_tl_data[i], &ptr)) != 0)
2158 		    break;
2159 		if ((st = krb5_dbe_update_tl_data(context, entry, ptr)) != 0)
2160 		    break;
2161 		/* Solaris kerberos: fix memory leak */
2162 		if (ptr) {
2163 		    if (ptr->tl_data_contents)
2164 			free(ptr->tl_data_contents);
2165 		    free(ptr);
2166 		    ptr = NULL;
2167 		}
2168 	    }
2169 	    ldap_value_free_len (ber_tl_data);
2170 	    if (st != 0)
2171 		goto cleanup;
2172 	    mask |= KDB_EXTRA_DATA_ATTR;
2173 	}
2174     }
2175 
2176     /* update the mask of attributes present on the directory object to the tl_data */
2177     if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask)) != 0)
2178 	goto cleanup;
2179     if ((st=krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data)) != 0)
2180 	goto cleanup;
2181 
2182 #ifdef HAVE_EDIRECTORY
2183     {
2184 	krb5_timestamp              expiretime=0;
2185 	char                        *is_login_disabled=NULL;
2186 
2187 	/* LOGIN EXPIRATION TIME */
2188 	if ((st=krb5_ldap_get_time(ld, ent, "loginexpirationtime", &expiretime,
2189 		    &attr_present)) != 0)
2190 	    goto cleanup;
2191 
2192 	if (attr_present == TRUE) {
2193 	    if ((mask & KDB_PRINC_EXPIRE_TIME_ATTR) == 1) {
2194 		if (expiretime < entry->expiration)
2195 		    entry->expiration = expiretime;
2196 	    } else {
2197 		entry->expiration = expiretime;
2198 	    }
2199 	}
2200 
2201 	/* LOGIN DISABLED */
2202 	if ((st=krb5_ldap_get_string(ld, ent, "logindisabled", &is_login_disabled,
2203 		    &attr_present)) != 0)
2204 	    goto cleanup;
2205 	if (attr_present == TRUE) {
2206 	    if (strcasecmp(is_login_disabled, "TRUE")== 0)
2207 		entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
2208 	    free (is_login_disabled);
2209 	}
2210     }
2211 #endif
2212 
2213     if ((st=krb5_read_tkt_policy (context, ldap_context, entry, tktpolname)) !=0)
2214 	goto cleanup;
2215 
2216     /* We already know that the policy is inside the realm container. */
2217     if (polname) {
2218 	osa_policy_ent_t   pwdpol;
2219 	int                cnt=0;
2220 	krb5_timestamp     last_pw_changed;
2221 	krb5_ui_4          pw_max_life;
2222 
2223 	memset(&pwdpol, 0, sizeof(pwdpol));
2224 
2225 	if ((st=krb5_ldap_get_password_policy(context, polname, &pwdpol, &cnt)) != 0)
2226 	    goto cleanup;
2227 	pw_max_life = pwdpol->pw_max_life;
2228 	/* Solaris Kerberos: fix memory leak */
2229 	krb5_ldap_free_password_policy(context, pwdpol);
2230 
2231 	if (pw_max_life > 0) {
2232 	    if ((st=krb5_dbe_lookup_last_pwd_change(context, entry, &last_pw_changed)) != 0)
2233 		goto cleanup;
2234 
2235 	    if ((mask & KDB_PWD_EXPIRE_TIME_ATTR) == 1) {
2236 		if ((last_pw_changed + pw_max_life) < entry->pw_expiration)
2237 		    entry->pw_expiration = last_pw_changed + pw_max_life;
2238 	    } else
2239 		entry->pw_expiration = last_pw_changed + pw_max_life;
2240 	}
2241     }
2242     /* XXX so krb5_encode_princ_contents() will be happy */
2243     entry->len = KRB5_KDB_V1_BASE_LENGTH;
2244 
2245 cleanup:
2246 
2247     if (DN != NULL)
2248 	ldap_memfree(DN);
2249 
2250     if (userinfo_tl_data.tl_data_contents != NULL)
2251 	free(userinfo_tl_data.tl_data_contents);
2252 
2253     /* Solaris Kerberos: added this to fix memleak */
2254     if (kadm_tl_data.tl_data_contents != NULL)
2255 	free(kadm_tl_data.tl_data_contents);
2256 
2257     if (pwdpolicydn != NULL)
2258 	free(pwdpolicydn);
2259 
2260     if (polname != NULL)
2261 	free(polname);
2262 
2263     if (tktpolname != NULL)
2264 	free (tktpolname);
2265 
2266     if (policydn != NULL)
2267 	free(policydn);
2268 
2269     if (link_references) {
2270         int i;
2271         for (i=0; link_references[i] != NULL; ++i)
2272             free (link_references[i]);
2273         free (link_references);
2274     }
2275 
2276     return (st);
2277 }
2278 
2279 /*
2280  * Solaris libldap does not provide the following functions which are in
2281  * OpenLDAP.  Note, Solaris Kerberos added the use_SSL to do a SSL init.  Also
2282  * added errstr to return specific error if it isn't NULL.  Yes, this is ugly
2283  * and no, the errstr should not be free()'ed.
2284  */
2285 #ifndef HAVE_LDAP_INITIALIZE
2286 int
2287 ldap_initialize(LDAP **ldp, char *url, int use_SSL, char **errstr)
2288 {
2289     int rc = LDAP_SUCCESS;
2290     LDAP *ld = NULL;
2291     LDAPURLDesc *ludp = NULL;
2292 
2293     /* For now, we don't use any DN that may be provided.  And on
2294        Solaris (based on Mozilla's LDAP client code), we need the
2295        _nodn form to parse "ldap://host" without a trailing slash.
2296 
2297        Also, this version won't handle an input string which contains
2298        multiple URLs, unlike the OpenLDAP ldap_initialize.  See
2299        https://bugzilla.mozilla.org/show_bug.cgi?id=353336#c1 .  */
2300 
2301     /* to avoid reinit and leaking handles, *ldp must be NULL */
2302     if (*ldp != NULL)
2303 	return LDAP_SUCCESS;
2304 
2305 #ifdef HAVE_LDAP_URL_PARSE_NODN
2306     rc = ldap_url_parse_nodn(url, &ludp);
2307 #else
2308     rc = ldap_url_parse(url, &ludp);
2309 #endif
2310     if (rc == 0) {
2311 	if (use_SSL == SSL_ON)
2312 	    ld = ldapssl_init(ludp->lud_host, ludp->lud_port, 1);
2313 	else
2314 	    ld = ldap_init(ludp->lud_host, ludp->lud_port);
2315 
2316 	if (ld != NULL)
2317 	    *ldp = ld;
2318 	else {
2319 	    if (errstr != NULL)
2320 		*errstr = strerror(errno);
2321 	    rc = LDAP_OPERATIONS_ERROR;
2322 	}
2323 
2324 	ldap_free_urldesc(ludp);
2325     } else {
2326 	/* report error from ldap url parsing */
2327 	if (errstr != NULL)
2328 	    *errstr = ldap_err2string(rc);
2329 	/* convert to generic LDAP error */
2330 	rc = LDAP_OPERATIONS_ERROR;
2331     }
2332     return rc;
2333 }
2334 #endif /* HAVE_LDAP_INITIALIZE */
2335 
2336 #ifndef HAVE_LDAP_UNBIND_EXT_S
2337 int
2338 ldap_unbind_ext_s(LDAP *ld, LDAPControl **sctrls, LDAPControl **cctrls)
2339 {
2340     return ldap_unbind_ext(ld, sctrls, cctrls);
2341 }
2342 #endif /* HAVE_LDAP_UNBIND_EXT_S */
2343