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
56 * UTF-8 encoding of the character used to delimiter the filename
58 #define SOLIDUS_UTF 0x002F
59 #define REVERSE_SOLIDUS_UTF 0x005C
61 static imv_agent_t
*imv_attestation
;
64 * Supported PTS measurement algorithms
66 static pts_meas_algorithms_t supported_algorithms
= 0;
69 * PTS file measurement database
71 static pts_database_t
*pts_db
;
76 static pts_creds_t
*pts_creds
;
79 * PTS credential manager
81 static credential_manager_t
*pts_credmgr
;
84 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
86 TNC_Result
TNC_IMV_Initialize(TNC_IMVID imv_id
,
87 TNC_Version min_version
,
88 TNC_Version max_version
,
89 TNC_Version
*actual_version
)
91 char *hash_alg
, *uri
, *cadir
;
95 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
96 return TNC_RESULT_ALREADY_INITIALIZED
;
98 imv_attestation
= imv_agent_create(imv_name
, IMV_VENDOR_ID
, IMV_SUBTYPE
,
99 imv_id
, actual_version
);
100 if (!imv_attestation
|| !pts_meas_probe_algorithms(&supported_algorithms
))
102 return TNC_RESULT_FATAL
;
104 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
106 DBG1(DBG_IMV
, "no common IF-IMV version");
107 return TNC_RESULT_NO_COMMON_VERSION
;
111 * Specify supported PTS measurement algorithms
113 * sha1 : PTS_MEAS_ALGO_SHA1
114 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
115 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
117 * we expect the PTS-IMC to select the strongest supported algorithm
119 hash_alg
= lib
->settings
->get_str(lib
->settings
,
120 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
121 if (!strcaseeq(hash_alg
, "sha384") && !strcaseeq(hash_alg
, "sha2_384"))
123 /* remove SHA384 algorithm */
124 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA384
;
126 if (strcaseeq(hash_alg
, "sha1"))
128 /* remove SHA256 algorithm */
129 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA256
;
132 /* create a PTS credential manager */
133 pts_credmgr
= credential_manager_create();
135 /* create PTS credential set */
136 cadir
= lib
->settings
->get_str(lib
->settings
,
137 "libimcv.plugins.imv-attestation.cadir", NULL
);
138 pts_creds
= pts_creds_create(cadir
);
141 pts_credmgr
->add_set(pts_credmgr
, pts_creds
->get_set(pts_creds
));
144 /* attach file measurement database */
145 uri
= lib
->settings
->get_str(lib
->settings
,
146 "libimcv.plugins.imv-attestation.database", NULL
);
147 pts_db
= pts_database_create(uri
);
149 return TNC_RESULT_SUCCESS
;
153 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
155 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
156 TNC_ConnectionID connection_id
,
157 TNC_ConnectionState new_state
)
160 imv_attestation_state_t
*attestation_state
;
163 if (!imv_attestation
)
165 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
166 return TNC_RESULT_NOT_INITIALIZED
;
170 case TNC_CONNECTION_STATE_CREATE
:
171 state
= imv_attestation_state_create(connection_id
);
172 return imv_attestation
->create_state(imv_attestation
, state
);
173 case TNC_CONNECTION_STATE_DELETE
:
174 return imv_attestation
->delete_state(imv_attestation
, connection_id
);
175 case TNC_CONNECTION_STATE_HANDSHAKE
:
176 result
= imv_attestation
->change_state(imv_attestation
, connection_id
,
178 if (result
!= TNC_RESULT_SUCCESS
)
182 attestation_state
= (imv_attestation_state_t
*)state
;
184 /* TODO: Get some configurations */
186 return TNC_RESULT_SUCCESS
;
188 return imv_attestation
->change_state(imv_attestation
, connection_id
,
193 static TNC_Result
send_message(TNC_ConnectionID connection_id
)
199 imv_attestation_state_t
*attestation_state
;
200 imv_attestation_handshake_state_t handshake_state
;
203 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
205 return TNC_RESULT_FATAL
;
207 attestation_state
= (imv_attestation_state_t
*)state
;
208 handshake_state
= attestation_state
->get_handshake_state(attestation_state
);
209 pts
= attestation_state
->get_pts(attestation_state
);
211 msg
= pa_tnc_msg_create();
214 /* Switch on the attribute type IMV has received */
215 switch (handshake_state
)
217 case IMV_ATTESTATION_STATE_INIT
:
219 pts_proto_caps_flag_t flags
;
221 /* Send Request Protocol Capabilities attribute */
222 flags
= pts
->get_proto_caps(pts
);
223 attr
= tcg_pts_attr_proto_caps_create(flags
, TRUE
);
224 attr
->set_noskip_flag(attr
, TRUE
);
225 msg
->add_attribute(msg
, attr
);
227 /* Send Measurement Algorithms attribute */
228 attr
= tcg_pts_attr_meas_algo_create(supported_algorithms
, FALSE
);
229 attr
->set_noskip_flag(attr
, TRUE
);
230 msg
->add_attribute(msg
, attr
);
232 attestation_state
->set_handshake_state(attestation_state
,
233 IMV_ATTESTATION_STATE_MEAS
);
237 case IMV_ATTESTATION_STATE_MEAS
:
239 enumerator_t
*enumerator
;
240 u_int32_t delimiter
= SOLIDUS_UTF
;
241 char *platform_info
, *pathname
;
245 attestation_state
->set_handshake_state(attestation_state
,
246 IMV_ATTESTATION_STATE_END
);
248 /* Does the PTS-IMC have TPM support? */
249 if (pts
->get_proto_caps(pts
) & PTS_PROTO_CAPS_T
)
251 /* Send Get TPM Version attribute */
252 attr
= tcg_pts_attr_get_tpm_version_info_create();
253 attr
->set_noskip_flag(attr
, TRUE
);
254 msg
->add_attribute(msg
, attr
);
256 /* Send Get AIK attribute */
257 attr
= tcg_pts_attr_get_aik_create();
258 attr
->set_noskip_flag(attr
, TRUE
);
259 msg
->add_attribute(msg
, attr
);
262 /* Get Platform and OS of the PTS-IMC */
263 platform_info
= pts
->get_platform_info(pts
);
265 if (!pts_db
|| !platform_info
)
267 DBG1(DBG_IMV
, "%s%s%s not available",
268 (pts_db
) ?
"" : "pts database",
269 (!pts_db
&& !platform_info
) ?
"and" : "",
270 (platform_info
) ?
"" : "platform info");
273 DBG1(DBG_IMV
, "platform is '%s'", platform_info
);
275 /* Send Request File Measurement attribute */
276 enumerator
= pts_db
->create_file_enumerator(pts_db
, platform_info
);
281 while (enumerator
->enumerate(enumerator
, &id
, &type
, &pathname
))
283 is_dir
= (type
!= 0);
284 DBG2(DBG_IMV
, "measurement request %d for %s '%s'",
285 id
, is_dir ?
"directory" : "file", pathname
);
286 attr
= tcg_pts_attr_req_file_meas_create(is_dir
, id
, delimiter
,
288 attr
->set_noskip_flag(attr
, TRUE
);
289 msg
->add_attribute(msg
, attr
);
290 attestation_state
->add_request(attestation_state
, id
, is_dir
);
292 enumerator
->destroy(enumerator
);
295 case IMV_ATTESTATION_STATE_COMP_EVID
:
296 case IMV_ATTESTATION_STATE_IML
:
297 DBG1(DBG_IMV
, "Attestation IMV has nothing to send: \"%s\"",
299 return TNC_RESULT_FATAL
;
301 DBG1(DBG_IMV
, "Attestation IMV is in unknown state: \"%s\"",
303 return TNC_RESULT_FATAL
;
307 result
= imv_attestation
->send_message(imv_attestation
, connection_id
,
308 msg
->get_encoding(msg
));
315 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
317 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
318 TNC_ConnectionID connection_id
,
319 TNC_BufferReference msg
,
321 TNC_MessageType msg_type
)
323 pa_tnc_msg_t
*pa_tnc_msg
;
326 imv_attestation_state_t
*attestation_state
;
328 enumerator_t
*enumerator
;
330 bool fatal_error
= FALSE
;
331 bool measurement_error
= FALSE
;
333 if (!imv_attestation
)
335 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
336 return TNC_RESULT_NOT_INITIALIZED
;
339 /* get current IMV state */
340 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
342 return TNC_RESULT_FATAL
;
344 attestation_state
= (imv_attestation_state_t
*)state
;
345 pts
= attestation_state
->get_pts(attestation_state
);
347 /* parse received PA-TNC message and automatically handle any errors */
348 result
= imv_attestation
->receive_message(imv_attestation
, connection_id
,
349 chunk_create(msg
, msg_len
), msg_type
,
352 /* no parsed PA-TNC attributes available if an error occurred */
358 /* analyze PA-TNC attributes */
359 enumerator
= pa_tnc_msg
->create_attribute_enumerator(pa_tnc_msg
);
360 while (enumerator
->enumerate(enumerator
, &attr
))
362 if (attr
->get_vendor_id(attr
) == PEN_IETF
)
364 if (attr
->get_type(attr
) == IETF_ATTR_PA_TNC_ERROR
)
366 ietf_attr_pa_tnc_error_t
*error_attr
;
367 pen_t error_vendor_id
;
368 pa_tnc_error_code_t error_code
;
369 chunk_t msg_info
, attr_info
;
372 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
373 error_vendor_id
= error_attr
->get_vendor_id(error_attr
);
374 error_code
= error_attr
->get_error_code(error_attr
);
375 msg_info
= error_attr
->get_msg_info(error_attr
);
377 if (error_vendor_id
== PEN_IETF
)
379 DBG1(DBG_IMV
, "received PA-TNC error '%N' "
380 "concerning message %#B",
381 pa_tnc_error_code_names
, error_code
, &msg_info
);
385 case PA_ERROR_INVALID_PARAMETER
:
386 offset
= error_attr
->get_offset(error_attr
);
387 DBG1(DBG_IMV
, " occurred at offset of %u bytes",
390 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED
:
391 attr_info
= error_attr
->get_attr_info(error_attr
);
392 DBG1(DBG_IMV
, " unsupported attribute %#B",
399 else if (error_vendor_id
== PEN_TCG
)
401 DBG1(DBG_IMV
, "received TCG-PTS error '%N'",
402 pts_error_code_names
, error_code
);
403 DBG1(DBG_IMV
, "error information: %B", &msg_info
);
407 else if (attr
->get_type(attr
) == IETF_ATTR_PRODUCT_INFORMATION
)
409 ietf_attr_product_info_t
*attr_cast
;
412 attr_cast
= (ietf_attr_product_info_t
*)attr
;
413 platform_info
= attr_cast
->get_info(attr_cast
, NULL
, NULL
);
414 pts
->set_platform_info(pts
, platform_info
);
417 else if (attr
->get_vendor_id(attr
) == PEN_TCG
)
419 switch (attr
->get_type(attr
))
421 case TCG_PTS_PROTO_CAPS
:
423 tcg_pts_attr_proto_caps_t
*attr_cast
;
424 pts_proto_caps_flag_t flags
;
426 attr_cast
= (tcg_pts_attr_proto_caps_t
*)attr
;
427 flags
= attr_cast
->get_flags(attr_cast
);
428 pts
->set_proto_caps(pts
, flags
);
431 case TCG_PTS_MEAS_ALGO_SELECTION
:
433 tcg_pts_attr_meas_algo_t
*attr_cast
;
434 pts_meas_algorithms_t selected_algorithm
;
436 attr_cast
= (tcg_pts_attr_meas_algo_t
*)attr
;
437 selected_algorithm
= attr_cast
->get_algorithms(attr_cast
);
438 pts
->set_meas_algorithm(pts
, selected_algorithm
);
441 case TCG_PTS_TPM_VERSION_INFO
:
443 tcg_pts_attr_tpm_version_info_t
*attr_cast
;
444 chunk_t tpm_version_info
;
446 attr_cast
= (tcg_pts_attr_tpm_version_info_t
*)attr
;
447 tpm_version_info
= attr_cast
->get_tpm_version_info(attr_cast
);
448 pts
->set_tpm_version_info(pts
, tpm_version_info
);
453 tcg_pts_attr_aik_t
*attr_cast
;
454 certificate_t
*aik
, *issuer
;
456 bool trusted
= FALSE
;
458 attr_cast
= (tcg_pts_attr_aik_t
*)attr
;
459 aik
= attr_cast
->get_aik(attr_cast
);
462 /* TODO generate error attribute */
465 if (aik
->get_type(aik
) == CERT_X509
)
467 DBG1(DBG_IMV
, "verifying AIK certificate");
468 e
= pts_credmgr
->create_trusted_enumerator(pts_credmgr
,
469 KEY_ANY
, aik
->get_issuer(aik
), FALSE
);
470 while (e
->enumerate(e
, &issuer
))
472 if (aik
->issued_by(aik
, issuer
))
479 DBG1(DBG_IMV
, "AIK certificate is %strusted",
480 trusted ?
"" : "not ");
482 pts
->set_aik(pts
, aik
);
486 /* PTS-based Attestation Evidence */
487 case TCG_PTS_SIMPLE_COMP_EVID
:
489 case TCG_PTS_SIMPLE_EVID_FINAL
:
491 case TCG_PTS_FILE_MEAS
:
493 tcg_pts_attr_file_meas_t
*attr_cast
;
494 u_int16_t request_id
;
496 pts_meas_algorithms_t algo
;
497 pts_file_meas_t
*measurements
;
499 enumerator_t
*e_hash
;
502 platform_info
= pts
->get_platform_info(pts
);
503 if (!pts_db
|| !platform_info
)
508 attr_cast
= (tcg_pts_attr_file_meas_t
*)attr
;
509 measurements
= attr_cast
->get_measurements(attr_cast
);
510 algo
= pts
->get_meas_algorithm(pts
);
511 request_id
= measurements
->get_request_id(measurements
);
512 file_count
= measurements
->get_file_count(measurements
);
514 DBG1(DBG_IMV
, "measurement request %d returned %d file%s:",
515 request_id
, file_count
, (file_count
== 1) ?
"":"s");
517 if (!attestation_state
->check_off_request(attestation_state
,
518 request_id
, &is_dir
))
520 DBG1(DBG_IMV
, " no entry found for this request");
524 /* check hashes from database against measurements */
525 e_hash
= pts_db
->create_hash_enumerator(pts_db
,
526 platform_info
, algo
, request_id
, is_dir
);
527 if (!measurements
->verify(measurements
, e_hash
, is_dir
))
529 measurement_error
= TRUE
;
531 e_hash
->destroy(e_hash
);
535 /* TODO: Not implemented yet */
536 case TCG_PTS_DH_NONCE_PARAMS_RESP
:
537 case TCG_PTS_UNIX_FILE_META
:
538 case TCG_PTS_INTEG_MEAS_LOG
:
539 /* Attributes using XML */
540 case TCG_PTS_TEMPL_REF_MANI_SET_META
:
541 case TCG_PTS_VERIFICATION_RESULT
:
542 case TCG_PTS_INTEG_REPORT
:
544 case TCG_PTS_WIN_FILE_META
:
545 case TCG_PTS_REGISTRY_VALUE
:
546 /* Received on IMC side only*/
547 case TCG_PTS_REQ_PROTO_CAPS
:
548 case TCG_PTS_DH_NONCE_PARAMS_REQ
:
549 case TCG_PTS_DH_NONCE_FINISH
:
550 case TCG_PTS_MEAS_ALGO
:
551 case TCG_PTS_GET_TPM_VERSION_INFO
:
552 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META
:
553 case TCG_PTS_UPDATE_TEMPL_REF_MANI
:
554 case TCG_PTS_GET_AIK
:
555 case TCG_PTS_REQ_FUNCT_COMP_EVID
:
556 case TCG_PTS_GEN_ATTEST_EVID
:
557 case TCG_PTS_REQ_FILE_META
:
558 case TCG_PTS_REQ_FILE_MEAS
:
559 case TCG_PTS_REQ_INTEG_MEAS_LOG
:
561 DBG1(DBG_IMV
, "received unsupported attribute '%N'",
562 tcg_attr_names
, attr
->get_type(attr
));
567 enumerator
->destroy(enumerator
);
568 pa_tnc_msg
->destroy(pa_tnc_msg
);
573 state
->set_recommendation(state
,
574 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
575 TNC_IMV_EVALUATION_RESULT_ERROR
);
576 return imv_attestation
->provide_recommendation(imv_attestation
,
580 if (attestation_state
->get_handshake_state(attestation_state
) &
581 IMV_ATTESTATION_STATE_END
)
583 if (attestation_state
->get_request_count(attestation_state
))
585 DBG1(DBG_IMV
, "failure due to %d pending file measurements",
586 attestation_state
->get_request_count(attestation_state
));
587 measurement_error
= TRUE
;
589 if (measurement_error
)
591 state
->set_recommendation(state
,
592 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
593 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR
);
597 state
->set_recommendation(state
,
598 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
599 TNC_IMV_EVALUATION_RESULT_COMPLIANT
);
601 return imv_attestation
->provide_recommendation(imv_attestation
,
605 return send_message(connection_id
);
609 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
611 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
612 TNC_ConnectionID connection_id
)
614 if (!imv_attestation
)
616 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
617 return TNC_RESULT_NOT_INITIALIZED
;
619 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
623 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
625 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
626 TNC_ConnectionID connection_id
)
629 imv_attestation_state_t
*attestation_state
;
631 if (!imv_attestation
)
633 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
634 return TNC_RESULT_NOT_INITIALIZED
;
636 /* get current IMV state */
637 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
639 return TNC_RESULT_FATAL
;
641 attestation_state
= (imv_attestation_state_t
*)state
;
643 /* Check if IMV has to initiate the PA-TNC exchange */
644 if (attestation_state
->get_handshake_state(attestation_state
) ==
645 IMV_ATTESTATION_STATE_INIT
)
647 return send_message(connection_id
);
649 return TNC_RESULT_SUCCESS
;
653 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
655 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
657 if (!imv_attestation
)
659 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
660 return TNC_RESULT_NOT_INITIALIZED
;
664 pts_credmgr
->remove_set(pts_credmgr
, pts_creds
->get_set(pts_creds
));
665 pts_creds
->destroy(pts_creds
);
668 pts_credmgr
->destroy(pts_credmgr
);
669 imv_attestation
->destroy(imv_attestation
);
670 imv_attestation
= NULL
;
672 return TNC_RESULT_SUCCESS
;
676 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
678 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
679 TNC_TNCS_BindFunctionPointer bind_function
)
681 if (!imv_attestation
)
683 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
684 return TNC_RESULT_NOT_INITIALIZED
;
686 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);