2 * Copyright (C) 2011 Sansar Choinyambuu
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
16 #include "imv_attestation_state.h"
18 #include <imv/imv_agent.h>
19 #include <pa_tnc/pa_tnc_msg.h>
20 #include <ietf/ietf_attr.h>
21 #include <ietf/ietf_attr_pa_tnc_error.h>
22 #include <ietf/ietf_attr_product_info.h>
24 #include <pts/pts_database.h>
25 #include <pts/pts_creds.h>
26 #include <pts/pts_error.h>
28 #include <tcg/tcg_attr.h>
29 #include <tcg/tcg_pts_attr_proto_caps.h>
30 #include <tcg/tcg_pts_attr_meas_algo.h>
31 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
32 #include <tcg/tcg_pts_attr_tpm_version_info.h>
33 #include <tcg/tcg_pts_attr_get_aik.h>
34 #include <tcg/tcg_pts_attr_aik.h>
35 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
36 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
37 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
38 #include <tcg/tcg_pts_attr_simple_evid_final.h>
39 #include <tcg/tcg_pts_attr_req_file_meas.h>
40 #include <tcg/tcg_pts_attr_file_meas.h>
42 #include <tncif_pa_subtypes.h>
46 #include <credentials/credential_manager.h>
50 static const char imv_name
[] = "Attestation";
52 #define IMV_VENDOR_ID PEN_TCG
53 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
55 static imv_agent_t
*imv_attestation
;
58 * Supported PTS measurement algorithms
60 static pts_meas_algorithms_t supported_algorithms
= 0;
63 * PTS file measurement database
65 static pts_database_t
*pts_db
;
70 static pts_creds_t
*pts_creds
;
73 * PTS credential manager
75 static credential_manager_t
*pts_credmgr
;
78 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
80 TNC_Result
TNC_IMV_Initialize(TNC_IMVID imv_id
,
81 TNC_Version min_version
,
82 TNC_Version max_version
,
83 TNC_Version
*actual_version
)
85 char *hash_alg
, *uri
, *cadir
;
89 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
90 return TNC_RESULT_ALREADY_INITIALIZED
;
92 imv_attestation
= imv_agent_create(imv_name
, IMV_VENDOR_ID
, IMV_SUBTYPE
,
93 imv_id
, actual_version
);
94 if (!imv_attestation
|| !pts_meas_probe_algorithms(&supported_algorithms
))
96 return TNC_RESULT_FATAL
;
98 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
100 DBG1(DBG_IMV
, "no common IF-IMV version");
101 return TNC_RESULT_NO_COMMON_VERSION
;
105 * Specify supported PTS measurement algorithms
107 * sha1 : PTS_MEAS_ALGO_SHA1
108 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
109 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
111 * we expect the PTS-IMC to select the strongest supported algorithm
113 hash_alg
= lib
->settings
->get_str(lib
->settings
,
114 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
115 if (!strcaseeq(hash_alg
, "sha384") && !strcaseeq(hash_alg
, "sha2_384"))
117 /* remove SHA384 algorithm */
118 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA384
;
120 if (strcaseeq(hash_alg
, "sha1"))
122 /* remove SHA256 algorithm */
123 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA256
;
126 /* create a PTS credential manager */
127 pts_credmgr
= credential_manager_create();
129 /* create PTS credential set */
130 cadir
= lib
->settings
->get_str(lib
->settings
,
131 "libimcv.plugins.imv-attestation.cadir", NULL
);
132 pts_creds
= pts_creds_create(cadir
);
135 pts_credmgr
->add_set(pts_credmgr
, pts_creds
->get_set(pts_creds
));
138 /* attach file measurement database */
139 uri
= lib
->settings
->get_str(lib
->settings
,
140 "libimcv.plugins.imv-attestation.database", NULL
);
141 pts_db
= pts_database_create(uri
);
143 return TNC_RESULT_SUCCESS
;
147 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
149 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
150 TNC_ConnectionID connection_id
,
151 TNC_ConnectionState new_state
)
154 imv_attestation_state_t
*attestation_state
;
157 if (!imv_attestation
)
159 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
160 return TNC_RESULT_NOT_INITIALIZED
;
164 case TNC_CONNECTION_STATE_CREATE
:
165 state
= imv_attestation_state_create(connection_id
);
166 return imv_attestation
->create_state(imv_attestation
, state
);
167 case TNC_CONNECTION_STATE_DELETE
:
168 return imv_attestation
->delete_state(imv_attestation
, connection_id
);
169 case TNC_CONNECTION_STATE_HANDSHAKE
:
170 result
= imv_attestation
->change_state(imv_attestation
, connection_id
,
172 if (result
!= TNC_RESULT_SUCCESS
)
176 attestation_state
= (imv_attestation_state_t
*)state
;
178 /* TODO: Get some configurations */
180 return TNC_RESULT_SUCCESS
;
182 return imv_attestation
->change_state(imv_attestation
, connection_id
,
187 static TNC_Result
send_message(TNC_ConnectionID connection_id
)
193 imv_attestation_state_t
*attestation_state
;
194 imv_attestation_handshake_state_t handshake_state
;
197 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
199 return TNC_RESULT_FATAL
;
201 attestation_state
= (imv_attestation_state_t
*)state
;
202 handshake_state
= attestation_state
->get_handshake_state(attestation_state
);
203 pts
= attestation_state
->get_pts(attestation_state
);
205 msg
= pa_tnc_msg_create();
208 /* Switch on the attribute type IMV has received */
209 switch (handshake_state
)
211 case IMV_ATTESTATION_STATE_INIT
:
213 pts_proto_caps_flag_t flags
;
215 /* Send Request Protocol Capabilities attribute */
216 flags
= pts
->get_proto_caps(pts
);
217 attr
= tcg_pts_attr_proto_caps_create(flags
, TRUE
);
218 attr
->set_noskip_flag(attr
, TRUE
);
219 msg
->add_attribute(msg
, attr
);
221 /* Send Measurement Algorithms attribute */
222 attr
= tcg_pts_attr_meas_algo_create(supported_algorithms
, FALSE
);
223 attr
->set_noskip_flag(attr
, TRUE
);
224 msg
->add_attribute(msg
, attr
);
226 attestation_state
->set_handshake_state(attestation_state
,
227 IMV_ATTESTATION_STATE_MEAS
);
231 case IMV_ATTESTATION_STATE_MEAS
:
233 enumerator_t
*enumerator
;
234 u_int32_t delimiter
= SOLIDUS_UTF
;
235 char *platform_info
, *pathname
;
239 attestation_state
->set_handshake_state(attestation_state
,
240 IMV_ATTESTATION_STATE_END
);
242 /* Does the PTS-IMC have TPM support? */
243 if (pts
->get_proto_caps(pts
) & PTS_PROTO_CAPS_T
)
245 /* Send Get TPM Version attribute */
246 attr
= tcg_pts_attr_get_tpm_version_info_create();
247 attr
->set_noskip_flag(attr
, TRUE
);
248 msg
->add_attribute(msg
, attr
);
250 /* Send Get AIK attribute */
251 attr
= tcg_pts_attr_get_aik_create();
252 attr
->set_noskip_flag(attr
, TRUE
);
253 msg
->add_attribute(msg
, attr
);
256 /* Get Platform and OS of the PTS-IMC */
257 platform_info
= pts
->get_platform_info(pts
);
259 if (!pts_db
|| !platform_info
)
261 DBG1(DBG_IMV
, "%s%s%s not available",
262 (pts_db
) ?
"" : "pts database",
263 (!pts_db
&& !platform_info
) ?
"and" : "",
264 (platform_info
) ?
"" : "platform info");
267 DBG1(DBG_IMV
, "platform is '%s'", platform_info
);
269 /* Send Request File Measurement attribute */
270 enumerator
= pts_db
->create_file_enumerator(pts_db
, platform_info
);
275 while (enumerator
->enumerate(enumerator
, &id
, &type
, &pathname
))
277 is_dir
= (type
!= 0);
278 DBG2(DBG_IMV
, "measurement request %d for %s '%s'",
279 id
, is_dir ?
"directory" : "file", pathname
);
280 attr
= tcg_pts_attr_req_file_meas_create(is_dir
, id
, delimiter
,
282 attr
->set_noskip_flag(attr
, TRUE
);
283 msg
->add_attribute(msg
, attr
);
284 attestation_state
->add_request(attestation_state
, id
, is_dir
);
286 enumerator
->destroy(enumerator
);
289 case IMV_ATTESTATION_STATE_COMP_EVID
:
290 case IMV_ATTESTATION_STATE_IML
:
291 DBG1(DBG_IMV
, "Attestation IMV has nothing to send: \"%s\"",
293 return TNC_RESULT_FATAL
;
295 DBG1(DBG_IMV
, "Attestation IMV is in unknown state: \"%s\"",
297 return TNC_RESULT_FATAL
;
301 result
= imv_attestation
->send_message(imv_attestation
, connection_id
,
302 msg
->get_encoding(msg
));
309 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
311 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
312 TNC_ConnectionID connection_id
,
313 TNC_BufferReference msg
,
315 TNC_MessageType msg_type
)
317 pa_tnc_msg_t
*pa_tnc_msg
;
320 imv_attestation_state_t
*attestation_state
;
322 enumerator_t
*enumerator
;
324 bool fatal_error
= FALSE
;
325 bool measurement_error
= FALSE
;
327 if (!imv_attestation
)
329 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
330 return TNC_RESULT_NOT_INITIALIZED
;
333 /* get current IMV state */
334 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
336 return TNC_RESULT_FATAL
;
338 attestation_state
= (imv_attestation_state_t
*)state
;
339 pts
= attestation_state
->get_pts(attestation_state
);
341 /* parse received PA-TNC message and automatically handle any errors */
342 result
= imv_attestation
->receive_message(imv_attestation
, connection_id
,
343 chunk_create(msg
, msg_len
), msg_type
,
346 /* no parsed PA-TNC attributes available if an error occurred */
352 /* analyze PA-TNC attributes */
353 enumerator
= pa_tnc_msg
->create_attribute_enumerator(pa_tnc_msg
);
354 while (enumerator
->enumerate(enumerator
, &attr
))
356 if (attr
->get_vendor_id(attr
) == PEN_IETF
)
358 if (attr
->get_type(attr
) == IETF_ATTR_PA_TNC_ERROR
)
360 ietf_attr_pa_tnc_error_t
*error_attr
;
361 pen_t error_vendor_id
;
362 pa_tnc_error_code_t error_code
;
363 chunk_t msg_info
, attr_info
;
366 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
367 error_vendor_id
= error_attr
->get_vendor_id(error_attr
);
368 error_code
= error_attr
->get_error_code(error_attr
);
369 msg_info
= error_attr
->get_msg_info(error_attr
);
371 if (error_vendor_id
== PEN_IETF
)
373 DBG1(DBG_IMV
, "received PA-TNC error '%N' "
374 "concerning message %#B",
375 pa_tnc_error_code_names
, error_code
, &msg_info
);
379 case PA_ERROR_INVALID_PARAMETER
:
380 offset
= error_attr
->get_offset(error_attr
);
381 DBG1(DBG_IMV
, " occurred at offset of %u bytes",
384 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED
:
385 attr_info
= error_attr
->get_attr_info(error_attr
);
386 DBG1(DBG_IMV
, " unsupported attribute %#B",
393 else if (error_vendor_id
== PEN_TCG
)
395 DBG1(DBG_IMV
, "received TCG-PTS error '%N'",
396 pts_error_code_names
, error_code
);
397 DBG1(DBG_IMV
, "error information: %B", &msg_info
);
401 else if (attr
->get_type(attr
) == IETF_ATTR_PRODUCT_INFORMATION
)
403 ietf_attr_product_info_t
*attr_cast
;
406 attr_cast
= (ietf_attr_product_info_t
*)attr
;
407 platform_info
= attr_cast
->get_info(attr_cast
, NULL
, NULL
);
408 pts
->set_platform_info(pts
, platform_info
);
411 else if (attr
->get_vendor_id(attr
) == PEN_TCG
)
413 switch (attr
->get_type(attr
))
415 case TCG_PTS_PROTO_CAPS
:
417 tcg_pts_attr_proto_caps_t
*attr_cast
;
418 pts_proto_caps_flag_t flags
;
420 attr_cast
= (tcg_pts_attr_proto_caps_t
*)attr
;
421 flags
= attr_cast
->get_flags(attr_cast
);
422 pts
->set_proto_caps(pts
, flags
);
425 case TCG_PTS_MEAS_ALGO_SELECTION
:
427 tcg_pts_attr_meas_algo_t
*attr_cast
;
428 pts_meas_algorithms_t selected_algorithm
;
430 attr_cast
= (tcg_pts_attr_meas_algo_t
*)attr
;
431 selected_algorithm
= attr_cast
->get_algorithms(attr_cast
);
432 pts
->set_meas_algorithm(pts
, selected_algorithm
);
435 case TCG_PTS_TPM_VERSION_INFO
:
437 tcg_pts_attr_tpm_version_info_t
*attr_cast
;
438 chunk_t tpm_version_info
;
440 attr_cast
= (tcg_pts_attr_tpm_version_info_t
*)attr
;
441 tpm_version_info
= attr_cast
->get_tpm_version_info(attr_cast
);
442 pts
->set_tpm_version_info(pts
, tpm_version_info
);
447 tcg_pts_attr_aik_t
*attr_cast
;
448 certificate_t
*aik
, *issuer
;
450 bool trusted
= FALSE
;
452 attr_cast
= (tcg_pts_attr_aik_t
*)attr
;
453 aik
= attr_cast
->get_aik(attr_cast
);
456 /* TODO generate error attribute */
459 if (aik
->get_type(aik
) == CERT_X509
)
461 DBG1(DBG_IMV
, "verifying AIK certificate");
462 e
= pts_credmgr
->create_trusted_enumerator(pts_credmgr
,
463 KEY_ANY
, aik
->get_issuer(aik
), FALSE
);
464 while (e
->enumerate(e
, &issuer
))
466 if (aik
->issued_by(aik
, issuer
))
473 DBG1(DBG_IMV
, "AIK certificate is %strusted",
474 trusted ?
"" : "not ");
476 pts
->set_aik(pts
, aik
);
480 /* PTS-based Attestation Evidence */
481 case TCG_PTS_SIMPLE_COMP_EVID
:
483 case TCG_PTS_SIMPLE_EVID_FINAL
:
485 case TCG_PTS_FILE_MEAS
:
487 tcg_pts_attr_file_meas_t
*attr_cast
;
488 u_int16_t request_id
;
490 pts_meas_algorithms_t algo
;
491 pts_file_meas_t
*measurements
;
493 enumerator_t
*e_hash
;
496 platform_info
= pts
->get_platform_info(pts
);
497 if (!pts_db
|| !platform_info
)
502 attr_cast
= (tcg_pts_attr_file_meas_t
*)attr
;
503 measurements
= attr_cast
->get_measurements(attr_cast
);
504 algo
= pts
->get_meas_algorithm(pts
);
505 request_id
= measurements
->get_request_id(measurements
);
506 file_count
= measurements
->get_file_count(measurements
);
508 DBG1(DBG_IMV
, "measurement request %d returned %d file%s:",
509 request_id
, file_count
, (file_count
== 1) ?
"":"s");
511 if (!attestation_state
->check_off_request(attestation_state
,
512 request_id
, &is_dir
))
514 DBG1(DBG_IMV
, " no entry found for this request");
518 /* check hashes from database against measurements */
519 e_hash
= pts_db
->create_hash_enumerator(pts_db
,
520 platform_info
, algo
, request_id
, is_dir
);
521 if (!measurements
->verify(measurements
, e_hash
, is_dir
))
523 measurement_error
= TRUE
;
525 e_hash
->destroy(e_hash
);
529 /* TODO: Not implemented yet */
530 case TCG_PTS_DH_NONCE_PARAMS_RESP
:
531 case TCG_PTS_UNIX_FILE_META
:
532 case TCG_PTS_INTEG_MEAS_LOG
:
533 /* Attributes using XML */
534 case TCG_PTS_TEMPL_REF_MANI_SET_META
:
535 case TCG_PTS_VERIFICATION_RESULT
:
536 case TCG_PTS_INTEG_REPORT
:
538 case TCG_PTS_WIN_FILE_META
:
539 case TCG_PTS_REGISTRY_VALUE
:
540 /* Received on IMC side only*/
541 case TCG_PTS_REQ_PROTO_CAPS
:
542 case TCG_PTS_DH_NONCE_PARAMS_REQ
:
543 case TCG_PTS_DH_NONCE_FINISH
:
544 case TCG_PTS_MEAS_ALGO
:
545 case TCG_PTS_GET_TPM_VERSION_INFO
:
546 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META
:
547 case TCG_PTS_UPDATE_TEMPL_REF_MANI
:
548 case TCG_PTS_GET_AIK
:
549 case TCG_PTS_REQ_FUNCT_COMP_EVID
:
550 case TCG_PTS_GEN_ATTEST_EVID
:
551 case TCG_PTS_REQ_FILE_META
:
552 case TCG_PTS_REQ_FILE_MEAS
:
553 case TCG_PTS_REQ_INTEG_MEAS_LOG
:
555 DBG1(DBG_IMV
, "received unsupported attribute '%N'",
556 tcg_attr_names
, attr
->get_type(attr
));
561 enumerator
->destroy(enumerator
);
562 pa_tnc_msg
->destroy(pa_tnc_msg
);
567 state
->set_recommendation(state
,
568 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
569 TNC_IMV_EVALUATION_RESULT_ERROR
);
570 return imv_attestation
->provide_recommendation(imv_attestation
,
574 if (attestation_state
->get_handshake_state(attestation_state
) &
575 IMV_ATTESTATION_STATE_END
)
577 if (attestation_state
->get_request_count(attestation_state
))
579 DBG1(DBG_IMV
, "failure due to %d pending file measurements",
580 attestation_state
->get_request_count(attestation_state
));
581 measurement_error
= TRUE
;
583 if (measurement_error
)
585 state
->set_recommendation(state
,
586 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
587 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR
);
591 state
->set_recommendation(state
,
592 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
593 TNC_IMV_EVALUATION_RESULT_COMPLIANT
);
595 return imv_attestation
->provide_recommendation(imv_attestation
,
599 return send_message(connection_id
);
603 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
605 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
606 TNC_ConnectionID connection_id
)
608 if (!imv_attestation
)
610 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
611 return TNC_RESULT_NOT_INITIALIZED
;
613 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
617 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
619 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
620 TNC_ConnectionID connection_id
)
623 imv_attestation_state_t
*attestation_state
;
625 if (!imv_attestation
)
627 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
628 return TNC_RESULT_NOT_INITIALIZED
;
630 /* get current IMV state */
631 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
633 return TNC_RESULT_FATAL
;
635 attestation_state
= (imv_attestation_state_t
*)state
;
637 /* Check if IMV has to initiate the PA-TNC exchange */
638 if (attestation_state
->get_handshake_state(attestation_state
) ==
639 IMV_ATTESTATION_STATE_INIT
)
641 return send_message(connection_id
);
643 return TNC_RESULT_SUCCESS
;
647 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
649 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
651 if (!imv_attestation
)
653 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
654 return TNC_RESULT_NOT_INITIALIZED
;
658 pts_credmgr
->remove_set(pts_credmgr
, pts_creds
->get_set(pts_creds
));
659 pts_creds
->destroy(pts_creds
);
662 DESTROY_IF(pts_credmgr
);
663 imv_attestation
->destroy(imv_attestation
);
664 imv_attestation
= NULL
;
666 return TNC_RESULT_SUCCESS
;
670 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
672 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
673 TNC_TNCS_BindFunctionPointer bind_function
)
675 if (!imv_attestation
)
677 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
678 return TNC_RESULT_NOT_INITIALIZED
;
680 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);