123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- /*
- * Copyright (C) 2012 Martin Willi
- * Copyright (C) 2012 revosec AG
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
- #include "pt_tls_client.h"
- #include "pt_tls.h"
- #include <sasl/sasl_mechanism.h>
- #include <tls_socket.h>
- #include <utils/debug.h>
- #include <errno.h>
- #include <stdio.h>
- #include <unistd.h>
- typedef struct private_pt_tls_client_t private_pt_tls_client_t;
- /**
- * Private data of an pt_tls_client_t object.
- */
- struct private_pt_tls_client_t {
- /**
- * Public pt_tls_client_t interface.
- */
- pt_tls_client_t public;
- /**
- * TLS secured socket used by PT-TLS
- */
- tls_socket_t *tls;
- /**
- * Server address/port
- */
- host_t *address;
- /**
- * Server identity
- */
- identification_t *server;
- /**
- * Client authentication identity
- */
- identification_t *client;
- /**
- * Current PT-TLS message identifier
- */
- uint32_t identifier;
- };
- /**
- * Establish TLS secured TCP connection to TNC server
- */
- static bool make_connection(private_pt_tls_client_t *this)
- {
- int fd;
- fd = socket(this->address->get_family(this->address), SOCK_STREAM, 0);
- if (fd == -1)
- {
- DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
- return FALSE;
- }
- if (connect(fd, this->address->get_sockaddr(this->address),
- *this->address->get_sockaddr_len(this->address)) == -1)
- {
- DBG1(DBG_TNC, "connecting to PT-TLS server failed: %s", strerror(errno));
- close(fd);
- return FALSE;
- }
- this->tls = tls_socket_create(FALSE, this->server, this->client, fd,
- NULL, TLS_1_2, FALSE);
- if (!this->tls)
- {
- close(fd);
- return FALSE;
- }
- return TRUE;
- }
- /**
- * Negotiate PT-TLS version
- */
- static bool negotiate_version(private_pt_tls_client_t *this)
- {
- bio_writer_t *writer;
- bio_reader_t *reader;
- uint32_t type, vendor, identifier, reserved;
- uint8_t version;
- bool res;
- DBG1(DBG_TNC, "sending offer for PT-TLS version %d", PT_TLS_VERSION);
- writer = bio_writer_create(4);
- writer->write_uint8(writer, 0);
- writer->write_uint8(writer, PT_TLS_VERSION);
- writer->write_uint8(writer, PT_TLS_VERSION);
- writer->write_uint8(writer, PT_TLS_VERSION);
- res = pt_tls_write(this->tls, PT_TLS_VERSION_REQUEST, this->identifier++,
- writer->get_buf(writer));
- writer->destroy(writer);
- if (!res)
- {
- return FALSE;
- }
- reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
- if (!reader)
- {
- return FALSE;
- }
- if (vendor != 0 || type != PT_TLS_VERSION_RESPONSE ||
- !reader->read_uint24(reader, &reserved) ||
- !reader->read_uint8(reader, &version) ||
- version != PT_TLS_VERSION)
- {
- DBG1(DBG_TNC, "PT-TLS version negotiation failed");
- reader->destroy(reader);
- return FALSE;
- }
- reader->destroy(reader);
- return TRUE;
- }
- /**
- * Run a SASL mechanism
- */
- static status_t do_sasl(private_pt_tls_client_t *this, sasl_mechanism_t *sasl)
- {
- uint32_t type, vendor, identifier;
- uint8_t result;
- bio_reader_t *reader;
- bio_writer_t *writer;
- chunk_t data;
- bool res;
- writer = bio_writer_create(32);
- writer->write_data8(writer, chunk_from_str(sasl->get_name(sasl)));
- switch (sasl->build(sasl, &data))
- {
- case INVALID_STATE:
- break;
- case NEED_MORE:
- writer->write_data(writer, data);
- free(data.ptr);
- break;
- case SUCCESS:
- /* shouldn't happen */
- free(data.ptr);
- /* FALL */
- case FAILED:
- default:
- writer->destroy(writer);
- return FAILED;
- }
- res = pt_tls_write(this->tls, PT_TLS_SASL_MECH_SELECTION,
- this->identifier++, writer->get_buf(writer));
- writer->destroy(writer);
- if (!res)
- {
- return FAILED;
- }
- while (TRUE)
- {
- reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
- if (!reader)
- {
- return FAILED;
- }
- if (vendor != 0)
- {
- reader->destroy(reader);
- return FAILED;
- }
- switch (type)
- {
- case PT_TLS_SASL_AUTH_DATA:
- switch (sasl->process(sasl, reader->peek(reader)))
- {
- case NEED_MORE:
- reader->destroy(reader);
- break;
- case SUCCESS:
- /* should not happen, as it would come in a RESULT */
- case FAILED:
- default:
- reader->destroy(reader);
- return FAILED;
- }
- break;
- case PT_TLS_SASL_RESULT:
- if (!reader->read_uint8(reader, &result))
- {
- reader->destroy(reader);
- return FAILED;
- }
- DBG1(DBG_TNC, "received SASL %N result",
- pt_tls_sasl_result_names, result);
- switch (result)
- {
- case PT_TLS_SASL_RESULT_ABORT:
- reader->destroy(reader);
- return FAILED;
- case PT_TLS_SASL_RESULT_SUCCESS:
- switch (sasl->process(sasl, reader->peek(reader)))
- {
- case SUCCESS:
- reader->destroy(reader);
- return SUCCESS;
- case NEED_MORE:
- /* unacceptable, it won't get more. FALL */
- case FAILED:
- default:
- reader->destroy(reader);
- return FAILED;
- }
- case PT_TLS_SASL_RESULT_MECH_FAILURE:
- case PT_TLS_SASL_RESULT_FAILURE:
- /* non-fatal failure, try again */
- reader->destroy(reader);
- return NEED_MORE;
- }
- /* fall-through */
- default:
- reader->destroy(reader);
- return FAILED;
- }
- writer = bio_writer_create(32);
- switch (sasl->build(sasl, &data))
- {
- case INVALID_STATE:
- break;
- case SUCCESS:
- /* shouldn't happen, continue until we get a result */
- case NEED_MORE:
- writer->write_data(writer, data);
- free(data.ptr);
- break;
- case FAILED:
- default:
- writer->destroy(writer);
- return FAILED;
- }
- res = pt_tls_write(this->tls, PT_TLS_SASL_AUTH_DATA,
- this->identifier++, writer->get_buf(writer));
- writer->destroy(writer);
- if (!res)
- {
- return FAILED;
- }
- }
- }
- /**
- * Read SASL mechanism list, select and run mechanism
- */
- static status_t select_and_do_sasl(private_pt_tls_client_t *this)
- {
- bio_reader_t *reader;
- sasl_mechanism_t *sasl = NULL;
- uint32_t type, vendor, identifier;
- uint8_t len;
- chunk_t chunk;
- char buf[21];
- status_t status = NEED_MORE;
- reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
- if (!reader)
- {
- return FAILED;
- }
- if (vendor != 0 || type != PT_TLS_SASL_MECHS)
- {
- reader->destroy(reader);
- return FAILED;
- }
- if (!reader->remaining(reader))
- { /* mechanism list empty, SASL completed */
- DBG1(DBG_TNC, "PT-TLS authentication complete");
- reader->destroy(reader);
- return SUCCESS;
- }
- while (reader->remaining(reader))
- {
- if (!reader->read_uint8(reader, &len) ||
- !reader->read_data(reader, len & 0x1F, &chunk))
- {
- reader->destroy(reader);
- return FAILED;
- }
- snprintf(buf, sizeof(buf), "%.*s", (int)chunk.len, chunk.ptr);
- sasl = sasl_mechanism_create(buf, this->client);
- if (sasl)
- {
- break;
- }
- }
- reader->destroy(reader);
- if (!sasl)
- {
- /* TODO: send PT-TLS error (5) */
- return FAILED;
- }
- while (status == NEED_MORE)
- {
- status = do_sasl(this, sasl);
- }
- sasl->destroy(sasl);
- if (status == SUCCESS)
- { /* continue until we receive empty SASL mechanism list */
- return NEED_MORE;
- }
- return FAILED;
- }
- /**
- * Authenticate session using SASL
- */
- static bool authenticate(private_pt_tls_client_t *this)
- {
- while (TRUE)
- {
- switch (select_and_do_sasl(this))
- {
- case NEED_MORE:
- continue;
- case SUCCESS:
- return TRUE;
- case FAILED:
- default:
- return FALSE;
- }
- }
- }
- /**
- * Perform assessment
- */
- static bool assess(private_pt_tls_client_t *this, tls_t *tnccs)
- {
- while (TRUE)
- {
- size_t msglen;
- size_t buflen = PT_TLS_MAX_MESSAGE_LEN;
- char buf[buflen];
- bio_reader_t *reader;
- uint32_t vendor, type, identifier;
- chunk_t data;
- switch (tnccs->build(tnccs, buf, &buflen, &msglen))
- {
- case SUCCESS:
- return tnccs->is_complete(tnccs);
- case ALREADY_DONE:
- data = chunk_create(buf, buflen);
- if (!pt_tls_write(this->tls, PT_TLS_PB_TNC_BATCH,
- this->identifier++, data))
- {
- return FALSE;
- }
- break;
- case INVALID_STATE:
- break;
- case FAILED:
- default:
- return FALSE;
- }
- reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
- if (!reader)
- {
- return FALSE;
- }
- if (vendor == 0)
- {
- if (type == PT_TLS_ERROR)
- {
- DBG1(DBG_TNC, "received PT-TLS error");
- reader->destroy(reader);
- return FALSE;
- }
- if (type != PT_TLS_PB_TNC_BATCH)
- {
- DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type);
- reader->destroy(reader);
- return FALSE;
- }
- data = reader->peek(reader);
- switch (tnccs->process(tnccs, data.ptr, data.len))
- {
- case SUCCESS:
- reader->destroy(reader);
- return tnccs->is_complete(tnccs);
- case FAILED:
- default:
- reader->destroy(reader);
- return FALSE;
- case NEED_MORE:
- break;
- }
- }
- else
- {
- DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message");
- }
- reader->destroy(reader);
- }
- }
- METHOD(pt_tls_client_t, run_assessment, status_t,
- private_pt_tls_client_t *this, tnccs_t *tnccs)
- {
- if (!this->tls)
- {
- DBG1(DBG_TNC, "entering PT-TLS setup phase");
- if (!make_connection(this))
- {
- return FAILED;
- }
- }
- DBG1(DBG_TNC, "entering PT-TLS negotiation phase");
- if (!negotiate_version(this))
- {
- return FAILED;
- }
- DBG1(DBG_TNC, "doing SASL client authentication");
- if (!authenticate(this))
- {
- return FAILED;
- }
- tnccs->set_auth_type(tnccs, TNC_AUTH_X509_CERT);
- DBG1(DBG_TNC, "entering PT-TLS data transport phase");
- if (!assess(this, (tls_t*)tnccs))
- {
- return FAILED;
- }
- return SUCCESS;
- }
- METHOD(pt_tls_client_t, destroy, void,
- private_pt_tls_client_t *this)
- {
- if (this->tls)
- {
- int fd;
- fd = this->tls->get_fd(this->tls);
- this->tls->destroy(this->tls);
- close(fd);
- }
- this->address->destroy(this->address);
- this->server->destroy(this->server);
- this->client->destroy(this->client);
- free(this);
- }
- /**
- * See header
- */
- pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *server,
- identification_t *client)
- {
- private_pt_tls_client_t *this;
- INIT(this,
- .public = {
- .run_assessment = _run_assessment,
- .destroy = _destroy,
- },
- .address = address,
- .server = server,
- .client = client,
- );
- return &this->public;
- }
|