keytab.c revision 7934:6aeeafc994de
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__)
41static 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
54static 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);
58static int remove_principal(char *keytab_str, krb5_keytab keytab, char
59			    *princ_str, char *kvno_str);
60static char *etype_string(krb5_enctype enctype);
61static char *etype_istring(krb5_enctype enctype);
62
63static int quiet;
64
65static 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
72static 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
80static 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
133void 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
219void 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
258static
259int 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
409cleanup:
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
424int 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 */
567static 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 */
580static 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