Annotation of embedaddon/strongswan/src/sw-collector/sw-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
17: #include <stdio.h>
18: #include <string.h>
19: #include <errno.h>
20: #include <getopt.h>
21: #include <unistd.h>
22: #ifdef HAVE_SYSLOG
23: # include <syslog.h>
24: #endif
25:
26: #include "sw_collector_db.h"
27: #include "sw_collector_history.h"
28: #include "sw_collector_rest_api.h"
29: #include "sw_collector_dpkg.h"
30:
31: #include <library.h>
32: #include <utils/debug.h>
33: #include <utils/lexparser.h>
34: #include <collections/hashtable.h>
35:
36: #include <swid_gen/swid_gen.h>
37: #include <swid_gen/swid_gen_info.h>
38: /**
39: * global debug output variables
40: */
41: static int debug_level = 2;
42: static bool stderr_quiet = FALSE;
43: static int max_count = 0;
44:
45: typedef enum collector_op_t collector_op_t;
46:
47: enum collector_op_t {
48: COLLECTOR_OP_EXTRACT,
49: COLLECTOR_OP_LIST,
50: COLLECTOR_OP_UNREGISTERED,
51: COLLECTOR_OP_GENERATE,
52: COLLECTOR_OP_MIGRATE,
53: COLLECTOR_OP_CHECK
54: };
55:
56: /**
57: * sw_collector dbg function
58: */
59: static void sw_collector_dbg(debug_t group, level_t level, char *fmt, ...)
60: {
61: va_list args;
62:
63: if (level <= debug_level)
64: {
65: if (!stderr_quiet)
66: {
67: va_start(args, fmt);
68: vfprintf(stderr, fmt, args);
69: fprintf(stderr, "\n");
70: va_end(args);
71: }
72:
73: #ifdef HAVE_SYSLOG
74: {
75: int priority = LOG_INFO;
76: char buffer[8192];
77: char *current = buffer, *next;
78:
79: /* write in memory buffer first */
80: va_start(args, fmt);
81: vsnprintf(buffer, sizeof(buffer), fmt, args);
82: va_end(args);
83:
84: /* do a syslog with every line */
85: while (current)
86: {
87: next = strchr(current, '\n');
88: if (next)
89: {
90: *(next++) = '\0';
91: }
92: syslog(priority, "%s\n", current);
93: current = next;
94: }
95: }
96: #endif /* HAVE_SYSLOG */
97: }
98: }
99:
100: /**
101: * atexit handler
102: */
103: static void cleanup(void)
104: {
105: library_deinit();
106: #ifdef HAVE_SYSLOG
107: closelog();
108: #endif
109: }
110:
111: /**
112: * Display usage of sw-collector command
113: */
114: static void usage(void)
115: {
116: printf("\
117: Usage:\n\
118: sw-collector --help\n\
119: sw-collector [--debug <level>] [--quiet] [--count <event count>]\n\
120: sw-collector [--debug <level>] [--quiet] [--installed|--removed] \
121: --list|-unregistered\n\
122: sw-collector [--debug <level>] [--quiet] [--installed|--removed] \
123: [--full] --generate\n\
124: sw-collector [--debug <level>] [--quiet] --migrate\n\
125: sw-collector [--debug <level>] [--quiet] --check\n");
126: }
127:
128: /**
129: * Parse command line options
130: */
131: static collector_op_t do_args(int argc, char *argv[], bool *full_tags,
132: sw_collector_db_query_t *query_type)
133: {
134: collector_op_t op = COLLECTOR_OP_EXTRACT;
135: bool installed = FALSE, removed = FALSE, full = FALSE;
136:
137: /* reinit getopt state */
138: optind = 0;
139:
140: while (TRUE)
141: {
142: int c;
143:
144: struct option long_opts[] = {
145: { "help", no_argument, NULL, 'h' },
146: { "check", no_argument, NULL, 'C' },
147: { "count", required_argument, NULL, 'c' },
148: { "debug", required_argument, NULL, 'd' },
149: { "full", no_argument, NULL, 'f' },
150: { "generate", no_argument, NULL, 'g' },
151: { "installed", no_argument, NULL, 'i' },
152: { "list", no_argument, NULL, 'l' },
153: { "migrate", no_argument, NULL, 'm' },
154: { "quiet", no_argument, NULL, 'q' },
155: { "removed", no_argument, NULL, 'r' },
156: { "unregistered", no_argument, NULL, 'u' },
157: { 0,0,0,0 }
158: };
159:
160: c = getopt_long(argc, argv, "hCc:d:fgilmqru", long_opts, NULL);
161: switch (c)
162: {
163: case EOF:
164: break;
165: case 'h':
166: usage();
167: exit(SUCCESS);
168: case 'C':
169: op = COLLECTOR_OP_CHECK;
170: continue;
171: case 'c':
172: max_count = atoi(optarg);
173: continue;
174: case 'd':
175: debug_level = atoi(optarg);
176: continue;
177: case 'f':
178: full = TRUE;
179: continue;
180: case 'g':
181: op = COLLECTOR_OP_GENERATE;
182: continue;
183: case 'i':
184: installed = TRUE;
185: continue;
186: case 'l':
187: op = COLLECTOR_OP_LIST;
188: continue;
189: case 'm':
190: op = COLLECTOR_OP_MIGRATE;
191: continue;
192: case 'q':
193: stderr_quiet = TRUE;
194: continue;
195: case 'r':
196: removed = TRUE;
197: continue;
198: case 'u':
199: op = COLLECTOR_OP_UNREGISTERED;
200: continue;
201: default:
202: usage();
203: exit(EXIT_FAILURE);
204: }
205: break;
206: }
207:
208: if ((!installed && !removed) || (installed && removed))
209: {
210: *query_type = SW_QUERY_ALL;
211: }
212: else if (installed)
213: {
214: *query_type = SW_QUERY_INSTALLED;
215: }
216: else
217: {
218: *query_type = SW_QUERY_REMOVED;
219: }
220: *full_tags = full;
221:
222: return op;
223: }
224:
225: /**
226: * Extract software events from apt history log files
227: */
228: static int extract_history(sw_collector_db_t *db)
229: {
230: sw_collector_history_t *history = NULL;
231: uint32_t epoch, last_eid, eid = 0;
232: char *history_path, *last_time = NULL, rfc_time[21];
233: chunk_t *h, history_chunk, line, cmd;
234: int status = EXIT_FAILURE;
235: bool skip = TRUE;
236:
237: /* open history file for reading */
238: history_path = lib->settings->get_str(lib->settings, "%s.history", NULL,
239: lib->ns);
240: if (!history_path)
241: {
242: fprintf(stderr, "sw-collector.history path not set.\n");
243: return EXIT_FAILURE;
244: }
245: h = chunk_map(history_path, FALSE);
246: if (!h)
247: {
248: fprintf(stderr, "opening '%s' failed: %s", history_path,
249: strerror(errno));
250: return EXIT_FAILURE;
251: }
252: history_chunk = *h;
253:
254: /* Instantiate history extractor */
255: history = sw_collector_history_create(db, 1);
256: if (!history)
257: {
258: chunk_unmap(h);
259: return EXIT_FAILURE;
260: }
261:
262: /* retrieve last event in database */
263: if (!db->get_last_event(db, &last_eid, &epoch, &last_time) || !last_eid)
264: {
265: goto end;
266: }
267: DBG0(DBG_IMC, "Last-Event: %s, eid = %u, epoch = %u",
268: last_time, last_eid, epoch);
269:
270: /* parse history file */
271: while (fetchline(&history_chunk, &line))
272: {
273: if (line.len == 0)
274: {
275: continue;
276: }
277: if (!extract_token(&cmd, ':', &line))
278: {
279: fprintf(stderr, "terminator symbol ':' not found.\n");
280: goto end;
281: }
282: if (match("Start-Date", &cmd))
283: {
284: if (!history->extract_timestamp(history, line, rfc_time))
285: {
286: goto end;
287: }
288:
289: /* have we reached new history entries? */
290: if (skip && strcmp(rfc_time, last_time) > 0)
291: {
292: skip = FALSE;
293: }
294: if (skip)
295: {
296: continue;
297: }
298:
299: /* insert new event into database */
300: eid = db->add_event(db, rfc_time);
301: if (!eid)
302: {
303: goto end;
304: }
305: DBG1(DBG_IMC, "Start-Date: %s, eid = %u, epoch = %u",
306: rfc_time, eid, epoch);
307: }
308: else if (skip)
309: {
310: /* skip old history entries which have already been processed */
311: continue;
312: }
313: else if (match("Install", &cmd))
314: {
315: DBG1(DBG_IMC, " Install:");
316: if (!history->extract_packages(history, line, eid, SW_OP_INSTALL))
317: {
318: goto end;
319: }
320: }
321: else if (match("Upgrade", &cmd))
322: {
323: DBG1(DBG_IMC, " Upgrade:");
324: if (!history->extract_packages(history, line, eid, SW_OP_UPGRADE))
325: {
326: goto end;
327: }
328: }
329: else if (match("Remove", &cmd))
330: {
331: DBG1(DBG_IMC, " Remove:");
332: if (!history->extract_packages(history, line, eid, SW_OP_REMOVE))
333: {
334: goto end;
335: }
336: }
337: else if (match("Purge", &cmd))
338: {
339: DBG1(DBG_IMC, " Purge:");
340: if (!history->extract_packages(history, line, eid, SW_OP_REMOVE))
341: {
342: goto end;
343: }
344: }
345: else if (match("End-Date", &cmd))
346: {
347: /* Process 'max_count' events at a time */
348: if (max_count > 0 && eid - last_eid == max_count)
349: {
350: fprintf(stderr, "added %d events\n", max_count);
351: goto end;
352: }
353: }
354: }
355:
356: if (history->merge_installed_packages(history))
357: {
358: status = EXIT_SUCCESS;
359: }
360:
361: end:
362: free(last_time);
363: history->destroy(history);
364: chunk_unmap(h);
365:
366: return status;
367: }
368:
369: /**
370: * List all endpoint software identifiers stored in local collector database
371: */
372: static int list_identifiers(sw_collector_db_t *db, sw_collector_db_query_t type)
373: {
374: enumerator_t *e;
375: char *name, *package, *version;
376: uint32_t sw_id, count = 0, installed_count = 0, removed_count, installed;
377:
378: e = db->create_sw_enumerator(db, type, NULL);
379: if (!e)
380: {
381: return EXIT_FAILURE;
382: }
383: while (e->enumerate(e, &sw_id, &name, &package, &version, &installed))
384: {
385: printf("%s,%s,%s,%d\n", name, package, version, installed);
386: if (installed)
387: {
388: installed_count++;
389: }
390: count++;
391: }
392: removed_count = count - installed_count;
393: e->destroy(e);
394:
395: switch (type)
396: {
397: case SW_QUERY_ALL:
398: DBG1(DBG_IMC, "retrieved %u software identities with %u installed "
399: "and %u removed", count, installed_count, removed_count);
400: break;
401: case SW_QUERY_INSTALLED:
402: DBG1(DBG_IMC, "retrieved %u installed software identities", count);
403: break;
404: case SW_QUERY_REMOVED:
405: DBG1(DBG_IMC, "retrieved %u removed software identities", count);
406: break;
407: }
408:
409: return EXIT_SUCCESS;
410: }
411:
412: static bool query_registry(sw_collector_rest_api_t *rest_api, bool installed)
413: {
414: sw_collector_db_query_t type;
415: enumerator_t *enumerator;
416: char *sw_id;
417: int count = 0;
418:
419: type = installed ? SW_QUERY_INSTALLED : SW_QUERY_REMOVED;
420: enumerator = rest_api->create_sw_enumerator(rest_api, type);
421: if (!enumerator)
422: {
423: return FALSE;
424: }
425: while (enumerator->enumerate(enumerator, &sw_id))
426: {
427: printf("%s,%s\n", sw_id, installed ? "1" : "0");
428: count++;
429: }
430: enumerator->destroy(enumerator);
431: DBG1(DBG_IMC, "%d %s software identifiers not registered", count,
432: installed ? "installed" : "removed");
433: return TRUE;
434: }
435:
436:
437: /**
438: * List all endpoint software identifiers stored in local collector database
439: * that are not registered yet in central collector database
440: */
441: static int unregistered_identifiers(sw_collector_db_t *db,
442: sw_collector_db_query_t type)
443: {
444: sw_collector_rest_api_t *rest_api;
445: int status = EXIT_SUCCESS;
446:
447: rest_api = sw_collector_rest_api_create(db);
448: if (!rest_api)
449: {
450: return EXIT_FAILURE;
451: }
452:
453: /* List installed software identifiers not registered centrally */
454: if (type != SW_QUERY_REMOVED && !query_registry(rest_api, TRUE))
455: {
456: status = EXIT_FAILURE;
457: }
458:
459: /* List removed software identifiers not registered centrally */
460: if (type != SW_QUERY_INSTALLED && !query_registry(rest_api, FALSE))
461: {
462: status = EXIT_FAILURE;
463: }
464: rest_api->destroy(rest_api);
465:
466: return status;
467: }
468:
469: /**
470: * Generate ISO 19770-2:2015 SWID tags for [installed|removed|all]
471: * SW identifiers that are not registered centrally
472: */
473: static int generate_tags(sw_collector_db_t *db, bool full_tags,
474: sw_collector_db_query_t type)
475: {
476: swid_gen_t * swid_gen;
477: sw_collector_rest_api_t *rest_api;
478: char *name, *package, *version, *tag;
479: enumerator_t *enumerator;
480: uint32_t sw_id;
481: bool installed;
482: int count = 0, installed_count = 0, status = EXIT_FAILURE;
483:
484: swid_gen = swid_gen_create();
485: rest_api = sw_collector_rest_api_create(db);
486: if (!rest_api)
487: {
488: goto end;
489: }
490:
491: enumerator = rest_api->create_sw_enumerator(rest_api, type);
492: if (!enumerator)
493: {
494: goto end;
495: }
496: while (enumerator->enumerate(enumerator, &name))
497: {
498: sw_id = db->get_sw_id(db, name, &package, &version, NULL, &installed);
499: if (sw_id)
500: {
501: tag = swid_gen->generate_tag(swid_gen, name, package, version,
502: full_tags && installed, FALSE);
503: if (tag)
504: {
505: DBG2(DBG_IMC, " creating %s", name);
506: printf("%s\n", tag);
507: free(tag);
508: count++;
509: if (installed)
510: {
511: installed_count++;
512: }
513: }
514: free(package);
515: free(version);
516: }
517: }
518: enumerator->destroy(enumerator);
519: status = EXIT_SUCCESS;
520:
521: switch (type)
522: {
523: case SW_QUERY_ALL:
524: DBG1(DBG_IMC, "created %d tags for unregistered software "
525: "identifiers with %d installed and %d removed", count,
526: installed_count, count - installed_count);
527: break;
528: case SW_QUERY_INSTALLED:
529: DBG1(DBG_IMC, "created %d tags for unregistered installed software "
530: "identifiers", count);
531: break;
532: case SW_QUERY_REMOVED:
533: DBG1(DBG_IMC, "created %d tags for unregistered removed software "
534: "identifiers", count);
535: break;
536: }
537:
538: end:
539: swid_gen->destroy(swid_gen);
540: DESTROY_IF(rest_api);
541:
542: return status;
543: }
544:
545: /**
546: * Remove architecture suffix from package entries in the database
547: */
548: static int migrate(sw_collector_db_t *db)
549: {
550: sw_collector_dpkg_t *dpkg;
551:
552: char *package, *arch, *version;
553: char package_filter[BUF_LEN];
554: int res, count = 0;
555: int status = EXIT_SUCCESS;
556: enumerator_t *enumerator;
557:
558: dpkg = sw_collector_dpkg_create();
559: if (!dpkg)
560: {
561: return FAILED;
562: }
563:
564: enumerator = dpkg->create_sw_enumerator(dpkg);
565: while (enumerator->enumerate(enumerator, &package, &arch, &version))
566: {
567:
568: /* Look for package names with architecture suffix */
569: snprintf(package_filter, BUF_LEN, "%s:%%", package);
570:
571: res = db->update_package(db, package_filter, package);
572: if (res < 0)
573: {
574: status = EXIT_FAILURE;
575: break;
576: }
577: else if (res > 0)
578: {
579: count += res;
580: DBG2(DBG_IMC, "%s: removed arch suffix %d times", package, res);
581: }
582: }
583: enumerator->destroy(enumerator);
584: dpkg->destroy(dpkg);
585:
586: DBG1(DBG_IMC, "migrated %d sw identifier records", count);
587:
588: return status;
589: }
590:
591: /**
592: * Free hashtable entry
593: */
594: static void free_entry(void *value, void *key)
595: {
596: free(value);
597: free(key);
598: }
599:
600: /**
601: * Check consistency of installed software identifiers in collector database
602: */
603: static int check(sw_collector_db_t *db)
604: {
605: sw_collector_dpkg_t *dpkg;
606: swid_gen_info_t *info;
607: hashtable_t *table;
608: enumerator_t *e;
609: char *dpkg_name, *name, *package, *arch, *version;
610: uint32_t sw_id, count = 0, installed;
611:
612: dpkg = sw_collector_dpkg_create();
613: if (!dpkg)
614: {
615: return EXIT_FAILURE;
616: }
617: info = swid_gen_info_create();
618: table = hashtable_create(hashtable_hash_str, hashtable_equals_str, 4096);
619:
620: /* Store all installed sw identifiers (according to dpkg) in hashtable */
621: e = dpkg->create_sw_enumerator(dpkg);
622: while (e->enumerate(e, &package, &arch, &version))
623: {
624: dpkg_name = info->create_sw_id(info, package, version);
625: table->put(table, strdup(package), dpkg_name);
626: }
627: e->destroy(e);
628:
629: info->destroy(info);
630: dpkg->destroy(dpkg);
631:
632: e = db->create_sw_enumerator(db, SW_QUERY_ALL, NULL);
633: if (!e)
634: {
635: table->destroy_function(table, (void*)free_entry);
636: return EXIT_FAILURE;
637: }
638: while (e->enumerate(e, &sw_id, &name, &package, &version, &installed))
639: {
640: dpkg_name = table->get(table, package);
641: if (installed)
642: {
643: if (!dpkg_name)
644: {
645: printf("%4d %s erroneously noted as installed\n", sw_id, name);
646: }
647: else if (!streq(name, dpkg_name))
648: {
649: printf("%4d %s erroneously noted as installed instead of\n "
650: " %s\n", sw_id, name, dpkg_name);
651: }
652: }
653: else
654: {
655: if (dpkg_name && streq(name, dpkg_name))
656: {
657: printf("%4d %s erroneously noted as removed\n", sw_id, name);
658: }
659: }
660: count++;
661: }
662: e->destroy(e);
663:
664: table->destroy_function(table, (void*)free_entry);
665: printf("checked %d software identifiers\n", count);
666:
667: return EXIT_SUCCESS;
668: }
669:
670: int main(int argc, char *argv[])
671: {
672: sw_collector_db_t *db = NULL;
673: sw_collector_db_query_t query_type;
674: collector_op_t op;
675: bool full_tags;
676: char *uri;
677: int status = EXIT_FAILURE;
678:
679: op = do_args(argc, argv, &full_tags, &query_type);
680:
681: /* enable sw_collector debugging hook */
682: dbg = sw_collector_dbg;
683: #ifdef HAVE_SYSLOG
684: openlog("sw-collector", 0, LOG_DEBUG);
685: #endif
686:
687: atexit(cleanup);
688:
689: /* initialize library */
690: if (!library_init(NULL, "sw-collector"))
691: {
692: exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
693: }
694:
695: /* load sw-collector plugins */
696: if (!lib->plugins->load(lib->plugins,
697: lib->settings->get_str(lib->settings, "%s.load", PLUGINS, lib->ns)))
698: {
699: exit(SS_RC_INITIALIZATION_FAILED);
700: }
701:
702: /* connect to sw-collector database */
703: uri = lib->settings->get_str(lib->settings, "%s.database", NULL, lib->ns);
704: if (!uri)
705: {
706: fprintf(stderr, "sw-collector.database URI not set.\n");
707: exit(EXIT_FAILURE);
708: }
709: db = sw_collector_db_create(uri);
710: if (!db)
711: {
712: fprintf(stderr, "connection to sw-collector database failed.\n");
713: exit(EXIT_FAILURE);
714: }
715:
716: switch (op)
717: {
718: case COLLECTOR_OP_EXTRACT:
719: status = extract_history(db);
720: break;
721: case COLLECTOR_OP_LIST:
722: status = list_identifiers(db, query_type);
723: break;
724: case COLLECTOR_OP_UNREGISTERED:
725: status = unregistered_identifiers(db, query_type);
726: break;
727: case COLLECTOR_OP_GENERATE:
728: status = generate_tags(db, full_tags, query_type);
729: break;
730: case COLLECTOR_OP_MIGRATE:
731: status = migrate(db);
732: break;
733: case COLLECTOR_OP_CHECK:
734: status = check(db);
735: break;
736: }
737: db->destroy(db);
738:
739: exit(status);
740: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>