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