xref: /illumos-gate/usr/src/cmd/krb5/kadmin/cli/keytab.c (revision 6cefaae1e90a413ba01560575bb3998e1a3df40e)
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 
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 
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 
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 
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 
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
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 
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  */
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 */
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