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