Add support for MS "fast SGC".
[openssl.git] / ssl / s3_srvr.c
index e003d88357461aa7c9a0b661b9af3a215430d80a..e2526cc5c4f2192e77b29684c44d78486dfdeb99 100644 (file)
@@ -70,6 +70,7 @@
 
 static SSL_METHOD *ssl3_get_server_method(int ver);
 static int ssl3_get_client_hello(SSL *s);
+static int ssl3_check_client_hello(SSL *s);
 static int ssl3_send_server_hello(SSL *s);
 static int ssl3_send_server_key_exchange(SSL *s);
 static int ssl3_send_certificate_request(SSL *s);
@@ -141,6 +142,7 @@ int ssl3_accept(SSL *s)
                        s->new_session=1;
                        /* s->state=SSL_ST_ACCEPT; */
 
+               case SSL3_ST_SR_MS_SGC:
                case SSL_ST_BEFORE:
                case SSL_ST_ACCEPT:
                case SSL_ST_BEFORE|SSL_ST_ACCEPT:
@@ -184,8 +186,8 @@ int ssl3_accept(SSL *s)
 
                        if (s->state != SSL_ST_RENEGOTIATE)
                                {
+                               if(s->state != SSL3_ST_SR_MS_SGC) ssl3_init_finished_mac(s);
                                s->state=SSL3_ST_SR_CLNT_HELLO_A;
-                               ssl3_init_finished_mac(s);
                                s->ctx->stats.sess_accept++;
                                }
                        else
@@ -287,9 +289,19 @@ int ssl3_accept(SSL *s)
 
                case SSL3_ST_SW_CERT_REQ_A:
                case SSL3_ST_SW_CERT_REQ_B:
-                       if (!(s->verify_mode & SSL_VERIFY_PEER) ||
+                       if (/* don't request cert unless asked for it: */
+                               !(s->verify_mode & SSL_VERIFY_PEER) ||
+                               /* if SSL_VERIFY_CLIENT_ONCE is set,
+                                * don't request cert during re-negotiation: */
                                ((s->session->peer != NULL) &&
-                                (s->verify_mode & SSL_VERIFY_CLIENT_ONCE)))
+                                (s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) ||
+                               /* never request cert in anonymous ciphersuites
+                                * (see section "Certificate request" in SSL 3 drafts
+                                * and in RFC 2246): */
+                               ((s->s3->tmp.new_cipher->algorithms & SSL_aNULL) &&
+                                /* ... except when the application insists on verification
+                                 * (against the specs, but s3_clnt.c accepts this for SSL 3) */
+                                !(s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))
                                {
                                /* no cert request */
                                skip=1;
@@ -331,12 +343,18 @@ int ssl3_accept(SSL *s)
 
                case SSL3_ST_SR_CERT_A:
                case SSL3_ST_SR_CERT_B:
-                       /* could be sent for a DH cert, even if we
-                        * have not asked for it :-) */
-                       ret=ssl3_get_client_certificate(s);
-                       if (ret <= 0) goto end;
-                       s->init_num=0;
-                       s->state=SSL3_ST_SR_KEY_EXCH_A;
+                       /* Check for second client hello if MS SGC */
+                       ret = ssl3_check_client_hello(s);
+                       if(ret <= 0) goto end;
+                       if(ret == 2) s->state = SSL3_ST_SR_MS_SGC;
+                       else {
+                               /* could be sent for a DH cert, even if we
+                                * have not asked for it :-) */
+                               ret=ssl3_get_client_certificate(s);
+                               if (ret <= 0) goto end;
+                               s->init_num=0;
+                               s->state=SSL3_ST_SR_KEY_EXCH_A;
+                       }
                        break;
 
                case SSL3_ST_SR_KEY_EXCH_A:
@@ -500,6 +518,23 @@ static int ssl3_send_hello_request(SSL *s)
        return(ssl3_do_write(s,SSL3_RT_HANDSHAKE));
        }
 
+static int ssl3_check_client_hello(SSL *s)
+       {
+       int ok;
+       long n;
+
+       n=ssl3_get_message(s,
+               SSL3_ST_SR_CERT_A,
+               SSL3_ST_SR_CERT_B,
+               -1,
+               SSL3_RT_MAX_PLAIN_LENGTH,
+               &ok);
+       if (!ok) return((int)n);
+       s->s3->tmp.reuse_message = 1;
+       if(s->s3->tmp.message_type == SSL3_MT_CLIENT_HELLO) return 2;
+       return 1;
+}
+
 static int ssl3_get_client_hello(SSL *s)
        {
        int i,j,ok,al,ret= -1;
@@ -531,10 +566,9 @@ static int ssl3_get_client_hello(SSL *s)
        if (!ok) return((int)n);
        d=p=(unsigned char *)s->init_buf->data;
 
-       /* The version number has already been checked in ssl3_get_message.
-        * I a native TLSv1/SSLv3 method, the match must be correct except
-        * perhaps for the first message */
-/*     s->client_version=(((int)p[0])<<8)|(int)p[1]; */
+       /* use version from inside client hello, not from record header
+        * (may differ: see RFC 2246, Appendix E, second paragraph) */
+       s->client_version=(((int)p[0])<<8)|(int)p[1];
        p+=2;
 
        /* load the client random */
@@ -873,6 +907,7 @@ static int ssl3_send_server_key_exchange(SSL *s)
        EVP_PKEY *pkey;
        unsigned char *p,*d;
        int al,i;
+       unsigned int u;
        unsigned long type;
        int n;
        CERT *cert;
@@ -1027,15 +1062,14 @@ static int ssl3_send_server_key_exchange(SSL *s)
                                        q+=i;
                                        j+=i;
                                        }
-                               i=RSA_private_encrypt(j,md_buf,&(p[2]),
-                                       pkey->pkey.rsa,RSA_PKCS1_PADDING);
-                               if (i <= 0)
+                               if (RSA_sign(NID_md5_sha1, md_buf, j,
+                                       &(p[2]), &u, pkey->pkey.rsa) <= 0)
                                        {
                                        SSLerr(SSL_F_SSL3_SEND_SERVER_KEY_EXCHANGE,ERR_LIB_RSA);
                                        goto err;
                                        }
-                               s2n(i,p);
-                               n+=i+2;
+                               s2n(u,p);
+                               n+=u+2;
                                }
                        else
 #endif
