2 * Copyright (C) 2013-2014 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
19 #include "imv_swid_agent.h"
20 #include "imv_swid_state.h"
21 #include "imv_swid_rest.h"
24 #include "swid/swid_error.h"
25 #include "swid/swid_inventory.h"
26 #include "tcg/swid/tcg_swid_attr_req.h"
27 #include "tcg/swid/tcg_swid_attr_tag_inv.h"
28 #include "tcg/swid/tcg_swid_attr_tag_id_inv.h"
31 #include <ietf/ietf_attr_pa_tnc_error.h>
32 #include <imv/imv_agent.h>
33 #include <imv/imv_msg.h>
34 #include <ita/ita_attr.h>
35 #include <ita/ita_attr_angel.h>
37 #include <tncif_names.h>
38 #include <tncif_pa_subtypes.h>
41 #include <utils/debug.h>
42 #include <bio/bio_reader.h>
44 typedef struct private_imv_swid_agent_t private_imv_swid_agent_t
;
46 /* Subscribed PA-TNC message subtypes */
47 static pen_type_t msg_types
[] = {
48 { PEN_TCG
, PA_SUBTYPE_TCG_SWID
}
52 * Flag set when corresponding attribute has been received
54 enum imv_swid_attr_t
{
55 IMV_SWID_ATTR_TAG_INV
= (1<<0),
56 IMV_SWID_ATTR_TAG_ID_INV
= (1<<1)
60 * Private data of an imv_swid_agent_t object.
62 struct private_imv_swid_agent_t
{
65 * Public members of imv_swid_agent_t
67 imv_agent_if_t
public;
70 * IMV agent responsible for generic functions
75 * REST API to strongTNC manager
77 imv_swid_rest_t
*rest_api
;
81 METHOD(imv_agent_if_t
, bind_functions
, TNC_Result
,
82 private_imv_swid_agent_t
*this, TNC_TNCS_BindFunctionPointer bind_function
)
84 return this->agent
->bind_functions(this->agent
, bind_function
);
87 METHOD(imv_agent_if_t
, notify_connection_change
, TNC_Result
,
88 private_imv_swid_agent_t
*this, TNC_ConnectionID id
,
89 TNC_ConnectionState new_state
)
95 case TNC_CONNECTION_STATE_CREATE
:
96 state
= imv_swid_state_create(id
);
97 return this->agent
->create_state(this->agent
, state
);
98 case TNC_CONNECTION_STATE_DELETE
:
99 return this->agent
->delete_state(this->agent
, id
);
101 return this->agent
->change_state(this->agent
, id
, new_state
, NULL
);
106 * Process a received message
108 static TNC_Result
receive_msg(private_imv_swid_agent_t
*this,
109 imv_state_t
*state
, imv_msg_t
*in_msg
)
111 imv_swid_state_t
*swid_state
;
113 enumerator_t
*enumerator
;
116 bool fatal_error
= FALSE
;
118 /* parse received PA-TNC message and handle local and remote errors */
119 result
= in_msg
->receive(in_msg
, &fatal_error
);
120 if (result
!= TNC_RESULT_SUCCESS
)
125 swid_state
= (imv_swid_state_t
*)state
;
127 /* analyze PA-TNC attributes */
128 enumerator
= in_msg
->create_attribute_enumerator(in_msg
);
129 while (enumerator
->enumerate(enumerator
, &attr
))
131 uint32_t request_id
= 0, last_eid
, eid_epoch
;
132 swid_inventory_t
*inventory
;
135 type
= attr
->get_type(attr
);
137 if (type
.vendor_id
== PEN_IETF
&& type
.type
== IETF_ATTR_PA_TNC_ERROR
)
139 ietf_attr_pa_tnc_error_t
*error_attr
;
140 pen_type_t error_code
;
141 chunk_t msg_info
, description
;
142 bio_reader_t
*reader
;
143 uint32_t max_attr_size
;
146 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
147 error_code
= error_attr
->get_error_code(error_attr
);
149 if (error_code
.vendor_id
== PEN_TCG
)
152 msg_info
= error_attr
->get_msg_info(error_attr
);
153 reader
= bio_reader_create(msg_info
);
154 success
= reader
->read_uint32(reader
, &request_id
);
156 DBG1(DBG_IMV
, "received TCG error '%N' for request %d",
157 swid_error_code_names
, error_code
.type
, request_id
);
160 reader
->destroy(reader
);
163 if (error_code
.type
== TCG_SWID_RESPONSE_TOO_LARGE
)
165 if (!reader
->read_uint32(reader
, &max_attr_size
))
167 reader
->destroy(reader
);
170 DBG1(DBG_IMV
, " maximum PA-TNC attribute size is %u bytes",
173 description
= reader
->peek(reader
);
176 DBG1(DBG_IMV
, " description: %.*s", description
.len
,
179 reader
->destroy(reader
);
182 else if (type
.vendor_id
== PEN_ITA
)
186 case ITA_ATTR_START_ANGEL
:
187 swid_state
->set_angel_count(swid_state
, TRUE
);
189 case ITA_ATTR_STOP_ANGEL
:
190 swid_state
->set_angel_count(swid_state
, FALSE
);
196 else if (type
.vendor_id
!= PEN_TCG
)
203 case TCG_SWID_TAG_ID_INVENTORY
:
205 tcg_swid_attr_tag_id_inv_t
*attr_cast
;
208 state
->set_action_flags(state
, IMV_SWID_ATTR_TAG_ID_INV
);
210 attr_cast
= (tcg_swid_attr_tag_id_inv_t
*)attr
;
211 request_id
= attr_cast
->get_request_id(attr_cast
);
212 last_eid
= attr_cast
->get_last_eid(attr_cast
, &eid_epoch
);
213 inventory
= attr_cast
->get_inventory(attr_cast
);
214 tag_id_count
= inventory
->get_count(inventory
);
216 DBG2(DBG_IMV
, "received SWID tag ID inventory with %d item%s "
217 "for request %d at eid %d of epoch 0x%08x",
218 tag_id_count
, (tag_id_count
== 1) ?
"" : "s",
219 request_id
, last_eid
, eid_epoch
);
221 if (request_id
== swid_state
->get_request_id(swid_state
))
223 swid_state
->set_swid_inventory(swid_state
, inventory
);
224 swid_state
->set_count(swid_state
, tag_id_count
, 0);
228 DBG1(DBG_IMV
, "no workitem found for SWID tag ID inventory "
229 "with request ID %d", request_id
);
233 case TCG_SWID_TAG_INVENTORY
:
235 tcg_swid_attr_tag_inv_t
*attr_cast
;
237 chunk_t tag_encoding
;
238 json_object
*jobj
, *jarray
, *jstring
;
243 state
->set_action_flags(state
, IMV_SWID_ATTR_TAG_INV
);
245 attr_cast
= (tcg_swid_attr_tag_inv_t
*)attr
;
246 request_id
= attr_cast
->get_request_id(attr_cast
);
247 last_eid
= attr_cast
->get_last_eid(attr_cast
, &eid_epoch
);
248 inventory
= attr_cast
->get_inventory(attr_cast
);
249 tag_count
= inventory
->get_count(inventory
);
251 DBG2(DBG_IMV
, "received SWID tag inventory with %d item%s for "
252 "request %d at eid %d of epoch 0x%08x",
253 tag_count
, (tag_count
== 1) ?
"" : "s",
254 request_id
, last_eid
, eid_epoch
);
257 if (request_id
== swid_state
->get_request_id(swid_state
))
259 swid_state
->set_count(swid_state
, 0, tag_count
);
263 jobj
= json_object_new_object();
264 jarray
= json_object_new_array();
265 json_object_object_add(jobj
, "data", jarray
);
267 e
= inventory
->create_enumerator(inventory
);
268 while (e
->enumerate(e
, &tag
))
270 tag_encoding
= tag
->get_encoding(tag
);
271 tag_str
= strndup(tag_encoding
.ptr
, tag_encoding
.len
);
272 DBG3(DBG_IMV
, "%s", tag_str
);
273 jstring
= json_object_new_string(tag_str
);
274 json_object_array_add(jarray
, jstring
);
279 if (this->rest_api
->post(this->rest_api
,
280 "swid/add-tags/", jobj
, NULL
) != SUCCESS
)
282 DBG1(DBG_IMV
, "error in REST API add-tags request");
284 json_object_put(jobj
);
289 DBG1(DBG_IMV
, "no workitem found for SWID tag inventory "
290 "with request ID %d", request_id
);
297 enumerator
->destroy(enumerator
);
301 state
->set_recommendation(state
,
302 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
303 TNC_IMV_EVALUATION_RESULT_ERROR
);
304 out_msg
= imv_msg_create_as_reply(in_msg
);
305 result
= out_msg
->send_assessment(out_msg
);
306 out_msg
->destroy(out_msg
);
307 if (result
!= TNC_RESULT_SUCCESS
)
311 return this->agent
->provide_recommendation(this->agent
, state
);
314 return TNC_RESULT_SUCCESS
;
317 METHOD(imv_agent_if_t
, receive_message
, TNC_Result
,
318 private_imv_swid_agent_t
*this, TNC_ConnectionID id
,
319 TNC_MessageType msg_type
, chunk_t msg
)
325 if (!this->agent
->get_state(this->agent
, id
, &state
))
327 return TNC_RESULT_FATAL
;
329 in_msg
= imv_msg_create_from_data(this->agent
, state
, id
, msg_type
, msg
);
330 result
= receive_msg(this, state
, in_msg
);
331 in_msg
->destroy(in_msg
);
336 METHOD(imv_agent_if_t
, receive_message_long
, TNC_Result
,
337 private_imv_swid_agent_t
*this, TNC_ConnectionID id
,
338 TNC_UInt32 src_imc_id
, TNC_UInt32 dst_imv_id
,
339 TNC_VendorID msg_vid
, TNC_MessageSubtype msg_subtype
, chunk_t msg
)
345 if (!this->agent
->get_state(this->agent
, id
, &state
))
347 return TNC_RESULT_FATAL
;
349 in_msg
= imv_msg_create_from_long_data(this->agent
, state
, id
,
350 src_imc_id
, dst_imv_id
, msg_vid
, msg_subtype
, msg
);
351 result
= receive_msg(this, state
, in_msg
);
352 in_msg
->destroy(in_msg
);
358 METHOD(imv_agent_if_t
, batch_ending
, TNC_Result
,
359 private_imv_swid_agent_t
*this, TNC_ConnectionID id
)
363 imv_session_t
*session
;
364 imv_workitem_t
*workitem
;
365 imv_swid_state_t
*swid_state
;
366 imv_swid_handshake_state_t handshake_state
;
369 TNC_Result result
= TNC_RESULT_SUCCESS
;
370 bool no_workitems
= TRUE
;
371 uint32_t request_id
, received
;
373 enumerator_t
*enumerator
;
375 if (!this->agent
->get_state(this->agent
, id
, &state
))
377 return TNC_RESULT_FATAL
;
379 swid_state
= (imv_swid_state_t
*)state
;
380 handshake_state
= swid_state
->get_handshake_state(swid_state
);
381 session
= state
->get_session(state
);
382 imv_id
= this->agent
->get_id(this->agent
);
384 if (handshake_state
== IMV_SWID_STATE_END
)
386 return TNC_RESULT_SUCCESS
;
389 /* Create an empty out message - we might need it */
390 out_msg
= imv_msg_create(this->agent
, state
, id
, imv_id
, TNC_IMCID_ANY
,
395 DBG2(DBG_IMV
, "no workitems available - no evaluation possible");
396 state
->set_recommendation(state
,
397 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
398 TNC_IMV_EVALUATION_RESULT_DONT_KNOW
);
399 result
= out_msg
->send_assessment(out_msg
);
400 out_msg
->destroy(out_msg
);
401 swid_state
->set_handshake_state(swid_state
, IMV_SWID_STATE_END
);
403 if (result
!= TNC_RESULT_SUCCESS
)
407 return this->agent
->provide_recommendation(this->agent
, state
);
410 /* Look for SWID tag workitem and create SWID tag request */
411 if (handshake_state
== IMV_SWID_STATE_INIT
&&
412 session
->get_policy_started(session
))
414 enumerator
= session
->create_workitem_enumerator(session
);
417 while (enumerator
->enumerate(enumerator
, &workitem
))
419 if (workitem
->get_imv_id(workitem
) != TNC_IMVID_ANY
||
420 workitem
->get_type(workitem
) != IMV_WORKITEM_SWID_TAGS
)
425 flags
= TCG_SWID_ATTR_REQ_FLAG_NONE
;
426 if (strchr(workitem
->get_arg_str(workitem
), 'R'))
428 flags
|= TCG_SWID_ATTR_REQ_FLAG_R
;
430 if (strchr(workitem
->get_arg_str(workitem
), 'S'))
432 flags
|= TCG_SWID_ATTR_REQ_FLAG_S
;
434 if (strchr(workitem
->get_arg_str(workitem
), 'C'))
436 flags
|= TCG_SWID_ATTR_REQ_FLAG_C
;
438 request_id
= workitem
->get_id(workitem
);
439 swid_state
->set_request_id(swid_state
, request_id
);
440 attr
= tcg_swid_attr_req_create(flags
, request_id
, 0);
441 out_msg
->add_attribute(out_msg
, attr
);
442 workitem
->set_imv_id(workitem
, imv_id
);
443 no_workitems
= FALSE
;
444 DBG2(DBG_IMV
, "IMV %d issues SWID request %d",
448 enumerator
->destroy(enumerator
);
452 DBG2(DBG_IMV
, "IMV %d has no workitems - "
453 "no evaluation requested", imv_id
);
454 state
->set_recommendation(state
,
455 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
456 TNC_IMV_EVALUATION_RESULT_DONT_KNOW
);
458 handshake_state
= IMV_SWID_STATE_WORKITEMS
;
459 swid_state
->set_handshake_state(swid_state
, handshake_state
);
463 received
= state
->get_action_flags(state
);
465 if (handshake_state
== IMV_SWID_STATE_WORKITEMS
&&
466 (received
& (IMV_SWID_ATTR_TAG_INV
|IMV_SWID_ATTR_TAG_ID_INV
)) &&
467 swid_state
->get_angel_count(swid_state
) <= 0)
469 TNC_IMV_Evaluation_Result eval
;
470 TNC_IMV_Action_Recommendation rec
;
471 char result_str
[BUF_LEN
], *error_str
= "", *command
;
472 char *target
, *separator
;
473 int tag_id_count
, tag_count
, i
;
474 size_t max_attr_size
, attr_size
, entry_size
;
475 chunk_t tag_creator
, unique_sw_id
;
476 json_object
*jrequest
, *jresponse
, *jvalue
;
477 tcg_swid_attr_req_t
*cast_attr
;
478 swid_tag_id_t
*tag_id
;
479 status_t status
= SUCCESS
;
481 if (this->rest_api
&& (received
& IMV_SWID_ATTR_TAG_ID_INV
))
483 if (asprintf(&command
, "sessions/%d/swid-measurement/",
484 session
->get_session_id(session
, NULL
, NULL
)) < 0)
486 error_str
= "allocation of command string failed";
491 jrequest
= swid_state
->get_swid_inventory(swid_state
);
492 status
= this->rest_api
->post(this->rest_api
, command
,
493 jrequest
, &jresponse
);
494 if (status
== FAILED
)
496 error_str
= "error in REST API swid-measurement request";
505 enumerator
= session
->create_workitem_enumerator(session
);
506 while (enumerator
->enumerate(enumerator
, &workitem
))
508 if (workitem
->get_type(workitem
) == IMV_WORKITEM_SWID_TAGS
)
510 swid_state
->get_count(swid_state
, &tag_id_count
,
512 snprintf(result_str
, BUF_LEN
, "received inventory of "
513 "%d SWID tag ID%s and %d SWID tag%s",
514 tag_id_count
, (tag_id_count
== 1) ?
"" : "s",
515 tag_count
, (tag_count
== 1) ?
"" : "s");
516 session
->remove_workitem(session
, enumerator
);
518 eval
= TNC_IMV_EVALUATION_RESULT_COMPLIANT
;
519 rec
= workitem
->set_result(workitem
, result_str
, eval
);
520 state
->update_recommendation(state
, rec
, eval
);
521 imcv_db
->finalize_workitem(imcv_db
, workitem
);
522 workitem
->destroy(workitem
);
526 enumerator
->destroy(enumerator
);
529 if (received
& IMV_SWID_ATTR_TAG_INV
)
531 error_str
= "not all requested SWID tags were received";
533 json_object_put(jresponse
);
536 if (json_object_get_type(jresponse
) != json_type_array
)
538 error_str
= "response was not a json_array";
540 json_object_put(jresponse
);
544 /* Compute the maximum TCG SWID Request attribute size */
545 max_attr_size
= state
->get_max_msg_len(state
) -
548 /* Create the [first] TCG SWID Request attribute */
549 attr_size
= PA_TNC_ATTR_HEADER_SIZE
+ TCG_SWID_REQ_MIN_SIZE
;
550 attr
= tcg_swid_attr_req_create(TCG_SWID_ATTR_REQ_FLAG_NONE
,
551 swid_state
->get_request_id(swid_state
), 0);
553 tag_id_count
= json_object_array_length(jresponse
);
554 DBG1(DBG_IMV
, "%d SWID tag target%s", tag_id_count
,
555 (tag_id_count
== 1) ?
"" : "s");
557 for (i
= 0; i
< tag_id_count
; i
++)
559 jvalue
= json_object_array_get_idx(jresponse
, i
);
560 if (json_object_get_type(jvalue
) != json_type_string
)
562 error_str
= "json_string element expected in json_array";
564 json_object_put(jresponse
);
567 target
= (char*)json_object_get_string(jvalue
);
568 DBG1(DBG_IMV
, " %s", target
);
570 /* Separate target into tag_creator and unique_sw_id */
571 separator
= strchr(target
, '_');
574 error_str
= "separation of regid from "
575 "unique software ID failed";
578 tag_creator
= chunk_create(target
, separator
- target
);
580 unique_sw_id
= chunk_create(separator
, strlen(target
) -
581 tag_creator
.len
- 1);
582 tag_id
= swid_tag_id_create(tag_creator
, unique_sw_id
,
584 entry_size
= 2 + tag_creator
.len
+ 2 + unique_sw_id
.len
;
586 /* Have we reached the maximum attribute size? */
587 if (attr_size
+ entry_size
> max_attr_size
)
589 out_msg
->add_attribute(out_msg
, attr
);
590 attr_size
= PA_TNC_ATTR_HEADER_SIZE
+
591 TCG_SWID_REQ_MIN_SIZE
;
592 attr
= tcg_swid_attr_req_create(
593 TCG_SWID_ATTR_REQ_FLAG_NONE
,
594 swid_state
->get_request_id(swid_state
), 0);
596 cast_attr
= (tcg_swid_attr_req_t
*)attr
;
597 cast_attr
->add_target(cast_attr
, tag_id
);
599 json_object_put(jresponse
);
601 out_msg
->add_attribute(out_msg
, attr
);
608 if (status
== FAILED
)
610 enumerator
= session
->create_workitem_enumerator(session
);
611 while (enumerator
->enumerate(enumerator
, &workitem
))
613 if (workitem
->get_type(workitem
) == IMV_WORKITEM_SWID_TAGS
)
615 session
->remove_workitem(session
, enumerator
);
616 eval
= TNC_IMV_EVALUATION_RESULT_ERROR
;
617 rec
= workitem
->set_result(workitem
, error_str
, eval
);
618 state
->update_recommendation(state
, rec
, eval
);
619 imcv_db
->finalize_workitem(imcv_db
, workitem
);
620 workitem
->destroy(workitem
);
624 enumerator
->destroy(enumerator
);
628 /* finalized all workitems ? */
629 if (handshake_state
== IMV_SWID_STATE_WORKITEMS
&&
630 session
->get_workitem_count(session
, imv_id
) == 0)
632 result
= out_msg
->send_assessment(out_msg
);
633 out_msg
->destroy(out_msg
);
634 swid_state
->set_handshake_state(swid_state
, IMV_SWID_STATE_END
);
636 if (result
!= TNC_RESULT_SUCCESS
)
640 return this->agent
->provide_recommendation(this->agent
, state
);
643 /* send non-empty PA-TNC message with excl flag not set */
644 if (out_msg
->get_attribute_count(out_msg
))
646 result
= out_msg
->send(out_msg
, FALSE
);
648 out_msg
->destroy(out_msg
);
653 METHOD(imv_agent_if_t
, solicit_recommendation
, TNC_Result
,
654 private_imv_swid_agent_t
*this, TNC_ConnectionID id
)
658 if (!this->agent
->get_state(this->agent
, id
, &state
))
660 return TNC_RESULT_FATAL
;
662 return this->agent
->provide_recommendation(this->agent
, state
);
665 METHOD(imv_agent_if_t
, destroy
, void,
666 private_imv_swid_agent_t
*this)
668 DESTROY_IF(this->rest_api
);
669 this->agent
->destroy(this->agent
);
675 * Described in header.
677 imv_agent_if_t
*imv_swid_agent_create(const char *name
, TNC_IMVID id
,
678 TNC_Version
*actual_version
)
680 private_imv_swid_agent_t
*this;
683 u_int rest_api_timeout
;
685 agent
= imv_agent_create(name
, msg_types
, countof(msg_types
), id
,
694 .bind_functions
= _bind_functions
,
695 .notify_connection_change
= _notify_connection_change
,
696 .receive_message
= _receive_message
,
697 .receive_message_long
= _receive_message_long
,
698 .batch_ending
= _batch_ending
,
699 .solicit_recommendation
= _solicit_recommendation
,
705 rest_api_uri
= lib
->settings
->get_str(lib
->settings
,
706 "%s.plugins.imv-swid.rest_api_uri", NULL
, lib
->ns
);
707 rest_api_timeout
= lib
->settings
->get_int(lib
->settings
,
708 "%s.plugins.imv-swid.rest_api_timeout", 120, lib
->ns
);
711 this->rest_api
= imv_swid_rest_create(rest_api_uri
, rest_api_timeout
);
715 return &this->public;