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