summaryrefslogtreecommitdiffstats
path: root/lib/libssl/ssl_packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libssl/ssl_packet.c')
-rw-r--r--lib/libssl/ssl_packet.c278
1 files changed, 278 insertions, 0 deletions
diff --git a/lib/libssl/ssl_packet.c b/lib/libssl/ssl_packet.c
new file mode 100644
index 00000000000..0c5b4c463bf
--- /dev/null
+++ b/lib/libssl/ssl_packet.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2016, 2017 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ssl_locl.h"
+
+#include "bytestring.h"
+
+static int
+ssl_is_sslv2_client_hello(CBS *header)
+{
+ uint16_t record_length;
+ uint8_t message_type;
+ CBS cbs;
+
+ CBS_dup(header, &cbs);
+
+ if (!CBS_get_u16(&cbs, &record_length) ||
+ !CBS_get_u8(&cbs, &message_type))
+ return 0;
+
+ /*
+ * The SSLv2 record length field uses variable length (2 or 3 byte)
+ * encoding. Given the size of a client hello, we expect/require the
+ * 2-byte form which is indicated by a one in the most significant bit.
+ */
+ if ((record_length & 0x8000) == 0)
+ return 0;
+ if ((record_length & ~0x8000) < 3)
+ return 0;
+ if (message_type != SSL2_MT_CLIENT_HELLO)
+ return 0;
+
+ return 1;
+}
+
+static int
+ssl_is_sslv3_handshake(CBS *header)
+{
+ uint16_t record_version;
+ uint8_t record_type;
+ CBS cbs;
+
+ CBS_dup(header, &cbs);
+
+ if (!CBS_get_u8(&cbs, &record_type) ||
+ !CBS_get_u16(&cbs, &record_version))
+ return 0;
+
+ if (record_type != SSL3_RT_HANDSHAKE)
+ return 0;
+ if ((record_version >> 8) != SSL3_VERSION_MAJOR)
+ return 0;
+
+ return 1;
+}
+
+static int
+ssl_convert_sslv2_client_hello(SSL *s)
+{
+ CBB cbb, handshake, client_hello, cipher_suites, compression, session_id;
+ CBS cbs, challenge, cipher_specs, session;
+ uint16_t record_length, client_version, cipher_specs_length;
+ uint16_t session_id_length, challenge_length;
+ unsigned char *client_random, *data = NULL;
+ uint32_t cipher_spec;
+ uint8_t message_type;
+ size_t data_len;
+ int rv = -1;
+ int n;
+
+ memset(&cbb, 0, sizeof(cbb));
+
+ CBS_init(&cbs, s->internal->packet, SSL3_RT_HEADER_LENGTH);
+
+ if (!CBS_get_u16(&cbs, &record_length) ||
+ !CBS_get_u8(&cbs, &message_type) ||
+ !CBS_get_u16(&cbs, &client_version))
+ return -1;
+
+ /*
+ * The SSLv2 record length field uses variable length (2 or 3 byte)
+ * encoding. Given the size of a client hello, we expect/require the
+ * 2-byte form which is indicated by a one in the most significant bit.
+ * Also note that the record length value does not include the bytes
+ * used for the record length field.
+ */
+ if ((record_length & 0x8000) == 0)
+ return -1;
+ record_length &= ~0x8000;
+ if (record_length < SSL3_RT_HEADER_LENGTH - 2)
+ return -1;
+ if (message_type != SSL2_MT_CLIENT_HELLO)
+ return -1;
+
+ if (record_length < 9) {
+ SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,
+ SSL_R_RECORD_LENGTH_MISMATCH);
+ return -1;
+ }
+ if (record_length > 4096) {
+ SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO, SSL_R_RECORD_TOO_LARGE);
+ return -1;
+ }
+
+ n = ssl3_packet_extend(s, record_length + 2);
+ if (n != record_length + 2)
+ return n;
+
+ tls1_finish_mac(s, s->internal->packet + 2,
+ s->internal->packet_length - 2);
+ s->internal->mac_packet = 0;
+
+ if (s->internal->msg_callback)
+ s->internal->msg_callback(0, SSL2_VERSION, 0,
+ s->internal->packet + 2, s->internal->packet_length - 2, s,
+ s->internal->msg_callback_arg);
+
+ /* Decode the SSLv2 record containing the client hello. */
+ CBS_init(&cbs, s->internal->packet, s->internal->packet_length);
+
+ if (!CBS_get_u16(&cbs, &record_length))
+ return -1;
+ if (!CBS_get_u8(&cbs, &message_type))
+ return -1;
+ if (!CBS_get_u16(&cbs, &client_version))
+ return -1;
+ if (!CBS_get_u16(&cbs, &cipher_specs_length))
+ return -1;
+ if (!CBS_get_u16(&cbs, &session_id_length))
+ return -1;
+ if (!CBS_get_u16(&cbs, &challenge_length))
+ return -1;
+ if (!CBS_get_bytes(&cbs, &cipher_specs, cipher_specs_length))
+ return -1;
+ if (!CBS_get_bytes(&cbs, &session, session_id_length))
+ return -1;
+ if (!CBS_get_bytes(&cbs, &challenge, challenge_length))
+ return -1;
+ if (CBS_len(&cbs) != 0) {
+ SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO,
+ SSL_R_RECORD_LENGTH_MISMATCH);
+ return -1;
+ }
+
+ /* Build SSLv3/TLS record with client hello. */
+ if (!CBB_init(&cbb, SSL3_RT_MAX_PLAIN_LENGTH))
+ goto err;
+ if (!CBB_add_u8(&cbb, SSL3_RT_HANDSHAKE))
+ goto err;
+ if (!CBB_add_u16(&cbb, 0x0301))
+ goto err;
+ if (!CBB_add_u16_length_prefixed(&cbb, &handshake))
+ goto err;
+ if (!CBB_add_u8(&handshake, SSL3_MT_CLIENT_HELLO))
+ goto err;
+ if (!CBB_add_u24_length_prefixed(&handshake, &client_hello))
+ goto err;
+ if (!CBB_add_u16(&client_hello, client_version))
+ goto err;
+ if (!CBB_add_space(&client_hello, &client_random, SSL3_RANDOM_SIZE))
+ goto err;
+ memset(client_random, 0, SSL3_RANDOM_SIZE);
+ if (!CBS_write_bytes(&challenge, client_random, SSL3_RANDOM_SIZE, NULL))
+ goto err;
+ if (!CBB_add_u8_length_prefixed(&client_hello, &session_id))
+ goto err;
+ if (!CBB_add_u16_length_prefixed(&client_hello, &cipher_suites))
+ goto err;
+ while (CBS_len(&cipher_specs) > 0) {
+ if (!CBS_get_u24(&cipher_specs, &cipher_spec))
+ goto err;
+ if ((cipher_spec & 0xff0000) != 0)
+ continue;
+ if (!CBB_add_u16(&cipher_suites, cipher_spec & 0xffff))
+ goto err;
+ }
+ if (!CBB_add_u8_length_prefixed(&client_hello, &compression))
+ goto err;
+ if (!CBB_add_u8(&compression, 0))
+ goto err;
+ if (!CBB_finish(&cbb, &data, &data_len))
+ goto err;
+
+ if (data_len > s->s3->rbuf.len)
+ goto err;
+
+ s->internal->packet = s->s3->rbuf.buf;
+ s->internal->packet_length = data_len;
+ memcpy(s->internal->packet, data, data_len);
+ rv = 1;
+
+ err:
+ CBB_cleanup(&cbb);
+ free(data);
+
+ return (rv);
+}
+
+/*
+ * Potentially do legacy processing on the first packet received by a TLS
+ * server. We return 1 if we want SSLv3/TLS record processing to continue
+ * normally, otherwise we must set an SSLerr and return -1.
+ */
+int
+ssl_server_legacy_first_packet(SSL *s)
+{
+ uint16_t min_version;
+ const char *data;
+ CBS header;
+
+ if (SSL_IS_DTLS(s))
+ return 1;
+
+ CBS_init(&header, s->internal->packet, SSL3_RT_HEADER_LENGTH);
+
+ if (ssl_is_sslv3_handshake(&header) == 1)
+ return 1;
+
+ /* Only continue if this is not a version locked method. */
+ if (s->method->internal->min_version == s->method->internal->max_version)
+ return 1;
+
+ if (ssl_is_sslv2_client_hello(&header) == 1) {
+ /* Only permit SSLv2 client hellos if TLSv1.0 is enabled. */
+ if (ssl_enabled_version_range(s, &min_version, NULL) != 1) {
+ SSLerr(SSL_F_SSL23_CLIENT_HELLO,
+ SSL_R_NO_PROTOCOLS_AVAILABLE);
+ return -1;
+ }
+ if (min_version > TLS1_VERSION)
+ return 1;
+
+ if (ssl_convert_sslv2_client_hello(s) != 1) {
+ SSLerr(SSL_F_SSL23_CLIENT_HELLO,
+ SSL_R_BAD_PACKET_LENGTH);
+ return -1;
+ }
+
+ return 1;
+ }
+
+ /* Ensure that we have SSL3_RT_HEADER_LENGTH (5 bytes) of the packet. */
+ if (CBS_len(&header) != SSL3_RT_HEADER_LENGTH) {
+ SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+ data = (const char *)CBS_data(&header);
+
+ /* Is this a cleartext protocol? */
+ if (strncmp("GET ", data, 4) == 0 ||
+ strncmp("POST ", data, 5) == 0 ||
+ strncmp("HEAD ", data, 5) == 0 ||
+ strncmp("PUT ", data, 4) == 0) {
+ SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO, SSL_R_HTTP_REQUEST);
+ return -1;
+ }
+ if (strncmp("CONNE", data, 5) == 0) {
+ SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO, SSL_R_HTTPS_PROXY_REQUEST);
+ return -1;
+ }
+
+ SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL);
+
+ return -1;
+}