moved imv_manager to libtnccs
[strongswan.git] / src / libcharon / plugins / tnccs_11 / tnccs_11.c
1 /*
2 * Copyright (C) 2010 Andreas Steffen
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_11.h"
17 #include "batch/tnccs_batch.h"
18 #include "messages/tnccs_msg.h"
19 #include "messages/imc_imv_msg.h"
20 #include "messages/tnccs_error_msg.h"
21 #include "messages/tnccs_preferred_language_msg.h"
22 #include "messages/tnccs_reason_strings_msg.h"
23 #include "messages/tnccs_recommendation_msg.h"
24
25 #include <tncif_names.h>
26 #include <tncif_pa_subtypes.h>
27
28 #include <imc/imc_manager.h>
29
30 #include <daemon.h>
31 #include <debug.h>
32 #include <threading/mutex.h>
33 #include <tnc/tnccs/tnccs.h>
34
35 typedef struct private_tnccs_11_t private_tnccs_11_t;
36
37 /**
38 * Private data of a tnccs_11_t object.
39 */
40 struct private_tnccs_11_t {
41
42 /**
43 * Public tls_t interface.
44 */
45 tls_t public;
46
47 /**
48 * TNCC if TRUE, TNCS if FALSE
49 */
50 bool is_server;
51
52 /**
53 * Connection ID assigned to this TNCCS connection
54 */
55 TNC_ConnectionID connection_id;
56
57 /**
58 * Last TNCCS batch ID
59 */
60 int batch_id;
61
62 /**
63 * TNCCS batch being constructed
64 */
65 tnccs_batch_t *batch;
66
67 /**
68 * Mutex locking the batch in construction
69 */
70 mutex_t *mutex;
71
72 /**
73 * Flag set while processing
74 */
75 bool fatal_error;
76
77 /**
78 * Flag set by TNCCS-Recommendation message
79 */
80 bool delete_state;
81
82 /**
83 * SendMessage() by IMC/IMV only allowed if flag is set
84 */
85 bool send_msg;
86
87 /**
88 * Flag set by IMC/IMV RequestHandshakeRetry() function
89 */
90 bool request_handshake_retry;
91
92 /**
93 * Set of IMV recommendations (TNC Server only)
94 */
95 recommendations_t *recs;
96
97 /**
98 * TNC IMC manager controlling Integrity Measurement Collectors
99 */
100 imc_manager_t *imcs;
101
102 /**
103 * TNC IMV manager controlling Integrity Measurement Verifiers
104 */
105 imc_manager_t *imvs;
106
107 };
108
109 METHOD(tnccs_t, send_msg, TNC_Result,
110 private_tnccs_11_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
111 TNC_BufferReference msg,
112 TNC_UInt32 msg_len,
113 TNC_MessageType msg_type)
114 {
115 tnccs_msg_t *tnccs_msg;
116 u_int32_t vendor_id, subtype;
117 enum_name_t *pa_subtype_names;
118
119 if (!this->send_msg)
120 {
121 DBG1(DBG_TNC, "%s %u not allowed to call SendMessage()",
122 this->is_server ? "IMV" : "IMC",
123 this->is_server ? imv_id : imc_id);
124 return TNC_RESULT_ILLEGAL_OPERATION;
125 }
126 vendor_id = msg_type >> 8;
127 subtype = msg_type & 0xff;
128 pa_subtype_names = get_pa_subtype_names(vendor_id);
129 if (pa_subtype_names)
130 {
131 DBG2(DBG_TNC, "creating IMC-IMV message type '%N/%N' 0x%06x/0x%02x",
132 pen_names, vendor_id, pa_subtype_names, subtype, vendor_id, subtype);
133 }
134 else
135 {
136 DBG2(DBG_TNC, "creating IMC-IMV message type '%N' 0x%06x/0x%02x",
137 pen_names, vendor_id, vendor_id, subtype);
138 }
139 tnccs_msg = imc_imv_msg_create(msg_type, chunk_create(msg, msg_len));
140
141 /* adding an IMC-IMV Message to TNCCS batch */
142 this->mutex->lock(this->mutex);
143 if (!this->batch)
144 {
145 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
146 }
147 this->batch->add_msg(this->batch, tnccs_msg);
148 this->mutex->unlock(this->mutex);
149 return TNC_RESULT_SUCCESS;
150 }
151
152 /**
153 * Handle a single TNCCS message according to its type
154 */
155 static void handle_message(private_tnccs_11_t *this, tnccs_msg_t *msg)
156 {
157 switch (msg->get_type(msg))
158 {
159 case IMC_IMV_MSG:
160 {
161 imc_imv_msg_t *imc_imv_msg;
162 TNC_MessageType msg_type;
163 chunk_t msg_body;
164 u_int32_t vendor_id, subtype;
165 enum_name_t *pa_subtype_names;
166
167 imc_imv_msg = (imc_imv_msg_t*)msg;
168 msg_type = imc_imv_msg->get_msg_type(imc_imv_msg);
169 msg_body = imc_imv_msg->get_msg_body(imc_imv_msg);
170 vendor_id = msg_type >> 8;
171 subtype = msg_type & 0xff;
172
173 pa_subtype_names = get_pa_subtype_names(vendor_id);
174 if (pa_subtype_names)
175 {
176 DBG2(DBG_TNC, "handling IMC-IMV message type '%N/%N' 0x%06x/0x%02x",
177 pen_names, vendor_id, pa_subtype_names, subtype,
178 vendor_id, subtype);
179 }
180 else
181 {
182 DBG2(DBG_TNC, "handling IMC-IMV message type '%N' 0x%06x/0x%02x",
183 pen_names, vendor_id, vendor_id, subtype);
184 }
185
186 this->send_msg = TRUE;
187 if (this->is_server)
188 {
189 this->imvs->receive_message(this->imvs,
190 this->connection_id, msg_body.ptr, msg_body.len, msg_type);
191 }
192 else
193 {
194 this->imcs->receive_message(this->imcs,
195 this->connection_id, msg_body.ptr, msg_body.len,msg_type);
196 }
197 this->send_msg = FALSE;
198 break;
199 }
200 case TNCCS_MSG_RECOMMENDATION:
201 {
202 tnccs_recommendation_msg_t *rec_msg;
203 TNC_IMV_Action_Recommendation rec;
204 TNC_ConnectionState state = TNC_CONNECTION_STATE_ACCESS_NONE;
205
206 rec_msg = (tnccs_recommendation_msg_t*)msg;
207 rec = rec_msg->get_recommendation(rec_msg);
208 if (this->is_server)
209 {
210 DBG1(DBG_TNC, "ignoring NCCS-Recommendation message from "
211 " TNC client");
212 break;
213 }
214 DBG1(DBG_TNC, "TNC recommendation is '%N'",
215 TNC_IMV_Action_Recommendation_names, rec);
216 switch (rec)
217 {
218 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
219 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
220 break;
221 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
222 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
223 break;
224 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
225 default:
226 state = TNC_CONNECTION_STATE_ACCESS_NONE;
227 }
228 this->imcs->notify_connection_change(this->imcs,
229 this->connection_id, state);
230 this->delete_state = TRUE;
231 break;
232 }
233 case TNCCS_MSG_ERROR:
234 {
235 tnccs_error_msg_t *err_msg;
236 tnccs_error_type_t error_type;
237 char *error_msg;
238
239 err_msg = (tnccs_error_msg_t*)msg;
240 error_msg = err_msg->get_message(err_msg, &error_type);
241 DBG1(DBG_TNC, "received '%N' TNCCS-Error: %s",
242 tnccs_error_type_names, error_type, error_msg);
243
244 /* we assume that all errors are fatal */
245 this->fatal_error = TRUE;
246 break;
247 }
248 case TNCCS_MSG_PREFERRED_LANGUAGE:
249 {
250 tnccs_preferred_language_msg_t *lang_msg;
251 char *lang;
252
253 lang_msg = (tnccs_preferred_language_msg_t*)msg;
254 lang = lang_msg->get_preferred_language(lang_msg);
255
256 DBG2(DBG_TNC, "setting preferred language to '%s'", lang);
257 this->recs->set_preferred_language(this->recs,
258 chunk_create(lang, strlen(lang)));
259 break;
260 }
261 case TNCCS_MSG_REASON_STRINGS:
262 {
263 tnccs_reason_strings_msg_t *reason_msg;
264 chunk_t reason_string, reason_lang;
265
266 reason_msg = (tnccs_reason_strings_msg_t*)msg;
267 reason_string = reason_msg->get_reason(reason_msg, &reason_lang);
268 DBG2(DBG_TNC, "reason string is '%.*s'", reason_string.len,
269 reason_string.ptr);
270 DBG2(DBG_TNC, "reason language is '%.*s'", reason_lang.len,
271 reason_lang.ptr);
272 break;
273 }
274 default:
275 break;
276 }
277 }
278
279 METHOD(tls_t, process, status_t,
280 private_tnccs_11_t *this, void *buf, size_t buflen)
281 {
282 chunk_t data;
283 tnccs_batch_t *batch;
284 tnccs_msg_t *msg;
285 enumerator_t *enumerator;
286 status_t status;
287
288 if (this->is_server && !this->connection_id)
289 {
290 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
291 (tnccs_t*)this, _send_msg,
292 &this->request_handshake_retry, &this->recs);
293 if (!this->connection_id)
294 {
295 return FAILED;
296 }
297 charon->imvs->notify_connection_change(charon->imvs,
298 this->connection_id, TNC_CONNECTION_STATE_CREATE);
299 charon->imvs->notify_connection_change(charon->imvs,
300 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
301 }
302
303 data = chunk_create(buf, buflen);
304 DBG1(DBG_TNC, "received TNCCS Batch (%u bytes) for Connection ID %u",
305 data.len, this->connection_id);
306 DBG3(DBG_TNC, "%.*s", data.len, data.ptr);
307 batch = tnccs_batch_create_from_data(this->is_server, ++this->batch_id, data);
308 status = batch->process(batch);
309
310 if (status == FAILED)
311 {
312 this->fatal_error = TRUE;
313 this->mutex->lock(this->mutex);
314 if (this->batch)
315 {
316 DBG1(DBG_TNC, "cancelling TNCCS batch");
317 this->batch->destroy(this->batch);
318 this->batch_id--;
319 }
320 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
321
322 /* add error messages to outbound batch */
323 enumerator = batch->create_error_enumerator(batch);
324 while (enumerator->enumerate(enumerator, &msg))
325 {
326 this->batch->add_msg(this->batch, msg->get_ref(msg));
327 }
328 enumerator->destroy(enumerator);
329 this->mutex->unlock(this->mutex);
330 }
331 else
332 {
333 enumerator = batch->create_msg_enumerator(batch);
334 while (enumerator->enumerate(enumerator, &msg))
335 {
336 handle_message(this, msg);
337 }
338 enumerator->destroy(enumerator);
339
340 /* received any TNCCS-Error messages */
341 if (this->fatal_error)
342 {
343 DBG1(DBG_TNC, "a fatal TNCCS-Error occurred, terminating connection");
344 batch->destroy(batch);
345 return FAILED;
346 }
347
348 this->send_msg = TRUE;
349 if (this->is_server)
350 {
351 this->imvs->batch_ending(this->imvs, this->connection_id);
352 }
353 else
354 {
355 this->imcs->batch_ending(this->imcs, this->connection_id);
356 }
357 this->send_msg = FALSE;
358 }
359 batch->destroy(batch);
360
361 return NEED_MORE;
362 }
363
364 /**
365 * Add a recommendation message if a final recommendation is available
366 */
367 static void check_and_build_recommendation(private_tnccs_11_t *this)
368 {
369 TNC_IMV_Action_Recommendation rec;
370 TNC_IMV_Evaluation_Result eval;
371 TNC_IMVID id;
372 chunk_t reason, language;
373 enumerator_t *enumerator;
374 tnccs_msg_t *msg;
375
376 if (!this->recs->have_recommendation(this->recs, &rec, &eval))
377 {
378 charon->imvs->solicit_recommendation(charon->imvs, this->connection_id);
379 }
380 if (this->recs->have_recommendation(this->recs, &rec, &eval))
381 {
382 if (!this->batch)
383 {
384 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
385 }
386
387 msg = tnccs_recommendation_msg_create(rec);
388 this->batch->add_msg(this->batch, msg);
389
390 /* currently we just send the first Reason String */
391 enumerator = this->recs->create_reason_enumerator(this->recs);
392 if (enumerator->enumerate(enumerator, &id, &reason, &language))
393 {
394 msg = tnccs_reason_strings_msg_create(reason, language);
395 this->batch->add_msg(this->batch, msg);
396 }
397 enumerator->destroy(enumerator);
398 this->recs->clear_reasons(this->recs);
399
400 /* we have reache the final state */
401 this->delete_state = TRUE;
402 }
403 }
404
405 METHOD(tls_t, build, status_t,
406 private_tnccs_11_t *this, void *buf, size_t *buflen, size_t *msglen)
407 {
408 status_t status;
409
410 /* Initialize the connection */
411 if (!this->is_server && !this->connection_id)
412 {
413 tnccs_msg_t *msg;
414 char *pref_lang;
415
416 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
417 (tnccs_t*)this, _send_msg,
418 &this->request_handshake_retry, NULL);
419 if (!this->connection_id)
420 {
421 return FAILED;
422 }
423
424 /* Create TNCCS-PreferredLanguage message */
425 pref_lang = this->imcs->get_preferred_language(this->imcs);
426 msg = tnccs_preferred_language_msg_create(pref_lang);
427 this->mutex->lock(this->mutex);
428 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
429 this->batch->add_msg(this->batch, msg);
430 this->mutex->unlock(this->mutex);
431
432 this->imcs->notify_connection_change(this->imcs,
433 this->connection_id, TNC_CONNECTION_STATE_CREATE);
434 this->imcs->notify_connection_change(this->imcs,
435 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
436 this->send_msg = TRUE;
437 this->imcs->begin_handshake(this->imcs, this->connection_id);
438 this->send_msg = FALSE;
439 }
440
441 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
442 this->mutex->lock(this->mutex);
443
444 if (this->recs && !this->delete_state &&
445 (!this->batch || this->fatal_error))
446 {
447 check_and_build_recommendation(this);
448 }
449
450 if (this->batch)
451 {
452 chunk_t data;
453
454 this->batch->build(this->batch);
455 data = this->batch->get_encoding(this->batch);
456 DBG1(DBG_TNC, "sending TNCCS Batch (%d bytes) for Connection ID %u",
457 data.len, this->connection_id);
458 DBG3(DBG_TNC, "%.*s", data.len, data.ptr);
459 *msglen = data.len;
460
461 if (data.len > *buflen)
462 {
463 DBG1(DBG_TNC, "fragmentation of TNCCS batch not supported yet");
464 }
465 else
466 {
467 *buflen = data.len;
468 }
469 memcpy(buf, data.ptr, *buflen);
470 this->batch->destroy(this->batch);
471 this->batch = NULL;
472 status = ALREADY_DONE;
473 }
474 else
475 {
476 DBG1(DBG_TNC, "no TNCCS Batch to send");
477 status = INVALID_STATE;
478 }
479 this->mutex->unlock(this->mutex);
480
481 return status;
482 }
483
484 METHOD(tls_t, is_server, bool,
485 private_tnccs_11_t *this)
486 {
487 return this->is_server;
488 }
489
490 METHOD(tls_t, get_purpose, tls_purpose_t,
491 private_tnccs_11_t *this)
492 {
493 return TLS_PURPOSE_EAP_TNC;
494 }
495
496 METHOD(tls_t, is_complete, bool,
497 private_tnccs_11_t *this)
498 {
499 TNC_IMV_Action_Recommendation rec;
500 TNC_IMV_Evaluation_Result eval;
501
502 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
503 {
504 return charon->imvs->enforce_recommendation(charon->imvs, rec, eval);
505 }
506 else
507 {
508 return FALSE;
509 }
510 }
511
512 METHOD(tls_t, get_eap_msk, chunk_t,
513 private_tnccs_11_t *this)
514 {
515 return chunk_empty;
516 }
517
518 METHOD(tls_t, destroy, void,
519 private_tnccs_11_t *this)
520 {
521 charon->tnccs->remove_connection(charon->tnccs, this->connection_id,
522 this->is_server);
523 this->mutex->destroy(this->mutex);
524 DESTROY_IF(this->batch);
525 free(this);
526 }
527
528 /**
529 * See header
530 */
531 tls_t *tnccs_11_create(bool is_server)
532 {
533 private_tnccs_11_t *this;
534
535 INIT(this,
536 .public = {
537 .process = _process,
538 .build = _build,
539 .is_server = _is_server,
540 .get_purpose = _get_purpose,
541 .is_complete = _is_complete,
542 .get_eap_msk = _get_eap_msk,
543 .destroy = _destroy,
544 },
545 .is_server = is_server,
546 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
547 .imcs = lib->get(lib, "imc-manager"),
548 .imvs = lib->get(lib, "imv-manager"),
549 );
550
551 return &this->public;
552 }