d31cca423702a9e30719d182660f2d6877894c81
[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 = 0;
64
65 /**
66 * High Entropy Random Data
67 * used in calculation of shared secret for the assessment session
68 */
69 static char *initiator_nonce = NULL;
70
71 /**
72 * PTS file measurement database
73 */
74 static pts_database_t *pts_db;
75
76 /**
77 * PTS credentials
78 */
79 static pts_creds_t *pts_creds;
80
81 /**
82 * PTS credential manager
83 */
84 static credential_manager_t *pts_credmgr;
85
86 /**
87 * TRUE if DH Nonce Parameters Request attribute is sent
88 */
89 static bool dh_nonce_req_sent = FALSE;
90
91 /**
92 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
93 */
94 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
95 TNC_Version min_version,
96 TNC_Version max_version,
97 TNC_Version *actual_version)
98 {
99 char *hash_alg, *dh_group, *uri, *cadir;
100 rng_t *rng;
101
102 if (imv_attestation)
103 {
104 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
105 return TNC_RESULT_ALREADY_INITIALIZED;
106 }
107 if (!pts_meas_algo_probe(&supported_algorithms) ||
108 !pts_dh_group_probe(&supported_dh_groups))
109 {
110 return TNC_RESULT_FATAL;
111 }
112 if (!pts_probe_dh_groups(&supported_dh_groups))
113 {
114 return TNC_RESULT_FATAL;
115 }
116 imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
117 imv_id, actual_version);
118 if (!imv_attestation)
119 {
120 return TNC_RESULT_FATAL;
121 }
122
123 libpts_init();
124
125 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
126 {
127 DBG1(DBG_IMV, "no common IF-IMV version");
128 return TNC_RESULT_NO_COMMON_VERSION;
129 }
130
131 hash_alg = lib->settings->get_str(lib->settings,
132 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
133 dh_group = lib->settings->get_str(lib->settings,
134 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
135
136 if (!pts_meas_algo_update(hash_alg, &supported_algorithms) ||
137 !pts_dh_group_update(dh_group, &supported_dh_groups))
138 {
139 return TNC_RESULT_FATAL;
140 }
141
142 /**
143 * Specify supported PTS Diffie-Hellman groups
144 *
145 * modp1024: PTS_DH_GROUP_IKE2
146 * modp1536: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5
147 * modp2048: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14
148 * ecp256: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14 |
149 * PTS_DH_GROUP_IKE19
150 * ecp384: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14 |
151 * PTS_DH_GROUP_IKE19 | PTS_DH_GROUP_IKE20
152 *
153 * we expect the PTS-IMC to select the strongest supported group
154 */
155 dh_group = lib->settings->get_str(lib->settings,
156 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
157 if (!pts_update_supported_dh_groups(dh_group, &supported_dh_groups))
158 {
159 return TNC_RESULT_FATAL;
160 }
161
162 /* create a PTS credential manager */
163 pts_credmgr = credential_manager_create();
164
165 /* create PTS credential set */
166 cadir = lib->settings->get_str(lib->settings,
167 "libimcv.plugins.imv-attestation.cadir", NULL);
168 pts_creds = pts_creds_create(cadir);
169 if (pts_creds)
170 {
171 pts_credmgr->add_set(pts_credmgr, pts_creds->get_set(pts_creds));
172 }
173
174 /* attach file measurement database */
175 uri = lib->settings->get_str(lib->settings,
176 "libimcv.plugins.imv-attestation.database", NULL);
177 pts_db = pts_database_create(uri);
178
179 return TNC_RESULT_SUCCESS;
180 }
181
182 /**
183 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
184 */
185 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
186 TNC_ConnectionID connection_id,
187 TNC_ConnectionState new_state)
188 {
189 imv_state_t *state;
190 imv_attestation_state_t *attestation_state;
191 TNC_Result result;
192
193 if (!imv_attestation)
194 {
195 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
196 return TNC_RESULT_NOT_INITIALIZED;
197 }
198 switch (new_state)
199 {
200 case TNC_CONNECTION_STATE_CREATE:
201 state = imv_attestation_state_create(connection_id);
202 return imv_attestation->create_state(imv_attestation, state);
203 case TNC_CONNECTION_STATE_DELETE:
204 return imv_attestation->delete_state(imv_attestation, connection_id);
205 case TNC_CONNECTION_STATE_HANDSHAKE:
206 result = imv_attestation->change_state(imv_attestation,
207 connection_id, new_state, &state);
208 if (result != TNC_RESULT_SUCCESS)
209 {
210 return result;
211 }
212 attestation_state = (imv_attestation_state_t*)state;
213
214 return TNC_RESULT_SUCCESS;
215 default:
216 return imv_attestation->change_state(imv_attestation, connection_id,
217 new_state, NULL);
218 }
219 }
220
221 static TNC_Result send_message(TNC_ConnectionID connection_id)
222 {
223 pa_tnc_msg_t *msg;
224 imv_state_t *state;
225 imv_attestation_state_t *attestation_state;
226 TNC_Result result;
227
228 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
229 {
230 return TNC_RESULT_FATAL;
231 }
232 attestation_state = (imv_attestation_state_t*)state;
233 msg = pa_tnc_msg_create();
234
235 if (imv_attestation_build(msg, attestation_state, supported_algorithms,
236 supported_dh_groups, pts_db))
237 {
238 msg->build(msg);
239 result = imv_attestation->send_message(imv_attestation, connection_id,
240 msg->get_encoding(msg));
241 }
242 else
243 {
244 <<<<<<< HEAD
245 result = TNC_RESULT_FATAL;
246 =======
247 case IMV_ATTESTATION_STATE_INIT:
248 {
249 pts_proto_caps_flag_t flags;
250
251 /* Send Request Protocol Capabilities attribute */
252 flags = pts->get_proto_caps(pts);
253 attr = tcg_pts_attr_proto_caps_create(flags, TRUE);
254 attr->set_noskip_flag(attr, TRUE);
255 msg->add_attribute(msg, attr);
256
257 /* Send Measurement Algorithms attribute */
258 attr = tcg_pts_attr_meas_algo_create(supported_algorithms, FALSE);
259 attr->set_noskip_flag(attr, TRUE);
260 msg->add_attribute(msg, attr);
261
262 attestation_state->set_handshake_state(attestation_state,
263 IMV_ATTESTATION_STATE_TPM_INIT);
264 break;
265 }
266 case IMV_ATTESTATION_STATE_TPM_INIT:
267 {
268 if (!dh_nonce_req_sent)
269 {
270 /* Send DH nonce parameters request attribute */
271 attr = tcg_pts_attr_dh_nonce_params_req_create(0, supported_dh_groups);
272 attr->set_noskip_flag(attr, TRUE);
273 msg->add_attribute(msg, attr);
274 dh_nonce_req_sent = TRUE;
275 }
276 else
277 {
278 pts_meas_algorithms_t selected_algorithm;
279 chunk_t initiator_pub_val;
280
281 /* Send DH nonce finish attribute */
282 selected_algorithm = pts->get_meas_algorithm(pts);
283 pts->get_my_public_value(pts, &initiator_pub_val);
284
285 attr = tcg_pts_attr_dh_nonce_finish_create(NONCE_LEN,
286 selected_algorithm,
287 chunk_create(initiator_nonce, NONCE_LEN),
288 initiator_pub_val);
289 attr->set_noskip_flag(attr, TRUE);
290 msg->add_attribute(msg, attr);
291
292 /* Send Get TPM Version attribute */
293 attr = tcg_pts_attr_get_tpm_version_info_create();
294 attr->set_noskip_flag(attr, TRUE);
295 msg->add_attribute(msg, attr);
296
297 /* Send Get AIK attribute */
298 attr = tcg_pts_attr_get_aik_create();
299 attr->set_noskip_flag(attr, TRUE);
300 msg->add_attribute(msg, attr);
301
302 attestation_state->set_handshake_state(attestation_state,
303 IMV_ATTESTATION_STATE_MEAS);
304 }
305
306 break;
307 }
308 case IMV_ATTESTATION_STATE_MEAS:
309 {
310
311 enumerator_t *enumerator;
312 u_int32_t delimiter = SOLIDUS_UTF;
313 char *platform_info, *pathname;
314 u_int16_t request_id;
315 int id, type;
316 bool is_dir;
317
318 attestation_state->set_handshake_state(attestation_state,
319 IMV_ATTESTATION_STATE_COMP_EVID);
320
321 /* Get Platform and OS of the PTS-IMC */
322 platform_info = pts->get_platform_info(pts);
323
324 if (!pts_db || !platform_info)
325 {
326 DBG1(DBG_IMV, "%s%s%s not available",
327 (pts_db) ? "" : "pts database",
328 (!pts_db && !platform_info) ? "and" : "",
329 (platform_info) ? "" : "platform info");
330 break;
331 }
332 DBG1(DBG_IMV, "platform is '%s'", platform_info);
333
334 /* Send Request File Metadata attribute */
335 attr = tcg_pts_attr_req_file_meta_create(FALSE, SOLIDUS_UTF, "/etc/tnc_config");
336 attr->set_noskip_flag(attr, TRUE);
337 msg->add_attribute(msg, attr);
338
339 /* Send Request File Measurement attribute */
340 enumerator = pts_db->create_file_enumerator(pts_db, platform_info);
341 if (!enumerator)
342 {
343 break;
344 }
345 while (enumerator->enumerate(enumerator, &id, &type, &pathname))
346 {
347 is_dir = (type != 0);
348 request_id = attestation_state->add_request(attestation_state,
349 id, is_dir);
350 DBG2(DBG_IMV, "measurement request %d for %s '%s'",
351 request_id, is_dir ? "directory" : "file", pathname);
352 attr = tcg_pts_attr_req_file_meas_create(is_dir, request_id,
353 delimiter, pathname);
354 attr->set_noskip_flag(attr, TRUE);
355 msg->add_attribute(msg, attr);
356 }
357 enumerator->destroy(enumerator);
358 break;
359 }
360 case IMV_ATTESTATION_STATE_COMP_EVID:
361 {
362 pts_attr_req_funct_comp_evid_flag_t flags;
363 u_int32_t sub_comp_depth;
364 pts_qualifier_t qualifier;
365 pts_funct_comp_name_t name;
366
367 attestation_state->set_handshake_state(attestation_state,
368 IMV_ATTESTATION_STATE_END);
369
370 flags = PTS_REQ_FUNC_COMP_FLAG_PCR;
371 sub_comp_depth = 0;
372 qualifier.kernel = FALSE;
373 qualifier.sub_component = FALSE;
374 qualifier.type = PTS_FUNC_COMP_TYPE_ALL;
375 name = PTS_FUNC_COMP_NAME_BIOS;
376
377 /* Send Request Functional Component Evidence attribute */
378 attr = tcg_pts_attr_req_funct_comp_evid_create(flags, sub_comp_depth,
379 PEN_TCG, qualifier, name);
380 attr->set_noskip_flag(attr, TRUE);
381 msg->add_attribute(msg, attr);
382 /* Send Generate Attestation Evidence attribute */
383 attr = tcg_pts_attr_gen_attest_evid_create();
384 attr->set_noskip_flag(attr, TRUE);
385 msg->add_attribute(msg, attr);
386
387 break;
388 }
389 default:
390 DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"",
391 handshake_state);
392 return TNC_RESULT_FATAL;
393 >>>>>>> fixed setting of PTS DH group
394 }
395 msg->destroy(msg);
396
397 return result;
398 }
399
400 /**
401 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
402 */
403 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
404 TNC_ConnectionID connection_id,
405 TNC_BufferReference msg,
406 TNC_UInt32 msg_len,
407 TNC_MessageType msg_type)
408 {
409 pa_tnc_msg_t *pa_tnc_msg;
410 pa_tnc_attr_t *attr;
411 linked_list_t *attr_list;
412 imv_state_t *state;
413 imv_attestation_state_t *attestation_state;
414 pts_t *pts;
415 enumerator_t *enumerator;
416 TNC_Result result;
417
418 if (!imv_attestation)
419 {
420 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
421 return TNC_RESULT_NOT_INITIALIZED;
422 }
423
424 /* get current IMV state */
425 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
426 {
427 return TNC_RESULT_FATAL;
428 }
429 attestation_state = (imv_attestation_state_t*)state;
430 pts = attestation_state->get_pts(attestation_state);
431
432 /* parse received PA-TNC message and automatically handle any errors */
433 result = imv_attestation->receive_message(imv_attestation, connection_id,
434 chunk_create(msg, msg_len), msg_type,
435 &pa_tnc_msg);
436
437 /* no parsed PA-TNC attributes available if an error occurred */
438 if (!pa_tnc_msg)
439 {
440 return result;
441 }
442
443 attr_list = linked_list_create();
444 result = TNC_RESULT_SUCCESS;
445
446 /* analyze PA-TNC attributes */
447 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
448 while (enumerator->enumerate(enumerator, &attr))
449 {
450 if (attr->get_vendor_id(attr) == PEN_IETF)
451 {
452 if (attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
453 {
454 ietf_attr_pa_tnc_error_t *error_attr;
455 pen_t error_vendor_id;
456 pa_tnc_error_code_t error_code;
457 chunk_t msg_info, attr_info;
458 u_int32_t offset;
459
460 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
461 error_vendor_id = error_attr->get_vendor_id(error_attr);
462 error_code = error_attr->get_error_code(error_attr);
463 msg_info = error_attr->get_msg_info(error_attr);
464
465 if (error_vendor_id == PEN_IETF)
466 {
467 DBG1(DBG_IMV, "received PA-TNC error '%N' "
468 "concerning message %#B",
469 pa_tnc_error_code_names, error_code, &msg_info);
470
471 switch (error_code)
472 {
473 case PA_ERROR_INVALID_PARAMETER:
474 offset = error_attr->get_offset(error_attr);
475 DBG1(DBG_IMV, " occurred at offset of %u bytes",
476 offset);
477 break;
478 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
479 attr_info = error_attr->get_attr_info(error_attr);
480 DBG1(DBG_IMV, " unsupported attribute %#B",
481 &attr_info);
482 break;
483 default:
484 break;
485 }
486 }
487 else if (error_vendor_id == PEN_TCG)
488 {
489 DBG1(DBG_IMV, "received TCG-PTS error '%N'",
490 pts_error_code_names, error_code);
491 DBG1(DBG_IMV, "error information: %B", &msg_info);
492 }
493 result = TNC_RESULT_FATAL;
494 }
495 else if (attr->get_type(attr) == IETF_ATTR_PRODUCT_INFORMATION)
496 {
497 ietf_attr_product_info_t *attr_cast;
498 char *platform_info;
499
500 attr_cast = (ietf_attr_product_info_t*)attr;
501 platform_info = attr_cast->get_info(attr_cast, NULL, NULL);
502 pts->set_platform_info(pts, platform_info);
503 }
504 }
505 else if (attr->get_vendor_id(attr) == PEN_TCG)
506 {
507 if (!imv_attestation_process(attr, attr_list, attestation_state,
508 supported_algorithms,supported_dh_groups, pts_db, pts_credmgr))
509 {
510 result = TNC_RESULT_FATAL;
511 break;
512 }
513 }
514 }
515 enumerator->destroy(enumerator);
516 pa_tnc_msg->destroy(pa_tnc_msg);
517
518 if (result != TNC_RESULT_SUCCESS)
519 {
520 attr_list->destroy(attr_list);
521 state->set_recommendation(state,
522 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
523 TNC_IMV_EVALUATION_RESULT_ERROR);
524 return imv_attestation->provide_recommendation(imv_attestation,
525 connection_id);
526 }
527
528 if (attr_list->get_count(attr_list))
529 {
530 pa_tnc_msg = pa_tnc_msg_create();
531
532 enumerator = attr_list->create_enumerator(attr_list);
533 while (enumerator->enumerate(enumerator, &attr))
534 {
535 pa_tnc_msg->add_attribute(pa_tnc_msg, attr);
536 }
537 enumerator->destroy(enumerator);
538
539 pa_tnc_msg->build(pa_tnc_msg);
540 result = imv_attestation->send_message(imv_attestation, connection_id,
541 pa_tnc_msg->get_encoding(pa_tnc_msg));
542
543 pa_tnc_msg->destroy(pa_tnc_msg);
544 attr_list->destroy(attr_list);
545
546 return result;
547 }
548 attr_list->destroy(attr_list);
549
550 if (attestation_state->get_handshake_state(attestation_state) ==
551 IMV_ATTESTATION_STATE_END)
552 {
553 if (attestation_state->get_request_count(attestation_state))
554 {
555 DBG1(DBG_IMV, "failure due to %d pending file measurements",
556 attestation_state->get_request_count(attestation_state));
557 attestation_state->set_measurement_error(attestation_state);
558 }
559 if (attestation_state->get_measurement_error(attestation_state))
560 {
561 state->set_recommendation(state,
562 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
563 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR);
564 }
565 else
566 {
567 state->set_recommendation(state,
568 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
569 TNC_IMV_EVALUATION_RESULT_COMPLIANT);
570 }
571 return imv_attestation->provide_recommendation(imv_attestation,
572 connection_id);
573 }
574
575 return send_message(connection_id);
576 }
577
578 /**
579 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
580 */
581 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
582 TNC_ConnectionID connection_id)
583 {
584 if (!imv_attestation)
585 {
586 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
587 return TNC_RESULT_NOT_INITIALIZED;
588 }
589 return imv_attestation->provide_recommendation(imv_attestation,
590 connection_id);
591 }
592
593 /**
594 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
595 */
596 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
597 TNC_ConnectionID connection_id)
598 {
599 imv_state_t *state;
600 imv_attestation_state_t *attestation_state;
601
602 if (!imv_attestation)
603 {
604 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
605 return TNC_RESULT_NOT_INITIALIZED;
606 }
607 /* get current IMV state */
608 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
609 {
610 return TNC_RESULT_FATAL;
611 }
612 attestation_state = (imv_attestation_state_t*)state;
613
614 /* Check if IMV has to initiate the PA-TNC exchange */
615 if (attestation_state->get_handshake_state(attestation_state) ==
616 IMV_ATTESTATION_STATE_INIT)
617 {
618 return send_message(connection_id);
619 }
620 return TNC_RESULT_SUCCESS;
621 }
622
623 /**
624 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
625 */
626 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
627 {
628 if (!imv_attestation)
629 {
630 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
631 return TNC_RESULT_NOT_INITIALIZED;
632 }
633 if (pts_creds)
634 {
635 pts_credmgr->remove_set(pts_credmgr, pts_creds->get_set(pts_creds));
636 pts_creds->destroy(pts_creds);
637 }
638 DESTROY_IF(pts_db);
639 DESTROY_IF(pts_credmgr);
640 free(initiator_nonce);
641
642 libpts_deinit();
643
644 imv_attestation->destroy(imv_attestation);
645 imv_attestation = NULL;
646
647 return TNC_RESULT_SUCCESS;
648 }
649
650 /**
651 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
652 */
653 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
654 TNC_TNCS_BindFunctionPointer bind_function)
655 {
656 if (!imv_attestation)
657 {
658 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
659 return TNC_RESULT_NOT_INITIALIZED;
660 }
661 return imv_attestation->bind_functions(imv_attestation, bind_function);
662 }