* 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
* 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
#include <afsconfig.h>
#include <afs/param.h>
+#include <afs/stds.h>
+
+#include <roken.h>
#ifdef IGNORE_SOME_GCC_WARNINGS
# pragma GCC diagnostic warning "-Wimplicit-function-declaration"
#endif
-#include <afs/stds.h>
-#include <sys/types.h>
-#ifdef AFS_NT40_ENV
-#include <winsock2.h>
-#else
-#include <netinet/in.h>
-#endif
-#include <string.h>
#include <rx/xdr.h>
#include <rx/rx.h>
+
+#define HC_DEPRECATED_CRYPTO
+#include <hcrypto/md4.h>
+#include <hcrypto/md5.h>
+#include <hcrypto/des.h>
+#include <hcrypto/hmac.h>
+
#include "lifetimes.h"
#include "rxkad.h"
+#include "rxkad_convert.h"
#include "v5gen-rewrite.h"
#include "v5gen.h"
#include "der.h"
#include "v5der.c"
#include "v5gen.c"
-#include "md4.h"
-#include "md5.h"
+
+#define RFC3961_NO_ENUMS
+#define RFC3961_NO_CKSUM
+#include <afs/rfc3961.h>
/*
* Principal conversion Taken from src/lib/krb5/krb/conv_princ from MIT Kerberos. If you
* bug with MIT by sending mail to krb5-bugs@mit.edu.
*/
-extern afs_int32 des_cbc_encrypt(void * in, void * out,
- afs_int32 length,
- des_key_schedule key, des_cblock *iv,
- int encrypt);
-extern int des_key_sched(des_cblock k, des_key_schedule schedule);
-
struct krb_convert {
char *v4_str;
char *v5_str;
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;
goto cleanup;
}
- /* If kvno is null, it's probably not included because it was kvno==0
+ /* 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;
case ETYPE_DES_CBC_CRC:
case ETYPE_DES_CBC_MD4:
case ETYPE_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:
- goto unknown_key;
+ 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_keybits(context, t5.enc_part.etype, &keysize);
+ if (code != 0) {
+ krb5_free_context(context);
+ goto unknown_key;
+ }
+ keysize = keysize / 8;
+ 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);
}
- /* 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);
if (code != 0)
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.
}
/* 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??? */
{
int (*cksum_func) (void *, size_t, void *, size_t,
struct ktc_encryptionKey *);
- des_cblock ivec;
- des_key_schedule s;
+ 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), (struct des_ks_struct *)&s);
+ DES_key_sched(ktc_to_cblock(key), &s);
#define CONFOUNDERSZ 8
abort();
}
- des_cbc_encrypt(in, out, insz, s, &ivec, 0);
+ DES_cbc_encrypt(in, out, insz, &s, &ivec, 0);
memcpy(cksum, (char *)out + CONFOUNDERSZ, cksumsz);
memset((char *)out + CONFOUNDERSZ, 0, cksumsz);
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;
+ 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=ETYPE_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(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);
+ 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);
+ 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;
+}