2 * Copyright (C) 2013-2017 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"
23 #include <imv/imv_agent.h>
24 #include <imv/imv_msg.h>
25 #include <ietf/ietf_attr_pa_tnc_error.h>
26 #include "rest/rest.h"
27 #include "tcg/seg/tcg_seg_attr_max_size.h"
28 #include "tcg/seg/tcg_seg_attr_seg_env.h"
29 #include "tcg/swid/tcg_swid_attr_req.h"
30 #include "tcg/swid/tcg_swid_attr_tag_inv.h"
31 #include "tcg/swid/tcg_swid_attr_tag_id_inv.h"
32 #include "swid/swid_error.h"
33 #include "swid/swid_inventory.h"
35 #include <tncif_names.h>
36 #include <tncif_pa_subtypes.h>
39 #include <utils/debug.h>
40 #include <bio/bio_reader.h>
42 typedef struct private_imv_swid_agent_t private_imv_swid_agent_t
;
44 /* Subscribed PA-TNC message subtypes */
45 static pen_type_t msg_types
[] = {
46 { PEN_TCG
, PA_SUBTYPE_TCG_SWID
}
50 * Flag set when corresponding attribute has been received
52 enum imv_swid_attr_t
{
53 IMV_SWID_ATTR_TAG_INV
= (1<<0),
54 IMV_SWID_ATTR_TAG_ID_INV
= (1<<1)
58 * Private data of an imv_swid_agent_t object.
60 struct private_imv_swid_agent_t
{
63 * Public members of imv_swid_agent_t
65 imv_agent_if_t
public;
68 * IMV agent responsible for generic functions
73 * REST API to strongTNC manager
79 METHOD(imv_agent_if_t
, bind_functions
, TNC_Result
,
80 private_imv_swid_agent_t
*this, TNC_TNCS_BindFunctionPointer bind_function
)
82 return this->agent
->bind_functions(this->agent
, bind_function
);
85 METHOD(imv_agent_if_t
, notify_connection_change
, TNC_Result
,
86 private_imv_swid_agent_t
*this, TNC_ConnectionID id
,
87 TNC_ConnectionState new_state
)
93 case TNC_CONNECTION_STATE_CREATE
:
94 state
= imv_swid_state_create(id
);
95 return this->agent
->create_state(this->agent
, state
);
96 case TNC_CONNECTION_STATE_DELETE
:
97 return this->agent
->delete_state(this->agent
, id
);
99 return this->agent
->change_state(this->agent
, id
, new_state
, NULL
);
104 * Process a received message
106 static TNC_Result
receive_msg(private_imv_swid_agent_t
*this,
107 imv_state_t
*state
, imv_msg_t
*in_msg
)
109 imv_swid_state_t
*swid_state
;
111 enumerator_t
*enumerator
;
114 bool fatal_error
= FALSE
;
116 /* generate an outgoing PA-TNC message - we might need it */
117 out_msg
= imv_msg_create_as_reply(in_msg
);
119 /* parse received PA-TNC message and handle local and remote errors */
120 result
= in_msg
->receive(in_msg
, out_msg
, &fatal_error
);
121 if (result
!= TNC_RESULT_SUCCESS
)
123 out_msg
->destroy(out_msg
);
127 swid_state
= (imv_swid_state_t
*)state
;
129 /* analyze PA-TNC attributes */
130 enumerator
= in_msg
->create_attribute_enumerator(in_msg
);
131 while (enumerator
->enumerate(enumerator
, &attr
))
133 uint32_t request_id
= 0, last_eid
, eid_epoch
;
134 swid_inventory_t
*inventory
;
137 type
= attr
->get_type(attr
);
139 if (type
.vendor_id
== PEN_IETF
&& type
.type
== IETF_ATTR_PA_TNC_ERROR
)
141 ietf_attr_pa_tnc_error_t
*error_attr
;
142 pen_type_t error_code
;
143 chunk_t msg_info
, description
;
144 bio_reader_t
*reader
;
145 uint32_t max_attr_size
;
148 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
149 error_code
= error_attr
->get_error_code(error_attr
);
151 if (error_code
.vendor_id
== PEN_TCG
)
154 msg_info
= error_attr
->get_msg_info(error_attr
);
155 reader
= bio_reader_create(msg_info
);
156 success
= reader
->read_uint32(reader
, &request_id
);
158 DBG1(DBG_IMV
, "received TCG error '%N' for request %d",
159 swid_error_code_names
, error_code
.type
, request_id
);
162 reader
->destroy(reader
);
165 if (error_code
.type
== TCG_SWID_RESPONSE_TOO_LARGE
)
167 if (!reader
->read_uint32(reader
, &max_attr_size
))
169 reader
->destroy(reader
);
172 DBG1(DBG_IMV
, " maximum PA-TNC attribute size is %u bytes",
175 description
= reader
->peek(reader
);
178 DBG1(DBG_IMV
, " description: %.*s", description
.len
,
181 reader
->destroy(reader
);
184 else if (type
.vendor_id
!= PEN_TCG
)
191 case TCG_SWID_TAG_ID_INVENTORY
:
193 tcg_swid_attr_tag_id_inv_t
*attr_cast
;
197 state
->set_action_flags(state
, IMV_SWID_ATTR_TAG_ID_INV
);
199 attr_cast
= (tcg_swid_attr_tag_id_inv_t
*)attr
;
200 request_id
= attr_cast
->get_request_id(attr_cast
);
201 last_eid
= attr_cast
->get_last_eid(attr_cast
, &eid_epoch
);
202 inventory
= attr_cast
->get_inventory(attr_cast
);
203 tag_id_count
= inventory
->get_count(inventory
);
204 missing
= attr_cast
->get_tag_id_count(attr_cast
);
205 swid_state
->set_missing(swid_state
, missing
);
207 DBG2(DBG_IMV
, "received SWID tag ID inventory with %d item%s "
208 "for request %d at eid %d of epoch 0x%08x, %d item%s to "
209 "follow", tag_id_count
, (tag_id_count
== 1) ? "" : "s",
210 request_id
, last_eid
, eid_epoch
, missing
,
211 (missing
== 1) ? "" : "s");
213 if (request_id
== swid_state
->get_request_id(swid_state
))
215 swid_state
->set_swid_inventory(swid_state
, inventory
);
216 swid_state
->set_count(swid_state
, tag_id_count
, 0,
217 in_msg
->get_src_id(in_msg
));
221 DBG1(DBG_IMV
, "no workitem found for SWID tag ID inventory "
222 "with request ID %d", request_id
);
224 attr_cast
->clear_inventory(attr_cast
);
227 case TCG_SWID_TAG_INVENTORY
:
229 tcg_swid_attr_tag_inv_t
*attr_cast
;
231 chunk_t tag_encoding
;
232 json_object
*jobj
, *jarray
, *jstring
;
238 state
->set_action_flags(state
, IMV_SWID_ATTR_TAG_INV
);
240 attr_cast
= (tcg_swid_attr_tag_inv_t
*)attr
;
241 request_id
= attr_cast
->get_request_id(attr_cast
);
242 last_eid
= attr_cast
->get_last_eid(attr_cast
, &eid_epoch
);
243 inventory
= attr_cast
->get_inventory(attr_cast
);
244 tag_count
= inventory
->get_count(inventory
);
245 missing
= attr_cast
->get_tag_count(attr_cast
);
246 swid_state
->set_missing(swid_state
, missing
);
248 DBG2(DBG_IMV
, "received SWID tag inventory with %d item%s for "
249 "request %d at eid %d of epoch 0x%08x, %d item%s to follow",
250 tag_count
, (tag_count
== 1) ? "" : "s", request_id
,
251 last_eid
, eid_epoch
, missing
, (missing
== 1) ? "" : "s");
253 if (request_id
== swid_state
->get_request_id(swid_state
))
255 swid_state
->set_count(swid_state
, 0, tag_count
,
256 in_msg
->get_src_id(in_msg
));
260 jobj
= json_object_new_object();
261 jarray
= json_object_new_array();
262 json_object_object_add(jobj
, "data", jarray
);
264 e
= inventory
->create_enumerator(inventory
);
265 while (e
->enumerate(e
, &tag
))
267 tag_encoding
= tag
->get_encoding(tag
);
268 tag_str
= strndup(tag_encoding
.ptr
, tag_encoding
.len
);
269 DBG3(DBG_IMV
, "%s", tag_str
);
270 jstring
= json_object_new_string(tag_str
);
271 json_object_array_add(jarray
, jstring
);
276 if (this->rest_api
->post(this->rest_api
,
277 "swid/add-tags/", jobj
, NULL
) != SUCCESS
)
279 DBG1(DBG_IMV
, "error in REST API add-tags request");
281 json_object_put(jobj
);
286 DBG1(DBG_IMV
, "no workitem found for SWID tag inventory "
287 "with request ID %d", request_id
);
289 attr_cast
->clear_inventory(attr_cast
);
296 enumerator
->destroy(enumerator
);
300 state
->set_recommendation(state
,
301 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
302 TNC_IMV_EVALUATION_RESULT_ERROR
);
303 result
= out_msg
->send_assessment(out_msg
);
304 if (result
== TNC_RESULT_SUCCESS
)
306 result
= this->agent
->provide_recommendation(this->agent
, state
);
311 /* send PA-TNC message with the EXCL flag set */
312 result
= out_msg
->send(out_msg
, TRUE
);
314 out_msg
->destroy(out_msg
);
319 METHOD(imv_agent_if_t
, receive_message
, TNC_Result
,
320 private_imv_swid_agent_t
*this, TNC_ConnectionID id
,
321 TNC_MessageType msg_type
, chunk_t msg
)
327 if (!this->agent
->get_state(this->agent
, id
, &state
))
329 return TNC_RESULT_FATAL
;
331 in_msg
= imv_msg_create_from_data(this->agent
, state
, id
, msg_type
, msg
);
332 result
= receive_msg(this, state
, in_msg
);
333 in_msg
->destroy(in_msg
);
338 METHOD(imv_agent_if_t
, receive_message_long
, TNC_Result
,
339 private_imv_swid_agent_t
*this, TNC_ConnectionID id
,
340 TNC_UInt32 src_imc_id
, TNC_UInt32 dst_imv_id
,
341 TNC_VendorID msg_vid
, TNC_MessageSubtype msg_subtype
, chunk_t msg
)
347 if (!this->agent
->get_state(this->agent
, id
, &state
))
349 return TNC_RESULT_FATAL
;
351 in_msg
= imv_msg_create_from_long_data(this->agent
, state
, id
,
352 src_imc_id
, dst_imv_id
, msg_vid
, msg_subtype
, msg
);
353 result
= receive_msg(this, state
, in_msg
);
354 in_msg
->destroy(in_msg
);
360 METHOD(imv_agent_if_t
, batch_ending
, TNC_Result
,
361 private_imv_swid_agent_t
*this, TNC_ConnectionID id
)
365 imv_session_t
*session
;
366 imv_workitem_t
*workitem
;
367 imv_swid_state_t
*swid_state
;
368 imv_swid_handshake_state_t handshake_state
;
371 TNC_Result result
= TNC_RESULT_SUCCESS
;
372 bool no_workitems
= TRUE
;
373 uint32_t request_id
, received
;
375 enumerator_t
*enumerator
;
377 if (!this->agent
->get_state(this->agent
, id
, &state
))
379 return TNC_RESULT_FATAL
;
381 swid_state
= (imv_swid_state_t
*)state
;
382 handshake_state
= swid_state
->get_handshake_state(swid_state
);
383 session
= state
->get_session(state
);
384 imv_id
= this->agent
->get_id(this->agent
);
386 if (handshake_state
== IMV_SWID_STATE_END
)
388 return TNC_RESULT_SUCCESS
;
391 /* Create an empty out message - we might need it */
392 out_msg
= imv_msg_create(this->agent
, state
, id
, imv_id
,
393 swid_state
->get_imc_id(swid_state
),
398 DBG2(DBG_IMV
, "no workitems available - no evaluation possible");
399 state
->set_recommendation(state
,
400 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
401 TNC_IMV_EVALUATION_RESULT_DONT_KNOW
);
402 result
= out_msg
->send_assessment(out_msg
);
403 out_msg
->destroy(out_msg
);
404 swid_state
->set_handshake_state(swid_state
, IMV_SWID_STATE_END
);
406 if (result
!= TNC_RESULT_SUCCESS
)
410 return this->agent
->provide_recommendation(this->agent
, state
);
413 /* Look for SWID tag workitem and create SWID tag request */
414 if (handshake_state
== IMV_SWID_STATE_INIT
&&
415 session
->get_policy_started(session
))
417 size_t max_attr_size
= SWID_MAX_ATTR_SIZE
;
419 seg_contract_t
*contract
;
420 seg_contract_manager_t
*contracts
;
423 enumerator
= session
->create_workitem_enumerator(session
);
426 while (enumerator
->enumerate(enumerator
, &workitem
))
428 if (workitem
->get_imv_id(workitem
) != TNC_IMVID_ANY
||
429 workitem
->get_type(workitem
) != IMV_WORKITEM_SWID_TAGS
)
434 flags
= TCG_SWID_ATTR_REQ_FLAG_NONE
;
435 if (strchr(workitem
->get_arg_str(workitem
), 'R'))
437 flags
|= TCG_SWID_ATTR_REQ_FLAG_R
;
439 if (strchr(workitem
->get_arg_str(workitem
), 'S'))
441 flags
|= TCG_SWID_ATTR_REQ_FLAG_S
;
443 if (strchr(workitem
->get_arg_str(workitem
), 'C'))
445 flags
|= TCG_SWID_ATTR_REQ_FLAG_C
;
448 /* Determine maximum PA-TNC attribute segment size */
449 max_seg_size
= state
->get_max_msg_len(state
)
451 - PA_TNC_ATTR_HEADER_SIZE
452 - TCG_SEG_ATTR_SEG_ENV_HEADER
;
454 /* Announce support of PA-TNC segmentation to IMC */
455 contract
= seg_contract_create(msg_types
[0], max_attr_size
,
456 max_seg_size
, TRUE
, imv_id
, FALSE
);
457 contract
->get_info_string(contract
, buf
, BUF_LEN
, TRUE
);
458 DBG2(DBG_IMV
, "%s", buf
);
459 contracts
= state
->get_contracts(state
);
460 contracts
->add_contract(contracts
, contract
);
461 attr
= tcg_seg_attr_max_size_create(max_attr_size
,
463 out_msg
->add_attribute(out_msg
, attr
);
465 /* Issue a SWID request */
466 request_id
= workitem
->get_id(workitem
);
467 swid_state
->set_request_id(swid_state
, request_id
);
468 attr
= tcg_swid_attr_req_create(flags
, request_id
, 0);
469 out_msg
->add_attribute(out_msg
, attr
);
470 workitem
->set_imv_id(workitem
, imv_id
);
471 no_workitems
= FALSE
;
472 DBG2(DBG_IMV
, "IMV %d issues SWID request %d",
476 enumerator
->destroy(enumerator
);
480 DBG2(DBG_IMV
, "IMV %d has no workitems - "
481 "no evaluation requested", imv_id
);
482 state
->set_recommendation(state
,
483 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
484 TNC_IMV_EVALUATION_RESULT_DONT_KNOW
);
486 handshake_state
= IMV_SWID_STATE_WORKITEMS
;
487 swid_state
->set_handshake_state(swid_state
, handshake_state
);
491 received
= state
->get_action_flags(state
);
493 if (handshake_state
== IMV_SWID_STATE_WORKITEMS
&&
494 (received
& (IMV_SWID_ATTR_TAG_INV
|IMV_SWID_ATTR_TAG_ID_INV
)) &&
495 swid_state
->get_missing(swid_state
) == 0)
497 TNC_IMV_Evaluation_Result eval
;
498 TNC_IMV_Action_Recommendation rec
;
499 char result_str
[BUF_LEN
], *error_str
= "", *command
;
500 char *target
, *separator
;
501 int tag_id_count
, tag_count
, i
;
502 chunk_t tag_creator
, unique_sw_id
;
503 json_object
*jrequest
, *jresponse
, *jvalue
;
504 tcg_swid_attr_req_t
*cast_attr
;
505 swid_tag_id_t
*tag_id
;
506 status_t status
= SUCCESS
;
508 if (this->rest_api
&& (received
& IMV_SWID_ATTR_TAG_ID_INV
))
510 if (asprintf(&command
, "sessions/%d/swid-measurement/",
511 session
->get_session_id(session
, NULL
, NULL
)) < 0)
513 error_str
= "allocation of command string failed";
518 jrequest
= swid_state
->get_swid_inventory(swid_state
);
519 status
= this->rest_api
->post(this->rest_api
, command
,
520 jrequest
, &jresponse
);
521 if (status
== FAILED
)
523 error_str
= "error in REST API swid-measurement request";
532 enumerator
= session
->create_workitem_enumerator(session
);
533 while (enumerator
->enumerate(enumerator
, &workitem
))
535 if (workitem
->get_type(workitem
) == IMV_WORKITEM_SWID_TAGS
)
537 swid_state
->get_count(swid_state
, &tag_id_count
,
539 snprintf(result_str
, BUF_LEN
, "received inventory of "
540 "%d SWID tag ID%s and %d SWID tag%s",
541 tag_id_count
, (tag_id_count
== 1) ? "" : "s",
542 tag_count
, (tag_count
== 1) ? "" : "s");
543 session
->remove_workitem(session
, enumerator
);
545 eval
= TNC_IMV_EVALUATION_RESULT_COMPLIANT
;
546 rec
= workitem
->set_result(workitem
, result_str
, eval
);
547 state
->update_recommendation(state
, rec
, eval
);
548 imcv_db
->finalize_workitem(imcv_db
, workitem
);
549 workitem
->destroy(workitem
);
553 enumerator
->destroy(enumerator
);
556 if (received
& IMV_SWID_ATTR_TAG_INV
)
558 error_str
= "not all requested SWID tags were received";
560 json_object_put(jresponse
);
563 if (json_object_get_type(jresponse
) != json_type_array
)
565 error_str
= "response was not a json_array";
567 json_object_put(jresponse
);
571 /* Create a TCG SWID Request attribute */
572 attr
= tcg_swid_attr_req_create(TCG_SWID_ATTR_REQ_FLAG_NONE
,
573 swid_state
->get_request_id(swid_state
), 0);
574 tag_id_count
= json_object_array_length(jresponse
);
575 DBG1(DBG_IMV
, "%d SWID tag target%s", tag_id_count
,
576 (tag_id_count
== 1) ? "" : "s");
577 swid_state
->set_missing(swid_state
, tag_id_count
);
579 for (i
= 0; i
< tag_id_count
; i
++)
581 jvalue
= json_object_array_get_idx(jresponse
, i
);
582 if (json_object_get_type(jvalue
) != json_type_string
)
584 error_str
= "json_string element expected in json_array";
586 json_object_put(jresponse
);
589 target
= (char*)json_object_get_string(jvalue
);
590 DBG1(DBG_IMV
, " %s", target
);
592 /* Separate target into tag_creator and unique_sw_id */
593 separator
= strstr(target
, "__");
596 error_str
= "separation of regid from "
597 "unique software ID failed";
600 tag_creator
= chunk_create(target
, separator
- target
);
602 unique_sw_id
= chunk_create(separator
, strlen(target
) -
603 tag_creator
.len
- 2);
604 tag_id
= swid_tag_id_create(tag_creator
, unique_sw_id
,
606 cast_attr
= (tcg_swid_attr_req_t
*)attr
;
607 cast_attr
->add_target(cast_attr
, tag_id
);
609 json_object_put(jresponse
);
611 out_msg
->add_attribute(out_msg
, attr
);
618 if (status
== FAILED
)
620 enumerator
= session
->create_workitem_enumerator(session
);
621 while (enumerator
->enumerate(enumerator
, &workitem
))
623 if (workitem
->get_type(workitem
) == IMV_WORKITEM_SWID_TAGS
)
625 session
->remove_workitem(session
, enumerator
);
626 eval
= TNC_IMV_EVALUATION_RESULT_ERROR
;
627 rec
= workitem
->set_result(workitem
, error_str
, eval
);
628 state
->update_recommendation(state
, rec
, eval
);
629 imcv_db
->finalize_workitem(imcv_db
, workitem
);
630 workitem
->destroy(workitem
);
634 enumerator
->destroy(enumerator
);
638 /* finalized all workitems ? */
639 if (handshake_state
== IMV_SWID_STATE_WORKITEMS
&&
640 session
->get_workitem_count(session
, imv_id
) == 0)
642 result
= out_msg
->send_assessment(out_msg
);
643 out_msg
->destroy(out_msg
);
644 swid_state
->set_handshake_state(swid_state
, IMV_SWID_STATE_END
);
646 if (result
!= TNC_RESULT_SUCCESS
)
650 return this->agent
->provide_recommendation(this->agent
, state
);
653 /* send non-empty PA-TNC message with excl flag not set */
654 if (out_msg
->get_attribute_count(out_msg
))
656 result
= out_msg
->send(out_msg
, FALSE
);
658 out_msg
->destroy(out_msg
);
663 METHOD(imv_agent_if_t
, solicit_recommendation
, TNC_Result
,
664 private_imv_swid_agent_t
*this, TNC_ConnectionID id
)
668 if (!this->agent
->get_state(this->agent
, id
, &state
))
670 return TNC_RESULT_FATAL
;
672 return this->agent
->provide_recommendation(this->agent
, state
);
675 METHOD(imv_agent_if_t
, destroy
, void,
676 private_imv_swid_agent_t
*this)
678 DESTROY_IF(this->rest_api
);
679 this->agent
->destroy(this->agent
);
684 * Described in header.
686 imv_agent_if_t
*imv_swid_agent_create(const char *name
, TNC_IMVID id
,
687 TNC_Version
*actual_version
)
689 private_imv_swid_agent_t
*this;
692 u_int rest_api_timeout
;
694 agent
= imv_agent_create(name
, msg_types
, countof(msg_types
), id
,
700 agent
->add_non_fatal_attr_type(agent
,
701 pen_type_create(PEN_TCG
, TCG_SEG_MAX_ATTR_SIZE_REQ
));
705 .bind_functions
= _bind_functions
,
706 .notify_connection_change
= _notify_connection_change
,
707 .receive_message
= _receive_message
,
708 .receive_message_long
= _receive_message_long
,
709 .batch_ending
= _batch_ending
,
710 .solicit_recommendation
= _solicit_recommendation
,
716 rest_api_uri
= lib
->settings
->get_str(lib
->settings
,
717 "%s.plugins.imv-swid.rest_api_uri", NULL
, lib
->ns
);
718 rest_api_timeout
= lib
->settings
->get_int(lib
->settings
,
719 "%s.plugins.imv-swid.rest_api_timeout", 120, lib
->ns
);
722 this->rest_api
= rest_create(rest_api_uri
, rest_api_timeout
);
725 return &this->public;