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