Implemented send_message of File Measurement PTS attribute on imc_attestation
[strongswan.git] / src / libimcv / plugins / imc_attestation / imc_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 "imc_attestation_state.h"
17
18 #include <imc/imc_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_pts_attr_proto_caps.h>
24 #include <tcg/tcg_pts_attr_meas_algo.h>
25 #include <tcg/tcg_pts_attr_tpm_version_info.h>
26 #include <tcg/tcg_pts_attr_aik.h>
27 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
28 #include <tcg/tcg_pts_attr_simple_evid_final.h>
29 #include <tcg/tcg_pts_attr_file_meas.h>
30
31 #include <tncif_pa_subtypes.h>
32
33 #include <pen/pen.h>
34 #include <debug.h>
35 #include <utils/linked_list.h>
36 #include <crypto/hashers/hasher.h>
37 #include <dirent.h>
38 #include <errno.h>
39
40 #include <trousers/tss.h>
41 #include <trousers/trousers.h>
42
43
44 /* IMC definitions */
45
46 static const char imc_name[] = "Attestation";
47
48 #define IMC_VENDOR_ID PEN_TCG
49 #define IMC_SUBTYPE PA_SUBTYPE_TCG_PTS
50 #define IMC_ATTESTATION_BUF_SIZE 32768
51
52 static imc_agent_t *imc_attestation;
53
54 /**
55 * Supported PTS measurement algorithms
56 */
57 static pts_meas_algorithms_t supported_algorithms = 0;
58
59 /**
60 * Selected PTS measurement algorithm after attribute exchange
61 */
62 static pts_meas_algorithms_t selected_algorithm = PTS_MEAS_ALGO_SHA256;
63
64 /**
65 * List of files and directories to measure
66 */
67 static linked_list_t *file_list, *directory_list;
68
69 /**
70 * List of file measurements
71 */
72 static linked_list_t *file_measurements;
73
74 /* TODO: Move the struct to some header file? Duplicate with imv_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-IMC Specification 1.2
87 */
88 TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id,
89 TNC_Version min_version,
90 TNC_Version max_version,
91 TNC_Version *actual_version)
92 {
93 if (imc_attestation)
94 {
95 DBG1(DBG_IMC, "IMC \"%s\" has already been initialized", imc_name);
96 return TNC_RESULT_ALREADY_INITIALIZED;
97 }
98 imc_attestation = imc_agent_create(imc_name, IMC_VENDOR_ID, IMC_SUBTYPE,
99 imc_id, actual_version);
100 if (!imc_attestation ||
101 !tcg_pts_probe_meas_algorithms(&supported_algorithms))
102 {
103 return TNC_RESULT_FATAL;
104 }
105 if (min_version > TNC_IFIMC_VERSION_1 || max_version < TNC_IFIMC_VERSION_1)
106 {
107 DBG1(DBG_IMC, "no common IF-IMC version");
108 return TNC_RESULT_NO_COMMON_VERSION;
109 }
110 return TNC_RESULT_SUCCESS;
111 }
112
113 /**
114 * see section 3.7.2 of TCG TNC IF-IMC Specification 1.2
115 */
116 TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id,
117 TNC_ConnectionID connection_id,
118 TNC_ConnectionState new_state)
119 {
120 imc_state_t *state;
121 /* TODO: Not used so far */
122 //imc_attestation_state_t *attestation_state;
123
124 if (!imc_attestation)
125 {
126 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
127 return TNC_RESULT_NOT_INITIALIZED;
128 }
129 switch (new_state)
130 {
131 case TNC_CONNECTION_STATE_CREATE:
132 state = imc_attestation_state_create(connection_id);
133 return imc_attestation->create_state(imc_attestation, state);
134 case TNC_CONNECTION_STATE_DELETE:
135 return imc_attestation->delete_state(imc_attestation, connection_id);
136 case TNC_CONNECTION_STATE_HANDSHAKE:
137 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
138 case TNC_CONNECTION_STATE_ACCESS_NONE:
139 default:
140 return imc_attestation->change_state(imc_attestation, connection_id,
141 new_state, NULL);
142 }
143 }
144
145 /**
146 * Get the TPM Version Information
147 */
148 static TSS_RESULT get_tpm_version_info(BYTE *tpm_version_info)
149 {
150 TSS_HCONTEXT hContext;
151 TSS_HTPM hTPM;
152 TSS_RESULT uiResult;
153 UINT32 uiResultLen;
154 /* TODO: Needed for parsing version info on IMV side */
155 //TPM_CAP_VERSION_INFO versionInfo;
156 //UINT64 offset = 0;
157
158 uiResult = Tspi_Context_Create(&hContext);
159 if (uiResult != TSS_SUCCESS) {
160 DBG1(DBG_IMC,"Error 0x%x on Tspi_Context_Create\n", uiResult);
161 return uiResult;
162 }
163 uiResult = Tspi_Context_Connect(hContext, NULL);
164 if (uiResult != TSS_SUCCESS) {
165 DBG1(DBG_IMC,"Error 0x%x on Tspi_Context_Connect\n", uiResult);
166 return uiResult;
167 }
168 uiResult = Tspi_Context_GetTpmObject (hContext, &hTPM);
169 if (uiResult != TSS_SUCCESS) {
170 DBG1(DBG_IMC,"Error 0x%x on Tspi_Context_GetTpmObject\n", uiResult);
171 return uiResult;
172 }
173
174 uiResult = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL, &uiResultLen,
175 &tpm_version_info);
176 if (uiResult != TSS_SUCCESS) {
177 DBG1(DBG_IMC,"Error 0x%x on Tspi_TPM_GetCapability\n", uiResult);
178 return uiResult;
179 }
180 }
181
182 /**
183 * Get Hash Measurement of a file
184 */
185 static TNC_Result hash_file(char *path, char *out)
186 {
187 BYTE buffer[IMC_ATTESTATION_BUF_SIZE];
188 FILE *file;
189 int bytes_read;
190 hasher_t *hasher;
191 hash_algorithm_t hash_alg;
192
193 /* Create a hasher */
194 hash_alg = tcg_pts_meas_to_hash_algorithm(selected_algorithm);
195 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
196 if (!hasher)
197 {
198 DBG1(DBG_IMC, "hasher %N not available", hash_algorithm_names, hash_alg);
199 return TNC_RESULT_FATAL;
200 }
201
202 file = fopen(path, "rb");
203 if (!file)
204 {
205 DBG1(DBG_IMC,"file '%s' can not be opened", path);
206 hasher->destroy(hasher);
207 return TNC_RESULT_FATAL;
208 }
209 while (TRUE)
210 {
211 bytes_read = fread(buffer, 1, sizeof(buffer), file);
212 if (bytes_read > 0)
213 {
214 hasher->get_hash(hasher, chunk_create(buffer, bytes_read), NULL);
215 }
216 else
217 {
218 hasher->get_hash(hasher, chunk_empty, out);
219 break;
220 }
221 }
222 fclose(file);
223 hasher->destroy(hasher);
224
225 return TNC_RESULT_SUCCESS;
226 }
227
228 /**
229 * Get all the files in a directory
230 */
231 static TNC_Result hash_directory(char *path)
232 {
233 DIR *dir;
234 struct dirent *ent;
235 linked_list_t *file_measurements;
236 file_meas_entry_t *entry;
237
238 file_measurements = linked_list_create();
239 entry = malloc_thing(file_meas_entry_t);
240
241 dir = opendir(path);
242 if (dir == NULL)
243 {
244 DBG1(DBG_IMC, "opening directory '%s' failed: %s", path, strerror(errno));
245 return TNC_RESULT_FATAL;
246 }
247 while ((ent = readdir(dir)))
248 {
249 char *file_hash;
250
251 if(hash_file(ent->d_name,file_hash) != TNC_RESULT_SUCCESS)
252 {
253 DBG1(DBG_IMC, "Hashing the given file has failed");
254 return TNC_RESULT_FATAL;
255 }
256
257 entry->measurement = chunk_create(file_hash,strlen(file_hash));
258 entry->file_name_len = strlen(ent->d_name);
259 entry->file_name = chunk_create(ent->d_name,strlen(ent->d_name));
260
261 file_measurements->insert_last(file_measurements,entry);
262 }
263 closedir(dir);
264
265 return TNC_RESULT_SUCCESS;
266 }
267
268 static TNC_Result send_message(TNC_ConnectionID connection_id)
269 {
270 pa_tnc_msg_t *msg;
271 pa_tnc_attr_t *attr;
272 imc_state_t *state;
273 imc_attestation_state_t *attestation_state;
274 imc_attestation_handshake_state_t handshake_state;
275 TNC_Result result;
276
277 if (!imc_attestation->get_state(imc_attestation, connection_id, &state))
278 {
279 return TNC_RESULT_FATAL;
280 }
281 attestation_state = (imc_attestation_state_t*)state;
282 handshake_state = attestation_state->get_handshake_state(attestation_state);
283
284 /* Switch on the attribute type IMC has received */
285 switch (handshake_state)
286 {
287 case IMC_ATTESTATION_STATE_REQ_PROTO_CAP:
288 {
289 pts_proto_caps_flag_t flags;
290 flags = PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_V;
291 attr = tcg_pts_attr_proto_caps_create(flags, FALSE);
292 break;
293 }
294 case IMC_ATTESTATION_STATE_REQ_MEAS_ALGO:
295 {
296 attr = tcg_pts_attr_meas_algo_create(selected_algorithm, TRUE);
297 break;
298 }
299 case IMC_ATTESTATION_STATE_GET_TPM_INFO:
300 {
301 TSS_RESULT uiResult;
302 BYTE *tpm_version_info;
303
304 uiResult = get_tpm_version_info(tpm_version_info);
305 if (uiResult != TSS_SUCCESS) {
306 DBG1(DBG_IMC,"Error 0x%x on get_tpm_version_info\n", uiResult);
307 return uiResult;
308 }
309
310 attr = tcg_pts_attr_tpm_version_info_create(
311 chunk_create((char *)tpm_version_info,
312 strlen(tpm_version_info)));
313 break;
314 }
315 case IMC_ATTESTATION_STATE_REQ_FILE_MEAS:
316 {
317 measurement_req_entry_t *entry;
318 enumerator_t *enumerator;
319 tcg_pts_attr_file_meas_t *attr_file_meas;
320 u_int16_t meas_len = HASH_SIZE_SHA1;
321
322 if (selected_algorithm & PTS_MEAS_ALGO_SHA384)
323 {
324 meas_len = HASH_SIZE_SHA384;
325 }
326 else if(selected_algorithm & PTS_MEAS_ALGO_SHA256)
327 {
328 meas_len = HASH_SIZE_SHA512;
329 }
330
331 msg = pa_tnc_msg_create();
332
333 /**
334 * Hash the files and add them as attribute
335 */
336 enumerator = enumerator_create_single(file_list, NULL);
337 while (enumerator->enumerate(enumerator, &entry))
338 {
339 char * file_hash;
340
341 attr = tcg_pts_attr_file_meas_create(1,
342 entry->request_id, meas_len);
343 attr->set_noskip_flag(attr, TRUE);
344 attr_file_meas = (tcg_pts_attr_file_meas_t*)attr;
345
346 if(hash_file(entry->path,file_hash) != TNC_RESULT_SUCCESS)
347 {
348 DBG1(DBG_IMC, "Hashing the given file has failed");
349 return TNC_RESULT_FATAL;
350 }
351 attr_file_meas->add_file_meas(attr_file_meas,
352 chunk_create(file_hash,strlen(file_hash)),
353 chunk_create(entry->path,strlen(entry->path)));
354
355 msg->add_attribute(msg, attr);
356 }
357
358 /**
359 * Hash the files in each directory and add them as attribute
360 */
361 enumerator = enumerator_create_single(directory_list, NULL);
362 while (enumerator->enumerate(enumerator, &entry))
363 {
364 enumerator_t *meas_enumerator;
365 file_meas_entry_t *meas_entry;
366 u_int64_t num_of_files = 0 ;
367
368 if(hash_directory(entry->path) != TNC_RESULT_SUCCESS)
369 {
370 DBG1(DBG_IMC, "Hashing the files in a given directory has failed");
371 return TNC_RESULT_FATAL;
372 }
373
374 attr = tcg_pts_attr_file_meas_create(0,
375 entry->request_id, meas_len);
376 attr->set_noskip_flag(attr, TRUE);
377 attr_file_meas = (tcg_pts_attr_file_meas_t*)attr;
378
379 meas_enumerator = enumerator_create_single(file_measurements, NULL);
380 while (meas_enumerator->enumerate(meas_enumerator, &meas_entry))
381 {
382 num_of_files++;
383 attr_file_meas->add_file_meas(attr_file_meas,
384 meas_entry->measurement,
385 meas_entry->file_name);
386 }
387
388 attr_file_meas->set_number_of_files(attr_file_meas,
389 num_of_files);
390 msg->add_attribute(msg, attr);
391 }
392 enumerator->destroy(enumerator);
393 goto end;
394 }
395 case IMC_ATTESTATION_STATE_GET_AIK:
396 /* TODO: Implement AIK retrieve */
397 case IMC_ATTESTATION_STATE_REQ_FUNCT_COMP_EVID:
398 case IMC_ATTESTATION_STATE_GEN_ATTEST_EVID:
399 case IMC_ATTESTATION_STATE_REQ_FILE_METADATA:
400 case IMC_ATTESTATION_STATE_REQ_IML:
401 case IMC_ATTESTATION_STATE_INIT:
402 DBG1(DBG_IMC, "Attestation IMC has nothing to send: \"%s\"", handshake_state);
403 return TNC_RESULT_FATAL;
404 default:
405 DBG1(DBG_IMC, "Attestation IMC is in unknown state: \"%s\"", handshake_state);
406 return TNC_RESULT_FATAL;
407 }
408
409
410 attr->set_noskip_flag(attr, TRUE);
411 msg = pa_tnc_msg_create();
412 msg->add_attribute(msg, attr);
413
414 end:
415 msg->build(msg);
416 result = imc_attestation->send_message(imc_attestation, connection_id,
417 msg->get_encoding(msg));
418 msg->destroy(msg);
419
420 return result;
421 }
422
423 /**
424 * see section 3.7.3 of TCG TNC IF-IMC Specification 1.2
425 */
426 TNC_Result TNC_IMC_BeginHandshake(TNC_IMCID imc_id,
427 TNC_ConnectionID connection_id)
428 {
429 if (!imc_attestation)
430 {
431 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
432 return TNC_RESULT_NOT_INITIALIZED;
433 }
434 return send_message(connection_id);
435 }
436
437 /**
438 * see section 3.7.4 of TCG TNC IF-IMC Specification 1.2
439 */
440 TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id,
441 TNC_ConnectionID connection_id,
442 TNC_BufferReference msg,
443 TNC_UInt32 msg_len,
444 TNC_MessageType msg_type)
445 {
446 pa_tnc_msg_t *pa_tnc_msg;
447 pa_tnc_attr_t *attr;
448 enumerator_t *enumerator;
449 TNC_Result result;
450 bool fatal_error = FALSE;
451
452 if (!imc_attestation)
453 {
454 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
455 return TNC_RESULT_NOT_INITIALIZED;
456 }
457
458 /* parse received PA-TNC message and automatically handle any errors */
459 result = imc_attestation->receive_message(imc_attestation, connection_id,
460 chunk_create(msg, msg_len), msg_type,
461 &pa_tnc_msg);
462
463 /* no parsed PA-TNC attributes available if an error occurred */
464 if (!pa_tnc_msg)
465 {
466 return result;
467 }
468
469 /* analyze PA-TNC attributes */
470 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
471 while (enumerator->enumerate(enumerator, &attr))
472 {
473 if (attr->get_vendor_id(attr) == PEN_IETF &&
474 attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
475 {
476 ietf_attr_pa_tnc_error_t *error_attr;
477 pa_tnc_error_code_t error_code;
478 chunk_t msg_info, attr_info;
479 u_int32_t offset;
480
481 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
482 error_code = error_attr->get_error_code(error_attr);
483 msg_info = error_attr->get_msg_info(error_attr);
484
485 DBG1(DBG_IMC, "received PA-TNC error '%N' concerning message %#B",
486 pa_tnc_error_code_names, error_code, &msg_info);
487 switch (error_code)
488 {
489 case PA_ERROR_INVALID_PARAMETER:
490 offset = error_attr->get_offset(error_attr);
491 DBG1(DBG_IMC, " occurred at offset of %u bytes", offset);
492 break;
493 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
494 attr_info = error_attr->get_attr_info(error_attr);
495 DBG1(DBG_IMC, " unsupported attribute %#B", &attr_info);
496 break;
497 default:
498 break;
499 }
500 fatal_error = TRUE;
501 }
502 else if (attr->get_vendor_id(attr) == PEN_TCG)
503 {
504 /**
505 * Handle TCG PTS attributes
506 */
507 switch(attr->get_type(attr))
508 {
509 case TCG_PTS_REQ_PROTO_CAPS:
510 break;
511 case TCG_PTS_MEAS_ALGO:
512 /* TODO: save the selected algorithm here */
513 //selected_algorithm =
514 break;
515 case TCG_PTS_GET_TPM_VERSION_INFO:
516 break;
517 case TCG_PTS_GET_AIK:
518 break;
519
520 /* PTS-based Attestation Evidence */
521 case TCG_PTS_REQ_FUNCT_COMP_EVID:
522 break;
523 case TCG_PTS_GEN_ATTEST_EVID:
524 break;
525 case TCG_PTS_REQ_FILE_MEAS:
526 break;
527
528 /* TODO: Not implemented yet */
529 case TCG_PTS_DH_NONCE_PARAMS_REQ:
530 case TCG_PTS_DH_NONCE_FINISH:
531 case TCG_PTS_REQ_FILE_META:
532 case TCG_PTS_REQ_INTEG_MEAS_LOG:
533 /* Attributes using XML */
534 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META:
535 case TCG_PTS_UPDATE_TEMPL_REF_MANI:
536 /* On Windows only*/
537 case TCG_PTS_REQ_REGISTRY_VALUE:
538 /* Received on IMV side only*/
539 case TCG_PTS_PROTO_CAPS:
540 case TCG_PTS_DH_NONCE_PARAMS_RESP:
541 case TCG_PTS_MEAS_ALGO_SELECTION:
542 case TCG_PTS_TPM_VERSION_INFO:
543 case TCG_PTS_TEMPL_REF_MANI_SET_META:
544 case TCG_PTS_AIK:
545 case TCG_PTS_SIMPLE_COMP_EVID:
546 case TCG_PTS_SIMPLE_EVID_FINAL:
547 case TCG_PTS_VERIFICATION_RESULT:
548 case TCG_PTS_INTEG_REPORT:
549 case TCG_PTS_UNIX_FILE_META:
550 case TCG_PTS_FILE_MEAS:
551 case TCG_PTS_INTEG_MEAS_LOG:
552 default:
553 DBG1(DBG_IMC, "received unsupported attribute '%N'",
554 tcg_attr_names, attr->get_type(attr));
555 break;
556 }
557
558
559 }
560 }
561 enumerator->destroy(enumerator);
562 pa_tnc_msg->destroy(pa_tnc_msg);
563
564 /* if no error occurred then always return the same response */
565 return fatal_error ? TNC_RESULT_FATAL : send_message(connection_id);
566 }
567
568 /**
569 * see section 3.7.5 of TCG TNC IF-IMC Specification 1.2
570 */
571 TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id,
572 TNC_ConnectionID connection_id)
573 {
574 if (!imc_attestation)
575 {
576 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
577 return TNC_RESULT_NOT_INITIALIZED;
578 }
579 return TNC_RESULT_SUCCESS;
580 }
581
582 /**
583 * see section 3.7.6 of TCG TNC IF-IMC Specification 1.2
584 */
585 TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id)
586 {
587 if (!imc_attestation)
588 {
589 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
590 return TNC_RESULT_NOT_INITIALIZED;
591 }
592 imc_attestation->destroy(imc_attestation);
593 imc_attestation = NULL;
594
595 return TNC_RESULT_SUCCESS;
596 }
597
598 /**
599 * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.2
600 */
601 TNC_Result TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id,
602 TNC_TNCC_BindFunctionPointer bind_function)
603 {
604 if (!imc_attestation)
605 {
606 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
607 return TNC_RESULT_NOT_INITIALIZED;
608 }
609 return imc_attestation->bind_functions(imc_attestation, bind_function);
610 }