]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libpttls/pt_tls_server.c
Fixed some typos, courtesy of codespell
[thirdparty/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 uint32_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 uint32_t vendor, type, identifier;
75 uint8_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 uint32_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 uint32_t vendor, type, identifier;
264 sasl_mechanism_t *sasl;
265 bio_reader_t *reader;
266 chunk_t chunk;
267 uint8_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 empty 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 status_t assess(private_pt_tls_server_t *this, tls_t *tnccs)
404 {
405 size_t msglen;
406 size_t buflen = PT_TLS_MAX_MESSAGE_LEN;
407 char buf[buflen];
408 bio_reader_t *reader;
409 uint32_t vendor, type, identifier;
410 chunk_t data;
411 status_t status;
412
413 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
414 if (!reader)
415 {
416 return FAILED;
417 }
418 if (vendor == 0)
419 {
420 if (type == PT_TLS_ERROR)
421 {
422 DBG1(DBG_TNC, "received PT-TLS error");
423 reader->destroy(reader);
424 return FAILED;
425 }
426 if (type != PT_TLS_PB_TNC_BATCH)
427 {
428 DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type);
429 reader->destroy(reader);
430 return FAILED;
431 }
432 data = reader->peek(reader);
433 switch (tnccs->process(tnccs, data.ptr, data.len))
434 {
435 case SUCCESS:
436 reader->destroy(reader);
437 return tnccs->is_complete(tnccs) ? SUCCESS : FAILED;
438 case FAILED:
439 default:
440 reader->destroy(reader);
441 return FALSE;
442 case NEED_MORE:
443 break;
444 }
445 }
446 else
447 {
448 DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message");
449 }
450 reader->destroy(reader);
451
452 status = tnccs->build(tnccs, buf, &buflen, &msglen);
453 if (status == ALREADY_DONE)
454 {
455 data = chunk_create(buf, buflen);
456 if (!pt_tls_write(this->tls, PT_TLS_PB_TNC_BATCH,
457 this->identifier++, data))
458 {
459 return FAILED;
460 }
461 }
462 return status;
463 }
464
465 METHOD(pt_tls_server_t, handle, status_t,
466 private_pt_tls_server_t *this)
467 {
468 switch (this->state)
469 {
470 case PT_TLS_SERVER_VERSION:
471 DBG1(DBG_TNC, "entering PT-TLS negotiation phase");
472 if (!negotiate_version(this))
473 {
474 return FAILED;
475 }
476 DBG1(DBG_TNC, "negotiated PT-TLS version %d", PT_TLS_VERSION);
477 this->state = PT_TLS_SERVER_AUTH;
478 /* fall through to next state */
479 case PT_TLS_SERVER_AUTH:
480 DBG1(DBG_TNC, "doing SASL client authentication");
481 if (!authenticate(this))
482 {
483 return FAILED;
484 }
485 this->state = PT_TLS_SERVER_TNCCS;
486 DBG1(DBG_TNC, "entering PT-TLS data transport phase");
487 break;
488 case PT_TLS_SERVER_TNCCS:
489 switch (assess(this, (tls_t*)this->tnccs))
490 {
491 case SUCCESS:
492 this->state = PT_TLS_SERVER_END;
493 return SUCCESS;
494 case FAILED:
495 return FAILED;
496 default:
497 break;
498 }
499 break;
500 default:
501 return FAILED;
502 }
503 return NEED_MORE;
504 }
505
506 METHOD(pt_tls_server_t, get_fd, int,
507 private_pt_tls_server_t *this)
508 {
509 return this->tls->get_fd(this->tls);
510 }
511
512 METHOD(pt_tls_server_t, destroy, void,
513 private_pt_tls_server_t *this)
514 {
515 this->tnccs->destroy(this->tnccs);
516 this->tls->destroy(this->tls);
517 free(this);
518 }
519
520 /**
521 * See header
522 */
523 pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd,
524 pt_tls_auth_t auth, tnccs_t *tnccs)
525 {
526 private_pt_tls_server_t *this;
527
528 INIT(this,
529 .public = {
530 .handle = _handle,
531 .get_fd = _get_fd,
532 .destroy = _destroy,
533 },
534 .state = PT_TLS_SERVER_VERSION,
535 .tls = tls_socket_create(TRUE, server, NULL, fd, NULL, TLS_1_2, FALSE),
536 .tnccs = (tls_t*)tnccs,
537 .auth = auth,
538 );
539
540 if (!this->tls)
541 {
542 this->tnccs->destroy(this->tnccs);
543 free(this);
544 return NULL;
545 }
546
547 return &this->public;
548 }