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_req_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_get_aik.h>
28 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
29 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
30 #include <tcg/tcg_pts_attr_req_file_meas.h>
32 #include <tncif_pa_subtypes.h>
36 #include <utils/linked_list.h>
38 #include <trousers/tss.h>
39 #include <trousers/trousers.h>
43 static const char imv_name
[] = "Attestation";
45 #define IMV_VENDOR_ID PEN_TCG
46 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
49 * UTF-8 encoding of the character used to delimiter the filename
51 #define SOLIDUS_UTF 0x002F
52 #define REVERSE_SOLIDUS_UTF 0x005C
54 static imv_agent_t
*imv_attestation
;
57 * List of files and directories to measure
59 static linked_list_t
*file_list
, *directory_list
;
62 * Monotonic increasing number for Request File Measurement attribute
64 static u_int16_t request_id_counter
= 0;
67 * Struct to hold file or directory name with the request ID for Request File Measurement attribute
69 typedef struct measurement_req_entry_t measurement_req_entry_t
;
71 struct measurement_req_entry_t
{
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
)
86 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
87 return TNC_RESULT_ALREADY_INITIALIZED
;
89 imv_attestation
= imv_agent_create(imv_name
, IMV_VENDOR_ID
, IMV_SUBTYPE
,
90 imv_id
, actual_version
);
93 return TNC_RESULT_FATAL
;
95 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
97 DBG1(DBG_IMV
, "no common IF-IMV version");
98 return TNC_RESULT_NO_COMMON_VERSION
;
100 return TNC_RESULT_SUCCESS
;
104 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
106 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
107 TNC_ConnectionID connection_id
,
108 TNC_ConnectionState new_state
)
111 imv_attestation_state_t
*attestation_state
;
112 enumerator_t
*enumerator
;
115 measurement_req_entry_t
*entry
;
118 if (!imv_attestation
)
120 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
121 return TNC_RESULT_NOT_INITIALIZED
;
125 case TNC_CONNECTION_STATE_CREATE
:
126 state
= imv_attestation_state_create(connection_id
);
127 return imv_attestation
->create_state(imv_attestation
, state
);
128 case TNC_CONNECTION_STATE_DELETE
:
129 return imv_attestation
->delete_state(imv_attestation
, connection_id
);
130 case TNC_CONNECTION_STATE_HANDSHAKE
:
131 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
133 return TNC_RESULT_FATAL
;
135 state
->change_state(state
, new_state
);
136 attestation_state
= (imv_attestation_state_t
*)state
;
138 /** Get the files to measure for
139 * PTS Request File Measurement attribute
142 file_list
= linked_list_create();
143 directory_list
= linked_list_create();
145 files
= lib
->settings
->get_str(lib
->settings
,
146 "libimcv.plugins.imc-attestation.files", "none");
147 enumerator
= enumerator_create_token(files
, " ", " ");
148 while (enumerator
->enumerate(enumerator
, &token
))
150 entry
= malloc_thing(measurement_req_entry_t
);
151 token
= strdup(token
);
153 entry
->request_id
= request_id_counter
;
154 file_list
->insert_last(file_list
, entry
);
156 request_id_counter
++;
159 /** Get the directories to measure for
160 * PTS Request File Measurement attribute
163 directories
= lib
->settings
->get_str(lib
->settings
,
164 "libimcv.plugins.imc-attestation.directories", "none");
165 enumerator
= enumerator_create_token(directories
, " ", " ");
166 while (enumerator
->enumerate(enumerator
, &token
))
168 entry
= malloc_thing(measurement_req_entry_t
);
169 token
= strdup(token
);
171 entry
->request_id
= request_id_counter
;
172 directory_list
->insert_last(directory_list
, entry
);
174 request_id_counter
++;
176 enumerator
->destroy(enumerator
);
177 return TNC_RESULT_SUCCESS
;
179 return imv_attestation
->change_state(imv_attestation
, connection_id
,
184 static TNC_Result
send_message(TNC_ConnectionID connection_id
)
190 imv_attestation_state_t
*attestation_state
;
191 imv_attestation_handshake_state_t handshake_state
;
193 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
195 return TNC_RESULT_FATAL
;
198 attestation_state
= (imv_attestation_state_t
*)state
;
199 handshake_state
= attestation_state
->get_handshake_state(attestation_state
);
201 /* Switch on the attribute type IMV has received */
202 switch (handshake_state
)
204 case IMV_ATTESTATION_STATE_INIT
:
206 /* Send Request Protocol Capabilities attribute */
207 pts_proto_caps_flag_t flags
;
208 flags
= PTS_PROTO_CAPS_T
| PTS_PROTO_CAPS_VER
| PTS_PROTO_CAPS_CURRENT
;
209 attr
= tcg_pts_attr_req_proto_caps_create(flags
);
212 case IMV_ATTESTATION_STATE_PROTO_CAP
:
214 /* Send Measurement Algorithms attribute */
215 pts_meas_algorithms_t algorithms
;
216 algorithms
= PTS_MEAS_ALGO_SHA1
| PTS_MEAS_ALGO_SHA256
| PTS_MEAS_ALGO_SHA384
;
217 attr
= tcg_pts_attr_meas_algo_create(algorithms
);
220 case IMV_ATTESTATION_STATE_MEAS_ALGO
:
222 /* Send Get TPM Version Information attribute */
223 attr
= tcg_pts_attr_get_tpm_version_info_create();
226 case IMV_ATTESTATION_STATE_TPM_INFO
:
228 /* Send Get AIK attribute */
229 /* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
230 //attr = tcg_pts_attr_get_aik_create();
233 case IMV_ATTESTATION_STATE_AIK
:
235 /* Send Request File Measurement attribute */
236 enumerator_t
*enumerator
;
237 measurement_req_entry_t
*entry
;
239 u_int16_t request_id
;
240 u_int32_t delimiter
= SOLIDUS_UTF
;
242 msg
= pa_tnc_msg_create();
245 * Add files to measure to PTS Request File Measurement attribute
247 enumerator
= enumerator_create_single(file_list
, NULL
);
248 while (enumerator
->enumerate(enumerator
, &entry
))
250 attr
= tcg_pts_attr_req_file_meas_create(false,
251 entry
->request_id
, delimiter
,
252 chunk_create(entry
->path
, strlen(entry
->path
)));
253 attr
->set_noskip_flag(attr
, TRUE
);
254 msg
->add_attribute(msg
, attr
);
256 /** Add directories to measure to PTS Request File Measurement attribute
258 enumerator
= enumerator_create_single(directory_list
, NULL
);
259 while (enumerator
->enumerate(enumerator
, &entry
))
261 attr
= tcg_pts_attr_req_file_meas_create(true,
262 entry
->request_id
, delimiter
,
263 chunk_create(entry
->path
, strlen(entry
->path
)));
264 attr
->set_noskip_flag(attr
, TRUE
);
265 msg
->add_attribute(msg
, attr
);
267 enumerator
->destroy(enumerator
);
270 case IMV_ATTESTATION_STATE_SIMPLE_COMP_EVID
:
271 case IMV_ATTESTATION_STATE_SIMPLE_EVID_FINAL
:
272 case IMV_ATTESTATION_STATE_FILE_METADATA
:
273 case IMV_ATTESTATION_STATE_FILE_MEAS
:
274 case IMV_ATTESTATION_STATE_IML
:
275 DBG1(DBG_IMV
, "Attestation IMV has nothing to send: \"%s\"", handshake_state
);
276 return TNC_RESULT_FATAL
;
278 DBG1(DBG_IMV
, "Attestation IMV is in unknown state: \"%s\"", handshake_state
);
279 return TNC_RESULT_FATAL
;
282 attr
->set_noskip_flag(attr
, TRUE
);
283 msg
= pa_tnc_msg_create();
284 msg
->add_attribute(msg
, attr
);
288 result
= imv_attestation
->send_message(imv_attestation
, connection_id
,
289 msg
->get_encoding(msg
));
296 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
298 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
299 TNC_ConnectionID connection_id
,
300 TNC_BufferReference msg
,
302 TNC_MessageType msg_type
)
304 pa_tnc_msg_t
*pa_tnc_msg
;
307 imv_attestation_state_t
*imv_attestation_state
;
308 enumerator_t
*enumerator
;
310 bool fatal_error
= FALSE
;
312 if (!imv_attestation
)
314 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
315 return TNC_RESULT_NOT_INITIALIZED
;
318 /* get current IMV state */
319 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
321 return TNC_RESULT_FATAL
;
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 pa_tnc_error_code_t error_code
;
344 chunk_t msg_info
, attr_info
;
347 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
348 error_code
= error_attr
->get_error_code(error_attr
);
349 msg_info
= error_attr
->get_msg_info(error_attr
);
351 DBG1(DBG_IMV
, "received PA-TNC error '%N' concerning message %#B",
352 pa_tnc_error_code_names
, error_code
, &msg_info
);
355 case PA_ERROR_INVALID_PARAMETER
:
356 offset
= error_attr
->get_offset(error_attr
);
357 DBG1(DBG_IMV
, " occurred at offset of %u bytes", offset
);
359 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED
:
360 attr_info
= error_attr
->get_attr_info(error_attr
);
361 DBG1(DBG_IMV
, " unsupported attribute %#B", &attr_info
);
368 else if (attr
->get_vendor_id(attr
) == PEN_TCG
)
371 * Handle TCG PTS attributes
373 switch(attr
->get_type(attr
))
375 case TCG_PTS_PROTO_CAPS
:
377 case TCG_PTS_MEAS_ALGO_SELECTION
:
379 case TCG_PTS_TPM_VERSION_INFO
:
384 /* PTS-based Attestation Evidence */
385 case TCG_PTS_SIMPLE_COMP_EVID
:
387 case TCG_PTS_SIMPLE_EVID_FINAL
:
389 case TCG_PTS_FILE_MEAS
:
392 /* TODO: Not implemented yet */
393 case TCG_PTS_DH_NONCE_PARAMS_RESP
:
394 case TCG_PTS_UNIX_FILE_META
:
395 case TCG_PTS_INTEG_MEAS_LOG
:
396 /* Attributes using XML */
397 case TCG_PTS_TEMPL_REF_MANI_SET_META
:
398 case TCG_PTS_VERIFICATION_RESULT
:
399 case TCG_PTS_INTEG_REPORT
:
401 case TCG_PTS_WIN_FILE_META
:
402 case TCG_PTS_REGISTRY_VALUE
:
403 /* Received on IMC side only*/
404 case TCG_PTS_REQ_PROTO_CAPS
:
405 case TCG_PTS_DH_NONCE_PARAMS_REQ
:
406 case TCG_PTS_DH_NONCE_FINISH
:
407 case TCG_PTS_MEAS_ALGO
:
408 case TCG_PTS_GET_TPM_VERSION_INFO
:
409 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META
:
410 case TCG_PTS_UPDATE_TEMPL_REF_MANI
:
411 case TCG_PTS_GET_AIK
:
412 case TCG_PTS_REQ_FUNCT_COMP_EVID
:
413 case TCG_PTS_GEN_ATTEST_EVID
:
414 case TCG_PTS_REQ_FILE_META
:
415 case TCG_PTS_REQ_FILE_MEAS
:
416 case TCG_PTS_REQ_INTEG_MEAS_LOG
:
418 DBG1(DBG_IMV
, "received unsupported attribute '%N'",
419 tcg_attr_names
, attr
->get_type(attr
));
424 enumerator
->destroy(enumerator
);
425 pa_tnc_msg
->destroy(pa_tnc_msg
);
429 state
->set_recommendation(state
,
430 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
431 TNC_IMV_EVALUATION_RESULT_ERROR
);
432 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
435 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
439 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
441 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
442 TNC_ConnectionID connection_id
)
444 if (!imv_attestation
)
446 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
447 return TNC_RESULT_NOT_INITIALIZED
;
449 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
453 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
455 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
456 TNC_ConnectionID connection_id
)
458 if (!imv_attestation
)
460 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
461 return TNC_RESULT_NOT_INITIALIZED
;
463 return TNC_RESULT_SUCCESS
;
467 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
469 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
471 if (!imv_attestation
)
473 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
474 return TNC_RESULT_NOT_INITIALIZED
;
476 imv_attestation
->destroy(imv_attestation
);
477 imv_attestation
= NULL
;
479 return TNC_RESULT_SUCCESS
;
483 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
485 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
486 TNC_TNCS_BindFunctionPointer bind_function
)
488 if (!imv_attestation
)
490 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
491 return TNC_RESULT_NOT_INITIALIZED
;
493 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);