Commit 8025e251 authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

PR: 2121

Submitted by: Robin Seggelmann <seggelmann@fh-muenster.de>

Add extension support to DTLS code mainly using existing implementation for
TLS.
parent 637f374a
Loading
Loading
Loading
Loading
+60 −1
Original line number Diff line number Diff line
@@ -286,16 +286,44 @@ int dtls1_connect(SSL *s)

		case SSL3_ST_CR_CERT_A:
		case SSL3_ST_CR_CERT_B:
#ifndef OPENSSL_NO_TLSEXT
			ret=ssl3_check_finished(s);
			if (ret <= 0) goto end;
			if (ret == 2)
				{
				s->hit = 1;
				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;
				}
#endif
			/* Check if it is anon DH or PSK */
			if (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) &&
			    !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK))
				{
				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;

@@ -437,11 +465,36 @@ int dtls1_connect(SSL *s)
				}
			else
				{
#ifndef OPENSSL_NO_TLSEXT
				/* Allow NewSessionTicket if ticket expected */
				if (s->tlsext_ticket_expected)
					s->s3->tmp.next_state=SSL3_ST_CR_SESSION_TICKET_A;
				else
#endif
				
				s->s3->tmp.next_state=SSL3_ST_CR_FINISHED_A;
				}
			s->init_num=0;
			break;

#ifndef OPENSSL_NO_TLSEXT
		case SSL3_ST_CR_SESSION_TICKET_A:
		case SSL3_ST_CR_SESSION_TICKET_B:
			ret=ssl3_get_new_session_ticket(s);
			if (ret <= 0) goto end;
			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:
		case SSL3_ST_CR_FINISHED_B:
			s->d1->change_cipher_spec_ok = 1;
@@ -554,8 +607,14 @@ int dtls1_client_hello(SSL *s)
	buf=(unsigned char *)s->init_buf->data;
	if (s->state == SSL3_ST_CW_CLNT_HELLO_A)
		{
		SSL_SESSION *sess = s->session;
		if ((s->session == NULL) ||
			(s->session->ssl_version != s->version) ||
#ifdef OPENSSL_NO_TLSEXT
			!sess->session_id_length ||
#else
			(!sess->session_id_length && !sess->tlsext_tick) ||
#endif
			(s->session->not_resumable))
			{
			if (!ssl_get_new_session(s,0))
@@ -637,7 +696,7 @@ int dtls1_client_hello(SSL *s)
		*(p++)=0; /* Add the NULL method */

#ifndef OPENSSL_NO_TLSEXT
		if ((p = ssl_add_clienthello_dtlsext(s, p, buf+SSL3_RT_MAX_PLAIN_LENGTH)) == NULL)
		if ((p = ssl_add_clienthello_tlsext(s, p, buf+SSL3_RT_MAX_PLAIN_LENGTH)) == NULL)
			{
			SSLerr(SSL_F_SSL3_CLIENT_HELLO,ERR_R_INTERNAL_ERROR);
			goto err;
+0 −191
Original line number Diff line number Diff line
@@ -382,194 +382,3 @@ int dtls1_listen(SSL *s, struct sockaddr *client)
	(void) BIO_dgram_get_peer(SSL_get_rbio(s), client);
	return 1;
	}

#ifndef OPENSSL_NO_TLSEXT
unsigned char *ssl_add_clienthello_dtlsext(SSL *s, unsigned char *p, unsigned char *limit)
	{
	int extdatalen = 0;
	unsigned char *ret = p;
	int el;

	ret+=2;
	
	if (ret>=limit) return NULL; /* this really never occurs, but ... */

	/* Renegotiate extension */
	if(!ssl_add_clienthello_renegotiate_ext(s, 0, &el, 0))
		{
		SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR);
		return NULL;
		}

	if((limit - p - 4 - el) < 0) return NULL;
	  
	s2n(TLSEXT_TYPE_renegotiate,ret);
	s2n(el,ret);

	if(!ssl_add_clienthello_renegotiate_ext(s, ret, &el, el))
		{
		SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR);
		return NULL;
		}

	ret += el;

	if ((extdatalen = ret-p-2)== 0) 
		return p;

	s2n(extdatalen,p);

	return ret;
	}

