/* * Copyright (c) 1995, 1996, 1997, 2002 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * 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. */ /* * Copyright 1992, 2002 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 #include #include #include #ifdef IGNORE_SOME_GCC_WARNINGS # pragma GCC diagnostic warning "-Wimplicit-function-declaration" #endif #include #include #define HC_DEPRECATED_CRYPTO #include #include #include #include #include "lifetimes.h" #include "rxkad.h" #include "rxkad_convert.h" #include "v5gen-rewrite.h" #include "v5gen.h" #include "der.h" #include #if defined(IGNORE_SOME_GCC_WARNINGS) && !defined(__clang__) && __GNUC__ >= 7 # pragma GCC diagnostic push # pragma GCC diagnostic warning "-Wformat-truncation" #endif #include "v5der.c" #if defined(IGNORE_SOME_GCC_WARNINGS) && !defined(__clang__) && __GNUC__ >= 7 # pragma GCC diagnostic pop #endif #include "v5gen.c" #define RFC3961_NO_ENUMS #define RFC3961_NO_CKSUM #include /* * Principal conversion Taken from src/lib/krb5/krb/conv_princ from MIT Kerberos. If you * find a need to change the services here, please consider opening a * bug with MIT by sending mail to krb5-bugs@mit.edu. */ struct krb_convert { char *v4_str; char *v5_str; unsigned int flags; unsigned int len; }; #define DO_REALM_CONVERSION 0x00000001 /* * Kadmin doesn't do realm conversion because it's currently * kadmin/REALM.NAME. Zephyr doesn't because it's just zephyr/zephyr. * * "Realm conversion" is a bit of a misnomer; really, the v5 name is * using a FQDN or something that looks like it, where the v4 name is * just using the first label. Sometimes that second principal name * component is a hostname, sometimes the realm name, sometimes it's * neither. * * This list should probably be more configurable, and more than * likely on a per-realm basis, so locally-defined services can be * added, or not. */ static const struct krb_convert sconv_list[] = { /* Realm conversion, Change service name */ #define RC(V5NAME,V4NAME) { V5NAME, V4NAME, DO_REALM_CONVERSION, sizeof(V5NAME)-1 } /* Realm conversion */ #define R(NAME) { NAME, NAME, DO_REALM_CONVERSION, sizeof(NAME)-1 } /* No Realm conversion */ #define NR(NAME) { NAME, NAME, 0, sizeof(NAME)-1 } NR("kadmin"), RC("rcmd", "host"), R("discuss"), R("rvdsrv"), R("sample"), R("olc"), R("pop"), R("sis"), R("rfs"), R("imap"), R("ftp"), R("ecat"), R("daemon"), R("gnats"), R("moira"), R("prms"), R("mandarin"), R("register"), R("changepw"), R("sms"), R("afpserver"), R("gdss"), R("news"), R("abs"), R("nfs"), R("tftp"), NR("zephyr"), R("http"), R("khttp"), R("pgpsigner"), R("irc"), R("mandarin-agent"), R("write"), R("palladium"), R("imap"), R("smtp"), R("lmtp"), R("ldap"), R("acap"), R("argus"), R("mupdate"), R("argus"), {0, 0, 0, 0}, #undef R #undef RC #undef NR }; 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, int (*get_key) (void *, int, struct ktc_encryptionKey *), rxkad_get_key_enctype_func get_key_enctype, char *get_key_rock, int serv_kvno, char *name, char *inst, char *cell, struct ktc_encryptionKey *session_key, afs_int32 * host, afs_uint32 * start, afs_uint32 * end, afs_int32 disableCheckdot) { char plain[MAXKRB5TICKETLEN]; struct ktc_encryptionKey serv_key; void *keybuf; size_t keysize, allocsiz; krb5_context context; krb5_keyblock k; krb5_crypto cr; krb5_data plaindata; Ticket t5; /* Must free */ EncTicketPart decr_part; /* Must free */ int code; size_t siz, plainsiz = 0; int v5_serv_kvno; char *v5_comp0, *v5_comp1, *c; const struct krb_convert *p; memset(&t5, 0, sizeof(t5)); memset(&decr_part, 0, sizeof(decr_part)); *host = 0; if (ticket_len == 0) return RXKADBADTICKET; /* no ticket */ if (serv_kvno == RXKAD_TKT_TYPE_KERBEROS_V5) { code = decode_Ticket((unsigned char *)ticket, ticket_len, &t5, &siz); if (code != 0) goto cleanup; if (t5.tkt_vno != 5) goto bad_ticket; } else { code = decode_EncryptedData((unsigned char *)ticket, ticket_len, &t5.enc_part, &siz); if (code != 0) goto cleanup; } /* If kvno is null, it's probably not included because it was kvno==0 * in the ticket */ if (t5.enc_part.kvno == NULL) { v5_serv_kvno = 0; } else { v5_serv_kvno = *t5.enc_part.kvno; } /* Check that the key type really fit into 8 bytes */ switch (t5.enc_part.etype) { case KRB5_ENCTYPE_DES_CBC_CRC: case KRB5_ENCTYPE_DES_CBC_MD4: case KRB5_ENCTYPE_DES_CBC_MD5: /* check ticket */ if (t5.enc_part.cipher.length > sizeof(plain) || t5.enc_part.cipher.length % 8 != 0) goto bad_ticket; code = (*get_key) (get_key_rock, v5_serv_kvno, &serv_key); if (code) goto unknown_key; /* Decrypt data here, save in plain, assume it will shrink */ code = krb5_des_decrypt(&serv_key, t5.enc_part.etype, t5.enc_part.cipher.data, t5.enc_part.cipher.length, plain, &plainsiz); break; default: if (get_key_enctype == NULL) goto unknown_key; code = krb5_init_context(&context); if (code != 0) goto unknown_key; code = krb5_enctype_valid(context, t5.enc_part.etype); if (code != 0) { krb5_free_context(context); goto unknown_key; } code = krb5_enctype_keysize(context, t5.enc_part.etype, &keysize); if (code != 0) { krb5_free_context(context); goto unknown_key; } allocsiz = keysize; keybuf = rxi_Alloc(allocsiz); /* this is not quite a hole for afsconf_GetKeyByTypes. A wrapper that calls afsconf_GetKeyByTypes and afsconf_typedKey_values is needed */ code = get_key_enctype(get_key_rock, v5_serv_kvno, t5.enc_part.etype, keybuf, &keysize); if (code) { rxi_Free(keybuf, allocsiz); krb5_free_context(context); goto unknown_key; } code = krb5_keyblock_init(context, t5.enc_part.etype, keybuf, keysize, &k); rxi_Free(keybuf, allocsiz); if (code != 0) { krb5_free_context(context); goto unknown_key; } code = krb5_crypto_init(context, &k, t5.enc_part.etype, &cr); krb5_free_keyblock_contents(context, &k); if (code != 0) { krb5_free_context(context); goto unknown_key; } #ifndef KRB5_KU_TICKET #define KRB5_KU_TICKET 2 #endif code = krb5_decrypt(context, cr, KRB5_KU_TICKET, t5.enc_part.cipher.data, t5.enc_part.cipher.length, &plaindata); krb5_crypto_destroy(context, cr); if (code == 0) { if (plaindata.length > MAXKRB5TICKETLEN) { krb5_data_free(&plaindata); krb5_free_context(context); goto bad_ticket; } memcpy(plain, plaindata.data, plaindata.length); plainsiz = plaindata.length; krb5_data_free(&plaindata); } krb5_free_context(context); } if (code != 0) goto bad_ticket; /* Decode ticket */ code = decode_EncTicketPart((unsigned char *)plain, plainsiz, &decr_part, &siz); if (code != 0) goto bad_ticket; /* Extract realm and principal */ strncpy(cell, decr_part.crealm, MAXKTCNAMELEN); cell[MAXKTCNAMELEN - 1] = '\0'; inst[0] = '\0'; switch (decr_part.cname.name_string.len) { case 2: v5_comp0 = decr_part.cname.name_string.val[0]; v5_comp1 = decr_part.cname.name_string.val[1]; p = sconv_list; while (p->v4_str) { if (strcmp(p->v5_str, v5_comp0) == 0) { /* * It is, so set the new name now, and chop off * instance's domain name if requested. */ strncpy(name, p->v4_str, MAXKTCNAMELEN); name[MAXKTCNAMELEN - 1] = '\0'; if (p->flags & DO_REALM_CONVERSION) { c = strchr(v5_comp1, '.'); if (!c || (c - v5_comp1) >= MAXKTCNAMELEN - 1) goto bad_ticket; strncpy(inst, v5_comp1, c - v5_comp1); inst[c - v5_comp1] = '\0'; } break; } p++; } if (!p->v4_str) { strncpy(inst, decr_part.cname.name_string.val[1], MAXKTCNAMELEN); inst[MAXKTCNAMELEN - 1] = '\0'; strncpy(name, decr_part.cname.name_string.val[0], MAXKTCNAMELEN); name[MAXKTCNAMELEN - 1] = '\0'; } break; case 1: strncpy(name, decr_part.cname.name_string.val[0], MAXKTCNAMELEN); name[MAXKTCNAMELEN - 1] = '\0'; break; default: goto bad_ticket; } if (!disableCheckdot) { /* * If the first part of the name_string contains a dot, punt since * then we can't see the diffrence between the kerberos 5 * principals foo.root and foo/root later in the fileserver. */ if (strchr(decr_part.cname.name_string.val[0], '.') != NULL) goto bad_ticket; } /* Verify that decr_part.key is of right type */ if (tkt_DeriveDesKey(decr_part.key.keytype, decr_part.key.keyvalue.data, decr_part.key.keyvalue.length, session_key) != 0) goto bad_ticket; /* Check lifetimes and host addresses, flags etc */ { time_t now = time(0); /* Use fast time package instead??? */ *start = decr_part.authtime; if (decr_part.starttime) *start = *decr_part.starttime; if (decr_part.flags.invalid) goto no_auth; if (now > decr_part.endtime) goto tkt_expired; *end = decr_part.endtime; } cleanup: if (serv_kvno == RXKAD_TKT_TYPE_KERBEROS_V5) free_Ticket(&t5); else free_EncryptedData(&t5.enc_part); free_EncTicketPart(&decr_part); memset(&serv_key, 0, sizeof(serv_key)); return code; unknown_key: code = RXKADUNKNOWNKEY; goto cleanup; no_auth: code = RXKADNOAUTH; goto cleanup; tkt_expired: code = RXKADEXPIRED; goto cleanup; bad_ticket: code = RXKADBADTICKET; goto cleanup; } static int verify_checksum_md4(void *data, size_t len, void *cksum, size_t cksumsz, struct ktc_encryptionKey *key) { MD4_CTX md4; unsigned char tmp[16]; MD4_Init(&md4); MD4_Update(&md4, data, len); MD4_Final(tmp, &md4); if (memcmp(tmp, cksum, cksumsz) != 0) return 1; return 0; } static int verify_checksum_md5(void *data, size_t len, void *cksum, size_t cksumsz, struct ktc_encryptionKey *key) { MD5_CTX md5; unsigned char tmp[16]; MD5_Init(&md5); MD5_Update(&md5, data, len); MD5_Final(tmp, &md5); if (memcmp(tmp, cksum, cksumsz) != 0) return 1; return 0; } static int verify_checksum_crc(void *data, size_t len, void *cksum, size_t cksumsz, struct ktc_encryptionKey *key) { afs_uint32 crc; char r[4]; _rxkad_crc_init_table(); crc = _rxkad_crc_update(data, len, 0); r[0] = crc & 0xff; r[1] = (crc >> 8) & 0xff; r[2] = (crc >> 16) & 0xff; r[3] = (crc >> 24) & 0xff; if (memcmp(cksum, r, 4) != 0) return 1; return 0; } static int krb5_des_decrypt(struct ktc_encryptionKey *key, int etype, void *in, size_t insz, void *out, size_t * outsz) { int (*cksum_func) (void *, size_t, void *, size_t, struct ktc_encryptionKey *); DES_cblock ivec; DES_key_schedule s; char cksum[24]; size_t cksumsz; int ret = 1; /* failure */ cksum_func = NULL; DES_key_sched(ktc_to_cblock(key), &s); #define CONFOUNDERSZ 8 switch (etype) { case KRB5_ENCTYPE_DES_CBC_CRC: memcpy(&ivec, key, sizeof(ivec)); cksumsz = 4; cksum_func = verify_checksum_crc; break; case KRB5_ENCTYPE_DES_CBC_MD4: memset(&ivec, 0, sizeof(ivec)); cksumsz = 16; cksum_func = verify_checksum_md4; break; case KRB5_ENCTYPE_DES_CBC_MD5: memset(&ivec, 0, sizeof(ivec)); cksumsz = 16; cksum_func = verify_checksum_md5; break; default: abort(); } DES_cbc_encrypt(in, out, insz, &s, &ivec, 0); memcpy(cksum, (char *)out + CONFOUNDERSZ, cksumsz); memset((char *)out + CONFOUNDERSZ, 0, cksumsz); if (cksum_func) ret = (*cksum_func) (out, insz, cksum, cksumsz, key); *outsz = insz - CONFOUNDERSZ - cksumsz; memmove(out, (char *)out + CONFOUNDERSZ + cksumsz, *outsz); return ret; } int tkt_MakeTicket5(char *ticket, int *ticketLen, int enctype, int *kvno, void *key, size_t keylen, char *name, char *inst, char *cell, afs_uint32 start, afs_uint32 end, struct ktc_encryptionKey *sessionKey, char *sname, char *sinst) { EncTicketPart data; EncryptedData encdata; unsigned char *buf, *encodebuf; size_t encodelen, allocsiz; heim_general_string carray[2]; int code; krb5_context context; krb5_keyblock kb; krb5_crypto cr; krb5_data encrypted; size_t tl; memset(&encrypted, 0, sizeof(encrypted)); cr = NULL; context = NULL; buf = NULL; memset(&kb, 0, sizeof(kb)); memset(&data, 0, sizeof(data)); data.flags.transited_policy_checked = 1; data.key.keytype=KRB5_ENCTYPE_DES_CBC_CRC; data.key.keyvalue.data=sessionKey->data; data.key.keyvalue.length=8; data.crealm=cell; carray[0]=name; carray[1]=inst; data.cname.name_type=KRB5_NT_PRINCIPAL; data.cname.name_string.val=carray; data.cname.name_string.len=inst[0]?2:1; data.authtime=start; data.endtime=end; allocsiz = length_EncTicketPart(&data); buf = rxi_Alloc(allocsiz); encodelen = allocsiz; /* encode function wants pointer to end of buffer */ encodebuf = buf + allocsiz - 1; code = encode_EncTicketPart(encodebuf, allocsiz, &data, &encodelen); if (code) goto cleanup; code = krb5_init_context(&context); if (code) goto cleanup; code = krb5_keyblock_init(context, enctype, key, keylen, &kb); if (code) goto cleanup; code = krb5_crypto_init(context, &kb, enctype, &cr); if (code) goto cleanup; code = krb5_encrypt(context, cr, KRB5_KU_TICKET, buf, encodelen, &encrypted); if (code) goto cleanup; memset(&encdata, 0, sizeof(encdata)); encdata.etype=enctype; encdata.kvno=kvno; encdata.cipher.data=encrypted.data; encdata.cipher.length=encrypted.length; if (length_EncryptedData(&encdata) > *ticketLen) { code = RXKADTICKETLEN; goto cleanup; } tl=*ticketLen; code = encode_EncryptedData((unsigned char *)ticket + *ticketLen - 1, *ticketLen, &encdata, &tl); if (code == 0) { *kvno=RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY; /* * encode function fills in from the end. move data to * beginning of buffer */ memmove(ticket, ticket + *ticketLen - tl, tl); *ticketLen=tl; } cleanup: krb5_data_free(&encrypted); if (cr != NULL) krb5_crypto_destroy(context, cr); krb5_free_keyblock_contents(context, &kb); if (context != NULL) { krb5_free_context(context); } rxi_Free(buf, allocsiz); if ((code & 0xFFFFFF00) == ERROR_TABLE_BASE_asn1) 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); HMAC_CTX_cleanup(&mctx); 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 KRB5_ENCTYPE_DES_CBC_CRC: case KRB5_ENCTYPE_DES_CBC_MD4: case KRB5_ENCTYPE_DES_CBC_MD5: if (keylen != 8) return 1; /* Extract session key */ memcpy(output, keydata, 8); break; case KRB5_ENCTYPE_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 KRB5_ENCTYPE_DES3_CBC_MD5: case KRB5_ENCTYPE_OLD_DES3_CBC_SHA1: case KRB5_ENCTYPE_DES3_CBC_SHA1: if (compress_parity_bits(keydata, &keylen)) return 1; AFS_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; }