Add a libpttls providing NEA PT-TLS / TNC IF-T for TLS transport layer
[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 <tls_socket.h>
20 #include <utils/debug.h>
21
22 #include <errno.h>
23 #include <stdio.h>
24 #include <unistd.h>
25
26 typedef struct private_pt_tls_client_t private_pt_tls_client_t;
27
28 /**
29 * Private data of an pt_tls_client_t object.
30 */
31 struct private_pt_tls_client_t {
32
33 /**
34 * Public pt_tls_client_t interface.
35 */
36 pt_tls_client_t public;
37
38 /**
39 * TLS secured socket used by PT-TLS
40 */
41 tls_socket_t *tls;
42
43 /**
44 * Server address
45 */
46 char *server;
47
48 /**
49 * Server port
50 */
51 u_int16_t port;
52
53 /**
54 * Current PT-TLS message identifier
55 */
56 u_int32_t identifier;
57 };
58
59 /**
60 * Establish TLS secured TCP connection to TNC server
61 */
62 static bool make_connection(private_pt_tls_client_t *this)
63 {
64 identification_t *id;
65 host_t *server;
66 int fd;
67
68 server = host_create_from_dns(this->server, AF_UNSPEC, this->port);
69 if (!server)
70 {
71 return FALSE;
72 }
73
74 fd = socket(server->get_family(server), SOCK_STREAM, 0);
75 if (fd == -1)
76 {
77 DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
78 server->destroy(server);
79 return FALSE;
80 }
81 if (connect(fd, server->get_sockaddr(server),
82 *server->get_sockaddr_len(server)) == -1)
83 {
84 DBG1(DBG_TNC, "connecting to PT-TLS server failed: %s", strerror(errno));
85 server->destroy(server);
86 close(fd);
87 return FALSE;
88 }
89 server->destroy(server);
90
91 id = identification_create_from_string(this->server);
92 this->tls = tls_socket_create(FALSE, id, NULL, fd, NULL);
93 id->destroy(id);
94 if (!this->tls)
95 {
96 close(fd);
97 return FALSE;
98 }
99 return TRUE;
100 }
101
102 /**
103 * Negotiate PT-TLS version
104 */
105 static bool negotiate_version(private_pt_tls_client_t *this)
106 {
107 bio_writer_t *writer;
108 bio_reader_t *reader;
109 u_int32_t type, vendor, identifier, reserved;
110 u_int8_t version;
111
112 DBG1(DBG_TNC, "sending offer for PT-TLS version %d", PT_TLS_VERSION);
113
114 writer = bio_writer_create(4);
115 writer->write_uint8(writer, 0);
116 writer->write_uint8(writer, PT_TLS_VERSION);
117 writer->write_uint8(writer, PT_TLS_VERSION);
118 writer->write_uint8(writer, PT_TLS_VERSION);
119 if (!pt_tls_write(this->tls, writer, PT_TLS_VERSION_REQUEST,
120 this->identifier++))
121 {
122 return FALSE;
123 }
124
125 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
126 if (!reader)
127 {
128 return FALSE;
129 }
130 if (vendor != 0 || type != PT_TLS_VERSION_RESPONSE ||
131 !reader->read_uint24(reader, &reserved) ||
132 !reader->read_uint8(reader, &version) ||
133 version != PT_TLS_VERSION)
134 {
135 DBG1(DBG_TNC, "PT-TLS version negotiation failed");
136 reader->destroy(reader);
137 return FALSE;
138 }
139 reader->destroy(reader);
140 return TRUE;
141 }
142
143 /**
144 * Authenticate session using SASL
145 */
146 static bool authenticate(private_pt_tls_client_t *this)
147 {
148 bio_reader_t *reader;
149 u_int32_t type, vendor, identifier;
150
151 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
152 if (!reader)
153 {
154 return FALSE;
155 }
156 if (vendor != 0 || type != PT_TLS_SASL_MECHS)
157 {
158 DBG1(DBG_TNC, "PT-TLS authentication failed");
159 reader->destroy(reader);
160 return FALSE;
161 }
162
163 if (reader->remaining(reader))
164 { /* mechanism list not empty, FAIL until we support it */
165 reader->destroy(reader);
166 return FALSE;
167 }
168 DBG1(DBG_TNC, "PT-TLS authentication complete");
169 reader->destroy(reader);
170 return TRUE;
171 }
172
173 /**
174 * Perform assessment
175 */
176 static bool assess(private_pt_tls_client_t *this, tls_t *tnccs)
177 {
178 while (TRUE)
179 {
180 bio_writer_t *writer;
181 bio_reader_t *reader;
182 u_int32_t vendor, type, identifier;
183 chunk_t data;
184
185 writer = bio_writer_create(32);
186 while (TRUE)
187 {
188 char buf[2048];
189 size_t buflen, msglen;
190
191 buflen = sizeof(buf);
192 switch (tnccs->build(tnccs, buf, &buflen, &msglen))
193 {
194 case SUCCESS:
195 writer->destroy(writer);
196 return tnccs->is_complete(tnccs);
197 case FAILED:
198 default:
199 writer->destroy(writer);
200 return FALSE;
201 case INVALID_STATE:
202 writer->destroy(writer);
203 break;
204 case NEED_MORE:
205 writer->write_data(writer, chunk_create(buf, buflen));
206 continue;
207 case ALREADY_DONE:
208 writer->write_data(writer, chunk_create(buf, buflen));
209 if (!pt_tls_write(this->tls, writer, PT_TLS_PB_TNC_BATCH,
210 this->identifier++))
211 {
212 return FALSE;
213 }
214 writer = bio_writer_create(32);
215 continue;
216 }
217 break;
218 }
219
220 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
221 if (!reader)
222 {
223 return FALSE;
224 }
225 if (vendor == 0)
226 {
227 if (type == PT_TLS_ERROR)
228 {
229 DBG1(DBG_TNC, "received PT-TLS error");
230 reader->destroy(reader);
231 return FALSE;
232 }
233 if (type != PT_TLS_PB_TNC_BATCH)
234 {
235 DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type);
236 reader->destroy(reader);
237 return FALSE;
238 }
239 data = reader->peek(reader);
240 switch (tnccs->process(tnccs, data.ptr, data.len))
241 {
242 case SUCCESS:
243 reader->destroy(reader);
244 return tnccs->is_complete(tnccs);
245 case FAILED:
246 default:
247 reader->destroy(reader);
248 return FALSE;
249 case NEED_MORE:
250 break;
251 }
252 }
253 else
254 {
255 DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message");
256 }
257 reader->destroy(reader);
258 }
259 }
260
261 METHOD(pt_tls_client_t, run_assessment, status_t,
262 private_pt_tls_client_t *this, tnccs_t *tnccs)
263 {
264 if (!this->tls)
265 {
266 if (!make_connection(this))
267 {
268 return FAILED;
269 }
270 }
271 if (!negotiate_version(this))
272 {
273 return FAILED;
274 }
275 if (!authenticate(this))
276 {
277 return FAILED;
278 }
279 if (!assess(this, (tls_t*)tnccs))
280 {
281 return FAILED;
282 }
283 return SUCCESS;
284 }
285
286
287 METHOD(pt_tls_client_t, destroy, void,
288 private_pt_tls_client_t *this)
289 {
290 if (this->tls)
291 {
292 close(this->tls->get_fd(this->tls));
293 this->tls->destroy(this->tls);
294 }
295 free(this->server);
296 free(this);
297 }
298
299 /**
300 * See header
301 */
302 pt_tls_client_t *pt_tls_client_create(char *server, u_int16_t port)
303 {
304 private_pt_tls_client_t *this;
305
306 INIT(this,
307 .public = {
308 .run_assessment = _run_assessment,
309 .destroy = _destroy,
310 },
311 .server = strdup(server),
312 .port = port,
313 );
314
315 return &this->public;
316 }