From 9d511ea37d6b6953bdd8322e6581d5376250e5c8 Mon Sep 17 00:00:00 2001 From: Jeffrey Hutzelman Date: Thu, 21 Aug 2014 13:05:59 -0400 Subject: [PATCH] libkafs: derivation from non-DES key (rxkad-kdf) Add support for the "rxkad-kdf" protocol for deriving rxkad session keys from non-DES Kerberos session keys. This allows rxkad to be used in realms where the KDC is unwilling or unable to issue tickets with single-DES session keys. --- lib/kafs/Makefile.am | 3 +- lib/kafs/afskrb5.c | 26 +++---- lib/kafs/kafs_locl.h | 3 + lib/kafs/rxkad_kdf.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 233 insertions(+), 15 deletions(-) create mode 100644 lib/kafs/rxkad_kdf.c diff --git a/lib/kafs/Makefile.am b/lib/kafs/Makefile.am index 34a6144c7..8c1983ef7 100644 --- a/lib/kafs/Makefile.am +++ b/lib/kafs/Makefile.am @@ -5,7 +5,7 @@ include $(top_srcdir)/Makefile.am.common AM_CPPFLAGS += $(AFS_EXTRA_DEFS) $(ROKEN_RENAME) if KRB5 -DEPLIB_krb5 = ../krb5/libkrb5.la +DEPLIB_krb5 = ../krb5/libkrb5.la $(LIB_hcrypto) krb5_am_workaround = $(INCLUDE_hcrypto) -I$(top_srcdir)/lib/krb5 else DEPLIB_krb5 = @@ -61,6 +61,7 @@ endif dist_libkafs_la_SOURCES = \ afssys.c \ afskrb5.c \ + rxkad_kdf.c \ common.c \ $(AIX_SRC) \ kafs_locl.h \ diff --git a/lib/kafs/afskrb5.c b/lib/kafs/afskrb5.c index c04f43abb..6033f2958 100644 --- a/lib/kafs/afskrb5.c +++ b/lib/kafs/afskrb5.c @@ -51,10 +51,6 @@ v5_to_kt(krb5_creds *cred, uid_t uid, struct kafs_token *kt, int local524) kt->ticket = NULL; - /* check if des key */ - if (cred->session.keyvalue.length != 8) - return EINVAL; - if (local524) { Ticket t; unsigned char *buf; @@ -98,8 +94,16 @@ v5_to_kt(krb5_creds *cred, uid_t uid, struct kafs_token *kt, int local524) * Build a struct ClearToken */ + ret = _kafs_derive_des_key(cred->session.keytype, + cred->session.keyvalue.data, + cred->session.keyvalue.length, + kt->ct.HandShakeKey); + if (ret) { + free(kt->ticket); + kt->ticket = NULL; + return ret; + } kt->ct.AuthHandle = kvno; - memcpy(kt->ct.HandShakeKey, cred->session.keyvalue.data, 8); kt->ct.ViceId = uid; kt->ct.BeginTimestamp = cred->times.starttime; kt->ct.EndTimestamp = cred->times.endtime; @@ -163,21 +167,15 @@ get_cred(struct kafs_data *data, const char *name, const char *inst, return ret; } - in_creds.session.keytype = ETYPE_DES_CBC_CRC; - /* check if des is disable, and in that case enable it for afs */ - invalid = krb5_enctype_valid(d->context, in_creds.session.keytype); + invalid = krb5_enctype_valid(d->context, ETYPE_DES_CBC_CRC); if (invalid) - krb5_enctype_enable(d->context, in_creds.session.keytype); + krb5_enctype_enable(d->context, ETYPE_DES_CBC_CRC); ret = krb5_get_credentials(d->context, 0, d->id, &in_creds, &out_creds); - if (ret) { - in_creds.session.keytype = ETYPE_DES_CBC_MD5; - ret = krb5_get_credentials(d->context, 0, d->id, &in_creds, &out_creds); - } if (invalid) - krb5_enctype_disable(d->context, in_creds.session.keytype); + krb5_enctype_disable(d->context, ETYPE_DES_CBC_CRC); krb5_free_principal(d->context, in_creds.server); krb5_free_principal(d->context, in_creds.client); diff --git a/lib/kafs/kafs_locl.h b/lib/kafs/kafs_locl.h index ea670f313..a028d8ea8 100644 --- a/lib/kafs/kafs_locl.h +++ b/lib/kafs/kafs_locl.h @@ -150,6 +150,9 @@ _kafs_v4_to_kt(CREDENTIALS *, uid_t, struct kafs_token *); void _kafs_fixup_viceid(struct ClearToken *, uid_t); +int +_kafs_derive_des_key(krb5_enctype, void *, size_t, char[8]); + #ifdef _AIX int aix_pioctl(char*, int, struct ViceIoctl*, int); int aix_setpag(void); diff --git a/lib/kafs/rxkad_kdf.c b/lib/kafs/rxkad_kdf.c new file mode 100644 index 000000000..61bb0f4b8 --- /dev/null +++ b/lib/kafs/rxkad_kdf.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 1995-2003 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2013-2014 Carnegie Mellon University + * All rights reserved. + * + * Portions Copyright (c) 2013 by the Massachusetts Institute of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kafs_locl.h" + +static int rxkad_derive_des_key(const void *, size_t, char[8]); +static int compress_parity_bits(void *, size_t *); + +/** + * 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. + * + * @param[in] in pointer to input key data + * @param[in] insize length of input key data + * @param[out] out 8-byte buffer to hold the derived key + * + * @return Returns 0 to indicate success, or an error code. + * + * @retval KRB5DES_WEAK_KEY Successive derivation attempts with all + * 255 possible counter values each produced weak DES keys. This input + * cannot be used to produce a usable key. + */ +static int +rxkad_derive_des_key(const void *in, size_t insize, char out[8]) +{ + unsigned char i; + static unsigned char label[] = "rxkad"; + /* bits of output, as 32 bit word, MSB first */ + static unsigned char Lbuf[4] = { 0, 0, 0, 64 }; + /* only needs to be 16 for md5, but lets be sure it fits */ + unsigned char tmp[64]; + unsigned int mdsize; + DES_cblock ktmp; + HMAC_CTX mctx; + + /* 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, label, sizeof(label)); /* 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, ktmp, 8); + return 0; + } + } + return KRB5DES_WEAK_KEY; +} + +/** + * 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. + * + * @param[in,out] buffer Buffer containing the key to be converted + * @param[in,out] bufsiz Points to the size of the key data. On + * return, this is updated to reflect the size of the compressed data. + * + * @return Returns 0 to indicate success, or an error code. + * + * @retval KRB5_BAD_KEYSIZE The key size was not a multiple of 8 bytes. + */ +static int +compress_parity_bits(void *buffer, size_t *bufsiz) +{ + unsigned char *cb, tmp; + int i, j, nk; + + if (*bufsiz % 8 != 0) + return KRB5_BAD_KEYSIZE; + 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; +} + +/** + * Derive a DES key for use with rxkad and fcrypt from a given Kerberos + * key of (almost) any type. This function encodes enctype-specific + * knowledge about how to derive a DES key from a given key type. + * 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. + * + * @param[in] enctype Kerberos enctype of the input key + * @param[in] keydata Input key data + * @param[in] keylen Size of input key data + * @param[out] output 8-byte buffer to hold the derived key + * + * @return Returns 0 to indicate success, or an error code. + * + * @retval KRB5_PROG_ETYPE_NOSUPP The enctype is one for which rxkad-kdf + * is not supported. This includes several reserved enctypes, enctype + * values used in PKINIT to stand for CMS algorithm identifiers, and all + * private-use (negative) enctypes. + * + * @retval KRB5_BAD_KEYSIZE The key size was not a multiple of 8 bytes + * (for 3DES key types), exactly 8 bytes (for DES key types), or at least + * 8 bytes (for other key types). + * + * @retval KRB5DES_WEAK_KEY Successive derivation attempts with all + * 255 possible counter values each produced weak DES keys. This input + * cannot be used to produce a usable key. + */ +int +_kafs_derive_des_key(krb5_enctype enctype, void *keydata, size_t keylen, + char output[8]) +{ + int ret = 0; + + switch (enctype) { + case ETYPE_DES_CBC_CRC: + case ETYPE_DES_CBC_MD4: + case ETYPE_DES_CBC_MD5: + if (keylen != 8) + return KRB5_BAD_KEYSIZE; + + /* 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 KRB5_PROG_ETYPE_NOSUPP; + /*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: + ret = compress_parity_bits(keydata, &keylen); + if (ret) + return ret; + /* FALLTHROUGH */ + default: + if (enctype < 0) + return KRB5_PROG_ETYPE_NOSUPP; + if (keylen < 7) + return KRB5_BAD_KEYSIZE; + ret = rxkad_derive_des_key(keydata, keylen, output); + } + return ret; +} -- 2.11.4.GIT