kdc: cmd_append fix broken commit
[heimdal.git] / kadmin / mod.c
blob7c7b2dd7ce447bcaadc739d95777aad402b2785d
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 = 0;
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; ret == 0 && i < strings->num_strings; i++) {
148 ret = krb5_parse_name(contextp, strings->strings[i], &p);
149 if (ret)
150 krb5_err(contextp, 1, ret, "Could not parse alias %s",
151 strings->strings[i]);
152 if (ret == 0)
153 ret = copy_Principal(p, &ext.data.u.aliases.aliases.val[i]);
154 if (ret)
155 krb5_err(contextp, 1, ret, "Could not copy parsed alias %s",
156 strings->strings[i]);
157 krb5_free_principal(contextp, p);
161 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
162 &ext, &size, ret);
163 free_HDB_extension(&ext);
164 if (ret)
165 abort();
166 if (buf.length != size)
167 abort();
169 add_tl(princ, KRB5_TL_EXTENSION, &buf);
172 static void
173 add_pkinit_acl(krb5_context contextp, kadm5_principal_ent_rec *princ,
174 struct getarg_strings *strings)
176 krb5_error_code ret;
177 HDB_extension ext;
178 krb5_data buf;
179 size_t size = 0;
180 int i;
182 memset(&ext, 0, sizeof(ext));
183 ext.mandatory = FALSE;
184 ext.data.element = choice_HDB_extension_data_pkinit_acl;
185 ext.data.u.aliases.case_insensitive = 0;
187 if (strings->num_strings == 1 && strings->strings[0][0] == '\0') {
188 ext.data.u.pkinit_acl.val = NULL;
189 ext.data.u.pkinit_acl.len = 0;
190 } else {
191 ext.data.u.pkinit_acl.val =
192 calloc(strings->num_strings,
193 sizeof(ext.data.u.pkinit_acl.val[0]));
194 ext.data.u.pkinit_acl.len = strings->num_strings;
196 for (i = 0; i < strings->num_strings; i++) {
197 ext.data.u.pkinit_acl.val[i].subject = estrdup(strings->strings[i]);
201 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
202 &ext, &size, ret);
203 free_HDB_extension(&ext);
204 if (ret)
205 abort();
206 if (buf.length != size)
207 abort();
209 add_tl(princ, KRB5_TL_EXTENSION, &buf);
212 static krb5_error_code
213 add_etypes(krb5_context contextp,
214 kadm5_principal_ent_rec *princ,
215 struct getarg_strings *strings)
217 krb5_error_code ret = 0;
218 HDB_EncTypeList etypes;
219 krb5_data buf;
220 size_t i, size;
222 etypes.len = strings->num_strings;
223 if ((etypes.val = calloc(strings->num_strings,
224 sizeof(etypes.val[0]))) == NULL)
225 krb5_err(contextp, 1, ret, "Out of memory");
227 for (i = 0; i < strings->num_strings; i++) {
228 krb5_enctype etype;
230 ret = krb5_string_to_enctype(contextp, strings->strings[i], &etype);
231 if (ret) {
232 krb5_warn(contextp, ret, "Could not parse enctype %s",
233 strings->strings[i]);
234 free(etypes.val);
235 return ret;
237 etypes.val[i] = etype;
240 if (ret == 0) {
241 ASN1_MALLOC_ENCODE(HDB_EncTypeList, buf.data, buf.length,
242 &etypes, &size, ret);
244 if (ret || buf.length != size)
245 abort();
246 add_tl(princ, KRB5_TL_ETYPES, &buf);
247 free(etypes.val);
248 return 0;
251 static void
252 add_kvno_diff(krb5_context contextp, kadm5_principal_ent_rec *princ,
253 int is_svc_diff, krb5_kvno kvno_diff)
255 krb5_error_code ret;
256 HDB_extension ext;
257 krb5_data buf;
258 size_t size = 0;
260 if (kvno_diff < 0)
261 return;
262 if (kvno_diff > 2048)
263 kvno_diff = 2048;
265 if (is_svc_diff) {
266 ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc;
267 ext.data.u.hist_kvno_diff_svc = (unsigned int)kvno_diff;
268 } else {
269 ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt;
270 ext.data.u.hist_kvno_diff_clnt = (unsigned int)kvno_diff;
272 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
273 &ext, &size, ret);
274 if (ret)
275 abort();
276 if (buf.length != size)
277 abort();
279 add_tl(princ, KRB5_TL_EXTENSION, &buf);
282 static void
283 add_krb5_config(kadm5_principal_ent_rec *princ, const char *fname)
285 HDB_extension ext;
286 krb5_data buf;
287 size_t size;
288 int ret;
290 memset(&ext, 0, sizeof(ext));
291 ext.mandatory = FALSE;
292 ext.data.element = choice_HDB_extension_data_krb5_config;
294 if ((ret = rk_undumpdata(fname,
295 &ext.data.u.krb5_config.data,
296 &ext.data.u.krb5_config.length))) {
297 krb5_warn(context, ret, "Could not read %s", fname);
298 return;
301 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
302 &ext, &size, ret);
303 free_HDB_extension(&ext);
304 if (ret)
305 abort();
306 if (buf.length != size)
307 abort();
308 add_tl(princ, KRB5_TL_EXTENSION, &buf);
311 static int
312 do_mod_entry(krb5_principal principal, void *data)
314 krb5_error_code ret;
315 kadm5_principal_ent_rec princ;
316 int mask = 0;
317 struct modify_options *e = data;
319 memset (&princ, 0, sizeof(princ));
320 ret = kadm5_get_principal(kadm_handle, principal, &princ,
321 KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
322 KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
323 KADM5_PRINC_EXPIRE_TIME |
324 KADM5_PW_EXPIRATION);
325 if(ret)
326 return ret;
328 if(e->max_ticket_life_string ||
329 e->max_renewable_life_string ||
330 e->expiration_time_string ||
331 e->pw_expiration_time_string ||
332 e->attributes_string ||
333 e->policy_string ||
334 e->kvno_integer != -1 ||
335 e->service_enctypes_strings.num_strings ||
336 e->constrained_delegation_strings.num_strings ||
337 e->alias_strings.num_strings ||
338 e->pkinit_acl_strings.num_strings ||
339 e->krb5_config_file_string ||
340 e->hist_kvno_diff_clnt_integer != -1 ||
341 e->hist_kvno_diff_svc_integer != -1) {
342 ret = set_entry(context, &princ, &mask,
343 e->max_ticket_life_string,
344 e->max_renewable_life_string,
345 e->expiration_time_string,
346 e->pw_expiration_time_string,
347 e->attributes_string,
348 e->policy_string);
349 if(e->kvno_integer != -1) {
350 princ.kvno = e->kvno_integer;
351 mask |= KADM5_KVNO;
353 if (e->constrained_delegation_strings.num_strings) {
354 add_constrained_delegation(context, &princ,
355 &e->constrained_delegation_strings);
356 mask |= KADM5_TL_DATA;
358 if (e->alias_strings.num_strings) {
359 add_aliases(context, &princ, &e->alias_strings);
360 mask |= KADM5_TL_DATA;
362 if (e->pkinit_acl_strings.num_strings) {
363 add_pkinit_acl(context, &princ, &e->pkinit_acl_strings);
364 mask |= KADM5_TL_DATA;
366 if (e->service_enctypes_strings.num_strings) {
367 ret = add_etypes(context, &princ, &e->service_enctypes_strings);
368 mask |= KADM5_TL_DATA;
370 if (e->hist_kvno_diff_clnt_integer != -1) {
371 add_kvno_diff(context, &princ, 0, e->hist_kvno_diff_clnt_integer);
372 mask |= KADM5_TL_DATA;
374 if (e->hist_kvno_diff_svc_integer != -1) {
375 add_kvno_diff(context, &princ, 1, e->hist_kvno_diff_svc_integer);
376 mask |= KADM5_TL_DATA;
378 if (e->krb5_config_file_string) {
379 add_krb5_config(&princ, e->krb5_config_file_string);
380 mask |= KADM5_TL_DATA;
382 } else
383 ret = edit_entry(&princ, &mask, NULL, 0);
384 if(ret == 0) {
385 ret = kadm5_modify_principal(kadm_handle, &princ, mask);
386 if(ret)
387 krb5_warn(context, ret, "kadm5_modify_principal");
390 kadm5_free_principal_ent(kadm_handle, &princ);
391 return ret;
395 mod_entry(struct modify_options *opt, int argc, char **argv)
397 krb5_error_code ret = 0;
398 int i;
400 for(i = 0; i < argc; i++) {
401 ret = foreach_principal(argv[i], do_mod_entry, "mod", opt);
402 if (ret)
403 break;
405 return ret != 0;
408 static int
409 do_mod_ns_entry(krb5_principal principal, void *data)
411 krb5_error_code ret;
412 kadm5_principal_ent_rec princ;
413 int mask = 0;
414 struct modify_namespace_options *e = data;
416 memset (&princ, 0, sizeof(princ));
417 ret = kadm5_get_principal(kadm_handle, principal, &princ,
418 KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
419 KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
420 KADM5_PRINC_EXPIRE_TIME |
421 KADM5_PW_EXPIRATION);
422 if(ret)
423 return ret;
425 if(e->max_ticket_life_string ||
426 e->max_renewable_life_string ||
427 e->attributes_string ||
428 e->enctypes_strings.num_strings ||
429 e->krb5_config_file_string) {
430 ret = set_entry(context, &princ, &mask, e->max_ticket_life_string,
431 e->max_renewable_life_string, NULL, NULL,
432 e->attributes_string, NULL);
433 if (e->enctypes_strings.num_strings) {
434 ret = add_etypes(context, &princ, &e->enctypes_strings);
435 mask |= KADM5_TL_DATA;
437 if (e->krb5_config_file_string) {
438 add_krb5_config(&princ, e->krb5_config_file_string);
439 mask |= KADM5_TL_DATA;
441 } else
442 ret = edit_entry(&princ, &mask, NULL, 0);
443 if(ret == 0) {
444 ret = kadm5_modify_principal(kadm_handle, &princ, mask);
445 if(ret)
446 krb5_warn(context, ret, "kadm5_modify_principal");
449 kadm5_free_principal_ent(kadm_handle, &princ);
450 return ret;
454 modify_namespace(struct modify_namespace_options *opt, int argc, char **argv)
456 krb5_error_code ret = 0;
457 int i;
459 for(i = 0; i < argc; i++) {
460 ret = foreach_principal(argv[i], do_mod_ns_entry, "mod_ns", opt);
461 if (ret)
462 break;
464 return ret != 0;
467 #if 0
468 struct modify_namespace_key_rotation_options {
469 int force_flag;
470 int keep_base_key_flag;
471 char* revoke_old_string;
472 char* new_key_rotation_epoch_string;
473 char* new_key_rotation_period_string;
475 #endif
477 static int
478 princ2kstuple(kadm5_principal_ent_rec *princ,
479 unsigned int kvno,
480 krb5_key_salt_tuple **kstuple,
481 size_t *nkstuple)
483 krb5_error_code ret = 0;
484 HDB_EncTypeList etypes;
485 krb5_tl_data *tl;
486 size_t i;
488 *kstuple = 0;
489 *nkstuple = 0;
490 etypes.len = 0;
491 etypes.val = 0;
492 for (tl = princ->tl_data; tl; tl = tl->tl_data_next) {
493 if (tl->tl_data_type != KRB5_TL_ETYPES || tl->tl_data_length < 0)
494 continue;
495 ret = decode_HDB_EncTypeList(tl->tl_data_contents, tl->tl_data_length,
496 &etypes, NULL);
497 if (ret)
498 break;
499 *nkstuple = etypes.len;
500 *kstuple = ecalloc(etypes.len, sizeof(kstuple[0][0]));
501 for (i = 0; i < etypes.len; i++) {
502 (*kstuple)[i].ks_enctype = etypes.val[i];
503 (*kstuple)[i].ks_salttype = 0;
505 return 0;
507 if (princ->n_key_data > 0) {
508 *kstuple = ecalloc(1, sizeof(kstuple[0][0]));
509 *nkstuple = 1;
510 for (i = 0; i < princ->n_key_data; i++) {
511 if (princ->key_data->key_data_kvno == kvno) {
512 (*kstuple)[0].ks_enctype = princ->key_data->key_data_type[0];
513 (*kstuple)[0].ks_salttype = princ->key_data->key_data_type[1];
514 return 0;
518 krb5_warnx(context, "Could not determine what enctypes to generate "
519 "keys for; recreate namespace?");
520 return EINVAL;
523 static int
524 randkey_kr(kadm5_principal_ent_rec *princ,
525 unsigned int old_kvno,
526 unsigned int kvno)
528 krb5_key_salt_tuple *kstuple = 0;
529 krb5_error_code ret = 0;
530 size_t nkstuple = 0;
533 * We might be using kadm5clnt, so we'll use kadm5_randkey_principal_3(),
534 * which will generate new keys on the server side. This allows a race,
535 * but it will be detected by the key rotation update checks in lib/kadm5
536 * and lib/hdb.
538 ret = princ2kstuple(princ, old_kvno, &kstuple, &nkstuple);
539 if (ret == 0)
540 ret = kadm5_randkey_principal_3(kadm_handle, princ->principal, 1,
541 nkstuple, kstuple, NULL, NULL);
542 free(kstuple);
543 return ret;
546 static int
547 do_mod_ns_kr(krb5_principal principal, void *data)
549 krb5_error_code ret;
550 kadm5_principal_ent_rec princ;
551 struct modify_namespace_key_rotation_options *e = data;
552 HDB_Ext_KeyRotation existing;
553 HDB_Ext_KeyRotation new_kr;
554 HDB_extension ext;
555 KeyRotation new_krs[3];
556 krb5_tl_data *tl;
557 krb5_data d;
558 time_t now = time(NULL);
559 size_t size;
560 int freeit = 0;
562 d.data = 0;
563 d.length = 0;
564 new_kr.len = 0;
565 new_kr.val = new_krs;
566 ext.mandatory = 0;
567 ext.data.element = choice_HDB_extension_data_key_rotation;
568 ext.data.u.key_rotation.len = 0;
569 ext.data.u.key_rotation.val = 0;
570 existing.len = 0;
571 existing.val = 0;
572 memset(&new_krs, 0, sizeof(new_krs));
573 memset(&princ, 0, sizeof(princ));
575 if (e->force_flag || e->revoke_old_string) {
576 krb5_warnx(context, "--force and --revoke-old not implemented yet");
577 return ENOTSUP;
580 ret = kadm5_get_principal(kadm_handle, principal, &princ,
581 KADM5_PRINCIPAL | KADM5_KVNO |
582 KADM5_KEY_DATA | KADM5_TL_DATA);
583 if (ret == 0) {
584 freeit = 1;
585 for (tl = princ.tl_data; tl; tl = tl->tl_data_next) {
586 if (tl->tl_data_type != KRB5_TL_KRB5_CONFIG)
587 continue;
588 ret = decode_HDB_Ext_KeyRotation(tl->tl_data_contents,
589 tl->tl_data_length, &existing, NULL);
590 if (ret) {
591 krb5_warn(context, ret, "unable to decode existing key "
592 "rotation schedule");
593 kadm5_free_principal_ent(kadm_handle, &princ);
594 return ret;
597 if (!existing.len) {
598 krb5_warnx(context, "no key rotation schedule; "
599 "re-create namespace?");
600 kadm5_free_principal_ent(kadm_handle, &princ);
601 return EINVAL;
605 if (ret) {
606 krb5_warn(context, ret, "No such namespace");
607 kadm5_free_principal_ent(kadm_handle, &princ);
608 return ret;
611 if (existing.len > 1)
612 new_kr.val[1] = existing.val[0];
613 if (existing.len > 2)
614 new_kr.val[2] = existing.val[1];
615 new_kr.val[0].flags = existing.val[0].flags;
616 new_kr.val[0].base_kvno = princ.kvno + 2; /* XXX Compute better */
617 new_kr.val[0].base_key_kvno = existing.val[0].base_key_kvno + 1;
618 if (e->new_key_rotation_epoch_string) {
619 if ((ret = str2time_t(e->new_key_rotation_epoch_string,
620 &new_kr.val[0].epoch)))
621 krb5_warn(context, ret, "Invalid epoch specification: %s",
622 e->new_key_rotation_epoch_string);
623 } else {
624 new_kr.val[0].epoch = existing.val[0].epoch +
625 existing.val[0].period * (princ.kvno - new_kr.val[0].base_kvno);
627 if (ret == 0 && e->new_key_rotation_period_string) {
628 time_t t;
630 if ((ret = str2time_t(e->new_key_rotation_period_string, &t)))
631 krb5_warn(context, ret, "Invalid period specification: %s",
632 e->new_key_rotation_period_string);
633 else
634 new_kr.val[0].period = t;
635 } else {
636 new_kr.val[0].period = existing.val[0].period +
637 existing.val[0].period * (princ.kvno - new_kr.val[0].base_kvno);
639 if (new_kr.val[0].epoch < now) {
640 krb5_warnx(context, "New epoch cannot be in the past");
641 ret = EINVAL;
643 if (new_kr.val[0].epoch < 30) {
644 krb5_warnx(context, "New period cannot be less than 30s");
645 ret = EINVAL;
647 if (ret == 0)
648 ret = randkey_kr(&princ, princ.kvno, new_kr.val[0].base_key_kvno);
649 ext.data.u.key_rotation = new_kr;
650 if (ret == 0)
651 ASN1_MALLOC_ENCODE(HDB_extension, d.data, d.length,
652 &ext, &size, ret);
653 if (ret == 0)
654 add_tl(&princ, KRB5_TL_EXTENSION, &d);
655 if (ret == 0) {
656 ret = kadm5_modify_principal(kadm_handle, &princ,
657 KADM5_PRINCIPAL | KADM5_TL_DATA);
658 if (ret)
659 krb5_warn(context, ret, "Could not update namespace");
662 krb5_data_free(&d);
663 free_HDB_Ext_KeyRotation(&existing);
664 if (freeit)
665 kadm5_free_principal_ent(kadm_handle, &princ);
666 return ret;
670 modify_ns_kr(struct modify_namespace_key_rotation_options *opt,
671 int argc,
672 char **argv)
674 krb5_error_code ret = 0;
675 int i;
677 for(i = 0; i < argc; i++) {
678 ret = foreach_principal(argv[i], do_mod_ns_kr, "mod_ns", opt);
679 if (ret)
680 break;
682 return ret != 0;
683 return 0;
686 #define princ_realm(P) ((P)->realm)
687 #define princ_num_comp(P) ((P)->name.name_string.len)
688 #define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
690 static int
691 princ_cmp(const void *a, const void *b)
693 krb5_const_principal pa = a;
694 krb5_const_principal pb = b;
695 size_t i;
696 int r;
698 r = strcmp(princ_realm(pa), princ_realm(pb));
699 if (r == 0)
700 r = princ_num_comp(pa) - princ_num_comp(pb);
701 for (i = 0; r == 0 && i < princ_num_comp(pa); i++)
702 r = strcmp(princ_ncomp(pa, i), princ_ncomp(pb, i));
703 return r;
706 /* Sort and remove dups */
707 static void
708 uniq(HDB_Ext_Aliases *a)
710 size_t i = 0;
712 qsort(a->aliases.val, a->aliases.len, sizeof(a->aliases.val[0]),
713 princ_cmp);
715 /* While there are at least two principals left to look at... */
716 while (i + 1 < a->aliases.len) {
717 if (princ_cmp(&a->aliases.val[i], &a->aliases.val[i + 1])) {
718 /* ...if they are different, increment i and loop */
719 i++;
720 continue;
722 /* ...else drop the one on the right and loop w/o incrementing i */
723 free_Principal(&a->aliases.val[i + 1]);
724 if (i + 2 < a->aliases.len)
725 memmove(&a->aliases.val[i + 1],
726 &a->aliases.val[i + 2],
727 sizeof(a->aliases.val[i + 1]) * (a->aliases.len - (i + 2)));
728 a->aliases.len--;
733 add_alias(void *opt, int argc, char **argv)
735 kadm5_principal_ent_rec princ;
736 krb5_error_code ret;
737 krb5_principal p = NULL;
738 HDB_Ext_Aliases *a;
739 HDB_extension ext;
740 krb5_tl_data *tl = NULL;
741 krb5_data d;
742 size_t i;
744 memset(&princ, 0, sizeof(princ));
745 krb5_data_zero(&d);
747 if (argc < 2) {
748 krb5_warnx(context, "Principal not given");
749 return 1;
751 ret = krb5_parse_name(context, argv[0], &p);
752 if (ret) {
753 krb5_warn(context, ret, "Invalid principal: %s", argv[0]);
754 return 1;
757 ret = kadm5_get_principal(kadm_handle, p, &princ,
758 KADM5_PRINCIPAL_NORMAL_MASK | KADM5_TL_DATA);
759 if (ret) {
760 krb5_warn(context, ret, "Principal not found %s", argv[0]);
761 return 1;
763 krb5_free_principal(context, p);
764 p = NULL;
766 a = &ext.data.u.aliases;
767 a->case_insensitive = 0;
768 a->aliases.len = 0;
769 a->aliases.val = 0;
770 if ((tl = get_tl(&princ, KRB5_TL_ALIASES))) {
771 ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, tl->tl_data_length,
772 a, NULL);
773 if (ret) {
774 kadm5_free_principal_ent(kadm_handle, &princ);
775 krb5_warn(context, ret, "Principal has invalid aliases extension "
776 "contents: %s", argv[0]);
777 return 1;
781 argv++;
782 argc--;
784 a->aliases.val = realloc(a->aliases.val,
785 sizeof(a->aliases.val[0]) * (a->aliases.len + argc));
786 if (a->aliases.val == NULL)
787 krb5_err(context, 1, errno, "Out of memory");
788 for (i = 0; ret == 0 && i < argc; i++) {
789 ret = krb5_parse_name(context, argv[i], &p);
790 if (ret) {
791 krb5_warn(context, ret, "krb5_parse_name");
792 break;
794 ret = copy_Principal(p, &a->aliases.val[a->aliases.len]);
795 krb5_free_principal(context, p);
796 if (ret == 0)
797 a->aliases.len++;
799 uniq(a);
801 ext.data.element = choice_HDB_extension_data_aliases;
802 ext.mandatory = 0;
803 if (ret == 0)
804 ASN1_MALLOC_ENCODE(HDB_extension, d.data, d.length, &ext, &i, ret);
805 free_HDB_extension(&ext);
806 if (ret == 0) {
807 int16_t len = d.length;
809 if (len < 0 || d.length != (size_t)len) {
810 krb5_warnx(context, "Too many aliases; does not fit in 32767 bytes");
811 ret = EOVERFLOW;
814 if (ret == 0) {
815 add_tl(&princ, KRB5_TL_EXTENSION, &d);
816 krb5_data_zero(&d);
817 ret = kadm5_modify_principal(kadm_handle, &princ,
818 KADM5_PRINCIPAL | KADM5_TL_DATA);
819 if (ret)
820 krb5_warn(context, ret, "kadm5_modify_principal");
823 kadm5_free_principal_ent(kadm_handle, &princ);
824 krb5_data_free(&d);
825 return ret == 0 ? 0 : 1;