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