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