moved TSS into pts object
[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.h>
24 #include <tcg/tcg_attr.h>
25 #include <tcg/tcg_pts_attr_proto_caps.h>
26 #include <tcg/tcg_pts_attr_meas_algo.h>
27 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
28 #include <tcg/tcg_pts_attr_tpm_version_info.h>
29 #include <tcg/tcg_pts_attr_get_aik.h>
30 #include <tcg/tcg_pts_attr_aik.h>
31 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
32 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
33 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
34 #include <tcg/tcg_pts_attr_simple_evid_final.h>
35 #include <tcg/tcg_pts_attr_req_file_meas.h>
36 #include <tcg/tcg_pts_attr_file_meas.h>
37
38 #include <tncif_pa_subtypes.h>
39
40 #include <pen/pen.h>
41 #include <debug.h>
42 #include <utils/linked_list.h>
43
44 /* IMV definitions */
45
46 static const char imv_name[] = "Attestation";
47
48 #define IMV_VENDOR_ID PEN_TCG
49 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
50
51 /**
52 * UTF-8 encoding of the character used to delimiter the filename
53 */
54 #define SOLIDUS_UTF 0x002F
55 #define REVERSE_SOLIDUS_UTF 0x005C
56
57 static imv_agent_t *imv_attestation;
58
59 /**
60 * Supported PTS measurement algorithms
61 */
62 static pts_meas_algorithms_t supported_algorithms = 0;
63
64 /**
65 * List of files and directories to measure
66 */
67 static linked_list_t *file_list, *directory_list;
68
69 /**
70 * Monotonic increasing number for Request File Measurement attribute
71 */
72 static u_int16_t request_id_counter = 0;
73
74 /* TODO: Move the struct to some header file? Duplicate with imc_attestation*/
75 /**
76 * Struct to hold file or directory name with the request ID for Request File Measurement attribute
77 */
78 typedef struct measurement_req_entry_t measurement_req_entry_t;
79
80 struct measurement_req_entry_t {
81 char *path;
82 u_int16_t request_id;
83 };
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;
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 imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
101 imv_id, actual_version);
102 if (!imv_attestation || !pts_meas_probe_algorithms(&supported_algorithms))
103 {
104 return TNC_RESULT_FATAL;
105 }
106 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
107 {
108 DBG1(DBG_IMV, "no common IF-IMV version");
109 return TNC_RESULT_NO_COMMON_VERSION;
110 }
111
112 /**
113 * Specify supported PTS measurement algorithms
114 *
115 * sha1 : PTS_MEAS_ALGO_SHA1
116 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
117 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
118 *
119 * we expect the PTS-IMC to select the strongest supported algorithm
120 */
121 hash_alg = lib->settings->get_str(lib->settings,
122 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
123 if (!strcaseeq(hash_alg, "sha384") && !strcaseeq(hash_alg, "sha2_384"))
124 {
125 /* remove SHA384 algorithm */
126 supported_algorithms &= ~PTS_MEAS_ALGO_SHA384;
127 }
128 if (strcaseeq(hash_alg, "sha1"))
129 {
130 /* remove SHA256 algorithm */
131 supported_algorithms &= ~PTS_MEAS_ALGO_SHA256;
132 }
133
134 return TNC_RESULT_SUCCESS;
135 }
136
137 /**
138 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
139 */
140 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
141 TNC_ConnectionID connection_id,
142 TNC_ConnectionState new_state)
143 {
144 imv_state_t *state;
145 imv_attestation_state_t *attestation_state;
146 enumerator_t *enumerator;
147 char *files;
148 char *directories;
149 measurement_req_entry_t *entry;
150 char *token;
151 TNC_Result result;
152
153 if (!imv_attestation)
154 {
155 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
156 return TNC_RESULT_NOT_INITIALIZED;
157 }
158 switch (new_state)
159 {
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:
166 result = imv_attestation->change_state(imv_attestation, connection_id,
167 new_state, &state);
168 if (result != TNC_RESULT_SUCCESS)
169 {
170 return result;
171 }
172 attestation_state = (imv_attestation_state_t*)state;
173
174 /**
175 * Get the files to measure for
176 * PTS Request File Measurement attribute
177 */
178
179 file_list = linked_list_create();
180 directory_list = linked_list_create();
181
182 files = lib->settings->get_str(lib->settings,
183 "libimcv.plugins.imc-attestation.files", "none");
184 enumerator = enumerator_create_token(files, " ", " ");
185 while (enumerator->enumerate(enumerator, &token))
186 {
187 entry = malloc_thing(measurement_req_entry_t);
188 token = strdup(token);
189 entry->path = token;
190 entry->request_id = request_id_counter;
191 file_list->insert_last(file_list, entry);
192 free(token);
193 request_id_counter ++;
194 }
195
196 /**
197 * Get the directories to measure for
198 * PTS Request File Measurement attribute
199 */
200
201 directories = lib->settings->get_str(lib->settings,
202 "libimcv.plugins.imc-attestation.directories", "none");
203 enumerator = enumerator_create_token(directories, " ", " ");
204 while (enumerator->enumerate(enumerator, &token))
205 {
206 entry = malloc_thing(measurement_req_entry_t);
207 token = strdup(token);
208 entry->path = token;
209 entry->request_id = request_id_counter;
210 directory_list->insert_last(directory_list, entry);
211 free(token);
212 request_id_counter ++;
213 }
214 enumerator->destroy(enumerator);
215 return TNC_RESULT_SUCCESS;
216 default:
217 return imv_attestation->change_state(imv_attestation, connection_id,
218 new_state, NULL);
219 }
220 }
221
222 static TNC_Result send_message(TNC_ConnectionID connection_id)
223 {
224 pa_tnc_msg_t *msg;
225 pa_tnc_attr_t *attr;
226 TNC_Result result;
227 imv_state_t *state;
228 imv_attestation_state_t *attestation_state;
229 imv_attestation_handshake_state_t handshake_state;
230
231 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
232 {
233 return TNC_RESULT_FATAL;
234 }
235
236 attestation_state = (imv_attestation_state_t*)state;
237 handshake_state = attestation_state->get_handshake_state(attestation_state);
238
239 /* Switch on the attribute type IMV has received */
240 switch (handshake_state)
241 {
242 case IMV_ATTESTATION_STATE_INIT:
243 {
244 pts_proto_caps_flag_t flags;
245
246 /* Send Request Protocol Capabilities attribute */
247 flags = PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_V | PTS_PROTO_CAPS_C;
248 attr = tcg_pts_attr_proto_caps_create(flags, TRUE);
249 break;
250 }
251 case IMV_ATTESTATION_STATE_PROTO_CAPS:
252 {
253 /* Send Measurement Algorithms attribute */
254 attr = tcg_pts_attr_meas_algo_create(supported_algorithms, FALSE);
255 break;
256 }
257 case IMV_ATTESTATION_STATE_MEAS_ALGO:
258 {
259 /* Send Get TPM Version Information attribute */
260 attr = tcg_pts_attr_get_tpm_version_info_create();
261 break;
262 }
263 case IMV_ATTESTATION_STATE_TPM_INFO:
264 {
265 /* Send Get AIK attribute */
266 /* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
267 //attr = tcg_pts_attr_get_aik_create();
268 //break;
269 }
270 case IMV_ATTESTATION_STATE_AIK:
271 {
272 /* Send Request File Measurement attribute */
273 enumerator_t *enumerator;
274 measurement_req_entry_t *entry;
275 char *path;
276 u_int16_t request_id;
277 u_int32_t delimiter = SOLIDUS_UTF;
278
279 msg = pa_tnc_msg_create();
280
281 /**
282 * Add files to measure to PTS Request File Measurement attribute
283 */
284 enumerator = enumerator_create_single(file_list, NULL);
285 while (enumerator->enumerate(enumerator, &entry))
286 {
287 attr = tcg_pts_attr_req_file_meas_create(false,
288 entry->request_id, delimiter,
289 chunk_create(entry->path, strlen(entry->path)));
290 attr->set_noskip_flag(attr, TRUE);
291 msg->add_attribute(msg, attr);
292 }
293 /** Add directories to measure to PTS Request File Measurement attribute
294 */
295 enumerator = enumerator_create_single(directory_list, NULL);
296 while (enumerator->enumerate(enumerator, &entry))
297 {
298 attr = tcg_pts_attr_req_file_meas_create(true,
299 entry->request_id, delimiter,
300 chunk_create(entry->path, strlen(entry->path)));
301 attr->set_noskip_flag(attr, TRUE);
302 msg->add_attribute(msg, attr);
303 }
304 enumerator->destroy(enumerator);
305 goto end;
306 }
307 case IMV_ATTESTATION_STATE_SIMPLE_COMP_EVID:
308 case IMV_ATTESTATION_STATE_SIMPLE_EVID_FINAL:
309 case IMV_ATTESTATION_STATE_FILE_METADATA:
310 case IMV_ATTESTATION_STATE_FILE_MEAS:
311 case IMV_ATTESTATION_STATE_IML:
312 DBG1(DBG_IMV, "Attestation IMV has nothing to send: \"%s\"", handshake_state);
313 return TNC_RESULT_FATAL;
314 default:
315 DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"", handshake_state);
316 return TNC_RESULT_FATAL;
317 }
318
319 msg = pa_tnc_msg_create();
320 attr->set_noskip_flag(attr, TRUE);
321 msg->add_attribute(msg, attr);
322
323 end:
324 msg->build(msg);
325 result = imv_attestation->send_message(imv_attestation, connection_id,
326 msg->get_encoding(msg));
327 msg->destroy(msg);
328
329 return result;
330 }
331
332 /**
333 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
334 */
335 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
336 TNC_ConnectionID connection_id,
337 TNC_BufferReference msg,
338 TNC_UInt32 msg_len,
339 TNC_MessageType msg_type)
340 {
341 pa_tnc_msg_t *pa_tnc_msg;
342 pa_tnc_attr_t *attr;
343 imv_state_t *state;
344 imv_attestation_state_t *attestation_state;
345 enumerator_t *enumerator;
346 TNC_Result result;
347 bool fatal_error = FALSE;
348
349 if (!imv_attestation)
350 {
351 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
352 return TNC_RESULT_NOT_INITIALIZED;
353 }
354
355 /* get current IMV state */
356 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
357 {
358 return TNC_RESULT_FATAL;
359 }
360
361 /* parse received PA-TNC message and automatically handle any errors */
362 result = imv_attestation->receive_message(imv_attestation, connection_id,
363 chunk_create(msg, msg_len), msg_type,
364 &pa_tnc_msg);
365
366 /* no parsed PA-TNC attributes available if an error occurred */
367 if (!pa_tnc_msg)
368 {
369 return result;
370 }
371
372 /* analyze PA-TNC attributes */
373 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
374 while (enumerator->enumerate(enumerator, &attr))
375 {
376 if (attr->get_vendor_id(attr) == PEN_IETF &&
377 attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
378 {
379 ietf_attr_pa_tnc_error_t *error_attr;
380 pa_tnc_error_code_t error_code;
381 chunk_t msg_info, attr_info;
382 u_int32_t offset;
383
384 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
385 error_code = error_attr->get_error_code(error_attr);
386 msg_info = error_attr->get_msg_info(error_attr);
387
388 DBG1(DBG_IMV, "received PA-TNC error '%N' concerning message %#B",
389 pa_tnc_error_code_names, error_code, &msg_info);
390 switch (error_code)
391 {
392 case PA_ERROR_INVALID_PARAMETER:
393 offset = error_attr->get_offset(error_attr);
394 DBG1(DBG_IMV, " occurred at offset of %u bytes", offset);
395 break;
396 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
397 attr_info = error_attr->get_attr_info(error_attr);
398 DBG1(DBG_IMV, " unsupported attribute %#B", &attr_info);
399 break;
400 default:
401 break;
402 }
403 fatal_error = TRUE;
404 }
405 else if (attr->get_vendor_id(attr) == PEN_TCG)
406 {
407 /**
408 * Handle TCG PTS attributes
409 */
410
411 /* get current IMC state */
412 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
413 {
414 return TNC_RESULT_FATAL;
415 }
416 attestation_state = (imv_attestation_state_t*)state;
417
418 switch(attr->get_type(attr))
419 {
420 case TCG_PTS_PROTO_CAPS:
421 {
422 tcg_pts_attr_proto_caps_t *attr_proto_caps;
423 pts_proto_caps_flag_t proto_caps;
424
425 attr_proto_caps = (tcg_pts_attr_proto_caps_t*)attr;
426 proto_caps = attr_proto_caps->get_flags(attr_proto_caps);
427 /* TODO: What to do with the protocol capabilities from imc */
428 attestation_state->set_handshake_state(attestation_state,
429 IMV_ATTESTATION_STATE_PROTO_CAPS);
430 break;
431 }
432 case TCG_PTS_MEAS_ALGO_SELECTION:
433 {
434 tcg_pts_attr_meas_algo_t *attr_meas;
435 pts_meas_algorithms_t selected_algorithm;
436 hash_algorithm_t hash_alg;
437
438 attr_meas = (tcg_pts_attr_meas_algo_t*)attr;
439 selected_algorithm = attr_meas->get_algorithms(attr_meas);
440 hash_alg = pts_meas_to_hash_algorithm(selected_algorithm);
441 if (hash_alg == HASH_UNKNOWN)
442 {
443 /* TODO generate an error message */
444 break;
445 }
446 DBG2(DBG_IMV, "selected PTS measurement algorithm is %N",
447 hash_algorithm_names, hash_alg);
448 /* TODO: What to do with the selected algorithm from imc */
449
450 attestation_state->set_handshake_state(attestation_state,
451 IMV_ATTESTATION_STATE_MEAS_ALGO);
452 break;
453 }
454 case TCG_PTS_TPM_VERSION_INFO:
455 {
456 tcg_pts_attr_tpm_version_info_t *attr_tpm;
457 chunk_t tpm_version_info;
458 pts_t *pts;
459
460 attr_tpm = (tcg_pts_attr_tpm_version_info_t*)attr;
461 tpm_version_info = attr_tpm->get_tpm_version_info(attr_tpm);
462 pts = attestation_state->get_pts(attestation_state);
463 pts->set_tpm_version_info(pts, tpm_version_info);
464
465 attestation_state->set_handshake_state(attestation_state,
466 IMV_ATTESTATION_STATE_TPM_INFO);
467 break;
468 }
469 case TCG_PTS_AIK:
470 {
471 /* TODO: Save the AIK key and certificate */
472 attestation_state->set_handshake_state(attestation_state,
473 IMV_ATTESTATION_STATE_AIK);
474 break;
475 }
476
477 /* PTS-based Attestation Evidence */
478 case TCG_PTS_SIMPLE_COMP_EVID:
479 break;
480 case TCG_PTS_SIMPLE_EVID_FINAL:
481 break;
482 case TCG_PTS_FILE_MEAS:
483 {
484 tcg_pts_attr_file_meas_t *attr_file_meas;
485 u_int64_t num_of_files;
486 u_int16_t request_id;
487 u_int16_t meas_len;
488
489 attr_file_meas = (tcg_pts_attr_file_meas_t*)attr;
490 num_of_files = attr_file_meas->get_number_of_files(attr_file_meas);
491 request_id = attr_file_meas->get_request_id(attr_file_meas);
492 meas_len = attr_file_meas->get_meas_len(attr_file_meas);
493
494 /* TODO: Start working here */
495
496 attestation_state->set_handshake_state(attestation_state,
497 IMV_ATTESTATION_STATE_FILE_MEAS);
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 if (fatal_error)
537 {
538 state->set_recommendation(state,
539 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
540 TNC_IMV_EVALUATION_RESULT_ERROR);
541 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
542 }
543 return send_message(connection_id);
544 }
545
546 /**
547 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
548 */
549 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
550 TNC_ConnectionID connection_id)
551 {
552 if (!imv_attestation)
553 {
554 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
555 return TNC_RESULT_NOT_INITIALIZED;
556 }
557 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
558 }
559
560 /**
561 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
562 */
563 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
564 TNC_ConnectionID connection_id)
565 {
566 imv_state_t *state;
567 imv_attestation_state_t *attestation_state;
568
569 if (!imv_attestation)
570 {
571 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
572 return TNC_RESULT_NOT_INITIALIZED;
573 }
574 /* get current IMV state */
575 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
576 {
577 return TNC_RESULT_FATAL;
578 }
579 attestation_state = (imv_attestation_state_t*)state;
580
581 /* Check if IMV has to initiate the PA-TNC exchange */
582 if (attestation_state->get_handshake_state(attestation_state) ==
583 IMV_ATTESTATION_STATE_INIT)
584 {
585 return send_message(connection_id);
586 }
587 return TNC_RESULT_SUCCESS;
588 }
589
590 /**
591 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
592 */
593 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
594 {
595 if (!imv_attestation)
596 {
597 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
598 return TNC_RESULT_NOT_INITIALIZED;
599 }
600 imv_attestation->destroy(imv_attestation);
601 imv_attestation = NULL;
602
603 return TNC_RESULT_SUCCESS;
604 }
605
606 /**
607 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
608 */
609 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
610 TNC_TNCS_BindFunctionPointer bind_function)
611 {
612 if (!imv_attestation)
613 {
614 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
615 return TNC_RESULT_NOT_INITIALIZED;
616 }
617 return imv_attestation->bind_functions(imv_attestation, bind_function);
618 }