Implemented TLS Alert handling
[strongswan.git] / src / libtls / tls_protection.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_protection.h"
17
18 #include <debug.h>
19
20 typedef struct private_tls_protection_t private_tls_protection_t;
21
22 /**
23 * Private data of an tls_protection_t object.
24 */
25 struct private_tls_protection_t {
26
27 /**
28 * Public tls_protection_t interface.
29 */
30 tls_protection_t public;
31
32 /**
33 * negotiated TLS version
34 */
35 tls_version_t version;
36
37 /**
38 * Upper layer, TLS record compression
39 */
40 tls_compression_t *compression;
41
42 /**
43 * TLS alert handler
44 */
45 tls_alert_t *alert;
46
47 /**
48 * RNG if we generate IVs ourself
49 */
50 rng_t *rng;
51
52 /**
53 * Sequence number of incoming records
54 */
55 u_int32_t seq_in;
56
57 /**
58 * Sequence number for outgoing records
59 */
60 u_int32_t seq_out;
61
62 /**
63 * Signer instance for inbound traffic
64 */
65 signer_t *signer_in;
66
67 /**
68 * Signer instance for outbound traffic
69 */
70 signer_t *signer_out;
71
72 /**
73 * Crypter instance for inbound traffic
74 */
75 crypter_t *crypter_in;
76
77 /**
78 * Crypter instance for outbound traffic
79 */
80 crypter_t *crypter_out;
81
82 /**
83 * Current IV for input decryption
84 */
85 chunk_t iv_in;
86
87 /**
88 * Current IV for output decryption
89 */
90 chunk_t iv_out;
91 };
92
93 /**
94 * Create the header to append to the record data to create the MAC
95 */
96 static chunk_t sigheader(u_int32_t seq, u_int8_t type,
97 u_int16_t version, u_int16_t length)
98 {
99 /* we only support 32 bit sequence numbers, but TLS uses 64 bit */
100 u_int32_t seq_high = 0;
101
102 seq = htonl(seq);
103 version = htons(version);
104 length = htons(length);
105
106 return chunk_cat("ccccc", chunk_from_thing(seq_high),
107 chunk_from_thing(seq), chunk_from_thing(type),
108 chunk_from_thing(version), chunk_from_thing(length));
109 }
110
111 METHOD(tls_protection_t, process, status_t,
112 private_tls_protection_t *this, tls_content_type_t type, chunk_t data)
113 {
114 if (this->alert->fatal(this->alert))
115 { /* don't accept more input, fatal error ocurred */
116 return NEED_MORE;
117 }
118
119 if (this->crypter_in)
120 {
121 chunk_t iv, next_iv = chunk_empty;
122 u_int8_t bs, padding_length;
123
124 bs = this->crypter_in->get_block_size(this->crypter_in);
125 if (this->iv_in.len)
126 { /* < TLSv1.1 uses IV from key derivation/last block */
127 if (data.len < bs || data.len % bs)
128 {
129 DBG1(DBG_TLS, "encrypted TLS record length invalid");
130 this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
131 return NEED_MORE;
132 }
133 iv = this->iv_in;
134 next_iv = chunk_clone(chunk_create(data.ptr + data.len - bs, bs));
135 }
136 else
137 { /* TLSv1.1 uses random IVs, prepended to record */
138 iv.len = this->crypter_in->get_iv_size(this->crypter_in);
139 iv = chunk_create(data.ptr, iv.len);
140 data = chunk_skip(data, iv.len);
141 if (data.len < bs || data.len % bs)
142 {
143 DBG1(DBG_TLS, "encrypted TLS record length invalid");
144 this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
145 return NEED_MORE;
146 }
147 }
148 this->crypter_in->decrypt(this->crypter_in, data, iv, NULL);
149
150 if (next_iv.len)
151 { /* next record IV is last ciphertext block of this record */
152 memcpy(this->iv_in.ptr, next_iv.ptr, next_iv.len);
153 free(next_iv.ptr);
154 }
155
156 padding_length = data.ptr[data.len - 1];
157 if (padding_length >= data.len)
158 {
159 DBG1(DBG_TLS, "invalid TLS record padding");
160 this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
161 return NEED_MORE;
162 }
163 data.len -= padding_length + 1;
164 }
165 if (this->signer_in)
166 {
167 chunk_t mac, macdata, header;
168 u_int8_t bs;
169
170 bs = this->signer_in->get_block_size(this->signer_in);
171 if (data.len <= bs)
172 {
173 DBG1(DBG_TLS, "TLS record too short to verify MAC");
174 this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
175 return NEED_MORE;
176 }
177 mac = chunk_skip(data, data.len - bs);
178 data.len -= bs;
179
180 header = sigheader(this->seq_in, type, this->version, data.len);
181 macdata = chunk_cat("mc", header, data);
182 if (!this->signer_in->verify_signature(this->signer_in, macdata, mac))
183 {
184 DBG1(DBG_TLS, "TLS record MAC verification failed");
185 free(macdata.ptr);
186 this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
187 return NEED_MORE;
188 }
189 free(macdata.ptr);
190 }
191
192 if (type == TLS_CHANGE_CIPHER_SPEC)
193 {
194 this->seq_in = 0;
195 }
196 else
197 {
198 this->seq_in++;
199 }
200 return this->compression->process(this->compression, type, data);
201 }
202
203 METHOD(tls_protection_t, build, status_t,
204 private_tls_protection_t *this, tls_content_type_t *type, chunk_t *data)
205 {
206 status_t status;
207
208 status = this->compression->build(this->compression, type, data);
209 if (*type == TLS_CHANGE_CIPHER_SPEC)
210 {
211 this->seq_out = 0;
212 return status;
213 }
214
215 if (status == NEED_MORE)
216 {
217 if (this->signer_out)
218 {
219 chunk_t mac, header;
220
221 header = sigheader(this->seq_out, *type, this->version, data->len);
222 this->signer_out->get_signature(this->signer_out, header, NULL);
223 free(header.ptr);
224 this->signer_out->allocate_signature(this->signer_out, *data, &mac);
225 if (this->crypter_out)
226 {
227 chunk_t padding, iv;
228 u_int8_t bs, padding_length;
229
230 bs = this->crypter_out->get_block_size(this->crypter_out);
231 padding_length = bs - ((data->len + mac.len + 1) % bs);
232
233 padding = chunk_alloca(padding_length);
234 memset(padding.ptr, padding_length, padding.len);
235
236 if (this->iv_out.len)
237 { /* < TLSv1.1 uses IV from key derivation/last block */
238 iv = this->iv_out;
239 }
240 else
241 { /* TLSv1.1 uses random IVs, prepended to record */
242 if (!this->rng)
243 {
244 DBG1(DBG_TLS, "no RNG supported to generate TLS IV");
245 free(data->ptr);
246 return FAILED;
247 }
248 iv.len = this->crypter_out->get_iv_size(this->crypter_out);
249 this->rng->allocate_bytes(this->rng, iv.len, &iv);
250 }
251
252 *data = chunk_cat("mmcc", *data, mac, padding,
253 chunk_from_thing(padding_length));
254 /* encrypt inline */
255 this->crypter_out->encrypt(this->crypter_out, *data, iv, NULL);
256
257 if (this->iv_out.len)
258 { /* next record IV is last ciphertext block of this record */
259 memcpy(this->iv_out.ptr, data->ptr + data->len -
260 this->iv_out.len, this->iv_out.len);
261 }
262 else
263 { /* prepend IV */
264 *data = chunk_cat("mm", iv, *data);
265 }
266 }
267 else
268 { /* NULL encryption */
269 *data = chunk_cat("mm", *data, mac);
270 }
271 }
272 this->seq_out++;
273 }
274 return status;
275 }
276
277 METHOD(tls_protection_t, set_cipher, void,
278 private_tls_protection_t *this, bool inbound, signer_t *signer,
279 crypter_t *crypter, chunk_t iv)
280 {
281 if (inbound)
282 {
283 this->signer_in = signer;
284 this->crypter_in = crypter;
285 this->iv_in = iv;
286 }
287 else
288 {
289 this->signer_out = signer;
290 this->crypter_out = crypter;
291 this->iv_out = iv;
292 if (!iv.len)
293 { /* generate IVs if none given */
294 this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
295 }
296 }
297 }
298
299 METHOD(tls_protection_t, set_version, void,
300 private_tls_protection_t *this, tls_version_t version)
301 {
302 this->version = version;
303 }
304
305 METHOD(tls_protection_t, destroy, void,
306 private_tls_protection_t *this)
307 {
308 DESTROY_IF(this->rng);
309 free(this);
310 }
311
312 /**
313 * See header
314 */
315 tls_protection_t *tls_protection_create(tls_compression_t *compression,
316 tls_alert_t *alert)
317 {
318 private_tls_protection_t *this;
319
320 INIT(this,
321 .public = {
322 .process = _process,
323 .build = _build,
324 .set_cipher = _set_cipher,
325 .set_version = _set_version,
326 .destroy = _destroy,
327 },
328 .alert = alert,
329 .compression = compression,
330 );
331
332 return &this->public;
333 }