OS IMV proposes IF-M segmentation contract
[strongswan.git] / src / libimcv / ietf / ietf_attr_installed_packages.c
1 /*
2 * Copyright (C) 2012-2014 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_installed_packages.h"
17
18 #include <string.h>
19
20 #include <pa_tnc/pa_tnc_msg.h>
21 #include <bio/bio_writer.h>
22 #include <bio/bio_reader.h>
23 #include <collections/linked_list.h>
24 #include <utils/debug.h>
25
26
27 typedef struct private_ietf_attr_installed_packages_t private_ietf_attr_installed_packages_t;
28 typedef struct package_entry_t package_entry_t;
29
30 /**
31 * PA-TNC Installed Packages Type (see section 4.2.7 of RFC 5792)
32 *
33 * 1 2 3
34 * 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
35 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36 * | Reserved | Package Count |
37 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38 * | Pkg Name Len | Package Name (Variable Length) |
39 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40 * | Version Len | Package Version Number (Variable Length) |
41 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42 */
43
44 /**
45 * Private data of an ietf_attr_installed_packages_t object.
46 */
47 struct private_ietf_attr_installed_packages_t {
48
49 /**
50 * Public members of ietf_attr_installed_packages_t
51 */
52 ietf_attr_installed_packages_t public;
53
54 /**
55 * Vendor-specific attribute type
56 */
57 pen_type_t type;
58
59 /**
60 * Length of attribute value
61 */
62 size_t length;
63
64 /**
65 * Offset up to which attribute value has been processed
66 */
67 size_t offset;
68
69 /**
70 * Current position of attribute value pointer
71 */
72 chunk_t value;
73
74 /**
75 * Contains complete attribute or current segment
76 */
77 chunk_t segment;
78
79 /**
80 * Noskip flag
81 */
82 bool noskip_flag;
83
84 /**
85 * Number of Installed Packages in attribute
86 */
87 uint16_t count;
88
89 /**
90 * List of Installed Package entries
91 */
92 linked_list_t *packages;
93
94 /**
95 * Reference count
96 */
97 refcount_t ref;
98 };
99
100 /**
101 * Package entry
102 */
103 struct package_entry_t {
104 chunk_t name;
105 chunk_t version;
106 };
107
108 /**
109 * Free a package entry
110 */
111 static void free_package_entry(package_entry_t *entry)
112 {
113 free(entry->name.ptr);
114 free(entry->version.ptr);
115 free(entry);
116 }
117
118 METHOD(pa_tnc_attr_t, get_type, pen_type_t,
119 private_ietf_attr_installed_packages_t *this)
120 {
121 return this->type;
122 }
123
124 METHOD(pa_tnc_attr_t, get_value, chunk_t,
125 private_ietf_attr_installed_packages_t *this)
126 {
127 return this->value;
128 }
129
130 METHOD(pa_tnc_attr_t, get_noskip_flag, bool,
131 private_ietf_attr_installed_packages_t *this)
132 {
133 return this->noskip_flag;
134 }
135
136 METHOD(pa_tnc_attr_t, set_noskip_flag,void,
137 private_ietf_attr_installed_packages_t *this, bool noskip)
138 {
139 this->noskip_flag = noskip;
140 }
141
142 METHOD(pa_tnc_attr_t, build, void,
143 private_ietf_attr_installed_packages_t *this)
144 {
145 bio_writer_t *writer;
146 enumerator_t *enumerator;
147 package_entry_t *entry;
148
149 if (this->value.ptr)
150 {
151 return;
152 }
153 writer = bio_writer_create(IETF_INSTALLED_PACKAGES_MIN_SIZE);
154 writer->write_uint16(writer, 0x0000);
155 writer->write_uint16(writer, this->packages->get_count(this->packages));
156
157 enumerator = this->packages->create_enumerator(this->packages);
158 while (enumerator->enumerate(enumerator, &entry))
159 {
160 writer->write_data8(writer, entry->name);
161 writer->write_data8(writer, entry->version);
162 }
163 enumerator->destroy(enumerator);
164
165 this->value = writer->extract_buf(writer);
166 this->segment = this->value;
167 this->length = this->value.len;
168 writer->destroy(writer);
169 }
170
171 METHOD(pa_tnc_attr_t, process, status_t,
172 private_ietf_attr_installed_packages_t *this, u_int32_t *offset)
173 {
174 bio_reader_t *reader;
175 package_entry_t *entry;
176 status_t status = NEED_MORE;
177 chunk_t name, version;
178 u_int16_t reserved;
179 u_char *pos;
180
181 if (this->offset == 0)
182 {
183 if (this->length < IETF_INSTALLED_PACKAGES_MIN_SIZE)
184 {
185 DBG1(DBG_TNC, "insufficient data for %N/%N", pen_names, PEN_IETF,
186 ietf_attr_names, this->type.type);
187 *offset = this->offset;
188 return FAILED;
189 }
190 if (this->value.len < IETF_INSTALLED_PACKAGES_MIN_SIZE)
191 {
192 return NEED_MORE;
193 }
194 reader = bio_reader_create(this->value);
195 reader->read_uint16(reader, &reserved);
196 reader->read_uint16(reader, &this->count);
197 this->offset = IETF_INSTALLED_PACKAGES_MIN_SIZE;
198 this->value = reader->peek(reader);
199 reader->destroy(reader);
200 }
201
202 reader = bio_reader_create(this->value);
203
204 while (this->count)
205 {
206 if (!reader->read_data8(reader, &name))
207 {
208 goto end;
209 }
210 pos = memchr(name.ptr, '\0', name.len);
211 if (pos)
212 {
213 DBG1(DBG_TNC, "nul termination in IETF installed package name");
214 *offset = this->offset + 1 + (pos - name.ptr);
215 status = FAILED;
216 goto end;
217 }
218 this->offset += 1 + name.len;
219
220 if (!reader->read_data8(reader, &version))
221 {
222 goto end;
223 }
224 pos = memchr(version.ptr, '\0', version.len);
225 if (pos)
226 {
227 DBG1(DBG_TNC, "nul termination in IETF installed package version");
228 *offset = this->offset + 1 + (pos - version.ptr);
229 status = FAILED;
230 goto end;
231 }
232 this->offset += 1 + version.len;
233 this->value = reader->peek(reader);
234
235 entry = malloc_thing(package_entry_t);
236 entry->name = chunk_clone(name);
237 entry->version = chunk_clone(version);
238 this->packages->insert_last(this->packages, entry);
239
240 /* at least one tag ID was processed */
241 status = SUCCESS;
242 this->count--;
243 }
244
245 if (this->length != this->offset)
246 {
247 DBG1(DBG_TNC, "inconsistent length for %N/%N", pen_names, PEN_IETF,
248 ietf_attr_names, this->type.type);
249 *offset = this->offset;
250 status = FAILED;
251 }
252
253 end:
254 reader->destroy(reader);
255 return status;
256 }
257
258 METHOD(pa_tnc_attr_t, add_segment, void,
259 private_ietf_attr_installed_packages_t *this, chunk_t segment)
260 {
261 this->value = chunk_cat("cc", this->value, segment);
262 chunk_free(&this->segment);
263 this->segment = this->value;
264 }
265
266 METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*,
267 private_ietf_attr_installed_packages_t *this)
268 {
269 ref_get(&this->ref);
270 return &this->public.pa_tnc_attribute;
271 }
272
273 METHOD(pa_tnc_attr_t, destroy, void,
274 private_ietf_attr_installed_packages_t *this)
275 {
276 if (ref_put(&this->ref))
277 {
278 this->packages->destroy_function(this->packages, (void*)free_package_entry);
279 free(this->segment.ptr);
280 free(this);
281 }
282 }
283
284 METHOD(ietf_attr_installed_packages_t, add, void,
285 private_ietf_attr_installed_packages_t *this, chunk_t name, chunk_t version)
286 {
287 package_entry_t *entry;
288
289 /* restrict package name and package version number fields to 255 octets */
290 name.len = min(255, name.len);
291 version.len = min(255, version.len);
292
293 entry = malloc_thing(package_entry_t);
294 entry->name = chunk_clone(name);
295 entry->version = chunk_clone(version);
296 this->packages->insert_last(this->packages, entry);
297 }
298
299 /**
300 * Enumerate package filter entries
301 */
302 static bool package_filter(void *null, package_entry_t **entry, chunk_t *name,
303 void *i2, chunk_t *version)
304 {
305 *name = (*entry)->name;
306 *version = (*entry)->version;
307 return TRUE;
308 }
309
310 METHOD(ietf_attr_installed_packages_t, create_enumerator, enumerator_t*,
311 private_ietf_attr_installed_packages_t *this)
312 {
313 return enumerator_create_filter(
314 this->packages->create_enumerator(this->packages),
315 (void*)package_filter, NULL, NULL);
316 }
317
318 METHOD(ietf_attr_installed_packages_t, get_count, uint16_t,
319 private_ietf_attr_installed_packages_t *this)
320 {
321 return this->count;
322 }
323
324 METHOD(ietf_attr_installed_packages_t, clear_packages, void,
325 private_ietf_attr_installed_packages_t *this)
326 {
327 package_entry_t *entry;
328
329 while (this->packages->remove_first(this->packages,(void**)&entry))
330 {
331 free_package_entry(entry);
332 }
333 }
334
335 /**
336 * Described in header.
337 */
338 pa_tnc_attr_t *ietf_attr_installed_packages_create(void)
339 {
340 private_ietf_attr_installed_packages_t *this;
341
342 INIT(this,
343 .public = {
344 .pa_tnc_attribute = {
345 .get_type = _get_type,
346 .get_value = _get_value,
347 .get_noskip_flag = _get_noskip_flag,
348 .set_noskip_flag = _set_noskip_flag,
349 .build = _build,
350 .process = _process,
351 .add_segment = _add_segment,
352 .get_ref = _get_ref,
353 .destroy = _destroy,
354 },
355 .add = _add,
356 .create_enumerator = _create_enumerator,
357 .get_count = _get_count,
358 .clear_packages = _clear_packages,
359 },
360 .type = { PEN_IETF, IETF_ATTR_INSTALLED_PACKAGES },
361 .packages = linked_list_create(),
362 .ref = 1,
363 );
364
365 return &this->public.pa_tnc_attribute;
366 }
367
368 /**
369 * Described in header. .length = length,
370
371 */
372 pa_tnc_attr_t *ietf_attr_installed_packages_create_from_data(size_t length,
373 chunk_t data)
374 {
375 private_ietf_attr_installed_packages_t *this;
376
377 INIT(this,
378 .public = {
379 .pa_tnc_attribute = {
380 .get_type = _get_type,
381 .get_value = _get_value,
382 .get_noskip_flag = _get_noskip_flag,
383 .set_noskip_flag = _set_noskip_flag,
384 .build = _build,
385 .process = _process,
386 .add_segment = _add_segment,
387 .get_ref = _get_ref,
388 .destroy = _destroy,
389 },
390 .add = _add,
391 .create_enumerator = _create_enumerator,
392 .get_count = _get_count,
393 .clear_packages = _clear_packages,
394 },
395 .type = {PEN_IETF, IETF_ATTR_INSTALLED_PACKAGES },
396 .length = length,
397 .segment = chunk_clone(data),
398 .packages = linked_list_create(),
399 .ref = 1,
400 );
401
402 /* received either complete attribute value or first segment */
403 this->value = this->segment;
404
405 return &this->public.pa_tnc_attribute;
406 }
407
408