Lift BIO above SSL_PROTOCOL_METHOD. This gets us closer to exposing BIO-free APIs. The next step is probably to make the experimental bssl::OpenRecord function call a split out core of ssl_read_impl. Change-Id: I4acebb43f708df8c52eb4e328da8ae3551362fb9 Reviewed-on: https://boringssl-review.googlesource.com/21865 Commit-Queue: Steven Valdez <svaldez@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc index 7fc843e..b513a42 100644 --- a/ssl/s3_both.cc +++ b/ssl/s3_both.cc
@@ -274,55 +274,28 @@ return 1; } -static int read_v2_client_hello(SSL *ssl) { - // Read the first 5 bytes, the size of the TLS record header. This is - // sufficient to detect a V2ClientHello and ensures that we never read beyond - // the first record. - int ret = ssl_read_buffer_extend_to(ssl, SSL3_RT_HEADER_LENGTH); - if (ret <= 0) { - return ret; - } - const uint8_t *p = ssl_read_buffer(ssl).data(); - - // Some dedicated error codes for protocol mixups should the application wish - // to interpret them differently. (These do not overlap with ClientHello or - // V2ClientHello.) - if (strncmp("GET ", (const char *)p, 4) == 0 || - strncmp("POST ", (const char *)p, 5) == 0 || - strncmp("HEAD ", (const char *)p, 5) == 0 || - strncmp("PUT ", (const char *)p, 4) == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_R_HTTP_REQUEST); - return -1; - } - if (strncmp("CONNE", (const char *)p, 5) == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_R_HTTPS_PROXY_REQUEST); - return -1; - } - - if ((p[0] & 0x80) == 0 || p[2] != SSL2_MT_CLIENT_HELLO || - p[3] != SSL3_VERSION_MAJOR) { - // Not a V2ClientHello. - return 1; - } - +static ssl_open_record_t read_v2_client_hello(SSL *ssl, size_t *out_consumed, + Span<const uint8_t> in) { + *out_consumed = 0; + assert(in.size() >= SSL3_RT_HEADER_LENGTH); // Determine the length of the V2ClientHello. - size_t msg_length = ((p[0] & 0x7f) << 8) | p[1]; + size_t msg_length = ((in[0] & 0x7f) << 8) | in[1]; if (msg_length > (1024 * 4)) { OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); - return -1; + return ssl_open_record_error; } if (msg_length < SSL3_RT_HEADER_LENGTH - 2) { // Reject lengths that are too short early. We have already read // |SSL3_RT_HEADER_LENGTH| bytes, so we should not attempt to process an // (invalid) V2ClientHello which would be shorter than that. OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_LENGTH_MISMATCH); - return -1; + return ssl_open_record_error; } - // Read the remainder of the V2ClientHello. - ret = ssl_read_buffer_extend_to(ssl, 2 + msg_length); - if (ret <= 0) { - return ret; + // Ask for the remainder of the V2ClientHello. + if (in.size() < 2 + msg_length) { + *out_consumed = 2 + msg_length; + return ssl_open_record_partial; } CBS v2_client_hello = CBS(ssl_read_buffer(ssl).subspan(2, msg_length)); @@ -330,7 +303,7 @@ // hash. This is only ever called at the start of the handshake, so hs is // guaranteed to be non-NULL. if (!ssl->s3->hs->transcript.Update(v2_client_hello)) { - return -1; + return ssl_open_record_error; } ssl_do_msg_callback(ssl, 0 /* read */, 0 /* V2ClientHello */, @@ -349,7 +322,7 @@ !CBS_get_bytes(&v2_client_hello, &challenge, challenge_length) || CBS_len(&v2_client_hello) != 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); - return -1; + return ssl_open_record_error; } // msg_type has already been checked. @@ -385,7 +358,7 @@ !CBB_add_u8(&hello_body, 0) || !CBB_add_u16_length_prefixed(&hello_body, &cipher_suites)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); - return -1; + return ssl_open_record_error; } // Copy the cipher suites. @@ -393,7 +366,7 @@ uint32_t cipher_spec; if (!CBS_get_u24(&cipher_specs, &cipher_spec)) { OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); - return -1; + return ssl_open_record_error; } // Skip SSLv2 ciphers. @@ -402,7 +375,7 @@ } if (!CBB_add_u16(&cipher_suites, cipher_spec)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return -1; + return ssl_open_record_error; } } @@ -411,15 +384,12 @@ !CBB_add_u8(&hello_body, 0) || !CBB_finish(client_hello.get(), NULL, &ssl->init_buf->length)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return -1; + return ssl_open_record_error; } - // Consume and discard the V2ClientHello. - ssl_read_buffer_consume(ssl, 2 + msg_length); - ssl_read_buffer_discard(ssl); - + *out_consumed = 2 + msg_length; ssl->s3->is_v2_hello = true; - return 1; + return ssl_open_record_success; } static bool parse_message(const SSL *ssl, SSLMessage *out, @@ -497,58 +467,92 @@ return ssl->init_buf != NULL && ssl->init_buf->length > msg_len; } -int ssl3_read_message(SSL *ssl) { +ssl_open_record_t ssl3_open_handshake(SSL *ssl, size_t *out_consumed, + uint8_t *out_alert, Span<uint8_t> in) { + *out_consumed = 0; // Re-create the handshake buffer if needed. if (ssl->init_buf == NULL) { ssl->init_buf = BUF_MEM_new(); if (ssl->init_buf == NULL) { - return -1; + *out_alert = SSL_AD_INTERNAL_ERROR; + return ssl_open_record_error; } } // Bypass the record layer for the first message to handle V2ClientHello. if (ssl->server && !ssl->s3->v2_hello_done) { - int ret = read_v2_client_hello(ssl); - if (ret > 0) { - ssl->s3->v2_hello_done = true; + // Ask for the first 5 bytes, the size of the TLS record header. This is + // sufficient to detect a V2ClientHello and ensures that we never read + // beyond the first record. + if (in.size() < SSL3_RT_HEADER_LENGTH) { + *out_consumed = SSL3_RT_HEADER_LENGTH; + return ssl_open_record_partial; } - return ret; - } - SSL3_RECORD *rr = &ssl->s3->rrec; - // Get new packet if necessary. - if (rr->length == 0) { - int ret = ssl3_get_record(ssl); - if (ret <= 0) { + // Some dedicated error codes for protocol mixups should the application + // wish to interpret them differently. (These do not overlap with + // ClientHello or V2ClientHello.) + const char *str = reinterpret_cast<const char*>(in.data()); + if (strncmp("GET ", str, 4) == 0 || + strncmp("POST ", str, 5) == 0 || + strncmp("HEAD ", str, 5) == 0 || + strncmp("PUT ", str, 4) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_HTTP_REQUEST); + *out_alert = 0; + return ssl_open_record_error; + } + if (strncmp("CONNE", str, 5) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_HTTPS_PROXY_REQUEST); + *out_alert = 0; + return ssl_open_record_error; + } + + // Check for a V2ClientHello. + if ((in[0] & 0x80) != 0 && in[2] == SSL2_MT_CLIENT_HELLO && + in[3] == SSL3_VERSION_MAJOR) { + auto ret = read_v2_client_hello(ssl, out_consumed, in); + if (ret == ssl_open_record_error) { + *out_alert = 0; + } else if (ret == ssl_open_record_success) { + ssl->s3->v2_hello_done = true; + } return ret; } + + ssl->s3->v2_hello_done = true; + } + + uint8_t type; + Span<uint8_t> body; + auto ret = tls_open_record(ssl, &type, &body, out_consumed, out_alert, in); + if (ret != ssl_open_record_success) { + return ret; } // WatchGuard's TLS 1.3 interference bug is very distinctive: they drop the // ServerHello and send the remaining encrypted application data records // as-is. This manifests as an application data record when we expect // handshake. Report a dedicated error code for this case. - if (!ssl->server && rr->type == SSL3_RT_APPLICATION_DATA && + if (!ssl->server && type == SSL3_RT_APPLICATION_DATA && ssl->s3->aead_read_ctx->is_null_cipher()) { OPENSSL_PUT_ERROR(SSL, SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - return -1; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } - if (rr->type != SSL3_RT_HANDSHAKE) { + if (type != SSL3_RT_HANDSHAKE) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - return -1; + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; } // Append the entire handshake record to the buffer. - if (!BUF_MEM_append(ssl->init_buf, rr->data, rr->length)) { - return -1; + if (!BUF_MEM_append(ssl->init_buf, body.data(), body.size())) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return ssl_open_record_error; } - rr->length = 0; - ssl_read_buffer_discard(ssl); - return 1; + return ssl_open_record_success; } void ssl3_next_message(SSL *ssl) {