cc10ab9989a52ea1f9022e1f72ad89e9566006aa
[strongswan.git] / src / libcharon / plugins / tnccs_20 / tnccs_20.c
1 /*
2 * Copyright (C) 2010 Sansar Choinyanbuu
3 * HSR Hochschule fuer Technik Rapperswil
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 "tnccs_20.h"
17 #include "tnccs_20_types.h"
18 #include "messages/pb_tnc_message.h"
19 #include "messages/pb_pa_message.h"
20
21 #include <debug.h>
22 #include <daemon.h>
23 #include <threading/mutex.h>
24 #include <tnc/tncif.h>
25 #include <tnc/tncifimv_names.h>
26 #include <tnc/tnccs/tnccs.h>
27
28 typedef struct private_tnccs_20_t private_tnccs_20_t;
29
30 /**
31 * Private data of a tnccs_20_t object.
32 */
33 struct private_tnccs_20_t {
34
35 /**
36 * Public tls_t interface.
37 */
38 tls_t public;
39
40 /**
41 * TNCC if TRUE, TNCS if FALSE
42 */
43 bool is_server;
44
45 /**
46 * Connection ID assigned to this TNCCS connection
47 */
48 TNC_ConnectionID connection_id;
49
50 /**
51 * Batch being constructed
52 */
53 chunk_t batch;
54
55 /**
56 * Mutex locking the batch in construction
57 */
58 mutex_t *mutex;
59
60 /**
61 * Flag set by IMC/IMV RequestHandshakeRetry() function
62 */
63 bool request_handshake_retry;
64
65 /**
66 * Set of IMV recommendations (TNC Server only)
67 */
68 recommendations_t *recs;
69 };
70
71 /**
72 * PB-TNC Message (see section 4.2 of RFC 5793)
73 *
74 * 0 1 2 3
75 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
76 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77 * | Flags | PB-TNC Vendor ID |
78 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79 * | PB-TNC Message Type |
80 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
81 * | PB-TNC Message Length |
82 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
83 * | PB-TNC Message Value (Variable Length) |
84 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
85 */
86
87 #define PB_TNC_HEADER_SIZE 12
88 #define PB_TNC_NOSKIP_FLAG (1<<7)
89 #define PB_TNC_IETF_VENDOR_ID 0x000000
90
91 static chunk_t build_pb_tnc_msg(pb_tnc_msg_type_t msg_type, chunk_t msg_value)
92 {
93 chunk_t msg, msg_header;
94 tls_writer_t *writer;
95 size_t msg_len;
96
97 msg_len = PB_TNC_HEADER_SIZE + msg_value.len;
98 writer = tls_writer_create(PB_TNC_HEADER_SIZE);
99 writer->write_uint8 (writer, PB_TNC_NOSKIP_FLAG);
100 writer->write_uint24(writer, PB_TNC_IETF_VENDOR_ID);
101 writer->write_uint32(writer, msg_type);
102 writer->write_uint32(writer, msg_len);
103 msg_header = writer->get_buf(writer);
104 msg = chunk_cat("cc", msg_header, msg_value);
105 writer->destroy(writer);
106
107 DBG2(DBG_TNC, "building %N message (%u bytes)", pb_tnc_msg_type_names,
108 msg_type, msg_len);
109 DBG3(DBG_TNC,"%B", &msg);
110
111 return msg;
112 }
113
114 static status_t process_pb_tnc_msg(tls_reader_t *reader,
115 pb_tnc_message_t **pb_tnc_msg)
116 {
117 u_int8_t flags;
118 u_int32_t vendor_id, msg_type;
119 size_t msg_len;
120 chunk_t msg, msg_value;
121
122 msg = reader->peek(reader);
123
124 if (msg.len < PB_TNC_HEADER_SIZE)
125 {
126 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message header",
127 msg.len);
128 return FAILED;
129 }
130 reader->read_uint8 (reader, &flags);
131 reader->read_uint24(reader, &vendor_id);
132 reader->read_uint32(reader, &msg_type);
133 reader->read_uint32(reader, &msg_len);
134
135 DBG2(DBG_TNC, "processing PB-TNC message (%u bytes)", msg_len);
136
137 if (msg_len < PB_TNC_HEADER_SIZE)
138 {
139 DBG1(DBG_TNC, "%u bytes too small for PB-TNC message length", msg_len);
140 return FAILED;
141 }
142 if (msg_len > msg.len)
143 {
144 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message", msg.len);
145 return FAILED;
146 }
147 msg.len = msg_len;
148 DBG3(DBG_TNC, "%B", &msg);
149 reader->read_data(reader, msg_len - PB_TNC_HEADER_SIZE, &msg_value);
150
151 if (vendor_id != PB_TNC_IETF_VENDOR_ID || msg_type > PB_MSG_ROOF)
152 {
153 if (flags & PB_TNC_NOSKIP_FLAG)
154 {
155 DBG1(DBG_TNC, "cannot process PB-TNC message with Vendor ID 0x%06x "
156 " and type 0x%08x", vendor_id, msg);
157 return FAILED;
158 }
159 else
160 {
161 DBG1(DBG_TNC, "ignore PB-TNC message with Vendor ID 0x%06x "
162 " and type 0x%08x", vendor_id, msg);
163 return INVALID_STATE;
164 }
165 }
166 DBG2(DBG_TNC, "processing %N message (%u bytes)", pb_tnc_msg_type_names,
167 msg_type, msg_value.len);
168 *pb_tnc_msg = pb_tnc_message_create(msg_type, msg_value);
169
170 return SUCCESS;
171 }
172
173 METHOD(tnccs_t, send_message, void,
174 private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
175 TNC_BufferReference msg,
176 TNC_UInt32 msg_len,
177 TNC_MessageType msg_type)
178 {
179 pb_tnc_message_t *pb_pa_msg;
180 chunk_t pb_tnc_msg;
181 TNC_MessageSubtype msg_sub_type;
182 TNC_VendorID msg_vendor_id;
183
184 msg_sub_type = msg_type & TNC_SUBTYPE_ANY;
185 msg_vendor_id = (msg_type >> 8) & TNC_VENDORID_ANY;
186
187 pb_pa_msg = pb_pa_message_create(msg_vendor_id, msg_sub_type, imc_id, imv_id,
188 chunk_create(msg, msg_len));
189 pb_pa_msg->build(pb_pa_msg);
190 pb_tnc_msg = build_pb_tnc_msg(PB_MSG_PA, pb_pa_msg->get_encoding(pb_pa_msg));
191 pb_pa_msg->destroy(pb_pa_msg);
192
193 this->mutex->lock(this->mutex);
194 this->batch = chunk_cat("mm", this->batch, pb_tnc_msg);
195 this->mutex->unlock(this->mutex);
196 }
197
198 METHOD(tls_t, process, status_t,
199 private_tnccs_20_t *this, void *buf, size_t buflen)
200 {
201 tls_reader_t *reader;
202 pb_tnc_message_t *msg = NULL;
203 status_t status;
204 char *pos;
205 size_t len;
206
207 if (this->is_server && !this->connection_id)
208 {
209 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
210 (tnccs_t*)this, _send_message,
211 &this->request_handshake_retry, &this->recs);
212 if (!this->connection_id)
213 {
214 return FAILED;
215 }
216 charon->imvs->notify_connection_change(charon->imvs,
217 this->connection_id, TNC_CONNECTION_STATE_CREATE);
218
219 /* Test reason enumerator */
220 {
221 chunk_t reason, reason_2 = { "virus alarm", 11 };
222 chunk_t reason_lang, reason_lang_2 = { "en, ru", 6 };
223 TNC_IMVID id;
224 enumerator_t *enumerator;
225
226 this->recs->set_reason_string(this->recs, 2, reason_2);
227 this->recs->set_reason_language(this->recs, 2, reason_lang_2);
228
229 enumerator = this->recs->create_reason_enumerator(this->recs);
230 while (enumerator->enumerate(enumerator, &id, &reason, &reason_lang))
231 {
232 DBG1(DBG_TNC, "IMV %u: reason = '%.*s', lang = '%.*s'",
233 id, reason.len, reason.ptr, reason_lang.len, reason_lang.ptr);
234 }
235 enumerator->destroy(enumerator);
236 }
237 }
238 DBG1(DBG_TNC, "received TNCCS Batch (%u bytes) for Connection ID %u",
239 buflen, this->connection_id);
240
241 pos = strchr(buf, '|');
242 if (pos)
243 {
244 pos++;
245 len = buflen - (pos - (char*)buf);
246 }
247 else
248 {
249 pos = buf;
250 len = buflen;
251 }
252 reader = tls_reader_create(chunk_create(pos, len));
253 while (reader->remaining(reader) > 0)
254 {
255 switch (process_pb_tnc_msg(reader, &msg))
256 {
257 case SUCCESS:
258 break;
259 case INVALID_STATE:
260 continue;
261 default:
262 reader->destroy(reader);
263 return FAILED;
264 }
265
266 status = msg->process(msg);
267 if (status != SUCCESS)
268 {
269 msg->destroy(msg);
270 reader->destroy(reader);
271 return status;
272 }
273
274 switch (msg->get_type(msg))
275 {
276 case PB_MSG_PA:
277 {
278 TNC_MessageType msg_type;
279 u_int32_t vendor_id, subtype;
280 chunk_t msg_body;
281 pb_pa_message_t *pb_pa_msg;
282
283 pb_pa_msg = (pb_pa_message_t*)msg;
284 vendor_id = pb_pa_msg->get_vendor_id(pb_pa_msg, &subtype);
285 msg_type = (vendor_id << 8) | subtype;
286 msg_body = pb_pa_msg->get_body(pb_pa_msg);
287 DBG2(DBG_TNC, "message type: 0x%08x", msg_type);
288 if (this->is_server)
289 {
290 charon->imvs->receive_message(charon->imvs,
291 this->connection_id, msg_body.ptr, msg_body.len,
292 msg_type);
293 }
294 else
295 {
296 charon->imcs->receive_message(charon->imcs,
297 this->connection_id, msg_body.ptr, msg_body.len,
298 msg_type);
299 }
300 break;
301 }
302 case PB_MSG_ERROR:
303 break;
304 case PB_MSG_EXPERIMENTAL:
305 break;
306 case PB_MSG_LANGUAGE_PREFERENCE:
307 break;
308 case PB_MSG_ASSESSMENT_RESULT:
309 case PB_MSG_ACCESS_RECOMMENDATION:
310 case PB_MSG_REMEDIATION_PARAMETERS:
311 case PB_MSG_REASON_STRING:
312 break;
313 }
314 msg->destroy(msg);
315 }
316 reader->destroy(reader);
317
318 if (this->is_server)
319 {
320 charon->imvs->batch_ending(charon->imvs, this->connection_id);
321 }
322 else
323 {
324 charon->imcs->batch_ending(charon->imcs, this->connection_id);
325 }
326
327 return NEED_MORE;
328 }
329
330 METHOD(tls_t, build, status_t,
331 private_tnccs_20_t *this, void *buf, size_t *buflen, size_t *msglen)
332 {
333 char *msg = this->is_server ? "tncs->tncc 2.0|" : "tncc->tncs 2.0|";
334 size_t len;
335
336 this->mutex->lock(this->mutex);
337 this->batch = chunk_cat("cm", chunk_create(msg, strlen(msg)), this->batch);
338 this->mutex->unlock(this->mutex);
339
340 if (!this->is_server && !this->connection_id)
341 {
342 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
343 (tnccs_t*)this, _send_message,
344 &this->request_handshake_retry, NULL);
345 if (!this->connection_id)
346 {
347 return FAILED;
348 }
349 charon->imcs->notify_connection_change(charon->imcs,
350 this->connection_id, TNC_CONNECTION_STATE_CREATE);
351 charon->imcs->notify_connection_change(charon->imcs,
352 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
353 charon->imcs->begin_handshake(charon->imcs, this->connection_id);
354 }
355
356 this->mutex->lock(this->mutex);
357 len = this->batch.len;
358 *msglen = len;
359 *buflen = len;
360 memcpy(buf, this->batch.ptr, len);
361 chunk_free(&this->batch);
362 this->mutex->unlock(this->mutex);
363
364 DBG1(DBG_TNC, "sending TNCCS Batch (%d bytes) for Connection ID %u",
365 len, this->connection_id);
366
367 return ALREADY_DONE;
368 }
369
370 METHOD(tls_t, is_server, bool,
371 private_tnccs_20_t *this)
372 {
373 return this->is_server;
374 }
375
376 METHOD(tls_t, get_purpose, tls_purpose_t,
377 private_tnccs_20_t *this)
378 {
379 return TLS_PURPOSE_EAP_TNC;
380 }
381
382 METHOD(tls_t, is_complete, bool,
383 private_tnccs_20_t *this)
384 {
385 TNC_IMV_Action_Recommendation rec;
386 TNC_IMV_Evaluation_Result eval;
387
388 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
389 {
390 DBG2(DBG_TNC, "Final recommendation '%N' and evaluation '%N'",
391 action_recommendation_names, rec, evaluation_result_names, eval);
392
393 return charon->imvs->enforce_recommendation(charon->imvs, rec);
394 }
395 else
396 {
397 return FALSE;
398 }
399 }
400
401 METHOD(tls_t, get_eap_msk, chunk_t,
402 private_tnccs_20_t *this)
403 {
404 return chunk_empty;
405 }
406
407 METHOD(tls_t, destroy, void,
408 private_tnccs_20_t *this)
409 {
410 charon->tnccs->remove_connection(charon->tnccs, this->connection_id);
411 this->mutex->destroy(this->mutex);
412 free(this->batch.ptr);
413 free(this);
414 }
415
416 /**
417 * See header
418 */
419 tls_t *tnccs_20_create(bool is_server)
420 {
421 private_tnccs_20_t *this;
422
423 INIT(this,
424 .public = {
425 .process = _process,
426 .build = _build,
427 .is_server = _is_server,
428 .get_purpose = _get_purpose,
429 .is_complete = _is_complete,
430 .get_eap_msk = _get_eap_msk,
431 .destroy = _destroy,
432 },
433 .is_server = is_server,
434 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
435 );
436
437 return &this->public;
438 }