829cb88067fb67ef66c54b5a2566f71f54ae4804
[strongswan.git] / src / libimcv / plugins / imv_attestation / imv_attestation.c
1 /*
2 * Copyright (C) 2011 Sansar Choinyambuu
3 * HSR Hochschule fuer Technik Rapperswil
4 *
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>.
9 *
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
13 * for more details.
14 */
15
16 #include "imv_attestation_state.h"
17 #include "imv_attestation_process.h"
18 #include "imv_attestation_build.h"
19
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>
25
26 #include <libpts.h>
27
28 #include <pts/pts.h>
29 #include <pts/pts_database.h>
30 #include <pts/pts_creds.h>
31
32 #include <tcg/tcg_attr.h>
33
34 #include <tncif_pa_subtypes.h>
35
36 #include <pen/pen.h>
37 #include <debug.h>
38 #include <credentials/credential_manager.h>
39 #include <utils/linked_list.h>
40
41 /* IMV definitions */
42
43 static const char imv_name[] = "Attestation";
44
45 #define IMV_VENDOR_ID PEN_TCG
46 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
47
48 static imv_agent_t *imv_attestation;
49
50 /**
51 * Supported PTS measurement algorithms
52 */
53 static pts_meas_algorithms_t supported_algorithms = PTS_MEAS_ALGO_NONE;
54
55 /**
56 * Supported PTS Diffie Hellman Groups
57 */
58 static pts_dh_group_t supported_dh_groups = PTS_DH_GROUP_NONE;
59
60 /**
61 * Supported PTS Diffie Hellman Groups
62 */
63 static pts_dh_group_t supported_dh_groups = PTS_DH_GROUP_NONE;
64
65 /**
66 * PTS file measurement database
67 */
68 static pts_database_t *pts_db;
69
70 /**
71 * PTS credentials
72 */
73 static pts_creds_t *pts_creds;
74
75 /**
76 * PTS credential manager
77 */
78 static credential_manager_t *pts_credmgr;
79
80 /**
81 * TRUE if DH Nonce Parameters Request attribute is sent
82 */
83 static bool dh_nonce_req_sent = FALSE;
84
85 /**
86 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
87 */
88 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
89 TNC_Version min_version,
90 TNC_Version max_version,
91 TNC_Version *actual_version)
92 {
93 char *hash_alg, *dh_group, *uri, *cadir;
94
95 if (imv_attestation)
96 {
97 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
98 return TNC_RESULT_ALREADY_INITIALIZED;
99 }
100 if (!pts_meas_algo_probe(&supported_algorithms) ||
101 !pts_dh_group_probe(&supported_dh_groups))
102 {
103 return TNC_RESULT_FATAL;
104 }
105 if (!pts_probe_dh_groups(&supported_dh_groups))
106 {
107 return TNC_RESULT_FATAL;
108 }
109 imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
110 imv_id, actual_version);
111 if (!imv_attestation)
112 {
113 return TNC_RESULT_FATAL;
114 }
115
116 libpts_init();
117
118 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
119 {
120 DBG1(DBG_IMV, "no common IF-IMV version");
121 return TNC_RESULT_NO_COMMON_VERSION;
122 }
123
124 hash_alg = lib->settings->get_str(lib->settings,
125 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
126 dh_group = lib->settings->get_str(lib->settings,
127 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
128
129 if (!pts_meas_algo_update(hash_alg, &supported_algorithms) ||
130 !pts_dh_group_update(dh_group, &supported_dh_groups))
131 {
132 return TNC_RESULT_FATAL;
133 }
134
135 /**
136 * Specify supported PTS Diffie-Hellman groups
137 *
138 * modp1024: PTS_DH_GROUP_IKE2
139 * modp1536: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5
140 * modp2048: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14
141 * ecp256: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14 |
142 * PTS_DH_GROUP_IKE19
143 * ecp384: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14 |
144 * PTS_DH_GROUP_IKE19 | PTS_DH_GROUP_IKE20
145 *
146 * we expect the PTS-IMC to select the strongest supported group
147 */
148 dh_group = lib->settings->get_str(lib->settings,
149 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
150
151 if (!pts_meas_algo_update(hash_alg, &supported_algorithms) ||
152 !pts_dh_group_update(dh_group, &supported_dh_groups))
153 {
154 return TNC_RESULT_FATAL;
155 }
156
157 /* create a PTS credential manager */
158 pts_credmgr = credential_manager_create();
159
160 /* create PTS credential set */
161 cadir = lib->settings->get_str(lib->settings,
162 "libimcv.plugins.imv-attestation.cadir", NULL);
163 pts_creds = pts_creds_create(cadir);
164 if (pts_creds)
165 {
166 pts_credmgr->add_set(pts_credmgr, pts_creds->get_set(pts_creds));
167 }
168
169 /* attach file measurement database */
170 uri = lib->settings->get_str(lib->settings,
171 "libimcv.plugins.imv-attestation.database", NULL);
172 pts_db = pts_database_create(uri);
173
174 return TNC_RESULT_SUCCESS;
175 }
176
177 /**
178 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
179 */
180 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
181 TNC_ConnectionID connection_id,
182 TNC_ConnectionState new_state)
183 {
184 imv_state_t *state;
185 imv_attestation_state_t *attestation_state;
186 TNC_Result result;
187
188 if (!imv_attestation)
189 {
190 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
191 return TNC_RESULT_NOT_INITIALIZED;
192 }
193 switch (new_state)
194 {
195 case TNC_CONNECTION_STATE_CREATE:
196 state = imv_attestation_state_create(connection_id);
197 return imv_attestation->create_state(imv_attestation, state);
198 case TNC_CONNECTION_STATE_DELETE:
199 return imv_attestation->delete_state(imv_attestation, connection_id);
200 case TNC_CONNECTION_STATE_HANDSHAKE:
201 result = imv_attestation->change_state(imv_attestation,
202 connection_id, new_state, &state);
203 if (result != TNC_RESULT_SUCCESS)
204 {
205 return result;
206 }
207 attestation_state = (imv_attestation_state_t*)state;
208
209 return TNC_RESULT_SUCCESS;
210 default:
211 return imv_attestation->change_state(imv_attestation, connection_id,
212 new_state, NULL);
213 }
214 }
215
216 static TNC_Result send_message(TNC_ConnectionID connection_id)
217 {
218 pa_tnc_msg_t *msg;
219 imv_state_t *state;
220 imv_attestation_state_t *attestation_state;
221 TNC_Result result;
222
223 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
224 {
225 return TNC_RESULT_FATAL;
226 }
227 attestation_state = (imv_attestation_state_t*)state;
228 msg = pa_tnc_msg_create();
229
230 if (imv_attestation_build(msg, attestation_state, supported_algorithms,
231 supported_dh_groups, pts_db))
232 {
233 msg->build(msg);
234 result = imv_attestation->send_message(imv_attestation, connection_id,
235 msg->get_encoding(msg));
236 }
237 else
238 {
239 result = TNC_RESULT_FATAL;
240 }
241 msg->destroy(msg);
242
243 return result;
244 }
245
246 /**
247 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
248 */
249 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
250 TNC_ConnectionID connection_id,
251 TNC_BufferReference msg,
252 TNC_UInt32 msg_len,
253 TNC_MessageType msg_type)
254 {
255 pa_tnc_msg_t *pa_tnc_msg;
256 pa_tnc_attr_t *attr;
257 linked_list_t *attr_list;
258 imv_state_t *state;
259 imv_attestation_state_t *attestation_state;
260 pts_t *pts;
261 enumerator_t *enumerator;
262 TNC_Result result;
263
264 if (!imv_attestation)
265 {
266 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
267 return TNC_RESULT_NOT_INITIALIZED;
268 }
269
270 /* get current IMV state */
271 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
272 {
273 return TNC_RESULT_FATAL;
274 }
275 attestation_state = (imv_attestation_state_t*)state;
276 pts = attestation_state->get_pts(attestation_state);
277
278 /* parse received PA-TNC message and automatically handle any errors */
279 result = imv_attestation->receive_message(imv_attestation, connection_id,
280 chunk_create(msg, msg_len), msg_type,
281 &pa_tnc_msg);
282
283 /* no parsed PA-TNC attributes available if an error occurred */
284 if (!pa_tnc_msg)
285 {
286 return result;
287 }
288
289 attr_list = linked_list_create();
290 result = TNC_RESULT_SUCCESS;
291
292 /* analyze PA-TNC attributes */
293 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
294 while (enumerator->enumerate(enumerator, &attr))
295 {
296 if (attr->get_vendor_id(attr) == PEN_IETF)
297 {
298 if (attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
299 {
300 ietf_attr_pa_tnc_error_t *error_attr;
301 pen_t error_vendor_id;
302 pa_tnc_error_code_t error_code;
303 chunk_t msg_info, attr_info;
304 u_int32_t offset;
305
306 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
307 error_vendor_id = error_attr->get_vendor_id(error_attr);
308 error_code = error_attr->get_error_code(error_attr);
309 msg_info = error_attr->get_msg_info(error_attr);
310
311 if (error_vendor_id == PEN_IETF)
312 {
313 DBG1(DBG_IMV, "received PA-TNC error '%N' "
314 "concerning message %#B",
315 pa_tnc_error_code_names, error_code, &msg_info);
316
317 switch (error_code)
318 {
319 case PA_ERROR_INVALID_PARAMETER:
320 offset = error_attr->get_offset(error_attr);
321 DBG1(DBG_IMV, " occurred at offset of %u bytes",
322 offset);
323 break;
324 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
325 attr_info = error_attr->get_attr_info(error_attr);
326 DBG1(DBG_IMV, " unsupported attribute %#B",
327 &attr_info);
328 break;
329 default:
330 break;
331 }
332 }
333 else if (error_vendor_id == PEN_TCG)
334 {
335 DBG1(DBG_IMV, "received TCG-PTS error '%N'",
336 pts_error_code_names, error_code);
337 DBG1(DBG_IMV, "error information: %B", &msg_info);
338 }
339 result = TNC_RESULT_FATAL;
340 }
341 else if (attr->get_type(attr) == IETF_ATTR_PRODUCT_INFORMATION)
342 {
343 ietf_attr_product_info_t *attr_cast;
344 char *platform_info;
345
346 attr_cast = (ietf_attr_product_info_t*)attr;
347 platform_info = attr_cast->get_info(attr_cast, NULL, NULL);
348 pts->set_platform_info(pts, platform_info);
349 }
350 }
351 else if (attr->get_vendor_id(attr) == PEN_TCG)
352 {
353 if (!imv_attestation_process(attr, attr_list, attestation_state,
354 supported_algorithms,supported_dh_groups, pts_db, pts_credmgr))
355 {
356 result = TNC_RESULT_FATAL;
357 break;
358 }
359 }
360 }
361 enumerator->destroy(enumerator);
362 pa_tnc_msg->destroy(pa_tnc_msg);
363
364 if (result != TNC_RESULT_SUCCESS)
365 {
366 attr_list->destroy(attr_list);
367 state->set_recommendation(state,
368 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
369 TNC_IMV_EVALUATION_RESULT_ERROR);
370 return imv_attestation->provide_recommendation(imv_attestation,
371 connection_id);
372 }
373
374 if (attr_list->get_count(attr_list))
375 {
376 pa_tnc_msg = pa_tnc_msg_create();
377
378 enumerator = attr_list->create_enumerator(attr_list);
379 while (enumerator->enumerate(enumerator, &attr))
380 {
381 pa_tnc_msg->add_attribute(pa_tnc_msg, attr);
382 }
383 enumerator->destroy(enumerator);
384
385 pa_tnc_msg->build(pa_tnc_msg);
386 result = imv_attestation->send_message(imv_attestation, connection_id,
387 pa_tnc_msg->get_encoding(pa_tnc_msg));
388
389 pa_tnc_msg->destroy(pa_tnc_msg);
390 attr_list->destroy(attr_list);
391
392 return result;
393 }
394 attr_list->destroy(attr_list);
395
396 if (attestation_state->get_handshake_state(attestation_state) ==
397 IMV_ATTESTATION_STATE_END)
398 {
399 if (attestation_state->get_request_count(attestation_state))
400 {
401 DBG1(DBG_IMV, "failure due to %d pending file measurements",
402 attestation_state->get_request_count(attestation_state));
403 attestation_state->set_measurement_error(attestation_state);
404 }
405 if (attestation_state->get_measurement_error(attestation_state))
406 {
407 state->set_recommendation(state,
408 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
409 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR);
410 }
411 else
412 {
413 state->set_recommendation(state,
414 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
415 TNC_IMV_EVALUATION_RESULT_COMPLIANT);
416 }
417 return imv_attestation->provide_recommendation(imv_attestation,
418 connection_id);
419 }
420
421 return send_message(connection_id);
422 }
423
424 /**
425 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
426 */
427 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
428 TNC_ConnectionID connection_id)
429 {
430 if (!imv_attestation)
431 {
432 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
433 return TNC_RESULT_NOT_INITIALIZED;
434 }
435 return imv_attestation->provide_recommendation(imv_attestation,
436 connection_id);
437 }
438
439 /**
440 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
441 */
442 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
443 TNC_ConnectionID connection_id)
444 {
445 imv_state_t *state;
446 imv_attestation_state_t *attestation_state;
447
448 if (!imv_attestation)
449 {
450 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
451 return TNC_RESULT_NOT_INITIALIZED;
452 }
453 /* get current IMV state */
454 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
455 {
456 return TNC_RESULT_FATAL;
457 }
458 attestation_state = (imv_attestation_state_t*)state;
459
460 /* Check if IMV has to initiate the PA-TNC exchange */
461 if (attestation_state->get_handshake_state(attestation_state) ==
462 IMV_ATTESTATION_STATE_INIT)
463 {
464 return send_message(connection_id);
465 }
466 return TNC_RESULT_SUCCESS;
467 }
468
469 /**
470 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
471 */
472 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
473 {
474 if (!imv_attestation)
475 {
476 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
477 return TNC_RESULT_NOT_INITIALIZED;
478 }
479 if (pts_creds)
480 {
481 pts_credmgr->remove_set(pts_credmgr, pts_creds->get_set(pts_creds));
482 pts_creds->destroy(pts_creds);
483 }
484 DESTROY_IF(pts_db);
485 DESTROY_IF(pts_credmgr);
486
487 libpts_deinit();
488
489 imv_attestation->destroy(imv_attestation);
490 imv_attestation = NULL;
491
492 return TNC_RESULT_SUCCESS;
493 }
494
495 /**
496 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
497 */
498 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
499 TNC_TNCS_BindFunctionPointer bind_function)
500 {
501 if (!imv_attestation)
502 {
503 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
504 return TNC_RESULT_NOT_INITIALIZED;
505 }
506 return imv_attestation->bind_functions(imv_attestation, bind_function);
507 }