Use python-based swidGenerator to generated SWID tags
[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 static status_t generate_tags(private_swid_inventory_t *this, char *generator,
56 swid_inventory_t *targets, bool pretty, bool full)
57 {
58 FILE *file;
59 char command[512], line[2048];
60 chunk_t tag_creator, unique_sw_id, tag_file_path = chunk_empty;
61 swid_tag_id_t *tag_id;
62 swid_tag_t *tag;
63 status_t status = SUCCESS;
64
65 /* Assemble the SWID generator command */
66 snprintf(command, sizeof(command), "%s %s%s%s\n", generator,
67 (this->full_tags) ? "swid" : "software-id",
68 (this->full_tags && pretty) ? " --pretty" : "",
69 (this->full_tags && full) ? " --full" : "");
70
71 /* Open a pipe stream for reading the output of the dpkg-query commmand */
72 file = popen(command, "r");
73 if (!file)
74 {
75 DBG1(DBG_IMC, "failed to run swid_generator command");
76 return NOT_SUPPORTED;
77 }
78 if (this->full_tags)
79 {
80 bio_writer_t *writer;
81 chunk_t tag_encoding;
82 bool more_tags = TRUE, end_of_tag;
83
84 DBG2(DBG_IMC, "SWID tags generated by package manager:");
85 while (more_tags)
86 {
87 end_of_tag = FALSE;
88 writer = bio_writer_create(512);
89 do
90 {
91 if (fgets(line, sizeof(line), file) <= 0)
92 {
93 more_tags = FALSE;
94 end_of_tag = TRUE;
95 break;
96 }
97 if (line[0] == '\n')
98 {
99 end_of_tag = TRUE;
100 break;
101 }
102 else
103 {
104 writer->write_data(writer, chunk_from_str(line));
105 }
106 }
107 while (!end_of_tag);
108
109 tag_encoding = writer->get_buf(writer);
110
111 /* remove trailing newline if present */
112 if (tag_encoding.len > 0 &&
113 tag_encoding.ptr[tag_encoding.len - 1] == '\n')
114 {
115 tag_encoding.len--;
116 }
117 DBG2(DBG_IMC, " %.*s", tag_encoding.len, tag_encoding.ptr);
118
119 tag = swid_tag_create(tag_encoding, tag_file_path);
120 this->list->insert_last(this->list, tag);
121 writer->destroy(writer);
122 }
123 }
124 else
125 {
126 DBG2(DBG_IMC, "SWID tag IDs generated by package manager:");
127 while (TRUE)
128 {
129 char *separator;
130 size_t len;
131
132 if (fgets(line, sizeof(line), file) <= 0)
133 {
134 goto end;
135 }
136 len = strlen(line);
137
138 /* remove trailing newline if present */
139 if (len > 0 && line[len - 1] == '\n')
140 {
141 len--;
142 }
143 DBG2(DBG_IMC, " %.*s", len, line);
144
145 separator = strchr(line, '_');
146 if (!separator)
147 {
148 DBG1(DBG_IMC, "separatation of regid from unique software ID "
149 "failed");
150 status = FAILED;
151 goto end;
152 }
153 tag_creator = chunk_create(line, separator - line);
154 separator++;
155
156 unique_sw_id = chunk_create(separator, len - (separator - line));
157 tag_id = swid_tag_id_create(tag_creator, unique_sw_id, tag_file_path);
158 this->list->insert_last(this->list, tag_id);
159
160 if (fgets(line, sizeof(line), file) <= 0)
161 {
162 goto end;
163 }
164 }
165 }
166
167 end:
168 pclose(file);
169 return status;
170 }
171
172 static bool collect_tags(private_swid_inventory_t *this, char *pathname,
173 swid_inventory_t *targets)
174 {
175 char *rel_name, *abs_name;
176 struct stat st;
177 bool success = FALSE;
178 enumerator_t *enumerator;
179
180 enumerator = enumerator_create_directory(pathname);
181 if (!enumerator)
182 {
183 DBG1(DBG_IMC, "directory '%s' can not be opened, %s",
184 pathname, strerror(errno));
185 return FALSE;
186 }
187 DBG2(DBG_IMC, "entering %s", pathname);
188
189 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
190 {
191 char * start, *stop;
192 chunk_t tag_creator;
193 chunk_t unique_sw_id = chunk_empty, tag_file_path = chunk_empty;
194 if (!strstr(rel_name, "regid."))
195 {
196 continue;
197 }
198 if (S_ISDIR(st.st_mode))
199 {
200 /* In case of a targeted request */
201 if (targets->get_count(targets))
202 {
203 enumerator_t *target_enumerator;
204 swid_tag_id_t *tag_id;
205 bool match = FALSE;
206
207 target_enumerator = targets->create_enumerator(targets);
208 while (target_enumerator->enumerate(target_enumerator, &tag_id))
209 {
210 if (chunk_equals(tag_id->get_tag_creator(tag_id),
211 chunk_from_str(rel_name)))
212 {
213 match = TRUE;
214 break;
215 }
216 }
217 target_enumerator->destroy(target_enumerator);
218
219 if (!match)
220 {
221 continue;
222 }
223 }
224
225 if (!collect_tags(this, abs_name, targets))
226 {
227 goto end;
228 }
229 continue;
230 }
231
232 /* parse the regid filename into its components */
233 start = rel_name;
234 stop = strchr(start, '_');
235 if (!stop)
236 {
237 DBG1(DBG_IMC, " %s", rel_name);
238 DBG1(DBG_IMC, " '_' separator not found");
239 goto end;
240 }
241 tag_creator = chunk_create(start, stop-start);
242 start = stop + 1;
243
244 stop = strstr(start, ".swidtag");
245 if (!stop)
246 {
247 DBG1(DBG_IMC, " %s", rel_name);
248 DBG1(DBG_IMC, " swidtag postfix not found");
249 goto end;
250 }
251 unique_sw_id = chunk_create(start, stop-start);
252 tag_file_path = chunk_from_str(abs_name);
253
254 /* In case of a targeted request */
255 if (targets->get_count(targets))
256 {
257 enumerator_t *target_enumerator;
258 swid_tag_id_t *tag_id;
259 bool match = FALSE;
260
261 target_enumerator = targets->create_enumerator(targets);
262 while (target_enumerator->enumerate(target_enumerator, &tag_id))
263 {
264 if (chunk_equals(tag_id->get_unique_sw_id(tag_id, NULL),
265 unique_sw_id) &&
266 chunk_equals(tag_id->get_tag_creator(tag_id),
267 tag_creator))
268 {
269 match = TRUE;
270 break;
271 }
272 }
273 target_enumerator->destroy(target_enumerator);
274
275 if (!match)
276 {
277 continue;
278 }
279 }
280 DBG2(DBG_IMC, " %s", rel_name);
281
282 if (this->full_tags)
283 {
284 swid_tag_t *tag;
285 chunk_t *xml_tag;
286
287 xml_tag = chunk_map(abs_name, FALSE);
288 if (!xml_tag)
289 {
290 DBG1(DBG_IMC, " opening '%s' failed: %s", abs_name,
291 strerror(errno));
292 goto end;
293 }
294
295 tag = swid_tag_create(*xml_tag, tag_file_path);
296 this->list->insert_last(this->list, tag);
297 chunk_unmap(xml_tag);
298 }
299 else
300 {
301 swid_tag_id_t *tag_id;
302
303 tag_id = swid_tag_id_create(tag_creator, unique_sw_id, tag_file_path);
304 this->list->insert_last(this->list, tag_id);
305 }
306 }
307 success = TRUE;
308
309 end:
310 enumerator->destroy(enumerator);
311 DBG2(DBG_IMC, "leaving %s", pathname);
312
313 return success;
314 }
315
316 METHOD(swid_inventory_t, collect, bool,
317 private_swid_inventory_t *this, char *directory, char *generator,
318 swid_inventory_t *targets, bool pretty, bool full)
319 {
320 /**
321 * Tags are generated by a package manager
322 */
323 generate_tags(this, generator, targets, pretty, full);
324
325 /**
326 * Collect swidtag files by iteratively entering all directories in
327 * the tree under the "directory" path.
328 */
329 return collect_tags(this, directory, targets);
330 }
331
332 METHOD(swid_inventory_t, add, void,
333 private_swid_inventory_t *this, void *item)
334 {
335 this->list->insert_last(this->list, item);
336 }
337
338 METHOD(swid_inventory_t, get_count, int,
339 private_swid_inventory_t *this)
340 {
341 return this->list->get_count(this->list);
342 }
343
344 METHOD(swid_inventory_t, create_enumerator, enumerator_t*,
345 private_swid_inventory_t *this)
346 {
347 return this->list->create_enumerator(this->list);
348 }
349
350 METHOD(swid_inventory_t, destroy, void,
351 private_swid_inventory_t *this)
352 {
353 if (this->full_tags)
354 {
355 this->list->destroy_offset(this->list, offsetof(swid_tag_t, destroy));
356 }
357 else
358 {
359 this->list->destroy_offset(this->list, offsetof(swid_tag_id_t, destroy));
360 }
361 free(this);
362 }
363
364 /**
365 * See header
366 */
367 swid_inventory_t *swid_inventory_create(bool full_tags)
368 {
369 private_swid_inventory_t *this;
370
371 INIT(this,
372 .public = {
373 .collect = _collect,
374 .add = _add,
375 .get_count = _get_count,
376 .create_enumerator = _create_enumerator,
377 .destroy = _destroy,
378 },
379 .full_tags = full_tags,
380 .list = linked_list_create(),
381 );
382
383 return &this->public;
384 }