tkt_MakeTicket5: Use correct bitmask operator
[openafs.git] / src / rxkad / ticket5.c
index f7bf5dd..5a53163 100644 (file)
@@ -56,6 +56,7 @@
 
 #include <afsconfig.h>
 #include <afs/param.h>
+#include <afs/stds.h>
 
 #include <roken.h>
 
 # 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 "v5der.c"
 #include "v5gen.c"
 
+#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
  * find a need to change the services here, please consider opening a
@@ -172,19 +175,26 @@ 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,
                  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;
@@ -227,25 +237,82 @@ tkt_DecodeTicket5(char *ticket, afs_int32 ticket_len,
     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;
 
@@ -309,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:
+    if (tkt_DeriveDesKey(decr_part.key.keytype, decr_part.key.keyvalue.data,
+                        decr_part.key.keyvalue.length, session_key) != 0)
        goto bad_ticket;
-    }
-
-    if (decr_part.key.keyvalue.length != 8)
-       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??? */
@@ -471,3 +526,232 @@ 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,
+               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;
+}