centralized processing of assessment information
[strongswan.git] / src / libimcv / imv / imv_msg.c
1 /*
2 * Copyright (C) 2012 Andreas Steffen
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_msg.h"
17
18 #include "ietf/ietf_attr.h"
19 #include "ietf/ietf_attr_assess_result.h"
20 #include "ietf/ietf_attr_remediation_instr.h"
21
22 #include <tncif_names.h>
23
24 #include <pen/pen.h>
25 #include <collections/linked_list.h>
26 #include <utils/debug.h>
27
28 typedef struct private_imv_msg_t private_imv_msg_t;
29
30 /**
31 * Private data of a imv_msg_t object.
32 *
33 */
34 struct private_imv_msg_t {
35
36 /**
37 * Public imv_msg_t interface.
38 */
39 imv_msg_t public;
40
41 /**
42 * Connection ID
43 */
44 TNC_ConnectionID connection_id;
45
46 /**
47 * source ID
48 */
49 TNC_UInt32 src_id;
50
51 /**
52 * destination ID
53 */
54 TNC_UInt32 dst_id;
55
56 /**
57 * PA-TNC message type
58 */
59 pen_type_t msg_type;
60
61 /**
62 * List of PA-TNC attributes to be sent
63 */
64 linked_list_t *attr_list;
65
66 /**
67 * PA-TNC message
68 */
69 pa_tnc_msg_t *pa_msg;
70
71 /**
72 * Assigned IMV agent
73 */
74 imv_agent_t *agent;
75
76 /**
77 * Assigned IMV state
78 */
79 imv_state_t *state;
80 };
81
82 METHOD(imv_msg_t, get_src_id, TNC_UInt32,
83 private_imv_msg_t *this)
84 {
85 return this->src_id;
86 }
87
88 METHOD(imv_msg_t, get_dst_id, TNC_UInt32,
89 private_imv_msg_t *this)
90 {
91 return this->dst_id;
92 }
93
94 METHOD(imv_msg_t, set_msg_type, void,
95 private_imv_msg_t *this, pen_type_t msg_type)
96 {
97 if (msg_type.vendor_id != this->msg_type.vendor_id ||
98 msg_type.type != this->msg_type.type)
99 {
100 this->msg_type = msg_type;
101 this->dst_id = TNC_IMCID_ANY;
102 }
103 }
104
105 METHOD(imv_msg_t, add_attribute, void,
106 private_imv_msg_t *this, pa_tnc_attr_t *attr)
107 {
108 this->attr_list->insert_last(this->attr_list, attr);
109 }
110
111 METHOD(imv_msg_t, send_, TNC_Result,
112 private_imv_msg_t *this, bool excl)
113 {
114 pa_tnc_msg_t *pa_tnc_msg;
115 pa_tnc_attr_t *attr;
116 TNC_UInt32 msg_flags;
117 TNC_MessageType msg_type;
118 bool attr_added;
119 chunk_t msg;
120 enumerator_t *enumerator;
121 TNC_Result result = TNC_RESULT_SUCCESS;
122
123 while (this->attr_list->get_count(this->attr_list))
124 {
125 pa_tnc_msg = pa_tnc_msg_create(this->state->get_max_msg_len(this->state));
126 attr_added = FALSE;
127
128 enumerator = this->attr_list->create_enumerator(this->attr_list);
129 while (enumerator->enumerate(enumerator, &attr))
130 {
131 if (pa_tnc_msg->add_attribute(pa_tnc_msg, attr))
132 {
133 attr_added = TRUE;
134 }
135 else
136 {
137 if (attr_added)
138 {
139 break;
140 }
141 else
142 {
143 DBG1(DBG_IMV, "PA-TNC attribute too large to send, deleted");
144 attr->destroy(attr);
145 }
146 }
147 this->attr_list->remove_at(this->attr_list, enumerator);
148 }
149 enumerator->destroy(enumerator);
150
151 /* build and send the PA-TNC message via the IF-IMV interface */
152 if (!pa_tnc_msg->build(pa_tnc_msg))
153 {
154 pa_tnc_msg->destroy(pa_tnc_msg);
155 return TNC_RESULT_FATAL;
156 }
157 msg = pa_tnc_msg->get_encoding(pa_tnc_msg);
158 DBG3(DBG_IMV, "created PA-TNC message: %B", &msg);
159
160 if (this->state->has_long(this->state) && this->agent->send_message_long)
161 {
162 excl = excl && this->state->has_excl(this->state) &&
163 this->dst_id != TNC_IMCID_ANY;
164 msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0;
165 result = this->agent->send_message_long(this->src_id,
166 this->connection_id, msg_flags, msg.ptr, msg.len,
167 this->msg_type.vendor_id, this->msg_type.type,
168 this->dst_id);
169 }
170 else if (this->agent->send_message)
171 {
172 msg_type = (this->msg_type.vendor_id << 8) |
173 (this->msg_type.type & 0x000000ff);
174 result = this->agent->send_message(this->src_id, this->connection_id,
175 msg.ptr, msg.len, msg_type);
176 }
177
178 pa_tnc_msg->destroy(pa_tnc_msg);
179
180 if (result != TNC_RESULT_SUCCESS)
181 {
182 break;
183 }
184 }
185 return result;
186 }
187
188 METHOD(imv_msg_t, send_assessment, TNC_Result,
189 private_imv_msg_t *this)
190 {
191 TNC_IMV_Action_Recommendation rec;
192 TNC_IMV_Evaluation_Result eval;
193 pa_tnc_attr_t *attr;
194 char *string = NULL, *lang_code = NULL, *uri = NULL;
195 enumerator_t *e;
196
197 /* Send an IETF Assessment Result attribute if enabled */
198 if (lib->settings->get_bool(lib->settings, "libimcv.assessment_result",
199 TRUE))
200 {
201 this->state->get_recommendation(this->state, &rec, &eval);
202 attr = ietf_attr_assess_result_create(eval);
203 add_attribute(this, attr);
204
205 /* Send IETF Remediation Instructions if available */
206 if (eval != TNC_IMV_EVALUATION_RESULT_COMPLIANT)
207 {
208 e = this->agent->create_language_enumerator(this->agent,
209 this->state);
210 if (this->state->get_remediation_instructions(this->state,
211 e, &string, &lang_code, &uri))
212 {
213 if (string && lang_code)
214 {
215 attr = ietf_attr_remediation_instr_create_from_string(
216 chunk_create(string, strlen(string)),
217 chunk_create(lang_code, strlen(lang_code)));
218 add_attribute(this, attr);
219 }
220 if (uri)
221 {
222 attr = ietf_attr_remediation_instr_create_from_uri(
223 chunk_create(uri, strlen(uri)));
224 add_attribute(this, attr);
225 }
226 }
227 e->destroy(e);
228 }
229
230 /* send PA-TNC message with the excl flag set */
231 return send_(this, TRUE);
232 }
233 return TNC_RESULT_SUCCESS;
234 }
235
236 METHOD(imv_msg_t, receive, TNC_Result,
237 private_imv_msg_t *this, bool *fatal_error)
238 {
239 enumerator_t *enumerator;
240 pa_tnc_attr_t *attr;
241 chunk_t msg;
242
243 if (this->state->has_long(this->state))
244 {
245 if (this->dst_id != TNC_IMVID_ANY)
246 {
247 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
248 "from IMC %u to IMV %u",
249 this->agent->get_id(this->agent),
250 this->agent->get_name(this->agent),
251 this->connection_id, this->src_id, this->dst_id);
252 }
253 else
254 {
255 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
256 "from IMC %u", this->agent->get_id(this->agent),
257 this->agent->get_name(this->agent),
258 this->connection_id, this->src_id);
259 }
260 }
261 else
262 {
263 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u",
264 this->agent->get_id(this->agent),
265 this->agent->get_name(this->agent),
266 this->connection_id);
267 }
268 msg = this->pa_msg->get_encoding(this->pa_msg);
269 DBG3(DBG_IMV, "%B", &msg);
270
271 switch (this->pa_msg->process(this->pa_msg))
272 {
273 case SUCCESS:
274 break;
275 case VERIFY_ERROR:
276 {
277 imv_msg_t *error_msg;
278 TNC_Result result;
279
280 error_msg = imv_msg_create_as_reply(&this->public);
281
282 /* extract and copy by reference all error attributes */
283 enumerator = this->pa_msg->create_error_enumerator(this->pa_msg);
284 while (enumerator->enumerate(enumerator, &attr))
285 {
286 error_msg->add_attribute(error_msg, attr->get_ref(attr));
287 }
288 enumerator->destroy(enumerator);
289
290 /*
291 * send the PA-TNC message containing all error attributes
292 * with the excl flag set
293 */
294 result = error_msg->send(error_msg, TRUE);
295 error_msg->destroy(error_msg);
296 return result;
297 }
298 case FAILED:
299 default:
300 return TNC_RESULT_FATAL;
301 }
302
303 /* preprocess any received IETF standard error attributes */
304 *fatal_error = this->pa_msg->process_ietf_std_errors(this->pa_msg);
305
306 return TNC_RESULT_SUCCESS;
307 }
308
309 METHOD(imv_msg_t, delete_attributes, void,
310 private_imv_msg_t *this)
311 {
312 pa_tnc_attr_t *attr;
313
314 while (this->attr_list->remove_last(this->attr_list, (void**)&attr) == SUCCESS)
315 {
316 attr->destroy(attr);
317 }
318 }
319
320 METHOD(imv_msg_t, create_attribute_enumerator, enumerator_t*,
321 private_imv_msg_t *this)
322 {
323 return this->pa_msg->create_attribute_enumerator(this->pa_msg);
324 }
325
326 METHOD(imv_msg_t, destroy, void,
327 private_imv_msg_t *this)
328 {
329 this->attr_list->destroy_offset(this->attr_list,
330 offsetof(pa_tnc_attr_t, destroy));
331 DESTROY_IF(this->pa_msg);
332 free(this);
333 }
334
335 /**
336 * See header
337 */
338 imv_msg_t *imv_msg_create(imv_agent_t *agent, imv_state_t *state,
339 TNC_ConnectionID connection_id,
340 TNC_UInt32 src_id, TNC_UInt32 dst_id,
341 pen_type_t msg_type)
342 {
343 private_imv_msg_t *this;
344
345 INIT(this,
346 .public = {
347 .get_src_id = _get_src_id,
348 .get_dst_id = _get_dst_id,
349 .set_msg_type = _set_msg_type,
350 .send = _send_,
351 .send_assessment = _send_assessment,
352 .receive = _receive,
353 .add_attribute = _add_attribute,
354 .delete_attributes = _delete_attributes,
355 .create_attribute_enumerator = _create_attribute_enumerator,
356 .destroy = _destroy,
357 },
358 .connection_id = connection_id,
359 .src_id = src_id,
360 .dst_id = dst_id,
361 .msg_type = msg_type,
362 .attr_list = linked_list_create(),
363 .agent = agent,
364 .state = state,
365 );
366
367 return &this->public;
368 }
369
370 /**
371 * See header
372 */
373 imv_msg_t* imv_msg_create_as_reply(imv_msg_t *msg)
374 {
375 private_imv_msg_t *in;
376 TNC_UInt32 src_id;
377
378 in = (private_imv_msg_t*)msg;
379 src_id = (in->dst_id != TNC_IMVID_ANY) ?
380 in->dst_id : in->agent->get_id(in->agent);
381
382 return imv_msg_create(in->agent, in->state, in->connection_id, src_id,
383 in->src_id, in->msg_type);
384 }
385
386 /**
387 * See header
388 */
389 imv_msg_t *imv_msg_create_from_data(imv_agent_t *agent, imv_state_t *state,
390 TNC_ConnectionID connection_id,
391 TNC_MessageType msg_type,
392 chunk_t msg)
393 {
394 TNC_VendorID msg_vid;
395 TNC_MessageSubtype msg_subtype;
396
397 msg_vid = msg_type >> 8;
398 msg_subtype = msg_type & TNC_SUBTYPE_ANY;
399
400 return imv_msg_create_from_long_data(agent, state, connection_id,
401 TNC_IMCID_ANY, agent->get_id(agent),
402 msg_vid, msg_subtype, msg);
403 }
404
405 /**
406 * See header
407 */
408 imv_msg_t *imv_msg_create_from_long_data(imv_agent_t *agent, imv_state_t *state,
409 TNC_ConnectionID connection_id,
410 TNC_UInt32 src_id,
411 TNC_UInt32 dst_id,
412 TNC_VendorID msg_vid,
413 TNC_MessageSubtype msg_subtype,
414 chunk_t msg)
415 {
416 private_imv_msg_t *this;
417
418 INIT(this,
419 .public = {
420 .get_src_id = _get_src_id,
421 .get_dst_id = _get_dst_id,
422 .set_msg_type = _set_msg_type,
423 .send = _send_,
424 .send_assessment = _send_assessment,
425 .receive = _receive,
426 .add_attribute = _add_attribute,
427 .delete_attributes = _delete_attributes,
428 .create_attribute_enumerator = _create_attribute_enumerator,
429 .destroy = _destroy,
430 },
431 .connection_id = connection_id,
432 .src_id = src_id,
433 .dst_id = dst_id,
434 .msg_type = pen_type_create(msg_vid, msg_subtype),
435 .attr_list = linked_list_create(),
436 .pa_msg = pa_tnc_msg_create_from_data(msg),
437 .agent = agent,
438 .state = state,
439 );
440
441 return &this->public;
442 }
443