1 /* aklog/akeyconvert.c - migrate keys from rxkad.keytab to KeyFileExt */
3 * Copyright (C) 2015 by the Massachusetts Institute of Technology.
4 * Copyright (C) 2016 Benjamin Kaduk.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 * Helper for migrations from OpenAFS 1.6.x to OpenAFS 1.8.x when
35 * the rxkad-k5 extension is in use.
37 * Read keys from the current rxkad.keytab and add them to the
38 * KeyFileExt, creating it if necessary. Detect duplicated
39 * kvno/enctype keys, which are possible when attached to different
40 * principals in the rxkad.keytab, but are not possible in the
43 * The implementation reads the entire keytab contents into memory,
44 * then sorts by principal (most significant), kvno, and enctype (least
45 * significant) to facilitate selecting the newest kvno for each principal
46 * and avoiding duplicate kvno/enctype values. The direction of sort is
47 * chosen so as to hopefully put the more often used keys at the beginning
50 * By default, only copy the latest key for each principal, but provide an
51 * option to copy all keys.
54 #include <afsconfig.h>
55 #include <afs/param.h>
56 #include <sys/errno.h>
59 #include <afs/cellconfig.h>
60 #include <afs/dirpath.h>
69 #define KERBEROS_APPLE_DEPRECATED(x)
70 /* krb5_free_unparsed_name() is deprecated; it's unclear why. */
71 #define KRB5_DEPRECATED_FUNCTION(x)
75 #elif HAVE_ET_COM_ERR_H
76 # include <et/com_err.h>
77 #elif HAVE_KRB5_COM_ERR_H
78 # include <krb5/com_err.h>
80 # error com_err is required for akeyconvert
83 #if HAVE_KRB5_KEYTAB_ENTRY_KEY
84 # define deref_entry_keylen(x) ((x).key.length)
85 # define deref_entry_keyval(x) ((x).key.contents)
86 # define deref_entry_enctype(x) ((x).key.enctype)
87 #elif HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
88 # define deref_entry_keylen(x) ((x).keyblock.keyvalue.length)
89 # define deref_entry_keyval(x) ((x).keyblock.keyvalue.data)
90 # define deref_entry_enctype(x) ((x).keyblock.keytype)
92 # error krb5_keytab_entry structure unknown
94 #ifndef HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS
95 # define krb5_free_keytab_entry_contents krb5_kt_free_entry
97 #ifndef HAVE_KRB5_FREE_UNPARSED_NAME
98 # define krb5_free_unparsed_name(x, y) free((y))
106 * Convert keytab entry to the OpenAFS typedKey format, allocating
107 * storage for the output.
109 * Returns 0 on success.
112 ktent_to_typedKey(krb5_keytab_entry entry, struct afsconf_typedKey **out)
114 struct rx_opaque key;
117 key.len = deref_entry_keylen(entry);
118 key.val = deref_entry_keyval(entry);
119 enctype = deref_entry_enctype(entry);
120 if (enctype == 1 /* ETYPE_DES_CBC_CRC */ ||
121 enctype == 2 /* ETYPE_DES_CBC_MD4 */ ||
122 enctype == 3 /* ETYPE_DES_CBC_MD5 */) {
123 *out = afsconf_typedKey_new(afsconf_rxkad, entry.vno, 0, &key);
129 /* else, an rxkad_krb5 key */
130 *out = afsconf_typedKey_new(afsconf_rxkad_krb5, entry.vno,
131 deref_entry_enctype(entry), &key);
139 princ_sort(const void *aa, const void *bb)
141 const krb5_keytab_entry *a, *b;
142 char *name1 = NULL, *name2 = NULL;
150 opr_Verify(krb5_init_context(&ctx) == 0);
151 equal = krb5_principal_compare(ctx, a->principal, b->principal);
156 opr_Verify(krb5_unparse_name(ctx, a->principal, &name1) == 0);
157 opr_Verify(krb5_unparse_name(ctx, b->principal, &name2) == 0);
158 ret = strcmp(name1, name2);
159 opr_Assert(ret != 0);
162 krb5_free_unparsed_name(ctx, name1);
163 krb5_free_unparsed_name(ctx, name2);
164 krb5_free_context(ctx);
169 kvno_sort(const void *aa, const void *bb)
171 const krb5_keytab_entry *a, *b;
176 if (a->vno == b->vno)
178 else if (a->vno > b->vno)
185 etype_sort(const void *aa, const void *bb)
187 const krb5_keytab_entry *a, *b;
192 if (deref_entry_enctype(*a) == deref_entry_enctype(*b))
194 else if (deref_entry_enctype(*a) > deref_entry_enctype(*b))
201 ke_sort(const void *a, const void *b)
205 ret = kvno_sort(a, b);
208 return etype_sort(a, b);
212 full_sort(const void *a, const void *b)
216 ret = princ_sort(a, b);
219 return ke_sort(a, b);
223 slurp_keytab(krb5_context ctx, char *kt_path, krb5_keytab_entry **ents_out,
226 krb5_keytab kt = NULL;
227 krb5_keytab_entry entry, *ents = NULL;
228 krb5_kt_cursor cursor;
234 memset(&cursor, 0, sizeof(cursor));
236 code = krb5_kt_resolve(ctx, kt_path, &kt);
240 code = krb5_kt_start_seq_get(ctx, kt, &cursor);
243 while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) {
245 krb5_free_keytab_entry_contents(ctx, &entry);
247 krb5_kt_end_seq_get(ctx, kt, &cursor);
248 if (code != 0 && code != KRB5_KT_END)
251 ents = calloc(n, sizeof(*ents));
256 code = krb5_kt_start_seq_get(ctx, kt, &cursor);
260 while ((code = krb5_kt_next_entry(ctx, kt, ents + i, &cursor)) == 0) {
262 /* Out of space; bail early */
263 fprintf(stderr, "Warning: keytab size changed during processing\n");
267 krb5_kt_end_seq_get(ctx, kt, &cursor);
268 if (code != 0 && code != KRB5_KT_END)
277 krb5_kt_close(ctx, kt);
282 * Check for duplicate kvno/enctype pairs (across different principals).
284 * This is a fatal error, but emit a diagnostic for all instances before
287 * Requires the input array (ents) to be sorted by kvno and enctype.
290 check_dups(struct afsconf_dir *dir, krb5_keytab_entry *ents, int nents)
292 int i, old_kvno = 0, old_etype = 0;
295 for (i = 0; i < nents; ++i) {
296 if (old_kvno == ents[i].vno &&
297 old_etype == deref_entry_enctype(ents[i])) {
298 fprintf(stderr, "Duplicate kvno/enctype %i/%i\n", old_kvno,
300 code = AFSCONF_KEYINUSE;
302 old_kvno = ents[i].vno;
303 old_etype = deref_entry_enctype(ents[i]);
306 fprintf(stderr, "FATAL: duplicate key identifiers found.\n");
311 * Go through the list of keytab entries and write them to the KeyFileExt.
313 * If do_all is set, write all entries; otherwise, only write the highest
314 * kvno for each principal.
316 * Emit a diagnostic for kvno/enctype pairs which are already in the
317 * KeyFileExt (and thus cannot be added), but continue on.
319 * Requires the input array (ents) to be fully sorted, by principal, kvno,
323 convert_kt(struct afsconf_dir *dir, krb5_context ctx, krb5_keytab_entry *ents,
324 int nents, int do_all)
327 krb5_principal old_princ, wellknown_princ;
328 struct afsconf_typedKey *key = NULL;
329 afsconf_keyType type;
330 afs_int32 best_kvno = 0, code;
332 code = krb5_parse_name(ctx, "WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS",
334 old_princ = wellknown_princ;
338 for (i = 0; i < nents; ++i) {
339 if (!krb5_principal_compare(ctx, old_princ, ents[i].principal)) {
340 best_kvno = ents[i].vno;
342 if (krb5_principal_compare(ctx, old_princ, ents[i].principal) &&
343 best_kvno != ents[i].vno && !do_all)
345 old_princ = ents[i].principal;
346 code = ktent_to_typedKey(ents[i], &key);
349 afsconf_typedKey_values(key, &type, NULL, NULL, NULL);
350 if (type == afsconf_rxkad) {
352 "Cannot add single-DES keys to KeyFileExt, continuing\n");
353 afsconf_typedKey_put(&key);
356 code = afsconf_AddTypedKey(dir, key, 0);
357 if (code == AFSCONF_KEYINUSE) {
359 "Key already exists for kvno %i enctype %i, continuing\n",
360 ents[i].vno, deref_entry_enctype(ents[i]));
361 afsconf_typedKey_put(&key);
367 afsconf_typedKey_put(&key);
370 printf("Wrote %i keys\n", n);
373 afsconf_typedKey_put(&key);
374 krb5_free_principal(ctx, wellknown_princ);
379 * Liberate the Shepherds of the Trees from the forest that they might
380 * seek out the Entwives.
382 * Deallocate the storage for the keytab entries stored in the
383 * array ents (of length nents), and also deallocate the storage
384 * for the array itself.
386 * Safe to call with a NULL ents parameter.
389 free_ents(krb5_context ctx, krb5_keytab_entry *ents, int nents)
395 for(i = 0; i < nents; ++i)
396 krb5_free_keytab_entry_contents(ctx, ents + i);
401 CommandProc(struct cmd_syndesc *as, void *arock)
403 char *kt_path = NULL;
404 krb5_context ctx = NULL;
405 krb5_keytab_entry *ents = NULL;
406 struct afsconf_dir *dir;
408 int do_all, nents = -1;
410 code = krb5_init_context(&ctx);
414 dir = afsconf_Open(AFSDIR_SERVER_ETC_DIR);
416 fprintf(stderr, "Failed to open server config directory\n");
421 code = asprintf(&kt_path, "%s/%s", dir->name, AFSDIR_RXKAD_KEYTAB_FILE);
427 code = slurp_keytab(ctx, kt_path, &ents, &nents);
429 fprintf(stderr, "failed to read keytab\n");
433 /* Sort the keytab by kvno and enctype. */
434 qsort(ents, nents, sizeof(*ents), &ke_sort);
436 /* Check for duplicates before sorting by principal. */
437 code = check_dups(dir, ents, nents);
441 qsort(ents, nents, sizeof(*ents), &full_sort);
443 do_all = cmd_OptionPresent(as, OPT_all);
444 code = convert_kt(dir, ctx, ents, nents, do_all);
446 fprintf(stderr, "Failed to convert keys, errno %i\n", errno);
451 free_ents(ctx, ents, nents);
453 krb5_free_context(ctx);
459 main(int argc, char *argv[])
461 struct cmd_syndesc *ts;
464 ts = cmd_CreateSyntax(NULL, CommandProc, NULL, 0,
465 "Convert cell keys for the 1.6->1.8 OpenAFS upgrade");
466 cmd_AddParmAtOffset(ts, OPT_all, "-all", CMD_FLAG, CMD_OPTIONAL,
467 "convert old keys as well as the current keys");
468 code = cmd_Dispatch(argc, argv);