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