Make sure that SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG can't
[openssl.git] / ssl / s3_srvr.c
index 3d63e2e89db8b5e5973f4b8773fdcf4f2b6d04df..b124a8559c5e2217a28c2bf58e77855aa248253e 100644 (file)
@@ -314,9 +314,18 @@ int ssl3_accept(SSL *s)
                case SSL3_ST_SW_SRVR_HELLO_B:
                        ret=ssl3_send_server_hello(s);
                        if (ret <= 0) goto end;
-
+#ifndef OPENSSL_NO_TLSEXT
                        if (s->hit)
-                               s->state=SSL3_ST_SW_CHANGE_A;
+                               {
+                               if (s->tlsext_ticket_expected)
+                                       s->state=SSL3_ST_SW_SESSION_TICKET_A;
+                               else
+                                       s->state=SSL3_ST_SW_CHANGE_A;
+                               }
+#else
+                       if (s->hit)
+                                       s->state=SSL3_ST_SW_CHANGE_A;
+#endif
                        else
                                s->state=SSL3_ST_SW_CERT_A;
                        s->init_num=0;
@@ -548,11 +557,14 @@ int ssl3_accept(SSL *s)
                        ret=ssl3_get_finished(s,SSL3_ST_SR_FINISHED_A,
                                SSL3_ST_SR_FINISHED_B);
                        if (ret <= 0) goto end;
-                       if (s->hit)
-                               s->state=SSL_ST_OK;
 #ifndef OPENSSL_NO_TLSEXT
-                       else if (s->tlsext_ticket_expected)
+                       if (s->tlsext_ticket_expected)
                                s->state=SSL3_ST_SW_SESSION_TICKET_A;
+                       else if (s->hit)
+                               s->state=SSL_ST_OK;
+#else
+                       if (s->hit)
+                               s->state=SSL_ST_OK;
 #endif
                        else
                                s->state=SSL3_ST_SW_CHANGE_A;
@@ -935,22 +947,28 @@ int ssl3_get_client_hello(SSL *s)
                                break;
                                }
                        }
-               if (j == 0)
+               if (j == 0 && (s->options & SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG) && (sk_SSL_CIPHER_num(ciphers) == 1))
                        {
-                       if ((s->options & SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG) && (sk_SSL_CIPHER_num(ciphers) == 1))
+                       /* Special case as client bug workaround: the previously used cipher may
+                        * not be in the current list, the client instead might be trying to
+                        * continue using a cipher that before wasn't chosen due to server
+                        * preferences.  We'll have to reject the connection if the cipher is not
+                        * enabled, though. */
+                       c = sk_SSL_CIPHER_value(ciphers, 0);
+                       if (sk_SSL_CIPHER_find(SSL_get_ciphers(s), c) >= 0)
                                {
-                               /* Very bad for multi-threading.... */
-                               s->session->cipher=sk_SSL_CIPHER_value(ciphers, 0);
-                               }
-                       else
-                               {
-                               /* we need to have the cipher in the cipher
-                                * list if we are asked to reuse it */
-                               al=SSL_AD_ILLEGAL_PARAMETER;
-                               SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_REQUIRED_CIPHER_MISSING);
-                               goto f_err;
+                               s->session->cipher = c;
+                               j = 1;
                                }
                        }
+               if (j == 0)
+                       {
+                       /* we need to have the cipher in the cipher
+                        * list if we are asked to reuse it */
+                       al=SSL_AD_ILLEGAL_PARAMETER;
+                       SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_REQUIRED_CIPHER_MISSING);
+                       goto f_err;
+                       }
                }
 
        /* compression */
@@ -1055,7 +1073,6 @@ int ssl3_get_client_hello(SSL *s)
                        goto f_err;
                        }
                s->s3->tmp.new_cipher=c;
-               ssl3_digest_cached_records(s);
                }
        else
                {
@@ -1086,10 +1103,9 @@ int ssl3_get_client_hello(SSL *s)
                else
 #endif
                s->s3->tmp.new_cipher=s->session->cipher;
-               /* Clear cached handshake records */
-               BIO_free(s->s3->handshake_buffer);
-               s->s3->handshake_buffer = NULL;
                }
+
+       ssl3_digest_cached_records(s);
        
        /* we now have the following setup. 
         * client_random
@@ -1145,8 +1161,16 @@ int ssl3_send_server_hello(SSL *s)
                 * session-id if we want it to be single use.
                 * Currently I will not implement the '0' length session-id
                 * 12-Jan-98 - I'll now support the '0' length stuff.
+                *
+                * We also have an additional case where stateless session
+                * resumption is successful: we always send back the old
+                * session id. In this case s->hit is non zero: this can
+                * only happen if stateless session resumption is succesful
+                * if session caching is disabled so existing functionality
+                * is unaffected.
                 */
