refactored PTS measurement 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_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
37 #include <trousers/tss.h>
38 #include <trousers/trousers.h>
39 #include <openssl/sha.h>
40
41
42 /* IMC definitions */
43
44 static const char imc_name[] = "Attestation";
45
46 #define IMC_VENDOR_ID PEN_TCG
47 #define IMC_SUBTYPE PA_SUBTYPE_TCG_PTS
48 #define IMC_ATTESTATION_MAX_FILE_SIZE 32768
49
50 static imc_agent_t *imc_attestation;
51
52
53 /**
54 * Selected Measurement Algorithm, which is selected during
55 * the PTS Measurement Algorithm attributes exchange
56 * Default value is SHA256
57 */
58 static pts_meas_algorithms_t selected_algorithm = PTS_MEAS_ALGO_SHA256;
59
60 /**
61 * List of files and directories to measure
62 */
63 static linked_list_t *files, *directories;
64
65 /**
66 * see section 3.7.1 of TCG TNC IF-IMC Specification 1.2
67 */
68 TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id,
69 TNC_Version min_version,
70 TNC_Version max_version,
71 TNC_Version *actual_version)
72 {
73 if (imc_attestation)
74 {
75 DBG1(DBG_IMC, "IMC \"%s\" has already been initialized", imc_name);
76 return TNC_RESULT_ALREADY_INITIALIZED;
77 }
78 imc_attestation = imc_agent_create(imc_name, IMC_VENDOR_ID, IMC_SUBTYPE,
79 imc_id, actual_version);
80 if (!imc_attestation)
81 {
82 return TNC_RESULT_FATAL;
83 }
84 if (min_version > TNC_IFIMC_VERSION_1 || max_version < TNC_IFIMC_VERSION_1)
85 {
86 DBG1(DBG_IMC, "no common IF-IMC version");
87 return TNC_RESULT_NO_COMMON_VERSION;
88 }
89 return TNC_RESULT_SUCCESS;
90 }
91
92 /**
93 * see section 3.7.2 of TCG TNC IF-IMC Specification 1.2
94 */
95 TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id,
96 TNC_ConnectionID connection_id,
97 TNC_ConnectionState new_state)
98 {
99 imc_state_t *state;
100 /* TODO: Not used so far */
101 //imc_attestation_state_t *attestation_state;
102
103 if (!imc_attestation)
104 {
105 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
106 return TNC_RESULT_NOT_INITIALIZED;
107 }
108 switch (new_state)
109 {
110 case TNC_CONNECTION_STATE_CREATE:
111 state = imc_attestation_state_create(connection_id);
112 return imc_attestation->create_state(imc_attestation, state);
113 case TNC_CONNECTION_STATE_DELETE:
114 return imc_attestation->delete_state(imc_attestation, connection_id);
115 case TNC_CONNECTION_STATE_HANDSHAKE:
116 case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
117 case TNC_CONNECTION_STATE_ACCESS_NONE:
118 default:
119 return imc_attestation->change_state(imc_attestation, connection_id,
120 new_state, NULL);
121 }
122 }
123
124 /**
125 * Get the TPM Version Information
126 */
127 static TSS_RESULT get_tpm_version_info(BYTE *tpm_version_info)
128 {
129 TSS_HCONTEXT hContext;
130 TSS_HTPM hTPM;
131 TSS_RESULT uiResult;
132 UINT32 uiResultLen;
133 /* TODO: Needed for parsing version info on IMV side */
134 //TPM_CAP_VERSION_INFO versionInfo;
135 //UINT64 offset = 0;
136
137 uiResult = Tspi_Context_Create(&hContext);
138 if (uiResult != TSS_SUCCESS) {
139 DBG1(DBG_IMC,"Error 0x%x on Tspi_Context_Create\n", uiResult);
140 return uiResult;
141 }
142 uiResult = Tspi_Context_Connect(hContext, NULL);
143 if (uiResult != TSS_SUCCESS) {
144 DBG1(DBG_IMC,"Error 0x%x on Tspi_Context_Connect\n", uiResult);
145 return uiResult;
146 }
147 uiResult = Tspi_Context_GetTpmObject (hContext, &hTPM);
148 if (uiResult != TSS_SUCCESS) {
149 DBG1(DBG_IMC,"Error 0x%x on Tspi_Context_GetTpmObject\n", uiResult);
150 return uiResult;
151 }
152
153 uiResult = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL, &uiResultLen,
154 &tpm_version_info);
155 if (uiResult != TSS_SUCCESS) {
156 DBG1(DBG_IMC,"Error 0x%x on Tspi_TPM_GetCapability\n", uiResult);
157 return uiResult;
158 }
159 }
160
161 /**
162 * Get Hash Measurement of a file
163 * Uses openssl's sha.h
164 */
165 static TNC_Result hash_file(char *path, unsigned char *out)
166 {
167 BYTE *buffer;
168 FILE *file;
169 int bytesRead = 0;
170
171 file = fopen(path, "rb");
172 if (!file) {
173 DBG1(DBG_IMC,"File can not be opened %s\n", path);
174 return TNC_RESULT_FATAL;
175 }
176
177 buffer = malloc(IMC_ATTESTATION_MAX_FILE_SIZE);
178 if(!buffer)
179 {
180 DBG1(DBG_IMC,"Buffer couldn't be allocated memory");
181 goto fatal;
182 }
183
184 switch(selected_algorithm)
185 {
186 case PTS_MEAS_ALGO_SHA1:
187 {
188 SHA_CTX sha1;
189 SHA1_Init(&sha1);
190
191 while((bytesRead = fread(buffer, 1, IMC_ATTESTATION_MAX_FILE_SIZE, file)))
192 {
193 SHA1_Update(&sha1, buffer, bytesRead);
194 }
195 SHA1_Final(out, &sha1);
196 break;
197 }
198 case PTS_MEAS_ALGO_SHA256:
199 {
200 SHA256_CTX sha256;
201 SHA256_Init(&sha256);
202
203 while((bytesRead = fread(buffer, 1, IMC_ATTESTATION_MAX_FILE_SIZE, file)))
204 {
205 SHA256_Update(&sha256, buffer, bytesRead);
206 }
207 SHA256_Final(out, &sha256);
208 break;
209 }
210 case PTS_MEAS_ALGO_SHA384:
211 /*{
212 SHA384_CTX sha384;
213 SHA384_Init(&sha384);
214
215 while((bytesRead = fread(buffer, 1, IMC_ATTESTATION_MAX_FILE_SIZE, file)))
216 {
217 SHA384_Update(&sha384, buffer, bytesRead);
218 }
219 SHA384_Final(out, &sha384);
220 break;
221 }
222 */
223 default:
224 DBG1(DBG_IMC,"Unsupported Selected Hashing Algorithm \n");
225 return TNC_RESULT_FATAL;
226 }
227
228 fclose(file);
229 free(buffer);
230 return TNC_RESULT_SUCCESS;
231
232 fatal:
233 fclose(file);
234 return TNC_RESULT_FATAL;
235 }
236
237 static TNC_Result send_message(TNC_ConnectionID connection_id)
238 {
239 pa_tnc_msg_t *msg;
240 pa_tnc_attr_t *attr;
241 imc_state_t *state;
242 imc_attestation_state_t *attestation_state;
243 imc_attestation_handshake_state_t handshake_state;
244 TNC_Result result;
245
246 if (!imc_attestation->get_state(imc_attestation, connection_id, &state))
247 {
248 return TNC_RESULT_FATAL;
249 }
250 attestation_state = (imc_attestation_state_t*)state;
251 handshake_state = attestation_state->get_handshake_state(attestation_state);
252
253 /* Switch on the attribute type IMC has received */
254 switch (handshake_state)
255 {
256 case IMC_ATTESTATION_STATE_REQ_PROTO_CAP:
257 {
258 pts_proto_caps_flag_t flags;
259 flags = PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_VER;
260 attr = tcg_pts_attr_proto_caps_create(flags);
261 break;
262 }
263 case IMC_ATTESTATION_STATE_REQ_MEAS_ALGO:
264 {
265 pts_meas_algorithms_t algorithm;
266 algorithm = PTS_MEAS_ALGO_SHA1;
267 /* Save the selected algorithm for further attributes creation */
268 selected_algorithm = algorithm;
269 attr = tcg_pts_attr_meas_algo_create(algorithm, TRUE);
270 break;
271 }
272 case IMC_ATTESTATION_STATE_GET_TPM_INFO:
273 {
274 TSS_RESULT uiResult;
275 BYTE *tpm_version_info;
276
277 uiResult = get_tpm_version_info(tpm_version_info);
278 if (uiResult != TSS_SUCCESS) {
279 DBG1(DBG_IMC,"Error 0x%x on get_tpm_version_info\n", uiResult);
280 return uiResult;
281 }
282
283 attr = tcg_pts_attr_tpm_version_info_create(
284 chunk_create((char *)tpm_version_info,
285 strlen(tpm_version_info)));
286 break;
287 }
288 /* TODO: working on */
289 /*case IMC_ATTESTATION_STATE_REQ_FILE_MEAS:
290 {
291 enumerator_t *enumerator;
292 measurement_req_entry_t *entry;
293
294 enumerator = enumerator_create_single(file_list, NULL);
295 while (enumerator->enumerate(enumerator, &entry))
296 {
297 attr = tcg_pts_attr_req_file_meas_create(false,
298 entry.request_id, delimiter,
299 chunk_create(entry.path,strlen(entry.path)));
300 attr->set_noskip_flag(attr, TRUE);
301 msg->add_attribute(msg, attr);
302 }
303
304 enumerator = enumerator_create_single(file_list, NULL);
305 while (enumerator->enumerate(enumerator, &entry))
306 {
307 attr = tcg_pts_attr_req_file_meas_create(false,
308 entry.request_id, delimiter,
309 chunk_create(entry.path,strlen(entry.path)));
310 attr->set_noskip_flag(attr, TRUE);
311 msg->add_attribute(msg, attr);
312 }
313 break;
314 }*/
315 case IMC_ATTESTATION_STATE_GET_AIK:
316 case IMC_ATTESTATION_STATE_REQ_FUNCT_COMP_EVID:
317 case IMC_ATTESTATION_STATE_GEN_ATTEST_EVID:
318 case IMC_ATTESTATION_STATE_REQ_FILE_METADATA:
319 case IMC_ATTESTATION_STATE_REQ_IML:
320 case IMC_ATTESTATION_STATE_INIT:
321 DBG1(DBG_IMC, "Attestation IMC has nothing to send: \"%s\"", handshake_state);
322 return TNC_RESULT_FATAL;
323 default:
324 DBG1(DBG_IMC, "Attestation IMC is in unknown state: \"%s\"", handshake_state);
325 return TNC_RESULT_FATAL;
326 }
327
328
329 attr->set_noskip_flag(attr, TRUE);
330 msg = pa_tnc_msg_create();
331 msg->add_attribute(msg, attr);
332 msg->build(msg);
333 result = imc_attestation->send_message(imc_attestation, connection_id,
334 msg->get_encoding(msg));
335 msg->destroy(msg);
336
337 return result;
338 }
339
340 /**
341 * see section 3.7.3 of TCG TNC IF-IMC Specification 1.2
342 */
343 TNC_Result TNC_IMC_BeginHandshake(TNC_IMCID imc_id,
344 TNC_ConnectionID connection_id)
345 {
346 if (!imc_attestation)
347 {
348 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
349 return TNC_RESULT_NOT_INITIALIZED;
350 }
351 return send_message(connection_id);
352 }
353
354 /**
355 * see section 3.7.4 of TCG TNC IF-IMC Specification 1.2
356 */
357 TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id,
358 TNC_ConnectionID connection_id,
359 TNC_BufferReference msg,
360 TNC_UInt32 msg_len,
361 TNC_MessageType msg_type)
362 {
363 pa_tnc_msg_t *pa_tnc_msg;
364 pa_tnc_attr_t *attr;
365 enumerator_t *enumerator;
366 TNC_Result result;
367 bool fatal_error = FALSE;
368
369 if (!imc_attestation)
370 {
371 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
372 return TNC_RESULT_NOT_INITIALIZED;
373 }
374
375 /* parse received PA-TNC message and automatically handle any errors */
376 result = imc_attestation->receive_message(imc_attestation, connection_id,
377 chunk_create(msg, msg_len), msg_type,
378 &pa_tnc_msg);
379
380 /* no parsed PA-TNC attributes available if an error occurred */
381 if (!pa_tnc_msg)
382 {
383 return result;
384 }
385
386 /* analyze PA-TNC attributes */
387 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
388 while (enumerator->enumerate(enumerator, &attr))
389 {
390 if (attr->get_vendor_id(attr) == PEN_IETF &&
391 attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
392 {
393 ietf_attr_pa_tnc_error_t *error_attr;
394 pa_tnc_error_code_t error_code;
395 chunk_t msg_info, attr_info;
396 u_int32_t offset;
397
398 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
399 error_code = error_attr->get_error_code(error_attr);
400 msg_info = error_attr->get_msg_info(error_attr);
401
402 DBG1(DBG_IMC, "received PA-TNC error '%N' concerning message %#B",
403 pa_tnc_error_code_names, error_code, &msg_info);
404 switch (error_code)
405 {
406 case PA_ERROR_INVALID_PARAMETER:
407 offset = error_attr->get_offset(error_attr);
408 DBG1(DBG_IMC, " occurred at offset of %u bytes", offset);
409 break;
410 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
411 attr_info = error_attr->get_attr_info(error_attr);
412 DBG1(DBG_IMC, " unsupported attribute %#B", &attr_info);
413 break;
414 default:
415 break;
416 }
417 fatal_error = TRUE;
418 }
419 else if (attr->get_vendor_id(attr) == PEN_TCG)
420 {
421 /**
422 * Handle TCG PTS attributes
423 */
424 switch(attr->get_type(attr))
425 {
426 case TCG_PTS_REQ_PROTO_CAPS:
427 break;
428 case TCG_PTS_MEAS_ALGO:
429 break;
430 case TCG_PTS_GET_TPM_VERSION_INFO:
431 break;
432 case TCG_PTS_GET_AIK:
433 break;
434
435 /* PTS-based Attestation Evidence */
436 case TCG_PTS_REQ_FUNCT_COMP_EVID:
437 break;
438 case TCG_PTS_GEN_ATTEST_EVID:
439 break;
440 case TCG_PTS_REQ_FILE_MEAS:
441 break;
442
443 /* TODO: Not implemented yet */
444 case TCG_PTS_DH_NONCE_PARAMS_REQ:
445 case TCG_PTS_DH_NONCE_FINISH:
446 case TCG_PTS_REQ_FILE_META:
447 case TCG_PTS_REQ_INTEG_MEAS_LOG:
448 /* Attributes using XML */
449 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META:
450 case TCG_PTS_UPDATE_TEMPL_REF_MANI:
451 /* On Windows only*/
452 case TCG_PTS_REQ_REGISTRY_VALUE:
453 /* Received on IMV side only*/
454 case TCG_PTS_PROTO_CAPS:
455 case TCG_PTS_DH_NONCE_PARAMS_RESP:
456 case TCG_PTS_MEAS_ALGO_SELECTION:
457 case TCG_PTS_TPM_VERSION_INFO:
458 case TCG_PTS_TEMPL_REF_MANI_SET_META:
459 case TCG_PTS_AIK:
460 case TCG_PTS_SIMPLE_COMP_EVID:
461 case TCG_PTS_SIMPLE_EVID_FINAL:
462 case TCG_PTS_VERIFICATION_RESULT:
463 case TCG_PTS_INTEG_REPORT:
464 case TCG_PTS_UNIX_FILE_META:
465 case TCG_PTS_FILE_MEAS:
466 case TCG_PTS_INTEG_MEAS_LOG:
467 default:
468 DBG1(DBG_IMC, "received unsupported attribute '%N'",
469 tcg_attr_names, attr->get_type(attr));
470 break;
471 }
472
473
474 }
475 }
476 enumerator->destroy(enumerator);
477 pa_tnc_msg->destroy(pa_tnc_msg);
478
479 /* if no error occurred then always return the same response */
480 return fatal_error ? TNC_RESULT_FATAL : send_message(connection_id);
481 }
482
483 /**
484 * see section 3.7.5 of TCG TNC IF-IMC Specification 1.2
485 */
486 TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id,
487 TNC_ConnectionID connection_id)
488 {
489 if (!imc_attestation)
490 {
491 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
492 return TNC_RESULT_NOT_INITIALIZED;
493 }
494 return TNC_RESULT_SUCCESS;
495 }
496
497 /**
498 * see section 3.7.6 of TCG TNC IF-IMC Specification 1.2
499 */
500 TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id)
501 {
502 if (!imc_attestation)
503 {
504 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
505 return TNC_RESULT_NOT_INITIALIZED;
506 }
507 imc_attestation->destroy(imc_attestation);
508 imc_attestation = NULL;
509
510 return TNC_RESULT_SUCCESS;
511 }
512
513 /**
514 * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.2
515 */
516 TNC_Result TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id,
517 TNC_TNCC_BindFunctionPointer bind_function)
518 {
519 if (!imc_attestation)
520 {
521 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
522 return TNC_RESULT_NOT_INITIALIZED;
523 }
524 return imc_attestation->bind_functions(imc_attestation, bind_function);
525 }