git ssb

0+

cel / sslh



Tree: ca461ea077d748dde25eb6610e66514fa0151baf

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#include <stdio.h>
32#include <stdlib.h> /* malloc() */
33#include <string.h> /* strncpy() */
34#include <sys/socket.h>
35#include "tls.h"
36
37#define TLS_HEADER_LEN 5
38#define TLS_HANDSHAKE_CONTENT_TYPE 0x16
39#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01
40
41#ifndef MIN
42#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
43#endif
44
45static int parse_extensions(const char *, size_t, char **);
46static int parse_server_name_extension(const char *, size_t, char **);
47
48const 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 */
68int
69parse_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
173int
174parse_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
201int
202parse_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