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"
17 #include "imv_attestation_process.h"
18 #include "imv_attestation_build.h"
20 #include <imv/imv_agent.h>
21 #include <pa_tnc/pa_tnc_msg.h>
22 #include <ietf/ietf_attr.h>
23 #include <ietf/ietf_attr_pa_tnc_error.h>
24 #include <ietf/ietf_attr_product_info.h>
29 #include <pts/pts_database.h>
30 #include <pts/pts_creds.h>
32 #include <tcg/tcg_attr.h>
34 #include <tncif_pa_subtypes.h>
38 #include <credentials/credential_manager.h>
39 #include <utils/linked_list.h>
43 static const char imv_name
[] = "Attestation";
45 #define IMV_VENDOR_ID PEN_TCG
46 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
48 static imv_agent_t
*imv_attestation
;
51 * Supported PTS measurement algorithms
53 static pts_meas_algorithms_t supported_algorithms
= PTS_MEAS_ALGO_NONE
;
56 * Supported PTS Diffie Hellman Groups
58 static pts_dh_group_t supported_dh_groups
= PTS_DH_GROUP_NONE
;
61 * PTS file measurement database
63 static pts_database_t
*pts_db
;
68 static pts_creds_t
*pts_creds
;
71 * PTS credential manager
73 static credential_manager_t
*pts_credmgr
;
76 * see section 3.8.1 of TCG TNC IF-IMV Specification 1.3
78 TNC_Result
TNC_IMV_Initialize(TNC_IMVID imv_id
,
79 TNC_Version min_version
,
80 TNC_Version max_version
,
81 TNC_Version
*actual_version
)
83 char *hash_alg
, *dh_group
, *uri
, *cadir
;
87 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
88 return TNC_RESULT_ALREADY_INITIALIZED
;
90 if (!pts_meas_algo_probe(&supported_algorithms
) ||
91 !pts_dh_group_probe(&supported_dh_groups
))
93 return TNC_RESULT_FATAL
;
95 imv_attestation
= imv_agent_create(imv_name
, IMV_VENDOR_ID
, IMV_SUBTYPE
,
96 imv_id
, actual_version
);
99 return TNC_RESULT_FATAL
;
104 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
106 DBG1(DBG_IMV
, "no common IF-IMV version");
107 return TNC_RESULT_NO_COMMON_VERSION
;
110 hash_alg
= lib
->settings
->get_str(lib
->settings
,
111 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
112 dh_group
= lib
->settings
->get_str(lib
->settings
,
113 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
115 if (!pts_meas_algo_update(hash_alg
, &supported_algorithms
) ||
116 !pts_dh_group_update(dh_group
, &supported_dh_groups
))
118 return TNC_RESULT_FATAL
;
121 /* create a PTS credential manager */
122 pts_credmgr
= credential_manager_create();
124 /* create PTS credential set */
125 cadir
= lib
->settings
->get_str(lib
->settings
,
126 "libimcv.plugins.imv-attestation.cadir", NULL
);
127 pts_creds
= pts_creds_create(cadir
);
130 pts_credmgr
->add_set(pts_credmgr
, pts_creds
->get_set(pts_creds
));
133 /* attach file measurement database */
134 uri
= lib
->settings
->get_str(lib
->settings
,
135 "libimcv.plugins.imv-attestation.database", NULL
);
136 pts_db
= pts_database_create(uri
);
138 return TNC_RESULT_SUCCESS
;
142 * see section 3.8.2 of TCG TNC IF-IMV Specification 1.3
144 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
145 TNC_ConnectionID connection_id
,
146 TNC_ConnectionState new_state
)
150 if (!imv_attestation
)
152 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
153 return TNC_RESULT_NOT_INITIALIZED
;
157 case TNC_CONNECTION_STATE_CREATE
:
158 state
= imv_attestation_state_create(connection_id
);
159 return imv_attestation
->create_state(imv_attestation
, state
);
160 case TNC_CONNECTION_STATE_DELETE
:
161 return imv_attestation
->delete_state(imv_attestation
, connection_id
);
162 case TNC_CONNECTION_STATE_HANDSHAKE
:
164 return imv_attestation
->change_state(imv_attestation
, connection_id
,
169 static TNC_Result
send_message(TNC_ConnectionID connection_id
)
174 imv_attestation_state_t
*attestation_state
;
176 linked_list_t
*attr_list
;
177 enumerator_t
*enumerator
;
179 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
181 return TNC_RESULT_FATAL
;
183 attestation_state
= (imv_attestation_state_t
*)state
;
184 attr_list
= linked_list_create();
186 if (imv_attestation_build(attr_list
, attestation_state
, supported_algorithms
,
187 supported_dh_groups
, pts_db
))
189 if (attr_list
->get_count(attr_list
))
191 msg
= pa_tnc_msg_create();
193 /* move PA-TNC attributes to PA-TNC message */
194 enumerator
= attr_list
->create_enumerator(attr_list
);
195 while (enumerator
->enumerate(enumerator
, &attr
))
197 msg
->add_attribute(msg
, attr
);
199 enumerator
->destroy(enumerator
);
202 result
= imv_attestation
->send_message(imv_attestation
,
203 connection_id
, FALSE
, 0, TNC_IMCID_ANY
,
204 msg
->get_encoding(msg
));
209 result
= TNC_RESULT_SUCCESS
;
211 attr_list
->destroy(attr_list
);
215 attr_list
->destroy_offset(attr_list
, offsetof(pa_tnc_attr_t
, destroy
));
216 result
= TNC_RESULT_FATAL
;
222 static TNC_Result
receive_message(TNC_IMVID imv_id
,
223 TNC_ConnectionID connection_id
,
224 TNC_UInt32 msg_flags
,
226 TNC_VendorID msg_vid
,
227 TNC_MessageSubtype msg_subtype
,
228 TNC_UInt32 src_imc_id
,
229 TNC_UInt32 dst_imv_id
)
231 pa_tnc_msg_t
*pa_tnc_msg
;
233 linked_list_t
*attr_list
;
235 imv_attestation_state_t
*attestation_state
;
237 enumerator_t
*enumerator
;
240 if (!imv_attestation
)
242 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
243 return TNC_RESULT_NOT_INITIALIZED
;
246 /* get current IMV state */
247 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
249 return TNC_RESULT_FATAL
;
251 attestation_state
= (imv_attestation_state_t
*)state
;
252 pts
= attestation_state
->get_pts(attestation_state
);
254 /* parse received PA-TNC message and automatically handle any errors */
255 result
= imv_attestation
->receive_message(imv_attestation
, state
, msg
,
256 msg_vid
, msg_subtype
, src_imc_id
, dst_imv_id
, &pa_tnc_msg
);
258 /* no parsed PA-TNC attributes available if an error occurred */
264 /* preprocess any IETF standard error attributes */
265 result
= pa_tnc_msg
->process_ietf_std_errors(pa_tnc_msg
) ?
266 TNC_RESULT_FATAL
: TNC_RESULT_SUCCESS
;
268 attr_list
= linked_list_create();
270 /* analyze PA-TNC attributes */
271 enumerator
= pa_tnc_msg
->create_attribute_enumerator(pa_tnc_msg
);
272 while (enumerator
->enumerate(enumerator
, &attr
))
274 if (attr
->get_vendor_id(attr
) == PEN_IETF
)
276 if (attr
->get_type(attr
) == IETF_ATTR_PA_TNC_ERROR
)
278 ietf_attr_pa_tnc_error_t
*error_attr
;
279 pen_t error_vendor_id
;
280 pa_tnc_error_code_t error_code
;
283 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
284 error_vendor_id
= error_attr
->get_vendor_id(error_attr
);
286 if (error_vendor_id
== PEN_TCG
)
288 error_code
= error_attr
->get_error_code(error_attr
);
289 msg_info
= error_attr
->get_msg_info(error_attr
);
291 DBG1(DBG_IMV
, "received TCG-PTS error '%N'",
292 pts_error_code_names
, error_code
);
293 DBG1(DBG_IMV
, "error information: %B", &msg_info
);
295 result
= TNC_RESULT_FATAL
;
298 else if (attr
->get_type(attr
) == IETF_ATTR_PRODUCT_INFORMATION
)
300 ietf_attr_product_info_t
*attr_cast
;
303 attr_cast
= (ietf_attr_product_info_t
*)attr
;
304 platform_info
= attr_cast
->get_info(attr_cast
, NULL
, NULL
);
305 pts
->set_platform_info(pts
, platform_info
);
308 else if (attr
->get_vendor_id(attr
) == PEN_TCG
)
310 if (!imv_attestation_process(attr
, attr_list
, attestation_state
,
311 supported_algorithms
,supported_dh_groups
, pts_db
, pts_credmgr
))
313 result
= TNC_RESULT_FATAL
;
318 enumerator
->destroy(enumerator
);
319 pa_tnc_msg
->destroy(pa_tnc_msg
);
321 if (result
!= TNC_RESULT_SUCCESS
)
323 attr_list
->destroy_offset(attr_list
, offsetof(pa_tnc_attr_t
, destroy
));
324 state
->set_recommendation(state
,
325 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
326 TNC_IMV_EVALUATION_RESULT_ERROR
);
327 return imv_attestation
->provide_recommendation(imv_attestation
,
331 if (attr_list
->get_count(attr_list
))
333 pa_tnc_msg
= pa_tnc_msg_create();
335 /* move PA-TNC attributes to PA-TNC message */
336 enumerator
= attr_list
->create_enumerator(attr_list
);
337 while (enumerator
->enumerate(enumerator
, &attr
))
339 pa_tnc_msg
->add_attribute(pa_tnc_msg
, attr
);
341 enumerator
->destroy(enumerator
);
343 pa_tnc_msg
->build(pa_tnc_msg
);
344 result
= imv_attestation
->send_message(imv_attestation
, connection_id
,
345 FALSE
, 0, TNC_IMCID_ANY
,
346 pa_tnc_msg
->get_encoding(pa_tnc_msg
));
348 pa_tnc_msg
->destroy(pa_tnc_msg
);
349 attr_list
->destroy(attr_list
);
353 attr_list
->destroy(attr_list
);
355 /* check the IMV state for the next PA-TNC attributes to send */
356 result
= send_message(connection_id
);
357 if (result
!= TNC_RESULT_SUCCESS
)
359 state
->set_recommendation(state
,
360 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
361 TNC_IMV_EVALUATION_RESULT_ERROR
);
362 return imv_attestation
->provide_recommendation(imv_attestation
,
366 if (attestation_state
->get_handshake_state(attestation_state
) ==
367 IMV_ATTESTATION_STATE_END
)
369 if (attestation_state
->get_file_meas_request_count(attestation_state
))
371 DBG1(DBG_IMV
, "failure due to %d pending file measurements",
372 attestation_state
->get_file_meas_request_count(attestation_state
));
373 attestation_state
->set_measurement_error(attestation_state
);
375 if (attestation_state
->get_component_count(attestation_state
))
377 DBG1(DBG_IMV
, "failure due to %d components waiting for evidence",
378 attestation_state
->get_component_count(attestation_state
));
379 attestation_state
->set_measurement_error(attestation_state
);
381 if (attestation_state
->get_measurement_error(attestation_state
))
383 state
->set_recommendation(state
,
384 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
385 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR
);
389 state
->set_recommendation(state
,
390 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
391 TNC_IMV_EVALUATION_RESULT_COMPLIANT
);
393 return imv_attestation
->provide_recommendation(imv_attestation
,
401 * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3
403 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
404 TNC_ConnectionID connection_id
,
405 TNC_BufferReference msg
,
407 TNC_MessageType msg_type
)
409 TNC_VendorID msg_vid
;
410 TNC_MessageSubtype msg_subtype
;
412 msg_vid
= msg_type
>> 8;
413 msg_subtype
= msg_type
& TNC_SUBTYPE_ANY
;
415 return receive_message(imv_id
, connection_id
, 0, chunk_create(msg
, msg_len
),
416 msg_vid
, msg_subtype
, 0, TNC_IMVID_ANY
);
420 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
422 TNC_Result
TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id
,
423 TNC_ConnectionID connection_id
,
424 TNC_UInt32 msg_flags
,
425 TNC_BufferReference msg
,
427 TNC_VendorID msg_vid
,
428 TNC_MessageSubtype msg_subtype
,
429 TNC_UInt32 src_imc_id
,
430 TNC_UInt32 dst_imv_id
)
432 return receive_message(imv_id
, connection_id
, msg_flags
,
433 chunk_create(msg
, msg_len
), msg_vid
, msg_subtype
,
434 src_imc_id
, dst_imv_id
);
438 * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3
440 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
441 TNC_ConnectionID connection_id
)
443 if (!imv_attestation
)
445 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
446 return TNC_RESULT_NOT_INITIALIZED
;
448 return imv_attestation
->provide_recommendation(imv_attestation
,
453 * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3
455 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
456 TNC_ConnectionID connection_id
)
459 imv_attestation_state_t
*attestation_state
;
461 if (!imv_attestation
)
463 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
464 return TNC_RESULT_NOT_INITIALIZED
;
466 /* get current IMV state */
467 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
469 return TNC_RESULT_FATAL
;
471 attestation_state
= (imv_attestation_state_t
*)state
;
473 /* Check if IMV has to initiate the PA-TNC exchange */
474 if (attestation_state
->get_handshake_state(attestation_state
) ==
475 IMV_ATTESTATION_STATE_INIT
)
477 return send_message(connection_id
);
479 return TNC_RESULT_SUCCESS
;
483 * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3
485 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
487 if (!imv_attestation
)
489 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
490 return TNC_RESULT_NOT_INITIALIZED
;
494 pts_credmgr
->remove_set(pts_credmgr
, pts_creds
->get_set(pts_creds
));
495 pts_creds
->destroy(pts_creds
);
498 DESTROY_IF(pts_credmgr
);
502 imv_attestation
->destroy(imv_attestation
);
503 imv_attestation
= NULL
;
505 return TNC_RESULT_SUCCESS
;
509 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3
511 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
512 TNC_TNCS_BindFunctionPointer bind_function
)
514 if (!imv_attestation
)
516 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
517 return TNC_RESULT_NOT_INITIALIZED
;
519 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);