diff options
Diffstat (limited to 'ssl/statem/statem_clnt.c')
-rw-r--r-- | ssl/statem/statem_clnt.c | 2853 |
1 files changed, 2853 insertions, 0 deletions
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c new file mode 100644 index 0000000000..3ff427650b --- /dev/null +++ b/ssl/statem/statem_clnt.c @@ -0,0 +1,2853 @@ +/* ssl/statem/statem_clnt.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * Portions of the attached software ("Contribution") are developed by + * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project. + * + * The Contribution is licensed pursuant to the OpenSSL open source + * license provided above. + * + * ECC cipher suite support in OpenSSL originally written by + * Vipul Gupta and Sumit Gupta of Sun Microsystems Laboratories. + * + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. + */ + +#include <stdio.h> +#include "../ssl_locl.h" +#include <openssl/buffer.h> +#include <openssl/rand.h> +#include <openssl/objects.h> +#include <openssl/evp.h> +#include <openssl/md5.h> +#ifndef OPENSSL_NO_DH +# include <openssl/dh.h> +#endif +#include <openssl/bn.h> +#ifndef OPENSSL_NO_ENGINE +# include <openssl/engine.h> +#endif + +static int ssl_set_version(SSL *s); +static int ca_dn_cmp(const X509_NAME *const *a, const X509_NAME *const *b); +static int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, + unsigned char *p); + +/* + * Work out what version we should be using for the initial ClientHello if + * the version is currently set to (D)TLS_ANY_VERSION. + * Returns 1 on success + * Returns 0 on error + */ +static int ssl_set_version(SSL *s) +{ + unsigned long mask, options = s->options; + + if (s->method->version == TLS_ANY_VERSION) { + /* + * SSL_OP_NO_X disables all protocols above X *if* there are + * some protocols below X enabled. This is required in order + * to maintain "version capability" vector contiguous. So + * that if application wants to disable TLS1.0 in favour of + * TLS1>=1, it would be insufficient to pass SSL_NO_TLSv1, the + * answer is SSL_OP_NO_TLSv1|SSL_OP_NO_SSLv3. + */ + mask = SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1 +#if !defined(OPENSSL_NO_SSL3) + | SSL_OP_NO_SSLv3 +#endif + ; +#if !defined(OPENSSL_NO_TLS1_2_CLIENT) + if (options & SSL_OP_NO_TLSv1_2) { + if ((options & mask) != mask) { + s->version = TLS1_1_VERSION; + } else { + SSLerr(SSL_F_SSL_SET_VERSION, SSL_R_NO_PROTOCOLS_AVAILABLE); + return 0; + } + } else { + s->version = TLS1_2_VERSION; + } +#else + if ((options & mask) == mask) { + SSLerr(SSL_F_SSL_SET_VERSION, SSL_R_NO_PROTOCOLS_AVAILABLE); + return 0; + } + s->version = TLS1_1_VERSION; +#endif + + mask &= ~SSL_OP_NO_TLSv1_1; + if ((options & SSL_OP_NO_TLSv1_1) && (options & mask) != mask) + s->version = TLS1_VERSION; + mask &= ~SSL_OP_NO_TLSv1; +#if !defined(OPENSSL_NO_SSL3) + if ((options & SSL_OP_NO_TLSv1) && (options & mask) != mask) + s->version = SSL3_VERSION; +#endif + + if (s->version != TLS1_2_VERSION && tls1_suiteb(s)) { + SSLerr(SSL_F_SSL_SET_VERSION, + SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE); + return 0; + } + + if (s->version == SSL3_VERSION && FIPS_mode()) { + SSLerr(SSL_F_SSL_SET_VERSION, SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE); + return 0; + } + + } else if (s->method->version == DTLS_ANY_VERSION) { + /* Determine which DTLS version to use */ + /* If DTLS 1.2 disabled correct the version number */ + if (options & SSL_OP_NO_DTLSv1_2) { + if (tls1_suiteb(s)) { + SSLerr(SSL_F_SSL_SET_VERSION, + SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE); + return 0; + } + /* + * Disabling all versions is silly: return an error. + */ + if (options & SSL_OP_NO_DTLSv1) { + SSLerr(SSL_F_SSL_SET_VERSION, SSL_R_WRONG_SSL_VERSION); + return 0; + } + /* + * Update method so we don't use any DTLS 1.2 features. + */ + s->method = DTLSv1_client_method(); + s->version = DTLS1_VERSION; + } else { + /* + * We only support one version: update method + */ + if (options & SSL_OP_NO_DTLSv1) + s->method = DTLSv1_2_client_method(); + s->version = DTLS1_2_VERSION; + } + } + + s->client_version = s->version; + + return 1; +} + +int tls_construct_client_hello(SSL *s) +{ + unsigned char *buf; + unsigned char *p, *d; + int i; + unsigned long l; + int al = 0; +#ifndef OPENSSL_NO_COMP + int j; + SSL_COMP *comp; +#endif + SSL_SESSION *sess = s->session; + + buf = (unsigned char *)s->init_buf->data; + + /* Work out what SSL/TLS/DTLS version to use */ + if (ssl_set_version(s) == 0) + goto err; + + if ((sess == NULL) || (sess->ssl_version != s->version) || + /* + * In the case of EAP-FAST, we can have a pre-shared + * "ticket" without a session ID. + */ + (!sess->session_id_length && !sess->tlsext_tick) || + (sess->not_resumable)) { + if (!ssl_get_new_session(s, 0)) + goto err; + } + /* else use the pre-loaded session */ + + p = s->s3->client_random; + + /* + * for DTLS if client_random is initialized, reuse it, we are + * required to use same upon reply to HelloVerify + */ + if (SSL_IS_DTLS(s)) { + size_t idx; + i = 1; + for (idx = 0; idx < sizeof(s->s3->client_random); idx++) { + if (p[idx]) { + i = 0; + break; + } + } + } else + i = 1; + + if (i && ssl_fill_hello_random(s, 0, p, + sizeof(s->s3->client_random)) <= 0) + goto err; + + /* Do the message type and length last */ + d = p = ssl_handshake_start(s); + + /*- + * version indicates the negotiated version: for example from + * an SSLv2/v3 compatible client hello). The client_version + * field is the maximum version we permit and it is also + * used in RSA encrypted premaster secrets. Some servers can + * choke if we initially report a higher version then + * renegotiate to a lower one in the premaster secret. This + * didn't happen with TLS 1.0 as most servers supported it + * but it can with TLS 1.1 or later if the server only supports + * 1.0. + * + * Possible scenario with previous logic: + * 1. Client hello indicates TLS 1.2 + * 2. Server hello says TLS 1.0 + * 3. RSA encrypted premaster secret uses 1.2. + * 4. Handhaked proceeds using TLS 1.0. + * 5. Server sends hello request to renegotiate. + * 6. Client hello indicates TLS v1.0 as we now + * know that is maximum server supports. + * 7. Server chokes on RSA encrypted premaster secret + * containing version 1.0. + * + * For interoperability it should be OK to always use the + * maximum version we support in client hello and then rely + * on the checking of version to ensure the servers isn't + * being inconsistent: for example initially negotiating with + * TLS 1.0 and renegotiating with TLS 1.2. We do this by using + * client_version in client hello and not resetting it to + * the negotiated version. + */ + *(p++) = s->client_version >> 8; + *(p++) = s->client_version & 0xff; + + /* Random stuff */ + memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + + /* Session ID */ + if (s->new_session) + i = 0; + else + i = s->session->session_id_length; + *(p++) = i; + if (i != 0) { + if (i > (int)sizeof(s->session->session_id)) { + SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, ERR_R_INTERNAL_ERROR); + goto err; + } + memcpy(p, s->session->session_id, i); + p += i; + } + + /* cookie stuff for DTLS */ + if (SSL_IS_DTLS(s)) { + if (s->d1->cookie_len > sizeof(s->d1->cookie)) { + SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, ERR_R_INTERNAL_ERROR); + goto err; + } + *(p++) = s->d1->cookie_len; + memcpy(p, s->d1->cookie, s->d1->cookie_len); + p += s->d1->cookie_len; + } + + /* Ciphers supported */ + i = ssl_cipher_list_to_bytes(s, SSL_get_ciphers(s), &(p[2])); + if (i == 0) { + SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, SSL_R_NO_CIPHERS_AVAILABLE); + goto err; + } +#ifdef OPENSSL_MAX_TLS1_2_CIPHER_LENGTH + /* + * Some servers hang if client hello > 256 bytes as hack workaround + * chop number of supported ciphers to keep it well below this if we + * use TLS v1.2 + */ + if (TLS1_get_version(s) >= TLS1_2_VERSION + && i > OPENSSL_MAX_TLS1_2_CIPHER_LENGTH) + i = OPENSSL_MAX_TLS1_2_CIPHER_LENGTH & ~1; +#endif + s2n(i, p); + p += i; + + /* COMPRESSION */ +#ifdef OPENSSL_NO_COMP + *(p++) = 1; +#else + + if (!ssl_allow_compression(s) || !s->ctx->comp_methods) + j = 0; + else + j = sk_SSL_COMP_num(s->ctx->comp_methods); + *(p++) = 1 + j; + for (i = 0; i < j; i++) { + comp = sk_SSL_COMP_value(s->ctx->comp_methods, i); + *(p++) = comp->id; + } +#endif + *(p++) = 0; /* Add the NULL method */ + + /* TLS extensions */ + if (ssl_prepare_clienthello_tlsext(s) <= 0) { + SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT); + goto err; + } + if ((p = + ssl_add_clienthello_tlsext(s, p, buf + SSL3_RT_MAX_PLAIN_LENGTH, + &al)) == NULL) { + ssl3_send_alert(s, SSL3_AL_FATAL, al); + SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, ERR_R_INTERNAL_ERROR); + goto err; + } + + l = p - d; + if (!ssl_set_handshake_header(s, SSL3_MT_CLIENT_HELLO, l)) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, ERR_R_INTERNAL_ERROR); + goto err; + } + + return 1; + err: + statem_set_error(s); + return 0; +} + +enum MSG_PROCESS_RETURN dtls_process_hello_verify(SSL *s, PACKET *pkt) +{ + int al; + unsigned int cookie_len; + PACKET cookiepkt; + + if (!PACKET_forward(pkt, 2) + || !PACKET_get_length_prefixed_1(pkt, &cookiepkt)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_DTLS_PROCESS_HELLO_VERIFY, SSL_R_LENGTH_MISMATCH); + goto f_err; + } + + cookie_len = PACKET_remaining(&cookiepkt); + if (cookie_len > sizeof(s->d1->cookie)) { + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_DTLS_PROCESS_HELLO_VERIFY, SSL_R_LENGTH_TOO_LONG); + goto f_err; + } + + if (!PACKET_copy_bytes(&cookiepkt, s->d1->cookie, cookie_len)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_DTLS_PROCESS_HELLO_VERIFY, SSL_R_LENGTH_MISMATCH); + goto f_err; + } + s->d1->cookie_len = cookie_len; + + return MSG_PROCESS_FINISHED_READING; + f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + statem_set_error(s); + return MSG_PROCESS_ERROR; +} + +enum MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) +{ + STACK_OF(SSL_CIPHER) *sk; + const SSL_CIPHER *c; + PACKET session_id; + size_t session_id_len; + unsigned char *cipherchars; + int i, al = SSL_AD_INTERNAL_ERROR; + unsigned int compression; +#ifndef OPENSSL_NO_COMP + SSL_COMP *comp; +#endif + + if (s->method->version == TLS_ANY_VERSION) { + unsigned int sversion; + + if (!PACKET_get_net_2(pkt, &sversion)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); + goto f_err; + } + +#if TLS_MAX_VERSION != TLS1_2_VERSION +#error Code needs updating for new TLS version +#endif +#ifndef OPENSSL_NO_SSL3 + if ((sversion == SSL3_VERSION) && !(s->options & SSL_OP_NO_SSLv3)) { + if (FIPS_mode()) { + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE); + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + s->method = SSLv3_client_method(); + } else +#endif + if ((sversion == TLS1_VERSION) && !(s->options & SSL_OP_NO_TLSv1)) { + s->method = TLSv1_client_method(); + } else if ((sversion == TLS1_1_VERSION) && + !(s->options & SSL_OP_NO_TLSv1_1)) { + s->method = TLSv1_1_client_method(); + } else if ((sversion == TLS1_2_VERSION) && + !(s->options & SSL_OP_NO_TLSv1_2)) { + s->method = TLSv1_2_client_method(); + } else { + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_UNSUPPORTED_PROTOCOL); + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + s->session->ssl_version = s->version = s->method->version; + + if (!ssl_security(s, SSL_SECOP_VERSION, 0, s->version, NULL)) { + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_VERSION_TOO_LOW); + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + } else if (s->method->version == DTLS_ANY_VERSION) { + /* Work out correct protocol version to use */ + unsigned int hversion; + int options; + + if (!PACKET_get_net_2(pkt, &hversion)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); + goto f_err; + } + + options = s->options; + if (hversion == DTLS1_2_VERSION && !(options & SSL_OP_NO_DTLSv1_2)) + s->method = DTLSv1_2_client_method(); + else if (tls1_suiteb(s)) { + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE); + s->version = hversion; + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } else if (hversion == DTLS1_VERSION && !(options & SSL_OP_NO_DTLSv1)) + s->method = DTLSv1_client_method(); + else { + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_WRONG_SSL_VERSION); + s->version = hversion; + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + s->session->ssl_version = s->version = s->method->version; + } else { + unsigned char *vers; + + if (!PACKET_get_bytes(pkt, &vers, 2)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); + goto f_err; + } + if ((vers[0] != (s->version >> 8)) + || (vers[1] != (s->version & 0xff))) { + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_WRONG_SSL_VERSION); + s->version = (s->version & 0xff00) | vers[1]; + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + } + + /* load the server hello data */ + /* load the server random */ + if (!PACKET_copy_bytes(pkt, s->s3->server_random, SSL3_RANDOM_SIZE)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); + goto f_err; + } + + s->hit = 0; + + /* Get the session-id. */ + if (!PACKET_get_length_prefixed_1(pkt, &session_id)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_SSL3_GET_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); + goto f_err; + } + session_id_len = PACKET_remaining(&session_id); + if (session_id_len > sizeof s->session->session_id + || session_id_len > SSL3_SESSION_ID_SIZE) { + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_SSL3_SESSION_ID_TOO_LONG); + goto f_err; + } + + if (!PACKET_get_bytes(pkt, &cipherchars, TLS_CIPHER_LEN)) { + SSLerr(SSL_F_SSL3_GET_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); + al = SSL_AD_DECODE_ERROR; + goto f_err; + } + + /* + * Check if we can resume the session based on external pre-shared secret. + * EAP-FAST (RFC 4851) supports two types of session resumption. + * Resumption based on server-side state works with session IDs. + * Resumption based on pre-shared Protected Access Credentials (PACs) + * works by overriding the SessionTicket extension at the application + * layer, and does not send a session ID. (We do not know whether EAP-FAST + * servers would honour the session ID.) Therefore, the session ID alone + * is not a reliable indicator of session resumption, so we first check if + * we can resume, and later peek at the next handshake message to see if the + * server wants to resume. + */ + if (s->version >= TLS1_VERSION && s->tls_session_secret_cb && + s->session->tlsext_tick) { + 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, cipherchars); + } else { + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, ERR_R_INTERNAL_ERROR); + al = SSL_AD_INTERNAL_ERROR; + goto f_err; + } + } + + if (session_id_len != 0 && session_id_len == s->session->session_id_length + && memcmp(PACKET_data(&session_id), s->session->session_id, + session_id_len) == 0) { + if (s->sid_ctx_length != s->session->sid_ctx_length + || memcmp(s->session->sid_ctx, s->sid_ctx, s->sid_ctx_length)) { + /* actually a client application bug */ + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT); + goto f_err; + } + s->hit = 1; + } else { + /* + * If we were trying for session-id reuse but the server + * didn't echo the ID, make a new SSL_SESSION. + * In the case of EAP-FAST and PAC, we do not send a session ID, + * so the PAC-based session secret is always preserved. It'll be + * overwritten if the server refuses resumption. + */ + if (s->session->session_id_length > 0) { + if (!ssl_get_new_session(s, 0)) { + goto f_err; + } + } + + s->session->session_id_length = session_id_len; + /* session_id_len could be 0 */ + memcpy(s->session->session_id, PACKET_data(&session_id), + session_id_len); + } + + c = ssl_get_cipher_by_char(s, cipherchars); + if (c == NULL) { + /* unknown cipher */ + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_UNKNOWN_CIPHER_RETURNED); + goto f_err; + } + /* Set version disabled mask now we know version */ + if (!SSL_USE_TLS1_2_CIPHERS(s)) + s->s3->tmp.mask_ssl = SSL_TLSV1_2; + else + s->s3->tmp.mask_ssl = 0; + /* + * If it is a disabled cipher we didn't send it in client hello, so + * return an error. + */ + if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_CHECK)) { + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_WRONG_CIPHER_RETURNED); + goto f_err; + } + + sk = ssl_get_ciphers_by_id(s); + i = sk_SSL_CIPHER_find(sk, c); + if (i < 0) { + /* we did not say we would use this cipher */ + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_WRONG_CIPHER_RETURNED); + goto f_err; + } + + /* + * Depending on the session caching (internal/external), the cipher + * and/or cipher_id values may not be set. Make sure that cipher_id is + * set and use it for comparison. + */ + if (s->session->cipher) + s->session->cipher_id = s->session->cipher->id; + if (s->hit && (s->session->cipher_id != c->id)) { + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED); + goto f_err; + } + s->s3->tmp.new_cipher = c; + /* + * Don't digest cached records if no sigalgs: we may need them for client + * authentication. + */ + if (!SSL_USE_SIGALGS(s) && !ssl3_digest_cached_records(s, 0)) + goto f_err; + /* lets get the compression algorithm */ + /* COMPRESSION */ + if (!PACKET_get_1(pkt, &compression)) { + SSLerr(SSL_F_SSL3_GET_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); + al = SSL_AD_DECODE_ERROR; + goto f_err; + } +#ifdef OPENSSL_NO_COMP + if (compression != 0) { + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM); + goto f_err; + } + /* + * If compression is disabled we'd better not try to resume a session + * using compression. + */ + if (s->session->compress_meth != 0) { + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_INCONSISTENT_COMPRESSION); + goto f_err; + } +#else + if (s->hit && compression != s->session->compress_meth) { + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED); + goto f_err; + } + if (compression == 0) + comp = NULL; + else if (!ssl_allow_compression(s)) { + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_COMPRESSION_DISABLED); + goto f_err; + } else { + comp = ssl3_comp_find(s->ctx->comp_methods, compression); + } + + if (compression != 0 && comp == NULL) { + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM); + goto f_err; + } else { + s->s3->tmp.new_compression = comp; + } +#endif + + /* TLS extensions */ + if (!ssl_parse_serverhello_tlsext(s, pkt)) { + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_PARSE_TLSEXT); + goto err; + } + + if (PACKET_remaining(pkt) != 0) { + /* wrong packet length */ + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_BAD_PACKET_LENGTH); + goto f_err; + } + +#ifndef OPENSSL_NO_SCTP + if (SSL_IS_DTLS(s) && s->hit) { + unsigned char sctpauthkey[64]; + char labelbuffer[sizeof(DTLS1_SCTP_AUTH_LABEL)]; + + /* + * Add new shared key for SCTP-Auth, will be ignored if + * no SCTP used. + */ + snprintf((char *)labelbuffer, + sizeof(DTLS1_SCTP_AUTH_LABEL), + DTLS1_SCTP_AUTH_LABEL); + + if (SSL_export_keying_material(s, sctpauthkey, + sizeof(sctpauthkey), + labelbuffer, + sizeof(labelbuffer), NULL, 0, + 0) <= 0) + goto err; + + BIO_ctrl(SSL_get_wbio(s), + BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, + sizeof(sctpauthkey), sctpauthkey); + } +#endif + + return MSG_PROCESS_CONTINUE_READING; + f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + err: + statem_set_error(s); + return MSG_PROCESS_ERROR; +} + +enum MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt) +{ + int al, i, ret = MSG_PROCESS_ERROR, exp_idx; + unsigned long cert_list_len, cert_len; + X509 *x = NULL; + unsigned char *certstart, *certbytes; + STACK_OF(X509) *sk = NULL; + EVP_PKEY *pkey = NULL; + + if ((sk = sk_X509_new_null()) == NULL) { + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!PACKET_get_net_3(pkt, &cert_list_len) + || PACKET_remaining(pkt) != cert_list_len) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_LENGTH_MISMATCH); + goto f_err; + } + while (PACKET_remaining(pkt)) { + if (!PACKET_get_net_3(pkt, &cert_len) + || !PACKET_get_bytes(pkt, &certbytes, cert_len)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, + SSL_R_CERT_LENGTH_MISMATCH); + goto f_err; + } + + certstart = certbytes; + x = d2i_X509(NULL, (const unsigned char **)&certbytes, cert_len); + if (x == NULL) { + al = SSL_AD_BAD_CERTIFICATE; + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, ERR_R_ASN1_LIB); + goto f_err; + } + if (certbytes != (certstart + cert_len)) { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, + SSL_R_CERT_LENGTH_MISMATCH); + goto f_err; + } + if (!sk_X509_push(sk, x)) { + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, ERR_R_MALLOC_FAILURE); + goto err; + } + x = NULL; + } + + i = ssl_verify_cert_chain(s, sk); + if (s->verify_mode != SSL_VERIFY_NONE && i <= 0) { + al = ssl_verify_alarm_type(s->verify_result); + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, + SSL_R_CERTIFICATE_VERIFY_FAILED); + goto f_err; + } + ERR_clear_error(); /* but we keep s->verify_result */ + if (i > 1) { + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, i); + al = SSL_AD_HANDSHAKE_FAILURE; + goto f_err; + } + + s->session->peer_chain = sk; + /* + * Inconsistency alert: cert_chain does include the peer's certificate, + * which we don't include in s3_srvr.c + */ + x = sk_X509_value(sk, 0); + sk = NULL; + /* + * VRS 19990621: possible memory leak; sk=null ==> !sk_pop_free() @end + */ + + pkey = X509_get_pubkey(x); + + if (pkey == NULL || EVP_PKEY_missing_parameters(pkey)) { + x = NULL; + al = SSL3_AL_FATAL; + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, + SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS); + goto f_err; + } + + i = ssl_cert_type(x, pkey); + if (i < 0) { + x = NULL; + al = SSL3_AL_FATAL; + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, + SSL_R_UNKNOWN_CERTIFICATE_TYPE); + goto f_err; + } + + exp_idx = ssl_cipher_get_cert_index(s->s3->tmp.new_cipher); + if (exp_idx >= 0 && i != exp_idx) { + x = NULL; + al = SSL_AD_ILLEGAL_PARAMETER; + SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, + SSL_R_WRONG_CERTIFICATE_TYPE); + goto f_err; + } + s->session->peer_type = i; + + X509_free(s->session->peer); + X509_up_ref(x); + s->session->peer = x; + s->session->verify_result = s->verify_result; + + x = NULL; + ret = MSG_PROCESS_CONTINUE_READING; + goto done; + + f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + err: + statem_set_error(s); + done: + EVP_PKEY_free(pkey); + X509_free(x); + sk_X509_pop_free(sk, X509_free); + return ret; +} + +enum MSG_PROCESS_RETURN tls_process_key_exchange(SSL *s, PACKET *pkt) +{ +#ifndef OPENSSL_NO_RSA + unsigned char *q, md_buf[EVP_MAX_MD_SIZE * 2]; +#endif + EVP_MD_CTX md_ctx; + int al, j, verify_ret; + long alg_k, alg_a; + EVP_PKEY *pkey = NULL; + const EVP_MD *md = NULL; +#ifndef OPENSSL_NO_RSA + RSA *rsa = NULL; +#endif +#ifndef OPENSSL_NO_DH + DH *dh = NULL; +#endif +#ifndef OPENSSL_NO_EC + EC_KEY *ecdh = NULL; + BN_CTX *bn_ctx = NULL; + EC_POINT *srvr_ecpoint = NULL; + int curve_nid = 0; +#endif + PACKET save_p |