xref: /illumos-gate/usr/src/cmd/krb5/kadmin/cli/keytab.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
1 /*
2  * Copyright 2004 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.26 2000/02/19 01:57:07 tlyu 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.26 2000/02/19 01:57:07 tlyu 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 <k5-int.h>
52 #include <kadm5/admin.h>
53 
54 static int add_principal(void *handle, char *keytab_str, krb5_keytab keytab,
55 			 int 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 
62 extern char *krb5_defkeyname;
63 extern char *whoami;
64 extern krb5_context context;
65 extern void *handle;
66 static int quiet;
67 
68 void
69 add_usage()
70 {
71      fprintf(stderr, "%s: %s\n", gettext("Usage"),
72 	"ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] "
73 	"[principal | -glob princ-exp] [...]\n");
74 }
75 
76 void
77 rem_usage()
78 {
79 	fprintf(stderr, "%s: %s\n",
80 	    gettext("Usage"),
81 	    "ktremove [-k[eytab] keytab] [-q] principal "
82 	    "[kvno|\"all\"|\"old\"]\n");
83 }
84 
85 int
86 process_keytab(krb5_context context, char **keytab_str,
87 		   krb5_keytab *keytab)
88 {
89      int code;
90      char buf[BUFSIZ];
91 
92      if (*keytab_str == NULL) {
93 	if (code = krb5_kt_default(context, keytab)) {
94 		com_err(whoami, code, gettext("while opening default keytab"));
95 		return (1);
96 	}
97 	if (code = krb5_kt_get_name(context, *keytab, buf, BUFSIZ)) {
98 		com_err(whoami, code, gettext("while retrieving keytab name"));
99 		return (1);
100 	}
101 	if (!(*keytab_str = strdup(buf))) {
102 		com_err(whoami, ENOMEM, gettext("while creating keytab name"));
103 		return(1);
104 	}
105      } else {
106 	  if (strchr(*keytab_str, ':') != NULL) {
107 	       *keytab_str = strdup(*keytab_str);
108 	       if (*keytab_str == NULL) {
109 				com_err(whoami, ENOMEM,
110 				    gettext("while creating keytab name"));
111 				return (1);
112 	       }
113 	  } else {
114 	       char *tmp = *keytab_str;
115 
116 	       *keytab_str = (char *)
117 		    malloc(strlen("WRFILE:")+strlen(tmp)+1);
118 	       if (*keytab_str == NULL) {
119 				com_err(whoami, ENOMEM,
120 				    gettext("while creating keytab name"));
121 				return (1);
122 	       }
123 	       sprintf(*keytab_str, "WRFILE:%s", tmp);
124 	  }
125 
126 	  code = krb5_kt_resolve(context, *keytab_str, keytab);
127 	  if (code != 0) {
128 			com_err(whoami, code,
129 			    gettext("while resolving keytab %s"), *keytab_str);
130 	       free(keytab_str);
131 			return (1);
132 	  }
133      }
134 
135 	return (0);
136 }
137 
138 
139 void
140 kadmin_keytab_add(int argc, char **argv)
141 {
142      krb5_keytab keytab = 0;
143      char *princ_str, *keytab_str = NULL, **princs;
144      int code, num, i;
145      krb5_error_code retval;
146      int keepold = 0, n_ks_tuple = 0;
147      krb5_key_salt_tuple *ks_tuple = NULL;
148 
149 	argc--;
150 	argv++;
151      quiet = 0;
152      while (argc) {
153 	  if (strncmp(*argv, "-k", 2) == 0) {
154 			argc--;
155 			argv++;
156 	       if (!argc || keytab_str) {
157 		    add_usage();
158 		    return;
159 	       }
160 	       keytab_str = *argv;
161 	  } else if (strcmp(*argv, "-q") == 0) {
162 	       quiet++;
163 	  } else if (strcmp(*argv, "-e") == 0) {
164 	       argc--;
165 	       if (argc < 1) {
166 		    add_usage();
167 		    return;
168 	       }
169 	       retval = krb5_string_to_keysalts(*++argv, ", \t", ":.-", 0,
170 						&ks_tuple, &n_ks_tuple);
171 	       if (retval) {
172 		    com_err("ktadd", retval,
173 			    gettext("while parsing keysalts %s"),
174 			    *argv);
175 
176 		    return;
177 	       }
178 	  } else
179 	       break;
180 		argc--;
181 		argv++;
182      }
183 
184      if (argc == 0) {
185 	  add_usage();
186 	  return;
187      }
188 
189      if (process_keytab(context, &keytab_str, &keytab))
190 	  return;
191 
192      while (*argv) {
193 	  if (strcmp(*argv, "-glob") == 0) {
194 	       if (*++argv == NULL) {
195 		    add_usage();
196 		    break;
197 	       }
198 			if (code = kadm5_get_principals(handle, *argv,
199 							&princs, &num)) {
200 				com_err(whoami, code,
201 					gettext("while expanding expression "
202 						"\"%s\"."),
203 			    *argv);
204 		    argv++;
205 		    continue;
206 	       }
207 
208 	       for (i = 0; i < num; i++)
209 		    (void) add_principal(handle, keytab_str, keytab,
210 					 keepold, n_ks_tuple, ks_tuple,
211 					 princs[i]);
212 	       kadm5_free_name_list(handle, princs, num);
213 	  } else
214 	       (void) add_principal(handle, keytab_str, keytab,
215 				    keepold, n_ks_tuple, ks_tuple,
216 				    *argv);
217 	  argv++;
218      }
219 
220      code = krb5_kt_close(context, keytab);
221      if (code != 0)
222 		com_err(whoami, code, gettext("while closing keytab"));
223 
224      free(keytab_str);
225 }
226 
227 void
228 kadmin_keytab_remove(int argc, char **argv)
229 {
230      krb5_keytab keytab = 0;
231      char *princ_str, *keytab_str = NULL;
232      int code;
233 
234 	argc--;
235 	argv++;
236      quiet = 0;
237      while (argc) {
238 	  if (strncmp(*argv, "-k", 2) == 0) {
239 			argc--;
240 			argv++;
241 	       if (!argc || keytab_str) {
242 		    rem_usage();
243 		    return;
244 	       }
245 	       keytab_str = *argv;
246 	  } else if (strcmp(*argv, "-q") == 0) {
247 	       quiet++;
248 	  } else
249 	       break;
250 		argc--;
251 		argv++;
252      }
253 
254      if (argc != 1 && argc != 2) {
255 	  rem_usage();
256 	  return;
257      }
258      if (process_keytab(context, &keytab_str, &keytab))
259 	  return;
260 
261      (void) remove_principal(keytab_str, keytab, argv[0], argv[1]);
262 
263      code = krb5_kt_close(context, keytab);
264      if (code != 0)
265 		com_err(whoami, code, gettext("while closing keytab"));
266 
267      free(keytab_str);
268 }
269 
270 int add_principal(void *handle, char *keytab_str, krb5_keytab keytab,
271 		  int keepold, int n_ks_tuple,
272 		  krb5_key_salt_tuple *ks_tuple,
273 		  char *princ_str)
274 {
275      kadm5_principal_ent_rec princ_rec;
276      krb5_principal princ;
277      krb5_keytab_entry new_entry;
278      krb5_keyblock *keys;
279      int code, code2, mask, nkeys, i;
280      int nktypes = 0;
281      krb5_key_salt_tuple *permitted_etypes = NULL;
282 
283      (void) memset((char *)&princ_rec, 0, sizeof(princ_rec));
284 
285      princ = NULL;
286      keys = NULL;
287      nkeys = 0;
288 
289      code = krb5_parse_name(context, princ_str, &princ);
290      if (code != 0) {
291 		com_err(whoami, code,
292 		    gettext("while parsing -add principal name %s"),
293 		  princ_str);
294 	  goto cleanup;
295      }
296 
297      if (ks_tuple == NULL) {
298 	krb5_enctype *ptr, *ktypes = NULL;
299 
300 	code = krb5_get_permitted_enctypes(context, &ktypes);
301 	if (!code && ktypes && *ktypes) {
302 		krb5_int32 salttype;
303 		/*
304 		 * Count the results.  This is stupid, the API above
305 		 * should have included an output param to indicate
306 	 	 * the size of the list that is returned.
307 		 */
308 		for (ptr = ktypes; *ptr; ptr++) nktypes++;
309 
310 		/* Allocate a new key-salt tuple set */
311 		permitted_etypes = (krb5_key_salt_tuple *)malloc (
312 			sizeof (krb5_key_salt_tuple) * nktypes);
313 		if (permitted_etypes == NULL) {
314 			free(ktypes);
315 			return (ENOMEM);
316 		}
317 
318 		/*
319 		 * Because the keysalt parameter doesn't matter for
320 		 * keys stored in the keytab, use the default "normal"
321 		 * salt for all keys
322 		 */
323 		(void) krb5_string_to_salttype("normal", &salttype);
324 		for (i = 0; i < nktypes; i++) {
325 			permitted_etypes[i].ks_enctype = ktypes[i];
326 			permitted_etypes[i].ks_salttype = salttype;
327 		}
328 		free(ktypes);
329 	} else {
330 		if (ktypes)
331 			free(ktypes);
332 		goto cleanup;
333 	}
334      } else {
335 	permitted_etypes = ks_tuple;
336 	nktypes = n_ks_tuple;
337      }
338 
339      code = kadm5_randkey_principal_3(handle, princ,
340 				  keepold, nktypes, permitted_etypes,
341 				  &keys, &nkeys);
342 
343 #ifndef _KADMIN_LOCAL_
344 	/* this block is not needed in the kadmin.local client */
345 
346 	/*
347 	 * If the above call failed, we may be talking to an older
348 	 * admin server, so try the older API.
349 	 */
350 	if (code == KADM5_RPC_ERROR) {
351 		code = kadm5_randkey_principal_old(handle, princ, &keys, &nkeys);
352 	}
353 #endif /* !KADMIN_LOCAL */
354 	if (code != 0) {
355 		if (code == KADM5_UNK_PRINC) {
356 			fprintf(stderr,
357 			    gettext("%s: Principal %s does not exist.\n"),
358 		       whoami, princ_str);
359 		} else {
360 			com_err(whoami, code,
361 				gettext("while changing %s's key"),
362 				princ_str);
363 		}
364 		goto cleanup;
365 	}
366 
367      code = kadm5_get_principal(handle, princ, &princ_rec,
368 				KADM5_PRINCIPAL_NORMAL_MASK);
369      if (code != 0) {
370 		com_err(whoami, code, gettext("while retrieving principal"));
371 	  goto cleanup;
372      }
373 
374      for (i = 0; i < nkeys; i++) {
375 	  memset((char *) &new_entry, 0, sizeof(new_entry));
376 	  new_entry.principal = princ;
377 	  new_entry.key = keys[i];
378 	  new_entry.vno = princ_rec.kvno;
379 
380 	  code = krb5_kt_add_entry(context, keytab, &new_entry);
381 	  if (code != 0) {
382 			com_err(whoami, code,
383 				gettext("while adding key to keytab"));
384 	       (void) kadm5_free_principal_ent(handle, &princ_rec);
385 	       goto cleanup;
386 	  }
387 
388 	  if (!quiet)
389 			printf(gettext("Entry for principal %s with kvno %d, "
390 				"encryption type %s added to keytab %s.\n"),
391 		      princ_str, princ_rec.kvno,
392 		      etype_string(keys[i].enctype), keytab_str);
393      }
394 
395      code = kadm5_free_principal_ent(handle, &princ_rec);
396      if (code != 0) {
397 		com_err(whoami, code, gettext("while freeing principal entry"));
398 	  goto cleanup;
399      }
400 
401 cleanup:
402      if (nkeys) {
403 	  for (i = 0; i < nkeys; i++)
404 	       krb5_free_keyblock_contents(context, &keys[i]);
405 	  free(keys);
406      }
407      if (princ)
408 	  krb5_free_principal(context, princ);
409 
410      if (permitted_etypes != NULL && ks_tuple == NULL)
411 	free(permitted_etypes);
412 
413      return (code);
414 }
415 
416 int
417 remove_principal(char *keytab_str, krb5_keytab keytab, char
418 		     *princ_str, char *kvno_str)
419 {
420      krb5_principal princ;
421      krb5_keytab_entry entry;
422      krb5_kt_cursor cursor;
423 	enum {
424 		UNDEF, SPEC, HIGH, ALL, OLD
425 	}    mode;
426      int code, kvno, did_something;
427 
428      code = krb5_parse_name(context, princ_str, &princ);
429      if (code != 0) {
430 		com_err(whoami, code,
431 			gettext("while parsing principal name %s"),
432 		  princ_str);
433 		return (code);
434      }
435      mode = UNDEF;
436      if (kvno_str == NULL) {
437 	  mode = HIGH;
438 	  kvno = 0;
439      } else if (strcmp(kvno_str, "all") == 0) {
440 	  mode = ALL;
441 	  kvno = 0;
442      } else if (strcmp(kvno_str, "old") == 0) {
443 	  mode = OLD;
444 	  kvno = 0;
445      } else {
446 	  mode = SPEC;
447 	  kvno = atoi(kvno_str);
448      }
449 
450      /* kvno is set to specified value for SPEC, 0 otherwise */
451      code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry);
452      if (code != 0) {
453 	  if (code == ENOENT) {
454 			fprintf(stderr,
455 				gettext("%s: Keytab %s does not exist.\n"),
456 		       whoami, keytab_str);
457 	  } else if (code == KRB5_KT_NOTFOUND) {
458 	       if (mode != SPEC)
459 				fprintf(stderr,
460 					gettext("%s: No entry for principal "
461 						"%s exists in keytab %s\n"),
462 			    whoami, princ_str, keytab_str);
463 	       else
464 				fprintf(stderr,
465 					gettext("%s: No entry for principal "
466 						"%s with kvno %d exists in "
467 						"keytab %s.\n"),
468 					whoami, princ_str, kvno, keytab_str);
469 	  } else {
470 			com_err(whoami, code,
471 				gettext("while retrieving highest "
472 					"kvno from keytab"));
473 	  }
474 		return (code);
475      }
476      /* set kvno to spec'ed value for SPEC, highest kvno otherwise */
477      kvno = entry.vno;
478      krb5_kt_free_entry(context, &entry);
479 
480      code = krb5_kt_start_seq_get(context, keytab, &cursor);
481      if (code != 0) {
482 		com_err(whoami, code, gettext("while starting keytab scan"));
483 		return (code);
484      }
485      did_something = 0;
486 	while ((code = krb5_kt_next_entry(context,
487 		    keytab, &entry, &cursor)) == 0) {
488 	  if (krb5_principal_compare(context, princ, entry.principal) &&
489 	      ((mode == ALL) ||
490 	       (mode == SPEC && entry.vno == kvno) ||
491 	       (mode == OLD && entry.vno != kvno) ||
492 	       (mode == HIGH && entry.vno == kvno))) {
493 
494 	       /*
495 			 * Ack!  What a kludge... the scanning functions
496 			 * lock the keytab so entries cannot be removed
497 			 * while they are operating.
498 		*/
499 	       code = krb5_kt_end_seq_get(context, keytab, &cursor);
500 	       if (code != 0) {
501 				com_err(whoami, code,
502 					gettext("while temporarily "
503 						"ending keytab scan"));
504 				return (code);
505 	       }
506 	       code = krb5_kt_remove_entry(context, keytab, &entry);
507 	       if (code != 0) {
508 				com_err(whoami, code,
509 					gettext("while deleting entry "
510 						"from keytab"));
511 				return (code);
512 	       }
513 	       code = krb5_kt_start_seq_get(context, keytab, &cursor);
514 	       if (code != 0) {
515 				com_err(whoami, code,
516 				    gettext("while restarting keytab scan"));
517 				return (code);
518 	       }
519 	       did_something++;
520 	       if (!quiet)
521 				printf(gettext("Entry for principal "
522 					    "%s with kvno %d "
523 					    "removed from keytab %s.\n"),
524 			   princ_str, entry.vno, keytab_str);
525 	  }
526 	  krb5_kt_free_entry(context, &entry);
527      }
528      if (code && code != KRB5_KT_END) {
529 		com_err(whoami, code, gettext("while scanning keytab"));
530 		return (code);
531      }
532      if (code = krb5_kt_end_seq_get(context, keytab, &cursor)) {
533 		com_err(whoami, code, gettext("while ending keytab scan"));
534 		return (code);
535      }
536      /*
537 	 * If !did_someting then mode must be OLD or we would have already
538 	 * returned with an error.  But check it anyway just to prevent
539 	 * unexpected error messages...
540       */
541      if (!did_something && mode == OLD) {
542 		fprintf(stderr,
543 		    gettext("%s: There is only one entry for principal "
544 			"%s in keytab %s\n"),
545 		    whoami, princ_str, keytab_str);
546 		return (1);
547      }
548 	return (0);
549 }
550 
551 /*
552  * etype_string(enctype): return a string representation of the
553  * encryption type.  XXX copied from klist.c; this should be a
554  * library function, or perhaps just #defines
555  */
556 static char *
557 etype_string(enctype)
558     krb5_enctype enctype;
559 {
560     static char buf[100];
561     krb5_error_code ret;
562 
563     if (ret = krb5_enctype_to_string(enctype, buf, sizeof(buf)))
564 	sprintf(buf, "etype %d", enctype);
565 
566     return (buf);
567 }
568