a10aa26580f11312cf12621ea755e99e995a5c26
[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_names.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 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 default:
203 break;
204 }
205 }
206
207 METHOD(tls_t, process, status_t,
208 private_tnccs_11_t *this, void *buf, size_t buflen)
209 {
210 chunk_t data;
211 tnccs_batch_t *batch;
212 tnccs_msg_t *msg;
213 enumerator_t *enumerator;
214 status_t status;
215
216 if (this->is_server && !this->connection_id)
217 {
218 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
219 (tnccs_t*)this, _send_msg,
220 &this->request_handshake_retry, &this->recs);
221 if (!this->connection_id)
222 {
223 return FAILED;
224 }
225 charon->imvs->notify_connection_change(charon->imvs,
226 this->connection_id, TNC_CONNECTION_STATE_CREATE);
227 }
228
229 data = chunk_create(buf, buflen);
230 DBG1(DBG_TNC, "received TNCCS Batch (%u bytes) for Connection ID %u",
231 data.len, this->connection_id);
232 DBG3(DBG_TNC, "%.*s", data.len, data.ptr);
233 batch = tnccs_batch_create_from_data(this->is_server, ++this->batch_id, data);
234 status = batch->process(batch);
235
236 if (status == FAILED)
237 {
238 this->fatal_error = TRUE;
239 this->mutex->lock(this->mutex);
240 if (this->batch)
241 {
242 DBG1(DBG_TNC, "cancelling TNCCS batch");
243 this->batch->destroy(this->batch);
244 this->batch_id--;
245 }
246 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
247
248 /* add error messages to outbound batch */
249 enumerator = batch->create_error_enumerator(batch);
250 while (enumerator->enumerate(enumerator, &msg))
251 {
252 this->batch->add_msg(this->batch, msg->get_ref(msg));
253 }
254 enumerator->destroy(enumerator);
255 this->mutex->unlock(this->mutex);
256 }
257 else
258 {
259 enumerator = batch->create_msg_enumerator(batch);
260 while (enumerator->enumerate(enumerator, &msg))
261 {
262 handle_message(this, msg);
263 }
264 enumerator->destroy(enumerator);
265
266 /* received any TNCCS-Error messages */
267 if (this->fatal_error)
268 {
269 DBG1(DBG_TNC, "a fatal TNCCS-Error occurred, terminating connection");
270 batch->destroy(batch);
271 return FAILED;
272 }
273
274 if (this->is_server)
275 {
276 charon->imvs->batch_ending(charon->imvs, this->connection_id);
277 }
278 else
279 {
280 charon->imcs->batch_ending(charon->imcs, this->connection_id);
281 }
282 }
283 batch->destroy(batch);
284
285 return NEED_MORE;
286 }
287
288 METHOD(tls_t, build, status_t,
289 private_tnccs_11_t *this, void *buf, size_t *buflen, size_t *msglen)
290 {
291 status_t status;
292
293 /* Initialize the connection */
294 if (!this->is_server && !this->connection_id)
295 {
296 tnccs_msg_t *msg;
297 char *pref_lang;
298
299 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
300 (tnccs_t*)this, _send_msg,
301 &this->request_handshake_retry, NULL);
302 if (!this->connection_id)
303 {
304 return FAILED;
305 }
306
307 /* Create TNCCS-PreferredLanguage message */
308 pref_lang = charon->imcs->get_preferred_language(charon->imcs);
309 msg = tnccs_preferred_language_msg_create(pref_lang);
310 this->mutex->lock(this->mutex);
311 this->batch = tnccs_batch_create(this->is_server, ++this->batch_id);
312 this->batch->add_msg(this->batch, msg);
313 this->mutex->unlock(this->mutex);
314
315 charon->imcs->notify_connection_change(charon->imcs,
316 this->connection_id, TNC_CONNECTION_STATE_CREATE);
317 charon->imcs->notify_connection_change(charon->imcs,
318 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
319 charon->imcs->begin_handshake(charon->imcs, this->connection_id);
320 }
321
322 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
323 this->mutex->lock(this->mutex);
324
325 if (this->batch)
326 {
327 chunk_t data;
328
329 this->batch->build(this->batch);
330 data = this->batch->get_encoding(this->batch);
331 DBG1(DBG_TNC, "sending TNCCS Batch (%d bytes) for Connection ID %u",
332 data.len, this->connection_id);
333 DBG3(DBG_TNC, "%.*s", data.len, data.ptr);
334 *msglen = data.len;
335
336 if (data.len > *buflen)
337 {
338 DBG1(DBG_TNC, "fragmentation of TNCCS batch not supported yet");
339 }
340 else
341 {
342 *buflen = data.len;
343 }
344 memcpy(buf, data.ptr, *buflen);
345 this->batch->destroy(this->batch);
346 this->batch = NULL;
347 status = ALREADY_DONE;
348 }
349 else
350 {
351 DBG1(DBG_TNC, "no TNCCS Batch to send");
352 status = INVALID_STATE;
353 }
354 this->mutex->unlock(this->mutex);
355
356 return status;
357 }
358
359 METHOD(tls_t, is_server, bool,
360 private_tnccs_11_t *this)
361 {
362 return this->is_server;
363 }
364
365 METHOD(tls_t, get_purpose, tls_purpose_t,
366 private_tnccs_11_t *this)
367 {
368 return TLS_PURPOSE_EAP_TNC;
369 }
370
371 METHOD(tls_t, is_complete, bool,
372 private_tnccs_11_t *this)
373 {
374 TNC_IMV_Action_Recommendation rec;
375 TNC_IMV_Evaluation_Result eval;
376
377 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
378 {
379 DBG2(DBG_TNC, "Final recommendation is '%N' and evaluation is '%N'",
380 action_recommendation_names, rec, evaluation_result_names, eval);
381
382 return charon->imvs->enforce_recommendation(charon->imvs, rec);
383 }
384 else
385 {
386 return FALSE;
387 }
388 }
389
390 METHOD(tls_t, get_eap_msk, chunk_t,
391 private_tnccs_11_t *this)
392 {
393 return chunk_empty;
394 }
395
396 METHOD(tls_t, destroy, void,
397 private_tnccs_11_t *this)
398 {
399 if (this->is_server)
400 {
401 charon->imvs->notify_connection_change(charon->imvs,
402 this->connection_id, TNC_CONNECTION_STATE_DELETE);
403 }
404 else
405 {
406 charon->imcs->notify_connection_change(charon->imcs,
407 this->connection_id, TNC_CONNECTION_STATE_DELETE);
408 }
409 charon->tnccs->remove_connection(charon->tnccs, this->connection_id);
410 this->mutex->destroy(this->mutex);
411 DESTROY_IF(this->batch);
412 free(this);
413 }
414
415 /**
416 * See header
417 */
418 tls_t *tnccs_11_create(bool is_server)
419 {
420 private_tnccs_11_t *this;
421
422 INIT(this,
423 .public = {
424 .process = _process,
425 .build = _build,
426 .is_server = _is_server,
427 .get_purpose = _get_purpose,
428 .is_complete = _is_complete,
429 .get_eap_msk = _get_eap_msk,
430 .destroy = _destroy,
431 },
432 .is_server = is_server,
433 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
434 );
435
436 return &this->public;
437 }