0395f747200183cb55ec490ee59275e4daf44e64
[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 #include <tcg/tcg_attr.h>
23
24 #include <tcg/tcg_pts_attr_req_proto_caps.h>
25 #include <tcg/tcg_pts_attr_meas_algo.h>
26 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
27 #include <tcg/tcg_pts_attr_get_aik.h>
28 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
29 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
30 #include <tcg/tcg_pts_attr_req_file_meas.h>
31
32 #include <tncif_pa_subtypes.h>
33
34 #include <pen/pen.h>
35 #include <debug.h>
36 #include <utils/linked_list.h>
37
38 #include <trousers/tss.h>
39 #include <trousers/trousers.h>
40
41 /* IMV definitions */
42
43 static const char imv_name[] = "Attestation";
44
45 /**
46 * UTF-8 encoding of the character used to delimiter the filename
47 */
48 #define SOLIDUS_UTF = 0x002F
49 #define REVERSE_SOLIDUS_UTF = 0x005C
50
51 #define IMV_VENDOR_ID PEN_TCG
52 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
53
54 static imv_agent_t *imv_attestation;
55
56 /**
57 * List of files and directories to measure
58 */
59 static linked_list_t *file_list, *directory_list;
60
61 /**
62 * Monotonic increasing number for Request File Measurement attribute
63 */
64 static u_int16_t request_id_counter = 0;
65
66 /**
67 * Struct to hold file or directory name with the request ID for Request File Measurement attribute
68 */
69 typedef struct measurement_req_entry_t measurement_req_entry_t;
70
71 struct measurement_req_entry_t {
72 char *path;
73 u_int16_t request_id;
74 };
75
76 /**
77 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
78 */
79 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
80 TNC_Version min_version,
81 TNC_Version max_version,
82 TNC_Version *actual_version)
83 {
84 if (imv_attestation)
85 {
86 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
87 return TNC_RESULT_ALREADY_INITIALIZED;
88 }
89 imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
90 imv_id, actual_version);
91 if (!imv_attestation)
92 {
93 return TNC_RESULT_FATAL;
94 }
95 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
96 {
97 DBG1(DBG_IMV, "no common IF-IMV version");
98 return TNC_RESULT_NO_COMMON_VERSION;
99 }
100 return TNC_RESULT_SUCCESS;
101 }
102
103 /**
104 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
105 */
106 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
107 TNC_ConnectionID connection_id,
108 TNC_ConnectionState new_state)
109 {
110 imv_state_t *state;
111 imv_attestation_state_t *attestation_state;
112 enumerator_t *enumerator;
113 char *files;
114 char *directories;
115 measurement_req_entry_t *entry;
116 char *token;
117
118 if (!imv_attestation)
119 {
120 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
121 return TNC_RESULT_NOT_INITIALIZED;
122 }
123 switch (new_state)
124 {
125 case TNC_CONNECTION_STATE_CREATE:
126 state = imv_attestation_state_create(connection_id);
127 return imv_attestation->create_state(imv_attestation, state);
128 case TNC_CONNECTION_STATE_DELETE:
129 return imv_attestation->delete_state(imv_attestation, connection_id);
130 case TNC_CONNECTION_STATE_HANDSHAKE:
131 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
132 {
133 return TNC_RESULT_FATAL;
134 }
135 state->change_state(state, new_state);
136 attestation_state = (imv_attestation_state_t*)state;
137
138 /** Get the files to measure for
139 * PTS Request File Measurement attribute
140 */
141
142 file_list = linked_list_create();
143 directory_list = linked_list_create();
144
145 files = lib->settings->get_str(lib->settings,
146 "libimcv.plugins.imc-attestation.files", "none");
147 enumerator = enumerator_create_token(files, " ", " ");
148 while (enumerator->enumerate(enumerator, &token))
149 {
150 entry = malloc_thing(measurement_req_entry_t);
151 token = strdup(token);
152 entry->path = token;
153 entry->request_id = request_id_counter;
154 file_list->insert_last(file_list, entry);
155 free(token);
156 request_id_counter ++;
157 }
158
159 /** Get the directories to measure for
160 * PTS Request File Measurement attribute
161 */
162
163 directories = lib->settings->get_str(lib->settings,
164 "libimcv.plugins.imc-attestation.directories", "none");
165 enumerator = enumerator_create_token(directories, " ", " ");
166 while (enumerator->enumerate(enumerator, &token))
167 {
168 entry = malloc_thing(measurement_req_entry_t);
169 token = strdup(token);
170 entry->path = token;
171 entry->request_id = request_id_counter;
172 directory_list->insert_last(directory_list, entry);
173 free(token);
174 request_id_counter ++;
175 }
176 enumerator->destroy(enumerator);
177 return TNC_RESULT_SUCCESS;
178 default:
179 return imv_attestation->change_state(imv_attestation, connection_id,
180 new_state, NULL);
181 }
182 }
183
184 static TNC_Result send_message(TNC_ConnectionID connection_id)
185 {
186 pa_tnc_msg_t *msg;
187 pa_tnc_attr_t *attr;
188 TNC_Result result;
189 imv_state_t *state;
190 imv_attestation_state_t *attestation_state;
191 imv_attestation_handshake_state_t handshake_state;
192
193 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
194 {
195 return TNC_RESULT_FATAL;
196 }
197
198 attestation_state = (imv_attestation_state_t*)state;
199 handshake_state = attestation_state->get_handshake_state(attestation_state);
200
201 /* Switch on the attribute type IMV has received */
202 switch (handshake_state)
203 {
204 case IMV_ATTESTATION_STATE_INIT:
205 {
206 /* Send Request Protocol Capabilities attribute */
207 pts_attr_req_proto_caps_flag_t flags;
208 flags = PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_VER | PTS_PROTO_CAPS_CURRENT;
209 attr = tcg_pts_attr_req_proto_caps_create(flags);
210 break;
211 }
212 case IMV_ATTESTATION_STATE_PROTO_CAP:
213 {
214 /* Send Measurement Algorithms attribute */
215 pts_attr_meas_algorithms_t algorithms;
216 algorithms = PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384;
217 attr = tcg_pts_attr_meas_algo_create(algorithms);
218 break;
219 }
220 case IMV_ATTESTATION_STATE_MEAS_ALGO:
221 {
222 /* Send Get TPM Version Information attribute */
223 attr = tcg_pts_attr_get_tpm_version_info_create();
224 break;
225 }
226 case IMV_ATTESTATION_STATE_TPM_INFO:
227 {
228 /* Send Get AIK attribute */
229 /* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
230 //attr = tcg_pts_attr_get_aik_create();
231 //break;
232 }
233 case IMV_ATTESTATION_STATE_AIK:
234 {
235 /* Send Request File Measurement attribute */
236 enumerator_t *enumerator;
237 measurement_req_entry_t *entry;
238 char *path;
239 u_int16_t request_id;
240 u_int32_t delimiter = SOLIDUS_UTF;
241
242 msg = pa_tnc_msg_create();
243
244 /** Add files to measure to PTS Request File Measurement attribute
245 */
246 enumerator = enumerator_create_single(file_list, NULL);
247 while (enumerator->enumerate(enumerator, &entry))
248 {
249 attr = tcg_pts_attr_req_file_meas_create(false,
250 entry.request_id, delimiter,
251 chunk_create(entry.path,strlen(entry.path)));
252 attr->set_noskip_flag(attr, TRUE);
253 msg->add_attribute(msg, attr);
254 }
255 /** Add directories to measure to PTS Request File Measurement attribute
256 */
257 enumerator = enumerator_create_single(directory_list, NULL);
258 while (enumerator->enumerate(enumerator, &entry))
259 {
260 attr = tcg_pts_attr_req_file_meas_create(true,
261 entry.request_id, delimiter,
262 chunk_create(entry.path,strlen(entry.path)));
263 attr->set_noskip_flag(attr, TRUE);
264 msg->add_attribute(msg, attr);
265 }
266 enumerator->destroy(enumerator);
267 goto end;
268 }
269 case IMV_ATTESTATION_STATE_SIMPLE_COMP_EVID:
270 case IMV_ATTESTATION_STATE_SIMPLE_EVID_FINAL:
271 case IMV_ATTESTATION_STATE_FILE_METADATA:
272 case IMV_ATTESTATION_STATE_FILE_MEAS:
273 case IMV_ATTESTATION_STATE_IML:
274 DBG1(DBG_IMV, "Attestation IMV has nothing to send: \"%s\"", handshake_state);
275 return TNC_RESULT_FATAL;
276 default:
277 DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"", handshake_state);
278 return TNC_RESULT_FATAL;
279 }
280
281 attr->set_noskip_flag(attr, TRUE);
282 msg = pa_tnc_msg_create();
283 msg->add_attribute(msg, attr);
284
285 end:
286 msg->build(msg);
287 result = imv_attestation->send_message(imv_attestation, connection_id,
288 msg->get_encoding(msg));
289 msg->destroy(msg);
290
291 return result;
292 }
293
294 /**
295 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
296 */
297 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
298 TNC_ConnectionID connection_id,
299 TNC_BufferReference msg,
300 TNC_UInt32 msg_len,
301 TNC_MessageType msg_type)
302 {
303 pa_tnc_msg_t *pa_tnc_msg;
304 pa_tnc_attr_t *attr;
305 imv_state_t *state;
306 imv_attestation_state_t *imv_attestation_state;
307 enumerator_t *enumerator;
308 TNC_Result result;
309 bool fatal_error = FALSE;
310
311 if (!imv_attestation)
312 {
313 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
314 return TNC_RESULT_NOT_INITIALIZED;
315 }
316
317 /* get current IMV state */
318 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
319 {
320 return TNC_RESULT_FATAL;
321 }
322
323 /* parse received PA-TNC message and automatically handle any errors */
324 result = imv_attestation->receive_message(imv_attestation, connection_id,
325 chunk_create(msg, msg_len), msg_type,
326 &pa_tnc_msg);
327
328 /* no parsed PA-TNC attributes available if an error occurred */
329 if (!pa_tnc_msg)
330 {
331 return result;
332 }
333
334 /* analyze PA-TNC attributes */
335 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
336 while (enumerator->enumerate(enumerator, &attr))
337 {
338 if (attr->get_vendor_id(attr) == PEN_IETF &&
339 attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
340 {
341 ietf_attr_pa_tnc_error_t *error_attr;
342 pa_tnc_error_code_t error_code;
343 chunk_t msg_info, attr_info;
344 u_int32_t offset;
345
346 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
347 error_code = error_attr->get_error_code(error_attr);
348 msg_info = error_attr->get_msg_info(error_attr);
349
350 DBG1(DBG_IMV, "received PA-TNC error '%N' concerning message %#B",
351 pa_tnc_error_code_names, error_code, &msg_info);
352 switch (error_code)
353 {
354 case PA_ERROR_INVALID_PARAMETER:
355 offset = error_attr->get_offset(error_attr);
356 DBG1(DBG_IMV, " occurred at offset of %u bytes", offset);
357 break;
358 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
359 attr_info = error_attr->get_attr_info(error_attr);
360 DBG1(DBG_IMV, " unsupported attribute %#B", &attr_info);
361 break;
362 default:
363 break;
364 }
365 fatal_error = TRUE;
366 }
367 else if (attr->get_vendor_id(attr) == PEN_TCG)
368 {
369 /**
370 * Handle TCG PTS attributes
371 */
372 switch(attr->get_type(attr))
373 {
374 case TCG_PTS_PROTO_CAPS:
375 break;
376 case TCG_PTS_MEAS_ALGO_SELECTION:
377 break;
378 case TCG_PTS_TPM_VERSION_INFO:
379 break;
380 case TCG_PTS_AIK:
381 break;
382
383 /* PTS-based Attestation Evidence */
384 case TCG_PTS_SIMPLE_COMP_EVID:
385 break;
386 case TCG_PTS_SIMPLE_EVID_FINAL:
387 break;
388 case TCG_PTS_FILE_MEAS:
389 break;
390
391 /* TODO: Not implemented yet */
392 case TCG_PTS_DH_NONCE_PARAMS_RESP:
393 case TCG_PTS_UNIX_FILE_META:
394 case TCG_PTS_INTEG_MEAS_LOG:
395 /* Attributes using XML */
396 case TCG_PTS_TEMPL_REF_MANI_SET_META:
397 case TCG_PTS_VERIFICATION_RESULT:
398 case TCG_PTS_INTEG_REPORT:
399 /* On Windows only*/
400 case TCG_PTS_WIN_FILE_META:
401 case TCG_PTS_REGISTRY_VALUE:
402 /* Received on IMC side only*/
403 case TCG_PTS_REQ_PROTO_CAPS:
404 case TCG_PTS_DH_NONCE_PARAMS_REQ:
405 case TCG_PTS_DH_NONCE_FINISH:
406 case TCG_PTS_MEAS_ALGO:
407 case TCG_PTS_GET_TPM_VERSION_INFO:
408 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META:
409 case TCG_PTS_UPDATE_TEMPL_REF_MANI:
410 case TCG_PTS_GET_AIK:
411 case TCG_PTS_REQ_FUNCT_COMP_EVID:
412 case TCG_PTS_GEN_ATTEST_EVID:
413 case TCG_PTS_REQ_FILE_META:
414 case TCG_PTS_REQ_FILE_MEAS:
415 case TCG_PTS_REQ_INTEG_MEAS_LOG:
416 default:
417 DBG1(DBG_IMV, "received unsupported attribute '%N'",
418 tcg_attr_names, attr->get_type(attr));
419 break;
420 }
421 }
422 }
423 enumerator->destroy(enumerator);
424 pa_tnc_msg->destroy(pa_tnc_msg);
425
426 if (fatal_error)
427 {
428 state->set_recommendation(state,
429 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
430 TNC_IMV_EVALUATION_RESULT_ERROR);
431 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
432 }
433
434 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
435 }
436
437 /**
438 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
439 */
440 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
441 TNC_ConnectionID connection_id)
442 {
443 if (!imv_attestation)
444 {
445 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
446 return TNC_RESULT_NOT_INITIALIZED;
447 }
448 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
449 }
450
451 /**
452 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
453 */
454 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
455 TNC_ConnectionID connection_id)
456 {
457 if (!imv_attestation)
458 {
459 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
460 return TNC_RESULT_NOT_INITIALIZED;
461 }
462 return TNC_RESULT_SUCCESS;
463 }
464
465 /**
466 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
467 */
468 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
469 {
470 if (!imv_attestation)
471 {
472 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
473 return TNC_RESULT_NOT_INITIALIZED;
474 }
475 imv_attestation->destroy(imv_attestation);
476 imv_attestation = NULL;
477
478 return TNC_RESULT_SUCCESS;
479 }
480
481 /**
482 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
483 */
484 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
485 TNC_TNCS_BindFunctionPointer bind_function)
486 {
487 if (!imv_attestation)
488 {
489 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
490 return TNC_RESULT_NOT_INITIALIZED;
491 }
492 return imv_attestation->bind_functions(imv_attestation, bind_function);
493 }