diff options
Diffstat (limited to 'src/ssl/s3_pkt.c')
-rw-r--r-- | src/ssl/s3_pkt.c | 234 |
1 files changed, 101 insertions, 133 deletions
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c index c42d000..4a9ae83 100644 --- a/src/ssl/s3_pkt.c +++ b/src/ssl/s3_pkt.c @@ -129,9 +129,13 @@ int ssl3_read_n(SSL *s, int n, int extend) { * if |extend| is 1, increase packet by another n bytes. * * The packet will be in the sub-array of |s->s3->rbuf.buf| specified by - * |s->packet| and |s->packet_length|. (If |s->read_ahead| is set and |extend| - * is 0, additional bytes may be read into |rbuf|, up to the size of the - * buffer.) */ + * |s->packet| and |s->packet_length|. (If DTLS and |extend| is 0, additional + * bytes will be read into |rbuf|, up to the size of the buffer.) + * + * TODO(davidben): |dtls1_get_record| and |ssl3_get_record| have very + * different needs. Separate the two record layers. In DTLS, |BIO_read| is + * called at most once, and only when |extend| is 0. In TLS, the buffer never + * contains more than one record. */ int i, len, left; uintptr_t align = 0; uint8_t *pkt; @@ -173,10 +177,10 @@ int ssl3_read_n(SSL *s, int n, int extend) { /* ... now we can act as if 'extend' was set */ } - /* For DTLS/UDP reads should not span multiple packets because the read - * operation returns the whole packet at once (as long as it fits into the - * buffer). */ - if (SSL_IS_DTLS(s) && left > 0 && n > left) { + /* In DTLS, if there is leftover data from the previous packet or |extend| is + * true, clamp to the previous read. DTLS records may not span packet + * boundaries. */ + if (SSL_IS_DTLS(s) && n > left && (left > 0 || extend)) { n = left; } @@ -207,7 +211,7 @@ int ssl3_read_n(SSL *s, int n, int extend) { } int max = n; - if (s->read_ahead && !extend) { + if (SSL_IS_DTLS(s) && !extend) { max = rb->len - rb->offset; } @@ -262,16 +266,14 @@ int ssl3_read_n(SSL *s, int n, int extend) { * ssl->s3->rrec.length - number of bytes */ /* used only by ssl3_read_bytes */ static int ssl3_get_record(SSL *s) { - int ssl_major, ssl_minor, al; - int n, i, ret = -1; - SSL3_RECORD *rr; + uint8_t ssl_major, ssl_minor; + int al, n, i, ret = -1; + SSL3_RECORD *rr = &s->s3->rrec; uint8_t *p; - short version; + uint16_t version; size_t extra; unsigned empty_record_count = 0; - rr = &s->s3->rrec; - again: /* check if we have the header */ if (s->rstate != SSL_ST_READ_BODY || @@ -296,7 +298,7 @@ again: rr->type = *(p++); ssl_major = *(p++); ssl_minor = *(p++); - version = (ssl_major << 8) | ssl_minor; + version = (((uint16_t)ssl_major) << 8) | ssl_minor; n2s(p, rr->length); if (s->s3->have_version && version != s->version) { @@ -339,40 +341,40 @@ again: s->rstate = SSL_ST_READ_HEADER; /* set state for later operations */ - /* At this point, s->packet_length == SSL3_RT_HEADER_LNGTH + rr->length, and - * we have that many bytes in s->packet. */ - rr->input = &s->packet[SSL3_RT_HEADER_LENGTH]; - - /* ok, we can now read from |s->packet| data into |rr|. |rr->input| points at - * |rr->length| bytes, which need to be copied into |rr->data| by decryption. - * When the data is 'copied' into the |rr->data| buffer, |rr->input| will be - * pointed at the new buffer. */ + /* |rr->data| points to |rr->length| bytes of ciphertext in |s->packet|. */ + rr->data = &s->packet[SSL3_RT_HEADER_LENGTH]; - /* We now have - encrypted [ MAC [ compressed [ plain ] ] ] - * rr->length bytes of encrypted compressed stuff. */ - - /* decrypt in place in 'rr->input' */ - rr->data = rr->input; - - if (!s->enc_method->enc(s, 0)) { + /* Decrypt the packet in-place. + * + * 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, s->s3->read_sequence, rr->data, + rr->length)) { al = SSL_AD_BAD_RECORD_MAC; OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC); goto f_err; } - - if (rr->length > SSL3_RT_MAX_PLAIN_LENGTH + extra) { + if (!ssl3_record_sequence_update(s->s3->read_sequence, 8)) { + goto err; + } + if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH + extra) { al = SSL_AD_RECORD_OVERFLOW; OPENSSL_PUT_ERROR(SSL, ssl3_get_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 is the number of bytes in the record; * ssl->s3->rrec.off is the offset to first valid byte; - * ssl->s3->rrec.data is where to take bytes from (increment after use). */ + * 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; @@ -396,6 +398,10 @@ err: return ret; } +int ssl3_write_app_data(SSL *ssl, const void *buf, int len) { + return ssl3_write_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len); +} + /* 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 ssl3_write_bytes(SSL *s, int type, const void *buf_, int len) { @@ -471,8 +477,8 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len) { /* ssl3_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, |s->s3->wrec| is updated to include - * the new record. */ + * on success and zero on error. On success, it updates the write sequence + * number. */ static int ssl3_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) { @@ -485,61 +491,30 @@ static int ssl3_seal_record(SSL *s, uint8_t *out, size_t *out_len, /* Some servers hang if initial ClientHello is larger than 256 bytes and * record version number > TLS 1.0. */ + uint16_t wire_version = s->version; if (!s->s3->have_version && s->version > SSL3_VERSION) { - out[1] = TLS1_VERSION >> 8; - out[2] = TLS1_VERSION & 0xff; - } else { - out[1] = s->version >> 8; - out[2] = s->version & 0xff; + wire_version = TLS1_VERSION; } + out[1] = wire_version >> 8; + out[2] = wire_version & 0xff; - size_t explicit_nonce_len = 0; - if (s->aead_write_ctx != NULL && - s->aead_write_ctx->variable_nonce_included_in_record) { - explicit_nonce_len = s->aead_write_ctx->variable_nonce_len; - } - size_t max_overhead = 0; - if (s->aead_write_ctx != NULL) { - max_overhead = s->aead_write_ctx->tag_len; - } - - /* Assemble the input for |s->enc_method->enc|. The input is the plaintext - * with |explicit_nonce_len| bytes of space prepended for the explicit - * nonce. The input is copied into |out| and then encrypted in-place to take - * advantage of alignment. - * - * TODO(davidben): |tls1_enc| should accept its inputs and outputs directly - * rather than looking up in |wrec| and friends. The |max_overhead| bounds - * check would also be unnecessary if |max_out| were passed down. */ - SSL3_RECORD *wr = &s->s3->wrec; - size_t plaintext_len = in_len + explicit_nonce_len; - if (plaintext_len < in_len || plaintext_len > INT_MAX || - plaintext_len + max_overhead < plaintext_len) { - OPENSSL_PUT_ERROR(SSL, ssl3_seal_record, ERR_R_OVERFLOW); - return 0; - } - if (max_out - SSL3_RT_HEADER_LENGTH < plaintext_len + max_overhead) { - OPENSSL_PUT_ERROR(SSL, ssl3_seal_record, SSL_R_BUFFER_TOO_SMALL); - return 0; - } - wr->type = type; - wr->input = out + SSL3_RT_HEADER_LENGTH; - wr->data = wr->input; - wr->length = plaintext_len; - memcpy(wr->input + explicit_nonce_len, in, in_len); - - if (!s->enc_method->enc(s, 1)) { + size_t ciphertext_len; + if (!SSL_AEAD_CTX_seal(s->aead_write_ctx, out + SSL3_RT_HEADER_LENGTH, + &ciphertext_len, max_out - SSL3_RT_HEADER_LENGTH, + type, wire_version, s->s3->write_sequence, in, + in_len) || + !ssl3_record_sequence_update(s->s3->write_sequence, 8)) { return 0; } - /* |wr->length| has now been set to the ciphertext length. */ - if (wr->length >= 1 << 16) { + if (ciphertext_len >= 1 << 16) { OPENSSL_PUT_ERROR(SSL, ssl3_seal_record, ERR_R_OVERFLOW); return 0; } - out[3] = wr->length >> 8; - out[4] = wr->length & 0xff; - *out_len = SSL3_RT_HEADER_LENGTH + (size_t)wr->length; + out[3] = ciphertext_len >> 8; + out[4] = ciphertext_len & 0xff; + + *out_len = SSL3_RT_HEADER_LENGTH + ciphertext_len; if (s->msg_callback) { s->msg_callback(1 /* write */, 0, SSL3_RT_HEADER, out, SSL3_RT_HEADER_LENGTH, @@ -696,6 +671,14 @@ int ssl3_expect_change_cipher_spec(SSL *s) { return 1; } +int ssl3_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) { + return ssl3_read_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len, peek); +} + +void ssl3_read_close_notify(SSL *ssl) { + ssl3_read_bytes(ssl, 0, NULL, 0, 0); +} + /* Return up to 'len' payload bytes received in 'type' records. * 'type' is one of the following: * @@ -859,22 +842,18 @@ start: 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. */ + /* Process unexpected records. */ if (rr->type == SSL3_RT_HANDSHAKE) { /* If peer renegotiations are disabled, all out-of-order handshake records - * are fatal. */ - if (s->reject_peer_renegotiations) { + * are fatal. Renegotiations as a server are never supported. */ + if (!s->accept_peer_renegotiations || s->server) { al = SSL_AD_NO_RENEGOTIATION; OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_NO_RENEGOTIATION); goto f_err; } + /* HelloRequests may be fragmented across multiple records. */ const size_t size = sizeof(s->s3->handshake_fragment); const size_t avail = size - s->s3->handshake_fragment_len; const size_t todo = (rr->length < avail) ? rr->length : avail; @@ -886,45 +865,53 @@ start: if (s->s3->handshake_fragment_len < size) { goto start; /* fragment was too small */ } - } - /* s->s3->handshake_fragment_len == 4 iff rr->type == SSL3_RT_HANDSHAKE; - * (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 || + /* Parse out and consume a HelloRequest. */ + if (s->s3->handshake_fragment[0] != SSL3_MT_HELLO_REQUEST || + s->s3->handshake_fragment[1] != 0 || s->s3->handshake_fragment[2] != 0 || s->s3->handshake_fragment[3] != 0) { al = SSL_AD_DECODE_ERROR; OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_BAD_HELLO_REQUEST); goto f_err; } + s->s3->handshake_fragment_len = 0; 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->renegotiate) { - ssl3_renegotiate(s); - if (ssl3_renegotiate_check(s)) { - i = s->handshake_func(s); - if (i < 0) { - return i; - } - if (i == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); - return -1; - } - } + if (!SSL_is_init_finished(s) || !s->s3->initial_handshake_complete) { + /* This cannot happen. If a handshake is in progress, |type| must be + * |SSL3_RT_HANDSHAKE|. */ + assert(0); + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* Renegotiation is only supported at quiescent points in the application + * protocol, namely in HTTPS, just before reading the HTTP response. Require + * the record-layer be idle and avoid complexities of sending a handshake + * record while an application_data record is being written. */ + if (s->s3->wbuf.left != 0 || s->s3->rbuf.left != 0) { + al = SSL_AD_NO_RENEGOTIATION; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_NO_RENEGOTIATION); + goto f_err; + } + + /* Begin a new handshake. */ + s->state = SSL_ST_CONNECT; + i = s->handshake_func(s); + if (i < 0) { + return i; + } + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; } - /* we either finished a handshake or ignored the request, now try again to - * obtain the (application) data we were asked for */ + + /* The handshake completed synchronously. Continue reading records. */ goto start; } @@ -1043,25 +1030,6 @@ 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->state = s->server ? SSL_ST_ACCEPT : SSL_ST_CONNECT; - s->renegotiate = 1; - s->new_session = 1; - } - i = s->handshake_func(s); - if (i < 0) { - return i; - } - if (i == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); - return -1; - } - - goto start; - } - /* We already handled these. */ assert(rr->type != SSL3_RT_CHANGE_CIPHER_SPEC && rr->type != SSL3_RT_ALERT && rr->type != SSL3_RT_HANDSHAKE); |