1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
4 *
5 * $Id$
6 * $Source$
7 */
8
9 /*
10 * Copyright (C) 1998 by the FundsXpress, INC.
11 *
12 * All rights reserved.
13 *
14 * Export of this software from the United States of America may require
15 * a specific license from the United States Government. It is the
16 * responsibility of any person or organization contemplating export to
17 * obtain such a license before exporting.
18 *
19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
20 * distribute this software and its documentation for any purpose and
21 * without fee is hereby granted, provided that the above copyright
22 * notice appear in all copies and that both that copyright notice and
23 * this permission notice appear in supporting documentation, and that
24 * the name of FundsXpress. not be used in advertising or publicity pertaining
25 * to distribution of the software without specific, written prior
26 * permission. FundsXpress makes no representations about the suitability of
27 * this software for any purpose. It is provided "as is" without express
28 * or implied warranty.
29 *
30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
31 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
32 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
33 */
34
35 #include "k5-int.h"
36 #include <kadm5/admin.h>
37 #include <adm_proto.h>
38 #include "kadmin.h"
39
40 static void add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
41 krb5_boolean keepold,
42 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
43 char *princ_str);
44 static void remove_principal(char *keytab_str, krb5_keytab keytab,
45 char *princ_str, char *kvno_str);
46 static char *etype_string(krb5_enctype enctype);
47
48 static int quiet;
49
50 static int norandkey;
51
52 static void
add_usage()53 add_usage()
54 {
55 fprintf(stderr, _("Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] "
56 "[-norandkey] [principal | -glob princ-exp] [...]\n"));
57 }
58
59 static void
rem_usage()60 rem_usage()
61 {
62 fprintf(stderr, _("Usage: ktremove [-k[eytab] keytab] [-q] principal "
63 "[kvno|\"all\"|\"old\"]\n"));
64 }
65
66 static int
process_keytab(krb5_context my_context,char ** keytab_str,krb5_keytab * keytab)67 process_keytab(krb5_context my_context, char **keytab_str,
68 krb5_keytab *keytab)
69 {
70 int code;
71 char *name = *keytab_str;
72
73 if (name == NULL) {
74 name = malloc(BUFSIZ);
75 if (!name) {
76 com_err(whoami, ENOMEM, _("while creating keytab name"));
77 return 1;
78 }
79 code = krb5_kt_default(my_context, keytab);
80 if (code != 0) {
81 com_err(whoami, code, _("while opening default keytab"));
82 free(name);
83 return 1;
84 }
85 code = krb5_kt_get_name(my_context, *keytab, name, BUFSIZ);
86 if (code != 0) {
87 com_err(whoami, code, _("while getting keytab name"));
88 free(name);
89 return 1;
90 }
91 } else {
92 if (strchr(name, ':') != NULL)
93 name = strdup(name);
94 else if (asprintf(&name, "WRFILE:%s", name) < 0)
95 name = NULL;
96 if (name == NULL) {
97 com_err(whoami, ENOMEM, _("while creating keytab name"));
98 return 1;
99 }
100
101 code = krb5_kt_resolve(my_context, name, keytab);
102 if (code != 0) {
103 com_err(whoami, code, _("while resolving keytab %s"), name);
104 free(name);
105 return 1;
106 }
107 }
108
109 *keytab_str = name;
110 return 0;
111 }
112
113 void
kadmin_keytab_add(int argc,char ** argv)114 kadmin_keytab_add(int argc, char **argv)
115 {
116 krb5_keytab keytab = 0;
117 char *keytab_str = NULL, **princs;
118 int code, num, i;
119 krb5_error_code retval;
120 int n_ks_tuple = 0;
121 krb5_boolean keepold = FALSE;
122 krb5_key_salt_tuple *ks_tuple = NULL;
123
124 argc--; argv++;
125 quiet = 0;
126 norandkey = 0;
127 while (argc) {
128 if (strncmp(*argv, "-k", 2) == 0) {
129 argc--; argv++;
130 if (!argc || keytab_str) {
131 add_usage();
132 return;
133 }
134 keytab_str = *argv;
135 } else if (strcmp(*argv, "-q") == 0) {
136 quiet++;
137 } else if (strcmp(*argv, "-norandkey") == 0) {
138 norandkey++;
139 } else if (strcmp(*argv, "-e") == 0) {
140 argc--;
141 if (argc < 1) {
142 add_usage();
143 return;
144 }
145 retval = krb5_string_to_keysalts(*++argv, NULL, NULL, 0,
146 &ks_tuple, &n_ks_tuple);
147 if (retval) {
148 com_err("ktadd", retval, _("while parsing keysalts %s"),
149 *argv);
150
151 return;
152 }
153 } else
154 break;
155 argc--; argv++;
156 }
157
158 if (argc == 0) {
159 add_usage();
160 return;
161 }
162
163 if (norandkey && ks_tuple) {
164 fprintf(stderr,
165 _("cannot specify keysaltlist when not changing key\n"));
166 return;
167 }
168
169 if (process_keytab(context, &keytab_str, &keytab))
170 return;
171
172 while (*argv) {
173 if (strcmp(*argv, "-glob") == 0) {
174 if (*++argv == NULL) {
175 add_usage();
176 break;
177 }
178
179 code = kadm5_get_principals(handle, *argv, &princs, &num);
180 if (code) {
181 com_err(whoami, code, _("while expanding expression \"%s\"."),
182 *argv);
183 argv++;
184 continue;
185 }
186
187 for (i = 0; i < num; i++)
188 add_principal(handle, keytab_str, keytab, keepold,
189 n_ks_tuple, ks_tuple, princs[i]);
190 kadm5_free_name_list(handle, princs, num);
191 } else {
192 add_principal(handle, keytab_str, keytab, keepold,
193 n_ks_tuple, ks_tuple, *argv);
194 argv++;
195 }
196 }
197
198 code = krb5_kt_close(context, keytab);
199 if (code != 0)
200 com_err(whoami, code, _("while closing keytab"));
201
202 free(keytab_str);
203 }
204
205 void
kadmin_keytab_remove(int argc,char ** argv)206 kadmin_keytab_remove(int argc, char **argv)
207 {
208 krb5_keytab keytab = 0;
209 char *keytab_str = NULL;
210 int code;
211
212 argc--; argv++;
213 quiet = 0;
214 while (argc) {
215 if (strncmp(*argv, "-k", 2) == 0) {
216 argc--; argv++;
217 if (!argc || keytab_str) {
218 rem_usage();
219 return;
220 }
221 keytab_str = *argv;
222 } else if (strcmp(*argv, "-q") == 0) {
223 quiet++;
224 } else
225 break;
226 argc--; argv++;
227 }
228
229 if (argc != 1 && argc != 2) {
230 rem_usage();
231 return;
232 }
233 if (process_keytab(context, &keytab_str, &keytab))
234 return;
235
236 remove_principal(keytab_str, keytab, argv[0], argv[1]);
237
238 code = krb5_kt_close(context, keytab);
239 if (code != 0)
240 com_err(whoami, code, _("while closing keytab"));
241
242 free(keytab_str);
243 }
244
245 /* Generate new random keys for princ, and convert them into a kadm5_key_data
246 * array (with no salt information). */
247 static krb5_error_code
fetch_new_keys(void * lhandle,krb5_principal princ,krb5_boolean keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,kadm5_key_data ** key_data_out,int * nkeys_out)248 fetch_new_keys(void *lhandle, krb5_principal princ, krb5_boolean keepold,
249 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
250 kadm5_key_data **key_data_out, int *nkeys_out)
251 {
252 krb5_error_code code;
253 kadm5_key_data *key_data;
254 kadm5_principal_ent_rec princ_rec;
255 krb5_keyblock *keys = NULL;
256 int i, nkeys = 0;
257
258 *key_data_out = NULL;
259 *nkeys_out = 0;
260 memset(&princ_rec, 0, sizeof(princ_rec));
261
262 /* Generate new random keys. */
263 code = randkey_princ(lhandle, princ, keepold, n_ks_tuple, ks_tuple,
264 &keys, &nkeys);
265 if (code)
266 goto cleanup;
267
268 /* Get the principal entry to find the kvno of the new keys. (This is not
269 * atomic, but randkey doesn't report the new kvno.) */
270 code = kadm5_get_principal(lhandle, princ, &princ_rec,
271 KADM5_PRINCIPAL_NORMAL_MASK);
272 if (code)
273 goto cleanup;
274
275 key_data = k5calloc(nkeys, sizeof(*key_data), &code);
276 if (key_data == NULL)
277 goto cleanup;
278
279 /* Transfer the keyblocks and free the container array. */
280 for (i = 0; i < nkeys; i++) {
281 key_data[i].key = keys[i];
282 key_data[i].kvno = princ_rec.kvno;
283 }
284 *key_data_out = key_data;
285 *nkeys_out = nkeys;
286 free(keys);
287 keys = NULL;
288 nkeys = 0;
289
290 cleanup:
291 for (i = 0; i < nkeys; i++)
292 krb5_free_keyblock_contents(context, &keys[i]);
293 free(keys);
294 kadm5_free_principal_ent(lhandle, &princ_rec);
295 return code;
296 }
297
298 static void
add_principal(void * lhandle,char * keytab_str,krb5_keytab keytab,krb5_boolean keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,char * princ_str)299 add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
300 krb5_boolean keepold, int n_ks_tuple,
301 krb5_key_salt_tuple *ks_tuple, char *princ_str)
302 {
303 krb5_principal princ = NULL;
304 krb5_keytab_entry new_entry;
305 kadm5_key_data *key_data;
306 int code, nkeys, i;
307
308 princ = NULL;
309 key_data = NULL;
310 nkeys = 0;
311
312 code = krb5_parse_name(context, princ_str, &princ);
313 if (code != 0) {
314 com_err(whoami, code, _("while parsing -add principal name %s"),
315 princ_str);
316 goto cleanup;
317 }
318
319 if (norandkey) {
320 code = kadm5_get_principal_keys(handle, princ, 0, &key_data, &nkeys);
321 } else {
322 code = fetch_new_keys(handle, princ, keepold, n_ks_tuple, ks_tuple,
323 &key_data, &nkeys);
324 }
325
326 if (code != 0) {
327 if (code == KADM5_UNK_PRINC) {
328 fprintf(stderr, _("%s: Principal %s does not exist.\n"),
329 whoami, princ_str);
330 } else
331 com_err(whoami, code, _("while changing %s's key"), princ_str);
332 goto cleanup;
333 }
334
335 for (i = 0; i < nkeys; i++) {
336 memset(&new_entry, 0, sizeof(new_entry));
337 new_entry.principal = princ;
338 new_entry.key = key_data[i].key;
339 new_entry.vno = key_data[i].kvno;
340
341 code = krb5_kt_add_entry(context, keytab, &new_entry);
342 if (code != 0) {
343 com_err(whoami, code, _("while adding key to keytab"));
344 goto cleanup;
345 }
346
347 if (!quiet) {
348 printf(_("Entry for principal %s with kvno %d, "
349 "encryption type %s added to keytab %s.\n"),
350 princ_str, key_data[i].kvno,
351 etype_string(key_data[i].key.enctype), keytab_str);
352 }
353 }
354
355 cleanup:
356 kadm5_free_kadm5_key_data(context, nkeys, key_data);
357 krb5_free_principal(context, princ);
358 }
359
360 static void
remove_principal(char * keytab_str,krb5_keytab keytab,char * princ_str,char * kvno_str)361 remove_principal(char *keytab_str, krb5_keytab keytab,
362 char *princ_str, char *kvno_str)
363 {
364 krb5_principal princ = NULL;
365 krb5_keytab_entry entry;
366 krb5_kt_cursor cursor;
367 enum { UNDEF, SPEC, HIGH, ALL, OLD } mode;
368 int code, did_something;
369 krb5_kvno kvno;
370
371 code = krb5_parse_name(context, princ_str, &princ);
372 if (code != 0) {
373 com_err(whoami, code, _("while parsing principal name %s"), princ_str);
374 goto cleanup;
375 }
376
377 mode = UNDEF;
378 if (kvno_str == NULL) {
379 mode = HIGH;
380 kvno = 0;
381 } else if (strcmp(kvno_str, "all") == 0) {
382 mode = ALL;
383 kvno = 0;
384 } else if (strcmp(kvno_str, "old") == 0) {
385 mode = OLD;
386 kvno = 0;
387 } else {
388 mode = SPEC;
389 kvno = atoi(kvno_str);
390 }
391
392 /* kvno is set to specified value for SPEC, 0 otherwise */
393 code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry);
394 if (code != 0) {
395 if (code == ENOENT) {
396 fprintf(stderr, _("%s: Keytab %s does not exist.\n"),
397 whoami, keytab_str);
398 } else if (code == KRB5_KT_NOTFOUND) {
399 if (mode != SPEC) {
400 fprintf(stderr, _("%s: No entry for principal %s exists in "
401 "keytab %s\n"),
402 whoami, princ_str, keytab_str);
403 } else {
404 fprintf(stderr, _("%s: No entry for principal %s with kvno %d "
405 "exists in keytab %s\n"),
406 whoami, princ_str, kvno, keytab_str);
407 }
408 } else {
409 com_err(whoami, code,
410 _("while retrieving highest kvno from keytab"));
411 }
412 goto cleanup;
413 }
414
415 /* set kvno to spec'ed value for SPEC, highest kvno otherwise */
416 if (mode != SPEC)
417 kvno = entry.vno;
418 krb5_kt_free_entry(context, &entry);
419
420 code = krb5_kt_start_seq_get(context, keytab, &cursor);
421 if (code != 0) {
422 com_err(whoami, code, _("while starting keytab scan"));
423 goto cleanup;
424 }
425
426 did_something = 0;
427 while ((code = krb5_kt_next_entry(context, keytab, &entry,
428 &cursor)) == 0) {
429 if (krb5_principal_compare(context, princ, entry.principal) &&
430 ((mode == ALL) ||
431 (mode == SPEC && entry.vno == kvno) ||
432 (mode == OLD && entry.vno != kvno) ||
433 (mode == HIGH && entry.vno == kvno))) {
434
435 /*
436 * Ack! What a kludge... the scanning functions lock
437 * the keytab so entries cannot be removed while they
438 * are operating.
439 */
440 code = krb5_kt_end_seq_get(context, keytab, &cursor);
441 if (code != 0) {
442 com_err(whoami, code,
443 _("while temporarily ending keytab scan"));
444 goto cleanup;
445 }
446 code = krb5_kt_remove_entry(context, keytab, &entry);
447 if (code != 0) {
448 com_err(whoami, code, _("while deleting entry from keytab"));
449 goto cleanup;
450 }
451 code = krb5_kt_start_seq_get(context, keytab, &cursor);
452 if (code != 0) {
453 com_err(whoami, code, _("while restarting keytab scan"));
454 goto cleanup;
455 }
456
457 did_something++;
458 if (!quiet) {
459 printf(_("Entry for principal %s with kvno %d removed from "
460 "keytab %s.\n"), princ_str, entry.vno, keytab_str);
461 }
462 }
463 krb5_kt_free_entry(context, &entry);
464 }
465 if (code && code != KRB5_KT_END) {
466 com_err(whoami, code, _("while scanning keytab"));
467 goto cleanup;
468 }
469 code = krb5_kt_end_seq_get(context, keytab, &cursor);
470 if (code) {
471 com_err(whoami, code, _("while ending keytab scan"));
472 goto cleanup;
473 }
474
475 /*
476 * If !did_someting then mode must be OLD or we would have
477 * already returned with an error. But check it anyway just to
478 * prevent unexpected error messages...
479 */
480 if (!did_something && mode == OLD) {
481 fprintf(stderr, _("%s: There is only one entry for principal %s in "
482 "keytab %s\n"), whoami, princ_str, keytab_str);
483 }
484
485 cleanup:
486 krb5_free_principal(context, princ);
487 }
488
489 /*
490 * etype_string(enctype): return a string representation of the
491 * encryption type. XXX copied from klist.c; this should be a
492 * library function, or perhaps just #defines
493 */
494 static char *
etype_string(krb5_enctype enctype)495 etype_string(krb5_enctype enctype)
496 {
497 static char buf[100];
498 krb5_error_code ret;
499
500 ret = krb5_enctype_to_name(enctype, FALSE, buf, sizeof(buf));
501 if (ret)
502 snprintf(buf, sizeof(buf), "etype %d", enctype);
503
504 return buf;
505 }
506