Added some debug statements
[strongswan.git] / src / libpttls / pt_tls_client.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_client.h"
17 #include "pt_tls.h"
18
19 #include <sasl/sasl_mechanism.h>
20
21 #include <tls_socket.h>
22 #include <utils/debug.h>
23
24 #include <errno.h>
25 #include <stdio.h>
26 #include <unistd.h>
27
28 typedef struct private_pt_tls_client_t private_pt_tls_client_t;
29
30 /**
31 * Private data of an pt_tls_client_t object.
32 */
33 struct private_pt_tls_client_t {
34
35 /**
36 * Public pt_tls_client_t interface.
37 */
38 pt_tls_client_t public;
39
40 /**
41 * TLS secured socket used by PT-TLS
42 */
43 tls_socket_t *tls;
44
45 /**
46 * Server address/port
47 */
48 host_t *address;
49
50 /**
51 * Server identity
52 */
53 identification_t *server;
54
55 /**
56 * Client authentication identity
57 */
58 identification_t *client;
59
60 /**
61 * Current PT-TLS message identifier
62 */
63 u_int32_t identifier;
64 };
65
66 /**
67 * Establish TLS secured TCP connection to TNC server
68 */
69 static bool make_connection(private_pt_tls_client_t *this)
70 {
71 int fd;
72
73 fd = socket(this->address->get_family(this->address), SOCK_STREAM, 0);
74 if (fd == -1)
75 {
76 DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
77 return FALSE;
78 }
79 if (connect(fd, this->address->get_sockaddr(this->address),
80 *this->address->get_sockaddr_len(this->address)) == -1)
81 {
82 DBG1(DBG_TNC, "connecting to PT-TLS server failed: %s", strerror(errno));
83 close(fd);
84 return FALSE;
85 }
86
87 this->tls = tls_socket_create(FALSE, this->server, this->client, fd, NULL);
88 if (!this->tls)
89 {
90 close(fd);
91 return FALSE;
92 }
93 return TRUE;
94 }
95
96 /**
97 * Negotiate PT-TLS version
98 */
99 static bool negotiate_version(private_pt_tls_client_t *this)
100 {
101 bio_writer_t *writer;
102 bio_reader_t *reader;
103 u_int32_t type, vendor, identifier, reserved;
104 u_int8_t version;
105
106 DBG1(DBG_TNC, "sending offer for PT-TLS version %d", PT_TLS_VERSION);
107
108 writer = bio_writer_create(4);
109 writer->write_uint8(writer, 0);
110 writer->write_uint8(writer, PT_TLS_VERSION);
111 writer->write_uint8(writer, PT_TLS_VERSION);
112 writer->write_uint8(writer, PT_TLS_VERSION);
113 if (!pt_tls_write(this->tls, writer, PT_TLS_VERSION_REQUEST,
114 this->identifier++))
115 {
116 return FALSE;
117 }
118
119 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
120 if (!reader)
121 {
122 return FALSE;
123 }
124 if (vendor != 0 || type != PT_TLS_VERSION_RESPONSE ||
125 !reader->read_uint24(reader, &reserved) ||
126 !reader->read_uint8(reader, &version) ||
127 version != PT_TLS_VERSION)
128 {
129 DBG1(DBG_TNC, "PT-TLS version negotiation failed");
130 reader->destroy(reader);
131 return FALSE;
132 }
133 reader->destroy(reader);
134 return TRUE;
135 }
136
137 /**
138 * Run a SASL mechanism
139 */
140 static status_t do_sasl(private_pt_tls_client_t *this, sasl_mechanism_t *sasl)
141 {
142 u_int32_t type, vendor, identifier;
143 u_int8_t result;
144 bio_reader_t *reader;
145 bio_writer_t *writer;
146 chunk_t data;
147
148 writer = bio_writer_create(32);
149 writer->write_data8(writer, chunk_from_str(sasl->get_name(sasl)));
150 switch (sasl->build(sasl, &data))
151 {
152 case INVALID_STATE:
153 break;
154 case NEED_MORE:
155 writer->write_data(writer, data);
156 free(data.ptr);
157 break;
158 case SUCCESS:
159 /* shouldn't happen */
160 free(data.ptr);
161 /* FALL */
162 case FAILED:
163 default:
164 writer->destroy(writer);
165 return FAILED;
166 }
167 if (!pt_tls_write(this->tls, writer, PT_TLS_SASL_MECH_SELECTION,
168 this->identifier++))
169 {
170 return FAILED;
171 }
172 while (TRUE)
173 {
174 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
175 if (!reader)
176 {
177 return FAILED;
178 }
179 if (vendor != 0)
180 {
181 reader->destroy(reader);
182 return FAILED;
183 }
184 switch (type)
185 {
186 case PT_TLS_SASL_AUTH_DATA:
187 switch (sasl->process(sasl, reader->peek(reader)))
188 {
189 case NEED_MORE:
190 reader->destroy(reader);
191 break;
192 case SUCCESS:
193 /* should not happen, as it would come in a RESULT */
194 case FAILED:
195 default:
196 reader->destroy(reader);
197 return FAILED;
198 }
199 break;
200 case PT_TLS_SASL_RESULT:
201 if (!reader->read_uint8(reader, &result))
202 {
203 reader->destroy(reader);
204 return FAILED;
205 }
206 DBG1(DBG_TNC, "received SASL %N result",
207 pt_tls_sasl_result_names, result);
208
209 switch (result)
210 {
211 case PT_TLS_SASL_RESULT_ABORT:
212 reader->destroy(reader);
213 return FAILED;
214 case PT_TLS_SASL_RESULT_SUCCESS:
215 switch (sasl->process(sasl, reader->peek(reader)))
216 {
217 case SUCCESS:
218 reader->destroy(reader);
219 return SUCCESS;
220 case NEED_MORE:
221 /* inacceptable, it won't get more. FALL */
222 case FAILED:
223 default:
224 reader->destroy(reader);
225 return FAILED;
226 }
227 break;
228 case PT_TLS_SASL_RESULT_MECH_FAILURE:
229 case PT_TLS_SASL_RESULT_FAILURE:
230 /* non-fatal failure, try again */
231 reader->destroy(reader);
232 return NEED_MORE;
233 }
234 /* fall-through */
235 default:
236 reader->destroy(reader);
237 return FAILED;
238 }
239
240 writer = bio_writer_create(32);
241 switch (sasl->build(sasl, &data))
242 {
243 case INVALID_STATE:
244 break;
245 case SUCCESS:
246 /* shoudln't happen, continue until we get a result */
247 case NEED_MORE:
248 writer->write_data(writer, data);
249 free(data.ptr);
250 break;
251 case FAILED:
252 default:
253 writer->destroy(writer);
254 return FAILED;
255 }
256 if (!pt_tls_write(this->tls, writer, PT_TLS_SASL_AUTH_DATA,
257 this->identifier++))
258 {
259 return FAILED;
260 }
261 }
262 }
263
264 /**
265 * Read SASL mechanism list, select and run mechanism
266 */
267 static status_t select_and_do_sasl(private_pt_tls_client_t *this)
268 {
269 bio_reader_t *reader;
270 sasl_mechanism_t *sasl = NULL;
271 u_int32_t type, vendor, identifier;
272 u_int8_t len;
273 chunk_t chunk;
274 char buf[21];
275 status_t status = NEED_MORE;
276
277 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
278 if (!reader)
279 {
280 return FAILED;
281 }
282 if (vendor != 0 || type != PT_TLS_SASL_MECHS)
283 {
284 reader->destroy(reader);
285 return FAILED;
286 }
287 if (!reader->remaining(reader))
288 { /* mechanism list empty, SASL completed */
289 DBG1(DBG_TNC, "PT-TLS authentication complete");
290 reader->destroy(reader);
291 return SUCCESS;
292 }
293 while (reader->remaining(reader))
294 {
295 if (!reader->read_uint8(reader, &len) ||
296 !reader->read_data(reader, len & 0x1F, &chunk))
297 {
298 reader->destroy(reader);
299 return FAILED;
300 }
301 snprintf(buf, sizeof(buf), "%.*s", (int)chunk.len, chunk.ptr);
302 sasl = sasl_mechanism_create(buf, this->client);
303 if (sasl)
304 {
305 break;
306 }
307 }
308 reader->destroy(reader);
309
310 if (!sasl)
311 {
312 /* TODO: send PT-TLS error (5) */
313 return FAILED;
314 }
315 while (status == NEED_MORE)
316 {
317 status = do_sasl(this, sasl);
318 }
319 sasl->destroy(sasl);
320 if (status == SUCCESS)
321 { /* continue until we receive empty SASL mechanism list */
322 return NEED_MORE;
323 }
324 return FAILED;
325 }
326
327 /**
328 * Authenticate session using SASL
329 */
330 static bool authenticate(private_pt_tls_client_t *this)
331 {
332 while (TRUE)
333 {
334 switch (select_and_do_sasl(this))
335 {
336 case NEED_MORE:
337 continue;
338 case SUCCESS:
339 return TRUE;
340 case FAILED:
341 default:
342 return FALSE;
343 }
344 }
345 }
346
347 /**
348 * Perform assessment
349 */
350 static bool assess(private_pt_tls_client_t *this, tls_t *tnccs)
351 {
352 while (TRUE)
353 {
354 bio_writer_t *writer;
355 bio_reader_t *reader;
356 u_int32_t vendor, type, identifier;
357 chunk_t data;
358
359 writer = bio_writer_create(32);
360 while (TRUE)
361 {
362 char buf[2048];
363 size_t buflen, msglen;
364
365 buflen = sizeof(buf);
366 switch (tnccs->build(tnccs, buf, &buflen, &msglen))
367 {
368 case SUCCESS:
369 writer->destroy(writer);
370 return tnccs->is_complete(tnccs);
371 case FAILED:
372 default:
373 writer->destroy(writer);
374 return FALSE;
375 case INVALID_STATE:
376 writer->destroy(writer);
377 break;
378 case NEED_MORE:
379 writer->write_data(writer, chunk_create(buf, buflen));
380 continue;
381 case ALREADY_DONE:
382 writer->write_data(writer, chunk_create(buf, buflen));
383 if (!pt_tls_write(this->tls, writer, PT_TLS_PB_TNC_BATCH,
384 this->identifier++))
385 {
386 return FALSE;
387 }
388 writer = bio_writer_create(32);
389 continue;
390 }
391 break;
392 }
393
394 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
395 if (!reader)
396 {
397 return FALSE;
398 }
399 if (vendor == 0)
400 {
401 if (type == PT_TLS_ERROR)
402 {
403 DBG1(DBG_TNC, "received PT-TLS error");
404 reader->destroy(reader);
405 return FALSE;
406 }
407 if (type != PT_TLS_PB_TNC_BATCH)
408 {
409 DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type);
410 reader->destroy(reader);
411 return FALSE;
412 }
413 data = reader->peek(reader);
414 switch (tnccs->process(tnccs, data.ptr, data.len))
415 {
416 case SUCCESS:
417 reader->destroy(reader);
418 return tnccs->is_complete(tnccs);
419 case FAILED:
420 default:
421 reader->destroy(reader);
422 return FALSE;
423 case NEED_MORE:
424 break;
425 }
426 }
427 else
428 {
429 DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message");
430 }
431 reader->destroy(reader);
432 }
433 }
434
435 METHOD(pt_tls_client_t, run_assessment, status_t,
436 private_pt_tls_client_t *this, tnccs_t *tnccs)
437 {
438 if (!this->tls)
439 {
440 DBG1(DBG_TNC, "entering PT-TLS setup phase");
441 if (!make_connection(this))
442 {
443 return FAILED;
444 }
445 }
446
447 DBG1(DBG_TNC, "entering PT-TLS negotiation phase");
448 if (!negotiate_version(this))
449 {
450 return FAILED;
451 }
452
453 DBG1(DBG_TNC, "doing SASL client authentication");
454 if (!authenticate(this))
455 {
456 return FAILED;
457 }
458
459 DBG1(DBG_TNC, "entering PT-TLS data transport phase");
460 if (!assess(this, (tls_t*)tnccs))
461 {
462 return FAILED;
463 }
464 return SUCCESS;
465 }
466
467
468 METHOD(pt_tls_client_t, destroy, void,
469 private_pt_tls_client_t *this)
470 {
471 if (this->tls)
472 {
473 int fd;
474
475 fd = this->tls->get_fd(this->tls);
476 this->tls->destroy(this->tls);
477 close(fd);
478 }
479 this->address->destroy(this->address);
480 this->server->destroy(this->server);
481 this->client->destroy(this->client);
482 free(this);
483 }
484
485 /**
486 * See header
487 */
488 pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *server,
489 identification_t *client)
490 {
491 private_pt_tls_client_t *this;
492
493 INIT(this,
494 .public = {
495 .run_assessment = _run_assessment,
496 .destroy = _destroy,
497 },
498 .address = address,
499 .server = server,
500 .client = client,
501 );
502
503 return &this->public;
504 }