Updatde from stable branch.
[openssl.git] / ssl / s3_clnt.c
index f8f43eb938c7a9bd59b8cab67e86a27fa7b4af52..5d8908657137512f5056bd99e9f4acae48402736 100644 (file)
 #include <openssl/dh.h>
 #endif
 #include <openssl/bn.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif
 
 static const SSL_METHOD *ssl3_get_client_method(int ver);
 static int ca_dn_cmp(const X509_NAME * const *a,const X509_NAME * const *b);
@@ -295,7 +298,10 @@ int ssl3_connect(SSL *s)
                        if (ret == 2)
                                {
                                s->hit = 1;
-                               s->state=SSL3_ST_CR_FINISHED_A;
+                               if (s->tlsext_ticket_expected)
+                                       s->state=SSL3_ST_CR_SESSION_TICKET_A;
+                               else
+                                       s->state=SSL3_ST_CR_FINISHED_A;
                                s->init_num=0;
                                break;
                                }
@@ -307,10 +313,24 @@ int ssl3_connect(SSL *s)
                                {
                                ret=ssl3_get_server_certificate(s);
                                if (ret <= 0) goto end;
+#ifndef OPENSSL_NO_TLSEXT
+                               if (s->tlsext_status_expected)
+                                       s->state=SSL3_ST_CR_CERT_STATUS_A;
+                               else
+                                       s->state=SSL3_ST_CR_KEY_EXCH_A;
+                               }
+                       else
+                               {
+                               skip = 1;
+                               s->state=SSL3_ST_CR_KEY_EXCH_A;
+                               }
+#else
                                }
                        else
                                skip=1;
+
                        s->state=SSL3_ST_CR_KEY_EXCH_A;
+#endif
                        s->init_num=0;
                        break;
 
@@ -473,6 +493,14 @@ int ssl3_connect(SSL *s)
                        s->state=SSL3_ST_CR_FINISHED_A;
                        s->init_num=0;
                break;
+
+               case SSL3_ST_CR_CERT_STATUS_A:
+               case SSL3_ST_CR_CERT_STATUS_B:
+                       ret=ssl3_get_cert_status(s);
+                       if (ret <= 0) goto end;
+                       s->state=SSL3_ST_CR_KEY_EXCH_A;
+                       s->init_num=0;
+               break;
 #endif
 
                case SSL3_ST_CR_FINISHED_A:
@@ -691,7 +719,7 @@ err:
 int ssl3_get_server_hello(SSL *s)
        {
        STACK_OF(SSL_CIPHER) *sk;
-       SSL_CIPHER *c;
+       const SSL_CIPHER *c;
        unsigned char *p,*d;
        int i,al,ok;
        unsigned int j;
@@ -760,6 +788,23 @@ int ssl3_get_server_hello(SSL *s)
                goto f_err;
                }
 
+#ifndef OPENSSL_NO_TLSEXT
+       /* check if we want to resume the session based on external pre-shared secret */
+       if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
+               {
+               SSL_CIPHER *pref_cipher=NULL;
+               s->session->master_key_length=sizeof(s->session->master_key);
+               if (s->tls_session_secret_cb(s, s->session->master_key,
+                                            &s->session->master_key_length,
+                                            NULL, &pref_cipher,
+                                            s->tls_session_secret_cb_arg))
+                       {
+                       s->session->cipher = pref_cipher ?
+                               pref_cipher : ssl_get_cipher_by_char(s, p+j);
+                       }
+               }
+#endif /* OPENSSL_NO_TLSEXT */
+
        if (j != 0 && j == s->session->session_id_length
            && memcmp(p,s->session->session_id,j) == 0)
            {
@@ -826,7 +871,8 @@ int ssl3_get_server_hello(SSL *s)
                        }
                }
        s->s3->tmp.new_cipher=c;
-       ssl3_digest_cached_records(s);
+       if (!ssl3_digest_cached_records(s))
+               goto f_err;
 
        /* lets get the compression algorithm */
        /* COMPRESSION */
@@ -972,7 +1018,7 @@ int ssl3_get_server_certificate(SSL *s)
                }
 
        i=ssl_verify_cert_chain(s,sk);
