kdc: use attribute dictionary in altsec authorizer plugin
[heimdal.git] / kadmin / ank.c
blob1e5cd6117dcfab40f2d4f3b55dbfd9b3d3f8f550
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 /* No useful password policies for namespaces */
38 #define NSPOLICY "default"
41 * fetch the default principal corresponding to `princ'
44 static krb5_error_code
45 get_default (kadm5_server_context *contextp,
46 krb5_principal princ,
47 kadm5_principal_ent_t default_ent)
49 krb5_error_code ret;
50 krb5_principal def_principal;
51 krb5_const_realm realm = krb5_principal_get_realm(contextp->context, princ);
53 ret = krb5_make_principal (contextp->context, &def_principal,
54 realm, "default", NULL);
55 if (ret)
56 return ret;
57 ret = kadm5_get_principal (contextp, def_principal, default_ent,
58 KADM5_PRINCIPAL_NORMAL_MASK);
59 krb5_free_principal (contextp->context, def_principal);
60 return ret;
64 * Add the principal `name' to the database.
65 * Prompt for all data not given by the input parameters.
68 static krb5_error_code
69 add_one_principal(const char *name,
70 int rand_key,
71 int rand_password,
72 int use_defaults,
73 char *password,
74 char *policy,
75 size_t nkstuple,
76 krb5_key_salt_tuple *kstuple,
77 krb5_key_data *key_data,
78 const char *max_ticket_life,
79 const char *max_renewable_life,
80 const char *attributes,
81 const char *expiration,
82 const char *pw_expiration)
84 krb5_error_code ret;
85 kadm5_principal_ent_rec princ, defrec;
86 kadm5_principal_ent_rec *default_ent = NULL;
87 krb5_principal princ_ent = NULL;
88 krb5_timestamp pw_expire;
89 int mask = 0;
90 int default_mask = 0;
91 char pwbuf[1024];
93 memset(&princ, 0, sizeof(princ));
94 ret = krb5_parse_name(context, name, &princ_ent);
95 if (ret) {
96 krb5_warn(context, ret, "krb5_parse_name");
97 return ret;
99 princ.principal = princ_ent;
100 mask |= KADM5_PRINCIPAL;
102 ret = set_entry(context, &princ, &mask,
103 max_ticket_life, max_renewable_life,
104 expiration, pw_expiration, attributes, policy);
105 if (ret)
106 goto out;
108 default_ent = &defrec;
109 ret = get_default (kadm_handle, princ_ent, default_ent);
110 if (ret) {
111 default_ent = NULL;
112 default_mask = 0;
113 } else {
114 default_mask = KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
115 KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION;
118 if(use_defaults)
119 set_defaults(&princ, &mask, default_ent, default_mask);
120 else
121 if(edit_entry(&princ, &mask, default_ent, default_mask))
122 goto out;
123 if(rand_key || key_data) {
124 princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
125 mask |= KADM5_ATTRIBUTES;
126 random_password (pwbuf, sizeof(pwbuf));
127 password = pwbuf;
128 } else if (rand_password) {
129 random_password (pwbuf, sizeof(pwbuf));
130 password = pwbuf;
131 } else if(password == NULL) {
132 char *princ_name;
133 char *prompt;
134 int aret;
136 ret = krb5_unparse_name(context, princ_ent, &princ_name);
137 if (ret)
138 goto out;
139 aret = asprintf (&prompt, "%s's Password: ", princ_name);
140 free (princ_name);
141 if (aret == -1) {
142 ret = ENOMEM;
143 krb5_set_error_message(context, ret, "out of memory");
144 goto out;
146 ret = UI_UTIL_read_pw_string (pwbuf, sizeof(pwbuf), prompt,
147 UI_UTIL_FLAG_VERIFY |
148 UI_UTIL_FLAG_VERIFY_SILENT);
149 free (prompt);
150 if (ret) {
151 ret = KRB5_LIBOS_BADPWDMATCH;
152 krb5_set_error_message(context, ret, "failed to verify password");
153 goto out;
155 password = pwbuf;
158 ret = kadm5_create_principal(kadm_handle, &princ, mask, password);
159 if(ret) {
160 krb5_warn(context, ret, "kadm5_create_principal");
161 goto out;
163 /* Save requested password expiry before it's clobbered */
164 pw_expire = princ.pw_expiration;
165 if (rand_key) {
166 krb5_keyblock *new_keys;
167 int n_keys, i;
168 ret = kadm5_randkey_principal_3(kadm_handle, princ_ent, 0,
169 nkstuple, kstuple, &new_keys, &n_keys);
170 if(ret){
171 krb5_warn(context, ret, "kadm5_randkey_principal");
172 n_keys = 0;
174 for(i = 0; i < n_keys; i++)
175 krb5_free_keyblock_contents(context, &new_keys[i]);
176 if (n_keys > 0)
177 free(new_keys);
178 kadm5_get_principal(kadm_handle, princ_ent, &princ,
179 KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES);
180 krb5_free_principal(context, princ_ent);
181 princ_ent = princ.principal;
182 princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX);
183 princ.pw_expiration = pw_expire;
185 * Updating kvno w/o key data and vice-versa gives _kadm5_setup_entry()
186 * and _kadm5_set_keys2() headaches. But we used to, so we handle
187 * this in in those two functions. Might as well leave this code as
188 * it was then.
190 princ.kvno = 1;
191 kadm5_modify_principal(kadm_handle, &princ,
192 KADM5_PW_EXPIRATION | KADM5_ATTRIBUTES | KADM5_KVNO);
193 } else if (key_data) {
194 ret = kadm5_chpass_principal_with_key (kadm_handle, princ_ent,
195 3, key_data);
196 if (ret) {
197 krb5_warn(context, ret, "kadm5_chpass_principal_with_key");
199 kadm5_get_principal(kadm_handle, princ_ent, &princ,
200 KADM5_PRINCIPAL | KADM5_ATTRIBUTES);
201 krb5_free_principal(context, princ_ent);
202 princ_ent = princ.principal;
203 princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX);
204 princ.pw_expiration = pw_expire;
205 kadm5_modify_principal(kadm_handle, &princ,
206 KADM5_PW_EXPIRATION | KADM5_ATTRIBUTES);
207 } else if (rand_password) {
208 char *princ_name;
210 krb5_unparse_name(context, princ_ent, &princ_name);
211 printf ("added %s with password \"%s\"\n", princ_name, password);
212 free (princ_name);
214 out:
215 kadm5_free_principal_ent(kadm_handle, &princ); /* frees princ_ent */
216 if(default_ent)
217 kadm5_free_principal_ent (kadm_handle, default_ent);
218 if (password != NULL)
219 memset (password, 0, strlen(password));
220 return ret;
224 * parse the string `key_string' into `key', returning 0 iff succesful.
228 * the ank command
232 * Parse arguments and add all the principals.
236 add_new_key(struct add_options *opt, int argc, char **argv)
238 krb5_error_code ret = 0;
239 krb5_key_salt_tuple *kstuple = NULL;
240 krb5_key_data key_data[3];
241 krb5_key_data *kdp = NULL;
242 const char *enctypes;
243 size_t i, nkstuple;
244 int num;
246 num = 0;
247 if (opt->random_key_flag)
248 ++num;
249 if (opt->random_password_flag)
250 ++num;
251 if (opt->password_string)
252 ++num;
253 if (opt->key_string)
254 ++num;
256 if (num > 1) {
257 fprintf (stderr, "give only one of "
258 "--random-key, --random-password, --password, --key\n");
259 return 1;
262 enctypes = opt->enctypes_string;
263 if (enctypes == NULL || enctypes[0] == '\0')
264 enctypes = krb5_config_get_string(context, NULL, "libdefaults",
265 "supported_enctypes", NULL);
266 if (enctypes == NULL || enctypes[0] == '\0')
267 enctypes = "aes128-cts-hmac-sha1-96";
268 ret = krb5_string_to_keysalts2(context, enctypes, &nkstuple, &kstuple);
269 if (ret) {
270 fprintf(stderr, "enctype(s) unknown\n");
271 return ret;
275 if (opt->key_string) {
276 const char *error;
278 if (parse_des_key (opt->key_string, key_data, &error)) {
279 fprintf(stderr, "failed parsing key \"%s\": %s\n",
280 opt->key_string, error);
281 free(kstuple);
282 return 1;
284 kdp = key_data;
287 for(i = 0; i < argc; i++) {
288 ret = add_one_principal(argv[i],
289 opt->random_key_flag,
290 opt->random_password_flag,
291 opt->use_defaults_flag,
292 opt->password_string,
293 opt->policy_string,
294 nkstuple,
295 kstuple,
296 kdp,
297 opt->max_ticket_life_string,
298 opt->max_renewable_life_string,
299 opt->attributes_string,
300 opt->expiration_time_string,
301 opt->pw_expiration_time_string);
302 if (ret) {
303 krb5_warn (context, ret, "adding %s", argv[i]);
304 break;
307 if (kdp) {
308 int16_t dummy = 3;
309 kadm5_free_key_data (kadm_handle, &dummy, key_data);
311 free(kstuple);
312 return ret != 0;
315 static krb5_error_code
316 kstuple2etypes(kadm5_principal_ent_rec *rec,
317 int *maskp,
318 size_t nkstuple,
319 krb5_key_salt_tuple *kstuple)
321 krb5_error_code ret;
322 HDB_EncTypeList etypes;
323 krb5_data buf;
324 size_t len, i;
326 etypes.len = 0;
327 if ((etypes.val = calloc(nkstuple, sizeof(etypes.val[0]))) == NULL)
328 return krb5_enomem(context);
329 for (i = 0; i < nkstuple; i++)
330 etypes.val[i] = kstuple[i].ks_enctype;
331 ASN1_MALLOC_ENCODE(HDB_EncTypeList, buf.data, buf.length,
332 &etypes, &len, ret);
333 if (ret == 0)
334 add_tl(rec, KRB5_TL_ETYPES, &buf);
335 free(etypes.val);
336 if (ret == 0)
337 (*maskp) |= KADM5_TL_DATA;
338 return ret;
342 * Add the namespace `name' to the database.
343 * Prompt for all data not given by the input parameters.
345 static krb5_error_code
346 add_one_namespace(const char *name,
347 size_t nkstuple,
348 krb5_key_salt_tuple *kstuple,
349 const char *max_ticket_life,
350 const char *max_renewable_life,
351 const char *key_rotation_epoch,
352 const char *key_rotation_period,
353 const char *attributes)
355 krb5_error_code ret;
356 kadm5_principal_ent_rec princ;
357 kadm5_principal_ent_rec *default_ent = NULL;
358 krb5_principal princ_ent = NULL;
359 int mask = 0;
360 int default_mask = 0;
361 HDB_extension ext;
362 krb5_data buf;
363 const char *comp0;
364 const char *comp1;
365 time_t kre;
366 char pwbuf[1024];
367 krb5_deltat krp;
369 if (!key_rotation_epoch) {
370 krb5_warnx(context, "key rotation epoch defaulted to \"now\"");
371 key_rotation_epoch = "now";
373 if (!key_rotation_period) {
374 krb5_warnx(context, "key rotation period defaulted to \"5d\"");
375 key_rotation_period = "5d";
377 if ((ret = str2time_t(key_rotation_epoch, &kre)) != 0) {
378 krb5_warn(context, ret, "invalid rotation epoch: %s",
379 key_rotation_epoch);
380 return ret;
382 if (ret == 0 && (ret = str2deltat(key_rotation_period, &krp)) != 0) {
383 krb5_warn(context, ret, "invalid rotation period: %s",
384 key_rotation_period);
385 return ret;
388 if (ret == 0) {
389 memset(&princ, 0, sizeof(princ));
390 princ.kvno = 1;
391 ret = krb5_parse_name(context, name, &princ_ent);
392 if (ret)
393 krb5_warn(context, ret, "krb5_parse_name");
395 if (ret != 0)
396 return ret;
399 * Check that namespace has exactly one component, and prepend
400 * WELLKNOWN/HOSTBASED-NAMESPACE
402 if (krb5_principal_get_num_comp(context, princ_ent) != 2
403 || (comp0 = krb5_principal_get_comp_string(context, princ_ent, 0)) == 0
404 || (comp1 = krb5_principal_get_comp_string(context, princ_ent, 1)) == 0
405 || *comp0 == 0 || *comp1 == 0
406 || strcmp(comp0, "krbtgt") == 0)
407 krb5_warn(context, ret = EINVAL,
408 "namespaces must have exactly two non-empty components "
409 "like host-base principal names");
410 if (ret == 0)
411 ret = krb5_principal_set_comp_string(context, princ_ent, 2, comp0);
412 if (ret == 0)
413 ret = krb5_principal_set_comp_string(context, princ_ent, 3, comp1);
414 if (ret == 0)
415 ret = krb5_principal_set_comp_string(context, princ_ent, 0,
416 "WELLKNOWN");
417 if (ret == 0)
418 ret = krb5_principal_set_comp_string(context, princ_ent, 1,
419 HDB_WK_NAMESPACE);
421 /* Set up initial key rotation extension */
422 if (ret == 0) {
423 KeyRotation kr;
424 size_t size;
426 /* Setup key rotation metadata in a convenient way */
427 kr.flags = int2KeyRotationFlags(0);
428 kr.base_key_kvno = 1;
430 * Avoid kvnos 0/1/2 which don't normally appear in fully created
431 * principals.
433 kr.base_kvno = 3;
435 /* XXX: Sanity check */
436 kr.epoch = kre;
437 kr.period = krp;
439 memset(&ext, 0, sizeof(ext));
440 ext.mandatory = FALSE;
441 ext.data.element = choice_HDB_extension_data_key_rotation;
442 ext.data.u.key_rotation.len = 1;
443 ext.data.u.key_rotation.val = &kr;
445 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
446 &ext, &size, ret);
447 add_tl(&princ, KRB5_TL_EXTENSION, &buf);
448 mask |= KADM5_TL_DATA;
451 if (ret == 0) {
452 princ.principal = princ_ent;
453 mask |= KADM5_PRINCIPAL | KADM5_KVNO;
455 ret = set_entry(context, &princ, &mask,
456 max_ticket_life, max_renewable_life,
457 "never", "never", attributes, NSPOLICY);
459 if (ret == 0)
460 ret = edit_entry(&princ, &mask, default_ent, default_mask);
462 if (ret == 0)
463 ret = kstuple2etypes(&princ, &mask, nkstuple, kstuple);
465 /* XXX Shouldn't need a password for this */
466 random_password(pwbuf, sizeof(pwbuf));
467 ret = kadm5_create_principal_3(kadm_handle, &princ, mask,
468 nkstuple, kstuple, pwbuf);
469 if (ret)
470 krb5_warn(context, ret, "kadm5_create_principal_3");
472 kadm5_free_principal_ent(kadm_handle, &princ); /* frees princ_ent */
473 if (default_ent)
474 kadm5_free_principal_ent(kadm_handle, default_ent);
475 memset(pwbuf, 0, sizeof(pwbuf));
476 return ret;
480 add_new_namespace(struct add_namespace_options *opt, int argc, char **argv)
482 krb5_error_code ret = 0;
483 krb5_key_salt_tuple *kstuple = NULL;
484 const char *enctypes;
485 size_t i, nkstuple;
487 if (argc < 1) {
488 fprintf(stderr, "at least one namespace name required\n");
489 return 1;
492 enctypes = opt->enctypes_string;
493 if (enctypes == NULL || enctypes[0] == '\0')
494 enctypes = krb5_config_get_string(context, NULL, "libdefaults",
495 "supported_enctypes", NULL);
496 if (enctypes == NULL || enctypes[0] == '\0')
497 enctypes = "aes128-cts-hmac-sha1-96";
498 ret = krb5_string_to_keysalts2(context, enctypes, &nkstuple, &kstuple);
499 if (ret) {
500 fprintf(stderr, "enctype(s) unknown\n");
501 return ret;
504 for (i = 0; i < argc; i++) {
505 ret = add_one_namespace(argv[i], nkstuple, kstuple,
506 opt->max_ticket_life_string,
507 opt->max_renewable_life_string,
508 opt->key_rotation_epoch_string,
509 opt->key_rotation_period_string,
510 opt->attributes_string);
511 if (ret) {
512 krb5_warn(context, ret, "adding namespace %s", argv[i]);
513 break;
517 free(kstuple);
518 return ret != 0;