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