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