Add a libpttls providing NEA PT-TLS / TNC IF-T for TLS transport layer
[strongswan.git] / src / libpttls / pt_tls_server.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_server.h"
17 #include "pt_tls.h"
18
19 #include <utils/debug.h>
20
21 #include <tnc/tnc.h>
22
23 typedef struct private_pt_tls_server_t private_pt_tls_server_t;
24
25 /**
26 * Private data of an pt_tls_server_t object.
27 */
28 struct private_pt_tls_server_t {
29
30 /**
31 * Public pt_tls_server_t interface.
32 */
33 pt_tls_server_t public;
34
35 /**
36 * TLS protected socket
37 */
38 tls_socket_t *tls;
39
40 enum {
41 /* expecting version negotiation */
42 PT_TLS_SERVER_VERSION,
43 /* expecting an SASL exchange */
44 PT_TLS_SERVER_AUTH,
45 /* expecting TNCCS exchange */
46 PT_TLS_SERVER_TNCCS,
47 /* terminating state */
48 PT_TLS_SERVER_END,
49 } state;
50
51 /**
52 * Message Identifier
53 */
54 u_int32_t identifier;
55
56 /**
57 * TNCCS protocol handler, implemented as tls_t
58 */
59 tls_t *tnccs;
60 };
61
62 /**
63 * Negotiate PT-TLS version
64 */
65 static bool negotiate_version(private_pt_tls_server_t *this)
66 {
67 bio_reader_t *reader;
68 bio_writer_t *writer;
69 u_int32_t vendor, type, identifier;
70 u_int8_t reserved, vmin, vmax, vpref;
71
72 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
73 if (!reader)
74 {
75 return FALSE;
76 }
77 if (vendor != 0 || type != PT_TLS_VERSION_REQUEST ||
78 !reader->read_uint8(reader, &reserved) ||
79 !reader->read_uint8(reader, &vmin) ||
80 !reader->read_uint8(reader, &vmax) ||
81 !reader->read_uint8(reader, &vpref))
82 {
83 DBG1(DBG_TNC, "PT-TLS version negotiation failed");
84 reader->destroy(reader);
85 return FALSE;
86 }
87 reader->destroy(reader);
88
89 if (vmin > PT_TLS_VERSION || vmax < PT_TLS_VERSION)
90 {
91 /* TODO: send error */
92 return FALSE;
93 }
94
95 writer = bio_writer_create(4);
96 writer->write_uint24(writer, 0);
97 writer->write_uint8(writer, PT_TLS_VERSION);
98
99 return pt_tls_write(this->tls, writer, PT_TLS_VERSION_RESPONSE,
100 this->identifier++);
101 }
102
103 /**
104 * Authenticated PT-TLS session with SASL
105 */
106 static bool authenticate(private_pt_tls_server_t *this)
107 {
108 bio_writer_t *writer;
109
110 /* send empty SASL mechanims list to skip authentication */
111 writer = bio_writer_create(0);
112 return pt_tls_write(this->tls, writer, PT_TLS_SASL_MECHS,
113 this->identifier++);
114 }
115
116 /**
117 * Perform assessment
118 */
119 static bool assess(private_pt_tls_server_t *this, tls_t *tnccs)
120 {
121 while (TRUE)
122 {
123 bio_writer_t *writer;
124 bio_reader_t *reader;
125 u_int32_t vendor, type, identifier;
126 chunk_t data;
127
128 writer = bio_writer_create(32);
129 while (TRUE)
130 {
131 char buf[2048];
132 size_t buflen, msglen;
133
134 buflen = sizeof(buf);
135 switch (tnccs->build(tnccs, buf, &buflen, &msglen))
136 {
137 case SUCCESS:
138 writer->destroy(writer);
139 return tnccs->is_complete(tnccs);
140 case FAILED:
141 default:
142 writer->destroy(writer);
143 return FALSE;
144 case INVALID_STATE:
145 writer->destroy(writer);
146 break;
147 case NEED_MORE:
148 writer->write_data(writer, chunk_create(buf, buflen));
149 continue;
150 case ALREADY_DONE:
151 writer->write_data(writer, chunk_create(buf, buflen));
152 if (!pt_tls_write(this->tls, writer, PT_TLS_PB_TNC_BATCH,
153 this->identifier++))
154 {
155 return FALSE;
156 }
157 writer = bio_writer_create(32);
158 continue;
159 }
160 break;
161 }
162
163 reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
164 if (!reader)
165 {
166 return FALSE;
167 }
168 if (vendor == 0)
169 {
170 if (type == PT_TLS_ERROR)
171 {
172 DBG1(DBG_TNC, "received PT-TLS error");
173 reader->destroy(reader);
174 return FALSE;
175 }
176 if (type != PT_TLS_PB_TNC_BATCH)
177 {
178 DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type);
179 reader->destroy(reader);
180 return FALSE;
181 }
182 data = reader->peek(reader);
183 switch (tnccs->process(tnccs, data.ptr, data.len))
184 {
185 case SUCCESS:
186 reader->destroy(reader);
187 return tnccs->is_complete(tnccs);
188 case FAILED:
189 default:
190 reader->destroy(reader);
191 return FALSE;
192 case NEED_MORE:
193 break;
194 }
195 }
196 else
197 {
198 DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message");
199 }
200 reader->destroy(reader);
201 }
202 }
203
204 METHOD(pt_tls_server_t, handle, status_t,
205 private_pt_tls_server_t *this)
206 {
207 switch (this->state)
208 {
209 case PT_TLS_SERVER_VERSION:
210 if (!negotiate_version(this))
211 {
212 return FAILED;
213 }
214 DBG1(DBG_TNC, "negotiated PT-TLS version %d", PT_TLS_VERSION);
215 this->state = PT_TLS_SERVER_AUTH;
216 break;
217 case PT_TLS_SERVER_AUTH:
218 DBG1(DBG_TNC, "sending empty mechanism list to skip SASL");
219 if (!authenticate(this))
220 {
221 return FAILED;
222 }
223 this->state = PT_TLS_SERVER_TNCCS;
224 this->tnccs = (tls_t*)tnc->tnccs->create_instance(tnc->tnccs,
225 TNCCS_2_0, TRUE);
226 if (!this->tnccs)
227 {
228 return FAILED;
229 }
230 break;
231 case PT_TLS_SERVER_TNCCS:
232 if (!assess(this, (tls_t*)this->tnccs))
233 {
234 return FAILED;
235 }
236 this->state = PT_TLS_SERVER_END;
237 return SUCCESS;
238 default:
239 return FAILED;
240 }
241 return NEED_MORE;
242 }
243
244 METHOD(pt_tls_server_t, get_fd, int,
245 private_pt_tls_server_t *this)
246 {
247 return this->tls->get_fd(this->tls);
248 }
249
250 METHOD(pt_tls_server_t, destroy, void,
251 private_pt_tls_server_t *this)
252 {
253 DESTROY_IF(this->tnccs);
254 this->tls->destroy(this->tls);
255 free(this);
256 }
257
258 /**
259 * See header
260 */
261 pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd)
262 {
263 private_pt_tls_server_t *this;
264
265 INIT(this,
266 .public = {
267 .handle = _handle,
268 .get_fd = _get_fd,
269 .destroy = _destroy,
270 },
271 .state = PT_TLS_SERVER_VERSION,
272 .tls = tls_socket_create(TRUE, server, NULL, fd, NULL),
273 );
274
275 if (!this->tls)
276 {
277 free(this);
278 return NULL;
279 }
280
281 return &this->public;
282 }