cf/largefile.m4: Fix build with autoconf-2.72
[heimdal.git] / kadmin / ank.c
blobfba3450aa89d90f3792df885d089cd420fdbd1c1
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];
92 char *princ_name = NULL;
94 memset(&princ, 0, sizeof(princ));
95 ret = krb5_parse_name(context, name, &princ_ent);
96 if (ret) {
97 krb5_warn(context, ret, "krb5_parse_name");
98 return ret;
101 if (rand_password) {
102 ret = krb5_unparse_name(context, princ_ent, &princ_name);
103 if (ret) {
104 krb5_warn(context, ret, "krb5_parse_name");
105 goto out;
108 princ.principal = princ_ent;
109 mask |= KADM5_PRINCIPAL;
111 ret = set_entry(context, &princ, &mask,
112 max_ticket_life, max_renewable_life,
113 expiration, pw_expiration, attributes, policy);
114 if (ret)
115 goto out;
117 default_ent = &defrec;
118 ret = get_default (kadm_handle, princ_ent, default_ent);
119 if (ret) {
120 default_ent = NULL;
121 default_mask = 0;
122 } else {
123 default_mask = KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
124 KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION;
127 if(use_defaults)
128 set_defaults(&princ, &mask, default_ent, default_mask);
129 else
130 if(edit_entry(&princ, &mask, default_ent, default_mask))
131 goto out;
132 if(rand_key || key_data) {
133 princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
134 mask |= KADM5_ATTRIBUTES;
135 random_password (pwbuf, sizeof(pwbuf));
136 password = pwbuf;
137 } else if (rand_password) {
138 random_password (pwbuf, sizeof(pwbuf));
139 password = pwbuf;
140 } else if(password == NULL) {
141 char *prompt;
142 int aret;
144 ret = krb5_unparse_name(context, princ_ent, &princ_name);
145 if (ret)
146 goto out;
147 aret = asprintf (&prompt, "%s's Password: ", princ_name);
148 if (aret == -1) {
149 ret = ENOMEM;
150 krb5_set_error_message(context, ret, "out of memory");
151 goto out;
153 ret = UI_UTIL_read_pw_string (pwbuf, sizeof(pwbuf), prompt,
154 UI_UTIL_FLAG_VERIFY |
155 UI_UTIL_FLAG_VERIFY_SILENT);
156 free (prompt);
157 if (ret) {
158 ret = KRB5_LIBOS_BADPWDMATCH;
159 krb5_set_error_message(context, ret, "failed to verify password");
160 goto out;
162 password = pwbuf;
165 ret = kadm5_create_principal(kadm_handle, &princ, mask, password);
166 if(ret) {
167 krb5_warn(context, ret, "kadm5_create_principal");
168 goto out;
170 /* Save requested password expiry before it's clobbered */
171 pw_expire = princ.pw_expiration;
172 if (rand_key) {
173 krb5_keyblock *new_keys;
174 int n_keys, i;
175 ret = kadm5_randkey_principal_3(kadm_handle, princ_ent, 0,
176 nkstuple, kstuple, &new_keys, &n_keys);
177 if(ret){
178 krb5_warn(context, ret, "kadm5_randkey_principal");
179 n_keys = 0;
181 for(i = 0; i < n_keys; i++)
182 krb5_free_keyblock_contents(context, &new_keys[i]);
183 if (n_keys > 0)
184 free(new_keys);
185 ret = kadm5_get_principal(kadm_handle, princ_ent, &princ,
186 KADM5_PRINCIPAL | KADM5_KVNO |
187 KADM5_ATTRIBUTES);
188 if (ret) {
189 krb5_warn(context, ret, "kadm5_get_principal");
190 goto out;
192 krb5_free_principal(context, princ_ent);
193 princ_ent = princ.principal;
194 princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX);
195 princ.pw_expiration = pw_expire;
197 * Updating kvno w/o key data and vice-versa gives _kadm5_setup_entry()
198 * and _kadm5_set_keys2() headaches. But we used to, so we handle
199 * this in in those two functions. Might as well leave this code as
200 * it was then.
202 princ.kvno = 1;
203 kadm5_modify_principal(kadm_handle, &princ,
204 KADM5_PW_EXPIRATION | KADM5_ATTRIBUTES | KADM5_KVNO);
205 } else if (key_data) {
206 ret = kadm5_chpass_principal_with_key (kadm_handle, princ_ent,
207 3, key_data);
208 if (ret) {
209 krb5_warn(context, ret, "kadm5_chpass_principal_with_key");
211 kadm5_get_principal(kadm_handle, princ_ent, &princ,
212 KADM5_PRINCIPAL | KADM5_ATTRIBUTES);
213 krb5_free_principal(context, princ_ent);
214 princ_ent = princ.principal;
215 princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX);
216 princ.pw_expiration = pw_expire;
217 kadm5_modify_principal(kadm_handle, &princ,
218 KADM5_PW_EXPIRATION | KADM5_ATTRIBUTES);
219 } else if (rand_password) {
220 printf ("added %s with password \"%s\"\n", princ_name, password);
222 out:
223 free(princ_name);
224 kadm5_free_principal_ent(kadm_handle, &princ); /* frees princ_ent */
225 if(default_ent)
226 kadm5_free_principal_ent (kadm_handle, default_ent);
227 if (password != NULL) {
228 size_t len = strlen(password);
229 memset_s(password, len, 0, len);
231 return ret;
235 * parse the string `key_string' into `key', returning 0 iff succesful.
239 * the ank command
243 * Parse arguments and add all the principals.
247 add_new_key(struct add_options *opt, int argc, char **argv)
249 krb5_error_code ret = 0;
250 krb5_key_salt_tuple *kstuple = NULL;
251 krb5_key_data key_data[3];
252 krb5_key_data *kdp = NULL;
253 const char *enctypes;
254 size_t i, nkstuple;
255 int num;
257 num = 0;
258 if (opt->random_key_flag)
259 ++num;
260 if (opt->random_password_flag)
261 ++num;
262 if (opt->password_string)
263 ++num;
264 if (opt->key_string)
265 ++num;
267 if (num > 1) {
268 fprintf (stderr, "give only one of "
269 "--random-key, --random-password, --password, --key\n");
270 return 1;
273 enctypes = opt->enctypes_string;
274 if (enctypes == NULL || enctypes[0] == '\0')
275 enctypes = krb5_config_get_string(context, NULL, "libdefaults",
276 "supported_enctypes", NULL);
277 if (enctypes == NULL || enctypes[0] == '\0')
278 enctypes = "aes128-cts-hmac-sha1-96";
279 ret = krb5_string_to_keysalts2(context, enctypes, &nkstuple, &kstuple);
280 if (ret) {
281 fprintf(stderr, "enctype(s) unknown\n");
282 return ret;
286 if (opt->key_string) {
287 const char *error;
289 if (parse_des_key (opt->key_string, key_data, &error)) {
290 fprintf(stderr, "failed parsing key \"%s\": %s\n",
291 opt->key_string, error);
292 free(kstuple);
293 return 1;
295 kdp = key_data;
298 for(i = 0; i < argc; i++) {
299 ret = add_one_principal(argv[i],
300 opt->random_key_flag,
301 opt->random_password_flag,
302 opt->use_defaults_flag,
303 opt->password_string,
304 opt->policy_string,
305 nkstuple,
306 kstuple,
307 kdp,
308 opt->max_ticket_life_string,
309 opt->max_renewable_life_string,
310 opt->attributes_string,
311 opt->expiration_time_string,
312 opt->pw_expiration_time_string);
313 if (ret) {
314 krb5_warn (context, ret, "adding %s", argv[i]);
315 break;
318 if (kdp) {
319 int16_t dummy = 3;
320 kadm5_free_key_data (kadm_handle, &dummy, key_data);
322 free(kstuple);
323 return ret != 0;
326 static krb5_error_code
327 kstuple2etypes(kadm5_principal_ent_rec *rec,
328 int *maskp,
329 size_t nkstuple,
330 krb5_key_salt_tuple *kstuple)
332 krb5_error_code ret;
333 HDB_EncTypeList etypes;
334 krb5_data buf;
335 size_t len, i;
337 etypes.len = 0;
338 if ((etypes.val = calloc(nkstuple, sizeof(etypes.val[0]))) == NULL)
339 return krb5_enomem(context);
340 for (i = 0; i < nkstuple; i++)
341 etypes.val[i] = kstuple[i].ks_enctype;
342 ASN1_MALLOC_ENCODE(HDB_EncTypeList, buf.data, buf.length,
343 &etypes, &len, ret);
344 if (ret == 0)
345 add_tl(rec, KRB5_TL_ETYPES, &buf);
346 free(etypes.val);
347 if (ret == 0)
348 (*maskp) |= KADM5_TL_DATA;
349 return ret;
353 * Add the namespace `name' to the database.
354 * Prompt for all data not given by the input parameters.
356 static krb5_error_code
357 add_one_namespace(const char *name,
358 size_t nkstuple,
359 krb5_key_salt_tuple *kstuple,
360 const char *max_ticket_life,
361 const char *max_renewable_life,
362 const char *key_rotation_epoch,
363 const char *key_rotation_period,
364 const char *attributes)
366 krb5_error_code ret;
367 kadm5_principal_ent_rec princ;
368 krb5_principal princ_ent = NULL;
369 int mask = 0;
370 int default_mask = 0;
371 HDB_extension ext;
372 krb5_data buf;
373 const char *comp0;
374 const char *comp1;
375 time_t kre;
376 char pwbuf[1024];
377 krb5_deltat krp;
379 if (!key_rotation_epoch) {
380 krb5_warnx(context, "key rotation epoch defaulted to \"now\"");
381 key_rotation_epoch = "now";
383 if (!key_rotation_period) {
384 krb5_warnx(context, "key rotation period defaulted to \"5d\"");
385 key_rotation_period = "5d";
387 if ((ret = str2time_t(key_rotation_epoch, &kre)) != 0) {
388 krb5_warn(context, ret, "invalid rotation epoch: %s",
389 key_rotation_epoch);
390 return ret;
392 if (ret == 0 && (ret = str2deltat(key_rotation_period, &krp)) != 0) {
393 krb5_warn(context, ret, "invalid rotation period: %s",
394 key_rotation_period);
395 return ret;
398 if (ret == 0) {
399 memset(&princ, 0, sizeof(princ));
400 princ.kvno = 1;
401 ret = krb5_parse_name(context, name, &princ_ent);
402 if (ret)
403 krb5_warn(context, ret, "krb5_parse_name");
404 else
405 princ.principal = princ_ent;
407 if (ret != 0)
408 return ret;
411 * Check that namespace has exactly one component, and prepend
412 * WELLKNOWN/HOSTBASED-NAMESPACE
414 if (krb5_principal_get_num_comp(context, princ_ent) != 2
415 || (comp0 = krb5_principal_get_comp_string(context, princ_ent, 0)) == 0
416 || (comp1 = krb5_principal_get_comp_string(context, princ_ent, 1)) == 0
417 || *comp0 == 0 || *comp1 == 0
418 || strcmp(comp0, "krbtgt") == 0)
419 krb5_warn(context, ret = EINVAL,
420 "namespaces must have exactly two non-empty components "
421 "like host-base principal names");
422 if (ret == 0)
423 ret = krb5_principal_set_comp_string(context, princ_ent, 2, comp0);
424 if (ret == 0)
425 ret = krb5_principal_set_comp_string(context, princ_ent, 3, comp1);
426 if (ret == 0)
427 ret = krb5_principal_set_comp_string(context, princ_ent, 0,
428 "WELLKNOWN");
429 if (ret == 0)
430 ret = krb5_principal_set_comp_string(context, princ_ent, 1,
431 HDB_WK_NAMESPACE);
433 /* Set up initial key rotation extension */
434 if (ret == 0) {
435 KeyRotation kr;
436 size_t size;
438 /* Setup key rotation metadata in a convenient way */
439 kr.flags = int2KeyRotationFlags(0);
440 kr.base_key_kvno = 1;
442 * Avoid kvnos 0/1/2 which don't normally appear in fully created
443 * principals.
445 kr.base_kvno = 3;
447 /* XXX: Sanity check */
448 kr.epoch = kre;
449 kr.period = krp;
451 memset(&ext, 0, sizeof(ext));
452 ext.mandatory = FALSE;
453 ext.data.element = choice_HDB_extension_data_key_rotation;
454 ext.data.u.key_rotation.len = 1;
455 ext.data.u.key_rotation.val = &kr;
457 ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
458 &ext, &size, ret);
459 add_tl(&princ, KRB5_TL_EXTENSION, &buf);
460 mask |= KADM5_TL_DATA;
463 if (ret == 0) {
464 mask |= KADM5_PRINCIPAL | KADM5_KVNO;
466 ret = set_entry(context, &princ, &mask,
467 max_ticket_life, max_renewable_life,
468 "never", "never", attributes, NSPOLICY);
470 if (ret == 0)
471 ret = edit_entry(&princ, &mask, NULL, default_mask);
473 if (ret == 0)
474 ret = kstuple2etypes(&princ, &mask, nkstuple, kstuple);
476 /* XXX Shouldn't need a password for this */
477 random_password(pwbuf, sizeof(pwbuf));
478 if (ret == 0) {
479 ret = kadm5_create_principal_3(kadm_handle, &princ, mask,
480 nkstuple, kstuple, pwbuf);
481 if (ret)
482 krb5_warn(context, ret, "kadm5_create_principal_3");
485 kadm5_free_principal_ent(kadm_handle, &princ); /* frees princ_ent */
486 memset(pwbuf, 0, sizeof(pwbuf));
487 return ret;
491 add_new_namespace(struct add_namespace_options *opt, int argc, char **argv)
493 krb5_error_code ret = 0;
494 krb5_key_salt_tuple *kstuple = NULL;
495 const char *enctypes;
496 size_t i, nkstuple;
498 if (argc < 1) {
499 fprintf(stderr, "at least one namespace name required\n");
500 return 1;
503 enctypes = opt->enctypes_string;
504 if (enctypes == NULL || enctypes[0] == '\0')
505 enctypes = krb5_config_get_string(context, NULL, "libdefaults",
506 "supported_enctypes", NULL);
507 if (enctypes == NULL || enctypes[0] == '\0')
508 enctypes = "aes128-cts-hmac-sha1-96";
509 ret = krb5_string_to_keysalts2(context, enctypes, &nkstuple, &kstuple);
510 if (ret) {
511 fprintf(stderr, "enctype(s) unknown\n");
512 return ret;
515 for (i = 0; i < argc; i++) {
516 ret = add_one_namespace(argv[i], nkstuple, kstuple,
517 opt->max_ticket_life_string,
518 opt->max_renewable_life_string,
519 opt->key_rotation_epoch_string,
520 opt->key_rotation_period_string,
521 opt->attributes_string);
522 if (ret) {
523 krb5_warn(context, ret, "adding namespace %s", argv[i]);
524 break;
528 free(kstuple);
529 return ret != 0;