asetkey: add 'add-random' command
[openafs.git] / src / aklog / asetkey.c
1 /*
2  * $Id$
3  *
4  * asetkey - Manipulates an AFS KeyFile
5  *
6  * Updated for Kerberos 5
7  */
8
9 #include <afsconfig.h>
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <roken.h>
14
15 #define KERBEROS_APPLE_DEPRECATED(x)
16 #include <krb5.h>
17
18 #ifndef HAVE_KERBEROSV_HEIM_ERR_H
19 #include <afs/com_err.h>
20 #endif
21 #include <afs/cellconfig.h>
22 #include <afs/keys.h>
23 #include <afs/dirpath.h>
24
25 #ifdef HAVE_KRB5_CREDS_KEYBLOCK
26 #define USING_MIT 1
27 #endif
28 #ifdef HAVE_KRB5_CREDS_SESSION
29 #define USING_HEIMDAL 1
30 #endif
31
32 static int
33 stringToType(const char *string) {
34     if (strcmp(string, "rxkad") == 0)
35         return afsconf_rxkad;
36     if (strcmp(string, "rxkad_krb5") == 0)
37         return afsconf_rxkad_krb5;
38     if (strcmp(string, "rxgk") == 0)
39         return afsconf_rxgk;
40
41     return atoi(string);
42 }
43
44 static void
45 printKey(const struct rx_opaque *key)
46 {
47     int i;
48
49     for (i = 0; i < key->len; i++)
50         printf("%02x", ((unsigned char *)key->val)[i]);
51     printf("\n");
52 }
53
54
55 static int
56 char2hex(char c)
57 {
58   if (c >= '0' && c <= '9')
59     return (c - 48);
60   if ((c >= 'a') && (c <= 'f'))
61     return (c - 'a' + 10);
62
63   if ((c >= 'A') && (c <= 'F'))
64     return (c - 'A' + 10);
65
66   return -1;
67 }
68
69 static struct afsconf_typedKey *
70 keyFromCommandLine(afsconf_keyType type, int kvno, int subType,
71                    const char *string, size_t length)
72 {
73     struct rx_opaque key;
74     struct afsconf_typedKey *typedKey;
75     const char *cp;
76     int i;
77
78     if (strlen(string) != 2*length) {
79         printf("key %s is not in right format\n", string);
80         printf(" <key> should be an %d byte hex representation \n", (int) length);
81         exit(1);
82     }
83
84     rx_opaque_alloc(&key, length);
85     cp = string;
86     for (i = 0; i< length; i++) {
87        ((char *)key.val)[i] = char2hex(*cp) * 16 + char2hex(*(cp+1));
88        cp+=2;
89     }
90
91     typedKey = afsconf_typedKey_new(type, kvno, subType, &key);
92     rx_opaque_freeContents(&key);
93     return typedKey;
94 }
95
96 #ifdef USING_HEIMDAL
97 #define deref_key_length(key)                   \
98             key->keyvalue.length
99
100 #define deref_key_contents(key)                 \
101             key->keyvalue.data
102 #else
103 #define deref_key_length(key)                   \
104             key->length
105
106 #define deref_key_contents(key)                 \
107             key->contents
108 #endif
109
110 static struct afsconf_typedKey *
111 keyFromKeytab(int kvno, afsconf_keyType type, int subtype, const char *keytab, const char *princ)
112 {
113     int retval;
114     krb5_principal principal;
115     krb5_keyblock *key;
116     krb5_context context;
117     struct rx_opaque buffer;
118     struct afsconf_typedKey *typedKey;
119
120     krb5_init_context(&context);
121
122     retval = krb5_parse_name(context, princ, &principal);
123     if (retval) {
124         afs_com_err("asetkey", retval, "while parsing AFS principal");
125         exit(1);
126     }
127
128     if (type == afsconf_rxkad) {
129         retval = krb5_kt_read_service_key(context, (char *)keytab, principal,
130                                           kvno, ENCTYPE_DES_CBC_CRC, &key);
131         if (retval == KRB5_KT_NOTFOUND)
132             retval = krb5_kt_read_service_key(context, (char *)keytab,
133                                               principal, kvno,
134                                               ENCTYPE_DES_CBC_MD5, &key);
135         if (retval == KRB5_KT_NOTFOUND)
136             retval = krb5_kt_read_service_key(context, (char *)keytab,
137                                               principal, kvno,
138                                               ENCTYPE_DES_CBC_MD4, &key);
139     } else if (type == afsconf_rxkad_krb5 || type == afsconf_rxgk) {
140         retval = krb5_kt_read_service_key(context, (char *)keytab, principal,
141                                           kvno, subtype, &key);
142     } else {
143         retval=AFSCONF_BADKEY;
144     }
145     if (retval == KRB5_KT_NOTFOUND) {
146         char * princname = NULL;
147
148         krb5_unparse_name(context, principal, &princname);
149
150         if (type == afsconf_rxkad) {
151             afs_com_err("asetkey", retval,
152                         "for keytab entry with Principal %s, kvno %u, "
153                         "DES-CBC-CRC/MD5/MD4",
154                         princname ? princname : princ, kvno);
155         } else {
156             afs_com_err("asetkey", retval,
157                         "for keytab entry with Principal %s, kvno %u",
158                         princname ? princname : princ, kvno);
159         }
160         exit(1);
161     }
162
163     if (retval != 0) {
164         afs_com_err("asetkey", retval, "while extracting AFS service key");
165         exit(1);
166     }
167
168     if (type == afsconf_rxkad && deref_key_length(key) != 8) {
169         fprintf(stderr, "Key length should be 8, but is really %u!\n",
170                 (unsigned int)deref_key_length(key));
171         exit(1);
172     }
173
174     rx_opaque_populate(&buffer, deref_key_contents(key), deref_key_length(key));
175
176     typedKey = afsconf_typedKey_new(type, kvno, subtype, &buffer);
177     rx_opaque_freeContents(&buffer);
178     krb5_free_principal(context, principal);
179     krb5_free_keyblock(context, key);
180     return typedKey;
181 }
182
183 static void
184 addKey(struct afsconf_dir *dir, int argc, char **argv) {
185     struct afsconf_typedKey *typedKey;
186     int type;
187     int kvno;
188     int code;
189
190     switch (argc) {
191       case 4:
192         typedKey = keyFromCommandLine(afsconf_rxkad, atoi(argv[2]), 0,
193                                       argv[3], 8);
194         break;
195       case 5:
196         typedKey = keyFromKeytab(atoi(argv[2]), afsconf_rxkad, 0, argv[3], argv[4]);
197         break;
198       case 6:
199         type = stringToType(argv[2]);
200         kvno = atoi(argv[3]);
201         if (type == afsconf_rxkad) {
202             typedKey = keyFromCommandLine(afsconf_rxkad, kvno, 0, argv[5], 8);
203         } else if (type == afsconf_rxgk || type == afsconf_rxkad_krb5) {
204             typedKey = keyFromCommandLine(type, kvno, atoi(argv[4]), argv[5], strlen(argv[5])/2);
205         } else {
206             fprintf(stderr, "Unknown key type %s\n", argv[2]);
207             exit(1);
208         }
209         break;
210       case 7:
211         type = stringToType(argv[2]);
212         kvno = atoi(argv[3]);
213         if (type == afsconf_rxkad || type == afsconf_rxkad_krb5 || type == afsconf_rxgk) {
214             typedKey = keyFromKeytab(kvno, type, atoi(argv[4]), argv[5],
215                                      argv[6]);
216         } else {
217             fprintf(stderr, "Unknown key type %s\n", argv[2]);
218             exit(1);
219         }
220         break;
221       default:
222         fprintf(stderr, "%s add: usage is '%s add <kvno> <keyfile> "
223                         "<princ>\n", argv[0], argv[0]);
224         fprintf(stderr, "\tOR\n\t%s add <kvno> <key>\n", argv[0]);
225         fprintf(stderr, "\tOR\n\t%s add <type> <kvno> <subtype> <key>\n",
226                 argv[0]);
227         fprintf(stderr, "\tOR\n\t%s add <type> <kvno> <subtype> <keyfile> <princ>\n",
228                 argv[0]);
229         fprintf(stderr, "\t\tEx: %s add 0 \"80b6a7cd7a9dadb6\"\n", argv[0]);
230                 exit(1);
231     }
232     code = afsconf_AddTypedKey(dir, typedKey, 1);
233     afsconf_typedKey_put(&typedKey);
234     if (code) {
235         afs_com_err("asetkey", code, "while adding new key");
236         exit(1);
237     }
238 }
239
240 static struct afsconf_typedKey *
241 random_key(char **argv, int type, int kvno, int subtype)
242 {
243     struct afsconf_typedKey *typedKey;
244     krb5_context ctx;
245     krb5_keyblock keyblock;
246     struct rx_opaque key;
247     int code;
248
249     code = krb5_init_context(&ctx);
250     if (code) {
251         afs_com_err(argv[0], code, "while initializing krb5 ctx");
252         exit(1);
253     }
254
255     memset(&keyblock, 0, sizeof(keyblock));
256     code = krb5_c_make_random_key(ctx, subtype, &keyblock);
257     if (code) {
258         afs_com_err(argv[0], code, "while generating random key");
259         exit(1);
260     }
261
262     memset(&key, 0, sizeof(key));
263     key.len = keyblock.length;
264     key.val = keyblock.contents;
265
266     typedKey = afsconf_typedKey_new(type, kvno, subtype, &key);
267
268     krb5_free_keyblock_contents(ctx, &keyblock);
269     krb5_free_context(ctx);
270
271     return typedKey;
272 }
273
274 static void
275 addRandomKey(struct afsconf_dir *dir, int argc, char **argv)
276 {
277     struct afsconf_typedKey *typedKey;
278     int type;
279     int kvno;
280     int code;
281     int subtype;
282
283     /* Just pick a reasonable enctype */
284     const int RAND_ENCTYPE = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
285
286     subtype = RAND_ENCTYPE;
287
288     switch (argc) {
289     case 5:
290         subtype = atoi(argv[4]);
291         /* fall through */
292     case 4:
293         type = stringToType(argv[2]);
294         kvno = atoi(argv[3]);
295
296         typedKey = random_key(argv, type, kvno, subtype);
297
298         code = afsconf_AddTypedKey(dir, typedKey, 1);
299         afsconf_typedKey_put(&typedKey);
300         if (code) {
301             afs_com_err(argv[0], code, "while adding random key");
302             exit(1);
303         }
304
305         printf("Added random key with type %d kvno %d subtype %d\n",
306                type, kvno, subtype);
307         break;
308
309     default:
310         fprintf(stderr, "%s add-random: usage is '%s add-random <type> <kvno>\n",
311                 argv[0], argv[0]);
312         fprintf(stderr, "\tOR\n\t%s add-random <type> <kvno> <subtype>\n", argv[0]);
313         exit(1);
314     }
315 }
316
317 static void
318 deleteKey(struct afsconf_dir *dir, int argc, char **argv)
319 {
320     int type;
321     int subtype;
322     int kvno;
323     int code;
324
325     switch (argc) {
326     case 3:
327         kvno = atoi(argv[2]);
328         code = afsconf_DeleteKey(dir, kvno);
329         if (code) {
330             afs_com_err(argv[0], code, "while deleting key %d", kvno);
331             exit(1);
332         }
333         printf("Deleted rxkad key %d\n", kvno);
334         break;
335
336     case 4:
337         type = stringToType(argv[2]);
338         kvno = atoi(argv[3]);
339         code = afsconf_DeleteKeyByType(dir, type, kvno);
340         if (code) {
341             afs_com_err(argv[0], code, "while deleting key (type %d kvno %d)",
342                         type, kvno);
343             exit(1);
344         }
345         printf("Deleted key (type %d kvno %d)\n", type, kvno);
346         break;
347
348     case 5:
349         type = stringToType(argv[2]);
350         kvno = atoi(argv[3]);
351         subtype = atoi(argv[4]);
352         code = afsconf_DeleteKeyBySubType(dir, type, kvno, subtype);
353         if (code) {
354             afs_com_err(argv[0], code, "while deleting key (type %d kvno %d subtype %d)\n",
355                         type, kvno, subtype);
356             exit(1);
357         }
358         printf("Deleted key (type %d kvno %d subtype %d)\n", type, kvno, subtype);
359         break;
360
361     default:
362         fprintf(stderr, "%s delete: usage is '%s delete <kvno>\n",
363                 argv[0], argv[0]);
364         fprintf(stderr, "\tOR\n\t%s delete <type> <kvno>\n", argv[0]);
365         fprintf(stderr, "\tOR\n\t%s delete <type> <kvno> <subtype>\n", argv[0]);
366         exit(1);
367     }
368 }
369
370 static void
371 listKey(struct afsconf_dir *dir, int argc, char **argv)
372 {
373     struct afsconf_typedKeyList *keys;
374     int i;
375     int code;
376
377     code = afsconf_GetAllKeys(dir, &keys);
378     if (code) {
379         afs_com_err("asetkey", code, "while retrieving keys");
380         exit(1);
381     }
382     for (i = 0; i < keys->nkeys; i++) {
383         afsconf_keyType type;
384         int kvno;
385         int minorType;
386         struct rx_opaque *keyMaterial;
387
388         afsconf_typedKey_values(keys->keys[i], &type, &kvno, &minorType,
389                                     &keyMaterial);
390         switch(type) {
391           case afsconf_rxkad:
392             if (kvno != -1) {
393                 printf("rxkad\tkvno %4d: key is: ", kvno);
394                 printKey(keyMaterial);
395             }
396             break;
397           case afsconf_rxkad_krb5:
398             if (kvno != -1) {
399                 printf("rxkad_krb5\tkvno %4d enctype %d; key is: ",
400                        kvno, minorType);
401                 printKey(keyMaterial);
402             }
403             break;
404           case afsconf_rxgk:
405             if (kvno != -1) {
406                 printf("rxgk\tkvno %4d enctype %d; key is: ",
407                        kvno, minorType);
408                 printKey(keyMaterial);
409             }
410             break;
411           default:
412             printf("unknown(%d)\tkvno %4d subtype %d; key is: ", type,
413                    kvno, minorType);
414             printKey(keyMaterial);
415             break;
416           }
417     }
418     printf("All done.\n");
419 }
420
421 int
422 main(int argc, char *argv[])
423 {
424     struct afsconf_dir *tdir;
425     const char *confdir;
426
427     if (argc == 1) {
428         fprintf(stderr, "%s: usage is '%s <opcode> options, e.g.\n",
429                 argv[0], argv[0]);
430         fprintf(stderr, "\t%s add <kvno> <keyfile> <princ>\n", argv[0]);
431         fprintf(stderr, "\tOR\n\t%s add <kvno> <key>\n", argv[0]);
432         fprintf(stderr, "\tOR\n\t%s add <type> <kvno> <subtype> <key>\n",
433                 argv[0]);
434         fprintf(stderr, "\tOR\n\t%s add <type> <kvno> <subtype> <keyfile> <princ>\n",
435                 argv[0]);
436         fprintf(stderr, "\t\tEx: %s add 0 \"80b6a7cd7a9dadb6\"\n", argv[0]);
437         fprintf(stderr, "\t%s add-random <type> <kvno>\n", argv[0]);
438         fprintf(stderr, "\t%s add-random <type> <kvno> <subtype>\n", argv[0]);
439         fprintf(stderr, "\t%s delete <kvno>\n", argv[0]);
440         fprintf(stderr, "\t%s delete <type> <kvno>\n", argv[0]);
441         fprintf(stderr, "\t%s delete <type> <kvno> <subtype>\n", argv[0]);
442         fprintf(stderr, "\t%s list\n", argv[0]);
443         exit(1);
444     }
445
446     confdir = AFSDIR_SERVER_ETC_DIRPATH;
447
448     tdir = afsconf_Open(confdir);
449     if (!tdir) {
450         fprintf(stderr, "%s: can't initialize conf dir '%s'\n", argv[0],
451                 confdir);
452         exit(1);
453     }
454     if (strcmp(argv[1], "add")==0) {
455         addKey(tdir, argc, argv);
456     }
457     else if (strcmp(argv[1], "delete")==0) {
458         deleteKey(tdir, argc, argv);
459     }
460     else if (strcmp(argv[1], "list") == 0) {
461         listKey(tdir, argc, argv);
462
463     }
464     else if (strcmp(argv[1], "add-random") == 0) {
465         addRandomKey(tdir, argc, argv);
466     }
467     else {
468         fprintf(stderr, "%s: unknown operation '%s', type '%s' for "
469                 "assistance\n", argv[0], argv[1], argv[0]);
470         exit(1);
471     }
472     exit(0);
473 }