f78d999e1da8132400e42581f6b076975fafa9ca
[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
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
23 #include <tcg/pts/pts_database.h>
24 #include <tcg/pts/pts_error.h>
25
26 #include <tcg/tcg_attr.h>
27 #include <tcg/tcg_pts_attr_proto_caps.h>
28 #include <tcg/tcg_pts_attr_meas_algo.h>
29 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
30 #include <tcg/tcg_pts_attr_tpm_version_info.h>
31 #include <tcg/tcg_pts_attr_get_aik.h>
32 #include <tcg/tcg_pts_attr_aik.h>
33 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
34 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
35 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
36 #include <tcg/tcg_pts_attr_simple_evid_final.h>
37 #include <tcg/tcg_pts_attr_req_file_meas.h>
38 #include <tcg/tcg_pts_attr_file_meas.h>
39
40 #include <tncif_pa_subtypes.h>
41
42 #include <pen/pen.h>
43 #include <debug.h>
44 #include <utils/linked_list.h>
45
46 /* IMV definitions */
47
48 static const char imv_name[] = "Attestation";
49
50 #define IMV_VENDOR_ID PEN_TCG
51 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
52
53 /**
54 * UTF-8 encoding of the character used to delimiter the filename
55 */
56 #define SOLIDUS_UTF 0x002F
57 #define REVERSE_SOLIDUS_UTF 0x005C
58
59 static imv_agent_t *imv_attestation;
60
61 /**
62 * Supported PTS measurement algorithms
63 */
64 static pts_meas_algorithms_t supported_algorithms = 0;
65
66 /**
67 * PTS file measurement database
68 */
69 static pts_database_t *pts_db;
70
71 /**
72 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
73 */
74 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
75 TNC_Version min_version,
76 TNC_Version max_version,
77 TNC_Version *actual_version)
78 {
79 char *hash_alg, *uri;
80
81 if (imv_attestation)
82 {
83 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
84 return TNC_RESULT_ALREADY_INITIALIZED;
85 }
86 imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
87 imv_id, actual_version);
88 if (!imv_attestation || !pts_meas_probe_algorithms(&supported_algorithms))
89 {
90 return TNC_RESULT_FATAL;
91 }
92 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
93 {
94 DBG1(DBG_IMV, "no common IF-IMV version");
95 return TNC_RESULT_NO_COMMON_VERSION;
96 }
97
98 /**
99 * Specify supported PTS measurement algorithms
100 *
101 * sha1 : PTS_MEAS_ALGO_SHA1
102 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
103 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
104 *
105 * we expect the PTS-IMC to select the strongest supported algorithm
106 */
107 hash_alg = lib->settings->get_str(lib->settings,
108 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
109 if (!strcaseeq(hash_alg, "sha384") && !strcaseeq(hash_alg, "sha2_384"))
110 {
111 /* remove SHA384 algorithm */
112 supported_algorithms &= ~PTS_MEAS_ALGO_SHA384;
113 }
114 if (strcaseeq(hash_alg, "sha1"))
115 {
116 /* remove SHA256 algorithm */
117 supported_algorithms &= ~PTS_MEAS_ALGO_SHA256;
118 }
119
120 /* attach file measurement database */
121 uri = lib->settings->get_str(lib->settings,
122 "libimcv.plugins.imv-attestation.database", NULL);
123 pts_db = pts_database_create(uri);
124
125 return TNC_RESULT_SUCCESS;
126 }
127
128 /**
129 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
130 */
131 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
132 TNC_ConnectionID connection_id,
133 TNC_ConnectionState new_state)
134 {
135 imv_state_t *state;
136 imv_attestation_state_t *attestation_state;
137 TNC_Result result;
138
139 if (!imv_attestation)
140 {
141 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
142 return TNC_RESULT_NOT_INITIALIZED;
143 }
144 switch (new_state)
145 {
146 case TNC_CONNECTION_STATE_CREATE:
147 state = imv_attestation_state_create(connection_id);
148 return imv_attestation->create_state(imv_attestation, state);
149 case TNC_CONNECTION_STATE_DELETE:
150 return imv_attestation->delete_state(imv_attestation, connection_id);
151 case TNC_CONNECTION_STATE_HANDSHAKE:
152 result = imv_attestation->change_state(imv_attestation, connection_id,
153 new_state, &state);
154 if (result != TNC_RESULT_SUCCESS)
155 {
156 return result;
157 }
158 attestation_state = (imv_attestation_state_t*)state;
159
160 /* TODO: Get some configurations */
161
162 return TNC_RESULT_SUCCESS;
163 default:
164 return imv_attestation->change_state(imv_attestation, connection_id,
165 new_state, NULL);
166 }
167 }
168
169 static TNC_Result send_message(TNC_ConnectionID connection_id)
170 {
171 pa_tnc_msg_t *msg;
172 pa_tnc_attr_t *attr;
173 pts_t *pts;
174 imv_state_t *state;
175 imv_attestation_state_t *attestation_state;
176 imv_attestation_handshake_state_t handshake_state;
177 TNC_Result result;
178
179 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
180 {
181 return TNC_RESULT_FATAL;
182 }
183 attestation_state = (imv_attestation_state_t*)state;
184 handshake_state = attestation_state->get_handshake_state(attestation_state);
185 pts = attestation_state->get_pts(attestation_state);
186
187 msg = pa_tnc_msg_create();
188
189
190 /* Switch on the attribute type IMV has received */
191 switch (handshake_state)
192 {
193 case IMV_ATTESTATION_STATE_INIT:
194 {
195 pts_proto_caps_flag_t flags;
196
197 /* Send Request Protocol Capabilities attribute */
198 flags = pts->get_proto_caps(pts);
199 attr = tcg_pts_attr_proto_caps_create(flags, TRUE);
200 attr->set_noskip_flag(attr, TRUE);
201 msg->add_attribute(msg, attr);
202
203 /* Send Measurement Algorithms attribute */
204 attr = tcg_pts_attr_meas_algo_create(supported_algorithms, FALSE);
205 attr->set_noskip_flag(attr, TRUE);
206 msg->add_attribute(msg, attr);
207 break;
208 }
209
210 case IMV_ATTESTATION_STATE_MEAS:
211 {
212 enumerator_t *enumerator;
213 u_int32_t delimiter = SOLIDUS_UTF;
214 char *platform_info, *pathname;
215 int id, type;
216 bool is_directory;
217
218 /* Does the PTS-IMC have TPM support? */
219 if (pts->get_proto_caps(pts) & PTS_PROTO_CAPS_T)
220 {
221 /* Send Get TPM Version attribute */
222 attr = tcg_pts_attr_get_tpm_version_info_create();
223 attr->set_noskip_flag(attr, TRUE);
224 msg->add_attribute(msg, attr);
225
226 /* Send Get AIK attribute */
227 attr = tcg_pts_attr_get_aik_create();
228 attr->set_noskip_flag(attr, TRUE);
229 msg->add_attribute(msg, attr);
230 }
231
232 /* Get Platform and OS of the PTS-IMC */
233 platform_info = pts->get_platform_info(pts);
234
235 if (!pts_db || !platform_info)
236 {
237 DBG1(DBG_IMV, "%s%s%s not available",
238 (pts_db) ? "" : "pts database",
239 (!pts_db && !platform_info) ? "and" : "",
240 (platform_info) ? "" : "platform info");
241 break;
242 }
243 DBG1(DBG_IMV, "platform is '%s'", platform_info);
244
245 /* Send Request File Measurement attribute */
246 enumerator = pts_db->create_file_enumerator(pts_db, platform_info);
247 if (!enumerator)
248 {
249 break;
250 }
251 while (enumerator->enumerate(enumerator, &id, &type, &pathname))
252 {
253 is_directory = (type != 0);
254 DBG2(DBG_IMV, "measurement request %d for %s '%s'",
255 id, is_directory ? "directory" : "file", pathname);
256 attr = tcg_pts_attr_req_file_meas_create(is_directory, id,
257 delimiter, pathname);
258 attr->set_noskip_flag(attr, TRUE);
259 msg->add_attribute(msg, attr);
260 }
261 enumerator->destroy(enumerator);
262 break;
263 }
264 case IMV_ATTESTATION_STATE_COMP_EVID:
265 case IMV_ATTESTATION_STATE_IML:
266 DBG1(DBG_IMV, "Attestation IMV has nothing to send: \"%s\"",
267 handshake_state);
268 return TNC_RESULT_FATAL;
269 default:
270 DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"",
271 handshake_state);
272 return TNC_RESULT_FATAL;
273 }
274
275 msg->build(msg);
276 result = imv_attestation->send_message(imv_attestation, connection_id,
277 msg->get_encoding(msg));
278 msg->destroy(msg);
279
280 return result;
281 }
282
283 /**
284 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
285 */
286 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
287 TNC_ConnectionID connection_id,
288 TNC_BufferReference msg,
289 TNC_UInt32 msg_len,
290 TNC_MessageType msg_type)
291 {
292 pa_tnc_msg_t *pa_tnc_msg;
293 pa_tnc_attr_t *attr;
294 imv_state_t *state;
295 imv_attestation_state_t *attestation_state;
296 pts_t *pts;
297 enumerator_t *enumerator;
298 TNC_Result result;
299 bool fatal_error = FALSE;
300 bool measurement_error = FALSE;
301
302 if (!imv_attestation)
303 {
304 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
305 return TNC_RESULT_NOT_INITIALIZED;
306 }
307
308 /* get current IMV state */
309 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
310 {
311 return TNC_RESULT_FATAL;
312 }
313 attestation_state = (imv_attestation_state_t*)state;
314 pts = attestation_state->get_pts(attestation_state);
315
316 /* parse received PA-TNC message and automatically handle any errors */
317 result = imv_attestation->receive_message(imv_attestation, connection_id,
318 chunk_create(msg, msg_len), msg_type,
319 &pa_tnc_msg);
320
321 /* no parsed PA-TNC attributes available if an error occurred */
322 if (!pa_tnc_msg)
323 {
324 return result;
325 }
326
327 /* analyze PA-TNC attributes */
328 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
329 while (enumerator->enumerate(enumerator, &attr))
330 {
331 if (attr->get_vendor_id(attr) == PEN_IETF &&
332 attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
333 {
334 ietf_attr_pa_tnc_error_t *error_attr;
335 pen_t error_vendor_id;
336 pa_tnc_error_code_t error_code;
337 chunk_t msg_info, attr_info;
338 u_int32_t offset;
339
340 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
341 error_vendor_id = error_attr->get_vendor_id(error_attr);
342 error_code = error_attr->get_error_code(error_attr);
343 msg_info = error_attr->get_msg_info(error_attr);
344
345 if (error_vendor_id == PEN_IETF)
346 {
347 DBG1(DBG_IMV, "received PA-TNC error '%N' concerning message %#B",
348 pa_tnc_error_code_names, error_code, &msg_info);
349
350 switch (error_code)
351 {
352 case PA_ERROR_INVALID_PARAMETER:
353 offset = error_attr->get_offset(error_attr);
354 DBG1(DBG_IMV, " occurred at offset of %u bytes", offset);
355 break;
356 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
357 attr_info = error_attr->get_attr_info(error_attr);
358 DBG1(DBG_IMV, " unsupported attribute %#B", &attr_info);
359 break;
360 default:
361 break;
362 }
363 }
364 else if (error_vendor_id == PEN_TCG)
365 {
366 DBG1(DBG_IMV, "received TCG-PTS error '%N'",
367 pts_error_code_names, error_code);
368 DBG1(DBG_IMV, "error information: %B", &msg_info);
369 }
370 fatal_error = TRUE;
371 }
372 else if (attr->get_vendor_id(attr) == PEN_TCG)
373 {
374 switch (attr->get_type(attr))
375 {
376 case TCG_PTS_PROTO_CAPS:
377 {
378 tcg_pts_attr_proto_caps_t *attr_cast;
379 pts_proto_caps_flag_t flags;
380
381 attr_cast = (tcg_pts_attr_proto_caps_t*)attr;
382 flags = attr_cast->get_flags(attr_cast);
383 pts->set_proto_caps(pts, flags);
384
385 attestation_state->set_handshake_state(attestation_state,
386 IMV_ATTESTATION_STATE_MEAS);
387 break;
388 }
389 case TCG_PTS_MEAS_ALGO_SELECTION:
390 {
391 tcg_pts_attr_meas_algo_t *attr_cast;
392 pts_meas_algorithms_t selected_algorithm;
393
394 attr_cast = (tcg_pts_attr_meas_algo_t*)attr;
395 selected_algorithm = attr_cast->get_algorithms(attr_cast);
396 pts->set_meas_algorithm(pts, selected_algorithm);
397
398 attestation_state->set_handshake_state(attestation_state,
399 IMV_ATTESTATION_STATE_MEAS);
400 break;
401 }
402 case TCG_PTS_TPM_VERSION_INFO:
403 {
404 tcg_pts_attr_tpm_version_info_t *attr_cast;
405 chunk_t tpm_version_info;
406
407 attr_cast = (tcg_pts_attr_tpm_version_info_t*)attr;
408 tpm_version_info = attr_cast->get_tpm_version_info(attr_cast);
409 pts->set_tpm_version_info(pts, tpm_version_info);
410
411 attestation_state->set_handshake_state(attestation_state,
412 IMV_ATTESTATION_STATE_END);
413 break;
414 }
415 case TCG_PTS_AIK:
416 {
417 tcg_pts_attr_aik_t *attr_cast;
418 chunk_t aik;
419 bool is_naked_key;
420
421 attr_cast = (tcg_pts_attr_aik_t*)attr;
422 aik = attr_cast->get_aik(attr_cast);
423 is_naked_key = attr_cast->get_naked_flag(attr_cast);
424 pts->set_aik(pts, aik, is_naked_key);
425
426 attestation_state->set_handshake_state(attestation_state,
427 IMV_ATTESTATION_STATE_END);
428 break;
429 }
430
431 /* PTS-based Attestation Evidence */
432 case TCG_PTS_SIMPLE_COMP_EVID:
433 break;
434 case TCG_PTS_SIMPLE_EVID_FINAL:
435 break;
436 case TCG_PTS_FILE_MEAS:
437 {
438 tcg_pts_attr_file_meas_t *attr_cast;
439 u_int16_t request_id;
440 int file_count;
441 pts_meas_algorithms_t algo;
442 pts_file_meas_t *measurements;
443 chunk_t measurement;
444 char *platform_info, *filename;
445 enumerator_t *e_meas;
446
447 platform_info = pts->get_platform_info(pts);
448 if (!pts_db || !platform_info)
449 {
450 break;
451 }
452
453 attr_cast = (tcg_pts_attr_file_meas_t*)attr;
454 measurements = attr_cast->get_measurements(attr_cast);
455 algo = pts->get_meas_algorithm(pts);
456 request_id = measurements->get_request_id(measurements);
457 file_count = measurements->get_file_count(measurements);
458
459 DBG1(DBG_IMV, "measurement request %d returned %d file%s:",
460 request_id, file_count, (file_count == 1) ? "":"s");
461
462 e_meas = measurements->create_enumerator(measurements);
463 while (e_meas->enumerate(e_meas, &filename, &measurement))
464 {
465 enumerator_t *e;
466 chunk_t db_measurement;
467
468 e = pts_db->create_meas_enumerator(pts_db,
469 platform_info, request_id, algo);
470 if (!e)
471 {
472 DBG1(DBG_IMV, " database enumerator failed");
473 continue;
474 }
475 if (!e->enumerate(e, &db_measurement))
476 {
477 DBG1(DBG_IMV, " measurement for '%s' not found"
478 " in database", filename);
479 e->destroy(e);
480 continue;
481 }
482 if (chunk_equals(db_measurement, measurement))
483 {
484 DBG2(DBG_IMV, " %#B for '%s' is ok",
485 &measurement, filename);
486 }
487 else
488 {
489 DBG1(DBG_IMV, " %#B for '%s' does not match %#B",
490 &measurement, filename, &db_measurement);
491 measurement_error = TRUE;
492 }
493 e->destroy(e);
494 }
495 e_meas->destroy(e_meas);
496 attestation_state->set_handshake_state(attestation_state,
497 IMV_ATTESTATION_STATE_END);
498 break;
499 }
500
501 /* TODO: Not implemented yet */
502 case TCG_PTS_DH_NONCE_PARAMS_RESP:
503 case TCG_PTS_UNIX_FILE_META:
504 case TCG_PTS_INTEG_MEAS_LOG:
505 /* Attributes using XML */
506 case TCG_PTS_TEMPL_REF_MANI_SET_META:
507 case TCG_PTS_VERIFICATION_RESULT:
508 case TCG_PTS_INTEG_REPORT:
509 /* On Windows only*/
510 case TCG_PTS_WIN_FILE_META:
511 case TCG_PTS_REGISTRY_VALUE:
512 /* Received on IMC side only*/
513 case TCG_PTS_REQ_PROTO_CAPS:
514 case TCG_PTS_DH_NONCE_PARAMS_REQ:
515 case TCG_PTS_DH_NONCE_FINISH:
516 case TCG_PTS_MEAS_ALGO:
517 case TCG_PTS_GET_TPM_VERSION_INFO:
518 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META:
519 case TCG_PTS_UPDATE_TEMPL_REF_MANI:
520 case TCG_PTS_GET_AIK:
521 case TCG_PTS_REQ_FUNCT_COMP_EVID:
522 case TCG_PTS_GEN_ATTEST_EVID:
523 case TCG_PTS_REQ_FILE_META:
524 case TCG_PTS_REQ_FILE_MEAS:
525 case TCG_PTS_REQ_INTEG_MEAS_LOG:
526 default:
527 DBG1(DBG_IMV, "received unsupported attribute '%N'",
528 tcg_attr_names, attr->get_type(attr));
529 break;
530 }
531 }
532 }
533 enumerator->destroy(enumerator);
534 pa_tnc_msg->destroy(pa_tnc_msg);
535
536
537
538 if (fatal_error)
539 {
540 state->set_recommendation(state,
541 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
542 TNC_IMV_EVALUATION_RESULT_ERROR);
543 return imv_attestation->provide_recommendation(imv_attestation,
544 connection_id);
545 }
546
547 if (attestation_state->get_handshake_state(attestation_state) &
548 IMV_ATTESTATION_STATE_END)
549 {
550 if (measurement_error)
551 {
552 state->set_recommendation(state,
553 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
554 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR);
555 }
556 else
557 {
558 state->set_recommendation(state,
559 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
560 TNC_IMV_EVALUATION_RESULT_COMPLIANT);
561 }
562 return imv_attestation->provide_recommendation(imv_attestation,
563 connection_id);
564 }
565
566 return send_message(connection_id);
567 }
568
569 /**
570 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
571 */
572 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
573 TNC_ConnectionID connection_id)
574 {
575 if (!imv_attestation)
576 {
577 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
578 return TNC_RESULT_NOT_INITIALIZED;
579 }
580 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
581 }
582
583 /**
584 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
585 */
586 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
587 TNC_ConnectionID connection_id)
588 {
589 imv_state_t *state;
590 imv_attestation_state_t *attestation_state;
591
592 if (!imv_attestation)
593 {
594 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
595 return TNC_RESULT_NOT_INITIALIZED;
596 }
597 /* get current IMV state */
598 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
599 {
600 return TNC_RESULT_FATAL;
601 }
602 attestation_state = (imv_attestation_state_t*)state;
603
604 /* Check if IMV has to initiate the PA-TNC exchange */
605 if (attestation_state->get_handshake_state(attestation_state) ==
606 IMV_ATTESTATION_STATE_INIT)
607 {
608 return send_message(connection_id);
609 }
610 return TNC_RESULT_SUCCESS;
611 }
612
613 /**
614 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
615 */
616 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
617 {
618 if (!imv_attestation)
619 {
620 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
621 return TNC_RESULT_NOT_INITIALIZED;
622 }
623 DESTROY_IF(pts_db);
624 imv_attestation->destroy(imv_attestation);
625 imv_attestation = NULL;
626
627 return TNC_RESULT_SUCCESS;
628 }
629
630 /**
631 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
632 */
633 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
634 TNC_TNCS_BindFunctionPointer bind_function)
635 {
636 if (!imv_attestation)
637 {
638 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
639 return TNC_RESULT_NOT_INITIALIZED;
640 }
641 return imv_attestation->bind_functions(imv_attestation, bind_function);
642 }