Proto Caps and Meas Algorithms
[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/tcg_attr.h>
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 /* IMV definitions */
44
45 static const char imv_name[] = "Attestation";
46
47 #define IMV_VENDOR_ID PEN_TCG
48 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
49
50 /**
51 * UTF-8 encoding of the character used to delimiter the filename
52 */
53 #define SOLIDUS_UTF 0x002F
54 #define REVERSE_SOLIDUS_UTF 0x005C
55
56 static imv_agent_t *imv_attestation;
57
58 /**
59 * Supported PTS measurement algorithms
60 */
61 static pts_meas_algorithms_t supported_algorithms = 0;
62
63 /**
64 * List of files and directories to measure
65 */
66 static linked_list_t *file_list, *directory_list;
67
68 /**
69 * Monotonic increasing number for Request File Measurement attribute
70 */
71 static u_int16_t request_id_counter = 0;
72
73 /* TODO: Move the struct to some header file? Duplicate with imc_attestation*/
74 /**
75 * Struct to hold file or directory name with the request ID for Request File Measurement attribute
76 */
77 typedef struct measurement_req_entry_t measurement_req_entry_t;
78
79 struct measurement_req_entry_t {
80 char *path;
81 u_int16_t request_id;
82 };
83
84 /**
85 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
86 */
87 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
88 TNC_Version min_version,
89 TNC_Version max_version,
90 TNC_Version *actual_version)
91 {
92 char *hash_alg;
93
94 if (imv_attestation)
95 {
96 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
97 return TNC_RESULT_ALREADY_INITIALIZED;
98 }
99 imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
100 imv_id, actual_version);
101 if (!imv_attestation || !pts_meas_probe_algorithms(&supported_algorithms))
102 {
103 return TNC_RESULT_FATAL;
104 }
105 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
106 {
107 DBG1(DBG_IMV, "no common IF-IMV version");
108 return TNC_RESULT_NO_COMMON_VERSION;
109 }
110
111 /**
112 * Specify supported PTS measurement algorithms
113 *
114 * sha1 : PTS_MEAS_ALGO_SHA1
115 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
116 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
117 *
118 * we expect the PTS-IMC to select the strongest supported algorithm
119 */
120 hash_alg = lib->settings->get_str(lib->settings,
121 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
122 if (!strcaseeq(hash_alg, "sha384") && !strcaseeq(hash_alg, "sha2_384"))
123 {
124 /* remove SHA384 algorithm */
125 supported_algorithms &= ~PTS_MEAS_ALGO_SHA384;
126 }
127 if (strcaseeq(hash_alg, "sha1"))
128 {
129 /* remove SHA256 algorithm */
130 supported_algorithms &= ~PTS_MEAS_ALGO_SHA256;
131 }
132
133 return TNC_RESULT_SUCCESS;
134 }
135
136 /**
137 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
138 */
139 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
140 TNC_ConnectionID connection_id,
141 TNC_ConnectionState new_state)
142 {
143 imv_state_t *state;
144 imv_attestation_state_t *attestation_state;
145 enumerator_t *enumerator;
146 char *files;
147 char *directories;
148 measurement_req_entry_t *entry;
149 char *token;
150 TNC_Result result;
151
152 if (!imv_attestation)
153 {
154 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
155 return TNC_RESULT_NOT_INITIALIZED;
156 }
157 switch (new_state)
158 {
159 case TNC_CONNECTION_STATE_CREATE:
160 state = imv_attestation_state_create(connection_id);
161 return imv_attestation->create_state(imv_attestation, state);
162 case TNC_CONNECTION_STATE_DELETE:
163 return imv_attestation->delete_state(imv_attestation, connection_id);
164 case TNC_CONNECTION_STATE_HANDSHAKE:
165 result = imv_attestation->change_state(imv_attestation, connection_id,
166 new_state, &state);
167 if (result != TNC_RESULT_SUCCESS)
168 {
169 return result;
170 }
171 attestation_state = (imv_attestation_state_t*)state;
172
173 /**
174 * Get the files to measure for
175 * PTS Request File Measurement attribute
176 */
177
178 file_list = linked_list_create();
179 directory_list = linked_list_create();
180
181 files = lib->settings->get_str(lib->settings,
182 "libimcv.plugins.imv-attestation.files", "none");
183 enumerator = enumerator_create_token(files, " ", " ");
184 while (enumerator->enumerate(enumerator, &token))
185 {
186 entry = malloc_thing(measurement_req_entry_t);
187 token = strdup(token);
188 entry->path = token;
189 entry->request_id = request_id_counter;
190 file_list->insert_last(file_list, entry);
191 DBG3(DBG_IMV, "File to measure:%s, with request id:%d",token, entry->request_id);
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.imv-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 DBG3(DBG_IMV, "Directory to measure:%s, with request id:%d",token, entry->request_id);
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 TNC_Result result;
227 pts_t *pts;
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 attestation_state = (imv_attestation_state_t*)state;
237 handshake_state = attestation_state->get_handshake_state(attestation_state);
238 pts = attestation_state->get_pts(attestation_state);
239
240 msg = pa_tnc_msg_create();
241
242
243 /* Switch on the attribute type IMV has received */
244 switch (handshake_state)
245 {
246 case IMV_ATTESTATION_STATE_INIT:
247 {
248 pa_tnc_attr_t *attr_req_proto_cap, *attr_meas_algo;
249 pts_proto_caps_flag_t flags;
250
251 /* Send Request Protocol Capabilities attribute */
252 flags = pts->get_proto_caps(pts);
253 attr_req_proto_cap = tcg_pts_attr_proto_caps_create(flags, TRUE);
254 attr_req_proto_cap->set_noskip_flag(attr_req_proto_cap, TRUE);
255 msg->add_attribute(msg, attr_req_proto_cap);
256
257 /* Send Measurement Algorithms attribute */
258 attr_meas_algo = tcg_pts_attr_meas_algo_create(supported_algorithms, FALSE);
259 attr_meas_algo->set_noskip_flag(attr_meas_algo, TRUE);
260 msg->add_attribute(msg, attr_meas_algo);
261 break;
262 }
263
264 case IMV_ATTESTATION_STATE_MEAS:
265 {
266 pa_tnc_attr_t *attr_req_file_meas;
267 enumerator_t *enumerator;
268 measurement_req_entry_t *entry;
269 pts_meas_algorithms_t communicated_caps;
270 u_int32_t delimiter = SOLIDUS_UTF;
271
272 /* Send Get TPM Version Information attribute */
273 communicated_caps = pts->get_proto_caps(pts);
274 if(communicated_caps & PTS_PROTO_CAPS_T)
275 {
276 pa_tnc_attr_t *attr_get_tpm_version, *attr_get_aik;
277
278 attr_get_tpm_version = tcg_pts_attr_get_tpm_version_info_create();
279 attr_get_tpm_version->set_noskip_flag(attr_get_tpm_version, TRUE);
280 msg->add_attribute(msg, attr_get_tpm_version);
281
282 /* Send Get AIK attribute */
283 /* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
284 //attr_get_aik = tcg_pts_attr_get_aik_create();
285 //attr_get_aik->set_noskip_flag(attr_get_aik, TRUE);
286 //msg->add_attribute(msg, attr_get_aik);
287 }
288
289 /* Send Request File Measurement attribute */
290 /**
291 * Add files to measure to PTS Request File Measurement attribute
292 */
293 enumerator = file_list->create_enumerator(file_list);
294 while (enumerator->enumerate(enumerator, &entry))
295 {
296 attr_req_file_meas = tcg_pts_attr_req_file_meas_create(false,
297 entry->request_id, delimiter,
298 chunk_create(entry->path, strlen(entry->path)));
299 attr_req_file_meas->set_noskip_flag(attr_req_file_meas, TRUE);
300 msg->add_attribute(msg, attr_req_file_meas);
301 }
302 /** Add directories to measure to PTS Request File Measurement attribute
303 */
304 enumerator = file_list->create_enumerator(directory_list);
305 while (enumerator->enumerate(enumerator, &entry))
306 {
307 attr_req_file_meas = tcg_pts_attr_req_file_meas_create(true,
308 entry->request_id, delimiter,
309 chunk_create(entry->path, strlen(entry->path)));
310 attr_req_file_meas->set_noskip_flag(attr_req_file_meas, TRUE);
311 msg->add_attribute(msg, attr_req_file_meas);
312 }
313
314 enumerator->destroy(enumerator);
315 file_list->destroy(file_list);
316 directory_list->destroy(directory_list);
317 break;
318 }
319 case IMV_ATTESTATION_STATE_COMP_EVID:
320 case IMV_ATTESTATION_STATE_IML:
321 DBG1(DBG_IMV, "Attestation IMV has nothing to send: \"%s\"", handshake_state);
322 return TNC_RESULT_FATAL;
323 default:
324 DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"", handshake_state);
325 return TNC_RESULT_FATAL;
326 }
327
328 msg->build(msg);
329 result = imv_attestation->send_message(imv_attestation, connection_id,
330 msg->get_encoding(msg));
331 msg->destroy(msg);
332
333 return result;
334 }
335
336 /**
337 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
338 */
339 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
340 TNC_ConnectionID connection_id,
341 TNC_BufferReference msg,
342 TNC_UInt32 msg_len,
343 TNC_MessageType msg_type)
344 {
345 pa_tnc_msg_t *pa_tnc_msg;
346 pa_tnc_attr_t *attr;
347 imv_state_t *state;
348 imv_attestation_state_t *attestation_state;
349 pts_t *pts;
350 enumerator_t *enumerator;
351 TNC_Result result;
352 bool fatal_error = FALSE;
353
354 if (!imv_attestation)
355 {
356 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
357 return TNC_RESULT_NOT_INITIALIZED;
358 }
359
360 /* get current IMV state */
361 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
362 {
363 return TNC_RESULT_FATAL;
364 }
365 attestation_state = (imv_attestation_state_t*)state;
366 pts = attestation_state->get_pts(attestation_state);
367
368 /* parse received PA-TNC message and automatically handle any errors */
369 result = imv_attestation->receive_message(imv_attestation, connection_id,
370 chunk_create(msg, msg_len), msg_type,
371 &pa_tnc_msg);
372
373 /* no parsed PA-TNC attributes available if an error occurred */
374 if (!pa_tnc_msg)
375 {
376 return result;
377 }
378
379 /* analyze PA-TNC attributes */
380 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
381 while (enumerator->enumerate(enumerator, &attr))
382 {
383 if (attr->get_vendor_id(attr) == PEN_IETF &&
384 attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
385 {
386 ietf_attr_pa_tnc_error_t *error_attr;
387 pa_tnc_error_code_t error_code;
388 chunk_t msg_info, attr_info;
389 u_int32_t offset;
390
391 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
392 error_code = error_attr->get_error_code(error_attr);
393 msg_info = error_attr->get_msg_info(error_attr);
394
395 DBG1(DBG_IMV, "received PA-TNC error '%N' concerning message %#B",
396 pa_tnc_error_code_names, error_code, &msg_info);
397 switch (error_code)
398 {
399 case PA_ERROR_INVALID_PARAMETER:
400 offset = error_attr->get_offset(error_attr);
401 DBG1(DBG_IMV, " occurred at offset of %u bytes", offset);
402 break;
403 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
404 attr_info = error_attr->get_attr_info(error_attr);
405 DBG1(DBG_IMV, " unsupported attribute %#B", &attr_info);
406 break;
407 default:
408 break;
409 }
410 fatal_error = TRUE;
411 }
412 else if (attr->get_vendor_id(attr) == PEN_TCG)
413 {
414 switch(attr->get_type(attr))
415 {
416 case TCG_PTS_PROTO_CAPS:
417 {
418 tcg_pts_attr_proto_caps_t *attr_cast;
419 pts_proto_caps_flag_t flags;
420
421 attr_cast = (tcg_pts_attr_proto_caps_t*)attr;
422 flags = attr_cast->get_flags(attr_cast);
423 pts->set_proto_caps(pts, flags);
424
425 attestation_state->set_handshake_state(attestation_state,
426 IMV_ATTESTATION_STATE_MEAS);
427 break;
428 }
429 case TCG_PTS_MEAS_ALGO_SELECTION:
430 {
431 tcg_pts_attr_meas_algo_t *attr_cast;
432 pts_meas_algorithms_t selected_algorithm;
433
434 attr_cast = (tcg_pts_attr_meas_algo_t*)attr;
435 selected_algorithm = attr_cast->get_algorithms(attr_cast);
436 pts->set_meas_algorithm(pts, selected_algorithm);
437
438 attestation_state->set_handshake_state(attestation_state,
439 IMV_ATTESTATION_STATE_MEAS);
440 break;
441 }
442 case TCG_PTS_TPM_VERSION_INFO:
443 {
444 tcg_pts_attr_tpm_version_info_t *attr_cast;
445 chunk_t tpm_version_info;
446
447 attr_cast = (tcg_pts_attr_tpm_version_info_t*)attr;
448 tpm_version_info = attr_cast->get_tpm_version_info(attr_cast);
449 pts->set_tpm_version_info(pts, tpm_version_info);
450
451 attestation_state->set_handshake_state(attestation_state,
452 IMV_ATTESTATION_STATE_END);
453 break;
454 }
455 case TCG_PTS_AIK:
456 {
457 /* TODO: Save the AIK key and certificate */
458 attestation_state->set_handshake_state(attestation_state,
459 IMV_ATTESTATION_STATE_END);
460 break;
461 }
462
463 /* PTS-based Attestation Evidence */
464 case TCG_PTS_SIMPLE_COMP_EVID:
465 break;
466 case TCG_PTS_SIMPLE_EVID_FINAL:
467 break;
468 case TCG_PTS_FILE_MEAS:
469 {
470 tcg_pts_attr_file_meas_t *attr_cast;
471 u_int64_t num_of_files;
472 u_int16_t request_id;
473 u_int16_t meas_len;
474
475 attr_cast = (tcg_pts_attr_file_meas_t*)attr;
476 num_of_files = attr_cast->get_number_of_files(attr_cast);
477 request_id = attr_cast->get_request_id(attr_cast);
478 meas_len = attr_cast->get_meas_len(attr_cast);
479
480 /* TODO: Start working here */
481
482 attestation_state->set_handshake_state(attestation_state,
483 IMV_ATTESTATION_STATE_END);
484 break;
485 }
486
487 /* TODO: Not implemented yet */
488 case TCG_PTS_DH_NONCE_PARAMS_RESP:
489 case TCG_PTS_UNIX_FILE_META:
490 case TCG_PTS_INTEG_MEAS_LOG:
491 /* Attributes using XML */
492 case TCG_PTS_TEMPL_REF_MANI_SET_META:
493 case TCG_PTS_VERIFICATION_RESULT:
494 case TCG_PTS_INTEG_REPORT:
495 /* On Windows only*/
496 case TCG_PTS_WIN_FILE_META:
497 case TCG_PTS_REGISTRY_VALUE:
498 /* Received on IMC side only*/
499 case TCG_PTS_REQ_PROTO_CAPS:
500 case TCG_PTS_DH_NONCE_PARAMS_REQ:
501 case TCG_PTS_DH_NONCE_FINISH:
502 case TCG_PTS_MEAS_ALGO:
503 case TCG_PTS_GET_TPM_VERSION_INFO:
504 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META:
505 case TCG_PTS_UPDATE_TEMPL_REF_MANI:
506 case TCG_PTS_GET_AIK:
507 case TCG_PTS_REQ_FUNCT_COMP_EVID:
508 case TCG_PTS_GEN_ATTEST_EVID:
509 case TCG_PTS_REQ_FILE_META:
510 case TCG_PTS_REQ_FILE_MEAS:
511 case TCG_PTS_REQ_INTEG_MEAS_LOG:
512 default:
513 DBG1(DBG_IMV, "received unsupported attribute '%N'",
514 tcg_attr_names, attr->get_type(attr));
515 break;
516 }
517 }
518 }
519 enumerator->destroy(enumerator);
520 pa_tnc_msg->destroy(pa_tnc_msg);
521
522 if (fatal_error)
523 {
524 state->set_recommendation(state,
525 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
526 TNC_IMV_EVALUATION_RESULT_ERROR);
527 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
528 }
529 return send_message(connection_id);
530 }
531
532 /**
533 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
534 */
535 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
536 TNC_ConnectionID connection_id)
537 {
538 if (!imv_attestation)
539 {
540 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
541 return TNC_RESULT_NOT_INITIALIZED;
542 }
543 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
544 }
545
546 /**
547 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
548 */
549 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
550 TNC_ConnectionID connection_id)
551 {
552 imv_state_t *state;
553 imv_attestation_state_t *attestation_state;
554
555 if (!imv_attestation)
556 {
557 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
558 return TNC_RESULT_NOT_INITIALIZED;
559 }
560 /* get current IMV state */
561 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
562 {
563 return TNC_RESULT_FATAL;
564 }
565 attestation_state = (imv_attestation_state_t*)state;
566
567 /* Check if IMV has to initiate the PA-TNC exchange */
568 if (attestation_state->get_handshake_state(attestation_state) ==
569 IMV_ATTESTATION_STATE_INIT)
570 {
571 return send_message(connection_id);
572 }
573 return TNC_RESULT_SUCCESS;
574 }
575
576 /**
577 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
578 */
579 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
580 {
581 if (!imv_attestation)
582 {
583 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
584 return TNC_RESULT_NOT_INITIALIZED;
585 }
586 imv_attestation->destroy(imv_attestation);
587 imv_attestation = NULL;
588
589 return TNC_RESULT_SUCCESS;
590 }
591
592 /**
593 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
594 */
595 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
596 TNC_TNCS_BindFunctionPointer bind_function)
597 {
598 if (!imv_attestation)
599 {
600 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
601 return TNC_RESULT_NOT_INITIALIZED;
602 }
603 return imv_attestation->bind_functions(imv_attestation, bind_function);
604 }