Annotation of embedaddon/strongswan/src/libimcv/swima/swima_collector.c, revision 1.1.1.1
1.1 misho 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: #define _GNU_SOURCE /* for asprintf() */
17:
18: #include "swima_collector.h"
19:
20: #include <swid_gen/swid_gen.h>
21:
22: #include <collections/linked_list.h>
23: #include <utils/debug.h>
24:
25: #include <stdio.h>
26: #include <fcntl.h>
27: #include <unistd.h>
28: #include <sys/stat.h>
29: #include <libgen.h>
30: #include <errno.h>
31:
32: #define SOURCE_ID_GENERATOR 1
33: #define SOURCE_ID_COLLECTOR 2
34:
35: #ifndef SWID_DIRECTORY
36: #define SWID_DIRECTORY NULL
37: #endif
38:
39: /**
40: * Directories to be skipped by collector
41: */
42: static const char* skip_directories[] = {
43: "/usr/share/doc",
44: "/usr/share/help",
45: "/usr/share/icons",
46: "/usr/share/gnome/help"
47: };
48:
49: typedef struct private_swima_collector_t private_swima_collector_t;
50:
51: /**
52: * Private data of a swima_collector_t object.
53: *
54: */
55: struct private_swima_collector_t {
56:
57: /**
58: * Public swima_collector_t interface.
59: */
60: swima_collector_t public;
61:
62: /**
63: * Collect Software Identifiers only
64: */
65: bool sw_id_only;
66:
67: /**
68: * Software Collector Database [if it exists]
69: */
70: database_t *db;
71:
72: /**
73: * List of Software [Identifier] records
74: */
75: swima_inventory_t *inventory;
76:
77: /**
78: * List of Software [Identifier] events
79: */
80: swima_events_t *events;
81:
82: };
83:
84: /**
85: * Extract Software Identifier from SWID tag
86: */
87: static status_t extract_sw_id(chunk_t swid_tag, chunk_t *sw_id)
88: {
89: char *pos, *tag, *tagid, *regid;
90: size_t len, tagid_len, regid_len;
91: status_t status = NOT_FOUND;
92:
93: /* Copy at most 1023 bytes of the SWID tag and null-terminate it */
94: len = min(1023, swid_tag.len);
95: pos = tag = strndup(swid_tag.ptr, len);
96:
97: tagid= strstr(pos, "tagId=\"");
98: if (tagid == NULL)
99: {
100: goto end;
101: }
102: tagid += 7;
103: len -= tagid - pos - 7;
104:
105: pos = strchr(tagid, '"');
106: if (pos == NULL)
107: {
108: goto end;
109: }
110: tagid_len = pos - tagid;
111:
112: regid= strstr(pos, "regid=\"");
113: if (regid == NULL)
114: {
115: goto end;
116: }
117: regid += 7;
118: len -= regid - pos - 7;
119:
120: pos = strchr(regid, '"');
121: if (pos == NULL)
122: {
123: goto end;
124: }
125: regid_len = pos - regid;
126:
127: *sw_id = chunk_cat("ccc", chunk_create(regid, regid_len),
128: chunk_from_chars('_','_'),
129: chunk_create(tagid, tagid_len));
130: status = SUCCESS;
131: end:
132: free(tag);
133:
134: return status;
135: }
136:
137: static status_t retrieve_inventory(private_swima_collector_t *this,
138: swima_inventory_t *targets)
139: {
140: char *name;
141: uint32_t record_id, source;
142: swima_record_t *sw_record;
143: chunk_t sw_id;
144: enumerator_t *e;
145:
146: /* Retrieve complete software identifier inventory */
147: e = this->db->query(this->db,
148: "SELECT id, name, source FROM sw_identifiers WHERE installed = 1 "
149: "ORDER BY name ASC", DB_UINT, DB_TEXT, DB_UINT);
150: if (!e)
151: {
152: DBG1(DBG_IMC, "database query for installed sw_identifiers failed");
153: return FAILED;
154: }
155: while (e->enumerate(e, &record_id, &name, &source))
156: {
157: sw_id = chunk_from_str(name);
158: sw_record = swima_record_create(record_id, sw_id, chunk_empty);
159: sw_record->set_source_id(sw_record, source);
160: this->inventory->add(this->inventory, sw_record);
161: }
162: e->destroy(e);
163:
164: return SUCCESS;
165: }
166:
167: static status_t retrieve_events(private_swima_collector_t *this,
168: swima_inventory_t *targets)
169: {
170: enumerator_t *e;
171: char *name, *timestamp;
172: uint32_t record_id, source, action, eid, earliest_eid;
173: chunk_t sw_id, ev_ts;
174: swima_record_t *sw_record;
175: swima_event_t *sw_event;
176:
177: earliest_eid = targets->get_eid(targets, NULL);
178:
179: /* Retrieve complete software identifier inventory */
180: e = this->db->query(this->db,
181: "SELECT e.id, e.timestamp, i.id, i.name, i.source, s.action "
182: "FROM sw_events as s JOIN events AS e ON s.eid = e.id "
183: "JOIN sw_identifiers as i ON s.sw_id = i.id WHERE s.eid >= ?"
184: "ORDER BY s.eid, i.name, s.action ASC", DB_UINT, earliest_eid,
185: DB_UINT, DB_TEXT, DB_UINT, DB_TEXT, DB_UINT, DB_UINT);
186: if (!e)
187: {
188: DBG1(DBG_IMC, "database query for sw_events failed");
189: return FAILED;
190: }
191: while (e->enumerate(e, &eid, ×tamp, &record_id, &name, &source, &action))
192: {
193: sw_id = chunk_from_str(name);
194: ev_ts = chunk_from_str(timestamp);
195: sw_record = swima_record_create(record_id, sw_id, chunk_empty);
196: sw_record->set_source_id(sw_record, source);
197: sw_event = swima_event_create(eid, ev_ts, action, sw_record);
198: this->events->add(this->events, sw_event);
199: }
200: e->destroy(e);
201:
202: return SUCCESS;
203: }
204:
205: static status_t generate_tags(private_swima_collector_t *this,
206: swima_inventory_t *targets, bool pretty, bool full)
207: {
208: swid_gen_t *swid_gen;
209: swima_record_t *target, *sw_record;
210: enumerator_t *enumerator;
211: status_t status = SUCCESS;
212:
213: swid_gen = swid_gen_create();
214:
215: if (targets->get_count(targets) == 0)
216: {
217: chunk_t out, sw_id, swid_tag = chunk_empty;
218:
219: DBG2(DBG_IMC, "SWID tag%s generation by package manager",
220: this->sw_id_only ? " ID" : "");
221:
222: enumerator = swid_gen->create_tag_enumerator(swid_gen, this->sw_id_only,
223: full, pretty);
224: if (enumerator)
225: {
226: while (enumerator->enumerate(enumerator, &out))
227: {
228: if (this->sw_id_only)
229: {
230: sw_id = out;
231: }
232: else
233: {
234: swid_tag = out;
235: status = extract_sw_id(swid_tag, &sw_id);
236: if (status != SUCCESS)
237: {
238: DBG1(DBG_IMC, "software id could not be extracted "
239: "from tag");
240: chunk_free(&swid_tag);
241: break;
242: }
243: }
244: sw_record = swima_record_create(0, sw_id, chunk_empty);
245: sw_record->set_source_id(sw_record, SOURCE_ID_GENERATOR);
246: if (!this->sw_id_only)
247: {
248: sw_record->set_record(sw_record, swid_tag);
249: chunk_free(&swid_tag);
250: }
251: this->inventory->add(this->inventory, sw_record);
252: chunk_free(&sw_id);
253: }
254: enumerator->destroy(enumerator);
255: }
256: else
257: {
258: status = NOT_SUPPORTED;
259: }
260: }
261: else if (!this->sw_id_only)
262: {
263: DBG2(DBG_IMC, "targeted SWID tag generation");
264:
265: enumerator = targets->create_enumerator(targets);
266: while (enumerator->enumerate(enumerator, &target))
267: {
268: swima_record_t *sw_record;
269: char *tag = NULL, *name, *package, *version;
270: u_int installed;
271: chunk_t sw_id;
272: enumerator_t *e;
273:
274: sw_id = target->get_sw_id(target, NULL);
275: name = strndup(sw_id.ptr, sw_id.len);
276:
277: if (this->db)
278: {
279: e = this->db->query(this->db,
280: "SELECT package, version, installed "
281: "FROM sw_identifiers WHERE name = ?", DB_TEXT, name,
282: DB_TEXT, DB_TEXT, DB_UINT);
283: if (!e)
284: {
285: DBG1(DBG_IMC, "database query for sw_identifiers failed");
286: status = FAILED;
287: free(name);
288: break;
289: }
290: if (e->enumerate(e, &package, &version, &installed))
291: {
292: tag = swid_gen->generate_tag(swid_gen, name, package,
293: version, full && installed, pretty);
294: }
295: e->destroy(e);
296: }
297: else
298: {
299: tag = swid_gen->generate_tag(swid_gen, name, NULL, NULL,
300: full, pretty);
301: }
302: free(name);
303:
304: if (tag)
305: {
306: DBG2(DBG_IMC, " %.*s", sw_id.len, sw_id.ptr);
307: sw_record = swima_record_create(0, sw_id, chunk_empty);
308: sw_record->set_source_id(sw_record, SOURCE_ID_GENERATOR);
309: sw_record->set_record(sw_record, chunk_from_str(tag));
310: this->inventory->add(this->inventory, sw_record);
311: free(tag);
312: }
313: }
314: enumerator->destroy(enumerator);
315: }
316: swid_gen->destroy(swid_gen);
317:
318: return status;
319: }
320:
321: static bool collect_tags(private_swima_collector_t *this, char *pathname,
322: swima_inventory_t *targets, bool is_swidtag_dir)
323: {
324: char *rel_name, *abs_name, *suffix, *pos, *uri;
325: chunk_t *swid_tag, sw_id, sw_locator;
326: swima_record_t *sw_record;
327: struct stat st;
328: bool success = FALSE, skip, is_new_swidtag_dir;
329: enumerator_t *enumerator;
330: int i;
331:
332: if (!pathname)
333: {
334: return TRUE;
335: }
336:
337: enumerator = enumerator_create_directory(pathname);
338: if (!enumerator)
339: {
340: DBG1(DBG_IMC, "directory '%s' can not be opened, %s",
341: pathname, strerror(errno));
342: return FALSE;
343: }
344:
345: while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
346: {
347: if (S_ISDIR(st.st_mode))
348: {
349: skip = FALSE;
350:
351: for (i = 0; i < countof(skip_directories); i++)
352: {
353: if (streq(abs_name, skip_directories[i]))
354: {
355: skip = TRUE;
356: break;
357: }
358: }
359:
360: if (skip)
361: {
362: continue;
363: }
364:
365: is_new_swidtag_dir = streq(rel_name, "swidtag");
366: if (is_new_swidtag_dir)
367: {
368: DBG2(DBG_IMC, "entering %s", pathname);
369: }
370: if (!collect_tags(this, abs_name, targets, is_swidtag_dir ||
371: is_new_swidtag_dir))
372: {
373: goto end;
374: }
375: if (is_new_swidtag_dir)
376: {
377: DBG2(DBG_IMC, "leaving %s", pathname);
378: }
379: }
380:
381: if (!is_swidtag_dir)
382: {
383: continue;
384: }
385:
386: /* found a swidtag file? */
387: suffix = strstr(rel_name, ".swidtag");
388: if (!suffix)
389: {
390: continue;
391: }
392:
393: /* load the swidtag file */
394: swid_tag = chunk_map(abs_name, FALSE);
395: if (!swid_tag)
396: {
397: DBG1(DBG_IMC, " opening '%s' failed: %s", abs_name,
398: strerror(errno));
399: goto end;
400: }
401:
402: /* extract software identity from SWID tag */
403: if (extract_sw_id(*swid_tag, &sw_id) != SUCCESS)
404: {
405: DBG1(DBG_IMC, "software id could not be extracted from SWID tag");
406: chunk_unmap(swid_tag);
407: goto end;
408: }
409:
410: /* In case of a targeted request */
411: if (targets->get_count(targets))
412: {
413: enumerator_t *target_enumerator;
414: swima_record_t *target;
415: bool match = FALSE;
416:
417: target_enumerator = targets->create_enumerator(targets);
418: while (target_enumerator->enumerate(target_enumerator, &target))
419: {
420: if (chunk_equals(target->get_sw_id(target, NULL), sw_id))
421: {
422: DBG2(DBG_IMC, " %.*s", sw_id.len, sw_id.ptr);
423: match = TRUE;
424: break;
425: }
426: }
427: target_enumerator->destroy(target_enumerator);
428:
429: if (!match)
430: {
431: chunk_unmap(swid_tag);
432: chunk_free(&sw_id);
433: continue;
434: }
435: }
436: DBG2(DBG_IMC, " %s", rel_name);
437:
438: sw_locator = chunk_empty;
439: pos = strstr(pathname, "/swidtag");
440: if (pos &&
441: asprintf(&uri, "file://%.*s", (int)(pos - pathname), pathname) > 0)
442: {
443: sw_locator = chunk_from_str(uri);
444: }
445: sw_record = swima_record_create(0, sw_id, sw_locator);
446: sw_record->set_source_id(sw_record, SOURCE_ID_COLLECTOR);
447: if (!this->sw_id_only)
448: {
449: sw_record->set_record(sw_record, *swid_tag);
450: }
451: this->inventory->add(this->inventory, sw_record);
452:
453: chunk_unmap(swid_tag);
454: chunk_free(&sw_id);
455: chunk_free(&sw_locator);
456: }
457: success = TRUE;
458:
459: end:
460: enumerator->destroy(enumerator);
461:
462: return success;
463: }
464:
465: METHOD(swima_collector_t, collect_inventory, swima_inventory_t*,
466: private_swima_collector_t *this, bool sw_id_only, swima_inventory_t *targets)
467: {
468: bool pretty, full;
469: char *directory;
470: status_t status;
471:
472: directory = lib->settings->get_str(lib->settings,
473: "%s.plugins.imc-swima.swid_directory",
474: SWID_DIRECTORY, lib->ns);
475: pretty = lib->settings->get_bool(lib->settings,
476: "%s.plugins.imc-swima.swid_pretty",
477: FALSE, lib->ns);
478: full = lib->settings->get_bool(lib->settings,
479: "%s.plugins.imc-swima.swid_full",
480: FALSE, lib->ns);
481:
482: /**
483: * Re-initialize collector
484: */
485: this->sw_id_only = sw_id_only;
486: this->inventory->clear(this->inventory);
487:
488: /**
489: * Source 1: Tags are generated by a package manager
490: */
491: if (sw_id_only && this->db)
492: {
493: status = retrieve_inventory(this, targets);
494: }
495: else
496: {
497: status = generate_tags(this, targets, pretty, full);
498: }
499:
500: /**
501: * Source 2: Collect swidtag files by iteratively entering all
502: * directories in the tree under the "directory" path.
503: */
504: DBG2(DBG_IMC, "SWID tag%s collection", sw_id_only ? " ID" : "");
505: collect_tags(this, directory, targets, FALSE);
506:
507: return status == SUCCESS ? this->inventory : NULL;
508: }
509:
510: METHOD(swima_collector_t, collect_events, swima_events_t*,
511: private_swima_collector_t *this, bool sw_id_only, swima_inventory_t *targets)
512: {
513: if (!sw_id_only || !this->db)
514: {
515: return NULL;
516: }
517:
518: /**
519: * Re-initialize collector
520: */
521: this->sw_id_only = sw_id_only;
522: this->events->clear(this->events);
523:
524: return retrieve_events(this, targets) == SUCCESS ? this->events : NULL;
525: }
526:
527: METHOD(swima_collector_t, destroy, void,
528: private_swima_collector_t *this)
529: {
530: DESTROY_IF(this->db);
531: this->inventory->destroy(this->inventory);
532: this->events->destroy(this->events);
533: free(this);
534: }
535:
536: /**
537: * See header
538: */
539: swima_collector_t *swima_collector_create(void)
540: {
541: private_swima_collector_t *this;
542: char *database;
543: uint32_t last_eid = 1, eid_epoch = 0x11223344;
544:
545: INIT(this,
546: .public = {
547: .collect_inventory = _collect_inventory,
548: .collect_events = _collect_events,
549: .destroy = _destroy,
550: },
551: .inventory = swima_inventory_create(),
552: .events = swima_events_create(),
553: );
554:
555: database = lib->settings->get_str(lib->settings,
556: "%s.plugins.imc-swima.swid_database", NULL, lib->ns);
557:
558: /* If we have an URI, try to connect to sw_collector database */
559: if (database)
560: {
561: database_t *db = lib->db->create(lib->db, database);
562:
563: if (db)
564: {
565: enumerator_t *e;
566:
567: /* Get last event ID and corresponding epoch */
568: e = db->query(db,
569: "SELECT id, epoch FROM events ORDER BY timestamp DESC",
570: DB_UINT, DB_UINT);
571: if (!e || !e->enumerate(e, &last_eid, &eid_epoch))
572: {
573: DBG1(DBG_IMC, "database query for last event failed");
574: DESTROY_IF(e);
575: db->destroy(db);
576: }
577: else
578: {
579: /* The query worked, attach collector database permanently */
580: e->destroy(e);
581: this->db = db;
582: }
583: }
584: else
585: {
586: DBG1(DBG_IMC, "opening sw-collector database URI '%s' failed",
587: database);
588: }
589: }
590: if (!this->db)
591: {
592: /* Set the event ID epoch and last event ID manually */
593: eid_epoch = lib->settings->get_int(lib->settings,
594: "%s.plugins.imc-swima.eid_epoch",
595: eid_epoch, lib->ns);
596: }
597: this->inventory->set_eid(this->inventory, last_eid, eid_epoch);
598: this->events->set_eid(this->events, last_eid, eid_epoch);
599:
600: return &this->public;
601: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>