Imported Upstream version 1.17
[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] = { 0 };
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 static krb5_error_code
655 check_dn_in_container(krb5_context context, const char *dn,
656                       char *const *subtrees, unsigned int ntrees)
657 {
658     unsigned int i;
659     size_t dnlen = strlen(dn), stlen;
660
661     for (i = 0; i < ntrees; i++) {
662         if (subtrees[i] == NULL || *subtrees[i] == '\0')
663             return 0;
664         stlen = strlen(subtrees[i]);
665         if (dnlen >= stlen &&
666             strcasecmp(dn + dnlen - stlen, subtrees[i]) == 0 &&
667             (dnlen == stlen || dn[dnlen - stlen - 1] == ','))
668             return 0;
669     }
670
671     k5_setmsg(context, EINVAL, _("DN is out of the realm subtree"));
672     return EINVAL;
673 }
674
675 static krb5_error_code
676 check_dn_exists(krb5_context context,
677                 krb5_ldap_server_handle *ldap_server_handle,
678                 const char *dn, krb5_boolean nonkrb_only)
679 {
680     krb5_error_code st = 0, tempst;
681     krb5_ldap_context *ldap_context = context->dal_handle->db_context;
682     LDAP *ld = ldap_server_handle->ldap_handle;
683     LDAPMessage *result = NULL, *ent;
684     char *attrs[] = { "krbticketpolicyreference", "krbprincipalname", NULL };
685     char **values;
686
687     LDAP_SEARCH_1(dn, LDAP_SCOPE_BASE, 0, attrs, IGNORE_STATUS);
688     if (st != LDAP_SUCCESS)
689         return set_ldap_error(context, st, OP_SEARCH);
690
691     ent = ldap_first_entry(ld, result);
692     CHECK_NULL(ent);
693
694     values = ldap_get_values(ld, ent, "krbticketpolicyreference");
695     if (values != NULL)
696         ldap_value_free(values);
697
698     values = ldap_get_values(ld, ent, "krbprincipalname");
699     if (values != NULL) {
700         ldap_value_free(values);
701         if (nonkrb_only) {
702             st = EINVAL;
703             k5_setmsg(context, st, _("ldap object is already kerberized"));
704             goto cleanup;
705         }
706     }
707
708 cleanup:
709     ldap_msgfree(result);
710     return st;
711 }
712
713 static krb5_error_code
714 validate_xargs(krb5_context context,
715                krb5_ldap_server_handle *ldap_server_handle,
716                const xargs_t *xargs, const char *standalone_dn,
717                char *const *subtrees, unsigned int ntrees)
718 {
719     krb5_error_code st;
720
721     if (xargs->dn != NULL) {
722         /* The supplied dn must be within a realm container. */
723         st = check_dn_in_container(context, xargs->dn, subtrees, ntrees);
724         if (st)
725             return st;
726         /* The supplied dn must exist without Kerberos attributes. */
727         st = check_dn_exists(context, ldap_server_handle, xargs->dn, TRUE);
728         if (st)
729             return st;
730     }
731
732     if (xargs->linkdn != NULL) {
733         /* The supplied linkdn must be within a realm container. */
734         st = check_dn_in_container(context, xargs->linkdn, subtrees, ntrees);
735         if (st)
736             return st;
737         /* The supplied linkdn must exist. */
738         st = check_dn_exists(context, ldap_server_handle, xargs->linkdn,
739                              FALSE);
740         if (st)
741             return st;
742     }
743
744     if (xargs->containerdn != NULL && standalone_dn != NULL) {
745         /* standalone_dn (likely composed using containerdn) must be within a
746          * container. */
747         st = check_dn_in_container(context, standalone_dn, subtrees, ntrees);
748         if (st)
749             return st;
750     }
751
752     return 0;
753 }
754
755 krb5_error_code
756 krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
757                         char **db_args)
758 {
759     int                         l=0, kerberos_principal_object_type=0;
760     unsigned int                ntrees=0, tre=0;
761     krb5_error_code             st=0, tempst=0;
762     LDAP                        *ld=NULL;
763     LDAPMessage                 *result=NULL, *ent=NULL;
764     char                        **subtreelist = NULL;
765     char                        *user=NULL, *subtree=NULL, *principal_dn=NULL;
766     char                        *strval[10]={NULL}, errbuf[1024];
767     char                        *filtuser=NULL;
768     struct berval               **bersecretkey=NULL;
769     LDAPMod                     **mods=NULL;
770     krb5_boolean                create_standalone=FALSE;
771     krb5_boolean                establish_links=FALSE;
772     char                        *standalone_principal_dn=NULL;
773     krb5_tl_data                *tl_data=NULL;
774     krb5_key_data               **keys=NULL;
775     kdb5_dal_handle             *dal_handle=NULL;
776     krb5_ldap_context           *ldap_context=NULL;
777     krb5_ldap_server_handle     *ldap_server_handle=NULL;
778     osa_princ_ent_rec           princ_ent = {0};
779     xargs_t                     xargs = {0};
780     char                        *polname = NULL;
781     OPERATION optype;
782     krb5_boolean                found_entry = FALSE;
783
784     /* Clear the global error string */
785     krb5_clear_error_message(context);
786
787     SETUP_CONTEXT();
788     if (ldap_context->lrparams == NULL || ldap_context->container_dn == NULL)
789         return EINVAL;
790
791     /* get ldap handle */
792     GET_HANDLE();
793
794     if (!is_principal_in_realm(ldap_context, entry->princ)) {
795         st = EINVAL;
796         k5_setmsg(context, st,
797                   _("Principal does not belong to the default realm"));
798         goto cleanup;
799     }
800
801     /* get the principal information to act on */
802     if (((st=krb5_unparse_name(context, entry->princ, &user)) != 0) ||
803         ((st=krb5_ldap_unparse_principal_name(user)) != 0))
804         goto cleanup;
805     filtuser = ldap_filter_correct(user);
806     if (filtuser == NULL) {
807         st = ENOMEM;
808         goto cleanup;
809     }
810
811     /* Identity the type of operation, it can be
812      * add principal or modify principal.
813      * hack if the entry->mask has KRB_PRINCIPAL flag set
814      * then it is a add operation
815      */
816     if (entry->mask & KADM5_PRINCIPAL)
817         optype = ADD_PRINCIPAL;
818     else
819         optype = MODIFY_PRINCIPAL;
820
821     if (((st=krb5_get_princ_type(context, entry, &kerberos_principal_object_type)) != 0) ||
822         ((st=krb5_get_userdn(context, entry, &principal_dn)) != 0))
823         goto cleanup;
824
825     if ((st=process_db_args(context, db_args, &xargs, optype)) != 0)
826         goto cleanup;
827
828     if (entry->mask & KADM5_LOAD) {
829         unsigned int     tree = 0;
830         int              numlentries = 0;
831         char             *filter = NULL;
832
833         /*  A load operation is special, will do a mix-in (add krbprinc
834          *  attrs to a non-krb object entry) if an object exists with a
835          *  matching krbprincipalname attribute so try to find existing
836          *  object and set principal_dn.  This assumes that the
837          *  krbprincipalname attribute is unique (only one object entry has
838          *  a particular krbprincipalname attribute).
839          */
840         if (asprintf(&filter, FILTER"%s))", filtuser) < 0) {
841             filter = NULL;
842             st = ENOMEM;
843             goto cleanup;
844         }
845
846         /* get the current subtree list */
847         if ((st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees)) != 0)
848             goto cleanup;
849
850         found_entry = FALSE;
851         /* search for entry with matching krbprincipalname attribute */
852         for (tree = 0; found_entry == FALSE && tree < ntrees; ++tree) {
853             if (principal_dn == NULL) {
854                 LDAP_SEARCH_1(subtreelist[tree], ldap_context->lrparams->search_scope, filter, principal_attributes, IGNORE_STATUS);
855             } else {
856                 /* just look for entry with principal_dn */
857                 LDAP_SEARCH_1(principal_dn, LDAP_SCOPE_BASE, filter, principal_attributes, IGNORE_STATUS);
858             }
859             if (st == LDAP_SUCCESS) {
860                 numlentries = ldap_count_entries(ld, result);
861                 if (numlentries > 1) {
862                     free(filter);
863                     st = EINVAL;
864                     k5_setmsg(context, st,
865                               _("operation can not continue, more than one "
866                                 "entry with principal name \"%s\" found"),
867                               user);
868                     goto cleanup;
869                 } else if (numlentries == 1) {
870                     found_entry = TRUE;
871                     if (principal_dn == NULL) {
872                         ent = ldap_first_entry(ld, result);
873                         if (ent != NULL) {
874                             /* setting principal_dn will cause that entry to be modified further down */
875                             if ((principal_dn = ldap_get_dn(ld, ent)) == NULL) {
876                                 ldap_get_option (ld, LDAP_OPT_RESULT_CODE, &st);
877                                 st = set_ldap_error (context, st, 0);
878                                 free(filter);
879                                 goto cleanup;
880                             }
881                         }
882                     }
883                 }
884             } else if (st != LDAP_NO_SUCH_OBJECT) {
885                 /* could not perform search, return with failure */
886                 st = set_ldap_error (context, st, 0);
887                 free(filter);
888                 goto cleanup;
889             }
890             ldap_msgfree(result);
891             result = NULL;
892             /*
893              * If it isn't found then assume a standalone princ entry is to
894              * be created.
895              */
896         } /* end for (tree = 0; principal_dn == ... */
897
898         free(filter);
899
900         if (found_entry == FALSE && principal_dn != NULL) {
901             /*
902              * if principal_dn is null then there is code further down to
903              * deal with setting standalone_principal_dn.  Also note that
904              * this will set create_standalone true for
905              * non-mix-in entries which is okay if loading from a dump.
906              */
907             create_standalone = TRUE;
908             standalone_principal_dn = strdup(principal_dn);
909             CHECK_NULL(standalone_principal_dn);
910         }
911     } /* end if (entry->mask & KADM5_LOAD */
912
913     /* time to generate the DN information with the help of
914      * containerdn, principalcontainerreference or
915      * realmcontainerdn information
916      */
917     if (principal_dn == NULL && xargs.dn == NULL) { /* creation of standalone principal */
918         /* get the subtree information */
919         if (entry->princ->length == 2 && entry->princ->data[0].length == strlen("krbtgt") &&
920             strncmp(entry->princ->data[0].data, "krbtgt", entry->princ->data[0].length) == 0) {
921             /* if the principal is a inter-realm principal, always created in the realm container */
922             subtree = strdup(ldap_context->lrparams->realmdn);
923         } else if (xargs.containerdn) {
924             if ((st=checkattributevalue(ld, xargs.containerdn, NULL, NULL, NULL)) != 0) {
925                 if (st == KRB5_KDB_NOENTRY || st == KRB5_KDB_CONSTRAINT_VIOLATION) {
926                     int ost = st;
927                     st = EINVAL;
928                     k5_wrapmsg(context, ost, st, _("'%s' not found"),
929                                xargs.containerdn);
930                 }
931                 goto cleanup;
932             }
933             subtree = strdup(xargs.containerdn);
934         } else if (ldap_context->lrparams->containerref && strlen(ldap_context->lrparams->containerref) != 0) {
935             /*
936              * Here the subtree should be changed with
937              * principalcontainerreference attribute value
938              */
939             subtree = strdup(ldap_context->lrparams->containerref);
940         } else {
941             subtree = strdup(ldap_context->lrparams->realmdn);
942         }
943         CHECK_NULL(subtree);
944
945         if (asprintf(&standalone_principal_dn, "krbprincipalname=%s,%s",
946                      filtuser, subtree) < 0)
947             standalone_principal_dn = NULL;
948         CHECK_NULL(standalone_principal_dn);
949         /*
950          * free subtree when you are done using the subtree
951          * set the boolean create_standalone to TRUE
952          */
953         create_standalone = TRUE;
954         free(subtree);
955         subtree = NULL;
956     }
957
958     /*
959      * If the DN information is presented by the user, time to
960      * validate the input to ensure that the DN falls under
961      * any of the subtrees
962      */
963     if (xargs.dn_from_kbd == TRUE) {
964         /* Get the current subtree list if we haven't already done so. */
965         if (subtreelist == NULL) {
966             st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees);
967             if (st)
968                 goto cleanup;
969         }
970
971         st = validate_xargs(context, ldap_server_handle, &xargs,
972                             standalone_principal_dn, subtreelist, ntrees);
973         if (st)
974             goto cleanup;
975     }
976
977     if (xargs.linkdn != NULL) {
978         /*
979          * link information can be changed using modprinc.
980          * However, link information can be changed only on the
981          * standalone kerberos principal objects. A standalone
982          * kerberos principal object is of type krbprincipal
983          * structural objectclass.
984          *
985          * NOTE: kerberos principals on an ldap object can't be
986          * linked to other ldap objects.
987          */
988         if (optype == MODIFY_PRINCIPAL &&
989             kerberos_principal_object_type != KDB_STANDALONE_PRINCIPAL_OBJECT) {
990             st = EINVAL;
991             snprintf(errbuf, sizeof(errbuf),
992                      _("link information can not be set/updated as the "
993                        "kerberos principal belongs to an ldap object"));
994             k5_setmsg(context, st, "%s", errbuf);
995             goto cleanup;
996         }
997         /*
998          * Check the link information. If there is already a link
999          * existing then this operation is not allowed.
1000          */
1001         {
1002             char **linkdns=NULL;
1003             int  j=0;
1004
1005             if ((st=krb5_get_linkdn(context, entry, &linkdns)) != 0) {
1006                 snprintf(errbuf, sizeof(errbuf),
1007                          _("Failed getting object references"));
1008                 k5_setmsg(context, st, "%s", errbuf);
1009                 goto cleanup;
1010             }
1011             if (linkdns != NULL) {
1012                 st = EINVAL;
1013                 snprintf(errbuf, sizeof(errbuf),
1014                          _("kerberos principal is already linked to a ldap "
1015                            "object"));
1016                 k5_setmsg(context, st, "%s", errbuf);
1017                 for (j=0; linkdns[j] != NULL; ++j)
1018                     free (linkdns[j]);
1019                 free (linkdns);
1020                 goto cleanup;
1021             }
1022         }
1023
1024         establish_links = TRUE;
1025     }
1026
1027     if (entry->mask & KADM5_LAST_SUCCESS) {
1028         memset(strval, 0, sizeof(strval));
1029         if ((strval[0]=getstringtime(entry->last_success)) == NULL)
1030             goto cleanup;
1031         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastSuccessfulAuth", LDAP_MOD_REPLACE, strval)) != 0) {
1032             free (strval[0]);
1033             goto cleanup;
1034         }
1035         free (strval[0]);
1036     }
1037
1038     if (entry->mask & KADM5_LAST_FAILED) {
1039         memset(strval, 0, sizeof(strval));
1040         if ((strval[0]=getstringtime(entry->last_failed)) == NULL)
1041             goto cleanup;
1042         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastFailedAuth", LDAP_MOD_REPLACE, strval)) != 0) {
1043             free (strval[0]);
1044             goto cleanup;
1045         }
1046         free(strval[0]);
1047     }
1048
1049     if (entry->mask & KADM5_FAIL_AUTH_COUNT) {
1050         krb5_kvno fail_auth_count;
1051
1052         fail_auth_count = entry->fail_auth_count;
1053         if (entry->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT)
1054             fail_auth_count++;
1055
1056         st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
1057                                        LDAP_MOD_REPLACE,
1058                                        fail_auth_count);
1059         if (st != 0)
1060             goto cleanup;
1061     } else if (entry->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT) {
1062         int attr_mask = 0;
1063         krb5_boolean has_fail_count;
1064
1065         /* Check if the krbLoginFailedCount attribute exists.  (Through
1066          * krb5 1.8.1, it wasn't set in new entries.) */
1067         st = krb5_get_attributes_mask(context, entry, &attr_mask);
1068         if (st != 0)
1069             goto cleanup;
1070         has_fail_count = ((attr_mask & KDB_FAIL_AUTH_COUNT_ATTR) != 0);
1071
1072         /*
1073          * If the client library and server supports RFC 4525,
1074          * then use it to increment by one the value of the
1075          * krbLoginFailedCount attribute. Otherwise, assert the
1076          * (provided) old value by deleting it before adding.
1077          */
1078 #ifdef LDAP_MOD_INCREMENT
1079         if (ldap_server_handle->server_info->modify_increment &&
1080             has_fail_count) {
1081             st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
1082                                            LDAP_MOD_INCREMENT, 1);
1083             if (st != 0)
1084                 goto cleanup;
1085         } else {
1086 #endif /* LDAP_MOD_INCREMENT */
1087             if (has_fail_count) {
1088                 st = krb5_add_int_mem_ldap_mod(&mods,
1089                                                "krbLoginFailedCount",
1090                                                LDAP_MOD_DELETE,
1091                                                entry->fail_auth_count);
1092                 if (st != 0)
1093                     goto cleanup;
1094             }
1095             st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
1096                                            LDAP_MOD_ADD,
1097                                            entry->fail_auth_count + 1);
1098             if (st != 0)
1099                 goto cleanup;
1100 #ifdef LDAP_MOD_INCREMENT
1101         }
1102 #endif
1103     } else if (optype == ADD_PRINCIPAL) {
1104         /* Initialize krbLoginFailedCount in new entries to help avoid a
1105          * race during the first failed login. */
1106         st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount",
1107                                        LDAP_MOD_ADD, 0);
1108     }
1109
1110     if (entry->mask & KADM5_MAX_LIFE) {
1111         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_REPLACE, entry->max_life)) != 0)
1112             goto cleanup;
1113     }
1114
1115     if (entry->mask & KADM5_MAX_RLIFE) {
1116         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_REPLACE,
1117                                           entry->max_renewable_life)) != 0)
1118             goto cleanup;
1119     }
1120
1121     if (entry->mask & KADM5_ATTRIBUTES) {
1122         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_REPLACE,
1123                                           entry->attributes)) != 0)
1124             goto cleanup;
1125     }
1126
1127     if (entry->mask & KADM5_PRINCIPAL) {
1128         memset(strval, 0, sizeof(strval));
1129         strval[0] = user;
1130         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_REPLACE, strval)) != 0)
1131             goto cleanup;
1132     }
1133
1134     if (entry->mask & KADM5_PRINC_EXPIRE_TIME) {
1135         memset(strval, 0, sizeof(strval));
1136         if ((strval[0]=getstringtime(entry->expiration)) == NULL)
1137             goto cleanup;
1138         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalexpiration", LDAP_MOD_REPLACE, strval)) != 0) {
1139             free (strval[0]);
1140             goto cleanup;
1141         }
1142         free (strval[0]);
1143     }
1144
1145     if (entry->mask & KADM5_PW_EXPIRATION) {
1146         memset(strval, 0, sizeof(strval));
1147         if ((strval[0]=getstringtime(entry->pw_expiration)) == NULL)
1148             goto cleanup;
1149         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpasswordexpiration",
1150                                           LDAP_MOD_REPLACE,
1151                                           strval)) != 0) {
1152             free (strval[0]);
1153             goto cleanup;
1154         }
1155         free (strval[0]);
1156     }
1157
1158     if (entry->mask & KADM5_POLICY || entry->mask & KADM5_KEY_HIST) {
1159         memset(&princ_ent, 0, sizeof(princ_ent));
1160         for (tl_data=entry->tl_data; tl_data; tl_data=tl_data->tl_data_next) {
1161             if (tl_data->tl_data_type == KRB5_TL_KADM_DATA) {
1162                 if ((st = krb5_lookup_tl_kadm_data(tl_data, &princ_ent)) != 0) {
1163                     goto cleanup;
1164                 }
1165                 break;
1166             }
1167         }
1168     }
1169
1170     if (entry->mask & KADM5_POLICY) {
1171         if (princ_ent.aux_attributes & KADM5_POLICY) {
1172             memset(strval, 0, sizeof(strval));
1173             if ((st = krb5_ldap_name_to_policydn (context, princ_ent.policy, &polname)) != 0)
1174                 goto cleanup;
1175             strval[0] = polname;
1176             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_REPLACE, strval)) != 0)
1177                 goto cleanup;
1178         } else {
1179             st = EINVAL;
1180             k5_setmsg(context, st, "Password policy value null");
1181             goto cleanup;
1182         }
1183     } else if (entry->mask & KADM5_LOAD && found_entry == TRUE) {
1184         /*
1185          * a load is special in that existing entries must have attrs that
1186          * removed.
1187          */
1188
1189         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_REPLACE, NULL)) != 0)
1190             goto cleanup;
1191     }
1192
1193     if (entry->mask & KADM5_POLICY_CLR) {
1194         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_DELETE, NULL)) != 0)
1195             goto cleanup;
1196     }
1197
1198     if (entry->mask & KADM5_KEY_HIST) {
1199         bersecretkey = krb5_encode_histkey(&princ_ent);
1200         if (bersecretkey == NULL) {
1201             st = ENOMEM;
1202             goto cleanup;
1203         }
1204
1205         st = krb5_add_ber_mem_ldap_mod(&mods, "krbpwdhistory",
1206                                        LDAP_MOD_REPLACE | LDAP_MOD_BVALUES,
1207                                        bersecretkey);
1208         if (st != 0)
1209             goto cleanup;
1210         free_berdata(bersecretkey);
1211         bersecretkey = NULL;
1212     }
1213
1214     if (entry->mask & KADM5_KEY_DATA || entry->mask & KADM5_KVNO) {
1215         krb5_kvno mkvno;
1216
1217         if ((st=krb5_dbe_lookup_mkvno(context, entry, &mkvno)) != 0)
1218             goto cleanup;
1219         bersecretkey = krb5_encode_krbsecretkey (entry->key_data,
1220                                                  entry->n_key_data, mkvno);
1221
1222         if (bersecretkey == NULL) {
1223             st = ENOMEM;
1224             goto cleanup;
1225         }
1226         /* An empty list of bervals is only accepted for modify operations,
1227          * not add operations. */
1228         if (bersecretkey[0] != NULL || !create_standalone) {
1229             st = krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey",
1230                                            LDAP_MOD_REPLACE | LDAP_MOD_BVALUES,
1231                                            bersecretkey);
1232             if (st != 0)
1233                 goto cleanup;
1234         }
1235
1236         if (!(entry->mask & KADM5_PRINCIPAL)) {
1237             memset(strval, 0, sizeof(strval));
1238             if ((strval[0]=getstringtime(entry->pw_expiration)) == NULL)
1239                 goto cleanup;
1240             if ((st=krb5_add_str_mem_ldap_mod(&mods,
1241                                               "krbpasswordexpiration",
1242                                               LDAP_MOD_REPLACE, strval)) != 0) {
1243                 free (strval[0]);
1244                 goto cleanup;
1245             }
1246             free (strval[0]);
1247         }
1248
1249         /* Update last password change whenever a new key is set */
1250         {
1251             krb5_timestamp last_pw_changed;
1252             if ((st=krb5_dbe_lookup_last_pwd_change(context, entry,
1253                                                     &last_pw_changed)) != 0)
1254                 goto cleanup;
1255
1256             memset(strval, 0, sizeof(strval));
1257             if ((strval[0] = getstringtime(last_pw_changed)) == NULL)
1258                 goto cleanup;
1259
1260             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastPwdChange",
1261                                               LDAP_MOD_REPLACE, strval)) != 0) {
1262                 free (strval[0]);
1263                 goto cleanup;
1264             }
1265             free (strval[0]);
1266         }
1267
1268     } /* Modify Key data ends here */
1269
1270     /* Auth indicators will also be stored in krbExtraData when processing
1271      * tl_data. */
1272     st = update_ldap_mod_auth_ind(context, entry, &mods);
1273     if (st != 0)
1274         goto cleanup;
1275
1276     /* Set tl_data */
1277     if (entry->tl_data != NULL) {
1278         int count = 0;
1279         struct berval **ber_tl_data = NULL;
1280         krb5_tl_data *ptr;
1281         krb5_timestamp unlock_time;
1282         for (ptr = entry->tl_data; ptr != NULL; ptr = ptr->tl_data_next) {
1283             if (ptr->tl_data_type == KRB5_TL_LAST_PWD_CHANGE
1284 #ifdef SECURID
1285                 || ptr->tl_data_type == KRB5_TL_DB_ARGS
1286 #endif
1287                 || ptr->tl_data_type == KRB5_TL_KADM_DATA
1288                 || ptr->tl_data_type == KDB_TL_USER_INFO
1289                 || ptr->tl_data_type == KRB5_TL_CONSTRAINED_DELEGATION_ACL
1290                 || ptr->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK)
1291                 continue;
1292             count++;
1293         }
1294         if (count != 0) {
1295             int j;
1296             ber_tl_data = (struct berval **) calloc (count + 1,
1297                                                      sizeof (struct berval*));
1298             if (ber_tl_data == NULL) {
1299                 st = ENOMEM;
1300                 goto cleanup;
1301             }
1302             for (j = 0, ptr = entry->tl_data; ptr != NULL; ptr = ptr->tl_data_next) {
1303                 /* Ignore tl_data that are stored in separate directory
1304                  * attributes */
1305                 if (ptr->tl_data_type == KRB5_TL_LAST_PWD_CHANGE
1306 #ifdef SECURID
1307                     || ptr->tl_data_type == KRB5_TL_DB_ARGS
1308 #endif
1309                     || ptr->tl_data_type == KRB5_TL_KADM_DATA
1310                     || ptr->tl_data_type == KDB_TL_USER_INFO
1311                     || ptr->tl_data_type == KRB5_TL_CONSTRAINED_DELEGATION_ACL
1312                     || ptr->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK)
1313                     continue;
1314                 if ((st = tl_data2berval (ptr, &ber_tl_data[j])) != 0)
1315                     break;
1316                 j++;
1317             }
1318             if (st == 0) {
1319                 ber_tl_data[count] = NULL;
1320                 st=krb5_add_ber_mem_ldap_mod(&mods, "krbExtraData",
1321                                              LDAP_MOD_REPLACE |
1322                                              LDAP_MOD_BVALUES, ber_tl_data);
1323             }
1324             free_berdata(ber_tl_data);
1325             if (st != 0)
1326                 goto cleanup;
1327         }
1328         if ((st=krb5_dbe_lookup_last_admin_unlock(context, entry,
1329                                                   &unlock_time)) != 0)
1330             goto cleanup;
1331         if (unlock_time != 0) {
1332             /* Update last admin unlock */
1333             memset(strval, 0, sizeof(strval));
1334             if ((strval[0] = getstringtime(unlock_time)) == NULL)
1335                 goto cleanup;
1336
1337             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastAdminUnlock",
1338                                               LDAP_MOD_REPLACE, strval)) != 0) {
1339                 free (strval[0]);
1340                 goto cleanup;
1341             }
1342             free (strval[0]);
1343         }
1344     }
1345
1346     /* Directory specific attribute */
1347     if (xargs.tktpolicydn != NULL) {
1348         int tmask=0;
1349
1350         if (strlen(xargs.tktpolicydn) != 0) {
1351             st = checkattributevalue(ld, xargs.tktpolicydn, "objectclass", policyclass, &tmask);
1352             CHECK_CLASS_VALIDITY(st, tmask, _("ticket policy object value: "));
1353
1354             strval[0] = xargs.tktpolicydn;
1355             strval[1] = NULL;
1356             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbticketpolicyreference", LDAP_MOD_REPLACE, strval)) != 0)
1357                 goto cleanup;
1358
1359         } else {
1360             /* if xargs.tktpolicydn is a empty string, then delete
1361              * already existing krbticketpolicyreference attr */
1362             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbticketpolicyreference", LDAP_MOD_DELETE, NULL)) != 0)
1363                 goto cleanup;
1364         }
1365
1366     }
1367
1368     if (establish_links == TRUE) {
1369         memset(strval, 0, sizeof(strval));
1370         strval[0] = xargs.linkdn;
1371         if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbObjectReferences", LDAP_MOD_REPLACE, strval)) != 0)
1372             goto cleanup;
1373     }
1374
1375     /*
1376      * in case mods is NULL then return
1377      * not sure but can happen in a modprinc
1378      * so no need to return an error
1379      * addprinc will at least have the principal name
1380      * and the keys passed in
1381      */
1382     if (mods == NULL)
1383         goto cleanup;
1384
1385     if (create_standalone == TRUE) {
1386         memset(strval, 0, sizeof(strval));
1387         strval[0] = "krbprincipal";
1388         strval[1] = "krbprincipalaux";
1389         strval[2] = "krbTicketPolicyAux";
1390
1391         if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
1392             goto cleanup;
1393
1394         st = ldap_add_ext_s(ld, standalone_principal_dn, mods, NULL, NULL);
1395         if (st == LDAP_ALREADY_EXISTS && entry->mask & KADM5_LOAD) {
1396             /* a load operation must replace an existing entry */
1397             st = ldap_delete_ext_s(ld, standalone_principal_dn, NULL, NULL);
1398             if (st != LDAP_SUCCESS) {
1399                 snprintf(errbuf, sizeof(errbuf),
1400                          _("Principal delete failed (trying to replace "
1401                            "entry): %s"), ldap_err2string(st));
1402                 st = translate_ldap_error (st, OP_ADD);
1403                 k5_setmsg(context, st, "%s", errbuf);
1404                 goto cleanup;
1405             } else {
1406                 st = ldap_add_ext_s(ld, standalone_principal_dn, mods, NULL, NULL);
1407             }
1408         }
1409         if (st != LDAP_SUCCESS) {
1410             snprintf(errbuf, sizeof(errbuf), _("Principal add failed: %s"),
1411                      ldap_err2string(st));
1412             st = translate_ldap_error (st, OP_ADD);
1413             k5_setmsg(context, st, "%s", errbuf);
1414             goto cleanup;
1415         }
1416     } else {
1417         /*
1418          * Here existing ldap object is modified and can be related
1419          * to any attribute, so always ensure that the ldap
1420          * object is extended with all the kerberos related
1421          * objectclasses so that there are no constraint
1422          * violations.
1423          */
1424         {
1425             char *attrvalues[] = {"krbprincipalaux", "krbTicketPolicyAux", NULL};
1426             int p, q, r=0, amask=0;
1427
1428             if ((st=checkattributevalue(ld, (xargs.dn) ? xargs.dn : principal_dn,
1429                                         "objectclass", attrvalues, &amask)) != 0)
1430                 goto cleanup;
1431
1432             memset(strval, 0, sizeof(strval));
1433             for (p=1, q=0; p<=2; p<<=1, ++q) {
1434                 if ((p & amask) == 0)
1435                     strval[r++] = attrvalues[q];
1436             }
1437             if (r != 0) {
1438                 if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
1439                     goto cleanup;
1440             }
1441         }
1442         if (xargs.dn != NULL)
1443             st=ldap_modify_ext_s(ld, xargs.dn, mods, NULL, NULL);
1444         else
1445             st = ldap_modify_ext_s(ld, principal_dn, mods, NULL, NULL);
1446
1447         if (st != LDAP_SUCCESS) {
1448             snprintf(errbuf, sizeof(errbuf), _("User modification failed: %s"),
1449                      ldap_err2string(st));
1450             st = translate_ldap_error (st, OP_MOD);
1451             k5_setmsg(context, st, "%s", errbuf);
1452             goto cleanup;
1453         }
1454
1455         if (entry->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT)
1456             entry->fail_auth_count++;
1457     }
1458
1459 cleanup:
1460     if (user)
1461         free(user);
1462
1463     if (filtuser)
1464         free(filtuser);
1465
1466     free_xargs(xargs);
1467
1468     if (standalone_principal_dn)
1469         free(standalone_principal_dn);
1470
1471     if (principal_dn)
1472         free (principal_dn);
1473
1474     if (polname != NULL)
1475         free(polname);
1476
1477     for (tre = 0; tre < ntrees; tre++)
1478         free(subtreelist[tre]);
1479     free(subtreelist);
1480
1481     if (subtree)
1482         free (subtree);
1483
1484     if (bersecretkey) {
1485         for (l=0; bersecretkey[l]; ++l) {
1486             if (bersecretkey[l]->bv_val)
1487                 free (bersecretkey[l]->bv_val);
1488             free (bersecretkey[l]);
1489         }
1490         free (bersecretkey);
1491     }
1492
1493     if (keys)
1494         free (keys);
1495
1496     ldap_mods_free(mods, 1);
1497     ldap_osa_free_princ_ent(&princ_ent);
1498     ldap_msgfree(result);
1499     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1500     return(st);
1501 }
1502
1503 krb5_error_code
1504 krb5_read_tkt_policy(krb5_context context, krb5_ldap_context *ldap_context,
1505                      krb5_db_entry *entries, char *policy)
1506 {
1507     krb5_error_code             st=0;
1508     int                         mask=0, omask=0;
1509     int                         tkt_mask=(KDB_MAX_LIFE_ATTR | KDB_MAX_RLIFE_ATTR | KDB_TKT_FLAGS_ATTR);
1510     krb5_ldap_policy_params     *tktpoldnparam=NULL;
1511
1512     if ((st=krb5_get_attributes_mask(context, entries, &mask)) != 0)
1513         goto cleanup;
1514
1515     if ((mask & tkt_mask) == tkt_mask)
1516         goto cleanup;
1517
1518     if (policy != NULL) {
1519         st = krb5_ldap_read_policy(context, policy, &tktpoldnparam, &omask);
1520         if (st && st != KRB5_KDB_NOENTRY) {
1521             k5_prependmsg(context, st, _("Error reading ticket policy"));
1522             goto cleanup;
1523         }
1524
1525         st = 0; /* reset the return status */
1526     }
1527
1528     if ((mask & KDB_MAX_LIFE_ATTR) == 0) {
1529         if ((omask & KDB_MAX_LIFE_ATTR) ==  KDB_MAX_LIFE_ATTR)
1530             entries->max_life = tktpoldnparam->maxtktlife;
1531         else if (ldap_context->lrparams->max_life)
1532             entries->max_life = ldap_context->lrparams->max_life;
1533     }
1534
1535     if ((mask & KDB_MAX_RLIFE_ATTR) == 0) {
1536         if ((omask & KDB_MAX_RLIFE_ATTR) == KDB_MAX_RLIFE_ATTR)
1537             entries->max_renewable_life = tktpoldnparam->maxrenewlife;
1538         else if (ldap_context->lrparams->max_renewable_life)
1539             entries->max_renewable_life = ldap_context->lrparams->max_renewable_life;
1540     }
1541
1542     if ((mask & KDB_TKT_FLAGS_ATTR) == 0) {
1543         if ((omask & KDB_TKT_FLAGS_ATTR) == KDB_TKT_FLAGS_ATTR)
1544             entries->attributes = tktpoldnparam->tktflags;
1545         else if (ldap_context->lrparams->tktflags)
1546             entries->attributes |= ldap_context->lrparams->tktflags;
1547     }
1548     krb5_ldap_free_policy(context, tktpoldnparam);
1549
1550 cleanup:
1551     return st;
1552 }
1553
1554 static void
1555 free_ldap_seqof_key_data(ldap_seqof_key_data *keysets, krb5_int16 n_keysets)
1556 {
1557     int i;
1558
1559     if (keysets == NULL)
1560         return;
1561
1562     for (i = 0; i < n_keysets; i++)
1563         k5_free_key_data(keysets[i].n_key_data, keysets[i].key_data);
1564     free(keysets);
1565 }
1566
1567 /*
1568  * Decode keys from ldap search results.
1569  *
1570  * Arguments:
1571  *  - bvalues
1572  *      The ldap search results containing the key data.
1573  *  - mkvno
1574  *      The master kvno that the keys were encrypted with.
1575  *  - keysets_out
1576  *      The decoded keys in a ldap_seqof_key_data struct.  Must be freed using
1577  *      free_ldap_seqof_key_data.
1578  *  - n_keysets_out
1579  *      The number of entries in keys_out.
1580  *  - total_keys_out
1581  *      An optional argument that if given will be set to the total number of
1582  *      keys found throughout all the entries: sum(keys_out.n_key_data)
1583  *      May be NULL.
1584  */
1585 static krb5_error_code
1586 decode_keys(struct berval **bvalues, ldap_seqof_key_data **keysets_out,
1587             krb5_int16 *n_keysets_out, krb5_int16 *total_keys_out)
1588 {
1589     krb5_error_code err = 0;
1590     krb5_int16 n_keys, i, ki, total_keys;
1591     ldap_seqof_key_data *keysets = NULL;
1592
1593     *keysets_out = NULL;
1594     *n_keysets_out = 0;
1595     if (total_keys_out)
1596         *total_keys_out = 0;
1597
1598     /* Precount the number of keys. */
1599     for (n_keys = 0, i = 0; bvalues[i] != NULL; i++) {
1600         if (bvalues[i]->bv_len > 0)
1601             n_keys++;
1602     }
1603
1604     keysets = k5calloc(n_keys, sizeof(ldap_seqof_key_data), &err);
1605     if (keysets == NULL)
1606         goto cleanup;
1607     memset(keysets, 0, n_keys * sizeof(ldap_seqof_key_data));
1608
1609     for (i = 0, ki = 0, total_keys = 0; bvalues[i] != NULL; i++) {
1610         krb5_data in;
1611
1612         if (bvalues[i]->bv_len == 0)
1613             continue;
1614         in.length = bvalues[i]->bv_len;
1615         in.data = bvalues[i]->bv_val;
1616
1617         err = asn1_decode_sequence_of_keys(&in, &keysets[ki]);
1618         if (err)
1619             goto cleanup;
1620
1621         if (total_keys_out)
1622             total_keys += keysets[ki].n_key_data;
1623         ki++;
1624     }
1625
1626     if (total_keys_out)
1627         *total_keys_out = total_keys;
1628
1629     *n_keysets_out = n_keys;
1630     *keysets_out = keysets;
1631     keysets = NULL;
1632     n_keys = 0;
1633
1634 cleanup:
1635     free_ldap_seqof_key_data(keysets, n_keys);
1636     return err;
1637 }
1638
1639 krb5_error_code
1640 krb5_decode_krbsecretkey(krb5_context context, krb5_db_entry *entries,
1641                          struct berval **bvalues, krb5_kvno *mkvno)
1642 {
1643     krb5_key_data *key_data = NULL, *tmp;
1644     krb5_error_code err = 0;
1645     ldap_seqof_key_data *keysets = NULL;
1646     krb5_int16 i, n_keysets = 0, total_keys = 0;
1647
1648     err = decode_keys(bvalues, &keysets, &n_keysets, &total_keys);
1649     if (err != 0) {
1650         k5_prependmsg(context, err,
1651                       _("unable to decode stored principal key data"));
1652         goto cleanup;
1653     }
1654
1655     key_data = k5calloc(total_keys, sizeof(krb5_key_data), &err);
1656     if (key_data == NULL)
1657         goto cleanup;
1658     memset(key_data, 0, total_keys * sizeof(krb5_key_data));
1659
1660     if (n_keysets > 0)
1661         *mkvno = keysets[0].mkvno;
1662
1663     /* Transfer key data values from keysets to a flat list in entries. */
1664     tmp = key_data;
1665     for (i = 0; i < n_keysets; i++) {
1666         memcpy(tmp, keysets[i].key_data,
1667                sizeof(krb5_key_data) * keysets[i].n_key_data);
1668         tmp += keysets[i].n_key_data;
1669         keysets[i].n_key_data = 0;
1670     }
1671     entries->n_key_data = total_keys;
1672     entries->key_data = key_data;
1673     key_data = NULL;
1674
1675 cleanup:
1676     free_ldap_seqof_key_data(keysets, n_keysets);
1677     k5_free_key_data(total_keys, key_data);
1678     return err;
1679 }
1680
1681 static int
1682 compare_osa_pw_hist_ent(const void *left_in, const void *right_in)
1683 {
1684     int kvno_left, kvno_right;
1685     osa_pw_hist_ent *left = (osa_pw_hist_ent *)left_in;
1686     osa_pw_hist_ent *right = (osa_pw_hist_ent *)right_in;
1687
1688     kvno_left = left->n_key_data ? left->key_data[0].key_data_kvno : 0;
1689     kvno_right = right->n_key_data ? right->key_data[0].key_data_kvno : 0;
1690     return kvno_left - kvno_right;
1691 }
1692
1693 /*
1694  * Decode the key history entries from an LDAP search.
1695  *
1696  * NOTE: the caller must free princ_ent->old_keys even on error.
1697  */
1698 krb5_error_code
1699 krb5_decode_histkey(krb5_context context, struct berval **bvalues,
1700                     osa_princ_ent_rec *princ_ent)
1701 {
1702     krb5_error_code err = 0;
1703     krb5_int16 i, n_keysets = 0;
1704     ldap_seqof_key_data *keysets = NULL;
1705
1706     err = decode_keys(bvalues, &keysets, &n_keysets, NULL);
1707     if (err != 0) {
1708         k5_prependmsg(context, err,
1709                       _("unable to decode stored principal pw history"));
1710         goto cleanup;
1711     }
1712
1713     princ_ent->old_keys = k5calloc(n_keysets, sizeof(osa_pw_hist_ent), &err);
1714     if (princ_ent->old_keys == NULL)
1715         goto cleanup;
1716     princ_ent->old_key_len = n_keysets;
1717
1718     if (n_keysets > 0)
1719         princ_ent->admin_history_kvno = keysets[0].mkvno;
1720
1721     /* Transfer key data pointers from keysets to princ_ent. */
1722     for (i = 0; i < n_keysets; i++) {
1723         princ_ent->old_keys[i].n_key_data = keysets[i].n_key_data;
1724         princ_ent->old_keys[i].key_data = keysets[i].key_data;
1725         keysets[i].n_key_data = 0;
1726         keysets[i].key_data = NULL;
1727     }
1728
1729     /* Sort the principal entries by kvno in ascending order. */
1730     qsort(princ_ent->old_keys, princ_ent->old_key_len, sizeof(osa_pw_hist_ent),
1731           &compare_osa_pw_hist_ent);
1732
1733     princ_ent->aux_attributes |= KADM5_KEY_HIST;
1734
1735     /* Set the next key to the end of the list.  The queue will be lengthened
1736      * if it isn't full yet; the first entry will be replaced if it is full. */
1737     princ_ent->old_key_next = princ_ent->old_key_len;
1738
1739 cleanup:
1740     free_ldap_seqof_key_data(keysets, n_keysets);
1741     return err;
1742 }
1743
1744 static char *
1745 getstringtime(krb5_timestamp epochtime)
1746 {
1747     struct tm           tme;
1748     char                *strtime=NULL;
1749     time_t              posixtime = ts2tt(epochtime);
1750
1751     if (gmtime_r(&posixtime, &tme) == NULL)
1752         return NULL;
1753
1754     strtime = calloc(50, 1);
1755     if (strtime == NULL)
1756         return NULL;
1757     if (strftime(strtime, 50, "%Y%m%d%H%M%SZ", &tme) == 0) {
1758         free(strtime);
1759         return NULL;
1760     }
1761     return strtime;
1762 }