Moved TLS stack to its own library
[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 /**
61 * Maximum size of a TLS fragment
62 */
63 #define MAX_TLS_FRAGMENT_LEN 16384
64
65 /**
66 * Maximum size of a TLS handshake message we accept
67 */
68 #define MAX_TLS_HANDSHAKE_LEN 65536
69
70 /**
71 * Process TLS handshake protocol data
72 */
73 static status_t process_handshake(private_tls_fragmentation_t *this,
74 tls_reader_t *reader)
75 {
76 while (reader->remaining(reader))
77 {
78 tls_reader_t *msg;
79 u_int8_t type;
80 u_int32_t len;
81 status_t status;
82 chunk_t data;
83
84 if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN)
85 {
86 DBG1(DBG_IKE, "TLS fragment has invalid length");
87 return FAILED;
88 }
89
90 if (this->input.len == 0)
91 { /* new handshake message */
92 if (!reader->read_uint8(reader, &type) ||
93 !reader->read_uint24(reader, &len))
94 {
95 return FAILED;
96 }
97 this->type = type;
98 if (len > MAX_TLS_HANDSHAKE_LEN)
99 {
100 DBG1(DBG_IKE, "TLS handshake message exceeds maximum length");
101 return FAILED;
102 }
103 chunk_free(&this->input);
104 this->inpos = 0;
105 if (len)
106 {
107 this->input = chunk_alloc(len);
108 }
109 }
110
111 len = min(this->input.len - this->inpos, reader->remaining(reader));
112 if (!reader->read_data(reader, len, &data))
113 {
114 return FAILED;
115 }
116 memcpy(this->input.ptr + this->inpos, data.ptr, len);
117 this->inpos += len;
118
119 if (this->input.len == this->inpos)
120 { /* message completely defragmented, process */
121 msg = tls_reader_create(this->input);
122 status = this->handshake->process(this->handshake, this->type, msg);
123 msg->destroy(msg);
124 chunk_free(&this->input);
125 if (status != NEED_MORE)
126 {
127 return status;
128 }
129 }
130 }
131 return NEED_MORE;
132 }
133
134 METHOD(tls_fragmentation_t, process, status_t,
135 private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
136 {
137 tls_reader_t *reader;
138 status_t status;
139
140 reader = tls_reader_create(data);
141 switch (type)
142 {
143 case TLS_CHANGE_CIPHER_SPEC:
144 if (this->handshake->change_cipherspec(this->handshake))
145 {
146 status = NEED_MORE;
147 break;
148 }
149 status = FAILED;
150 break;
151 case TLS_ALERT:
152 /* TODO: handle Alert */
153 status = FAILED;
154 break;
155 case TLS_HANDSHAKE:
156 status = process_handshake(this, reader);
157 break;
158 case TLS_APPLICATION_DATA:
159 /* skip application data */
160 status = NEED_MORE;
161 break;
162 default:
163 DBG1(DBG_IKE, "received unknown TLS content type %d, ignored", type);
164 status = NEED_MORE;
165 break;
166 }
167 reader->destroy(reader);
168 return status;
169 }
170
171 METHOD(tls_fragmentation_t, build, status_t,
172 private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
173 {
174 tls_handshake_type_t hs_type;
175 tls_writer_t *writer, *msg;
176 status_t status;
177
178 if (this->handshake->cipherspec_changed(this->handshake))
179 {
180 *type = TLS_CHANGE_CIPHER_SPEC;
181 *data = chunk_clone(chunk_from_chars(0x01));
182 return NEED_MORE;
183 }
184
185 if (!this->output.len)
186 {
187 msg = tls_writer_create(64);
188 do
189 {
190 writer = tls_writer_create(64);
191 status = this->handshake->build(this->handshake, &hs_type, writer);
192 switch (status)
193 {
194 case NEED_MORE:
195 msg->write_uint8(msg, hs_type);
196 msg->write_data24(msg, writer->get_buf(writer));
197 break;
198 case INVALID_STATE:
199 this->output = chunk_clone(msg->get_buf(msg));
200 break;
201 default:
202 break;
203 }
204 writer->destroy(writer);
205 }
206 while (status == NEED_MORE);
207
208 msg->destroy(msg);
209 if (status != INVALID_STATE)
210 {
211 return status;
212 }
213 }
214
215 if (this->output.len)
216 {
217 *type = TLS_HANDSHAKE;
218 if (this->output.len <= MAX_TLS_FRAGMENT_LEN)
219 {
220 *data = this->output;
221 this->output = chunk_empty;
222 return NEED_MORE;
223 }
224 *data = chunk_create(this->output.ptr, MAX_TLS_FRAGMENT_LEN);
225 this->output = chunk_clone(chunk_skip(this->output, MAX_TLS_FRAGMENT_LEN));
226 return NEED_MORE;
227 }
228 return status;
229 }
230
231 METHOD(tls_fragmentation_t, destroy, void,
232 private_tls_fragmentation_t *this)
233 {
234 free(this->input.ptr);
235 free(this->output.ptr);
236 free(this);
237 }
238
239 /**
240 * See header
241 */
242 tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake)
243 {
244 private_tls_fragmentation_t *this;
245
246 INIT(this,
247 .public = {
248 .process = _process,
249 .build = _build,
250 .destroy = _destroy,
251 },
252 .handshake = handshake,
253 );
254
255 return &this->public;
256 }