Backport DH client certificate support (from HEAD)

This commit is contained in:
Dr. Stephen Henson 2012-04-06 11:34:42 +00:00
parent 0ffa49970b
commit c523eb98d1
3 changed files with 122 additions and 48 deletions

View File

@ -4,6 +4,10 @@
Changes between 1.0.1 and 1.0.2 [xx XXX xxxx] Changes between 1.0.1 and 1.0.2 [xx XXX xxxx]
*) Support for fixed DH ciphersuite client authentication: where both
server and client use DH certificates with common parameters.
[Steve Henson]
*) Support for fixed DH ciphersuites: those requiring DH server *) Support for fixed DH ciphersuites: those requiring DH server
certificates. certificates.
[Steve Henson] [Steve Henson]

View File

@ -2462,7 +2462,21 @@ int ssl3_send_client_key_exchange(SSL *s)
goto err; goto err;
} }
} }
if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY)
{
/* Use client certificate key */
EVP_PKEY *clkey = s->cert->key->privatekey;
if (clkey)
dh_clnt = EVP_PKEY_get1_DH(clkey);
if (dh_clnt == NULL)
{
SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,
ERR_R_INTERNAL_ERROR);
goto err;
}
}
else
{
/* generate a new random key */ /* generate a new random key */
if ((dh_clnt=DHparams_dup(dh_srvr)) == NULL) if ((dh_clnt=DHparams_dup(dh_srvr)) == NULL)
{ {
@ -2475,6 +2489,7 @@ int ssl3_send_client_key_exchange(SSL *s)
DH_free(dh_clnt); DH_free(dh_clnt);
goto err; goto err;
} }
}
/* use the 'p' output buffer for the DH key, but /* use the 'p' output buffer for the DH key, but
* make sure to clear it out afterwards */ * make sure to clear it out afterwards */
@ -2497,11 +2512,16 @@ int ssl3_send_client_key_exchange(SSL *s)
/* clean up */ /* clean up */
memset(p,0,n); memset(p,0,n);
if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY)
n = 0;
else
{
/* send off the data */ /* send off the data */
n=BN_num_bytes(dh_clnt->pub_key); n=BN_num_bytes(dh_clnt->pub_key);
s2n(n,p); s2n(n,p);
BN_bn2bin(dh_clnt->pub_key,p); BN_bn2bin(dh_clnt->pub_key,p);
n+=2; n+=2;
}
DH_free(dh_clnt); DH_free(dh_clnt);
@ -3088,6 +3108,40 @@ err:
return(-1); return(-1);
} }
/* Check a certificate can be used for client authentication. Currently
* just check cert exists and if static DH client certificates can be used.
*/
static int ssl3_check_client_certificate(SSL *s)
{
unsigned long alg_k;
if (!s->cert || !s->cert->key->x509 || !s->cert->key->privatekey)
return 0;
alg_k=s->s3->tmp.new_cipher->algorithm_mkey;
/* See if we can use client certificate for fixed DH */
if (alg_k & (SSL_kDHr|SSL_kDHd))
{
SESS_CERT *scert = s->session->sess_cert;
int i = scert->peer_cert_type;
EVP_PKEY *clkey = NULL, *spkey = NULL;
clkey = s->cert->key->privatekey;
/* If client key not DH assume it can be used */
if (EVP_PKEY_id(clkey) != EVP_PKEY_DH)
return 1;
if (i >= 0)
spkey = X509_get_pubkey(scert->peer_pkeys[i].x509);
if (spkey)
{
/* Compare server and client parameters */
i = EVP_PKEY_cmp_parameters(clkey, spkey);
EVP_PKEY_free(spkey);
if (i != 1)
return 0;
}
s->s3->flags |= TLS1_FLAGS_SKIP_CERT_VERIFY;
}
return 1;
}
int ssl3_send_client_certificate(SSL *s) int ssl3_send_client_certificate(SSL *s)
{ {
X509 *x509=NULL; X509 *x509=NULL;
@ -3097,12 +3151,10 @@ int ssl3_send_client_certificate(SSL *s)
if (s->state == SSL3_ST_CW_CERT_A) if (s->state == SSL3_ST_CW_CERT_A)
{ {
if ((s->cert == NULL) || if (ssl3_check_client_certificate(s))
(s->cert->key->x509 == NULL) ||
(s->cert->key->privatekey == NULL))
s->state=SSL3_ST_CW_CERT_B;
else
s->state=SSL3_ST_CW_CERT_C; s->state=SSL3_ST_CW_CERT_C;
else
s->state=SSL3_ST_CW_CERT_B;
} }
/* We need to get a client cert */ /* We need to get a client cert */
@ -3134,6 +3186,8 @@ int ssl3_send_client_certificate(SSL *s)
if (x509 != NULL) X509_free(x509); if (x509 != NULL) X509_free(x509);
if (pkey != NULL) EVP_PKEY_free(pkey); if (pkey != NULL) EVP_PKEY_free(pkey);
if (i && !ssl3_check_client_certificate(s))
i = 0;
if (i == 0) if (i == 0)
{ {
if (s->version == SSL3_VERSION) if (s->version == SSL3_VERSION)

View File

@ -296,6 +296,7 @@ int ssl3_accept(SSL *s)
s->init_num=0; s->init_num=0;
s->s3->flags &= ~SSL3_FLAGS_SGC_RESTART_DONE; s->s3->flags &= ~SSL3_FLAGS_SGC_RESTART_DONE;
s->s3->flags &= ~TLS1_FLAGS_SKIP_CERT_VERIFY;
if (s->state != SSL_ST_RENEGOTIATE) if (s->state != SSL_ST_RENEGOTIATE)
{ {
@ -2121,7 +2122,7 @@ int ssl3_get_client_key_exchange(SSL *s)
#endif #endif
#ifndef OPENSSL_NO_DH #ifndef OPENSSL_NO_DH
BIGNUM *pub=NULL; BIGNUM *pub=NULL;
DH *dh_srvr; DH *dh_srvr, *dh_clnt = NULL;
#endif #endif
#ifndef OPENSSL_NO_KRB5 #ifndef OPENSSL_NO_KRB5
KSSL_ERR kssl_err; KSSL_ERR kssl_err;
@ -2255,8 +2256,11 @@ int ssl3_get_client_key_exchange(SSL *s)
#ifndef OPENSSL_NO_DH #ifndef OPENSSL_NO_DH
if (alg_k & (SSL_kEDH|SSL_kDHr|SSL_kDHd)) if (alg_k & (SSL_kEDH|SSL_kDHr|SSL_kDHd))
{ {
int idx = -1;
EVP_PKEY *skey = NULL;
if (n)
n2s(p,i); n2s(p,i);
if (n != i+2) if (n && n != i+2)
{ {
if (!(s->options & SSL_OP_SSLEAY_080_CLIENT_DH_BUG)) if (!(s->options & SSL_OP_SSLEAY_080_CLIENT_DH_BUG))
{ {
@ -2269,23 +2273,13 @@ int ssl3_get_client_key_exchange(SSL *s)
i=(int)n; i=(int)n;
} }
} }
if (n == 0L) /* the parameters are in the cert */
{
al=SSL_AD_HANDSHAKE_FAILURE;
SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_UNABLE_TO_DECODE_DH_CERTS);
goto f_err;
}
else
{
int idx = -1;
if (alg_k & SSL_kDHr) if (alg_k & SSL_kDHr)
idx = SSL_PKEY_DH_RSA; idx = SSL_PKEY_DH_RSA;
else if (alg_k & SSL_kDHd) else if (alg_k & SSL_kDHd)
idx = SSL_PKEY_DH_DSA; idx = SSL_PKEY_DH_DSA;
if (idx >= 0) if (idx >= 0)
{ {
EVP_PKEY *skey = s->cert->pkeys[idx].privatekey; skey = s->cert->pkeys[idx].privatekey;
if ((skey == NULL) || if ((skey == NULL) ||
(skey->type != EVP_PKEY_DH) || (skey->type != EVP_PKEY_DH) ||
(skey->pkey.dh == NULL)) (skey->pkey.dh == NULL))
@ -2304,8 +2298,26 @@ int ssl3_get_client_key_exchange(SSL *s)
} }
else else
dh_srvr=s->s3->tmp.dh; dh_srvr=s->s3->tmp.dh;
}
if (n == 0L)
{
/* Get pubkey from cert */
EVP_PKEY *clkey=X509_get_pubkey(s->session->peer);
if (clkey)
{
if (EVP_PKEY_cmp_parameters(clkey, skey) == 1)
dh_clnt = EVP_PKEY_get1_DH(clkey);
}
if (dh_clnt == NULL)
{
al=SSL_AD_HANDSHAKE_FAILURE;
SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_MISSING_TMP_DH_KEY);
goto f_err;
}
EVP_PKEY_free(clkey);
pub = dh_clnt->pub_key;
}
else
pub=BN_bin2bn(p,i,NULL); pub=BN_bin2bn(p,i,NULL);
if (pub == NULL) if (pub == NULL)
{ {
@ -2324,13 +2336,17 @@ int ssl3_get_client_key_exchange(SSL *s)
DH_free(s->s3->tmp.dh); DH_free(s->s3->tmp.dh);
s->s3->tmp.dh=NULL; s->s3->tmp.dh=NULL;
if (dh_clnt)
DH_free(dh_clnt);
else
BN_clear_free(pub); BN_clear_free(pub);
pub=NULL; pub=NULL;
s->session->master_key_length= s->session->master_key_length=
s->method->ssl3_enc->generate_master_secret(s, s->method->ssl3_enc->generate_master_secret(s,
s->session->master_key,p,i); s->session->master_key,p,i);
OPENSSL_cleanse(p,i); OPENSSL_cleanse(p,i);
if (dh_clnt)
return 2;
} }
else else
#endif #endif