pb_tnc_batch_t class implements parsing and building of PB-TNC batches
[strongswan.git] / src / libcharon / plugins / tnccs_20 / tnccs_20.c
1 /*
2 * Copyright (C) 2010 Sansar Choinyanbuu
3 * Copyright (C) 2010 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "tnccs_20.h"
18 #include "tnccs_20_types.h"
19 #include "batch/pb_tnc_batch.h"
20 #include "messages/pb_tnc_message.h"
21 #include "messages/pb_pa_message.h"
22 #include "messages/pb_error_message.h"
23 #include "messages/pb_language_preference_message.h"
24
25 #include <debug.h>
26 #include <daemon.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_20_t private_tnccs_20_t;
33
34 /**
35 * Private data of a tnccs_20_t object.
36 */
37 struct private_tnccs_20_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 * PB-TNC State Machine
51 */
52 pb_tnc_state_t state;
53
54 /**
55 * Connection ID assigned to this TNCCS connection
56 */
57 TNC_ConnectionID connection_id;
58
59 /**
60 * PB-TNC batch being constructed
61 */
62 pb_tnc_batch_t *batch;
63
64 /**
65 * Mutex locking the batch in construction
66 */
67 mutex_t *mutex;
68
69 /**
70 * Flag set by IMC/IMV RequestHandshakeRetry() function
71 */
72 bool request_handshake_retry;
73
74 /**
75 * Set of IMV recommendations (TNC Server only)
76 */
77 recommendations_t *recs;
78 };
79
80 METHOD(tnccs_t, send_message, void,
81 private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
82 TNC_BufferReference msg,
83 TNC_UInt32 msg_len,
84 TNC_MessageType msg_type)
85 {
86 TNC_MessageSubtype msg_sub_type;
87 TNC_VendorID msg_vendor_id;
88 pb_tnc_message_t *pb_tnc_msg;
89 pb_tnc_batch_type_t batch_type;
90
91 msg_sub_type = msg_type & TNC_SUBTYPE_ANY;
92 msg_vendor_id = (msg_type >> 8) & TNC_VENDORID_ANY;
93
94 pb_tnc_msg = pb_pa_message_create(msg_vendor_id, msg_sub_type, imc_id, imv_id,
95 chunk_create(msg, msg_len));
96
97 /* adding PA message to SDATA or CDATA batch only */
98 batch_type = this->is_server ? PB_BATCH_SDATA : PB_BATCH_CDATA;
99 this->mutex->lock(this->mutex);
100 if (!this->batch)
101 {
102 this->batch = pb_tnc_batch_create(this->is_server, batch_type);
103 }
104 if (this->batch->get_type(this->batch) == batch_type)
105 {
106 this->batch->add_message(this->batch, pb_tnc_msg);
107 }
108 else
109 {
110 pb_tnc_msg->destroy(pb_tnc_msg);
111 }
112 this->mutex->unlock(this->mutex);
113 }
114
115 METHOD(tls_t, process, status_t,
116 private_tnccs_20_t *this, void *buf, size_t buflen)
117 {
118 chunk_t data;
119 pb_tnc_batch_t *batch;
120 pb_tnc_message_t *msg;
121 pb_tnc_state_t old_state;
122 enumerator_t *enumerator;
123 status_t status;
124
125 if (this->is_server && !this->connection_id)
126 {
127 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
128 (tnccs_t*)this, _send_message,
129 &this->request_handshake_retry, &this->recs);
130 if (!this->connection_id)
131 {
132 return FAILED;
133 }
134 charon->imvs->notify_connection_change(charon->imvs,
135 this->connection_id, TNC_CONNECTION_STATE_CREATE);
136 }
137 data = chunk_create(buf, buflen);
138 DBG1(DBG_TNC, "received TNCCS Batch (%u bytes) for Connection ID %u",
139 data.len, this->connection_id);
140 DBG3(DBG_TNC, "%B", &data);
141 batch = pb_tnc_batch_create_from_data(this->is_server, data);
142
143 old_state = this->state;
144 status = batch->process(batch, &this->state);
145 if (this->state != old_state)
146 {
147 DBG2(DBG_TNC, "PB-TNC state transition from '%N' to '%N'",
148 pb_tnc_state_names, old_state, pb_tnc_state_names, this->state);
149 }
150 switch (status)
151 {
152 case SUCCESS:
153 default:
154 break;
155 case FAILED:
156 if (this->batch)
157 {
158 DBG1(DBG_TNC, "cancelling PB-TNC %N Batch",
159 pb_tnc_batch_type_names, this->batch->get_type(this->batch));
160 this->batch->destroy(this->batch);
161 }
162 this->batch = pb_tnc_batch_create(this->is_server, PB_BATCH_CLOSE);
163 /* fall through */
164 case VERIFY_ERROR:
165 enumerator = batch->create_error_enumerator(batch);
166 while (enumerator->enumerate(enumerator, &msg))
167 {
168 this->batch->add_message(this->batch, msg->get_ref(msg));
169 }
170 enumerator->destroy(enumerator);
171 }
172 batch->destroy(batch);
173
174 if (this->is_server)
175 {
176 charon->imvs->batch_ending(charon->imvs, this->connection_id);
177 }
178 else
179 {
180 charon->imcs->batch_ending(charon->imcs, this->connection_id);
181 }
182 return NEED_MORE;
183 }
184
185 METHOD(tls_t, build, status_t,
186 private_tnccs_20_t *this, void *buf, size_t *buflen, size_t *msglen)
187 {
188 if (!this->is_server && !this->connection_id)
189 {
190 pb_tnc_message_t *msg;
191 char *pref_lang;
192
193 this->connection_id = charon->tnccs->create_connection(charon->tnccs,
194 (tnccs_t*)this, _send_message,
195 &this->request_handshake_retry, NULL);
196 if (!this->connection_id)
197 {
198 return FAILED;
199 }
200
201 /* Create PB-TNC Language Preference Message */
202 pref_lang = charon->imcs->get_preferred_language(charon->imcs);
203 msg = pb_language_preference_message_create(chunk_create(pref_lang,
204 strlen(pref_lang)));
205 this->mutex->lock(this->mutex);
206 this->batch = pb_tnc_batch_create(this->is_server, PB_BATCH_CDATA);
207 this->batch->add_message(this->batch, msg);
208 this->mutex->unlock(this->mutex);
209
210 charon->imcs->notify_connection_change(charon->imcs,
211 this->connection_id, TNC_CONNECTION_STATE_CREATE);
212 charon->imcs->notify_connection_change(charon->imcs,
213 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
214 charon->imcs->begin_handshake(charon->imcs, this->connection_id);
215 }
216
217 if (this->batch)
218 {
219 pb_tnc_batch_type_t batch_type;
220 pb_tnc_state_t old_state;
221 status_t status;
222 chunk_t data;
223 bool unexpected_batch_type = FALSE;
224
225 batch_type = this->batch->get_type(this->batch);
226 old_state = this->state;
227
228 switch (this->state)
229 {
230 case PB_STATE_INIT:
231 if (batch_type == PB_BATCH_CDATA)
232 {
233 this->state = PB_STATE_SERVER_WORKING;
234 break;
235 }
236 if (batch_type == PB_BATCH_SDATA)
237 {
238 this->state = PB_STATE_CLIENT_WORKING;
239 break;
240 }
241 if (batch_type == PB_BATCH_CLOSE)
242 {
243 this->state = PB_STATE_END;
244 break;
245 }
246 unexpected_batch_type = TRUE;
247 break;
248 case PB_STATE_SERVER_WORKING:
249 if (batch_type == PB_BATCH_SDATA)
250 {
251 this->state = PB_STATE_CLIENT_WORKING;
252 break;
253 }
254 if (batch_type == PB_BATCH_RESULT)
255 {
256 this->state = PB_STATE_DECIDED;
257 break;
258 }
259 if (batch_type == PB_BATCH_CRETRY ||
260 batch_type == PB_BATCH_SRETRY)
261 {
262 break;
263 }
264 if (batch_type == PB_BATCH_CLOSE)
265 {
266 this->state = PB_STATE_END;
267 break;
268 }
269 unexpected_batch_type = TRUE;
270 break;
271 case PB_STATE_CLIENT_WORKING:
272 if (batch_type == PB_BATCH_CDATA)
273 {
274 this->state = PB_STATE_SERVER_WORKING;
275 break;
276 }
277 if (batch_type == PB_BATCH_CRETRY)
278 {
279 break;
280 }
281 if (batch_type == PB_BATCH_CLOSE)
282 {
283 this->state = PB_STATE_END;
284 break;
285 }
286 unexpected_batch_type = TRUE;
287 break;
288 case PB_STATE_DECIDED:
289 if (batch_type == PB_BATCH_CRETRY ||
290 batch_type == PB_BATCH_SRETRY)
291 {
292 this->state = PB_STATE_SERVER_WORKING;
293 break;
294 }
295 if (batch_type == PB_BATCH_CLOSE)
296 {
297 this->state = PB_STATE_END;
298 break;
299 }
300 unexpected_batch_type = TRUE;
301 break;
302 case PB_STATE_END:
303 if (batch_type == PB_BATCH_CLOSE)
304 {
305 break;
306 }
307 unexpected_batch_type = TRUE;
308 }
309
310 this->mutex->lock(this->mutex);
311 if (unexpected_batch_type)
312 {
313 DBG1(DBG_TNC, "cancelling unexpected PB-TNC Batch Type: %N",
314 pb_tnc_batch_type_names, batch_type);
315 status = INVALID_STATE;
316 }
317 else
318 {
319 this->batch->build(this->batch);
320 data = this->batch->get_encoding(this->batch);
321 DBG1(DBG_TNC, "sending PB-TNC %N Batch (%d bytes) for Connection ID %u",
322 pb_tnc_batch_type_names, batch_type, data.len,
323 this->connection_id);
324 DBG3(DBG_TNC, "%B", &data);
325
326 *msglen = data.len;
327 *buflen = data.len;
328 memcpy(buf, data.ptr, data.len);
329 status = ALREADY_DONE;
330 }
331 this->batch->destroy(this->batch);
332 this->batch = NULL;
333 this->mutex->unlock(this->mutex);
334
335 if (this->state != old_state)
336 {
337 DBG2(DBG_TNC, "PB-TNC state transition from '%N' to '%N'",
338 pb_tnc_state_names, old_state, pb_tnc_state_names, this->state);
339 }
340 return status;
341 }
342 else
343 {
344 DBG1(DBG_TNC, "no TNCCS Batch to send");
345 return INVALID_STATE;
346 }
347 }
348
349 METHOD(tls_t, is_server, bool,
350 private_tnccs_20_t *this)
351 {
352 return this->is_server;
353 }
354
355 METHOD(tls_t, get_purpose, tls_purpose_t,
356 private_tnccs_20_t *this)
357 {
358 return TLS_PURPOSE_EAP_TNC;
359 }
360
361 METHOD(tls_t, is_complete, bool,
362 private_tnccs_20_t *this)
363 {
364 TNC_IMV_Action_Recommendation rec;
365 TNC_IMV_Evaluation_Result eval;
366
367 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
368 {
369 DBG2(DBG_TNC, "Final recommendation '%N' and evaluation '%N'",
370 action_recommendation_names, rec, evaluation_result_names, eval);
371
372 return charon->imvs->enforce_recommendation(charon->imvs, rec);
373 }
374 else
375 {
376 return FALSE;
377 }
378 }
379
380 METHOD(tls_t, get_eap_msk, chunk_t,
381 private_tnccs_20_t *this)
382 {
383 return chunk_empty;
384 }
385
386 METHOD(tls_t, destroy, void,
387 private_tnccs_20_t *this)
388 {
389 charon->tnccs->remove_connection(charon->tnccs, this->connection_id);
390 this->mutex->destroy(this->mutex);
391 DESTROY_IF(this->batch);
392 free(this);
393 }
394
395 /**
396 * See header
397 */
398 tls_t *tnccs_20_create(bool is_server)
399 {
400 private_tnccs_20_t *this;
401
402 INIT(this,
403 .public = {
404 .process = _process,
405 .build = _build,
406 .is_server = _is_server,
407 .get_purpose = _get_purpose,
408 .is_complete = _is_complete,
409 .get_eap_msk = _get_eap_msk,
410 .destroy = _destroy,
411 },
412 .is_server = is_server,
413 .state = PB_STATE_INIT,
414 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
415 );
416
417 return &this->public;
418 }