add support for KDC side of DH PKINIT
git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@13158 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
351
kdc/pkinit.c
351
kdc/pkinit.c
@@ -217,6 +217,215 @@ pk_free_client_param(krb5_context context, pk_client_params *client_params)
|
||||
free(client_params);
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
check_dh_params(DH *dh)
|
||||
{
|
||||
/* XXX check the DH parameters come from 1st or 2nd Oeakley Group */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
generate_dh_keyblock(krb5_context context, pk_client_params *client_params,
|
||||
krb5_keyblock *reply_key)
|
||||
{
|
||||
unsigned char *dh_gen_key = NULL;
|
||||
krb5_keyblock key;
|
||||
int dh_gen_keylen;
|
||||
krb5_error_code ret;
|
||||
/* XXX don't hardcode the keytype */
|
||||
krb5_enctype enctype = ETYPE_DES3_CBC_SHA1;
|
||||
|
||||
memset(&key, 0, sizeof(key));
|
||||
|
||||
dh_gen_key = malloc(DH_size(client_params->dh));
|
||||
if (dh_gen_key == NULL) {
|
||||
krb5_set_error_string(context, "malloc: out of memory");
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!DH_generate_key(client_params->dh)) {
|
||||
krb5_set_error_string(context, "Can't generate Diffie-Hellman "
|
||||
"keys (%s)",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
ret = KRB5KRB_ERR_GENERIC;
|
||||
goto out;
|
||||
}
|
||||
if (client_params->dh_public_key == NULL) {
|
||||
krb5_set_error_string(context, "dh_public_key");
|
||||
ret = KRB5KRB_ERR_GENERIC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dh_gen_keylen = DH_compute_key(dh_gen_key,
|
||||
client_params->dh_public_key,
|
||||
client_params->dh);
|
||||
if (dh_gen_keylen == -1) {
|
||||
krb5_set_error_string(context, "Can't compute Diffie-Hellman key (%s)",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
ret = KRB5KRB_ERR_GENERIC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (enctype) {
|
||||
case ETYPE_DES_CBC_CRC:
|
||||
case ETYPE_DES_CBC_MD4:
|
||||
case ETYPE_DES_CBC_MD5:
|
||||
case ETYPE_DES3_CBC_SHA1:
|
||||
case ETYPE_OLD_DES3_CBC_SHA1: {
|
||||
DES_cblock *k;
|
||||
|
||||
ret = krb5_generate_random_keyblock(context, enctype, &key);
|
||||
if (ret)
|
||||
goto out;
|
||||
memset(key.keyvalue.data, 0, key.keyvalue.length);
|
||||
|
||||
if (dh_gen_keylen < key.keyvalue.length) {
|
||||
krb5_set_error_string(context, "Too small key generated by "
|
||||
"Diffie-Hellman mechanism");
|
||||
ret = KRB5KRB_ERR_GENERIC;
|
||||
goto out;
|
||||
}
|
||||
memcpy(key.keyvalue.data, dh_gen_key, key.keyvalue.length);
|
||||
k = key.keyvalue.data;
|
||||
DES_set_odd_parity(&k[0]);
|
||||
switch (enctype) {
|
||||
case ETYPE_DES3_CBC_SHA1:
|
||||
case ETYPE_OLD_DES3_CBC_SHA1:
|
||||
DES_set_odd_parity(&k[1]);
|
||||
DES_set_odd_parity(&k[2]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
krb5_set_error_string(context, "PKINIT DH, unsupported enctype: %d",
|
||||
(int)enctype);
|
||||
ret = KDC_ERROR_KEY_TOO_WEAK;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = krb5_copy_keyblock_contents(context, &key, reply_key);
|
||||
|
||||
out:
|
||||
if (dh_gen_key)
|
||||
free(dh_gen_key);
|
||||
if (key.keyvalue.data)
|
||||
krb5_free_keyblock_contents(context, &key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BIGNUM *
|
||||
any_to_BN(krb5_context context, const char *field, heim_any *f)
|
||||
{
|
||||
unsigned char *p = f->data;
|
||||
ASN1_INTEGER *i;
|
||||
BIGNUM *bn;
|
||||
|
||||
i = d2i_ASN1_INTEGER(NULL, &p, f->length);
|
||||
if (i == NULL) {
|
||||
krb5_set_error_string(context, "PKINIT: failed to decode %s", field);
|
||||
return NULL;
|
||||
}
|
||||
bn = ASN1_INTEGER_to_BN(i, NULL);
|
||||
ASN1_INTEGER_free(i);
|
||||
if (bn == NULL) {
|
||||
krb5_set_error_string(context,
|
||||
"PKINIT: failed to convert %s to bignum",
|
||||
field);
|
||||
return NULL;
|
||||
}
|
||||
return bn;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
get_dh_param(krb5_context context, SubjectPublicKeyInfo *dh_key_info,
|
||||
pk_client_params *client_params)
|
||||
{
|
||||
DomainParameters dhparam;
|
||||
DH *dh = NULL;
|
||||
krb5_error_code ret;
|
||||
int dhret;
|
||||
|
||||
memset(&dhparam, 0, sizeof(dhparam));
|
||||
|
||||
if (oid_cmp(&dh_key_info->algorithm.algorithm, &heim_dhpublicnumber_oid)) {
|
||||
krb5_set_error_string(context,
|
||||
"PKINIT invalid oid in clientPublicValue");
|
||||
return KRB5_BADMSGTYPE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (dh_key_info->algorithm.parameters == NULL) {
|
||||
krb5_set_error_string(context, "PKINIT missing algorithm parameter "
|
||||
"in clientPublicValue");
|
||||
return KRB5_BADMSGTYPE;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = decode_DomainParameters(dh_key_info->algorithm.parameters.data,
|
||||
dh_key_info->algorithm.parameters.length,
|
||||
&dhparam,
|
||||
NULL);
|
||||
if (ret) {
|
||||
krb5_set_error_string(context, "Can't decode algorithm "
|
||||
"parameters in clientPublicValue");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dh = DH_new();
|
||||
if (dh == NULL) {
|
||||
krb5_set_error_string(context, "Cannot create DH structure (%s)",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ret = KRB5_BADMSGTYPE;
|
||||
dh->p = any_to_BN(context, "DH prime", &dhparam.p);
|
||||
if (dh->p == NULL)
|
||||
goto out;
|
||||
dh->g = any_to_BN(context, "DH base", &dhparam.g);
|
||||
if (dh->g == NULL)
|
||||
goto out;
|
||||
dh->q = any_to_BN(context, "DH p-1 factor", &dhparam.q);
|
||||
if (dh->g == NULL)
|
||||
goto out;
|
||||
|
||||
{
|
||||
heim_any glue;
|
||||
glue.data = dh_key_info->subjectPublicKey.data;
|
||||
glue.length = dh_key_info->subjectPublicKey.length;
|
||||
|
||||
client_params->dh_public_key = any_to_BN(context,
|
||||
"subjectPublicKey",
|
||||
&glue);
|
||||
if (client_params->dh_public_key == NULL) {
|
||||
krb5_clear_error_string(context);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (DH_check(dh, &dhret) != 1) {
|
||||
krb5_set_error_string(context, "PKINIT DH data not ok: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
ret = KDC_ERROR_KEY_TOO_WEAK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
client_params->dh = dh;
|
||||
dh = NULL;
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (dh)
|
||||
DH_free(dh);
|
||||
free_DomainParameters(&dhparam);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
pk_rd_padata(krb5_context context,
|
||||
KDC_REQ *req,
|
||||
@@ -312,9 +521,9 @@ pk_rd_padata(krb5_context context,
|
||||
client_params->nonce = ap.pkAuthenticator.nonce;
|
||||
|
||||
if (ap.clientPublicValue) {
|
||||
ret = KRB5KRB_ERR_GENERIC;
|
||||
krb5_set_error_string(context, "PKINIT DH, no support for DH");
|
||||
goto out;
|
||||
ret = get_dh_param(context, ap.clientPublicValue, client_params);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -388,6 +597,7 @@ pk_rd_padata(krb5_context context,
|
||||
static krb5_error_code
|
||||
pk_mk_pa_reply_enckey(krb5_context context,
|
||||
pk_client_params *client_params,
|
||||
const KDC_REQ *req,
|
||||
krb5_keyblock *reply_key,
|
||||
ContentInfo *content_info)
|
||||
{
|
||||
@@ -400,9 +610,9 @@ pk_mk_pa_reply_enckey(krb5_context context,
|
||||
krb5_data buf, sd_data, enc_sd_data;
|
||||
|
||||
krb5_keyblock tmp_key;
|
||||
krb5_enctype enctype = ENCTYPE_DES3_CBC_NONE;
|
||||
krb5_enctype enctype = ETYPE_DES3_CBC_NONE;
|
||||
heim_oid *enc_type_oid = NULL;
|
||||
X509_NAME *issuer_name;
|
||||
heim_oid *enc_key_oid;
|
||||
|
||||
size_t size;
|
||||
|
||||
@@ -412,6 +622,15 @@ pk_mk_pa_reply_enckey(krb5_context context,
|
||||
memset(&tmp_key, 0, sizeof(tmp_key));
|
||||
memset(&ed, 0, sizeof(ed));
|
||||
|
||||
switch (enctype) {
|
||||
case ETYPE_DES3_CBC_NONE:
|
||||
enc_type_oid = &heim_des_ede3_cbc_oid;
|
||||
break;
|
||||
default:
|
||||
krb5_set_error_string(context, "not support for enctype %d", enctype);
|
||||
return KRB5_PROG_KEYTYPE_NOSUPP;
|
||||
}
|
||||
|
||||
{
|
||||
ReplyKeyPack kp;
|
||||
memset(&kp, 0, sizeof(kp));
|
||||
@@ -489,19 +708,20 @@ pk_mk_pa_reply_enckey(krb5_context context,
|
||||
ri->rid.u.issuerAndSerialNumber.issuer.length = buf.length;
|
||||
|
||||
{
|
||||
heim_oid *pk_enc_key_oid;
|
||||
krb5_data enc_tmp_key;
|
||||
|
||||
ret = pk_encrypt_key(context, &tmp_key,
|
||||
X509_get_pubkey(client_params->certificate->cert),
|
||||
&enc_tmp_key,
|
||||
&enc_key_oid);
|
||||
&pk_enc_key_oid);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ri->encryptedKey.length = enc_tmp_key.length;
|
||||
ri->encryptedKey.data = enc_tmp_key.data;
|
||||
|
||||
ret = copy_oid(enc_key_oid, &ri->keyEncryptionAlgorithm.algorithm);
|
||||
ret = copy_oid(pk_enc_key_oid, &ri->keyEncryptionAlgorithm.algorithm);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
@@ -518,7 +738,7 @@ pk_mk_pa_reply_enckey(krb5_context context,
|
||||
krb5_clear_error_string(context);
|
||||
goto out;
|
||||
}
|
||||
ret = copy_oid(&heim_des_ede3_cbc_oid,
|
||||
ret = copy_oid(enc_type_oid,
|
||||
&ed.encryptedContentInfo.contentEncryptionAlgorithm.algorithm);
|
||||
if (ret) {
|
||||
krb5_clear_error_string(context);
|
||||
@@ -562,9 +782,93 @@ pk_mk_pa_reply_enckey(krb5_context context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static krb5_error_code
|
||||
pk_mk_pa_reply_dh(krb5_context context,
|
||||
DH *kdc_dh,
|
||||
pk_client_params *client_params,
|
||||
krb5_keyblock *reply_key,
|
||||
ContentInfo *content_info)
|
||||
{
|
||||
ASN1_INTEGER *dh_pub_key = NULL;
|
||||
KDCDHKeyInfo dh_info;
|
||||
krb5_error_code ret;
|
||||
SignedData sd;
|
||||
krb5_data buf, sd_buf;
|
||||
size_t size;
|
||||
|
||||
memset(&dh_info, 0, sizeof(dh_info));
|
||||
memset(&sd, 0, sizeof(sd));
|
||||
krb5_data_zero(&buf);
|
||||
krb5_data_zero(&sd_buf);
|
||||
|
||||
dh_pub_key = BN_to_ASN1_INTEGER(kdc_dh->pub_key, NULL);
|
||||
if (dh_pub_key == NULL) {
|
||||
krb5_set_error_string(context, "BN_to_ASN1_INTEGER() failed (%s)",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
ret = KRB5_AP_ERR_OPENSSL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
OPENSSL_ASN1_MALLOC_ENCODE(ASN1_INTEGER, buf.data, buf.length, dh_pub_key,
|
||||
ret);
|
||||
ASN1_INTEGER_free(dh_pub_key);
|
||||
if (ret) {
|
||||
krb5_set_error_string(context, "Encoding of ASN1_INTEGER failed (%s)",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
|
||||
dh_info.subjectPublicKey.length = buf.length * 8;
|
||||
dh_info.subjectPublicKey.data = buf.data;
|
||||
|
||||
dh_info.nonce = client_params->nonce;
|
||||
|
||||
ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
|
||||
ret);
|
||||
if (ret) {
|
||||
krb5_set_error_string(context, "ASN.1 encoding of "
|
||||
"KdcDHKeyInfo failed (%d)", ret);
|
||||
goto out;
|
||||
}
|
||||
if (buf.length != size)
|
||||
krb5_abortx(context, "Internal ASN.1 encoder error");
|
||||
|
||||
/*
|
||||
* Create the SignedData structure and sign the KdcDHKeyInfo
|
||||
* filled in above
|
||||
*/
|
||||
|
||||
ret = _krb5_pk_create_sign(context,
|
||||
&heim_pkdhkeydata_oid,
|
||||
&buf,
|
||||
kdc_identity,
|
||||
&sd_buf);
|
||||
krb5_data_free(&buf);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = _krb5_pk_mk_ContentInfo(context, &sd_buf, &pkcs7_signed_oid,
|
||||
content_info);
|
||||
krb5_data_free(&sd_buf);
|
||||
|
||||
out:
|
||||
free_KDCDHKeyInfo(&dh_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
krb5_error_code
|
||||
pk_mk_pa_reply(krb5_context context,
|
||||
pk_client_params *client_params,
|
||||
const KDC_REQ *req,
|
||||
krb5_keyblock **reply_key,
|
||||
METHOD_DATA *md)
|
||||
{
|
||||
@@ -572,6 +876,7 @@ pk_mk_pa_reply(krb5_context context,
|
||||
PA_PK_AS_REP rep;
|
||||
void *buf;
|
||||
size_t len, size;
|
||||
krb5_enctype enctype;
|
||||
|
||||
if (!enable_pkinit) {
|
||||
krb5_clear_error_string(context);
|
||||
@@ -580,19 +885,43 @@ pk_mk_pa_reply(krb5_context context,
|
||||
|
||||
memset(&rep, 0, sizeof(rep));
|
||||
|
||||
if (req->req_body.etype.len < 1) {
|
||||
ret = KRB5KRB_ERR_GENERIC;
|
||||
krb5_set_error_string(context,
|
||||
"No valid enctype available from client");
|
||||
goto out;
|
||||
}
|
||||
|
||||
enctype = req->req_body.etype.val[0];
|
||||
enctype = ETYPE_DES3_CBC_SHA1;
|
||||
|
||||
if (client_params->dh == NULL) {
|
||||
rep.element = choice_PA_PK_AS_REP_encKeyPack;
|
||||
|
||||
krb5_generate_random_keyblock(context, ETYPE_DES3_CBC_SHA1,
|
||||
krb5_generate_random_keyblock(context, enctype,
|
||||
&client_params->reply_key);
|
||||
|
||||
ret = pk_mk_pa_reply_enckey(context,
|
||||
client_params,
|
||||
req,
|
||||
&client_params->reply_key,
|
||||
&rep.u.encKeyPack);
|
||||
} else {
|
||||
ret = KRB5KRB_ERR_GENERIC;
|
||||
krb5_set_error_string(context, "DH support not compiled in");
|
||||
rep.element = choice_PA_PK_AS_REP_dhSignedData;
|
||||
|
||||
ret = check_dh_params(client_params->dh);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = generate_dh_keyblock(context, client_params,
|
||||
&client_params->reply_key);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pk_mk_pa_reply_dh(context, client_params->dh,
|
||||
client_params,
|
||||
&client_params->reply_key,
|
||||
&rep.u.dhSignedData);
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
Reference in New Issue
Block a user