get rid of intermediate handshake_state variable
[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 #include <tcg/tcg_attr.h>
23
24 #include <tcg/tcg_pts_attr_proto_caps.h>
25 #include <tcg/tcg_pts_attr_meas_algo.h>
26 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
27 #include <tcg/tcg_pts_attr_tpm_version_info.h>
28 #include <tcg/tcg_pts_attr_get_aik.h>
29 #include <tcg/tcg_pts_attr_aik.h>
30 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
31 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
32 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
33 #include <tcg/tcg_pts_attr_simple_evid_final.h>
34 #include <tcg/tcg_pts_attr_req_file_meas.h>
35 #include <tcg/tcg_pts_attr_file_meas.h>
36
37 #include <tncif_pa_subtypes.h>
38
39 #include <pen/pen.h>
40 #include <debug.h>
41 #include <utils/linked_list.h>
42
43 #include <trousers/tss.h>
44 #include <trousers/trousers.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 * List of files and directories to measure
68 */
69 static linked_list_t *file_list, *directory_list;
70
71 /**
72 * Monotonic increasing number for Request File Measurement attribute
73 */
74 static u_int16_t request_id_counter = 0;
75
76 /* TODO: Move the struct to some header file? Duplicate with imc_attestation*/
77 /**
78 * Struct to hold file or directory name with the request ID for Request File Measurement attribute
79 */
80 typedef struct measurement_req_entry_t measurement_req_entry_t;
81
82 struct measurement_req_entry_t {
83 char *path;
84 u_int16_t request_id;
85 };
86
87 /**
88 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
89 */
90 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
91 TNC_Version min_version,
92 TNC_Version max_version,
93 TNC_Version *actual_version)
94 {
95 char *hash_alg;
96
97 if (imv_attestation)
98 {
99 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
100 return TNC_RESULT_ALREADY_INITIALIZED;
101 }
102 imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
103 imv_id, actual_version);
104 if (!imv_attestation ||
105 !tcg_pts_probe_meas_algorithms(&supported_algorithms))
106 {
107 return TNC_RESULT_FATAL;
108 }
109 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
110 {
111 DBG1(DBG_IMV, "no common IF-IMV version");
112 return TNC_RESULT_NO_COMMON_VERSION;
113 }
114
115 /**
116 * Specify supported PTS measurement algorithms
117 *
118 * sha1 : PTS_MEAS_ALGO_SHA1
119 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
120 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
121 *
122 * we expect the PTS-IMC to select the strongest supported algorithm
123 */
124 hash_alg = lib->settings->get_str(lib->settings,
125 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
126 if (!strcaseeq(hash_alg, "sha384") && !strcaseeq(hash_alg, "sha2_384"))
127 {
128 /* remove SHA384 algorithm */
129 supported_algorithms &= ~PTS_MEAS_ALGO_SHA384;
130 }
131 if (strcaseeq(hash_alg, "sha1"))
132 {
133 /* remove SHA256 algorithm */
134 supported_algorithms &= ~PTS_MEAS_ALGO_SHA256;
135 }
136
137 return TNC_RESULT_SUCCESS;
138 }
139
140 /**
141 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
142 */
143 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
144 TNC_ConnectionID connection_id,
145 TNC_ConnectionState new_state)
146 {
147 imv_state_t *state;
148 imv_attestation_state_t *attestation_state;
149 enumerator_t *enumerator;
150 char *files;
151 char *directories;
152 measurement_req_entry_t *entry;
153 char *token;
154
155 if (!imv_attestation)
156 {
157 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
158 return TNC_RESULT_NOT_INITIALIZED;
159 }
160 switch (new_state)
161 {
162 case TNC_CONNECTION_STATE_CREATE:
163 state = imv_attestation_state_create(connection_id);
164 return imv_attestation->create_state(imv_attestation, state);
165 case TNC_CONNECTION_STATE_DELETE:
166 return imv_attestation->delete_state(imv_attestation, connection_id);
167 case TNC_CONNECTION_STATE_HANDSHAKE:
168 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
169 {
170 return TNC_RESULT_FATAL;
171 }
172 state->change_state(state, new_state);
173 attestation_state = (imv_attestation_state_t*)state;
174
175 /**
176 * Get the files to measure for
177 * PTS Request File Measurement attribute
178 */
179
180 file_list = linked_list_create();
181 directory_list = linked_list_create();
182
183 files = lib->settings->get_str(lib->settings,
184 "libimcv.plugins.imc-attestation.files", "none");
185 enumerator = enumerator_create_token(files, " ", " ");
186 while (enumerator->enumerate(enumerator, &token))
187 {
188 entry = malloc_thing(measurement_req_entry_t);
189 token = strdup(token);
190 entry->path = token;
191 entry->request_id = request_id_counter;
192 file_list->insert_last(file_list, entry);
193 free(token);
194 request_id_counter ++;
195 }
196
197 /**
198 * Get the directories to measure for
199 * PTS Request File Measurement attribute
200 */
201
202 directories = lib->settings->get_str(lib->settings,
203 "libimcv.plugins.imc-attestation.directories", "none");
204 enumerator = enumerator_create_token(directories, " ", " ");
205 while (enumerator->enumerate(enumerator, &token))
206 {
207 entry = malloc_thing(measurement_req_entry_t);
208 token = strdup(token);
209 entry->path = token;
210 entry->request_id = request_id_counter;
211 directory_list->insert_last(directory_list, entry);
212 free(token);
213 request_id_counter ++;
214 }
215 enumerator->destroy(enumerator);
216 return TNC_RESULT_SUCCESS;
217 default:
218 return imv_attestation->change_state(imv_attestation, connection_id,
219 new_state, NULL);
220 }
221 }
222
223 static TNC_Result send_message(TNC_ConnectionID connection_id)
224 {
225 pa_tnc_msg_t *msg;
226 pa_tnc_attr_t *attr;
227 TNC_Result result;
228 imv_state_t *state;
229 imv_attestation_state_t *attestation_state;
230 imv_attestation_handshake_state_t handshake_state;
231
232 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
233 {
234 return TNC_RESULT_FATAL;
235 }
236
237 attestation_state = (imv_attestation_state_t*)state;
238 handshake_state = attestation_state->get_handshake_state(attestation_state);
239
240 /* Switch on the attribute type IMV has received */
241 switch (handshake_state)
242 {
243 case IMV_ATTESTATION_STATE_INIT:
244 {
245 pts_proto_caps_flag_t flags;
246
247 /* Send Request Protocol Capabilities attribute */
248 flags = PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_V | PTS_PROTO_CAPS_C;
249 attr = tcg_pts_attr_proto_caps_create(flags, TRUE);
250 break;
251 }
252 case IMV_ATTESTATION_STATE_PROTO_CAP:
253 {
254 /* Send Measurement Algorithms attribute */
255 attr = tcg_pts_attr_meas_algo_create(supported_algorithms, FALSE);
256 break;
257 }
258 case IMV_ATTESTATION_STATE_MEAS_ALGO:
259 {
260 /* Send Get TPM Version Information attribute */
261 attr = tcg_pts_attr_get_tpm_version_info_create();
262 break;
263 }
264 case IMV_ATTESTATION_STATE_TPM_INFO:
265 {
266 /* Send Get AIK attribute */
267 /* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
268 //attr = tcg_pts_attr_get_aik_create();
269 //break;
270 }
271 case IMV_ATTESTATION_STATE_AIK:
272 {
273 /* Send Request File Measurement attribute */
274 enumerator_t *enumerator;
275 measurement_req_entry_t *entry;
276 char *path;
277 u_int16_t request_id;
278 u_int32_t delimiter = SOLIDUS_UTF;
279
280 msg = pa_tnc_msg_create();
281
282 /**
283 * Add files to measure to PTS Request File Measurement attribute
284 */
285 enumerator = enumerator_create_single(file_list, NULL);
286 while (enumerator->enumerate(enumerator, &entry))
287 {
288 attr = tcg_pts_attr_req_file_meas_create(false,
289 entry->request_id, delimiter,
290 chunk_create(entry->path, strlen(entry->path)));
291 attr->set_noskip_flag(attr, TRUE);
292 msg->add_attribute(msg, attr);
293 }
294 /** Add directories to measure to PTS Request File Measurement attribute
295 */
296 enumerator = enumerator_create_single(directory_list, NULL);
297 while (enumerator->enumerate(enumerator, &entry))
298 {
299 attr = tcg_pts_attr_req_file_meas_create(true,
300 entry->request_id, delimiter,
301 chunk_create(entry->path, strlen(entry->path)));
302 attr->set_noskip_flag(attr, TRUE);
303 msg->add_attribute(msg, attr);
304 }
305 enumerator->destroy(enumerator);
306 goto end;
307 }
308 case IMV_ATTESTATION_STATE_SIMPLE_COMP_EVID:
309 case IMV_ATTESTATION_STATE_SIMPLE_EVID_FINAL:
310 case IMV_ATTESTATION_STATE_FILE_METADATA:
311 case IMV_ATTESTATION_STATE_FILE_MEAS:
312 case IMV_ATTESTATION_STATE_IML:
313 DBG1(DBG_IMV, "Attestation IMV has nothing to send: \"%s\"", handshake_state);
314 return TNC_RESULT_FATAL;
315 default:
316 DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"", handshake_state);
317 return TNC_RESULT_FATAL;
318 }
319
320 msg = pa_tnc_msg_create();
321 attr->set_noskip_flag(attr, TRUE);
322 msg->add_attribute(msg, attr);
323
324 end:
325 msg->build(msg);
326 result = imv_attestation->send_message(imv_attestation, connection_id,
327 msg->get_encoding(msg));
328 msg->destroy(msg);
329
330 return result;
331 }
332
333 /**
334 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
335 */
336 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
337 TNC_ConnectionID connection_id,
338 TNC_BufferReference msg,
339 TNC_UInt32 msg_len,
340 TNC_MessageType msg_type)
341 {
342 pa_tnc_msg_t *pa_tnc_msg;
343 pa_tnc_attr_t *attr;
344 imv_state_t *state;
345 imv_attestation_state_t *attestation_state;
346 enumerator_t *enumerator;
347 TNC_Result result;
348 bool fatal_error = FALSE;
349
350 if (!imv_attestation)
351 {
352 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
353 return TNC_RESULT_NOT_INITIALIZED;
354 }
355
356 /* get current IMV state */
357 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
358 {
359 return TNC_RESULT_FATAL;
360 }
361
362 /* parse received PA-TNC message and automatically handle any errors */
363 result = imv_attestation->receive_message(imv_attestation, connection_id,
364 chunk_create(msg, msg_len), msg_type,
365 &pa_tnc_msg);
366
367 /* no parsed PA-TNC attributes available if an error occurred */
368 if (!pa_tnc_msg)
369 {
370 return result;
371 }
372
373 /* analyze PA-TNC attributes */
374 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
375 while (enumerator->enumerate(enumerator, &attr))
376 {
377 if (attr->get_vendor_id(attr) == PEN_IETF &&
378 attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
379 {
380 ietf_attr_pa_tnc_error_t *error_attr;
381 pa_tnc_error_code_t error_code;
382 chunk_t msg_info, attr_info;
383 u_int32_t offset;
384
385 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
386 error_code = error_attr->get_error_code(error_attr);
387 msg_info = error_attr->get_msg_info(error_attr);
388
389 DBG1(DBG_IMV, "received PA-TNC error '%N' concerning message %#B",
390 pa_tnc_error_code_names, error_code, &msg_info);
391 switch (error_code)
392 {
393 case PA_ERROR_INVALID_PARAMETER:
394 offset = error_attr->get_offset(error_attr);
395 DBG1(DBG_IMV, " occurred at offset of %u bytes", offset);
396 break;
397 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
398 attr_info = error_attr->get_attr_info(error_attr);
399 DBG1(DBG_IMV, " unsupported attribute %#B", &attr_info);
400 break;
401 default:
402 break;
403 }
404 fatal_error = TRUE;
405 }
406 else if (attr->get_vendor_id(attr) == PEN_TCG)
407 {
408 /**
409 * Handle TCG PTS attributes
410 */
411
412 /* get current IMC state */
413 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
414 {
415 return TNC_RESULT_FATAL;
416 }
417 attestation_state = (imv_attestation_state_t*)state;
418
419 switch(attr->get_type(attr))
420 {
421 case TCG_PTS_PROTO_CAPS:
422 {
423 tcg_pts_attr_proto_caps_t *attr_proto_caps;
424 pts_proto_caps_flag_t proto_caps;
425
426 attr_proto_caps = (tcg_pts_attr_proto_caps_t*)attr;
427 proto_caps = attr_proto_caps->get_flags(attr_proto_caps);
428 /* TODO: What to do with the protocol capabilities from imc */
429 attestation_state->set_handshake_state(attestation_state,
430 IMV_ATTESTATION_STATE_PROTO_CAP);
431 break;
432 }
433 case TCG_PTS_MEAS_ALGO_SELECTION:
434 {
435 tcg_pts_attr_meas_algo_t *attr_meas_algo_selection;
436 pts_meas_algorithms_t selected_algorithm;
437
438 attr_meas_algo_selection = (tcg_pts_attr_meas_algo_t*)attr;
439 selected_algorithm = attr_meas_algo_selection->get_algorithms(attr_meas_algo_selection);
440 /* TODO: What to do with the selected algorithm from imc */
441
442 attestation_state->set_handshake_state(attestation_state,
443 IMV_ATTESTATION_STATE_MEAS_ALGO);
444 break;
445 }
446 case TCG_PTS_TPM_VERSION_INFO:
447 {
448 tcg_pts_attr_tpm_version_info_t *attr_tpm_version_info;
449 chunk_t tpm_version_info;
450 TSS_RESULT uiResult;
451 TPM_CAP_VERSION_INFO versionInfo;
452 UINT64 offset = 0;
453
454 attr_tpm_version_info = (tcg_pts_attr_tpm_version_info_t*)attr;
455 tpm_version_info = attr_tpm_version_info->get_tpm_version_info(attr_tpm_version_info);
456
457 uiResult = Trspi_UnloadBlob_CAP_VERSION_INFO(&offset, tpm_version_info.ptr, &versionInfo);
458 if (uiResult != TSS_SUCCESS) {
459 DBG1(DBG_IMV, "Error 0x%x on Trspi_UnloadBlob_CAP_VERSION_INFO\n", uiResult);
460 return TNC_RESULT_FATAL;
461 }
462
463 DBG3(DBG_IMV, " TPM 1.2 Version Info:\n");
464 DBG3(DBG_IMV, " Chip Version: %hhu.%hhu.%hhu.%hhu\n",
465 versionInfo.version.major, versionInfo.version.minor,
466 versionInfo.version.revMajor, versionInfo.version.revMinor);
467 DBG3(DBG_IMV, " Spec Level: %hu\n", versionInfo.specLevel);
468 DBG3(DBG_IMV, " Errata Revision: %hhu\n", versionInfo.errataRev);
469 DBG3(DBG_IMV, " TPM Vendor ID: %c%c%c%c\n",
470 versionInfo.tpmVendorID[0], versionInfo.tpmVendorID[1],
471 versionInfo.tpmVendorID[2], versionInfo.tpmVendorID[3]);
472
473 attestation_state->set_handshake_state(attestation_state,
474 IMV_ATTESTATION_STATE_TPM_INFO);
475 break;
476 }
477 case TCG_PTS_AIK:
478 {
479 /* TODO: Save the AIK key and certificate */
480 attestation_state->set_handshake_state(attestation_state,
481 IMV_ATTESTATION_STATE_AIK);
482 break;
483 }
484
485 /* PTS-based Attestation Evidence */
486 case TCG_PTS_SIMPLE_COMP_EVID:
487 break;
488 case TCG_PTS_SIMPLE_EVID_FINAL:
489 break;
490 case TCG_PTS_FILE_MEAS:
491 {
492 tcg_pts_attr_file_meas_t *attr_file_meas;
493 u_int64_t num_of_files;
494 u_int16_t request_id;
495 u_int16_t meas_len;
496
497 attr_file_meas = (tcg_pts_attr_file_meas_t*)attr;
498 num_of_files = attr_file_meas->get_number_of_files(attr_file_meas);
499 request_id = attr_file_meas->get_request_id(attr_file_meas);
500 meas_len = attr_file_meas->get_meas_len(attr_file_meas);
501
502 /* TODO: Start working here */
503
504 attestation_state->set_handshake_state(attestation_state,
505 IMV_ATTESTATION_STATE_FILE_MEAS);
506 break;
507 }
508
509 /* TODO: Not implemented yet */
510 case TCG_PTS_DH_NONCE_PARAMS_RESP:
511 case TCG_PTS_UNIX_FILE_META:
512 case TCG_PTS_INTEG_MEAS_LOG:
513 /* Attributes using XML */
514 case TCG_PTS_TEMPL_REF_MANI_SET_META:
515 case TCG_PTS_VERIFICATION_RESULT:
516 case TCG_PTS_INTEG_REPORT:
517 /* On Windows only*/
518 case TCG_PTS_WIN_FILE_META:
519 case TCG_PTS_REGISTRY_VALUE:
520 /* Received on IMC side only*/
521 case TCG_PTS_REQ_PROTO_CAPS:
522 case TCG_PTS_DH_NONCE_PARAMS_REQ:
523 case TCG_PTS_DH_NONCE_FINISH:
524 case TCG_PTS_MEAS_ALGO:
525 case TCG_PTS_GET_TPM_VERSION_INFO:
526 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META:
527 case TCG_PTS_UPDATE_TEMPL_REF_MANI:
528 case TCG_PTS_GET_AIK:
529 case TCG_PTS_REQ_FUNCT_COMP_EVID:
530 case TCG_PTS_GEN_ATTEST_EVID:
531 case TCG_PTS_REQ_FILE_META:
532 case TCG_PTS_REQ_FILE_MEAS:
533 case TCG_PTS_REQ_INTEG_MEAS_LOG:
534 default:
535 DBG1(DBG_IMV, "received unsupported attribute '%N'",
536 tcg_attr_names, attr->get_type(attr));
537 break;
538 }
539 }
540 }
541 enumerator->destroy(enumerator);
542 pa_tnc_msg->destroy(pa_tnc_msg);
543
544 if (fatal_error)
545 {
546 state->set_recommendation(state,
547 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
548 TNC_IMV_EVALUATION_RESULT_ERROR);
549 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
550 }
551
552 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
553 }
554
555 /**
556 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
557 */
558 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
559 TNC_ConnectionID connection_id)
560 {
561 if (!imv_attestation)
562 {
563 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
564 return TNC_RESULT_NOT_INITIALIZED;
565 }
566 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
567 }
568
569 /**
570 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
571 */
572 TNC_Result TNC_IMV_BatchEnding(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 TNC_RESULT_SUCCESS;
581 }
582
583 /**
584 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
585 */
586 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
587 {
588 if (!imv_attestation)
589 {
590 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
591 return TNC_RESULT_NOT_INITIALIZED;
592 }
593 imv_attestation->destroy(imv_attestation);
594 imv_attestation = NULL;
595
596 return TNC_RESULT_SUCCESS;
597 }
598
599 /**
600 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
601 */
602 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
603 TNC_TNCS_BindFunctionPointer bind_function)
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 return imv_attestation->bind_functions(imv_attestation, bind_function);
611 }