do not dump tls application data any more
[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 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 tls_handshake_type_t hs_type;
206 tls_writer_t *writer, *msg;
207 status_t status = INVALID_STATE;
208
209 if (this->handshake->cipherspec_changed(this->handshake))
210 {
211 *type = TLS_CHANGE_CIPHER_SPEC;
212 *data = chunk_clone(chunk_from_chars(0x01));
213 return NEED_MORE;
214 }
215
216 if (!this->output.len)
217 {
218 msg = tls_writer_create(64);
219
220 if (this->handshake->finished(this->handshake))
221 {
222 if (this->application)
223 {
224 status = this->application->build(this->application, msg);
225 if (status == INVALID_STATE)
226 {
227 *type = TLS_APPLICATION_DATA;
228 this->output = chunk_clone(msg->get_buf(msg));
229 if (this->output.len)
230 {
231 DBG2(DBG_IKE, "sending TLS application data");
232 }
233 }
234 }
235 }
236 else
237 {
238 do
239 {
240 writer = tls_writer_create(64);
241 status = this->handshake->build(this->handshake, &hs_type, writer);
242 switch (status)
243 {
244 case NEED_MORE:
245 DBG2(DBG_IKE, "sending TLS %N message",
246 tls_handshake_type_names, hs_type);
247 msg->write_uint8(msg, hs_type);
248 msg->write_data24(msg, writer->get_buf(writer));
249 break;
250 case INVALID_STATE:
251 *type = TLS_HANDSHAKE;
252 this->output = chunk_clone(msg->get_buf(msg));
253 break;
254 default:
255 break;
256 }
257 writer->destroy(writer);
258 }
259 while (status == NEED_MORE);
260 }
261
262 msg->destroy(msg);
263 if (status != INVALID_STATE)
264 {
265 return status;
266 }
267 }
268
269 if (this->output.len)
270 {
271 if (this->output.len <= MAX_TLS_FRAGMENT_LEN)
272 {
273 *data = this->output;
274 this->output = chunk_empty;
275 return NEED_MORE;
276 }
277 *data = chunk_create(this->output.ptr, MAX_TLS_FRAGMENT_LEN);
278 this->output = chunk_clone(chunk_skip(this->output, MAX_TLS_FRAGMENT_LEN));
279 return NEED_MORE;
280 }
281 return status;
282 }
283
284 METHOD(tls_fragmentation_t, destroy, void,
285 private_tls_fragmentation_t *this)
286 {
287 free(this->input.ptr);
288 free(this->output.ptr);
289 free(this);
290 }
291
292 /**
293 * See header
294 */
295 tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake,
296 tls_application_t *application)
297 {
298 private_tls_fragmentation_t *this;
299
300 INIT(this,
301 .public = {
302 .process = _process,
303 .build = _build,
304 .destroy = _destroy,
305 },
306 .handshake = handshake,
307 .application = application,
308 );
309
310 return &this->public;
311 }