libimcv: Add generic constructor for PA-TNC attributes
[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 METHOD(pa_tnc_attr_manager_t, create, pa_tnc_attr_t*,
124 private_pa_tnc_attr_manager_t *this, bio_reader_t *reader, bool segmented,
125 uint32_t *offset, chunk_t msg_info, pa_tnc_attr_t **error)
126 {
127 uint8_t flags;
128 uint32_t type, length, value_len;
129 chunk_t value;
130 ietf_attr_pa_tnc_error_t *error_attr;
131 pen_t vendor_id;
132 pen_type_t unsupported_type;
133 pen_type_t error_code = { PEN_IETF, PA_ERROR_INVALID_PARAMETER };
134 enum_name_t *pa_attr_names;
135 pa_tnc_attr_t *attr = NULL;
136 enumerator_t *enumerator;
137 entry_t *entry;
138
139 /* properly initialize error return argument in case of no error */
140 *error = NULL;
141
142 if (reader->remaining(reader) < PA_TNC_ATTR_HEADER_SIZE)
143 {
144 DBG1(DBG_TNC, "insufficient bytes for PA-TNC attribute header");
145 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
146 msg_info, *offset);
147 return NULL;
148 }
149 reader->read_uint8 (reader, &flags);
150 reader->read_uint24(reader, &vendor_id);
151 reader->read_uint32(reader, &type);
152 reader->read_uint32(reader, &length);
153
154 pa_attr_names = imcv_pa_tnc_attributes->get_names(imcv_pa_tnc_attributes,
155 vendor_id);
156 if (pa_attr_names)
157 {
158 DBG2(DBG_TNC, "processing PA-TNC attribute type '%N/%N' "
159 "0x%06x/0x%08x", pen_names, vendor_id,
160 pa_attr_names, type, vendor_id, type);
161 }
162 else
163 {
164 DBG2(DBG_TNC, "processing PA-TNC attribute type '%N' "
165 "0x%06x/0x%08x", pen_names, vendor_id,
166 vendor_id, type);
167 }
168
169 if (length < PA_TNC_ATTR_HEADER_SIZE)
170 {
171 DBG1(DBG_TNC, "%u bytes too small for PA-TNC attribute length",
172 length);
173 *error = ietf_attr_pa_tnc_error_create_with_offset(error_code,
174 msg_info, *offset + PA_TNC_ATTR_INFO_SIZE);
175 return NULL;
176 }
177 length -= PA_TNC_ATTR_HEADER_SIZE;
178 value_len = segmented ? reader->remaining(reader) : length;
179
180 if (!reader->read_data(reader, value_len, &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, length, 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) += PA_TNC_ATTR_HEADER_SIZE + 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 (*offset) += PA_TNC_ATTR_HEADER_SIZE;
235
236 return attr;
237 }
238
239 METHOD(pa_tnc_attr_manager_t, construct, pa_tnc_attr_t*,
240 private_pa_tnc_attr_manager_t *this, pen_t vendor_id, uint32_t type,
241 chunk_t value)
242 {
243 enum_name_t *pa_attr_names;
244 pa_tnc_attr_t *attr = NULL;
245 enumerator_t *enumerator;
246 entry_t *entry;
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, "generating 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, "generating PA-TNC attribute type '%N' "
259 "0x%06x/0x%08x", pen_names, vendor_id,
260 vendor_id, type);
261 }
262 enumerator = this->list->create_enumerator(this->list);
263 while (enumerator->enumerate(enumerator, &entry))
264 {
265 if (entry->vendor_id == vendor_id)
266 {
267 if (entry->attr_create)
268 {
269 attr = entry->attr_create(type, value.len, value);
270 }
271 break;
272 }
273 }
274 enumerator->destroy(enumerator);
275 return attr;
276 }
277
278 METHOD(pa_tnc_attr_manager_t, destroy, void,
279 private_pa_tnc_attr_manager_t *this)
280 {
281 this->list->destroy_function(this->list, free);
282 free(this);
283 }
284
285 /**
286 * See header
287 */
288 pa_tnc_attr_manager_t *pa_tnc_attr_manager_create(void)
289 {
290 private_pa_tnc_attr_manager_t *this;
291
292 INIT(this,
293 .public = {
294 .add_vendor = _add_vendor,
295 .remove_vendor = _remove_vendor,
296 .get_names = _get_names,
297 .create = _create,
298 .construct = _construct,
299 .destroy = _destroy,
300 },
301 .list = linked_list_create(),
302 );
303
304 return &this->public;
305 }
306