From c523eb98d1694afd5d73cb5fe3b521c6064c130f Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Fri, 6 Apr 2012 11:34:42 +0000 Subject: [PATCH] Backport DH client certificate support (from HEAD) --- CHANGES | 4 +++ ssl/s3_clnt.c | 92 ++++++++++++++++++++++++++++++++++++++++----------- ssl/s3_srvr.c | 76 +++++++++++++++++++++++++----------------- 3 files changed, 123 insertions(+), 49 deletions(-) diff --git a/CHANGES b/CHANGES index 1383cfdead..6439302fa1 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ 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 certificates. [Steve Henson] diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 36be0eb64a..c4ff1ea7a6 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -2462,18 +2462,33 @@ int ssl3_send_client_key_exchange(SSL *s) goto err; } } - - /* generate a new random key */ - if ((dh_clnt=DHparams_dup(dh_srvr)) == NULL) + if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY) { - SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,ERR_R_DH_LIB); - goto err; + /* 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; + } } - if (!DH_generate_key(dh_clnt)) + else { - SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,ERR_R_DH_LIB); - DH_free(dh_clnt); - goto err; + /* generate a new random key */ + if ((dh_clnt=DHparams_dup(dh_srvr)) == NULL) + { + SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,ERR_R_DH_LIB); + goto err; + } + if (!DH_generate_key(dh_clnt)) + { + SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,ERR_R_DH_LIB); + DH_free(dh_clnt); + goto err; + } } /* use the 'p' output buffer for the DH key, but @@ -2497,11 +2512,16 @@ int ssl3_send_client_key_exchange(SSL *s) /* clean up */ memset(p,0,n); - /* send off the data */ - n=BN_num_bytes(dh_clnt->pub_key); - s2n(n,p); - BN_bn2bin(dh_clnt->pub_key,p); - n+=2; + if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY) + n = 0; + else + { + /* send off the data */ + n=BN_num_bytes(dh_clnt->pub_key); + s2n(n,p); + BN_bn2bin(dh_clnt->pub_key,p); + n+=2; + } DH_free(dh_clnt); @@ -3088,6 +3108,40 @@ err: 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) { X509 *x509=NULL; @@ -3097,12 +3151,10 @@ int ssl3_send_client_certificate(SSL *s) if (s->state == SSL3_ST_CW_CERT_A) { - if ((s->cert == NULL) || - (s->cert->key->x509 == NULL) || - (s->cert->key->privatekey == NULL)) - s->state=SSL3_ST_CW_CERT_B; - else + if (ssl3_check_client_certificate(s)) s->state=SSL3_ST_CW_CERT_C; + else + s->state=SSL3_ST_CW_CERT_B; } /* 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 (pkey != NULL) EVP_PKEY_free(pkey); + if (i && !ssl3_check_client_certificate(s)) + i = 0; if (i == 0) { if (s->version == SSL3_VERSION) diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index bcde8f245a..5c826792d3 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -296,6 +296,7 @@ int ssl3_accept(SSL *s) s->init_num=0; s->s3->flags &= ~SSL3_FLAGS_SGC_RESTART_DONE; + s->s3->flags &= ~TLS1_FLAGS_SKIP_CERT_VERIFY; if (s->state != SSL_ST_RENEGOTIATE) { @@ -2121,7 +2122,7 @@ int ssl3_get_client_key_exchange(SSL *s) #endif #ifndef OPENSSL_NO_DH BIGNUM *pub=NULL; - DH *dh_srvr; + DH *dh_srvr, *dh_clnt = NULL; #endif #ifndef OPENSSL_NO_KRB5 KSSL_ERR kssl_err; @@ -2255,8 +2256,11 @@ int ssl3_get_client_key_exchange(SSL *s) #ifndef OPENSSL_NO_DH if (alg_k & (SSL_kEDH|SSL_kDHr|SSL_kDHd)) { - n2s(p,i); - if (n != i+2) + int idx = -1; + EVP_PKEY *skey = NULL; + if (n) + n2s(p,i); + if (n && n != i+2) { if (!(s->options & SSL_OP_SSLEAY_080_CLIENT_DH_BUG)) { @@ -2269,44 +2273,52 @@ int ssl3_get_client_key_exchange(SSL *s) i=(int)n; } } - - if (n == 0L) /* the parameters are in the cert */ + if (alg_k & SSL_kDHr) + idx = SSL_PKEY_DH_RSA; + else if (alg_k & SSL_kDHd) + idx = SSL_PKEY_DH_DSA; + if (idx >= 0) + { + skey = s->cert->pkeys[idx].privatekey; + if ((skey == NULL) || + (skey->type != EVP_PKEY_DH) || + (skey->pkey.dh == NULL)) + { + al=SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_MISSING_RSA_CERTIFICATE); + goto f_err; + } + dh_srvr = skey->pkey.dh; + } + else if (s->s3->tmp.dh == NULL) { al=SSL_AD_HANDSHAKE_FAILURE; - SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_UNABLE_TO_DECODE_DH_CERTS); + SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_MISSING_TMP_DH_KEY); goto f_err; } else + dh_srvr=s->s3->tmp.dh; + + if (n == 0L) { - int idx = -1; - if (alg_k & SSL_kDHr) - idx = SSL_PKEY_DH_RSA; - else if (alg_k & SSL_kDHd) - idx = SSL_PKEY_DH_DSA; - if (idx >= 0) - { - EVP_PKEY *skey = s->cert->pkeys[idx].privatekey; - if ((skey == NULL) || - (skey->type != EVP_PKEY_DH) || - (skey->pkey.dh == NULL)) - { - al=SSL_AD_HANDSHAKE_FAILURE; - SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_MISSING_RSA_CERTIFICATE); - goto f_err; - } - dh_srvr = skey->pkey.dh; + /* 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); } - else if (s->s3->tmp.dh == NULL) + 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; } - else - dh_srvr=s->s3->tmp.dh; + EVP_PKEY_free(clkey); + pub = dh_clnt->pub_key; } - - pub=BN_bin2bn(p,i,NULL); + else + pub=BN_bin2bn(p,i,NULL); if (pub == NULL) { SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_BN_LIB); @@ -2324,13 +2336,17 @@ int ssl3_get_client_key_exchange(SSL *s) DH_free(s->s3->tmp.dh); s->s3->tmp.dh=NULL; - - BN_clear_free(pub); + if (dh_clnt) + DH_free(dh_clnt); + else + BN_clear_free(pub); pub=NULL; s->session->master_key_length= s->method->ssl3_enc->generate_master_secret(s, s->session->master_key,p,i); OPENSSL_cleanse(p,i); + if (dh_clnt) + return 2; } else #endif -- 2.25.1