1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
2 
3 /*
4  * lib/kdb/kdb_ldap/ldap_principal2.c
5  *
6  * Copyright (c) 2004-2005, Novell, Inc.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are met:
11  *
12  *   * Redistributions of source code must retain the above copyright notice,
13  *       this list of conditions and the following disclaimer.
14  *   * Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in the
16  *       documentation and/or other materials provided with the distribution.
17  *   * The copyright holder's name is not used to endorse or promote products
18  *       derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 /*
33  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
34  * Use is subject to license terms.
35  */
36 
37 #include <time.h>
38 #include "ldap_main.h"
39 #include "kdb_ldap.h"
40 #include "ldap_principal.h"
41 #include "princ_xdr.h"
42 #include "ldap_tkt_policy.h"
43 #include "ldap_pwd_policy.h"
44 #include "ldap_err.h"
45 #include <kadm5/admin.h>
46 #include <libintl.h>
47 
48 extern char* principal_attributes[];
49 extern char* max_pwd_life_attr[];
50 
51 static char *
52 getstringtime(krb5_timestamp);
53 
54 krb5_error_code
55 berval2tl_data(struct berval *in, krb5_tl_data **out)
56 {
57     *out = (krb5_tl_data *) malloc (sizeof (krb5_tl_data));
58     if (*out == NULL)
59 	return ENOMEM;
60 
61     (*out)->tl_data_length = in->bv_len - 2;
62     (*out)->tl_data_contents =  (krb5_octet *) malloc
63 	((*out)->tl_data_length * sizeof (krb5_octet));
64     if ((*out)->tl_data_contents == NULL) {
65 	free (*out);
66 	return ENOMEM;
67     }
68 
69     /* Solaris Kerberos: need cast */
70     UNSTORE16_INT ((unsigned char *)in->bv_val, (*out)->tl_data_type);
71     memcpy ((*out)->tl_data_contents, in->bv_val + 2, (*out)->tl_data_length);
72 
73     return 0;
74 }
75 
76 /*
77  * look up a principal in the directory.
78  */
79 
80 krb5_error_code
81 krb5_ldap_get_principal(context, searchfor, entries, nentries, more)
82     krb5_context context;
83     krb5_const_principal searchfor;
84     krb5_db_entry *entries;	/* filled in */
85     int *nentries;		/* how much room/how many found */
86     krb5_boolean *more;		/* are there more? */
87 {
88     char                        *user=NULL, *filter=NULL, **subtree=NULL;
89     unsigned int                tree=0, ntrees=1, princlen=0;
90     krb5_error_code	        tempst=0, st=0;
91     char                        **values=NULL;
92     LDAP	                *ld=NULL;
93     LDAPMessage	                *result=NULL, *ent=NULL;
94     krb5_ldap_context           *ldap_context=NULL;
95     kdb5_dal_handle             *dal_handle=NULL;
96     krb5_ldap_server_handle     *ldap_server_handle=NULL;
97 
98     /* Clear the global error string */
99     krb5_clear_error_message(context);
100 
101     /* set initial values */
102     *nentries = 0;
103     *more = 0;
104     memset(entries, 0, sizeof(*entries));
105 
106     if (searchfor == NULL)
107 	return EINVAL;
108 
109     dal_handle = (kdb5_dal_handle *) context->db_context;
110     ldap_context = (krb5_ldap_context *) dal_handle->db_context;
111 
112     CHECK_LDAP_HANDLE(ldap_context);
113 
114     if (is_principal_in_realm(ldap_context, searchfor) != 0) {
115 	*more = 0;
116 	krb5_set_error_message (context, st, gettext("Principal does not belong to realm"));
117 	goto cleanup;
118     }
119 
120     if ((st=krb5_unparse_name(context, searchfor, &user)) != 0)
121 	goto cleanup;
122 
123     if ((st=krb5_ldap_unparse_principal_name(user)) != 0)
124 	goto cleanup;
125 
126     princlen = strlen(FILTER) + strlen(user) + 2 + 1;      /* 2 for closing brackets */
127     if ((filter = malloc(princlen)) == NULL) {
128 	st = ENOMEM;
129 	goto cleanup;
130     }
131     snprintf(filter, princlen, FILTER"%s))", user);
132 
133     if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees)) != 0)
134 	goto cleanup;
135 
136     GET_HANDLE();
137     for (tree=0; tree < ntrees && *nentries == 0; ++tree) {
138 
139 	LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes);
140 	for (ent=ldap_first_entry(ld, result); ent != NULL && *nentries == 0; ent=ldap_next_entry(ld, ent)) {
141 
142 	    /* get the associated directory user information */
143 	    if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
144 		int i;
145 
146 		/* a wild-card in a principal name can return a list of kerberos principals.
147 		 * Make sure that the correct principal is returned.
148 		 * NOTE: a principalname k* in ldap server will return all the principals starting with a k
149 		 */
150 		for (i=0; values[i] != NULL; ++i) {
151 		    if (strcasecmp(values[i], user) == 0) {
152 			*nentries = 1;
153 			break;
154 		    }
155 		}
156 		ldap_value_free(values);
157 
158 		if (*nentries == 0) /* no matching principal found */
159 		    continue;
160 	    }
161 
162 	    if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent, searchfor,
163 			entries)) != 0)
164 		goto cleanup;
165 	}
166 	ldap_msgfree(result);
167 	result = NULL;
168     } /* for (tree=0 ... */
169 
170     /* once done, put back the ldap handle */
171     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
172     ldap_server_handle = NULL;
173 
174 cleanup:
175     ldap_msgfree(result);
176 
177     if (*nentries == 0 || st != 0)
178 	krb5_dbe_free_contents(context, entries);
179 
180     if (filter)
181 	free (filter);
182 
183     if (subtree) {
184 	for (; ntrees; --ntrees)
185 	    if (subtree[ntrees-1])
186 		free (subtree[ntrees-1]);
187 	free (subtree);
188     }
189 
190     if (ldap_server_handle)
191 	krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
192 
193     if (user)
194 	free(user);
195 
196     return st;
197 }
198 
199 typedef enum{ ADD_PRINCIPAL, MODIFY_PRINCIPAL } OPERATION;
200 /*
201  * ptype is creating confusions. Additionally the logic
202  * surronding ptype is redundunt and can be achevied
203  * with the help of dn and containerdn members.
204  * so dropping the ptype member
205  */
206 
207 typedef struct _xargs_t {
208     char           *dn;
209     char           *linkdn;
210     krb5_boolean   dn_from_kbd;
211     char           *containerdn;
212     char           *tktpolicydn;
213 }xargs_t;
214 
215 static void
216 free_xargs(xargs)
217     xargs_t xargs;
218 {
219     if (xargs.dn)
220 	free (xargs.dn);
221     if (xargs.linkdn)
222 	free(xargs.linkdn);
223     if (xargs.containerdn)
224 	free (xargs.containerdn);
225     if (xargs.tktpolicydn)
226 	free (xargs.tktpolicydn);
227 }
228 
229 static krb5_error_code
230 process_db_args(context, db_args, xargs, optype)
231     krb5_context   context;
232     char           **db_args;
233     xargs_t        *xargs;
234     OPERATION      optype;
235 {
236     int                   i=0;
237     krb5_error_code       st=0;
238     char                  errbuf[1024];
239     char                  *arg=NULL, *arg_val=NULL;
240     char                  **dptr=NULL;
241     unsigned int          arg_val_len=0;
242 
243     if (db_args) {
244 	for (i=0; db_args[i]; ++i) {
245 	    arg = strtok_r(db_args[i], "=", &arg_val);
246 	    if (strcmp(arg, TKTPOLICY_ARG) == 0) {
247 		dptr = &xargs->tktpolicydn;
248 	    } else {
249 		if (strcmp(arg, USERDN_ARG) == 0) {
250 		    if (optype == MODIFY_PRINCIPAL ||
251 			xargs->dn != NULL || xargs->containerdn != NULL ||
252 			xargs->linkdn != NULL) {
253 			st = EINVAL;
254 			snprintf(errbuf, sizeof(errbuf),
255 				 gettext("%s option not supported"), arg);
256 			krb5_set_error_message(context, st, "%s", errbuf);
257 			goto cleanup;
258 		    }
259 		    dptr = &xargs->dn;
260 		} else if (strcmp(arg, CONTAINERDN_ARG) == 0) {
261 		    if (optype == MODIFY_PRINCIPAL ||
262 			xargs->dn != NULL || xargs->containerdn != NULL) {
263 			st = EINVAL;
264 			snprintf(errbuf, sizeof(errbuf),
265 				 gettext("%s option not supported"), arg);
266 			krb5_set_error_message(context, st, "%s", errbuf);
267 			goto cleanup;
268 		    }
269 		    dptr = &xargs->containerdn;
270 		} else if (strcmp(arg, LINKDN_ARG) == 0) {
271 		    if (xargs->dn != NULL || xargs->linkdn != NULL) {
272 			st = EINVAL;
273 			snprintf(errbuf, sizeof(errbuf),
274 				 gettext("%s option not supported"), arg);
275 			krb5_set_error_message(context, st, "%s", errbuf);
276 			goto cleanup;
277 		    }
278 		    dptr = &xargs->linkdn;
279 		} else {
280 		    st = EINVAL;
281 		    snprintf(errbuf, sizeof(errbuf), gettext("unknown option: %s"), arg);
282 		    krb5_set_error_message(context, st, "%s", errbuf);
283 		    goto cleanup;
284 		}
285 
286 		xargs->dn_from_kbd = TRUE;
287 		if (arg_val == NULL || strlen(arg_val) == 0) {
288 		    st = EINVAL;
289 		    snprintf(errbuf, sizeof(errbuf),
290 			     gettext("%s option value missing"), arg);
291 		    krb5_set_error_message(context, st, "%s", errbuf);
292 		    goto cleanup;
293 		}
294 	    }
295 
296 	    if (arg_val == NULL) {
297 		st = EINVAL;
298 		snprintf(errbuf, sizeof(errbuf),
299 			 gettext("%s option value missing"), arg);
300 		krb5_set_error_message(context, st, "%s", errbuf);
301 		goto cleanup;
302 	    }
303 	    arg_val_len = strlen(arg_val) + 1;
304 
305 	    if (strcmp(arg, TKTPOLICY_ARG) == 0) {
306 		if ((st = krb5_ldap_name_to_policydn (context,
307 						      arg_val,
308 						      dptr)) != 0)
309 		    goto cleanup;
310 	    } else {
311 		*dptr = calloc (1, arg_val_len);
312 		if (*dptr == NULL) {
313 		    st = ENOMEM;
314 		    goto cleanup;
315 		}
316 		memcpy(*dptr, arg_val, arg_val_len);
317 	    }
318 	}
319     }
320 
321 cleanup:
322     return st;
323 }
324 
325 krb5int_access accessor;
326 extern int kldap_ensure_initialized (void);
327 
328 static krb5_error_code
329 asn1_encode_sequence_of_keys (krb5_key_data *key_data, krb5_int16 n_key_data,
330 			      krb5_int32 mkvno, krb5_data **code)
331 {
332     krb5_error_code err;
333 
334     /*
335      * This should be pushed back into other library initialization
336      * code.
337      */
338     err = kldap_ensure_initialized ();
339     if (err)
340 	return err;
341 
342     return accessor.asn1_ldap_encode_sequence_of_keys(key_data, n_key_data,
343 						      mkvno, code);
344 }
345 
346 static krb5_error_code
347 asn1_decode_sequence_of_keys (krb5_data *in, krb5_key_data **out,
348 			      krb5_int16 *n_key_data, int *mkvno)
349 {
350     krb5_error_code err;
351 
352     /*
353      * This should be pushed back into other library initialization
354      * code.
355      */
356     err = kldap_ensure_initialized ();
357     if (err)
358 	return err;
359 
360     return accessor.asn1_ldap_decode_sequence_of_keys(in, out, n_key_data,
361 						      mkvno);
362 }
363 
364 
365 /* Decoding ASN.1 encoded key */
366 static struct berval **
367 krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data) {
368     struct berval **ret = NULL;
369     int currkvno;
370     int num_versions = 1;
371     int i, j, last;
372     krb5_error_code err = 0;
373 
374     if (n_key_data <= 0)
375 	return NULL;
376 
377     /* Find the number of key versions */
378     for (i = 0; i < n_key_data - 1; i++)
379 	if (key_data[i].key_data_kvno != key_data[i + 1].key_data_kvno)
380 	    num_versions++;
381 
382     ret = (struct berval **) calloc (num_versions + 1, sizeof (struct berval *));
383     if (ret == NULL) {
384 	err = ENOMEM;
385 	goto cleanup;
386     }
387     for (i = 0, last = 0, j = 0, currkvno = key_data[0].key_data_kvno; i < n_key_data; i++) {
388 	krb5_data *code;
389 	if (i == n_key_data - 1 || key_data[i + 1].key_data_kvno != currkvno) {
390 	    code = NULL;
391 	    asn1_encode_sequence_of_keys (key_data+last,
392 					  (krb5_int16) i - last + 1,
393 					  0, /* For now, mkvno == 0*/
394 					  &code);
395 	    if (code == NULL) {
396 		err = ENOMEM;
397 		goto cleanup;
398 	    }
399 	    ret[j] = malloc (sizeof (struct berval));
400 	    if (ret[j] == NULL) {
401 		err = ENOMEM;
402 		goto cleanup;
403 	    }
404 	    /*CHECK_NULL(ret[j]); */
405 	    ret[j]->bv_len = code->length;
406 	    ret[j]->bv_val = code->data;
407 	    j++;
408 	    last = i + 1;
409 
410 	    currkvno = key_data[i].key_data_kvno;
411 	    /* Solaris Kerberos: fix memleak */
412 	    free(code);
413 	}
414     }
415     ret[num_versions] = NULL;
416 
417 cleanup:
418 
419     if (err != 0) {
420 	if (ret != NULL) {
421 	    for (i = 0; i <= num_versions; i++)
422 		if (ret[i] != NULL)
423 		    free (ret[i]);
424 	    free (ret);
425 	    ret = NULL;
426 	}
427     }
428 
429     return ret;
430 }
431 
432 static krb5_error_code tl_data2berval (krb5_tl_data *in, struct berval **out) {
433     *out = (struct berval *) malloc (sizeof (struct berval));
434     if (*out == NULL)
435 	return ENOMEM;
436 
437     (*out)->bv_len = in->tl_data_length + 2;
438     (*out)->bv_val =  (char *) malloc ((*out)->bv_len);
439     if ((*out)->bv_val == NULL) {
440 	free (*out);
441 	return ENOMEM;
442     }
443 
444     /* Solaris Kerberos: need cast */
445     STORE16_INT((unsigned char *)(*out)->bv_val, in->tl_data_type);
446     memcpy ((*out)->bv_val + 2, in->tl_data_contents, in->tl_data_length);
447 
448     return 0;
449 }
450 
451 krb5_error_code
452 krb5_ldap_put_principal(context, entries, nentries, db_args)
453     krb5_context               context;
454     krb5_db_entry              *entries;
455     register int               *nentries;         /* number of entry structs to update */
456     char                       **db_args;
457 {
458     int 		        i=0, l=0, kerberos_principal_object_type=0;
459     krb5_error_code 	        st=0, tempst=0;
460     LDAP  		        *ld=NULL;
461     LDAPMessage                 *result=NULL, *ent=NULL;
462     char                        *user=NULL, *subtree=NULL, *principal_dn=NULL;
463     char                        **values=NULL, *strval[10]={NULL}, errbuf[1024];
464     struct berval	        **bersecretkey=NULL;
465     LDAPMod 		        **mods=NULL;
466     krb5_boolean                create_standalone_prinicipal=FALSE;
467     krb5_boolean                krb_identity_exists=FALSE, establish_links=FALSE;
468     char                        *standalone_principal_dn=NULL;
469     krb5_tl_data                *tl_data=NULL;
470     kdb5_dal_handle             *dal_handle=NULL;
471     krb5_ldap_context           *ldap_context=NULL;
472     krb5_ldap_server_handle     *ldap_server_handle=NULL;
473     osa_princ_ent_rec 	        princ_ent;
474     xargs_t                     xargs = {0};
475     char                        *polname = NULL;
476     OPERATION optype;
477     krb5_boolean     		found_entry = FALSE;
478     struct berval **ber_tl_data = NULL;
479 
480     /* Clear the global error string */
481     krb5_clear_error_message(context);
482 
483     SETUP_CONTEXT();
484     if (ldap_context->lrparams == NULL || ldap_context->krbcontainer == NULL)
485 	return EINVAL;
486 
487     /* get ldap handle */
488     GET_HANDLE();
489 
490     for (i=0; i < *nentries; ++i, ++entries) {
491 	if (is_principal_in_realm(ldap_context, entries->princ) != 0) {
492 	    st = EINVAL;
493 	    krb5_set_error_message(context, st, gettext("Principal does not belong to the default realm"));
494 	    goto cleanup;
495 	}
496 
497 	/* get the principal information to act on */
498 	if (entries->princ) {
499 	    if (((st=krb5_unparse_name(context, entries->princ, &user)) != 0) ||
500 		((st=krb5_ldap_unparse_principal_name(user)) != 0))
501 		goto cleanup;
502 	}
503 
504 	/* Identity the type of operation, it can be
505 	 * add principal or modify principal.
506 	 * hack if the entries->mask has KRB_PRINCIPAL flag set
507 	 * then it is a add operation
508 	 */
509 	if (entries->mask & KADM5_PRINCIPAL)
510 	    optype = ADD_PRINCIPAL;
511 	else
512 	    optype = MODIFY_PRINCIPAL;
513 
514 	if (((st=krb5_get_princ_type(context, entries, &kerberos_principal_object_type)) != 0) ||
515 	    ((st=krb5_get_userdn(context, entries, &principal_dn)) != 0))
516 	    goto cleanup;
517 
518 	if ((st=process_db_args(context, db_args, &xargs, optype)) != 0)
519 	    goto cleanup;
520 
521 	if (entries->mask & KADM5_LOAD) {
522 	    int              tree = 0, princlen = 0, numlentries = 0;
523 	    unsigned int     ntrees = 0;
524 	    char             **subtreelist = NULL, *filter = NULL;
525 
526 	    /*  A load operation is special, will do a mix-in (add krbprinc
527 	     *  attrs to a non-krb object entry) if an object exists with a
528 	     *  matching krbprincipalname attribute so try to find existing
529 	     *  object and set principal_dn.  This assumes that the
530 	     *  krbprincipalname attribute is unique (only one object entry has
531 	     *  a particular krbprincipalname attribute).
532 	     */
533 	    if (user == NULL) {
534 		/* must have principal name for search */
535 		st = EINVAL;
536 		krb5_set_error_message(context, st, gettext("operation can not continue, principal name not found"));
537 		goto cleanup;
538 	    }
539 	    princlen = strlen(FILTER) + strlen(user) + 2 + 1;      /* 2 for closing brackets */
540 	    if ((filter = malloc(princlen)) == NULL) {
541 		st = ENOMEM;
542 		goto cleanup;
543 	    }
544 	    snprintf(filter, princlen, FILTER"%s))", user);
545 
546 	    /* get the current subtree list */
547 	    if ((st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees)) != 0)
548 		goto cleanup;
549 
550 	    found_entry = FALSE;
551 	    /* search for entry with matching krbprincipalname attribute */
552 	    for (tree = 0; found_entry == FALSE && tree < ntrees; ++tree) {
553 		result = NULL;
554 		if (principal_dn == NULL) {
555 		    LDAP_SEARCH_1(subtreelist[tree], ldap_context->lrparams->search_scope, filter, principal_attributes, IGNORE_STATUS);
556 		} else {
557 		    /* just look for entry with principal_dn */
558 		    LDAP_SEARCH_1(principal_dn, LDAP_SCOPE_BASE, filter, principal_attributes, IGNORE_STATUS);
559 		}
560 		if (st == LDAP_SUCCESS) {
561 		    numlentries = ldap_count_entries(ld, result);
562 		    if (numlentries > 1) {
563 			ldap_msgfree(result);
564 			free(filter);
565 			st = EINVAL;
566 			krb5_set_error_message(context, st,
567 			    gettext("operation can not continue, more than one entry with principal name \"%s\" found"),
568 			    user);
569 			goto cleanup;
570 		    } else if (numlentries == 1) {
571 			found_entry = TRUE;
572 			if (principal_dn == NULL) {
573 			    ent = ldap_first_entry(ld, result);
574 			    if (ent != NULL) {
575 				/* setting principal_dn will cause that entry to be modified further down */
576 				if ((principal_dn = ldap_get_dn(ld, ent)) == NULL) {
577 				    ldap_get_option (ld, LDAP_OPT_RESULT_CODE, &st);
578 				    st = set_ldap_error (context, st, 0);
579 				    ldap_msgfree(result);
580 				    free(filter);
581 				    goto cleanup;
582 				}
583 			    }
584 			}
585 		    }
586 		    if (result)
587 			ldap_msgfree(result);
588 		} else if (st != LDAP_NO_SUCH_OBJECT) {
589 		    /* could not perform search, return with failure */
590 		    st = set_ldap_error (context, st, 0);
591 		    free(filter);
592 		    goto cleanup;
593 		}
594 		/*
595 		 * If it isn't found then assume a standalone princ entry is to
596 		 * be created.
597 		 */
598 	    } /* end for (tree = 0; principal_dn == ... */
599 
600 	    free(filter);
601 
602 	    if (found_entry == FALSE && principal_dn != NULL) {
603 		/*
604 		 * if principal_dn is null then there is code further down to
605 		 * deal with setting standalone_principal_dn.  Also note that
606 		 * this will set create_standalone_prinicipal true for
607 		 * non-mix-in entries which is okay if loading from a dump.
608 		 */
609 		create_standalone_prinicipal = TRUE;
610 		standalone_principal_dn = strdup(principal_dn);
611 		CHECK_NULL(standalone_principal_dn);
612 	    }
613 	} /* end if (entries->mask & KADM5_LOAD */
614 
615 	/* time to generate the DN information with the help of
616 	 * containerdn, principalcontainerreference or
617 	 * realmcontainerdn information
618 	 */
619 	if (principal_dn == NULL && xargs.dn == NULL) { /* creation of standalone principal */
620 	    /* get the subtree information */
621 	    if (entries->princ->length == 2 && entries->princ->data[0].length == strlen("krbtgt") &&
622 		strncmp(entries->princ->data[0].data, "krbtgt", entries->princ->data[0].length) == 0) {
623 		/* if the principal is a inter-realm principal, always created in the realm container */
624 		subtree = strdup(ldap_context->lrparams->realmdn);
625 	    } else if (xargs.containerdn) {
626 		if ((st=checkattributevalue(ld, xargs.containerdn, NULL, NULL, NULL)) != 0) {
627 		    if (st == KRB5_KDB_NOENTRY || st == KRB5_KDB_CONSTRAINT_VIOLATION) {
628 			int ost = st;
629 			st = EINVAL;
630 			snprintf(errbuf, sizeof(errbuf), gettext("'%s' not found: "), xargs.containerdn);
631 			prepend_err_str(context, errbuf, st, ost);
632 		    }
633 		    goto cleanup;
634 		}
635 		subtree = strdup(xargs.containerdn);
636 	    } else if (ldap_context->lrparams->containerref && strlen(ldap_context->lrparams->containerref) != 0) {
637 		/*
638 		 * Here the subtree should be changed with
639 		 * principalcontainerreference attribute value
640 		 */
641 		subtree = strdup(ldap_context->lrparams->containerref);
642 	    } else {
643 		subtree = strdup(ldap_context->lrparams->realmdn);
644 	    }
645 	    CHECK_NULL(subtree);
646 
647 	    standalone_principal_dn = malloc(strlen("krbprincipalname=") + strlen(user) + strlen(",") +
648 					     strlen(subtree) + 1);
649 	    CHECK_NULL(standalone_principal_dn);
650 	    /*LINTED*/
651 	    sprintf(standalone_principal_dn, "krbprincipalname=%s,%s", user, subtree);
652 	    /*
653 	     * free subtree when you are done using the subtree
654 	     * set the boolean create_standalone_prinicipal to TRUE
655 	     */
656 	    create_standalone_prinicipal = TRUE;
657 	    free(subtree);
658 	    subtree = NULL;
659 	}
660 
661 	/*
662 	 * If the DN information is presented by the user, time to
663 	 * validate the input to ensure that the DN falls under
664 	 * any of the subtrees
665 	 */
666 	if (xargs.dn_from_kbd == TRUE) {
667 	    /* make sure the DN falls in the subtree */
668 	    int              tre=0, dnlen=0, subtreelen=0;
669 	    unsigned int     ntrees = 0;
670 	    char             **subtreelist=NULL;
671 	    char             *dn=NULL;
672 	    krb5_boolean     outofsubtree=TRUE;
673 
674 	    if (xargs.dn != NULL) {
675 		dn = xargs.dn;
676 	    } else if (xargs.linkdn != NULL) {
677 		dn = xargs.linkdn;
678 	    } else if (standalone_principal_dn != NULL) {
679 		/*
680 		 * Even though the standalone_principal_dn is constructed
681 		 * within this function, there is the containerdn input
682 		 * from the user that can become part of the it.
683 		 */
684 		dn = standalone_principal_dn;
685 	    }
686 
687 	    /* get the current subtree list */
688 	    if ((st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees)) != 0)
689 		goto cleanup;
690 
691 	    for (tre=0; tre<ntrees; ++tre) {
692 		if (subtreelist[tre] == NULL || strlen(subtreelist[tre]) == 0) {
693 		    outofsubtree = FALSE;
694 		    break;
695 		} else {
696 		    dnlen = strlen (dn);
697 		    subtreelen = strlen(subtreelist[tre]);
698 		    if ((dnlen >= subtreelen) && (strcasecmp((dn + dnlen - subtreelen), subtreelist[tre]) == 0)) {
699 			outofsubtree = FALSE;
700 			break;
701 		    }
702 		}
703 	    }
704 
705 	    for (tre=0; tre < ntrees; ++tre) {
706 		free(subtreelist[tre]);
707 	    }
708 
709 	    if (outofsubtree == TRUE) {
710 		st = EINVAL;
711 		krb5_set_error_message(context, st, gettext("DN is out of the realm subtree"));
712 		goto cleanup;
713 	    }
714 
715 	    /*
716 	     * dn value will be set either by dn, linkdn or the standalone_principal_dn
717 	     * In the first 2 cases, the dn should be existing and in the last case we
718 	     * are supposed to create the ldap object. so the below should not be
719 	     * executed for the last case.
720 	     */
721 
722 	    if (standalone_principal_dn == NULL) {
723 		/*
724 		 * If the ldap object is missing, this results in an error.
725 		 */
726 
727 		/*
728 		 * Search for krbprincipalname attribute here.
729 		 * This is to find if a kerberos identity is already present
730 		 * on the ldap object, in which case adding a kerberos identity
731 		 * on the ldap object should result in an error.
732 		 */
733 		char  *attributes[]={"krbticketpolicyreference", "krbprincipalname", NULL};
734 
735 		LDAP_SEARCH_1(dn, LDAP_SCOPE_BASE, 0, attributes, IGNORE_STATUS);
736 		if (st == LDAP_SUCCESS) {
737 		    ent = ldap_first_entry(ld, result);
738 		    if (ent != NULL) {
739 			if ((values=ldap_get_values(ld, ent, "krbticketpolicyreference")) != NULL) {
740 			    ldap_value_free(values);
741 			}
742 
743 			if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
744 			    krb_identity_exists = TRUE;
745 			    ldap_value_free(values);
746 			}
747 		    }
748 		    ldap_msgfree(result);
749 		} else {
750 		    st = set_ldap_error(context, st, OP_SEARCH);
751 		    goto cleanup;
752 		}
753 	    }
754 	}
755 
756 	/*
757 	 * If xargs.dn is set then the request is to add a
758 	 * kerberos principal on a ldap object, but if
759 	 * there is one already on the ldap object this
760 	 * should result in an error.
761 	 */
762 
763 	if (xargs.dn != NULL && krb_identity_exists == TRUE) {
764 	    st = EINVAL;
765 	    snprintf(errbuf, sizeof(errbuf), gettext("ldap object is already kerberized"));
766 	    krb5_set_error_message(context, st, "%s", errbuf);
767 	    goto cleanup;
768 	}
769 
770 	if (xargs.linkdn != NULL) {
771 	    /*
772 	     * link information can be changed using modprinc.
773 	     * However, link information can be changed only on the
774 	     * standalone kerberos principal objects. A standalone
775 	     * kerberos principal object is of type krbprincipal
776 	     * structural objectclass.
777 	     *
778 	     * NOTE: kerberos principals on an ldap object can't be
779 	     * linked to other ldap objects.
780 	     */
781 	    if (optype == MODIFY_PRINCIPAL &&
782 		kerberos_principal_object_type != KDB_STANDALONE_PRINCIPAL_OBJECT) {
783 		st = EINVAL;
784 		snprintf(errbuf, sizeof(errbuf),
785 		    gettext("link information can not be set/updated as the kerberos principal belongs to an ldap object"));
786 		krb5_set_error_message(context, st, "%s", errbuf);
787 		goto cleanup;
788 	    }
789             /*
790              * Check the link information. If there is already a link
791              * existing then this operation is not allowed.
792              */
793             {
794                 char **linkdns=NULL;
795                 int  j=0;
796 
797                 if ((st=krb5_get_linkdn(context, entries, &linkdns)) != 0) {
798                     snprintf(errbuf, sizeof(errbuf),
799                              gettext("Failed getting object references"));
800                     krb5_set_error_message(context, st, "%s", errbuf);
801                     goto cleanup;
802                 }
803                 if (linkdns != NULL) {
804                     st = EINVAL;
805                     snprintf(errbuf, sizeof(errbuf),
806                              gettext("kerberos principal is already linked "
807                              "to a ldap object"));
808                     krb5_set_error_message(context, st, "%s", errbuf);
809                     for (j=0; linkdns[j] != NULL; ++j)
810                         free (linkdns[j]);
811                     free (linkdns);
812                     goto cleanup;
813                 }
814             }
815 
816 	    establish_links = TRUE;
817 	}
818 
819 	if ((entries->last_success)!=0) {
820 	    memset(strval, 0, sizeof(strval));
821 	    if ((strval[0]=getstringtime(entries->last_success)) == NULL)
822 		goto cleanup;
823 	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastSuccessfulAuth", LDAP_MOD_REPLACE, strval)) != 0) {
824 		free (strval[0]);
825 		goto cleanup;
826 	    }
827 	    free (strval[0]);
828 	}
829 
830 	if (entries->last_failed!=0) {
831 	    memset(strval, 0, sizeof(strval));
832 	    if ((strval[0]=getstringtime(entries->last_failed)) == NULL)
833 		goto cleanup;
834 	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastFailedAuth", LDAP_MOD_REPLACE, strval)) != 0) {
835 		free (strval[0]);
836 		goto cleanup;
837 	    }
838 	    free(strval[0]);
839 	}
840 
841 	if (entries->fail_auth_count!=0) {
842 	    if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount", LDAP_MOD_REPLACE, entries->fail_auth_count)) !=0)
843 		goto cleanup;
844 	}
845 
846 	if (entries->mask & KADM5_MAX_LIFE) {
847 	    if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_REPLACE, entries->max_life)) != 0)
848 		goto cleanup;
849 	}
850 
851 	if (entries->mask & KADM5_MAX_RLIFE) {
852 	    if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_REPLACE,
853 					      entries->max_renewable_life)) != 0)
854 		goto cleanup;
855 	}
856 
857 	if (entries->mask & KADM5_ATTRIBUTES) {
858 	    if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_REPLACE,
859 					      entries->attributes)) != 0)
860 		goto cleanup;
861 	}
862 
863 	if (entries->mask & KADM5_PRINCIPAL) {
864 	    memset(strval, 0, sizeof(strval));
865 	    strval[0] = user;
866 	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_REPLACE, strval)) != 0)
867 		goto cleanup;
868 	}
869 
870 	/*
871 	 * Solaris Kerberos: this logic was not working properly when
872 	 * default_principal_expiration set.
873 	 */
874 	if (entries->mask & KADM5_PRINC_EXPIRE_TIME || entries->expiration != 0) {
875 	    memset(strval, 0, sizeof(strval));
876 	    if ((strval[0]=getstringtime(entries->expiration)) == NULL)
877 		goto cleanup;
878 	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalexpiration", LDAP_MOD_REPLACE, strval)) != 0) {
879 		free (strval[0]);
880 		goto cleanup;
881 	    }
882 	    free (strval[0]);
883 	}
884 
885 	/*
886 	 * Solaris Kerberos: in case KADM5_PW_EXPIRATION isn't set, check
887 	 * pw_expiration
888 	 */
889 	if (entries->mask & KADM5_PW_EXPIRATION || entries->pw_expiration != 0) {
890 	    memset(strval, 0, sizeof(strval));
891 	    if ((strval[0]=getstringtime(entries->pw_expiration)) == NULL)
892 		goto cleanup;
893 	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpasswordexpiration",
894 					      LDAP_MOD_REPLACE,
895 					      strval)) != 0) {
896 		free (strval[0]);
897 		goto cleanup;
898 	    }
899 	    free (strval[0]);
900 	}
901 
902 	if (entries->mask & KADM5_POLICY) {
903 	    memset(&princ_ent, 0, sizeof(princ_ent));
904 	    for (tl_data=entries->tl_data; tl_data; tl_data=tl_data->tl_data_next) {
905 		if (tl_data->tl_data_type == KRB5_TL_KADM_DATA) {
906 		    /* FIX ME: I guess the princ_ent should be freed after this call */
907 		    if ((st = krb5_lookup_tl_kadm_data(tl_data, &princ_ent)) != 0) {
908 			goto cleanup;
909 		    }
910 		}
911 	    }
912 
913 	    if (princ_ent.aux_attributes & KADM5_POLICY) {
914 		memset(strval, 0, sizeof(strval));
915 		if ((st = krb5_ldap_name_to_policydn (context, princ_ent.policy, &polname)) != 0)
916 		    goto cleanup;
917 		strval[0] = polname;
918 		/* Solaris Kerberos: fix memleak */
919 		free(princ_ent.policy);
920 		if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_REPLACE, strval)) != 0)
921 		    goto cleanup;
922 	    } else {
923 		st = EINVAL;
924 		krb5_set_error_message(context, st, gettext("Password policy value null"));
925 		goto cleanup;
926 	    }
927 	} else if (entries->mask & KADM5_LOAD && found_entry == TRUE) {
928 	    /*
929 	     * a load is special in that existing entries must have attrs that
930 	     * removed.
931 	     */
932 
933 	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_REPLACE, NULL)) != 0)
934 		goto cleanup;
935 	}
936 
937 	if (entries->mask & KADM5_POLICY_CLR) {
938 	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_DELETE, NULL)) != 0)
939 		goto cleanup;
940 	}
941 
942 	if (entries->mask & KADM5_KEY_DATA || entries->mask & KADM5_KVNO) {
943 	    bersecretkey = krb5_encode_krbsecretkey (entries->key_data,
944 						     entries->n_key_data);
945 
946 	    if ((st=krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey",
947 					      LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, bersecretkey)) != 0)
948 		goto cleanup;
949 
950 	    if (!(entries->mask & KADM5_PRINCIPAL)) {
951 		memset(strval, 0, sizeof(strval));
952 		if ((strval[0]=getstringtime(entries->pw_expiration)) == NULL)
953 		    goto cleanup;
954 		if ((st=krb5_add_str_mem_ldap_mod(&mods,
955 						  "krbpasswordexpiration",
956 						  LDAP_MOD_REPLACE, strval)) != 0) {
957 		    free (strval[0]);
958 		    goto cleanup;
959 		}
960 		free (strval[0]);
961 	    }
962 
963 	    /* Update last password change whenever a new key is set */
964 	    {
965 		krb5_timestamp last_pw_changed;
966 		if ((st=krb5_dbe_lookup_last_pwd_change(context, entries,
967 							&last_pw_changed)) != 0)
968 		    goto cleanup;
969 
970 		memset(strval, 0, sizeof(strval));
971 		if ((strval[0] = getstringtime(last_pw_changed)) == NULL)
972 		    goto cleanup;
973 
974 		if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastPwdChange",
975 						  LDAP_MOD_REPLACE, strval)) != 0) {
976 		    free (strval[0]);
977 		    goto cleanup;
978 		}
979 		free (strval[0]);
980 	    }
981 
982 	} /* Modify Key data ends here */
983 
984 	/* Set tl_data */
985 	if (entries->tl_data != NULL) {
986 	    int count = 0;
987 	    /* struct berval **ber_tl_data = NULL; */
988 	    krb5_tl_data *ptr;
989 	    for (ptr = entries->tl_data; ptr != NULL; ptr = ptr->tl_data_next) {
990 		if (ptr->tl_data_type == KRB5_TL_LAST_PWD_CHANGE
991 #ifdef SECURID
992 		    || ptr->tl_data_type == KRB5_TL_DB_ARGS
993 #endif
994 		    || ptr->tl_data_type == KDB_TL_USER_INFO)
995 		    continue;
996 
997 		/* Solaris Kerberos: fix key history issue */
998 		if (ptr->tl_data_type == KRB5_TL_KADM_DATA && ! entries->mask & KADM5_KEY_HIST)
999 		    continue;
1000 
1001 		count++;
1002 	    }
1003 	    if (count != 0) {
1004 		int j;
1005 		ber_tl_data = (struct berval **) calloc (count + 1,
1006 							 sizeof (struct berval*));
1007 		if (ber_tl_data == NULL) {
1008 		    st = ENOMEM;
1009 		    goto cleanup;
1010 		}
1011 		for (j = 0, ptr = entries->tl_data; ptr != NULL; ptr = ptr->tl_data_next) {
1012 		    /* Ignore tl_data that are stored in separate directory
1013 		     * attributes */
1014 		    if (ptr->tl_data_type == KRB5_TL_LAST_PWD_CHANGE
1015 #ifdef SECURID
1016 			|| ptr->tl_data_type == KRB5_TL_DB_ARGS
1017 #endif
1018 			|| ptr->tl_data_type == KDB_TL_USER_INFO)
1019 			continue;
1020 
1021 		    /*
1022 		     * Solaris Kerberos: key history needs to be stored (it's in
1023 		     * the KRB5_TL_KADM_DATA).
1024 		     */
1025 		    if (ptr->tl_data_type == KRB5_TL_KADM_DATA && ! entries->mask & KADM5_KEY_HIST)
1026 			continue;
1027 
1028 		    if ((st = tl_data2berval (ptr, &ber_tl_data[j])) != 0)
1029 			break;
1030 		    j++;
1031 		}
1032 		if (st != 0) {
1033 		    /* Solaris Kerberos: don't free here, do it at cleanup */
1034 		    goto cleanup;
1035 		}
1036 		ber_tl_data[count] = NULL;
1037 		if ((st=krb5_add_ber_mem_ldap_mod(&mods, "krbExtraData",
1038 						  LDAP_MOD_REPLACE | LDAP_MOD_BVALUES,
1039 						  ber_tl_data)) != 0)
1040 		    goto cleanup;
1041 	    }
1042 	}
1043 
1044 	/* Directory specific attribute */
1045 	if (xargs.tktpolicydn != NULL) {
1046 	    int tmask=0;
1047 
1048 	    if (strlen(xargs.tktpolicydn) != 0) {
1049 		st = checkattributevalue(ld, xargs.tktpolicydn, "objectclass", policyclass, &tmask);
1050 		CHECK_CLASS_VALIDITY(st, tmask, "ticket policy object value: ");
1051 
1052 		strval[0] = xargs.tktpolicydn;
1053 		strval[1] = NULL;
1054 		if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbticketpolicyreference", LDAP_MOD_REPLACE, strval)) != 0)
1055 		    goto cleanup;
1056 
1057 	    } else {
1058 		/* if xargs.tktpolicydn is a empty string, then delete
1059 		 * already existing krbticketpolicyreference attr */
1060 		if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbticketpolicyreference", LDAP_MOD_DELETE, NULL)) != 0)
1061 		    goto cleanup;
1062 	    }
1063 
1064 	}
1065 
1066 	if (establish_links == TRUE) {
1067 	    memset(strval, 0, sizeof(strval));
1068 	    strval[0] = xargs.linkdn;
1069 	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbObjectReferences", LDAP_MOD_REPLACE, strval)) != 0)
1070 		goto cleanup;
1071 	}
1072 
1073 	/*
1074 	 * in case mods is NULL then return
1075 	 * not sure but can happen in a modprinc
1076 	 * so no need to return an error
1077 	 * addprinc will at least have the principal name
1078 	 * and the keys passed in
1079 	 */
1080 	if (mods == NULL)
1081 	    goto cleanup;
1082 
1083 	if (create_standalone_prinicipal == TRUE) {
1084 	    memset(strval, 0, sizeof(strval));
1085 	    strval[0] = "krbprincipal";
1086 	    strval[1] = "krbprincipalaux";
1087 	    strval[2] = "krbTicketPolicyAux";
1088 
1089 	    if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
1090 		goto cleanup;
1091 
1092 	    st = ldap_add_ext_s(ld, standalone_principal_dn, mods, NULL, NULL);
1093 	    if (st == LDAP_ALREADY_EXISTS && entries->mask & KADM5_LOAD) {
1094 		/* a load operation must replace an existing entry */
1095 		st = ldap_delete_ext_s(ld, standalone_principal_dn, NULL, NULL);
1096 		if (st != LDAP_SUCCESS) {
1097 		    snprintf(errbuf, sizeof (errbuf), gettext("Principal delete failed (trying to replace entry): %s"),
1098 			ldap_err2string(st));
1099 		    st = translate_ldap_error (st, OP_ADD);
1100 		    krb5_set_error_message(context, st, "%s", errbuf);
1101 		    goto cleanup;
1102 		} else {
1103 		    st = ldap_add_ext_s(ld, standalone_principal_dn, mods, NULL, NULL);
1104 		}
1105 	    }
1106 	    if (st != LDAP_SUCCESS) {
1107 		snprintf(errbuf, sizeof (errbuf), gettext("Principal add failed: %s"), ldap_err2string(st));
1108 		st = translate_ldap_error (st, OP_ADD);
1109 		krb5_set_error_message(context, st, "%s", errbuf);
1110 		goto cleanup;
1111 	    }
1112 	} else {
1113 	    /*
1114 	     * Here existing ldap object is modified and can be related
1115 	     * to any attribute, so always ensure that the ldap
1116 	     * object is extended with all the kerberos related
1117 	     * objectclasses so that there are no constraint
1118 	     * violations.
1119 	     */
1120 	    {
1121 		char *attrvalues[] = {"krbprincipalaux", "krbTicketPolicyAux", NULL};
1122 		int p, q, r=0, amask=0;
1123 
1124 		if ((st=checkattributevalue(ld, (xargs.dn) ? xargs.dn : principal_dn,
1125 					    "objectclass", attrvalues, &amask)) != 0)
1126 		    goto cleanup;
1127 
1128 		memset(strval, 0, sizeof(strval));
1129 		for (p=1, q=0; p<=2; p<<=1, ++q) {
1130 		    if ((p & amask) == 0)
1131 			strval[r++] = attrvalues[q];
1132 		}
1133 		if (r != 0) {
1134 		    if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
1135 			goto cleanup;
1136 		}
1137 	    }
1138 	    if (xargs.dn != NULL)
1139 		st=ldap_modify_ext_s(ld, xargs.dn, mods, NULL, NULL);
1140 	    else
1141 		st = ldap_modify_ext_s(ld, principal_dn, mods, NULL, NULL);
1142 
1143 	    if (st != LDAP_SUCCESS) {
1144 		snprintf(errbuf, sizeof (errbuf), gettext("User modification failed: %s"), ldap_err2string(st));
1145 		st = translate_ldap_error (st, OP_MOD);
1146 		krb5_set_error_message(context, st, "%s", errbuf);
1147 		goto cleanup;
1148 	    }
1149 	}
1150     }
1151 
1152 cleanup:
1153     if (user)
1154 	free(user);
1155 
1156     free_xargs(xargs);
1157 
1158     if (standalone_principal_dn)
1159 	free(standalone_principal_dn);
1160 
1161     if (principal_dn)
1162 	free (principal_dn);
1163 
1164     /* Solaris Kerberos: fix memleak */
1165     if (ber_tl_data) {
1166 	int j;
1167 
1168 	for (j = 0; ber_tl_data[j] != NULL; j++) {
1169 	    free (ber_tl_data[j]->bv_val);
1170 	    free (ber_tl_data[j]);
1171 	}
1172 	free(ber_tl_data);
1173     }
1174 
1175     if (polname != NULL)
1176 	free(polname);
1177 
1178     if (subtree)
1179 	free (subtree);
1180 
1181     if (bersecretkey) {
1182 	for (l=0; bersecretkey[l]; ++l) {
1183 	    if (bersecretkey[l]->bv_val)
1184 		free (bersecretkey[l]->bv_val);
1185 	    free (bersecretkey[l]);
1186 	}
1187 	free (bersecretkey);
1188     }
1189 
1190     ldap_mods_free(mods, 1);
1191     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1192     *nentries = i;
1193     return(st);
1194 }
1195 
1196 krb5_error_code
1197 krb5_read_tkt_policy (context, ldap_context, entries, policy)
1198     krb5_context                context;
1199     krb5_ldap_context           *ldap_context;
1200     krb5_db_entry               *entries;
1201     char                        *policy;
1202 {
1203     krb5_error_code             st=0;
1204     unsigned int                mask=0, omask=0;
1205     int                         tkt_mask=(KDB_MAX_LIFE_ATTR | KDB_MAX_RLIFE_ATTR | KDB_TKT_FLAGS_ATTR);
1206     krb5_ldap_policy_params     *tktpoldnparam=NULL;
1207 
1208     if ((st=krb5_get_attributes_mask(context, entries, &mask)) != 0)
1209 	goto cleanup;
1210 
1211     if ((mask & tkt_mask) == tkt_mask)
1212 	goto cleanup;
1213 
1214     if (policy != NULL) {
1215 	st = krb5_ldap_read_policy(context, policy, &tktpoldnparam, &omask);
1216 	if (st && st != KRB5_KDB_NOENTRY) {
1217 	    prepend_err_str(context, gettext("Error reading ticket policy. "), st, st);
1218 	    goto cleanup;
1219 	}
1220 
1221 	st = 0; /* reset the return status */
1222     }
1223 
1224     if ((mask & KDB_MAX_LIFE_ATTR) == 0) {
1225 	if ((omask & KDB_MAX_LIFE_ATTR) ==  KDB_MAX_LIFE_ATTR)
1226 	    entries->max_life = tktpoldnparam->maxtktlife;
1227 	else if (ldap_context->lrparams->max_life)
1228 	    entries->max_life = ldap_context->lrparams->max_life;
1229     }
1230 
1231     if ((mask & KDB_MAX_RLIFE_ATTR) == 0) {
1232 	if ((omask & KDB_MAX_RLIFE_ATTR) == KDB_MAX_RLIFE_ATTR)
1233 	    entries->max_renewable_life = tktpoldnparam->maxrenewlife;
1234 	else if (ldap_context->lrparams->max_renewable_life)
1235 	    entries->max_renewable_life = ldap_context->lrparams->max_renewable_life;
1236     }
1237 
1238     if ((mask & KDB_TKT_FLAGS_ATTR) == 0) {
1239 	if ((omask & KDB_TKT_FLAGS_ATTR) == KDB_TKT_FLAGS_ATTR)
1240 	    entries->attributes = tktpoldnparam->tktflags;
1241 	else if (ldap_context->lrparams->tktflags)
1242 	    entries->attributes |= ldap_context->lrparams->tktflags;
1243     }
1244     krb5_ldap_free_policy(context, tktpoldnparam);
1245 
1246 cleanup:
1247     return st;
1248 }
1249 
1250 krb5_error_code
1251 krb5_decode_krbsecretkey(context, entries, bvalues)
1252     krb5_context                context;
1253     krb5_db_entry               *entries;
1254     struct berval               **bvalues;
1255 {
1256     char                        *user=NULL;
1257     int                         i=0, j=0, noofkeys=0;
1258     krb5_key_data               *key_data=NULL, *tmp;
1259     krb5_error_code             st=0;
1260 
1261     if ((st=krb5_unparse_name(context, entries->princ, &user)) != 0)
1262 	goto cleanup;
1263 
1264     for (i=0; bvalues[i] != NULL; ++i) {
1265 	int mkvno; /* Not used currently */
1266 	krb5_int16 n_kd;
1267 	krb5_key_data *kd;
1268 	krb5_data in;
1269 
1270 	if (bvalues[i]->bv_len == 0)
1271 	    continue;
1272 	in.length = bvalues[i]->bv_len;
1273 	in.data = bvalues[i]->bv_val;
1274 
1275 	st = asn1_decode_sequence_of_keys (&in,
1276 					   &kd,
1277 					   &n_kd,
1278 					   &mkvno);
1279 
1280 	if (st != 0) {
1281 	    const char *msg = error_message(st);
1282 	    st = -1; /* Something more appropriate ? */
1283 	    krb5_set_error_message (context, st,
1284 				    gettext("unable to decode stored principal key data (%s)"), msg);
1285 	    goto cleanup;
1286 	}
1287 	noofkeys += n_kd;
1288 	tmp = key_data;
1289 	key_data = realloc (key_data, noofkeys * sizeof (krb5_key_data));
1290 	if (key_data == NULL) {
1291 	    key_data = tmp;
1292 	    st = ENOMEM;
1293 	    goto cleanup;
1294 	}
1295 	for (j = 0; j < n_kd; j++)
1296 	    key_data[noofkeys - n_kd + j] = kd[j];
1297 	free (kd);
1298     }
1299 
1300     entries->n_key_data = noofkeys;
1301     entries->key_data = key_data;
1302 
1303 cleanup:
1304     ldap_value_free_len(bvalues);
1305     free (user);
1306     return st;
1307 }
1308 
1309 static char *
1310 getstringtime(epochtime)
1311     krb5_timestamp    epochtime;
1312 {
1313     struct tm           tme;
1314     char                *strtime=NULL;
1315     time_t		posixtime = epochtime;
1316 
1317     strtime = calloc (50, 1);
1318     if (strtime == NULL)
1319 	return NULL;
1320 
1321     if (gmtime_r(&posixtime, &tme) == NULL)
1322 	return NULL;
1323 
1324     strftime(strtime, 50, DATE_FORMAT, &tme);
1325     return strtime;
1326 }
1327 
1328