diff options
Diffstat (limited to 'src/ssl/d1_pkt.c')
-rw-r--r-- | src/ssl/d1_pkt.c | 767 |
1 files changed, 209 insertions, 558 deletions
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c index 553499f..f2ade3a 100644 --- a/src/ssl/d1_pkt.c +++ b/src/ssl/d1_pkt.c @@ -109,6 +109,8 @@ * copied and put under another distribution licence * [including the GNU Public Licence.] */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> @@ -122,352 +124,158 @@ #include "internal.h" -/* mod 128 saturating subtract of two 64-bit values in big-endian order */ -static int satsub64be(const uint8_t *v1, const uint8_t *v2) { - int ret, sat, brw, i; - - if (sizeof(long) == 8) { - do { - const union { - long one; - char little; - } is_endian = {1}; - long l; - - if (is_endian.little) { - break; - } - /* not reached on little-endians */ - /* following test is redundant, because input is - * always aligned, but I take no chances... */ - if (((size_t)v1 | (size_t)v2) & 0x7) { - break; - } - - l = *((long *)v1); - l -= *((long *)v2); - if (l > 128) { - return 128; - } else if (l < -128) { - return -128; - } else { - return (int)l; - } - } while (0); - } - - ret = (int)v1[7] - (int)v2[7]; - sat = 0; - brw = ret >> 8; /* brw is either 0 or -1 */ - if (ret & 0x80) { - for (i = 6; i >= 0; i--) { - brw += (int)v1[i] - (int)v2[i]; - sat |= ~brw; - brw >>= 8; - } - } else { - for (i = 6; i >= 0; i--) { - brw += (int)v1[i] - (int)v2[i]; - sat |= brw; - brw >>= 8; - } - } - brw <<= 8; /* brw is either 0 or -256 */ - - if (sat & 0xff) { - return brw | 0x80; - } else { - return brw + (ret & 0xFF); - } -} - -static int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap); -static void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap); -static int dtls1_process_record(SSL *s); -static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, +static int do_dtls1_write(SSL *ssl, int type, const uint8_t *buf, unsigned int len, enum dtls1_use_epoch_t use_epoch); -static int dtls1_process_record(SSL *s) { - int al; - SSL3_RECORD *rr = &s->s3->rrec; - - /* check is not needed I believe */ - if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) { - al = SSL_AD_RECORD_OVERFLOW; - OPENSSL_PUT_ERROR(SSL, dtls1_process_record, - SSL_R_ENCRYPTED_LENGTH_TOO_LONG); - goto f_err; - } - - /* |rr->data| points to |rr->length| bytes of ciphertext in |s->packet|. */ - rr->data = &s->packet[DTLS1_RT_HEADER_LENGTH]; - - uint8_t seq[8]; - seq[0] = rr->epoch >> 8; - seq[1] = rr->epoch & 0xff; - memcpy(&seq[2], &rr->seq_num[2], 6); - - /* Decrypt the packet in-place. Note it is important that |SSL_AEAD_CTX_open| - * not write beyond |rr->length|. There may be another record in the packet. - * - * TODO(davidben): This assumes |s->version| is the same as the record-layer - * version which isn't always true, but it only differs with the NULL cipher - * which ignores the parameter. */ - size_t plaintext_len; - if (!SSL_AEAD_CTX_open(s->aead_read_ctx, rr->data, &plaintext_len, rr->length, - rr->type, s->version, seq, rr->data, rr->length)) { - /* Bad packets are silently dropped in DTLS. Clear the error queue of any - * errors decryption may have added. */ - ERR_clear_error(); - rr->length = 0; - s->packet_length = 0; - goto err; - } - - if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH) { - al = SSL_AD_RECORD_OVERFLOW; - OPENSSL_PUT_ERROR(SSL, dtls1_process_record, SSL_R_DATA_LENGTH_TOO_LONG); - goto f_err; - } - assert(plaintext_len < (1u << 16)); - rr->length = plaintext_len; - - rr->off = 0; - /* So at this point the following is true - * ssl->s3->rrec.type is the type of record - * ssl->s3->rrec.length == number of bytes in record - * ssl->s3->rrec.off == offset to first valid byte - * ssl->s3->rrec.data == the first byte of the record body. */ - - /* we have pulled in a full packet so zero things */ - s->packet_length = 0; - return 1; - -f_err: - ssl3_send_alert(s, SSL3_AL_FATAL, al); - -err: - return 0; -} - -/* Call this to get a new input record. - * It will return <= 0 if more data is needed, normally due to an error - * or non-blocking IO. - * When it finishes, one packet has been decoded and can be found in - * ssl->s3->rrec.type - is the type of record - * ssl->s3->rrec.data, - data - * ssl->s3->rrec.length, - number of bytes - * - * used only by dtls1_read_bytes */ -int dtls1_get_record(SSL *s) { - uint8_t ssl_major, ssl_minor; - int n; - SSL3_RECORD *rr; - uint8_t *p = NULL; - uint16_t version; - - rr = &(s->s3->rrec); - - /* get something from the wire */ +/* dtls1_get_record reads a new input record. On success, it places it in + * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if + * more data is needed. */ +static int dtls1_get_record(SSL *ssl) { again: - /* check if we have the header */ - if ((s->rstate != SSL_ST_READ_BODY) || - (s->packet_length < DTLS1_RT_HEADER_LENGTH)) { - n = ssl3_read_n(s, DTLS1_RT_HEADER_LENGTH, 0); - /* read timeout is handled by dtls1_read_bytes */ - if (n <= 0) { - return n; /* error or non-blocking */ - } - - /* this packet contained a partial record, dump it */ - if (s->packet_length != DTLS1_RT_HEADER_LENGTH) { - s->packet_length = 0; - goto again; + /* Read a new packet if there is no unconsumed one. */ + if (ssl_read_buffer_len(ssl) == 0) { + int ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */); + if (ret <= 0) { + return ret; } + } + assert(ssl_read_buffer_len(ssl) > 0); - s->rstate = SSL_ST_READ_BODY; - - p = s->packet; + /* Ensure the packet is large enough to decrypt in-place. */ + if (ssl_read_buffer_len(ssl) < ssl_record_prefix_len(ssl)) { + ssl_read_buffer_clear(ssl); + goto again; + } - if (s->msg_callback) { - s->msg_callback(0, 0, SSL3_RT_HEADER, p, DTLS1_RT_HEADER_LENGTH, s, - s->msg_callback_arg); - } + uint8_t *out = ssl_read_buffer(ssl) + ssl_record_prefix_len(ssl); + size_t max_out = ssl_read_buffer_len(ssl) - ssl_record_prefix_len(ssl); + uint8_t type, alert; + size_t len, consumed; + switch (dtls_open_record(ssl, &type, out, &len, &consumed, &alert, max_out, + ssl_read_buffer(ssl), ssl_read_buffer_len(ssl))) { + case ssl_open_record_success: + ssl_read_buffer_consume(ssl, consumed); - /* Pull apart the header into the DTLS1_RECORD */ - rr->type = *(p++); - ssl_major = *(p++); - ssl_minor = *(p++); - version = (((uint16_t)ssl_major) << 8) | ssl_minor; - - /* sequence number is 64 bits, with top 2 bytes = epoch */ - n2s(p, rr->epoch); - - memcpy(&(s->s3->read_sequence[2]), p, 6); - p += 6; - - n2s(p, rr->length); - - /* Lets check version */ - if (s->s3->have_version) { - if (version != s->version) { - /* The record's version doesn't match, so silently drop it. - * - * TODO(davidben): This doesn't work. The DTLS record layer is not - * packet-based, so the remainder of the packet isn't dropped and we - * get a framing error. It's also unclear what it means to silently - * drop a record in a packet containing two records. */ - rr->length = 0; - s->packet_length = 0; - goto again; + if (len > 0xffff) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return -1; } - } - if ((version & 0xff00) != (s->version & 0xff00)) { - /* wrong version, silently discard record */ - rr->length = 0; - s->packet_length = 0; - goto again; - } + SSL3_RECORD *rr = &ssl->s3->rrec; + rr->type = type; + rr->length = (uint16_t)len; + rr->data = out; + return 1; - if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) { - /* record too long, silently discard it */ - rr->length = 0; - s->packet_length = 0; + case ssl_open_record_discard: + ssl_read_buffer_consume(ssl, consumed); goto again; - } - - /* now s->rstate == SSL_ST_READ_BODY */ - } - /* s->rstate == SSL_ST_READ_BODY, get and decode the data */ - - if (rr->length > s->packet_length - DTLS1_RT_HEADER_LENGTH) { - /* now s->packet_length == DTLS1_RT_HEADER_LENGTH */ - n = ssl3_read_n(s, rr->length, 1); - /* This packet contained a partial record, dump it. */ - if (n != rr->length) { - rr->length = 0; - s->packet_length = 0; - goto again; - } + case ssl_open_record_error: + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return -1; - /* now n == rr->length, - * and s->packet_length == DTLS1_RT_HEADER_LENGTH + rr->length */ + case ssl_open_record_partial: + /* Impossible in DTLS. */ + break; } - s->rstate = SSL_ST_READ_HEADER; /* set state for later operations */ - if (rr->epoch != s->d1->r_epoch) { - /* This record is from the wrong epoch. If it is the next epoch, it could be - * buffered. For simplicity, drop it and expect retransmit to handle it - * later; DTLS is supposed to handle packet loss. */ - rr->length = 0; - s->packet_length = 0; - goto again; - } + assert(0); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; +} - /* Check whether this is a repeat, or aged record. */ - if (!dtls1_record_replay_check(s, &s->d1->bitmap)) { - rr->length = 0; - s->packet_length = 0; /* dump this record */ - goto again; /* get another record */ +int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) { + return dtls1_read_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len, peek); +} + +int dtls1_read_change_cipher_spec(SSL *ssl) { + uint8_t byte; + int ret = dtls1_read_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, &byte, + 1 /* len */, 0 /* no peek */); + if (ret <= 0) { + return ret; } + assert(ret == 1); - /* just read a 0 length packet */ - if (rr->length == 0) { - goto again; + if (ssl->s3->rrec.length != 0 || byte != SSL3_MT_CCS) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; } - if (!dtls1_process_record(s)) { - rr->length = 0; - s->packet_length = 0; /* dump this record */ - goto again; /* get another record */ + if (ssl->msg_callback != NULL) { + ssl->msg_callback(0, ssl->version, SSL3_RT_CHANGE_CIPHER_SPEC, &byte, 1, + ssl, ssl->msg_callback_arg); } - dtls1_record_bitmap_update(s, &s->d1->bitmap); /* Mark receipt of record. */ return 1; } -int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) { - return dtls1_read_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len, peek); -} - void dtls1_read_close_notify(SSL *ssl) { - dtls1_read_bytes(ssl, 0, NULL, 0, 0); + /* Bidirectional shutdown doesn't make sense for an unordered transport. DTLS + * alerts also aren't delivered reliably, so we may even time out because the + * peer never received our close_notify. Report to the caller that the channel + * has fully shut down. */ + ssl->shutdown |= SSL_RECEIVED_SHUTDOWN; } /* Return up to 'len' payload bytes received in 'type' records. * 'type' is one of the following: * - * - SSL3_RT_HANDSHAKE (when ssl3_get_message calls us) - * - SSL3_RT_APPLICATION_DATA (when ssl3_read calls us) - * - 0 (during a shutdown, no data has to be returned) + * - SSL3_RT_HANDSHAKE (when dtls1_get_message calls us) + * - SSL3_RT_CHANGE_CIPHER_SPEC (when dtls1_read_change_cipher_spec calls us) + * - SSL3_RT_APPLICATION_DATA (when dtls1_read_app_data calls us) * - * If we don't have stored data to work from, read a SSL/TLS record first - * (possibly multiple records if we still don't have anything to return). + * If we don't have stored data to work from, read a DTLS record first (possibly + * multiple records if we still don't have anything to return). * * This function must handle any surprises the peer may have for us, such as - * Alert records (e.g. close_notify), ChangeCipherSpec records (not really - * a surprise, but handled as if it were), or renegotiation requests. - * Also if record payloads contain fragments too small to process, we store - * them until there is enough for the respective protocol (the record protocol - * may use arbitrary fragmentation and even interleaving): - * Change cipher spec protocol - * just 1 byte needed, no need for keeping anything stored - * Alert protocol - * 2 bytes needed (AlertLevel, AlertDescription) - * Handshake protocol - * 4 bytes needed (HandshakeType, uint24 length) -- we just have - * to detect unexpected Client Hello and Hello Request messages - * here, anything else is handled by higher layers - * Application data protocol - * none of our business - */ -int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek) { + * Alert records (e.g. close_notify) and out of records. */ +int dtls1_read_bytes(SSL *ssl, int type, unsigned char *buf, int len, int peek) { int al, i, ret; unsigned int n; SSL3_RECORD *rr; - void (*cb)(const SSL *ssl, int type2, int val) = NULL; + void (*cb)(const SSL *ssl, int type, int value) = NULL; - /* XXX: check what the second '&& type' is about */ - if ((type && (type != SSL3_RT_APPLICATION_DATA) && - (type != SSL3_RT_HANDSHAKE) && type) || - (peek && (type != SSL3_RT_APPLICATION_DATA))) { - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, ERR_R_INTERNAL_ERROR); + if ((type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE && + type != SSL3_RT_CHANGE_CIPHER_SPEC) || + (peek && type != SSL3_RT_APPLICATION_DATA)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } - if (!s->in_handshake && SSL_in_init(s)) { + if (!ssl->in_handshake && SSL_in_init(ssl)) { /* type == SSL3_RT_APPLICATION_DATA */ - i = s->handshake_func(s); + i = ssl->handshake_func(ssl); if (i < 0) { return i; } if (i == 0) { - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); return -1; } } start: - s->rwstate = SSL_NOTHING; + ssl->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; + /* ssl->s3->rrec.type - is the type of record + * ssl->s3->rrec.data - data + * ssl->s3->rrec.off - offset into 'data' for next read + * ssl->s3->rrec.length - number of bytes. */ + rr = &ssl->s3->rrec; /* Check for timeout */ - if (DTLSv1_handle_timeout(s) > 0) { + if (DTLSv1_handle_timeout(ssl) > 0) { goto start; } /* get new packet if necessary */ - if (rr->length == 0 || s->rstate == SSL_ST_READ_BODY) { - ret = dtls1_get_record(s); + if (rr->length == 0) { + ret = dtls1_get_record(ssl); if (ret <= 0) { - ret = dtls1_read_failed(s, ret); + ret = dtls1_read_failed(ssl, ret); /* anything other than a timeout is an error */ if (ret <= 0) { return ret; @@ -479,38 +287,32 @@ start: /* we now have a packet which can be read and processed */ - /* |change_cipher_spec is set when we receive a ChangeCipherSpec and reset by - * ssl3_get_finished. */ - if (s->s3->change_cipher_spec && rr->type != SSL3_RT_HANDSHAKE && - rr->type != SSL3_RT_ALERT) { - /* We now have an unexpected record between CCS and Finished. Most likely - * the packets were reordered on their way. DTLS is unreliable, so drop the - * packet and expect the peer to retransmit. */ - rr->length = 0; - goto start; - } - /* If the other end has shut down, throw anything we read away (even in * 'peek' mode) */ - if (s->shutdown & SSL_RECEIVED_SHUTDOWN) { + if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) { rr->length = 0; - s->rwstate = SSL_NOTHING; + ssl->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->aead_read_ctx == NULL)) { + if (type == rr->type) { + /* Make sure that we are not getting application data when we + * are doing a handshake for the first time. */ + if (SSL_in_init(ssl) && (type == SSL3_RT_APPLICATION_DATA) && + (ssl->aead_read_ctx == NULL)) { /* TODO(davidben): Is this check redundant with the handshake_func * check? */ al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_APP_DATA_IN_HANDSHAKE); + OPENSSL_PUT_ERROR(SSL, SSL_R_APP_DATA_IN_HANDSHAKE); goto f_err; } + /* Discard empty records. */ + if (rr->length == 0) { + goto start; + } + if (len <= 0) { return len; } @@ -521,13 +323,13 @@ start: n = (unsigned int)len; } - memcpy(buf, &(rr->data[rr->off]), n); + memcpy(buf, rr->data, n); if (!peek) { rr->length -= n; - rr->off += n; + rr->data += n; if (rr->length == 0) { - s->rstate = SSL_ST_READ_HEADER; - rr->off = 0; + /* The record has been consumed, so we may now clear the buffer. */ + ssl_read_buffer_discard(ssl); } } @@ -542,255 +344,173 @@ start: /* Alerts may not be fragmented. */ if (rr->length < 2) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_ALERT); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT); goto f_err; } - if (s->msg_callback) { - s->msg_callback(0, s->version, SSL3_RT_ALERT, &rr->data[rr->off], 2, s, - s->msg_callback_arg); + if (ssl->msg_callback) { + ssl->msg_callback(0, ssl->version, SSL3_RT_ALERT, rr->data, 2, ssl, + ssl->msg_callback_arg); } - const uint8_t alert_level = rr->data[rr->off++]; - const uint8_t alert_descr = rr->data[rr->off++]; + const uint8_t alert_level = rr->data[0]; + const uint8_t alert_descr = rr->data[1]; rr->length -= 2; + rr->data += 2; - if (s->info_callback != NULL) { - cb = s->info_callback; - } else if (s->ctx->info_callback != NULL) { - cb = s->ctx->info_callback; + if (ssl->info_callback != NULL) { + cb = ssl->info_callback; + } else if (ssl->ctx->info_callback != NULL) { + cb = ssl->ctx->info_callback; } if (cb != NULL) { uint16_t alert = (alert_level << 8) | alert_descr; - cb(s, SSL_CB_READ_ALERT, alert); + cb(ssl, SSL_CB_READ_ALERT, alert); } if (alert_level == SSL3_AL_WARNING) { - s->s3->warn_alert = alert_descr; + ssl->s3->warn_alert = alert_descr; if (alert_descr == SSL_AD_CLOSE_NOTIFY) { - s->shutdown |= SSL_RECEIVED_SHUTDOWN; + ssl->shutdown |= SSL_RECEIVED_SHUTDOWN; return 0; } } else if (alert_level == SSL3_AL_FATAL) { char tmp[16]; - s->rwstate = SSL_NOTHING; - s->s3->fatal_alert = alert_descr; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, - SSL_AD_REASON_OFFSET + alert_descr); + ssl->rwstate = SSL_NOTHING; + ssl->s3->fatal_alert = alert_descr; + OPENSSL_PUT_ERROR(SSL, 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); + ssl->shutdown |= SSL_RECEIVED_SHUTDOWN; + SSL_CTX_remove_session(ssl->ctx, ssl->session); return 0; } else { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNKNOWN_ALERT_TYPE); + OPENSSL_PUT_ERROR(SSL, 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; + /* Cross-epoch records are discarded, but we may receive out-of-order + * application data between ChangeCipherSpec and Finished or a ChangeCipherSpec + * before the appropriate point in the handshake. Those must be silently + * discarded. + * + * However, only allow the out-of-order records in the correct epoch. + * Application data must come in the encrypted epoch, and ChangeCipherSpec in + * the unencrypted epoch (we never renegotiate). Other cases fall through and + * fail with a fatal error. */ + if ((rr->type == SSL3_RT_APPLICATION_DATA && ssl->aead_read_ctx != NULL) || + (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC && ssl->aead_read_ctx == NULL)) { rr->length = 0; - return 0; + goto start; } - 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; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_CHANGE_CIPHER_SPEC); - goto f_err; - } - - 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); - } - - /* We can't process a CCS now, because previous handshake - * messages are still missing, so just drop it. - */ - if (!s->d1->change_cipher_spec_ok) { + if (rr->type == SSL3_RT_HANDSHAKE) { + if (type != SSL3_RT_APPLICATION_DATA) { + /* Out-of-order handshake record while looking for ChangeCipherSpec. Drop + * it silently. */ + assert(type == SSL3_RT_CHANGE_CIPHER_SPEC); + rr->length = 0; goto start; } - s->d1->change_cipher_spec_ok = 0; - - s->s3->change_cipher_spec = 1; - if (!ssl3_do_change_cipher_spec(s)) { - goto err; - } - - /* do this whenever CCS is processed */ - dtls1_reset_seq_numbers(s, SSL3_CC_READ); - - goto start; - } - - /* Unexpected handshake message. It may be a retransmitted Finished (the only - * post-CCS message). Otherwise, it's a pre-CCS handshake message from an - * unsupported renegotiation attempt. */ - if (rr->type == SSL3_RT_HANDSHAKE && !s->in_handshake) { + /* Parse the first fragment header to determine if this is a pre-CCS or + * post-CCS handshake record. DTLS resets handshake message numbers on each + * handshake, so renegotiations and retransmissions are ambiguous. */ if (rr->length < DTLS1_HM_HEADER_LENGTH) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_HANDSHAKE_RECORD); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD); goto f_err; } struct hm_header_st msg_hdr; - dtls1_get_message_header(&rr->data[rr->off], &msg_hdr); + dtls1_get_message_header(rr->data, &msg_hdr); - /* Ignore a stray Finished from the previous handshake. */ if (msg_hdr.type == SSL3_MT_FINISHED) { if (msg_hdr.frag_off == 0) { /* Retransmit our last flight of messages. If the peer sends the second * Finished, they may not have received ours. Only do this for the * first fragment, in case the Finished was fragmented. */ - if (dtls1_check_timeout_num(s) < 0) { + if (dtls1_check_timeout_num(ssl) < 0) { return -1; } - dtls1_retransmit_buffered_messages(s); + dtls1_retransmit_buffered_messages(ssl); } rr->length = 0; goto start; } - } - /* We already handled these. */ - assert(rr->type != SSL3_RT_CHANGE_CIPHER_SPEC && rr->type != SSL3_RT_ALERT); + /* Otherwise, this is a pre-CCS handshake message from an unsupported + * renegotiation attempt. Fall through to the error path. */ + } al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNEXPECTED_RECORD); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); f_err: - ssl3_send_alert(s, SSL3_AL_FATAL, al); -err: + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); return -1; } -int dtls1_write_app_data(SSL *s, const void *buf_, int len) { +int dtls1_write_app_data(SSL *ssl, const void *buf_, int len) { int i; - if (SSL_in_init(s) && !s->in_handshake) { - i = s->handshake_func(s); + if (SSL_in_init(ssl) && !ssl->in_handshake) { + i = ssl->handshake_func(ssl); if (i < 0) { return i; } if (i == 0) { - OPENSSL_PUT_ERROR(SSL, dtls1_write_app_data, SSL_R_SSL_HANDSHAKE_FAILURE); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); return -1; } } if (len > SSL3_RT_MAX_PLAIN_LENGTH) { - OPENSSL_PUT_ERROR(SSL, dtls1_write_app_data, SSL_R_DTLS_MESSAGE_TOO_BIG); + OPENSSL_PUT_ERROR(SSL, SSL_R_DTLS_MESSAGE_TOO_BIG); return -1; } - i = dtls1_write_bytes(s, SSL3_RT_APPLICATION_DATA, buf_, len, + i = dtls1_write_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf_, len, dtls1_use_current_epoch); return i; } /* Call this to write data in records of type 'type' It will return <= 0 if not * all data has been sent or non-blocking IO. */ -int dtls1_write_bytes(SSL *s, int type, const void *buf, int len, +int dtls1_write_bytes(SSL *ssl, int type, const void *buf, int len, enum dtls1_use_epoch_t use_epoch) { int i; assert(len <= SSL3_RT_MAX_PLAIN_LENGTH); - s->rwstate = SSL_NOTHING; - i = do_dtls1_write(s, type, buf, len, use_epoch); + ssl->rwstate = SSL_NOTHING; + i = do_dtls1_write(ssl, type, buf, len, use_epoch); return i; } -/* dtls1_seal_record seals a new record of type |type| and plaintext |in| and - * writes it to |out|. At most |max_out| bytes will be written. It returns one - * on success and zero on error. On success, it updates the write sequence - * number. */ -static int dtls1_seal_record(SSL *s, uint8_t *out, size_t *out_len, - size_t max_out, uint8_t type, const uint8_t *in, - size_t in_len, enum dtls1_use_epoch_t use_epoch) { - if (max_out < DTLS1_RT_HEADER_LENGTH) { - OPENSSL_PUT_ERROR(SSL, dtls1_seal_record, SSL_R_BUFFER_TOO_SMALL); - return 0; - } - - /* Determine the parameters for the current epoch. */ - uint16_t epoch = s->d1->w_epoch; - SSL_AEAD_CTX *aead = s->aead_write_ctx; - uint8_t *seq = s->s3->write_sequence; - if (use_epoch == dtls1_use_previous_epoch) { - /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1 - * (negotiated cipher) exist. */ - assert(s->d1->w_epoch == 1); - epoch = s->d1->w_epoch - 1; - aead = NULL; - seq = s->d1->last_write_sequence; - } - - out[0] = type; - - uint16_t wire_version = s->s3->have_version ? s->version : DTLS1_VERSION; - out[1] = wire_version >> 8; - out[2] = wire_version & 0xff; - - out[3] = epoch >> 8; - out[4] = epoch & 0xff; - memcpy(&out[5], &seq[2], 6); - - size_t ciphertext_len; - if (!SSL_AEAD_CTX_seal(aead, out + DTLS1_RT_HEADER_LENGTH, &ciphertext_len, - max_out - DTLS1_RT_HEADER_LENGTH, type, wire_version, - &out[3] /* seq */, in, in_len) || - !ssl3_record_sequence_update(&seq[2], 6)) { - return 0; - } - - if (ciphertext_len >= 1 << 16) { - OPENSSL_PUT_ERROR(SSL, dtls1_seal_record, ERR_R_OVERFLOW); - return 0; - } - out[11] = ciphertext_len >> 8; - out[12] = ciphertext_len & 0xff; - - *out_len = DTLS1_RT_HEADER_LENGTH + ciphertext_len; - - if (s->msg_callback) { - s->msg_callback(1 /* write */, 0, SSL3_RT_HEADER, out, - DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg); - } - - return 1; -} - -static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, +static int do_dtls1_write(SSL *ssl, int type, const uint8_t *buf, unsigned int len, enum dtls1_use_epoch_t use_epoch) { - SSL3_BUFFER *wb = &s->s3->wbuf; - - /* ssl3_write_pending drops the write if |BIO_write| fails in DTLS, so there - * is never pending data. */ - assert(s->s3->wbuf.left == 0); + /* There should never be a pending write buffer in DTLS. One can't write half + * a datagram, so the write buffer is always dropped in + * |ssl_write_buffer_flush|. */ + assert(!ssl_write_buffer_is_pending(ssl)); /* If we have an alert to send, lets send it */ - if (s->s3->alert_dispatch) { - int ret = s->method->ssl_dispatch_alert(s); + if (ssl->s3->alert_dispatch) { + int ret = ssl->method->ssl_dispatch_alert(ssl); if (ret <= 0) { return ret; } /* if it went, fall through and send more stuff */ } - if (wb->buf == NULL && !ssl3_setup_write_buffer(s)) { + if (len > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } @@ -798,130 +518,61 @@ static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, return 0; } - /* Align the output so the ciphertext is aligned to |SSL3_ALIGN_PAYLOAD|. */ - uintptr_t align = (uintptr_t)wb->buf + DTLS1_RT_HEADER_LENGTH; - align = (0 - align) & (SSL3_ALIGN_PAYLOAD - 1); - uint8_t *out = wb->buf + align; - wb->offset = align; - size_t max_out = wb->len - wb->offset; - + size_t max_out = len + ssl_max_seal_overhead(ssl); + uint8_t *out; size_t ciphertext_len; - if (!dtls1_seal_record(s, out, &ciphertext_len, max_out, type, buf, len, - use_epoch)) { + if (!ssl_write_buffer_init(ssl, &out, max_out) || + !dtls_seal_record(ssl, out, &ciphertext_len, max_out, type, buf, len, + use_epoch)) { + ssl_write_buffer_clear(ssl); return -1; } + ssl_write_buffer_set_len(ssl, ciphertext_len); - /* now let's set up wb */ - wb->left = ciphertext_len; - - /* 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); -} - -static int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap) { - int cmp; - unsigned int shift; - const uint8_t *seq = s->s3->read_sequence; - - cmp = satsub64be(seq, bitmap->max_seq_num); - if (cmp > 0) { - memcpy(s->s3->rrec.seq_num, seq, 8); - return 1; /* this record in new */ - } - shift = -cmp; - if (shift >= sizeof(bitmap->map) * 8) { - return 0; /* stale, outside the window */ - } else if (bitmap->map & (((uint64_t)1) << shift)) { - return 0; /* record previously received */ + int ret = ssl_write_buffer_flush(ssl); + if (ret <= 0) { + return ret; } - - memcpy(s->s3->rrec.seq_num, seq, 8); - return 1; + return (int)len; } -static void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap) { - int cmp; - unsigned int shift; - const uint8_t *seq = s->s3->read_sequence; - - cmp = satsub64be(seq, bitmap->max_seq_num); - if (cmp > 0) { - shift = cmp; - if (shift < sizeof(bitmap->map) * 8) { - bitmap->map <<= shift, bitmap->map |= 1UL; - } else { - bitmap->map = 1UL; - } - memcpy(bitmap->max_seq_num, seq, 8); - } else { - shift = -cmp; - if (shift < sizeof(bitmap->map) * 8) { - bitmap->map |= ((uint64_t)1) << shift; - } - } -} - -int dtls1_dispatch_alert(SSL *s) { +int dtls1_dispatch_alert(SSL *ssl) { int i, j; - void (*cb)(const SSL *ssl, int type, int val) = NULL; + void (*cb)(const SSL *ssl, int type, int value) = NULL; uint8_t buf[DTLS1_AL_HEADER_LENGTH]; uint8_t *ptr = &buf[0]; - s->s3->alert_dispatch = 0; + ssl->s3->alert_dispatch = 0; memset(buf, 0x00, sizeof(buf)); - *ptr++ = s->s3->send_alert[0]; - *ptr++ = s->s3->send_alert[1]; + *ptr++ = ssl->s3->send_alert[0]; + *ptr++ = ssl->s3->send_alert[1]; - i = do_dtls1_write(s, SSL3_RT_ALERT, &buf[0], sizeof(buf), + i = do_dtls1_write(ssl, SSL3_RT_ALERT, &buf[0], sizeof(buf), dtls1_use_current_epoch); if (i <= 0) { - s->s3->alert_dispatch = 1; + ssl->s3->alert_dispatch = 1; } else { - if (s->s3->send_alert[0] == SSL3_AL_FATAL) { - (void)BIO_flush(s->wbio); + if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) { + (void)BIO_flush(ssl->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 (ssl->msg_callback) { + ssl->msg_callback(1, ssl->version, SSL3_RT_ALERT, ssl->s3->send_alert, 2, + ssl, ssl->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 (ssl->info_callback != NULL) { + cb = ssl->info_callback; + } else if (ssl->ctx->info_callback != NULL) { + cb = ssl->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); + j = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1]; + cb(ssl, SSL_CB_WRITE_ALERT, j); } } return i; } - -void dtls1_reset_seq_numbers(SSL *s, int rw) { - uint8_t *seq; - unsigned int seq_bytes = sizeof(s->s3->read_sequence); - - if (rw & SSL3_CC_READ) { - seq = s->s3->read_sequence; - s->d1->r_epoch++; - memset(&s->d1->bitmap, 0, sizeof(DTLS1_BITMAP)); - } else { - seq = s->s3->write_sequence; - memcpy(s->d1->last_write_sequence, seq, sizeof(s->s3->write_sequence)); - s->d1->w_epoch++; - } - - memset(seq, 0x00, seq_bytes); -} |