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