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 krb5int_confounder_verify(const struct krb5_cksumtypes *ctp, krb5_key key, krb5_keyusage usage, const krb5_crypto_iov *data, size_t num_data, const krb5_data *input, krb5_boolean *valid) { krb5_error_code ret; unsigned char *plaintext = NULL; krb5_key xorkey = NULL; krb5_data computed = empty_data(); krb5_crypto_iov *hash_iov = NULL, iov; size_t blocksize = ctp->enc->block_size, hashsize = ctp->hash->hashsize; plaintext = k5memdup(input->data, input->length, &ret); if (plaintext == NULL) return ret; ret = mk_xorkey(key, &xorkey); if (ret != 0) goto cleanup; /* Decrypt the input checksum. */ iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = make_data(plaintext, input->length); ret = ctp->enc->decrypt(xorkey, NULL, &iov, 1); if (ret != 0) goto cleanup; /* Hash the confounder, then the input data. */ 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(plaintext, blocksize); memcpy(hash_iov + 1, data, num_data * sizeof(krb5_crypto_iov)); ret = alloc_data(&computed, hashsize); if (ret != 0) goto cleanup; ret = ctp->hash->hash(hash_iov, num_data + 1, &computed); if (ret != 0) goto cleanup; /* Compare the decrypted hash to the computed one. */ *valid = (memcmp(plaintext + blocksize, computed.data, hashsize) == 0); cleanup: zapfree(plaintext, input->length); zapfree(computed.data, hashsize); free(hash_iov); krb5_k_free_key(NULL, xorkey); return ret; }
void krb5_dbe_free(krb5_context context, krb5_db_entry *entry) { krb5_tl_data * tl_data_next; krb5_tl_data * tl_data; int i, j; if (entry == NULL) return; free(entry->e_data); krb5_free_principal(context, entry->princ); for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) { tl_data_next = tl_data->tl_data_next; free(tl_data->tl_data_contents); free(tl_data); } if (entry->key_data) { for (i = 0; i < entry->n_key_data; i++) { for (j = 0; j < entry->key_data[i].key_data_ver; j++) { if (entry->key_data[i].key_data_length[j]) { zapfree(entry->key_data[i].key_data_contents[j], entry->key_data[i].key_data_length[j]); } entry->key_data[i].key_data_contents[j] = NULL; entry->key_data[i].key_data_length[j] = 0; entry->key_data[i].key_data_type[j] = 0; } } free(entry->key_data); } free(entry); }
/* * Marshal a KRB-PRIV message into der_out, encrypted with key. Store the * ciphertext in enc_out. Use the timestamp and sequence number from rdata and * the addresses from local_addr and remote_addr (the second of which may be * NULL). der_out and enc_out should be freed by the caller when finished. */ static krb5_error_code create_krbpriv(krb5_context context, const krb5_data *userdata, krb5_key key, const krb5_replay_data *rdata, krb5_address *local_addr, krb5_address *remote_addr, krb5_data *cstate, krb5_data *der_out, krb5_enc_data *enc_out) { krb5_enctype enctype = krb5_k_key_enctype(context, key); krb5_error_code ret; krb5_priv privmsg; krb5_priv_enc_part encpart; krb5_data *der_encpart = NULL, *der_krbpriv; size_t enclen; memset(&privmsg, 0, sizeof(privmsg)); privmsg.enc_part.kvno = 0; privmsg.enc_part.enctype = enctype; encpart.user_data = *userdata; encpart.s_address = local_addr; encpart.r_address = remote_addr; encpart.timestamp = rdata->timestamp; encpart.usec = rdata->usec; encpart.seq_number = rdata->seq; /* Start by encoding the to-be-encrypted part of the message. */ ret = encode_krb5_enc_priv_part(&encpart, &der_encpart); if (ret) return ret; /* put together an eblock for this encryption */ ret = krb5_c_encrypt_length(context, enctype, der_encpart->length, &enclen); if (ret) goto cleanup; ret = alloc_data(&privmsg.enc_part.ciphertext, enclen); if (ret) goto cleanup; ret = krb5_k_encrypt(context, key, KRB5_KEYUSAGE_KRB_PRIV_ENCPART, (cstate->length > 0) ? cstate : NULL, der_encpart, &privmsg.enc_part); if (ret) goto cleanup; ret = encode_krb5_priv(&privmsg, &der_krbpriv); if (ret) goto cleanup; *der_out = *der_krbpriv; free(der_krbpriv); *enc_out = privmsg.enc_part; memset(&privmsg.enc_part, 0, sizeof(privmsg.enc_part)); cleanup: zapfree(privmsg.enc_part.ciphertext.data, privmsg.enc_part.ciphertext.length); zapfreedata(der_encpart); return ret; }
/* * Compute a derived key into the keyblock outkey. This variation on * krb5int_derive_key does not cache the result, as it is only used * directly in situations which are not expected to be repeated with * the same inkey and constant. */ krb5_error_code krb5int_derive_keyblock(const struct krb5_enc_provider *enc, krb5_key inkey, krb5_keyblock *outkey, const krb5_data *in_constant) { krb5_error_code ret; krb5_data rawkey = empty_data(); /* Allocate a buffer for the raw key bytes. */ ret = alloc_data(&rawkey, enc->keybytes); if (ret) goto cleanup; /* Derive pseudo-random data for the key bytes. */ ret = krb5int_derive_random(enc, inkey, &rawkey, in_constant); if (ret) goto cleanup; /* Postprocess the key. */ ret = enc->make_key(&rawkey, outkey); cleanup: zapfree(rawkey.data, enc->keybytes); return ret; }
/* * Decrypt and decode the enc_part of a krb5_cred using the receiving subkey or * the session key of authcon. If neither key is present, ctext->ciphertext is * assumed to be unencrypted plain text (RFC 6448). */ static krb5_error_code decrypt_encpart(krb5_context context, krb5_enc_data *ctext, krb5_auth_context authcon, krb5_cred_enc_part **encpart_out) { krb5_error_code ret; krb5_data plain = empty_data(); krb5_boolean decrypted = FALSE; *encpart_out = NULL; if (authcon->recv_subkey == NULL && authcon->key == NULL) return decode_krb5_enc_cred_part(&ctext->ciphertext, encpart_out); ret = alloc_data(&plain, ctext->ciphertext.length); if (ret) return ret; if (authcon->recv_subkey != NULL) { ret = krb5_k_decrypt(context, authcon->recv_subkey, KRB5_KEYUSAGE_KRB_CRED_ENCPART, 0, ctext, &plain); decrypted = (ret == 0); } if (!decrypted && authcon->key != NULL) { ret = krb5_k_decrypt(context, authcon->key, KRB5_KEYUSAGE_KRB_CRED_ENCPART, 0, ctext, &plain); decrypted = (ret == 0); } if (decrypted) ret = decode_krb5_enc_cred_part(&plain, encpart_out); zapfree(plain.data, plain.length); return ret; }
krb5_error_code KRB5_CALLCONV krb5_k_decrypt(krb5_context context, krb5_key key, krb5_keyusage usage, const krb5_data *ivec, const krb5_enc_data *input, krb5_data *output) { const struct krb5_keytypes *ktp; krb5_crypto_iov iov[4]; krb5_error_code ret; unsigned int header_len, trailer_len, plain_len; char *scratch = NULL; ktp = find_enctype(key->keyblock.enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; if (input->enctype != ENCTYPE_UNKNOWN && ktp->etype != input->enctype) return KRB5_BAD_ENCTYPE; /* Verify the input and output lengths. */ header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER); if (input->ciphertext.length < header_len + trailer_len) return KRB5_BAD_MSIZE; plain_len = input->ciphertext.length - header_len - trailer_len; if (output->length < plain_len) return KRB5_BAD_MSIZE; scratch = k5alloc(header_len + trailer_len, &ret); if (scratch == NULL) return ret; iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; iov[0].data = make_data(scratch, header_len); memcpy(iov[0].data.data, input->ciphertext.data, header_len); iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data = make_data(output->data, plain_len); memcpy(iov[1].data.data, input->ciphertext.data + header_len, plain_len); /* Use empty padding since tokens don't indicate the padding length. */ iov[2].flags = KRB5_CRYPTO_TYPE_PADDING; iov[2].data = empty_data(); iov[3].flags = KRB5_CRYPTO_TYPE_TRAILER; iov[3].data = make_data(scratch + header_len, trailer_len); memcpy(iov[3].data.data, input->ciphertext.data + header_len + plain_len, trailer_len); ret = ktp->decrypt(ktp, key, usage, ivec, iov, 4); if (ret != 0) zap(output->data, plain_len); else output->length = plain_len; zapfree(scratch, header_len + trailer_len); return ret; }
krb5_error_code krb5int_derive_random(const struct krb5_enc_provider *enc, krb5_key inkey, krb5_data *outrnd, const krb5_data *in_constant) { size_t blocksize, keybytes, n; krb5_crypto_iov iov; krb5_error_code ret; blocksize = enc->block_size; keybytes = enc->keybytes; if (blocksize == 1) return KRB5_BAD_ENCTYPE; if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes) return KRB5_CRYPTO_INTERNAL; /* Allocate encryption data buffer. */ iov.flags = KRB5_CRYPTO_TYPE_DATA; ret = alloc_data(&iov.data, blocksize); if (ret) return ret; /* Initialize the input block. */ if (in_constant->length == blocksize) { memcpy(iov.data.data, in_constant->data, blocksize); } else { krb5int_nfold(in_constant->length * 8, (unsigned char *) in_constant->data, blocksize * 8, (unsigned char *) iov.data.data); } /* Loop encrypting the blocks until enough key bytes are generated. */ n = 0; while (n < keybytes) { ret = enc->encrypt(inkey, 0, &iov, 1); if (ret) goto cleanup; if ((keybytes - n) <= blocksize) { memcpy(outrnd->data + n, iov.data.data, (keybytes - n)); break; } memcpy(outrnd->data + n, iov.data.data, blocksize); n += blocksize; } cleanup: zapfree(iov.data.data, blocksize); return ret; }
krb5_error_code KRB5_CALLCONV krb5_mk_priv(krb5_context context, krb5_auth_context authcon, const krb5_data *userdata, krb5_data *der_out, krb5_replay_data *rdata_out) { krb5_error_code ret; krb5_key key; krb5_replay_data rdata; krb5_data der_krbpriv = empty_data(); krb5_enc_data enc; krb5_address *local_addr, *remote_addr, lstorage, rstorage; *der_out = empty_data(); memset(&enc, 0, sizeof(enc)); memset(&lstorage, 0, sizeof(lstorage)); memset(&rstorage, 0, sizeof(rstorage)); if (!authcon->local_addr) return KRB5_LOCAL_ADDR_REQUIRED; ret = k5_privsafe_gen_rdata(context, authcon, &rdata, rdata_out); if (ret) goto cleanup; ret = k5_privsafe_gen_addrs(context, authcon, &lstorage, &rstorage, &local_addr, &remote_addr); if (ret) goto cleanup; key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key; ret = create_krbpriv(context, userdata, key, &rdata, local_addr, remote_addr, &authcon->cstate, &der_krbpriv, &enc); if (ret) goto cleanup; ret = k5_privsafe_check_replay(context, authcon, NULL, &enc, NULL); if (ret) goto cleanup; *der_out = der_krbpriv; der_krbpriv = empty_data(); if ((authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) || (authcon->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) authcon->local_seq_number++; cleanup: krb5_free_data_contents(context, &der_krbpriv); zapfree(enc.ciphertext.data, enc.ciphertext.length); free(lstorage.contents); free(rstorage.contents); return ret; }
krb5_error_code krb5int_dk_cmac_encrypt(const struct krb5_keytypes *ktp, krb5_key key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { const struct krb5_enc_provider *enc = ktp->enc; krb5_error_code ret; krb5_crypto_iov *header, *trailer, *padding; krb5_data cksum = empty_data(); krb5_key ke = NULL, ki = NULL; /* E(Confounder | Plaintext | Pad) | Checksum */ /* Validate header and trailer lengths, and zero out padding length. */ header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length < enc->block_size) return KRB5_BAD_MSIZE; trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); if (trailer == NULL || trailer->data.length < enc->block_size) return KRB5_BAD_MSIZE; padding = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_PADDING); if (padding != NULL) padding->data.length = 0; /* Derive the encryption and integrity keys. */ ret = derive_keys(enc, key, usage, &ke, &ki); if (ret != 0) goto cleanup; /* Generate confounder. */ header->data.length = enc->block_size; ret = krb5_c_random_make_octets(NULL, &header->data); if (ret != 0) goto cleanup; /* Checksum the plaintext. */ ret = krb5int_cmac_checksum(enc, ki, data, num_data, &trailer->data); if (ret != 0) goto cleanup; /* Encrypt the plaintext (header | data | padding) */ ret = enc->encrypt(ke, ivec, data, num_data); if (ret != 0) goto cleanup; cleanup: krb5_k_free_key(NULL, ke); krb5_k_free_key(NULL, ki); zapfree(cksum.data, cksum.length); return ret; }
static krb5_error_code k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data, size_t num_data) { ArcfourContext *arcfour_ctx = NULL; ArcFourCipherState *cipher_state = NULL; krb5_error_code ret; size_t i; if (key->keyblock.length != 16) return KRB5_BAD_KEYSIZE; if (state != NULL && (state->length != sizeof(ArcFourCipherState))) return KRB5_BAD_MSIZE; if (state != NULL) { cipher_state = (ArcFourCipherState *)state->data; arcfour_ctx = &cipher_state->ctx; if (cipher_state->initialized == 0) { ret = k5_arcfour_init(arcfour_ctx, key->keyblock.contents, key->keyblock.length); if (ret != 0) return ret; cipher_state->initialized = 1; } } else { arcfour_ctx = (ArcfourContext *)malloc(sizeof(ArcfourContext)); if (arcfour_ctx == NULL) return ENOMEM; ret = k5_arcfour_init(arcfour_ctx, key->keyblock.contents, key->keyblock.length); if (ret != 0) { free(arcfour_ctx); return ret; } } for (i = 0; i < num_data; i++) { krb5_crypto_iov *iov = &data[i]; if (ENCRYPT_IOV(iov)) k5_arcfour_crypt(arcfour_ctx, (unsigned char *)iov->data.data, (const unsigned char *)iov->data.data, iov->data.length); } if (state == NULL) zapfree(arcfour_ctx, sizeof(ArcfourContext)); return 0; }
krb5_error_code KRB5_CALLCONV krb5_k_make_checksum(krb5_context context, krb5_cksumtype cksumtype, krb5_key key, krb5_keyusage usage, const krb5_data *input, krb5_checksum *cksum) { const struct krb5_cksumtypes *ctp; krb5_crypto_iov iov; krb5_data cksum_data; krb5_octet *trunc; krb5_error_code ret; if (cksumtype == 0) { ret = krb5int_c_mandatory_cksumtype(context, key->keyblock.enctype, &cksumtype); if (ret != 0) return ret; } ctp = find_cksumtype(cksumtype); if (ctp == NULL) return KRB5_BAD_ENCTYPE; ret = verify_key(ctp, key); if (ret != 0) return ret; ret = alloc_data(&cksum_data, ctp->compute_size); if (ret != 0) return ret; iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = *input; ret = ctp->checksum(ctp, key, usage, &iov, 1, &cksum_data); if (ret != 0) goto cleanup; cksum->magic = KV5M_CHECKSUM; cksum->checksum_type = cksumtype; cksum->length = ctp->output_size; cksum->contents = (krb5_octet *) cksum_data.data; cksum_data.data = NULL; if (ctp->output_size < ctp->compute_size) { trunc = realloc(cksum->contents, ctp->output_size); if (trunc != NULL) cksum->contents = trunc; } cleanup: zapfree(cksum_data.data, ctp->compute_size); return ret; }
krb5_error_code KRB5_CALLCONV krb5_c_string_to_key_with_params(krb5_context context, krb5_enctype enctype, const krb5_data *string, const krb5_data *salt, const krb5_data *params, krb5_keyblock *key) { krb5_error_code ret; const struct krb5_keytypes *ktp; size_t keylength; ktp = find_enctype(enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; keylength = ktp->enc->keylength; /* * xxx AFS string2key function is indicated by a special length in * the salt in much of the code. However only the DES enctypes can * deal with this. Using s2kparams would be a much better solution. */ if (salt && salt->length == SALT_TYPE_AFS_LENGTH) { switch (enctype) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD4: case ENCTYPE_DES_CBC_MD5: break; default: return KRB5_CRYPTO_INTERNAL; } } key->contents = malloc(keylength); if (key->contents == NULL) return ENOMEM; key->magic = KV5M_KEYBLOCK; key->enctype = enctype; key->length = keylength; ret = (*ktp->str2key)(ktp, string, salt, params, key); if (ret) { zapfree(key->contents, keylength); key->length = 0; key->contents = NULL; } return ret; }
krb5_error_code krb5int_dk_cmac_decrypt(const struct krb5_keytypes *ktp, krb5_key key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { const struct krb5_enc_provider *enc = ktp->enc; krb5_error_code ret; krb5_crypto_iov *header, *trailer; krb5_data cksum = empty_data(); krb5_key ke = NULL, ki = NULL; /* E(Confounder | Plaintext | Pad) | Checksum */ /* Validate header and trailer lengths. */ header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length != enc->block_size) return KRB5_BAD_MSIZE; trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); if (trailer == NULL || trailer->data.length != enc->block_size) return KRB5_BAD_MSIZE; /* Derive the encryption and integrity keys. */ ret = derive_keys(enc, key, usage, &ke, &ki); if (ret != 0) goto cleanup; /* Decrypt the plaintext (header | data | padding). */ ret = enc->decrypt(ke, ivec, data, num_data); if (ret != 0) goto cleanup; /* Verify the hash. */ ret = alloc_data(&cksum, enc->block_size); if (ret != 0) goto cleanup; ret = krb5int_cmac_checksum(enc, ki, data, num_data, &cksum); if (ret != 0) goto cleanup; if (k5_bcmp(cksum.data, trailer->data.data, enc->block_size) != 0) ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; cleanup: krb5_k_free_key(NULL, ke); krb5_k_free_key(NULL, ki); zapfree(cksum.data, cksum.length); return ret; }
krb5_error_code krb5int_derive_key(const struct krb5_enc_provider *enc, krb5_key inkey, krb5_key *outkey, const krb5_data *in_constant) { krb5_keyblock keyblock; krb5_error_code ret; krb5_key dkey; *outkey = NULL; /* Check for a cached result. */ dkey = find_cached_dkey(inkey->derived, in_constant); if (dkey != NULL) { *outkey = dkey; return 0; } /* Derive into a temporary keyblock. */ keyblock.length = enc->keylength; keyblock.contents = malloc(keyblock.length); /* Set the enctype as the krb5_k_free_key will iterate over list or derived keys and invoke krb5_k_free_key which will lookup the enctype for key_cleanup handler */ keyblock.enctype = inkey->keyblock.enctype; if (keyblock.contents == NULL) return ENOMEM; ret = krb5int_derive_keyblock(enc, inkey, &keyblock, in_constant); if (ret) goto cleanup; /* Cache the derived key. */ ret = add_cached_dkey(inkey, in_constant, &keyblock, &dkey); if (ret != 0) goto cleanup; *outkey = dkey; cleanup: zapfree(keyblock.contents, keyblock.length); return ret; }
krb5_error_code KRB5_CALLCONV krb5_c_string_to_key_with_params(krb5_context context, krb5_enctype enctype, const krb5_data *string, const krb5_data *salt, const krb5_data *params, krb5_keyblock *key) { krb5_error_code ret; krb5_data empty = empty_data(); const struct krb5_keytypes *ktp; size_t keylength; ktp = find_enctype(enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; keylength = ktp->enc->keylength; /* For compatibility with past behavior, treat a null salt as empty. */ if (salt == NULL) salt = ∅ /* Fail gracefully if someone is using the old AFS string-to-key hack. */ if (salt->length == SALT_TYPE_AFS_LENGTH) return EINVAL; key->contents = malloc(keylength); if (key->contents == NULL) return ENOMEM; key->magic = KV5M_KEYBLOCK; key->enctype = enctype; key->length = keylength; ret = (*ktp->str2key)(ktp, string, salt, params, key); if (ret) { zapfree(key->contents, keylength); key->length = 0; key->contents = NULL; } return ret; }
krb5_error_code krb5int_derive_key(const struct krb5_enc_provider *enc, krb5_key inkey, krb5_key *outkey, const krb5_data *in_constant, enum deriv_alg alg) { krb5_keyblock keyblock; krb5_error_code ret; krb5_key dkey; *outkey = NULL; /* Check for a cached result. */ dkey = find_cached_dkey(inkey->derived, in_constant); if (dkey != NULL) { *outkey = dkey; return 0; } /* Derive into a temporary keyblock. */ keyblock.length = enc->keylength; keyblock.contents = malloc(keyblock.length); keyblock.enctype = inkey->keyblock.enctype; if (keyblock.contents == NULL) return ENOMEM; ret = krb5int_derive_keyblock(enc, inkey, &keyblock, in_constant, alg); if (ret) goto cleanup; /* Cache the derived key. */ ret = add_cached_dkey(inkey, in_constant, &keyblock, &dkey); if (ret != 0) goto cleanup; *outkey = dkey; cleanup: zapfree(keyblock.contents, keyblock.length); return ret; }
/* Derive a key by XOR with 0xF0 bytes. */ static krb5_error_code mk_xorkey(krb5_key origkey, krb5_key *xorkey) { krb5_error_code retval = 0; unsigned char *xorbytes; krb5_keyblock xorkeyblock; size_t i = 0; xorbytes = k5memdup(origkey->keyblock.contents, origkey->keyblock.length, &retval); if (xorbytes == NULL) return retval; for (i = 0; i < origkey->keyblock.length; i++) xorbytes[i] ^= 0xf0; /* Do a shallow copy here. */ xorkeyblock = origkey->keyblock; xorkeyblock.contents = xorbytes; retval = krb5_k_create_key(0, &xorkeyblock, xorkey); zapfree(xorbytes, sizeof(xorbytes)); return retval; }
/* Construct an AP-REQ message for a TGS request. */ static krb5_error_code tgs_construct_ap_req(krb5_context context, krb5_data *checksum_data, krb5_creds *tgt, krb5_keyblock *subkey, krb5_data **ap_req_asn1_out) { krb5_cksumtype cksumtype; krb5_error_code ret; krb5_checksum checksum; krb5_authenticator authent; krb5_ap_req ap_req; krb5_data *authent_asn1 = NULL; krb5_ticket *ticket = NULL; krb5_enc_data authent_enc; *ap_req_asn1_out = NULL; memset(&checksum, 0, sizeof(checksum)); memset(&ap_req, 0, sizeof(ap_req)); memset(&authent_enc, 0, sizeof(authent_enc)); /* Determine the authenticator checksum type. */ switch (tgt->keyblock.enctype) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD4: case ENCTYPE_DES_CBC_MD5: case ENCTYPE_ARCFOUR_HMAC: case ENCTYPE_ARCFOUR_HMAC_EXP: cksumtype = context->kdc_req_sumtype; break; default: ret = krb5int_c_mandatory_cksumtype(context, tgt->keyblock.enctype, &cksumtype); if (ret) goto cleanup; } /* Generate checksum. */ ret = krb5_c_make_checksum(context, cksumtype, &tgt->keyblock, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, checksum_data, &checksum); if (ret) goto cleanup; /* Construct, encode, and encrypt an authenticator. */ authent.subkey = subkey; authent.seq_number = 0; authent.checksum = &checksum; authent.client = tgt->client; authent.authorization_data = tgt->authdata; ret = krb5_us_timeofday(context, &authent.ctime, &authent.cusec); if (ret) goto cleanup; ret = encode_krb5_authenticator(&authent, &authent_asn1); if (ret) goto cleanup; ret = krb5_encrypt_helper(context, &tgt->keyblock, KRB5_KEYUSAGE_TGS_REQ_AUTH, authent_asn1, &authent_enc); if (ret) goto cleanup; ret = decode_krb5_ticket(&tgt->ticket, &ticket); if (ret) goto cleanup; /* Encode the AP-REQ. */ ap_req.authenticator = authent_enc; ap_req.ticket = ticket; ret = encode_krb5_ap_req(&ap_req, ap_req_asn1_out); cleanup: free(checksum.contents); krb5_free_ticket(context, ticket); krb5_free_data_contents(context, &authent_enc.ciphertext); if (authent_asn1 != NULL) zapfree(authent_asn1->data, authent_asn1->length); free(authent_asn1); return ret; }
/* Construct a cookie pa-data item using the cookie values from state, or a * trivial "MIT" cookie if no values are set. */ krb5_error_code kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state, krb5_db_entry *local_tgt, krb5_const_principal client_princ, krb5_pa_data **cookie_out) { krb5_error_code ret; krb5_secure_cookie cookie; krb5_pa_data **contents = state->out_cookie_padata, *pa; krb5_keyblock *key = NULL; krb5_timestamp now; krb5_enc_data enc; krb5_data *der_cookie = NULL; krb5_kvno kvno; size_t ctlen; *cookie_out = NULL; memset(&enc, 0, sizeof(enc)); /* Make a trivial cookie if there are no contents to marshal or we don't * have a TGT entry to encrypt them. */ if (contents == NULL || *contents == NULL || local_tgt == NULL) return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out); ret = get_cookie_key(context, local_tgt, 0, client_princ, &key, &kvno); if (ret) goto cleanup; /* Encode the cookie. */ ret = krb5_timeofday(context, &now); if (ret) goto cleanup; cookie.time = now; cookie.data = contents; ret = encode_krb5_secure_cookie(&cookie, &der_cookie); if (ret) goto cleanup; /* Encrypt the cookie in key. */ ret = krb5_c_encrypt_length(context, key->enctype, der_cookie->length, &ctlen); if (ret) goto cleanup; ret = alloc_data(&enc.ciphertext, ctlen); if (ret) goto cleanup; ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, der_cookie, &enc); if (ret) goto cleanup; /* Construct the cookie pa-data entry. */ ret = alloc_padata(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, &pa); memcpy(pa->contents, "MIT1", 4); store_32_be(kvno, pa->contents + 4); memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length); *cookie_out = pa; cleanup: krb5_free_keyblock(context, key); if (der_cookie != NULL) { zapfree(der_cookie->data, der_cookie->length); free(der_cookie); } krb5_free_data_contents(context, &enc.ciphertext); return ret; }
/* * Locate and decode the FAST cookie in req, storing its contents in state for * later access by preauth modules. If the cookie is expired, return * KRB5KDC_ERR_PREAUTH_EXPIRED if its contents are relevant to req, and ignore * it if they aren't. */ krb5_error_code kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state, krb5_kdc_req *req, krb5_db_entry *local_tgt) { krb5_error_code ret; krb5_secure_cookie *cookie = NULL; krb5_timestamp now; krb5_keyblock *key = NULL; krb5_enc_data enc; krb5_pa_data *pa; krb5_kvno kvno; krb5_data plain = empty_data(); pa = krb5int_find_pa_data(context, req->padata, KRB5_PADATA_FX_COOKIE); if (pa == NULL) return 0; /* If it's not an MIT version 1 cookie, ignore it. It may be an empty * "MIT" cookie or a cookie generated by a different KDC implementation. */ if (pa->length <= 8 || memcmp(pa->contents, "MIT1", 4) != 0) return 0; /* Extract the kvno and generate the corresponding cookie key. */ kvno = load_32_be(pa->contents + 4); ret = get_cookie_key(context, local_tgt, kvno, req->client, &key, NULL); if (ret) goto cleanup; /* Decrypt and decode the cookie. */ memset(&enc, 0, sizeof(enc)); enc.enctype = key->enctype; enc.ciphertext = make_data(pa->contents + 8, pa->length - 8); ret = alloc_data(&plain, pa->length - 8); if (ret) goto cleanup; ret = krb5_c_decrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, &enc, &plain); if (ret) goto cleanup; ret = decode_krb5_secure_cookie(&plain, &cookie); if (ret) goto cleanup; /* Check if the cookie is expired. */ ret = krb5_timeofday(context, &now); if (ret) goto cleanup; if (now - COOKIE_LIFETIME > cookie->time) { /* Don't accept the cookie contents. Only return an error if the * cookie is relevant to the request. */ if (is_relevant(cookie->data, req->padata)) ret = KRB5KDC_ERR_PREAUTH_EXPIRED; goto cleanup; } /* Steal the pa-data list pointer from the cookie and store it in state. */ state->in_cookie_padata = cookie->data; cookie->data = NULL; cleanup: zapfree(plain.data, plain.length); krb5_free_keyblock(context, key); k5_free_secure_cookie(context, cookie); return 0; }
krb5_error_code krb5int_c_combine_keys(krb5_context context, krb5_keyblock *key1, krb5_keyblock *key2, krb5_keyblock *outkey) { unsigned char *r1 = NULL, *r2 = NULL, *combined = NULL, *rnd = NULL; unsigned char *output = NULL; size_t keybytes, keylength; const struct krb5_enc_provider *enc; krb5_data input, randbits; krb5_keyblock tkeyblock; krb5_key tkey = NULL; krb5_error_code ret; const struct krb5_keytypes *ktp; krb5_boolean myalloc = FALSE; if (!enctype_ok(key1->enctype) || !enctype_ok(key2->enctype)) return KRB5_CRYPTO_INTERNAL; if (key1->length != key2->length || key1->enctype != key2->enctype) return KRB5_CRYPTO_INTERNAL; /* Find our encryption algorithm. */ ktp = find_enctype(key1->enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; enc = ktp->enc; keybytes = enc->keybytes; keylength = enc->keylength; /* Allocate and set up buffers. */ r1 = k5alloc(keybytes, &ret); if (ret) goto cleanup; r2 = k5alloc(keybytes, &ret); if (ret) goto cleanup; rnd = k5alloc(keybytes, &ret); if (ret) goto cleanup; combined = k5alloc(keybytes * 2, &ret); if (ret) goto cleanup; output = k5alloc(keylength, &ret); if (ret) goto cleanup; /* * Get R1 and R2 (by running the input keys through the DR algorithm. * Note this is most of derive-key, but not all. */ input.length = key2->length; input.data = (char *) key2->contents; ret = dr(enc, key1, r1, &input); if (ret) goto cleanup; input.length = key1->length; input.data = (char *) key1->contents; ret = dr(enc, key2, r2, &input); if (ret) goto cleanup; /* * Concatenate the two keys together, and then run them through * n-fold to reduce them to a length appropriate for the random-to-key * operation. Note here that krb5int_nfold() takes sizes in bits, hence * the multiply by 8. */ memcpy(combined, r1, keybytes); memcpy(combined + keybytes, r2, keybytes); krb5int_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd); /* * Run the "random" bits through random-to-key to produce a encryption * key. */ randbits.length = keybytes; randbits.data = (char *) rnd; tkeyblock.length = keylength; tkeyblock.contents = output; ret = (*ktp->rand2key)(&randbits, &tkeyblock); if (ret) goto cleanup; ret = krb5_k_create_key(NULL, &tkeyblock, &tkey); if (ret) goto cleanup; /* * Run through derive-key one more time to produce the final key. * Note that the input to derive-key is the ASCII string "combine". */ input.length = 7; input.data = "combine"; /* * Just FYI: _if_ we have space here in the key, then simply use it * without modification. But if the key is blank (no allocated storage) * then allocate some memory for it. This allows programs to use one of * the existing keys as the output key, _or_ pass in a blank keyblock * for us to allocate. It's easier for us to allocate it since we already * know the crypto library internals */ if (outkey->length == 0 || outkey->contents == NULL) { outkey->contents = k5alloc(keylength, &ret); if (ret) goto cleanup; outkey->length = keylength; outkey->enctype = key1->enctype; myalloc = TRUE; } ret = krb5int_derive_keyblock(enc, tkey, outkey, &input, DERIVE_RFC3961); if (ret) { if (myalloc) { free(outkey->contents); outkey->contents = NULL; } goto cleanup; } cleanup: zapfree(r1, keybytes); zapfree(r2, keybytes); zapfree(rnd, keybytes); zapfree(combined, keybytes * 2); zapfree(output, keylength); krb5_k_free_key(NULL, tkey); return ret; }
static krb5_error_code process_chpw_request(krb5_context context, void *server_handle, char *realm, krb5_keytab keytab, const krb5_fulladdr *local_faddr, const krb5_fulladdr *remote_faddr, krb5_data *req, krb5_data *rep) { krb5_error_code ret; char *ptr; unsigned int plen, vno; krb5_data ap_req, ap_rep = empty_data(); krb5_data cipher = empty_data(), clear = empty_data(); krb5_auth_context auth_context = NULL; krb5_principal changepw = NULL; krb5_principal client, target = NULL; krb5_ticket *ticket = NULL; krb5_replay_data replay; krb5_error krberror; int numresult; char strresult[1024]; char *clientstr = NULL, *targetstr = NULL; const char *errmsg = NULL; size_t clen; char *cdots; struct sockaddr_storage ss; socklen_t salen; char addrbuf[100]; krb5_address *addr = remote_faddr->address; *rep = empty_data(); if (req->length < 4) { /* either this, or the server is printing bad messages, or the caller passed in garbage */ ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request was truncated", sizeof(strresult)); goto bailout; } ptr = req->data; /* verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request length was inconsistent", sizeof(strresult)); goto bailout; } /* verify version number */ vno = (*ptr++ & 0xff) ; vno = (vno<<8) | (*ptr++ & 0xff); if (vno != 1 && vno != RFC3244_VERSION) { ret = KRB5KDC_ERR_BAD_PVNO; numresult = KRB5_KPASSWD_BAD_VERSION; snprintf(strresult, sizeof(strresult), "Request contained unknown protocol version number %d", vno); goto bailout; } /* read, check ap-req length */ ap_req.length = (*ptr++ & 0xff); ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); if (ptr + ap_req.length >= req->data + req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request was truncated in AP-REQ", sizeof(strresult)); goto bailout; } /* verify ap_req */ ap_req.data = ptr; ptr += ap_req.length; ret = krb5_auth_con_init(context, &auth_context); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed initializing auth context", sizeof(strresult)); goto chpwfail; } ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed initializing auth context", sizeof(strresult)); goto chpwfail; } ret = krb5_build_principal(context, &changepw, strlen(realm), realm, "kadmin", "changepw", NULL); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed building kadmin/changepw principal", sizeof(strresult)); goto chpwfail; } ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, NULL, &ticket); if (ret) { numresult = KRB5_KPASSWD_AUTHERROR; strlcpy(strresult, "Failed reading application request", sizeof(strresult)); goto chpwfail; } /* construct the ap-rep */ ret = krb5_mk_rep(context, auth_context, &ap_rep); if (ret) { numresult = KRB5_KPASSWD_AUTHERROR; strlcpy(strresult, "Failed replying to application request", sizeof(strresult)); goto chpwfail; } /* decrypt the ChangePasswdData */ cipher.length = (req->data + req->length) - ptr; cipher.data = ptr; /* * Don't set a remote address in auth_context before calling krb5_rd_priv, * so that we can work against clients behind a NAT. Reflection attacks * aren't a concern since we use sequence numbers and since our requests * don't look anything like our responses. Also don't set a local address, * since we don't know what interface the request was received on. */ ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed decrypting request", sizeof(strresult)); goto chpwfail; } client = ticket->enc_part2->client; /* decode ChangePasswdData for setpw requests */ if (vno == RFC3244_VERSION) { krb5_data *clear_data; ret = decode_krb5_setpw_req(&clear, &clear_data, &target); if (ret != 0) { numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Failed decoding ChangePasswdData", sizeof(strresult)); goto chpwfail; } zapfree(clear.data, clear.length); clear = *clear_data; free(clear_data); if (target != NULL) { ret = krb5_unparse_name(context, target, &targetstr); if (ret != 0) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed unparsing target name for log", sizeof(strresult)); goto chpwfail; } } } ret = krb5_unparse_name(context, client, &clientstr); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed unparsing client name for log", sizeof(strresult)); goto chpwfail; } /* for cpw, verify that this is an AS_REQ ticket */ if (vno == 1 && (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) { numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED; strlcpy(strresult, "Ticket must be derived from a password", sizeof(strresult)); goto chpwfail; } /* change the password */ ptr = k5memdup0(clear.data, clear.length, &ret); ret = schpw_util_wrapper(server_handle, client, target, (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0, ptr, NULL, strresult, sizeof(strresult)); if (ret) errmsg = krb5_get_error_message(context, ret); /* zap the password */ zapfree(clear.data, clear.length); zapfree(ptr, clear.length); clear = empty_data(); clen = strlen(clientstr); trunc_name(&clen, &cdots); switch (addr->addrtype) { case ADDRTYPE_INET: { struct sockaddr_in *sin = ss2sin(&ss); sin->sin_family = AF_INET; memcpy(&sin->sin_addr, addr->contents, addr->length); sin->sin_port = htons(remote_faddr->port); salen = sizeof(*sin); break; } case ADDRTYPE_INET6: { struct sockaddr_in6 *sin6 = ss2sin6(&ss); sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, addr->contents, addr->length); sin6->sin6_port = htons(remote_faddr->port); salen = sizeof(*sin6); break; } default: { struct sockaddr *sa = ss2sa(&ss); sa->sa_family = AF_UNSPEC; salen = sizeof(*sa); break; } } if (getnameinfo(ss2sa(&ss), salen, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0) strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf)); if (vno == RFC3244_VERSION) { size_t tlen; char *tdots; const char *targetp; if (target == NULL) { tlen = clen; tdots = cdots; targetp = targetstr; } else { tlen = strlen(targetstr); trunc_name(&tlen, &tdots); targetp = clientstr; } krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for " "%.*s%s: %s"), addrbuf, (int) clen, clientstr, cdots, (int) tlen, targetp, tdots, errmsg ? errmsg : "success"); } else { krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"), addrbuf, (int) clen, clientstr, cdots, errmsg ? errmsg : "success"); } switch (ret) { case KADM5_AUTH_CHANGEPW: numresult = KRB5_KPASSWD_ACCESSDENIED; break; case KADM5_PASS_Q_TOOSHORT: case KADM5_PASS_REUSE: case KADM5_PASS_Q_CLASS: case KADM5_PASS_Q_DICT: case KADM5_PASS_Q_GENERIC: case KADM5_PASS_TOOSOON: numresult = KRB5_KPASSWD_SOFTERROR; break; case 0: numresult = KRB5_KPASSWD_SUCCESS; strlcpy(strresult, "", sizeof(strresult)); break; default: numresult = KRB5_KPASSWD_HARDERROR; break; } chpwfail: ret = alloc_data(&clear, 2 + strlen(strresult)); if (ret) goto bailout; ptr = clear.data; *ptr++ = (numresult>>8) & 0xff; *ptr++ = numresult & 0xff; memcpy(ptr, strresult, strlen(strresult)); cipher = empty_data(); if (ap_rep.length) { ret = krb5_auth_con_setaddrs(context, auth_context, local_faddr->address, NULL); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed storing client and server internet addresses", sizeof(strresult)); } else { ret = krb5_mk_priv(context, auth_context, &clear, &cipher, &replay); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed encrypting reply", sizeof(strresult)); } } } /* if no KRB-PRIV was constructed, then we need a KRB-ERROR. if this fails, just bail. there's nothing else we can do. */ if (cipher.length == 0) { /* clear out ap_rep now, so that it won't be inserted in the reply */ if (ap_rep.length) { free(ap_rep.data); ap_rep = empty_data(); } krberror.ctime = 0; krberror.cusec = 0; krberror.susec = 0; ret = krb5_timeofday(context, &krberror.stime); if (ret) goto bailout; /* this is really icky. but it's what all the other callers to mk_error do. */ krberror.error = ret; krberror.error -= ERROR_TABLE_BASE_krb5; if (krberror.error < 0 || krberror.error > KRB_ERR_MAX) krberror.error = KRB_ERR_GENERIC; krberror.client = NULL; ret = krb5_build_principal(context, &krberror.server, strlen(realm), realm, "kadmin", "changepw", NULL); if (ret) goto bailout; krberror.text.length = 0; krberror.e_data = clear; ret = krb5_mk_error(context, &krberror, &cipher); krb5_free_principal(context, krberror.server); if (ret) goto bailout; } /* construct the reply */ ret = alloc_data(rep, 6 + ap_rep.length + cipher.length); if (ret) goto bailout; ptr = rep->data; /* length */ *ptr++ = (rep->length>>8) & 0xff; *ptr++ = rep->length & 0xff; /* version == 0x0001 big-endian */ *ptr++ = 0; *ptr++ = 1; /* ap_rep length, big-endian */ *ptr++ = (ap_rep.length>>8) & 0xff; *ptr++ = ap_rep.length & 0xff; /* ap-rep data */ if (ap_rep.length) { memcpy(ptr, ap_rep.data, ap_rep.length); ptr += ap_rep.length; } /* krb-priv or krb-error */ memcpy(ptr, cipher.data, cipher.length); bailout: krb5_auth_con_free(context, auth_context); krb5_free_principal(context, changepw); krb5_free_ticket(context, ticket); free(ap_rep.data); free(clear.data); free(cipher.data); krb5_free_principal(context, target); krb5_free_unparsed_name(context, targetstr); krb5_free_unparsed_name(context, clientstr); krb5_free_error_message(context, errmsg); return ret; }
/* * NIST SP800-108 KDF in feedback mode (section 5.2). * Parameters: * - CMAC (with enc as the enc provider) is the PRF. * - A block counter of four bytes is used. * - Label is the key derivation constant. * - Context is empty. * - Four bytes are used to encode the output length in the PRF input. */ static krb5_error_code derive_random_sp800_108_cmac(const struct krb5_enc_provider *enc, krb5_key inkey, krb5_data *outrnd, const krb5_data *in_constant) { size_t blocksize, keybytes, n; krb5_crypto_iov iov[6]; krb5_error_code ret; krb5_data prf; unsigned int i; unsigned char ibuf[4], Lbuf[4]; blocksize = enc->block_size; keybytes = enc->keybytes; if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes) return KRB5_CRYPTO_INTERNAL; /* Allocate encryption data buffer. */ ret = alloc_data(&prf, blocksize); if (ret) return ret; /* K(i-1): the previous block of PRF output, initially all-zeros. */ iov[0].flags = KRB5_CRYPTO_TYPE_DATA; iov[0].data = prf; /* [i]2: four-byte big-endian binary string giving the block counter */ iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data = make_data(ibuf, sizeof(ibuf)); /* Label: the fixed derived-key input */ iov[2].flags = KRB5_CRYPTO_TYPE_DATA; iov[2].data = *in_constant; /* 0x00: separator byte */ iov[3].flags = KRB5_CRYPTO_TYPE_DATA; iov[3].data = make_data("", 1); /* Context: (unused) */ iov[4].flags = KRB5_CRYPTO_TYPE_DATA; iov[4].data = empty_data(); /* [L]2: four-byte big-endian binary string giving the output length */ iov[5].flags = KRB5_CRYPTO_TYPE_DATA; iov[5].data = make_data(Lbuf, sizeof(Lbuf)); store_32_be(outrnd->length * 8, Lbuf); for (i = 1, n = 0; n < keybytes; i++) { /* Update the block counter. */ store_32_be(i, ibuf); /* Compute a CMAC checksum, storing the result into K(i-1). */ ret = krb5int_cmac_checksum(enc, inkey, iov, 6, &prf); if (ret) goto cleanup; /* Copy the result into the appropriate part of the output buffer. */ if (keybytes - n <= blocksize) { memcpy(outrnd->data + n, prf.data, keybytes - n); break; } memcpy(outrnd->data + n, prf.data, blocksize); n += blocksize; } cleanup: zapfree(prf.data, blocksize); return ret; }
krb5_error_code krb5int_arcfour_decrypt(const struct krb5_keytypes *ktp, krb5_key key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { const struct krb5_enc_provider *enc = ktp->enc; const struct krb5_hash_provider *hash = ktp->hash; krb5_error_code ret; krb5_crypto_iov *header, *trailer; krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL; krb5_data checksum, header_data, comp_checksum = empty_data(); 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; /* Allocate buffers. */ ret = alloc_data(&comp_checksum, hash->hashsize); if (ret != 0) goto cleanup; ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, &usage_keyblock); if (ret != 0) goto cleanup; ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, &enc_keyblock); if (ret != 0) goto cleanup; checksum = make_data(header->data.data, hash->hashsize); /* Adjust pointers so confounder is at start of header. */ header->data.length -= hash->hashsize; header->data.data += hash->hashsize; /* We may have to try two usage values; see below. */ do { /* Derive a usage key from the session key and usage. */ ret = usage_key(enc, hash, &key->keyblock, usage, usage_keyblock); if (ret != 0) goto cleanup; /* Derive the encryption key from the usage key and checksum. */ ret = enc_key(enc, hash, usage_keyblock, &checksum, enc_keyblock); if (ret) goto cleanup; /* Decrypt the ciphertext. */ ret = keyblock_crypt(enc, enc_keyblock, ivec, data, num_data); if (ret != 0) goto cleanup; /* Compute HMAC(usage key, plaintext) to get the checksum. */ ret = krb5int_hmac_keyblock(hash, usage_keyblock, data, num_data, &comp_checksum); if (ret != 0) goto cleanup; if (k5_bcmp(checksum.data, comp_checksum.data, hash->hashsize) != 0) { if (usage == 9) { /* * RFC 4757 specifies usage 8 for TGS-REP encrypted parts * encrypted in a subkey, but the value used by MS is actually * 9. We now use 9 to start with, but fall back to 8 on * failure in case we are communicating with a KDC using the * value from the RFC. ivec is always NULL in this case. * We need to re-encrypt the data in the wrong key first. */ ret = keyblock_crypt(enc, enc_keyblock, NULL, data, num_data); if (ret != 0) goto cleanup; usage = 8; continue; } ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; goto cleanup; } break; } while (1); cleanup: header->data = header_data; /* Restore header pointers. */ krb5int_c_free_keyblock(NULL, usage_keyblock); krb5int_c_free_keyblock(NULL, enc_keyblock); zapfree(comp_checksum.data, comp_checksum.length); return ret; }
static void aes_key_cleanup(krb5_key key) { zapfree(key->cache, sizeof(struct aes_key_info_cache)); }
krb5_error_code krb5int_hmac_keyblock(const struct krb5_hash_provider *hash, const krb5_keyblock *keyblock, const krb5_crypto_iov *data, size_t num_data, krb5_data *output) { unsigned char *xorkey = NULL, *ihash = NULL; unsigned int i; krb5_crypto_iov *ihash_iov = NULL, ohash_iov[2]; krb5_data hashout; krb5_error_code ret; if (keyblock->length > hash->blocksize) return KRB5_CRYPTO_INTERNAL; if (output->length < hash->hashsize) return KRB5_BAD_MSIZE; /* Allocate space for the xor key, hash input vector, and inner hash */ xorkey = k5alloc(hash->blocksize, &ret); if (xorkey == NULL) goto cleanup; ihash = k5alloc(hash->hashsize, &ret); if (ihash == NULL) goto cleanup; ihash_iov = k5calloc(num_data + 1, sizeof(krb5_crypto_iov), &ret); if (ihash_iov == NULL) goto cleanup; /* Create the inner padded key. */ memset(xorkey, 0x36, hash->blocksize); for (i = 0; i < keyblock->length; i++) xorkey[i] ^= keyblock->contents[i]; /* Compute the inner hash over the inner key and input data. */ ihash_iov[0].flags = KRB5_CRYPTO_TYPE_DATA; ihash_iov[0].data = make_data(xorkey, hash->blocksize); memcpy(ihash_iov + 1, data, num_data * sizeof(krb5_crypto_iov)); hashout = make_data(ihash, hash->hashsize); ret = hash->hash(ihash_iov, num_data + 1, &hashout); if (ret != 0) goto cleanup; /* Create the outer padded key. */ memset(xorkey, 0x5c, hash->blocksize); for (i = 0; i < keyblock->length; i++) xorkey[i] ^= keyblock->contents[i]; /* Compute the outer hash over the outer key and inner hash value. */ ohash_iov[0].flags = KRB5_CRYPTO_TYPE_DATA; ohash_iov[0].data = make_data(xorkey, hash->blocksize); ohash_iov[1].flags = KRB5_CRYPTO_TYPE_DATA; ohash_iov[1].data = make_data(ihash, hash->hashsize); output->length = hash->hashsize; ret = hash->hash(ohash_iov, 2, output); if (ret != 0) memset(output->data, 0, output->length); cleanup: zapfree(xorkey, hash->blocksize); zapfree(ihash, hash->hashsize); free(ihash_iov); return ret; }
krb5_error_code krb5int_dk_string_to_key(const struct krb5_keytypes *ktp, const krb5_data *string, const krb5_data *salt, const krb5_data *parms, krb5_keyblock *keyblock) { krb5_error_code ret; size_t keybytes, keylength, concatlen; unsigned char *concat = NULL, *foldstring = NULL, *foldkeydata = NULL; krb5_data indata; krb5_keyblock foldkeyblock; krb5_key foldkey = NULL; /* keyblock->length is checked by krb5int_derive_key. */ keybytes = ktp->enc->keybytes; keylength = ktp->enc->keylength; concatlen = string->length + salt->length; concat = k5alloc(concatlen, &ret); if (ret != 0) goto cleanup; foldstring = k5alloc(keybytes, &ret); if (ret != 0) goto cleanup; foldkeydata = k5alloc(keylength, &ret); if (ret != 0) goto cleanup; /* construct input string ( = string + salt), fold it, make_key it */ if (string->length > 0) memcpy(concat, string->data, string->length); if (salt->length > 0) memcpy(concat + string->length, salt->data, salt->length); krb5int_nfold(concatlen*8, concat, keybytes*8, foldstring); indata.length = keybytes; indata.data = (char *) foldstring; foldkeyblock.length = keylength; foldkeyblock.contents = foldkeydata; foldkeyblock.enctype = ktp->etype; ret = ktp->rand2key(&indata, &foldkeyblock); if (ret != 0) goto cleanup; ret = krb5_k_create_key(NULL, &foldkeyblock, &foldkey); if (ret != 0) goto cleanup; /* now derive the key from this one */ indata.length = kerberos_len; indata.data = (char *) kerberos; ret = krb5int_derive_keyblock(ktp->enc, NULL, foldkey, keyblock, &indata, DERIVE_RFC3961); if (ret != 0) memset(keyblock->contents, 0, keyblock->length); cleanup: zapfree(concat, concatlen); zapfree(foldstring, keybytes); zapfree(foldkeydata, keylength); krb5_k_free_key(NULL, foldkey); return ret; }