-       if ((s->verify_mode != SSL_VERIFY_NONE) && (!i)
+       if ((s->verify_mode != SSL_VERIFY_NONE) && (i <= 0)
 #ifndef OPENSSL_NO_KRB5
            && !((s->s3->tmp.new_cipher->algorithm_mkey & SSL_kKRB5) &&
                 (s->s3->tmp.new_cipher->algorithm_auth & SSL_aKRB5))
@@ -1508,7 +1554,7 @@ int ssl3_get_key_exchange(SSL *s)
                        EVP_VerifyUpdate(&md_ctx,&(s->s3->client_random[0]),SSL3_RANDOM_SIZE);
                        EVP_VerifyUpdate(&md_ctx,&(s->s3->server_random[0]),SSL3_RANDOM_SIZE);
                        EVP_VerifyUpdate(&md_ctx,param,param_len);
-                       if (!EVP_VerifyFinal(&md_ctx,p,(int)n,pkey))
+                       if (EVP_VerifyFinal(&md_ctx,p,(int)n,pkey) <= 0)
                                {
                                /* bad signature */
                                al=SSL_AD_DECRYPT_ERROR;
@@ -1526,7 +1572,7 @@ int ssl3_get_key_exchange(SSL *s)
                        EVP_VerifyUpdate(&md_ctx,&(s->s3->client_random[0]),SSL3_RANDOM_SIZE);
                        EVP_VerifyUpdate(&md_ctx,&(s->s3->server_random[0]),SSL3_RANDOM_SIZE);
                        EVP_VerifyUpdate(&md_ctx,param,param_len);
-                       if (!EVP_VerifyFinal(&md_ctx,p,(int)n,pkey))
+                       if (EVP_VerifyFinal(&md_ctx,p,(int)n,pkey) <= 0)
                                {
                                /* bad signature */
                                al=SSL_AD_DECRYPT_ERROR;
@@ -1795,6 +1841,74 @@ f_err:
 err:
        return(-1);
        }
+
+int ssl3_get_cert_status(SSL *s)
+       {
+       int ok, al;
+       unsigned long resplen,n;
+       const unsigned char *p;
+
+       n=s->method->ssl_get_message(s,
+               SSL3_ST_CR_CERT_STATUS_A,
+               SSL3_ST_CR_CERT_STATUS_B,
+               SSL3_MT_CERTIFICATE_STATUS,
+               16384,
+               &ok);
+
+       if (!ok) return((int)n);
+       if (n < 4)
+               {
+               /* need at least status type + length */
+               al = SSL_AD_DECODE_ERROR;
+               SSLerr(SSL_F_SSL3_GET_CERT_STATUS,SSL_R_LENGTH_MISMATCH);
+               goto f_err;
+               }
+       p = (unsigned char *)s->init_msg;
+       if (*p++ != TLSEXT_STATUSTYPE_ocsp)
+               {
+               al = SSL_AD_DECODE_ERROR;
+               SSLerr(SSL_F_SSL3_GET_CERT_STATUS,SSL_R_UNSUPPORTED_STATUS_TYPE);
+               goto f_err;
+               }
+       n2l3(p, resplen);
+       if (resplen + 4 != n)
+               {
+               al = SSL_AD_DECODE_ERROR;
+               SSLerr(SSL_F_SSL3_GET_CERT_STATUS,SSL_R_LENGTH_MISMATCH);
+               goto f_err;
+               }
+       if (s->tlsext_ocsp_resp)
+               OPENSSL_free(s->tlsext_ocsp_resp);
+       s->tlsext_ocsp_resp = BUF_memdup(p, resplen);
+       if (!s->tlsext_ocsp_resp)
+               {
+               al = SSL_AD_INTERNAL_ERROR;
+               SSLerr(SSL_F_SSL3_GET_CERT_STATUS,ERR_R_MALLOC_FAILURE);
+               goto f_err;
+               }
+       s->tlsext_ocsp_resplen = resplen;
+       if (s->ctx->tlsext_status_cb)
+               {
+               int ret;
+               ret = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg);
+               if (ret == 0)
+                       {
+                       al = SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE;
+                       SSLerr(SSL_F_SSL3_GET_CERT_STATUS,SSL_R_INVALID_STATUS_RESPONSE);
+                       goto f_err;
+                       }
+               if (ret < 0)
+                       {
+                       al = SSL_AD_INTERNAL_ERROR;
+                       SSLerr(SSL_F_SSL3_GET_CERT_STATUS,ERR_R_MALLOC_FAILURE);
+                       goto f_err;
+                       }
+               }
+       return 1;
+f_err:
+       ssl3_send_alert(s,SSL3_AL_FATAL,al);
+       return(-1);
+       }
 #endif
 
 int ssl3_get_server_done(SSL *s)
@@ -2050,6 +2164,13 @@ int ssl3_send_client_key_exchange(SSL *s)
                        {
                        DH *dh_srvr,*dh_clnt;
 
+                       if (s->session->sess_cert == NULL) 
+                               {
+                               ssl3_send_alert(s,SSL3_AL_FATAL,SSL_AD_UNEXPECTED_MESSAGE);
+                               SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,SSL_R_UNEXPECTED_MESSAGE);
+                               goto err;
+                               }
+
                        if (s->session->sess_cert->peer_dh_tmp != NULL)
                                dh_srvr=s->session->sess_cert->peer_dh_tmp;
                        else
@@ -2287,6 +2408,84 @@ int ssl3_send_client_key_exchange(SSL *s)
                        EVP_PKEY_free(srvr_pub_pkey);
                        }
 #endif /* !OPENSSL_NO_ECDH */
+               else if (alg_k & SSL_kGOST) 
+                       {
+                       /* GOST key exchange message creation */
+                       EVP_PKEY_CTX *pkey_ctx;
+                       X509 *peer_cert; 
+                       size_t msglen;
+                       unsigned int md_len;
+                       int keytype;
+                       unsigned char premaster_secret[32],shared_ukm[32];
+                       EVP_MD_CTX *ukm_hash;
+                       EVP_PKEY *pub_key;
+
+                       /* Get server sertificate PKEY and create ctx from it */
+                       peer_cert=s->session->sess_cert->peer_pkeys[(keytype=SSL_PKEY_GOST01)].x509;
+                       if (!peer_cert) 
+                               peer_cert=s->session->sess_cert->peer_pkeys[(keytype=SSL_PKEY_GOST94)].x509;
+                       if (!peer_cert)         {
+                                       SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER);
+                                       goto err;
+                               }       
+                               
+                       pkey_ctx=EVP_PKEY_CTX_new(pub_key=X509_get_pubkey(peer_cert),NULL);
+                       /* If we have send a certificate, and certificate key
+
+                        * parameters match those of server certificate, use
+                        * certificate key for key exchange
+                        */
+
+                        /* Otherwise, generate ephemeral key pair */
+                                       
+                       EVP_PKEY_encrypt_init(pkey_ctx);
+                         /* Generate session key */    
+                   RAND_bytes(premaster_secret,32);
+                       /* If we have client certificate, use its secret as peer key */
+                       if (s->cert->key->privatekey) {
+                               if (EVP_PKEY_derive_set_peer(pkey_ctx,s->cert->key->privatekey) <0) {
+                                       /* If there was an error - just ignore it. Ephemeral key
+                                       * would be used
+                                       */
+                                       ERR_clear_error();
+                               } else {
+                                       /* Set flag "client cert key is used for key
+                                        * exchange"*/
+                               }       
+                       }                       
+                       /* Compute shared IV and store it in algorithm-specific
+                        * context data */
+                       ukm_hash = EVP_MD_CTX_create();
+                       EVP_DigestInit(ukm_hash,EVP_get_digestbynid(NID_id_GostR3411_94));
+                       EVP_DigestUpdate(ukm_hash,s->s3->client_random,SSL3_RANDOM_SIZE);
+                       EVP_DigestUpdate(ukm_hash,s->s3->server_random,SSL3_RANDOM_SIZE);
+                       EVP_DigestFinal_ex(ukm_hash, shared_ukm, &md_len);
+                       EVP_MD_CTX_destroy(ukm_hash);
+                       if (EVP_PKEY_CTX_ctrl(pkey_ctx,-1,EVP_PKEY_OP_ENCRYPT,EVP_PKEY_CTRL_SET_IV,
+                               8,shared_ukm)<0) {
+                                       SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,
+                                               SSL_R_LIBRARY_BUG);
+                                       goto err;
+                               }       
+                       /* Make GOST keytransport blob message */
+                       /*Encapsulate it into sequence */
+                       *(p++)=V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED;
+                       *(p++)=0x81;
+                       msglen=256;
+                       if (EVP_PKEY_encrypt(pkey_ctx,(unsigned char *)p+1,&msglen,premaster_secret,32)<0) {
+                       SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,
+                                       SSL_R_LIBRARY_BUG);
+                               goto err;
+                       }       
+                       *(p++)= msglen & 0xff;
+                       n=msglen+3;
+                       EVP_PKEY_CTX_free(pkey_ctx);
+                       s->session->master_key_length=
+                               s->method->ssl3_enc->generate_master_secret(s,
+                                       s->session->master_key,premaster_secret,32);
+                       EVP_PKEY_free(pub_key);
+
+                       }
 #ifndef OPENSSL_NO_PSK
                else if (alg_k & SSL_kPSK)
                        {
@@ -2405,6 +2604,7 @@ int ssl3_send_client_verify(SSL *s)
        unsigned char *p,*d;
        unsigned char data[MD5_DIGEST_LENGTH+SHA_DIGEST_LENGTH];
        EVP_PKEY *pkey;
+       EVP_PKEY_CTX *pctx=NULL;
 #ifndef OPENSSL_NO_RSA
        unsigned u=0;
 #endif
@@ -2418,11 +2618,19 @@ int ssl3_send_client_verify(SSL *s)
                d=(unsigned char *)s->init_buf->data;
                p= &(d[4]);
                pkey=s->cert->key->privatekey;
-
-               s->method->ssl3_enc->cert_verify_mac(s,
-                       NID_sha1,
-                       &(data[MD5_DIGEST_LENGTH]));
-
+/* Create context from key and test if sha1 is allowed as digest */
+               pctx = EVP_PKEY_CTX_new(pkey,NULL);
+               EVP_PKEY_sign_init(pctx);
+               if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha1())>0)
+                       {
+                       s->method->ssl3_enc->cert_verify_mac(s,
+                                               NID_sha1,
+                                               &(data[MD5_DIGEST_LENGTH]));
+                       }
+               else
+                       {
+                       ERR_clear_error();
+                       }
 #ifndef OPENSSL_NO_RSA
                if (pkey->type == EVP_PKEY_RSA)
                        {
@@ -2474,10 +2682,30 @@ int ssl3_send_client_verify(SSL *s)
                        }
                else
 #endif
