Converted all IMVs to use generic IF-IMV API
[strongswan.git] / src / libpts / plugins / imv_attestation / imv_attestation_agent.c
1 /*
2 * Copyright (C) 2011-2012 Sansar Choinyambuu
3 * Copyright (C) 2011-2013 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "imv_attestation_agent.h"
18 #include "imv_attestation_state.h"
19 #include "imv_attestation_process.h"
20 #include "imv_attestation_build.h"
21
22 #include <imcv.h>
23 #include <imv/imv_agent.h>
24 #include <imv/imv_msg.h>
25 #include <ietf/ietf_attr.h>
26 #include <ietf/ietf_attr_pa_tnc_error.h>
27 #include <ietf/ietf_attr_product_info.h>
28 #include <ietf/ietf_attr_string_version.h>
29
30 #include <libpts.h>
31
32 #include <pts/pts.h>
33 #include <pts/pts_database.h>
34 #include <pts/pts_creds.h>
35
36 #include <tcg/tcg_attr.h>
37
38 #include <tncif_pa_subtypes.h>
39
40 #include <pen/pen.h>
41 #include <utils/debug.h>
42 #include <credentials/credential_manager.h>
43 #include <collections/linked_list.h>
44
45 typedef struct private_imv_attestation_agent_t private_imv_attestation_agent_t;
46
47 /* Subscribed PA-TNC message subtypes */
48 static pen_type_t msg_types[] = {
49 { PEN_TCG, PA_SUBTYPE_TCG_PTS },
50 { PEN_IETF, PA_SUBTYPE_IETF_OPERATING_SYSTEM }
51 };
52
53 /**
54 * Private data of an imv_attestation_agent_t object.
55 */
56 struct private_imv_attestation_agent_t {
57
58 /**
59 * Public members of imv_attestation_agent_t
60 */
61 imv_agent_if_t public;
62
63 /**
64 * IMV agent responsible for generic functions
65 */
66 imv_agent_t *agent;
67
68 /**
69 * Supported PTS measurement algorithms
70 */
71 pts_meas_algorithms_t supported_algorithms;
72
73 /**
74 * Supported PTS Diffie Hellman Groups
75 */
76 pts_dh_group_t supported_dh_groups;
77
78 /**
79 * PTS file measurement database
80 */
81 pts_database_t *pts_db;
82
83 /**
84 * PTS credentials
85 */
86 pts_creds_t *pts_creds;
87
88 /**
89 * PTS credential manager
90 */
91 credential_manager_t *pts_credmgr;
92
93 };
94
95 METHOD(imv_agent_if_t, bind_functions, TNC_Result,
96 private_imv_attestation_agent_t *this, TNC_TNCS_BindFunctionPointer bind_function)
97 {
98 return this->agent->bind_functions(this->agent, bind_function);
99 }
100
101 METHOD(imv_agent_if_t, notify_connection_change, TNC_Result,
102 private_imv_attestation_agent_t *this, TNC_ConnectionID id,
103 TNC_ConnectionState new_state)
104 {
105 imv_state_t *state;
106
107 switch (new_state)
108 {
109 case TNC_CONNECTION_STATE_CREATE:
110 state = imv_attestation_state_create(id);
111 return this->agent->create_state(this->agent, state);
112 case TNC_CONNECTION_STATE_DELETE:
113 return this->agent->delete_state(this->agent, id);
114 default:
115 return this->agent->change_state(this->agent, id, new_state, NULL);
116 }
117 }
118
119 /**
120 * Build a message to be sent
121 */
122 static TNC_Result send_message(private_imv_attestation_agent_t *this,
123 imv_state_t *state, imv_msg_t *out_msg)
124 {
125 imv_attestation_state_t *attestation_state;
126 TNC_Result result;
127
128 attestation_state = (imv_attestation_state_t*)state;
129
130 if (imv_attestation_build(out_msg, attestation_state,
131 this->supported_algorithms,
132 this->supported_dh_groups, this->pts_db))
133 {
134 result = out_msg->send(out_msg, TRUE);
135 }
136 else
137 {
138 result = TNC_RESULT_FATAL;
139 }
140
141 return result;
142 }
143
144 /**
145 * Process a received message
146 */
147 static TNC_Result receive_msg(private_imv_attestation_agent_t *this,
148 imv_state_t *state, imv_msg_t *in_msg)
149 {
150 imv_attestation_state_t *attestation_state;
151 imv_msg_t *out_msg;
152 enumerator_t *enumerator;
153 pa_tnc_attr_t *attr;
154 pen_type_t type;
155 TNC_Result result;
156 pts_t *pts;
157 chunk_t os_name = chunk_empty;
158 chunk_t os_version = chunk_empty;
159 bool fatal_error = FALSE;
160
161 /* parse received PA-TNC message and handle local and remote errors */
162 result = in_msg->receive(in_msg, &fatal_error);
163 if (result != TNC_RESULT_SUCCESS)
164 {
165 return result;
166 }
167
168 attestation_state = (imv_attestation_state_t*)state;
169 pts = attestation_state->get_pts(attestation_state);
170
171 out_msg = imv_msg_create_as_reply(in_msg);
172 out_msg->set_msg_type(out_msg, msg_types[0]);
173
174 /* analyze PA-TNC attributes */
175 enumerator = in_msg->create_attribute_enumerator(in_msg);
176 while (enumerator->enumerate(enumerator, &attr))
177 {
178 type = attr->get_type(attr);
179
180 if (type.vendor_id == PEN_IETF)
181 {
182 switch (type.type)
183 {
184 case IETF_ATTR_PA_TNC_ERROR:
185 {
186 ietf_attr_pa_tnc_error_t *error_attr;
187 pen_type_t error_code;
188 chunk_t msg_info;
189
190 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
191 error_code = error_attr->get_error_code(error_attr);
192
193 if (error_code.vendor_id == PEN_TCG)
194 {
195 msg_info = error_attr->get_msg_info(error_attr);
196
197 DBG1(DBG_IMV, "received TCG-PTS error '%N'",
198 pts_error_code_names, error_code.type);
199 DBG1(DBG_IMV, "error information: %B", &msg_info);
200
201 result = TNC_RESULT_FATAL;
202 }
203 break;
204 }
205 case IETF_ATTR_PRODUCT_INFORMATION:
206 {
207 ietf_attr_product_info_t *attr_cast;
208
209 attr_cast = (ietf_attr_product_info_t*)attr;
210 os_name = attr_cast->get_info(attr_cast, NULL, NULL);
211 break;
212 }
213 case IETF_ATTR_STRING_VERSION:
214 {
215 ietf_attr_string_version_t *attr_cast;
216
217 attr_cast = (ietf_attr_string_version_t*)attr;
218 os_version = attr_cast->get_version(attr_cast, NULL, NULL);
219 break;
220 }
221 default:
222 break;
223 }
224 }
225 else if (type.vendor_id == PEN_TCG)
226 {
227 if (!imv_attestation_process(attr, out_msg, attestation_state,
228 this->supported_algorithms, this->supported_dh_groups,
229 this->pts_db, this->pts_credmgr))
230 {
231 result = TNC_RESULT_FATAL;
232 break;
233 }
234 }
235 }
236 enumerator->destroy(enumerator);
237
238 if (os_name.len && os_version.len)
239 {
240 pts->set_platform_info(pts, os_name, os_version);
241 }
242
243 if (fatal_error || result != TNC_RESULT_SUCCESS)
244 {
245 state->set_recommendation(state,
246 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
247 TNC_IMV_EVALUATION_RESULT_ERROR);
248 result = out_msg->send_assessment(out_msg);
249 out_msg->destroy(out_msg);
250 if (result != TNC_RESULT_SUCCESS)
251 {
252 return result;
253 }
254 return this->agent->provide_recommendation(this->agent, state);
255 }
256
257 /* send PA-TNC message with excl flag set */
258 result = out_msg->send(out_msg, TRUE);
259
260 if (result != TNC_RESULT_SUCCESS)
261 {
262 out_msg->destroy(out_msg);
263 return result;
264 }
265
266 /* check the IMV state for the next PA-TNC attributes to send */
267 result = send_message(this, state, out_msg);
268
269 if (result != TNC_RESULT_SUCCESS)
270 {
271 state->set_recommendation(state,
272 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
273 TNC_IMV_EVALUATION_RESULT_ERROR);
274 result = out_msg->send_assessment(out_msg);
275 out_msg->destroy(out_msg);
276 if (result != TNC_RESULT_SUCCESS)
277 {
278 return result;
279 }
280 return this->agent->provide_recommendation(this->agent, state);
281 }
282
283 if (attestation_state->get_handshake_state(attestation_state) ==
284 IMV_ATTESTATION_STATE_END)
285 {
286 if (attestation_state->get_file_meas_request_count(attestation_state))
287 {
288 DBG1(DBG_IMV, "failure due to %d pending file measurements",
289 attestation_state->get_file_meas_request_count(attestation_state));
290 attestation_state->set_measurement_error(attestation_state,
291 IMV_ATTESTATION_ERROR_FILE_MEAS_PEND);
292 }
293 if (attestation_state->get_measurement_error(attestation_state))
294 {
295 state->set_recommendation(state,
296 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
297 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR);
298 }
299 else
300 {
301 state->set_recommendation(state,
302 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
303 TNC_IMV_EVALUATION_RESULT_COMPLIANT);
304 }
305 result = out_msg->send_assessment(out_msg);
306 out_msg->destroy(out_msg);
307 if (result != TNC_RESULT_SUCCESS)
308 {
309 return result;
310 }
311 return this->agent->provide_recommendation(this->agent, state);
312 }
313 out_msg->destroy(out_msg);
314
315 return result;
316
317 }
318
319 METHOD(imv_agent_if_t, receive_message, TNC_Result,
320 private_imv_attestation_agent_t *this, TNC_ConnectionID id,
321 TNC_MessageType msg_type, chunk_t msg)
322 {
323 imv_state_t *state;
324 imv_msg_t *in_msg;
325 TNC_Result result;
326
327 if (!this->agent->get_state(this->agent, id, &state))
328 {
329 return TNC_RESULT_FATAL;
330 }
331 in_msg = imv_msg_create_from_data(this->agent, state, id, msg_type, msg);
332 result = receive_msg(this, state, in_msg);
333 in_msg->destroy(in_msg);
334
335 return result;
336 }
337
338 METHOD(imv_agent_if_t, receive_message_long, TNC_Result,
339 private_imv_attestation_agent_t *this, TNC_ConnectionID id,
340 TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id,
341 TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype, chunk_t msg)
342 {
343 imv_state_t *state;
344 imv_msg_t *in_msg;
345 TNC_Result result;
346
347 if (!this->agent->get_state(this->agent, id, &state))
348 {
349 return TNC_RESULT_FATAL;
350 }
351 in_msg = imv_msg_create_from_long_data(this->agent, state, id,
352 src_imc_id, dst_imv_id, msg_vid, msg_subtype, msg);
353 result = receive_msg(this, state, in_msg);
354 in_msg->destroy(in_msg);
355
356 return result;
357
358 }
359
360 METHOD(imv_agent_if_t, batch_ending, TNC_Result,
361 private_imv_attestation_agent_t *this, TNC_ConnectionID id)
362 {
363 return TNC_RESULT_SUCCESS;
364 }
365
366 METHOD(imv_agent_if_t, solicit_recommendation, TNC_Result,
367 private_imv_attestation_agent_t *this, TNC_ConnectionID id)
368 {
369 imv_state_t *state;
370
371 if (!this->agent->get_state(this->agent, id, &state))
372 {
373 return TNC_RESULT_FATAL;
374 }
375 return this->agent->provide_recommendation(this->agent, state);
376 }
377
378 METHOD(imv_agent_if_t, destroy, void,
379 private_imv_attestation_agent_t *this)
380 {
381 if (this->pts_creds)
382 {
383 this->pts_credmgr->remove_set(this->pts_credmgr,
384 this->pts_creds->get_set(this->pts_creds));
385 this->pts_creds->destroy(this->pts_creds);
386 }
387 DESTROY_IF(this->pts_db);
388 DESTROY_IF(this->pts_credmgr);
389 this->agent->destroy(this->agent);
390 free(this);
391 libpts_deinit();
392 }
393
394 /**
395 * Described in header.
396 */
397 imv_agent_if_t *imv_attestation_agent_create(const char *name, TNC_IMVID id,
398 TNC_Version *actual_version)
399 {
400 private_imv_attestation_agent_t *this;
401 char *hash_alg, *dh_group, *cadir;
402
403 hash_alg = lib->settings->get_str(lib->settings,
404 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
405 dh_group = lib->settings->get_str(lib->settings,
406 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
407 cadir = lib->settings->get_str(lib->settings,
408 "libimcv.plugins.imv-attestation.cadir", NULL);
409 libpts_init();
410
411 INIT(this,
412 .public = {
413 .bind_functions = _bind_functions,
414 .notify_connection_change = _notify_connection_change,
415 .receive_message = _receive_message,
416 .receive_message_long = _receive_message_long,
417 .batch_ending = _batch_ending,
418 .solicit_recommendation = _solicit_recommendation,
419 .destroy = _destroy,
420 },
421 .agent = imv_agent_create(name, msg_types, countof(msg_types), id,
422 actual_version),
423 .supported_algorithms = PTS_MEAS_ALGO_NONE,
424 .supported_dh_groups = PTS_DH_GROUP_NONE,
425 .pts_credmgr = credential_manager_create(),
426 .pts_creds = pts_creds_create(cadir),
427 .pts_db = pts_database_create(imcv_db),
428 );
429
430 if (!this->agent ||
431 !pts_meas_algo_update(hash_alg, &this->supported_algorithms) ||
432 !pts_dh_group_update(dh_group, &this->supported_dh_groups))
433 {
434 destroy(this);
435 return NULL;
436 }
437
438 if (this->pts_creds)
439 {
440 this->pts_credmgr->add_set(this->pts_credmgr,
441 this->pts_creds->get_set(this->pts_creds));
442 }
443
444 return &this->public;
445 }
446