@@ -1239,7 +1273,7 @@ static int ssl3_get_client_key_exchange(SSL *s)
 
                i=RSA_private_decrypt((int)n,p,p,rsa,RSA_PKCS1_PADDING);
 
-#if 1
+#if 0
                /* If a bad decrypt, use a random master key */
                if ((i != SSL_MAX_MASTER_KEY_LENGTH) ||
                        ((p[0] != (s->client_version>>8)) ||
@@ -1271,7 +1305,7 @@ static int ssl3_get_client_key_exchange(SSL *s)
                        goto f_err;
                        }
 
-               if ((p[0] != (s->version>>8)) || (p[1] != (s->version & 0xff)))
+               if ((p[0] != (s->client_version>>8)) || (p[1] != (s->client_version & 0xff)))
                        {
                        al=SSL_AD_DECODE_ERROR;
                        SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_BAD_PROTOCOL_VERSION_NUMBER);
@@ -1450,16 +1484,16 @@ static int ssl3_get_cert_verify(SSL *s)
 #ifndef NO_RSA 
        if (pkey->type == EVP_PKEY_RSA)
                {
-               i=RSA_public_decrypt(i,p,p,pkey->pkey.rsa,RSA_PKCS1_PADDING);
+               i=RSA_verify(NID_md5_sha1, s->s3->tmp.finish_md,
+                       MD5_DIGEST_LENGTH+SHA_DIGEST_LENGTH, p, i, 
+                                                       pkey->pkey.rsa);
                if (i < 0)
                        {
                        al=SSL_AD_DECRYPT_ERROR;
                        SSLerr(SSL_F_SSL3_GET_CERT_VERIFY,SSL_R_BAD_RSA_DECRYPT);
                        goto f_err;
                        }
-               if ((i != (MD5_DIGEST_LENGTH+SHA_DIGEST_LENGTH)) ||
-                       memcmp(&(s->s3->tmp.finish_md[0]),p,
-                               MD5_DIGEST_LENGTH+SHA_DIGEST_LENGTH))
+               if (i == 0)
                        {
                        al=SSL_AD_DECRYPT_ERROR;
                        SSLerr(SSL_F_SSL3_GET_CERT_VERIFY,SSL_R_BAD_RSA_SIGNATURE);
@@ -1532,7 +1566,7 @@ static int ssl3_get_client_certificate(SSL *s)
                        al=SSL_AD_HANDSHAKE_FAILURE;
                        goto f_err;
                        }
-               /* If tls asked for a client cert we must return a 0 list */
+               /* If tls asked for a client cert, the client must return a 0 list */
                if ((s->version > SSL3_VERSION) && s->s3->tmp.cert_request)
                        {
                        SSLerr(SSL_F_SSL3_GET_CLIENT_CERTIFICATE,SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST);
@@ -1628,6 +1662,7 @@ static int ssl3_get_client_certificate(SSL *s)
        if (s->session->peer != NULL) /* This should not be needed */
                X509_free(s->session->peer);
        s->session->peer=sk_X509_shift(sk);
+       s->session->verify_result = s->verify_result;
 
        /* With the current implementation, sess_cert will always be NULL
         * when we arrive here. */