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 <imv/imv_agent.h>
25 #include <imv/imv_msg.h>
26 #include <ietf/ietf_attr_pa_tnc_error.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
75 imv_swid_rest_t
*rest_api
;
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);
220 DBG1(DBG_IMV
, "no workitem found for SWID tag ID inventory "
221 "with request ID %d", request_id
);
223 attr_cast
->clear_inventory(attr_cast
);
226 case TCG_SWID_TAG_INVENTORY
:
228 tcg_swid_attr_tag_inv_t
*attr_cast
;
230 chunk_t tag_encoding
;
231 json_object
*jobj
, *jarray
, *jstring
;
237 state
->set_action_flags(state
, IMV_SWID_ATTR_TAG_INV
);
239 attr_cast
= (tcg_swid_attr_tag_inv_t
*)attr
;
240 request_id
= attr_cast
->get_request_id(attr_cast
);
241 last_eid
= attr_cast
->get_last_eid(attr_cast
, &eid_epoch
);
242 inventory
= attr_cast
->get_inventory(attr_cast
);
243 tag_count
= inventory
->get_count(inventory
);
244 missing
= attr_cast
->get_tag_count(attr_cast
);
245 swid_state
->set_missing(swid_state
, missing
);
247 DBG2(DBG_IMV
, "received SWID tag inventory with %d item%s for "
248 "request %d at eid %d of epoch 0x%08x, %d item%s to follow",
249 tag_count
, (tag_count
== 1) ? "" : "s", request_id
,
250 last_eid
, eid_epoch
, missing
, (missing
== 1) ? "" : "s");
252 if (request_id
== swid_state
->get_request_id(swid_state
))
254 swid_state
->set_count(swid_state
, 0, tag_count
);
258 jobj
= json_object_new_object();
259 jarray
= json_object_new_array();
260 json_object_object_add(jobj
, "data", jarray
);
262 e
= inventory
->create_enumerator(inventory
);
263 while (e
->enumerate(e
, &tag
))
265 tag_encoding
= tag
->get_encoding(tag
);
266 tag_str
= strndup(tag_encoding
.ptr
, tag_encoding
.len
);
267 DBG3(DBG_IMV
, "%s", tag_str
);
268 jstring
= json_object_new_string(tag_str
);
269 json_object_array_add(jarray
, jstring
);
274 if (this->rest_api
->post(this->rest_api
,
275 "swid/add-tags/", jobj
, NULL
) != SUCCESS
)
277 DBG1(DBG_IMV
, "error in REST API add-tags request");
279 json_object_put(jobj
);
284 DBG1(DBG_IMV
, "no workitem found for SWID tag inventory "
285 "with request ID %d", request_id
);
287 attr_cast
->clear_inventory(attr_cast
);
294 enumerator
->destroy(enumerator
);
298 state
->set_recommendation(state
,
299 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
300 TNC_IMV_EVALUATION_RESULT_ERROR
);
301 result
= out_msg
->send_assessment(out_msg
);
302 if (result
== TNC_RESULT_SUCCESS
)
304 result
= this->agent
->provide_recommendation(this->agent
, state
);
309 /* send PA-TNC message with the EXCL flag set */
310 result
= out_msg
->send(out_msg
, TRUE
);
312 out_msg
->destroy(out_msg
);
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 size_t max_attr_size
= SWID_MAX_ATTR_SIZE
;
416 seg_contract_t
*contract
;
417 seg_contract_manager_t
*contracts
;
420 enumerator
= session
->create_workitem_enumerator(session
);
423 while (enumerator
->enumerate(enumerator
, &workitem
))
425 if (workitem
->get_imv_id(workitem
) != TNC_IMVID_ANY
||
426 workitem
->get_type(workitem
) != IMV_WORKITEM_SWID_TAGS
)
431 flags
= TCG_SWID_ATTR_REQ_FLAG_NONE
;
432 if (strchr(workitem
->get_arg_str(workitem
), 'R'))
434 flags
|= TCG_SWID_ATTR_REQ_FLAG_R
;
436 if (strchr(workitem
->get_arg_str(workitem
), 'S'))
438 flags
|= TCG_SWID_ATTR_REQ_FLAG_S
;
440 if (strchr(workitem
->get_arg_str(workitem
), 'C'))
442 flags
|= TCG_SWID_ATTR_REQ_FLAG_C
;
445 /* Determine maximum PA-TNC attribute segment size */
446 max_seg_size
= state
->get_max_msg_len(state
)
448 - PA_TNC_ATTR_HEADER_SIZE
449 - TCG_SEG_ATTR_SEG_ENV_HEADER
450 - PA_TNC_ATTR_HEADER_SIZE
451 - TCG_SEG_ATTR_MAX_SIZE_SIZE
;
453 /* Announce support of PA-TNC segmentation to IMC */
454 contract
= seg_contract_create(msg_types
[0], max_attr_size
,
455 max_seg_size
, TRUE
, imv_id
, FALSE
);
456 contract
->get_info_string(contract
, buf
, BUF_LEN
, TRUE
);
457 DBG2(DBG_IMV
, "%s", buf
);
458 contracts
= state
->get_contracts(state
);
459 contracts
->add_contract(contracts
, contract
);
460 attr
= tcg_seg_attr_max_size_create(max_attr_size
,
462 out_msg
->add_attribute(out_msg
, attr
);
464 /* Issue a SWID request */
465 request_id
= workitem
->get_id(workitem
);
466 swid_state
->set_request_id(swid_state
, request_id
);
467 attr
= tcg_swid_attr_req_create(flags
, request_id
, 0);
468 out_msg
->add_attribute(out_msg
, attr
);
469 workitem
->set_imv_id(workitem
, imv_id
);
470 no_workitems
= FALSE
;
471 DBG2(DBG_IMV
, "IMV %d issues SWID request %d",
475 enumerator
->destroy(enumerator
);
479 DBG2(DBG_IMV
, "IMV %d has no workitems - "
480 "no evaluation requested", imv_id
);
481 state
->set_recommendation(state
,
482 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
483 TNC_IMV_EVALUATION_RESULT_DONT_KNOW
);
485 handshake_state
= IMV_SWID_STATE_WORKITEMS
;
486 swid_state
->set_handshake_state(swid_state
, handshake_state
);
490 received
= state
->get_action_flags(state
);
492 if (handshake_state
== IMV_SWID_STATE_WORKITEMS
&&
493 (received
& (IMV_SWID_ATTR_TAG_INV
|IMV_SWID_ATTR_TAG_ID_INV
)) &&
494 swid_state
->get_missing(swid_state
) == 0)
496 TNC_IMV_Evaluation_Result eval
;
497 TNC_IMV_Action_Recommendation rec
;
498 char result_str
[BUF_LEN
], *error_str
= "", *command
;
499 char *target
, *separator
;
500 int tag_id_count
, tag_count
, i
;
501 chunk_t tag_creator
, unique_sw_id
;
502 json_object
*jrequest
, *jresponse
, *jvalue
;
503 tcg_swid_attr_req_t
*cast_attr
;
504 swid_tag_id_t
*tag_id
;
505 status_t status
= SUCCESS
;
507 if (this->rest_api
&& (received
& IMV_SWID_ATTR_TAG_ID_INV
))
509 if (asprintf(&command
, "sessions/%d/swid-measurement/",
510 session
->get_session_id(session
, NULL
, NULL
)) < 0)
512 error_str
= "allocation of command string failed";
517 jrequest
= swid_state
->get_swid_inventory(swid_state
);
518 status
= this->rest_api
->post(this->rest_api
, command
,
519 jrequest
, &jresponse
);
520 if (status
== FAILED
)
522 error_str
= "error in REST API swid-measurement request";
531 enumerator
= session
->create_workitem_enumerator(session
);
532 while (enumerator
->enumerate(enumerator
, &workitem
))
534 if (workitem
->get_type(workitem
) == IMV_WORKITEM_SWID_TAGS
)
536 swid_state
->get_count(swid_state
, &tag_id_count
,
538 snprintf(result_str
, BUF_LEN
, "received inventory of "
539 "%d SWID tag ID%s and %d SWID tag%s",
540 tag_id_count
, (tag_id_count
== 1) ? "" : "s",
541 tag_count
, (tag_count
== 1) ? "" : "s");
542 session
->remove_workitem(session
, enumerator
);
544 eval
= TNC_IMV_EVALUATION_RESULT_COMPLIANT
;
545 rec
= workitem
->set_result(workitem
, result_str
, eval
);
546 state
->update_recommendation(state
, rec
, eval
);
547 imcv_db
->finalize_workitem(imcv_db
, workitem
);
548 workitem
->destroy(workitem
);
552 enumerator
->destroy(enumerator
);
555 if (received
& IMV_SWID_ATTR_TAG_INV
)
557 error_str
= "not all requested SWID tags were received";
559 json_object_put(jresponse
);
562 if (json_object_get_type(jresponse
) != json_type_array
)
564 error_str
= "response was not a json_array";
566 json_object_put(jresponse
);
570 /* Create a TCG SWID Request attribute */
571 attr
= tcg_swid_attr_req_create(TCG_SWID_ATTR_REQ_FLAG_NONE
,
572 swid_state
->get_request_id(swid_state
), 0);
573 tag_id_count
= json_object_array_length(jresponse
);
574 DBG1(DBG_IMV
, "%d SWID tag target%s", tag_id_count
,
575 (tag_id_count
== 1) ? "" : "s");
576 swid_state
->set_missing(swid_state
, tag_id_count
);
578 for (i
= 0; i
< tag_id_count
; i
++)
580 jvalue
= json_object_array_get_idx(jresponse
, i
);
581 if (json_object_get_type(jvalue
) != json_type_string
)
583 error_str
= "json_string element expected in json_array";
585 json_object_put(jresponse
);
588 target
= (char*)json_object_get_string(jvalue
);
589 DBG1(DBG_IMV
, " %s", target
);
591 /* Separate target into tag_creator and unique_sw_id */
592 separator
= strchr(target
, '_');
595 error_str
= "separation of regid from "
596 "unique software ID failed";
599 tag_creator
= chunk_create(target
, separator
- target
);
601 unique_sw_id
= chunk_create(separator
, strlen(target
) -
602 tag_creator
.len
- 1);
603 tag_id
= swid_tag_id_create(tag_creator
, unique_sw_id
,
605 cast_attr
= (tcg_swid_attr_req_t
*)attr
;
606 cast_attr
->add_target(cast_attr
, tag_id
);
608 json_object_put(jresponse
);
610 out_msg
->add_attribute(out_msg
, attr
);
617 if (status
== FAILED
)
619 enumerator
= session
->create_workitem_enumerator(session
);
620 while (enumerator
->enumerate(enumerator
, &workitem
))
622 if (workitem
->get_type(workitem
) == IMV_WORKITEM_SWID_TAGS
)
624 session
->remove_workitem(session
, enumerator
);
625 eval
= TNC_IMV_EVALUATION_RESULT_ERROR
;
626 rec
= workitem
->set_result(workitem
, error_str
, eval
);
627 state
->update_recommendation(state
, rec
, eval
);
628 imcv_db
->finalize_workitem(imcv_db
, workitem
);
629 workitem
->destroy(workitem
);
633 enumerator
->destroy(enumerator
);
637 /* finalized all workitems ? */
638 if (handshake_state
== IMV_SWID_STATE_WORKITEMS
&&
639 session
->get_workitem_count(session
, imv_id
) == 0)
641 result
= out_msg
->send_assessment(out_msg
);
642 out_msg
->destroy(out_msg
);
643 swid_state
->set_handshake_state(swid_state
, IMV_SWID_STATE_END
);
645 if (result
!= TNC_RESULT_SUCCESS
)
649 return this->agent
->provide_recommendation(this->agent
, state
);
652 /* send non-empty PA-TNC message with excl flag not set */
653 if (out_msg
->get_attribute_count(out_msg
))
655 result
= out_msg
->send(out_msg
, FALSE
);
657 out_msg
->destroy(out_msg
);
662 METHOD(imv_agent_if_t
, solicit_recommendation
, TNC_Result
,
663 private_imv_swid_agent_t
*this, TNC_ConnectionID id
)
667 if (!this->agent
->get_state(this->agent
, id
, &state
))
669 return TNC_RESULT_FATAL
;
671 return this->agent
->provide_recommendation(this->agent
, state
);
674 METHOD(imv_agent_if_t
, destroy
, void,
675 private_imv_swid_agent_t
*this)
677 DESTROY_IF(this->rest_api
);
678 this->agent
->destroy(this->agent
);
683 * Described in header.
685 imv_agent_if_t
*imv_swid_agent_create(const char *name
, TNC_IMVID id
,
686 TNC_Version
*actual_version
)
688 private_imv_swid_agent_t
*this;
691 u_int rest_api_timeout
;
693 agent
= imv_agent_create(name
, msg_types
, countof(msg_types
), id
,
699 agent
->add_non_fatal_attr_type(agent
,
700 pen_type_create(PEN_TCG
, TCG_SEG_MAX_ATTR_SIZE_REQ
));
704 .bind_functions
= _bind_functions
,
705 .notify_connection_change
= _notify_connection_change
,
706 .receive_message
= _receive_message
,
707 .receive_message_long
= _receive_message_long
,
708 .batch_ending
= _batch_ending
,
709 .solicit_recommendation
= _solicit_recommendation
,
715 rest_api_uri
= lib
->settings
->get_str(lib
->settings
,
716 "%s.plugins.imv-swid.rest_api_uri", NULL
, lib
->ns
);
717 rest_api_timeout
= lib
->settings
->get_int(lib
->settings
,
718 "%s.plugins.imv-swid.rest_api_timeout", 120, lib
->ns
);
721 this->rest_api
= imv_swid_rest_create(rest_api_uri
, rest_api_timeout
);
724 return &this->public;