int ssl_parse_clienthello_dtlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al)
	{
	unsigned short type;
	unsigned short size;
	unsigned short len;
	unsigned char *data = *p;
	int renegotiate_seen = 0;

	if (data >= (d+n-2))
		{
		if (s->new_session
			&& !(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
			{
			/* We should always see one extension: the renegotiate extension */
	 		SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_TLSEXT, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
			*al = SSL_AD_ILLEGAL_PARAMETER; /* is this the right alert? */
			return 0;
			}
		return 1;
		}
	n2s(data,len);

	if (data > (d+n-len)) 
		return 1;

	while (data <= (d+n-4))
		{
		n2s(data,type);
		n2s(data,size);
		
		if (data+size > (d+n))
	   		return 1;
		
		if (type == TLSEXT_TYPE_renegotiate)
			{
			if(!ssl_parse_clienthello_renegotiate_ext(s, data, size, al))
				return 0;
			renegotiate_seen = 1;
			}
		
		data+=size;
		}

	if (s->new_session && !renegotiate_seen
		&& !(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
		{
		*al = SSL_AD_ILLEGAL_PARAMETER; /* is this the right alert? */
	 	SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_TLSEXT, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
		return 0;
		}

	*p = data;
	return 1;
	}

unsigned char *ssl_add_serverhello_dtlsext(SSL *s, unsigned char *p, unsigned char *limit)
	{
	int extdatalen = 0;
	unsigned char *ret = p;
	
	ret+=2;
	
	if (ret>=limit) return NULL; /* this really never occurs, but ... */
	
	if(s->s3->send_connection_binding)
		{
		int el;

		if(!ssl_add_serverhello_renegotiate_ext(s, 0, &el, 0))
			{
			SSLerr(SSL_F_SSL_ADD_SERVERHELLO_TLSEXT, ERR_R_INTERNAL_ERROR);
			return NULL;
			}

		if((limit - p - 4 - el) < 0) return NULL;
          
		s2n(TLSEXT_TYPE_renegotiate,ret);
		s2n(el,ret);

		if(!ssl_add_serverhello_renegotiate_ext(s, ret, &el, el))
			{
			SSLerr(SSL_F_SSL_ADD_SERVERHELLO_TLSEXT, ERR_R_INTERNAL_ERROR);
			return NULL;
			}

		ret += el;
		}

	if ((extdatalen = ret-p-2)== 0) 
		return p;

	s2n(extdatalen,p);

	return ret;
	}

int ssl_parse_serverhello_dtlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al)
	{
	unsigned short type;
	unsigned short size;
	unsigned short len;
	unsigned char *data = *p;
	int renegotiate_seen = 0;
	
	if (data >= (d+n-2))
		{
		if (s->new_session
			&& !(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
			{
			/* We should always see one extension: the renegotiate extension */
	 		SSLerr(SSL_F_SSL_PARSE_SERVERHELLO_TLSEXT, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
			*al = SSL_AD_ILLEGAL_PARAMETER; /* is this the right alert? */
			return 0;
			}
		return 1;
		}
	n2s(data,len);
	
	if (data > (d+n-len)) 
		return 1;
	
	while (data <= (d+n-4))
		{
		n2s(data,type);
		n2s(data,size);
		
		if (data+size > (d+n))
	   		return 1;
		
		if (type == TLSEXT_TYPE_renegotiate)
			{
			if(!ssl_parse_serverhello_renegotiate_ext(s, data, size, al))
				return 0;
			renegotiate_seen = 1;
			}
		
		data+=size;
		}

	if (s->new_session && !renegotiate_seen
		&& !(s->ctx->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
		{
		*al = SSL_AD_ILLEGAL_PARAMETER; /* is this the right alert? */
	 	SSLerr(SSL_F_SSL_PARSE_SERVERHELLO_TLSEXT, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
		return 0;
		}

	*p = data;
	return 1;
	}
#endif
+160 −2
Original line number Diff line number Diff line
@@ -305,8 +305,18 @@ int dtls1_accept(SSL *s)
			ret=dtls1_send_server_hello(s);
			if (ret <= 0) goto end;

#ifndef OPENSSL_NO_TLSEXT
			if (s->hit)
				{
				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;
@@ -321,10 +331,24 @@ int dtls1_accept(SSL *s)
				dtls1_start_timer(s);
				ret=dtls1_send_server_certificate(s);
				if (ret <= 0) goto end;
#ifndef OPENSSL_NO_TLSEXT
				if (s->tlsext_status_expected)
					s->state=SSL3_ST_SW_CERT_STATUS_A;
				else
					s->state=SSL3_ST_SW_KEY_EXCH_A;
				}
			else
				{
				skip = 1;
				s->state=SSL3_ST_SW_KEY_EXCH_A;
				}
#else
				}
			else
				skip=1;

			s->state=SSL3_ST_SW_KEY_EXCH_A;
#endif
			s->init_num=0;
			break;

@@ -519,11 +543,34 @@ int dtls1_accept(SSL *s)
			dtls1_stop_timer(s);
			if (s->hit)
				s->state=SSL_ST_OK;
#ifndef OPENSSL_NO_TLSEXT
			else if (s->tlsext_ticket_expected)
				s->state=SSL3_ST_SW_SESSION_TICKET_A;
#endif
			else
				s->state=SSL3_ST_SW_CHANGE_A;
			s->init_num=0;
			break;

#ifndef OPENSSL_NO_TLSEXT
		case SSL3_ST_SW_SESSION_TICKET_A:
		case SSL3_ST_SW_SESSION_TICKET_B:
			ret=dtls1_send_newsession_ticket(s);
			if (ret <= 0) goto end;
			s->state=SSL3_ST_SW_CHANGE_A;
			s->init_num=0;
			break;

		case SSL3_ST_SW_CERT_STATUS_A:
		case SSL3_ST_SW_CERT_STATUS_B:
			ret=ssl3_send_cert_status(s);
			if (ret <= 0) goto end;
			s->state=SSL3_ST_SW_KEY_EXCH_A;
			s->init_num=0;
			break;

#endif

		case SSL3_ST_SW_CHANGE_A:
		case SSL3_ST_SW_CHANGE_B:

@@ -765,7 +812,7 @@ int dtls1_send_server_hello(SSL *s)
#endif

#ifndef OPENSSL_NO_TLSEXT
		if ((p = ssl_add_serverhello_dtlsext(s, p, buf+SSL3_RT_MAX_PLAIN_LENGTH)) == NULL)
		if ((p = ssl_add_serverhello_tlsext(s, p, buf+SSL3_RT_MAX_PLAIN_LENGTH)) == NULL)
			{
			SSLerr(SSL_F_SSL3_SEND_SERVER_HELLO,ERR_R_INTERNAL_ERROR);
			return -1;
@@ -1394,3 +1441,114 @@ int dtls1_send_server_certificate(SSL *s)
	/* SSL3_ST_SW_CERT_B */
	return(dtls1_do_write(s,SSL3_RT_HANDSHAKE));
	}

#ifndef OPENSSL_NO_TLSEXT
int dtls1_send_newsession_ticket(SSL *s)
	{
	if (s->state == SSL3_ST_SW_SESSION_TICKET_A)
		{
		unsigned char *p, *senc, *macstart;
		int len, slen;
		unsigned int hlen, msg_len;
		EVP_CIPHER_CTX ctx;
		HMAC_CTX hctx;
		SSL_CTX *tctx = s->initial_ctx;
		unsigned char iv[EVP_MAX_IV_LENGTH];
		unsigned char key_name[16];

		/* get session encoding length */
		slen = i2d_SSL_SESSION(s->session, NULL);
		/* Some length values are 16 bits, so forget it if session is
 		 * too long
 		 */
		if (slen > 0xFF00)
			return -1;
		/* Grow buffer if need be: the length calculation is as
 		 * follows 12 (DTLS handshake message header) +
 		 * 4 (ticket lifetime hint) + 2 (ticket length) +
 		 * 16 (key name) + max_iv_len (iv length) +
 		 * session_length + max_enc_block_size (max encrypted session
 		 * length) + max_md_size (HMAC).
 		 */
		if (!BUF_MEM_grow(s->init_buf,
			DTLS1_HM_HEADER_LENGTH + 22 + EVP_MAX_IV_LENGTH +
			EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE + slen))
			return -1;
		senc = OPENSSL_malloc(slen);
		if (!senc)
			return -1;
		p = senc;
		i2d_SSL_SESSION(s->session, &p);

		p=(unsigned char *)&(s->init_buf->data[DTLS1_HM_HEADER_LENGTH]);
		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 (tctx->tlsext_ticket_key_cb)
			{
			if (tctx->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,
					tctx->tlsext_tick_aes_key, iv);
			HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16,
					tlsext_tick_md(), NULL);
			memcpy(key_name, tctx->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, key_name, 16);
		p += 16;
		/* output IV */
		memcpy(p, iv, EVP_CIPHER_CTX_iv_length(&ctx));
		p += EVP_CIPHER_CTX_iv_length(&ctx);
		/* Encrypt session data */
		EVP_EncryptUpdate(&ctx, p, &len, senc, slen);
		p += len;
		EVP_EncryptFinal(&ctx, p, &len);
		p += len;
		EVP_CIPHER_CTX_cleanup(&ctx);

		HMAC_Update(&hctx, macstart, p - macstart);
		HMAC_Final(&hctx, p, &hlen);
		HMAC_CTX_cleanup(&hctx);

		p += hlen;
		/* Now write out lengths: p points to end of data written */
		/* Total length */
		len = p - (unsigned char *)&(s->init_buf->data[DTLS1_HM_HEADER_LENGTH]);
		p=(unsigned char *)&(s->init_buf->data[DTLS1_HM_HEADER_LENGTH]) + 4;
		s2n(len - 18, p);  /* Ticket length */

		/* number of bytes to write */
		s->init_num= len;
		s->state=SSL3_ST_SW_SESSION_TICKET_B;
		s->init_off=0;
		OPENSSL_free(senc);

		/* XDTLS:  set message header ? */
		msg_len = s->init_num - DTLS1_HM_HEADER_LENGTH;
		dtls1_set_message_header(s, (void *)s->init_buf->data,
			SSL3_MT_NEWSESSION_TICKET, msg_len, 0, msg_len);

		/* buffer the message to handle re-xmits */
		dtls1_buffer_message(s, 0);
		}

	/* SSL3_ST_SW_SESSION_TICKET_B */
	return(dtls1_do_write(s,SSL3_RT_HANDSHAKE));
	}
#endif
+3 −16
Original line number Diff line number Diff line
@@ -166,9 +166,6 @@

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);
#ifndef OPENSSL_NO_TLSEXT
static int ssl3_check_finished(SSL *s);
#endif

static const SSL_METHOD *ssl3_get_client_method(int ver)
	{
@@ -915,7 +912,7 @@ int ssl3_get_server_hello(SSL *s)

#ifndef OPENSSL_NO_TLSEXT
	/* TLS extensions*/
	if (s->version > SSL3_VERSION && s->version != DTLS1_VERSION && s->version != DTLS1_BAD_VER)
	if (s->version > SSL3_VERSION)
		{
		if (!ssl_parse_serverhello_tlsext(s,&p,d,n, &al))
			{
@@ -929,17 +926,6 @@ int ssl3_get_server_hello(SSL *s)
				goto err;
			}
		}

	/* DTLS extensions */
	if (s->version == DTLS1_VERSION || s->version == DTLS1_BAD_VER)
	{
		if (!ssl_parse_serverhello_dtlsext(s,&p,d,n, &al))
		{
			/* 'al' set by ssl_parse_serverhello_dtlsext */
			SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PARSE_TLSEXT);
			goto f_err;
		}
	}
#endif

	if (p != (d+n))
@@ -1832,6 +1818,7 @@ int ssl3_get_new_session_ticket(SSL *s)
		SSLerr(SSL_F_SSL3_GET_NEW_SESSION_TICKET,SSL_R_LENGTH_MISMATCH);
		goto f_err;
		}

	p=d=(unsigned char *)s->init_msg;
	n2l(p, s->session->tlsext_tick_lifetime_hint);
	n2s(p, ticklen);
@@ -2996,7 +2983,7 @@ err:
 */

#ifndef OPENSSL_NO_TLSEXT
static int ssl3_check_finished(SSL *s)
int ssl3_check_finished(SSL *s)
	{
	int ok;
	long n;
+1 −12
Original line number Diff line number Diff line
@@ -1015,7 +1015,7 @@ int ssl3_get_client_hello(SSL *s)

#ifndef OPENSSL_NO_TLSEXT
	/* TLS extensions*/
	if (s->version > SSL3_VERSION && s->version != DTLS1_VERSION && s->version != DTLS1_BAD_VER)
	if (s->version > SSL3_VERSION)
		{
		if (!ssl_parse_clienthello_tlsext(s,&p,d,n, &al))
			{
@@ -1081,17 +1081,6 @@ int ssl3_get_client_hello(SSL *s)
			s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
			}
		}

	/* DTLS extensions */
	if (s->version == DTLS1_VERSION || s->version == DTLS1_BAD_VER)
		{
		if (!ssl_parse_clienthello_dtlsext(s,&p,d,n, &al))
			{
				/* 'al' set by ssl_parse_clienthello_dtlsext */
				SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PARSE_TLSEXT);
				goto f_err;
			}
		}
#endif

	/* Worst case, we will use the NULL compression, but if we have other
Loading