Optimized PT-TLS data transfer
[strongswan.git] / src / libpttls / pt_tls_server.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 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 "pt_tls_server.h"
17
18 #include <sasl/sasl_mechanism.h>
19
20 #include <utils/debug.h>
21
22 typedef struct private_pt_tls_server_t private_pt_tls_server_t;
23
24 /**
25 * Private data of an pt_tls_server_t object.
26 */
27 struct private_pt_tls_server_t {
28
29 /**
30 * Public pt_tls_server_t interface.
31 */
32 pt_tls_server_t public;
33
34 /**
35 * TLS protected socket
36 */
37 tls_socket_t *tls;
38
39 /**
40 * Client authentication requirements
41 */
42 pt_tls_auth_t auth;
43
44 enum {
45 /* expecting version negotiation */
46 PT_TLS_SERVER_VERSION,
47 /* expecting an SASL exchange */
48 PT_TLS_SERVER_AUTH,
49 /* expecting TNCCS exchange */
50 PT_TLS_SERVER_TNCCS,
51 /* terminating state */
52 PT_TLS_SERVER_END,
53 } state;
54
55 /**
56 * Message Identifier
57 */
58 u_int32_t identifier;
59
60 /**
61 * TNCCS protocol handler, implemented as tls_t
62 */
63 tls_t *tnccs;
64
65 };
66
67 /**
68 * Negotiate PT-TLS version
69 */
70 static bool negotiate_version(private_pt_tls_server_t *this)
71 {
72 bio_reader_t *reader;
73 bio_writer_t *writer;
74 u_int32_t vendor, type, identifier;
75 u_int8_t reserved, vmin, vmax, vpref;
76 bool res;
77
78 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
79 if (!reader)
80 {
81 return FALSE;
82 }
83 if (vendor != 0 || type != PT_TLS_VERSION_REQUEST ||
84 !reader->read_uint8(reader, &reserved) ||
85 !reader->read_uint8(reader, &vmin) ||
86 !reader->read_uint8(reader, &vmax) ||
87 !reader->read_uint8(reader, &vpref))
88 {
89 DBG1(DBG_TNC, "PT-TLS version negotiation failed");
90 reader->destroy(reader);
91 return FALSE;
92 }
93 reader->destroy(reader);
94
95 if (vmin > PT_TLS_VERSION || vmax < PT_TLS_VERSION)
96 {
97 /* TODO: send error */
98 return FALSE;
99 }
100
101 writer = bio_writer_create(4);
102 writer->write_uint24(writer, 0);
103 writer->write_uint8(writer, PT_TLS_VERSION);
104 res = pt_tls_write(this->tls, PT_TLS_VERSION_RESPONSE,
105 this->identifier++, writer->get_buf(writer));
106 writer->destroy(writer);
107 return res;
108 }
109
110 /**
111 * Process SASL data, send result
112 */
113 static status_t process_sasl(private_pt_tls_server_t *this,
114 sasl_mechanism_t *sasl, chunk_t data)
115 {
116 bio_writer_t *writer;
117 identification_t *client;
118 tnccs_t *tnccs;
119 bool res;
120
121 switch (sasl->process(sasl, data))
122 {
123 case NEED_MORE:
124 return NEED_MORE;
125 case SUCCESS:
126 DBG1(DBG_TNC, "SASL %s authentication successful",
127 sasl->get_name(sasl));
128 client = sasl->get_client(sasl);
129 if (client)
130 {
131 DBG1(DBG_TNC, "SASL client identity is '%Y'", client);
132 this->tnccs->set_peer_id(this->tnccs, client);
133 if (streq(sasl->get_name(sasl), "PLAIN"))
134 {
135 tnccs = (tnccs_t*)this->tnccs;
136 tnccs->set_auth_type(tnccs, TNC_AUTH_PASSWORD);
137 }
138 }
139 writer = bio_writer_create(1);
140 writer->write_uint8(writer, PT_TLS_SASL_RESULT_SUCCESS);
141 res = pt_tls_write(this->tls, PT_TLS_SASL_RESULT,
142 this->identifier++, writer->get_buf(writer));
143 writer->destroy(writer);
144 return res ? SUCCESS : FAILED;
145 case FAILED:
146 default:
147 DBG1(DBG_TNC, "SASL %s authentication failed",
148 sasl->get_name(sasl));
149 writer = bio_writer_create(1);
150 /* sending abort does not allow the client to retry */
151 writer->write_uint8(writer, PT_TLS_SASL_RESULT_ABORT);
152 pt_tls_write(this->tls, PT_TLS_SASL_RESULT,
153 this->identifier++, writer->get_buf(writer));
154 return FAILED;
155 }
156 }
157
158 /**
159 * Read a SASL message and process it
160 */
161 static status_t read_sasl(private_pt_tls_server_t *this,
162 sasl_mechanism_t *sasl)
163 {
164 u_int32_t vendor, type, identifier;
165 bio_reader_t *reader;
166 status_t status;
167 chunk_t data;
168
169 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
170 if (!reader)
171 {
172 return FAILED;
173 }
174 if (vendor != 0 || type != PT_TLS_SASL_AUTH_DATA ||
175 !reader->read_data(reader, reader->remaining(reader), &data))
176 {
177 reader->destroy(reader);
178 return FAILED;
179 }
180 status = process_sasl(this, sasl, data);
181 reader->destroy(reader);
182 return status;
183 }
184
185 /**
186 * Build and write SASL message, or result message
187 */
188 static status_t write_sasl(private_pt_tls_server_t *this,
189 sasl_mechanism_t *sasl)
190 {
191 bio_writer_t *writer;
192 chunk_t chunk;
193 bool res;
194
195 switch (sasl->build(sasl, &chunk))
196 {
197 case NEED_MORE:
198 res = pt_tls_write(this->tls, PT_TLS_SASL_AUTH_DATA,
199 this->identifier++, chunk);
200 free(chunk.ptr);
201 return res ? NEED_MORE : FAILED;
202 case SUCCESS:
203 DBG1(DBG_TNC, "SASL %s authentication successful",
204 sasl->get_name(sasl));
205 writer = bio_writer_create(1 + chunk.len);
206 writer->write_uint8(writer, PT_TLS_SASL_RESULT_SUCCESS);
207 writer->write_data(writer, chunk);
208 free(chunk.ptr);
209 res = pt_tls_write(this->tls, PT_TLS_SASL_RESULT,
210 this->identifier++, writer->get_buf(writer));
211 writer->destroy(writer);
212 return res ? SUCCESS : FAILED;
213 case FAILED:
214 default:
215 DBG1(DBG_TNC, "SASL %s authentication failed",
216 sasl->get_name(sasl));
217 /* sending abort does not allow the client to retry */
218 chunk = chunk_from_chars(PT_TLS_SASL_RESULT_ABORT);
219 pt_tls_write(this->tls, PT_TLS_SASL_RESULT,
220 this->identifier++, chunk);
221 return FAILED;
222 }
223 }
224
225 /**
226 * Send the list of supported SASL mechanisms
227 */
228 static bool send_sasl_mechs(private_pt_tls_server_t *this)
229 {
230 enumerator_t *enumerator;
231 bio_writer_t *writer = NULL;
232 char *name;
233 bool res;
234
235 enumerator = sasl_mechanism_create_enumerator(TRUE);
236 while (enumerator->enumerate(enumerator, &name))
237 {
238 if (!writer)
239 {
240 writer = bio_writer_create(32);
241 }
242 DBG1(DBG_TNC, "offering SASL %s", name);
243 writer->write_data8(writer, chunk_from_str(name));
244 }
245 enumerator->destroy(enumerator);
246
247 if (!writer)
248 { /* no mechanisms available? */
249 return FALSE;
250 }
251 res = pt_tls_write(this->tls, PT_TLS_SASL_MECHS,
252 this->identifier++, writer->get_buf(writer));
253 writer->destroy(writer);
254 return res;
255 }
256
257 /**
258 * Read the selected SASL mechanism, and process piggybacked data
259 */
260 static status_t read_sasl_mech_selection(private_pt_tls_server_t *this,
261 sasl_mechanism_t **out)
262 {
263 u_int32_t vendor, type, identifier;
264 sasl_mechanism_t *sasl;
265 bio_reader_t *reader;
266 chunk_t chunk;
267 u_int8_t len;
268 char buf[21];
269
270 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
271 if (!reader)
272 {
273 return FAILED;
274 }
275 if (vendor != 0 || type != PT_TLS_SASL_MECH_SELECTION ||
276 !reader->read_uint8(reader, &len) ||
277 !reader->read_data(reader, len & 0x1F, &chunk))
278 {
279 reader->destroy(reader);
280 return FAILED;
281 }
282 snprintf(buf, sizeof(buf), "%.*s", (int)chunk.len, chunk.ptr);
283
284 DBG1(DBG_TNC, "client starts SASL %s authentication", buf);
285
286 sasl = sasl_mechanism_create(buf, NULL);
287 if (!sasl)
288 {
289 reader->destroy(reader);
290 return FAILED;
291 }
292 /* initial SASL data piggybacked? */
293 if (reader->remaining(reader))
294 {
295 switch (process_sasl(this, sasl, reader->peek(reader)))
296 {
297 case NEED_MORE:
298 break;
299 case SUCCESS:
300 reader->destroy(reader);
301 *out = sasl;
302 return SUCCESS;
303 case FAILED:
304 default:
305 reader->destroy(reader);
306 sasl->destroy(sasl);
307 return FAILED;
308 }
309 }
310 reader->destroy(reader);
311 *out = sasl;
312 return NEED_MORE;
313 }
314
315 /**
316 * Do a single SASL exchange
317 */
318 static bool do_sasl(private_pt_tls_server_t *this)
319 {
320 sasl_mechanism_t *sasl;
321 identification_t *client_id;
322 tnccs_t *tnccs;
323 status_t status;
324
325 client_id = this->tls->get_peer_id(this->tls);
326 tnccs = (tnccs_t*)this->tnccs;
327
328 switch (this->auth)
329 {
330 case PT_TLS_AUTH_NONE:
331 return TRUE;
332 case PT_TLS_AUTH_TLS:
333 if (client_id)
334 {
335 this->tnccs->set_peer_id(this->tnccs, client_id);
336 tnccs->set_auth_type(tnccs, TNC_AUTH_X509_CERT);
337 return TRUE;
338 }
339 DBG1(DBG_TNC, "requiring TLS certificate-based "
340 "client authentication");
341 return FALSE;
342 case PT_TLS_AUTH_SASL:
343 break;
344 case PT_TLS_AUTH_TLS_OR_SASL:
345 if (client_id)
346 {
347 this->tnccs->set_peer_id(this->tnccs, client_id);
348 tnccs->set_auth_type(tnccs, TNC_AUTH_X509_CERT);
349 DBG1(DBG_TNC, "skipping SASL, client already authenticated by "
350 "TLS certificate");
351 return TRUE;
352 }
353 break;
354 case PT_TLS_AUTH_TLS_AND_SASL:
355 default:
356 if (!client_id)
357 {
358 DBG1(DBG_TNC, "requiring TLS certificate-based "
359 "client authentication");
360 return FALSE;
361 }
362 break;
363 }
364
365 if (!send_sasl_mechs(this))
366 {
367 return FALSE;
368 }
369 status = read_sasl_mech_selection(this, &sasl);
370 if (status == FAILED)
371 {
372 return FALSE;
373 }
374 while (status == NEED_MORE)
375 {
376 status = write_sasl(this, sasl);
377 if (status == NEED_MORE)
378 {
379 status = read_sasl(this, sasl);
380 }
381 }
382 sasl->destroy(sasl);
383 return status == SUCCESS;
384 }
385
386 /**
387 * Authenticated PT-TLS session with a single SASL method
388 */
389 static bool authenticate(private_pt_tls_server_t *this)
390 {
391 if (do_sasl(this))
392 {
393 /* complete SASL with emtpy mechanism list */
394 return pt_tls_write(this->tls, PT_TLS_SASL_MECHS, this->identifier++,
395 chunk_empty);
396 }
397 return FALSE;
398 }
399
400 /**
401 * Perform assessment
402 */
403 static bool assess(private_pt_tls_server_t *this, tls_t *tnccs)
404 {
405 while (TRUE)
406 {
407 size_t msglen;
408 size_t buflen = PT_TLS_MAX_MESSAGE_LEN;
409 char buf[buflen];
410 bio_reader_t *reader;
411 u_int32_t vendor, type, identifier;
412 chunk_t data;
413
414 switch (tnccs->build(tnccs, buf, &buflen, &msglen))
415 {
416 case SUCCESS:
417 return tnccs->is_complete(tnccs);
418 case ALREADY_DONE:
419 data = chunk_create(buf, buflen);
420 if (!pt_tls_write(this->tls, PT_TLS_PB_TNC_BATCH,
421 this->identifier++, data))
422 {
423 return FALSE;
424 }
425 break;
426 case INVALID_STATE:
427 break;
428 case FAILED:
429 default:
430 return FALSE;
431 }
432
433 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
434 if (!reader)
435 {
436 return FALSE;
437 }
438 if (vendor == 0)
439 {
440 if (type == PT_TLS_ERROR)
441 {
442 DBG1(DBG_TNC, "received PT-TLS error");
443 reader->destroy(reader);
444 return FALSE;
445 }
446 if (type != PT_TLS_PB_TNC_BATCH)
447 {
448 DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type);
449 reader->destroy(reader);
450 return FALSE;
451 }
452 data = reader->peek(reader);
453 switch (tnccs->process(tnccs, data.ptr, data.len))
454 {
455 case SUCCESS:
456 reader->destroy(reader);
457 return tnccs->is_complete(tnccs);
458 case FAILED:
459 default:
460 reader->destroy(reader);
461 return FALSE;
462 case NEED_MORE:
463 break;
464 }
465 }
466 else
467 {
468 DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message");
469 }
470 reader->destroy(reader);
471 }
472 }
473
474 METHOD(pt_tls_server_t, handle, status_t,
475 private_pt_tls_server_t *this)
476 {
477 switch (this->state)
478 {
479 case PT_TLS_SERVER_VERSION:
480 DBG1(DBG_TNC, "entering PT-TLS negotiation phase");
481 if (!negotiate_version(this))
482 {
483 return FAILED;
484 }
485 DBG1(DBG_TNC, "negotiated PT-TLS version %d", PT_TLS_VERSION);
486 this->state = PT_TLS_SERVER_AUTH;
487 /* fall through to next state */
488 case PT_TLS_SERVER_AUTH:
489 DBG1(DBG_TNC, "doing SASL client authentication");
490 if (!authenticate(this))
491 {
492 return FAILED;
493 }
494 this->state = PT_TLS_SERVER_TNCCS;
495 break;
496 case PT_TLS_SERVER_TNCCS:
497 DBG1(DBG_TNC, "entering PT-TLS data transport phase");
498 if (!assess(this, (tls_t*)this->tnccs))
499 {
500 return FAILED;
501 }
502 this->state = PT_TLS_SERVER_END;
503 return SUCCESS;
504 default:
505 return FAILED;
506 }
507 return NEED_MORE;
508 }
509
510 METHOD(pt_tls_server_t, get_fd, int,
511 private_pt_tls_server_t *this)
512 {
513 return this->tls->get_fd(this->tls);
514 }
515
516 METHOD(pt_tls_server_t, destroy, void,
517 private_pt_tls_server_t *this)
518 {
519 this->tnccs->destroy(this->tnccs);
520 this->tls->destroy(this->tls);
521 free(this);
522 }
523
524 /**
525 * See header
526 */
527 pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd,
528 pt_tls_auth_t auth, tnccs_t *tnccs)
529 {
530 private_pt_tls_server_t *this;
531
532 INIT(this,
533 .public = {
534 .handle = _handle,
535 .get_fd = _get_fd,
536 .destroy = _destroy,
537 },
538 .state = PT_TLS_SERVER_VERSION,
539 .tls = tls_socket_create(TRUE, server, NULL, fd, NULL),
540 .tnccs = (tls_t*)tnccs,
541 .auth = auth,
542 );
543
544 if (!this->tls)
545 {
546 this->tnccs->destroy(this->tnccs);
547 free(this);
548 return NULL;
549 }
550
551 return &this->public;
552 }