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
);