Files: ca461ea077d748dde25eb6610e66514fa0151baf / tls.c
7427 bytesRaw
1 | /* |
2 | * Copyright (c) 2011 and 2012, Dustin Lundquist <dustin@null-ptr.net> |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions are met: |
7 | * |
8 | * 1. Redistributions of source code must retain the above copyright notice, |
9 | * this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
24 | * POSSIBILITY OF SUCH DAMAGE. |
25 | */ |
26 | /* |
27 | * This is a minimal TLS implementation intended only to parse the server name |
28 | * extension. This was created based primarily on Wireshark dissection of a |
29 | * TLS handshake and RFC4366. |
30 | */ |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | static int parse_extensions(const char *, size_t, char **); |
46 | static int parse_server_name_extension(const char *, size_t, char **); |
47 | |
48 | const char tls_alert[] = { |
49 | 0x15, /* TLS Alert */ |
50 | 0x03, 0x01, /* TLS version */ |
51 | 0x00, 0x02, /* Payload length */ |
52 | 0x02, 0x28, /* Fatal, handshake failure */ |
53 | }; |
54 | |
55 | /* Parse a TLS packet for the Server Name Indication extension in the client |
56 | * hello handshake, returning the first servername found (pointer to static |
57 | * array) |
58 | * |
59 | * Returns: |
60 | * >=0 - length of the hostname and updates *hostname |
61 | * caller is responsible for freeing *hostname |
62 | * -1 - Incomplete request |
63 | * -2 - No Host header included in this request |
64 | * -3 - Invalid hostname pointer |
65 | * -4 - malloc failure |
66 | * < -4 - Invalid TLS client hello |
67 | */ |
68 | int |
69 | parse_tls_header(const char *data, size_t data_len, char **hostname) { |
70 | char tls_content_type; |
71 | char tls_version_major; |
72 | char tls_version_minor; |
73 | size_t pos = TLS_HEADER_LEN; |
74 | size_t len; |
75 | |
76 | if (hostname == NULL) |
77 | return -3; |
78 | |
79 | /* Check that our TCP payload is at least large enough for a TLS header */ |
80 | if (data_len < TLS_HEADER_LEN) |
81 | return -1; |
82 | |
83 | /* SSL 2.0 compatible Client Hello |
84 | * |
85 | * High bit of first byte (length) and content type is Client Hello |
86 | * |
87 | * See RFC5246 Appendix E.2 |
88 | */ |
89 | if (data[0] & 0x80 && data[2] == 1) { |
90 | if (verbose) fprintf(stderr, "Received SSL 2.0 Client Hello which can not support SNI.\n"); |
91 | return -2; |
92 | } |
93 | |
94 | tls_content_type = data[0]; |
95 | if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { |
96 | if (verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n"); |
97 | return -5; |
98 | } |
99 | |
100 | tls_version_major = data[1]; |
101 | tls_version_minor = data[2]; |
102 | if (tls_version_major < 3) { |
103 | if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which which can not support SNI.\n", |
104 | tls_version_major, tls_version_minor); |
105 | |
106 | return -2; |
107 | } |
108 | |
109 | /* TLS record length */ |
110 | len = ((unsigned char)data[3] << 8) + |
111 | (unsigned char)data[4] + TLS_HEADER_LEN; |
112 | data_len = MIN(data_len, len); |
113 | |
114 | /* Check we received entire TLS record length */ |
115 | if (data_len < len) |
116 | return -1; |
117 | |
118 | /* |
119 | * Handshake |
120 | */ |
121 | if (pos + 1 > data_len) { |
122 | return -5; |
123 | } |
124 | if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { |
125 | if (verbose) fprintf(stderr, "Not a client hello\n"); |
126 | |
127 | return -5; |
128 | } |
129 | |
130 | /* Skip past fixed length records: |
131 | 1 Handshake Type |
132 | 3 Length |
133 | 2 Version (again) |
134 | 32 Random |
135 | to Session ID Length |
136 | */ |
137 | pos += 38; |
138 | |
139 | /* Session ID */ |
140 | if (pos + 1 > data_len) |
141 | return -5; |
142 | len = (unsigned char)data[pos]; |
143 | pos += 1 + len; |
144 | |
145 | /* Cipher Suites */ |
146 | if (pos + 2 > data_len) |
147 | return -5; |
148 | len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; |
149 | pos += 2 + len; |
150 | |
151 | /* Compression Methods */ |
152 | if (pos + 1 > data_len) |
153 | return -5; |
154 | len = (unsigned char)data[pos]; |
155 | pos += 1 + len; |
156 | |
157 | if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { |
158 | if (verbose) fprintf(stderr, "Received SSL 3.0 handshake without extensions\n"); |
159 | return -2; |
160 | } |
161 | |
162 | /* Extensions */ |
163 | if (pos + 2 > data_len) |
164 | return -5; |
165 | len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; |
166 | pos += 2; |
167 | |
168 | if (pos + len > data_len) |
169 | return -5; |
170 | return parse_extensions(data + pos, len, hostname); |
171 | } |
172 | |
173 | int |
174 | parse_extensions(const char *data, size_t data_len, char **hostname) { |
175 | size_t pos = 0; |
176 | size_t len; |
177 | |
178 | /* Parse each 4 bytes for the extension header */ |
179 | while (pos + 4 <= data_len) { |
180 | /* Extension Length */ |
181 | len = ((unsigned char)data[pos + 2] << 8) + |
182 | (unsigned char)data[pos + 3]; |
183 | |
184 | /* Check if it's a server name extension */ |
185 | if (data[pos] == 0x00 && data[pos + 1] == 0x00) { |
186 | /* There can be only one extension of each type, so we break |
187 | our state and move p to beinnging of the extension here */ |
188 | if (pos + 4 + len > data_len) |
189 | return -5; |
190 | return parse_server_name_extension(data + pos + 4, len, hostname); |
191 | } |
192 | pos += 4 + len; /* Advance to the next extension header */ |
193 | } |
194 | /* Check we ended where we expected to */ |
195 | if (pos != data_len) |
196 | return -5; |
197 | |
198 | return -2; |
199 | } |
200 | |
201 | int |
202 | parse_server_name_extension(const char *data, size_t data_len, |
203 | char **hostname) { |
204 | size_t pos = 2; /* skip server name list length */ |
205 | size_t len; |
206 | |
207 | while (pos + 3 < data_len) { |
208 | len = ((unsigned char)data[pos + 1] << 8) + |
209 | (unsigned char)data[pos + 2]; |
210 | |
211 | if (pos + 3 + len > data_len) |
212 | return -5; |
213 | |
214 | switch (data[pos]) { /* name type */ |
215 | case 0x00: /* host_name */ |
216 | *hostname = malloc(len + 1); |
217 | if (*hostname == NULL) { |
218 | if (verbose) fprintf(stderr, "malloc() failure\n"); |
219 | return -4; |
220 | } |
221 | |
222 | strncpy(*hostname, data + pos + 3, len); |
223 | |
224 | (*hostname)[len] = '\0'; |
225 | |
226 | return len; |
227 | default: |
228 | if (verbose) fprintf(stderr, "Unknown server name extension name type: %d\n", |
229 | data[pos]); |
230 | } |
231 | pos += 3 + len; |
232 | } |
233 | /* Check we ended where we expected to */ |
234 | if (pos != data_len) |
235 | return -5; |
236 | |
237 | return -2; |
238 | } |
239 |
Built with git-ssb-web