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>
25 #include <tcg/tcg_attr.h>
26 #include <tcg/tcg_pts_attr_proto_caps.h>
27 #include <tcg/tcg_pts_attr_meas_algo.h>
28 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
29 #include <tcg/tcg_pts_attr_tpm_version_info.h>
30 #include <tcg/tcg_pts_attr_get_aik.h>
31 #include <tcg/tcg_pts_attr_aik.h>
32 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
33 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
34 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
35 #include <tcg/tcg_pts_attr_simple_evid_final.h>
36 #include <tcg/tcg_pts_attr_req_file_meas.h>
37 #include <tcg/tcg_pts_attr_file_meas.h>
39 #include <tncif_pa_subtypes.h>
43 #include <utils/linked_list.h>
47 static const char imv_name
[] = "Attestation";
49 #define IMV_VENDOR_ID PEN_TCG
50 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
53 * UTF-8 encoding of the character used to delimiter the filename
55 #define SOLIDUS_UTF 0x002F
56 #define REVERSE_SOLIDUS_UTF 0x005C
58 static imv_agent_t
*imv_attestation
;
61 * Supported PTS measurement algorithms
63 static pts_meas_algorithms_t supported_algorithms
= 0;
66 * PTS file measurement database
68 static pts_database_t
*pts_db
;
71 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
73 TNC_Result
TNC_IMV_Initialize(TNC_IMVID imv_id
,
74 TNC_Version min_version
,
75 TNC_Version max_version
,
76 TNC_Version
*actual_version
)
82 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
83 return TNC_RESULT_ALREADY_INITIALIZED
;
85 imv_attestation
= imv_agent_create(imv_name
, IMV_VENDOR_ID
, IMV_SUBTYPE
,
86 imv_id
, actual_version
);
87 if (!imv_attestation
|| !pts_meas_probe_algorithms(&supported_algorithms
))
89 return TNC_RESULT_FATAL
;
91 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
93 DBG1(DBG_IMV
, "no common IF-IMV version");
94 return TNC_RESULT_NO_COMMON_VERSION
;
98 * Specify supported PTS measurement algorithms
100 * sha1 : PTS_MEAS_ALGO_SHA1
101 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
102 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
104 * we expect the PTS-IMC to select the strongest supported algorithm
106 hash_alg
= lib
->settings
->get_str(lib
->settings
,
107 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
108 if (!strcaseeq(hash_alg
, "sha384") && !strcaseeq(hash_alg
, "sha2_384"))
110 /* remove SHA384 algorithm */
111 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA384
;
113 if (strcaseeq(hash_alg
, "sha1"))
115 /* remove SHA256 algorithm */
116 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA256
;
119 /* attach file measurement database */
120 uri
= lib
->settings
->get_str(lib
->settings
,
121 "libimcv.plugins.imv-attestation.database", NULL
);
122 pts_db
= pts_database_create(uri
);
124 return TNC_RESULT_SUCCESS
;
128 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
130 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
131 TNC_ConnectionID connection_id
,
132 TNC_ConnectionState new_state
)
135 imv_attestation_state_t
*attestation_state
;
138 if (!imv_attestation
)
140 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
141 return TNC_RESULT_NOT_INITIALIZED
;
145 case TNC_CONNECTION_STATE_CREATE
:
146 state
= imv_attestation_state_create(connection_id
);
147 return imv_attestation
->create_state(imv_attestation
, state
);
148 case TNC_CONNECTION_STATE_DELETE
:
149 return imv_attestation
->delete_state(imv_attestation
, connection_id
);
150 case TNC_CONNECTION_STATE_HANDSHAKE
:
151 result
= imv_attestation
->change_state(imv_attestation
, connection_id
,
153 if (result
!= TNC_RESULT_SUCCESS
)
157 attestation_state
= (imv_attestation_state_t
*)state
;
159 /* TODO: Get some configurations */
161 return TNC_RESULT_SUCCESS
;
163 return imv_attestation
->change_state(imv_attestation
, connection_id
,
168 static TNC_Result
send_message(TNC_ConnectionID connection_id
)
174 imv_attestation_state_t
*attestation_state
;
175 imv_attestation_handshake_state_t handshake_state
;
178 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
180 return TNC_RESULT_FATAL
;
182 attestation_state
= (imv_attestation_state_t
*)state
;
183 handshake_state
= attestation_state
->get_handshake_state(attestation_state
);
184 pts
= attestation_state
->get_pts(attestation_state
);
186 msg
= pa_tnc_msg_create();
189 /* Switch on the attribute type IMV has received */
190 switch (handshake_state
)
192 case IMV_ATTESTATION_STATE_INIT
:
194 pts_proto_caps_flag_t flags
;
196 /* Send Request Protocol Capabilities attribute */
197 flags
= pts
->get_proto_caps(pts
);
198 attr
= tcg_pts_attr_proto_caps_create(flags
, TRUE
);
199 attr
->set_noskip_flag(attr
, TRUE
);
200 msg
->add_attribute(msg
, attr
);
202 /* Send Measurement Algorithms attribute */
203 attr
= tcg_pts_attr_meas_algo_create(supported_algorithms
, FALSE
);
204 attr
->set_noskip_flag(attr
, TRUE
);
205 msg
->add_attribute(msg
, attr
);
209 case IMV_ATTESTATION_STATE_MEAS
:
211 enumerator_t
*enumerator
;
212 u_int32_t delimiter
= SOLIDUS_UTF
;
213 char *platform_info
, *pathname
;
217 /* Does the PTS-IMC have TPM support? */
218 if (pts
->get_proto_caps(pts
) & PTS_PROTO_CAPS_T
)
220 /* Send Get TPM Version attribute */
221 attr
= tcg_pts_attr_get_tpm_version_info_create();
222 attr
->set_noskip_flag(attr
, TRUE
);
223 msg
->add_attribute(msg
, attr
);
225 /* Send Get AIK attribute */
226 attr
= tcg_pts_attr_get_aik_create();
227 attr
->set_noskip_flag(attr
, TRUE
);
228 msg
->add_attribute(msg
, attr
);
231 /* Get Platform and OS of the PTS-IMC */
232 platform_info
= pts
->get_platform_info(pts
);
234 if (!pts_db
|| !platform_info
)
236 DBG1(DBG_IMV
, "%s%s%s not available",
237 (pts_db
) ?
"" : "pts database",
238 (!pts_db
&& !platform_info
) ?
"and" : "",
239 (platform_info
) ?
"" : "platform info");
242 DBG1(DBG_IMV
, "platform is '%s'", platform_info
);
244 /* Send Request File Measurement attribute */
245 enumerator
= pts_db
->create_file_enumerator(pts_db
, platform_info
);
250 while (enumerator
->enumerate(enumerator
, &id
, &type
, &pathname
))
252 is_directory
= (type
!= 0);
253 DBG2(DBG_IMV
, "measurement request %d for %s '%s'",
254 id
, is_directory ?
"directory" : "file", pathname
);
255 attr
= tcg_pts_attr_req_file_meas_create(is_directory
, id
,
256 delimiter
, pathname
);
257 attr
->set_noskip_flag(attr
, TRUE
);
258 msg
->add_attribute(msg
, attr
);
260 enumerator
->destroy(enumerator
);
263 case IMV_ATTESTATION_STATE_COMP_EVID
:
264 case IMV_ATTESTATION_STATE_IML
:
265 DBG1(DBG_IMV
, "Attestation IMV has nothing to send: \"%s\"",
267 return TNC_RESULT_FATAL
;
269 DBG1(DBG_IMV
, "Attestation IMV is in unknown state: \"%s\"",
271 return TNC_RESULT_FATAL
;
275 result
= imv_attestation
->send_message(imv_attestation
, connection_id
,
276 msg
->get_encoding(msg
));
283 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
285 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
286 TNC_ConnectionID connection_id
,
287 TNC_BufferReference msg
,
289 TNC_MessageType msg_type
)
291 pa_tnc_msg_t
*pa_tnc_msg
;
294 imv_attestation_state_t
*attestation_state
;
296 enumerator_t
*enumerator
;
298 bool fatal_error
= FALSE
;
299 bool measurement_error
= FALSE
;
301 if (!imv_attestation
)
303 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
304 return TNC_RESULT_NOT_INITIALIZED
;
307 /* get current IMV state */
308 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
310 return TNC_RESULT_FATAL
;
312 attestation_state
= (imv_attestation_state_t
*)state
;
313 pts
= attestation_state
->get_pts(attestation_state
);
315 /* parse received PA-TNC message and automatically handle any errors */
316 result
= imv_attestation
->receive_message(imv_attestation
, connection_id
,
317 chunk_create(msg
, msg_len
), msg_type
,
320 /* no parsed PA-TNC attributes available if an error occurred */
326 /* analyze PA-TNC attributes */
327 enumerator
= pa_tnc_msg
->create_attribute_enumerator(pa_tnc_msg
);
328 while (enumerator
->enumerate(enumerator
, &attr
))
330 if (attr
->get_vendor_id(attr
) == PEN_IETF
&&
331 attr
->get_type(attr
) == IETF_ATTR_PA_TNC_ERROR
)
333 ietf_attr_pa_tnc_error_t
*error_attr
;
334 pa_tnc_error_code_t error_code
;
335 chunk_t msg_info
, attr_info
;
338 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
339 error_code
= error_attr
->get_error_code(error_attr
);
340 msg_info
= error_attr
->get_msg_info(error_attr
);
342 DBG1(DBG_IMV
, "received PA-TNC error '%N' concerning message %#B",
343 pa_tnc_error_code_names
, error_code
, &msg_info
);
346 case PA_ERROR_INVALID_PARAMETER
:
347 offset
= error_attr
->get_offset(error_attr
);
348 DBG1(DBG_IMV
, " occurred at offset of %u bytes", offset
);
350 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED
:
351 attr_info
= error_attr
->get_attr_info(error_attr
);
352 DBG1(DBG_IMV
, " unsupported attribute %#B", &attr_info
);
359 else if (attr
->get_vendor_id(attr
) == PEN_TCG
)
361 switch (attr
->get_type(attr
))
363 case TCG_PTS_PROTO_CAPS
:
365 tcg_pts_attr_proto_caps_t
*attr_cast
;
366 pts_proto_caps_flag_t flags
;
368 attr_cast
= (tcg_pts_attr_proto_caps_t
*)attr
;
369 flags
= attr_cast
->get_flags(attr_cast
);
370 pts
->set_proto_caps(pts
, flags
);
372 attestation_state
->set_handshake_state(attestation_state
,
373 IMV_ATTESTATION_STATE_MEAS
);
376 case TCG_PTS_MEAS_ALGO_SELECTION
:
378 tcg_pts_attr_meas_algo_t
*attr_cast
;
379 pts_meas_algorithms_t selected_algorithm
;
381 attr_cast
= (tcg_pts_attr_meas_algo_t
*)attr
;
382 selected_algorithm
= attr_cast
->get_algorithms(attr_cast
);
383 pts
->set_meas_algorithm(pts
, selected_algorithm
);
385 attestation_state
->set_handshake_state(attestation_state
,
386 IMV_ATTESTATION_STATE_MEAS
);
389 case TCG_PTS_TPM_VERSION_INFO
:
391 tcg_pts_attr_tpm_version_info_t
*attr_cast
;
392 chunk_t tpm_version_info
;
394 attr_cast
= (tcg_pts_attr_tpm_version_info_t
*)attr
;
395 tpm_version_info
= attr_cast
->get_tpm_version_info(attr_cast
);
396 pts
->set_tpm_version_info(pts
, tpm_version_info
);
398 attestation_state
->set_handshake_state(attestation_state
,
399 IMV_ATTESTATION_STATE_END
);
404 tcg_pts_attr_aik_t
*attr_cast
;
408 attr_cast
= (tcg_pts_attr_aik_t
*)attr
;
409 aik
= attr_cast
->get_aik(attr_cast
);
410 is_naked_key
= attr_cast
->get_naked_flag(attr_cast
);
411 pts
->set_aik(pts
, aik
, is_naked_key
);
413 attestation_state
->set_handshake_state(attestation_state
,
414 IMV_ATTESTATION_STATE_END
);
418 /* PTS-based Attestation Evidence */
419 case TCG_PTS_SIMPLE_COMP_EVID
:
421 case TCG_PTS_SIMPLE_EVID_FINAL
:
423 case TCG_PTS_FILE_MEAS
:
425 tcg_pts_attr_file_meas_t
*attr_cast
;
426 u_int16_t request_id
;
428 pts_meas_algorithms_t algo
;
429 pts_file_meas_t
*measurements
;
431 char *platform_info
, *filename
;
432 enumerator_t
*e_meas
;
434 platform_info
= pts
->get_platform_info(pts
);
435 if (!pts_db
|| !platform_info
)
440 attr_cast
= (tcg_pts_attr_file_meas_t
*)attr
;
441 measurements
= attr_cast
->get_measurements(attr_cast
);
442 algo
= pts
->get_meas_algorithm(pts
);
443 request_id
= measurements
->get_request_id(measurements
);
444 file_count
= measurements
->get_file_count(measurements
);
446 DBG1(DBG_IMV
, "measurement request %d returned %d file%s:",
447 request_id
, file_count
, (file_count
== 1) ?
"":"s");
449 e_meas
= measurements
->create_enumerator(measurements
);
450 while (e_meas
->enumerate(e_meas
, &filename
, &measurement
))
453 chunk_t db_measurement
;
455 e
= pts_db
->create_meas_enumerator(pts_db
,
456 platform_info
, request_id
, algo
);
459 DBG1(DBG_IMV
, " database enumerator failed");
462 if (!e
->enumerate(e
, &db_measurement
))
464 DBG1(DBG_IMV
, " measurement for '%s' not found"
465 " in database", filename
);
469 if (chunk_equals(db_measurement
, measurement
))
471 DBG2(DBG_IMV
, " %#B for '%s' is ok",
472 &measurement
, filename
);
476 DBG1(DBG_IMV
, " %#B for '%s' does not match %#B",
477 &measurement
, filename
, &db_measurement
);
478 measurement_error
= TRUE
;
482 e_meas
->destroy(e_meas
);
483 attestation_state
->set_handshake_state(attestation_state
,
484 IMV_ATTESTATION_STATE_END
);
488 /* TODO: Not implemented yet */
489 case TCG_PTS_DH_NONCE_PARAMS_RESP
:
490 case TCG_PTS_UNIX_FILE_META
:
491 case TCG_PTS_INTEG_MEAS_LOG
:
492 /* Attributes using XML */
493 case TCG_PTS_TEMPL_REF_MANI_SET_META
:
494 case TCG_PTS_VERIFICATION_RESULT
:
495 case TCG_PTS_INTEG_REPORT
:
497 case TCG_PTS_WIN_FILE_META
:
498 case TCG_PTS_REGISTRY_VALUE
:
499 /* Received on IMC side only*/
500 case TCG_PTS_REQ_PROTO_CAPS
:
501 case TCG_PTS_DH_NONCE_PARAMS_REQ
:
502 case TCG_PTS_DH_NONCE_FINISH
:
503 case TCG_PTS_MEAS_ALGO
:
504 case TCG_PTS_GET_TPM_VERSION_INFO
:
505 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META
:
506 case TCG_PTS_UPDATE_TEMPL_REF_MANI
:
507 case TCG_PTS_GET_AIK
:
508 case TCG_PTS_REQ_FUNCT_COMP_EVID
:
509 case TCG_PTS_GEN_ATTEST_EVID
:
510 case TCG_PTS_REQ_FILE_META
:
511 case TCG_PTS_REQ_FILE_MEAS
:
512 case TCG_PTS_REQ_INTEG_MEAS_LOG
:
514 DBG1(DBG_IMV
, "received unsupported attribute '%N'",
515 tcg_attr_names
, attr
->get_type(attr
));
520 enumerator
->destroy(enumerator
);
521 pa_tnc_msg
->destroy(pa_tnc_msg
);
527 state
->set_recommendation(state
,
528 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
529 TNC_IMV_EVALUATION_RESULT_ERROR
);
530 return imv_attestation
->provide_recommendation(imv_attestation
,
534 if (attestation_state
->get_handshake_state(attestation_state
) &
535 IMV_ATTESTATION_STATE_END
)
537 if (measurement_error
)
539 state
->set_recommendation(state
,
540 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
541 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR
);
545 state
->set_recommendation(state
,
546 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
547 TNC_IMV_EVALUATION_RESULT_COMPLIANT
);
549 return imv_attestation
->provide_recommendation(imv_attestation
,
553 return send_message(connection_id
);
557 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
559 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
560 TNC_ConnectionID connection_id
)
562 if (!imv_attestation
)
564 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
565 return TNC_RESULT_NOT_INITIALIZED
;
567 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
571 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
573 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
574 TNC_ConnectionID connection_id
)
577 imv_attestation_state_t
*attestation_state
;
579 if (!imv_attestation
)
581 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
582 return TNC_RESULT_NOT_INITIALIZED
;
584 /* get current IMV state */
585 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
587 return TNC_RESULT_FATAL
;
589 attestation_state
= (imv_attestation_state_t
*)state
;
591 /* Check if IMV has to initiate the PA-TNC exchange */
592 if (attestation_state
->get_handshake_state(attestation_state
) ==
593 IMV_ATTESTATION_STATE_INIT
)
595 return send_message(connection_id
);
597 return TNC_RESULT_SUCCESS
;
601 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
603 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
605 if (!imv_attestation
)
607 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
608 return TNC_RESULT_NOT_INITIALIZED
;
611 imv_attestation
->destroy(imv_attestation
);
612 imv_attestation
= NULL
;
614 return TNC_RESULT_SUCCESS
;
618 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
620 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
621 TNC_TNCS_BindFunctionPointer bind_function
)
623 if (!imv_attestation
)
625 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
626 return TNC_RESULT_NOT_INITIALIZED
;
628 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);