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