2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
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>.
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
16 #include "pt_tls_server.h"
18 #include <sasl/sasl_mechanism.h>
20 #include <utils/debug.h>
22 typedef struct private_pt_tls_server_t private_pt_tls_server_t
;
25 * Private data of an pt_tls_server_t object.
27 struct private_pt_tls_server_t
{
30 * Public pt_tls_server_t interface.
32 pt_tls_server_t
public;
35 * TLS protected socket
40 * Client authentication requirements
45 /* expecting version negotiation */
46 PT_TLS_SERVER_VERSION
,
47 /* expecting an SASL exchange */
49 /* expecting TNCCS exchange */
51 /* terminating state */
61 * TNCCS protocol handler, implemented as tls_t
68 * Negotiate PT-TLS version
70 static bool negotiate_version(private_pt_tls_server_t
*this)
74 uint32_t vendor
, type
, identifier
;
75 uint8_t reserved
, vmin
, vmax
, vpref
;
78 reader
= pt_tls_read(this->tls
, &vendor
, &type
, &identifier
);
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
))
89 DBG1(DBG_TNC
, "PT-TLS version negotiation failed");
90 reader
->destroy(reader
);
93 reader
->destroy(reader
);
95 if (vmin
> PT_TLS_VERSION
|| vmax
< PT_TLS_VERSION
)
97 /* TODO: send error */
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
);
111 * Process SASL data, send result
113 static status_t
process_sasl(private_pt_tls_server_t
*this,
114 sasl_mechanism_t
*sasl
, chunk_t data
)
116 bio_writer_t
*writer
;
117 identification_t
*client
;
121 switch (sasl
->process(sasl
, data
))
126 DBG1(DBG_TNC
, "SASL %s authentication successful",
127 sasl
->get_name(sasl
));
128 client
= sasl
->get_client(sasl
);
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"))
135 tnccs
= (tnccs_t
*)this->tnccs
;
136 tnccs
->set_auth_type(tnccs
, TNC_AUTH_PASSWORD
);
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
;
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
));
159 * Read a SASL message and process it
161 static status_t
read_sasl(private_pt_tls_server_t
*this,
162 sasl_mechanism_t
*sasl
)
164 uint32_t vendor
, type
, identifier
;
165 bio_reader_t
*reader
;
169 reader
= pt_tls_read(this->tls
, &vendor
, &type
, &identifier
);
174 if (vendor
!= 0 || type
!= PT_TLS_SASL_AUTH_DATA
||
175 !reader
->read_data(reader
, reader
->remaining(reader
), &data
))
177 reader
->destroy(reader
);
180 status
= process_sasl(this, sasl
, data
);
181 reader
->destroy(reader
);
186 * Build and write SASL message, or result message
188 static status_t
write_sasl(private_pt_tls_server_t
*this,
189 sasl_mechanism_t
*sasl
)
191 bio_writer_t
*writer
;
195 switch (sasl
->build(sasl
, &chunk
))
198 res
= pt_tls_write(this->tls
, PT_TLS_SASL_AUTH_DATA
,
199 this->identifier
++, chunk
);
201 return res
? NEED_MORE
: FAILED
;
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
);
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
;
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
);
226 * Send the list of supported SASL mechanisms
228 static bool send_sasl_mechs(private_pt_tls_server_t
*this)
230 enumerator_t
*enumerator
;
231 bio_writer_t
*writer
= NULL
;
235 enumerator
= sasl_mechanism_create_enumerator(TRUE
);
236 while (enumerator
->enumerate(enumerator
, &name
))
240 writer
= bio_writer_create(32);
242 DBG1(DBG_TNC
, "offering SASL %s", name
);
243 writer
->write_data8(writer
, chunk_from_str(name
));
245 enumerator
->destroy(enumerator
);
248 { /* no mechanisms available? */
251 res
= pt_tls_write(this->tls
, PT_TLS_SASL_MECHS
,
252 this->identifier
++, writer
->get_buf(writer
));
253 writer
->destroy(writer
);
258 * Read the selected SASL mechanism, and process piggybacked data
260 static status_t
read_sasl_mech_selection(private_pt_tls_server_t
*this,
261 sasl_mechanism_t
**out
)
263 uint32_t vendor
, type
, identifier
;
264 sasl_mechanism_t
*sasl
;
265 bio_reader_t
*reader
;
270 reader
= pt_tls_read(this->tls
, &vendor
, &type
, &identifier
);
275 if (vendor
!= 0 || type
!= PT_TLS_SASL_MECH_SELECTION
||
276 !reader
->read_uint8(reader
, &len
) ||
277 !reader
->read_data(reader
, len
& 0x1F, &chunk
))
279 reader
->destroy(reader
);
282 snprintf(buf
, sizeof(buf
), "%.*s", (int)chunk
.len
, chunk
.ptr
);
284 DBG1(DBG_TNC
, "client starts SASL %s authentication", buf
);
286 sasl
= sasl_mechanism_create(buf
, NULL
);
289 reader
->destroy(reader
);
292 /* initial SASL data piggybacked? */
293 if (reader
->remaining(reader
))
295 switch (process_sasl(this, sasl
, reader
->peek(reader
)))
300 reader
->destroy(reader
);
305 reader
->destroy(reader
);
310 reader
->destroy(reader
);
316 * Do a single SASL exchange
318 static bool do_sasl(private_pt_tls_server_t
*this)
320 sasl_mechanism_t
*sasl
;
321 identification_t
*client_id
;
325 client_id
= this->tls
->get_peer_id(this->tls
);
326 tnccs
= (tnccs_t
*)this->tnccs
;
330 case PT_TLS_AUTH_NONE
:
332 case PT_TLS_AUTH_TLS
:
335 this->tnccs
->set_peer_id(this->tnccs
, client_id
);
336 tnccs
->set_auth_type(tnccs
, TNC_AUTH_X509_CERT
);
339 DBG1(DBG_TNC
, "requiring TLS certificate-based "
340 "client authentication");
342 case PT_TLS_AUTH_SASL
:
344 case PT_TLS_AUTH_TLS_OR_SASL
:
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 "
354 case PT_TLS_AUTH_TLS_AND_SASL
:
358 DBG1(DBG_TNC
, "requiring TLS certificate-based "
359 "client authentication");
365 if (!send_sasl_mechs(this))
369 status
= read_sasl_mech_selection(this, &sasl
);
370 if (status
== FAILED
)
374 while (status
== NEED_MORE
)
376 status
= write_sasl(this, sasl
);
377 if (status
== NEED_MORE
)
379 status
= read_sasl(this, sasl
);
383 return status
== SUCCESS
;
387 * Authenticated PT-TLS session with a single SASL method
389 static bool authenticate(private_pt_tls_server_t
*this)
393 /* complete SASL with empty mechanism list */
394 return pt_tls_write(this->tls
, PT_TLS_SASL_MECHS
, this->identifier
++,
403 static status_t
assess(private_pt_tls_server_t
*this, tls_t
*tnccs
)
406 size_t buflen
= PT_TLS_MAX_MESSAGE_LEN
;
408 bio_reader_t
*reader
;
409 uint32_t vendor
, type
, identifier
;
413 reader
= pt_tls_read(this->tls
, &vendor
, &type
, &identifier
);
420 if (type
== PT_TLS_ERROR
)
422 DBG1(DBG_TNC
, "received PT-TLS error");
423 reader
->destroy(reader
);
426 if (type
!= PT_TLS_PB_TNC_BATCH
)
428 DBG1(DBG_TNC
, "unexpected PT-TLS message: %d", type
);
429 reader
->destroy(reader
);
432 data
= reader
->peek(reader
);
433 switch (tnccs
->process(tnccs
, data
.ptr
, data
.len
))
436 reader
->destroy(reader
);
437 return tnccs
->is_complete(tnccs
) ? SUCCESS
: FAILED
;
440 reader
->destroy(reader
);
448 DBG1(DBG_TNC
, "ignoring vendor specific PT-TLS message");
450 reader
->destroy(reader
);
452 status
= tnccs
->build(tnccs
, buf
, &buflen
, &msglen
);
453 if (status
== ALREADY_DONE
)
455 data
= chunk_create(buf
, buflen
);
456 if (!pt_tls_write(this->tls
, PT_TLS_PB_TNC_BATCH
,
457 this->identifier
++, data
))
465 METHOD(pt_tls_server_t
, handle
, status_t
,
466 private_pt_tls_server_t
*this)
470 case PT_TLS_SERVER_VERSION
:
471 DBG1(DBG_TNC
, "entering PT-TLS negotiation phase");
472 if (!negotiate_version(this))
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))
485 this->state
= PT_TLS_SERVER_TNCCS
;
486 DBG1(DBG_TNC
, "entering PT-TLS data transport phase");
488 case PT_TLS_SERVER_TNCCS
:
489 switch (assess(this, (tls_t
*)this->tnccs
))
492 this->state
= PT_TLS_SERVER_END
;
506 METHOD(pt_tls_server_t
, get_fd
, int,
507 private_pt_tls_server_t
*this)
509 return this->tls
->get_fd(this->tls
);
512 METHOD(pt_tls_server_t
, destroy
, void,
513 private_pt_tls_server_t
*this)
515 this->tnccs
->destroy(this->tnccs
);
516 this->tls
->destroy(this->tls
);
523 pt_tls_server_t
*pt_tls_server_create(identification_t
*server
, int fd
,
524 pt_tls_auth_t auth
, tnccs_t
*tnccs
)
526 private_pt_tls_server_t
*this;
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
,
542 this->tnccs
->destroy(this->tnccs
);
547 return &this->public;