-               if (!(s->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER))
+               if (!(s->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)
+                       && !s->hit)
                        s->session->session_id_length=0;
 
                sl=s->session->session_id_length;
@@ -1190,13 +1214,13 @@ int ssl3_send_server_hello(SSL *s)
                *(d++)=SSL3_MT_SERVER_HELLO;
                l2n3(l,d);
 
-               s->state=SSL3_ST_CW_CLNT_HELLO_B;
+               s->state=SSL3_ST_SW_SRVR_HELLO_B;
                /* number of bytes to write */
                s->init_num=p-buf;
                s->init_off=0;
                }
 
-       /* SSL3_ST_CW_CLNT_HELLO_B */
+       /* SSL3_ST_SW_SRVR_HELLO_B */
        return(ssl3_do_write(s,SSL3_RT_HANDSHAKE));
        }
 
@@ -1220,7 +1244,7 @@ int ssl3_send_server_done(SSL *s)
                s->init_off=0;
                }
 
-       /* SSL3_ST_CW_CLNT_HELLO_B */
+       /* SSL3_ST_SW_SRVR_DONE_B */
        return(ssl3_do_write(s,SSL3_RT_HANDSHAKE));
        }
 
@@ -2833,6 +2857,8 @@ int ssl3_send_newsession_ticket(SSL *s)
                unsigned int hlen;
                EVP_CIPHER_CTX ctx;
                HMAC_CTX hctx;
+               unsigned char iv[EVP_MAX_IV_LENGTH];
+               unsigned char key_name[16];
 
                /* get session encoding length */
                slen = i2d_SSL_SESSION(s->session, NULL);
@@ -2863,29 +2889,47 @@ int ssl3_send_newsession_ticket(SSL *s)
                *(p++)=SSL3_MT_NEWSESSION_TICKET;
                /* Skip message length for now */
                p += 3;
+               EVP_CIPHER_CTX_init(&ctx);
+               HMAC_CTX_init(&hctx);
+               /* Initialize HMAC and cipher contexts. If callback present
+                * it does all the work otherwise use generated values
+                * from parent ctx.
+                */
+               if (s->ctx->tlsext_ticket_key_cb)
+                       {
+                       if (s->ctx->tlsext_ticket_key_cb(s, key_name, iv, &ctx,
+                                                        &hctx, 1) < 0)
+                               {
+                               OPENSSL_free(senc);
+                               return -1;
+                               }
+                       }
+               else
+                       {
+                       RAND_pseudo_bytes(iv, 16);
+                       EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
+                                       s->ctx->tlsext_tick_aes_key, iv);
+                       HMAC_Init_ex(&hctx, s->ctx->tlsext_tick_hmac_key, 16,
+                                       tlsext_tick_md(), NULL);
+                       memcpy(key_name, s->ctx->tlsext_tick_key_name, 16);
+                       }
                l2n(s->session->tlsext_tick_lifetime_hint, p);
                /* Skip ticket length for now */
                p += 2;
                /* Output key name */
                macstart = p;
-               memcpy(p, s->ctx->tlsext_tick_key_name, 16);
+               memcpy(p, key_name, 16);
                p += 16;
-               /* Generate and output IV */
-               RAND_pseudo_bytes(p, 16);
-               EVP_CIPHER_CTX_init(&ctx);
+               /* output IV */
+               memcpy(p, iv, EVP_CIPHER_CTX_iv_length(&ctx));
+               p += EVP_CIPHER_CTX_iv_length(&ctx);
                /* Encrypt session data */
-               EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
-                                       s->ctx->tlsext_tick_aes_key, p);
-               p += 16;
                EVP_EncryptUpdate(&ctx, p, &len, senc, slen);
                p += len;
                EVP_EncryptFinal(&ctx, p, &len);
                p += len;
                EVP_CIPHER_CTX_cleanup(&ctx);
 
-               HMAC_CTX_init(&hctx);
-               HMAC_Init_ex(&hctx, s->ctx->tlsext_tick_hmac_key, 16,
-                               tlsext_tick_md(), NULL);
                HMAC_Update(&hctx, macstart, p - macstart);
                HMAC_Final(&hctx, p, &hlen);
                HMAC_CTX_cleanup(&hctx);