libimcv: Implemented IETF SW PA-TNC attributes
[strongswan.git] / src / libimcv / ietf / swima / ietf_swima_attr_sw_inv.c
1 /*
2 * Copyright (C) 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_swima_attr_sw_inv.h"
17 #include "swima/swima_record.h"
18
19 #include <pa_tnc/pa_tnc_msg.h>
20 #include <bio/bio_writer.h>
21 #include <bio/bio_reader.h>
22 #include <utils/debug.h>
23
24
25 typedef struct private_ietf_swima_attr_sw_inv_t private_ietf_swima_attr_sw_inv_t;
26
27 /**
28 * Software [Identifier] Inventory
29 * see sections 5.8/5.10 of IETF SW Inventory Message and Attributes for PA-TNC
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 * | Flags | Software Identifier Count |
35 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36 * | Request ID Copy / Subscription ID |
37 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38 * | EID Epoch |
39 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40 * | Last EID |
41 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42 * | Record Identifier |
43 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44 * | Data Model Type PEN |Data Model Type|
45 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46 * | Source ID Num | Software Identifier Length |Software Id (v)|
47 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48 * | Software Locator Length | Software Locator (Var. Len) |
49 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50 *
51 * Software Inventory only
52 * see section 5.10 of IETF SW Inventory Message and Attributes for PA-TNC
53 *
54 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55 * | Record Length |
56 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57 * | Record (Variable length) |
58 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
59 */
60
61 /**
62 * Private data of an ietf_swima_attr_sw_inv_t object.
63 */
64 struct private_ietf_swima_attr_sw_inv_t {
65
66 /**
67 * Public members of ietf_swima_attr_sw_inv_t
68 */
69 ietf_swima_attr_sw_inv_t public;
70
71 /**
72 * Vendor-specific attribute type
73 */
74 pen_type_t type;
75
76 /**
77 * Length of attribute value
78 */
79 size_t length;
80
81 /**
82 * Offset up to which attribute value has been processed
83 */
84 size_t offset;
85
86 /**
87 * Current position of attribute value pointer
88 */
89 chunk_t value;
90
91 /**
92 * Contains complete attribute or current segment
93 */
94 chunk_t segment;
95
96 /**
97 * Noskip flag
98 */
99 bool noskip_flag;
100
101 /**
102 * Request ID
103 */
104 uint32_t request_id;
105
106 /**
107 * Attribute flags
108 */
109 uint8_t flags;
110
111 /**
112 * Number of unprocessed software inventory evidence records in attribute
113 */
114 uint32_t record_count;
115
116 /**
117 * SWID Tag ID Inventory
118 */
119 swima_inventory_t *inventory;
120
121 /**
122 * Reference count
123 */
124 refcount_t ref;
125 };
126
127 METHOD(pa_tnc_attr_t, get_type, pen_type_t,
128 private_ietf_swima_attr_sw_inv_t *this)
129 {
130 return this->type;
131 }
132
133 METHOD(pa_tnc_attr_t, get_value, chunk_t,
134 private_ietf_swima_attr_sw_inv_t *this)
135 {
136 return this->value;
137 }
138
139 METHOD(pa_tnc_attr_t, get_noskip_flag, bool,
140 private_ietf_swima_attr_sw_inv_t *this)
141 {
142 return this->noskip_flag;
143 }
144
145 METHOD(pa_tnc_attr_t, set_noskip_flag,void,
146 private_ietf_swima_attr_sw_inv_t *this, bool noskip)
147 {
148 this->noskip_flag = noskip;
149 }
150
151 METHOD(pa_tnc_attr_t, build, void,
152 private_ietf_swima_attr_sw_inv_t *this)
153 {
154 bio_writer_t *writer;
155 swima_record_t *sw_record;
156 chunk_t sw_id, sw_locator, record;
157 pen_type_t data_model;
158 uint32_t record_id, last_eid, eid_epoch;
159 uint8_t source_id;
160 enumerator_t *enumerator;
161
162 if (this->value.ptr)
163 {
164 return;
165 }
166 last_eid = this->inventory->get_eid(this->inventory, &eid_epoch);
167
168 writer = bio_writer_create(IETF_SWIMA_SW_INV_MIN_SIZE);
169 writer->write_uint8 (writer, this->flags);
170 writer->write_uint24(writer, this->inventory->get_count(this->inventory));
171 writer->write_uint32(writer, this->request_id);
172 writer->write_uint32(writer, eid_epoch);
173 writer->write_uint32(writer, last_eid);
174
175 enumerator = this->inventory->create_enumerator(this->inventory);
176 while (enumerator->enumerate(enumerator, &sw_record))
177 {
178 record_id = sw_record->get_record_id(sw_record);
179 data_model = sw_record->get_data_model(sw_record);
180 source_id = sw_record->get_source_id(sw_record);
181 sw_id = sw_record->get_sw_id(sw_record, &sw_locator);
182
183 writer->write_uint32(writer, record_id);
184 writer->write_uint24(writer, data_model.vendor_id);
185 writer->write_uint8 (writer, data_model.type);
186 writer->write_uint8 (writer, source_id);
187 writer->write_data16(writer, sw_id);
188 writer->write_data16(writer, sw_locator);
189
190 if (this->type.type == IETF_ATTR_SW_INVENTORY)
191 {
192 record = sw_record->get_record(sw_record);
193 writer->write_data32(writer, record);
194 }
195 }
196 enumerator->destroy(enumerator);
197
198 this->value = writer->extract_buf(writer);
199 this->segment = this->value;
200 this->length = this->value.len;
201 writer->destroy(writer);
202 }
203
204 METHOD(pa_tnc_attr_t, process, status_t,
205 private_ietf_swima_attr_sw_inv_t *this, uint32_t *offset)
206 {
207 bio_reader_t *reader;
208 uint32_t data_model_pen, record_id, last_eid, eid_epoch;
209 uint8_t data_model_type, source_id;
210 pen_type_t data_model;
211 chunk_t sw_id, sw_locator, record;
212 swima_record_t *sw_record;
213 status_t status = NEED_MORE;
214
215 if (this->offset == 0)
216 {
217 if (this->length < IETF_SWIMA_SW_INV_MIN_SIZE)
218 {
219 DBG1(DBG_TNC, "insufficient data for %N/%N", pen_names, PEN_IETF,
220 ietf_attr_names, this->type.type);
221 *offset = this->offset;
222 return FAILED;
223 }
224 if (this->value.len < IETF_SWIMA_SW_INV_MIN_SIZE)
225 {
226 return NEED_MORE;
227 }
228 reader = bio_reader_create(this->value);
229 reader->read_uint8 (reader, &this->flags);
230 reader->read_uint24(reader, &this->record_count);
231 reader->read_uint32(reader, &this->request_id);
232 reader->read_uint32(reader, &eid_epoch);
233 reader->read_uint32(reader, &last_eid);
234 this->offset = IETF_SWIMA_SW_INV_MIN_SIZE;
235 this->value = reader->peek(reader);
236 this->inventory->set_eid(this->inventory, last_eid, eid_epoch);
237 reader->destroy(reader);
238 }
239
240 reader = bio_reader_create(this->value);
241
242 while (this->record_count)
243 {
244 if (!reader->read_uint32(reader, &record_id) ||
245 !reader->read_uint24(reader, &data_model_pen) ||
246 !reader->read_uint8 (reader, &data_model_type) ||
247 !reader->read_uint8 (reader, &source_id) ||
248 !reader->read_data16(reader, &sw_id) ||
249 !reader->read_data16(reader, &sw_locator))
250 {
251 goto end;
252 }
253 record = chunk_empty;
254
255 if (this->type.type == IETF_ATTR_SW_INVENTORY &&
256 !reader->read_data32(reader, &record))
257 {
258 goto end;
259 }
260 data_model = pen_type_create(data_model_pen, data_model_type);
261 sw_record = swima_record_create(record_id, sw_id, sw_locator);
262 sw_record->set_data_model(sw_record, data_model);
263 sw_record->set_source_id(sw_record, source_id);
264 sw_record->set_record(sw_record, record);
265 this->inventory->add(this->inventory, sw_record);
266 this->offset += this->value.len - reader->remaining(reader);
267 this->value = reader->peek(reader);
268
269 /* at least one software inventory evidence record was processed */
270 status = SUCCESS;
271 this->record_count--;
272 }
273
274 if (this->length == this->offset)
275 {
276 status = SUCCESS;
277 }
278 else
279 {
280 DBG1(DBG_TNC, "inconsistent length for %N/%N", pen_names, PEN_IETF,
281 ietf_attr_names, this->type.type);
282 *offset = this->offset;
283 status = FAILED;
284 }
285
286 end:
287 reader->destroy(reader);
288 return status;
289 }
290
291 METHOD(pa_tnc_attr_t, add_segment, void,
292 private_ietf_swima_attr_sw_inv_t *this, chunk_t segment)
293 {
294 this->value = chunk_cat("cc", this->value, segment);
295 chunk_free(&this->segment);
296 this->segment = this->value;
297 }
298
299 METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*,
300 private_ietf_swima_attr_sw_inv_t *this)
301 {
302 ref_get(&this->ref);
303 return &this->public.pa_tnc_attribute;
304 }
305
306 METHOD(pa_tnc_attr_t, destroy, void,
307 private_ietf_swima_attr_sw_inv_t *this)
308 {
309 if (ref_put(&this->ref))
310 {
311 this->inventory->destroy(this->inventory);
312 free(this->segment.ptr);
313 free(this);
314 }
315 }
316
317 METHOD(ietf_swima_attr_sw_inv_t, get_flags, uint8_t,
318 private_ietf_swima_attr_sw_inv_t *this)
319 {
320 return this->flags;
321 }
322
323 METHOD(ietf_swima_attr_sw_inv_t, get_request_id, uint32_t,
324 private_ietf_swima_attr_sw_inv_t *this)
325 {
326 return this->request_id;
327 }
328
329 METHOD(ietf_swima_attr_sw_inv_t, get_record_count, uint32_t,
330 private_ietf_swima_attr_sw_inv_t *this)
331 {
332 return this->record_count;
333 }
334
335 METHOD(ietf_swima_attr_sw_inv_t, set_inventory, void,
336 private_ietf_swima_attr_sw_inv_t *this, swima_inventory_t *inventory)
337 {
338 this->inventory->destroy(this->inventory);
339 this->inventory = inventory->get_ref(inventory);
340 }
341
342 METHOD(ietf_swima_attr_sw_inv_t, get_inventory, swima_inventory_t*,
343 private_ietf_swima_attr_sw_inv_t *this)
344 {
345 return this->inventory;
346 }
347
348 METHOD(ietf_swima_attr_sw_inv_t, clear_inventory, void,
349 private_ietf_swima_attr_sw_inv_t *this)
350 {
351 this->inventory->clear(this->inventory);
352 }
353
354 /**
355 * Described in header.
356 */
357 pa_tnc_attr_t *ietf_swima_attr_sw_inv_create(uint8_t flags, uint32_t request_id,
358 bool sw_id_only)
359 {
360 private_ietf_swima_attr_sw_inv_t *this;
361 ietf_attr_t type;
362
363 type = sw_id_only ? IETF_ATTR_SW_ID_INVENTORY : IETF_ATTR_SW_INVENTORY;
364
365 INIT(this,
366 .public = {
367 .pa_tnc_attribute = {
368 .get_type = _get_type,
369 .get_value = _get_value,
370 .get_noskip_flag = _get_noskip_flag,
371 .set_noskip_flag = _set_noskip_flag,
372 .build = _build,
373 .process = _process,
374 .add_segment = _add_segment,
375 .get_ref = _get_ref,
376 .destroy = _destroy,
377 },
378 .get_flags = _get_flags,
379 .get_request_id = _get_request_id,
380 .get_record_count = _get_record_count,
381 .set_inventory = _set_inventory,
382 .get_inventory = _get_inventory,
383 .clear_inventory = _clear_inventory,
384 },
385 .type = { PEN_IETF, type },
386 .flags = flags,
387 .request_id = request_id,
388 .inventory = swima_inventory_create(),
389 .ref = 1,
390 );
391
392 return &this->public.pa_tnc_attribute;
393 }
394
395
396 /**
397 * Described in header.
398 */
399 pa_tnc_attr_t *ietf_swima_attr_sw_inv_create_from_data(size_t length,
400 chunk_t data, bool sw_id_only)
401 {
402 private_ietf_swima_attr_sw_inv_t *this;
403 ietf_attr_t type;
404
405 type = sw_id_only ? IETF_ATTR_SW_ID_INVENTORY : IETF_ATTR_SW_INVENTORY;
406
407 INIT(this,
408 .public = {
409 .pa_tnc_attribute = {
410 .get_type = _get_type,
411 .get_value = _get_value,
412 .get_noskip_flag = _get_noskip_flag,
413 .set_noskip_flag = _set_noskip_flag,
414 .build = _build,
415 .process = _process,
416 .add_segment = _add_segment,
417 .get_ref = _get_ref,
418 .destroy = _destroy,
419 },
420 .get_flags = _get_flags,
421 .get_request_id = _get_request_id,
422 .get_record_count = _get_record_count,
423 .set_inventory = _set_inventory,
424 .get_inventory = _get_inventory,
425 .clear_inventory = _clear_inventory,
426 },
427 .type = { PEN_IETF, type },
428 .length = length,
429 .segment = chunk_clone(data),
430 .inventory = swima_inventory_create(),
431 .ref = 1,
432 );
433
434 /* received either complete attribute value or first segment */
435 this->value = this->segment;
436
437 return &this->public.pa_tnc_attribute;
438 }