49fdc20b89d0f4aae12b0ffec0effa291b1e3573
[krb5.git] / src / kadmin / cli / keytab.c
1 /*
2  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
3  *
4  * $Id$
5  * $Source$
6  */
7
8 /*
9  * Copyright (C) 1998 by the FundsXpress, INC.
10  * 
11  * All rights reserved.
12  * 
13  * Export of this software from the United States of America may require
14  * a specific license from the United States Government.  It is the
15  * responsibility of any person or organization contemplating export to
16  * obtain such a license before exporting.
17  * 
18  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19  * distribute this software and its documentation for any purpose and
20  * without fee is hereby granted, provided that the above copyright
21  * notice appear in all copies and that both that copyright notice and
22  * this permission notice appear in supporting documentation, and that
23  * the name of FundsXpress. not be used in advertising or publicity pertaining
24  * to distribution of the software without specific, written prior
25  * permission.  FundsXpress makes no representations about the suitability of
26  * this software for any purpose.  It is provided "as is" without express
27  * or implied warranty.
28  * 
29  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
30  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
31  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
32  */
33
34 #if !defined(lint) && !defined(__CODECENTER__)
35 static char *rcsid = "$Header$";
36 #endif
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include <krb5.h>
43 #include <kadm5/admin.h>
44 #include <krb5/adm_proto.h>
45 #include "kadmin.h"
46
47 static int add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
48                          krb5_boolean keepold,
49                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
50                          char *princ_str);
51 static int remove_principal(char *keytab_str, krb5_keytab keytab, char
52                             *princ_str, char *kvno_str);
53 static char *etype_string(krb5_enctype enctype);
54
55 static int quiet;
56
57 static void add_usage()
58 {
59      fprintf(stderr, "Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] [principal | -glob princ-exp] [...]\n");
60 }
61      
62 static void rem_usage()
63 {
64      fprintf(stderr, "Usage: ktremove [-k[eytab] keytab] [-q] principal [kvno|\"all\"|\"old\"]\n");
65 }
66
67 static int process_keytab(krb5_context my_context, char **keytab_str,
68                    krb5_keytab *keytab) 
69 {
70      int code;
71      
72      if (*keytab_str == NULL) {
73           /* XXX krb5_defkeyname is an internal library global and
74              should go away */
75           if (! (*keytab_str = strdup(krb5_defkeyname))) {
76                com_err(whoami, ENOMEM, "while creating keytab name");
77                return 1;
78           }
79           code = krb5_kt_default(my_context, keytab);
80           if (code != 0) {
81                com_err(whoami, code, "while opening default keytab");
82                free(*keytab_str);
83                return 1;
84           }
85      } else {
86           if (strchr(*keytab_str, ':') != NULL) {
87                *keytab_str = strdup(*keytab_str);
88                if (*keytab_str == NULL) {
89                     com_err(whoami, ENOMEM, "while creating keytab name");
90                     return 1;
91                }
92           } else {
93                char *tmp = *keytab_str;
94
95                *keytab_str = (char *)
96                     malloc(strlen("WRFILE:")+strlen(tmp)+1);
97                if (*keytab_str == NULL) {
98                     com_err(whoami, ENOMEM, "while creating keytab name");
99                     return 1;
100                }
101                sprintf(*keytab_str, "WRFILE:%s", tmp);
102           }
103           
104           code = krb5_kt_resolve(my_context, *keytab_str, keytab);
105           if (code != 0) {
106                com_err(whoami, code, "while resolving keytab %s", *keytab_str);
107                free(keytab_str);
108                return 1;
109           }
110      }
111      
112      return 0;
113 }
114
115      
116 void kadmin_keytab_add(int argc, char **argv)
117 {
118      krb5_keytab keytab = 0;
119      char *keytab_str = NULL, **princs;
120      int code, num, i;
121      krb5_error_code retval;
122      int n_ks_tuple = 0;
123      krb5_boolean keepold = FALSE;
124      krb5_key_salt_tuple *ks_tuple = NULL;
125
126      argc--; argv++;
127      quiet = 0;
128      while (argc) {
129           if (strncmp(*argv, "-k", 2) == 0) {
130                argc--; argv++;
131                if (!argc || keytab_str) {
132                     add_usage();
133                     return;
134                }
135                keytab_str = *argv;
136           } else if (strcmp(*argv, "-q") == 0) {
137                quiet++;
138           } else if (strcmp(*argv, "-e") == 0) {
139                argc--;
140                if (argc < 1) {
141                     add_usage();
142                     return;
143                }
144                retval = krb5_string_to_keysalts(*++argv, ", \t", ":.-", 0,
145                                                 &ks_tuple, &n_ks_tuple);
146                if (retval) {
147                     com_err("ktadd", retval, "while parsing keysalts %s",
148                             *argv);
149
150                     return;
151                }
152           } else
153                break;
154           argc--; argv++;
155      }
156
157      if (argc == 0) {
158           add_usage();
159           return;
160      }
161
162      if (process_keytab(context, &keytab_str, &keytab))
163           return;
164      
165      while (*argv) {
166           if (strcmp(*argv, "-glob") == 0) {
167                if (*++argv == NULL) {
168                     add_usage();
169                     break;
170                }
171                
172                code = kadm5_get_principals(handle, *argv, &princs, &num);
173                if (code) {
174                     com_err(whoami, code, "while expanding expression \"%s\".",
175                             *argv);
176                     argv++;
177                     continue;
178                }
179                
180                for (i = 0; i < num; i++) 
181                     (void) add_principal(handle, keytab_str, keytab,
182                                          keepold, n_ks_tuple, ks_tuple,
183                                          princs[i]); 
184                kadm5_free_name_list(handle, princs, num);
185           } else
186                (void) add_principal(handle, keytab_str, keytab,
187                                     keepold, n_ks_tuple, ks_tuple,
188                                     *argv);
189           argv++;
190      }
191           
192      code = krb5_kt_close(context, keytab);
193      if (code != 0)
194           com_err(whoami, code, "while closing keytab");
195
196      free(keytab_str);
197 }
198
199 void kadmin_keytab_remove(int argc, char **argv)
200 {
201      krb5_keytab keytab = 0;
202      char *keytab_str = NULL;
203      int code;
204
205      argc--; argv++;
206      quiet = 0;
207      while (argc) {
208           if (strncmp(*argv, "-k", 2) == 0) {
209                argc--; argv++;
210                if (!argc || keytab_str) {
211                     rem_usage();
212                     return;
213                }
214                keytab_str = *argv;
215           } else if (strcmp(*argv, "-q") == 0) {
216                quiet++;
217           } else
218                break;
219           argc--; argv++;
220      }
221
222      if (argc != 1 && argc != 2) {
223           rem_usage();
224           return;
225      }
226      if (process_keytab(context, &keytab_str, &keytab))
227           return;
228
229      (void) remove_principal(keytab_str, keytab, argv[0], argv[1]);
230
231      code = krb5_kt_close(context, keytab);
232      if (code != 0)
233           com_err(whoami, code, "while closing keytab");
234
235      free(keytab_str);
236 }
237
238 static 
239 int add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
240                   krb5_boolean keepold, int n_ks_tuple,
241                   krb5_key_salt_tuple *ks_tuple,
242                   char *princ_str) 
243 {
244      kadm5_principal_ent_rec princ_rec;
245      krb5_principal princ;
246      krb5_keytab_entry new_entry;
247      krb5_keyblock *keys;
248      int code, nkeys, i;
249
250      (void) memset((char *)&princ_rec, 0, sizeof(princ_rec));
251
252      princ = NULL;
253      keys = NULL;
254      nkeys = 0;
255
256      code = krb5_parse_name(context, princ_str, &princ);
257      if (code != 0) {
258           com_err(whoami, code, "while parsing -add principal name %s",
259                   princ_str);
260           goto cleanup;
261      }
262
263      if (keepold || ks_tuple != NULL) {
264          code = kadm5_randkey_principal_3(lhandle, princ,
265                                           keepold, n_ks_tuple, ks_tuple,
266                                           &keys, &nkeys);
267      } else {
268          code = kadm5_randkey_principal(lhandle, princ, &keys, &nkeys);
269      }
270      if (code != 0) {
271           if (code == KADM5_UNK_PRINC) {
272                fprintf(stderr, "%s: Principal %s does not exist.\n",
273                        whoami, princ_str);
274           } else
275                com_err(whoami, code, "while changing %s's key",
276                        princ_str);
277           goto cleanup;
278      }
279
280      code = kadm5_get_principal(lhandle, princ, &princ_rec,
281                                 KADM5_PRINCIPAL_NORMAL_MASK);
282      if (code != 0) {
283           com_err(whoami, code, "while retrieving principal");
284           goto cleanup;
285      }
286
287      for (i = 0; i < nkeys; i++) {
288           memset((char *) &new_entry, 0, sizeof(new_entry));
289           new_entry.principal = princ;
290           new_entry.key = keys[i];
291           new_entry.vno = princ_rec.kvno;
292
293           code = krb5_kt_add_entry(context, keytab, &new_entry);
294           if (code != 0) {
295                com_err(whoami, code, "while adding key to keytab");
296                (void) kadm5_free_principal_ent(lhandle, &princ_rec);
297                goto cleanup;
298           }
299
300           if (!quiet)
301                printf("Entry for principal %s with kvno %d, "
302                       "encryption type %s added to keytab %s.\n",
303                       princ_str, princ_rec.kvno,
304                       etype_string(keys[i].enctype), keytab_str);
305      }
306
307      code = kadm5_free_principal_ent(lhandle, &princ_rec);
308      if (code != 0) {
309           com_err(whoami, code, "while freeing principal entry");
310           goto cleanup;
311      }
312
313 cleanup:
314      if (nkeys) {
315           for (i = 0; i < nkeys; i++)
316                krb5_free_keyblock_contents(context, &keys[i]);
317           free(keys);
318      }
319      if (princ)
320           krb5_free_principal(context, princ);
321
322      return code;
323 }
324
325 int remove_principal(char *keytab_str, krb5_keytab keytab, char
326                      *princ_str, char *kvno_str) 
327 {
328      krb5_principal princ;
329      krb5_keytab_entry entry;
330      krb5_kt_cursor cursor;
331      enum { UNDEF, SPEC, HIGH, ALL, OLD } mode;
332      int code, did_something;
333      krb5_kvno kvno;
334
335      code = krb5_parse_name(context, princ_str, &princ);
336      if (code != 0) {
337           com_err(whoami, code, "while parsing principal name %s",
338                   princ_str);
339           return code;
340      }
341
342      mode = UNDEF;
343      if (kvno_str == NULL) {
344           mode = HIGH;
345           kvno = 0;
346      } else if (strcmp(kvno_str, "all") == 0) {
347           mode = ALL;
348           kvno = 0;
349      } else if (strcmp(kvno_str, "old") == 0) {
350           mode = OLD;
351           kvno = 0;
352      } else {
353           mode = SPEC;
354           kvno = atoi(kvno_str);
355      }
356
357      /* kvno is set to specified value for SPEC, 0 otherwise */
358      code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry);
359      if (code != 0) {
360           if (code == ENOENT) {
361                fprintf(stderr, "%s: Keytab %s does not exist.\n",
362                        whoami, keytab_str);
363           } else if (code == KRB5_KT_NOTFOUND) {
364                if (mode != SPEC)
365                     fprintf(stderr, "%s: No entry for principal "
366                             "%s exists in keytab %s\n",
367                             whoami, princ_str, keytab_str);
368                else
369                     fprintf(stderr, "%s: No entry for principal "
370                             "%s with kvno %d exists in keytab "
371                             "%s.\n", whoami, princ_str, kvno,
372                             keytab_str);
373           } else {
374                com_err(whoami, code, "while retrieving highest kvno "
375                        "from keytab");
376           }
377           return code;
378      }
379
380      /* set kvno to spec'ed value for SPEC, highest kvno otherwise */
381      kvno = entry.vno;
382      krb5_kt_free_entry(context, &entry);
383
384      code = krb5_kt_start_seq_get(context, keytab, &cursor);
385      if (code != 0) {
386           com_err(whoami, code, "while starting keytab scan");
387           return code;
388      }
389
390      did_something = 0;
391      while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
392           if (krb5_principal_compare(context, princ, entry.principal) &&
393               ((mode == ALL) ||
394                (mode == SPEC && entry.vno == kvno) ||
395                (mode == OLD && entry.vno != kvno) ||
396                (mode == HIGH && entry.vno == kvno))) {
397
398                /*
399                 * Ack!  What a kludge... the scanning functions lock
400                 * the keytab so entries cannot be removed while they
401                 * are operating.
402                 */
403                code = krb5_kt_end_seq_get(context, keytab, &cursor);
404                if (code != 0) {
405                     com_err(whoami, code, "while temporarily ending "
406                             "keytab scan");
407                     return code;
408                }
409                code = krb5_kt_remove_entry(context, keytab, &entry);
410                if (code != 0) {
411                     com_err(whoami, code, "while deleting entry from keytab");
412                     return code;
413                }
414                code = krb5_kt_start_seq_get(context, keytab, &cursor);
415                if (code != 0) {
416                     com_err(whoami, code, "while restarting keytab scan");
417                     return code;
418                }
419
420                did_something++;
421                if (!quiet)
422                     printf("Entry for principal %s with kvno %d "
423                            "removed from keytab %s.\n", 
424                            princ_str, entry.vno, keytab_str);
425           }
426           krb5_kt_free_entry(context, &entry);
427      }
428      if (code && code != KRB5_KT_END) {
429           com_err(whoami, code, "while scanning keytab");
430           return code;
431      }
432      if ((code = krb5_kt_end_seq_get(context, keytab, &cursor))) {
433           com_err(whoami, code, "while ending keytab scan");
434           return code;
435      }
436
437      /*
438       * If !did_someting then mode must be OLD or we would have
439       * already returned with an error.  But check it anyway just to
440       * prevent unexpected error messages...
441       */
442      if (!did_something && mode == OLD) {
443           fprintf(stderr, "%s: There is only one entry for principal "
444                   "%s in keytab %s\n", whoami, princ_str, keytab_str);
445           return 1;
446      }
447      
448      return 0;
449 }
450
451 /*
452  * etype_string(enctype): return a string representation of the
453  * encryption type.  XXX copied from klist.c; this should be a
454  * library function, or perhaps just #defines
455  */
456 static char *etype_string(enctype)
457     krb5_enctype enctype;
458 {
459     static char buf[100];
460     krb5_error_code ret;
461
462     if ((ret = krb5_enctype_to_string(enctype, buf, sizeof(buf))))
463         sprintf(buf, "etype %d", enctype);
464
465     return buf;
466 }