xref: /freebsd/crypto/krb5/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c */
3 /*
4  * Copyright (C) 2016 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 /*
33  * Copyright (c) 2004-2005, Novell, Inc.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions are met:
38  *
39  *   * Redistributions of source code must retain the above copyright notice,
40  *       this list of conditions and the following disclaimer.
41  *   * Redistributions in binary form must reproduce the above copyright
42  *       notice, this list of conditions and the following disclaimer in the
43  *       documentation and/or other materials provided with the distribution.
44  *   * The copyright holder's name is not used to endorse or promote products
45  *       derived from this software without specific prior written permission.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57  * POSSIBILITY OF SUCH DAMAGE.
58  */
59 /*
60  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
61  * Use is subject to license terms.
62  */
63 
64 #include "ldap_main.h"
65 #include "kdb_ldap.h"
66 #include "ldap_principal.h"
67 #include "princ_xdr.h"
68 #include "ldap_tkt_policy.h"
69 #include "ldap_pwd_policy.h"
70 #include "ldap_err.h"
71 #include <kadm5/admin.h>
72 #include <time.h>
73 
74 extern char* principal_attributes[];
75 extern char* max_pwd_life_attr[];
76 
77 static char *
78 getstringtime(krb5_timestamp);
79 
80 krb5_error_code
berval2tl_data(struct berval * in,krb5_tl_data ** out)81 berval2tl_data(struct berval *in, krb5_tl_data **out)
82 {
83     *out = (krb5_tl_data *) malloc (sizeof (krb5_tl_data));
84     if (*out == NULL)
85         return ENOMEM;
86 
87     (*out)->tl_data_length = in->bv_len - 2;
88     (*out)->tl_data_contents =  (krb5_octet *) malloc
89         ((*out)->tl_data_length * sizeof (krb5_octet));
90     if ((*out)->tl_data_contents == NULL) {
91         free (*out);
92         return ENOMEM;
93     }
94 
95     UNSTORE16_INT (in->bv_val, (*out)->tl_data_type);
96     memcpy ((*out)->tl_data_contents, in->bv_val + 2, (*out)->tl_data_length);
97 
98     return 0;
99 }
100 
101 /*
102  * Search for filter at the specified base and scope, expecting to find a
103  * single result.  Set *entry_out to an object containing all principal
104  * attributes.  Set *result_out to the result message to be freed with
105  * ldap_msgfree().  Set both to NULL if no matching entry is found.
106  */
107 static krb5_error_code
search_at(krb5_context context,krb5_ldap_context * ldap_context,krb5_ldap_server_handle * ldap_server_handle,const char * base,int scope,const char * filter,const char * user,LDAPMessage ** entry_out,LDAPMessage ** result_out)108 search_at(krb5_context context, krb5_ldap_context *ldap_context,
109           krb5_ldap_server_handle *ldap_server_handle, const char *base,
110           int scope, const char *filter, const char *user,
111           LDAPMessage **entry_out, LDAPMessage **result_out)
112 {
113     krb5_error_code st, tempst;
114     LDAPMessage *result = NULL;
115     LDAP *ld = ldap_server_handle->ldap_handle;
116     int nentries;
117 
118     *entry_out = *result_out = NULL;
119 
120     LDAP_SEARCH(base, scope, filter, principal_attributes);
121     nentries = ldap_count_entries(ld, result);
122     if (nentries > 1) {
123         st = EINVAL;
124         k5_setmsg(context, st,
125                   _("Operation cannot continue; more than one "
126                     "entry with principal name \"%s\" found"),
127                   user);
128         goto cleanup;
129     }
130 
131     if (nentries == 1) {
132         *result_out = result;
133         *entry_out = ldap_first_entry(ld, result);
134         return 0;
135     }
136 
137 cleanup:
138     ldap_msgfree(result);
139     return st;
140 }
141 
142 /*
143  * Search for an LDAP object matching princ, either at the specified dn with
144  * base scope, or, if dn is NULL, in all configured subtrees with the
145  * configured search scope (usually subtree).  Set *entry_out and *result_out
146  * in the same way as search_at().
147  */
148 static krb5_error_code
search_princ(krb5_context context,krb5_ldap_context * ldap_context,krb5_ldap_server_handle * ldap_server_handle,krb5_const_principal princ,const char * dn,LDAPMessage ** entry_out,LDAPMessage ** result_out)149 search_princ(krb5_context context, krb5_ldap_context *ldap_context,
150              krb5_ldap_server_handle *ldap_server_handle,
151              krb5_const_principal princ, const char *dn,
152              LDAPMessage **entry_out, LDAPMessage **result_out)
153 {
154     krb5_error_code st;
155     char *user = NULL, *filtuser = NULL, *filter = NULL;
156     char **subtreelist = NULL;
157     size_t ntrees = 0, i;
158 
159     *entry_out = *result_out = NULL;
160 
161     st = krb5_ldap_unparse_name(context, princ, &user);
162     if (st)
163         goto cleanup;
164 
165     filtuser = ldap_filter_correct(user);
166     if (filtuser == NULL) {
167         st = ENOMEM;
168         goto cleanup;
169     }
170 
171     if (asprintf(&filter, FILTER"%s))", filtuser) < 0) {
172         filter = NULL;
173         st = ENOMEM;
174         goto cleanup;
175     }
176 
177     if (dn != NULL) {
178         st = search_at(context, ldap_context, ldap_server_handle, dn,
179                        LDAP_SCOPE_BASE, filter, user, entry_out, result_out);
180         goto cleanup;
181     }
182 
183     st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees);
184     if (st)
185         goto cleanup;
186 
187     for (i = 0; i < ntrees; i++) {
188         st = search_at(context, ldap_context, ldap_server_handle,
189                        subtreelist[i], ldap_context->lrparams->search_scope,
190                        filter, user, entry_out, result_out);
191         if (st || *entry_out != NULL)
192             goto cleanup;
193     }
194 
195 cleanup:
196     free(user);
197     free(filtuser);
198     free(filter);
199     while (ntrees > 0)
200         free(subtreelist[--ntrees]);
201     free(subtreelist);
202     return st;
203 }
204 
205 /*
206  * look up a principal in the directory.
207  */
208 
209 krb5_error_code
krb5_ldap_get_principal(krb5_context context,krb5_const_principal searchfor,unsigned int flags,krb5_db_entry ** entry_ptr)210 krb5_ldap_get_principal(krb5_context context, krb5_const_principal searchfor,
211                         unsigned int flags, krb5_db_entry **entry_ptr)
212 {
213     krb5_error_code st;
214     char **values = NULL;
215     LDAP *ld = NULL;
216     LDAPMessage *ent = NULL, *result = NULL;
217     kdb5_dal_handle *dal_handle;
218     krb5_ldap_context *ldap_context;
219     krb5_ldap_server_handle *ldap_server_handle = NULL;
220     krb5_principal cprinc = NULL;
221     krb5_db_entry *entry = NULL;
222 
223     *entry_ptr = NULL;
224 
225     /* Clear the global error string */
226     krb5_clear_error_message(context);
227 
228     if (searchfor == NULL)
229         return EINVAL;
230 
231     dal_handle = context->dal_handle;
232     ldap_context = (krb5_ldap_context *) dal_handle->db_context;
233 
234     CHECK_LDAP_HANDLE(ldap_context);
235 
236     if (!is_principal_in_realm(ldap_context, searchfor)) {
237         st = KRB5_KDB_NOENTRY;
238         k5_setmsg(context, st, _("Principal does not belong to realm"));
239         goto cleanup;
240     }
241 
242     GET_HANDLE();
243 
244     st = search_princ(context, ldap_context, ldap_server_handle, searchfor,
245                       NULL, &ent, &result);
246     if (st)
247         goto cleanup;
248     if (ent == NULL) {
249         st = KRB5_KDB_NOENTRY;
250         goto cleanup;
251     }
252 
253     /* Use the canonical principal name if one is present in the object. */
254     values = ldap_get_values(ld, ent, "krbCanonicalName");
255     if (values != NULL && values[0] != NULL) {
256         st = krb5_ldap_parse_name(context, values[0], &cprinc);
257         if (st != 0)
258             goto cleanup;
259     }
260     ldap_value_free(values);
261 
262     entry = k5alloc(sizeof(*entry), &st);
263     if (entry == NULL)
264         goto cleanup;
265     st = populate_krb5_db_entry(context, ldap_context, ld, ent,
266                                 cprinc != NULL ? cprinc : searchfor, entry);
267     if (st)
268         goto cleanup;
269 
270     *entry_ptr = entry;
271     entry = NULL;
272 
273 cleanup:
274     ldap_msgfree(result);
275     krb5_db_free_principal(context, entry);
276     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
277     krb5_free_principal(context, cprinc);
278     return st;
279 }
280 
281 typedef enum{ ADD_PRINCIPAL, MODIFY_PRINCIPAL } OPERATION;
282 /*
283  * ptype is creating confusions. Additionally the logic
284  * surronding ptype is redundunt and can be achevied
285  * with the help of dn and containerdn members.
286  * so dropping the ptype member
287  */
288 
289 typedef struct _xargs_t {
290     char           *dn;
291     char           *linkdn;
292     krb5_boolean   dn_from_kbd;
293     char           *containerdn;
294     char           *tktpolicydn;
295 }xargs_t;
296 
297 static void
free_xargs(xargs_t xargs)298 free_xargs(xargs_t xargs)
299 {
300     if (xargs.dn)
301         free (xargs.dn);
302     if (xargs.linkdn)
303         free(xargs.linkdn);
304     if (xargs.containerdn)
305         free (xargs.containerdn);
306     if (xargs.tktpolicydn)
307         free (xargs.tktpolicydn);
308 }
309 
310 static krb5_error_code
process_db_args(krb5_context context,char ** db_args,xargs_t * xargs,OPERATION optype)311 process_db_args(krb5_context context, char **db_args, xargs_t *xargs,
312                 OPERATION optype)
313 {
314     size_t                i=0, arg_val_len=0;
315     krb5_error_code       st=0;
316     char                  *arg=NULL, *arg_val=NULL;
317     char                  **dptr=NULL;
318 
319     if (db_args) {
320         for (i=0; db_args[i]; ++i) {
321             arg = strtok_r(db_args[i], "=", &arg_val);
322             arg = (arg != NULL) ? arg : "";
323             if (strcmp(arg, TKTPOLICY_ARG) == 0) {
324                 dptr = &xargs->tktpolicydn;
325             } else {
326                 if (strcmp(arg, USERDN_ARG) == 0) {
327                     if (optype == MODIFY_PRINCIPAL ||
328                         xargs->dn != NULL || xargs->containerdn != NULL ||
329                         xargs->linkdn != NULL) {
330                         st = EINVAL;
331                         k5_setmsg(context, st, _("%s option not supported"),
332                                   arg);
333                         goto cleanup;
334                     }
335                     dptr = &xargs->dn;
336                 } else if (strcmp(arg, CONTAINERDN_ARG) == 0) {
337                     if (optype == MODIFY_PRINCIPAL ||
338                         xargs->dn != NULL || xargs->containerdn != NULL) {
339                         st = EINVAL;
340                         k5_setmsg(context, st, _("%s option not supported"),
341                                   arg);
342                         goto cleanup;
343                     }
344                     dptr = &xargs->containerdn;
345                 } else if (strcmp(arg, LINKDN_ARG) == 0) {
346                     if (xargs->dn != NULL || xargs->linkdn != NULL) {
347                         st = EINVAL;
348                         k5_setmsg(context, st, _("%s option not supported"),
349                                   arg);
350                         goto cleanup;
351                     }
352                     dptr = &xargs->linkdn;
353                 } else {
354                     st = EINVAL;
355                     k5_setmsg(context, st, _("unknown option: %s"), arg);
356                     goto cleanup;
357                 }
358 
359                 xargs->dn_from_kbd = TRUE;
360                 if (arg_val == NULL || strlen(arg_val) == 0) {
361                     st = EINVAL;
362                     k5_setmsg(context, st, _("%s option value missing"), arg);
363                     goto cleanup;
364                 }
365             }
366 
367             if (arg_val == NULL) {
368                 st = EINVAL;
369                 k5_setmsg(context, st, _("%s option value missing"), arg);
370                 goto cleanup;
371             }
372             arg_val_len = strlen(arg_val) + 1;
373 
374             if (strcmp(arg, TKTPOLICY_ARG) == 0) {
375                 if ((st = krb5_ldap_name_to_policydn (context,
376                                                       arg_val,
377                                                       dptr)) != 0)
378                     goto cleanup;
379             } else {
380                 *dptr = k5memdup(arg_val, arg_val_len, &st);
381                 if (*dptr == NULL)
382                     goto cleanup;
383             }
384         }
385     }
386 
387 cleanup:
388     return st;
389 }
390 
391 krb5int_access accessor;
392 
393 static krb5_error_code
asn1_encode_sequence_of_keys(krb5_key_data * key_data,krb5_int16 n_key_data,krb5_int32 mkvno,krb5_data ** code)394 asn1_encode_sequence_of_keys(krb5_key_data *key_data, krb5_int16 n_key_data,
395                              krb5_int32 mkvno, krb5_data **code)
396 {
397     krb5_error_code err;
398     ldap_seqof_key_data val;
399 
400     /*
401      * This should be pushed back into other library initialization
402      * code.
403      */
404     err = kldap_ensure_initialized ();
405     if (err)
406         return err;
407 
408     val.key_data = key_data;
409     val.n_key_data = n_key_data;
410     val.mkvno = mkvno;
411     val.kvno = key_data[0].key_data_kvno;
412 
413     return accessor.asn1_ldap_encode_sequence_of_keys(&val, code);
414 }
415 
416 static krb5_error_code
asn1_decode_sequence_of_keys(krb5_data * in,ldap_seqof_key_data * out)417 asn1_decode_sequence_of_keys(krb5_data *in, ldap_seqof_key_data *out)
418 {
419     krb5_error_code err;
420     ldap_seqof_key_data *p;
421     int i;
422 
423     memset(out, 0, sizeof(*out));
424 
425     /*
426      * This should be pushed back into other library initialization
427      * code.
428      */
429     err = kldap_ensure_initialized ();
430     if (err)
431         return err;
432 
433     err = accessor.asn1_ldap_decode_sequence_of_keys(in, &p);
434     if (err)
435         return err;
436 
437     /* Set kvno and key_data_ver in each key_data element. */
438     for (i = 0; i < p->n_key_data; i++) {
439         p->key_data[i].key_data_kvno = p->kvno;
440         /* The decoder sets key_data_ver to 1 if no salt is present, but leaves
441          * it at 0 if salt is present. */
442         if (p->key_data[i].key_data_ver == 0)
443             p->key_data[i].key_data_ver = 2;
444     }
445 
446     *out = *p;
447     free(p);
448     return 0;
449 }
450 
451 /*
452  * Free a NULL-terminated struct berval *array[] and all its contents.
453  * Does not set array to NULL after freeing it.
454  */
455 void
free_berdata(struct berval ** array)456 free_berdata(struct berval **array)
457 {
458     size_t i;
459 
460     if (array != NULL) {
461         for (i = 0; array[i] != NULL; i++) {
462             if (array[i]->bv_val != NULL)
463                 free(array[i]->bv_val);
464             free(array[i]);
465         }
466         free(array);
467     }
468 }
469 
470 /*
471  * Encode krb5_key_data into a berval struct for insertion into LDAP.
472  */
473 static krb5_error_code
encode_keys(krb5_key_data * key_data_in,int n_key_data,krb5_kvno mkvno,struct berval ** bval_out)474 encode_keys(krb5_key_data *key_data_in, int n_key_data, krb5_kvno mkvno,
475             struct berval **bval_out)
476 {
477     krb5_error_code err = 0;
478     int i;
479     krb5_key_data *key_data = NULL;
480     struct berval *bval = NULL;
481     krb5_data *code;
482 
483     *bval_out = NULL;
484     if (n_key_data <= 0) {
485         err = EINVAL;
486         goto cleanup;
487     }
488 
489     /* Make a shallow copy of the key data so we can alter it. */
490     key_data = k5calloc(n_key_data, sizeof(*key_data), &err);
491     if (key_data == NULL)
492         goto cleanup;
493     memcpy(key_data, key_data_in, n_key_data * sizeof(*key_data));
494 
495     /* Unpatched krb5 1.11 and 1.12 cannot decode KrbKey sequences with no salt
496      * field.  For compatibility, always encode a salt field. */
497     for (i = 0; i < n_key_data; i++) {
498         if (key_data[i].key_data_ver == 1) {
499             key_data[i].key_data_ver = 2;
500             key_data[i].key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL;
501             key_data[i].key_data_length[1] = 0;
502             key_data[i].key_data_contents[1] = NULL;
503         }
504     }
505 
506     bval = k5alloc(sizeof(struct berval), &err);
507     if (bval == NULL)
508         goto cleanup;
509 
510     err = asn1_encode_sequence_of_keys(key_data, n_key_data, mkvno, &code);
511     if (err)
512         goto cleanup;
513 
514     /* Steal the data pointer from code for bval and discard code. */
515     bval->bv_len = code->length;
516     bval->bv_val = code->data;
517     free(code);
518 
519     *bval_out = bval;
520     bval = NULL;
521 
522 cleanup:
523     free(key_data);
524     free(bval);
525     return err;
526 }
527 
528 /* Decoding ASN.1 encoded key */
529 struct berval **
krb5_encode_krbsecretkey(krb5_key_data * key_data,int n_key_data,krb5_kvno mkvno)530 krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data,
531                          krb5_kvno mkvno)
532 {
533     struct berval **ret = NULL;
534     int currkvno;
535     int num_versions = 0;
536     int i, j, last;
537     krb5_error_code err = 0;
538 
539     if (n_key_data < 0)
540         return NULL;
541 
542     /* Find the number of key versions */
543     if (n_key_data > 0) {
544         for (i = 0, num_versions = 1; i < n_key_data - 1; i++) {
545             if (key_data[i].key_data_kvno != key_data[i + 1].key_data_kvno)
546                 num_versions++;
547         }
548     }
549 
550     ret = calloc(num_versions + 1, sizeof(struct berval *));
551     if (ret == NULL) {
552         err = ENOMEM;
553         goto cleanup;
554     }
555     ret[num_versions] = NULL;
556 
557     /* n_key_data may be 0 if a principal is created without a key. */
558     if (n_key_data == 0)
559         goto cleanup;
560 
561     currkvno = key_data[0].key_data_kvno;
562     for (i = 0, last = 0, j = 0; i < n_key_data; i++) {
563         if (i == n_key_data - 1 || key_data[i + 1].key_data_kvno != currkvno) {
564             err = encode_keys(key_data + last, (krb5_int16)i - last + 1, mkvno,
565                               &ret[j]);
566             if (err)
567                 goto cleanup;
568             j++;
569             last = i + 1;
570 
571             if (i < n_key_data - 1)
572                 currkvno = key_data[i + 1].key_data_kvno;
573         }
574     }
575 
576 cleanup:
577     if (err != 0) {
578         free_berdata(ret);
579         ret = NULL;
580     }
581 
582     return ret;
583 }
584 
585 /*
586  * Encode a principal's key history for insertion into ldap.
587  */
588 static struct berval **
krb5_encode_histkey(osa_princ_ent_rec * princ_ent)589 krb5_encode_histkey(osa_princ_ent_rec *princ_ent)
590 {
591     unsigned int i;
592     krb5_error_code err = 0;
593     struct berval **ret = NULL;
594 
595     if (princ_ent->old_key_len <= 0)
596         return NULL;
597 
598     ret = k5calloc(princ_ent->old_key_len + 1, sizeof(struct berval *), &err);
599     if (ret == NULL)
600         goto cleanup;
601 
602     for (i = 0; i < princ_ent->old_key_len; i++) {
603         if (princ_ent->old_keys[i].n_key_data <= 0) {
604             err = EINVAL;
605             goto cleanup;
606         }
607         err = encode_keys(princ_ent->old_keys[i].key_data,
608                           princ_ent->old_keys[i].n_key_data,
609                           princ_ent->admin_history_kvno, &ret[i]);
610         if (err)
611             goto cleanup;
612     }
613 
614     ret[princ_ent->old_key_len] = NULL;
615 
616 cleanup:
617     if (err != 0) {
618         free_berdata(ret);
619         ret = NULL;
620     }
621 
622     return ret;
623 }
624 
625 static krb5_error_code
tl_data2berval(krb5_tl_data * in,struct berval ** out)626 tl_data2berval (krb5_tl_data *in, struct berval **out)
627 {
628     *out = (struct berval *) malloc (sizeof (struct berval));
629     if (*out == NULL)
630         return ENOMEM;
631 
632     (*out)->bv_len = in->tl_data_length + 2;
633     (*out)->bv_val =  (char *) malloc ((*out)->bv_len);
634     if ((*out)->bv_val == NULL) {
635         free (*out);
636         return ENOMEM;
637     }
638 
639     STORE16_INT((*out)->bv_val, in->tl_data_type);
640     memcpy ((*out)->bv_val + 2, in->tl_data_contents, in->tl_data_length);
641 
642     return 0;
643 }
644 
645 /* Parse the "require_auth" string for auth indicators, adding them to the
646  * krbPrincipalAuthInd attribute. */
647 static krb5_error_code
update_ldap_mod_auth_ind(krb5_context context,krb5_db_entry * entry,LDAPMod *** mods)648 update_ldap_mod_auth_ind(krb5_context context, krb5_db_entry *entry,
649                          LDAPMod ***mods)
650 {
651     krb5_error_code ret;
652     char *auth_ind = NULL;
653     char *strval[10] = { 0 };
654     char *ai, *ai_save = NULL;
655     size_t i = 0, sv_num = sizeof(strval) / sizeof(*strval);
656     int mask;
657 
658     ret = krb5_dbe_get_string(context, entry, KRB5_KDB_SK_REQUIRE_AUTH,
659                               &auth_ind);
660     if (ret)
661         return ret;
662     if (auth_ind == NULL) {
663         /* If we know krbPrincipalAuthInd attributes are present from loading
664          * the entry, delete them. */
665         ret = krb5_get_attributes_mask(context, entry, &mask);
666         if (!ret && (mask & KDB_AUTH_IND_ATTR)) {
667             return krb5_add_str_mem_ldap_mod(mods, "krbPrincipalAuthInd",
668                                              LDAP_MOD_DELETE, NULL);
669         }
670         return 0;
671     }
672 
673     ai = strtok_r(auth_ind, " ", &ai_save);
674     while (ai != NULL && i < sv_num) {
675         strval[i++] = ai;
676         ai = strtok_r(NULL, " ", &ai_save);
677     }
678 
679     ret = krb5_add_str_mem_ldap_mod(mods, "krbPrincipalAuthInd",
680                                     LDAP_MOD_REPLACE, strval);
681     krb5_dbe_free_string(context, auth_ind);
682     return ret;
683 }
684 
685 static krb5_error_code
check_dn_in_container(krb5_context context,const char * dn,char * const * subtrees,size_t ntrees)686 check_dn_in_container(krb5_context context, const char *dn,
687                       char *const *subtrees, size_t ntrees)
688 {
689     size_t dnlen = strlen(dn), stlen, i;
690 
691     for (i = 0; i < ntrees; i++) {
692         if (subtrees[i] == NULL || *subtrees[i] == '\0')
693             return 0;
694         stlen = strlen(subtrees[i]);
695         if (dnlen >= stlen &&
696             strcasecmp(dn + dnlen - stlen, subtrees[i]) == 0 &&
697             (dnlen == stlen || dn[dnlen - stlen - 1] == ','))
698             return 0;
699     }
700 
701     k5_setmsg(context, EINVAL, _("DN is out of the realm subtree"));
702     return EINVAL;
703 }
704 
705 static krb5_error_code
check_dn_exists(krb5_context context,krb5_ldap_server_handle * ldap_server_handle,const char * dn,krb5_boolean nonkrb_only)706 check_dn_exists(krb5_context context,
707                 krb5_ldap_server_handle *ldap_server_handle,
708                 const char *dn, krb5_boolean nonkrb_only)
709 {
710     krb5_error_code st = 0, tempst;
711     krb5_ldap_context *ldap_context = context->dal_handle->db_context;
712     LDAP *ld = ldap_server_handle->ldap_handle;
713     LDAPMessage *result = NULL, *ent;
714     char *attrs[] = { "krbticketpolicyreference", "krbprincipalname", NULL };
715     char **values;
716 
717     LDAP_SEARCH_1(dn, LDAP_SCOPE_BASE, 0, attrs, IGNORE_STATUS);
718     if (st != LDAP_SUCCESS)
719         return set_ldap_error(context, st, OP_SEARCH);
720 
721     ent = ldap_first_entry(ld, result);
722     CHECK_NULL(ent);
723 
724     values = ldap_get_values(ld, ent, "krbticketpolicyreference");
725     if (values != NULL)
726         ldap_value_free(values);
727 
728     values = ldap_get_values(ld, ent, "krbprincipalname");
729     if (values != NULL) {
730         ldap_value_free(values);
731         if (nonkrb_only) {
732             st = EINVAL;
733             k5_setmsg(context, st, _("ldap object is already kerberized"));
734             goto cleanup;
735         }
736     }
737 
738 cleanup:
739     ldap_msgfree(result);
740     return st;
741 }
742 
743 static krb5_error_code
validate_xargs(krb5_context context,krb5_ldap_server_handle * ldap_server_handle,const xargs_t * xargs,const char * standalone_dn,char * const * subtrees,size_t ntrees)744 validate_xargs(krb5_context context,
745                krb5_ldap_server_handle *ldap_server_handle,
746                const xargs_t *xargs, const char *standalone_dn,
747                char *const *subtrees, size_t ntrees)
748 {
749     krb5_error_code st;
750 
751     if (xargs->dn != NULL) {
752         /* The supplied dn must be within a realm container. */
753         st = check_dn_in_container(context, xargs->dn, subtrees, ntrees);
754         if (st)
755             return st;
756         /* The supplied dn must exist without Kerberos attributes. */
757         st = check_dn_exists(context, ldap_server_handle, xargs->dn, TRUE);
758         if (st)
759             return st;
760     }
761 
762     if (xargs->linkdn != NULL) {
763         /* The supplied linkdn must be within a realm container. */
764         st = check_dn_in_container(context, xargs->linkdn, subtrees, ntrees);
765         if (st)
766             return st;
767         /* The supplied linkdn must exist. */
768         st = check_dn_exists(context, ldap_server_handle, xargs->linkdn,
769                              FALSE);
770         if (st)
771             return st;
772     }
773 
774     if (xargs->containerdn != NULL && standalone_dn != NULL) {
775         /* standalone_dn (likely composed using containerdn) must be within a
776          * container. */
777         st = check_dn_in_container(context, standalone_dn, subtrees, ntrees);
778         if (st)
779             return st;
780     }
781 
782     return 0;
783 }
784 
785 static krb5_error_code
add_alias(krb5_context context,krb5_ldap_context * ldap_context,krb5_ldap_server_handle * ldap_server_handle,krb5_const_principal alias,krb5_const_principal target)786 add_alias(krb5_context context, krb5_ldap_context *ldap_context,
787           krb5_ldap_server_handle *ldap_server_handle,
788           krb5_const_principal alias, krb5_const_principal target)
789 {
790     krb5_error_code st;
791     LDAP *ld = ldap_server_handle->ldap_handle;
792     LDAPMessage *ent, *result = NULL;
793     LDAPMod **mods = NULL;
794     char **canon = NULL, **names = NULL, *user = NULL, *dn = NULL;
795     char *strval[2] = { NULL }, errbuf[1024];
796 
797     st = search_princ(context, ldap_context, ldap_server_handle, target, NULL,
798                       &ent, &result);
799     if (st)
800         goto cleanup;
801     if (ent == NULL) {
802         st = KRB5_KDB_NOENTRY;
803         k5_setmsg(context, st, _("target principal not found"));
804         goto cleanup;
805     }
806 
807     dn = ldap_get_dn(ld, ent);
808     if (dn == NULL) {
809         ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &st);
810         st = set_ldap_error(context, st, 0);
811         goto cleanup;
812     }
813     canon = ldap_get_values(ld, ent, "krbCanonicalName");
814     names = ldap_get_values(ld, ent, "krbPrincipalName");
815 
816     /* Add a krbCanonicalName attribute if one isn't set. */
817     if (canon == NULL) {
818         if (ldap_count_values(names) != 1) {
819             st = KRB5_KDB_INTERNAL_ERROR;
820             k5_setmsg(context, st,
821                       _("cannot add alias to entry with multiple "
822                         "krbPrincipalName values and no krbCanonicalName "
823                         "attribute"));
824             goto cleanup;
825         }
826         strval[0] = names[0];
827         st = krb5_add_str_mem_ldap_mod(&mods, "krbCanonicalName", LDAP_MOD_ADD,
828                                        strval);
829         if (st)
830             goto cleanup;
831     }
832 
833     /* Add a krbPrincipalName value for the alias name. */
834     st = krb5_ldap_unparse_name(context, alias, &user);
835     if (st)
836         goto cleanup;
837     strval[0] = user;
838     st = krb5_add_str_mem_ldap_mod(&mods, "krbPrincipalName", LDAP_MOD_ADD,
839                                    strval);
840     if (st)
841         goto cleanup;
842 
843     st = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
844     if (st == LDAP_TYPE_OR_VALUE_EXISTS) {
845         st = KRB5_KDB_INUSE;
846         goto cleanup;
847     } else if (st != LDAP_SUCCESS) {
848         snprintf(errbuf, sizeof(errbuf), _("Alias modification failed: %s"),
849                  ldap_err2string(st));
850         st = translate_ldap_error(st, OP_MOD);
851         k5_setmsg(context, st, "%s", errbuf);
852         goto cleanup;
853     }
854 
855 cleanup:
856     ldap_msgfree(result);
857     ldap_memfree(dn);
858     ldap_value_free(canon);
859     ldap_value_free(names);
860     krb5_free_unparsed_name(context, user);
861     ldap_mods_free(mods, 1);
862     return st;
863 }
864 
865 krb5_error_code
krb5_ldap_put_principal(krb5_context context,krb5_db_entry * entry,char ** db_args)866 krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
867                         char **db_args)
868 {
869     int                         kerberos_principal_object_type=0;
870     size_t                      l=0, ntrees=0, tre=0;
871     krb5_error_code             st=0;
872     LDAP                        *ld=NULL;
873     LDAPMessage                 *result=NULL, *ent=NULL;
874     char                        **subtreelist = NULL;
875     char                        *user=NULL, *subtree=NULL, *principal_dn=NULL;
876     char                        *strval[10]={NULL}, errbuf[1024];
877     char                        *filtuser=NULL;
878     struct berval               **bersecretkey=NULL;
879     LDAPMod                     **mods=NULL;
880     krb5_boolean                create_standalone=FALSE;
881     krb5_boolean                establish_links=FALSE;
882     char                        *standalone_principal_dn=NULL;
883     krb5_tl_data                *tl_data=NULL;
884     krb5_key_data               **keys=NULL;
885     kdb5_dal_handle             *dal_handle=NULL;
886     krb5_ldap_context           *ldap_context=NULL;
887     krb5_ldap_server_handle     *ldap_server_handle=NULL;
888     krb5_principal              alias_target=NULL;
889     osa_princ_ent_rec           princ_ent = {0};
890     xargs_t                     xargs = {0};
891     char                        *polname = NULL;
892     OPERATION optype;
893     krb5_boolean                found_entry = FALSE;
894     char                        *filter = NULL;
895 
896     /* Clear the global error string */
897     krb5_clear_error_message(context);
898 
899     SETUP_CONTEXT();
900     if (ldap_context->lrparams == NULL || ldap_context->container_dn == NULL)
901         return EINVAL;
902 
903     /* get ldap handle */
904     GET_HANDLE();
905 
906     if (!is_principal_in_realm(ldap_context, entry->princ)) {
907         st = EINVAL;
908         k5_setmsg(context, st,
909                   _("Principal does not belong to the default realm"));
910         goto cleanup;
911     }
912 
913     /* If this is an alias entry, add an alias to the target and return. */
914     st = krb5_dbe_read_alias(context, entry, &alias_target);
915     if (st)
916         goto cleanup;
917     if (alias_target != NULL) {
918         st = add_alias(context, ldap_context, ldap_server_handle, entry->princ,
919                        alias_target);
920         krb5_free_principal(context, alias_target);
921         goto cleanup;
922     }
923 
924     /* get the principal information to act on */
925     st = krb5_ldap_unparse_name(context, entry->princ, &user);
926     if (st)
927         goto cleanup;
928     filtuser = ldap_filter_correct(user);
929     if (filtuser == NULL) {
930         st = ENOMEM;
931         goto cleanup;
932     }
933 
934     /* Identity the type of operation, it can be
935      * add principal or modify principal.
936      * hack if the entry->mask has KRB_PRINCIPAL flag set
937      * then it is a add operation
938      */
939     if (entry->mask & KADM5_PRINCIPAL)
940         optype = ADD_PRINCIPAL;
941     else
942         optype = MODIFY_PRINCIPAL;
943 
944     if (((st=krb5_get_princ_type(context, entry, &kerberos_principal_object_type)) != 0) ||
945         ((st=krb5_get_userdn(context, entry, &principal_dn)) != 0))
946         goto cleanup;
947 
948     if ((st=process_db_args(context, db_args, &xargs, optype)) != 0)
949         goto cleanup;
950 
951     if (entry->mask & KADM5_LOAD) {
952         /*
953          * A load operation is special, will do a mix-in (add krbprinc attrs to
954          * a non-krb object entry) if an object exists with a matching
955          * krbprincipalname attribute so try to find existing object and set
956          * principal_dn.  This assumes that the krbprincipalname attribute is
957          * unique (only one object entry has a particular krbprincipalname
958          * attribute).
959          */
960         st = search_princ(context, ldap_context, ldap_server_handle,
961                           entry->princ, principal_dn, &ent, &result);
962         if (st && st != KRB5_KDB_NOENTRY)
963             goto cleanup;
964         if (ent != NULL && principal_dn == NULL) {
965             /* Remember this DN to be modified later. */
966             principal_dn = ldap_get_dn(ld, ent);
967             if (principal_dn == NULL) {
968                 ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &st);
969                 st = set_ldap_error(context, st, 0);
970                 goto cleanup;
971             }
972         }
973 
974         if (ent == NULL && principal_dn != NULL) {
975             /*
976              * if principal_dn is null then there is code further down to
977              * deal with setting standalone_principal_dn.  Also note that
978              * this will set create_standalone true for
979              * non-mix-in entries which is okay if loading from a dump.
980              */
981             create_standalone = TRUE;
982             standalone_principal_dn = strdup(principal_dn);
983             CHECK_NULL(standalone_principal_dn);
984         }
985     } /* end if (entry->mask & KADM5_LOAD */
986 
987     /* time to generate the DN information with the help of
988      * containerdn, principalcontainerreference or
989      * realmcontainerdn information
990      */
991     if (principal_dn == NULL && xargs.dn == NULL) { /* creation of standalone principal */
992         /* get the subtree information */
993         if (entry->princ->length == 2 && entry->princ->data[0].length == strlen("krbtgt") &&
994             strncmp(entry->princ->data[0].data, "krbtgt", entry->princ->data[0].length) == 0) {
995             /* if the principal is a inter-realm principal, always created in the realm container */
996             subtree = strdup(ldap_context->lrparams->realmdn);
997         } else if (xargs.containerdn) {
998             if ((st=checkattributevalue(ld, xargs.containerdn, NULL, NULL, NULL)) != 0) {
999                 if (st == KRB5_KDB_NOENTRY || st == KRB5_KDB_CONSTRAINT_VIOLATION) {
1000                     int ost = st;
1001                     st = EINVAL;
1002                     k5_wrapmsg(context, ost, st, _("'%s' not found"),
1003                                xargs.containerdn);
1004                 }
1005                 goto cleanup;
1006             }
1007             subtree = strdup(xargs.containerdn);
1008         } else if (ldap_context->lrparams->containerref && strlen(ldap_context->lrparams->containerref) != 0) {
1009             /*
1010              * Here the subtree should be changed with
1011              * principalcontainerreference attribute value
1012              */
1013             subtree = strdup(ldap_context->lrparams->containerref);
1014         } else {
1015             subtree = strdup(ldap_context->lrparams->realmdn);
1016         }
1017         CHECK_NULL(subtree);
1018 
1019         if (asprintf(&standalone_principal_dn, "krbprincipalname=%s,%s",
1020                      filtuser, subtree) < 0)
1021             standalone_principal_dn = NULL;
1022         CHECK_NULL(standalone_principal_dn);
1023         /*
1024          * free subtree when you are done using the subtree
1025          * set the boolean create_standalone to TRUE
1026          */
1027         create_standalone = TRUE;
1028         free(subtree);
1029         subtree = NULL;
1030     }
1031 
1032     /*
1033      * If the DN information is presented by the user, time to
1034      * validate the input to ensure that the DN falls under
1035      * any of the subtrees
1036      */
1037     if (xargs.dn_from_kbd == TRUE) {
1038         st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees);
1039         if (st)
1040             goto cleanup;
1041 
1042         st = validate_xargs(context, ldap_server_handle, &xargs,
1043                             standalone_principal_dn, subtreelist, ntrees);
1044         if (st)
1045             goto cleanup;
1046     }
1047 
1048     if (xargs.linkdn != NULL) {
1049         /*
1050          * link information can be changed using modprinc.
1051          * However, link information can be changed only on the
1052          * standalone kerberos principal objects. A standalone
1053          * kerberos principal object is of type krbprincipal
1054          * structural objectclass.
1055          *
1056          * NOTE: kerberos principals on an ldap object can't be
1057          * linked to other ldap objects.
1058          */
1059         if (optype == MODIFY_PRINCIPAL &&
1060             kerberos_principal_object_type != KDB_STANDALONE_PRINCIPAL_OBJECT) {
1061             st = EINVAL;
1062             snprintf(errbuf, sizeof(errbuf),
1063                      _("link information can not be set/updated as the "
1064                        "kerberos principal belongs to an ldap object"));
1065             k5_setmsg(context, st, "%s", errbuf);
1066             goto cleanup;
1067         }
1068         /*
1069          * Check the link information. If there is already a link
1070          * existing then this operation is not allowed.
1071          */
1072         {
1073             char **linkdns=NULL;
1074             size_t j=0;
1075 
1076             if ((st=krb5_get_linkdn(context, entry, &linkdns)) != 0) {
1077                 snprintf(errbuf, sizeof(errbuf),
1078                          _("Failed getting object references"));
1079                 k5_setmsg(context, st, "%s", errbuf);
1080                 goto cleanup;
1081             }
1082             if (linkdns != NULL) {
1083                 st = EINVAL;
1084                 snprintf(errbuf, sizeof(errbuf),
1085                          _("kerberos principal is already linked to a ldap "
1086                            "object"));
1087                 k5_setmsg(context, st, "%s", errbuf);
1088                 for (j=0; linkdns[j] != NULL; ++j)
1089                     free (linkdns[j]);
1090                 free (linkdns);
1091                 goto cleanup;
1092             }
1093         }
1094 
1095         establish_links = TRUE;
1096     }
1097 
1098     if (entry->mask & KADM5_LAST_SUCCESS) {
1099         memset(strval, 0, sizeof(strval));
1100         if ((strval[0]=getstringtime(entry->last_success)) == NULL)
1101             goto cleanup;
1102         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastSuccessfulAuth", LDAP_MOD_REPLACE, strval)) != 0) {
1103             free (strval[0]);
1104             goto cleanup;
1105         }
1106         free (strval[0]);
1107     }
1108 
1109     if (entry->mask & KADM5_LAST_FAILED) {
1110         memset(strval, 0, sizeof(strval));
1111         if ((strval[0]=getstringtime(entry->last_failed)) == NULL)
1112             goto cleanup;
1113         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastFailedAuth", LDAP_MOD_REPLACE, strval)) != 0) {
1114             free (strval[0]);
1115             goto cleanup;
1116         }
1117         free(strval[0]);
1118     }
1119 
1120     if (entry->mask & KADM5_FAIL_AUTH_COUNT) {
1121         krb5_kvno fail_auth_count;
1122 
1123         fail_auth_count = entry->fail_auth_count;
1124         if (entry->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT)
1125             fail_auth_count++;
1126 
1127         st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
1128                                        LDAP_MOD_REPLACE,
1129                                        fail_auth_count);
1130         if (st != 0)
1131             goto cleanup;
1132     } else if (entry->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT) {
1133         int attr_mask = 0;
1134         krb5_boolean has_fail_count;
1135 
1136         /* Check if the krbLoginFailedCount attribute exists.  (Through
1137          * krb5 1.8.1, it wasn't set in new entries.) */
1138         st = krb5_get_attributes_mask(context, entry, &attr_mask);
1139         if (st != 0)
1140             goto cleanup;
1141         has_fail_count = ((attr_mask & KDB_FAIL_AUTH_COUNT_ATTR) != 0);
1142 
1143         /*
1144          * If the client library and server supports RFC 4525,
1145          * then use it to increment by one the value of the
1146          * krbLoginFailedCount attribute. Otherwise, assert the
1147          * (provided) old value by deleting it before adding.
1148          */
1149 #ifdef LDAP_MOD_INCREMENT
1150         if (ldap_server_handle->server_info->modify_increment &&
1151             has_fail_count) {
1152             st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
1153                                            LDAP_MOD_INCREMENT, 1);
1154             if (st != 0)
1155                 goto cleanup;
1156         } else {
1157 #endif /* LDAP_MOD_INCREMENT */
1158             if (has_fail_count) {
1159                 st = krb5_add_int_mem_ldap_mod(&mods,
1160                                                "krbLoginFailedCount",
1161                                                LDAP_MOD_DELETE,
1162                                                entry->fail_auth_count);
1163                 if (st != 0)
1164                     goto cleanup;
1165             }
1166             st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
1167                                            LDAP_MOD_ADD,
1168                                            entry->fail_auth_count + 1);
1169             if (st != 0)
1170                 goto cleanup;
1171 #ifdef LDAP_MOD_INCREMENT
1172         }
1173 #endif
1174     } else if (optype == ADD_PRINCIPAL) {
1175         /* Initialize krbLoginFailedCount in new entries to help avoid a
1176          * race during the first failed login. */
1177         st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
1178                                        LDAP_MOD_ADD, 0);
1179     }
1180 
1181     if (entry->mask & KADM5_MAX_LIFE) {
1182         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_REPLACE, entry->max_life)) != 0)
1183             goto cleanup;
1184     }
1185 
1186     if (entry->mask & KADM5_MAX_RLIFE) {
1187         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_REPLACE,
1188                                           entry->max_renewable_life)) != 0)
1189             goto cleanup;
1190     }
1191 
1192     if (entry->mask & KADM5_ATTRIBUTES) {
1193         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_REPLACE,
1194                                           entry->attributes)) != 0)
1195             goto cleanup;
1196     }
1197 
1198     if (entry->mask & KADM5_PRINCIPAL) {
1199         memset(strval, 0, sizeof(strval));
1200         strval[0] = user;
1201         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_REPLACE, strval)) != 0)
1202             goto cleanup;
1203     }
1204 
1205     if (entry->mask & KADM5_PRINC_EXPIRE_TIME) {
1206         memset(strval, 0, sizeof(strval));
1207         if ((strval[0]=getstringtime(entry->expiration)) == NULL)
1208             goto cleanup;
1209         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalexpiration", LDAP_MOD_REPLACE, strval)) != 0) {
1210             free (strval[0]);
1211             goto cleanup;
1212         }
1213         free (strval[0]);
1214     }
1215 
1216     if (entry->mask & KADM5_PW_EXPIRATION) {
1217         memset(strval, 0, sizeof(strval));
1218         if ((strval[0]=getstringtime(entry->pw_expiration)) == NULL)
1219             goto cleanup;
1220         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpasswordexpiration",
1221                                           LDAP_MOD_REPLACE,
1222                                           strval)) != 0) {
1223             free (strval[0]);
1224             goto cleanup;
1225         }
1226         free (strval[0]);
1227     }
1228 
1229     if (entry->mask & KADM5_POLICY || entry->mask & KADM5_KEY_HIST) {
1230         memset(&princ_ent, 0, sizeof(princ_ent));
1231         for (tl_data=entry->tl_data; tl_data; tl_data=tl_data->tl_data_next) {
1232             if (tl_data->tl_data_type == KRB5_TL_KADM_DATA) {
1233                 if ((st = krb5_lookup_tl_kadm_data(tl_data, &princ_ent)) != 0) {
1234                     goto cleanup;
1235                 }
1236                 break;
1237             }
1238         }
1239     }
1240 
1241     if (entry->mask & KADM5_POLICY) {
1242         if (princ_ent.aux_attributes & KADM5_POLICY) {
1243             memset(strval, 0, sizeof(strval));
1244             if ((st = krb5_ldap_name_to_policydn (context, princ_ent.policy, &polname)) != 0)
1245                 goto cleanup;
1246             strval[0] = polname;
1247             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_REPLACE, strval)) != 0)
1248                 goto cleanup;
1249         } else {
1250             st = EINVAL;
1251             k5_setmsg(context, st, "Password policy value null");
1252             goto cleanup;
1253         }
1254     } else if (entry->mask & KADM5_LOAD && found_entry == TRUE) {
1255         /*
1256          * a load is special in that existing entries must have attrs that
1257          * removed.
1258          */
1259 
1260         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_REPLACE, NULL)) != 0)
1261             goto cleanup;
1262     }
1263 
1264     if (entry->mask & KADM5_POLICY_CLR) {
1265         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_DELETE, NULL)) != 0)
1266             goto cleanup;
1267     }
1268 
1269     if (entry->mask & KADM5_KEY_HIST) {
1270         bersecretkey = krb5_encode_histkey(&princ_ent);
1271         if (bersecretkey == NULL) {
1272             st = ENOMEM;
1273             goto cleanup;
1274         }
1275 
1276         st = krb5_add_ber_mem_ldap_mod(&mods, "krbpwdhistory",
1277                                        LDAP_MOD_REPLACE | LDAP_MOD_BVALUES,
1278                                        bersecretkey);
1279         if (st != 0)
1280             goto cleanup;
1281         free_berdata(bersecretkey);
1282         bersecretkey = NULL;
1283     }
1284 
1285     if (entry->mask & KADM5_KEY_DATA || entry->mask & KADM5_KVNO) {
1286         krb5_kvno mkvno;
1287 
1288         if ((st=krb5_dbe_lookup_mkvno(context, entry, &mkvno)) != 0)
1289             goto cleanup;
1290         bersecretkey = krb5_encode_krbsecretkey (entry->key_data,
1291                                                  entry->n_key_data, mkvno);
1292 
1293         if (bersecretkey == NULL) {
1294             st = ENOMEM;
1295             goto cleanup;
1296         }
1297         /* An empty list of bervals is only accepted for modify operations,
1298          * not add operations. */
1299         if (bersecretkey[0] != NULL || !create_standalone) {
1300             st = krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey",
1301                                            LDAP_MOD_REPLACE | LDAP_MOD_BVALUES,
1302                                            bersecretkey);
1303             if (st != 0)
1304                 goto cleanup;
1305         }
1306 
1307         /* Update last password change whenever a new key is set */
1308         {
1309             krb5_timestamp last_pw_changed;
1310             if ((st=krb5_dbe_lookup_last_pwd_change(context, entry,
1311                                                     &last_pw_changed)) != 0)
1312                 goto cleanup;
1313 
1314             memset(strval, 0, sizeof(strval));
1315             if ((strval[0] = getstringtime(last_pw_changed)) == NULL)
1316                 goto cleanup;
1317 
1318             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastPwdChange",
1319                                               LDAP_MOD_REPLACE, strval)) != 0) {
1320                 free (strval[0]);
1321                 goto cleanup;
1322             }
1323             free (strval[0]);
1324         }
1325 
1326     } /* Modify Key data ends here */
1327 
1328     /* Set tl_data */
1329     if (entry->tl_data != NULL) {
1330         size_t count = 0;
1331         struct berval **ber_tl_data = NULL;
1332         krb5_tl_data *ptr;
1333         krb5_timestamp unlock_time;
1334 
1335         /* Normalize required auth indicators, but also store them as string
1336          * attributes within krbExtraData. */
1337         st = update_ldap_mod_auth_ind(context, entry, &mods);
1338         if (st != 0)
1339             goto cleanup;
1340 
1341         for (ptr = entry->tl_data; ptr != NULL; ptr = ptr->tl_data_next) {
1342             if (ptr->tl_data_type == KRB5_TL_LAST_PWD_CHANGE
1343 #ifdef SECURID
1344                 || ptr->tl_data_type == KRB5_TL_DB_ARGS
1345 #endif
1346                 || ptr->tl_data_type == KRB5_TL_KADM_DATA
1347                 || ptr->tl_data_type == KDB_TL_USER_INFO
1348                 || ptr->tl_data_type == KRB5_TL_CONSTRAINED_DELEGATION_ACL
1349                 || ptr->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK)
1350                 continue;
1351             count++;
1352         }
1353         if (count != 0) {
1354             size_t j;
1355             ber_tl_data = (struct berval **) calloc (count + 1,
1356                                                      sizeof (struct berval*));
1357             if (ber_tl_data == NULL) {
1358                 st = ENOMEM;
1359                 goto cleanup;
1360             }
1361             for (j = 0, ptr = entry->tl_data; ptr != NULL; ptr = ptr->tl_data_next) {
1362                 /* Ignore tl_data that are stored in separate directory
1363                  * attributes */
1364                 if (ptr->tl_data_type == KRB5_TL_LAST_PWD_CHANGE
1365 #ifdef SECURID
1366                     || ptr->tl_data_type == KRB5_TL_DB_ARGS
1367 #endif
1368                     || ptr->tl_data_type == KRB5_TL_KADM_DATA
1369                     || ptr->tl_data_type == KDB_TL_USER_INFO
1370                     || ptr->tl_data_type == KRB5_TL_CONSTRAINED_DELEGATION_ACL
1371                     || ptr->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK)
1372                     continue;
1373                 if ((st = tl_data2berval (ptr, &ber_tl_data[j])) != 0)
1374                     break;
1375                 j++;
1376             }
1377             if (st == 0) {
1378                 ber_tl_data[count] = NULL;
1379                 st=krb5_add_ber_mem_ldap_mod(&mods, "krbExtraData",
1380                                              LDAP_MOD_REPLACE |
1381                                              LDAP_MOD_BVALUES, ber_tl_data);
1382             }
1383             free_berdata(ber_tl_data);
1384             if (st != 0)
1385                 goto cleanup;
1386         }
1387         if ((st=krb5_dbe_lookup_last_admin_unlock(context, entry,
1388                                                   &unlock_time)) != 0)
1389             goto cleanup;
1390         if (unlock_time != 0) {
1391             /* Update last admin unlock */
1392             memset(strval, 0, sizeof(strval));
1393             if ((strval[0] = getstringtime(unlock_time)) == NULL)
1394                 goto cleanup;
1395 
1396             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastAdminUnlock",
1397                                               LDAP_MOD_REPLACE, strval)) != 0) {
1398                 free (strval[0]);
1399                 goto cleanup;
1400             }
1401             free (strval[0]);
1402         }
1403     }
1404 
1405     /* Directory specific attribute */
1406     if (xargs.tktpolicydn != NULL) {
1407         int tmask=0;
1408 
1409         if (strlen(xargs.tktpolicydn) != 0) {
1410             st = checkattributevalue(ld, xargs.tktpolicydn, "objectclass", policyclass, &tmask);
1411             CHECK_CLASS_VALIDITY(st, tmask, _("ticket policy object value: "));
1412 
1413             strval[0] = xargs.tktpolicydn;
1414             strval[1] = NULL;
1415             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbticketpolicyreference", LDAP_MOD_REPLACE, strval)) != 0)
1416                 goto cleanup;
1417 
1418         } else {
1419             /* if xargs.tktpolicydn is a empty string, then delete
1420              * already existing krbticketpolicyreference attr */
1421             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbticketpolicyreference", LDAP_MOD_DELETE, NULL)) != 0)
1422                 goto cleanup;
1423         }
1424 
1425     }
1426 
1427     if (establish_links == TRUE) {
1428         memset(strval, 0, sizeof(strval));
1429         strval[0] = xargs.linkdn;
1430         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbObjectReferences", LDAP_MOD_REPLACE, strval)) != 0)
1431             goto cleanup;
1432     }
1433 
1434     /*
1435      * in case mods is NULL then return
1436      * not sure but can happen in a modprinc
1437      * so no need to return an error
1438      * addprinc will at least have the principal name
1439      * and the keys passed in
1440      */
1441     if (mods == NULL)
1442         goto cleanup;
1443 
1444     if (create_standalone == TRUE) {
1445         memset(strval, 0, sizeof(strval));
1446         strval[0] = "krbprincipal";
1447         strval[1] = "krbprincipalaux";
1448         strval[2] = "krbTicketPolicyAux";
1449 
1450         if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
1451             goto cleanup;
1452 
1453         st = ldap_add_ext_s(ld, standalone_principal_dn, mods, NULL, NULL);
1454         if (st == LDAP_ALREADY_EXISTS && entry->mask & KADM5_LOAD) {
1455             /* a load operation must replace an existing entry */
1456             st = ldap_delete_ext_s(ld, standalone_principal_dn, NULL, NULL);
1457             if (st != LDAP_SUCCESS) {
1458                 snprintf(errbuf, sizeof(errbuf),
1459                          _("Principal delete failed (trying to replace "
1460                            "entry): %s"), ldap_err2string(st));
1461                 st = translate_ldap_error (st, OP_ADD);
1462                 k5_setmsg(context, st, "%s", errbuf);
1463                 goto cleanup;
1464             } else {
1465                 st = ldap_add_ext_s(ld, standalone_principal_dn, mods, NULL, NULL);
1466             }
1467         }
1468         if (st != LDAP_SUCCESS) {
1469             snprintf(errbuf, sizeof(errbuf), _("Principal add failed: %s"),
1470                      ldap_err2string(st));
1471             st = translate_ldap_error (st, OP_ADD);
1472             k5_setmsg(context, st, "%s", errbuf);
1473             goto cleanup;
1474         }
1475     } else {
1476         /*
1477          * Here existing ldap object is modified and can be related
1478          * to any attribute, so always ensure that the ldap
1479          * object is extended with all the kerberos related
1480          * objectclasses so that there are no constraint
1481          * violations.
1482          */
1483         {
1484             char *attrvalues[] = {"krbprincipalaux", "krbTicketPolicyAux", NULL};
1485             size_t q, r=0;
1486             int p, amask=0;
1487 
1488             if ((st=checkattributevalue(ld, (xargs.dn) ? xargs.dn : principal_dn,
1489                                         "objectclass", attrvalues, &amask)) != 0)
1490                 goto cleanup;
1491 
1492             memset(strval, 0, sizeof(strval));
1493             for (p=1, q=0; p<=2; p<<=1, ++q) {
1494                 if ((p & amask) == 0)
1495                     strval[r++] = attrvalues[q];
1496             }
1497             if (r != 0) {
1498                 if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
1499                     goto cleanup;
1500             }
1501         }
1502         if (xargs.dn != NULL)
1503             st=ldap_modify_ext_s(ld, xargs.dn, mods, NULL, NULL);
1504         else
1505             st = ldap_modify_ext_s(ld, principal_dn, mods, NULL, NULL);
1506 
1507         if (st != LDAP_SUCCESS) {
1508             snprintf(errbuf, sizeof(errbuf), _("User modification failed: %s"),
1509                      ldap_err2string(st));
1510             st = translate_ldap_error (st, OP_MOD);
1511             k5_setmsg(context, st, "%s", errbuf);
1512             goto cleanup;
1513         }
1514 
1515         if (entry->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT)
1516             entry->fail_auth_count++;
1517     }
1518 
1519 cleanup:
1520     free(filter);
1521     if (user)
1522         free(user);
1523 
1524     if (filtuser)
1525         free(filtuser);
1526 
1527     free_xargs(xargs);
1528 
1529     if (standalone_principal_dn)
1530         free(standalone_principal_dn);
1531 
1532     if (principal_dn)
1533         free (principal_dn);
1534 
1535     if (polname != NULL)
1536         free(polname);
1537 
1538     for (tre = 0; tre < ntrees; tre++)
1539         free(subtreelist[tre]);
1540     free(subtreelist);
1541 
1542     if (subtree)
1543         free (subtree);
1544 
1545     if (bersecretkey) {
1546         for (l=0; bersecretkey[l]; ++l) {
1547             if (bersecretkey[l]->bv_val)
1548                 free (bersecretkey[l]->bv_val);
1549             free (bersecretkey[l]);
1550         }
1551         free (bersecretkey);
1552     }
1553 
1554     if (keys)
1555         free (keys);
1556 
1557     ldap_mods_free(mods, 1);
1558     ldap_osa_free_princ_ent(&princ_ent);
1559     ldap_msgfree(result);
1560     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1561     return(st);
1562 }
1563 
1564 krb5_error_code
krb5_read_tkt_policy(krb5_context context,krb5_ldap_context * ldap_context,krb5_db_entry * entries,char * policy)1565 krb5_read_tkt_policy(krb5_context context, krb5_ldap_context *ldap_context,
1566                      krb5_db_entry *entries, char *policy)
1567 {
1568     krb5_error_code             st=0;
1569     int                         mask=0, omask=0;
1570     int                         tkt_mask=(KDB_MAX_LIFE_ATTR | KDB_MAX_RLIFE_ATTR | KDB_TKT_FLAGS_ATTR);
1571     krb5_ldap_policy_params     *tktpoldnparam=NULL;
1572 
1573     if ((st=krb5_get_attributes_mask(context, entries, &mask)) != 0)
1574         goto cleanup;
1575 
1576     if ((mask & tkt_mask) == tkt_mask)
1577         goto cleanup;
1578 
1579     if (policy != NULL) {
1580         st = krb5_ldap_read_policy(context, policy, &tktpoldnparam, &omask);
1581         if (st && st != KRB5_KDB_NOENTRY) {
1582             k5_prependmsg(context, st, _("Error reading ticket policy"));
1583             goto cleanup;
1584         }
1585 
1586         st = 0; /* reset the return status */
1587     }
1588 
1589     if ((mask & KDB_MAX_LIFE_ATTR) == 0) {
1590         if ((omask & KDB_MAX_LIFE_ATTR) ==  KDB_MAX_LIFE_ATTR)
1591             entries->max_life = tktpoldnparam->maxtktlife;
1592         else if (ldap_context->lrparams->max_life)
1593             entries->max_life = ldap_context->lrparams->max_life;
1594     }
1595 
1596     if ((mask & KDB_MAX_RLIFE_ATTR) == 0) {
1597         if ((omask & KDB_MAX_RLIFE_ATTR) == KDB_MAX_RLIFE_ATTR)
1598             entries->max_renewable_life = tktpoldnparam->maxrenewlife;
1599         else if (ldap_context->lrparams->max_renewable_life)
1600             entries->max_renewable_life = ldap_context->lrparams->max_renewable_life;
1601     }
1602 
1603     if ((mask & KDB_TKT_FLAGS_ATTR) == 0) {
1604         if ((omask & KDB_TKT_FLAGS_ATTR) == KDB_TKT_FLAGS_ATTR)
1605             entries->attributes = tktpoldnparam->tktflags;
1606         else if (ldap_context->lrparams->tktflags)
1607             entries->attributes |= ldap_context->lrparams->tktflags;
1608     }
1609     krb5_ldap_free_policy(context, tktpoldnparam);
1610 
1611 cleanup:
1612     return st;
1613 }
1614 
1615 static void
free_ldap_seqof_key_data(ldap_seqof_key_data * keysets,krb5_int16 n_keysets)1616 free_ldap_seqof_key_data(ldap_seqof_key_data *keysets, krb5_int16 n_keysets)
1617 {
1618     int i;
1619 
1620     if (keysets == NULL)
1621         return;
1622 
1623     for (i = 0; i < n_keysets; i++)
1624         k5_free_key_data(keysets[i].n_key_data, keysets[i].key_data);
1625     free(keysets);
1626 }
1627 
1628 /*
1629  * Decode keys from ldap search results.
1630  *
1631  * Arguments:
1632  *  - bvalues
1633  *      The ldap search results containing the key data.
1634  *  - mkvno
1635  *      The master kvno that the keys were encrypted with.
1636  *  - keysets_out
1637  *      The decoded keys in a ldap_seqof_key_data struct.  Must be freed using
1638  *      free_ldap_seqof_key_data.
1639  *  - n_keysets_out
1640  *      The number of entries in keys_out.
1641  *  - total_keys_out
1642  *      An optional argument that if given will be set to the total number of
1643  *      keys found throughout all the entries: sum(keys_out.n_key_data)
1644  *      May be NULL.
1645  */
1646 static krb5_error_code
decode_keys(struct berval ** bvalues,ldap_seqof_key_data ** keysets_out,krb5_int16 * n_keysets_out,krb5_int16 * total_keys_out)1647 decode_keys(struct berval **bvalues, ldap_seqof_key_data **keysets_out,
1648             krb5_int16 *n_keysets_out, krb5_int16 *total_keys_out)
1649 {
1650     krb5_error_code err = 0;
1651     size_t n_keys, i;
1652     krb5_int16 ki, total_keys;
1653     ldap_seqof_key_data *keysets = NULL;
1654 
1655     *keysets_out = NULL;
1656     *n_keysets_out = 0;
1657     if (total_keys_out)
1658         *total_keys_out = 0;
1659 
1660     /* Precount the number of keys. */
1661     for (n_keys = 0, i = 0; bvalues[i] != NULL; i++) {
1662         if (bvalues[i]->bv_len > 0)
1663             n_keys++;
1664     }
1665     if (n_keys > INT16_MAX / 2)
1666         return EOVERFLOW;
1667 
1668     keysets = k5calloc(n_keys, sizeof(ldap_seqof_key_data), &err);
1669     if (keysets == NULL)
1670         goto cleanup;
1671     memset(keysets, 0, n_keys * sizeof(ldap_seqof_key_data));
1672 
1673     for (i = 0, ki = 0, total_keys = 0; bvalues[i] != NULL; i++) {
1674         krb5_data in;
1675 
1676         if (bvalues[i]->bv_len == 0)
1677             continue;
1678         in.length = bvalues[i]->bv_len;
1679         in.data = bvalues[i]->bv_val;
1680 
1681         err = asn1_decode_sequence_of_keys(&in, &keysets[ki]);
1682         if (err)
1683             goto cleanup;
1684 
1685         if (total_keys_out)
1686             total_keys += keysets[ki].n_key_data;
1687         ki++;
1688     }
1689 
1690     if (total_keys_out)
1691         *total_keys_out = total_keys;
1692 
1693     *n_keysets_out = n_keys;
1694     *keysets_out = keysets;
1695     keysets = NULL;
1696     n_keys = 0;
1697 
1698 cleanup:
1699     free_ldap_seqof_key_data(keysets, n_keys);
1700     return err;
1701 }
1702 
1703 krb5_error_code
krb5_decode_krbsecretkey(krb5_context context,krb5_db_entry * entries,struct berval ** bvalues,krb5_kvno * mkvno)1704 krb5_decode_krbsecretkey(krb5_context context, krb5_db_entry *entries,
1705                          struct berval **bvalues, krb5_kvno *mkvno)
1706 {
1707     krb5_key_data *key_data = NULL, *tmp;
1708     krb5_error_code err = 0;
1709     ldap_seqof_key_data *keysets = NULL;
1710     krb5_int16 i, n_keysets = 0, total_keys = 0;
1711 
1712     err = decode_keys(bvalues, &keysets, &n_keysets, &total_keys);
1713     if (err != 0) {
1714         k5_prependmsg(context, err,
1715                       _("unable to decode stored principal key data"));
1716         goto cleanup;
1717     }
1718 
1719     key_data = k5calloc(total_keys, sizeof(krb5_key_data), &err);
1720     if (key_data == NULL)
1721         goto cleanup;
1722     memset(key_data, 0, total_keys * sizeof(krb5_key_data));
1723 
1724     if (n_keysets > 0)
1725         *mkvno = keysets[0].mkvno;
1726 
1727     /* Transfer key data values from keysets to a flat list in entries. */
1728     tmp = key_data;
1729     for (i = 0; i < n_keysets; i++) {
1730         memcpy(tmp, keysets[i].key_data,
1731                sizeof(krb5_key_data) * keysets[i].n_key_data);
1732         tmp += keysets[i].n_key_data;
1733         keysets[i].n_key_data = 0;
1734     }
1735     entries->n_key_data = total_keys;
1736     entries->key_data = key_data;
1737     key_data = NULL;
1738 
1739 cleanup:
1740     free_ldap_seqof_key_data(keysets, n_keysets);
1741     k5_free_key_data(total_keys, key_data);
1742     return err;
1743 }
1744 
1745 static int
compare_osa_pw_hist_ent(const void * left_in,const void * right_in)1746 compare_osa_pw_hist_ent(const void *left_in, const void *right_in)
1747 {
1748     int kvno_left, kvno_right;
1749     osa_pw_hist_ent *left = (osa_pw_hist_ent *)left_in;
1750     osa_pw_hist_ent *right = (osa_pw_hist_ent *)right_in;
1751 
1752     kvno_left = left->n_key_data ? left->key_data[0].key_data_kvno : 0;
1753     kvno_right = right->n_key_data ? right->key_data[0].key_data_kvno : 0;
1754     return kvno_left - kvno_right;
1755 }
1756 
1757 /*
1758  * Decode the key history entries from an LDAP search.
1759  *
1760  * NOTE: the caller must free princ_ent->old_keys even on error.
1761  */
1762 krb5_error_code
krb5_decode_histkey(krb5_context context,struct berval ** bvalues,osa_princ_ent_rec * princ_ent)1763 krb5_decode_histkey(krb5_context context, struct berval **bvalues,
1764                     osa_princ_ent_rec *princ_ent)
1765 {
1766     krb5_error_code err = 0;
1767     krb5_int16 i, n_keysets = 0;
1768     ldap_seqof_key_data *keysets = NULL;
1769 
1770     err = decode_keys(bvalues, &keysets, &n_keysets, NULL);
1771     if (err != 0) {
1772         k5_prependmsg(context, err,
1773                       _("unable to decode stored principal pw history"));
1774         goto cleanup;
1775     }
1776 
1777     princ_ent->old_keys = k5calloc(n_keysets, sizeof(osa_pw_hist_ent), &err);
1778     if (princ_ent->old_keys == NULL)
1779         goto cleanup;
1780     princ_ent->old_key_len = n_keysets;
1781 
1782     if (n_keysets > 0)
1783         princ_ent->admin_history_kvno = keysets[0].mkvno;
1784 
1785     /* Transfer key data pointers from keysets to princ_ent. */
1786     for (i = 0; i < n_keysets; i++) {
1787         princ_ent->old_keys[i].n_key_data = keysets[i].n_key_data;
1788         princ_ent->old_keys[i].key_data = keysets[i].key_data;
1789         keysets[i].n_key_data = 0;
1790         keysets[i].key_data = NULL;
1791     }
1792 
1793     /* Sort the principal entries by kvno in ascending order. */
1794     qsort(princ_ent->old_keys, princ_ent->old_key_len, sizeof(osa_pw_hist_ent),
1795           &compare_osa_pw_hist_ent);
1796 
1797     princ_ent->aux_attributes |= KADM5_KEY_HIST;
1798 
1799     /* Set the next key to the end of the list.  The queue will be lengthened
1800      * if it isn't full yet; the first entry will be replaced if it is full. */
1801     princ_ent->old_key_next = princ_ent->old_key_len;
1802 
1803 cleanup:
1804     free_ldap_seqof_key_data(keysets, n_keysets);
1805     return err;
1806 }
1807 
1808 static char *
getstringtime(krb5_timestamp epochtime)1809 getstringtime(krb5_timestamp epochtime)
1810 {
1811     struct tm           tme;
1812     char                *strtime=NULL;
1813     time_t              posixtime = ts2tt(epochtime);
1814 
1815     if (gmtime_r(&posixtime, &tme) == NULL)
1816         return NULL;
1817 
1818     strtime = calloc(50, 1);
1819     if (strtime == NULL)
1820         return NULL;
1821     if (strftime(strtime, 50, "%Y%m%d%H%M%SZ", &tme) == 0) {
1822         free(strtime);
1823         return NULL;
1824     }
1825     return strtime;
1826 }
1827