From 7e4e06b87a09197816b0e1ae132e38dc30090574 Mon Sep 17 00:00:00 2001 From: Chaskiel Grundman Date: Sun, 17 Mar 2013 21:58:47 -0400 Subject: [PATCH] Derive DES/fcrypt session key from other key types If a kerberos 5 ticket has a session key with a non-DES enctype, use the NIST SP800-108 KDF in counter mode with HMAC_MD5 as the PRF to construct a DES key to be used by rxkad. To satisfy the requirements of the KDF, DES3 keys are first compressed into a 168 bit form by reversing the RFC3961 random-to-key algorithm Windows has three additional places to get tokens, who knew? Change-Id: I4dc8e83a641f9892b31c109fb9025251de3dcb27 --- src/WINNT/afsd/afskfw.c | 8 +- src/WINNT/aklog/aklog.c | 10 +- src/WINNT/netidmgr_plugin/afsfuncs.c | 11 +- src/aklog/aklog.c | 13 ++- src/libafsrpc/afsrpc.def | 1 + src/libafsrpc/libafsrpc.la.sym | 1 + src/rxkad/liboafs_rxkad.la.sym | 1 + src/rxkad/rxkad_prototypes.h | 6 + src/rxkad/ticket5.c | 159 ++++++++++++++++++++++++--- 9 files changed, 177 insertions(+), 33 deletions(-) diff --git a/src/WINNT/afsd/afskfw.c b/src/WINNT/afsd/afskfw.c index c37804af2..636b58b59 100644 --- a/src/WINNT/afsd/afskfw.c +++ b/src/WINNT/afsd/afskfw.c @@ -2631,8 +2631,6 @@ KFW_AFS_klog( increds.client = client_principal; increds.times.endtime = 0; - /* Ask for DES since that is what V4 understands */ - increds.session.keytype = ENCTYPE_DES_CBC_CRC; /* ALWAYS first try service/cell@CLIENT_REALM */ if (code = krb5_build_principal(context, &increds.server, @@ -2740,9 +2738,9 @@ KFW_AFS_klog( atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; atoken.startTime = k5creds->times.starttime; atoken.endTime = k5creds->times.endtime; - memcpy(&atoken.sessionKey, - k5creds->session.keyvalue.data, - k5creds->session.keyvalue.length); + if (tkt_DeriveDesKey(k5creds->session.keytype, k5creds->session.keyvalue.data, + k5creds->session.keyvalue.length, &atoken.sessionKey)) + goto cleanup; atoken.ticketLen = k5creds->ticket.length; memcpy(atoken.ticket, k5creds->ticket.data, atoken.ticketLen); diff --git a/src/WINNT/aklog/aklog.c b/src/WINNT/aklog/aklog.c index 9044899b0..55a7ccca2 100644 --- a/src/WINNT/aklog/aklog.c +++ b/src/WINNT/aklog/aklog.c @@ -512,7 +512,8 @@ static int get_v5cred(krb5_context context, increds.client = client_principal; increds.times.endtime = 0; /* Ask for DES since that is what V4 understands */ - increds.session.keytype = ENCTYPE_DES_CBC_CRC; + if (c != NULL) + increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds); if (r) { @@ -1001,7 +1002,12 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm) atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; atoken.startTime = v5cred->times.starttime; atoken.endTime = v5cred->times.endtime; - memcpy(&atoken.sessionKey, v5cred->session.keyvalue.data, v5cred->session.keyvalue.length); + if (tkt_DeriveDesKey(v5cred->session.keytype, + v5cred->session.keyvalue.data, + v5cred->session.keyvalue.length, &atoken.sessionKey)) { + status = AKLOG_MISC; + goto done; + } atoken.ticketLen = v5cred->ticket.length; memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen); } else { diff --git a/src/WINNT/netidmgr_plugin/afsfuncs.c b/src/WINNT/netidmgr_plugin/afsfuncs.c index 5dc099194..db9b72550 100644 --- a/src/WINNT/netidmgr_plugin/afsfuncs.c +++ b/src/WINNT/netidmgr_plugin/afsfuncs.c @@ -876,7 +876,8 @@ afs_klog(khm_handle identity, increds.client = client_principal; increds.times.endtime = 0; /* Ask for DES since that is what V4 understands */ - increds.session.keytype = ENCTYPE_DES_CBC_CRC; + if (method == AFS_TOKEN_KRB524) + increds.session.keytype = ENCTYPE_DES_CBC_CRC; #ifdef KRB5_TC_NOTICKET flags = KRB5_TC_OPENCLOSE; @@ -1060,9 +1061,11 @@ afs_klog(khm_handle identity, atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; atoken.startTime = k5creds->times.starttime; atoken.endTime = k5creds->times.endtime; - memcpy(&atoken.sessionKey, - k5creds->session.keyvalue.data, - k5creds->session.keyvalue.length); + if (tkt_DeriveDesKey(k5creds->session.keytype, + k5creds->session.keyvalue.data, + k5creds->session.keyvalue.length, + &atoken.sessionKey)) + goto cleanup; atoken.ticketLen = k5creds->ticket.length; memcpy(atoken.ticket, k5creds->ticket.data, atoken.ticketLen); diff --git a/src/aklog/aklog.c b/src/aklog/aklog.c index 9e5b811f8..b5c0108ef 100644 --- a/src/aklog/aklog.c +++ b/src/aklog/aklog.c @@ -669,6 +669,8 @@ rxkad_build_native_token(krb5_context context, krb5_creds *v5cred, char k4inst[INST_SZ]; char k4realm[REALM_SZ]; #endif + void *inkey = get_cred_keydata(v5cred); + size_t inkey_sz = get_cred_keylen(v5cred); afs_dprintf("Using Kerberos V5 ticket natively\n"); @@ -712,8 +714,10 @@ rxkad_build_native_token(krb5_context context, krb5_creds *v5cred, token.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; token.startTime = v5cred->times.starttime;; token.endTime = v5cred->times.endtime; - memcpy(&token.sessionKey, get_cred_keydata(v5cred), - get_cred_keylen(v5cred)); + if (tkt_DeriveDesKey(get_creds_enctype(v5cred), inkey, inkey_sz, + &token.sessionKey) != 0) { + return RXKADBADKEY; + } token.ticketLen = v5cred->ticket.length; memcpy(token.ticket, v5cred->ticket.data, token.ticketLen); @@ -2124,8 +2128,9 @@ get_credv5(krb5_context context, char *name, char *inst, char *realm, increds.client = client_principal; increds.times.endtime = 0; - /* Ask for DES since that is what V4 understands */ - get_creds_enctype((&increds)) = ENCTYPE_DES_CBC_CRC; + if (do524) + /* Ask for DES since that is what V4 understands */ + get_creds_enctype((&increds)) = ENCTYPE_DES_CBC_CRC; if (keytab) { int allowed_enctypes[] = { diff --git a/src/libafsrpc/afsrpc.def b/src/libafsrpc/afsrpc.def index 5772ad0ea..d7beb72ea 100755 --- a/src/libafsrpc/afsrpc.def +++ b/src/libafsrpc/afsrpc.def @@ -342,6 +342,7 @@ EXPORTS afs_set_com_err_hook @347 rxkad_NewKrb5ServerSecurityObject @348 tkt_MakeTicket5 @349 + tkt_DeriveDesKey @350 ; for performance testing rx_TSFPQGlobSize @2001 DATA diff --git a/src/libafsrpc/libafsrpc.la.sym b/src/libafsrpc/libafsrpc.la.sym index d1b9c4608..763611626 100644 --- a/src/libafsrpc/libafsrpc.la.sym +++ b/src/libafsrpc/libafsrpc.la.sym @@ -164,6 +164,7 @@ rxs_Release time_to_life tkt_CheckTimes tkt_DecodeTicket +tkt_DeriveDesKey tkt_MakeTicket tkt_MakeTicket5 xdr_afsUUID diff --git a/src/rxkad/liboafs_rxkad.la.sym b/src/rxkad/liboafs_rxkad.la.sym index 4933f9b28..cfcc5850e 100644 --- a/src/rxkad/liboafs_rxkad.la.sym +++ b/src/rxkad/liboafs_rxkad.la.sym @@ -6,5 +6,6 @@ rxkad_NewKrb5ServerSecurityObject rxkad_NewServerSecurityObject time_to_life tkt_CheckTimes +tkt_DeriveDesKey tkt_MakeTicket tkt_MakeTicket5 diff --git a/src/rxkad/rxkad_prototypes.h b/src/rxkad/rxkad_prototypes.h index 525b9d9af..a13c9fe4b 100644 --- a/src/rxkad/rxkad_prototypes.h +++ b/src/rxkad/rxkad_prototypes.h @@ -167,5 +167,11 @@ extern int tkt_MakeTicket5(char *ticket, int *ticketLen, int enctype, int *kvno, char *name, char *inst, char *cell, afs_uint32 start, afs_uint32 end, struct ktc_encryptionKey *sessionKey, char *sname, char *sinst); +/* + * Compute a des key from a key of a semi-arbitrary kerberos 5 enctype. + * Modifies keydata if enctype is 3des. + */ +extern int tkt_DeriveDesKey(int enctype, void *keydata, size_t keylen, struct ktc_encryptionKey + *output); #endif diff --git a/src/rxkad/ticket5.c b/src/rxkad/ticket5.c index 1dad81a65..f875cd068 100644 --- a/src/rxkad/ticket5.c +++ b/src/rxkad/ticket5.c @@ -67,9 +67,11 @@ #include #include +#define HC_DEPRECATED_CRYPTO #include #include #include +#include #include "lifetimes.h" #include "rxkad.h" @@ -173,9 +175,9 @@ static const struct krb_convert sconv_list[] = { static int krb5_des_decrypt(struct ktc_encryptionKey *, int, void *, size_t, void *, size_t *); - - - +static int rxkad_derive_des_key(const void *, size_t, + struct ktc_encryptionKey *); +static int compress_parity_bits(void *, size_t *); int tkt_DecodeTicket5(char *ticket, afs_int32 ticket_len, @@ -374,21 +376,9 @@ tkt_DecodeTicket5(char *ticket, afs_int32 ticket_len, } /* Verify that decr_part.key is of right type */ - switch (decr_part.key.keytype) { - case ETYPE_DES_CBC_CRC: - case ETYPE_DES_CBC_MD4: - case ETYPE_DES_CBC_MD5: - break; - default: - goto bad_ticket; - } - - if (decr_part.key.keyvalue.length != 8) + if (tkt_DeriveDesKey(decr_part.key.keytype, decr_part.key.keyvalue.data, + decr_part.key.keyvalue.length, session_key) != 0) goto bad_ticket; - - /* Extract session key */ - memcpy(session_key, decr_part.key.keyvalue.data, 8); - /* Check lifetimes and host addresses, flags etc */ { time_t now = time(0); /* Use fast time package instead??? */ @@ -537,7 +527,6 @@ krb5_des_decrypt(struct ktc_encryptionKey *key, int etype, void *in, return ret; } - int tkt_MakeTicket5(char *ticket, int *ticketLen, int enctype, int *kvno, void *key, size_t keylen, @@ -632,3 +621,137 @@ cleanup: return RXKADINCONSISTENCY; return code; } + +/* + * Use NIST SP800-108 with HMAC(MD5) in counter mode as the PRF to derive a + * des key from another type of key. + * + * L is 64, as we take 64 random bits and turn them into a 56-bit des key. + * The output of hmac_md5 is 128 bits; we take the first 64 only, so n + * properly should be 1. However, we apply a slight variation due to the + * possibility of producing a weak des key. If the output key is weak, do NOT + * simply correct it, instead, the counter is advanced and the next output + * used. As such, we code so as to have n be the full 255 permitted by our + * encoding of the counter i in an 8-bit field. L itself is encoded as a + * 32-bit field, big-endian. We use the constant string "rxkad" as a label + * for this key derivation, the standard NUL byte separator, and omit a + * key-derivation context. The input key is unique to the krb5 service ticket, + * which is unlikely to be used in an other location. If it is used in such + * a fashion, both locations will derive the same des key from the PRF, but + * this is no different from if a krb5 des key had been used in the same way, + * as traditional krb5 rxkad uses the ticket session key directly as the token + * key. + */ +static int +rxkad_derive_des_key(const void *in, size_t insize, + struct ktc_encryptionKey *out) +{ + unsigned char i; + char Lbuf[4]; /* bits of output, as 32 bit word, MSB first */ + char tmp[64]; /* only needs to be 16 for md5, but lets be sure it fits */ + unsigned int mdsize; + DES_cblock ktmp; + HMAC_CTX mctx; + + Lbuf[0] = 0; + Lbuf[1] = 0; + Lbuf[2] = 0; + Lbuf[3] = 64; + + /* stop when 8 bit counter wraps to 0 */ + for (i = 1; i; i++) { + HMAC_CTX_init(&mctx); + HMAC_Init_ex(&mctx, in, insize, EVP_md5(), NULL); + HMAC_Update(&mctx, &i, 1); + HMAC_Update(&mctx, "rxkad", strlen("rxkad") + 1); /* includes label and separator */ + HMAC_Update(&mctx, Lbuf, 4); + mdsize = sizeof(tmp); + HMAC_Final(&mctx, tmp, &mdsize); + memcpy(ktmp, tmp, 8); + DES_set_odd_parity(&ktmp); + if (!DES_is_weak_key(&ktmp)) { + memcpy(out->data, ktmp, 8); + return 0; + } + } + return -1; +} + +/* + * This is the inverse of the random-to-key for 3des specified in + * rfc3961, converting blocks of 8 bytes to blocks of 7 bytes by distributing + * the bits of each 8th byte as the lsb of the previous 7 bytes. + */ +static int +compress_parity_bits(void *buffer, size_t *bufsiz) +{ + unsigned char *cb, tmp; + int i, j, nk; + + if (*bufsiz % 8 != 0) + return 1; + cb = (unsigned char *)buffer; + nk = *bufsiz / 8; + for (i = 0; i < nk; i++) { + tmp = cb[8 * i + 7] >> 1; + for (j = 0; j < 7; j++) { + cb[8 * i + j] &= 0xfe; + cb[8 * i + j] |= tmp & 0x1; + tmp >>= 1; + } + } + for (i = 1; i < nk; i++) + memmove(cb + 7 * i, cb + 8 * i, 7); + *bufsiz = 7 * nk; + return 0; +} + +/* + * Enctype-specific knowledge about how to derive a des key from a given + * key. If given a des key, use it directly; otherwise, perform any + * parity fixup that may be needed and pass through to the hmad-md5 bits. + */ +int +tkt_DeriveDesKey(int enctype, void *keydata, size_t keylen, + struct ktc_encryptionKey *output) +{ + switch (enctype) { + case ETYPE_DES_CBC_CRC: + case ETYPE_DES_CBC_MD4: + case ETYPE_DES_CBC_MD5: + if (keylen != 8) + return 1; + + /* Extract session key */ + memcpy(output, keydata, 8); + break; + case ETYPE_NULL: + case 4: + case 6: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + return 1; + /*In order to become a "Cryptographic Key" as specified in + * SP800-108, it must be indistinguishable from a random bitstring. */ + case ETYPE_DES3_CBC_MD5: + case ETYPE_OLD_DES3_CBC_SHA1: + case ETYPE_DES3_CBC_SHA1: + if (compress_parity_bits(keydata, &keylen)) + return 1; + /* FALLTHROUGH */ + default: + if (enctype < 0) + return 1; + if (keylen < 7) + return 1; + if (rxkad_derive_des_key(keydata, keylen, output) != 0) + return 1; + } + return 0; +} -- 2.39.5