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