-/* ssl/d1_lib.c */
/*
* DTLS implementation written by Nagendra Modadugu
* (nagendra@cs.stanford.edu) for the OpenSSL project 2005.
static void get_current_time(struct timeval *t);
static int dtls1_set_handshake_header(SSL *s, int type, unsigned long len);
static int dtls1_handshake_write(SSL *s);
-int dtls1_listen(SSL *s, struct sockaddr *client);
+static unsigned int dtls1_link_min_mtu(void);
/* XDTLS: figure out the right values */
static const unsigned int g_probable_mtu[] = { 1500, 512, 256 };
tls1_change_cipher_state,
tls1_final_finish_mac,
TLS1_FINISH_MAC_LENGTH,
- tls1_cert_verify_mac,
TLS_MD_CLIENT_FINISH_CONST, TLS_MD_CLIENT_FINISH_CONST_SIZE,
TLS_MD_SERVER_FINISH_CONST, TLS_MD_SERVER_FINISH_CONST_SIZE,
tls1_alert_code,
tls1_change_cipher_state,
tls1_final_finish_mac,
TLS1_FINISH_MAC_LENGTH,
- tls1_cert_verify_mac,
TLS_MD_CLIENT_FINISH_CONST, TLS_MD_CLIENT_FINISH_CONST_SIZE,
TLS_MD_SERVER_FINISH_CONST, TLS_MD_SERVER_FINISH_CONST_SIZE,
tls1_alert_code,
d1->link_mtu = 0;
d1->mtu = 0;
- if (!d1->buffered_messages || !d1->sent_messages) {
+ if (d1->buffered_messages == NULL || d1->sent_messages == NULL) {
pqueue_free(d1->buffered_messages);
pqueue_free(d1->sent_messages);
OPENSSL_free(d1);
void dtls1_clear(SSL *s)
{
- pqueue buffered_messages;
- pqueue sent_messages;
+ pqueue *buffered_messages;
+ pqueue *sent_messages;
unsigned int mtu;
unsigned int link_mtu;
if (s->options & SSL_OP_CISCO_ANYCONNECT)
s->client_version = s->version = DTLS1_BAD_VER;
else if (s->method->version == DTLS_ANY_VERSION)
- s->version = DTLS1_2_VERSION;
+ s->version = DTLS_MAX_VERSION;
else
s->version = s->method->version;
}
case DTLS_CTRL_HANDLE_TIMEOUT:
ret = dtls1_handle_timeout(s);
break;
- case DTLS_CTRL_LISTEN:
- ret = dtls1_listen(s, parg);
- break;
- case SSL_CTRL_CHECK_PROTO_VERSION:
- /*
- * For library-internal use; checks that the current protocol is the
- * highest enabled version (according to s->ctx->method, as version
- * negotiation may have changed s->method).
- */
- if (s->version == s->ctx->method->version)
- return 1;
- /*
- * Apparently we're using a version-flexible SSL_METHOD (not at its
- * highest protocol version).
- */
- if (s->ctx->method->version == DTLS_method()->version) {
-#if DTLS_MAX_VERSION != DTLS1_2_VERSION
-# error Code needs update for DTLS_method() support beyond DTLS1_2_VERSION.
-#endif
- if (!(s->options & SSL_OP_NO_DTLSv1_2))
- return s->version == DTLS1_2_VERSION;
- if (!(s->options & SSL_OP_NO_DTLSv1))
- return s->version == DTLS1_VERSION;
- }
- return 0; /* Unexpected state; fail closed. */
case DTLS_CTRL_SET_LINK_MTU:
if (larg < (long)dtls1_link_min_mtu())
return 0;
#define LISTEN_SEND_VERIFY_REQUEST 1
-int dtls1_listen(SSL *s, struct sockaddr *client)
+int DTLSv1_listen(SSL *s, BIO_ADDR *client)
{
int next, n, ret = 0, clearpkt = 0;
unsigned char cookie[DTLS1_COOKIE_LENGTH];
unsigned char seq[SEQ_NUM_SIZE];
- unsigned char *data, *p, *buf;
+ const unsigned char *data;
+ unsigned char *p, *buf;
unsigned long reclen, fragoff, fraglen, msglen;
unsigned int rectype, versmajor, msgseq, msgtype, clientvers, cookielen;
BIO *rbio, *wbio;
BUF_MEM *bufm;
- struct sockaddr_storage tmpclient;
+ BIO_ADDR *tmpclient = NULL;
PACKET pkt, msgpkt, msgpayload, session, cookiepkt;
/* Ensure there is no state left over from a previous invocation */
wbio = SSL_get_wbio(s);
if(!rbio || !wbio) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_BIO_NOT_SET);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_BIO_NOT_SET);
return -1;
}
/*
* We only peek at incoming ClientHello's until we're sure we are going to
* to respond with a HelloVerifyRequest. If its a ClientHello with a valid
- * cookie then we leave it in the BIO for dtls1_accept to handle.
+ * cookie then we leave it in the BIO for accept to handle.
*/
BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 1, NULL);
* SSL_accept)
*/
if ((s->version & 0xff00) != (DTLS1_VERSION & 0xff00)) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNSUPPORTED_SSL_VERSION);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_UNSUPPORTED_SSL_VERSION);
return -1;
}
if (s->init_buf == NULL) {
if ((bufm = BUF_MEM_new()) == NULL) {
- SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_MALLOC_FAILURE);
+ SSLerr(SSL_F_DTLSV1_LISTEN, ERR_R_MALLOC_FAILURE);
return -1;
}
if (!BUF_MEM_grow(bufm, SSL3_RT_MAX_PLAIN_LENGTH)) {
BUF_MEM_free(bufm);
- SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_MALLOC_FAILURE);
+ SSLerr(SSL_F_DTLSV1_LISTEN, ERR_R_MALLOC_FAILURE);
return -1;
}
s->init_buf = bufm;
clearpkt = 1;
if (!PACKET_buf_init(&pkt, buf, n)) {
- SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
+ SSLerr(SSL_F_DTLSV1_LISTEN, ERR_R_INTERNAL_ERROR);
return -1;
}
/* this packet contained a partial record, dump it */
if (n < DTLS1_RT_HEADER_LENGTH) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_RECORD_TOO_SMALL);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_RECORD_TOO_SMALL);
goto end;
}
/* Get the record header */
if (!PACKET_get_1(&pkt, &rectype)
|| !PACKET_get_1(&pkt, &versmajor)) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH);
goto end;
}
if (rectype != SSL3_RT_HANDSHAKE) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
goto end;
}
* the same.
*/
if (versmajor != DTLS1_VERSION_MAJOR) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_BAD_PROTOCOL_VERSION_NUMBER);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_BAD_PROTOCOL_VERSION_NUMBER);
goto end;
}
if (!PACKET_forward(&pkt, 1)
/* Save the sequence number: 64 bits, with top 2 bytes = epoch */
|| !PACKET_copy_bytes(&pkt, seq, SEQ_NUM_SIZE)
- || !PACKET_get_length_prefixed_2(&pkt, &msgpkt)
- || PACKET_remaining(&pkt) != 0) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+ || !PACKET_get_length_prefixed_2(&pkt, &msgpkt)) {
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH);
goto end;
}
+ /*
+ * We allow data remaining at the end of the packet because there could
+ * be a second record (but we ignore it)
+ */
/* This is an initial ClientHello so the epoch has to be 0 */
if (seq[0] != 0 || seq[1] != 0) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
goto end;
}
|| !PACKET_get_net_2(&msgpkt, &msgseq)
|| !PACKET_get_net_3(&msgpkt, &fragoff)
|| !PACKET_get_net_3(&msgpkt, &fraglen)
- || !PACKET_get_sub_packet(&msgpkt, &msgpayload, msglen)
+ || !PACKET_get_sub_packet(&msgpkt, &msgpayload, fraglen)
|| PACKET_remaining(&msgpkt) != 0) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH);
goto end;
}
if (msgtype != SSL3_MT_CLIENT_HELLO) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
goto end;
}
/* Message sequence number can only be 0 or 1 */
if(msgseq > 2) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_INVALID_SEQUENCE_NUMBER);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_INVALID_SEQUENCE_NUMBER);
goto end;
}
- /* We don't support a fragmented ClientHello whilst listening */
- if (fragoff != 0 || fraglen != msglen) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_FRAGMENTED_CLIENT_HELLO);
+ /*
+ * We don't support fragment reassembly for ClientHellos whilst
+ * listening because that would require server side state (which is
+ * against the whole point of the ClientHello/HelloVerifyRequest
+ * mechanism). Instead we only look at the first ClientHello fragment
+ * and require that the cookie must be contained within it.
+ */
+ if (fragoff != 0 || fraglen > msglen) {
+ /* Non initial ClientHello fragment (or bad fragment) */
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_FRAGMENTED_CLIENT_HELLO);
goto end;
}
if (s->msg_callback)
s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, data,
- msglen + DTLS1_HM_HEADER_LENGTH, s,
+ fraglen + DTLS1_HM_HEADER_LENGTH, s,
s->msg_callback_arg);
if (!PACKET_get_net_2(&msgpayload, &clientvers)) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH);
goto end;
}
/*
* Verify client version is supported
*/
- if ((clientvers > (unsigned int)s->method->version &&
- s->method->version != DTLS_ANY_VERSION)) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_WRONG_VERSION_NUMBER);
+ if (DTLS_VERSION_LT(clientvers, (unsigned int)s->method->version) &&
+ s->method->version != DTLS_ANY_VERSION) {
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_WRONG_VERSION_NUMBER);
goto end;
}
if (!PACKET_forward(&msgpayload, SSL3_RANDOM_SIZE)
|| !PACKET_get_length_prefixed_1(&msgpayload, &session)
|| !PACKET_get_length_prefixed_1(&msgpayload, &cookiepkt)) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+ /*
+ * Could be malformed or the cookie does not fit within the initial
+ * ClientHello fragment. Either way we can't handle it.
+ */
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH);
goto end;
}
* We have a cookie, so lets check it.
*/
if (s->ctx->app_verify_cookie_cb == NULL) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_NO_VERIFY_COOKIE_CALLBACK);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_NO_VERIFY_COOKIE_CALLBACK);
/* This is fatal */
return -1;
}
if (s->ctx->app_gen_cookie_cb == NULL ||
s->ctx->app_gen_cookie_cb(s, cookie, &cookielen) == 0 ||
cookielen > 255) {
- SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_COOKIE_GEN_CALLBACK_FAILURE);
+ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_COOKIE_GEN_CALLBACK_FAILURE);
/* This is fatal */
return -1;
}
s->msg_callback(1, 0, SSL3_RT_HEADER, buf,
DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg);
+
+ if ((tmpclient = BIO_ADDR_new()) == NULL) {
+ SSLerr(SSL_F_DTLSV1_LISTEN, ERR_R_MALLOC_FAILURE);
+ goto end;
+ }
+
/*
* This is unneccessary if rbio and wbio are one and the same - but
- * maybe they're not.
+ * maybe they're not. We ignore errors here - some BIOs do not
+ * support this.
*/
- if(BIO_dgram_get_peer(rbio, &tmpclient) <= 0
- || BIO_dgram_set_peer(wbio, &tmpclient) <= 0) {
- SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
- goto end;
+ if(BIO_dgram_get_peer(rbio, tmpclient) > 0) {
+ (void)BIO_dgram_set_peer(wbio, tmpclient);
}
+ BIO_ADDR_free(tmpclient);
+ tmpclient = NULL;
if (BIO_write(wbio, buf, reclen) < (int)reclen) {
if(BIO_should_retry(wbio)) {
*/
SSL_set_options(s, SSL_OP_COOKIE_EXCHANGE);
- /* Put us into the "init" state so that we don't get our state cleared */
- ossl_statem_set_in_init(s, 1);
+ /*
+ * Tell the state machine that we've done the initial hello verify
+ * exchange
+ */
+ ossl_statem_set_hello_verify_done(s);
- if(BIO_dgram_get_peer(rbio, client) <= 0) {
- SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
- return -1;
- }
+ /* Some BIOs may not support this. If we fail we clear the client address */
+ if (BIO_dgram_get_peer(rbio, client) <= 0)
+ BIO_ADDR_clear(client);
ret = 1;
clearpkt = 0;
end:
+ BIO_ADDR_free(tmpclient);
BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 0, NULL);
if (clearpkt) {
/* Dump this packet. Ignore return value */
unsigned int padding = 16; /* Use minimum padding */
if (s->msg_callback)
- s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
+ s->msg_callback(0, s->version, DTLS1_RT_HEARTBEAT,
p, length, s, s->msg_callback_arg);
/* Read type and payload length first */
return -1;
}
- r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, write_length);
+ r = dtls1_write_bytes(s, DTLS1_RT_HEARTBEAT, buffer, write_length);
if (r >= 0 && s->msg_callback)
- s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
+ s->msg_callback(1, s->version, DTLS1_RT_HEARTBEAT,
buffer, write_length, s, s->msg_callback_arg);
OPENSSL_free(buffer);
unsigned int padding = 16; /* Use minimum padding */
/* Only send if peer supports and accepts HB requests... */
- if (!(s->tlsext_heartbeat & SSL_TLSEXT_HB_ENABLED) ||
- s->tlsext_heartbeat & SSL_TLSEXT_HB_DONT_SEND_REQUESTS) {
+ if (!(s->tlsext_heartbeat & SSL_DTLSEXT_HB_ENABLED) ||
+ s->tlsext_heartbeat & SSL_DTLSEXT_HB_DONT_SEND_REQUESTS) {
SSLerr(SSL_F_DTLS1_HEARTBEAT, SSL_R_TLS_HEARTBEAT_PEER_DOESNT_ACCEPT);
return -1;
}
}
/* ...and no handshake in progress. */
- if (SSL_in_init(s) || s->in_handshake) {
+ if (SSL_in_init(s) || ossl_statem_get_in_handshake(s)) {
SSLerr(SSL_F_DTLS1_HEARTBEAT, SSL_R_UNEXPECTED_MESSAGE);
return -1;
}
- /*
- * Check if padding is too long, payload and padding must not exceed 2^14
- * - 3 = 16381 bytes in total.
- */
- OPENSSL_assert(payload + padding <= 16381);
-
/*-
* Create HeartBeat message, we just use a sequence number
* as payload to distuingish different messages and add
goto err;
}
- ret = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buf, 3 + payload + padding);
+ ret = dtls1_write_bytes(s, DTLS1_RT_HEARTBEAT, buf, 3 + payload + padding);
if (ret >= 0) {
if (s->msg_callback)
- s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
+ s->msg_callback(1, s->version, DTLS1_RT_HEARTBEAT,
buf, 3 + payload + padding,
s, s->msg_callback_arg);
return 1;
}
-unsigned int dtls1_link_min_mtu(void)
+static unsigned int dtls1_link_min_mtu(void)
{
return (g_probable_mtu[(sizeof(g_probable_mtu) /
sizeof(g_probable_mtu[0])) - 1]);