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