-                       {
+               if (pkey->type == NID_id_GostR3410_94 || pkey->type == NID_id_GostR3410_2001) 
+               {
+               unsigned char signbuf[64];
+               int i;
+               size_t sigsize=64;
+               s->method->ssl3_enc->cert_verify_mac(s,
+                       NID_id_GostR3411_94,
+                       data);
+               if (!EVP_PKEY_sign(pctx,signbuf,&sigsize,data,32)) {
+                       SSLerr(SSL_F_SSL3_SEND_CLIENT_VERIFY,
+                       ERR_R_INTERNAL_ERROR);
+                       goto err;
+               }
+               for (i=63,j=0; i>=0; j++, i--) {
+                       p[2+j]=signbuf[i];
+               }       
+               s2n(j,p);
+               n=j+2;
+               }
+               else
+               {
                        SSLerr(SSL_F_SSL3_SEND_CLIENT_VERIFY,ERR_R_INTERNAL_ERROR);
                        goto err;
-                       }
+               }
                *(d++)=SSL3_MT_CERTIFICATE_VERIFY;
                l2n3(n,d);
 
@@ -2485,8 +2713,10 @@ int ssl3_send_client_verify(SSL *s)
                s->init_num=(int)n+4;
                s->init_off=0;
                }
+       EVP_PKEY_CTX_free(pctx);
        return(ssl3_do_write(s,SSL3_RT_HANDSHAKE));
 err:
