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