Implemented IF-M segmentation
[strongswan.git] / src / libimcv / imv / imv_msg.c
1 /*
2 * Copyright (C) 2012-2014 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 #include <tncif_pa_subtypes.h>
24
25 #include <tcg/seg/tcg_seg_attr_max_size.h>
26 #include <tcg/seg/tcg_seg_attr_seg_env.h>
27 #include <tcg/seg/tcg_seg_attr_next_seg.h>
28
29 #include <pen/pen.h>
30 #include <collections/linked_list.h>
31 #include <utils/debug.h>
32
33 typedef struct private_imv_msg_t private_imv_msg_t;
34
35 /**
36 * Private data of a imv_msg_t object.
37 *
38 */
39 struct private_imv_msg_t {
40
41 /**
42 * Public imv_msg_t interface.
43 */
44 imv_msg_t public;
45
46 /**
47 * Connection ID
48 */
49 TNC_ConnectionID connection_id;
50
51 /**
52 * source ID
53 */
54 TNC_UInt32 src_id;
55
56 /**
57 * destination ID
58 */
59 TNC_UInt32 dst_id;
60
61 /**
62 * PA-TNC message type
63 */
64 pen_type_t msg_type;
65
66 /**
67 * List of PA-TNC attributes to be sent
68 */
69 linked_list_t *attr_list;
70
71 /**
72 * PA-TNC message
73 */
74 pa_tnc_msg_t *pa_msg;
75
76 /**
77 * Assigned IMV agent
78 */
79 imv_agent_t *agent;
80
81 /**
82 * Assigned IMV state
83 */
84 imv_state_t *state;
85 };
86
87 METHOD(imv_msg_t, get_src_id, TNC_UInt32,
88 private_imv_msg_t *this)
89 {
90 return this->src_id;
91 }
92
93 METHOD(imv_msg_t, get_dst_id, TNC_UInt32,
94 private_imv_msg_t *this)
95 {
96 return this->dst_id;
97 }
98
99 METHOD(imv_msg_t, set_msg_type, void,
100 private_imv_msg_t *this, pen_type_t msg_type)
101 {
102 if (msg_type.vendor_id != this->msg_type.vendor_id ||
103 msg_type.type != this->msg_type.type)
104 {
105 this->msg_type = msg_type;
106 this->dst_id = TNC_IMCID_ANY;
107 }
108 }
109
110 METHOD(imv_msg_t, get_msg_type, pen_type_t,
111 private_imv_msg_t *this)
112 {
113 return this->msg_type;
114 }
115
116 METHOD(imv_msg_t, add_attribute, void,
117 private_imv_msg_t *this, pa_tnc_attr_t *attr)
118 {
119 this->attr_list->insert_last(this->attr_list, attr);
120 }
121
122 METHOD(imv_msg_t, send_, TNC_Result,
123 private_imv_msg_t *this, bool excl)
124 {
125 pa_tnc_msg_t *pa_tnc_msg;
126 pa_tnc_attr_t *attr;
127 TNC_UInt32 msg_flags;
128 TNC_MessageType msg_type;
129 bool attr_added, oversize;
130 chunk_t msg;
131 seg_contract_t *contract;
132 seg_contract_manager_t *contracts;
133 enumerator_t *enumerator;
134 TNC_Result result = TNC_RESULT_SUCCESS;
135
136 /* Get IF-M segmentation contract for this subtype if any */
137 contracts = this->state->get_contracts(this->state);
138 contract = contracts->get_contract(contracts, this->msg_type, FALSE);
139
140 while (this->attr_list->get_count(this->attr_list))
141 {
142 pa_tnc_msg = pa_tnc_msg_create(this->state->get_max_msg_len(this->state));
143 attr_added = FALSE;
144
145 enumerator = this->attr_list->create_enumerator(this->attr_list);
146 while (enumerator->enumerate(enumerator, &attr))
147 {
148 if (contract && contract->check_size(contract, attr, &oversize))
149 {
150 if (oversize)
151 {
152 /* TODO generate SWID error msg */
153 }
154 else
155 {
156 attr = contract->first_segment(contract, attr);
157 }
158 }
159 if (pa_tnc_msg->add_attribute(pa_tnc_msg, attr))
160 {
161 attr_added = TRUE;
162 }
163 else
164 {
165 if (attr_added)
166 {
167 break;
168 }
169 else
170 {
171 DBG1(DBG_IMV, "PA-TNC attribute too large to send, deleted");
172 attr->destroy(attr);
173 }
174 }
175 this->attr_list->remove_at(this->attr_list, enumerator);
176 }
177 enumerator->destroy(enumerator);
178
179 /* build and send the PA-TNC message via the IF-IMV interface */
180 if (!pa_tnc_msg->build(pa_tnc_msg))
181 {
182 pa_tnc_msg->destroy(pa_tnc_msg);
183 return TNC_RESULT_FATAL;
184 }
185 msg = pa_tnc_msg->get_encoding(pa_tnc_msg);
186 DBG3(DBG_IMV, "created PA-TNC message: %B", &msg);
187
188 if (this->state->has_long(this->state) && this->agent->send_message_long)
189 {
190 excl = excl && this->state->has_excl(this->state) &&
191 this->dst_id != TNC_IMCID_ANY;
192 msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0;
193 result = this->agent->send_message_long(this->src_id,
194 this->connection_id, msg_flags, msg.ptr, msg.len,
195 this->msg_type.vendor_id, this->msg_type.type,
196 this->dst_id);
197 }
198 else if (this->agent->send_message)
199 {
200 msg_type = (this->msg_type.vendor_id << 8) |
201 (this->msg_type.type & 0x000000ff);
202 result = this->agent->send_message(this->src_id, this->connection_id,
203 msg.ptr, msg.len, msg_type);
204 }
205
206 pa_tnc_msg->destroy(pa_tnc_msg);
207
208 if (result != TNC_RESULT_SUCCESS)
209 {
210 break;
211 }
212 }
213 return result;
214 }
215
216 METHOD(imv_msg_t, send_assessment, TNC_Result,
217 private_imv_msg_t *this)
218 {
219 TNC_IMV_Action_Recommendation rec;
220 TNC_IMV_Evaluation_Result eval;
221 pa_tnc_attr_t *attr;
222 chunk_t string = chunk_empty;
223 char *lang_code = NULL, *uri = NULL;
224 enumerator_t *e;
225
226 /* Remove any attributes that have already been constructed */
227 while (this->attr_list->remove_last(this->attr_list, (void**)&attr) == SUCCESS)
228 {
229 attr->destroy(attr);
230 }
231
232 /* Send an IETF Assessment Result attribute if enabled */
233 if (lib->settings->get_bool(lib->settings, "%s.imcv.assessment_result",
234 TRUE, lib->ns))
235 {
236 this->state->get_recommendation(this->state, &rec, &eval);
237 attr = ietf_attr_assess_result_create(eval);
238 add_attribute(this, attr);
239
240 /* Send IETF Remediation Instructions if available */
241 if (eval != TNC_IMV_EVALUATION_RESULT_COMPLIANT)
242 {
243 e = this->agent->create_language_enumerator(this->agent,
244 this->state);
245 if (this->state->get_remediation_instructions(this->state,
246 e, &string, &lang_code, &uri))
247 {
248 if (string.len && lang_code)
249 {
250 attr = ietf_attr_remediation_instr_create_from_string(string,
251 chunk_create(lang_code, strlen(lang_code)));
252 add_attribute(this, attr);
253 }
254 if (uri)
255 {
256 attr = ietf_attr_remediation_instr_create_from_uri(
257 chunk_create(uri, strlen(uri)));
258 add_attribute(this, attr);
259 }
260 }
261 e->destroy(e);
262 }
263
264 /* send PA-TNC message with the excl flag set */
265 return send_(this, TRUE);
266 }
267 return TNC_RESULT_SUCCESS;
268 }
269
270 METHOD(imv_msg_t, receive, TNC_Result,
271 private_imv_msg_t *this, bool *fatal_error)
272 {
273 TNC_Result result = TNC_RESULT_SUCCESS;
274 linked_list_t *non_fatal_types;
275 enumerator_t *enumerator;
276 pa_tnc_attr_t *attr;
277 chunk_t msg;
278
279 if (this->state->has_long(this->state))
280 {
281 if (this->dst_id != TNC_IMVID_ANY)
282 {
283 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
284 "from IMC %u to IMV %u",
285 this->agent->get_id(this->agent),
286 this->agent->get_name(this->agent),
287 this->connection_id, this->src_id, this->dst_id);
288 }
289 else
290 {
291 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u "
292 "from IMC %u", this->agent->get_id(this->agent),
293 this->agent->get_name(this->agent),
294 this->connection_id, this->src_id);
295 }
296 }
297 else
298 {
299 DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u",
300 this->agent->get_id(this->agent),
301 this->agent->get_name(this->agent),
302 this->connection_id);
303 }
304 msg = this->pa_msg->get_encoding(this->pa_msg);
305 DBG3(DBG_IMV, "%B", &msg);
306
307 switch (this->pa_msg->process(this->pa_msg))
308 {
309 case SUCCESS:
310 break;
311 case VERIFY_ERROR:
312 {
313 imv_msg_t *error_msg;
314
315 error_msg = imv_msg_create_as_reply(&this->public);
316
317 /* extract and copy by reference all error attributes */
318 enumerator = this->pa_msg->create_error_enumerator(this->pa_msg);
319 while (enumerator->enumerate(enumerator, &attr))
320 {
321 error_msg->add_attribute(error_msg, attr->get_ref(attr));
322 }
323 enumerator->destroy(enumerator);
324
325 /*
326 * send the PA-TNC message containing all error attributes
327 * with the excl flag set
328 */
329 result = error_msg->send(error_msg, TRUE);
330 error_msg->destroy(error_msg);
331 return result;
332 }
333 case FAILED:
334 default:
335 return TNC_RESULT_FATAL;
336 }
337
338 /* process IF-M segmentation attributes */
339 enumerator = this->pa_msg->create_attribute_enumerator(this->pa_msg);
340 while (enumerator->enumerate(enumerator, &attr))
341 {
342 uint32_t max_attr_size, max_seg_size, my_max_attr_size, my_max_seg_size;
343 imv_msg_t *out_msg;
344 seg_contract_manager_t *contracts;
345 seg_contract_t *contract;
346 char buf[BUF_LEN];
347 pen_type_t type;
348
349 type = attr->get_type(attr);
350
351 if (type.vendor_id != PEN_TCG)
352 {
353 continue;
354 }
355
356 contracts = this->state->get_contracts(this->state);
357
358 switch (type.type)
359 {
360 case TCG_SEG_MAX_ATTR_SIZE_REQ:
361 {
362 tcg_seg_attr_max_size_t *attr_cast;
363
364 attr_cast = (tcg_seg_attr_max_size_t*)attr;
365 attr_cast->get_attr_size(attr_cast, &max_attr_size,
366 &max_seg_size);
367 contract = contracts->get_contract(contracts, this->msg_type,
368 FALSE);
369 if (contract)
370 {
371 contract->set_max_size(contract, max_attr_size,
372 max_seg_size);
373 }
374 else
375 {
376 contract = seg_contract_create(this->msg_type, max_attr_size,
377 max_seg_size, FALSE, this->src_id, FALSE);
378 contracts->add_contract(contracts, contract);
379 }
380 contract->get_info_string(contract, buf, BUF_LEN, TRUE);
381 DBG2(DBG_IMV, "%s", buf);
382
383 /* Determine maximum PA-TNC attribute segment size */
384 my_max_seg_size = this->state->get_max_msg_len(this->state)
385 - PA_TNC_HEADER_SIZE
386 - PA_TNC_ATTR_HEADER_SIZE
387 - TCG_SEG_ATTR_SEG_ENV_HEADER
388 - PA_TNC_ATTR_HEADER_SIZE
389 - TCG_SEG_ATTR_MAX_SIZE_SIZE;
390
391 /* If segmentation is possible select lower segment size */
392 if (max_seg_size != SEG_CONTRACT_NO_FRAGMENTATION &&
393 max_seg_size > my_max_seg_size)
394 {
395 max_seg_size = my_max_seg_size;
396 contract->set_max_size(contract, max_attr_size,
397 max_seg_size);
398 DBG2(DBG_IMV, " lowered maximum segment size to %u bytes",
399 max_seg_size);
400 }
401
402 /* Send Maximum Attribute Size Response */
403 out_msg = imv_msg_create_as_reply(&this->public);
404 attr = tcg_seg_attr_max_size_create(max_attr_size,
405 max_seg_size, FALSE);
406 out_msg->add_attribute(out_msg, attr);
407 result = out_msg->send(out_msg, TRUE);
408 out_msg->destroy(out_msg);
409 if (result != TNC_RESULT_SUCCESS)
410 {
411 break;
412 }
413 break;
414 }
415 case TCG_SEG_MAX_ATTR_SIZE_RESP:
416 {
417 tcg_seg_attr_max_size_t *attr_cast;
418
419 attr_cast = (tcg_seg_attr_max_size_t*)attr;
420 attr_cast->get_attr_size(attr_cast, &max_attr_size,
421 &max_seg_size);
422 contract = contracts->get_contract(contracts, this->msg_type,
423 TRUE);
424 if (contract)
425 {
426 contract->get_max_size(contract, &my_max_attr_size,
427 &my_max_seg_size);
428 if (my_max_seg_size != SEG_CONTRACT_NO_FRAGMENTATION &&
429 my_max_seg_size > max_seg_size)
430 {
431 my_max_seg_size = max_seg_size;
432 contract->set_max_size(contract, my_max_attr_size,
433 my_max_seg_size);
434 }
435 contract->get_info_string(contract, buf, BUF_LEN, FALSE);
436 DBG2(DBG_IMV, "%s", buf);
437 }
438 else
439 {
440 /* TODO no request pending */
441 DBG1(DBG_IMV, "no contract for this PA message type found");
442 }
443 break;
444 }
445 case TCG_SEG_ATTR_SEG_ENV:
446 {
447 tcg_seg_attr_seg_env_t *seg_env_attr;
448 pa_tnc_attr_t *error;
449 uint32_t base_attr_id;
450 bool more;
451
452 seg_env_attr = (tcg_seg_attr_seg_env_t*)attr;
453 base_attr_id = seg_env_attr->get_base_attr_id(seg_env_attr);
454
455 contract = contracts->get_contract(contracts, this->msg_type,
456 TRUE);
457 if (!contract)
458 {
459 DBG2(DBG_IMV, "no contract for received attribute segment "
460 "with base attribute ID %u", base_attr_id);
461 continue;
462 }
463 attr = contract->add_segment(contract, attr, &error, &more);
464 if (error)
465 {
466 out_msg = imv_msg_create_as_reply(&this->public);
467 out_msg->add_attribute(out_msg, error);
468 result = out_msg->send(out_msg, TRUE);
469 out_msg->destroy(out_msg);
470 }
471 if (attr)
472 {
473 this->pa_msg->add_attribute(this->pa_msg, attr);
474 }
475 if (more)
476 {
477 /* Send Next Segment Request */
478 out_msg = imv_msg_create_as_reply(&this->public);
479 attr = tcg_seg_attr_next_seg_create(base_attr_id, FALSE);
480 out_msg->add_attribute(out_msg, attr);
481 result = out_msg->send(out_msg, TRUE);
482 out_msg->destroy(out_msg);
483 }
484 break;
485 }
486 case TCG_SEG_NEXT_SEG_REQ:
487 {
488 tcg_seg_attr_next_seg_t *attr_cast;
489 uint32_t base_attr_id;
490
491 attr_cast = (tcg_seg_attr_next_seg_t*)attr;
492 base_attr_id = attr_cast->get_base_attr_id(attr_cast);
493
494 contract = contracts->get_contract(contracts, this->msg_type,
495 FALSE);
496 if (!contract)
497 {
498 /* TODO no contract - generate error message */
499 DBG2(DBG_IMV, "no contract for received next segment "
500 "request with base attribute ID %u", base_attr_id);
501 continue;
502 }
503 attr = contract->next_segment(contract, base_attr_id);
504 if (attr)
505 {
506 out_msg = imv_msg_create_as_reply(&this->public);
507 out_msg->add_attribute(out_msg, attr);
508 result = out_msg->send(out_msg, TRUE);
509 out_msg->destroy(out_msg);
510 }
511 else
512 {
513 /* TODO no more segments - generate error message */
514 }
515 break;
516 }
517 default:
518 break;
519 }
520 }
521 enumerator->destroy(enumerator);
522
523 /* preprocess any received IETF standard error attributes */
524 non_fatal_types = this->agent->get_non_fatal_attr_types(this->agent);
525 *fatal_error = this->pa_msg->process_ietf_std_errors(this->pa_msg,
526 non_fatal_types);
527
528 return result;
529 }
530
531 METHOD(imv_msg_t, get_attribute_count, int,
532 private_imv_msg_t *this)
533 {
534 return this->attr_list->get_count(this->attr_list);
535 }
536
537 METHOD(imv_msg_t, create_attribute_enumerator, enumerator_t*,
538 private_imv_msg_t *this)
539 {
540 return this->pa_msg->create_attribute_enumerator(this->pa_msg);
541 }
542
543 METHOD(imv_msg_t, get_encoding, chunk_t,
544 private_imv_msg_t *this)
545 {
546 if (this->pa_msg)
547 {
548 return this->pa_msg->get_encoding(this->pa_msg);
549 }
550 return chunk_empty;
551 }
552
553 METHOD(imv_msg_t, destroy, void,
554 private_imv_msg_t *this)
555 {
556 this->attr_list->destroy_offset(this->attr_list,
557 offsetof(pa_tnc_attr_t, destroy));
558 DESTROY_IF(this->pa_msg);
559 free(this);
560 }
561
562 /**
563 * See header
564 */
565 imv_msg_t *imv_msg_create(imv_agent_t *agent, imv_state_t *state,
566 TNC_ConnectionID connection_id,
567 TNC_UInt32 src_id, TNC_UInt32 dst_id,
568 pen_type_t msg_type)
569 {
570 private_imv_msg_t *this;
571
572 INIT(this,
573 .public = {
574 .get_src_id = _get_src_id,
575 .get_dst_id = _get_dst_id,
576 .set_msg_type = _set_msg_type,
577 .get_msg_type = _get_msg_type,
578 .send = _send_,
579 .send_assessment = _send_assessment,
580 .receive = _receive,
581 .add_attribute = _add_attribute,
582 .get_attribute_count = _get_attribute_count,
583 .create_attribute_enumerator = _create_attribute_enumerator,
584 .get_encoding = _get_encoding,
585 .destroy = _destroy,
586 },
587 .connection_id = connection_id,
588 .src_id = src_id,
589 .dst_id = dst_id,
590 .msg_type = msg_type,
591 .attr_list = linked_list_create(),
592 .agent = agent,
593 .state = state,
594 );
595
596 return &this->public;
597 }
598
599 /**
600 * See header
601 */
602 imv_msg_t* imv_msg_create_as_reply(imv_msg_t *msg)
603 {
604 private_imv_msg_t *in;
605 TNC_UInt32 src_id;
606
607 in = (private_imv_msg_t*)msg;
608 src_id = (in->dst_id != TNC_IMVID_ANY) ?
609 in->dst_id : in->agent->get_id(in->agent);
610
611 return imv_msg_create(in->agent, in->state, in->connection_id, src_id,
612 in->src_id, in->msg_type);
613 }
614
615 /**
616 * See header
617 */
618 imv_msg_t *imv_msg_create_from_data(imv_agent_t *agent, imv_state_t *state,
619 TNC_ConnectionID connection_id,
620 TNC_MessageType msg_type,
621 chunk_t msg)
622 {
623 TNC_VendorID msg_vid;
624 TNC_MessageSubtype msg_subtype;
625
626 msg_vid = msg_type >> 8;
627 msg_subtype = msg_type & TNC_SUBTYPE_ANY;
628
629 return imv_msg_create_from_long_data(agent, state, connection_id,
630 TNC_IMCID_ANY, agent->get_id(agent),
631 msg_vid, msg_subtype, msg);
632 }
633
634 /**
635 * See header
636 */
637 imv_msg_t *imv_msg_create_from_long_data(imv_agent_t *agent, imv_state_t *state,
638 TNC_ConnectionID connection_id,
639 TNC_UInt32 src_id,
640 TNC_UInt32 dst_id,
641 TNC_VendorID msg_vid,
642 TNC_MessageSubtype msg_subtype,
643 chunk_t msg)
644 {
645 private_imv_msg_t *this;
646
647 this = (private_imv_msg_t*)imv_msg_create(agent, state,
648 connection_id, src_id, dst_id,
649 pen_type_create(msg_vid, msg_subtype));
650 this->pa_msg = pa_tnc_msg_create_from_data(msg);
651
652 return &this->public;
653 }