Implemented IF-M segmentation
[strongswan.git] / src / libimcv / pa_tnc / pa_tnc_attr_manager.c
1 /*
2 * Copyright (C) 2011-2014 Andreas Steffen
3 *
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "pa_tnc_attr_manager.h"
18
19 #include "imcv.h"
20 #include "pa_tnc_attr.h"
21 #include "ietf/ietf_attr_pa_tnc_error.h"
22
23 #include <collections/linked_list.h>
24 #include <utils/debug.h>
25
26 typedef struct private_pa_tnc_attr_manager_t private_pa_tnc_attr_manager_t;
27 typedef struct entry_t entry_t;
28
29 struct entry_t {
30 pen_t vendor_id;
31 enum_name_t *attr_names;
32 pa_tnc_attr_create_t attr_create;
33 };
34
35 /**
36 * Private data of a pa_tnc_attr_manager_t object.
37 *
38 */
39 struct private_pa_tnc_attr_manager_t {
40
41 /**
42 * Public pa_tnc_attr_manager_t interface.
43 */
44 pa_tnc_attr_manager_t public;
45
46 /**
47 * List of PA-TNC vendor attributes
48 */
49 linked_list_t *list;
50 };
51
52 METHOD(pa_tnc_attr_manager_t, add_vendor, void,
53 private_pa_tnc_attr_manager_t *this, pen_t vendor_id,
54 pa_tnc_attr_create_t attr_create, enum_name_t *attr_names)
55 {
56 entry_t *entry;
57
58 entry = malloc_thing(entry_t);
59 entry->vendor_id = vendor_id;
60 entry->attr_create = attr_create;
61 entry->attr_names = attr_names;
62
63 this->list->insert_last(this->list, entry);
64 DBG2(DBG_TNC, "added %N attributes", pen_names, vendor_id);
65 }
66
67 METHOD(pa_tnc_attr_manager_t, remove_vendor, void,
68 private_pa_tnc_attr_manager_t *this, pen_t vendor_id)
69 {
70 enumerator_t *enumerator;
71 entry_t *entry;
72
73 enumerator = this->list->create_enumerator(this->list);
74 while (enumerator->enumerate(enumerator, &entry))
75 {
76 if (entry->vendor_id == vendor_id)
77 {
78 this->list->remove_at(this->list, enumerator);
79 free(entry);
80 DBG2(DBG_TNC, "removed %N attributes", pen_names, vendor_id);
81 }
82 }
83 enumerator->destroy(enumerator);
84 }
85
86 METHOD(pa_tnc_attr_manager_t, get_names, enum_name_t*,
87 private_pa_tnc_attr_manager_t *this, pen_t vendor_id)
88 {
89 enumerator_t *enumerator;
90 entry_t *entry;
91 enum_name_t *attr_names = NULL;
92
93 enumerator = this->list->create_enumerator(this->list);
94 while (enumerator->enumerate(enumerator, &entry))
95 {
96 if (entry->vendor_id == vendor_id)
97 {
98 attr_names = entry->attr_names;
99 break;
100 }
101 }
102 enumerator->destroy(enumerator);
103
104 return attr_names;
105 }
106
107 /**
108 * PA-TNC attribute
109 *
110 * 1 2 3
111 * 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
112 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
113 * | Flags | PA-TNC Attribute Vendor ID |
114 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
115 * | PA-TNC Attribute Type |
116 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
117 * | PA-TNC Attribute Length |
118 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
119 * | Attribute Value (Variable Length) |
120 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
121 */
122
123 #define PA_TNC_ATTR_INFO_SIZE 8
124
125 METHOD(pa_tnc_attr_manager_t, create, pa_tnc_attr_t*,
126 private_pa_tnc_attr_manager_t *this, bio_reader_t *reader, uint32_t *offset,
127 chunk_t msg_info, pa_tnc_attr_t **error)
128 {
129 uint8_t flags;
130 uint32_t type, length, attr_offset;
131 chunk_t value;
132 ietf_attr_pa_tnc_error_t *error_attr;
133 pen_t vendor_id;
134 pen_type_t unsupported_type;
135 pen_type_t error_code = { PEN_IETF, PA_ERROR_INVALID_PARAMETER };
136 enum_name_t *pa_attr_names;
137 pa_tnc_attr_t *attr = NULL;
138 enumerator_t *enumerator;
139 entry_t *entry;
140
141 /* properly initialize error return argument in case of no error */
142 *error = NULL;
143
144 if (reader->remaining(reader) < PA_TNC_ATTR_HEADER_SIZE)
145 {
146 DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute header");
147 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
148 msg_info, *offset);
149 return NULL;
150 }
151 reader->read_uint8 (reader, &flags);
152 reader->read_uint24(reader, &vendor_id);
153 reader->read_uint32(reader, &type);
154 reader->read_uint32(reader, &length);
155
156 pa_attr_names = imcv_pa_tnc_attributes->get_names(imcv_pa_tnc_attributes,
157 vendor_id);
158 if (pa_attr_names)
159 {
160 DBG2(DBG_TNC, "processing PA-TNC attribute type '%N/%N' "
161 "0x%06x/0x%08x", pen_names, vendor_id,
162 pa_attr_names, type, vendor_id, type);
163 }
164 else
165 {
166 DBG2(DBG_TNC, "processing PA-TNC attribute type '%N' "
167 "0x%06x/0x%08x", pen_names, vendor_id,
168 vendor_id, type);
169 }
170
171 if (length < PA_TNC_ATTR_HEADER_SIZE)
172 {
173 DBG1(DBG_TNC, "%u bytes too small for PA-TNC attribute length",
174 length);
175 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
176 msg_info, *offset + PA_TNC_ATTR_INFO_SIZE);
177 return NULL;
178 }
179
180 if (!reader->read_data(reader, length - PA_TNC_ATTR_HEADER_SIZE, &value))
181 {
182 DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute value");
183 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
184 msg_info, *offset + PA_TNC_ATTR_INFO_SIZE);
185 return NULL;
186 }
187 DBG3(DBG_TNC, "%B", &value);
188
189 if (vendor_id == PEN_RESERVED)
190 {
191 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
192 msg_info, *offset + 1);
193 return NULL;
194 }
195 if (type == IETF_ATTR_RESERVED)
196 {
197 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
198 msg_info, *offset + 4);
199 return NULL;
200 }
201
202 /* check if the attribute type is registered */
203 enumerator = this->list->create_enumerator(this->list);
204 while (enumerator->enumerate(enumerator, &entry))
205 {
206 if (entry->vendor_id == vendor_id)
207 {
208 if (entry->attr_create)
209 {
210 attr = entry->attr_create(type, value);
211 }
212 break;
213 }
214 }
215 enumerator->destroy(enumerator);
216
217 if (!attr)
218 {
219 if (!(flags & PA_TNC_ATTR_FLAG_NOSKIP))
220 {
221 DBG1(DBG_TNC, "skipping unsupported PA-TNC attribute");
222 offset += length;
223 return NULL;
224 }
225
226 DBG1(DBG_TNC, "unsupported PA-TNC attribute with NOSKIP flag");
227 unsupported_type = pen_type_create(vendor_id, type);
228 error_code = pen_type_create(PEN_IETF, PA_ERROR_ATTR_TYPE_NOT_SUPPORTED);
229 *error = ietf_attr_pa_tnc_error_create(error_code, msg_info);
230 error_attr = (ietf_attr_pa_tnc_error_t*)(*error);
231 error_attr->set_unsupported_attr(error_attr, flags, unsupported_type);
232 return NULL;
233 }
234 if (attr->process(attr, &attr_offset) != SUCCESS)
235 {
236 attr->destroy(attr);
237 attr = NULL;
238 if (vendor_id == PEN_IETF && type == IETF_ATTR_PA_TNC_ERROR)
239 {
240 /* error while processing a PA-TNC error attribute - abort */
241 return NULL;
242 }
243 error_code = pen_type_create(PEN_IETF, PA_ERROR_INVALID_PARAMETER);
244 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
245 msg_info, *offset + PA_TNC_ATTR_HEADER_SIZE + attr_offset);
246 return NULL;
247 }
248 (*offset) += length;
249
250 return attr;
251 }
252
253 METHOD(pa_tnc_attr_manager_t, destroy, void,
254 private_pa_tnc_attr_manager_t *this)
255 {
256 this->list->destroy_function(this->list, free);
257 free(this);
258 }
259
260 /**
261 * See header
262 */
263 pa_tnc_attr_manager_t *pa_tnc_attr_manager_create(void)
264 {
265 private_pa_tnc_attr_manager_t *this;
266
267 INIT(this,
268 .public = {
269 .add_vendor = _add_vendor,
270 .remove_vendor = _remove_vendor,
271 .get_names = _get_names,
272 .create = _create,
273 .destroy = _destroy,
274 },
275 .list = linked_list_create(),
276 );
277
278 return &this->public;
279 }
280