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