/* * Normally defined in lib/gssapi/krb5/delete_sec_context.c; this version * is tailored for imported lucid contexts and has fewer dependencies. * Does not handle output tokens. */ OM_uint32 krb5_gss_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token) { krb5_gss_ctx_id_t ctx; if (output_token) { *minor_status = EINVAL; return GSS_S_FAILURE; } *minor_status = 0; if (*context_handle == GSS_C_NO_CONTEXT) return GSS_S_COMPLETE; ctx = (krb5_gss_ctx_id_t)*context_handle; g_seqstate_free(ctx->seqstate); krb5_k_free_key(NULL, ctx->enc); krb5_k_free_key(NULL, ctx->seq); krb5_k_free_key(NULL, ctx->subkey); krb5_k_free_key(NULL, ctx->acceptor_subkey); memset(ctx, 0, sizeof(*ctx)); free(ctx); *context_handle = GSS_C_NO_CONTEXT; return GSS_S_COMPLETE; }
krb5_error_code KRB5_CALLCONV krb5_auth_con_free(krb5_context context, krb5_auth_context auth_context) { if (auth_context == NULL) return 0; if (auth_context->local_addr) krb5_free_address(context, auth_context->local_addr); if (auth_context->remote_addr) krb5_free_address(context, auth_context->remote_addr); if (auth_context->local_port) krb5_free_address(context, auth_context->local_port); if (auth_context->remote_port) krb5_free_address(context, auth_context->remote_port); if (auth_context->authentp) krb5_free_authenticator(context, auth_context->authentp); if (auth_context->key) krb5_k_free_key(context, auth_context->key); if (auth_context->send_subkey) krb5_k_free_key(context, auth_context->send_subkey); if (auth_context->recv_subkey) krb5_k_free_key(context, auth_context->recv_subkey); if (auth_context->rcache) krb5_rc_close(context, auth_context->rcache); if (auth_context->permitted_etypes) free(auth_context->permitted_etypes); if (auth_context->ad_context) krb5_authdata_context_free(context, auth_context->ad_context); free(auth_context); return 0; }
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; }
/* * This function overloads the keyblock field. It is only useful prior to * a krb5_rd_req_decode() call for user to user authentication where the * server has the key and needs to use it to decrypt the incoming request. * Once decrypted this key is no longer necessary and is then overwritten * with the session key sent by the client. */ krb5_error_code KRB5_CALLCONV krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock) { if (auth_context->key) krb5_k_free_key(context, auth_context->key); return(krb5_k_create_key(context, keyblock, &(auth_context->key))); }
/* Derive encryption and integrity keys for CMAC-using enctypes. */ static krb5_error_code derive_keys(const struct krb5_enc_provider *enc, krb5_key key, krb5_keyusage usage, krb5_key *ke_out, krb5_key *ki_out) { krb5_error_code ret; unsigned char buf[K5CLENGTH]; krb5_data constant = make_data(buf, K5CLENGTH); krb5_key ke, ki; *ke_out = *ki_out = NULL; /* Derive the encryption key. */ store_32_be(usage, buf); buf[4] = 0xAA; ret = krb5int_derive_key(enc, key, &ke, &constant, DERIVE_SP800_108_CMAC); if (ret != 0) return ret; /* Derive the integrity key. */ buf[4] = 0x55; ret = krb5int_derive_key(enc, key, &ki, &constant, DERIVE_SP800_108_CMAC); if (ret != 0) { krb5_k_free_key(NULL, ke); return ret; } *ke_out = ke; *ki_out = ki; return 0; }
krb5_error_code krb5int_dk_cmac_prf(const struct krb5_keytypes *ktp, krb5_key key, const krb5_data *in, krb5_data *out) { krb5_crypto_iov iov; krb5_data prfconst = make_data("prf", 3); krb5_key kp = NULL; krb5_error_code ret; if (ktp->prf_length != ktp->enc->block_size) return KRB5_BAD_MSIZE; iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = *in; /* Derive a key using the PRF constant. */ ret = krb5int_derive_key(ktp->enc, key, &kp, &prfconst, DERIVE_SP800_108_CMAC); if (ret != 0) goto cleanup; /* PRF is CMAC of input */ ret = krb5int_cmac_checksum(ktp->enc, kp, &iov, 1, out); if (ret != 0) goto cleanup; cleanup: krb5_k_free_key(NULL, kp); 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 KRB5_CALLCONV krb5_auth_con_setrecvsubkey_k(krb5_context ctx, krb5_auth_context ac, krb5_key key) { krb5_k_free_key(ctx, ac->recv_subkey); ac->recv_subkey = key; krb5_k_reference_key(ctx, key); return 0; }
static krb5_error_code kg_copy_keys(krb5_context context, krb5_gss_ctx_id_rec *ctx, krb5_key subkey) { krb5_error_code code; krb5_k_free_key(context, ctx->enc); ctx->enc = NULL; code = krb5_k_create_key(context, &subkey->keyblock, &ctx->enc); if (code != 0) return code; krb5_k_free_key(context, ctx->seq); ctx->seq = NULL; code = krb5_k_create_key(context, &subkey->keyblock, &ctx->seq); if (code != 0) return code; return 0; }
krb5_error_code KRB5_CALLCONV krb5_auth_con_setrecvsubkey(krb5_context ctx, krb5_auth_context ac, krb5_keyblock *keyblock) { if (ac->recv_subkey != NULL) krb5_k_free_key(ctx, ac->recv_subkey); ac->recv_subkey = NULL; if (keyblock != NULL) return krb5_k_create_key(ctx, keyblock, &ac->recv_subkey); else return 0; }
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; }
static void enc() { krb5_key k; krb5_crypto_iov iov; krb5_data cdata = make_data(cipher, 16); iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = make_data(plain, 16); krb5_k_create_key(NULL, &enc_key, &k); /* cbc-mac is the same as block encryption for a single block. */ krb5int_camellia_cbc_mac(k, &iov, 1, &ivec, &cdata); krb5_k_free_key(NULL, k); }
krb5_error_code KRB5_CALLCONV krb5_c_encrypt(krb5_context context, const krb5_keyblock *keyblock, krb5_keyusage usage, const krb5_data *cipher_state, const krb5_data *input, krb5_enc_data *output) { krb5_key key; krb5_error_code ret; ret = krb5_k_create_key(context, keyblock, &key); if (ret != 0) return ret; ret = krb5_k_encrypt(context, key, usage, cipher_state, input, output); krb5_k_free_key(context, key); return ret; }
/* Encrypt or decrypt using a keyblock. */ static krb5_error_code keyblock_crypt(const struct krb5_enc_provider *enc, krb5_keyblock *keyblock, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { krb5_error_code ret; krb5_key key; ret = krb5_k_create_key(NULL, keyblock, &key); if (ret != 0) return ret; /* Works for encryption or decryption since arcfour is a stream cipher. */ ret = enc->encrypt(key, ivec, data, num_data); krb5_k_free_key(NULL, key); return ret; }
krb5_error_code KRB5_CALLCONV krb5_c_decrypt_iov(krb5_context context, const krb5_keyblock *keyblock, krb5_keyusage usage, const krb5_data *cipher_state, krb5_crypto_iov *data, size_t num_data) { krb5_key key; krb5_error_code ret; ret = krb5_k_create_key(context, keyblock, &key); if (ret != 0) return ret; ret = krb5_k_decrypt_iov(context, key, usage, cipher_state, data, num_data); krb5_k_free_key(context, key); return ret; }
/* Our DR function, a simple wrapper around krb5int_derive_random(). */ static krb5_error_code dr(const struct krb5_enc_provider *enc, const krb5_keyblock *inkey, unsigned char *out, const krb5_data *in_constant) { krb5_data outdata = make_data(out, enc->keybytes); krb5_key key = NULL; krb5_error_code ret; ret = krb5_k_create_key(NULL, inkey, &key); if (ret != 0) return ret; ret = krb5int_derive_random(enc, key, &outdata, in_constant, DERIVE_RFC3961); krb5_k_free_key(NULL, key); return ret; }
krb5_error_code KRB5_CALLCONV krb5_c_make_checksum(krb5_context context, krb5_cksumtype cksumtype, const krb5_keyblock *keyblock, krb5_keyusage usage, const krb5_data *input, krb5_checksum *cksum) { krb5_key key = NULL; krb5_error_code ret; if (keyblock != NULL) { ret = krb5_k_create_key(context, keyblock, &key); if (ret != 0) return ret; } ret = krb5_k_make_checksum(context, cksumtype, key, usage, input, cksum); krb5_k_free_key(context, key); return ret; }
krb5_error_code krb5int_confounder_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_error_code ret; krb5_data conf, hashval; krb5_key xorkey = NULL; krb5_crypto_iov *hash_iov, iov; size_t blocksize = ctp->enc->block_size, hashsize = ctp->hash->hashsize; /* Partition the output buffer into confounder and hash. */ conf = make_data(output->data, blocksize); hashval = make_data(output->data + blocksize, hashsize); /* Create the confounder. */ ret = krb5_c_random_make_octets(NULL, &conf); if (ret != 0) return ret; ret = mk_xorkey(key, &xorkey); if (ret) return ret; /* 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 = conf; memcpy(hash_iov + 1, data, num_data * sizeof(krb5_crypto_iov)); ret = ctp->hash->hash(hash_iov, num_data + 1, &hashval); if (ret != 0) goto cleanup; /* Confounder and hash are in output buffer; encrypt them in place. */ iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = *output; ret = ctp->enc->encrypt(xorkey, NULL, &iov, 1); cleanup: free(hash_iov); krb5_k_free_key(NULL, xorkey); return ret; }
int main(int argc, char **argv) { #ifdef CAMELLIA krb5_context context = NULL; krb5_keyblock keyblock; krb5_key key; const struct krb5_enc_provider *enc = &krb5int_enc_camellia128; krb5_crypto_iov iov; unsigned char resultbuf[16]; krb5_data result = make_data(resultbuf, 16); /* Create the example key. */ keyblock.magic = KV5M_KEYBLOCK; keyblock.enctype = ENCTYPE_CAMELLIA128_CTS_CMAC; keyblock.length = 16; keyblock.contents = keybytes; assert(krb5_k_create_key(context, &keyblock, &key) == 0); /* Example 1. */ iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = make_data(input, 0); assert(krb5int_cmac_checksum(enc, key, &iov, 1, &result) == 0); check_result("example 1", resultbuf, cmac1); /* Example 2. */ iov.data.length = 16; assert(krb5int_cmac_checksum(enc, key, &iov, 1, &result) == 0); check_result("example 2", resultbuf, cmac2); /* Example 3. */ iov.data.length = 40; assert(krb5int_cmac_checksum(enc, key, &iov, 1, &result) == 0); check_result("example 3", resultbuf, cmac3); /* Example 4. */ iov.data.length = 64; assert(krb5int_cmac_checksum(enc, key, &iov, 1, &result) == 0); check_result("example 4", resultbuf, cmac4); printf("All CMAC tests passed.\n"); krb5_k_free_key(context, key); #endif /* CAMELLIA */ return 0; }
static krb5_error_code hmac1(const struct krb5_hash_provider *h, krb5_keyblock *key, krb5_data *in, krb5_data *out) { char tmp[40]; size_t blocksize, hashsize; krb5_error_code err; krb5_key k; krb5_crypto_iov iov; krb5_data d; printk(" test key", key); blocksize = h->blocksize; hashsize = h->hashsize; if (hashsize > sizeof(tmp)) abort(); if (key->length > blocksize) { iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = make_data(key->contents, key->length); d = make_data(tmp, hashsize); err = h->hash(&iov, 1, &d); if (err) { com_err(whoami, err, "hashing key before calling hmac"); exit(1); } key->length = d.length; key->contents = (krb5_octet *) d.data; printk(" pre-hashed key", key); } printd(" hmac input", in); krb5_k_create_key(NULL, key, &k); iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = *in; err = krb5int_hmac(h, k, &iov, 1, out); krb5_k_free_key(NULL, k); if (err == 0) printd(" hmac output", out); return err; }
/* Decode a reply to produce the clear-text output. */ static krb5_error_code get_clear_result(krb5_context context, krb5_auth_context auth_context, const krb5_data *packet, krb5_data **clear_out, krb5_boolean *is_error_out) { krb5_error_code ret; char *ptr, *end = packet->data + packet->length; unsigned int plen, vno, aplen; krb5_data ap_rep, cipher, error; krb5_ap_rep_enc_part *ap_rep_enc; krb5_replay_data replay; krb5_key send_subkey = NULL; krb5_data clear = empty_data(); *clear_out = NULL; *is_error_out = FALSE; /* Check for an unframed KRB-ERROR (expected for RFC 3244 requests; also * received from MS AD for version 1 requests). */ if (krb5_is_krb_error(packet)) { *is_error_out = TRUE; return get_error_edata(context, packet, clear_out); } if (packet->length < 6) return KRB5KRB_AP_ERR_MODIFIED; /* Decode and verify the length. */ ptr = packet->data; plen = (*ptr++ & 0xff); plen = (plen << 8) | (*ptr++ & 0xff); if (plen != packet->length) return KRB5KRB_AP_ERR_MODIFIED; /* Decode and verify the version number. */ vno = (*ptr++ & 0xff); vno = (vno << 8) | (*ptr++ & 0xff); if (vno != 1 && vno != 0xff80) return KRB5KDC_ERR_BAD_PVNO; /* Decode and check the AP-REP length. */ aplen = (*ptr++ & 0xff); aplen = (aplen << 8) | (*ptr++ & 0xff); if (aplen > end - ptr) return KRB5KRB_AP_ERR_MODIFIED; /* A zero-length AP-REQ indicates a framed KRB-ERROR response. (Expected * for protocol version 1; specified but unusual for RFC 3244 requests.) */ if (aplen == 0) { *is_error_out = TRUE; error = make_data(ptr, end - ptr); return get_error_edata(context, &error, clear_out); } /* We have an AP-REP. Save send_subkey to later smash recv_subkey. */ ret = krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey); if (ret) return ret; /* Verify the AP-REP. */ ap_rep = make_data(ptr, aplen); ptr += ap_rep.length; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) goto cleanup; krb5_free_ap_rep_enc_part(context, ap_rep_enc); /* Smash recv_subkey to be send_subkey, per spec. */ ret = krb5_auth_con_setrecvsubkey_k(context, auth_context, send_subkey); if (ret) goto cleanup; /* Extract and decrypt the result. */ cipher = make_data(ptr, end - ptr); ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay); if (ret) goto cleanup; ret = krb5_copy_data(context, &clear, clear_out); if (ret) goto cleanup; *is_error_out = FALSE; cleanup: krb5_k_free_key(context, send_subkey); krb5_free_data_contents(context, &clear); return ret; }
krb5_error_code KRB5_CALLCONV krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context, krb5_flags ap_req_options, krb5_data *in_data, krb5_creds *in_creds, krb5_data *outbuf) { krb5_error_code retval; krb5_checksum checksum; krb5_checksum *checksump = 0; krb5_auth_context new_auth_context; krb5_enctype *desired_etypes = NULL; krb5_ap_req request; krb5_data *scratch = 0; krb5_data *toutbuf; request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK; request.authenticator.ciphertext.data = NULL; request.ticket = 0; if (!in_creds->ticket.length) return(KRB5_NO_TKT_SUPPLIED); if ((ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) && !(ap_req_options & AP_OPTS_MUTUAL_REQUIRED)) return(EINVAL); /* we need a native ticket */ if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket))) return(retval); /* verify that the ticket is not expired */ if ((retval = krb5int_validate_times(context, &in_creds->times)) != 0) goto cleanup; /* generate auth_context if needed */ if (*auth_context == NULL) { if ((retval = krb5_auth_con_init(context, &new_auth_context))) goto cleanup; *auth_context = new_auth_context; } if ((*auth_context)->key != NULL) { krb5_k_free_key(context, (*auth_context)->key); (*auth_context)->key = NULL; } /* set auth context keyblock */ if ((retval = krb5_k_create_key(context, &in_creds->keyblock, &((*auth_context)->key)))) goto cleanup; /* generate seq number if needed */ if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && ((*auth_context)->local_seq_number == 0)) { if ((retval = krb5_generate_seq_number(context, &in_creds->keyblock, &(*auth_context)->local_seq_number))) goto cleanup; } /* generate subkey if needed */ if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) { retval = k5_generate_and_save_subkey(context, *auth_context, &in_creds->keyblock, in_creds->keyblock.enctype); if (retval) goto cleanup; } if (!in_data && (*auth_context)->checksum_func) { retval = (*auth_context)->checksum_func( context, *auth_context, (*auth_context)->checksum_func_data, &in_data); if (retval) goto cleanup; } if (in_data) { if ((*auth_context)->req_cksumtype == 0x8003) { /* XXX Special hack for GSSAPI */ checksum.checksum_type = 0x8003; checksum.length = in_data->length; checksum.contents = (krb5_octet *) in_data->data; } else { krb5_enctype enctype = krb5_k_key_enctype(context, (*auth_context)->key); krb5_cksumtype cksumtype = ap_req_cksum(context, *auth_context, enctype); if ((retval = krb5_k_make_checksum(context, cksumtype, (*auth_context)->key, KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM, in_data, &checksum))) goto cleanup_cksum; } checksump = &checksum; } /* Generate authenticator */ if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof( krb5_authenticator))) == NULL) { retval = ENOMEM; goto cleanup_cksum; } if (ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) { if ((*auth_context)->permitted_etypes == NULL) { retval = krb5_get_tgs_ktypes(context, in_creds->server, &desired_etypes); if (retval) goto cleanup_cksum; } else desired_etypes = (*auth_context)->permitted_etypes; } TRACE_MK_REQ(context, in_creds, (*auth_context)->local_seq_number, (*auth_context)->send_subkey, &in_creds->keyblock); if ((retval = generate_authenticator(context, (*auth_context)->authentp, in_creds->client, checksump, (*auth_context)->send_subkey, (*auth_context)->local_seq_number, in_creds->authdata, (*auth_context)->ad_context, desired_etypes, in_creds->keyblock.enctype))) goto cleanup_cksum; /* encode the authenticator */ if ((retval = encode_krb5_authenticator((*auth_context)->authentp, &scratch))) goto cleanup_cksum; /* call the encryption routine */ if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock, KRB5_KEYUSAGE_AP_REQ_AUTH, scratch, &request.authenticator))) goto cleanup_cksum; if ((retval = encode_krb5_ap_req(&request, &toutbuf))) goto cleanup_cksum; *outbuf = *toutbuf; free(toutbuf); cleanup_cksum: /* Null out these fields, to prevent pointer sharing problems; * they were supplied by the caller */ if ((*auth_context)->authentp != NULL) { (*auth_context)->authentp->client = NULL; (*auth_context)->authentp->checksum = NULL; } if (checksump && checksump->checksum_type != 0x8003) free(checksump->contents); cleanup: if (desired_etypes && desired_etypes != (*auth_context)->permitted_etypes) free(desired_etypes); if (request.ticket) krb5_free_ticket(context, request.ticket); if (request.authenticator.ciphertext.data) { (void) memset(request.authenticator.ciphertext.data, 0, request.authenticator.ciphertext.length); free(request.authenticator.ciphertext.data); } if (scratch) { memset(scratch->data, 0, scratch->length); free(scratch->data); free(scratch); } return retval; }
static krb5_error_code rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context, const krb5_ap_req *req, krb5_const_principal server, krb5_keytab keytab, krb5_flags *ap_req_options, krb5_ticket **ticket, int check_valid_flag) { krb5_error_code retval = 0; krb5_enctype *desired_etypes = NULL; int desired_etypes_len = 0; int rfc4537_etypes_len = 0; krb5_enctype *permitted_etypes = NULL; int permitted_etypes_len = 0; krb5_keyblock decrypt_key; decrypt_key.enctype = ENCTYPE_NULL; decrypt_key.contents = NULL; req->ticket->enc_part2 = NULL; /* if (req->ap_options & AP_OPTS_USE_SESSION_KEY) do we need special processing here ? */ /* decrypt the ticket */ if ((*auth_context)->key) { /* User to User authentication */ if ((retval = krb5_decrypt_tkt_part(context, &(*auth_context)->key->keyblock, req->ticket))) goto cleanup; if (check_valid_flag) { decrypt_key = (*auth_context)->key->keyblock; (*auth_context)->key->keyblock.contents = NULL; } krb5_k_free_key(context, (*auth_context)->key); (*auth_context)->key = NULL; } else { retval = decrypt_ticket(context, req, server, keytab, check_valid_flag ? &decrypt_key : NULL); if (retval) goto cleanup; } TRACE_RD_REQ_TICKET(context, req->ticket->enc_part2->client, req->ticket->server, req->ticket->enc_part2->session); /* XXX this is an evil hack. check_valid_flag is set iff the call is not from inside the kdc. we can use this to determine which key usage to use */ #ifndef LEAN_CLIENT if ((retval = decrypt_authenticator(context, req, &((*auth_context)->authentp), check_valid_flag))) goto cleanup; #endif if (!krb5_principal_compare(context, (*auth_context)->authentp->client, req->ticket->enc_part2->client)) { retval = KRB5KRB_AP_ERR_BADMATCH; goto cleanup; } if ((*auth_context)->remote_addr && !krb5_address_search(context, (*auth_context)->remote_addr, req->ticket->enc_part2->caddrs)) { retval = KRB5KRB_AP_ERR_BADADDR; goto cleanup; } if (!server) { server = req->ticket->server; } /* Get an rcache if necessary. */ if (((*auth_context)->rcache == NULL) && ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && server) { if ((retval = krb5_get_server_rcache(context, krb5_princ_component(context,server,0), &(*auth_context)->rcache))) goto cleanup; } /* okay, now check cross-realm policy */ #if defined(_SINGLE_HOP_ONLY) /* Single hop cross-realm tickets only */ { krb5_transited *trans = &(req->ticket->enc_part2->transited); /* If the transited list is empty, then we have at most one hop */ if (trans->tr_contents.length > 0 && trans->tr_contents.data[0]) retval = KRB5KRB_AP_ERR_ILL_CR_TKT; } #elif defined(_NO_CROSS_REALM) /* No cross-realm tickets */ { char * lrealm; krb5_data * realm; krb5_transited * trans; realm = krb5_princ_realm(context, req->ticket->enc_part2->client); trans = &(req->ticket->enc_part2->transited); /* * If the transited list is empty, then we have at most one hop * So we also have to check that the client's realm is the local one */ krb5_get_default_realm(context, &lrealm); if ((trans->tr_contents.length > 0 && trans->tr_contents.data[0]) || !data_eq_string(*realm, lrealm)) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; } free(lrealm); } #else /* Hierarchical Cross-Realm */ { krb5_data * realm; krb5_transited * trans; realm = krb5_princ_realm(context, req->ticket->enc_part2->client); trans = &(req->ticket->enc_part2->transited); /* * If the transited list is not empty, then check that all realms * transited are within the hierarchy between the client's realm * and the local realm. */ if (trans->tr_contents.length > 0 && trans->tr_contents.data[0]) { retval = krb5_check_transited_list(context, &(trans->tr_contents), realm, krb5_princ_realm (context,server)); } } #endif if (retval) goto cleanup; /* only check rcache if sender has provided one---some services may not be able to use replay caches (such as datagram servers) */ if ((*auth_context)->rcache) { krb5_donot_replay rep; krb5_tkt_authent tktauthent; tktauthent.ticket = req->ticket; tktauthent.authenticator = (*auth_context)->authentp; if (!(retval = krb5_auth_to_rep(context, &tktauthent, &rep))) { retval = krb5_rc_hash_message(context, &req->authenticator.ciphertext, &rep.msghash); if (!retval) { retval = krb5_rc_store(context, (*auth_context)->rcache, &rep); free(rep.msghash); } free(rep.server); free(rep.client); } if (retval) goto cleanup; } retval = krb5int_validate_times(context, &req->ticket->enc_part2->times); if (retval != 0) goto cleanup; if ((retval = krb5_check_clockskew(context, (*auth_context)->authentp->ctime))) goto cleanup; if (check_valid_flag) { if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) { retval = KRB5KRB_AP_ERR_TKT_INVALID; goto cleanup; } if ((retval = krb5_authdata_context_init(context, &(*auth_context)->ad_context))) goto cleanup; if ((retval = krb5int_authdata_verify(context, (*auth_context)->ad_context, AD_USAGE_MASK, auth_context, &decrypt_key, req))) goto cleanup; } /* read RFC 4537 etype list from sender */ retval = decode_etype_list(context, (*auth_context)->authentp, &desired_etypes, &rfc4537_etypes_len); if (retval != 0) goto cleanup; if (desired_etypes == NULL) desired_etypes = (krb5_enctype *)calloc(4, sizeof(krb5_enctype)); else desired_etypes = (krb5_enctype *)realloc(desired_etypes, (rfc4537_etypes_len + 4) * sizeof(krb5_enctype)); if (desired_etypes == NULL) { retval = ENOMEM; goto cleanup; } desired_etypes_len = rfc4537_etypes_len; /* * RFC 4537: * * If the EtypeList is present and the server prefers an enctype from * the client's enctype list over that of the AP-REQ authenticator * subkey (if that is present) or the service ticket session key, the * server MUST create a subkey using that enctype. This negotiated * subkey is sent in the subkey field of AP-REP message, and it is then * used as the protocol key or base key [RFC3961] for subsequent * communication. * * If the enctype of the ticket session key is included in the enctype * list sent by the client, it SHOULD be the last on the list; * otherwise, this enctype MUST NOT be negotiated if it was not included * in the list. * * The second paragraph does appear to contradict the first with respect * to whether it is legal to negotiate the ticket session key type if it * is absent in the EtypeList. A literal reading suggests that we can use * the AP-REQ subkey enctype. Also a client has no way of distinguishing * a server that does not RFC 4537 from one that has chosen the same * enctype as the ticket session key for the acceptor subkey, surely. */ if ((*auth_context)->authentp->subkey != NULL) { desired_etypes[desired_etypes_len++] = (*auth_context)->authentp->subkey->enctype; } desired_etypes[desired_etypes_len++] = req->ticket->enc_part2->session->enctype; desired_etypes[desired_etypes_len] = ENCTYPE_NULL; if (((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) == 0) { if ((*auth_context)->permitted_etypes != NULL) { permitted_etypes = (*auth_context)->permitted_etypes; } else { retval = krb5_get_permitted_enctypes(context, &permitted_etypes); if (retval != 0) goto cleanup; } permitted_etypes_len = krb5int_count_etypes(permitted_etypes); } else { permitted_etypes = NULL; permitted_etypes_len = 0; } /* check if the various etypes are permitted */ retval = negotiate_etype(context, desired_etypes, desired_etypes_len, rfc4537_etypes_len, permitted_etypes, permitted_etypes_len, &(*auth_context)->negotiated_etype); if (retval != 0) goto cleanup; TRACE_RD_REQ_NEGOTIATED_ETYPE(context, (*auth_context)->negotiated_etype); assert((*auth_context)->negotiated_etype != ENCTYPE_NULL); (*auth_context)->remote_seq_number = (*auth_context)->authentp->seq_number; if ((*auth_context)->authentp->subkey) { TRACE_RD_REQ_SUBKEY(context, (*auth_context)->authentp->subkey); if ((retval = krb5_k_create_key(context, (*auth_context)->authentp->subkey, &((*auth_context)->recv_subkey)))) goto cleanup; retval = krb5_k_create_key(context, (*auth_context)->authentp->subkey, &((*auth_context)->send_subkey)); if (retval) { krb5_k_free_key(context, (*auth_context)->recv_subkey); (*auth_context)->recv_subkey = NULL; goto cleanup; } } else { (*auth_context)->recv_subkey = 0; (*auth_context)->send_subkey = 0; } if ((retval = krb5_k_create_key(context, req->ticket->enc_part2->session, &((*auth_context)->key)))) goto cleanup; debug_log_authz_data("ticket", req->ticket->enc_part2->authorization_data); /* * If not AP_OPTS_MUTUAL_REQUIRED then and sequence numbers are used * then the default sequence number is the one's complement of the * sequence number sent ot us. */ if ((!(req->ap_options & AP_OPTS_MUTUAL_REQUIRED)) && (*auth_context)->remote_seq_number) { (*auth_context)->local_seq_number ^= (*auth_context)->remote_seq_number; } if (ticket) if ((retval = krb5_copy_ticket(context, req->ticket, ticket))) goto cleanup; if (ap_req_options) { *ap_req_options = req->ap_options & AP_OPTS_WIRE_MASK; if (rfc4537_etypes_len != 0) *ap_req_options |= AP_OPTS_ETYPE_NEGOTIATION; if ((*auth_context)->negotiated_etype != krb5_k_key_enctype(context, (*auth_context)->key)) *ap_req_options |= AP_OPTS_USE_SUBKEY; } retval = 0; cleanup: if (desired_etypes != NULL) free(desired_etypes); if (permitted_etypes != NULL && permitted_etypes != (*auth_context)->permitted_etypes) free(permitted_etypes); if (retval) { /* only free if we're erroring out...otherwise some applications will need the output. */ if (req->ticket->enc_part2) krb5_free_enc_tkt_part(context, req->ticket->enc_part2); req->ticket->enc_part2 = NULL; } if (check_valid_flag) krb5_free_keyblock_contents(context, &decrypt_key); return retval; }
int main () { krb5_context context = 0; krb5_data in, in2, out, out2, check, check2, state, signdata; krb5_crypto_iov iov[5]; int i, j, pos; unsigned int dummy; size_t len; krb5_enc_data enc_out, enc_out2; krb5_keyblock *keyblock; krb5_key key; memset(iov, 0, sizeof(iov)); in.data = "This is a test.\n"; in.length = strlen (in.data); in2.data = "This is another test.\n"; in2.length = strlen (in2.data); test ("Seeding random number generator", krb5_c_random_seed (context, &in)); /* Set up output buffers. */ out.data = malloc(2048); out2.data = malloc(2048); check.data = malloc(2048); check2.data = malloc(2048); if (out.data == NULL || out2.data == NULL || check.data == NULL || check2.data == NULL) abort(); out.magic = KV5M_DATA; out.length = 2048; out2.magic = KV5M_DATA; out2.length = 2048; check.length = 2048; check2.length = 2048; for (i = 0; interesting_enctypes[i]; i++) { krb5_enctype enctype = interesting_enctypes [i]; printf ("Testing enctype %d\n", enctype); test ("Initializing a keyblock", krb5_init_keyblock (context, enctype, 0, &keyblock)); test ("Generating random keyblock", krb5_c_make_random_key (context, enctype, keyblock)); test ("Creating opaque key from keyblock", krb5_k_create_key (context, keyblock, &key)); enc_out.ciphertext = out; enc_out2.ciphertext = out2; /* We use an intermediate `len' because size_t may be different size than `int' */ krb5_c_encrypt_length (context, keyblock->enctype, in.length, &len); enc_out.ciphertext.length = len; /* Encrypt, decrypt, and see if we got the plaintext back again. */ test ("Encrypting (c)", krb5_c_encrypt (context, keyblock, 7, 0, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Decrypting", krb5_c_decrypt (context, keyblock, 7, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); /* Try again with the opaque-key-using variants. */ memset(out.data, 0, out.length); test ("Encrypting (k)", krb5_k_encrypt (context, key, 7, 0, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Decrypting", krb5_k_decrypt (context, key, 7, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); /* Check if this enctype supports IOV encryption. */ if ( krb5_c_crypto_length(context, keyblock->enctype, KRB5_CRYPTO_TYPE_HEADER, &dummy) == 0 ){ /* Set up iovecs for stream decryption. */ memcpy(out2.data, enc_out.ciphertext.data, enc_out.ciphertext.length); iov[0].flags= KRB5_CRYPTO_TYPE_STREAM; iov[0].data.data = out2.data; iov[0].data.length = enc_out.ciphertext.length; iov[1].flags = KRB5_CRYPTO_TYPE_DATA; /* Decrypt the encrypted data from above and check it. */ test("IOV stream decrypting (c)", krb5_c_decrypt_iov( context, keyblock, 7, 0, iov, 2)); test("Comparing results", compare_results(&in, &iov[1].data)); /* Try again with the opaque-key-using variant. */ memcpy(out2.data, enc_out.ciphertext.data, enc_out.ciphertext.length); test("IOV stream decrypting (k)", krb5_k_decrypt_iov( context, key, 7, 0, iov, 2)); test("Comparing results", compare_results(&in, &iov[1].data)); /* Set up iovecs for AEAD encryption. */ signdata.magic = KV5M_DATA; signdata.data = (char *) "This should be signed"; signdata.length = strlen(signdata.data); iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data = in; /*We'll need to copy memory before encrypt*/ iov[2].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; iov[2].data = signdata; iov[3].flags = KRB5_CRYPTO_TYPE_PADDING; iov[4].flags = KRB5_CRYPTO_TYPE_TRAILER; /* "Allocate" data for the iovec buffers from the "out" buffer. */ test("Setting up iov lengths", krb5_c_crypto_length_iov(context, keyblock->enctype, iov, 5)); for (j=0,pos=0; j <= 4; j++ ){ if (iov[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) continue; iov[j].data.data = &out.data[pos]; pos += iov[j].data.length; } assert (iov[1].data.length == in.length); memcpy(iov[1].data.data, in.data, in.length); /* Encrypt and decrypt in place, and check the result. */ test("iov encrypting (c)", krb5_c_encrypt_iov(context, keyblock, 7, 0, iov, 5)); assert(iov[1].data.length == in.length); display("Header", &iov[0].data); display("Data", &iov[1].data); display("Padding", &iov[3].data); display("Trailer", &iov[4].data); test("iov decrypting", krb5_c_decrypt_iov(context, keyblock, 7, 0, iov, 5)); test("Comparing results", compare_results(&in, &iov[1].data)); /* Try again with opaque-key-using variants. */ test("iov encrypting (k)", krb5_k_encrypt_iov(context, key, 7, 0, iov, 5)); assert(iov[1].data.length == in.length); display("Header", &iov[0].data); display("Data", &iov[1].data); display("Padding", &iov[3].data); display("Trailer", &iov[4].data); test("iov decrypting", krb5_k_decrypt_iov(context, key, 7, 0, iov, 5)); test("Comparing results", compare_results(&in, &iov[1].data)); } enc_out.ciphertext.length = out.length; check.length = 2048; test ("init_state", krb5_c_init_state (context, keyblock, 7, &state)); test ("Encrypting with state", krb5_c_encrypt (context, keyblock, 7, &state, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Encrypting again with state", krb5_c_encrypt (context, keyblock, 7, &state, &in2, &enc_out2)); display ("Enc output", &enc_out2.ciphertext); test ("free_state", krb5_c_free_state (context, keyblock, &state)); test ("init_state", krb5_c_init_state (context, keyblock, 7, &state)); test ("Decrypting with state", krb5_c_decrypt (context, keyblock, 7, &state, &enc_out, &check)); test ("Decrypting again with state", krb5_c_decrypt (context, keyblock, 7, &state, &enc_out2, &check2)); test ("free_state", krb5_c_free_state (context, keyblock, &state)); test ("Comparing", compare_results (&in, &check)); test ("Comparing", compare_results (&in2, &check2)); krb5_free_keyblock (context, keyblock); krb5_k_free_key (context, key); } /* Test the RC4 decrypt fallback from key usage 9 to 8. */ test ("Initializing an RC4 keyblock", krb5_init_keyblock (context, ENCTYPE_ARCFOUR_HMAC, 0, &keyblock)); test ("Generating random RC4 key", krb5_c_make_random_key (context, ENCTYPE_ARCFOUR_HMAC, keyblock)); enc_out.ciphertext = out; krb5_c_encrypt_length (context, keyblock->enctype, in.length, &len); enc_out.ciphertext.length = len; check.length = 2048; test ("Encrypting with RC4 key usage 8", krb5_c_encrypt (context, keyblock, 8, 0, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Decrypting with RC4 key usage 9", krb5_c_decrypt (context, keyblock, 9, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); krb5_free_keyblock (context, keyblock); free(out.data); free(out2.data); free(check.data); free(check2.data); return 0; }
krb5_error_code kg_setup_keys(krb5_context context, krb5_gss_ctx_id_rec *ctx, krb5_key subkey, krb5_cksumtype *cksumtype) { krb5_error_code code; krb5int_access kaccess; assert(ctx != NULL); assert(subkey != NULL); *cksumtype = 0; ctx->proto = 0; if (ctx->enc == NULL) { ctx->signalg = -1; ctx->sealalg = -1; } code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION); if (code != 0) return code; code = (*kaccess.mandatory_cksumtype)(context, subkey->keyblock.enctype, cksumtype); if (code != 0) return code; switch (subkey->keyblock.enctype) { case ENCTYPE_DES_CBC_MD5: case ENCTYPE_DES_CBC_MD4: case ENCTYPE_DES_CBC_CRC: krb5_k_free_key(context, ctx->seq); code = krb5_k_create_key(context, &subkey->keyblock, &ctx->seq); if (code != 0) return code; krb5_k_free_key(context, ctx->enc); code = kg_derive_des_enc_key(context, subkey, &ctx->enc); if (code != 0) return code; ctx->enc->keyblock.enctype = ENCTYPE_DES_CBC_RAW; ctx->seq->keyblock.enctype = ENCTYPE_DES_CBC_RAW; ctx->signalg = SGN_ALG_DES_MAC_MD5; ctx->cksum_size = 8; ctx->sealalg = SEAL_ALG_DES; break; case ENCTYPE_DES3_CBC_SHA1: code = kg_copy_keys(context, ctx, subkey); if (code != 0) return code; ctx->enc->keyblock.enctype = ENCTYPE_DES3_CBC_RAW; ctx->seq->keyblock.enctype = ENCTYPE_DES3_CBC_RAW; ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD; ctx->cksum_size = 20; ctx->sealalg = SEAL_ALG_DES3KD; break; case ENCTYPE_ARCFOUR_HMAC: case ENCTYPE_ARCFOUR_HMAC_EXP: code = kg_copy_keys(context, ctx, subkey); if (code != 0) return code; ctx->signalg = SGN_ALG_HMAC_MD5; ctx->cksum_size = 8; ctx->sealalg = SEAL_ALG_MICROSOFT_RC4; break; default: ctx->proto = 1; break; } return 0; }
static void test_cts() { static const char input[4*16] = "I would like the General Gau's Chicken, please, and wonton soup."; static const unsigned char aeskey[16] = "chicken teriyaki"; static const int lengths[] = { 17, 31, 32, 47, 48, 64 }; unsigned int i; char outbuf[64], encivbuf[16], decivbuf[16]; krb5_crypto_iov iov; krb5_data in, enciv, deciv; krb5_keyblock keyblock; krb5_key key; krb5_error_code err; iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data.data = outbuf; in.data = (char *)input; enciv.length = deciv.length = 16; enciv.data = encivbuf; deciv.data = decivbuf; keyblock.contents = (krb5_octet *)aeskey; keyblock.length = 16; keyblock.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96; err = krb5_k_create_key(NULL, &keyblock, &key); if (err) { printf("error %ld from krb5_k_create_key\n", (long)err); exit(1); } memset(enciv.data, 0, 16); printk("AES 128-bit key", &keyblock); for (i = 0; i < sizeof(lengths)/sizeof(lengths[0]); i++) { memset(enciv.data, 0, 16); memset(deciv.data, 0, 16); printf("\n"); iov.data.length = in.length = lengths[i]; memcpy(outbuf, input, lengths[i]); printd("IV", &enciv); err = krb5int_aes_encrypt(key, &enciv, &iov, 1); if (err) { printf("error %ld from krb5int_aes_encrypt\n", (long)err); exit(1); } printd("Input", &in); printd("Output", &iov.data); printd("Next IV", &enciv); err = krb5int_aes_decrypt(key, &deciv, &iov, 1); if (err) { printf("error %ld from krb5int_aes_decrypt\n", (long)err); exit(1); } if (memcmp(outbuf, input, lengths[i]) != 0) { printd("Decryption result DOESN'T MATCH", &iov.data); exit(1); } if (memcmp(enciv.data, deciv.data, 16)) { printd("Decryption IV result DOESN'T MATCH", &deciv); exit(1); } } krb5_k_free_key(NULL, key); }
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; }
/* Import a lucid context structure, creating a krb5 GSS context structure * sufficient for use by by wrap/unwrap/get_mic/verify_mic operations. */ static krb5_error_code import_lucid_sec_context_v1(const gss_krb5_lucid_context_v1_t *lctx, gss_ctx_id_t *context_handle_out) { krb5_error_code ret; krb5_gss_ctx_id_t gctx; OM_uint32 tmpmin; krb5_key key = NULL; gctx = k5alloc(sizeof(*gctx), &ret); if (gctx == NULL) return ret; gctx->initiate = lctx->initiate; gctx->krb_times.endtime = lctx->endtime; gctx->seq_send = lctx->send_seq; gctx->seq_recv = lctx->recv_seq; gctx->proto = lctx->protocol; if (lctx->protocol == 0) { /* Ignore sign_alg and seal_alg since they follow from the enctype. */ ret = lkey_to_key(&lctx->rfc1964_kd.ctx_key, &key); if (ret) goto cleanup; /* For raw enctypes, choose an enctype expected by kg_setup_keys. */ if (key->keyblock.enctype == ENCTYPE_DES_CBC_RAW) key->keyblock.enctype = ENCTYPE_DES_CBC_CRC; else if (key->keyblock.enctype == ENCTYPE_DES3_CBC_RAW) key->keyblock.enctype = ENCTYPE_DES3_CBC_SHA1; ret = kg_setup_keys(NULL, gctx, key, &gctx->cksumtype); if (ret) goto cleanup; if (gctx->proto != 0) { /* ctx_key did not have a pre-CFX enctype. */ ret = EINVAL; goto cleanup; } } else if (lctx->protocol == 1) { ret = lkey_to_key(&lctx->cfx_kd.ctx_key, &gctx->subkey); if (ret) goto cleanup; ret = get_cksumtype(gctx->subkey, &gctx->cksumtype); if (ret) goto cleanup; if (lctx->cfx_kd.have_acceptor_subkey) { gctx->have_acceptor_subkey = 1; ret = lkey_to_key(&lctx->cfx_kd.acceptor_subkey, &gctx->acceptor_subkey); if (ret) goto cleanup; ret = get_cksumtype(gctx->acceptor_subkey, &gctx->acceptor_subkey_cksumtype); if (ret) goto cleanup; } } gctx->seed_init = 0; gctx->established = 1; gctx->mech_used = (gss_OID_desc *)gss_mech_krb5; /* * The lucid context doesn't convey the gss_flags which indicate whether * the protocol needs replay or sequence protection. Assume we don't * (because RPCSEC_GSS doesn't). */ g_seqstate_init(&gctx->seqstate, gctx->seq_recv, 0, 0, gctx->proto); *context_handle_out = (gss_ctx_id_t)gctx; gctx = NULL; cleanup: krb5_k_free_key(NULL, key); krb5_gss_delete_sec_context(&tmpmin, (gss_ctx_id_t *)&gctx, NULL); return ret; }
static krb5_error_code pbkdf2_string_to_key(const struct krb5_keytypes *ktp, const krb5_data *string, const krb5_data *salt, const krb5_data *pepper, const krb5_data *params, krb5_keyblock *key, enum deriv_alg deriv_alg, unsigned long def_iter_count) { const struct krb5_hash_provider *hash; unsigned long iter_count; krb5_data out; static const krb5_data usage = { KV5M_DATA, 8, "kerberos" }; krb5_key tempkey = NULL; krb5_error_code err; krb5_data sandp = empty_data(); if (params) { unsigned char *p = (unsigned char *) params->data; if (params->length != 4) return KRB5_ERR_BAD_S2K_PARAMS; iter_count = load_32_be(p); /* Zero means 2^32, which is way above what we will accept. Also don't * accept values less than the default, unless we're running tests. */ if (iter_count == 0 || (!k5_allow_weak_pbkdf2iter && iter_count < def_iter_count)) return KRB5_ERR_BAD_S2K_PARAMS; } else iter_count = def_iter_count; /* This is not a protocol specification constraint; this is an implementation limit, which should eventually be controlled by a config file. */ if (iter_count >= MAX_ITERATION_COUNT) return KRB5_ERR_BAD_S2K_PARAMS; /* Use the output keyblock contents for temporary space. */ out.data = (char *) key->contents; out.length = key->length; if (out.length != 16 && out.length != 32) return KRB5_CRYPTO_INTERNAL; if (pepper != NULL) { err = alloc_data(&sandp, pepper->length + 1 + salt->length); if (err) return err; if (pepper->length > 0) memcpy(sandp.data, pepper->data, pepper->length); sandp.data[pepper->length] = '\0'; if (salt->length > 0) memcpy(&sandp.data[pepper->length + 1], salt->data, salt->length); salt = &sandp; } hash = (ktp->hash != NULL) ? ktp->hash : &krb5int_hash_sha1; err = krb5int_pbkdf2_hmac(hash, &out, iter_count, string, salt); if (err) goto cleanup; err = krb5_k_create_key (NULL, key, &tempkey); if (err) goto cleanup; err = krb5int_derive_keyblock(ktp->enc, ktp->hash, tempkey, key, &usage, deriv_alg); cleanup: if (sandp.data) free(sandp.data); if (err) memset (out.data, 0, out.length); krb5_k_free_key (NULL, tempkey); return err; }
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; }