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