Commit message: Fix crash in dtls1_get_record whilst in the listen state where you get two
separate reads performed - one for the header and one for the body of the
handshake record.

CVE-2014-3571

Reviewed-by: Matt Caswell <matt@openssl.org>
CVE: CVE-2014-3571
CWE: NVD-CWE-Other
Vulnerability description: OpenSSL before 0.9.8zd, 1.0.0 before 1.0.0p, and 1.0.1 before 1.0.1k allows remote attackers to cause a denial of service (NULL pointer dereference and application crash) via a crafted DTLS message that is processed with a different read operation for the handshake header than for the handshake body, related to the dtls1_get_record function in d1_pkt.c and the ssl3_read_n function in s3_pkt.c.

Project: openssl

static int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
			 unsigned int len, int create_empty_fragment)
	{
	unsigned char *p,*plen;
	int i,mac_size,clear=0;
	int prefix_len=0;
	int eivlen;
	long align=0;
	SSL3_RECORD *wr;
	SSL3_BUFFER *wb=&(s->s3->wbuf);
	SSL_SESSION *sess;


	/* first check if there is a SSL3_BUFFER still being written
	 * out.  This will happen with non blocking IO */
	if (wb->left != 0)
		return(ssl3_write_pending(s,type,buf,len));

	/* If we have an alert to send, lets send it */
	if (s->s3->alert_dispatch)
		{
		i=s->method->ssl_dispatch_alert(s);
		if (i <= 0)
			return(i);
		/* if it went, fall through and send more stuff */
		}

 	if (wb->buf == NULL)
		if (!ssl3_setup_write_buffer(s))
			return -1;

	if (len == 0 && !create_empty_fragment)
		return 0;

	wr= &(s->s3->wrec);
	sess=s->session;

	if (	(sess == NULL) ||
		(s->enc_write_ctx == NULL) ||
		(EVP_MD_CTX_md(s->write_hash) == NULL))
		{
#if 1
		clear=s->enc_write_ctx?0:1;	/* must be AEAD cipher */
#else
		clear=1;
#endif
		mac_size=0;
		}
	else
		{
		mac_size=EVP_MD_CTX_size(s->write_hash);
		if (mac_size < 0)
			goto err;
		}

#if 0 && !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
	if (type==SSL3_RT_APPLICATION_DATA && s->compress==NULL &&
	    !SSL_USE_ETM(s) && SSL_USE_EXPLICIT_IV(s) && /*!SSL_IS_DTLS(s) &&*/
	    EVP_CIPHER_flags(s->enc_write_ctx->cipher)&EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK)
		do {
		unsigned char aad[13];
		EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param = {NULL,aad,sizeof(aad),0};
		int packlen;

		memcpy(aad,s->s3->write_sequence,8);
		aad[8]=type;
		aad[9]=(unsigned char)(s->version>>8);
		aad[10]=(unsigned char)(s->version);
		aad[11]=(unsigned char)(len>>8);
		aad[12]=(unsigned char)len;
		packlen = EVP_CIPHER_CTX_ctrl(s->enc_write_ctx,
				EVP_CTRL_TLS1_1_MULTIBLOCK_AAD,
				sizeof(mb_param),&mb_param);

		if (packlen==0 || packlen > wb->len) break;

		mb_param.out = wb->buf;
		mb_param.inp = buf;
		mb_param.len = len;
		EVP_CIPHER_CTX_ctrl(s->enc_write_ctx,
				EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT,
				sizeof(mb_param),&mb_param);

		s->s3->write_sequence[7] += mb_param.interleave;
		if (s->s3->write_sequence[7] < mb_param.interleave)
			{
			int j=6;
			while (j>=0 && (++s->s3->write_sequence[j--])==0) ;
			}

		wb->offset=0;
		wb->left = packlen;

		/* memorize arguments so that ssl3_write_pending can detect bad write retries later */
		s->s3->wpend_tot=len;
		s->s3->wpend_buf=buf;
		s->s3->wpend_type=type;
		s->s3->wpend_ret=len;

		/* we now just need to write the buffer */
		return ssl3_write_pending(s,type,buf,len);
		} while (0);
#endif

	/* 'create_empty_fragment' is true only when this function calls itself */
	if (!clear && !create_empty_fragment && !s->s3->empty_fragment_done)
		{
		/* countermeasure against known-IV weakness in CBC ciphersuites
		 * (see http://www.openssl.org/~bodo/tls-cbc.txt) */

		if (s->s3->need_empty_fragments && type == SSL3_RT_APPLICATION_DATA)
			{
			/* recursive function call with 'create_empty_fragment' set;
			 * this prepares and buffers the data for an empty fragment
			 * (these 'prefix_len' bytes are sent out later
			 * together with the actual payload) */
			prefix_len = do_ssl3_write(s, type, buf, 0, 1);
			if (prefix_len <= 0)
				goto err;

			if (prefix_len >
		(SSL3_RT_HEADER_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD))
				{
				/* insufficient space */
				SSLerr(SSL_F_DO_SSL3_WRITE, ERR_R_INTERNAL_ERROR);
				goto err;
				}
			}
		
		s->s3->empty_fragment_done = 1;
		}

	if (create_empty_fragment)
		{
#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
		/* extra fragment would be couple of cipher blocks,
		 * which would be multiple of SSL3_ALIGN_PAYLOAD, so
		 * if we want to align the real payload, then we can
		 * just pretent we simply have two headers. */
		align = (long)wb->buf + 2*SSL3_RT_HEADER_LENGTH;
		align = (-align)&(SSL3_ALIGN_PAYLOAD-1);
#endif
		p = wb->buf + align;
		wb->offset  = align;
		}
	else if (prefix_len)
		{
		p = wb->buf + wb->offset + prefix_len;
		}
	else
		{
#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
		align = (long)wb->buf + SSL3_RT_HEADER_LENGTH;
		align = (-align)&(SSL3_ALIGN_PAYLOAD-1);
#endif
		p = wb->buf + align;
		wb->offset  = align;
		}

	/* write the header */

	*(p++)=type&0xff;
	wr->type=type;

	*(p++)=(s->version>>8);
	/* Some servers hang if iniatial client hello is larger than 256
	 * bytes and record version number > TLS 1.0
	 */
	if (s->state == SSL3_ST_CW_CLNT_HELLO_B
				&& !s->renegotiate
				&& TLS1_get_version(s) > TLS1_VERSION)
		*(p++) = 0x1;
	else
		*(p++)=s->version&0xff;

	/* field where we are to write out packet length */
	plen=p; 
	p+=2;
	/* Explicit IV length, block ciphers appropriate version flag */
	if (s->enc_write_ctx && SSL_USE_EXPLICIT_IV(s))
		{
		int mode = EVP_CIPHER_CTX_mode(s->enc_write_ctx);
		if (mode == EVP_CIPH_CBC_MODE)
			{
			eivlen = EVP_CIPHER_CTX_iv_length(s->enc_write_ctx);
			if (eivlen <= 1)
				eivlen = 0;
			}
		/* Need explicit part of IV for GCM mode */
		else if (mode == EVP_CIPH_GCM_MODE)
			eivlen = EVP_GCM_TLS_EXPLICIT_IV_LEN;
		else
			eivlen = 0;
		}
	else 
		eivlen = 0;

	/* lets setup the record stuff. */
	wr->data=p + eivlen;
	wr->length=(int)len;
	wr->input=(unsigned char *)buf;

	/* we now 'read' from wr->input, wr->length bytes into
	 * wr->data */

	/* first we compress */
	if (s->compress != NULL)
		{
		if (!ssl3_do_compress(s))
			{
			SSLerr(SSL_F_DO_SSL3_WRITE,SSL_R_COMPRESSION_FAILURE);
			goto err;
			}
		}
	else
		{
		memcpy(wr->data,wr->input,wr->length);
		wr->input=wr->data;
		}

	/* we should still have the output to wr->data and the input
	 * from wr->input.  Length should be wr->length.
	 * wr->data still points in the wb->buf */

	if (!SSL_USE_ETM(s) && mac_size != 0)
		{
		if (s->method->ssl3_enc->mac(s,&(p[wr->length + eivlen]),1) < 0)
			goto err;
		wr->length+=mac_size;
		}

	wr->input=p;
	wr->data=p;

	if (eivlen)
		{
	/*	if (RAND_pseudo_bytes(p, eivlen) <= 0)
			goto err; */
		wr->length += eivlen;
		}

	if(s->method->ssl3_enc->enc(s,1)<1) goto err;

	if (SSL_USE_ETM(s) && mac_size != 0)
		{
		if (s->method->ssl3_enc->mac(s,p + wr->length,1) < 0)
			goto err;
		wr->length+=mac_size;
		}

	/* record length after mac and block padding */
	s2n(wr->length,plen);

	if (s->msg_callback)
		s->msg_callback(1, 0, SSL3_RT_HEADER, plen - 5, 5, s, s->msg_callback_arg);

	/* we should now have
	 * wr->data pointing to the encrypted data, which is
	 * wr->length long */
	wr->type=type; /* not needed but helps for debugging */
	wr->length+=SSL3_RT_HEADER_LENGTH;

	if (create_empty_fragment)
		{
		/* we are in a recursive call;
		 * just return the length, don't write out anything here
		 */
		return wr->length;
		}

	/* now let's set up wb */
	wb->left = prefix_len + wr->length;

	/* memorize arguments so that ssl3_write_pending can detect bad write retries later */
	s->s3->wpend_tot=len;
	s->s3->wpend_buf=buf;
	s->s3->wpend_type=type;
	s->s3->wpend_ret=len;

	/* we now just need to write the buffer */
	return ssl3_write_pending(s,type,buf,len);
err:
	return -1;
	}

