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 <tcg/tcg_attr.h>
24 #include <tcg/tcg_pts_attr_proto_caps.h>
25 #include <tcg/tcg_pts_attr_meas_algo.h>
26 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
27 #include <tcg/tcg_pts_attr_tpm_version_info.h>
28 #include <tcg/tcg_pts_attr_get_aik.h>
29 #include <tcg/tcg_pts_attr_aik.h>
30 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
31 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
32 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
33 #include <tcg/tcg_pts_attr_simple_evid_final.h>
34 #include <tcg/tcg_pts_attr_req_file_meas.h>
35 #include <tcg/tcg_pts_attr_file_meas.h>
37 #include <tncif_pa_subtypes.h>
41 #include <utils/linked_list.h>
43 #include <trousers/tss.h>
44 #include <trousers/trousers.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 * List of files and directories to measure
69 static linked_list_t
*file_list
, *directory_list
;
72 * Monotonic increasing number for Request File Measurement attribute
74 static u_int16_t request_id_counter
= 0;
76 /* TODO: Move the struct to some header file? Duplicate with imc_attestation*/
78 * Struct to hold file or directory name with the request ID for Request File Measurement attribute
80 typedef struct measurement_req_entry_t measurement_req_entry_t
;
82 struct measurement_req_entry_t
{
88 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
90 TNC_Result
TNC_IMV_Initialize(TNC_IMVID imv_id
,
91 TNC_Version min_version
,
92 TNC_Version max_version
,
93 TNC_Version
*actual_version
)
99 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
100 return TNC_RESULT_ALREADY_INITIALIZED
;
102 imv_attestation
= imv_agent_create(imv_name
, IMV_VENDOR_ID
, IMV_SUBTYPE
,
103 imv_id
, actual_version
);
104 if (!imv_attestation
|| !pts_meas_probe_algorithms(&supported_algorithms
))
106 return TNC_RESULT_FATAL
;
108 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
110 DBG1(DBG_IMV
, "no common IF-IMV version");
111 return TNC_RESULT_NO_COMMON_VERSION
;
115 * Specify supported PTS measurement algorithms
117 * sha1 : PTS_MEAS_ALGO_SHA1
118 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
119 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
121 * we expect the PTS-IMC to select the strongest supported algorithm
123 hash_alg
= lib
->settings
->get_str(lib
->settings
,
124 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
125 if (!strcaseeq(hash_alg
, "sha384") && !strcaseeq(hash_alg
, "sha2_384"))
127 /* remove SHA384 algorithm */
128 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA384
;
130 if (strcaseeq(hash_alg
, "sha1"))
132 /* remove SHA256 algorithm */
133 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA256
;
136 return TNC_RESULT_SUCCESS
;
140 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
142 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
143 TNC_ConnectionID connection_id
,
144 TNC_ConnectionState new_state
)
147 imv_attestation_state_t
*attestation_state
;
148 enumerator_t
*enumerator
;
151 measurement_req_entry_t
*entry
;
155 if (!imv_attestation
)
157 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
158 return TNC_RESULT_NOT_INITIALIZED
;
162 case TNC_CONNECTION_STATE_CREATE
:
163 state
= imv_attestation_state_create(connection_id
);
164 return imv_attestation
->create_state(imv_attestation
, state
);
165 case TNC_CONNECTION_STATE_DELETE
:
166 return imv_attestation
->delete_state(imv_attestation
, connection_id
);
167 case TNC_CONNECTION_STATE_HANDSHAKE
:
168 result
= imv_attestation
->change_state(imv_attestation
, connection_id
,
170 if (result
!= TNC_RESULT_SUCCESS
)
174 attestation_state
= (imv_attestation_state_t
*)state
;
177 * Get the files to measure for
178 * PTS Request File Measurement attribute
181 file_list
= linked_list_create();
182 directory_list
= linked_list_create();
184 files
= lib
->settings
->get_str(lib
->settings
,
185 "libimcv.plugins.imc-attestation.files", "none");
186 enumerator
= enumerator_create_token(files
, " ", " ");
187 while (enumerator
->enumerate(enumerator
, &token
))
189 entry
= malloc_thing(measurement_req_entry_t
);
190 token
= strdup(token
);
192 entry
->request_id
= request_id_counter
;
193 file_list
->insert_last(file_list
, entry
);
195 request_id_counter
++;
199 * Get the directories to measure for
200 * PTS Request File Measurement attribute
203 directories
= lib
->settings
->get_str(lib
->settings
,
204 "libimcv.plugins.imc-attestation.directories", "none");
205 enumerator
= enumerator_create_token(directories
, " ", " ");
206 while (enumerator
->enumerate(enumerator
, &token
))
208 entry
= malloc_thing(measurement_req_entry_t
);
209 token
= strdup(token
);
211 entry
->request_id
= request_id_counter
;
212 directory_list
->insert_last(directory_list
, entry
);
214 request_id_counter
++;
216 enumerator
->destroy(enumerator
);
217 return TNC_RESULT_SUCCESS
;
219 return imv_attestation
->change_state(imv_attestation
, connection_id
,
224 static TNC_Result
send_message(TNC_ConnectionID connection_id
)
230 imv_attestation_state_t
*attestation_state
;
231 imv_attestation_handshake_state_t handshake_state
;
233 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
235 return TNC_RESULT_FATAL
;
238 attestation_state
= (imv_attestation_state_t
*)state
;
239 handshake_state
= attestation_state
->get_handshake_state(attestation_state
);
241 /* Switch on the attribute type IMV has received */
242 switch (handshake_state
)
244 case IMV_ATTESTATION_STATE_INIT
:
246 pts_proto_caps_flag_t flags
;
248 /* Send Request Protocol Capabilities attribute */
249 flags
= PTS_PROTO_CAPS_T
| PTS_PROTO_CAPS_V
| PTS_PROTO_CAPS_C
;
250 attr
= tcg_pts_attr_proto_caps_create(flags
, TRUE
);
253 case IMV_ATTESTATION_STATE_PROTO_CAP
:
255 /* Send Measurement Algorithms attribute */
256 attr
= tcg_pts_attr_meas_algo_create(supported_algorithms
, FALSE
);
259 case IMV_ATTESTATION_STATE_MEAS_ALGO
:
261 /* Send Get TPM Version Information attribute */
262 attr
= tcg_pts_attr_get_tpm_version_info_create();
265 case IMV_ATTESTATION_STATE_TPM_INFO
:
267 /* Send Get AIK attribute */
268 /* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
269 //attr = tcg_pts_attr_get_aik_create();
272 case IMV_ATTESTATION_STATE_AIK
:
274 /* Send Request File Measurement attribute */
275 enumerator_t
*enumerator
;
276 measurement_req_entry_t
*entry
;
278 u_int16_t request_id
;
279 u_int32_t delimiter
= SOLIDUS_UTF
;
281 msg
= pa_tnc_msg_create();
284 * Add files to measure to PTS Request File Measurement attribute
286 enumerator
= enumerator_create_single(file_list
, NULL
);
287 while (enumerator
->enumerate(enumerator
, &entry
))
289 attr
= tcg_pts_attr_req_file_meas_create(false,
290 entry
->request_id
, delimiter
,
291 chunk_create(entry
->path
, strlen(entry
->path
)));
292 attr
->set_noskip_flag(attr
, TRUE
);
293 msg
->add_attribute(msg
, attr
);
295 /** Add directories to measure to PTS Request File Measurement attribute
297 enumerator
= enumerator_create_single(directory_list
, NULL
);
298 while (enumerator
->enumerate(enumerator
, &entry
))
300 attr
= tcg_pts_attr_req_file_meas_create(true,
301 entry
->request_id
, delimiter
,
302 chunk_create(entry
->path
, strlen(entry
->path
)));
303 attr
->set_noskip_flag(attr
, TRUE
);
304 msg
->add_attribute(msg
, attr
);
306 enumerator
->destroy(enumerator
);
309 case IMV_ATTESTATION_STATE_SIMPLE_COMP_EVID
:
310 case IMV_ATTESTATION_STATE_SIMPLE_EVID_FINAL
:
311 case IMV_ATTESTATION_STATE_FILE_METADATA
:
312 case IMV_ATTESTATION_STATE_FILE_MEAS
:
313 case IMV_ATTESTATION_STATE_IML
:
314 DBG1(DBG_IMV
, "Attestation IMV has nothing to send: \"%s\"", handshake_state
);
315 return TNC_RESULT_FATAL
;
317 DBG1(DBG_IMV
, "Attestation IMV is in unknown state: \"%s\"", handshake_state
);
318 return TNC_RESULT_FATAL
;
321 msg
= pa_tnc_msg_create();
322 attr
->set_noskip_flag(attr
, TRUE
);
323 msg
->add_attribute(msg
, attr
);
327 result
= imv_attestation
->send_message(imv_attestation
, connection_id
,
328 msg
->get_encoding(msg
));
335 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
337 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
338 TNC_ConnectionID connection_id
,
339 TNC_BufferReference msg
,
341 TNC_MessageType msg_type
)
343 pa_tnc_msg_t
*pa_tnc_msg
;
346 imv_attestation_state_t
*attestation_state
;
347 enumerator_t
*enumerator
;
349 bool fatal_error
= FALSE
;
351 if (!imv_attestation
)
353 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
354 return TNC_RESULT_NOT_INITIALIZED
;
357 /* get current IMV state */
358 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
360 return TNC_RESULT_FATAL
;
363 /* parse received PA-TNC message and automatically handle any errors */
364 result
= imv_attestation
->receive_message(imv_attestation
, connection_id
,
365 chunk_create(msg
, msg_len
), msg_type
,
368 /* no parsed PA-TNC attributes available if an error occurred */
374 /* analyze PA-TNC attributes */
375 enumerator
= pa_tnc_msg
->create_attribute_enumerator(pa_tnc_msg
);
376 while (enumerator
->enumerate(enumerator
, &attr
))
378 if (attr
->get_vendor_id(attr
) == PEN_IETF
&&
379 attr
->get_type(attr
) == IETF_ATTR_PA_TNC_ERROR
)
381 ietf_attr_pa_tnc_error_t
*error_attr
;
382 pa_tnc_error_code_t error_code
;
383 chunk_t msg_info
, attr_info
;
386 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
387 error_code
= error_attr
->get_error_code(error_attr
);
388 msg_info
= error_attr
->get_msg_info(error_attr
);
390 DBG1(DBG_IMV
, "received PA-TNC error '%N' concerning message %#B",
391 pa_tnc_error_code_names
, error_code
, &msg_info
);
394 case PA_ERROR_INVALID_PARAMETER
:
395 offset
= error_attr
->get_offset(error_attr
);
396 DBG1(DBG_IMV
, " occurred at offset of %u bytes", offset
);
398 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED
:
399 attr_info
= error_attr
->get_attr_info(error_attr
);
400 DBG1(DBG_IMV
, " unsupported attribute %#B", &attr_info
);
407 else if (attr
->get_vendor_id(attr
) == PEN_TCG
)
410 * Handle TCG PTS attributes
413 /* get current IMC state */
414 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
416 return TNC_RESULT_FATAL
;
418 attestation_state
= (imv_attestation_state_t
*)state
;
420 switch(attr
->get_type(attr
))
422 case TCG_PTS_PROTO_CAPS
:
424 tcg_pts_attr_proto_caps_t
*attr_proto_caps
;
425 pts_proto_caps_flag_t proto_caps
;
427 attr_proto_caps
= (tcg_pts_attr_proto_caps_t
*)attr
;
428 proto_caps
= attr_proto_caps
->get_flags(attr_proto_caps
);
429 /* TODO: What to do with the protocol capabilities from imc */
430 attestation_state
->set_handshake_state(attestation_state
,
431 IMV_ATTESTATION_STATE_PROTO_CAP
);
434 case TCG_PTS_MEAS_ALGO_SELECTION
:
436 tcg_pts_attr_meas_algo_t
*attr_meas
;
437 pts_meas_algorithms_t selected_algorithm
;
438 hash_algorithm_t hash_alg
;
440 attr_meas
= (tcg_pts_attr_meas_algo_t
*)attr
;
441 selected_algorithm
= attr_meas
->get_algorithms(attr_meas
);
442 hash_alg
= pts_meas_to_hash_algorithm(selected_algorithm
);
443 if (hash_alg
== HASH_UNKNOWN
)
445 /* TODO generate an error message */
448 DBG2(DBG_IMV
, "selected PTS measurement algorithm is %N",
449 hash_algorithm_names
, hash_alg
);
450 /* TODO: What to do with the selected algorithm from imc */
452 attestation_state
->set_handshake_state(attestation_state
,
453 IMV_ATTESTATION_STATE_MEAS_ALGO
);
456 case TCG_PTS_TPM_VERSION_INFO
:
458 tcg_pts_attr_tpm_version_info_t
*attr_tpm
;
459 chunk_t tpm_version_info
;
461 TPM_CAP_VERSION_INFO versionInfo
;
464 attr_tpm
= (tcg_pts_attr_tpm_version_info_t
*)attr
;
465 tpm_version_info
= attr_tpm
->get_tpm_version_info(attr_tpm
);
467 result
= Trspi_UnloadBlob_CAP_VERSION_INFO(&offset
,
468 tpm_version_info
.ptr
, &versionInfo
);
469 if (result
!= TSS_SUCCESS
)
471 DBG1(DBG_IMV
, "TSS Error 0x%x", result
);
472 return TNC_RESULT_FATAL
;
474 DBG2(DBG_IMV
, "TPM 1.2 Version Info: "
475 "Chip Version: %hhu.%hhu.%hhu.%hhu, "
476 "Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
477 versionInfo
.version
.major
, versionInfo
.version
.minor
,
478 versionInfo
.version
.revMajor
, versionInfo
.version
.revMinor
,
479 versionInfo
.specLevel
, versionInfo
.errataRev
,
480 versionInfo
.tpmVendorID
);
482 attestation_state
->set_handshake_state(attestation_state
,
483 IMV_ATTESTATION_STATE_TPM_INFO
);
488 /* TODO: Save the AIK key and certificate */
489 attestation_state
->set_handshake_state(attestation_state
,
490 IMV_ATTESTATION_STATE_AIK
);
494 /* PTS-based Attestation Evidence */
495 case TCG_PTS_SIMPLE_COMP_EVID
:
497 case TCG_PTS_SIMPLE_EVID_FINAL
:
499 case TCG_PTS_FILE_MEAS
:
501 tcg_pts_attr_file_meas_t
*attr_file_meas
;
502 u_int64_t num_of_files
;
503 u_int16_t request_id
;
506 attr_file_meas
= (tcg_pts_attr_file_meas_t
*)attr
;
507 num_of_files
= attr_file_meas
->get_number_of_files(attr_file_meas
);
508 request_id
= attr_file_meas
->get_request_id(attr_file_meas
);
509 meas_len
= attr_file_meas
->get_meas_len(attr_file_meas
);
511 /* TODO: Start working here */
513 attestation_state
->set_handshake_state(attestation_state
,
514 IMV_ATTESTATION_STATE_FILE_MEAS
);
518 /* TODO: Not implemented yet */
519 case TCG_PTS_DH_NONCE_PARAMS_RESP
:
520 case TCG_PTS_UNIX_FILE_META
:
521 case TCG_PTS_INTEG_MEAS_LOG
:
522 /* Attributes using XML */
523 case TCG_PTS_TEMPL_REF_MANI_SET_META
:
524 case TCG_PTS_VERIFICATION_RESULT
:
525 case TCG_PTS_INTEG_REPORT
:
527 case TCG_PTS_WIN_FILE_META
:
528 case TCG_PTS_REGISTRY_VALUE
:
529 /* Received on IMC side only*/
530 case TCG_PTS_REQ_PROTO_CAPS
:
531 case TCG_PTS_DH_NONCE_PARAMS_REQ
:
532 case TCG_PTS_DH_NONCE_FINISH
:
533 case TCG_PTS_MEAS_ALGO
:
534 case TCG_PTS_GET_TPM_VERSION_INFO
:
535 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META
:
536 case TCG_PTS_UPDATE_TEMPL_REF_MANI
:
537 case TCG_PTS_GET_AIK
:
538 case TCG_PTS_REQ_FUNCT_COMP_EVID
:
539 case TCG_PTS_GEN_ATTEST_EVID
:
540 case TCG_PTS_REQ_FILE_META
:
541 case TCG_PTS_REQ_FILE_MEAS
:
542 case TCG_PTS_REQ_INTEG_MEAS_LOG
:
544 DBG1(DBG_IMV
, "received unsupported attribute '%N'",
545 tcg_attr_names
, attr
->get_type(attr
));
550 enumerator
->destroy(enumerator
);
551 pa_tnc_msg
->destroy(pa_tnc_msg
);
555 state
->set_recommendation(state
,
556 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
557 TNC_IMV_EVALUATION_RESULT_ERROR
);
558 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
560 return send_message(connection_id
);
564 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
566 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
567 TNC_ConnectionID connection_id
)
569 if (!imv_attestation
)
571 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
572 return TNC_RESULT_NOT_INITIALIZED
;
574 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
578 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
580 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
581 TNC_ConnectionID connection_id
)
584 imv_attestation_state_t
*attestation_state
;
586 if (!imv_attestation
)
588 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
589 return TNC_RESULT_NOT_INITIALIZED
;
591 /* get current IMV state */
592 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
594 return TNC_RESULT_FATAL
;
596 attestation_state
= (imv_attestation_state_t
*)state
;
598 /* Check if IMV has to initiate the PA-TNC exchange */
599 if (attestation_state
->get_handshake_state(attestation_state
) ==
600 IMV_ATTESTATION_STATE_INIT
)
602 return send_message(connection_id
);
604 return TNC_RESULT_SUCCESS
;
608 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
610 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
612 if (!imv_attestation
)
614 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
615 return TNC_RESULT_NOT_INITIALIZED
;
617 imv_attestation
->destroy(imv_attestation
);
618 imv_attestation
= NULL
;
620 return TNC_RESULT_SUCCESS
;
624 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
626 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
627 TNC_TNCS_BindFunctionPointer bind_function
)
629 if (!imv_attestation
)
631 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
632 return TNC_RESULT_NOT_INITIALIZED
;
634 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);