├── Makefile ├── README.md ├── nf_conntrack_tls_core.c ├── ssl_client_hello.h ├── ssl_client_hello_read.c ├── ssl_client_hello_write.c ├── tls_ssl_record_parser.c └── tls_ssl_record_parser.h /Makefile: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License version 2 as 3 | # published by the Free Software Foundation. 4 | # 5 | 6 | nf_conntrack_tls-objs := nf_conntrack_tls_core.o tls_ssl_record_parser.o 7 | obj-m := nf_conntrack_tls.o 8 | 9 | # This module is experimental, hence the DEBUG 10 | CFLAGS_nf_conntrack_tls_core.o := -DDEBUG 11 | CFLAGS_tls_ssl_record_parser.o := -DDEBUG 12 | KDIR := /lib/modules/$(shell uname -r)/build 13 | PWD := $(shell pwd) 14 | 15 | default: 16 | $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SSL/TLS netfilter module 2 | 3 | ## THIS MODULE IS AN EXPERIMENTAL WORK IN PROGRESS 4 | 5 | nf_conntrack_tls is an experimental connection tracking module for linux which 6 | tracks the SSL/TLS state associated with an encrypted connection. 7 | 8 | This module tracks the SSL/TLS state machine and drops connections that no 9 | longer appear to obey some semantics of the SSL/TLS protocols. 10 | 11 | **Note:** I wouldn't recommend actually using the module. The module is/was an 12 | emergency and temporary measure borne out of necessity. In general, moving input 13 | processing risks to the kernel is an especially bad idea. But I've uploaded the 14 | module in case it can serve as a useful example of a stateful conn track module. 15 | 16 | Initially, three protocol validations are performed: 17 | 18 | 1. This module understands TLS heartbeats and can detect and stop a heartbleed attempt. 19 | 1. All-non-handshake and heartbeat records are subject to an optional "suspicious sequence length" check. This check places a limit on how many bytes without their first bit set (ie byte value is < 128) can appear in a contiguous sequence. An SSL connection should consist of random-looking data, a suspiciously long sequence of zeroed bytes or ascii data may indicate that something nefarious is going on. The default length limit is 128, however this can be changed at module load time. 20 | 1. Directionality is enforced on SSL2 handshake messages. 21 | 22 | Further validations are possible, including: TLS handshake directionality, protocol version tracking, protocol downgrade mitigation. These and more are future work, and contributions are welcome. 23 | 24 | ## Testing 25 | 26 | To compile this and test this netfilter module on Amazon Linux: 27 | 28 | make 29 | sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED -m helper --helper tls -p TCP --dport 443 -j ACCEPT 30 | sudo modprobe nf_conntrack 31 | sudo insmod ./nf_conntrack_tls.ko 32 | 33 | Note that the Makefile has DEBUG defined, which slows the operation of the module. 34 | -------------------------------------------------------------------------------- /nf_conntrack_tls_core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * XXXXXXXXX THIS MODULE IS AN EXPERIMENTAL WORK IN PROGRESS XXXXXXXXX 3 | * 4 | * The ultimate goal of this module is to provide a framework for safeguarding an 5 | * SSL/TLS stack. This module tracks the SSL/TLS state machine and drops connections 6 | * that no longer appear to obey some semantics of the SSL/TLS protocols. 7 | * 8 | * For now, it provides three protocol-level validations: 9 | * 10 | * 1. This module understands TLS heartbeats and can detect and stop 11 | * a heartbleed attempt. 12 | * 13 | * 2. All-non-handshake and heartbeat records are subject to an optional 14 | * "suspicious sequence length" check. This check places a limit on how 15 | * many bytes without their first bit set (ie byte value is < 128) can 16 | * appear in a contiguous sequence. An encrypted connection should consist 17 | * of random-looking data, a long sequence of zeroed bytes or ascii 18 | * data may indicate that something nefarious is going on. The default 19 | * length limit is 128 bytes, however this can be changed at module load time. 20 | * 21 | * 3. Directionality is enforced on SSL2 handshake messages. 22 | * 23 | * Further validations are possible, including: TLS handshake directionality, 24 | * protocol version tracking, protocol downgrade mitigation. These and more 25 | * are future work. Contributions are welcome! 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | 42 | #include "tls_ssl_record_parser.h" 43 | 44 | MODULE_LICENSE("GPL"); 45 | MODULE_AUTHOR("Colm MacCárthaigh "); 46 | MODULE_DESCRIPTION("TLS connection tracker"); 47 | MODULE_ALIAS("ip_conntrack_tls"); 48 | MODULE_ALIAS_NFCT_HELPER("tls"); 49 | 50 | #define HTTPS_PORT 443 /* Default port */ 51 | #define SUSPICIOUS_LENGTH 128 /* Default max length of suspicious sequence permitted */ 52 | 53 | /* How long should this state be maintained ? */ 54 | static const struct nf_conntrack_expect_policy tls_exp_policy = { 55 | .max_expected = 1, 56 | .timeout = 5 * 60, 57 | .name = "tls", 58 | }; 59 | 60 | #define MAX_PORTS 8 61 | static u_int16_t ports[MAX_PORTS]; 62 | static unsigned int ports_c; 63 | module_param_array(ports, ushort, &ports_c, 0400); 64 | 65 | /* We support a log only mode */ 66 | static bool log_only_mode; 67 | module_param(log_only_mode, bool, 0600); 68 | 69 | /* The maximum length of a suspicious sequence is tunable */ 70 | static unsigned int suspicious_sequence_length = SUSPICIOUS_LENGTH; 71 | module_param(suspicious_sequence_length, uint, 0400); 72 | 73 | /* We allocate a buffer per CPU */ 74 | static char *packet_buffer; 75 | 76 | /* Stores the parser config, which is global */ 77 | static struct tls_parser_config tls_parser_config; 78 | 79 | /* How big each buffer is. 16k should cover jumboframes */ 80 | #define PACKET_BUFFER_LEN 16384 81 | 82 | static int tls_helper(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) 83 | { 84 | unsigned int data_offset, tls_packet_len; 85 | int direction = CTINFO2DIR(ctinfo); 86 | int ret; 87 | struct tls_state *tls_state = nfct_help_data(ct); 88 | uint8_t *tls_packet; 89 | struct tcphdr tcp_header_data; 90 | struct tcphdr *tcp_header; 91 | char *message; 92 | 93 | if (NULL == tls_state) { 94 | return NF_DROP; 95 | } 96 | 97 | if (sizeof(struct tcphdr) >= skb->len) { 98 | return NF_DROP; 99 | } 100 | 101 | tcp_header = skb_header_pointer(skb, protoff, sizeof(tcp_header_data), &tcp_header_data); 102 | if (NULL == tcp_header) 103 | return NF_DROP; 104 | 105 | data_offset = protoff + (tcp_header->doff * 4); 106 | if (data_offset > skb->len) { 107 | return NF_DROP; 108 | } 109 | /* Accept empty TCP messages (handhakes and keepalives) */ 110 | else if (data_offset == skb->len) { 111 | return NF_ACCEPT; 112 | } 113 | 114 | tls_packet_len = skb->len - data_offset; 115 | do { 116 | int parser_return; 117 | int amount_to_read; 118 | 119 | amount_to_read = tls_packet_len; 120 | if (amount_to_read > PACKET_BUFFER_LEN) { 121 | amount_to_read = PACKET_BUFFER_LEN; 122 | } 123 | 124 | /* Default is to accept the packet */ 125 | ret = NF_ACCEPT; 126 | 127 | /* Aquire the lock and linearize the skb if neccessary */ 128 | tls_packet = skb_header_pointer(skb, data_offset, amount_to_read, &get_cpu_var(packet_buffer)); 129 | if (NULL == tls_packet) { 130 | return NF_DROP; 131 | } 132 | 133 | /* O.k, at this point we have tls_packet, which points to the actual 134 | * TLS wire-level data, in the TCP packet payload section. 135 | */ 136 | message = NULL; 137 | parser_return = 138 | tls_ssl2_record_parser(tls_state, &tls_parser_config, direction, tls_packet, tls_packet_len, 139 | &message); 140 | if (parser_return) { 141 | ret = NF_DROP; 142 | break; 143 | } 144 | 145 | tls_packet_len -= amount_to_read; 146 | data_offset += amount_to_read; 147 | } while (tls_packet_len); 148 | 149 | if (message) { 150 | pr_debug("nf_conntrack_tls: dropping packet due to '%s'", message); 151 | nf_ct_helper_log(skb, ct, "nf_conntrack_tls: dropping packet due to '%s'", message); 152 | } else if (NF_DROP == ret) { 153 | pr_debug("nf_conntrack_tls: dropping packet"); 154 | nf_ct_helper_log(skb, ct, "nf_conntrack_tls: dropping packet"); 155 | } 156 | 157 | if (log_only_mode) { 158 | ret = NF_ACCEPT; 159 | } 160 | 161 | return ret; 162 | } 163 | 164 | static struct nf_conntrack_helper tls_helpers[MAX_PORTS][2] __read_mostly; 165 | 166 | static void nf_conntrack_tls_fini(void) 167 | { 168 | int i, j; 169 | for (i = 0; i < ports_c; i++) { 170 | for (j = 0; j < 2; j++) { 171 | if (NULL == tls_helpers[i][j].me) 172 | continue; 173 | 174 | pr_debug("nf_ct_tls: unregistering helper for pf: %d " 175 | "port: %d\n", tls_helpers[i][j].tuple.src.l3num, ports[i]); 176 | nf_conntrack_helper_unregister(&tls_helpers[i][j]); 177 | } 178 | } 179 | } 180 | 181 | static int __init nf_conntrack_tls_init(void) 182 | { 183 | int i, j = -1, ret = 0; 184 | 185 | /* Allocate a 16k buffer per CPU for linearizing the SKB */ 186 | packet_buffer = (char *)alloc_percpu(char[PACKET_BUFFER_LEN]); 187 | if (!packet_buffer) 188 | return -ENOMEM; 189 | 190 | /* Set the config */ 191 | tls_parser_config.max_low_bytes_sequence_length = suspicious_sequence_length; 192 | 193 | /* If given no arguments, then run on port 443 */ 194 | if (0 == ports_c) 195 | ports[ports_c++] = HTTPS_PORT; 196 | 197 | /* Register an IPv4 and IPv6 helper for each port */ 198 | for (i = 0; i < ports_c; i++) { 199 | tls_helpers[i][0].tuple.src.l3num = PF_INET; 200 | tls_helpers[i][1].tuple.src.l3num = PF_INET6; 201 | for (j = 0; j < 2; j++) { 202 | /* We have two tls states, one for client, one for server */ 203 | tls_helpers[i][j].data_len = sizeof(struct tls_state); 204 | tls_helpers[i][j].tuple.src.u.tcp.port = htons(ports[i]); 205 | tls_helpers[i][j].tuple.dst.protonum = IPPROTO_TCP; 206 | tls_helpers[i][j].expect_policy = &tls_exp_policy; 207 | tls_helpers[i][j].me = THIS_MODULE; 208 | tls_helpers[i][j].help = tls_helper; 209 | 210 | sprintf(tls_helpers[i][j].name, "tls-%d", ports[i]); 211 | pr_debug("nf_ct_tls: registering helper for pf: %d " 212 | "port: %d\n", tls_helpers[i][j].tuple.src.l3num, ports[i]); 213 | 214 | ret = nf_conntrack_helper_register(&tls_helpers[i][j]); 215 | if (ret) { 216 | printk(KERN_ERR "nf_ct_tls: failed to register" 217 | " helper for pf: %d port: %d\n", tls_helpers[i][j].tuple.src.l3num, ports[i]); 218 | tls_helpers[i][j].me = NULL; 219 | nf_conntrack_tls_fini(); 220 | return ret; 221 | } 222 | } 223 | } 224 | 225 | return 0; 226 | } 227 | 228 | module_init(nf_conntrack_tls_init); 229 | module_exit(nf_conntrack_tls_fini); 230 | -------------------------------------------------------------------------------- /ssl_client_hello.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __KERNEL__ 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | /* Per http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html C.2 */ 10 | #define SSL_CLIENT_HELLO 1 11 | 12 | /* Cipher specs are 3 bytes each, the maximum size of a hello record is 13 | * 65536 bytes. Use a simplistic over-estimate assuming the entire record 14 | * was nothing but cipher specs. 15 | */ 16 | #define MAX_CIPHER_SPECS_LEN 21846 17 | 18 | /* 19 | * An SSL2 Client Hello message has the format: 20 | * 21 | * Bytes 0-1: Client version 22 | * Bytes 2-3: Cipher specs length (CSL) 23 | * Bytes 4-5: Session ID length (SIL) 24 | * Bytes 6-7: Challenge-length (CHL) 25 | * Bytes 8-CSL: Cipher specs 26 | * Bytes CSL-SIL: Session ID length 27 | * Bytes SIL-CHL: Challenge 28 | * 29 | */ 30 | struct ssl2_client_hello_data { 31 | 32 | /* The version advertised by the client */ 33 | uint8_t client_version[2]; 34 | 35 | /* The length of the cipher specifications */ 36 | uint16_t cipher_specs_len; 37 | 38 | /* The session id length */ 39 | uint16_t session_id_len; 40 | 41 | /* Maximum number of cipher specs */ 42 | uint8_t cipher_specs[MAX_CIPHER_SPECS_LEN][3]; 43 | 44 | /* The session id presended by the client */ 45 | uint8_t session_id[16]; 46 | 47 | /* The challenge */ 48 | uint8_t challenge[32]; 49 | }; 50 | 51 | /* 52 | * Read an SSL2 Client Hello message 53 | * 54 | * Parameters: 55 | * hello_data Pointer to the hello data structure to be filled 56 | * data_ptr pointer to the data stream to parse 57 | * data_len how much data is there to parse 58 | * message if we encounter an error, will be updated to point to 59 | * an error message 60 | * 61 | * Return: 62 | * 0 No problem encountered 63 | * -1 A problem was encountered parsing the client hello 64 | */ 65 | extern int ssl2_client_hello_read(struct ssl2_client_hello_data *hello_data, uint8_t * data_ptr, uint32_t data_len, 66 | char **message); 67 | 68 | /* 69 | * Read an SSL2 Client Hello message 70 | * 71 | * Parameters: 72 | * hello_data Pointer to the hello data structure to be filled 73 | * data_ptr pointer to the data stream to parse 74 | * data_len how much data is there to parse 75 | * message if we encounter an error, will be updated to point to 76 | * an error message 77 | * 78 | * Return: 79 | * >=0 The number of bytes written 80 | * -1 A problem was encountered parsing the client hello 81 | */ 82 | extern int ssl2_client_hello_write(struct ssl2_client_hello_data *hello_data, uint8_t * data_ptr, uint32_t data_len, 83 | char **message); 84 | -------------------------------------------------------------------------------- /ssl_client_hello_read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ssl_client_hello.h" 4 | 5 | /* Consume a byte from the data stream 6 | */ 7 | #define CONSUME_BYTES( len ) data_ptr += (len); data_len -= (len); 8 | #define CONSUME_BYTE() CONSUME_BYTES( 1 ) 9 | 10 | /* 11 | * Parse an SSL2 Client Hello message 12 | * 13 | * Parameters: 14 | * hello_data Pointer to the hello data structure to be filled 15 | * data_ptr pointer to the data stream to parse 16 | * data_len how much data is there to parse 17 | * message if we encounter an error, will be updated to point to 18 | * an error message 19 | * 20 | * Return: 21 | * 0 No problem encountered 22 | * -1 A problem was encountered parsing the client hello 23 | */ 24 | int ssl2_client_hello_read(struct ssl2_client_hello_data *hello_data, uint8_t * data_ptr, uint32_t data_len, char **message) 25 | { 26 | uint16_t challenge_len; 27 | 28 | if (hello_data == NULL) { 29 | *message = "NULL hello_data passed to ssl2_client_hello_read"; 30 | return -1; 31 | } 32 | 33 | if (data_len < 8) { 34 | *message = "SSL2 Client Hello record is too short"; 35 | return -1; 36 | } 37 | 38 | hello_data->client_version = (*data_ptr << 8); 39 | CONSUME_BYTE(); 40 | 41 | hello_data->client_version |= *data_ptr; 42 | CONSUME_BYTE(); 43 | 44 | hello_data->cipher_specs_len = (*data_ptr << 8); 45 | CONSUME_BYTE(); 46 | 47 | hello_data->cipher_specs_len |= *data_ptr; 48 | CONSUME_BYTE(); 49 | 50 | if (hello_data->cipher_specs_len % 3) { 51 | *message = "SSL2 Client Hello record has invalid cipher spec length"; 52 | return -1; 53 | } 54 | 55 | if (hello_data->cipher_specs_len > MAX_CIPHER_SPECS_LEN) { 56 | *message = "SSL2 Client Hello record has too large a cipher spec length"; 57 | return -1; 58 | } 59 | 60 | hello_data->session_id_len = (*data_ptr << 8); 61 | CONSUME_BYTE(); 62 | 63 | hello_data->session_id_len |= *data_ptr; 64 | CONSUME_BYTE(); 65 | 66 | if (hello_data->session_id_len != 0 && hello_data->session_id_len != 16) { 67 | *message = "SSL2 Client Hello record has invalid session id length"; 68 | return -1; 69 | } 70 | 71 | challenge_len = (*data_ptr << 8); 72 | CONSUME_BYTE(); 73 | 74 | challenge_len |= *data_ptr; 75 | CONSUME_BYTE(); 76 | 77 | if (challenge_len < 16 || challenge_len > 32) { 78 | *message = "SSL2 Client Hello record has invalid session id length"; 79 | return -1; 80 | } 81 | 82 | /* Check the record is long enough to contain the data it claims to 83 | * contain 84 | */ 85 | 86 | if (data_len < ((hello_data->cipher_specs_len * 3) + hello_data->session_id_len + challenge_len)) { 87 | *message = "SSL2 Client Hello record is too short"; 88 | return -1; 89 | } 90 | 91 | /* Copy the cipher specs */ 92 | if (memcpy(hello_data->cipher_specs, data_ptr, hello_data->cipher_specs_len * 3) != hello_data->cipher_specs) { 93 | *message = "SSL2 Client Hello record encountered memcpy error"; 94 | return -1; 95 | } 96 | CONSUME_BYTES(hello_data->cipher_specs_len * 3); 97 | 98 | /* Copy the session id */ 99 | if (memcpy(hello_data->session_id, data_ptr, hello_data->session_id_len) != hello_data->session_id) { 100 | *message = "SSL2 Client Hello record encountered memcpy error"; 101 | return -1; 102 | } 103 | CONSUME_BYTES(hello_data->session_id_len); 104 | 105 | /* Per Appendix E.2 of the TLS RFCs, the challenge should be padded with 106 | * leading zeroes. 107 | */ 108 | if (challenge_len < 32) { 109 | if (memset(hello_data->challenge, 0, 32 - challenge_len) != hello_data->challenge) { 110 | *message = "SSL2 Client Hello record encountered memset error"; 111 | return -1; 112 | } 113 | } 114 | 115 | /* Copy the challenge */ 116 | if (memcpy(hello_data->challenge + (32 - challenge_len), data_ptr, challenge_len) != 117 | hello_data->challenge + (32 - challenge_len)) { 118 | *message = "SSL2 Client Hello record encountered memcpy error"; 119 | return -1; 120 | } 121 | CONSUME_BYTES(challenge_len); 122 | 123 | /* The SSL2 Draft does not make it clear if the SSL record can or should 124 | * not contain any extraneous data. 125 | */ 126 | 127 | return 0; 128 | } 129 | -------------------------------------------------------------------------------- /ssl_client_hello_write.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ssl_client_hello.h" 4 | 5 | /* Consume a byte from the data stream 6 | */ 7 | #define WROTE_BYTES( len ) do { if (data_len < len) { *message = "Client hello is too long for buffer"; return -1; } data_ptr += (len); data_len -= (len); } while(0); 8 | #define WROTE_BYTE() WROTE_BYTES( 1 ) 9 | 10 | /* 11 | * Parse an SSL2 Client Hello message 12 | * 13 | * Parameters: 14 | * hello_data Pointer to the hello data structure to be filled 15 | * data_ptr pointer to the data stream to parse 16 | * data_len how much data is there to parse 17 | * message if we encounter an error, will be updated to point to 18 | * an error message 19 | * 20 | * Return: 21 | * >=0 The number of bytes written 22 | * -1 A problem was encountered parsing the client hello 23 | */ 24 | int ssl2_client_hello_write(struct ssl2_client_hello_data *hello_data, uint8_t * data_ptr, uint32_t data_len, char **message) 25 | { 26 | uint16_t challenge_len; 27 | 28 | if (hello_data == NULL) { 29 | *message = "NULL hello_data passed to ssl2_client_hello_write"; 30 | return -1; 31 | } 32 | 33 | *data_ptr = SSL_CLIENT_HELLO; 34 | WROTE_BYTE(); 35 | 36 | *data_ptr = hello_data->client_version[0]; 37 | WROTE_BYTE(); 38 | 39 | *data_ptr = hello_data->client_version[1]; 40 | WROTE_BYTE(); 41 | 42 | hello_data->cipher_specs_len = (*data_ptr << 8); 43 | WROTE_BYTE(); 44 | 45 | hello_data->cipher_specs_len |= *data_ptr; 46 | WROTE_BYTE(); 47 | 48 | if (hello_data->cipher_specs_len % 3) { 49 | *message = "SSL2 Client Hello record has invalid cipher spec length"; 50 | return -1; 51 | } 52 | 53 | if (hello_data->cipher_specs_len > MAX_CIPHER_SPECS_LEN) { 54 | *message = "SSL2 Client Hello record has too large a cipher spec length"; 55 | return -1; 56 | } 57 | 58 | hello_data->session_id_len = (*data_ptr << 8); 59 | WROTE_BYTE(); 60 | 61 | hello_data->session_id_len |= *data_ptr; 62 | WROTE_BYTE(); 63 | 64 | if (hello_data->session_id_len != 0 && hello_data->session_id_len != 16) { 65 | *message = "SSL2 Client Hello record has invalid session id length"; 66 | return -1; 67 | } 68 | 69 | challenge_len = (*data_ptr << 8); 70 | WROTE_BYTE(); 71 | 72 | challenge_len |= *data_ptr; 73 | WROTE_BYTE(); 74 | 75 | if (challenge_len < 16 || challenge_len > 32) { 76 | *message = "SSL2 Client Hello record has invalid session id length"; 77 | return -1; 78 | } 79 | 80 | /* Check the record is long enough to contain the data it claims to 81 | * contain 82 | */ 83 | 84 | if (data_len < ((hello_data->cipher_specs_len * 3) + hello_data->session_id_len + challenge_len)) { 85 | *message = "SSL2 Client Hello record is too short"; 86 | return -1; 87 | } 88 | 89 | /* Copy the cipher specs */ 90 | if (memcpy(hello_data->cipher_specs, data_ptr, hello_data->cipher_specs_len * 3) != hello_data->cipher_specs) { 91 | *message = "SSL2 Client Hello record encountered memcpy error"; 92 | return -1; 93 | } 94 | WROTE_BYTES(hello_data->cipher_specs_len * 3); 95 | 96 | /* Copy the session id */ 97 | if (memcpy(hello_data->session_id, data_ptr, hello_data->session_id_len) != hello_data->session_id) { 98 | *message = "SSL2 Client Hello record encountered memcpy error"; 99 | return -1; 100 | } 101 | WROTE_BYTES(hello_data->session_id_len); 102 | 103 | /* Per Appendix E.2 of the TLS RFCs, the challenge should be padded with 104 | * leading zeroes. 105 | */ 106 | if (challenge_len < 32) { 107 | if (memset(hello_data->challenge, 0, 32 - challenge_len) != hello_data->challenge) { 108 | *message = "SSL2 Client Hello record encountered memset error"; 109 | return -1; 110 | } 111 | } 112 | 113 | /* Copy the challenge */ 114 | if (memcpy(hello_data->challenge + (32 - challenge_len), data_ptr, challenge_len) != 115 | hello_data->challenge + (32 - challenge_len)) { 116 | *message = "SSL2 Client Hello record encountered memcpy error"; 117 | return -1; 118 | } 119 | WROTE_BYTES(challenge_len); 120 | 121 | /* The SSL2 Draft does not make it clear if the SSL record can or should 122 | * not contain any extraneous data. 123 | */ 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /tls_ssl_record_parser.c: -------------------------------------------------------------------------------- 1 | /* Read this header for a description of the state machine */ 2 | #include "tls_ssl_record_parser.h" 3 | 4 | /* Access a member of the state structure, either for the direction we're 5 | * handling, or for the other (peer) direction. 6 | */ 7 | #define STATE_MEMBER( member ) tls_state->member[direction] 8 | #define PEER_STATE_MEMBER( member ) tls_state->member[!direction] 9 | 10 | /* Update the state machine 11 | */ 12 | #define STATE_UPDATE( state ) tls_state->tls_processing_state[direction] = (state) 13 | 14 | /* Consume a byte from the data stream 15 | */ 16 | #define CONSUME_BYTES( len ) data_ptr += (len); data_len -= (len); 17 | #define CONSUME_BYTE() CONSUME_BYTES( 1 ) 18 | 19 | /* 20 | * Parse an SSL/TLS record stream 21 | * 22 | * Parameters: 23 | * tls_state tracks the TLS/SSL state machine 24 | * tls_parser_config configuration paramaeters 25 | * direction either CLIENT_TO_SERVER, or SERVER_TO_CLIENT 26 | * data_ptr pointer to the data stream to parse 27 | * data_len how much data is there to parse 28 | * message if we encounter an error, will be updated to point to 29 | * an error message 30 | * 31 | * Return: 32 | * 0 No problem encountered 33 | * -1 A problem was encountered handling the record stream 34 | */ 35 | int tls_ssl2_record_parser(struct tls_state *tls_state, struct tls_parser_config *tls_parser_config, 36 | int direction, const uint8_t * data_ptr, uint32_t data_len, char **message) 37 | { 38 | while (data_len) { 39 | int i, amount_to_read; 40 | 41 | switch (tls_state->tls_processing_state[direction]) { 42 | 43 | case EXPECTING_NEXT_RECORD: 44 | /* Reset the header */ 45 | STATE_MEMBER(record_header_read) = 0; 46 | 47 | /* We now have the first byte of a new record, but we need to figure out 48 | * if it's an SSL2 or TLS record. If either of the first two bits are set, 49 | * then it's SSL2. Otherwise we'll treat it as TLS. 50 | */ 51 | if (*data_ptr & 0x80) { 52 | STATE_UPDATE(READING_SSL_RECORD_HEADER); 53 | continue; 54 | } else if (*data_ptr & 0x40) { 55 | STATE_UPDATE(READING_SSL_PADDED_RECORD_HEADER); 56 | continue; 57 | } else if (*data_ptr < MINIMUM_TLS_CONTENT_TYPE || *data_ptr > MAXIMUM_TLS_CONTENT_TYPE) { 58 | *message = "invalid record type encountered"; 59 | return -1; 60 | } 61 | 62 | /* This is a TLS record */ 63 | STATE_UPDATE(READING_TLS_RECORD_HEADER); 64 | continue; 65 | 66 | case READING_SSL_RECORD_HEADER: 67 | STATE_MEMBER(record_header)[STATE_MEMBER(record_header_read)] = *data_ptr; 68 | STATE_MEMBER(record_header_read) += 1; 69 | CONSUME_BYTE(); 70 | 71 | /* Two byte length, plus the record type */ 72 | if (3 > STATE_MEMBER(record_header_read)) { 73 | continue; 74 | } 75 | 76 | STATE_MEMBER(record_length) = 77 | ((STATE_MEMBER(record_header)[0] & 0x7f) << 8) | STATE_MEMBER(record_header)[1]; 78 | 79 | /* One byte is consumed already (the record type) */ 80 | STATE_MEMBER(record_length_remaining) = STATE_MEMBER(record_length) - 1; 81 | STATE_MEMBER(record_type) = STATE_MEMBER(record_header)[2]; 82 | 83 | if (0 == STATE_MEMBER(record_length)) { 84 | *message = "0 length SSL record detected"; 85 | return -1; 86 | } 87 | 88 | switch (STATE_MEMBER(record_type)) { 89 | case SSL_CLIENT_HELLO: 90 | if (direction != CLIENT_TO_SERVER) { 91 | *message = "Client Hello in wrong direction"; 92 | return -1; 93 | } 94 | STATE_UPDATE(READING_CLEAR_RECORD); 95 | break; 96 | case SSL_SERVER_HELLO: 97 | if (direction != SERVER_TO_CLIENT) { 98 | *message = "Server Hello in wrong direction"; 99 | return -1; 100 | } 101 | STATE_UPDATE(READING_CLEAR_RECORD); 102 | break; 103 | case SSL_CLIENT_MASTER_KEY: 104 | if (direction != CLIENT_TO_SERVER) { 105 | *message = "Client master key in wrong direction"; 106 | return -1; 107 | } 108 | STATE_UPDATE(READING_CLEAR_RECORD); 109 | break; 110 | default: 111 | STATE_UPDATE(READING_ENCRYPTED_RECORD); 112 | break; 113 | } 114 | 115 | continue; 116 | 117 | case READING_SSL_PADDED_RECORD_HEADER: 118 | STATE_MEMBER(record_header)[STATE_MEMBER(record_header_read)] = *data_ptr; 119 | STATE_MEMBER(record_header_read) += 1; 120 | CONSUME_BYTE(); 121 | 122 | /* Three byte length, plus the message type */ 123 | if (4 > STATE_MEMBER(record_header_read)) { 124 | continue; 125 | } 126 | 127 | STATE_MEMBER(record_length) = 128 | ((STATE_MEMBER(record_header)[0] & 0x3f) << 8) | STATE_MEMBER(record_header)[1]; 129 | 130 | /* Add the padding length to the record length */ 131 | STATE_MEMBER(record_length) += STATE_MEMBER(record_header)[2]; 132 | 133 | /* One byte is consumed already (the record type) */ 134 | STATE_MEMBER(record_length_remaining) = STATE_MEMBER(record_length) - 1; 135 | STATE_MEMBER(record_type) = STATE_MEMBER(record_header)[3]; 136 | 137 | if (0 == STATE_MEMBER(record_length)) { 138 | *message = "0 length SSL record detected"; 139 | return -1; 140 | } 141 | 142 | STATE_UPDATE(READING_ENCRYPTED_RECORD); 143 | 144 | continue; 145 | 146 | case READING_TLS_RECORD_HEADER: 147 | STATE_MEMBER(record_header)[STATE_MEMBER(record_header_read)] = *data_ptr; 148 | STATE_MEMBER(record_header_read) += 1; 149 | CONSUME_BYTE(); 150 | 151 | /* One byte record type, 2 byte version code, 2 byte length code */ 152 | if (5 > STATE_MEMBER(record_header_read)) { 153 | continue; 154 | } 155 | 156 | STATE_MEMBER(record_length) = 157 | STATE_MEMBER(record_header)[3] << 8 | STATE_MEMBER(record_header)[4]; 158 | STATE_MEMBER(record_length_remaining) = STATE_MEMBER(record_length); 159 | STATE_MEMBER(record_type) = STATE_MEMBER(record_header)[0]; 160 | 161 | switch (STATE_MEMBER(record_type)) { 162 | case HEARTBEAT_TLS_CONTENT_TYPE: 163 | /* Check for a minimum length */ 164 | if (HEARTBEAT_TLS_MINIMUM_SIZE > STATE_MEMBER(record_length)) { 165 | *message = "Invalid (too short) Heartbeat message detected"; 166 | return -1; 167 | } 168 | 169 | /* Check if a heartbeat response was pending */ 170 | if (STATE_MEMBER(heartbeat_response_length_pending)) { 171 | 172 | /* If so, do the lengths match? */ 173 | if (STATE_MEMBER(record_length) != 174 | STATE_MEMBER(heartbeat_response_length_pending)) { 175 | *message = "Heartbleed response detected"; 176 | return -1; 177 | } 178 | 179 | /* Reset */ 180 | STATE_MEMBER(heartbeat_response_length_pending) = 0; 181 | } else { 182 | /* No response was pending, this is a request. Set 183 | * a response pending on the peer state. 184 | */ 185 | PEER_STATE_MEMBER(heartbeat_response_length_pending) = 186 | STATE_MEMBER(record_length);; 187 | } 188 | 189 | /* Fall through */ 190 | case HANDSHAKE_TLS_CONTENT_TYPE: 191 | STATE_UPDATE(READING_CLEAR_RECORD); 192 | break; 193 | 194 | default: 195 | STATE_UPDATE(READING_ENCRYPTED_RECORD); 196 | break; 197 | } 198 | 199 | continue; 200 | 201 | case READING_ENCRYPTED_RECORD: 202 | if (!tls_parser_config->max_low_bytes_sequence_length) { 203 | STATE_UPDATE(READING_CLEAR_RECORD); 204 | continue; 205 | } 206 | 207 | amount_to_read = STATE_MEMBER(record_length_remaining); 208 | if (amount_to_read > data_len) { 209 | amount_to_read = data_len; 210 | } 211 | 212 | /* Perform our simple run-length test */ 213 | for (i = 0; i < amount_to_read; i++) { 214 | if (data_ptr[i] & (1 << 7)) { 215 | STATE_MEMBER(low_bytes_sequence_length) = 0; 216 | } else { 217 | STATE_MEMBER(low_bytes_sequence_length)++; 218 | 219 | if (STATE_MEMBER(low_bytes_sequence_length) > 220 | tls_parser_config->max_low_bytes_sequence_length) { 221 | *message = "Data smuggling detected"; 222 | return -1; 223 | } 224 | } 225 | } 226 | 227 | /* Fall through ... */ 228 | 229 | case READING_CLEAR_RECORD: 230 | amount_to_read = STATE_MEMBER(record_length_remaining); 231 | if (amount_to_read > data_len) { 232 | amount_to_read = data_len; 233 | } 234 | 235 | STATE_MEMBER(record_length_remaining) -= amount_to_read; 236 | CONSUME_BYTES(amount_to_read); 237 | 238 | /* If we reached the end, look for the next record */ 239 | if (0 == STATE_MEMBER(record_length_remaining)) { 240 | STATE_UPDATE(EXPECTING_NEXT_RECORD); 241 | } 242 | 243 | continue; 244 | 245 | default: 246 | *message = "Invalid record parsing state reached"; 247 | return -1; 248 | } 249 | } 250 | 251 | return 0; 252 | } 253 | -------------------------------------------------------------------------------- /tls_ssl_record_parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __KERNEL__ 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | /* Per http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml */ 10 | #define MINIMUM_TLS_CONTENT_TYPE 20 11 | #define MAXIMUM_TLS_CONTENT_TYPE 24 12 | #define HANDSHAKE_TLS_CONTENT_TYPE 22 13 | #define HEARTBEAT_TLS_CONTENT_TYPE 24 14 | #define HEARTBEAT_TLS_MINIMUM_SIZE 19 15 | 16 | /* Per http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html C.2 */ 17 | #define SSL_CLIENT_HELLO 1 18 | #define SSL_SERVER_HELLO 4 19 | #define SSL_CLIENT_MASTER_KEY 2 20 | 21 | /* 22 | * The outermost layer of an TLS connection consists of a sequence 23 | * of records, one after another. Each record includes a length field, 24 | * so we know when to expect the next record. 25 | * 26 | * An SSL2 connection is a little different, it has its own, incompatible, 27 | * record layer. So we need to support both. 28 | * 29 | * A TLS/SSL record is described in RFC5246 and looks like; 30 | * 31 | * Byte 0: The SSL record type 32 | * Byte 1-2: SSL version 33 | * Byte 3-4: The length of the data 34 | * Byte 5-N+5: That data 35 | * 36 | * An SSL2 record is described in: 37 | * http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html 38 | * and is modal. The record format is a little different, depending on whether 39 | * the first two bits are set. 40 | * 41 | * If 0x80 is set in the first byte, then: 42 | * 43 | * Bytes 0-1: The length of the record (sort of) 44 | * Bytes 2-N+2: The record data 45 | * 46 | * If 0x80 is not set and 0x40 is set, then the format is: 47 | * 48 | * Bytes 0-1: The length of the record (sort of) 49 | * Byte 2: Length of the padding data 50 | * Bytes 3-N+3: The record data 51 | * Bytes N+3+P: The padding data 52 | * 53 | * Why "sort of"? In SSL2 We have to mask out the first bit, if it is set, or 54 | * first two bits if the first bit is not set, to arrive at the real length. 55 | * 56 | * It's also valid for clients to negotiate upwards, e.g. from SSL2 to TLS, so 57 | * both kinds of records may occur on the same session. 58 | * 59 | * Since any of these fields could occur right at a packet boundary, or even 60 | * split across two packets, we use a small state machine to record where we 61 | * are in the record stream handling. 62 | * 63 | * In this module, those states are; 64 | * 65 | * EXPECTING_NEXT_RECORD, 66 | * READING_TLS_RECORD_HEADER, 67 | * READING_SSL_RECORD_HEADER, 68 | * READING_SSL_PADDED_RECORD_HEADER, 69 | * READING_TLS_HEARTBEAT_RECORD, 70 | * READING_ENCRYPTED_RECORD, 71 | * READING_OPAQUE_RECORD 72 | * 73 | * SSL2 records go through the following states transitions: 74 | * 75 | * EXPECTING_NEXT_RECORD 76 | * READING_SSL_RECORD_HEADER | READING_SSL_PADDED_RECORD_HEADER 77 | * READING_ENCRYPTED_RECORD | READING_OPAQUE_RECORD 78 | * 79 | * TLS records go through these state transitions: 80 | * 81 | * EXPECTING_NEXT_RECORD 82 | * EXPECTING_TLS_RECORD_HEADER 83 | * READING_ENCRYPTED_RECORD | READING_OPAQUE_RECORD 84 | * 85 | * We have opaque and clear records. If the record type is opaque, it should 86 | * look random, and we check to see if it looks suspicuous. The clear records 87 | * we expect to contain plaintext, and give them a pass. Additionally we also 88 | * handle the heartbeat record type explicitly, and check for symmetry 89 | * between heartbeast requests and responses. 90 | * 91 | * One more note: There can be more than one "inner" messages per "outer" 92 | * record, but a single inner message may also span multiple outer messages, 93 | * making it neccessary to couple inner and outer state machines in an 94 | * unnatural way. 95 | */ 96 | struct tls_state { 97 | 98 | /* The state machine tracks both sides of a connection. 99 | * Each element is a 2-sized array, and the relevant 100 | * indexes are ... 101 | */ 102 | #define CLIENT_TO_SERVER 0 103 | #define SERVER_TO_CLIENT 1 104 | 105 | /* Tracks the state for the outermost part of the stream */ 106 | enum { EXPECTING_NEXT_RECORD, 107 | READING_TLS_RECORD_HEADER, 108 | READING_SSL_RECORD_HEADER, 109 | READING_SSL_PADDED_RECORD_HEADER, 110 | READING_ENCRYPTED_RECORD, 111 | READING_CLEAR_RECORD 112 | } tls_processing_state[2]; 113 | 114 | /* Record header, enough for a TLS header */ 115 | uint8_t record_header[2][5]; 116 | 117 | /* How much of the record header have we read? */ 118 | uint8_t record_header_read[2]; 119 | 120 | /* The type of the tls record we are processing */ 121 | uint8_t record_type[2]; 122 | 123 | /* The length of the TLS record we are processing */ 124 | uint16_t record_length[2]; 125 | 126 | /* The length remaining to be read */ 127 | uint16_t record_length_remaining[2]; 128 | 129 | /* Rolling count of bytes seen which don't have (1 << 7) 130 | * set. 131 | */ 132 | uint16_t low_bytes_sequence_length[2]; 133 | 134 | /* If a heartbeat request was sent on one side of a 135 | * a connection then a response of the same length 136 | * is expected on the other side. 137 | */ 138 | uint16_t heartbeat_response_length_pending[2]; 139 | }; 140 | 141 | struct tls_parser_config { 142 | /* What is the maximum number of low-order bytes we can tolerate */ 143 | uint16_t max_low_bytes_sequence_length; 144 | }; 145 | 146 | /* 147 | * Parse an SSL/TLS record stream 148 | * 149 | * Parameters: 150 | * tls_state tracks the TLS/SSL state machine 151 | * tls_parser_config configuration paramaeters 152 | * direction either CLIENT_TO_SERVER, or SERVER_TO_CLIENT 153 | * data_ptr pointer to the data stream to parse 154 | * data_len how much data is there to parse 155 | * message if we encounter an error, will be updated to point to 156 | * an error message 157 | * 158 | * Return: 159 | * 0 No problem encountered 160 | * -1 A problem was encountered handling the record stream 161 | */ 162 | extern int tls_ssl2_record_parser(struct tls_state *tls_state, struct tls_parser_config *config, 163 | int direction, const uint8_t * data_ptr, uint32_t data_len, char **message); 164 | --------------------------------------------------------------------------------