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