xref: /titanic_52/usr/src/lib/krb5/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c (revision 159d09a20817016f09b3ea28d1bdada4a336bb91)
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
45 extern char *strptime (const char *, const char *, struct tm *);
46 #endif
47 
48 static krb5_error_code
49 remove_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
57 static 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.  */
75 static errcode_t
76 prof_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.  */
108 static errcode_t
109 prof_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  */
142 krb5_error_code
143 krb5_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 
338 cleanup:
339     return(st);
340 }
341 
342 /*
343  * This function frees the krb5_ldap_context structure members.
344  */
345 
346 krb5_error_code
347 krb5_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 
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 
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 
1281 krb5_error_code
1282 krb5_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  */
1307 krb5_error_code
1308 krb5_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  */
1340 krb5_error_code
1341 krb5_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 
1377 cleanup:
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 
1389 krb5_error_code
1390 krb5_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 
1419 krb5_error_code
1420 krb5_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 
1447 krb5_error_code
1448 krb5_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 
1494 krb5_error_code
1495 krb5_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 
1536 static inline char *
1537 format_d (int val)
1538 {
1539     char tmpbuf[2+3*sizeof(val)];
1540     sprintf(tmpbuf, "%d", val);
1541     return strdup(tmpbuf);
1542 }
1543 
1544 krb5_error_code
1545 krb5_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 
1576 krb5_error_code
1577 krb5_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*/
1602 krb5_error_code
1603 krb5_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*/
1611 krb5_error_code
1612 krb5_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 
1619 krb5_error_code
1620 krb5_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*/
1628 krb5_error_code
1629 krb5_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*/
1637 krb5_error_code
1638 krb5_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 
1645 const char *
1646 krb5_ldap_errcode_2_string(krb5_context kcontext, long err_code)
1647 {
1648     return krb5_get_error_message(kcontext, err_code);
1649 }
1650 
1651 void
1652 krb5_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  */
1665 krb5_error_code
1666 krb5_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 
1735 cleanup:
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  */
1761 krb5_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 
1832 cleanup:
1833     return st;
1834 }
1835 
1836 krb5_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 
1888 cleanup:
1889     if (ptr != NULL)
1890 	free (ptr);
1891     return st;
1892 }
1893 
1894 /* remove overlapping and repeated subtree entries from the list of subtrees */
1895 static krb5_error_code
1896 remove_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  */
1960 krb5_error_code
1961 populate_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 
2243 cleanup:
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
2284 int
2285 ldap_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
2335 int
2336 ldap_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