add TLS handshake packet size to debug output
[strongswan.git] / src / libtls / tls_fragmentation.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "tls_fragmentation.h"
17
18 #include "tls_reader.h"
19
20 #include <debug.h>
21
22 typedef struct private_tls_fragmentation_t private_tls_fragmentation_t;
23
24 /**
25 * Private data of an tls_fragmentation_t object.
26 */
27 struct private_tls_fragmentation_t {
28
29 /**
30 * Public tls_fragmentation_t interface.
31 */
32 tls_fragmentation_t public;
33
34 /**
35 * Upper layer handshake protocol
36 */
37 tls_handshake_t *handshake;
38
39 /**
40 * Handshake input buffer
41 */
42 chunk_t input;
43
44 /**
45 * Position in input buffer
46 */
47 size_t inpos;
48
49 /**
50 * Currently processed handshake message type
51 */
52 tls_handshake_type_t type;
53
54 /**
55 * Handshake output buffer
56 */
57 chunk_t output;
58
59 /**
60 * Upper layer application data protocol
61 */
62 tls_application_t *application;
63 };
64
65 /**
66 * Maximum size of a TLS fragment
67 */
68 #define MAX_TLS_FRAGMENT_LEN 16384
69
70 /**
71 * Maximum size of a TLS handshake message we accept
72 */
73 #define MAX_TLS_HANDSHAKE_LEN 65536
74
75 /**
76 * Process TLS handshake protocol data
77 */
78 static status_t process_handshake(private_tls_fragmentation_t *this,
79 tls_reader_t *reader)
80 {
81 while (reader->remaining(reader))
82 {
83 tls_reader_t *msg;
84 u_int8_t type;
85 u_int32_t len;
86 status_t status;
87 chunk_t data;
88
89 if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN)
90 {
91 DBG1(DBG_IKE, "TLS fragment has invalid length");
92 return FAILED;
93 }
94
95 if (this->input.len == 0)
96 { /* new handshake message */
97 if (!reader->read_uint8(reader, &type) ||
98 !reader->read_uint24(reader, &len))
99 {
100 return FAILED;
101 }
102 this->type = type;
103 if (len > MAX_TLS_HANDSHAKE_LEN)
104 {
105 DBG1(DBG_IKE, "TLS handshake message exceeds maximum length");
106 return FAILED;
107 }
108 chunk_free(&this->input);
109 this->inpos = 0;
110 if (len)
111 {
112 this->input = chunk_alloc(len);
113 }
114 }
115
116 len = min(this->input.len - this->inpos, reader->remaining(reader));
117 if (!reader->read_data(reader, len, &data))
118 {
119 return FAILED;
120 }
121 memcpy(this->input.ptr + this->inpos, data.ptr, len);
122 this->inpos += len;
123
124 if (this->input.len == this->inpos)
125 { /* message completely defragmented, process */
126 msg = tls_reader_create(this->input);
127 DBG2(DBG_IKE, "received TLS %N message (%u bytes)",
128 tls_handshake_type_names, this->type, 4 + this->input.len);
129 status = this->handshake->process(this->handshake, this->type, msg);
130 msg->destroy(msg);
131 chunk_free(&this->input);
132 if (status != NEED_MORE)
133 {
134 return status;
135 }
136 }
137 }
138 return NEED_MORE;
139 }
140
141 /**
142 * Process TLS application data
143 */
144 static status_t process_application(private_tls_fragmentation_t *this,
145 tls_reader_t *reader)
146 {
147 while (reader->remaining(reader))
148 {
149 status_t status;
150
151 if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN)
152 {
153 DBG1(DBG_IKE, "TLS fragment has invalid length");
154 return FAILED;
155 }
156 DBG2(DBG_IKE, "received TLS application data");
157 status = this->application->process(this->application, reader);
158 if (status != NEED_MORE)
159 {
160 return status;
161 }
162 }
163 return NEED_MORE;
164 }
165
166 METHOD(tls_fragmentation_t, process, status_t,
167 private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
168 {
169 tls_reader_t *reader;
170 status_t status;
171
172 reader = tls_reader_create(data);
173 switch (type)
174 {
175 case TLS_CHANGE_CIPHER_SPEC:
176 if (this->handshake->change_cipherspec(this->handshake))
177 {
178 status = NEED_MORE;
179 break;
180 }
181 status = FAILED;
182 break;
183 case TLS_ALERT:
184 /* TODO: handle Alert */
185 status = FAILED;
186 break;
187 case TLS_HANDSHAKE:
188 status = process_handshake(this, reader);
189 break;
190 case TLS_APPLICATION_DATA:
191 status = process_application(this, reader);
192 break;
193 default:
194 DBG1(DBG_IKE, "received unknown TLS content type %d, ignored", type);
195 status = NEED_MORE;
196 break;
197 }
198 reader->destroy(reader);
199 return status;
200 }
201
202 METHOD(tls_fragmentation_t, build, status_t,
203 private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
204 {
205 chunk_t hs_data;
206 tls_handshake_type_t hs_type;
207 tls_writer_t *writer, *msg;
208 status_t status = INVALID_STATE;
209
210 if (this->handshake->cipherspec_changed(this->handshake))
211 {
212 *type = TLS_CHANGE_CIPHER_SPEC;
213 *data = chunk_clone(chunk_from_chars(0x01));
214 return NEED_MORE;
215 }
216
217 if (!this->output.len)
218 {
219 msg = tls_writer_create(64);
220
221 if (this->handshake->finished(this->handshake))
222 {
223 if (this->application)
224 {
225 status = this->application->build(this->application, msg);
226 if (status == INVALID_STATE)
227 {
228 *type = TLS_APPLICATION_DATA;
229 this->output = chunk_clone(msg->get_buf(msg));
230 if (this->output.len)
231 {
232 DBG2(DBG_IKE, "sending TLS application data");
233 }
234 }
235 }
236 }
237 else
238 {
239 do
240 {
241 writer = tls_writer_create(64);
242 status = this->handshake->build(this->handshake, &hs_type, writer);
243 switch (status)
244 {
245 case NEED_MORE:
246 hs_data = writer->get_buf(writer);
247 msg->write_uint8(msg, hs_type);
248 msg->write_data24(msg, hs_data);
249 DBG2(DBG_IKE, "sending TLS %N message (%u bytes)",
250 tls_handshake_type_names, hs_type, 4 + hs_data.len);
251 break;
252 case INVALID_STATE:
253 *type = TLS_HANDSHAKE;
254 this->output = chunk_clone(msg->get_buf(msg));
255 break;
256 default:
257 break;
258 }
259 writer->destroy(writer);
260 }
261 while (status == NEED_MORE);
262 }
263
264 msg->destroy(msg);
265 if (status != INVALID_STATE)
266 {
267 return status;
268 }
269 }
270
271 if (this->output.len)
272 {
273 if (this->output.len <= MAX_TLS_FRAGMENT_LEN)
274 {
275 *data = this->output;
276 this->output = chunk_empty;
277 return NEED_MORE;
278 }
279 *data = chunk_create(this->output.ptr, MAX_TLS_FRAGMENT_LEN);
280 this->output = chunk_clone(chunk_skip(this->output, MAX_TLS_FRAGMENT_LEN));
281 return NEED_MORE;
282 }
283 return status;
284 }
285
286 METHOD(tls_fragmentation_t, destroy, void,
287 private_tls_fragmentation_t *this)
288 {
289 free(this->input.ptr);
290 free(this->output.ptr);
291 free(this);
292 }
293
294 /**
295 * See header
296 */
297 tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake,
298 tls_application_t *application)
299 {
300 private_tls_fragmentation_t *this;
301
302 INIT(this,
303 .public = {
304 .process = _process,
305 .build = _build,
306 .destroy = _destroy,
307 },
308 .handshake = handshake,
309 .application = application,
310 );
311
312 return &this->public;
313 }