xref: /illumos-gate/usr/src/cmd/krb5/kadmin/cli/keytab.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
1 /*
2  * Copyright 2006 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 
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 	  } else
349 			com_err(whoami, code,
350 				gettext("while changing %s's key"),
351 				princ_str);
352 	  goto cleanup;
353      }
354 
355      code = kadm5_get_principal(lhandle, princ, &princ_rec,
356 				KADM5_PRINCIPAL_NORMAL_MASK);
357      if (code != 0) {
358 		com_err(whoami, code, gettext("while retrieving principal"));
359 	  goto cleanup;
360      }
361 
362      for (i = 0; i < nkeys; i++) {
363 	  memset((char *) &new_entry, 0, sizeof(new_entry));
364 	  new_entry.principal = princ;
365 	  new_entry.key = keys[i];
366 	  new_entry.vno = princ_rec.kvno;
367 
368 	  code = krb5_kt_add_entry(context, keytab, &new_entry);
369 	  if (code != 0) {
370 			com_err(whoami, code,
371 				gettext("while adding key to keytab"));
372 	       (void) kadm5_free_principal_ent(lhandle, &princ_rec);
373 	       goto cleanup;
374 	  }
375 
376 	  if (!quiet)
377 			printf(gettext("Entry for principal %s with kvno %d, "
378 				"encryption type %s added to keytab %s.\n"),
379 		      princ_str, princ_rec.kvno,
380 		      etype_string(keys[i].enctype), keytab_str);
381      }
382 
383      code = kadm5_free_principal_ent(lhandle, &princ_rec);
384      if (code != 0) {
385 		com_err(whoami, code, gettext("while freeing principal entry"));
386 	  goto cleanup;
387      }
388 
389 cleanup:
390      if (nkeys) {
391 	  for (i = 0; i < nkeys; i++)
392 	       krb5_free_keyblock_contents(context, &keys[i]);
393 	  free(keys);
394      }
395      if (princ)
396 	  krb5_free_principal(context, princ);
397 
398      if (permitted_etypes != NULL && ks_tuple == NULL)
399 	free(permitted_etypes);
400 
401      return code;
402 }
403 
404 int remove_principal(char *keytab_str, krb5_keytab keytab, char
405 		     *princ_str, char *kvno_str)
406 {
407      krb5_principal princ;
408      krb5_keytab_entry entry;
409      krb5_kt_cursor cursor;
410      enum { UNDEF, SPEC, HIGH, ALL, OLD } mode;
411      int code, did_something;
412      krb5_kvno kvno;
413 
414      code = krb5_parse_name(context, princ_str, &princ);
415      if (code != 0) {
416 		com_err(whoami, code,
417 			gettext("while parsing principal name %s"),
418 		  princ_str);
419 	  return code;
420      }
421 
422      mode = UNDEF;
423      if (kvno_str == NULL) {
424 	  mode = HIGH;
425 	  kvno = 0;
426      } else if (strcmp(kvno_str, "all") == 0) {
427 	  mode = ALL;
428 	  kvno = 0;
429      } else if (strcmp(kvno_str, "old") == 0) {
430 	  mode = OLD;
431 	  kvno = 0;
432      } else {
433 	  mode = SPEC;
434 	  kvno = atoi(kvno_str);
435      }
436 
437      /* kvno is set to specified value for SPEC, 0 otherwise */
438      code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry);
439      if (code != 0) {
440 	  if (code == ENOENT) {
441 			fprintf(stderr,
442 				gettext("%s: Keytab %s does not exist.\n"),
443 		       whoami, keytab_str);
444 	  } else if (code == KRB5_KT_NOTFOUND) {
445 	       if (mode != SPEC)
446 				fprintf(stderr,
447 					gettext("%s: No entry for principal "
448 						"%s exists in keytab %s\n"),
449 			    whoami, princ_str, keytab_str);
450 	       else
451 				fprintf(stderr,
452 					gettext("%s: No entry for principal "
453 						"%s with kvno %d exists in "
454 						"keytab %s.\n"),
455 					whoami, princ_str, kvno, keytab_str);
456 	  } else {
457 			com_err(whoami, code,
458 				gettext("while retrieving highest "
459 					"kvno from keytab"));
460 	  }
461 	  return code;
462      }
463 
464      /* set kvno to spec'ed value for SPEC, highest kvno otherwise */
465      kvno = entry.vno;
466      krb5_kt_free_entry(context, &entry);
467 
468      code = krb5_kt_start_seq_get(context, keytab, &cursor);
469      if (code != 0) {
470 		com_err(whoami, code, gettext("while starting keytab scan"));
471 	  return code;
472      }
473 
474      did_something = 0;
475      while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
476 	  if (krb5_principal_compare(context, princ, entry.principal) &&
477 	      ((mode == ALL) ||
478 	       (mode == SPEC && entry.vno == kvno) ||
479 	       (mode == OLD && entry.vno != kvno) ||
480 	       (mode == HIGH && entry.vno == kvno))) {
481 
482 	       /*
483 		* Ack!  What a kludge... the scanning functions lock
484 		* the keytab so entries cannot be removed while they
485 		* are operating.
486 		*/
487 	       code = krb5_kt_end_seq_get(context, keytab, &cursor);
488 	       if (code != 0) {
489 				com_err(whoami, code,
490 					gettext("while temporarily "
491 						"ending keytab scan"));
492 		    return code;
493 	       }
494 	       code = krb5_kt_remove_entry(context, keytab, &entry);
495 	       if (code != 0) {
496 				com_err(whoami, code,
497 					gettext("while deleting entry "
498 						"from keytab"));
499 		    return code;
500 	       }
501 	       code = krb5_kt_start_seq_get(context, keytab, &cursor);
502 	       if (code != 0) {
503 				com_err(whoami, code,
504 				    gettext("while restarting keytab scan"));
505 		    return code;
506 	       }
507 
508 	       did_something++;
509 	       if (!quiet)
510 				printf(gettext("Entry for principal "
511 					    "%s with kvno %d "
512 					    "removed from keytab %s.\n"),
513 			   princ_str, entry.vno, keytab_str);
514 	  }
515 	  krb5_kt_free_entry(context, &entry);
516      }
517      if (code && code != KRB5_KT_END) {
518 		com_err(whoami, code, gettext("while scanning keytab"));
519 	  return code;
520      }
521      if ((code = krb5_kt_end_seq_get(context, keytab, &cursor))) {
522 		com_err(whoami, code, gettext("while ending keytab scan"));
523 	  return code;
524      }
525 
526      /*
527       * If !did_someting then mode must be OLD or we would have
528       * already returned with an error.  But check it anyway just to
529       * prevent unexpected error messages...
530       */
531      if (!did_something && mode == OLD) {
532 		fprintf(stderr,
533 		    gettext("%s: There is only one entry for principal "
534 			"%s in keytab %s\n"),
535 		    whoami, princ_str, keytab_str);
536 	  return 1;
537      }
538 
539      return 0;
540 }
541 
542 /*
543  * etype_string(enctype): return a string representation of the
544  * encryption type.  XXX copied from klist.c; this should be a
545  * library function, or perhaps just #defines
546  */
547 static char *etype_string(enctype)
548     krb5_enctype enctype;
549 {
550     static char buf[100];
551     krb5_error_code ret;
552 
553     if ((ret = krb5_enctype_to_string(enctype, buf, sizeof(buf))))
554 	sprintf(buf, "etype %d", enctype);
555 
556     return buf;
557 }
558