Project: openssl

int ssl3_dispatch_alert(SSL *s)
	{
	int i,j;
	void (*cb)(const SSL *ssl,int type,int val)=NULL;

	s->s3->alert_dispatch=0;
	i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], 2, 0);
	if (i <= 0)
		{
		s->s3->alert_dispatch=1;
		}
	else
		{
		/* Alert sent to BIO.  If it is important, flush it now.
		 * If the message does not get sent due to non-blocking IO,
		 * we will not worry too much. */
		if (s->s3->send_alert[0] == SSL3_AL_FATAL)
			(void)BIO_flush(s->wbio);

		if (s->msg_callback)
			s->msg_callback(1, s->version, SSL3_RT_ALERT, s->s3->send_alert, 2, s, s->msg_callback_arg);

		if (s->info_callback != NULL)
			cb=s->info_callback;
		else if (s->ctx->info_callback != NULL)
			cb=s->ctx->info_callback;

		if (cb != NULL)
			{
			j=(s->s3->send_alert[0]<<8)|s->s3->send_alert[1];
			cb(s,SSL_CB_WRITE_ALERT,j);
			}
		}
	return(i);
	}

Project: openssl

int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
	{
	int al,i,j,ret;
	unsigned int n;
	SSL3_RECORD *rr;
	void (*cb)(const SSL *ssl,int type2,int val)=NULL;

	if (s->s3->rbuf.buf == NULL) /* Not initialized yet */
		if (!ssl3_setup_read_buffer(s))
			return(-1);

	if ((type && (type != SSL3_RT_APPLICATION_DATA) && (type != SSL3_RT_HANDSHAKE)) ||
	    (peek && (type != SSL3_RT_APPLICATION_DATA)))
		{
		SSLerr(SSL_F_SSL3_READ_BYTES, ERR_R_INTERNAL_ERROR);
		return -1;
		}

	if ((type == SSL3_RT_HANDSHAKE) && (s->s3->handshake_fragment_len > 0))
		/* (partially) satisfy request from storage */
		{
		unsigned char *src = s->s3->handshake_fragment;
		unsigned char *dst = buf;
		unsigned int k;

		/* peek == 0 */
		n = 0;
		while ((len > 0) && (s->s3->handshake_fragment_len > 0))
			{
			*dst++ = *src++;
			len--; s->s3->handshake_fragment_len--;
			n++;
			}
		/* move any remaining fragment bytes: */
		for (k = 0; k < s->s3->handshake_fragment_len; k++)
			s->s3->handshake_fragment[k] = *src++;
		return n;
	}

	/* Now s->s3->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */

	if (!s->in_handshake && SSL_in_init(s))
		{
		/* type == SSL3_RT_APPLICATION_DATA */
		i=s->handshake_func(s);
		if (i < 0) return(i);
		if (i == 0)
			{
			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
			return(-1);
			}
		}
start:
	s->rwstate=SSL_NOTHING;

	/*-
	 * s->s3->rrec.type	    - is the type of record
	 * s->s3->rrec.data,    - data
	 * s->s3->rrec.off,     - offset into 'data' for next read
	 * s->s3->rrec.length,  - number of bytes. 
	 */
	rr = &(s->s3->rrec);

	/* get new packet if necessary */
	if ((rr->length == 0) || (s->rstate == SSL_ST_READ_BODY))
		{
		ret=ssl3_get_record(s);
		if (ret <= 0) return(ret);
		}

	/* we now have a packet which can be read and processed */

	if (s->s3->change_cipher_spec /* set when we receive ChangeCipherSpec,
	                               * reset by ssl3_get_finished */
		&& (rr->type != SSL3_RT_HANDSHAKE))
		{
		al=SSL_AD_UNEXPECTED_MESSAGE;
		SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_DATA_BETWEEN_CCS_AND_FINISHED);
		goto f_err;
		}

	/* If the other end has shut down, throw anything we read away
	 * (even in 'peek' mode) */
	if (s->shutdown & SSL_RECEIVED_SHUTDOWN)
		{
		rr->length=0;
		s->rwstate=SSL_NOTHING;
		return(0);
		}


	if (type == rr->type) /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */
		{
		/* make sure that we are not getting application data when we
		 * are doing a handshake for the first time */
		if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) &&
			(s->enc_read_ctx == NULL))
			{
			al=SSL_AD_UNEXPECTED_MESSAGE;
			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_APP_DATA_IN_HANDSHAKE);
			goto f_err;
			}

		if (len <= 0) return(len);

		if ((unsigned int)len > rr->length)
			n = rr->length;
		else
			n = (unsigned int)len;

		memcpy(buf,&(rr->data[rr->off]),n);
		if (!peek)
			{
			rr->length-=n;
			rr->off+=n;
			if (rr->length == 0)
				{
				s->rstate=SSL_ST_READ_HEADER;
				rr->off=0;
				if (s->mode & SSL_MODE_RELEASE_BUFFERS && s->s3->rbuf.left == 0)
					ssl3_release_read_buffer(s);
				}
			}
		return(n);
		}


	/* If we get here, then type != rr->type; if we have a handshake
	 * message, then it was unexpected (Hello Request or Client Hello). */

	/* In case of record types for which we have 'fragment' storage,
	 * fill that so that we can process the data at a fixed place.
	 */
		{
		unsigned int dest_maxlen = 0;
		unsigned char *dest = NULL;
		unsigned int *dest_len = NULL;

		if (rr->type == SSL3_RT_HANDSHAKE)
			{
			dest_maxlen = sizeof s->s3->handshake_fragment;
			dest = s->s3->handshake_fragment;
			dest_len = &s->s3->handshake_fragment_len;
			}
		else if (rr->type == SSL3_RT_ALERT)
			{
			dest_maxlen = sizeof s->s3->alert_fragment;
			dest = s->s3->alert_fragment;
			dest_len = &s->s3->alert_fragment_len;
			}
#ifndef OPENSSL_NO_HEARTBEATS
		else if (rr->type == TLS1_RT_HEARTBEAT)
			{
			tls1_process_heartbeat(s);

			/* Exit and notify application to read again */
			rr->length = 0;
			s->rwstate=SSL_READING;
			BIO_clear_retry_flags(SSL_get_rbio(s));
			BIO_set_retry_read(SSL_get_rbio(s));
			return(-1);
			}
#endif

		if (dest_maxlen > 0)
			{
			n = dest_maxlen - *dest_len; /* available space in 'dest' */
			if (rr->length < n)
				n = rr->length; /* available bytes */

			/* now move 'n' bytes: */
			while (n-- > 0)
				{
				dest[(*dest_len)++] = rr->data[rr->off++];
				rr->length--;
				}

			if (*dest_len < dest_maxlen)
				goto start; /* fragment was too small */
			}
		}

	/*-
	 * s->s3->handshake_fragment_len == 4  iff  rr->type == SSL3_RT_HANDSHAKE;
	 * s->s3->alert_fragment_len == 2      iff  rr->type == SSL3_RT_ALERT.
	 * (Possibly rr is 'empty' now, i.e. rr->length may be 0.) 
	 */

	/* If we are a client, check for an incoming 'Hello Request': */
	if ((!s->server) &&
		(s->s3->handshake_fragment_len >= 4) &&
		(s->s3->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST) &&
		(s->session != NULL) && (s->session->cipher != NULL))
		{
		s->s3->handshake_fragment_len = 0;

		if ((s->s3->handshake_fragment[1] != 0) ||
			(s->s3->handshake_fragment[2] != 0) ||
			(s->s3->handshake_fragment[3] != 0))
			{
			al=SSL_AD_DECODE_ERROR;
			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_HELLO_REQUEST);
			goto f_err;
			}

		if (s->msg_callback)
			s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->s3->handshake_fragment, 4, s, s->msg_callback_arg);

		if (SSL_is_init_finished(s) &&
			!(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS) &&
			!s->s3->renegotiate)
			{
			ssl3_renegotiate(s);
			if (ssl3_renegotiate_check(s))
				{
				i=s->handshake_func(s);
				if (i < 0) return(i);
				if (i == 0)
					{
					SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
					return(-1);
					}

				if (!(s->mode & SSL_MODE_AUTO_RETRY))
					{
					if (s->s3->rbuf.left == 0) /* no read-ahead left? */
						{
						BIO *bio;
						/* In the case where we try to read application data,
						 * but we trigger an SSL handshake, we return -1 with
						 * the retry option set.  Otherwise renegotiation may
						 * cause nasty problems in the blocking world */
						s->rwstate=SSL_READING;
						bio=SSL_get_rbio(s);
						BIO_clear_retry_flags(bio);
						BIO_set_retry_read(bio);
						return(-1);
						}
					}
				}
			}
		/* we either finished a handshake or ignored the request,
		 * now try again to obtain the (application) data we were asked for */
		goto start;
		}
	/* If we are a server and get a client hello when renegotiation isn't
	 * allowed send back a no renegotiation alert and carry on.
	 * WARNING: experimental code, needs reviewing (steve)
	 */
	if (s->server &&
		SSL_is_init_finished(s) &&
    		!s->s3->send_connection_binding &&
		(s->version > SSL3_VERSION) &&
		(s->s3->handshake_fragment_len >= 4) &&
		(s->s3->handshake_fragment[0] == SSL3_MT_CLIENT_HELLO) &&
		(s->session != NULL) && (s->session->cipher != NULL) &&
		!(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
		
		{
		/*s->s3->handshake_fragment_len = 0;*/
		rr->length = 0;
		ssl3_send_alert(s,SSL3_AL_WARNING, SSL_AD_NO_RENEGOTIATION);
		goto start;
		}
	if (s->s3->alert_fragment_len >= 2)
		{
		int alert_level = s->s3->alert_fragment[0];
		int alert_descr = s->s3->alert_fragment[1];

		s->s3->alert_fragment_len = 0;

		if (s->msg_callback)
			s->msg_callback(0, s->version, SSL3_RT_ALERT, s->s3->alert_fragment, 2, s, s->msg_callback_arg);

		if (s->info_callback != NULL)
			cb=s->info_callback;
		else if (s->ctx->info_callback != NULL)
			cb=s->ctx->info_callback;

		if (cb != NULL)
			{
			j = (alert_level << 8) | alert_descr;
			cb(s, SSL_CB_READ_ALERT, j);
			}

		if (alert_level == 1) /* warning */
			{
			s->s3->warn_alert = alert_descr;
			if (alert_descr == SSL_AD_CLOSE_NOTIFY)
				{
				s->shutdown |= SSL_RECEIVED_SHUTDOWN;
				return(0);
				}
			/* This is a warning but we receive it if we requested
			 * renegotiation and the peer denied it. Terminate with
			 * a fatal alert because if application tried to
			 * renegotiatie it presumably had a good reason and
			 * expects it to succeed.
			 *
			 * In future we might have a renegotiation where we
			 * don't care if the peer refused it where we carry on.
			 */
			else if (alert_descr == SSL_AD_NO_RENEGOTIATION)
				{
				al = SSL_AD_HANDSHAKE_FAILURE;
				SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_NO_RENEGOTIATION);
				goto f_err;
				}
#ifdef SSL_AD_MISSING_SRP_USERNAME
			else if (alert_descr == SSL_AD_MISSING_SRP_USERNAME)
				return(0);
#endif
			}
		else if (alert_level == 2) /* fatal */
			{
			char tmp[16];

			s->rwstate=SSL_NOTHING;
			s->s3->fatal_alert = alert_descr;
			SSLerr(SSL_F_SSL3_READ_BYTES, SSL_AD_REASON_OFFSET + alert_descr);
			BIO_snprintf(tmp,sizeof tmp,"%d",alert_descr);
			ERR_add_error_data(2,"SSL alert number ",tmp);
			s->shutdown|=SSL_RECEIVED_SHUTDOWN;
			SSL_CTX_remove_session(s->ctx,s->session);
			return(0);
			}
		else
			{
			al=SSL_AD_ILLEGAL_PARAMETER;
			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNKNOWN_ALERT_TYPE);
			goto f_err;
			}

		goto start;
		}

	if (s->shutdown & SSL_SENT_SHUTDOWN) /* but we have not received a shutdown */
		{
		s->rwstate=SSL_NOTHING;
		rr->length=0;
		return(0);
		}

	if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC)
		{
		/* 'Change Cipher Spec' is just a single byte, so we know
		 * exactly what the record payload has to look like */
		if (	(rr->length != 1) || (rr->off != 0) ||
			(rr->data[0] != SSL3_MT_CCS))
			{
			al=SSL_AD_ILLEGAL_PARAMETER;
			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_BAD_CHANGE_CIPHER_SPEC);
			goto f_err;
			}

		/* Check we have a cipher to change to */
		if (s->s3->tmp.new_cipher == NULL)
			{
			al=SSL_AD_UNEXPECTED_MESSAGE;
			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_CCS_RECEIVED_EARLY);
			goto f_err;
			}

		if (!(s->s3->flags & SSL3_FLAGS_CCS_OK))
			{
			al=SSL_AD_UNEXPECTED_MESSAGE;
			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_CCS_RECEIVED_EARLY);
			goto f_err;
			}

		s->s3->flags &= ~SSL3_FLAGS_CCS_OK;

		rr->length=0;

		if (s->msg_callback)
			s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, 1, s, s->msg_callback_arg);

		s->s3->change_cipher_spec=1;
		if (!ssl3_do_change_cipher_spec(s))
			goto err;
		else
			goto start;
		}

	/* Unexpected handshake message (Client Hello, or protocol violation) */
	if ((s->s3->handshake_fragment_len >= 4) &&	!s->in_handshake)
		{
		if (((s->state&SSL_ST_MASK) == SSL_ST_OK) &&
			!(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS))
			{
#if 0 /* worked only because C operator preferences are not as expected (and
       * because this is not really needed for clients except for detecting
       * protocol violations): */
			s->state=SSL_ST_BEFORE|(s->server)
				?SSL_ST_ACCEPT
				:SSL_ST_CONNECT;
#else
			s->state = s->server ? SSL_ST_ACCEPT : SSL_ST_CONNECT;
#endif
			s->renegotiate=1;
			s->new_session=1;
			}
		i=s->handshake_func(s);
		if (i < 0) return(i);
		if (i == 0)
			{
			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
			return(-1);
			}

		if (!(s->mode & SSL_MODE_AUTO_RETRY))
			{
			if (s->s3->rbuf.left == 0) /* no read-ahead left? */
				{
				BIO *bio;
				/* In the case where we try to read application data,
				 * but we trigger an SSL handshake, we return -1 with
				 * the retry option set.  Otherwise renegotiation may
				 * cause nasty problems in the blocking world */
				s->rwstate=SSL_READING;
				bio=SSL_get_rbio(s);
				BIO_clear_retry_flags(bio);
				BIO_set_retry_read(bio);
				return(-1);
				}
			}
		goto start;
		}

	switch (rr->type)
		{
	default:
#ifndef OPENSSL_NO_TLS
		/* TLS up to v1.1 just ignores unknown message types:
		 * TLS v1.2 give an unexpected message alert.
		 */
		if (s->version >= TLS1_VERSION && s->version <= TLS1_1_VERSION)
			{
			rr->length = 0;
			goto start;
			}
#endif
		al=SSL_AD_UNEXPECTED_MESSAGE;
		SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNEXPECTED_RECORD);
		goto f_err;
	case SSL3_RT_CHANGE_CIPHER_SPEC:
	case SSL3_RT_ALERT:
	case SSL3_RT_HANDSHAKE:
		/* we already handled all of these, with the possible exception
		 * of SSL3_RT_HANDSHAKE when s->in_handshake is set, but that
		 * should not happen when type != rr->type */
		al=SSL_AD_UNEXPECTED_MESSAGE;
		SSLerr(SSL_F_SSL3_READ_BYTES,ERR_R_INTERNAL_ERROR);
		goto f_err;
	case SSL3_RT_APPLICATION_DATA:
		/* At this point, we were expecting handshake data,
		 * but have application data.  If the library was
		 * running inside ssl3_read() (i.e. in_read_app_data
		 * is set) and it makes sense to read application data
		 * at this point (session renegotiation not yet started),
		 * we will indulge it.
		 */
		if (s->s3->in_read_app_data &&
			(s->s3->total_renegotiations != 0) &&
			((
				(s->state & SSL_ST_CONNECT) &&
				(s->state >= SSL3_ST_CW_CLNT_HELLO_A) &&
				(s->state <= SSL3_ST_CR_SRVR_HELLO_A)
				) || (
					(s->state & SSL_ST_ACCEPT) &&
					(s->state <= SSL3_ST_SW_HELLO_REQ_A) &&
					(s->state >= SSL3_ST_SR_CLNT_HELLO_A)
					)
				))
			{
			s->s3->in_read_app_data=2;
			return(-1);
			}
		else
			{
			al=SSL_AD_UNEXPECTED_MESSAGE;
			SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_UNEXPECTED_RECORD);
			goto f_err;
			}
		}
	/* not reached */

f_err:
	ssl3_send_alert(s,SSL3_AL_FATAL,al);
err:
	return(-1);
	}

Project: openssl

int ssl3_send_alert(SSL *s, int level, int desc)
	{
	/* Map tls/ssl alert value to correct one */
	desc=s->method->ssl3_enc->alert_value(desc);
	if (s->version == SSL3_VERSION && desc == SSL_AD_PROTOCOL_VERSION)
		desc = SSL_AD_HANDSHAKE_FAILURE; /* SSL 3.0 does not have protocol_version alerts */
	if (desc < 0) return -1;
	/* If a fatal one, remove from cache */
	if ((level == SSL3_AL_FATAL) && (s->session != NULL))
		SSL_CTX_remove_session(s->ctx,s->session);

	s->s3->alert_dispatch=1;
	s->s3->send_alert[0]=level;
	s->s3->send_alert[1]=desc;
	if (s->s3->wbuf.left == 0) /* data still being written out? */
		return s->method->ssl_dispatch_alert(s);
	/* else data is still being written out, we will get written
	 * some time in the future */
	return -1;
	}