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>
23 #include <tcg/pts/pts_database.h>
24 #include <tcg/pts/pts_error.h>
26 #include <tcg/tcg_attr.h>
27 #include <tcg/tcg_pts_attr_proto_caps.h>
28 #include <tcg/tcg_pts_attr_meas_algo.h>
29 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
30 #include <tcg/tcg_pts_attr_tpm_version_info.h>
31 #include <tcg/tcg_pts_attr_get_aik.h>
32 #include <tcg/tcg_pts_attr_aik.h>
33 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
34 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
35 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
36 #include <tcg/tcg_pts_attr_simple_evid_final.h>
37 #include <tcg/tcg_pts_attr_req_file_meas.h>
38 #include <tcg/tcg_pts_attr_file_meas.h>
40 #include <tncif_pa_subtypes.h>
44 #include <utils/linked_list.h>
48 static const char imv_name
[] = "Attestation";
50 #define IMV_VENDOR_ID PEN_TCG
51 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
54 * UTF-8 encoding of the character used to delimiter the filename
56 #define SOLIDUS_UTF 0x002F
57 #define REVERSE_SOLIDUS_UTF 0x005C
59 static imv_agent_t
*imv_attestation
;
62 * Supported PTS measurement algorithms
64 static pts_meas_algorithms_t supported_algorithms
= 0;
67 * PTS file measurement database
69 static pts_database_t
*pts_db
;
72 * List of id's for the files that are requested for measurement
74 static linked_list_t
*requested_files
;
77 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
79 TNC_Result
TNC_IMV_Initialize(TNC_IMVID imv_id
,
80 TNC_Version min_version
,
81 TNC_Version max_version
,
82 TNC_Version
*actual_version
)
88 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
89 return TNC_RESULT_ALREADY_INITIALIZED
;
91 imv_attestation
= imv_agent_create(imv_name
, IMV_VENDOR_ID
, IMV_SUBTYPE
,
92 imv_id
, actual_version
);
93 if (!imv_attestation
|| !pts_meas_probe_algorithms(&supported_algorithms
))
95 return TNC_RESULT_FATAL
;
97 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
99 DBG1(DBG_IMV
, "no common IF-IMV version");
100 return TNC_RESULT_NO_COMMON_VERSION
;
104 * Specify supported PTS measurement algorithms
106 * sha1 : PTS_MEAS_ALGO_SHA1
107 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
108 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
110 * we expect the PTS-IMC to select the strongest supported algorithm
112 hash_alg
= lib
->settings
->get_str(lib
->settings
,
113 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
114 if (!strcaseeq(hash_alg
, "sha384") && !strcaseeq(hash_alg
, "sha2_384"))
116 /* remove SHA384 algorithm */
117 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA384
;
119 if (strcaseeq(hash_alg
, "sha1"))
121 /* remove SHA256 algorithm */
122 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA256
;
125 /* attach file measurement database */
126 uri
= lib
->settings
->get_str(lib
->settings
,
127 "libimcv.plugins.imv-attestation.database", NULL
);
128 pts_db
= pts_database_create(uri
);
130 return TNC_RESULT_SUCCESS
;
134 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
136 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
137 TNC_ConnectionID connection_id
,
138 TNC_ConnectionState new_state
)
141 imv_attestation_state_t
*attestation_state
;
144 if (!imv_attestation
)
146 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
147 return TNC_RESULT_NOT_INITIALIZED
;
151 case TNC_CONNECTION_STATE_CREATE
:
152 state
= imv_attestation_state_create(connection_id
);
153 return imv_attestation
->create_state(imv_attestation
, state
);
154 case TNC_CONNECTION_STATE_DELETE
:
155 return imv_attestation
->delete_state(imv_attestation
, connection_id
);
156 case TNC_CONNECTION_STATE_HANDSHAKE
:
157 result
= imv_attestation
->change_state(imv_attestation
, connection_id
,
159 if (result
!= TNC_RESULT_SUCCESS
)
163 attestation_state
= (imv_attestation_state_t
*)state
;
165 /* TODO: Get some configurations */
167 return TNC_RESULT_SUCCESS
;
169 return imv_attestation
->change_state(imv_attestation
, connection_id
,
174 static TNC_Result
send_message(TNC_ConnectionID connection_id
)
180 imv_attestation_state_t
*attestation_state
;
181 imv_attestation_handshake_state_t handshake_state
;
184 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
186 return TNC_RESULT_FATAL
;
188 attestation_state
= (imv_attestation_state_t
*)state
;
189 handshake_state
= attestation_state
->get_handshake_state(attestation_state
);
190 pts
= attestation_state
->get_pts(attestation_state
);
192 msg
= pa_tnc_msg_create();
195 /* Switch on the attribute type IMV has received */
196 switch (handshake_state
)
198 case IMV_ATTESTATION_STATE_INIT
:
200 pts_proto_caps_flag_t flags
;
202 /* Send Request Protocol Capabilities attribute */
203 flags
= pts
->get_proto_caps(pts
);
204 attr
= tcg_pts_attr_proto_caps_create(flags
, TRUE
);
205 attr
->set_noskip_flag(attr
, TRUE
);
206 msg
->add_attribute(msg
, attr
);
208 /* Send Measurement Algorithms attribute */
209 attr
= tcg_pts_attr_meas_algo_create(supported_algorithms
, FALSE
);
210 attr
->set_noskip_flag(attr
, TRUE
);
211 msg
->add_attribute(msg
, attr
);
215 case IMV_ATTESTATION_STATE_MEAS
:
217 enumerator_t
*enumerator
;
218 u_int32_t delimiter
= SOLIDUS_UTF
;
219 char *platform_info
, *pathname
;
223 /* Does the PTS-IMC have TPM support? */
224 if (pts
->get_proto_caps(pts
) & PTS_PROTO_CAPS_T
)
226 /* Send Get TPM Version attribute */
227 attr
= tcg_pts_attr_get_tpm_version_info_create();
228 attr
->set_noskip_flag(attr
, TRUE
);
229 msg
->add_attribute(msg
, attr
);
231 /* Send Get AIK attribute */
232 attr
= tcg_pts_attr_get_aik_create();
233 attr
->set_noskip_flag(attr
, TRUE
);
234 msg
->add_attribute(msg
, attr
);
237 /* Get Platform and OS of the PTS-IMC */
238 platform_info
= pts
->get_platform_info(pts
);
240 if (!pts_db
|| !platform_info
)
242 DBG1(DBG_IMV
, "%s%s%s not available",
243 (pts_db
) ?
"" : "pts database",
244 (!pts_db
&& !platform_info
) ?
"and" : "",
245 (platform_info
) ?
"" : "platform info");
248 DBG1(DBG_IMV
, "platform is '%s'", platform_info
);
250 /* Send Request File Measurement attribute */
251 enumerator
= pts_db
->create_file_enumerator(pts_db
, platform_info
);
257 requested_files
= linked_list_create();
258 while (enumerator
->enumerate(enumerator
, &id
, &type
, &pathname
))
260 is_directory
= (type
!= 0);
261 DBG2(DBG_IMV
, "measurement request %d for %s '%s'",
262 id
, is_directory ?
"directory" : "file", pathname
);
263 attr
= tcg_pts_attr_req_file_meas_create(is_directory
, id
,
264 delimiter
, pathname
);
265 attr
->set_noskip_flag(attr
, TRUE
);
266 msg
->add_attribute(msg
, attr
);
267 requested_files
->insert_last(requested_files
, (void*)id
);
269 enumerator
->destroy(enumerator
);
272 case IMV_ATTESTATION_STATE_COMP_EVID
:
273 case IMV_ATTESTATION_STATE_IML
:
274 DBG1(DBG_IMV
, "Attestation IMV has nothing to send: \"%s\"",
276 return TNC_RESULT_FATAL
;
278 DBG1(DBG_IMV
, "Attestation IMV is in unknown state: \"%s\"",
280 return TNC_RESULT_FATAL
;
284 result
= imv_attestation
->send_message(imv_attestation
, connection_id
,
285 msg
->get_encoding(msg
));
292 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
294 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
295 TNC_ConnectionID connection_id
,
296 TNC_BufferReference msg
,
298 TNC_MessageType msg_type
)
300 pa_tnc_msg_t
*pa_tnc_msg
;
303 imv_attestation_state_t
*attestation_state
;
305 enumerator_t
*enumerator
;
307 bool fatal_error
= FALSE
;
308 bool measurement_error
= FALSE
;
310 if (!imv_attestation
)
312 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
313 return TNC_RESULT_NOT_INITIALIZED
;
316 /* get current IMV state */
317 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
319 return TNC_RESULT_FATAL
;
321 attestation_state
= (imv_attestation_state_t
*)state
;
322 pts
= attestation_state
->get_pts(attestation_state
);
324 /* parse received PA-TNC message and automatically handle any errors */
325 result
= imv_attestation
->receive_message(imv_attestation
, connection_id
,
326 chunk_create(msg
, msg_len
), msg_type
,
329 /* no parsed PA-TNC attributes available if an error occurred */
335 /* analyze PA-TNC attributes */
336 enumerator
= pa_tnc_msg
->create_attribute_enumerator(pa_tnc_msg
);
337 while (enumerator
->enumerate(enumerator
, &attr
))
339 if (attr
->get_vendor_id(attr
) == PEN_IETF
&&
340 attr
->get_type(attr
) == IETF_ATTR_PA_TNC_ERROR
)
342 ietf_attr_pa_tnc_error_t
*error_attr
;
343 pen_t error_vendor_id
;
344 pa_tnc_error_code_t error_code
;
345 chunk_t msg_info
, attr_info
;
348 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
349 error_vendor_id
= error_attr
->get_vendor_id(error_attr
);
350 error_code
= error_attr
->get_error_code(error_attr
);
351 msg_info
= error_attr
->get_msg_info(error_attr
);
353 if (error_vendor_id
== PEN_IETF
)
355 DBG1(DBG_IMV
, "received PA-TNC error '%N' concerning message %#B",
356 pa_tnc_error_code_names
, error_code
, &msg_info
);
360 case PA_ERROR_INVALID_PARAMETER
:
361 offset
= error_attr
->get_offset(error_attr
);
362 DBG1(DBG_IMV
, " occurred at offset of %u bytes", offset
);
364 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED
:
365 attr_info
= error_attr
->get_attr_info(error_attr
);
366 DBG1(DBG_IMV
, " unsupported attribute %#B", &attr_info
);
372 else if (error_vendor_id
== PEN_TCG
)
374 DBG1(DBG_IMV
, "received TCG-PTS error '%N'",
375 pts_error_code_names
, error_code
);
376 DBG1(DBG_IMV
, "error information: %B", &msg_info
);
380 else if (attr
->get_vendor_id(attr
) == PEN_TCG
)
382 switch (attr
->get_type(attr
))
384 case TCG_PTS_PROTO_CAPS
:
386 tcg_pts_attr_proto_caps_t
*attr_cast
;
387 pts_proto_caps_flag_t flags
;
389 attr_cast
= (tcg_pts_attr_proto_caps_t
*)attr
;
390 flags
= attr_cast
->get_flags(attr_cast
);
391 pts
->set_proto_caps(pts
, flags
);
393 attestation_state
->set_handshake_state(attestation_state
,
394 IMV_ATTESTATION_STATE_MEAS
);
397 case TCG_PTS_MEAS_ALGO_SELECTION
:
399 tcg_pts_attr_meas_algo_t
*attr_cast
;
400 pts_meas_algorithms_t selected_algorithm
;
402 attr_cast
= (tcg_pts_attr_meas_algo_t
*)attr
;
403 selected_algorithm
= attr_cast
->get_algorithms(attr_cast
);
404 pts
->set_meas_algorithm(pts
, selected_algorithm
);
406 attestation_state
->set_handshake_state(attestation_state
,
407 IMV_ATTESTATION_STATE_MEAS
);
410 case TCG_PTS_TPM_VERSION_INFO
:
412 tcg_pts_attr_tpm_version_info_t
*attr_cast
;
413 chunk_t tpm_version_info
;
415 attr_cast
= (tcg_pts_attr_tpm_version_info_t
*)attr
;
416 tpm_version_info
= attr_cast
->get_tpm_version_info(attr_cast
);
417 pts
->set_tpm_version_info(pts
, tpm_version_info
);
419 attestation_state
->set_handshake_state(attestation_state
,
420 IMV_ATTESTATION_STATE_END
);
425 tcg_pts_attr_aik_t
*attr_cast
;
428 attr_cast
= (tcg_pts_attr_aik_t
*)attr
;
429 aik
= attr_cast
->get_aik(attr_cast
);
432 /* TODO generate error attribute */
435 pts
->set_aik(pts
, aik
);
436 attestation_state
->set_handshake_state(attestation_state
,
437 IMV_ATTESTATION_STATE_END
);
441 /* PTS-based Attestation Evidence */
442 case TCG_PTS_SIMPLE_COMP_EVID
:
444 case TCG_PTS_SIMPLE_EVID_FINAL
:
446 case TCG_PTS_FILE_MEAS
:
448 tcg_pts_attr_file_meas_t
*attr_cast
;
449 u_int16_t request_id
;
451 pts_meas_algorithms_t algo
;
452 pts_file_meas_t
*measurements
;
454 char *platform_info
, *filename
;
455 enumerator_t
*e_meas
;
457 linked_list_t
*files_in_dir_with_meas
;
459 files_in_dir_with_meas
= linked_list_create();
460 platform_info
= pts
->get_platform_info(pts
);
461 if (!pts_db
|| !platform_info
)
466 attr_cast
= (tcg_pts_attr_file_meas_t
*)attr
;
467 measurements
= attr_cast
->get_measurements(attr_cast
);
468 algo
= pts
->get_meas_algorithm(pts
);
469 request_id
= measurements
->get_request_id(measurements
);
470 file_count
= measurements
->get_file_count(measurements
);
472 DBG1(DBG_IMV
, "measurement request %d returned %d file%s:",
473 request_id
, file_count
, (file_count
== 1) ?
"":"s");
475 if (!pts_db
->is_directory(pts_db
, request_id
, &is_directory
))
477 DBG1(DBG_IMV
, "file entry with request id:%d not found", request_id
);
483 requested_files
->remove(requested_files
, (void*)request_id
, NULL
);
490 e
= pts_db
->create_files_in_dir_enumerator(pts_db
, request_id
);
491 while (e
->enumerate(e
, &file
))
493 files_in_dir_with_meas
->insert_last(files_in_dir_with_meas
, file
);
494 DBG3(DBG_IMV
, "expecting measurement for: %s with request_id: %d", file
, request_id
);
498 e_meas
= measurements
->create_enumerator(measurements
);
499 while (e_meas
->enumerate(e_meas
, &filename
, &measurement
))
502 chunk_t db_measurement
;
504 e
= (is_directory
) ? pts_db
->create_dir_meas_enumerator(pts_db
,
505 platform_info
, request_id
, filename
, algo
) :
506 pts_db
->create_file_meas_enumerator(pts_db
,
507 platform_info
, request_id
, algo
);
510 DBG1(DBG_IMV
, " database enumerator failed");
513 if (!e
->enumerate(e
, &db_measurement
))
515 DBG1(DBG_IMV
, " measurement for '%s' not found"
516 " in database", filename
);
520 if (chunk_equals(db_measurement
, measurement
))
522 DBG2(DBG_IMV
, " %#B for '%s' is ok",
523 &measurement
, filename
);
527 DBG1(DBG_IMV
, " %#B for '%s' does not match %#B",
528 &measurement
, filename
, &db_measurement
);
529 measurement_error
= TRUE
;
534 files_in_dir_with_meas
->remove(files_in_dir_with_meas
,
535 filename
, (bool (*)(void*,void*))strcmp
);
540 if(is_directory
&& !files_in_dir_with_meas
->get_count(files_in_dir_with_meas
))
542 requested_files
->remove(requested_files
, (void*)request_id
, NULL
);
545 e_meas
->destroy(e_meas
);
546 attestation_state
->set_handshake_state(attestation_state
,
547 IMV_ATTESTATION_STATE_END
);
551 /* TODO: Not implemented yet */
552 case TCG_PTS_DH_NONCE_PARAMS_RESP
:
553 case TCG_PTS_UNIX_FILE_META
:
554 case TCG_PTS_INTEG_MEAS_LOG
:
555 /* Attributes using XML */
556 case TCG_PTS_TEMPL_REF_MANI_SET_META
:
557 case TCG_PTS_VERIFICATION_RESULT
:
558 case TCG_PTS_INTEG_REPORT
:
560 case TCG_PTS_WIN_FILE_META
:
561 case TCG_PTS_REGISTRY_VALUE
:
562 /* Received on IMC side only*/
563 case TCG_PTS_REQ_PROTO_CAPS
:
564 case TCG_PTS_DH_NONCE_PARAMS_REQ
:
565 case TCG_PTS_DH_NONCE_FINISH
:
566 case TCG_PTS_MEAS_ALGO
:
567 case TCG_PTS_GET_TPM_VERSION_INFO
:
568 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META
:
569 case TCG_PTS_UPDATE_TEMPL_REF_MANI
:
570 case TCG_PTS_GET_AIK
:
571 case TCG_PTS_REQ_FUNCT_COMP_EVID
:
572 case TCG_PTS_GEN_ATTEST_EVID
:
573 case TCG_PTS_REQ_FILE_META
:
574 case TCG_PTS_REQ_FILE_MEAS
:
575 case TCG_PTS_REQ_INTEG_MEAS_LOG
:
577 DBG1(DBG_IMV
, "received unsupported attribute '%N'",
578 tcg_attr_names
, attr
->get_type(attr
));
583 enumerator
->destroy(enumerator
);
584 pa_tnc_msg
->destroy(pa_tnc_msg
);
590 state
->set_recommendation(state
,
591 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
592 TNC_IMV_EVALUATION_RESULT_ERROR
);
593 return imv_attestation
->provide_recommendation(imv_attestation
,
597 if (attestation_state
->get_handshake_state(attestation_state
) &
598 IMV_ATTESTATION_STATE_END
)
600 if (measurement_error
|| requested_files
->get_count(requested_files
))
605 e
= requested_files
->create_enumerator(requested_files
);
606 while (e
->enumerate(e
, &request
))
608 DBG1(DBG_IMV
, "measurement/s not received for requests: %d", request
);
612 state
->set_recommendation(state
,
613 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
614 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR
);
618 state
->set_recommendation(state
,
619 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
620 TNC_IMV_EVALUATION_RESULT_COMPLIANT
);
622 return imv_attestation
->provide_recommendation(imv_attestation
,
626 return send_message(connection_id
);
630 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
632 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
633 TNC_ConnectionID connection_id
)
635 if (!imv_attestation
)
637 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
638 return TNC_RESULT_NOT_INITIALIZED
;
640 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
644 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
646 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
647 TNC_ConnectionID connection_id
)
650 imv_attestation_state_t
*attestation_state
;
652 if (!imv_attestation
)
654 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
655 return TNC_RESULT_NOT_INITIALIZED
;
657 /* get current IMV state */
658 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
660 return TNC_RESULT_FATAL
;
662 attestation_state
= (imv_attestation_state_t
*)state
;
664 /* Check if IMV has to initiate the PA-TNC exchange */
665 if (attestation_state
->get_handshake_state(attestation_state
) ==
666 IMV_ATTESTATION_STATE_INIT
)
668 return send_message(connection_id
);
670 return TNC_RESULT_SUCCESS
;
674 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
676 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
678 if (!imv_attestation
)
680 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
681 return TNC_RESULT_NOT_INITIALIZED
;
684 imv_attestation
->destroy(imv_attestation
);
685 imv_attestation
= NULL
;
687 return TNC_RESULT_SUCCESS
;
691 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
693 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
694 TNC_TNCS_BindFunctionPointer bind_function
)
696 if (!imv_attestation
)
698 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
699 return TNC_RESULT_NOT_INITIALIZED
;
701 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);