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