/* Derive a usage key from a session key and krb5 usage constant. */ static krb5_error_code usage_key(const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, const krb5_keyblock *session_keyblock, krb5_keyusage usage, krb5_keyblock *out) { char salt_buf[14]; unsigned int salt_len; krb5_data out_data = make_data(out->contents, out->length); krb5_crypto_iov iov; krb5_keyusage ms_usage; /* Generate the salt. */ ms_usage = krb5int_arcfour_translate_usage(usage); if (session_keyblock->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { memcpy(salt_buf, l40, 10); store_32_le(ms_usage, salt_buf + 10); salt_len = 14; } else { store_32_le(ms_usage, salt_buf); salt_len = 4; } /* Compute HMAC(key, salt) to produce the usage key. */ iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = make_data(salt_buf, salt_len); return krb5int_hmac_keyblock(hash, session_keyblock, &iov, 1, &out_data); }
krb5_error_code krb5int_hmacmd5_checksum(const struct krb5_cksumtypes *ctp, krb5_key key, krb5_keyusage usage, const krb5_crypto_iov *data, size_t num_data, krb5_data *output) { krb5_keyusage ms_usage; krb5_error_code ret; krb5_keyblock ks, *keyblock; krb5_crypto_iov *hash_iov = NULL, iov; krb5_data ds = empty_data(), hashval = empty_data(); char t[4]; if (key == NULL || key->keyblock.length > ctp->hash->blocksize) return KRB5_BAD_ENCTYPE; if (ctp->ctype == CKSUMTYPE_HMAC_MD5_ARCFOUR) { /* Compute HMAC(key, "signaturekey\0") to get the signing key ks. */ ret = alloc_data(&ds, ctp->hash->hashsize); if (ret != 0) goto cleanup; iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = make_data("signaturekey", 13); ret = krb5int_hmac(ctp->hash, key, &iov, 1, &ds); if (ret) goto cleanup; ks.length = key->keyblock.length; ks.contents = (krb5_octet *) ds.data; keyblock = &ks; } else /* For md5-hmac, just use the key. */ keyblock = &key->keyblock; /* Compute the MD5 value of the input. */ ms_usage = krb5int_arcfour_translate_usage(usage); store_32_le(ms_usage, t); hash_iov = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret); if (hash_iov == NULL) goto cleanup; hash_iov[0].flags = KRB5_CRYPTO_TYPE_DATA; hash_iov[0].data = make_data(t, 4); memcpy(hash_iov + 1, data, num_data * sizeof(krb5_crypto_iov)); ret = alloc_data(&hashval, ctp->hash->hashsize); if (ret != 0) goto cleanup; ret = ctp->hash->hash(hash_iov, num_data + 1, &hashval); if (ret != 0) goto cleanup; /* Compute HMAC(ks, md5value). */ iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = hashval; ret = krb5int_hmac_keyblock(ctp->hash, keyblock, &iov, 1, output); cleanup: zapfree(ds.data, ds.length); zapfree(hashval.data, hashval.length); free(hash_iov); return ret; }
krb5_error_code krb5_make_fulladdr(krb5_context context, krb5_address *kaddr, krb5_address *kport, krb5_address *raddr) { register krb5_octet * marshal; krb5_int32 tmp32; krb5_int16 tmp16; if (kaddr == NULL || kport == NULL) return EINVAL; raddr->length = kaddr->length + kport->length + (4 * sizeof(krb5_int32)); if (!(raddr->contents = (krb5_octet *)malloc(raddr->length))) return ENOMEM; raddr->addrtype = ADDRTYPE_ADDRPORT; marshal = raddr->contents; tmp16 = kaddr->addrtype; *marshal++ = 0x00; *marshal++ = 0x00; store_16_le(tmp16, marshal); marshal += 2; tmp32 = kaddr->length; store_32_le(tmp32, marshal); marshal += 4; (void) memcpy(marshal, kaddr->contents, kaddr->length); marshal += kaddr->length; tmp16 = kport->addrtype; *marshal++ = 0x00; *marshal++ = 0x00; store_16_le(tmp16, marshal); marshal += 2; tmp32 = kport->length; store_32_le(tmp32, marshal); marshal += 4; (void) memcpy(marshal, kport->contents, kport->length); marshal += kport->length; return 0; }
/* in-place encoding of PAC header */ static krb5_error_code k5_pac_encode_header(krb5_context context, krb5_pac pac) { size_t i; unsigned char *p; size_t header_len; header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH); assert(pac->data.length >= header_len); p = (unsigned char *)pac->data.data; store_32_le(pac->pac->cBuffers, p); p += 4; store_32_le(pac->pac->Version, p); p += 4; for (i = 0; i < pac->pac->cBuffers; i++) { PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i]; store_32_le(buffer->ulType, p); p += 4; store_32_le(buffer->cbBufferSize, p); p += 4; store_64_le(buffer->Offset, p); p += 8; assert((buffer->Offset % PAC_ALIGNMENT) == 0); assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length); assert(buffer->Offset >= header_len); if (buffer->Offset % PAC_ALIGNMENT || buffer->Offset + buffer->cbBufferSize > pac->data.length || buffer->Offset < header_len) return ERANGE; } return 0; }
static krb5_error_code k5_insert_checksum(krb5_context context, krb5_pac pac, krb5_ui_4 type, const krb5_keyblock *key, krb5_cksumtype *cksumtype) { krb5_error_code ret; size_t len; krb5_data cksumdata; ret = krb5int_c_mandatory_cksumtype(context, key->enctype, cksumtype); if (ret != 0) return ret; ret = krb5_c_checksum_length(context, *cksumtype, &len); if (ret != 0) return ret; ret = k5_pac_locate_buffer(context, pac, type, &cksumdata); if (ret == 0) { /* * If we're resigning PAC, make sure we can fit checksum * into existing buffer */ if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len) return ERANGE; memset(cksumdata.data, 0, cksumdata.length); } else { /* Add a zero filled buffer */ cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len; cksumdata.data = NULL; ret = k5_pac_add_buffer(context, pac, type, &cksumdata, TRUE, &cksumdata); if (ret != 0) return ret; } /* Encode checksum type into buffer */ store_32_le((krb5_ui_4)*cksumtype, cksumdata.data); return 0; }
int main () { /* Test some low-level assumptions the Kerberos code depends on. */ union { uint64_t n64; uint32_t n32; uint16_t n16; unsigned char b[9]; } u; static unsigned char buf[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; assert(load_64_be(buf+1) == 0x0102030405060708LL); assert(load_64_le(buf+1) == 0x0807060504030201LL); assert(load_32_le(buf+2) == 0x05040302); assert(load_32_be(buf+2) == 0x02030405); assert(load_16_be(buf+3) == 0x0304); assert(load_16_le(buf+3) == 0x0403); u.b[0] = 0; assert((store_64_be(0x0102030405060708LL, u.b+1), !memcmp(buf, u.b, 9))); u.b[1] = 9; assert((store_64_le(0x0807060504030201LL, u.b+1), !memcmp(buf, u.b, 9))); u.b[2] = 10; assert((store_32_be(0x02030405, u.b+2), !memcmp(buf, u.b, 9))); u.b[3] = 11; assert((store_32_le(0x05040302, u.b+2), !memcmp(buf, u.b, 9))); u.b[4] = 12; assert((store_16_be(0x0304, u.b+3), !memcmp(buf, u.b, 9))); u.b[4] = 13; assert((store_16_le(0x0403, u.b+3), !memcmp(buf, u.b, 9))); /* Verify that load_*_n properly does native format. Assume the unaligned thing is okay. */ u.n64 = 0x090a0b0c0d0e0f00LL; assert(load_64_n((unsigned char *) &u.n64) == 0x090a0b0c0d0e0f00LL); u.n32 = 0x06070809; assert(load_32_n((unsigned char *) &u.n32) == 0x06070809); u.n16 = 0x0a0b; assert(load_16_n((unsigned char *) &u.n16) == 0x0a0b); return 0; }
static krb5_error_code k5_md5_hmac_hash (const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *iv, const krb5_data *input, krb5_data *output) { krb5_keyusage ms_usage; krb5_MD5_CTX ctx; unsigned char t[4]; krb5_data ds; krb5_MD5Init(&ctx); ms_usage = krb5int_arcfour_translate_usage (usage); store_32_le(ms_usage, t); krb5_MD5Update(&ctx, t, sizeof(t)); krb5_MD5Update(&ctx, (unsigned char *)input->data, input->length); krb5_MD5Final(&ctx); ds.magic = KV5M_DATA; ds.length = 16; ds.data = (char *)ctx.digest; return krb5_hmac ( &krb5int_hash_md5, key, 1, &ds, output); }
static krb5_error_code krb5int_arcfour_encrypt_iov(const struct krb5_aead_provider *aead, const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { krb5_error_code ret; krb5_crypto_iov *header, *trailer; krb5_keyblock k1, k2, k3; krb5_data d1, d2, d3; krb5_data checksum, confounder, header_data; krb5_keyusage ms_usage; char salt_data[14]; krb5_data salt; size_t i; d1.length = d2.length = d3.length = 0; d1.data = d2.data = d3.data = NULL; /* * Caller must have provided space for the header, padding * and trailer; per RFC 4757 we will arrange it as: * * Checksum | E(Confounder | Plaintext) */ header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length < hash->hashsize + CONFOUNDERLENGTH) return KRB5_BAD_MSIZE; header_data = header->data; /* Trailer may be absent */ trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); if (trailer != NULL) trailer->data.length = 0; /* Ensure that there is no padding */ for (i = 0; i < num_data; i++) { if (data[i].flags == KRB5_CRYPTO_TYPE_PADDING) data[i].data.length = 0; } ret = alloc_derived_key(enc, &k1, &d1, key); if (ret != 0) goto cleanup; ret = alloc_derived_key(enc, &k2, &d2, key); if (ret != 0) goto cleanup; ret = alloc_derived_key(enc, &k3, &d3, key); if (ret != 0) goto cleanup; /* Begin the encryption, compute K1 */ salt.data = salt_data; salt.length = sizeof(salt_data); ms_usage = krb5int_arcfour_translate_usage(usage); if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { strncpy(salt.data, krb5int_arcfour_l40, salt.length); store_32_le(ms_usage, salt.data + 10); } else { salt.length = 4; store_32_le(ms_usage, salt.data); } ret = krb5_hmac(hash, key, 1, &salt, &d1); if (ret != 0) goto cleanup; memcpy(k2.contents, k1.contents, k2.length); if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) memset(k1.contents + 7, 0xAB, 9); header->data.length = hash->hashsize + CONFOUNDERLENGTH; confounder.data = header->data.data + hash->hashsize; confounder.length = CONFOUNDERLENGTH; ret = krb5_c_random_make_octets(0, &confounder); if (ret != 0) goto cleanup; checksum.data = header->data.data; checksum.length = hash->hashsize; /* Adjust pointers so confounder is at start of header */ header->data.length -= hash->hashsize; header->data.data += hash->hashsize; ret = krb5int_hmac_iov(hash, &k2, data, num_data, &checksum); if (ret != 0) goto cleanup; ret = krb5_hmac(hash, &k1, 1, &checksum, &d3); if (ret != 0) goto cleanup; ret = enc->encrypt_iov(&k3, ivec, data, num_data); if (ret != 0) goto cleanup; cleanup: header->data = header_data; /* restore header pointers */ if (d1.data != NULL) { memset(d1.data, 0, d1.length); free(d1.data); } if (d2.data != NULL) { memset(d2.data, 0, d2.length); free(d2.data); } if (d3.data != NULL) { memset(d3.data, 0, d3.length); free(d3.data); } return ret; }
static krb5_error_code krb5int_arcfour_decrypt_iov(const struct krb5_aead_provider *aead, const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { krb5_error_code ret; krb5_crypto_iov *header, *trailer; krb5_keyblock k1, k2, k3; krb5_data d1, d2, d3; krb5_data checksum, header_data; krb5_keyusage ms_usage; char salt_data[14]; krb5_data salt; d1.length = d2.length = d3.length = 0; d1.data = d2.data = d3.data = NULL; header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length != hash->hashsize + CONFOUNDERLENGTH) return KRB5_BAD_MSIZE; header_data = header->data; trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); if (trailer != NULL && trailer->data.length != 0) return KRB5_BAD_MSIZE; ret = alloc_derived_key(enc, &k1, &d1, key); if (ret != 0) goto cleanup; ret = alloc_derived_key(enc, &k2, &d2, key); if (ret != 0) goto cleanup; ret = alloc_derived_key(enc, &k3, &d3, key); if (ret != 0) goto cleanup; /* Begin the decryption, compute K1 */ salt.data = salt_data; salt.length = sizeof(salt_data); ms_usage = krb5int_arcfour_translate_usage(usage); if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { strncpy(salt.data, krb5int_arcfour_l40, salt.length); store_32_le(ms_usage, (unsigned char *)salt.data + 10); } else { salt.length = 4; store_32_le(ms_usage, (unsigned char *)salt.data); } ret = krb5_hmac(hash, key, 1, &salt, &d1); if (ret != 0) goto cleanup; memcpy(k2.contents, k1.contents, k2.length); if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) memset(k1.contents + 7, 0xAB, 9); checksum.data = header->data.data; checksum.length = hash->hashsize; /* Adjust pointers so confounder is at start of header */ header->data.length -= hash->hashsize; header->data.data += hash->hashsize; ret = krb5_hmac(hash, &k1, 1, &checksum, &d3); if (ret != 0) goto cleanup; ret = enc->decrypt_iov(&k3, ivec, data, num_data); if (ret != 0) goto cleanup; ret = krb5int_hmac_iov(hash, &k2, data, num_data, &d1); if (ret != 0) goto cleanup; if (memcmp(checksum.data, d1.data, hash->hashsize) != 0) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; goto cleanup; } cleanup: header->data = header_data; /* restore header pointers */ if (d1.data != NULL) { memset(d1.data, 0, d1.length); free(d1.data); } if (d2.data != NULL) { memset(d2.data, 0, d2.length); free(d2.data); } if (d3.data != NULL) { memset(d3.data, 0, d3.length); free(d3.data); } return ret; }