Use python-based swidGenerator to generated SWID tags
[strongswan.git] / src / libimcv / pa_tnc / pa_tnc_msg.c
1 /*
2 * Copyright (C) 2011-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 "imcv.h"
17 #include "pa_tnc_msg.h"
18 #include "ietf/ietf_attr_pa_tnc_error.h"
19
20 #include <bio/bio_writer.h>
21 #include <bio/bio_reader.h>
22 #include <collections/linked_list.h>
23 #include <pen/pen.h>
24 #include <utils/debug.h>
25
26 typedef struct private_pa_tnc_msg_t private_pa_tnc_msg_t;
27
28 /**
29 * PA-TNC message header
30 *
31 * 1 2 3
32 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
33 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34 * | Version | Reserved |
35 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36 * | Message Identifier |
37 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38 */
39
40 #define PA_TNC_RESERVED 0x000000
41
42 /**
43 * PA-TNC attribute
44 *
45 * 1 2 3
46 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
47 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48 * | Flags | PA-TNC Attribute Vendor ID |
49 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50 * | PA-TNC Attribute Type |
51 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52 * | PA-TNC Attribute Length |
53 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54 * | Attribute Value (Variable Length) |
55 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56 */
57
58 #define PA_TNC_ATTR_FLAG_NONE 0x00
59 #define PA_TNC_ATTR_FLAG_NOSKIP (1<<7)
60 #define PA_TNC_ATTR_INFO_SIZE 8
61
62 /**
63 * Private data of a pa_tnc_msg_t object.
64 *
65 */
66 struct private_pa_tnc_msg_t {
67
68 /**
69 * Public pa_tnc_msg_t interface.
70 */
71 pa_tnc_msg_t public;
72
73 /**
74 * List of PA-TNC attributes
75 */
76 linked_list_t *attributes;
77
78 /**
79 * linked list of PA-TNC error messages
80 */
81 linked_list_t *errors;
82
83 /**
84 * Message identifier
85 */
86 uint32_t identifier;
87
88 /**
89 * Current PA-TNC Message size
90 */
91 size_t msg_len;
92
93 /**
94 * Maximum PA-TNC Message size
95 */
96 size_t max_msg_len;
97
98 /**
99 * Encoded message
100 */
101 chunk_t encoding;
102 };
103
104 METHOD(pa_tnc_msg_t, get_encoding, chunk_t,
105 private_pa_tnc_msg_t *this)
106 {
107 return this->encoding;
108 }
109
110 METHOD(pa_tnc_msg_t, add_attribute, bool,
111 private_pa_tnc_msg_t *this, pa_tnc_attr_t *attr)
112 {
113 chunk_t attr_value;
114 size_t attr_len;
115
116 attr->build(attr);
117 attr_value = attr->get_value(attr);
118 attr_len = PA_TNC_ATTR_HEADER_SIZE + attr_value.len;
119
120 if (this->max_msg_len && this->msg_len + attr_len > this->max_msg_len)
121 {
122 /* attribute just does not fit into this message */
123 return FALSE;
124 }
125 this->msg_len += attr_len;
126
127 this->attributes->insert_last(this->attributes, attr);
128 return TRUE;
129 }
130
131 METHOD(pa_tnc_msg_t, build, bool,
132 private_pa_tnc_msg_t *this)
133 {
134 bio_writer_t *writer;
135 enumerator_t *enumerator;
136 pa_tnc_attr_t *attr;
137 enum_name_t *pa_attr_names;
138 pen_type_t type;
139 uint8_t flags;
140 chunk_t value;
141 nonce_gen_t *ng;
142
143 /* generate a nonce as a message identifier */
144 ng = lib->crypto->create_nonce_gen(lib->crypto);
145 if (!ng || !ng->get_nonce(ng, 4, (uint8_t*)&this->identifier))
146 {
147 DBG1(DBG_TNC, "failed to generate random PA-TNC message identifier");
148 DESTROY_IF(ng);
149 return FALSE;
150 }
151 ng->destroy(ng);
152 DBG1(DBG_TNC, "creating PA-TNC message with ID 0x%08x", this->identifier);
153
154 /* build message header */
155 writer = bio_writer_create(this->msg_len);
156 writer->write_uint8 (writer, PA_TNC_VERSION);
157 writer->write_uint24(writer, PA_TNC_RESERVED);
158 writer->write_uint32(writer, this->identifier);
159
160 /* append encoded value of PA-TNC attributes */
161 enumerator = this->attributes->create_enumerator(this->attributes);
162 while (enumerator->enumerate(enumerator, &attr))
163 {
164 type = attr->get_type(attr);
165 value = attr->get_value(attr);
166 flags = attr->get_noskip_flag(attr) ? PA_TNC_ATTR_FLAG_NOSKIP :
167 PA_TNC_ATTR_FLAG_NONE;
168
169 pa_attr_names = imcv_pa_tnc_attributes->get_names(imcv_pa_tnc_attributes,
170 type.vendor_id);
171 if (pa_attr_names)
172 {
173 DBG2(DBG_TNC, "creating PA-TNC attribute type '%N/%N' "
174 "0x%06x/0x%08x", pen_names, type.vendor_id,
175 pa_attr_names, type.type, type.vendor_id, type.type);
176 }
177 else
178 {
179 DBG2(DBG_TNC, "creating PA-TNC attribute type '%N' "
180 "0x%06x/0x%08x", pen_names, type.vendor_id,
181 type.vendor_id, type.type);
182 }
183 DBG3(DBG_TNC, "%B", &value);
184
185 writer->write_uint8 (writer, flags);
186 writer->write_uint24(writer, type.vendor_id);
187 writer->write_uint32(writer, type.type);
188 writer->write_uint32(writer, PA_TNC_ATTR_HEADER_SIZE + value.len);
189 writer->write_data (writer, value);
190 }
191 enumerator->destroy(enumerator);
192
193 free(this->encoding.ptr);
194 this->encoding = writer->extract_buf(writer);
195 writer->destroy(writer);
196
197 return TRUE;
198 }
199
200 METHOD(pa_tnc_msg_t, process, status_t,
201 private_pa_tnc_msg_t *this)
202 {
203 bio_reader_t *reader;
204 pa_tnc_attr_t *error;
205 uint8_t version;
206 uint32_t reserved, offset, attr_offset;
207 pen_type_t error_code = { PEN_IETF, PA_ERROR_INVALID_PARAMETER };
208
209 /* process message header */
210 if (this->encoding.len < PA_TNC_HEADER_SIZE)
211 {
212 DBG1(DBG_TNC, "%u bytes insufficient to parse PA-TNC message header",
213 this->encoding.len);
214 return FAILED;
215 }
216 reader = bio_reader_create(this->encoding);
217 reader->read_uint8 (reader, &version);
218 reader->read_uint24(reader, &reserved);
219 reader->read_uint32(reader, &this->identifier);
220 DBG1(DBG_TNC, "processing PA-TNC message with ID 0x%08x", this->identifier);
221
222 if (version != PA_TNC_VERSION)
223 {
224 DBG1(DBG_TNC, "PA-TNC version %u not supported", version);
225 error_code = pen_type_create(PEN_IETF, PA_ERROR_VERSION_NOT_SUPPORTED);
226 error = ietf_attr_pa_tnc_error_create(error_code, this->encoding);
227 goto err;
228 }
229
230 /* offset of the first PA-TNC attribute in the PA-TNC message */
231 offset = PA_TNC_HEADER_SIZE;
232
233 /* pre-process PA-TNC attributes */
234 while (reader->remaining(reader) >= PA_TNC_ATTR_HEADER_SIZE)
235 {
236 pen_t vendor_id;
237 uint8_t flags;
238 uint32_t type, length;
239 chunk_t value, attr_info;
240 pa_tnc_attr_t *attr;
241 enum_name_t *pa_attr_names;
242 ietf_attr_pa_tnc_error_t *error_attr;
243
244 attr_info = reader->peek(reader);
245 attr_info.len = PA_TNC_ATTR_INFO_SIZE;
246 reader->read_uint8 (reader, &flags);
247 reader->read_uint24(reader, &vendor_id);
248 reader->read_uint32(reader, &type);
249 reader->read_uint32(reader, &length);
250
251 pa_attr_names = imcv_pa_tnc_attributes->get_names(imcv_pa_tnc_attributes,
252 vendor_id);
253 if (pa_attr_names)
254 {
255 DBG2(DBG_TNC, "processing PA-TNC attribute type '%N/%N' "
256 "0x%06x/0x%08x", pen_names, vendor_id,
257 pa_attr_names, type, vendor_id, type);
258 }
259 else
260 {
261 DBG2(DBG_TNC, "processing PA-TNC attribute type '%N' "
262 "0x%06x/0x%08x", pen_names, vendor_id,
263 vendor_id, type);
264 }
265
266 if (length < PA_TNC_ATTR_HEADER_SIZE)
267 {
268 DBG1(DBG_TNC, "%u bytes too small for PA-TNC attribute length",
269 length);
270 error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
271 this->encoding, offset + PA_TNC_ATTR_INFO_SIZE);
272 goto err;
273 }
274
275 if (!reader->read_data(reader, length - PA_TNC_ATTR_HEADER_SIZE, &value))
276 {
277 DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute value");
278 error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
279 this->encoding, offset + PA_TNC_ATTR_INFO_SIZE);
280 goto err;
281 }
282 DBG3(DBG_TNC, "%B", &value);
283
284 if (vendor_id == PEN_RESERVED)
285 {
286 error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
287 this->encoding, offset + 1);
288 goto err;
289 }
290 if (type == IETF_ATTR_RESERVED)
291 {
292 error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
293 this->encoding, offset + 4);
294 goto err;
295 }
296 attr = imcv_pa_tnc_attributes->create(imcv_pa_tnc_attributes,
297 vendor_id, type, value);
298 if (!attr)
299 {
300 if (flags & PA_TNC_ATTR_FLAG_NOSKIP)
301 {
302 DBG1(DBG_TNC, "unsupported PA-TNC attribute with NOSKIP flag");
303 error_code = pen_type_create(PEN_IETF,
304 PA_ERROR_ATTR_TYPE_NOT_SUPPORTED);
305 error = ietf_attr_pa_tnc_error_create(error_code,
306 this->encoding);
307 error_attr = (ietf_attr_pa_tnc_error_t*)error;
308 error_attr->set_attr_info(error_attr, attr_info);
309 goto err;
310 }
311 else
312 {
313 DBG1(DBG_TNC, "skipping unsupported PA-TNC attribute");
314 offset += length;
315 continue;
316 }
317 }
318
319 if (attr->process(attr, &attr_offset) != SUCCESS)
320 {
321 attr->destroy(attr);
322 if (vendor_id == PEN_IETF && type == IETF_ATTR_PA_TNC_ERROR)
323 {
324 /* error while processing a PA-TNC error attribute - abort */
325 reader->destroy(reader);
326 return FAILED;
327 }
328 error_code = pen_type_create(PEN_IETF,
329 PA_ERROR_INVALID_PARAMETER);
330 error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
331 this->encoding,
332 offset + PA_TNC_ATTR_HEADER_SIZE + attr_offset);
333 goto err;
334 }
335 this->attributes->insert_last(this->attributes, attr);
336 offset += length;
337 }
338
339 if (reader->remaining(reader) == 0)
340 {
341 reader->destroy(reader);
342 return SUCCESS;
343 }
344 DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute header");
345 error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
346 this->encoding, offset);
347
348 err:
349 reader->destroy(reader);
350 this->errors->insert_last(this->errors, error);
351 return VERIFY_ERROR;
352 }
353
354 METHOD(pa_tnc_msg_t, process_ietf_std_errors, bool,
355 private_pa_tnc_msg_t *this)
356 {
357 enumerator_t *enumerator;
358 pa_tnc_attr_t *attr;
359 pen_type_t type;
360 bool fatal_error = FALSE;
361
362 enumerator = this->attributes->create_enumerator(this->attributes);
363 while (enumerator->enumerate(enumerator, &attr))
364 {
365 type = attr->get_type(attr);
366
367 if (type.vendor_id == PEN_IETF && type.type == IETF_ATTR_PA_TNC_ERROR)
368 {
369 ietf_attr_pa_tnc_error_t *error_attr;
370 pen_type_t error_code;
371 chunk_t msg_info, attr_info;
372 uint32_t offset;
373
374 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
375 error_code = error_attr->get_error_code(error_attr);
376 msg_info = error_attr->get_msg_info(error_attr);
377
378 /* skip errors from non-IETF namespaces */
379 if (error_code.vendor_id != PEN_IETF)
380 {
381 continue;
382 }
383 DBG1(DBG_TNC, "received PA-TNC error '%N' concerning message "
384 "0x%08x/0x%08x", pa_tnc_error_code_names, error_code.type,
385 untoh32(msg_info.ptr), untoh32(msg_info.ptr + 4));
386
387 switch (error_code.type)
388 {
389 case PA_ERROR_INVALID_PARAMETER:
390 offset = error_attr->get_offset(error_attr);
391 DBG1(DBG_TNC, " occurred at offset of %u bytes", offset);
392 break;
393 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
394 attr_info = error_attr->get_attr_info(error_attr);
395 DBG1(DBG_TNC, " unsupported attribute %#B", &attr_info);
396 break;
397 default:
398 break;
399 }
400 fatal_error = TRUE;
401 }
402 }
403 enumerator->destroy(enumerator);
404
405 return fatal_error;
406 }
407
408 METHOD(pa_tnc_msg_t, create_attribute_enumerator, enumerator_t*,
409 private_pa_tnc_msg_t *this)
410 {
411 return this->attributes->create_enumerator(this->attributes);
412 }
413
414 METHOD(pa_tnc_msg_t, create_error_enumerator, enumerator_t*,
415 private_pa_tnc_msg_t *this)
416 {
417 return this->errors->create_enumerator(this->errors);
418 }
419
420 METHOD(pa_tnc_msg_t, destroy, void,
421 private_pa_tnc_msg_t *this)
422 {
423 this->attributes->destroy_offset(this->attributes,
424 offsetof(pa_tnc_attr_t, destroy));
425 this->errors->destroy_offset(this->errors,
426 offsetof(pa_tnc_attr_t, destroy));
427 free(this->encoding.ptr);
428 free(this);
429 }
430
431 /**
432 * See header
433 */
434 pa_tnc_msg_t *pa_tnc_msg_create(size_t max_msg_len)
435 {
436 private_pa_tnc_msg_t *this;
437
438 INIT(this,
439 .public = {
440 .get_encoding = _get_encoding,
441 .add_attribute = _add_attribute,
442 .build = _build,
443 .process = _process,
444 .process_ietf_std_errors = _process_ietf_std_errors,
445 .create_attribute_enumerator = _create_attribute_enumerator,
446 .create_error_enumerator = _create_error_enumerator,
447 .destroy = _destroy,
448 },
449 .attributes = linked_list_create(),
450 .errors = linked_list_create(),
451 .msg_len = PA_TNC_HEADER_SIZE,
452 .max_msg_len = max_msg_len,
453 );
454
455 return &this->public;
456 }
457
458 /**
459 * See header
460 */
461 pa_tnc_msg_t *pa_tnc_msg_create_from_data(chunk_t data)
462 {
463 private_pa_tnc_msg_t *this;
464
465 INIT(this,
466 .public = {
467 .get_encoding = _get_encoding,
468 .add_attribute = _add_attribute,
469 .build = _build,
470 .process = _process,
471 .process_ietf_std_errors = _process_ietf_std_errors,
472 .create_attribute_enumerator = _create_attribute_enumerator,
473 .create_error_enumerator = _create_error_enumerator,
474 .destroy = _destroy,
475 },
476 .encoding = chunk_clone(data),
477 .attributes = linked_list_create(),
478 .errors = linked_list_create(),
479 );
480
481 return &this->public;
482 }
483