564864937383532df51b114d585157acecc4af07
[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",
128 tls_handshake_type_names, this->type);
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 u_int32_t len;
150 chunk_t data;
151
152 if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN)
153 {
154 DBG1(DBG_IKE, "TLS fragment has invalid length");
155 return FAILED;
156 }
157
158 len = reader->remaining(reader);
159 if (!reader->read_data(reader, len, &data))
160 {
161 return FAILED;
162 }
163 DBG1(DBG_IKE, "received TLS application data: %B", &data);
164 }
165 return NEED_MORE;
166 }
167
168 METHOD(tls_fragmentation_t, process, status_t,
169 private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
170 {
171 tls_reader_t *reader;
172 status_t status;
173
174 reader = tls_reader_create(data);
175 switch (type)
176 {
177 case TLS_CHANGE_CIPHER_SPEC:
178 if (this->handshake->change_cipherspec(this->handshake))
179 {
180 status = NEED_MORE;
181 break;
182 }
183 status = FAILED;
184 break;
185 case TLS_ALERT:
186 /* TODO: handle Alert */
187 status = FAILED;
188 break;
189 case TLS_HANDSHAKE:
190 status = process_handshake(this, reader);
191 break;
192 case TLS_APPLICATION_DATA:
193 status = process_application(this, reader);
194 break;
195 default:
196 DBG1(DBG_IKE, "received unknown TLS content type %d, ignored", type);
197 status = NEED_MORE;
198 break;
199 }
200 reader->destroy(reader);
201 return status;
202 }
203
204 METHOD(tls_fragmentation_t, build, status_t,
205 private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
206 {
207 tls_handshake_type_t hs_type;
208 tls_writer_t *writer, *msg;
209 status_t status = INVALID_STATE;
210
211 if (this->handshake->cipherspec_changed(this->handshake))
212 {
213 *type = TLS_CHANGE_CIPHER_SPEC;
214 *data = chunk_clone(chunk_from_chars(0x01));
215 return NEED_MORE;
216 }
217
218 if (!this->output.len)
219 {
220 msg = tls_writer_create(64);
221
222 if (this->handshake->finished(this->handshake))
223 {
224 if (this->application)
225 {
226 status = this->application->build(this->application, msg);
227 if (status == INVALID_STATE)
228 {
229 *type = TLS_APPLICATION_DATA;
230 this->output = chunk_clone(msg->get_buf(msg));
231 if (this->output.len)
232 {
233 DBG2(DBG_IKE, "sending TLS application data: %B",
234 &this->output);
235 }
236 }
237 }
238 }
239 else
240 {
241 do
242 {
243 writer = tls_writer_create(64);
244 status = this->handshake->build(this->handshake, &hs_type, writer);
245 switch (status)
246 {
247 case NEED_MORE:
248 DBG2(DBG_IKE, "sending TLS %N message",
249 tls_handshake_type_names, hs_type);
250 msg->write_uint8(msg, hs_type);
251 msg->write_data24(msg, writer->get_buf(writer));
252 break;
253 case INVALID_STATE:
254 *type = TLS_HANDSHAKE;
255 this->output = chunk_clone(msg->get_buf(msg));
256 break;
257 default:
258 break;
259 }
260 writer->destroy(writer);
261 }
262 while (status == NEED_MORE);
263 }
264
265 msg->destroy(msg);
266 if (status != INVALID_STATE)
267 {
268 return status;
269 }
270 }
271
272 if (this->output.len)
273 {
274 if (this->output.len <= MAX_TLS_FRAGMENT_LEN)
275 {
276 *data = this->output;
277 this->output = chunk_empty;
278 return NEED_MORE;
279 }
280 *data = chunk_create(this->output.ptr, MAX_TLS_FRAGMENT_LEN);
281 this->output = chunk_clone(chunk_skip(this->output, MAX_TLS_FRAGMENT_LEN));
282 return NEED_MORE;
283 }
284 return status;
285 }
286
287 METHOD(tls_fragmentation_t, destroy, void,
288 private_tls_fragmentation_t *this)
289 {
290 free(this->input.ptr);
291 free(this->output.ptr);
292 free(this);
293 }
294
295 /**
296 * See header
297 */
298 tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake,
299 tls_application_t *application)
300 {
301 private_tls_fragmentation_t *this;
302
303 INIT(this,
304 .public = {
305 .process = _process,
306 .build = _build,
307 .destroy = _destroy,
308 },
309 .handshake = handshake,
310 .application = application,
311 );
312
313 return &this->public;
314 }