xref: /freebsd/crypto/krb5/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c */
3 /*
4  * Copyright (C) 2016 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 /*
33  * Copyright (c) 2004-2005, Novell, Inc.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions are met:
38  *
39  *   * Redistributions of source code must retain the above copyright notice,
40  *       this list of conditions and the following disclaimer.
41  *   * Redistributions in binary form must reproduce the above copyright
42  *       notice, this list of conditions and the following disclaimer in the
43  *       documentation and/or other materials provided with the distribution.
44  *   * The copyright holder's name is not used to endorse or promote products
45  *       derived from this software without specific prior written permission.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57  * POSSIBILITY OF SUCH DAMAGE.
58  */
59 /*
60  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
61  * Use is subject to license terms.
62  */
63 
64 #include "ldap_main.h"
65 #include "kdb_ldap.h"
66 #include "ldap_principal.h"
67 #include "princ_xdr.h"
68 #include "ldap_tkt_policy.h"
69 #include "ldap_pwd_policy.h"
70 #include "ldap_err.h"
71 #include <kadm5/admin.h>
72 #include <time.h>
73 
74 extern char* principal_attributes[];
75 extern char* max_pwd_life_attr[];
76 
77 static char *
78 getstringtime(krb5_timestamp);
79 
80 krb5_error_code
berval2tl_data(struct berval * in,krb5_tl_data ** out)81 berval2tl_data(struct berval *in, krb5_tl_data **out)
82 {
83     *out = (krb5_tl_data *) malloc (sizeof (krb5_tl_data));
84     if (*out == NULL)
85         return ENOMEM;
86 
87     (*out)->tl_data_length = in->bv_len - 2;
88     (*out)->tl_data_contents =  (krb5_octet *) malloc
89         ((*out)->tl_data_length * sizeof (krb5_octet));
90     if ((*out)->tl_data_contents == NULL) {
91         free (*out);
92         return ENOMEM;
93     }
94 
95     UNSTORE16_INT (in->bv_val, (*out)->tl_data_type);
96     memcpy ((*out)->tl_data_contents, in->bv_val + 2, (*out)->tl_data_length);
97 
98     return 0;
99 }
100 
101 /*
102  * look up a principal in the directory.
103  */
104 
105 krb5_error_code
krb5_ldap_get_principal(krb5_context context,krb5_const_principal searchfor,unsigned int flags,krb5_db_entry ** entry_ptr)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                     st = krb5_ldap_parse_principal_name(values[0], &cname);
193                     if (st != 0)
194                         goto cleanup;
195                     st = krb5_parse_name(context, cname, &cprinc);
196                     if (st != 0)
197                         goto cleanup;
198                 }
199                 ldap_value_free(values);
200                 if (!found)
201                     continue;
202             }
203 
204             entry = k5alloc(sizeof(*entry), &st);
205             if (entry == NULL)
206                 goto cleanup;
207             if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent,
208                                              cprinc ? cprinc : searchfor,
209                                              entry)) != 0)
210                 goto cleanup;
211         }
212         ldap_msgfree(result);
213         result = NULL;
214     } /* for (tree=0 ... */
215 
216     if (found) {
217         *entry_ptr = entry;
218         entry = NULL;
219     } else
220         st = KRB5_KDB_NOENTRY;
221 
222 cleanup:
223     ldap_msgfree(result);
224     krb5_db_free_principal(context, entry);
225 
226     if (filter)
227         free (filter);
228 
229     if (subtree) {
230         for (; ntrees; --ntrees)
231             if (subtree[ntrees-1])
232                 free (subtree[ntrees-1]);
233         free (subtree);
234     }
235 
236     if (ldap_server_handle)
237         krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
238 
239     if (user)
240         free(user);
241 
242     if (filtuser)
243         free(filtuser);
244 
245     if (cname)
246         free(cname);
247 
248     if (cprinc)
249         krb5_free_principal(context, cprinc);
250 
251     return st;
252 }
253 
254 typedef enum{ ADD_PRINCIPAL, MODIFY_PRINCIPAL } OPERATION;
255 /*
256  * ptype is creating confusions. Additionally the logic
257  * surronding ptype is redundunt and can be achevied
258  * with the help of dn and containerdn members.
259  * so dropping the ptype member
260  */
261 
262 typedef struct _xargs_t {
263     char           *dn;
264     char           *linkdn;
265     krb5_boolean   dn_from_kbd;
266     char           *containerdn;
267     char           *tktpolicydn;
268 }xargs_t;
269 
270 static void
free_xargs(xargs_t xargs)271 free_xargs(xargs_t xargs)
272 {
273     if (xargs.dn)
274         free (xargs.dn);
275     if (xargs.linkdn)
276         free(xargs.linkdn);
277     if (xargs.containerdn)
278         free (xargs.containerdn);
279     if (xargs.tktpolicydn)
280         free (xargs.tktpolicydn);
281 }
282 
283 static krb5_error_code
process_db_args(krb5_context context,char ** db_args,xargs_t * xargs,OPERATION optype)284 process_db_args(krb5_context context, char **db_args, xargs_t *xargs,
285                 OPERATION optype)
286 {
287     int                   i=0;
288     krb5_error_code       st=0;
289     char                  *arg=NULL, *arg_val=NULL;
290     char                  **dptr=NULL;
291     unsigned int          arg_val_len=0;
292 
293     if (db_args) {
294         for (i=0; db_args[i]; ++i) {
295             arg = strtok_r(db_args[i], "=", &arg_val);
296             arg = (arg != NULL) ? arg : "";
297             if (strcmp(arg, TKTPOLICY_ARG) == 0) {
298                 dptr = &xargs->tktpolicydn;
299             } else {
300                 if (strcmp(arg, USERDN_ARG) == 0) {
301                     if (optype == MODIFY_PRINCIPAL ||
302                         xargs->dn != NULL || xargs->containerdn != NULL ||
303                         xargs->linkdn != NULL) {
304                         st = EINVAL;
305                         k5_setmsg(context, st, _("%s option not supported"),
306                                   arg);
307                         goto cleanup;
308                     }
309                     dptr = &xargs->dn;
310                 } else if (strcmp(arg, CONTAINERDN_ARG) == 0) {
311                     if (optype == MODIFY_PRINCIPAL ||
312                         xargs->dn != NULL || xargs->containerdn != NULL) {
313                         st = EINVAL;
314                         k5_setmsg(context, st, _("%s option not supported"),
315                                   arg);
316                         goto cleanup;
317                     }
318                     dptr = &xargs->containerdn;
319                 } else if (strcmp(arg, LINKDN_ARG) == 0) {
320                     if (xargs->dn != NULL || xargs->linkdn != NULL) {
321                         st = EINVAL;
322                         k5_setmsg(context, st, _("%s option not supported"),
323                                   arg);
324                         goto cleanup;
325                     }
326                     dptr = &xargs->linkdn;
327                 } else {
328                     st = EINVAL;
329                     k5_setmsg(context, st, _("unknown option: %s"), arg);
330                     goto cleanup;
331                 }
332 
333                 xargs->dn_from_kbd = TRUE;
334                 if (arg_val == NULL || strlen(arg_val) == 0) {
335                     st = EINVAL;
336                     k5_setmsg(context, st, _("%s option value missing"), arg);
337                     goto cleanup;
338                 }
339             }
340 
341             if (arg_val == NULL) {
342                 st = EINVAL;
343                 k5_setmsg(context, st, _("%s option value missing"), arg);
344                 goto cleanup;
345             }
346             arg_val_len = strlen(arg_val) + 1;
347 
348             if (strcmp(arg, TKTPOLICY_ARG) == 0) {
349                 if ((st = krb5_ldap_name_to_policydn (context,
350                                                       arg_val,
351                                                       dptr)) != 0)
352                     goto cleanup;
353             } else {
354                 *dptr = k5memdup(arg_val, arg_val_len, &st);
355                 if (*dptr == NULL)
356                     goto cleanup;
357             }
358         }
359     }
360 
361 cleanup:
362     return st;
363 }
364 
365 krb5int_access accessor;
366 
367 static krb5_error_code
asn1_encode_sequence_of_keys(krb5_key_data * key_data,krb5_int16 n_key_data,krb5_int32 mkvno,krb5_data ** code)368 asn1_encode_sequence_of_keys(krb5_key_data *key_data, krb5_int16 n_key_data,
369                              krb5_int32 mkvno, krb5_data **code)
370 {
371     krb5_error_code err;
372     ldap_seqof_key_data val;
373 
374     /*
375      * This should be pushed back into other library initialization
376      * code.
377      */
378     err = kldap_ensure_initialized ();
379     if (err)
380         return err;
381 
382     val.key_data = key_data;
383     val.n_key_data = n_key_data;
384     val.mkvno = mkvno;
385     val.kvno = key_data[0].key_data_kvno;
386 
387     return accessor.asn1_ldap_encode_sequence_of_keys(&val, code);
388 }
389 
390 static krb5_error_code
asn1_decode_sequence_of_keys(krb5_data * in,ldap_seqof_key_data * out)391 asn1_decode_sequence_of_keys(krb5_data *in, ldap_seqof_key_data *out)
392 {
393     krb5_error_code err;
394     ldap_seqof_key_data *p;
395     int i;
396 
397     memset(out, 0, sizeof(*out));
398 
399     /*
400      * This should be pushed back into other library initialization
401      * code.
402      */
403     err = kldap_ensure_initialized ();
404     if (err)
405         return err;
406 
407     err = accessor.asn1_ldap_decode_sequence_of_keys(in, &p);
408     if (err)
409         return err;
410 
411     /* Set kvno and key_data_ver in each key_data element. */
412     for (i = 0; i < p->n_key_data; i++) {
413         p->key_data[i].key_data_kvno = p->kvno;
414         /* The decoder sets key_data_ver to 1 if no salt is present, but leaves
415          * it at 0 if salt is present. */
416         if (p->key_data[i].key_data_ver == 0)
417             p->key_data[i].key_data_ver = 2;
418     }
419 
420     *out = *p;
421     free(p);
422     return 0;
423 }
424 
425 /*
426  * Free a NULL-terminated struct berval *array[] and all its contents.
427  * Does not set array to NULL after freeing it.
428  */
429 void
free_berdata(struct berval ** array)430 free_berdata(struct berval **array)
431 {
432     int i;
433 
434     if (array != NULL) {
435         for (i = 0; array[i] != NULL; i++) {
436             if (array[i]->bv_val != NULL)
437                 free(array[i]->bv_val);
438             free(array[i]);
439         }
440         free(array);
441     }
442 }
443 
444 /*
445  * Encode krb5_key_data into a berval struct for insertion into LDAP.
446  */
447 static krb5_error_code
encode_keys(krb5_key_data * key_data_in,int n_key_data,krb5_kvno mkvno,struct berval ** bval_out)448 encode_keys(krb5_key_data *key_data_in, int n_key_data, krb5_kvno mkvno,
449             struct berval **bval_out)
450 {
451     krb5_error_code err = 0;
452     int i;
453     krb5_key_data *key_data = NULL;
454     struct berval *bval = NULL;
455     krb5_data *code;
456 
457     *bval_out = NULL;
458     if (n_key_data <= 0) {
459         err = EINVAL;
460         goto cleanup;
461     }
462 
463     /* Make a shallow copy of the key data so we can alter it. */
464     key_data = k5calloc(n_key_data, sizeof(*key_data), &err);
465     if (key_data == NULL)
466         goto cleanup;
467     memcpy(key_data, key_data_in, n_key_data * sizeof(*key_data));
468 
469     /* Unpatched krb5 1.11 and 1.12 cannot decode KrbKey sequences with no salt
470      * field.  For compatibility, always encode a salt field. */
471     for (i = 0; i < n_key_data; i++) {
472         if (key_data[i].key_data_ver == 1) {
473             key_data[i].key_data_ver = 2;
474             key_data[i].key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL;
475             key_data[i].key_data_length[1] = 0;
476             key_data[i].key_data_contents[1] = NULL;
477         }
478     }
479 
480     bval = k5alloc(sizeof(struct berval), &err);
481     if (bval == NULL)
482         goto cleanup;
483 
484     err = asn1_encode_sequence_of_keys(key_data, n_key_data, mkvno, &code);
485     if (err)
486         goto cleanup;
487 
488     /* Steal the data pointer from code for bval and discard code. */
489     bval->bv_len = code->length;
490     bval->bv_val = code->data;
491     free(code);
492 
493     *bval_out = bval;
494     bval = NULL;
495 
496 cleanup:
497     free(key_data);
498     free(bval);
499     return err;
500 }
501 
502 /* Decoding ASN.1 encoded key */
503 struct berval **
krb5_encode_krbsecretkey(krb5_key_data * key_data,int n_key_data,krb5_kvno mkvno)504 krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data,
505                          krb5_kvno mkvno)
506 {
507     struct berval **ret = NULL;
508     int currkvno;
509     int num_versions = 0;
510     int i, j, last;
511     krb5_error_code err = 0;
512 
513     if (n_key_data < 0)
514         return NULL;
515 
516     /* Find the number of key versions */
517     if (n_key_data > 0) {
518         for (i = 0, num_versions = 1; i < n_key_data - 1; i++) {
519             if (key_data[i].key_data_kvno != key_data[i + 1].key_data_kvno)
520                 num_versions++;
521         }
522     }
523 
524     ret = calloc(num_versions + 1, sizeof(struct berval *));
525     if (ret == NULL) {
526         err = ENOMEM;
527         goto cleanup;
528     }
529     ret[num_versions] = NULL;
530 
531     /* n_key_data may be 0 if a principal is created without a key. */
532     if (n_key_data == 0)
533         goto cleanup;
534 
535     currkvno = key_data[0].key_data_kvno;
536     for (i = 0, last = 0, j = 0; i < n_key_data; i++) {
537         if (i == n_key_data - 1 || key_data[i + 1].key_data_kvno != currkvno) {
538             err = encode_keys(key_data + last, (krb5_int16)i - last + 1, mkvno,
539                               &ret[j]);
540             if (err)
541                 goto cleanup;
542             j++;
543             last = i + 1;
544 
545             if (i < n_key_data - 1)
546                 currkvno = key_data[i + 1].key_data_kvno;
547         }
548     }
549 
550 cleanup:
551     if (err != 0) {
552         free_berdata(ret);
553         ret = NULL;
554     }
555 
556     return ret;
557 }
558 
559 /*
560  * Encode a principal's key history for insertion into ldap.
561  */
562 static struct berval **
krb5_encode_histkey(osa_princ_ent_rec * princ_ent)563 krb5_encode_histkey(osa_princ_ent_rec *princ_ent)
564 {
565     unsigned int i;
566     krb5_error_code err = 0;
567     struct berval **ret = NULL;
568 
569     if (princ_ent->old_key_len <= 0)
570         return NULL;
571 
572     ret = k5calloc(princ_ent->old_key_len + 1, sizeof(struct berval *), &err);
573     if (ret == NULL)
574         goto cleanup;
575 
576     for (i = 0; i < princ_ent->old_key_len; i++) {
577         if (princ_ent->old_keys[i].n_key_data <= 0) {
578             err = EINVAL;
579             goto cleanup;
580         }
581         err = encode_keys(princ_ent->old_keys[i].key_data,
582                           princ_ent->old_keys[i].n_key_data,
583                           princ_ent->admin_history_kvno, &ret[i]);
584         if (err)
585             goto cleanup;
586     }
587 
588     ret[princ_ent->old_key_len] = NULL;
589 
590 cleanup:
591     if (err != 0) {
592         free_berdata(ret);
593         ret = NULL;
594     }
595 
596     return ret;
597 }
598 
599 static krb5_error_code
tl_data2berval(krb5_tl_data * in,struct berval ** out)600 tl_data2berval (krb5_tl_data *in, struct berval **out)
601 {
602     *out = (struct berval *) malloc (sizeof (struct berval));
603     if (*out == NULL)
604         return ENOMEM;
605 
606     (*out)->bv_len = in->tl_data_length + 2;
607     (*out)->bv_val =  (char *) malloc ((*out)->bv_len);
608     if ((*out)->bv_val == NULL) {
609         free (*out);
610         return ENOMEM;
611     }
612 
613     STORE16_INT((*out)->bv_val, in->tl_data_type);
614     memcpy ((*out)->bv_val + 2, in->tl_data_contents, in->tl_data_length);
615 
616     return 0;
617 }
618 
619 /* Parse the "require_auth" string for auth indicators, adding them to the
620  * krbPrincipalAuthInd attribute. */
621 static krb5_error_code
update_ldap_mod_auth_ind(krb5_context context,krb5_db_entry * entry,LDAPMod *** mods)622 update_ldap_mod_auth_ind(krb5_context context, krb5_db_entry *entry,
623                          LDAPMod ***mods)
624 {
625     int i = 0;
626     krb5_error_code ret;
627     char *auth_ind = NULL;
628     char *strval[10] = { 0 };
629     char *ai, *ai_save = NULL;
630     int mask, sv_num = sizeof(strval) / sizeof(*strval);
631 
632     ret = krb5_dbe_get_string(context, entry, KRB5_KDB_SK_REQUIRE_AUTH,
633                               &auth_ind);
634     if (ret)
635         return ret;
636     if (auth_ind == NULL) {
637         /* If we know krbPrincipalAuthInd attributes are present from loading
638          * the entry, delete them. */
639         ret = krb5_get_attributes_mask(context, entry, &mask);
640         if (!ret && (mask & KDB_AUTH_IND_ATTR)) {
641             return krb5_add_str_mem_ldap_mod(mods, "krbPrincipalAuthInd",
642                                              LDAP_MOD_DELETE, NULL);
643         }
644         return 0;
645     }
646 
647     ai = strtok_r(auth_ind, " ", &ai_save);
648     while (ai != NULL && i < sv_num) {
649         strval[i++] = ai;
650         ai = strtok_r(NULL, " ", &ai_save);
651     }
652 
653     ret = krb5_add_str_mem_ldap_mod(mods, "krbPrincipalAuthInd",
654                                     LDAP_MOD_REPLACE, strval);
655     krb5_dbe_free_string(context, auth_ind);
656     return ret;
657 }
658 
659 static krb5_error_code
check_dn_in_container(krb5_context context,const char * dn,char * const * subtrees,unsigned int ntrees)660 check_dn_in_container(krb5_context context, const char *dn,
661                       char *const *subtrees, unsigned int ntrees)
662 {
663     unsigned int i;
664     size_t dnlen = strlen(dn), stlen;
665 
666     for (i = 0; i < ntrees; i++) {
667         if (subtrees[i] == NULL || *subtrees[i] == '\0')
668             return 0;
669         stlen = strlen(subtrees[i]);
670         if (dnlen >= stlen &&
671             strcasecmp(dn + dnlen - stlen, subtrees[i]) == 0 &&
672             (dnlen == stlen || dn[dnlen - stlen - 1] == ','))
673             return 0;
674     }
675 
676     k5_setmsg(context, EINVAL, _("DN is out of the realm subtree"));
677     return EINVAL;
678 }
679 
680 static krb5_error_code
check_dn_exists(krb5_context context,krb5_ldap_server_handle * ldap_server_handle,const char * dn,krb5_boolean nonkrb_only)681 check_dn_exists(krb5_context context,
682                 krb5_ldap_server_handle *ldap_server_handle,
683                 const char *dn, krb5_boolean nonkrb_only)
684 {
685     krb5_error_code st = 0, tempst;
686     krb5_ldap_context *ldap_context = context->dal_handle->db_context;
687     LDAP *ld = ldap_server_handle->ldap_handle;
688     LDAPMessage *result = NULL, *ent;
689     char *attrs[] = { "krbticketpolicyreference", "krbprincipalname", NULL };
690     char **values;
691 
692     LDAP_SEARCH_1(dn, LDAP_SCOPE_BASE, 0, attrs, IGNORE_STATUS);
693     if (st != LDAP_SUCCESS)
694         return set_ldap_error(context, st, OP_SEARCH);
695 
696     ent = ldap_first_entry(ld, result);
697     CHECK_NULL(ent);
698 
699     values = ldap_get_values(ld, ent, "krbticketpolicyreference");
700     if (values != NULL)
701         ldap_value_free(values);
702 
703     values = ldap_get_values(ld, ent, "krbprincipalname");
704     if (values != NULL) {
705         ldap_value_free(values);
706         if (nonkrb_only) {
707             st = EINVAL;
708             k5_setmsg(context, st, _("ldap object is already kerberized"));
709             goto cleanup;
710         }
711     }
712 
713 cleanup:
714     ldap_msgfree(result);
715     return st;
716 }
717 
718 static krb5_error_code
validate_xargs(krb5_context context,krb5_ldap_server_handle * ldap_server_handle,const xargs_t * xargs,const char * standalone_dn,char * const * subtrees,unsigned int ntrees)719 validate_xargs(krb5_context context,
720                krb5_ldap_server_handle *ldap_server_handle,
721                const xargs_t *xargs, const char *standalone_dn,
722                char *const *subtrees, unsigned int ntrees)
723 {
724     krb5_error_code st;
725 
726     if (xargs->dn != NULL) {
727         /* The supplied dn must be within a realm container. */
728         st = check_dn_in_container(context, xargs->dn, subtrees, ntrees);
729         if (st)
730             return st;
731         /* The supplied dn must exist without Kerberos attributes. */
732         st = check_dn_exists(context, ldap_server_handle, xargs->dn, TRUE);
733         if (st)
734             return st;
735     }
736 
737     if (xargs->linkdn != NULL) {
738         /* The supplied linkdn must be within a realm container. */
739         st = check_dn_in_container(context, xargs->linkdn, subtrees, ntrees);
740         if (st)
741             return st;
742         /* The supplied linkdn must exist. */
743         st = check_dn_exists(context, ldap_server_handle, xargs->linkdn,
744                              FALSE);
745         if (st)
746             return st;
747     }
748 
749     if (xargs->containerdn != NULL && standalone_dn != NULL) {
750         /* standalone_dn (likely composed using containerdn) must be within a
751          * container. */
752         st = check_dn_in_container(context, standalone_dn, subtrees, ntrees);
753         if (st)
754             return st;
755     }
756 
757     return 0;
758 }
759 
760 krb5_error_code
krb5_ldap_put_principal(krb5_context context,krb5_db_entry * entry,char ** db_args)761 krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
762                         char **db_args)
763 {
764     int                         l=0, kerberos_principal_object_type=0;
765     unsigned int                ntrees=0, tre=0;
766     krb5_error_code             st=0, tempst=0;
767     LDAP                        *ld=NULL;
768     LDAPMessage                 *result=NULL, *ent=NULL;
769     char                        **subtreelist = NULL;
770     char                        *user=NULL, *subtree=NULL, *principal_dn=NULL;
771     char                        *strval[10]={NULL}, errbuf[1024];
772     char                        *filtuser=NULL;
773     struct berval               **bersecretkey=NULL;
774     LDAPMod                     **mods=NULL;
775     krb5_boolean                create_standalone=FALSE;
776     krb5_boolean                establish_links=FALSE;
777     char                        *standalone_principal_dn=NULL;
778     krb5_tl_data                *tl_data=NULL;
779     krb5_key_data               **keys=NULL;
780     kdb5_dal_handle             *dal_handle=NULL;
781     krb5_ldap_context           *ldap_context=NULL;
782     krb5_ldap_server_handle     *ldap_server_handle=NULL;
783     osa_princ_ent_rec           princ_ent = {0};
784     xargs_t                     xargs = {0};
785     char                        *polname = NULL;
786     OPERATION optype;
787     krb5_boolean                found_entry = FALSE;
788     char                        *filter = NULL;
789 
790     /* Clear the global error string */
791     krb5_clear_error_message(context);
792 
793     SETUP_CONTEXT();
794     if (ldap_context->lrparams == NULL || ldap_context->container_dn == NULL)
795         return EINVAL;
796 
797     /* get ldap handle */
798     GET_HANDLE();
799 
800     if (!is_principal_in_realm(ldap_context, entry->princ)) {
801         st = EINVAL;
802         k5_setmsg(context, st,
803                   _("Principal does not belong to the default realm"));
804         goto cleanup;
805     }
806 
807     /* get the principal information to act on */
808     if (((st=krb5_unparse_name(context, entry->princ, &user)) != 0) ||
809         ((st=krb5_ldap_unparse_principal_name(user)) != 0))
810         goto cleanup;
811     filtuser = ldap_filter_correct(user);
812     if (filtuser == NULL) {
813         st = ENOMEM;
814         goto cleanup;
815     }
816 
817     /* Identity the type of operation, it can be
818      * add principal or modify principal.
819      * hack if the entry->mask has KRB_PRINCIPAL flag set
820      * then it is a add operation
821      */
822     if (entry->mask & KADM5_PRINCIPAL)
823         optype = ADD_PRINCIPAL;
824     else
825         optype = MODIFY_PRINCIPAL;
826 
827     if (((st=krb5_get_princ_type(context, entry, &kerberos_principal_object_type)) != 0) ||
828         ((st=krb5_get_userdn(context, entry, &principal_dn)) != 0))
829         goto cleanup;
830 
831     if ((st=process_db_args(context, db_args, &xargs, optype)) != 0)
832         goto cleanup;
833 
834     if (entry->mask & KADM5_LOAD) {
835         unsigned int     tree = 0;
836         int              numlentries = 0;
837 
838         /*  A load operation is special, will do a mix-in (add krbprinc
839          *  attrs to a non-krb object entry) if an object exists with a
840          *  matching krbprincipalname attribute so try to find existing
841          *  object and set principal_dn.  This assumes that the
842          *  krbprincipalname attribute is unique (only one object entry has
843          *  a particular krbprincipalname attribute).
844          */
845         if (asprintf(&filter, FILTER"%s))", filtuser) < 0) {
846             filter = NULL;
847             st = ENOMEM;
848             goto cleanup;
849         }
850 
851         /* get the current subtree list */
852         if ((st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees)) != 0)
853             goto cleanup;
854 
855         found_entry = FALSE;
856         /* search for entry with matching krbprincipalname attribute */
857         for (tree = 0; found_entry == FALSE && tree < ntrees; ++tree) {
858             if (principal_dn == NULL) {
859                 LDAP_SEARCH_1(subtreelist[tree], ldap_context->lrparams->search_scope, filter, principal_attributes, IGNORE_STATUS);
860             } else {
861                 /* just look for entry with principal_dn */
862                 LDAP_SEARCH_1(principal_dn, LDAP_SCOPE_BASE, filter, principal_attributes, IGNORE_STATUS);
863             }
864             if (st == LDAP_SUCCESS) {
865                 numlentries = ldap_count_entries(ld, result);
866                 if (numlentries > 1) {
867                     st = EINVAL;
868                     k5_setmsg(context, st,
869                               _("operation can not continue, more than one "
870                                 "entry with principal name \"%s\" found"),
871                               user);
872                     goto cleanup;
873                 } else if (numlentries == 1) {
874                     found_entry = TRUE;
875                     if (principal_dn == NULL) {
876                         ent = ldap_first_entry(ld, result);
877                         if (ent != NULL) {
878                             /* setting principal_dn will cause that entry to be modified further down */
879                             if ((principal_dn = ldap_get_dn(ld, ent)) == NULL) {
880                                 ldap_get_option (ld, LDAP_OPT_RESULT_CODE, &st);
881                                 st = set_ldap_error (context, st, 0);
882                                 goto cleanup;
883                             }
884                         }
885                     }
886                 }
887             } else if (st != LDAP_NO_SUCH_OBJECT) {
888                 /* could not perform search, return with failure */
889                 st = set_ldap_error (context, st, 0);
890                 goto cleanup;
891             }
892             ldap_msgfree(result);
893             result = NULL;
894             /*
895              * If it isn't found then assume a standalone princ entry is to
896              * be created.
897              */
898         } /* end for (tree = 0; principal_dn == ... */
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         /* Update last password change whenever a new key is set */
1237         {
1238             krb5_timestamp last_pw_changed;
1239             if ((st=krb5_dbe_lookup_last_pwd_change(context, entry,
1240                                                     &last_pw_changed)) != 0)
1241                 goto cleanup;
1242 
1243             memset(strval, 0, sizeof(strval));
1244             if ((strval[0] = getstringtime(last_pw_changed)) == NULL)
1245                 goto cleanup;
1246 
1247             if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastPwdChange",
1248                                               LDAP_MOD_REPLACE, strval)) != 0) {
1249                 free (strval[0]);
1250                 goto cleanup;
1251             }
1252             free (strval[0]);
1253         }
1254 
1255     } /* Modify Key data ends here */
1256 
1257     /* Set tl_data */
1258     if (entry->tl_data != NULL) {
1259         int count = 0;
1260         struct berval **ber_tl_data = NULL;
1261         krb5_tl_data *ptr;
1262         krb5_timestamp unlock_time;
1263 
1264         /* Normalize required auth indicators, but also store them as string
1265          * attributes within krbExtraData. */
1266         st = update_ldap_mod_auth_ind(context, entry, &mods);
1267         if (st != 0)
1268             goto cleanup;
1269 
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     free(filter);
1449     if (user)
1450         free(user);
1451 
1452     if (filtuser)
1453         free(filtuser);
1454 
1455     free_xargs(xargs);
1456 
1457     if (standalone_principal_dn)
1458         free(standalone_principal_dn);
1459 
1460     if (principal_dn)
1461         free (principal_dn);
1462 
1463     if (polname != NULL)
1464         free(polname);
1465 
1466     for (tre = 0; tre < ntrees; tre++)
1467         free(subtreelist[tre]);
1468     free(subtreelist);
1469 
1470     if (subtree)
1471         free (subtree);
1472 
1473     if (bersecretkey) {
1474         for (l=0; bersecretkey[l]; ++l) {
1475             if (bersecretkey[l]->bv_val)
1476                 free (bersecretkey[l]->bv_val);
1477             free (bersecretkey[l]);
1478         }
1479         free (bersecretkey);
1480     }
1481 
1482     if (keys)
1483         free (keys);
1484 
1485     ldap_mods_free(mods, 1);
1486     ldap_osa_free_princ_ent(&princ_ent);
1487     ldap_msgfree(result);
1488     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1489     return(st);
1490 }
1491 
1492 krb5_error_code
krb5_read_tkt_policy(krb5_context context,krb5_ldap_context * ldap_context,krb5_db_entry * entries,char * policy)1493 krb5_read_tkt_policy(krb5_context context, krb5_ldap_context *ldap_context,
1494                      krb5_db_entry *entries, char *policy)
1495 {
1496     krb5_error_code             st=0;
1497     int                         mask=0, omask=0;
1498     int                         tkt_mask=(KDB_MAX_LIFE_ATTR | KDB_MAX_RLIFE_ATTR | KDB_TKT_FLAGS_ATTR);
1499     krb5_ldap_policy_params     *tktpoldnparam=NULL;
1500 
1501     if ((st=krb5_get_attributes_mask(context, entries, &mask)) != 0)
1502         goto cleanup;
1503 
1504     if ((mask & tkt_mask) == tkt_mask)
1505         goto cleanup;
1506 
1507     if (policy != NULL) {
1508         st = krb5_ldap_read_policy(context, policy, &tktpoldnparam, &omask);
1509         if (st && st != KRB5_KDB_NOENTRY) {
1510             k5_prependmsg(context, st, _("Error reading ticket policy"));
1511             goto cleanup;
1512         }
1513 
1514         st = 0; /* reset the return status */
1515     }
1516 
1517     if ((mask & KDB_MAX_LIFE_ATTR) == 0) {
1518         if ((omask & KDB_MAX_LIFE_ATTR) ==  KDB_MAX_LIFE_ATTR)
1519             entries->max_life = tktpoldnparam->maxtktlife;
1520         else if (ldap_context->lrparams->max_life)
1521             entries->max_life = ldap_context->lrparams->max_life;
1522     }
1523 
1524     if ((mask & KDB_MAX_RLIFE_ATTR) == 0) {
1525         if ((omask & KDB_MAX_RLIFE_ATTR) == KDB_MAX_RLIFE_ATTR)
1526             entries->max_renewable_life = tktpoldnparam->maxrenewlife;
1527         else if (ldap_context->lrparams->max_renewable_life)
1528             entries->max_renewable_life = ldap_context->lrparams->max_renewable_life;
1529     }
1530 
1531     if ((mask & KDB_TKT_FLAGS_ATTR) == 0) {
1532         if ((omask & KDB_TKT_FLAGS_ATTR) == KDB_TKT_FLAGS_ATTR)
1533             entries->attributes = tktpoldnparam->tktflags;
1534         else if (ldap_context->lrparams->tktflags)
1535             entries->attributes |= ldap_context->lrparams->tktflags;
1536     }
1537     krb5_ldap_free_policy(context, tktpoldnparam);
1538 
1539 cleanup:
1540     return st;
1541 }
1542 
1543 static void
free_ldap_seqof_key_data(ldap_seqof_key_data * keysets,krb5_int16 n_keysets)1544 free_ldap_seqof_key_data(ldap_seqof_key_data *keysets, krb5_int16 n_keysets)
1545 {
1546     int i;
1547 
1548     if (keysets == NULL)
1549         return;
1550 
1551     for (i = 0; i < n_keysets; i++)
1552         k5_free_key_data(keysets[i].n_key_data, keysets[i].key_data);
1553     free(keysets);
1554 }
1555 
1556 /*
1557  * Decode keys from ldap search results.
1558  *
1559  * Arguments:
1560  *  - bvalues
1561  *      The ldap search results containing the key data.
1562  *  - mkvno
1563  *      The master kvno that the keys were encrypted with.
1564  *  - keysets_out
1565  *      The decoded keys in a ldap_seqof_key_data struct.  Must be freed using
1566  *      free_ldap_seqof_key_data.
1567  *  - n_keysets_out
1568  *      The number of entries in keys_out.
1569  *  - total_keys_out
1570  *      An optional argument that if given will be set to the total number of
1571  *      keys found throughout all the entries: sum(keys_out.n_key_data)
1572  *      May be NULL.
1573  */
1574 static krb5_error_code
decode_keys(struct berval ** bvalues,ldap_seqof_key_data ** keysets_out,krb5_int16 * n_keysets_out,krb5_int16 * total_keys_out)1575 decode_keys(struct berval **bvalues, ldap_seqof_key_data **keysets_out,
1576             krb5_int16 *n_keysets_out, krb5_int16 *total_keys_out)
1577 {
1578     krb5_error_code err = 0;
1579     krb5_int16 n_keys, i, ki, total_keys;
1580     ldap_seqof_key_data *keysets = NULL;
1581 
1582     *keysets_out = NULL;
1583     *n_keysets_out = 0;
1584     if (total_keys_out)
1585         *total_keys_out = 0;
1586 
1587     /* Precount the number of keys. */
1588     for (n_keys = 0, i = 0; bvalues[i] != NULL; i++) {
1589         if (bvalues[i]->bv_len > 0)
1590             n_keys++;
1591     }
1592 
1593     keysets = k5calloc(n_keys, sizeof(ldap_seqof_key_data), &err);
1594     if (keysets == NULL)
1595         goto cleanup;
1596     memset(keysets, 0, n_keys * sizeof(ldap_seqof_key_data));
1597 
1598     for (i = 0, ki = 0, total_keys = 0; bvalues[i] != NULL; i++) {
1599         krb5_data in;
1600 
1601         if (bvalues[i]->bv_len == 0)
1602             continue;
1603         in.length = bvalues[i]->bv_len;
1604         in.data = bvalues[i]->bv_val;
1605 
1606         err = asn1_decode_sequence_of_keys(&in, &keysets[ki]);
1607         if (err)
1608             goto cleanup;
1609 
1610         if (total_keys_out)
1611             total_keys += keysets[ki].n_key_data;
1612         ki++;
1613     }
1614 
1615     if (total_keys_out)
1616         *total_keys_out = total_keys;
1617 
1618     *n_keysets_out = n_keys;
1619     *keysets_out = keysets;
1620     keysets = NULL;
1621     n_keys = 0;
1622 
1623 cleanup:
1624     free_ldap_seqof_key_data(keysets, n_keys);
1625     return err;
1626 }
1627 
1628 krb5_error_code
krb5_decode_krbsecretkey(krb5_context context,krb5_db_entry * entries,struct berval ** bvalues,krb5_kvno * mkvno)1629 krb5_decode_krbsecretkey(krb5_context context, krb5_db_entry *entries,
1630                          struct berval **bvalues, krb5_kvno *mkvno)
1631 {
1632     krb5_key_data *key_data = NULL, *tmp;
1633     krb5_error_code err = 0;
1634     ldap_seqof_key_data *keysets = NULL;
1635     krb5_int16 i, n_keysets = 0, total_keys = 0;
1636 
1637     err = decode_keys(bvalues, &keysets, &n_keysets, &total_keys);
1638     if (err != 0) {
1639         k5_prependmsg(context, err,
1640                       _("unable to decode stored principal key data"));
1641         goto cleanup;
1642     }
1643 
1644     key_data = k5calloc(total_keys, sizeof(krb5_key_data), &err);
1645     if (key_data == NULL)
1646         goto cleanup;
1647     memset(key_data, 0, total_keys * sizeof(krb5_key_data));
1648 
1649     if (n_keysets > 0)
1650         *mkvno = keysets[0].mkvno;
1651 
1652     /* Transfer key data values from keysets to a flat list in entries. */
1653     tmp = key_data;
1654     for (i = 0; i < n_keysets; i++) {
1655         memcpy(tmp, keysets[i].key_data,
1656                sizeof(krb5_key_data) * keysets[i].n_key_data);
1657         tmp += keysets[i].n_key_data;
1658         keysets[i].n_key_data = 0;
1659     }
1660     entries->n_key_data = total_keys;
1661     entries->key_data = key_data;
1662     key_data = NULL;
1663 
1664 cleanup:
1665     free_ldap_seqof_key_data(keysets, n_keysets);
1666     k5_free_key_data(total_keys, key_data);
1667     return err;
1668 }
1669 
1670 static int
compare_osa_pw_hist_ent(const void * left_in,const void * right_in)1671 compare_osa_pw_hist_ent(const void *left_in, const void *right_in)
1672 {
1673     int kvno_left, kvno_right;
1674     osa_pw_hist_ent *left = (osa_pw_hist_ent *)left_in;
1675     osa_pw_hist_ent *right = (osa_pw_hist_ent *)right_in;
1676 
1677     kvno_left = left->n_key_data ? left->key_data[0].key_data_kvno : 0;
1678     kvno_right = right->n_key_data ? right->key_data[0].key_data_kvno : 0;
1679     return kvno_left - kvno_right;
1680 }
1681 
1682 /*
1683  * Decode the key history entries from an LDAP search.
1684  *
1685  * NOTE: the caller must free princ_ent->old_keys even on error.
1686  */
1687 krb5_error_code
krb5_decode_histkey(krb5_context context,struct berval ** bvalues,osa_princ_ent_rec * princ_ent)1688 krb5_decode_histkey(krb5_context context, struct berval **bvalues,
1689                     osa_princ_ent_rec *princ_ent)
1690 {
1691     krb5_error_code err = 0;
1692     krb5_int16 i, n_keysets = 0;
1693     ldap_seqof_key_data *keysets = NULL;
1694 
1695     err = decode_keys(bvalues, &keysets, &n_keysets, NULL);
1696     if (err != 0) {
1697         k5_prependmsg(context, err,
1698                       _("unable to decode stored principal pw history"));
1699         goto cleanup;
1700     }
1701 
1702     princ_ent->old_keys = k5calloc(n_keysets, sizeof(osa_pw_hist_ent), &err);
1703     if (princ_ent->old_keys == NULL)
1704         goto cleanup;
1705     princ_ent->old_key_len = n_keysets;
1706 
1707     if (n_keysets > 0)
1708         princ_ent->admin_history_kvno = keysets[0].mkvno;
1709 
1710     /* Transfer key data pointers from keysets to princ_ent. */
1711     for (i = 0; i < n_keysets; i++) {
1712         princ_ent->old_keys[i].n_key_data = keysets[i].n_key_data;
1713         princ_ent->old_keys[i].key_data = keysets[i].key_data;
1714         keysets[i].n_key_data = 0;
1715         keysets[i].key_data = NULL;
1716     }
1717 
1718     /* Sort the principal entries by kvno in ascending order. */
1719     qsort(princ_ent->old_keys, princ_ent->old_key_len, sizeof(osa_pw_hist_ent),
1720           &compare_osa_pw_hist_ent);
1721 
1722     princ_ent->aux_attributes |= KADM5_KEY_HIST;
1723 
1724     /* Set the next key to the end of the list.  The queue will be lengthened
1725      * if it isn't full yet; the first entry will be replaced if it is full. */
1726     princ_ent->old_key_next = princ_ent->old_key_len;
1727 
1728 cleanup:
1729     free_ldap_seqof_key_data(keysets, n_keysets);
1730     return err;
1731 }
1732 
1733 static char *
getstringtime(krb5_timestamp epochtime)1734 getstringtime(krb5_timestamp epochtime)
1735 {
1736     struct tm           tme;
1737     char                *strtime=NULL;
1738     time_t              posixtime = ts2tt(epochtime);
1739 
1740     if (gmtime_r(&posixtime, &tme) == NULL)
1741         return NULL;
1742 
1743     strtime = calloc(50, 1);
1744     if (strtime == NULL)
1745         return NULL;
1746     if (strftime(strtime, 50, "%Y%m%d%H%M%SZ", &tme) == 0) {
1747         free(strtime);
1748         return NULL;
1749     }
1750     return strtime;
1751 }
1752