86f47a357335d30f91c43e8531fbcc520272544a
[strongswan.git] / src / libpts / swid / swid_inventory.c
1 /*
2 * Copyright (C) 2013-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 "swid_inventory.h"
17 #include "swid_tag.h"
18 #include "swid_tag_id.h"
19
20 #include <collections/linked_list.h>
21 #include <bio/bio_writer.h>
22 #include <utils/debug.h>
23
24 #include <stdio.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <libgen.h>
29 #include <errno.h>
30
31 typedef struct private_swid_inventory_t private_swid_inventory_t;
32
33 /**
34 * Private data of a swid_inventory_t object.
35 *
36 */
37 struct private_swid_inventory_t {
38
39 /**
40 * Public swid_inventory_t interface.
41 */
42 swid_inventory_t public;
43
44 /**
45 * Full SWID tags or just SWID tag IDs
46 */
47 bool full_tags;
48
49 /**
50 * List of SWID tags or tag IDs
51 */
52 linked_list_t *list;
53 };
54
55 /**
56 * Read SWID tags issued by the swid_generator tool
57 */
58 static status_t read_swid_tags(private_swid_inventory_t *this, FILE *file)
59 {
60 swid_tag_t *tag;
61 bio_writer_t *writer;
62 chunk_t tag_encoding, tag_file_path = chunk_empty;
63 bool more_tags = TRUE, last_newline, end_of_tag;
64 char line[8192];
65 size_t len;
66
67 while (more_tags)
68 {
69 last_newline = TRUE;
70 end_of_tag = FALSE;
71 writer = bio_writer_create(512);
72 do
73 {
74 if (fgets(line, sizeof(line), file) <= 0)
75 {
76 more_tags = FALSE;
77 end_of_tag = TRUE;
78 break;
79 }
80 len = strlen(line);
81
82 if (last_newline && line[0] == '\n')
83 {
84 end_of_tag = TRUE;
85 break;
86 }
87 else
88 {
89 last_newline = (line[len-1] == '\n');
90 writer->write_data(writer, chunk_create(line, len));
91 }
92 }
93 while (!end_of_tag);
94
95 tag_encoding = writer->get_buf(writer);
96
97 if (tag_encoding.len > 1)
98 {
99 /* remove trailing newline if present */
100 if (tag_encoding.ptr[tag_encoding.len - 1] == '\n')
101 {
102 tag_encoding.len--;
103 }
104 DBG3(DBG_IMC, " %.*s", tag_encoding.len, tag_encoding.ptr);
105
106 tag = swid_tag_create(tag_encoding, tag_file_path);
107 this->list->insert_last(this->list, tag);
108 }
109 writer->destroy(writer);
110 }
111
112 return SUCCESS;
113 }
114
115 /**
116 * Read SWID tag or software IDs issued by the swid_generator tool
117 */
118 static status_t read_swid_tag_ids(private_swid_inventory_t *this, FILE *file)
119 {
120 swid_tag_id_t *tag_id;
121 chunk_t tag_creator, unique_sw_id, tag_file_path = chunk_empty;
122 char line[BUF_LEN];
123
124 while (TRUE)
125 {
126 char *separator;
127 size_t len;
128
129 if (fgets(line, sizeof(line), file) <= 0)
130 {
131 return SUCCESS;
132 }
133 len = strlen(line);
134
135 /* remove trailing newline if present */
136 if (len > 0 && line[len - 1] == '\n')
137 {
138 len--;
139 }
140 DBG3(DBG_IMC, " %.*s", len, line);
141
142 separator = strchr(line, '_');
143 if (!separator)
144 {
145 DBG1(DBG_IMC, "separation of regid from unique software ID failed");
146 return FAILED;
147 }
148 tag_creator = chunk_create(line, separator - line);
149 separator++;
150
151 unique_sw_id = chunk_create(separator, len - (separator - line));
152 tag_id = swid_tag_id_create(tag_creator, unique_sw_id, tag_file_path);
153 this->list->insert_last(this->list, tag_id);
154 }
155 }
156
157 static status_t generate_tags(private_swid_inventory_t *this, char *generator,
158 swid_inventory_t *targets, bool pretty, bool full)
159 {
160 FILE *file;
161 char command[BUF_LEN];
162 char entity_name[] = "strongSwan Project";
163 char doc_separator[] = "'\n\n'";
164
165 status_t status = SUCCESS;
166
167 if (targets->get_count(targets) == 0)
168 {
169 /* Assemble the SWID generator command */
170 if (this->full_tags)
171 {
172 snprintf(command, BUF_LEN, "%s swid --entity-name \"%s\" "
173 "--doc-separator %s%s%s",
174 generator, entity_name, doc_separator,
175 pretty ? " --pretty" : "", full ? " --full" : "");
176 }
177 else
178 {
179 snprintf(command, BUF_LEN, "%s software-id", generator);
180 }
181
182 /* Open a pipe stream for reading the SWID generator output */
183 file = popen(command, "r");
184 if (!file)
185 {
186 DBG1(DBG_IMC, "failed to run swid_generator command");
187 return NOT_SUPPORTED;
188 }
189
190 if (this->full_tags)
191 {
192 DBG2(DBG_IMC, "SWID tag generation by package manager");
193 status = read_swid_tags(this, file);
194 }
195 else
196 {
197 DBG2(DBG_IMC, "SWID tag ID generation by package manager");
198 status = read_swid_tag_ids(this, file);
199 }
200 pclose(file);
201 }
202 else if (this->full_tags)
203 {
204 swid_tag_id_t *tag_id;
205 enumerator_t *enumerator;
206
207 enumerator = targets->create_enumerator(targets);
208 while (enumerator->enumerate(enumerator, &tag_id))
209 {
210 char software_id[BUF_LEN];
211 chunk_t tag_creator, unique_sw_id;
212
213 tag_creator = tag_id->get_tag_creator(tag_id);
214 unique_sw_id = tag_id->get_unique_sw_id(tag_id, NULL);
215 snprintf(software_id, BUF_LEN, "%.*s_%.*s",
216 tag_creator.len, tag_creator.ptr,
217 unique_sw_id.len, unique_sw_id.ptr);
218
219 /* Assemble the SWID generator command */
220 snprintf(command, BUF_LEN, "%s swid --software-id %s "
221 "--entity-name \"%s\"%s%s",
222 generator, software_id, entity_name,
223 pretty ? " --pretty" : "",
224 full ? " --full" : "");
225
226 /* Open a pipe stream for reading the SWID generator output */
227 file = popen(command, "r");
228 if (!file)
229 {
230 DBG1(DBG_IMC, "failed to run swid_generator command");
231 return NOT_SUPPORTED;
232 }
233 status = read_swid_tags(this, file);
234 pclose(file);
235
236 if (status != SUCCESS)
237 {
238 break;
239 }
240 }
241 enumerator->destroy(enumerator);
242 }
243
244 return status;
245 }
246
247 static bool collect_tags(private_swid_inventory_t *this, char *pathname,
248 swid_inventory_t *targets)
249 {
250 char *rel_name, *abs_name;
251 struct stat st;
252 bool success = FALSE;
253 enumerator_t *enumerator;
254
255 enumerator = enumerator_create_directory(pathname);
256 if (!enumerator)
257 {
258 DBG1(DBG_IMC, "directory '%s' can not be opened, %s",
259 pathname, strerror(errno));
260 return FALSE;
261 }
262 DBG2(DBG_IMC, "entering %s", pathname);
263
264 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
265 {
266 char * start, *stop;
267 chunk_t tag_creator;
268 chunk_t unique_sw_id = chunk_empty, tag_file_path = chunk_empty;
269
270 if (!strstr(rel_name, "regid."))
271 {
272 continue;
273 }
274 if (S_ISDIR(st.st_mode))
275 {
276 /* In case of a targeted request */
277 if (targets->get_count(targets))
278 {
279 enumerator_t *target_enumerator;
280 swid_tag_id_t *tag_id;
281 bool match = FALSE;
282
283 target_enumerator = targets->create_enumerator(targets);
284 while (target_enumerator->enumerate(target_enumerator, &tag_id))
285 {
286 if (chunk_equals(tag_id->get_tag_creator(tag_id),
287 chunk_from_str(rel_name)))
288 {
289 match = TRUE;
290 break;
291 }
292 }
293 target_enumerator->destroy(target_enumerator);
294
295 if (!match)
296 {
297 continue;
298 }
299 }
300
301 if (!collect_tags(this, abs_name, targets))
302 {
303 goto end;
304 }
305 continue;
306 }
307
308 /* parse the regid filename into its components */
309 start = rel_name;
310 stop = strchr(start, '_');
311 if (!stop)
312 {
313 DBG1(DBG_IMC, " %s", rel_name);
314 DBG1(DBG_IMC, " '_' separator not found");
315 goto end;
316 }
317 tag_creator = chunk_create(start, stop-start);
318 start = stop + 1;
319
320 stop = strstr(start, ".swidtag");
321 if (!stop)
322 {
323 DBG1(DBG_IMC, " %s", rel_name);
324 DBG1(DBG_IMC, " swidtag postfix not found");
325 goto end;
326 }
327 unique_sw_id = chunk_create(start, stop-start);
328 tag_file_path = chunk_from_str(abs_name);
329
330 /* In case of a targeted request */
331 if (targets->get_count(targets))
332 {
333 chunk_t target_unique_sw_id, target_tag_creator;
334 enumerator_t *target_enumerator;
335 swid_tag_id_t *tag_id;
336 bool match = FALSE;
337
338 target_enumerator = targets->create_enumerator(targets);
339 while (target_enumerator->enumerate(target_enumerator, &tag_id))
340 {
341 target_unique_sw_id = tag_id->get_unique_sw_id(tag_id, NULL);
342 target_tag_creator = tag_id->get_tag_creator(tag_id);
343
344 if (chunk_equals(target_unique_sw_id, unique_sw_id) &&
345 chunk_equals(target_tag_creator, tag_creator))
346 {
347 match = TRUE;
348 break;
349 }
350 }
351 target_enumerator->destroy(target_enumerator);
352
353 if (!match)
354 {
355 continue;
356 }
357 }
358 DBG2(DBG_IMC, " %s", rel_name);
359
360 if (this->full_tags)
361 {
362 swid_tag_t *tag;
363 chunk_t *xml_tag;
364
365 xml_tag = chunk_map(abs_name, FALSE);
366 if (!xml_tag)
367 {
368 DBG1(DBG_IMC, " opening '%s' failed: %s", abs_name,
369 strerror(errno));
370 goto end;
371 }
372
373 tag = swid_tag_create(*xml_tag, tag_file_path);
374 this->list->insert_last(this->list, tag);
375 chunk_unmap(xml_tag);
376 }
377 else
378 {
379 swid_tag_id_t *tag_id;
380
381 tag_id = swid_tag_id_create(tag_creator, unique_sw_id, tag_file_path);
382 this->list->insert_last(this->list, tag_id);
383 }
384 }
385 success = TRUE;
386
387 end:
388 enumerator->destroy(enumerator);
389 DBG2(DBG_IMC, "leaving %s", pathname);
390
391 return success;
392 }
393
394 METHOD(swid_inventory_t, collect, bool,
395 private_swid_inventory_t *this, char *directory, char *generator,
396 swid_inventory_t *targets, bool pretty, bool full)
397 {
398 /**
399 * Tags are generated by a package manager
400 */
401 generate_tags(this, generator, targets, pretty, full);
402
403 /**
404 * Collect swidtag files by iteratively entering all directories in
405 * the tree under the "directory" path.
406 */
407 return collect_tags(this, directory, targets);
408 }
409
410 METHOD(swid_inventory_t, add, void,
411 private_swid_inventory_t *this, void *item)
412 {
413 this->list->insert_last(this->list, item);
414 }
415
416 METHOD(swid_inventory_t, get_count, int,
417 private_swid_inventory_t *this)
418 {
419 return this->list->get_count(this->list);
420 }
421
422 METHOD(swid_inventory_t, create_enumerator, enumerator_t*,
423 private_swid_inventory_t *this)
424 {
425 return this->list->create_enumerator(this->list);
426 }
427
428 METHOD(swid_inventory_t, destroy, void,
429 private_swid_inventory_t *this)
430 {
431 if (this->full_tags)
432 {
433 this->list->destroy_offset(this->list, offsetof(swid_tag_t, destroy));
434 }
435 else
436 {
437 this->list->destroy_offset(this->list, offsetof(swid_tag_id_t, destroy));
438 }
439 free(this);
440 }
441
442 /**
443 * See header
444 */
445 swid_inventory_t *swid_inventory_create(bool full_tags)
446 {
447 private_swid_inventory_t *this;
448
449 INIT(this,
450 .public = {
451 .collect = _collect,
452 .add = _add,
453 .get_count = _get_count,
454 .create_enumerator = _create_enumerator,
455 .destroy = _destroy,
456 },
457 .full_tags = full_tags,
458 .list = linked_list_create(),
459 );
460
461 return &this->public;
462 }