void *s2n_stuffer_raw_read(struct s2n_stuffer *stuffer, uint32_t data_len) { GUARD_PTR(s2n_stuffer_skip_read(stuffer, data_len)); stuffer->tainted = 1; return stuffer->blob.data + stuffer->read_cursor - data_len; }
/* See http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html 2.5 */ int s2n_sslv2_client_hello_recv(struct s2n_connection *conn) { struct s2n_stuffer *in = &conn->handshake.io; uint16_t session_id_length; uint16_t cipher_suites_length; uint16_t challenge_length; uint8_t *cipher_suites; if (conn->client_protocol_version < conn->config->cipher_preferences->minimum_protocol_version || conn->client_protocol_version > conn->server_protocol_version) { GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); S2N_ERROR(S2N_ERR_BAD_MESSAGE); } conn->actual_protocol_version = MIN(conn->client_protocol_version, conn->server_protocol_version); conn->client_hello_version = S2N_SSLv2; /* We start 5 bytes into the record */ GUARD(s2n_stuffer_read_uint16(in, &cipher_suites_length)); if (cipher_suites_length % S2N_SSLv2_CIPHER_SUITE_LEN) { S2N_ERROR(S2N_ERR_BAD_MESSAGE); } GUARD(s2n_stuffer_read_uint16(in, &session_id_length)); GUARD(s2n_stuffer_read_uint16(in, &challenge_length)); if (challenge_length > S2N_TLS_RANDOM_DATA_LEN) { S2N_ERROR(S2N_ERR_BAD_MESSAGE); } cipher_suites = s2n_stuffer_raw_read(in, cipher_suites_length); notnull_check(cipher_suites); GUARD(s2n_set_cipher_as_sslv2_server(conn, cipher_suites, cipher_suites_length / S2N_SSLv2_CIPHER_SUITE_LEN)); if (session_id_length > s2n_stuffer_data_available(in)) { S2N_ERROR(S2N_ERR_BAD_MESSAGE); } if (session_id_length > 0 && session_id_length <= S2N_TLS_SESSION_ID_MAX_LEN) { GUARD(s2n_stuffer_read_bytes(in, conn->session_id, session_id_length)); conn->session_id_len = (uint8_t) session_id_length; } else { GUARD(s2n_stuffer_skip_read(in, session_id_length)); } struct s2n_blob b; b.data = conn->secure.client_random; b.size = S2N_TLS_RANDOM_DATA_LEN; b.data += S2N_TLS_RANDOM_DATA_LEN - challenge_length; b.size -= S2N_TLS_RANDOM_DATA_LEN - challenge_length; GUARD(s2n_stuffer_read(in, &b)); conn->server->chosen_cert_chain = conn->config->cert_and_key_pairs; GUARD(s2n_conn_set_handshake_type(conn)); return 0; }
int s2n_stuffer_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) { GUARD(s2n_stuffer_skip_read(stuffer, size)); void *ptr = stuffer->blob.data + stuffer->read_cursor - size; notnull_check(ptr); memcpy_check(data, ptr, size); return 0; }
int s2n_stuffer_erase_and_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) { GUARD(s2n_stuffer_skip_read(stuffer, out->size)); void *ptr = stuffer->blob.data + stuffer->read_cursor - out->size; if (ptr == NULL) { return -1; } memcpy_check(out->data, ptr, out->size); memset(ptr, 0, out->size); return 0; }
static int s2n_stuffer_pem_read_contents(struct s2n_stuffer *pem, struct s2n_stuffer *asn1) { uint8_t base64_buf[64] = { 0 }; struct s2n_blob base64__blob = { .data = base64_buf, .size = sizeof(base64_buf) }; struct s2n_stuffer base64_stuffer = {{0}}; GUARD(s2n_stuffer_init(&base64_stuffer, &base64__blob)); while (1) { char c; /* Peek to see if the next char is a dash, meaning end of pem_contents */ GUARD(s2n_stuffer_peek_char(pem, &c)); if (c == '-') { break; } else { /* Else, move read pointer forward by 1 byte since we will be consuming it. */ GUARD(s2n_stuffer_skip_read(pem, 1)); } /* Skip non-base64 characters */ if (!s2n_is_base64_char(c)) { continue; } /* Flush base64_stuffer to asn1 stuffer if we're out of space, and reset base64_stuffer read/write pointers */ if (s2n_stuffer_space_remaining(&base64_stuffer) == 0) { GUARD(s2n_stuffer_read_base64(&base64_stuffer, asn1)); GUARD(s2n_stuffer_rewrite(&base64_stuffer)); } /* Copy next char to base64_stuffer */ GUARD(s2n_stuffer_write_bytes(&base64_stuffer, (uint8_t *) &c, 1)); }; /* Flush any remaining bytes to asn1 */ GUARD(s2n_stuffer_read_base64(&base64_stuffer, asn1)); return 0; } static int s2n_stuffer_data_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1, const char *keyword) { GUARD(s2n_stuffer_pem_read_begin(pem, keyword)); GUARD(s2n_stuffer_pem_read_contents(pem, asn1)); GUARD(s2n_stuffer_pem_read_end(pem, keyword)); return 0; }
int s2n_client_hello_recv(struct s2n_connection *conn) { struct s2n_stuffer *in = &conn->handshake.io; uint8_t compression_methods; uint16_t extensions_size; uint16_t cipher_suites_length; uint8_t *cipher_suites; uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; GUARD(s2n_stuffer_read_bytes(in, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); GUARD(s2n_stuffer_read_bytes(in, conn->secure.client_random, S2N_TLS_RANDOM_DATA_LEN)); GUARD(s2n_stuffer_read_uint8(in, &conn->session_id_len)); conn->client_protocol_version = (client_protocol_version[0] * 10) + client_protocol_version[1]; if (conn->client_protocol_version < conn->config->cipher_preferences->minimum_protocol_version || conn->client_protocol_version > conn->server_protocol_version) { GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); S2N_ERROR(S2N_ERR_BAD_MESSAGE); } conn->client_hello_version = conn->client_protocol_version; conn->actual_protocol_version = MIN(conn->client_protocol_version, conn->server_protocol_version); if (conn->session_id_len > S2N_TLS_SESSION_ID_MAX_LEN || conn->session_id_len > s2n_stuffer_data_available(in)) { S2N_ERROR(S2N_ERR_BAD_MESSAGE); } GUARD(s2n_stuffer_read_bytes(in, conn->session_id, conn->session_id_len)); GUARD(s2n_stuffer_read_uint16(in, &cipher_suites_length)); if (cipher_suites_length % S2N_TLS_CIPHER_SUITE_LEN) { S2N_ERROR(S2N_ERR_BAD_MESSAGE); } cipher_suites = s2n_stuffer_raw_read(in, cipher_suites_length); notnull_check(cipher_suites); /* Don't choose the cipher yet, read the extensions first */ GUARD(s2n_stuffer_read_uint8(in, &compression_methods)); GUARD(s2n_stuffer_skip_read(in, compression_methods)); /* This is going to be our default if the client has no preference. */ conn->secure.server_ecc_params.negotiated_curve = &s2n_ecc_supported_curves[0]; if (s2n_stuffer_data_available(in) >= 2) { /* Read extensions if they are present */ GUARD(s2n_stuffer_read_uint16(in, &extensions_size)); if (extensions_size > s2n_stuffer_data_available(in)) { S2N_ERROR(S2N_ERR_BAD_MESSAGE); } struct s2n_blob extensions; extensions.size = extensions_size; extensions.data = s2n_stuffer_raw_read(in, extensions.size); notnull_check(extensions.data); GUARD(s2n_client_extensions_recv(conn, &extensions)); } /* Now choose the ciphers and the cert chain. */ GUARD(s2n_set_cipher_as_tls_server(conn, cipher_suites, cipher_suites_length / 2)); conn->server->chosen_cert_chain = conn->config->cert_and_key_pairs; /* Set the handshake type */ GUARD(s2n_conn_set_handshake_type(conn)); return 0; }
static int s2n_recv_client_alpn(struct s2n_connection *conn, struct s2n_stuffer *extension) { uint16_t size_of_all; struct s2n_stuffer client_protos; struct s2n_stuffer server_protos; if (!conn->config->application_protocols.size) { /* No protocols configured, nothing to do */ return 0; } GUARD(s2n_stuffer_read_uint16(extension, &size_of_all)); if (size_of_all > s2n_stuffer_data_available(extension) || size_of_all < 3) { /* Malformed length, ignore the extension */ return 0; } struct s2n_blob application_protocols = { .data = s2n_stuffer_raw_read(extension, size_of_all), .size = size_of_all }; notnull_check(application_protocols.data); /* Find a matching protocol */ GUARD(s2n_stuffer_init(&client_protos, &application_protocols)); GUARD(s2n_stuffer_write(&client_protos, &application_protocols)); GUARD(s2n_stuffer_init(&server_protos, &conn->config->application_protocols)); GUARD(s2n_stuffer_write(&server_protos, &conn->config->application_protocols)); while (s2n_stuffer_data_available(&server_protos)) { uint8_t length; uint8_t protocol[255]; GUARD(s2n_stuffer_read_uint8(&server_protos, &length)); GUARD(s2n_stuffer_read_bytes(&server_protos, protocol, length)); while (s2n_stuffer_data_available(&client_protos)) { uint8_t client_length; GUARD(s2n_stuffer_read_uint8(&client_protos, &client_length)); if (client_length > s2n_stuffer_data_available(&client_protos)) { S2N_ERROR(S2N_ERR_BAD_MESSAGE); } if (client_length != length) { GUARD(s2n_stuffer_skip_read(&client_protos, client_length)); } else { uint8_t client_protocol[255]; GUARD(s2n_stuffer_read_bytes(&client_protos, client_protocol, client_length)); if (memcmp(client_protocol, protocol, client_length) == 0) { memcpy_check(conn->application_protocol, client_protocol, client_length); conn->application_protocol[client_length] = '\0'; return 0; } } } GUARD(s2n_stuffer_reread(&client_protos)); } S2N_ERROR(S2N_ERR_NO_APPLICATION_PROTOCOL); } static int s2n_recv_client_status_request(struct s2n_connection *conn, struct s2n_stuffer *extension) { if (s2n_stuffer_data_available(extension) < 5) { /* Malformed length, ignore the extension */ return 0; } uint8_t type; GUARD(s2n_stuffer_read_uint8(extension, &type)); if (type != (uint8_t) S2N_STATUS_REQUEST_OCSP) { /* We only support OCSP (type 1), ignore the extension */ return 0; } conn->status_type = (s2n_status_request_type) type; return 0; } static int s2n_recv_client_elliptic_curves(struct s2n_connection *conn, struct s2n_stuffer *extension) { uint16_t size_of_all; struct s2n_blob proposed_curves; GUARD(s2n_stuffer_read_uint16(extension, &size_of_all)); if (size_of_all > s2n_stuffer_data_available(extension) || size_of_all % 2) { /* Malformed length, ignore the extension */ return 0; } proposed_curves.size = size_of_all; proposed_curves.data = s2n_stuffer_raw_read(extension, proposed_curves.size); notnull_check(proposed_curves.data); if (s2n_ecc_find_supported_curve(&proposed_curves, &conn->secure.server_ecc_params.negotiated_curve) != 0) { /* Can't agree on a curve, ECC is not allowed. Return success to proceed with the handshake. */ conn->secure.server_ecc_params.negotiated_curve = NULL; } return 0; }