db1a98f5879b05b5136bfe8c6f9b12138c702629
[strongswan.git] / src / libpts / plugins / imv_swid / imv_swid_agent.c
1 /*
2 * Copyright (C) 2013 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 "imv_swid_agent.h"
17 #include "imv_swid_state.h"
18
19 #include "libpts.h"
20 #include "swid/swid_error.h"
21 #include "tcg/swid/tcg_swid_attr_req.h"
22 #include "tcg/swid/tcg_swid_attr_tag_inv.h"
23 #include "tcg/swid/tcg_swid_attr_tag_id_inv.h"
24
25 #include <imcv.h>
26 #include <ietf/ietf_attr_pa_tnc_error.h>
27 #include <imv/imv_agent.h>
28 #include <imv/imv_msg.h>
29
30 #include <tncif_names.h>
31 #include <tncif_pa_subtypes.h>
32
33 #include <pen/pen.h>
34 #include <utils/debug.h>
35 #include <bio/bio_reader.h>
36
37 typedef struct private_imv_swid_agent_t private_imv_swid_agent_t;
38
39 /* Subscribed PA-TNC message subtypes */
40 static pen_type_t msg_types[] = {
41 { PEN_TCG, PA_SUBTYPE_TCG_SWID }
42 };
43
44 /**
45 * Private data of an imv_swid_agent_t object.
46 */
47 struct private_imv_swid_agent_t {
48
49 /**
50 * Public members of imv_swid_agent_t
51 */
52 imv_agent_if_t public;
53
54 /**
55 * IMV agent responsible for generic functions
56 */
57 imv_agent_t *agent;
58
59 };
60
61 METHOD(imv_agent_if_t, bind_functions, TNC_Result,
62 private_imv_swid_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function)
63 {
64 return this->agent->bind_functions(this->agent, bind_function);
65 }
66
67 METHOD(imv_agent_if_t, notify_connection_change, TNC_Result,
68 private_imv_swid_agent_t *this, TNC_ConnectionID id,
69 TNC_ConnectionState new_state)
70 {
71 imv_state_t *state;
72
73 switch (new_state)
74 {
75 case TNC_CONNECTION_STATE_CREATE:
76 state = imv_swid_state_create(id);
77 return this->agent->create_state(this->agent, state);
78 case TNC_CONNECTION_STATE_DELETE:
79 return this->agent->delete_state(this->agent, id);
80 default:
81 return this->agent->change_state(this->agent, id, new_state, NULL);
82 }
83 }
84
85 /**
86 * Process a received message
87 */
88 static TNC_Result receive_msg(private_imv_swid_agent_t *this,
89 imv_state_t *state, imv_msg_t *in_msg)
90 {
91 imv_msg_t *out_msg;
92 imv_session_t *session;
93 enumerator_t *enumerator;
94 pa_tnc_attr_t *attr;
95 TNC_Result result;
96 bool fatal_error = FALSE;
97
98 /* parse received PA-TNC message and handle local and remote errors */
99 result = in_msg->receive(in_msg, &fatal_error);
100 if (result != TNC_RESULT_SUCCESS)
101 {
102 return result;
103 }
104
105 session = state->get_session(state);
106
107 /* analyze PA-TNC attributes */
108 enumerator = in_msg->create_attribute_enumerator(in_msg);
109 while (enumerator->enumerate(enumerator, &attr))
110 {
111 TNC_IMV_Evaluation_Result eval;
112 TNC_IMV_Action_Recommendation rec;
113 pen_type_t type;
114 u_int32_t request_id, last_eid, eid_epoch;
115 int tag_count = 0;
116 char result_str[BUF_LEN], *tag_item;
117 imv_workitem_t *workitem, *found = NULL;
118 enumerator_t *et, *ew;
119
120 type = attr->get_type(attr);
121
122 if (type.vendor_id == PEN_IETF && type.type == IETF_ATTR_PA_TNC_ERROR)
123 {
124 ietf_attr_pa_tnc_error_t *error_attr;
125 pen_type_t error_code;
126 chunk_t msg_info, description;
127 bio_reader_t *reader;
128 u_int32_t request_id = 0, max_attr_size;
129 bool success;
130
131 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
132 error_code = error_attr->get_error_code(error_attr);
133
134 if (error_code.vendor_id == PEN_TCG)
135 {
136 fatal_error = TRUE;
137 msg_info = error_attr->get_msg_info(error_attr);
138 reader = bio_reader_create(msg_info);
139 success = reader->read_uint32(reader, &request_id);
140
141 DBG1(DBG_IMV, "received TCG error '%N' for request %d",
142 swid_error_code_names, error_code.type, request_id);
143 if (!success)
144 {
145 reader->destroy(reader);
146 continue;
147 }
148 if (error_code.type == TCG_SWID_RESPONSE_TOO_LARGE)
149 {
150 if (!reader->read_uint32(reader, &max_attr_size))
151 {
152 reader->destroy(reader);
153 continue;
154 }
155 DBG1(DBG_IMV, " maximum PA-TNC attribute size is %u bytes",
156 max_attr_size);
157 }
158 description = reader->peek(reader);
159 if (description.len)
160 {
161 DBG1(DBG_IMV, " description: %.*s", description.len,
162 description.ptr);
163 }
164 reader->destroy(reader);
165 }
166 }
167 else if (type.vendor_id != PEN_TCG)
168 {
169 continue;
170 }
171
172 switch (type.type)
173 {
174 case TCG_SWID_TAG_ID_INVENTORY:
175 {
176 tcg_swid_attr_tag_id_inv_t *attr_cast;
177 swid_tag_id_t *tag_id;
178 chunk_t tag_creator, unique_sw_id;
179
180 attr_cast = (tcg_swid_attr_tag_id_inv_t*)attr;
181 request_id = attr_cast->get_request_id(attr_cast);
182 last_eid = attr_cast->get_last_eid(attr_cast, &eid_epoch);
183 tag_item = "tag ID";
184 DBG2(DBG_IMV, "received SWID %s inventory for request %d "
185 "at eid %d of epoch 0x%08x", tag_item,
186 request_id, last_eid, eid_epoch);
187
188 et = attr_cast->create_tag_id_enumerator(attr_cast);
189 while (et->enumerate(et, &tag_id))
190 {
191 tag_count++;
192 tag_creator = tag_id->get_tag_creator(tag_id);
193 unique_sw_id = tag_id->get_unique_sw_id(tag_id, NULL);
194 DBG3(DBG_IMV, " %.*s_%.*s.swidtag",
195 tag_creator.len, tag_creator.ptr,
196 unique_sw_id.len, unique_sw_id.ptr);
197 }
198 et->destroy(et);
199
200 if (request_id == 0)
201 {
202 /* TODO handle subscribed messages */
203 break;
204 }
205 break;
206 }
207 case TCG_SWID_TAG_INVENTORY:
208 {
209 tcg_swid_attr_tag_inv_t *attr_cast;
210 swid_tag_t *tag;
211 chunk_t tag_encoding;
212
213 attr_cast = (tcg_swid_attr_tag_inv_t*)attr;
214 request_id = attr_cast->get_request_id(attr_cast);
215 last_eid = attr_cast->get_last_eid(attr_cast, &eid_epoch);
216 tag_item = "tag";
217 DBG2(DBG_IMV, "received SWID %s inventory for request %d "
218 "at eid %d of epoch 0x%08x", tag_item,
219 request_id, last_eid, eid_epoch);
220
221 et = attr_cast->create_tag_enumerator(attr_cast);
222 while (et->enumerate(et, &tag))
223 {
224 tag_count++;
225 tag_encoding = tag->get_encoding(tag);
226 DBG3(DBG_IMV, "%.*s", tag_encoding.len, tag_encoding.ptr);
227 }
228 et->destroy(et);
229
230 if (request_id == 0)
231 {
232 /* TODO handle subscribed messages */
233 break;
234 }
235 break;
236 }
237 default:
238 continue;
239 }
240
241 ew = session->create_workitem_enumerator(session);
242 while (ew->enumerate(ew, &workitem))
243 {
244 if (workitem->get_id(workitem) == request_id)
245 {
246 found = workitem;
247 break;
248 }
249 }
250 if (!found)
251 {
252 DBG1(DBG_IMV, "no workitem found for SWID %s inventory "
253 "with request ID %d", tag_item, request_id);
254 ew->destroy(ew);
255 continue;
256 }
257
258 eval = TNC_IMV_EVALUATION_RESULT_COMPLIANT;
259 snprintf(result_str, BUF_LEN, "received inventory of %d SWID %s%s",
260 tag_count, tag_item, (tag_count == 1) ? "" : "s");
261 session->remove_workitem(session, ew);
262 ew->destroy(ew);
263 rec = found->set_result(found, result_str, eval);
264 state->update_recommendation(state, rec, eval);
265 imcv_db->finalize_workitem(imcv_db, found);
266 found->destroy(found);
267 }
268 enumerator->destroy(enumerator);
269
270 if (fatal_error)
271 {
272 state->set_recommendation(state,
273 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
274 TNC_IMV_EVALUATION_RESULT_ERROR);
275 out_msg = imv_msg_create_as_reply(in_msg);
276 result = out_msg->send_assessment(out_msg);
277 out_msg->destroy(out_msg);
278 if (result != TNC_RESULT_SUCCESS)
279 {
280 return result;
281 }
282 return this->agent->provide_recommendation(this->agent, state);
283 }
284
285 return TNC_RESULT_SUCCESS;
286 }
287
288 METHOD(imv_agent_if_t, receive_message, TNC_Result,
289 private_imv_swid_agent_t *this, TNC_ConnectionID id,
290 TNC_MessageType msg_type, chunk_t msg)
291 {
292 imv_state_t *state;
293 imv_msg_t *in_msg;
294 TNC_Result result;
295
296 if (!this->agent->get_state(this->agent, id, &state))
297 {
298 return TNC_RESULT_FATAL;
299 }
300 in_msg = imv_msg_create_from_data(this->agent, state, id, msg_type, msg);
301 result = receive_msg(this, state, in_msg);
302 in_msg->destroy(in_msg);
303
304 return result;
305 }
306
307 METHOD(imv_agent_if_t, receive_message_long, TNC_Result,
308 private_imv_swid_agent_t *this, TNC_ConnectionID id,
309 TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id,
310 TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype, chunk_t msg)
311 {
312 imv_state_t *state;
313 imv_msg_t *in_msg;
314 TNC_Result result;
315
316 if (!this->agent->get_state(this->agent, id, &state))
317 {
318 return TNC_RESULT_FATAL;
319 }
320 in_msg = imv_msg_create_from_long_data(this->agent, state, id,
321 src_imc_id, dst_imv_id, msg_vid, msg_subtype, msg);
322 result = receive_msg(this, state, in_msg);
323 in_msg->destroy(in_msg);
324
325 return result;
326
327 }
328
329 METHOD(imv_agent_if_t, batch_ending, TNC_Result,
330 private_imv_swid_agent_t *this, TNC_ConnectionID id)
331 {
332 imv_msg_t *out_msg;
333 imv_state_t *state;
334 imv_session_t *session;
335 imv_workitem_t *workitem;
336 imv_swid_state_t *swid_state;
337 imv_swid_handshake_state_t handshake_state;
338 pa_tnc_attr_t *attr;
339 TNC_IMVID imv_id;
340 TNC_Result result = TNC_RESULT_SUCCESS;
341 bool no_workitems = TRUE;
342 u_int32_t request_id;
343 u_int8_t flags;
344 enumerator_t *enumerator;
345
346 if (!this->agent->get_state(this->agent, id, &state))
347 {
348 return TNC_RESULT_FATAL;
349 }
350 swid_state = (imv_swid_state_t*)state;
351 handshake_state = swid_state->get_handshake_state(swid_state);
352 session = state->get_session(state);
353 imv_id = this->agent->get_id(this->agent);
354
355 if (handshake_state == IMV_SWID_STATE_END)
356 {
357 return TNC_RESULT_SUCCESS;
358 }
359
360 /* create an empty out message - we might need it */
361 out_msg = imv_msg_create(this->agent, state, id, imv_id, TNC_IMCID_ANY,
362 msg_types[0]);
363
364 if (!session)
365 {
366 DBG2(DBG_IMV, "no workitems available - no evaluation possible");
367 state->set_recommendation(state,
368 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
369 TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
370 result = out_msg->send_assessment(out_msg);
371 out_msg->destroy(out_msg);
372 swid_state->set_handshake_state(swid_state, IMV_SWID_STATE_END);
373
374 if (result != TNC_RESULT_SUCCESS)
375 {
376 return result;
377 }
378 return this->agent->provide_recommendation(this->agent, state);
379 }
380
381 if (handshake_state == IMV_SWID_STATE_INIT)
382 {
383 enumerator = session->create_workitem_enumerator(session);
384 if (enumerator)
385 {
386 while (enumerator->enumerate(enumerator, &workitem))
387 {
388 if (workitem->get_imv_id(workitem) != TNC_IMVID_ANY ||
389 workitem->get_type(workitem) != IMV_WORKITEM_SWID_TAGS)
390 {
391 continue;
392 }
393
394 flags = TCG_SWID_ATTR_REQ_FLAG_NONE;
395 if (strchr(workitem->get_arg_str(workitem), 'R'))
396 {
397 flags |= TCG_SWID_ATTR_REQ_FLAG_R;
398 }
399 if (strchr(workitem->get_arg_str(workitem), 'S'))
400 {
401 flags |= TCG_SWID_ATTR_REQ_FLAG_S;
402 }
403 if (strchr(workitem->get_arg_str(workitem), 'C'))
404 {
405 flags |= TCG_SWID_ATTR_REQ_FLAG_C;
406 }
407 request_id = workitem->get_id(workitem);
408
409 attr = tcg_swid_attr_req_create(flags, request_id, 0);
410 out_msg->add_attribute(out_msg, attr);
411 workitem->set_imv_id(workitem, imv_id);
412 no_workitems = FALSE;
413 DBG2(DBG_IMV, "IMV %d issues SWID request %d",
414 imv_id, request_id);
415 }
416 enumerator->destroy(enumerator);
417
418 if (no_workitems)
419 {
420 DBG2(DBG_IMV, "IMV %d has no workitems - "
421 "no evaluation requested", imv_id);
422 state->set_recommendation(state,
423 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
424 TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
425 }
426 handshake_state = IMV_SWID_STATE_WORKITEMS;
427 swid_state->set_handshake_state(swid_state, handshake_state);
428 }
429 }
430
431 /* finalized all workitems ? */
432 if (handshake_state == IMV_SWID_STATE_WORKITEMS &&
433 session->get_workitem_count(session, imv_id) == 0)
434 {
435 result = out_msg->send_assessment(out_msg);
436 out_msg->destroy(out_msg);
437 swid_state->set_handshake_state(swid_state, IMV_SWID_STATE_END);
438
439 if (result != TNC_RESULT_SUCCESS)
440 {
441 return result;
442 }
443 return this->agent->provide_recommendation(this->agent, state);
444 }
445
446 /* send non-empty PA-TNC message with excl flag not set */
447 if (out_msg->get_attribute_count(out_msg))
448 {
449 result = out_msg->send(out_msg, FALSE);
450 }
451 out_msg->destroy(out_msg);
452
453 return result;
454 }
455
456 METHOD(imv_agent_if_t, solicit_recommendation, TNC_Result,
457 private_imv_swid_agent_t *this, TNC_ConnectionID id)
458 {
459 imv_state_t *state;
460
461 if (!this->agent->get_state(this->agent, id, &state))
462 {
463 return TNC_RESULT_FATAL;
464 }
465 return this->agent->provide_recommendation(this->agent, state);
466 }
467
468 METHOD(imv_agent_if_t, destroy, void,
469 private_imv_swid_agent_t *this)
470 {
471 this->agent->destroy(this->agent);
472 free(this);
473 libpts_deinit();
474 }
475
476 /**
477 * Described in header.
478 */
479 imv_agent_if_t *imv_swid_agent_create(const char *name, TNC_IMVID id,
480 TNC_Version *actual_version)
481 {
482 private_imv_swid_agent_t *this;
483 imv_agent_t *agent;
484
485 agent = imv_agent_create(name, msg_types, countof(msg_types), id,
486 actual_version);
487 if (!agent)
488 {
489 return NULL;
490 }
491
492 INIT(this,
493 .public = {
494 .bind_functions = _bind_functions,
495 .notify_connection_change = _notify_connection_change,
496 .receive_message = _receive_message,
497 .receive_message_long = _receive_message_long,
498 .batch_ending = _batch_ending,
499 .solicit_recommendation = _solicit_recommendation,
500 .destroy = _destroy,
501 },
502 .agent = agent,
503 );
504
505 libpts_init();
506
507 return &this->public;
508 }
509