libimcv: Implemented IETF SW PA-TNC attributes
[strongswan.git] / src / libimcv / ietf / ietf_attr_pa_tnc_error.c
1 /*
2 * Copyright (C) 2011-2017 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 "ietf_attr_pa_tnc_error.h"
17
18 #include <pa_tnc/pa_tnc_msg.h>
19 #include <bio/bio_writer.h>
20 #include <bio/bio_reader.h>
21 #include <utils/debug.h>
22
23 ENUM_BEGIN(pa_tnc_error_code_names, PA_ERROR_RESERVED,
24 PA_ERROR_ATTR_TYPE_NOT_SUPPORTED,
25 "Reserved",
26 "Invalid Parameter",
27 "Version Not Supported",
28 "Attribute Type Not Supported"
29 );
30 ENUM_NEXT(pa_tnc_error_code_names, PA_ERROR_SW,
31 PA_ERROR_SW_SUBSCRIPTION_ID_REUSE,
32 PA_ERROR_ATTR_TYPE_NOT_SUPPORTED,
33 "SW Error",
34 "SW Subscription Denied",
35 "SW Response Too Large",
36 "SW Subscription Fulfillment Error",
37 "SW Subscription ID Reuse"
38 );
39 ENUM_END(pa_tnc_error_code_names, PA_ERROR_SW_SUBSCRIPTION_ID_REUSE);
40
41 typedef struct private_ietf_attr_pa_tnc_error_t private_ietf_attr_pa_tnc_error_t;
42
43 /**
44 * PA-TNC Error Attribute Type (see section 4.2.8 of RFC 5792)
45 *
46 * 1 2 3
47 * 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
48 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49 * | Reserved | PA-TNC Error Code Vendor ID |
50 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51 * | PA-TNC Error Code |
52 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53 * | Error Information (Variable Length) |
54 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55 */
56
57 #define PA_ERROR_HEADER_SIZE 8
58 #define PA_ERROR_RESERVED 0x00
59
60 /**
61 * All IETF Error Types return the first 8 bytes of the erroneous PA-TNC message
62 *
63 * 1 2 3
64 * 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
65 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
66 * | Version | Copy of Reserved |
67 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68 * | Message Identifier |
69 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70 */
71
72 #define PA_ERROR_MSG_INFO_SIZE 8
73 #define PA_ERROR_MSG_INFO_MAX_SIZE 1024
74
75 /**
76 * "Invalid Parameter" Error Code
77 * 1 2 3
78 * 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
79 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80 * | Offset |
81 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82 */
83
84 /**
85 * "Version Not Supported" Error Code
86 *
87 * 1 2 3
88 * 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
89 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90 * | Max Version | Min Version | Reserved |
91 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
92 */
93
94 #define PA_ERROR_VERSION_RESERVED 0x0000
95
96 /**
97 * "Attribute Type Not Supported" Error Code
98 *
99 * 1 2 3
100 * 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
101 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102 * | Flags | PA-TNC Attribute Vendor ID |
103 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104 * | PA-TNC Attribute Type |
105 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
106 */
107
108 #define PA_ERROR_ATTR_INFO_SIZE 8
109
110 /**
111 * Private data of an ietf_attr_pa_tnc_error_t object.
112 */
113 struct private_ietf_attr_pa_tnc_error_t {
114
115 /**
116 * Public members of ietf_attr_pa_tnc_error_t
117 */
118 ietf_attr_pa_tnc_error_t public;
119
120 /**
121 * Vendor-specific attribute type
122 */
123 pen_type_t type;
124
125 /**
126 * Length of attribute value
127 */
128 size_t length;
129
130 /**
131 * Attribute value or segment
132 */
133 chunk_t value;
134
135 /**
136 * Noskip flag
137 */
138 bool noskip_flag;
139
140 /**
141 * Vendor-specific error code
142 */
143 pen_type_t error_code;
144
145 /**
146 * First 8 bytes of erroneous PA-TNC message
147 */
148 chunk_t msg_info;
149
150 /**
151 * Flags of unsupported PA-TNC attribute
152 */
153 uint8_t flags;
154
155 /**
156 * Vendor ID and type of unsupported PA-TNC attribute
157 */
158 pen_type_t unsupported_type;
159
160 /**
161 * PA-TNC error offset
162 */
163 uint32_t error_offset;
164
165 /**
166 * Reference count
167 */
168 refcount_t ref;
169 };
170
171 METHOD(pa_tnc_attr_t, get_type, pen_type_t,
172 private_ietf_attr_pa_tnc_error_t *this)
173 {
174 return this->type;
175 }
176
177 METHOD(pa_tnc_attr_t, get_value, chunk_t,
178 private_ietf_attr_pa_tnc_error_t *this)
179 {
180 return this->value;
181 }
182
183 METHOD(pa_tnc_attr_t, get_noskip_flag, bool,
184 private_ietf_attr_pa_tnc_error_t *this)
185 {
186 return this->noskip_flag;
187 }
188
189 METHOD(pa_tnc_attr_t, set_noskip_flag,void,
190 private_ietf_attr_pa_tnc_error_t *this, bool noskip)
191 {
192 this->noskip_flag = noskip;
193 }
194
195 METHOD(pa_tnc_attr_t, build, void,
196 private_ietf_attr_pa_tnc_error_t *this)
197 {
198 bio_writer_t *writer;
199
200 if (this->value.ptr)
201 {
202 return;
203 }
204 writer = bio_writer_create(PA_ERROR_HEADER_SIZE + PA_ERROR_MSG_INFO_SIZE);
205 writer->write_uint8 (writer, PA_ERROR_RESERVED);
206 writer->write_uint24(writer, this->error_code.vendor_id);
207 writer->write_uint32(writer, this->error_code.type);
208 writer->write_data (writer, this->msg_info);
209
210 if (this->error_code.vendor_id == PEN_IETF)
211 {
212 switch (this->error_code.type)
213 {
214 case PA_ERROR_INVALID_PARAMETER:
215 writer->write_uint32(writer, this->error_offset);
216 break;
217 case PA_ERROR_VERSION_NOT_SUPPORTED:
218 writer->write_uint8 (writer, PA_TNC_VERSION);
219 writer->write_uint8 (writer, PA_TNC_VERSION);
220 writer->write_uint16(writer, PA_ERROR_VERSION_RESERVED);
221 break;
222 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
223 writer->write_uint8 (writer, this->flags);
224 writer->write_uint24(writer, this->unsupported_type.vendor_id);
225 writer->write_uint32(writer, this->unsupported_type.type);
226 break;
227 default:
228 break;
229 }
230 }
231 this->value = writer->extract_buf(writer);
232 this->length = this->value.len;
233 writer->destroy(writer);
234 }
235
236 METHOD(pa_tnc_attr_t, process, status_t,
237 private_ietf_attr_pa_tnc_error_t *this, uint32_t *offset)
238 {
239 bio_reader_t *reader;
240 uint8_t reserved;
241 uint32_t vendor_id, type;
242
243 *offset = 0;
244
245 if (this->value.len < this->length)
246 {
247 return NEED_MORE;
248 }
249 if (this->value.len < PA_ERROR_HEADER_SIZE)
250 {
251 DBG1(DBG_TNC, "insufficient data for PA-TNC error header");
252 return FAILED;
253 }
254 reader = bio_reader_create(this->value);
255 reader->read_uint8 (reader, &reserved);
256 reader->read_uint24(reader, &this->error_code.vendor_id);
257 reader->read_uint32(reader, &this->error_code.type);
258
259 if (this->error_code.vendor_id == PEN_IETF &&
260 this->error_code.type <= PA_ERROR_PA_TNC_MSG_ROOF)
261 {
262 if (!reader->read_data(reader, PA_ERROR_MSG_INFO_SIZE, &this->msg_info))
263 {
264 reader->destroy(reader);
265 DBG1(DBG_TNC, "insufficient data for IETF error information");
266 *offset = PA_ERROR_HEADER_SIZE;
267 return FAILED;
268 }
269 this->msg_info = chunk_clone(this->msg_info);
270
271 switch (this->error_code.type)
272 {
273 case PA_ERROR_INVALID_PARAMETER:
274 if (!reader->read_uint32(reader, &this->error_offset))
275 {
276 reader->destroy(reader);
277 DBG1(DBG_TNC, "insufficient data for error offset field");
278 *offset = PA_ERROR_HEADER_SIZE + PA_ERROR_MSG_INFO_SIZE;
279 return FAILED;
280 }
281 break;
282 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
283 if (reader->remaining(reader) < PA_ERROR_ATTR_INFO_SIZE)
284 {
285 reader->destroy(reader);
286 DBG1(DBG_TNC, "insufficient data for unsupported attribute "
287 "information");
288 *offset = PA_ERROR_HEADER_SIZE + PA_ERROR_MSG_INFO_SIZE;
289 return FAILED;
290 }
291 reader->read_uint8 (reader, &this->flags);
292 reader->read_uint24(reader, &vendor_id);
293 reader->read_uint32(reader, &type);
294 this->unsupported_type = pen_type_create(vendor_id, type);
295 break;
296 default:
297 break;
298 }
299 }
300 else
301 {
302 reader->read_data(reader, reader->remaining(reader), &this->msg_info);
303 this->msg_info = chunk_clone(this->msg_info);
304 }
305 reader->destroy(reader);
306
307 return SUCCESS;
308 }
309
310 METHOD(pa_tnc_attr_t, add_segment, void,
311 private_ietf_attr_pa_tnc_error_t *this, chunk_t segment)
312 {
313 this->value = chunk_cat("mc", this->value, segment);
314 }
315
316 METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*,
317 private_ietf_attr_pa_tnc_error_t *this)
318 {
319 ref_get(&this->ref);
320 return &this->public.pa_tnc_attribute;
321 }
322
323 METHOD(pa_tnc_attr_t, destroy, void,
324 private_ietf_attr_pa_tnc_error_t *this)
325 {
326 if (ref_put(&this->ref))
327 {
328 free(this->value.ptr);
329 free(this->msg_info.ptr);
330 free(this);
331 }
332 }
333
334 METHOD(ietf_attr_pa_tnc_error_t, get_error_code, pen_type_t,
335 private_ietf_attr_pa_tnc_error_t *this)
336 {
337 return this->error_code;
338 }
339
340 METHOD(ietf_attr_pa_tnc_error_t, get_msg_info, chunk_t,
341 private_ietf_attr_pa_tnc_error_t *this)
342 {
343 return this->msg_info;
344 }
345
346 METHOD(ietf_attr_pa_tnc_error_t, get_unsupported_attr, pen_type_t,
347 private_ietf_attr_pa_tnc_error_t *this, uint8_t *flags)
348 {
349 if (flags)
350 {
351 *flags = this->flags;
352 }
353 return this->unsupported_type;
354 }
355
356 METHOD(ietf_attr_pa_tnc_error_t, set_unsupported_attr, void,
357 private_ietf_attr_pa_tnc_error_t *this, uint8_t flags, pen_type_t type)
358 {
359 this->flags = flags;
360 this->unsupported_type = type;
361 }
362
363 METHOD(ietf_attr_pa_tnc_error_t, get_offset, uint32_t,
364 private_ietf_attr_pa_tnc_error_t *this)
365 {
366 return this->error_offset;
367 }
368
369 /**
370 * Generic constructor
371 */
372 static private_ietf_attr_pa_tnc_error_t* create_generic()
373 {
374 private_ietf_attr_pa_tnc_error_t *this;
375
376 INIT(this,
377 .public = {
378 .pa_tnc_attribute = {
379 .get_type = _get_type,
380 .get_value = _get_value,
381 .get_noskip_flag = _get_noskip_flag,
382 .set_noskip_flag = _set_noskip_flag,
383 .build = _build,
384 .process = _process,
385 .add_segment = _add_segment,
386 .get_ref = _get_ref,
387 .destroy = _destroy,
388 },
389 .get_error_code = _get_error_code,
390 .get_msg_info = _get_msg_info,
391 .get_unsupported_attr = _get_unsupported_attr,
392 .set_unsupported_attr = _set_unsupported_attr,
393 .get_offset = _get_offset,
394 },
395 .type = { PEN_IETF, IETF_ATTR_PA_TNC_ERROR },
396 .ref = 1,
397 );
398
399 return this;
400 }
401
402 /**
403 * Described in header.
404 */
405 pa_tnc_attr_t *ietf_attr_pa_tnc_error_create(pen_type_t error_code,
406 chunk_t msg_info)
407 {
408 private_ietf_attr_pa_tnc_error_t *this;
409
410 if (error_code.vendor_id == PEN_IETF &&
411 error_code.type <= PA_ERROR_PA_TNC_MSG_ROOF)
412 {
413 msg_info.len = PA_ERROR_MSG_INFO_SIZE;
414 }
415 else if (msg_info.len > PA_ERROR_MSG_INFO_MAX_SIZE)
416 {
417 msg_info.len = PA_ERROR_MSG_INFO_MAX_SIZE;
418 }
419
420 this = create_generic();
421 this->error_code = error_code;
422 this->msg_info = chunk_clone(msg_info);
423
424 return &this->public.pa_tnc_attribute;
425 }
426
427 /**
428 * Described in header.
429 */
430 pa_tnc_attr_t *ietf_attr_pa_tnc_error_create_with_offset(pen_type_t error_code,
431 chunk_t msg_info,
432 uint32_t error_offset)
433 {
434 private_ietf_attr_pa_tnc_error_t *this;
435
436 /* the first 8 bytes of the erroneous PA-TNC message are sent back */
437 msg_info.len = PA_ERROR_MSG_INFO_SIZE;
438
439 this = create_generic();
440 this->error_code = error_code;
441 this->msg_info = chunk_clone(msg_info);
442 this->error_offset = error_offset;
443
444 return &this->public.pa_tnc_attribute;
445 }
446
447 /**
448 * Described in header.
449 */
450 pa_tnc_attr_t *ietf_attr_pa_tnc_error_create_from_data(size_t length,
451 chunk_t data)
452 {
453 private_ietf_attr_pa_tnc_error_t *this;
454
455 this = create_generic();
456 this->length = length;
457 this->value = chunk_clone(data);
458
459 return &this->public.pa_tnc_attribute;
460 }