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