d7fd4362d921191b0a72225a2328ea83958abc28
[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, *lang_code;
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 if (eval != TNC_IMV_EVALUATION_RESULT_COMPLIANT)
206 {
207 e = this->agent->create_language_enumerator(this->agent,
208 this->state);
209 if (this->state->get_remediation_instructions(this->state,
210 e, &string, &lang_code))
211 {
212 attr = ietf_attr_remediation_instr_create_from_string(
213 chunk_create(string, strlen(string)),
214 chunk_create(lang_code, strlen(lang_code)));
215 add_attribute(this, attr);
216 }
217 e->destroy(e);
218 }
219
220 /* send PA-TNC message with the excl flag set */
221 return send_(this, TRUE);
222 }
223 return TNC_RESULT_SUCCESS;
224 }
225
226 METHOD(imv_msg_t, receive, TNC_Result,
227 private_imv_msg_t *this, bool *fatal_error)
228 {
229 enumerator_t *enumerator;
230 pa_tnc_attr_t *attr;
231 chunk_t msg;
232
233 if (this->state->has_long(this->state))
234 {
235 if (this->dst_id != TNC_IMVID_ANY)
236 {
237 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
238 "from IMC %u to IMV %u",
239 this->agent->get_id(this->agent),
240 this->agent->get_name(this->agent),
241 this->connection_id, this->src_id, this->dst_id);
242 }
243 else
244 {
245 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
246 "from IMC %u", this->agent->get_id(this->agent),
247 this->agent->get_name(this->agent),
248 this->connection_id, this->src_id);
249 }
250 }
251 else
252 {
253 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u",
254 this->agent->get_id(this->agent),
255 this->agent->get_name(this->agent),
256 this->connection_id);
257 }
258 msg = this->pa_msg->get_encoding(this->pa_msg);
259 DBG3(DBG_IMV, "%B", &msg);
260
261 switch (this->pa_msg->process(this->pa_msg))
262 {
263 case SUCCESS:
264 break;
265 case VERIFY_ERROR:
266 {
267 imv_msg_t *error_msg;
268 TNC_Result result;
269
270 error_msg = imv_msg_create_as_reply(&this->public);
271
272 /* extract and copy by reference all error attributes */
273 enumerator = this->pa_msg->create_error_enumerator(this->pa_msg);
274 while (enumerator->enumerate(enumerator, &attr))
275 {
276 error_msg->add_attribute(error_msg, attr->get_ref(attr));
277 }
278 enumerator->destroy(enumerator);
279
280 /*
281 * send the PA-TNC message containing all error attributes
282 * with the excl flag set
283 */
284 result = error_msg->send(error_msg, TRUE);
285 error_msg->destroy(error_msg);
286 return result;
287 }
288 case FAILED:
289 default:
290 return TNC_RESULT_FATAL;
291 }
292
293 /* preprocess any received IETF standard error attributes */
294 *fatal_error = this->pa_msg->process_ietf_std_errors(this->pa_msg);
295
296 return TNC_RESULT_SUCCESS;
297 }
298
299 METHOD(imv_msg_t, delete_attributes, void,
300 private_imv_msg_t *this)
301 {
302 pa_tnc_attr_t *attr;
303
304 while (this->attr_list->remove_last(this->attr_list, (void**)&attr) == SUCCESS)
305 {
306 attr->destroy(attr);
307 }
308 }
309
310 METHOD(imv_msg_t, create_attribute_enumerator, enumerator_t*,
311 private_imv_msg_t *this)
312 {
313 return this->pa_msg->create_attribute_enumerator(this->pa_msg);
314 }
315
316 METHOD(imv_msg_t, destroy, void,
317 private_imv_msg_t *this)
318 {
319 this->attr_list->destroy_offset(this->attr_list,
320 offsetof(pa_tnc_attr_t, destroy));
321 DESTROY_IF(this->pa_msg);
322 free(this);
323 }
324
325 /**
326 * See header
327 */
328 imv_msg_t *imv_msg_create(imv_agent_t *agent, imv_state_t *state,
329 TNC_ConnectionID connection_id,
330 TNC_UInt32 src_id, TNC_UInt32 dst_id,
331 pen_type_t msg_type)
332 {
333 private_imv_msg_t *this;
334
335 INIT(this,
336 .public = {
337 .get_src_id = _get_src_id,
338 .get_dst_id = _get_dst_id,
339 .set_msg_type = _set_msg_type,
340 .send = _send_,
341 .send_assessment = _send_assessment,
342 .receive = _receive,
343 .add_attribute = _add_attribute,
344 .delete_attributes = _delete_attributes,
345 .create_attribute_enumerator = _create_attribute_enumerator,
346 .destroy = _destroy,
347 },
348 .connection_id = connection_id,
349 .src_id = src_id,
350 .dst_id = dst_id,
351 .msg_type = msg_type,
352 .attr_list = linked_list_create(),
353 .agent = agent,
354 .state = state,
355 );
356
357 return &this->public;
358 }
359
360 /**
361 * See header
362 */
363 imv_msg_t* imv_msg_create_as_reply(imv_msg_t *msg)
364 {
365 private_imv_msg_t *in;
366 TNC_UInt32 src_id;
367
368 in = (private_imv_msg_t*)msg;
369 src_id = (in->dst_id != TNC_IMVID_ANY) ?
370 in->dst_id : in->agent->get_id(in->agent);
371
372 return imv_msg_create(in->agent, in->state, in->connection_id, src_id,
373 in->src_id, in->msg_type);
374 }
375
376 /**
377 * See header
378 */
379 imv_msg_t *imv_msg_create_from_data(imv_agent_t *agent, imv_state_t *state,
380 TNC_ConnectionID connection_id,
381 TNC_MessageType msg_type,
382 chunk_t msg)
383 {
384 TNC_VendorID msg_vid;
385 TNC_MessageSubtype msg_subtype;
386
387 msg_vid = msg_type >> 8;
388 msg_subtype = msg_type & TNC_SUBTYPE_ANY;
389
390 return imv_msg_create_from_long_data(agent, state, connection_id,
391 TNC_IMCID_ANY, agent->get_id(agent),
392 msg_vid, msg_subtype, msg);
393 }
394
395 /**
396 * See header
397 */
398 imv_msg_t *imv_msg_create_from_long_data(imv_agent_t *agent, imv_state_t *state,
399 TNC_ConnectionID connection_id,
400 TNC_UInt32 src_id,
401 TNC_UInt32 dst_id,
402 TNC_VendorID msg_vid,
403 TNC_MessageSubtype msg_subtype,
404 chunk_t msg)
405 {
406 private_imv_msg_t *this;
407
408 INIT(this,
409 .public = {
410 .get_src_id = _get_src_id,
411 .get_dst_id = _get_dst_id,
412 .set_msg_type = _set_msg_type,
413 .send = _send_,
414 .send_assessment = _send_assessment,
415 .receive = _receive,
416 .add_attribute = _add_attribute,
417 .delete_attributes = _delete_attributes,
418 .create_attribute_enumerator = _create_attribute_enumerator,
419 .destroy = _destroy,
420 },
421 .connection_id = connection_id,
422 .src_id = src_id,
423 .dst_id = dst_id,
424 .msg_type = pen_type_create(msg_vid, msg_subtype),
425 .attr_list = linked_list_create(),
426 .pa_msg = pa_tnc_msg_create_from_data(msg),
427 .agent = agent,
428 .state = state,
429 );
430
431 return &this->public;
432 }
433