From: Greg Hudson Date: Wed, 29 Jul 2009 16:10:32 +0000 (+0000) Subject: Enctype list configuration enhancements X-Git-Tag: krb5-1.8-alpha1~412 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=d71bb0ac2c2f24486971aca60efe0b5d7e878c30;p=krb5.git Enctype list configuration enhancements In the processing code for enctype lists, add support for "DEFAULT" to indicate the default list, for families (des/des3/aes/rc4), and for removing entries from the current list (-foo). Also add unit tests and document. ticket: 6539 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@22469 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/doc/admin.texinfo b/doc/admin.texinfo index f5d5e618c..9af5f6b25 100644 --- a/doc/admin.texinfo +++ b/doc/admin.texinfo @@ -360,6 +360,15 @@ but not recommended for use. @include support-enc.texinfo +The string DEFAULT can be used to refer to the default set of types for +the variable in question. Types or families can be removed from the +current list by prefixing them with a minus sign (``-''). Types or +families can be prefixed with a plus sign (``+'') for symmetry; it has +the same meaning as just listing the type or family. For example, +``DEFAULT -des'' would be the default set of encryption types with DES +types removed, and ``des3 DEFAULT'' would be the default set of +encryption types with triple DES types moved to the front. + While aes128-cts and aes256-cts are supported for all Kerberos operations, they are not supported by older versions of our GSSAPI implementation (krb5-1.3.1 and earlier). diff --git a/doc/support-enc.texinfo b/doc/support-enc.texinfo index c359db6ea..693f27393 100644 --- a/doc/support-enc.texinfo +++ b/doc/support-enc.texinfo @@ -1,6 +1,7 @@ @ignore -the information in this file should be consistent with the information -in krb5/src/lib/crypto/etypes.c (and krb5/src/include/krb5.h[in]?) +The information in this file should be consistent with the information +in krb5/src/lib/crypto/etypes.c and the family processing code in +krb5/src/lib/krb5/krb/init_ctx.c (krb5int_parse_enctype_list). @end ignore @table @code @@ -34,4 +35,12 @@ RC4 with HMAC/MD5 @itemx rc4-hmac-exp @itemx arcfour-hmac-md5-exp Exportable RC4 with HMAC/MD5 (weak) +@item des +The DES family: des-cbc-crc, des-cbc-md5, and des-cbc-md4 (weak) +@item des3 +The triple DES family: des3-cbc-sha1 +@item aes +The AES family: aes256-cts-hmac-sha1-96 and aes128-cts-hmac-sha1-96 +@item rc4 +The RC4 family: arcfour-hmac @end table diff --git a/src/include/k5-int.h b/src/include/k5-int.h index d4407b99d..c2f112709 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -2795,6 +2795,10 @@ krb5int_pac_sign(krb5_context context, const krb5_keyblock *privsvr_key, krb5_data *data); +krb5_error_code krb5int_parse_enctype_list(krb5_context context, char *profstr, + krb5_enctype *default_list, + krb5_enctype **result); + #ifdef DEBUG_ERROR_LOCATIONS #define krb5_set_error_message(ctx, code, ...) \ krb5_set_error_message_fl(ctx, code, __FILE__, __LINE__, __VA_ARGS__) diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in index db1f82372..1a08eccc4 100644 --- a/src/lib/krb5/krb/Makefile.in +++ b/src/lib/krb5/krb/Makefile.in @@ -284,7 +284,8 @@ SRCS= $(srcdir)/addr_comp.c \ $(srcdir)/t_deltat.c \ $(srcdir)/t_expand.c \ $(srcdir)/t_pac.c \ - $(srcdir)/t_princ.c + $(srcdir)/t_princ.c \ + $(srcdir)/t_etypes.c # Someday, when we have a "maintainer mode", do this right: BISON=bison @@ -316,6 +317,8 @@ T_PAC_OBJS= t_pac.o pac.o T_PRINC_OBJS= t_princ.o parse.o unparse.o +T_ETYPES_OBJS= t_etypes.o init_ctx.o + t_walk_rtree: $(T_WALK_RTREE_OBJS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o t_walk_rtree $(T_WALK_RTREE_OBJS) $(KRB5_BASE_LIBS) t_ad_fx_armor: t_ad_fx_armor.o @@ -344,8 +347,11 @@ t_pac: $(T_PAC_OBJS) $(KRB5_BASE_DEPLIBS) t_princ: $(T_PRINC_OBJS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o t_princ $(T_PRINC_OBJS) $(KRB5_BASE_LIBS) +t_etypes: $(T_ETYPES_OBJS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o t_etypes $(T_ETYPES_OBJS) $(KRB5_BASE_LIBS) + TEST_PROGS= t_walk_rtree t_kerb t_ser t_deltat t_expand t_authdata t_pac \ - t_princ + t_princ t_etypes check-unix:: $(TEST_PROGS) KRB5_CONFIG=$(srcdir)/t_krb5.conf ; export KRB5_CONFIG ;\ @@ -382,6 +388,7 @@ check-unix:: $(TEST_PROGS) $(RUN_SETUP) $(VALGRIND) ./t_authdata $(RUN_SETUP) $(VALGRIND) ./t_pac $(RUN_SETUP) $(VALGRIND) ./t_princ + $(RUN_SETUP) $(VALGRIND) ./t_etypes clean:: $(RM) $(OUTPRE)t_walk_rtree$(EXEEXT) $(OUTPRE)t_walk_rtree.$(OBJEXT) \ diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c index e6ae2a712..2545be396 100644 --- a/src/lib/krb5/krb/init_ctx.c +++ b/src/lib/krb5/krb/init_ctx.c @@ -60,19 +60,20 @@ #include "../krb5_libinit.h" #endif +/* This must be the largest enctype value defined in krb5.h. */ +#define MAX_ENCTYPE ENCTYPE_ARCFOUR_HMAC_EXP + /* The des-mdX entries are last for now, because it's easy to configure KDCs to issue TGTs with des-mdX keys and then not accept them. This'll be fixed, but for better compatibility, let's prefer des-crc for now. */ -#define DEFAULT_ETYPE_LIST \ - "aes256-cts-hmac-sha1-96 " \ - "aes128-cts-hmac-sha1-96 " \ - "des3-cbc-sha1 arcfour-hmac-md5 " \ - "des-cbc-crc des-cbc-md5 des-cbc-md4 " - -/* Not included: - "aes128-cts-hmac-sha1-96 " \ - */ +static krb5_enctype default_enctype_list[] = { + ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96, + ENCTYPE_DES3_CBC_SHA1, + ENCTYPE_ARCFOUR_HMAC, + ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_CBC_MD4, + 0 +}; #if (defined(_WIN32)) extern krb5_error_code krb5_vercheck(); @@ -344,92 +345,137 @@ krb5_set_default_tgs_ktypes(krb5_context context, const krb5_enctype *etypes) return set_default_etype_var(context, etypes, &context->tgs_etypes); } +/* + * Add etype to, or remove etype from, list (of size MAX_ENCTYPE + 1) + * which has *count entries. Filter out weak enctypes if allow_weak + * is false. + */ +static void +mod_list(krb5_enctype etype, krb5_boolean add, krb5_boolean allow_weak, + krb5_enctype *list, unsigned int *count) +{ + unsigned int i; + + assert(etype > 0 && etype <= MAX_ENCTYPE); + if (!allow_weak && krb5_c_weak_enctype(etype)) + return; + for (i = 0; i < *count; i++) { + if (list[i] == etype) { + if (!add) { + for (; i < *count - 1; i++) + list[i] = list[i + 1]; + (*count)--; + } + return; + } + } + if (add) { + assert(*count < MAX_ENCTYPE); + list[(*count)++] = etype; + } +} + +/* + * Set *result to a zero-terminated list of enctypes resulting from + * parsing profstr. profstr may be modified during parsing. + */ +krb5_error_code +krb5int_parse_enctype_list(krb5_context context, char *profstr, + krb5_enctype *default_list, krb5_enctype **result) +{ + char *token, *delim = " \t\r\n,", *save = NULL; + krb5_boolean sel, weak = context->allow_weak_crypto; + krb5_enctype etype, list[MAX_ENCTYPE]; + unsigned int i, count = 0; + + *result = NULL; + + /* Walk through the words in profstr. */ + for (token = strtok_r(profstr, delim, &save); token; + token = strtok_r(NULL, delim, &save)) { + /* Determine if we are adding or removing enctypes. */ + sel = TRUE; + if (*token == '+' || *token == '-') + sel = (*token++ == '+'); + + if (strcasecmp(token, "DEFAULT") == 0) { + /* Set all enctypes in the default list. */ + for (i = 0; default_list[i]; i++) + mod_list(default_list[i], sel, weak, list, &count); + } else if (strcasecmp(token, "des") == 0) { + mod_list(ENCTYPE_DES_CBC_CRC, sel, weak, list, &count); + mod_list(ENCTYPE_DES_CBC_MD5, sel, weak, list, &count); + mod_list(ENCTYPE_DES_CBC_MD4, sel, weak, list, &count); + } else if (strcasecmp(token, "des3") == 0) { + mod_list(ENCTYPE_DES3_CBC_SHA1, sel, weak, list, &count); + } else if (strcasecmp(token, "aes") == 0) { + mod_list(ENCTYPE_AES256_CTS_HMAC_SHA1_96, sel, weak, list, &count); + mod_list(ENCTYPE_AES128_CTS_HMAC_SHA1_96, sel, weak, list, &count); + } else if (strcasecmp(token, "rc4") == 0) { + mod_list(ENCTYPE_ARCFOUR_HMAC, sel, weak, list, &count); + } else if (krb5_string_to_enctype(token, &etype) == 0) { + /* Set a specific enctype. */ + mod_list(etype, sel, weak, list, &count); + } + } + + list[count] = 0; + return copy_enctypes(context, list, result); +} + +/* + * Set *etypes_ptr to a zero-terminated list of enctypes. ctx_list + * (containing application-specified enctypes) is used if non-NULL; + * otherwise the libdefaults profile string specified by profkey is + * used. default_list is the default enctype list to be used while + * parsing profile strings, and is also used if the profile string is + * not set. + */ static krb5_error_code -get_profile_etype_list(krb5_context context, krb5_enctype **ktypes, - char *profstr, krb5_enctype *ctx_list) +get_profile_etype_list(krb5_context context, krb5_enctype **etypes_ptr, + char *profkey, krb5_enctype *ctx_list, + krb5_enctype *default_list) { - krb5_enctype *old_ktypes; - krb5_enctype ktype; + krb5_enctype *etypes; krb5_error_code code; + char *profstr; + + *etypes_ptr = NULL; if (ctx_list) { - code = copy_enctypes(context, ctx_list, &old_ktypes); + /* Use application defaults. */ + code = copy_enctypes(context, ctx_list, &etypes); if (code) return code; } else { - /* - XXX - For now, we only support libdefaults - Perhaps this should be extended to allow for per-host / per-realm - session key types. - */ - - char *retval = NULL; - char *sp = NULL, *ep = NULL; - int i, j, count; - - code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS, profstr, - NULL, DEFAULT_ETYPE_LIST, &retval); + /* Parse profile setting, or "DEFAULT" if not specified. */ + code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS, + profkey, NULL, "DEFAULT", &profstr); + if (code) + return code; + code = krb5int_parse_enctype_list(context, profstr, default_list, + &etypes); + profile_release_string(profstr); if (code) return code; - - count = 0; - sp = retval; - while (*sp) { - for (ep = sp; *ep && (*ep != ',') && !isspace((int) (*ep)); ep++) - ; - if (*ep) { - *ep++ = '\0'; - while (isspace((int) (*ep)) || *ep == ',') - *ep++ = '\0'; - } - count++; - sp = ep; - } - - if ((old_ktypes = - (krb5_enctype *)malloc(sizeof(krb5_enctype) * (count + 1))) == - (krb5_enctype *) NULL) { - profile_release_string(retval); - return ENOMEM; - } - - sp = retval; - j = 0; - i = 1; - while (1) { - if (!krb5_string_to_enctype(sp, &ktype) && - (context->allow_weak_crypto || !krb5_c_weak_enctype(ktype))) { - old_ktypes[j] = ktype; - j++; - } - if (i++ >= count) - break; - - /* skip to next token */ - while (*sp) sp++; - while (! *sp) sp++; - } - - old_ktypes[j] = (krb5_enctype) 0; - profile_release_string(retval); } - if (old_ktypes[0] == 0) { - free (old_ktypes); - *ktypes = 0; + if (etypes[0] == 0) { + free(etypes); return KRB5_CONFIG_ETYPE_NOSUPP; } - *ktypes = old_ktypes; + *etypes_ptr = etypes; return 0; } krb5_error_code krb5_get_default_in_tkt_ktypes(krb5_context context, krb5_enctype **ktypes) { - return(get_profile_etype_list(context, ktypes, + return get_profile_etype_list(context, ktypes, KRB5_CONF_DEFAULT_TKT_ENCTYPES, - context->in_tkt_etypes)); + context->in_tkt_etypes, + default_enctype_list); } void @@ -447,11 +493,13 @@ krb5_get_tgs_ktypes(krb5_context context, krb5_const_principal princ, krb5_encty /* This one is set *only* by reading the config file; it's not set by the application. */ return get_profile_etype_list(context, ktypes, - KRB5_CONF_DEFAULT_TKT_ENCTYPES, NULL); + KRB5_CONF_DEFAULT_TKT_ENCTYPES, NULL, + default_enctype_list); else return get_profile_etype_list(context, ktypes, KRB5_CONF_DEFAULT_TGS_ENCTYPES, - context->tgs_etypes); + context->tgs_etypes, + default_enctype_list); } krb5_error_code KRB5_CALLCONV @@ -459,7 +507,7 @@ krb5_get_permitted_enctypes(krb5_context context, krb5_enctype **ktypes) { return get_profile_etype_list(context, ktypes, KRB5_CONF_PERMITTED_ENCTYPES, - context->tgs_etypes); + context->tgs_etypes, default_enctype_list); } krb5_boolean diff --git a/src/lib/krb5/krb/t_etypes.c b/src/lib/krb5/krb/t_etypes.c new file mode 100644 index 000000000..648a7eeb1 --- /dev/null +++ b/src/lib/krb5/krb/t_etypes.c @@ -0,0 +1,201 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * t_etypes.c -- test program for krb5int_parse_enctype_list + * + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include +#include "com_err.h" + +static struct { + const char *str; + krb5_enctype defaults[64]; + krb5_enctype expected_noweak[64]; + krb5_enctype expected[64]; +} tests[] = { + /* Empty string, unused default list */ + { "", + { ENCTYPE_DES_CBC_CRC, 0 }, + { 0 }, + { 0 } + }, + /* Single weak enctype */ + { "des-cbc-md4", + { 0 }, + { 0 }, + { ENCTYPE_DES_CBC_MD4, 0 } + }, + /* Single non-weak enctype */ + { "aes128-cts-hmac-sha1-96", + { 0 }, + { ENCTYPE_AES128_CTS_HMAC_SHA1_96, 0 }, + { ENCTYPE_AES128_CTS_HMAC_SHA1_96, 0 } + }, + /* Two enctypes, one an alias, one weak */ + { "rc4-hmac des-cbc-md5", + { 0 }, + { ENCTYPE_ARCFOUR_HMAC, 0 }, + { ENCTYPE_ARCFOUR_HMAC, ENCTYPE_DES_CBC_MD5, 0 } + }, + /* Three enctypes, all weak, case variation, funky separators */ + { " deS-HMac-shA1 , arCFour-hmaC-mD5-exp\tdeS3-Cbc-RAw\n", + { 0 }, + { 0 }, + { ENCTYPE_DES_HMAC_SHA1, ENCTYPE_ARCFOUR_HMAC_EXP, + ENCTYPE_DES3_CBC_RAW, 0 } + }, + /* Default set with enctypes added (one weak in each pair) */ + { "DEFAULT des-cbc-raw +des3-hmac-sha1", + { ENCTYPE_ARCFOUR_HMAC, ENCTYPE_ARCFOUR_HMAC_EXP, 0 }, + { ENCTYPE_ARCFOUR_HMAC, ENCTYPE_DES3_CBC_SHA1, 0 }, + { ENCTYPE_ARCFOUR_HMAC, ENCTYPE_ARCFOUR_HMAC_EXP, + ENCTYPE_DES_CBC_RAW, ENCTYPE_DES3_CBC_SHA1, 0 } + }, + /* Default set with enctypes removed */ + { "default -aes128-cts -des-hmac-sha1", + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96, + ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_HMAC_SHA1, 0 }, + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, 0 }, + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_DES_CBC_MD5, 0 } + }, + /* Family followed by enctype */ + { "aes des3-cbc-sha1-kd", + { 0 }, + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96, + ENCTYPE_DES3_CBC_SHA1, 0 }, + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96, + ENCTYPE_DES3_CBC_SHA1, 0 } + }, + /* Enctype followed by two families */ + { "+rc4-hmAC des3 +des", + { 0 }, + { ENCTYPE_ARCFOUR_HMAC, ENCTYPE_DES3_CBC_SHA1, 0 }, + { ENCTYPE_ARCFOUR_HMAC, ENCTYPE_DES3_CBC_SHA1, ENCTYPE_DES_CBC_CRC, + ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_CBC_MD4 } + }, + /* Default set with family added and enctype removed */ + { "DEFAULT +aes -arcfour-hmac-md5", + { ENCTYPE_ARCFOUR_HMAC, ENCTYPE_DES3_CBC_SHA1, ENCTYPE_DES_CBC_CRC, 0 }, + { ENCTYPE_DES3_CBC_SHA1, ENCTYPE_AES256_CTS_HMAC_SHA1_96, + ENCTYPE_AES128_CTS_HMAC_SHA1_96, 0 }, + { ENCTYPE_DES3_CBC_SHA1, ENCTYPE_DES_CBC_CRC, + ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96, 0 } + }, + /* Default set with families removed and enctypes added (one redundant) */ + { "DEFAULT -des -des3 rc4-hmac rc4-hmac-exp", + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96, + ENCTYPE_DES3_CBC_SHA1, ENCTYPE_ARCFOUR_HMAC, + ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_CBC_MD4, 0 }, + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96, + ENCTYPE_ARCFOUR_HMAC, 0 }, + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96, + ENCTYPE_ARCFOUR_HMAC, ENCTYPE_ARCFOUR_HMAC_EXP, 0 } + }, + /* Default set with family moved to front */ + { "des3 +DEFAULT", + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96, + ENCTYPE_DES3_CBC_SHA1, 0 }, + { ENCTYPE_DES3_CBC_SHA1, ENCTYPE_AES256_CTS_HMAC_SHA1_96, + ENCTYPE_AES128_CTS_HMAC_SHA1_96, 0 }, + { ENCTYPE_DES3_CBC_SHA1, ENCTYPE_AES256_CTS_HMAC_SHA1_96, + ENCTYPE_AES128_CTS_HMAC_SHA1_96, 0 } + }, + /* Two families with default set removed (exotic case), enctype added */ + { "aes +rc4 -DEFaulT des3-hmac-sha1", + { ENCTYPE_AES128_CTS_HMAC_SHA1_96, ENCTYPE_DES3_CBC_SHA1, + ENCTYPE_ARCFOUR_HMAC, 0 }, + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_DES3_CBC_SHA1, 0 }, + { ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_DES3_CBC_SHA1, 0 } + } +}; + +static void show_enctypes(krb5_context ctx, krb5_enctype *list) +{ + unsigned int i; + + for (i = 0; list[i]; i++) { + fprintf(stderr, "%d", (int) list[i]); + if (list[i + 1]) + fprintf(stderr, " "); + } + fprintf(stderr, "\n"); +} + +static void compare(krb5_context ctx, krb5_enctype *result, + krb5_enctype *expected, const char *profstr, + krb5_boolean weak) +{ + unsigned int i; + + for (i = 0; result[i]; i++) { + if (result[i] != expected[i]) + break; + } + if (!result[i] && !expected[i]) /* Success! */ + return; + fprintf(stderr, "Unexpected result while parsing: %s\n", profstr); + fprintf(stderr, "Expected: "); + show_enctypes(ctx, expected); + fprintf(stderr, "Result: "); + show_enctypes(ctx, result); + fprintf(stderr, "allow_weak_crypto was %s\n", weak ? "true" : "false"); + exit(1); +} + +int +main(int argc, char **argv) +{ + krb5_context ctx; + krb5_error_code ret; + krb5_enctype *list; + krb5_boolean weak; + unsigned int i; + char *copy; + + ret = krb5_init_context(&ctx); + if (ret) { + com_err("krb5_init_context", ret, ""); + return 2; + } + for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) { + for (weak = FALSE; weak <= TRUE; weak++) { + ctx->allow_weak_crypto = weak; + copy = strdup(tests[i].str); + ret = krb5int_parse_enctype_list(ctx, copy, tests[i].defaults, + &list); + if (ret) { + com_err("krb5int_parse_enctype_list", ret, ""); + return 2; + } + compare(ctx, list, + (weak) ? tests[i].expected : tests[i].expected_noweak, + tests[i].str, weak); + free(copy); + free(list); + } + } + return 0; +} +