s4:torture: Adapt KDC canon test to Heimdal upstream changes
[Samba.git] / source4 / heimdal / kadmin / mod.c
blob9541c6efcb41c0077e79821813aa13ea43dd0e85
1 /*
2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "kadmin_locl.h"
35 #include "kadmin-commands.h"
37 void
38 add_tl(kadm5_principal_ent_rec *princ, int type, krb5_data *data)
40 krb5_tl_data *tl, **ptl;
42 tl = ecalloc(1, sizeof(*tl));
43 tl->tl_data_next = NULL;
44 tl->tl_data_type = type;
45 tl->tl_data_length = data->length;
46 tl->tl_data_contents = data->data;
48 if (tl->tl_data_length < 0 || data->length != (size_t)tl->tl_data_length)
49 errx(1, "TL data overflow");
51 princ->n_tl_data++;
52 ptl = &princ->tl_data;
53 while (*ptl != NULL)
54 ptl = &(*ptl)->tl_data_next;
55 *ptl = tl;
57 return;
61 * Find a TL data of type KRB5_TL_EXTENSION that has an extension of type
62 * `etype' in it.
64 krb5_tl_data *
65 get_tl(kadm5_principal_ent_rec *princ, int type)
67 krb5_tl_data *tl = princ->tl_data;
69 while (tl && tl->tl_data_type != type)
70 tl = tl->tl_data_next;
71 return tl;
74 static void
75 add_constrained_delegation(krb5_context contextp,
76 kadm5_principal_ent_rec *princ,
77 struct getarg_strings *strings)
79 krb5_error_code ret;
80 HDB_extension ext;
81 krb5_data buf;
82 size_t size = 0;
84 memset(&ext, 0, sizeof(ext));
85 ext.mandatory = FALSE;
86 ext.data.element = choice_HDB_extension_data_allowed_to_delegate_to;
88 if (strings->num_strings == 1 && strings->strings[0][0] == '\0') {
89 ext.data.u.allowed_to_delegate_to.val = NULL;
90 ext.data.u.allowed_to_delegate_to.len = 0;
91 } else {
92 krb5_principal p;
93 int i;
95 ext.data.u.allowed_to_delegate_to.val =
96 calloc(strings->num_strings,
97 sizeof(ext.data.u.allowed_to_delegate_to.val[0]));
98 ext.data.u.allowed_to_delegate_to.len = strings->num_strings;
100 for (i = 0; i < strings->num_strings; i++) {
101 ret = krb5_parse_name(contextp, strings->strings[i], &p);
102 if (ret)
103 abort();
104 ret = copy_Principal(p, &ext.data.u.allowed_to_delegate_to.val[i]);
105 if (ret)
106 abort();
107 krb5_free_principal(contextp, p);
111 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
112 &ext, &size, ret);
113 free_HDB_extension(&ext);
114 if (ret)
115 abort();
116 if (buf.length != size)
117 abort();
119 add_tl(princ, KRB5_TL_EXTENSION, &buf);
122 static void
123 add_aliases(krb5_context contextp, kadm5_principal_ent_rec *princ,
124 struct getarg_strings *strings)
126 krb5_error_code ret;
127 HDB_extension ext;
128 krb5_data buf;
129 krb5_principal p;
130 size_t size = 0;
131 int i;
133 memset(&ext, 0, sizeof(ext));
134 ext.mandatory = FALSE;
135 ext.data.element = choice_HDB_extension_data_aliases;
136 ext.data.u.aliases.case_insensitive = 0;
138 if (strings->num_strings == 1 && strings->strings[0][0] == '\0') {
139 ext.data.u.aliases.aliases.val = NULL;
140 ext.data.u.aliases.aliases.len = 0;
141 } else {
142 ext.data.u.aliases.aliases.val =
143 calloc(strings->num_strings,
144 sizeof(ext.data.u.aliases.aliases.val[0]));
145 ext.data.u.aliases.aliases.len = strings->num_strings;
147 for (i = 0; i < strings->num_strings; i++) {
148 ret = krb5_parse_name(contextp, strings->strings[i], &p);
149 ret = copy_Principal(p, &ext.data.u.aliases.aliases.val[i]);
150 krb5_free_principal(contextp, p);
154 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
155 &ext, &size, ret);
156 free_HDB_extension(&ext);
157 if (ret)
158 abort();
159 if (buf.length != size)
160 abort();
162 add_tl(princ, KRB5_TL_EXTENSION, &buf);
165 static void
166 add_pkinit_acl(krb5_context contextp, kadm5_principal_ent_rec *princ,
167 struct getarg_strings *strings)
169 krb5_error_code ret;
170 HDB_extension ext;
171 krb5_data buf;
172 size_t size = 0;
173 int i;
175 memset(&ext, 0, sizeof(ext));
176 ext.mandatory = FALSE;
177 ext.data.element = choice_HDB_extension_data_pkinit_acl;
178 ext.data.u.aliases.case_insensitive = 0;
180 if (strings->num_strings == 1 && strings->strings[0][0] == '\0') {
181 ext.data.u.pkinit_acl.val = NULL;
182 ext.data.u.pkinit_acl.len = 0;
183 } else {
184 ext.data.u.pkinit_acl.val =
185 calloc(strings->num_strings,
186 sizeof(ext.data.u.pkinit_acl.val[0]));
187 ext.data.u.pkinit_acl.len = strings->num_strings;
189 for (i = 0; i < strings->num_strings; i++) {
190 ext.data.u.pkinit_acl.val[i].subject = estrdup(strings->strings[i]);
194 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
195 &ext, &size, ret);
196 free_HDB_extension(&ext);
197 if (ret)
198 abort();
199 if (buf.length != size)
200 abort();
202 add_tl(princ, KRB5_TL_EXTENSION, &buf);
205 static krb5_error_code
206 add_etypes(krb5_context contextp,
207 kadm5_principal_ent_rec *princ,
208 struct getarg_strings *strings)
210 krb5_error_code ret = 0;
211 HDB_EncTypeList etypes;
212 krb5_data buf;
213 size_t i, size;
215 etypes.len = strings->num_strings;
216 if ((etypes.val = calloc(strings->num_strings,
217 sizeof(etypes.val[0]))) == NULL)
218 krb5_err(contextp, 1, ret, "Out of memory");
220 for (i = 0; i < strings->num_strings; i++) {
221 krb5_enctype etype;
223 ret = krb5_string_to_enctype(contextp, strings->strings[i], &etype);
224 if (ret) {
225 krb5_warn(contextp, ret, "Could not parse enctype %s",
226 strings->strings[i]);
227 return ret;
229 etypes.val[i] = etype;
232 if (ret == 0) {
233 ASN1_MALLOC_ENCODE(HDB_EncTypeList, buf.data, buf.length,
234 &etypes, &size, ret);
236 if (ret || buf.length != size)
237 abort();
238 add_tl(princ, KRB5_TL_ETYPES, &buf);
239 return 0;
242 static void
243 add_kvno_diff(krb5_context contextp, kadm5_principal_ent_rec *princ,
244 int is_svc_diff, krb5_kvno kvno_diff)
246 krb5_error_code ret;
247 HDB_extension ext;
248 krb5_data buf;
249 size_t size = 0;
251 if (kvno_diff < 0)
252 return;
253 if (kvno_diff > 2048)
254 kvno_diff = 2048;
256 if (is_svc_diff) {
257 ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc;
258 ext.data.u.hist_kvno_diff_svc = (unsigned int)kvno_diff;
259 } else {
260 ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt;
261 ext.data.u.hist_kvno_diff_clnt = (unsigned int)kvno_diff;
263 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
264 &ext, &size, ret);
265 if (ret)
266 abort();
267 if (buf.length != size)
268 abort();
270 add_tl(princ, KRB5_TL_EXTENSION, &buf);
273 static void
274 add_krb5_config(kadm5_principal_ent_rec *princ, const char *fname)
276 HDB_extension ext;
277 krb5_data buf;
278 size_t size;
279 int ret;
281 memset(&ext, 0, sizeof(ext));
282 ext.mandatory = FALSE;
283 ext.data.element = choice_HDB_extension_data_krb5_config;
285 if ((ret = rk_undumpdata(fname,
286 &ext.data.u.krb5_config.data,
287 &ext.data.u.krb5_config.length))) {
288 krb5_warn(context, ret, "Could not read %s", fname);
289 return;
292 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
293 &ext, &size, ret);
294 free_HDB_extension(&ext);
295 if (ret)
296 abort();
297 if (buf.length != size)
298 abort();
299 add_tl(princ, KRB5_TL_EXTENSION, &buf);
302 static int
303 do_mod_entry(krb5_principal principal, void *data)
305 krb5_error_code ret;
306 kadm5_principal_ent_rec princ;
307 int mask = 0;
308 struct modify_options *e = data;
310 memset (&princ, 0, sizeof(princ));
311 ret = kadm5_get_principal(kadm_handle, principal, &princ,
312 KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
313 KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
314 KADM5_PRINC_EXPIRE_TIME |
315 KADM5_PW_EXPIRATION);
316 if(ret)
317 return ret;
319 if(e->max_ticket_life_string ||
320 e->max_renewable_life_string ||
321 e->expiration_time_string ||
322 e->pw_expiration_time_string ||
323 e->attributes_string ||
324 e->policy_string ||
325 e->kvno_integer != -1 ||
326 e->service_enctypes_strings.num_strings ||
327 e->constrained_delegation_strings.num_strings ||
328 e->alias_strings.num_strings ||
329 e->pkinit_acl_strings.num_strings ||
330 e->krb5_config_file_string ||
331 e->hist_kvno_diff_clnt_integer != -1 ||
332 e->hist_kvno_diff_svc_integer != -1) {
333 ret = set_entry(context, &princ, &mask,
334 e->max_ticket_life_string,
335 e->max_renewable_life_string,
336 e->expiration_time_string,
337 e->pw_expiration_time_string,
338 e->attributes_string,
339 e->policy_string);
340 if(e->kvno_integer != -1) {
341 princ.kvno = e->kvno_integer;
342 mask |= KADM5_KVNO;
344 if (e->constrained_delegation_strings.num_strings) {
345 add_constrained_delegation(context, &princ,
346 &e->constrained_delegation_strings);
347 mask |= KADM5_TL_DATA;
349 if (e->alias_strings.num_strings) {
350 add_aliases(context, &princ, &e->alias_strings);
351 mask |= KADM5_TL_DATA;
353 if (e->pkinit_acl_strings.num_strings) {
354 add_pkinit_acl(context, &princ, &e->pkinit_acl_strings);
355 mask |= KADM5_TL_DATA;
357 if (e->service_enctypes_strings.num_strings) {
358 ret = add_etypes(context, &princ, &e->service_enctypes_strings);
359 mask |= KADM5_TL_DATA;
361 if (e->hist_kvno_diff_clnt_integer != -1) {
362 add_kvno_diff(context, &princ, 0, e->hist_kvno_diff_clnt_integer);
363 mask |= KADM5_TL_DATA;
365 if (e->hist_kvno_diff_svc_integer != -1) {
366 add_kvno_diff(context, &princ, 1, e->hist_kvno_diff_svc_integer);
367 mask |= KADM5_TL_DATA;
369 if (e->krb5_config_file_string) {
370 add_krb5_config(&princ, e->krb5_config_file_string);
371 mask |= KADM5_TL_DATA;
373 } else
374 ret = edit_entry(&princ, &mask, NULL, 0);
375 if(ret == 0) {
376 ret = kadm5_modify_principal(kadm_handle, &princ, mask);
377 if(ret)
378 krb5_warn(context, ret, "kadm5_modify_principal");
381 kadm5_free_principal_ent(kadm_handle, &princ);
382 return ret;
386 mod_entry(struct modify_options *opt, int argc, char **argv)
388 krb5_error_code ret = 0;
389 int i;
391 for(i = 0; i < argc; i++) {
392 ret = foreach_principal(argv[i], do_mod_entry, "mod", opt);
393 if (ret)
394 break;
396 return ret != 0;
399 static int
400 do_mod_ns_entry(krb5_principal principal, void *data)
402 krb5_error_code ret;
403 kadm5_principal_ent_rec princ;
404 int mask = 0;
405 struct modify_namespace_options *e = data;
407 memset (&princ, 0, sizeof(princ));
408 ret = kadm5_get_principal(kadm_handle, principal, &princ,
409 KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
410 KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
411 KADM5_PRINC_EXPIRE_TIME |
412 KADM5_PW_EXPIRATION);
413 if(ret)
414 return ret;
416 if(e->max_ticket_life_string ||
417 e->max_renewable_life_string ||
418 e->attributes_string ||
419 e->enctypes_strings.num_strings ||
420 e->krb5_config_file_string) {
421 ret = set_entry(context, &princ, &mask, e->max_ticket_life_string,
422 e->max_renewable_life_string, NULL, NULL,
423 e->attributes_string, NULL);
424 if (e->enctypes_strings.num_strings) {
425 ret = add_etypes(context, &princ, &e->enctypes_strings);
426 mask |= KADM5_TL_DATA;
428 if (e->krb5_config_file_string) {
429 add_krb5_config(&princ, e->krb5_config_file_string);
430 mask |= KADM5_TL_DATA;
432 } else
433 ret = edit_entry(&princ, &mask, NULL, 0);
434 if(ret == 0) {
435 ret = kadm5_modify_principal(kadm_handle, &princ, mask);
436 if(ret)
437 krb5_warn(context, ret, "kadm5_modify_principal");
440 kadm5_free_principal_ent(kadm_handle, &princ);
441 return ret;
445 modify_namespace(struct modify_namespace_options *opt, int argc, char **argv)
447 krb5_error_code ret = 0;
448 int i;
450 for(i = 0; i < argc; i++) {
451 ret = foreach_principal(argv[i], do_mod_ns_entry, "mod_ns", opt);
452 if (ret)
453 break;
455 return ret != 0;
458 #if 0
459 struct modify_namespace_key_rotation_options {
460 int force_flag;
461 int keep_base_key_flag;
462 char* revoke_old_string;
463 char* new_key_rotation_epoch_string;
464 char* new_key_rotation_period_string;
466 #endif
468 static int
469 princ2kstuple(kadm5_principal_ent_rec *princ,
470 unsigned int kvno,
471 krb5_key_salt_tuple **kstuple,
472 size_t *nkstuple)
474 krb5_error_code ret = 0;
475 HDB_EncTypeList etypes;
476 krb5_tl_data *tl;
477 size_t i;
479 *kstuple = 0;
480 *nkstuple = 0;
481 etypes.len = 0;
482 etypes.val = 0;
483 for (tl = princ->tl_data; tl; tl = tl->tl_data_next) {
484 if (tl->tl_data_type != KRB5_TL_ETYPES || tl->tl_data_length < 0)
485 continue;
486 ret = decode_HDB_EncTypeList(tl->tl_data_contents, tl->tl_data_length,
487 &etypes, NULL);
488 if (ret)
489 break;
490 *nkstuple = etypes.len;
491 *kstuple = ecalloc(etypes.len, sizeof(kstuple[0][0]));
492 for (i = 0; i < etypes.len; i++) {
493 (*kstuple)[i].ks_enctype = etypes.val[i];
494 (*kstuple)[i].ks_salttype = 0;
496 return 0;
498 if (princ->n_key_data > 0) {
499 *kstuple = ecalloc(1, sizeof(kstuple[0][0]));
500 *nkstuple = 1;
501 for (i = 0; i < princ->n_key_data; i++) {
502 if (princ->key_data->key_data_kvno == kvno) {
503 (*kstuple)[0].ks_enctype = princ->key_data->key_data_type[0];
504 (*kstuple)[0].ks_salttype = princ->key_data->key_data_type[1];
505 return 0;
509 krb5_warnx(context, "Could not determine what enctypes to generate "
510 "keys for; recreate namespace?");
511 return EINVAL;
514 static int
515 randkey_kr(kadm5_principal_ent_rec *princ,
516 unsigned int old_kvno,
517 unsigned int kvno)
519 krb5_key_salt_tuple *kstuple = 0;
520 krb5_error_code ret = 0;
521 size_t nkstuple = 0;
524 * We might be using kadm5clnt, so we'll use kadm5_randkey_principal_3(),
525 * which will generate new keys on the server side. This allows a race,
526 * but it will be detected by the key rotation update checks in lib/kadm5
527 * and lib/hdb.
529 ret = princ2kstuple(princ, old_kvno, &kstuple, &nkstuple);
530 if (ret == 0)
531 ret = kadm5_randkey_principal_3(kadm_handle, princ->principal, 1,
532 nkstuple, kstuple, NULL, NULL);
533 free(kstuple);
534 return ret;
537 static int
538 do_mod_ns_kr(krb5_principal principal, void *data)
540 krb5_error_code ret;
541 kadm5_principal_ent_rec princ;
542 struct modify_namespace_key_rotation_options *e = data;
543 HDB_Ext_KeyRotation existing;
544 HDB_Ext_KeyRotation new_kr;
545 HDB_extension ext;
546 KeyRotation new_krs[3];
547 krb5_tl_data *tl;
548 krb5_data d;
549 time_t now = time(NULL);
550 size_t size;
551 int freeit = 0;
553 d.data = 0;
554 d.length = 0;
555 new_kr.len = 0;
556 new_kr.val = new_krs;
557 ext.mandatory = 0;
558 ext.data.element = choice_HDB_extension_data_key_rotation;
559 ext.data.u.key_rotation.len = 0;
560 ext.data.u.key_rotation.val = 0;
561 existing.len = 0;
562 existing.val = 0;
563 memset(&new_krs, 0, sizeof(new_krs));
564 memset(&princ, 0, sizeof(princ));
566 if (e->force_flag || e->revoke_old_string) {
567 krb5_warnx(context, "--force and --revoke-old not implemented yet");
568 return ENOTSUP;
571 ret = kadm5_get_principal(kadm_handle, principal, &princ,
572 KADM5_PRINCIPAL | KADM5_KVNO |
573 KADM5_KEY_DATA | KADM5_TL_DATA);
574 if (ret == 0) {
575 freeit = 1;
576 for (tl = princ.tl_data; tl; tl = tl->tl_data_next) {
577 if (tl->tl_data_type != KRB5_TL_KRB5_CONFIG)
578 continue;
579 ret = decode_HDB_Ext_KeyRotation(tl->tl_data_contents,
580 tl->tl_data_length, &existing, NULL);
581 if (ret) {
582 krb5_warn(context, ret, "unable to decode existing key "
583 "rotation schedule");
584 kadm5_free_principal_ent(kadm_handle, &princ);
585 return ret;
588 if (!existing.len) {
589 krb5_warnx(context, "no key rotation schedule; "
590 "re-create namespace?");
591 kadm5_free_principal_ent(kadm_handle, &princ);
592 return EINVAL;
596 if (ret) {
597 krb5_warn(context, ret, "No such namespace");
598 kadm5_free_principal_ent(kadm_handle, &princ);
599 return ret;
602 if (existing.len > 1)
603 new_kr.val[1] = existing.val[0];
604 if (existing.len > 2)
605 new_kr.val[2] = existing.val[1];
606 new_kr.val[0].flags = existing.val[0].flags;
607 new_kr.val[0].base_kvno = princ.kvno + 2; /* XXX Compute better */
608 new_kr.val[0].base_key_kvno = existing.val[0].base_key_kvno + 1;
609 if (e->new_key_rotation_epoch_string) {
610 if ((ret = str2time_t(e->new_key_rotation_epoch_string,
611 &new_kr.val[0].epoch)))
612 krb5_warn(context, ret, "Invalid epoch specification: %s",
613 e->new_key_rotation_epoch_string);
614 } else {
615 new_kr.val[0].epoch = existing.val[0].epoch +
616 existing.val[0].period * (princ.kvno - new_kr.val[0].base_kvno);
618 if (ret == 0 && e->new_key_rotation_period_string) {
619 time_t t;
621 if ((ret = str2time_t(e->new_key_rotation_period_string, &t)))
622 krb5_warn(context, ret, "Invalid period specification: %s",
623 e->new_key_rotation_period_string);
624 else
625 new_kr.val[0].period = t;
626 } else {
627 new_kr.val[0].period = existing.val[0].period +
628 existing.val[0].period * (princ.kvno - new_kr.val[0].base_kvno);
630 if (new_kr.val[0].epoch < now) {
631 krb5_warnx(context, "New epoch cannot be in the past");
632 ret = EINVAL;
634 if (new_kr.val[0].epoch < 30) {
635 krb5_warnx(context, "New period cannot be less than 30s");
636 ret = EINVAL;
638 if (ret == 0)
639 ret = randkey_kr(&princ, princ.kvno, new_kr.val[0].base_key_kvno);
640 ext.data.u.key_rotation = new_kr;
641 if (ret == 0)
642 ASN1_MALLOC_ENCODE(HDB_extension, d.data, d.length,
643 &ext, &size, ret);
644 if (ret == 0)
645 add_tl(&princ, KRB5_TL_EXTENSION, &d);
646 if (ret == 0) {
647 ret = kadm5_modify_principal(kadm_handle, &princ,
648 KADM5_PRINCIPAL | KADM5_TL_DATA);
649 if (ret)
650 krb5_warn(context, ret, "Could not update namespace");
653 krb5_data_free(&d);
654 free_HDB_Ext_KeyRotation(&existing);
655 if (freeit)
656 kadm5_free_principal_ent(kadm_handle, &princ);
657 return ret;
661 modify_ns_kr(struct modify_namespace_key_rotation_options *opt,
662 int argc,
663 char **argv)
665 krb5_error_code ret = 0;
666 int i;
668 for(i = 0; i < argc; i++) {
669 ret = foreach_principal(argv[i], do_mod_ns_kr, "mod_ns", opt);
670 if (ret)
671 break;
673 return ret != 0;
674 return 0;
677 #define princ_realm(P) ((P)->realm)
678 #define princ_num_comp(P) ((P)->name.name_string.len)
679 #define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
681 static int
682 princ_cmp(const void *a, const void *b)
684 krb5_const_principal pa = a;
685 krb5_const_principal pb = b;
686 size_t i;
687 int r;
689 r = strcmp(princ_realm(pa), princ_realm(pb));
690 if (r == 0)
691 r = princ_num_comp(pa) - princ_num_comp(pb);
692 for (i = 0; r == 0 && i < princ_num_comp(pa); i++)
693 r = strcmp(princ_ncomp(pa, i), princ_ncomp(pb, i));
694 return r;
697 /* Sort and remove dups */
698 static void
699 uniq(HDB_Ext_Aliases *a)
701 size_t i = 0;
703 qsort(a->aliases.val, a->aliases.len, sizeof(a->aliases.val[0]),
704 princ_cmp);
706 /* While there are at least two principals left to look at... */
707 while (i + 1 < a->aliases.len) {
708 if (princ_cmp(&a->aliases.val[i], &a->aliases.val[i + 1])) {
709 /* ...if they are different, increment i and loop */
710 i++;
711 continue;
713 /* ...else drop the one on the right and loop w/o incrementing i */
714 free_Principal(&a->aliases.val[i + 1]);
715 if (i + 2 < a->aliases.len)
716 memmove(&a->aliases.val[i + 1],
717 &a->aliases.val[i + 2],
718 sizeof(a->aliases.val[i + 1]) * (a->aliases.len - (i + 2)));
719 a->aliases.len--;
724 add_alias(void *opt, int argc, char **argv)
726 kadm5_principal_ent_rec princ;
727 krb5_error_code ret;
728 krb5_principal p = NULL;
729 HDB_Ext_Aliases *a;
730 HDB_extension ext;
731 krb5_tl_data *tl = NULL;
732 krb5_data d;
733 size_t i;
735 memset(&princ, 0, sizeof(princ));
736 krb5_data_zero(&d);
738 if (argc < 2) {
739 krb5_warnx(context, "Principal not given");
740 return 1;
742 ret = krb5_parse_name(context, argv[0], &p);
743 if (ret) {
744 krb5_warn(context, ret, "Invalid principal: %s", argv[0]);
745 return 1;
748 ret = kadm5_get_principal(kadm_handle, p, &princ,
749 KADM5_PRINCIPAL_NORMAL_MASK | KADM5_TL_DATA);
750 if (ret) {
751 krb5_warn(context, ret, "Principal not found %s", argv[0]);
752 return 1;
754 krb5_free_principal(context, p);
755 p = NULL;
757 a = &ext.data.u.aliases;
758 a->case_insensitive = 0;
759 a->aliases.len = 0;
760 a->aliases.val = 0;
761 if ((tl = get_tl(&princ, KRB5_TL_ALIASES))) {
762 ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, tl->tl_data_length,
763 a, NULL);
764 if (ret) {
765 kadm5_free_principal_ent(kadm_handle, &princ);
766 krb5_warn(context, ret, "Principal has invalid aliases extension "
767 "contents: %s", argv[0]);
768 return 1;
772 argv++;
773 argc--;
775 a->aliases.val = realloc(a->aliases.val,
776 sizeof(a->aliases.val[0]) * (a->aliases.len + argc));
777 if (a->aliases.val == NULL)
778 krb5_err(context, 1, errno, "Out of memory");
779 for (i = 0; ret == 0 && i < argc; i++) {
780 ret = krb5_parse_name(context, argv[i], &p);
781 if (ret) {
782 krb5_warn(context, ret, "krb5_parse_name");
783 break;
785 ret = copy_Principal(p, &a->aliases.val[a->aliases.len]);
786 krb5_free_principal(context, p);
787 if (ret == 0)
788 a->aliases.len++;
790 uniq(a);
792 ext.data.element = choice_HDB_extension_data_aliases;
793 ext.mandatory = 0;
794 if (ret == 0)
795 ASN1_MALLOC_ENCODE(HDB_extension, d.data, d.length, &ext, &i, ret);
796 free_HDB_extension(&ext);
797 if (ret == 0) {
798 int16_t len = d.length;
800 if (len < 0 || d.length != (size_t)len) {
801 krb5_warnx(context, "Too many aliases; does not fit in 32767 bytes");
802 ret = EOVERFLOW;
805 if (ret == 0) {
806 add_tl(&princ, KRB5_TL_EXTENSION, &d);
807 krb5_data_zero(&d);
808 ret = kadm5_modify_principal(kadm_handle, &princ,
809 KADM5_PRINCIPAL | KADM5_TL_DATA);
810 if (ret)
811 krb5_warn(context, ret, "kadm5_modify_principal");
814 kadm5_free_principal_ent(kadm_handle, &princ);
815 krb5_data_free(&d);
816 return ret == 0 ? 0 : 1;