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
;
177 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
179 return TNC_RESULT_FATAL
;
181 attestation_state
= (imv_attestation_state_t
*)state
;
182 handshake_state
= attestation_state
->get_handshake_state(attestation_state
);
183 pts
= attestation_state
->get_pts(attestation_state
);
185 msg
= pa_tnc_msg_create();
188 /* Switch on the attribute type IMV has received */
189 switch (handshake_state
)
191 case IMV_ATTESTATION_STATE_INIT
:
193 pa_tnc_attr_t
*attr_req_proto_cap
, *attr_meas_algo
;
194 pts_proto_caps_flag_t flags
;
196 /* Send Request Protocol Capabilities attribute */
197 flags
= pts
->get_proto_caps(pts
);
198 attr_req_proto_cap
= tcg_pts_attr_proto_caps_create(flags
, TRUE
);
199 attr_req_proto_cap
->set_noskip_flag(attr_req_proto_cap
, TRUE
);
200 msg
->add_attribute(msg
, attr_req_proto_cap
);
202 /* Send Measurement Algorithms attribute */
203 attr_meas_algo
= tcg_pts_attr_meas_algo_create(supported_algorithms
, FALSE
);
204 attr_meas_algo
->set_noskip_flag(attr_meas_algo
, TRUE
);
205 msg
->add_attribute(msg
, attr_meas_algo
);
209 case IMV_ATTESTATION_STATE_MEAS
:
211 pa_tnc_attr_t
*attr_req_file_meas
;
212 enumerator_t
*enumerator
;
213 pts_meas_algorithms_t communicated_caps
;
214 u_int32_t delimiter
= SOLIDUS_UTF
;
216 char *product
, *path
;
218 /* Send Get TPM Version Information attribute */
219 communicated_caps
= pts
->get_proto_caps(pts
);
220 if (communicated_caps
& PTS_PROTO_CAPS_T
)
222 pa_tnc_attr_t
*attr_get_tpm_version
, *attr_get_aik
;
224 attr_get_tpm_version
= tcg_pts_attr_get_tpm_version_info_create();
225 attr_get_tpm_version
->set_noskip_flag(attr_get_tpm_version
, TRUE
);
226 msg
->add_attribute(msg
, attr_get_tpm_version
);
228 /* Send Get AIK attribute */
229 /* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
230 //attr_get_aik = tcg_pts_attr_get_aik_create();
231 //attr_get_aik->set_noskip_flag(attr_get_aik, TRUE);
232 //msg->add_attribute(msg, attr_get_aik);
235 /* Send Request File Measurement attribute */
237 * Add files to measure to PTS Request File Measurement attribute
239 product
= "Ubuntu 10.10 x86_64";
245 enumerator
= pts_db
->create_file_enumerator(pts_db
, product
);
250 while (enumerator
->enumerate(enumerator
, &id
, &type
, &path
))
255 DBG2(DBG_IMV
, "id = %d, type = %d, path = '%s'", id
, type
, path
);
257 is_directory
= (type
!= 0) ?
true : false;
258 path_chunk
= chunk_create(path
, strlen(path
));
259 path_chunk
= chunk_clone(path_chunk
);
261 attr_req_file_meas
= tcg_pts_attr_req_file_meas_create(is_directory
,
262 (u_int16_t
)id
, delimiter
, path_chunk
);
263 attr_req_file_meas
->set_noskip_flag(attr_req_file_meas
, TRUE
);
264 msg
->add_attribute(msg
, attr_req_file_meas
);
266 enumerator
->destroy(enumerator
);
270 case IMV_ATTESTATION_STATE_COMP_EVID
:
271 case IMV_ATTESTATION_STATE_IML
:
272 DBG1(DBG_IMV
, "Attestation IMV has nothing to send: \"%s\"", handshake_state
);
273 return TNC_RESULT_FATAL
;
275 DBG1(DBG_IMV
, "Attestation IMV is in unknown state: \"%s\"", handshake_state
);
276 return TNC_RESULT_FATAL
;
280 result
= imv_attestation
->send_message(imv_attestation
, connection_id
,
281 msg
->get_encoding(msg
));
288 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
290 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
291 TNC_ConnectionID connection_id
,
292 TNC_BufferReference msg
,
294 TNC_MessageType msg_type
)
296 pa_tnc_msg_t
*pa_tnc_msg
;
299 imv_attestation_state_t
*attestation_state
;
301 enumerator_t
*enumerator
;
303 bool fatal_error
= FALSE
;
305 if (!imv_attestation
)
307 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
308 return TNC_RESULT_NOT_INITIALIZED
;
311 /* get current IMV state */
312 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
314 return TNC_RESULT_FATAL
;
316 attestation_state
= (imv_attestation_state_t
*)state
;
317 pts
= attestation_state
->get_pts(attestation_state
);
319 /* parse received PA-TNC message and automatically handle any errors */
320 result
= imv_attestation
->receive_message(imv_attestation
, connection_id
,
321 chunk_create(msg
, msg_len
), msg_type
,
324 /* no parsed PA-TNC attributes available if an error occurred */
330 /* analyze PA-TNC attributes */
331 enumerator
= pa_tnc_msg
->create_attribute_enumerator(pa_tnc_msg
);
332 while (enumerator
->enumerate(enumerator
, &attr
))
334 if (attr
->get_vendor_id(attr
) == PEN_IETF
&&
335 attr
->get_type(attr
) == IETF_ATTR_PA_TNC_ERROR
)
337 ietf_attr_pa_tnc_error_t
*error_attr
;
338 pa_tnc_error_code_t error_code
;
339 chunk_t msg_info
, attr_info
;
342 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
343 error_code
= error_attr
->get_error_code(error_attr
);
344 msg_info
= error_attr
->get_msg_info(error_attr
);
346 DBG1(DBG_IMV
, "received PA-TNC error '%N' concerning message %#B",
347 pa_tnc_error_code_names
, error_code
, &msg_info
);
350 case PA_ERROR_INVALID_PARAMETER
:
351 offset
= error_attr
->get_offset(error_attr
);
352 DBG1(DBG_IMV
, " occurred at offset of %u bytes", offset
);
354 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED
:
355 attr_info
= error_attr
->get_attr_info(error_attr
);
356 DBG1(DBG_IMV
, " unsupported attribute %#B", &attr_info
);
363 else if (attr
->get_vendor_id(attr
) == PEN_TCG
)
365 switch(attr
->get_type(attr
))
367 case TCG_PTS_PROTO_CAPS
:
369 tcg_pts_attr_proto_caps_t
*attr_cast
;
370 pts_proto_caps_flag_t flags
;
372 attr_cast
= (tcg_pts_attr_proto_caps_t
*)attr
;
373 flags
= attr_cast
->get_flags(attr_cast
);
374 pts
->set_proto_caps(pts
, flags
);
376 attestation_state
->set_handshake_state(attestation_state
,
377 IMV_ATTESTATION_STATE_MEAS
);
380 case TCG_PTS_MEAS_ALGO_SELECTION
:
382 tcg_pts_attr_meas_algo_t
*attr_cast
;
383 pts_meas_algorithms_t selected_algorithm
;
385 attr_cast
= (tcg_pts_attr_meas_algo_t
*)attr
;
386 selected_algorithm
= attr_cast
->get_algorithms(attr_cast
);
387 pts
->set_meas_algorithm(pts
, selected_algorithm
);
389 attestation_state
->set_handshake_state(attestation_state
,
390 IMV_ATTESTATION_STATE_MEAS
);
393 case TCG_PTS_TPM_VERSION_INFO
:
395 tcg_pts_attr_tpm_version_info_t
*attr_cast
;
396 chunk_t tpm_version_info
;
398 attr_cast
= (tcg_pts_attr_tpm_version_info_t
*)attr
;
399 tpm_version_info
= attr_cast
->get_tpm_version_info(attr_cast
);
400 pts
->set_tpm_version_info(pts
, tpm_version_info
);
402 attestation_state
->set_handshake_state(attestation_state
,
403 IMV_ATTESTATION_STATE_END
);
408 /* TODO: Save the AIK key and certificate */
409 attestation_state
->set_handshake_state(attestation_state
,
410 IMV_ATTESTATION_STATE_END
);
414 /* PTS-based Attestation Evidence */
415 case TCG_PTS_SIMPLE_COMP_EVID
:
417 case TCG_PTS_SIMPLE_EVID_FINAL
:
419 case TCG_PTS_FILE_MEAS
:
421 tcg_pts_attr_file_meas_t
*attr_cast
;
422 u_int64_t num_of_files
;
423 u_int16_t request_id
;
425 enumerator_t
*meas_enumerator
;
426 file_meas_entry_t
*meas_entry
;
427 bool comparisons_succeeded
= true;
429 attr_cast
= (tcg_pts_attr_file_meas_t
*)attr
;
430 num_of_files
= attr_cast
->get_number_of_files(attr_cast
);
431 request_id
= attr_cast
->get_request_id(attr_cast
);
432 meas_len
= attr_cast
->get_meas_len(attr_cast
);
434 meas_enumerator
= attr_cast
->create_file_meas_enumerator(attr_cast
);
435 while (meas_enumerator
->enumerate(meas_enumerator
, &meas_entry
))
437 enumerator_t
*hash_enumerator
;
438 pts_meas_algorithms_t selected_algorithm
;
439 char *product
= "Ubuntu 10.10 x86_64";
440 chunk_t db_measurement
;
442 DBG3(DBG_IMV
, "Received measurement: %B", &meas_entry
->measurement
);
448 selected_algorithm
= pts
->get_meas_algorithm(pts
);
450 hash_enumerator
= pts_db
->create_meas_enumerator(pts_db
, product
, request_id
, selected_algorithm
);
451 if (!hash_enumerator
)
455 while (hash_enumerator
->enumerate(hash_enumerator
, &db_measurement
))
457 DBG3(DBG_IMV
, "Expected measurement: %B", &db_measurement
);
459 /* Compare the received hash measurement with one saved in db */
460 if(chunk_compare(db_measurement
, meas_entry
->measurement
) == 0)
462 DBG1(DBG_IMV
, "Measurement comparison succeeded for: %s", meas_entry
->file_name
.ptr
);
466 DBG1(DBG_IMV
, "Measurement comparison failed for: %s", meas_entry
->file_name
.ptr
);
467 comparisons_succeeded
= false;
470 hash_enumerator
->destroy(hash_enumerator
);
474 meas_enumerator
->destroy(meas_enumerator
);
475 attestation_state
->set_handshake_state(attestation_state
,
476 IMV_ATTESTATION_STATE_END
);
478 (comparisons_succeeded
) ? state
->set_recommendation(state
,
479 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
480 TNC_IMV_EVALUATION_RESULT_COMPLIANT
) :
481 state
->set_recommendation(state
,
482 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
483 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR
);
485 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
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
);
525 state
->set_recommendation(state
,
526 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
527 TNC_IMV_EVALUATION_RESULT_ERROR
);
528 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
530 return send_message(connection_id
);
534 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
536 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
537 TNC_ConnectionID connection_id
)
539 if (!imv_attestation
)
541 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
542 return TNC_RESULT_NOT_INITIALIZED
;
544 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
548 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
550 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
551 TNC_ConnectionID connection_id
)
554 imv_attestation_state_t
*attestation_state
;
556 if (!imv_attestation
)
558 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
559 return TNC_RESULT_NOT_INITIALIZED
;
561 /* get current IMV state */
562 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
564 return TNC_RESULT_FATAL
;
566 attestation_state
= (imv_attestation_state_t
*)state
;
568 /* Check if IMV has to initiate the PA-TNC exchange */
569 if (attestation_state
->get_handshake_state(attestation_state
) ==
570 IMV_ATTESTATION_STATE_INIT
)
572 return send_message(connection_id
);
574 return TNC_RESULT_SUCCESS
;
578 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
580 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
582 if (!imv_attestation
)
584 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
585 return TNC_RESULT_NOT_INITIALIZED
;
588 imv_attestation
->destroy(imv_attestation
);
589 imv_attestation
= NULL
;
591 return TNC_RESULT_SUCCESS
;
595 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
597 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
598 TNC_TNCS_BindFunctionPointer bind_function
)
600 if (!imv_attestation
)
602 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
603 return TNC_RESULT_NOT_INITIALIZED
;
605 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);