+       EVP_PKEY_CTX_free(pctx);
        return(-1);
        }
 
@@ -2514,8 +2744,7 @@ int ssl3_send_client_certificate(SSL *s)
                 * ssl->rwstate=SSL_X509_LOOKUP; return(-1);
                 * We then get retied later */
                i=0;
-               if (s->ctx->client_cert_cb != NULL)
-                       i=s->ctx->client_cert_cb(s,&(x509),&(pkey));
+               i = ssl_do_client_cert_cb(s, &x509, &pkey);
                if (i < 0)
                        {
                        s->rwstate=SSL_X509_LOOKUP;
@@ -2716,6 +2945,7 @@ static int ssl3_check_finished(SSL *s)
        {
        int ok;
        long n;
+       /* If we have no ticket it cannot be a resumed session. */
        if (!s->session->tlsext_tick)
                return 1;
        /* this function is called when we really expect a Certificate
@@ -2735,3 +2965,21 @@ static int ssl3_check_finished(SSL *s)
        return 1;
        }
 #endif
+
+int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey)
+       {
+       int i = 0;
+#ifndef OPENSSL_NO_ENGINE
+       if (s->ctx->client_cert_engine)
+               {
+               i = ENGINE_load_ssl_client_cert(s->ctx->client_cert_engine, s,
+                                               SSL_get_client_CA_list(s),
+                                               px509, ppkey, NULL, NULL, NULL);
+               if (i != 0)
+                       return i;
+               }
+#endif
+       if (s->ctx->client_cert_cb)
+               i = s->ctx->client_cert_cb(s,px509,ppkey);
+       return i;
+       }