2 * Copyright (C) 2011-2012 Sansar Choinyambuu, Andreas Steffen
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 <imv/imv_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>
25 #include <ietf/ietf_attr_string_version.h>
30 #include <pts/pts_database.h>
31 #include <pts/pts_creds.h>
33 #include <tcg/tcg_attr.h>
35 #include <tncif_pa_subtypes.h>
38 #include <utils/debug.h>
39 #include <credentials/credential_manager.h>
40 #include <collections/linked_list.h>
44 static const char imv_name
[] = "Attestation";
46 static pen_type_t msg_types
[] = {
47 { PEN_TCG
, PA_SUBTYPE_TCG_PTS
},
48 { PEN_IETF
, PA_SUBTYPE_IETF_OPERATING_SYSTEM
}
51 static imv_agent_t
*imv_attestation
;
54 * Supported PTS measurement algorithms
56 static pts_meas_algorithms_t supported_algorithms
= PTS_MEAS_ALGO_NONE
;
59 * Supported PTS Diffie Hellman Groups
61 static pts_dh_group_t supported_dh_groups
= PTS_DH_GROUP_NONE
;
64 * PTS file measurement database
66 static pts_database_t
*pts_db
;
71 static pts_creds_t
*pts_creds
;
74 * PTS credential manager
76 static credential_manager_t
*pts_credmgr
;
79 * see section 3.8.1 of TCG TNC IF-IMV Specification 1.3
81 TNC_Result
TNC_IMV_Initialize(TNC_IMVID imv_id
,
82 TNC_Version min_version
,
83 TNC_Version max_version
,
84 TNC_Version
*actual_version
)
86 char *hash_alg
, *dh_group
, *uri
, *cadir
;
90 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
91 return TNC_RESULT_ALREADY_INITIALIZED
;
93 if (!pts_meas_algo_probe(&supported_algorithms
) ||
94 !pts_dh_group_probe(&supported_dh_groups
))
96 return TNC_RESULT_FATAL
;
98 imv_attestation
= imv_agent_create(imv_name
, msg_types
, countof(msg_types
),
99 imv_id
, actual_version
);
100 if (!imv_attestation
)
102 return TNC_RESULT_FATAL
;
107 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
109 DBG1(DBG_IMV
, "no common IF-IMV version");
110 return TNC_RESULT_NO_COMMON_VERSION
;
113 hash_alg
= lib
->settings
->get_str(lib
->settings
,
114 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
115 dh_group
= lib
->settings
->get_str(lib
->settings
,
116 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
118 if (!pts_meas_algo_update(hash_alg
, &supported_algorithms
) ||
119 !pts_dh_group_update(dh_group
, &supported_dh_groups
))
121 return TNC_RESULT_FATAL
;
124 /* create a PTS credential manager */
125 pts_credmgr
= credential_manager_create();
127 /* create PTS credential set */
128 cadir
= lib
->settings
->get_str(lib
->settings
,
129 "libimcv.plugins.imv-attestation.cadir", NULL
);
130 pts_creds
= pts_creds_create(cadir
);
133 pts_credmgr
->add_set(pts_credmgr
, pts_creds
->get_set(pts_creds
));
136 /* attach file measurement database */
137 uri
= lib
->settings
->get_str(lib
->settings
,
138 "libimcv.plugins.imv-attestation.database", NULL
);
139 pts_db
= pts_database_create(uri
);
141 return TNC_RESULT_SUCCESS
;
145 * see section 3.8.2 of TCG TNC IF-IMV Specification 1.3
147 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
148 TNC_ConnectionID connection_id
,
149 TNC_ConnectionState new_state
)
153 if (!imv_attestation
)
155 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
156 return TNC_RESULT_NOT_INITIALIZED
;
160 case TNC_CONNECTION_STATE_CREATE
:
161 state
= imv_attestation_state_create(connection_id
);
162 return imv_attestation
->create_state(imv_attestation
, state
);
163 case TNC_CONNECTION_STATE_DELETE
:
164 return imv_attestation
->delete_state(imv_attestation
, connection_id
);
165 case TNC_CONNECTION_STATE_HANDSHAKE
:
167 return imv_attestation
->change_state(imv_attestation
, connection_id
,
172 static TNC_Result
send_message(imv_state_t
*state
, imv_msg_t
*out_msg
)
174 imv_attestation_state_t
*attestation_state
;
177 attestation_state
= (imv_attestation_state_t
*)state
;
179 if (imv_attestation_build(out_msg
, attestation_state
, supported_algorithms
,
180 supported_dh_groups
, pts_db
))
182 result
= out_msg
->send(out_msg
, TRUE
);
186 result
= TNC_RESULT_FATAL
;
192 static TNC_Result
receive_message(imv_state_t
*state
, imv_msg_t
*in_msg
)
194 imv_attestation_state_t
*attestation_state
;
196 enumerator_t
*enumerator
;
201 chunk_t os_name
= chunk_empty
;
202 chunk_t os_version
= chunk_empty
;
203 bool fatal_error
= FALSE
;
205 /* parse received PA-TNC message and handle local and remote errors */
206 result
= in_msg
->receive(in_msg
, &fatal_error
);
207 if (result
!= TNC_RESULT_SUCCESS
)
212 attestation_state
= (imv_attestation_state_t
*)state
;
213 pts
= attestation_state
->get_pts(attestation_state
);
215 out_msg
= imv_msg_create_as_reply(in_msg
);
216 out_msg
->set_msg_type(out_msg
, msg_types
[0]);
218 /* analyze PA-TNC attributes */
219 enumerator
= in_msg
->create_attribute_enumerator(in_msg
);
220 while (enumerator
->enumerate(enumerator
, &attr
))
222 type
= attr
->get_type(attr
);
224 if (type
.vendor_id
== PEN_IETF
)
228 case IETF_ATTR_PA_TNC_ERROR
:
230 ietf_attr_pa_tnc_error_t
*error_attr
;
231 pen_type_t error_code
;
234 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
235 error_code
= error_attr
->get_error_code(error_attr
);
237 if (error_code
.vendor_id
== PEN_TCG
)
239 msg_info
= error_attr
->get_msg_info(error_attr
);
241 DBG1(DBG_IMV
, "received TCG-PTS error '%N'",
242 pts_error_code_names
, error_code
.type
);
243 DBG1(DBG_IMV
, "error information: %B", &msg_info
);
245 result
= TNC_RESULT_FATAL
;
249 case IETF_ATTR_PRODUCT_INFORMATION
:
251 ietf_attr_product_info_t
*attr_cast
;
253 attr_cast
= (ietf_attr_product_info_t
*)attr
;
254 os_name
= attr_cast
->get_info(attr_cast
, NULL
, NULL
);
257 case IETF_ATTR_STRING_VERSION
:
259 ietf_attr_string_version_t
*attr_cast
;
261 attr_cast
= (ietf_attr_string_version_t
*)attr
;
262 os_version
= attr_cast
->get_version(attr_cast
, NULL
, NULL
);
269 else if (type
.vendor_id
== PEN_TCG
)
271 if (!imv_attestation_process(attr
, out_msg
, attestation_state
,
272 supported_algorithms
,supported_dh_groups
, pts_db
, pts_credmgr
))
274 result
= TNC_RESULT_FATAL
;
279 enumerator
->destroy(enumerator
);
281 if (os_name
.len
&& os_version
.len
)
283 pts
->set_platform_info(pts
, os_name
, os_version
);
286 if (fatal_error
|| result
!= TNC_RESULT_SUCCESS
)
288 out_msg
->delete_attributes(out_msg
);
289 state
->set_recommendation(state
,
290 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
291 TNC_IMV_EVALUATION_RESULT_ERROR
);
292 result
= out_msg
->send_assessment(out_msg
);
293 out_msg
->destroy(out_msg
);
294 if (result
!= TNC_RESULT_SUCCESS
)
298 return imv_attestation
->provide_recommendation(imv_attestation
, state
);
301 /* send PA-TNC message with excl flag set */
302 result
= out_msg
->send(out_msg
, TRUE
);
304 if (result
!= TNC_RESULT_SUCCESS
)
306 out_msg
->destroy(out_msg
);
310 /* check the IMV state for the next PA-TNC attributes to send */
311 result
= send_message(state
, out_msg
);
313 if (result
!= TNC_RESULT_SUCCESS
)
315 state
->set_recommendation(state
,
316 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
317 TNC_IMV_EVALUATION_RESULT_ERROR
);
318 out_msg
->delete_attributes(out_msg
);
319 result
= out_msg
->send_assessment(out_msg
);
320 out_msg
->destroy(out_msg
);
321 if (result
!= TNC_RESULT_SUCCESS
)
325 return imv_attestation
->provide_recommendation(imv_attestation
, state
);
328 if (attestation_state
->get_handshake_state(attestation_state
) ==
329 IMV_ATTESTATION_STATE_END
)
331 if (attestation_state
->get_file_meas_request_count(attestation_state
))
333 DBG1(DBG_IMV
, "failure due to %d pending file measurements",
334 attestation_state
->get_file_meas_request_count(attestation_state
));
335 attestation_state
->set_measurement_error(attestation_state
,
336 IMV_ATTESTATION_ERROR_FILE_MEAS_PEND
);
338 if (attestation_state
->get_measurement_error(attestation_state
))
340 state
->set_recommendation(state
,
341 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
,
342 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR
);
346 state
->set_recommendation(state
,
347 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
348 TNC_IMV_EVALUATION_RESULT_COMPLIANT
);
350 result
= out_msg
->send_assessment(out_msg
);
351 out_msg
->destroy(out_msg
);
352 if (result
!= TNC_RESULT_SUCCESS
)
356 return imv_attestation
->provide_recommendation(imv_attestation
, state
);
358 out_msg
->destroy(out_msg
);
364 * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3
366 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
367 TNC_ConnectionID connection_id
,
368 TNC_BufferReference msg
,
370 TNC_MessageType msg_type
)
376 if (!imv_attestation
)
378 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
379 return TNC_RESULT_NOT_INITIALIZED
;
381 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
383 return TNC_RESULT_FATAL
;
385 in_msg
= imv_msg_create_from_data(imv_attestation
, state
, connection_id
,
386 msg_type
, chunk_create(msg
, msg_len
));
387 result
= receive_message(state
, in_msg
);
388 in_msg
->destroy(in_msg
);
394 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
396 TNC_Result
TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id
,
397 TNC_ConnectionID connection_id
,
398 TNC_UInt32 msg_flags
,
399 TNC_BufferReference msg
,
401 TNC_VendorID msg_vid
,
402 TNC_MessageSubtype msg_subtype
,
403 TNC_UInt32 src_imc_id
,
404 TNC_UInt32 dst_imv_id
)
410 if (!imv_attestation
)
412 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
413 return TNC_RESULT_NOT_INITIALIZED
;
415 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
417 return TNC_RESULT_FATAL
;
419 in_msg
= imv_msg_create_from_long_data(imv_attestation
, state
, connection_id
,
420 src_imc_id
, dst_imv_id
, msg_vid
, msg_subtype
,
421 chunk_create(msg
, msg_len
));
422 result
=receive_message(state
, in_msg
);
423 in_msg
->destroy(in_msg
);
429 * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3
431 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
432 TNC_ConnectionID connection_id
)
436 if (!imv_attestation
)
438 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
439 return TNC_RESULT_NOT_INITIALIZED
;
441 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
443 return TNC_RESULT_FATAL
;
445 return imv_attestation
->provide_recommendation(imv_attestation
, state
);
449 * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3
451 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
452 TNC_ConnectionID connection_id
)
454 if (!imv_attestation
)
456 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
457 return TNC_RESULT_NOT_INITIALIZED
;
459 return TNC_RESULT_SUCCESS
;
463 * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3
465 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
467 if (!imv_attestation
)
469 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
470 return TNC_RESULT_NOT_INITIALIZED
;
474 pts_credmgr
->remove_set(pts_credmgr
, pts_creds
->get_set(pts_creds
));
475 pts_creds
->destroy(pts_creds
);
478 DESTROY_IF(pts_credmgr
);
482 imv_attestation
->destroy(imv_attestation
);
483 imv_attestation
= NULL
;
485 return TNC_RESULT_SUCCESS
;
489 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3
491 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
492 TNC_TNCS_BindFunctionPointer bind_function
)
494 if (!imv_attestation
)
496 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
497 return TNC_RESULT_NOT_INITIALIZED
;
499 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);