diff options
author | Matt Caswell <matt@openssl.org> | 2015-09-14 22:49:35 +0100 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2015-09-23 13:53:26 +0100 |
commit | e3d0dae7cf8363ca462ac425b72c7bb31c3b4b7a (patch) | |
tree | 5324d0001be54ed135b4ff8d60fd0a4ea6ccf1ef | |
parent | 01b7851aa27aa144372f5484da916be042d9aa4f (diff) | |
download | openssl-e3d0dae7cf8363ca462ac425b72c7bb31c3b4b7a.tar.gz |
DTLSv1_listen rewrite
The existing implementation of DTLSv1_listen() is fundamentally flawed. This
function is used in DTLS solutions to listen for new incoming connections
from DTLS clients. A client will send an initial ClientHello. The server
will respond with a HelloVerifyRequest containing a unique cookie. The
client the responds with a second ClientHello - which this time contains the
cookie.
Once the cookie has been verified then DTLSv1_listen() returns to user code,
which is typically expected to continue the handshake with a call to (for
example) SSL_accept().
Whilst listening for incoming ClientHellos, the underlying BIO is usually in
an unconnected state. Therefore ClientHellos can come in from *any* peer.
The arrival of the first ClientHello without the cookie, and the second one
with it, could be interspersed with other intervening messages from
different clients.
The whole purpose of this mechanism is as a defence against DoS attacks. The
idea is to avoid allocating state on the server until the client has
verified that it is capable of receiving messages at the address it claims
to come from. However the existing DTLSv1_listen() implementation completely
fails to do this. It attempts to super-impose itself on the standard state
machine and reuses all of this code. However the standard state machine
expects to operate in a stateful manner with a single client, and this can
cause various problems.
A second more minor issue is that the return codes from this function are
quite confused, with no distinction made between fatal and non-fatal errors.
Most user code treats all errors as non-fatal, and simply retries the call
to DTLSv1_listen().
This commit completely rewrites the implementation of DTLSv1_listen() and
provides a stand alone implementation that does not rely on the existing
state machine. It also provides more consistent return codes.
Reviewed-by: Andy Polyakov <appro@openssl.org>
-rw-r--r-- | include/openssl/ssl.h | 5 | ||||
-rw-r--r-- | ssl/d1_both.c | 15 | ||||
-rw-r--r-- | ssl/d1_lib.c | 385 | ||||
-rw-r--r-- | ssl/d1_srvr.c | 44 | ||||
-rw-r--r-- | ssl/record/rec_layer_d1.c | 6 | ||||
-rw-r--r-- | ssl/record/record.h | 5 | ||||
-rw-r--r-- | ssl/ssl_err.c | 6 | ||||
-rw-r--r-- | ssl/ssl_locl.h | 9 |
8 files changed, 449 insertions, 26 deletions
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 1334eb6161..192640e1db 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1913,6 +1913,7 @@ void ERR_load_SSL_strings(void); # define SSL_F_DTLS1_GET_RECORD 254 # define SSL_F_DTLS1_HANDLE_TIMEOUT 297 # define SSL_F_DTLS1_HEARTBEAT 305 +# define SSL_F_DTLS1_LISTEN 350 # define SSL_F_DTLS1_OUTPUT_CERT_CHAIN 255 # define SSL_F_DTLS1_PREPROCESS_FRAGMENT 288 # define SSL_F_DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE 256 @@ -2166,6 +2167,7 @@ void ERR_load_SSL_strings(void); # define SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE 307 # define SSL_R_COMPRESSION_LIBRARY_ERROR 142 # define SSL_R_CONNECTION_TYPE_NOT_SET 144 +# define SSL_R_COOKIE_GEN_CALLBACK_FAILURE 400 # define SSL_R_COOKIE_MISMATCH 308 # define SSL_R_DATA_BETWEEN_CCS_AND_FINISHED 145 # define SSL_R_DATA_LENGTH_TOO_LONG 146 @@ -2189,6 +2191,7 @@ void ERR_load_SSL_strings(void); # define SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST 151 # define SSL_R_EXCESSIVE_MESSAGE_SIZE 152 # define SSL_R_EXTRA_DATA_IN_MESSAGE 153 +# define SSL_R_FRAGMENTED_CLIENT_HELLO 401 # define SSL_R_GOT_A_FIN_BEFORE_A_CCS 154 # define SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS 355 # define SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION 356 @@ -2201,6 +2204,7 @@ void ERR_load_SSL_strings(void); # define SSL_R_INVALID_COMPRESSION_ALGORITHM 341 # define SSL_R_INVALID_NULL_CMD_NAME 385 # define SSL_R_INVALID_PURPOSE 278 +# define SSL_R_INVALID_SEQUENCE_NUMBER 402 # define SSL_R_INVALID_SERVERINFO_DATA 388 # define SSL_R_INVALID_SRP_USERNAME 357 # define SSL_R_INVALID_STATUS_RESPONSE 328 @@ -2250,6 +2254,7 @@ void ERR_load_SSL_strings(void); # define SSL_R_NO_SHARED_SIGATURE_ALGORITHMS 376 # define SSL_R_NO_SRTP_PROFILES 359 # define SSL_R_NO_VERIFY_CALLBACK 194 +# define SSL_R_NO_VERIFY_COOKIE_CALLBACK 403 # define SSL_R_NULL_SSL_CTX 195 # define SSL_R_NULL_SSL_METHOD_PASSED 196 # define SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED 197 diff --git a/ssl/d1_both.c b/ssl/d1_both.c index 52b7304af2..a69622aea5 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c @@ -454,15 +454,26 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok) * absence of an optional handshake message */ if (s->s3->tmp.reuse_message) { - s->s3->tmp.reuse_message = 0; if ((mt >= 0) && (s->s3->tmp.message_type != mt)) { al = SSL_AD_UNEXPECTED_MESSAGE; SSLerr(SSL_F_DTLS1_GET_MESSAGE, SSL_R_UNEXPECTED_MESSAGE); goto f_err; } *ok = 1; - s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH; + + + /* + * Messages reused from dtls1_listen also have the record header in + * the buffer which we need to skip over. + */ + if (s->s3->tmp.reuse_message == DTLS1_SKIP_RECORD_HEADER) { + s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH + + DTLS1_RT_HEADER_LENGTH; + } else { + s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH; + } s->init_num = (int)s->s3->tmp.message_size; + s->s3->tmp.reuse_message = 0; return s->init_num; } diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index d3b582a98c..8a8ced8abb 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -499,23 +499,394 @@ static void get_current_time(struct timeval *t) #endif } + +#define LISTEN_SUCCESS 2 +#define LISTEN_SEND_VERIFY_REQUEST 1 + + int dtls1_listen(SSL *s, struct sockaddr *client) { - int ret; + int next, n, ret = 0, clearpkt = 0; + unsigned char cookie[DTLS1_COOKIE_LENGTH]; + unsigned char seq[SEQ_NUM_SIZE]; + unsigned char *data, *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; + PACKET pkt, msgpkt, msgpayload, session, cookiepkt; /* Ensure there is no state left over from a previous invocation */ if (!SSL_clear(s)) return -1; + ERR_clear_error(); + + rbio = SSL_get_rbio(s); + wbio = SSL_get_wbio(s); + + if(!rbio || !wbio) { + SSLerr(SSL_F_DTLS1_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. + */ + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 1, NULL); + + /* + * Note: This check deliberately excludes DTLS1_BAD_VER because that version + * requires the MAC to be calculated *including* the first ClientHello + * (without the cookie). Since DTLSv1_listen is stateless that cannot be + * supported. DTLS1_BAD_VER must use cookies in a stateful manner (e.g. via + * SSL_accept) + */ + if ((s->version & 0xff00) != (DTLS1_VERSION & 0xff00)) { + SSLerr(SSL_F_DTLS1_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); + 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); + return -1; + } + s->init_buf = bufm; + } + buf = (unsigned char *)s->init_buf->data; + + do { + /* Get a packet */ + + clear_sys_error(); + /* + * Technically a ClientHello could be SSL3_RT_MAX_PLAIN_LENGTH + * + DTLS1_RT_HEADER_LENGTH bytes long. Normally init_buf does not store + * the record header as well, but we do here. We've set up init_buf to + * be the standard size for simplicity. In practice we shouldn't ever + * receive a ClientHello as long as this. If we do it will get dropped + * in the record length check below. + */ + n = BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH); + + if (n <= 0) { + if(BIO_should_retry(rbio)) { + /* Non-blocking IO */ + goto end; + } + return -1; + } + + /* If we hit any problems we need to clear this packet from the BIO */ + clearpkt = 1; + + if (!PACKET_buf_init(&pkt, buf, n)) { + SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* + * Parse the received record. If there are any problems with it we just + * dump it - with no alert. RFC6347 says this "Unlike TLS, DTLS is + * resilient in the face of invalid records (e.g., invalid formatting, + * length, MAC, etc.). In general, invalid records SHOULD be silently + * discarded, thus preserving the association; however, an error MAY be + * logged for diagnostic purposes." + */ + + /* this packet contained a partial record, dump it */ + if (n < DTLS1_RT_HEADER_LENGTH) { + SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_RECORD_TOO_SMALL); + goto end; + } + + if (s->msg_callback) + s->msg_callback(0, 0, SSL3_RT_HEADER, buf, + DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg); + + /* Get the record header */ + if (!PACKET_get_1(&pkt, &rectype) + || !PACKET_get_1(&pkt, &versmajor)) { + SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH); + goto end; + } + + if (rectype != SSL3_RT_HANDSHAKE) { + SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE); + goto end; + } + + /* + * Check record version number. We only check that the major version is + * the same. + */ + if (versmajor != DTLS1_VERSION_MAJOR) { + SSLerr(SSL_F_DTLS1_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); + goto end; + } + + /* 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); + goto end; + } + + /* Get a pointer to the raw message for the later callback */ + data = PACKET_data(&msgpkt); + + /* Finished processing the record header, now process the message */ + if (!PACKET_get_1(&msgpkt, &msgtype) + || !PACKET_get_net_3(&msgpkt, &msglen) + || !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_remaining(&msgpkt) != 0) { + SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH); + goto end; + } + + if (msgtype != SSL3_MT_CLIENT_HELLO) { + SSLerr(SSL_F_DTLS1_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); + 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); + goto end; + } + + if (s->msg_callback) + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, data, + msglen + 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); + 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); + 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); + goto end; + } + + /* + * Check if we have a cookie or not. If not we need to send a + * HelloVerifyRequest. + */ + if (PACKET_remaining(&cookiepkt) == 0) { + next = LISTEN_SEND_VERIFY_REQUEST; + } else { + /* + * 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); + /* This is fatal */ + return -1; + } + if (PACKET_remaining(&cookiepkt) > sizeof(s->d1->rcvd_cookie) + || s->ctx->app_verify_cookie_cb(s, PACKET_data(&cookiepkt), + PACKET_remaining(&cookiepkt)) == 0) { + /* + * We treat invalid cookies in the same was as no cookie as + * per RFC6347 + */ + next = LISTEN_SEND_VERIFY_REQUEST; + } else { + /* Cookie verification succeeded */ + next = LISTEN_SUCCESS; + } + } + + if (next == LISTEN_SEND_VERIFY_REQUEST) { + /* + * There was no cookie in the ClientHello so we need to send a + * HelloVerifyRequest. If this fails we do not worry about trying + * to resend, we just drop it. + */ + + /* + * Dump the read packet, we don't need it any more. Ignore return + * value + */ + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 0, NULL); + BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH); + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 1, NULL); + + /* Generate the cookie */ + if (s->ctx->app_gen_cookie_cb == NULL || + s->ctx->app_gen_cookie_cb(s, cookie, &cookielen) == 0) { + SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_COOKIE_GEN_CALLBACK_FAILURE); + /* This is fatal */ + return -1; + } + + p = &buf[DTLS1_RT_HEADER_LENGTH]; + msglen = dtls1_raw_hello_verify_request(p + DTLS1_HM_HEADER_LENGTH, + cookie, cookielen); + + *p++ = DTLS1_MT_HELLO_VERIFY_REQUEST; + + /* Message length */ + l2n3(msglen, p); + + /* Message sequence number is always 0 for a HelloVerifyRequest */ + s2n(0, p); + + /* + * We never fragment a HelloVerifyRequest, so fragment offset is 0 + * and fragment length is message length + */ + l2n3(0, p); + l2n3(msglen, p); + + /* Set reclen equal to length of whole handshake message */ + reclen = msglen + DTLS1_HM_HEADER_LENGTH; + + /* Add the record header */ + p = buf; + + *(p++) = SSL3_RT_HANDSHAKE; + /* + * Special case: for hello verify request, client version 1.0 and we + * haven't decided which version to use yet send back using version + * 1.0 header: otherwise some clients will ignore it. + */ + if (s->method->version == DTLS_ANY_VERSION) { + *(p++) = DTLS1_VERSION >> 8; + *(p++) = DTLS1_VERSION & 0xff; + } else { + *(p++) = s->version >> 8; + *(p++) = s->version & 0xff; + } + + /* + * Record sequence number is always the same as in the received + * ClientHello + */ + memcpy(p, seq, SEQ_NUM_SIZE); + p += SEQ_NUM_SIZE; + + /* Length */ + s2n(reclen, p); + + /* + * Set reclen equal to length of whole record including record + * header + */ + reclen += DTLS1_RT_HEADER_LENGTH; + + if (s->msg_callback) + s->msg_callback(1, 0, SSL3_RT_HEADER, buf, + DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg); + + /* + * This is unneccessary if rbio and wbio are one and the same - but + * maybe they're not. + */ + 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_write(wbio, buf, reclen) < (int)reclen) { + if(BIO_should_retry(wbio)) { + /* + * Non-blocking IO...but we're stateless, so we're just + * going to drop this packet. + */ + goto end; + } + return -1; + } + + if (BIO_flush(wbio) <= 0) { + if(BIO_should_retry(wbio)) { + /* + * Non-blocking IO...but we're stateless, so we're just + * going to drop this packet. + */ + goto end; + } + return -1; + } + } + } while (next != LISTEN_SUCCESS); + + /* + * Set expected sequence numbers to continue the handshake. + */ + s->d1->handshake_read_seq = 1; + s->d1->handshake_write_seq = 1; + s->d1->next_handshake_write_seq = 1; + DTLS_RECORD_LAYER_set_write_sequence(&s->rlayer, seq); + + /* + * We are doing cookie exchange, so make sure we set that option in the + * SSL object + */ SSL_set_options(s, SSL_OP_COOKIE_EXCHANGE); - s->d1->listen = 1; - ret = SSL_accept(s); - if (ret <= 0) - return ret; + /* + * Put us into the "init" state so that dtls1_accept doesn't clear our + * state + */ + s->state = SSL_ST_ACCEPT; + + if(BIO_dgram_get_peer(rbio, client) <= 0) { + SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR); + return -1; + } - (void)BIO_dgram_get_peer(SSL_get_rbio(s), client); - return 1; + ret = 1; + clearpkt = 0; +end: + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 0, NULL); + if (clearpkt) { + /* Dump this packet. Ignore return value */ + BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH); + } + return ret; } static int dtls1_set_handshake_header(SSL *s, int htype, unsigned long len) diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c index f56bf5adef..3d3d3f751f 100644 --- a/ssl/d1_srvr.c +++ b/ssl/d1_srvr.c @@ -883,19 +883,35 @@ int dtls1_accept(SSL *s) return (ret); } -int dtls1_send_hello_verify_request(SSL *s) +unsigned int dtls1_raw_hello_verify_request(unsigned char *buf, + unsigned char *cookie, + unsigned char cookie_len) { unsigned int msg_len; - unsigned char *msg, *buf, *p; + unsigned char *p; + + p = buf; + /* Always use DTLS 1.0 version: see RFC 6347 */ + *(p++) = DTLS1_VERSION >> 8; + *(p++) = DTLS1_VERSION & 0xFF; + + *(p++) = (unsigned char)cookie_len; + memcpy(p, cookie, cookie_len); + p += cookie_len; + msg_len = p - buf; + + return msg_len; +} + + +int dtls1_send_hello_verify_request(SSL *s) +{ + unsigned int len; + unsigned char *buf; if (s->state == DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A) { buf = (unsigned char *)s->init_buf->data; - msg = p = &(buf[DTLS1_HM_HEADER_LENGTH]); - /* Always use DTLS 1.0 version: see RFC 6347 */ - *(p++) = DTLS1_VERSION >> 8; - *(p++) = DTLS1_VERSION & 0xFF; - if (s->ctx->app_gen_cookie_cb == NULL || s->ctx->app_gen_cookie_cb(s, s->d1->cookie, &(s->d1->cookie_len)) == 0) { @@ -905,18 +921,16 @@ int dtls1_send_hello_verify_request(SSL *s) return 0; } - *(p++) = (unsigned char)s->d1->cookie_len; - memcpy(p, s->d1->cookie, s->d1->cookie_len); - p += s->d1->cookie_len; - msg_len = p - msg; + len = dtls1_raw_hello_verify_request(&buf[DTLS1_HM_HEADER_LENGTH], + s->d1->cookie, s->d1->cookie_len); - dtls1_set_message_header(s, buf, - DTLS1_MT_HELLO_VERIFY_REQUEST, msg_len, 0, - msg_len); + dtls1_set_message_header(s, buf, DTLS1_MT_HELLO_VERIFY_REQUEST, len, 0, + len); + len += DTLS1_HM_HEADER_LENGTH; s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B; /* number of bytes to write */ - s->init_num = p - buf; + s->init_num = len; s->init_off = 0; } diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c index 74796bed6f..b5548e626a 100644 --- a/ssl/record/rec_layer_d1.c +++ b/ssl/record/rec_layer_d1.c @@ -226,6 +226,12 @@ void DTLS_RECORD_LAYER_resync_write(RECORD_LAYER *rl) memcpy(rl->write_sequence, rl->read_sequence, sizeof(rl->write_sequence)); } + +void DTLS_RECORD_LAYER_set_write_sequence(RECORD_LAYER *rl, unsigned char *seq) +{ + memcpy(rl->write_sequence, seq, SEQ_NUM_SIZE); +} + static int have_handshake_fragment(SSL *s, int type, unsigned char *buf, int len, int peek); diff --git a/ssl/record/record.h b/ssl/record/record.h index 5c8fead869..96370710c4 100644 --- a/ssl/record/record.h +++ b/ssl/record/record.h @@ -288,8 +288,8 @@ typedef struct record_layer_st { int wpend_ret; const unsigned char *wpend_buf; - unsigned char read_sequence[8]; - unsigned char write_sequence[8]; + unsigned char read_sequence[SEQ_NUM_SIZE]; + unsigned char write_sequence[SEQ_NUM_SIZE]; DTLS_RECORD_LAYER *d; } RECORD_LAYER; @@ -346,6 +346,7 @@ void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl); void DTLS_RECORD_LAYER_set_saved_w_epoch(RECORD_LAYER *rl, unsigned short e); void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl); void DTLS_RECORD_LAYER_resync_write(RECORD_LAYER *rl); +void DTLS_RECORD_LAYER_set_write_sequence(RECORD_LAYER *rl, unsigned char *seq); __owur int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf, int len, int peek); __owur int dtls1_write_bytes(SSL *s, int type, const void *buf, int len); diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 21836d831d..447bac6937 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -87,6 +87,7 @@ static ERR_STRING_DATA SSL_str_functs[] = { {ERR_FUNC(SSL_F_DTLS1_GET_RECORD), "dtls1_get_record"}, {ERR_FUNC(SSL_F_DTLS1_HANDLE_TIMEOUT), "dtls1_handle_timeout"}, {ERR_FUNC(SSL_F_DTLS1_HEARTBEAT), "dtls1_heartbeat"}, + {ERR_FUNC(SSL_F_DTLS1_LISTEN), "DTLS1_LISTEN"}, {ERR_FUNC(SSL_F_DTLS1_OUTPUT_CERT_CHAIN), "dtls1_output_cert_chain"}, {ERR_FUNC(SSL_F_DTLS1_PREPROCESS_FRAGMENT), "DTLS1_PREPROCESS_FRAGMENT"}, {ERR_FUNC(SSL_F_DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE), @@ -406,6 +407,8 @@ static ERR_STRING_DATA SSL_str_reasons[] = { "compression id not within private range"}, {ERR_REASON(SSL_R_COMPRESSION_LIBRARY_ERROR), "compression library error"}, {ERR_REASON(SSL_R_CONNECTION_TYPE_NOT_SET), "connection type not set"}, + {ERR_REASON(SSL_R_COOKIE_GEN_CALLBACK_FAILURE), + "cookie gen callback failure"}, {ERR_REASON(SSL_R_COOKIE_MISMATCH), "cookie mismatch"}, {ERR_REASON(SSL_R_DATA_BETWEEN_CCS_AND_FINISHED), "data between ccs and finished"}, @@ -440,6 +443,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = { "error in received cipher list"}, {ERR_REASON(SSL_R_EXCESSIVE_MESSAGE_SIZE), "excessive message size"}, {ERR_REASON(SSL_R_EXTRA_DATA_IN_MESSAGE), "extra data in message"}, + {ERR_REASON(SSL_R_FRAGMENTED_CLIENT_HELLO), "fragmented client hello"}, {ERR_REASON(SSL_R_GOT_A_FIN_BEFORE_A_CCS), "got a fin before a ccs"}, {ERR_REASON(SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS), "got next proto before a ccs"}, @@ -455,6 +459,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = { "invalid compression algorithm"}, {ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME), "invalid null cmd name"}, {ERR_REASON(SSL_R_INVALID_PURPOSE), "invalid purpose"}, + {ERR_REASON(SSL_R_INVALID_SEQUENCE_NUMBER), "invalid sequence number"}, {ERR_REASON(SSL_R_INVALID_SERVERINFO_DATA), "invalid serverinfo data"}, {ERR_REASON(SSL_R_INVALID_SRP_USERNAME), "invalid srp username"}, {ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE), "invalid status response"}, @@ -510,6 +515,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = { "no shared sigature algorithms"}, {ERR_REASON(SSL_R_NO_SRTP_PROFILES), "no srtp profiles"}, {ERR_REASON(SSL_R_NO_VERIFY_CALLBACK), "no verify callback"}, + {ERR_REASON(SSL_R_NO_VERIFY_COOKIE_CALLBACK), "no verify cookie callback"}, {ERR_REASON(SSL_R_NULL_SSL_CTX), "null ssl ctx"}, {ERR_REASON(SSL_R_NULL_SSL_METHOD_PASSED), "null ssl method passed"}, {ERR_REASON(SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED), diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 32e6338adc..84b7458a56 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1376,6 +1376,12 @@ typedef struct ssl3_state_st { /* Max MTU overhead we know about so far is 40 for IPv6 + 8 for UDP */ # define DTLS1_MAX_MTU_OVERHEAD 48 +/* + * Flag used in message reuse to indicate the buffer contains the record + * header as well as the the handshake message header. + */ +# define DTLS1_SKIP_RECORD_HEADER 2 + struct dtls1_retransmit_state { EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */ EVP_MD_CTX *write_hash; /* used for mac generation */ @@ -1990,6 +1996,9 @@ void dtls1_start_timer(SSL *s); void dtls1_stop_timer(SSL *s); __owur int dtls1_is_timer_expired(SSL *s); void dtls1_double_timeout(SSL *s); +__owur unsigned int dtls1_raw_hello_verify_request(unsigned char *buf, + unsigned char *cookie, + unsigned char cookie_len); __owur int dtls1_send_newsession_ticket(SSL *s); __owur unsigned int dtls1_min_mtu(SSL *s); __owur unsigned int dtls1_link_min_mtu(void); |