Support of multiple directed segmentation contracts
[strongswan.git] / src / libimcv / seg / seg_contract.c
1 /*
2 * Copyright (C) 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 "seg_contract.h"
17 #include "seg_env.h"
18 #include "ietf/ietf_attr_pa_tnc_error.h"
19 #include "tcg/seg/tcg_seg_attr_seg_env.h"
20
21 #include <utils/debug.h>
22 #include <bio/bio_writer.h>
23
24 #include <tncif_pa_subtypes.h>
25
26 typedef struct private_seg_contract_t private_seg_contract_t;
27
28 /**
29 * Private data of a seg_contract_t object.
30 */
31 struct private_seg_contract_t {
32
33 /**
34 * Public seg_contract_t interface.
35 */
36 seg_contract_t public;
37
38 /**
39 * PA-TNC message type
40 */
41 pen_type_t msg_type;
42
43 /**
44 * Maximum PA-TNC attribute size
45 */
46 uint32_t max_attr_size;
47
48 /**
49 * Maximum PA-TNC attribute segment size
50 */
51 uint32_t max_seg_size;
52
53 /**
54 * Maximum PA-TNC attribute segment size
55 */
56 uint32_t last_base_attr_id;
57
58 /**
59 * List of attribute segment envelopes
60 */
61
62 linked_list_t *seg_envs;
63
64 /**
65 * Is this a null contract?
66 */
67 bool is_null;
68
69 /**
70 * Contract role
71 */
72 bool is_issuer;
73
74 /**
75 * Issuer ID (either IMV or IMC ID)
76 */
77 TNC_UInt32 issuer_id;
78
79 /**
80 * Responder ID (either IMC or IMV ID)
81 */
82 TNC_UInt32 responder_id;
83
84 /**
85 * IMC/IMV role
86 */
87 bool is_imc;
88
89 };
90
91 METHOD(seg_contract_t, get_msg_type, pen_type_t,
92 private_seg_contract_t *this)
93 {
94 return this->msg_type;
95 }
96
97 METHOD(seg_contract_t, set_max_size, void,
98 private_seg_contract_t *this, uint32_t max_attr_size, uint32_t max_seg_size)
99 {
100 this->max_attr_size = max_attr_size;
101 this->max_seg_size = max_seg_size;
102 this->is_null = max_attr_size == SEG_CONTRACT_MAX_SIZE_VALUE &&
103 max_seg_size == SEG_CONTRACT_MAX_SIZE_VALUE;
104 }
105
106 METHOD(seg_contract_t, get_max_size, void,
107 private_seg_contract_t *this, uint32_t *max_attr_size, uint32_t *max_seg_size)
108 {
109 if (max_attr_size)
110 {
111 *max_attr_size = this->max_attr_size;
112 }
113 if (max_seg_size)
114 {
115 *max_seg_size = this->max_seg_size;
116 }
117 }
118
119 METHOD(seg_contract_t, check_size, bool,
120 private_seg_contract_t *this, pa_tnc_attr_t *attr, bool *oversize)
121 {
122 chunk_t attr_value;
123 size_t attr_len;
124
125 *oversize = FALSE;
126
127 if (this->is_null)
128 {
129 /* null segmentation contract */
130 return FALSE;
131 }
132 attr->build(attr);
133 attr_value = attr->get_value(attr);
134 attr_len = PA_TNC_ATTR_HEADER_SIZE + attr_value.len;
135
136 if (attr_len > this->max_attr_size)
137 {
138 /* oversize attribute */
139 *oversize = TRUE;
140 return FALSE;
141 }
142 if (this->max_seg_size == SEG_CONTRACT_NO_FRAGMENTATION)
143 {
144 /* no fragmentation wanted */
145 return FALSE;
146 }
147 return attr_value.len > this->max_seg_size + TCG_SEG_ATTR_SEG_ENV_HEADER;
148 }
149
150 METHOD(seg_contract_t, first_segment, pa_tnc_attr_t*,
151 private_seg_contract_t *this, pa_tnc_attr_t *attr)
152 {
153 seg_env_t *seg_env;
154
155 seg_env = seg_env_create(++this->last_base_attr_id, attr,
156 this->max_seg_size);
157 if (!seg_env)
158 {
159 return NULL;
160 }
161 this->seg_envs->insert_last(this->seg_envs, seg_env);
162
163 return seg_env->first_segment(seg_env);
164 }
165
166 METHOD(seg_contract_t, next_segment, pa_tnc_attr_t*,
167 private_seg_contract_t *this, uint32_t base_attr_id)
168 {
169 pa_tnc_attr_t *seg_env_attr = NULL;
170 seg_env_t *seg_env;
171 bool last_segment = FALSE;
172 enumerator_t *enumerator;
173
174 enumerator = this->seg_envs->create_enumerator(this->seg_envs);
175 while (enumerator->enumerate(enumerator, &seg_env))
176 {
177 if (seg_env->get_base_attr_id(seg_env) == base_attr_id)
178 {
179 seg_env_attr = seg_env->next_segment(seg_env, &last_segment);
180 if (!seg_env_attr)
181 {
182 break;
183 }
184 if (last_segment)
185 {
186 this->seg_envs->remove_at(this->seg_envs, enumerator);
187 seg_env->destroy(seg_env);
188 }
189 break;
190 }
191 }
192 enumerator->destroy(enumerator);
193
194 return seg_env_attr;
195 }
196
197 METHOD(seg_contract_t, add_segment, pa_tnc_attr_t*,
198 private_seg_contract_t *this, pa_tnc_attr_t *attr, pa_tnc_attr_t **error,
199 bool *more)
200 {
201 tcg_seg_attr_seg_env_t *seg_env_attr;
202 seg_env_t *current, *seg_env = NULL;
203 pa_tnc_attr_t *base_attr;
204 pen_type_t error_code;
205 uint32_t base_attr_id;
206 uint8_t flags;
207 chunk_t segment_data, msg_info;
208 enumerator_t *enumerator;
209
210 seg_env_attr = (tcg_seg_attr_seg_env_t*)attr;
211 base_attr_id = seg_env_attr->get_base_attr_id(seg_env_attr);
212 segment_data = seg_env_attr->get_segment(seg_env_attr, &flags);
213 *more = flags & SEG_ENV_FLAG_MORE;
214 *error = NULL;
215
216 enumerator = this->seg_envs->create_enumerator(this->seg_envs);
217 while (enumerator->enumerate(enumerator, &current))
218 {
219 if (current->get_base_attr_id(current) == base_attr_id)
220 {
221 seg_env = current;
222 this->seg_envs->remove_at(this->seg_envs, enumerator);
223 break;
224 }
225 }
226 enumerator->destroy(enumerator);
227
228 if (flags & SEG_ENV_FLAG_START)
229 {
230 if (seg_env)
231 {
232 DBG1(DBG_TNC, "base attribute ID %d is already in use",
233 base_attr_id);
234 this->seg_envs->insert_last(this->seg_envs, seg_env);
235 return NULL;
236 }
237 DBG2(DBG_TNC, "received first segment for base attribute ID %d "
238 "(%d bytes)", base_attr_id, segment_data.len);
239 seg_env = seg_env_create_from_data(base_attr_id, segment_data,
240 this->max_seg_size, error);
241 if (!seg_env)
242 {
243 return NULL;
244 }
245 }
246 else
247 {
248 if (!seg_env)
249 {
250 DBG1(DBG_TNC, "base attribute ID %d not found", base_attr_id);
251 return NULL;
252 }
253 DBG2(DBG_TNC, "received %s segment for base attribute ID %d "
254 "(%d bytes)", (*more) ? "next" : "last", base_attr_id,
255 segment_data.len);
256 if (!seg_env->add_segment(seg_env, segment_data, error))
257 {
258 seg_env->destroy(seg_env);
259 return NULL;
260 }
261 }
262 base_attr = seg_env->get_base_attr(seg_env);
263
264 if (*more)
265 {
266 /* reinsert into list since more segments are to come */
267 this->seg_envs->insert_last(this->seg_envs, seg_env);
268 }
269 else
270 {
271 /* added the last segment */
272 if (!base_attr)
273 {
274 /* base attribute waits for more data */
275 DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute value");
276 msg_info = seg_env->get_base_attr_info(seg_env);
277 error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER);
278 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
279 msg_info, PA_TNC_ATTR_INFO_SIZE);
280 }
281 seg_env->destroy(seg_env);
282 }
283 return base_attr;
284 }
285
286 METHOD(seg_contract_t, is_issuer, bool,
287 private_seg_contract_t *this)
288 {
289 return this->is_issuer;
290 }
291
292 METHOD(seg_contract_t, is_null, bool,
293 private_seg_contract_t *this)
294 {
295 return this->is_null;
296 }
297
298 METHOD(seg_contract_t, set_responder, void,
299 private_seg_contract_t *this, TNC_UInt32 responder_id)
300 {
301 this->responder_id = responder_id;
302 }
303
304 METHOD(seg_contract_t, get_responder, TNC_UInt32,
305 private_seg_contract_t *this)
306 {
307 return this->responder_id;
308 }
309
310 METHOD(seg_contract_t, get_issuer, TNC_UInt32,
311 private_seg_contract_t *this)
312 {
313 return this->issuer_id;
314 }
315
316 METHOD(seg_contract_t, clone_, seg_contract_t*,
317 private_seg_contract_t *this)
318 {
319 private_seg_contract_t *clone;
320
321 clone = malloc_thing(private_seg_contract_t);
322 memcpy(clone, this, sizeof(private_seg_contract_t));
323 clone->seg_envs = linked_list_create();
324
325 return &clone->public;
326 }
327
328 METHOD(seg_contract_t, get_info_string, void,
329 private_seg_contract_t *this, char *buf, size_t len, bool request)
330 {
331 enum_name_t *pa_subtype_names;
332 uint32_t msg_vid, msg_subtype;
333 char *pos = buf;
334 int written;
335
336 /* nul-terminate the string buffer */
337 buf[--len] = '\0';
338
339 if (this->is_issuer && request)
340 {
341 written = snprintf(pos, len, "%s %d requests",
342 this->is_imc ? "IMC" : "IMV", this->issuer_id);
343 }
344 else
345 {
346 written = snprintf(pos, len, "%s %d received",
347 this->is_imc ? "IMC" : "IMV",
348 this->is_issuer ? this->issuer_id :
349 this->responder_id);
350 }
351 if (written < 0 || written > len)
352 {
353 return;
354 }
355 pos += written;
356 len -= written;
357
358 written = snprintf(pos, len, " a %ssegmentation contract%s ",
359 this->is_null ? "null" : "", request ?
360 (this->is_issuer ? "" : " request") : " response");
361 if (written < 0 || written > len)
362 {
363 return;
364 }
365 pos += written;
366 len -= written;
367
368 if ((!this->is_issuer && this->issuer_id != TNC_IMVID_ANY) ||
369 ( this->is_issuer && this->responder_id != TNC_IMVID_ANY))
370 {
371 written = snprintf(pos, len, "from %s %d ",
372 this->is_imc ? "IMV" : "IMC",
373 this->is_issuer ? this->responder_id :
374 this->issuer_id);
375 if (written < 0 || written > len)
376 {
377 return;
378 }
379 pos += written;
380 len -= written;
381 }
382
383 msg_vid = this->msg_type.vendor_id;
384 msg_subtype = this->msg_type.type;
385 pa_subtype_names = get_pa_subtype_names(msg_vid);
386 if (pa_subtype_names)
387 {
388 written = snprintf(pos, len, "for PA message type '%N/%N' "
389 "0x%06x/0x%08x", pen_names, msg_vid,
390 pa_subtype_names, msg_subtype, msg_vid,
391 msg_subtype);
392 }
393 else
394 {
395 written = snprintf(pos, len, "for PA message type '%N' "
396 "0x%06x/0x%08x", pen_names, msg_vid,
397 msg_vid, msg_subtype);
398 }
399 if (written < 0 || written > len)
400 {
401 return;
402 }
403 pos += written;
404 len -= written;
405
406 if (!this->is_null)
407 {
408 written = snprintf(pos, len, "\n maximum attribute size of %u bytes "
409 "with ", this->max_attr_size);
410 if (written < 0 || written > len)
411 {
412 return;
413 }
414 pos += written;
415 len -= written;
416
417 if (this->max_seg_size == SEG_CONTRACT_MAX_SIZE_VALUE)
418 {
419 written = snprintf(pos, len, "no segmentation");
420 }
421 else
422 {
423 written = snprintf(pos, len, "maximum segment size of %u bytes",
424 this->max_seg_size);
425 }
426 }
427 }
428
429 METHOD(seg_contract_t, destroy, void,
430 private_seg_contract_t *this)
431 {
432 this->seg_envs->destroy_offset(this->seg_envs, offsetof(seg_env_t, destroy));
433 free(this);
434 }
435
436 /**
437 * See header
438 */
439 seg_contract_t *seg_contract_create(pen_type_t msg_type,
440 uint32_t max_attr_size,
441 uint32_t max_seg_size,
442 bool is_issuer, TNC_UInt32 issuer_id,
443 bool is_imc)
444 {
445 private_seg_contract_t *this;
446
447 INIT(this,
448 .public = {
449 .get_msg_type = _get_msg_type,
450 .set_max_size = _set_max_size,
451 .get_max_size = _get_max_size,
452 .check_size = _check_size,
453 .first_segment = _first_segment,
454 .next_segment = _next_segment,
455 .add_segment = _add_segment,
456 .is_issuer = _is_issuer,
457 .is_null = _is_null,
458 .set_responder = _set_responder,
459 .get_responder = _get_responder,
460 .get_issuer = _get_issuer,
461 .clone = _clone_,
462 .get_info_string = _get_info_string,
463 .destroy = _destroy,
464 },
465 .msg_type = msg_type,
466 .max_attr_size = max_attr_size,
467 .max_seg_size = max_seg_size,
468 .seg_envs = linked_list_create(),
469 .is_issuer = is_issuer,
470 .issuer_id = issuer_id,
471 .responder_id = is_imc ? TNC_IMVID_ANY : TNC_IMCID_ANY,
472 .is_imc = is_imc,
473 .is_null = max_attr_size == SEG_CONTRACT_MAX_SIZE_VALUE &&
474 max_seg_size == SEG_CONTRACT_MAX_SIZE_VALUE,
475 );
476
477 return &this->public;
478 }
479