2 * Copyright (C) 2011-2012 Sansar Choinyambuu
3 * Copyright (C) 2011-2013 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
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>.
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
17 #include "imv_attestation_agent.h"
18 #include "imv_attestation_state.h"
19 #include "imv_attestation_process.h"
20 #include "imv_attestation_build.h"
23 #include <imv/imv_agent.h>
24 #include <imv/imv_msg.h>
25 #include <ietf/ietf_attr.h>
26 #include <ietf/ietf_attr_attr_request.h>
27 #include <ietf/ietf_attr_pa_tnc_error.h>
28 #include <ietf/ietf_attr_product_info.h>
29 #include <ietf/ietf_attr_string_version.h>
34 #include <pts/pts_database.h>
35 #include <pts/pts_creds.h>
37 #include <tcg/tcg_attr.h>
38 #include <tcg/pts/tcg_pts_attr_req_file_meas.h>
39 #include <tcg/pts/tcg_pts_attr_req_file_meta.h>
41 #include <tncif_pa_subtypes.h>
44 #include <utils/debug.h>
45 #include <credentials/credential_manager.h>
46 #include <collections/linked_list.h>
48 typedef struct private_imv_attestation_agent_t private_imv_attestation_agent_t
;
50 /* Subscribed PA-TNC message subtypes */
51 static pen_type_t msg_types
[] = {
52 { PEN_TCG
, PA_SUBTYPE_TCG_PTS
},
53 { PEN_IETF
, PA_SUBTYPE_IETF_OPERATING_SYSTEM
}
57 * Private data of an imv_attestation_agent_t object.
59 struct private_imv_attestation_agent_t
{
62 * Public members of imv_attestation_agent_t
64 imv_agent_if_t
public;
67 * IMV agent responsible for generic functions
72 * Supported PTS measurement algorithms
74 pts_meas_algorithms_t supported_algorithms
;
77 * Supported PTS Diffie Hellman Groups
79 pts_dh_group_t supported_dh_groups
;
82 * PTS file measurement database
84 pts_database_t
*pts_db
;
89 pts_creds_t
*pts_creds
;
92 * PTS credential manager
94 credential_manager_t
*pts_credmgr
;
98 METHOD(imv_agent_if_t
, bind_functions
, TNC_Result
,
99 private_imv_attestation_agent_t
*this, TNC_TNCS_BindFunctionPointer bind_function
)
101 return this->agent
->bind_functions(this->agent
, bind_function
);
104 METHOD(imv_agent_if_t
, notify_connection_change
, TNC_Result
,
105 private_imv_attestation_agent_t
*this, TNC_ConnectionID id
,
106 TNC_ConnectionState new_state
)
112 case TNC_CONNECTION_STATE_CREATE
:
113 state
= imv_attestation_state_create(id
);
114 return this->agent
->create_state(this->agent
, state
);
115 case TNC_CONNECTION_STATE_DELETE
:
116 return this->agent
->delete_state(this->agent
, id
);
118 return this->agent
->change_state(this->agent
, id
, new_state
, NULL
);
123 * Process a received message
125 static TNC_Result
receive_msg(private_imv_attestation_agent_t
*this,
126 imv_state_t
*state
, imv_msg_t
*in_msg
)
128 imv_attestation_state_t
*attestation_state
;
130 enumerator_t
*enumerator
;
135 chunk_t os_name
= chunk_empty
;
136 chunk_t os_version
= chunk_empty
;
137 bool fatal_error
= FALSE
;
139 /* parse received PA-TNC message and handle local and remote errors */
140 result
= in_msg
->receive(in_msg
, &fatal_error
);
141 if (result
!= TNC_RESULT_SUCCESS
)
146 attestation_state
= (imv_attestation_state_t
*)state
;
147 pts
= attestation_state
->get_pts(attestation_state
);
149 out_msg
= imv_msg_create_as_reply(in_msg
);
150 out_msg
->set_msg_type(out_msg
, msg_types
[0]);
152 /* analyze PA-TNC attributes */
153 enumerator
= in_msg
->create_attribute_enumerator(in_msg
);
154 while (enumerator
->enumerate(enumerator
, &attr
))
156 type
= attr
->get_type(attr
);
158 if (type
.vendor_id
== PEN_IETF
)
162 case IETF_ATTR_PA_TNC_ERROR
:
164 ietf_attr_pa_tnc_error_t
*error_attr
;
165 pen_type_t error_code
;
168 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
169 error_code
= error_attr
->get_error_code(error_attr
);
171 if (error_code
.vendor_id
== PEN_TCG
)
173 msg_info
= error_attr
->get_msg_info(error_attr
);
175 DBG1(DBG_IMV
, "received TCG-PTS error '%N'",
176 pts_error_code_names
, error_code
.type
);
177 DBG1(DBG_IMV
, "error information: %B", &msg_info
);
182 case IETF_ATTR_PRODUCT_INFORMATION
:
184 ietf_attr_product_info_t
*attr_cast
;
186 attr_cast
= (ietf_attr_product_info_t
*)attr
;
187 os_name
= attr_cast
->get_info(attr_cast
, NULL
, NULL
);
190 case IETF_ATTR_STRING_VERSION
:
192 ietf_attr_string_version_t
*attr_cast
;
194 attr_cast
= (ietf_attr_string_version_t
*)attr
;
195 os_version
= attr_cast
->get_version(attr_cast
, NULL
, NULL
);
202 else if (type
.vendor_id
== PEN_TCG
)
204 if (!imv_attestation_process(attr
, out_msg
, state
,
205 this->supported_algorithms
, this->supported_dh_groups
,
206 this->pts_db
, this->pts_credmgr
))
208 result
= TNC_RESULT_FATAL
;
213 enumerator
->destroy(enumerator
);
216 * The IETF Product Information and String Version attributes
217 * are supposed to arrive in the same PA-TNC message
219 if (os_name
.len
&& os_version
.len
)
221 pts
->set_platform_info(pts
, os_name
, os_version
);
224 if (fatal_error
|| result
!= TNC_RESULT_SUCCESS
)
226 state
->set_recommendation(state
,
227 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
228 TNC_IMV_EVALUATION_RESULT_ERROR
);
229 result
= out_msg
->send_assessment(out_msg
);
230 out_msg
->destroy(out_msg
);
231 if (result
!= TNC_RESULT_SUCCESS
)
235 return this->agent
->provide_recommendation(this->agent
, state
);
238 /* send PA-TNC message with excl flag set */
239 result
= out_msg
->send(out_msg
, TRUE
);
240 out_msg
->destroy(out_msg
);
245 METHOD(imv_agent_if_t
, receive_message
, TNC_Result
,
246 private_imv_attestation_agent_t
*this, TNC_ConnectionID id
,
247 TNC_MessageType msg_type
, chunk_t msg
)
253 if (!this->agent
->get_state(this->agent
, id
, &state
))
255 return TNC_RESULT_FATAL
;
257 in_msg
= imv_msg_create_from_data(this->agent
, state
, id
, msg_type
, msg
);
258 result
= receive_msg(this, state
, in_msg
);
259 in_msg
->destroy(in_msg
);
264 METHOD(imv_agent_if_t
, receive_message_long
, TNC_Result
,
265 private_imv_attestation_agent_t
*this, TNC_ConnectionID id
,
266 TNC_UInt32 src_imc_id
, TNC_UInt32 dst_imv_id
,
267 TNC_VendorID msg_vid
, TNC_MessageSubtype msg_subtype
, chunk_t msg
)
273 if (!this->agent
->get_state(this->agent
, id
, &state
))
275 return TNC_RESULT_FATAL
;
277 in_msg
= imv_msg_create_from_long_data(this->agent
, state
, id
,
278 src_imc_id
, dst_imv_id
, msg_vid
, msg_subtype
, msg
);
279 result
= receive_msg(this, state
, in_msg
);
280 in_msg
->destroy(in_msg
);
285 METHOD(imv_agent_if_t
, batch_ending
, TNC_Result
,
286 private_imv_attestation_agent_t
*this, TNC_ConnectionID id
)
290 imv_session_t
*session
;
291 imv_attestation_state_t
*attestation_state
;
293 TNC_Result result
= TNC_RESULT_SUCCESS
;
297 if (!this->agent
->get_state(this->agent
, id
, &state
))
299 return TNC_RESULT_FATAL
;
301 attestation_state
= (imv_attestation_state_t
*)state
;
302 pts
= attestation_state
->get_pts(attestation_state
);
303 platform_info
= pts
->get_platform_info(pts
);
304 session
= state
->get_session(state
);
305 imv_id
= this->agent
->get_id(this->agent
);
307 /* exit if a recommendation has already been provided */
308 if (state
->get_action_flags(state
) & IMV_ATTESTATION_FLAG_REC
)
310 return TNC_RESULT_SUCCESS
;
313 /* send an IETF attribute request if no platform info was received */
314 if (!platform_info
&&
315 !(state
->get_action_flags(state
) & IMV_ATTESTATION_FLAG_ATTR_REQ
))
318 ietf_attr_attr_request_t
*attr_cast
;
321 attr
= ietf_attr_attr_request_create(PEN_IETF
,
322 IETF_ATTR_PRODUCT_INFORMATION
);
323 attr_cast
= (ietf_attr_attr_request_t
*)attr
;
324 attr_cast
->add(attr_cast
, PEN_IETF
, IETF_ATTR_STRING_VERSION
);
326 os_msg
= imv_msg_create(this->agent
, state
, id
, imv_id
, TNC_IMCID_ANY
,
328 os_msg
->add_attribute(os_msg
, attr
);
329 result
= os_msg
->send(os_msg
, FALSE
);
330 os_msg
->destroy(os_msg
);
332 if (result
!= TNC_RESULT_SUCCESS
)
336 state
->set_action_flags(state
, IMV_ATTESTATION_FLAG_ATTR_REQ
);
339 /* create an empty out message - we might need it */
340 out_msg
= imv_msg_create(this->agent
, state
, id
, imv_id
, TNC_IMCID_ANY
,
343 if (platform_info
&& session
&&
344 (state
->get_action_flags(state
) & IMV_ATTESTATION_FLAG_ALGO
) &&
345 !(state
->get_action_flags(state
) & IMV_ATTESTATION_FLAG_FILE_MEAS
))
347 imv_workitem_t
*workitem
;
348 bool is_dir
, no_workitems
= TRUE
;
349 u_int32_t delimiter
= SOLIDUS_UTF
;
350 u_int16_t request_id
;
353 enumerator_t
*enumerator
;
355 enumerator
= session
->create_workitem_enumerator(session
);
358 while (enumerator
->enumerate(enumerator
, &workitem
))
360 if (workitem
->get_imv_id(workitem
) != TNC_IMVID_ANY
)
365 switch (workitem
->get_type(workitem
))
367 case IMV_WORKITEM_FILE_REF_MEAS
:
368 case IMV_WORKITEM_FILE_MEAS
:
369 case IMV_WORKITEM_FILE_META
:
372 case IMV_WORKITEM_DIR_REF_MEAS
:
373 case IMV_WORKITEM_DIR_MEAS
:
374 case IMV_WORKITEM_DIR_META
:
377 case IMV_WORKITEM_TPM_ATTEST
:
379 TNC_IMV_Action_Recommendation rec
;
380 TNC_IMV_Evaluation_Result eval
;
381 bool no_d_flag
, no_t_flag
;
382 char result_str
[BUF_LEN
];
384 workitem
->set_imv_id(workitem
, imv_id
);
385 no_workitems
= FALSE
;
386 no_d_flag
= !(pts
->get_proto_caps(pts
) & PTS_PROTO_CAPS_D
);
387 no_t_flag
= !(pts
->get_proto_caps(pts
) & PTS_PROTO_CAPS_T
);
388 if (no_d_flag
|| no_t_flag
)
390 snprintf(result_str
, BUF_LEN
, "%s%s%s",
391 (no_t_flag
) ?
"no TPM available" : "",
392 (no_t_flag
&& no_d_flag
) ?
", " : "",
393 (no_d_flag
) ?
"no DH nonce negotiation" : "");
394 eval
= TNC_IMV_EVALUATION_RESULT_ERROR
;
395 session
->remove_workitem(session
, enumerator
);
396 rec
= workitem
->set_result(workitem
, result_str
, eval
);
397 state
->update_recommendation(state
, rec
, eval
);
398 imcv_db
->finalize_workitem(imcv_db
, workitem
);
399 workitem
->destroy(workitem
);
407 /* initiate file and directory measurements */
408 pathname
= this->pts_db
->get_pathname(this->pts_db
, is_dir
,
409 workitem
->get_arg_int(workitem
));
414 workitem
->set_imv_id(workitem
, imv_id
);
415 no_workitems
= FALSE
;
417 if (workitem
->get_type(workitem
) == IMV_WORKITEM_FILE_META
)
419 TNC_IMV_Action_Recommendation rec
;
420 TNC_IMV_Evaluation_Result eval
;
421 char result_str
[BUF_LEN
];
423 DBG2(DBG_IMV
, "IMV %d requests metadata for %s '%s'",
424 imv_id
, is_dir ?
"directory" : "file", pathname
);
426 /* currently just fire and forget metadata requests */
427 attr
= tcg_pts_attr_req_file_meta_create(is_dir
,
428 delimiter
, pathname
);
429 snprintf(result_str
, BUF_LEN
, "%s metadata requested",
430 is_dir ?
"directory" : "file");
431 eval
= TNC_IMV_EVALUATION_RESULT_COMPLIANT
;
432 session
->remove_workitem(session
, enumerator
);
433 rec
= workitem
->set_result(workitem
, result_str
, eval
);
434 state
->update_recommendation(state
, rec
, eval
);
435 imcv_db
->finalize_workitem(imcv_db
, workitem
);
436 workitem
->destroy(workitem
);
440 /* use lower 16 bits of the workitem ID as request ID */
441 request_id
= workitem
->get_id(workitem
) & 0xffff;
443 DBG2(DBG_IMV
, "IMV %d requests measurement %d for %s '%s'",
444 imv_id
, request_id
, is_dir ?
"directory" : "file",
446 attr
= tcg_pts_attr_req_file_meas_create(is_dir
, request_id
,
447 delimiter
, pathname
);
450 attr
->set_noskip_flag(attr
, TRUE
);
451 out_msg
->add_attribute(out_msg
, attr
);
453 enumerator
->destroy(enumerator
);
455 /* sent all file and directory measurement and metadata requests */
456 state
->set_action_flags(state
, IMV_ATTESTATION_FLAG_FILE_MEAS
);
460 DBG2(DBG_IMV
, "IMV %d has no workitems - "
461 "no evaluation requested", imv_id
);
462 state
->set_recommendation(state
,
463 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
464 TNC_IMV_EVALUATION_RESULT_DONT_KNOW
);
469 /* check the IMV state for the next PA-TNC attributes to send */
470 if (!imv_attestation_build(out_msg
, state
, this->supported_algorithms
,
471 this->supported_dh_groups
, this->pts_db
))
473 state
->set_recommendation(state
,
474 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
475 TNC_IMV_EVALUATION_RESULT_ERROR
);
476 result
= out_msg
->send_assessment(out_msg
);
477 out_msg
->destroy(out_msg
);
478 state
->set_action_flags(state
, IMV_ATTESTATION_FLAG_REC
);
480 if (result
!= TNC_RESULT_SUCCESS
)
484 return this->agent
->provide_recommendation(this->agent
, state
);
487 /* finalized all workitems? */
488 if (session
&& session
->get_policy_started(session
) &&
489 session
->get_workitem_count(session
, imv_id
) == 0 &&
490 attestation_state
->get_handshake_state(attestation_state
) ==
491 IMV_ATTESTATION_STATE_END
)
493 result
= out_msg
->send_assessment(out_msg
);
494 out_msg
->destroy(out_msg
);
495 state
->set_action_flags(state
, IMV_ATTESTATION_FLAG_REC
);
497 if (result
!= TNC_RESULT_SUCCESS
)
501 return this->agent
->provide_recommendation(this->agent
, state
);
504 /* send non-empty PA-TNC message with excl flag not set */
505 if (out_msg
->get_attribute_count(out_msg
))
507 result
= out_msg
->send(out_msg
, FALSE
);
509 out_msg
->destroy(out_msg
);
514 METHOD(imv_agent_if_t
, solicit_recommendation
, TNC_Result
,
515 private_imv_attestation_agent_t
*this, TNC_ConnectionID id
)
519 imv_attestation_state_t
*attestation_state
;
520 imv_session_t
*session
;
522 if (!this->agent
->get_state(this->agent
, id
, &state
))
524 return TNC_RESULT_FATAL
;
526 attestation_state
= (imv_attestation_state_t
*)state
;
527 session
= state
->get_session(state
);
528 imv_id
= this->agent
->get_id(this->agent
);
532 TNC_IMV_Evaluation_Result eval
;
533 TNC_IMV_Action_Recommendation rec
;
534 imv_workitem_t
*workitem
;
535 enumerator_t
*enumerator
;
537 int pending_file_meas
= 0;
539 enumerator
= session
->create_workitem_enumerator(session
);
542 while (enumerator
->enumerate(enumerator
, &workitem
))
544 if (workitem
->get_imv_id(workitem
) != imv_id
)
548 switch (workitem
->get_type(workitem
))
550 case IMV_WORKITEM_FILE_REF_MEAS
:
551 case IMV_WORKITEM_FILE_MEAS
:
552 case IMV_WORKITEM_DIR_REF_MEAS
:
553 case IMV_WORKITEM_DIR_MEAS
:
554 result_str
= "Pending file measurements";
557 case IMV_WORKITEM_TPM_ATTEST
:
558 attestation_state
->finalize_components(attestation_state
);
559 result_str
= "Pending component evidence";
564 session
->remove_workitem(session
, enumerator
);
565 eval
= TNC_IMV_EVALUATION_RESULT_ERROR
;
566 rec
= workitem
->set_result(workitem
, result_str
, eval
);
567 state
->update_recommendation(state
, rec
, eval
);
568 imcv_db
->finalize_workitem(imcv_db
, workitem
);
569 workitem
->destroy(workitem
);
571 enumerator
->destroy(enumerator
);
573 if (pending_file_meas
)
575 DBG1(DBG_IMV
, "failure due to %d pending file measurements",
577 attestation_state
->set_measurement_error(attestation_state
,
578 IMV_ATTESTATION_ERROR_FILE_MEAS_PEND
);
582 return this->agent
->provide_recommendation(this->agent
, state
);
585 METHOD(imv_agent_if_t
, destroy
, void,
586 private_imv_attestation_agent_t
*this)
590 this->pts_credmgr
->remove_set(this->pts_credmgr
,
591 this->pts_creds
->get_set(this->pts_creds
));
592 this->pts_creds
->destroy(this->pts_creds
);
594 DESTROY_IF(this->pts_db
);
595 DESTROY_IF(this->pts_credmgr
);
596 DESTROY_IF(this->agent
);
602 * Described in header.
604 imv_agent_if_t
*imv_attestation_agent_create(const char *name
, TNC_IMVID id
,
605 TNC_Version
*actual_version
)
607 private_imv_attestation_agent_t
*this;
609 char *hash_alg
, *dh_group
, *cadir
;
611 agent
= imv_agent_create(name
, msg_types
, countof(msg_types
), id
,
618 hash_alg
= lib
->settings
->get_str(lib
->settings
,
619 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
620 dh_group
= lib
->settings
->get_str(lib
->settings
,
621 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
622 cadir
= lib
->settings
->get_str(lib
->settings
,
623 "libimcv.plugins.imv-attestation.cadir", NULL
);
627 .bind_functions
= _bind_functions
,
628 .notify_connection_change
= _notify_connection_change
,
629 .receive_message
= _receive_message
,
630 .receive_message_long
= _receive_message_long
,
631 .batch_ending
= _batch_ending
,
632 .solicit_recommendation
= _solicit_recommendation
,
636 .supported_algorithms
= PTS_MEAS_ALGO_NONE
,
637 .supported_dh_groups
= PTS_DH_GROUP_NONE
,
638 .pts_credmgr
= credential_manager_create(),
639 .pts_creds
= pts_creds_create(cadir
),
640 .pts_db
= pts_database_create(imcv_db
),
645 if (!pts_meas_algo_probe(&this->supported_algorithms
) ||
646 !pts_dh_group_probe(&this->supported_dh_groups
) ||
647 !pts_meas_algo_update(hash_alg
, &this->supported_algorithms
) ||
648 !pts_dh_group_update(dh_group
, &this->supported_dh_groups
))
656 this->pts_credmgr
->add_set(this->pts_credmgr
,
657 this->pts_creds
->get_set(this->pts_creds
));
660 return &this->public;