642b47935b67a5986af0acc5c9e67ca591e021cb
[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, get_msg_type, pen_type_t,
106 private_imv_msg_t *this)
107 {
108 return this->msg_type;
109 }
110
111 METHOD(imv_msg_t, add_attribute, void,
112 private_imv_msg_t *this, pa_tnc_attr_t *attr)
113 {
114 this->attr_list->insert_last(this->attr_list, attr);
115 }
116
117 METHOD(imv_msg_t, send_, TNC_Result,
118 private_imv_msg_t *this, bool excl)
119 {
120 pa_tnc_msg_t *pa_tnc_msg;
121 pa_tnc_attr_t *attr;
122 TNC_UInt32 msg_flags;
123 TNC_MessageType msg_type;
124 bool attr_added;
125 chunk_t msg;
126 enumerator_t *enumerator;
127 TNC_Result result = TNC_RESULT_SUCCESS;
128
129 while (this->attr_list->get_count(this->attr_list))
130 {
131 pa_tnc_msg = pa_tnc_msg_create(this->state->get_max_msg_len(this->state));
132 attr_added = FALSE;
133
134 enumerator = this->attr_list->create_enumerator(this->attr_list);
135 while (enumerator->enumerate(enumerator, &attr))
136 {
137 if (pa_tnc_msg->add_attribute(pa_tnc_msg, attr))
138 {
139 attr_added = TRUE;
140 }
141 else
142 {
143 if (attr_added)
144 {
145 break;
146 }
147 else
148 {
149 DBG1(DBG_IMV, "PA-TNC attribute too large to send, deleted");
150 attr->destroy(attr);
151 }
152 }
153 this->attr_list->remove_at(this->attr_list, enumerator);
154 }
155 enumerator->destroy(enumerator);
156
157 /* build and send the PA-TNC message via the IF-IMV interface */
158 if (!pa_tnc_msg->build(pa_tnc_msg))
159 {
160 pa_tnc_msg->destroy(pa_tnc_msg);
161 return TNC_RESULT_FATAL;
162 }
163 msg = pa_tnc_msg->get_encoding(pa_tnc_msg);
164 DBG3(DBG_IMV, "created PA-TNC message: %B", &msg);
165
166 if (this->state->has_long(this->state) && this->agent->send_message_long)
167 {
168 excl = excl && this->state->has_excl(this->state) &&
169 this->dst_id != TNC_IMCID_ANY;
170 msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0;
171 result = this->agent->send_message_long(this->src_id,
172 this->connection_id, msg_flags, msg.ptr, msg.len,
173 this->msg_type.vendor_id, this->msg_type.type,
174 this->dst_id);
175 }
176 else if (this->agent->send_message)
177 {
178 msg_type = (this->msg_type.vendor_id << 8) |
179 (this->msg_type.type & 0x000000ff);
180 result = this->agent->send_message(this->src_id, this->connection_id,
181 msg.ptr, msg.len, msg_type);
182 }
183
184 pa_tnc_msg->destroy(pa_tnc_msg);
185
186 if (result != TNC_RESULT_SUCCESS)
187 {
188 break;
189 }
190 }
191 return result;
192 }
193
194 METHOD(imv_msg_t, send_assessment, TNC_Result,
195 private_imv_msg_t *this)
196 {
197 TNC_IMV_Action_Recommendation rec;
198 TNC_IMV_Evaluation_Result eval;
199 pa_tnc_attr_t *attr;
200 chunk_t string = chunk_empty;
201 char *lang_code = NULL, *uri = NULL;
202 enumerator_t *e;
203
204 /* Remove any attributes that have already been constructed */
205 while (this->attr_list->remove_last(this->attr_list, (void**)&attr) == SUCCESS)
206 {
207 attr->destroy(attr);
208 }
209
210 /* Send an IETF Assessment Result attribute if enabled */
211 if (lib->settings->get_bool(lib->settings, "libimcv.assessment_result",
212 TRUE))
213 {
214 this->state->get_recommendation(this->state, &rec, &eval);
215 attr = ietf_attr_assess_result_create(eval);
216 add_attribute(this, attr);
217
218 /* Send IETF Remediation Instructions if available */
219 if (eval != TNC_IMV_EVALUATION_RESULT_COMPLIANT)
220 {
221 e = this->agent->create_language_enumerator(this->agent,
222 this->state);
223 if (this->state->get_remediation_instructions(this->state,
224 e, &string, &lang_code, &uri))
225 {
226 if (string.len && lang_code)
227 {
228 attr = ietf_attr_remediation_instr_create_from_string(string,
229 chunk_create(lang_code, strlen(lang_code)));
230 add_attribute(this, attr);
231 }
232 if (uri)
233 {
234 attr = ietf_attr_remediation_instr_create_from_uri(
235 chunk_create(uri, strlen(uri)));
236 add_attribute(this, attr);
237 }
238 }
239 e->destroy(e);
240 }
241
242 /* send PA-TNC message with the excl flag set */
243 return send_(this, TRUE);
244 }
245 return TNC_RESULT_SUCCESS;
246 }
247
248 METHOD(imv_msg_t, receive, TNC_Result,
249 private_imv_msg_t *this, bool *fatal_error)
250 {
251 enumerator_t *enumerator;
252 pa_tnc_attr_t *attr;
253 chunk_t msg;
254
255 if (this->state->has_long(this->state))
256 {
257 if (this->dst_id != TNC_IMVID_ANY)
258 {
259 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
260 "from IMC %u to IMV %u",
261 this->agent->get_id(this->agent),
262 this->agent->get_name(this->agent),
263 this->connection_id, this->src_id, this->dst_id);
264 }
265 else
266 {
267 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
268 "from IMC %u", this->agent->get_id(this->agent),
269 this->agent->get_name(this->agent),
270 this->connection_id, this->src_id);
271 }
272 }
273 else
274 {
275 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u",
276 this->agent->get_id(this->agent),
277 this->agent->get_name(this->agent),
278 this->connection_id);
279 }
280 msg = this->pa_msg->get_encoding(this->pa_msg);
281 DBG3(DBG_IMV, "%B", &msg);
282
283 switch (this->pa_msg->process(this->pa_msg))
284 {
285 case SUCCESS:
286 break;
287 case VERIFY_ERROR:
288 {
289 imv_msg_t *error_msg;
290 TNC_Result result;
291
292 error_msg = imv_msg_create_as_reply(&this->public);
293
294 /* extract and copy by reference all error attributes */
295 enumerator = this->pa_msg->create_error_enumerator(this->pa_msg);
296 while (enumerator->enumerate(enumerator, &attr))
297 {
298 error_msg->add_attribute(error_msg, attr->get_ref(attr));
299 }
300 enumerator->destroy(enumerator);
301
302 /*
303 * send the PA-TNC message containing all error attributes
304 * with the excl flag set
305 */
306 result = error_msg->send(error_msg, TRUE);
307 error_msg->destroy(error_msg);
308 return result;
309 }
310 case FAILED:
311 default:
312 return TNC_RESULT_FATAL;
313 }
314
315 /* preprocess any received IETF standard error attributes */
316 *fatal_error = this->pa_msg->process_ietf_std_errors(this->pa_msg);
317
318 return TNC_RESULT_SUCCESS;
319 }
320
321 METHOD(imv_msg_t, get_attribute_count, int,
322 private_imv_msg_t *this)
323 {
324 return this->attr_list->get_count(this->attr_list);
325 }
326
327 METHOD(imv_msg_t, create_attribute_enumerator, enumerator_t*,
328 private_imv_msg_t *this)
329 {
330 return this->pa_msg->create_attribute_enumerator(this->pa_msg);
331 }
332
333 METHOD(imv_msg_t, get_encoding, chunk_t,
334 private_imv_msg_t *this)
335 {
336 if (this->pa_msg)
337 {
338 return this->pa_msg->get_encoding(this->pa_msg);
339 }
340 return chunk_empty;
341 }
342
343 METHOD(imv_msg_t, destroy, void,
344 private_imv_msg_t *this)
345 {
346 this->attr_list->destroy_offset(this->attr_list,
347 offsetof(pa_tnc_attr_t, destroy));
348 DESTROY_IF(this->pa_msg);
349 free(this);
350 }
351
352 /**
353 * See header
354 */
355 imv_msg_t *imv_msg_create(imv_agent_t *agent, imv_state_t *state,
356 TNC_ConnectionID connection_id,
357 TNC_UInt32 src_id, TNC_UInt32 dst_id,
358 pen_type_t msg_type)
359 {
360 private_imv_msg_t *this;
361
362 INIT(this,
363 .public = {
364 .get_src_id = _get_src_id,
365 .get_dst_id = _get_dst_id,
366 .set_msg_type = _set_msg_type,
367 .get_msg_type = _get_msg_type,
368 .send = _send_,
369 .send_assessment = _send_assessment,
370 .receive = _receive,
371 .add_attribute = _add_attribute,
372 .get_attribute_count = _get_attribute_count,
373 .create_attribute_enumerator = _create_attribute_enumerator,
374 .get_encoding = _get_encoding,
375 .destroy = _destroy,
376 },
377 .connection_id = connection_id,
378 .src_id = src_id,
379 .dst_id = dst_id,
380 .msg_type = msg_type,
381 .attr_list = linked_list_create(),
382 .agent = agent,
383 .state = state,
384 );
385
386 return &this->public;
387 }
388
389 /**
390 * See header
391 */
392 imv_msg_t* imv_msg_create_as_reply(imv_msg_t *msg)
393 {
394 private_imv_msg_t *in;
395 TNC_UInt32 src_id;
396
397 in = (private_imv_msg_t*)msg;
398 src_id = (in->dst_id != TNC_IMVID_ANY) ?
399 in->dst_id : in->agent->get_id(in->agent);
400
401 return imv_msg_create(in->agent, in->state, in->connection_id, src_id,
402 in->src_id, in->msg_type);
403 }
404
405 /**
406 * See header
407 */
408 imv_msg_t *imv_msg_create_from_data(imv_agent_t *agent, imv_state_t *state,
409 TNC_ConnectionID connection_id,
410 TNC_MessageType msg_type,
411 chunk_t msg)
412 {
413 TNC_VendorID msg_vid;
414 TNC_MessageSubtype msg_subtype;
415
416 msg_vid = msg_type >> 8;
417 msg_subtype = msg_type & TNC_SUBTYPE_ANY;
418
419 return imv_msg_create_from_long_data(agent, state, connection_id,
420 TNC_IMCID_ANY, agent->get_id(agent),
421 msg_vid, msg_subtype, msg);
422 }
423
424 /**
425 * See header
426 */
427 imv_msg_t *imv_msg_create_from_long_data(imv_agent_t *agent, imv_state_t *state,
428 TNC_ConnectionID connection_id,
429 TNC_UInt32 src_id,
430 TNC_UInt32 dst_id,
431 TNC_VendorID msg_vid,
432 TNC_MessageSubtype msg_subtype,
433 chunk_t msg)
434 {
435 private_imv_msg_t *this;
436
437 this = (private_imv_msg_t*)imv_msg_create(agent, state,
438 connection_id, src_id, dst_id,
439 pen_type_create(msg_vid, msg_subtype));
440 this->pa_msg = pa_tnc_msg_create_from_data(msg);
441
442 return &this->public;
443 }