Annotation of embedaddon/strongswan/src/sec-updater/sec-updater.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2012-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 <getopt.h>
18: #include <unistd.h>
19: #include <stdio.h>
20: #include <string.h>
21: #include <errno.h>
22: #include <syslog.h>
23: #include <time.h>
24: #include <sys/stat.h>
25: #include <stdlib.h>
26:
27: #include <library.h>
28: #include <utils/debug.h>
29:
30: #define EXIT_NO_UPDATES 80
31: #define TMP_DEB_FILE "/tmp/sec-updater.deb"
32: #define TMP_TAG_FILE "/tmp/sec-updater.tag"
33: #define SWID_GEN_CMD "/usr/local/bin/swid_generator"
34: #define TNC_MANAGE_CMD "/var/www/tnc/manage.py"
35:
36: typedef enum sec_update_state_t sec_update_state_t;
37:
38: enum sec_update_state_t {
39: SEC_UPDATE_STATE_BEGIN_PACKAGE,
40: SEC_UPDATE_STATE_VERSION,
41: SEC_UPDATE_STATE_FILENAME,
42: SEC_UPDATE_STATE_END_PACKAGE
43: };
44:
45: typedef struct stats_t stats_t;
46:
47: struct stats_t {
48: time_t release;
49: int product;
50: int packages;
51: int new_versions;
52: int updated_versions;
53: };
54:
55: /**
56: * global debug output variables
57: */
58: static int debug_level = 1;
59: static bool stderr_quiet = FALSE;
60:
61: /**
62: * sec_updater dbg function
63: */
64: static void sec_updater_dbg(debug_t group, level_t level, char *fmt, ...)
65: {
66: int priority = LOG_INFO;
67: char buffer[8192];
68: char *current = buffer, *next;
69: va_list args;
70:
71: if (level <= debug_level)
72: {
73: if (!stderr_quiet)
74: {
75: va_start(args, fmt);
76: vfprintf(stderr, fmt, args);
77: fprintf(stderr, "\n");
78: va_end(args);
79: }
80:
81: /* write in memory buffer first */
82: va_start(args, fmt);
83: vsnprintf(buffer, sizeof(buffer), fmt, args);
84: va_end(args);
85:
86: /* do a syslog with every line */
87: while (current)
88: {
89: next = strchr(current, '\n');
90: if (next)
91: {
92: *(next++) = '\0';
93: }
94: syslog(priority, "%s\n", current);
95: current = next;
96: }
97: }
98: }
99:
100: /**
101: * atexit handler to close everything on shutdown
102: */
103: static void cleanup(void)
104: {
105: closelog();
106: library_deinit();
107: }
108:
109: static void usage(void)
110: {
111: printf("\
112: Usage:\n\
113: sec-updater --help\n\
114: sec-updater [--debug <level>] [--quiet] [--security] --os <string>\n\
115: --arch <string> --uri <uri> --file <filename>\n\n\
116: Options:\n\
117: --help print usage information\n\
118: --debug <level> set debug level\n\
119: --quiet suppress debug output to stderr\n\
120: --os <string> operating system\n\
121: --arch <string> hw architecture\n\
122: --security set when parsing a file with security updates\n\
123: --file <filename> package information file to parse\n\
124: --uri <uri> uri where to download deb package from\n");
125: }
126:
127: /**
128: * Update the package database
129: */
130: static bool update_database(database_t *db, char *package, char *version,
131: bool security, stats_t *stats, bool *new)
132: {
133: int pid = 0, vid = 0, sec_flag;
134: bool first = TRUE, found = FALSE;
135: char *release;
136: enumerator_t *e;
137:
138: /* increment package count */
139: stats->packages++;
140:
141: /* set new output variable */
142: *new = FALSE;
143:
144: /* check if package is already in database */
145: e = db->query(db, "SELECT id FROM packages WHERE name = ?",
146: DB_TEXT, package, DB_INT);
147: if (!e)
148: {
149: return FALSE;
150: }
151: if (!e->enumerate(e, &pid))
152: {
153: pid = 0;
154: }
155: e->destroy(e);
156:
157: if (!pid)
158: {
159: return TRUE;
160: }
161:
162: /* retrieve all package versions stored in database */
163: e = db->query(db,
164: "SELECT id, release, security FROM versions "
165: "WHERE product = ? AND package = ?",
166: DB_INT, stats->product, DB_INT, pid, DB_INT, DB_TEXT, DB_INT);
167: if (!e)
168: {
169: return FALSE;
170: }
171:
172: while (e->enumerate(e, &vid, &release, &sec_flag))
173: {
174: char command[BUF_LEN];
175: char found_char = ' ';
176: bool update_version = FALSE;
177:
178: if (streq(version, release))
179: {
180: found = TRUE;
181: found_char = '*';
182: }
183: else if (security)
184: {
185: snprintf(command, BUF_LEN, "dpkg --compare-versions %s lt %s",
186: release, version);
187: if (system(command) == 0)
188: {
189: found_char = '!';
190: if (!sec_flag)
191: {
192: if (db->execute(db, NULL, "UPDATE versions "
193: "SET security = 1 WHERE id = ?", DB_INT, vid) != 1)
194: {
195: DBG1(DBG_IMV, " could not update version");
196: e->destroy(e);
197: return FALSE;
198: }
199: update_version = TRUE;
200: stats->updated_versions++;
201: }
202: }
203: }
204: if (debug_level < 2 && !update_version)
205: {
206: continue;
207: }
208: if (first)
209: {
210: DBG1(DBG_IMV, "%s", package);
211: first = FALSE;
212: }
213: DBG1(DBG_IMV, " %c%s %s", found_char , sec_flag ? "s" : " ", release);
214: }
215: e->destroy(e);
216:
217: if (!found)
218: {
219: if (first)
220: {
221: DBG1(DBG_IMV, "%s", package);
222: }
223: DBG1(DBG_IMV, " + %s", version);
224:
225: if (db->execute(db, &vid,
226: "INSERT INTO versions "
227: "(package, product, release, security, time) "
228: "VALUES (?, ?, ?, 0, ?)", DB_INT, pid, DB_INT, stats->product,
229: DB_TEXT, version, DB_INT, stats->release) != 1)
230: {
231: DBG1(DBG_IMV, " could not store version to database");
232: return FALSE;
233: }
234: stats->new_versions++;
235: *new = TRUE;
236: }
237:
238: return TRUE;
239: }
240:
241: /**
242: * Process a package file and store updates in the database
243: */
244: static int process_packages(char *path, char *os, char *arch, char *uri,
245: bool security)
246: {
247: char line[BUF_LEN], product[BUF_LEN], command[BUF_LEN];
248: char *db_uri, *download_uri = NULL, *swid_regid, *swid_entity;
249: char *pos, *package = NULL, *version = NULL, *filename = NULL;
250: char *swid_gen_cmd, *tnc_manage_cmd, *tmp_deb_file, *tmp_tag_file;
251: sec_update_state_t state;
252: enumerator_t *e;
253: database_t *db;
254: int len, pid;
255: chunk_t deb = chunk_empty;
256: FILE *file;
257: stats_t stats;
258: bool success = TRUE, new;
259:
260: /* initialize statistics */
261: memset(&stats, 0x00, sizeof(stats_t));
262:
263: /* Set release date to current time */
264: stats.release = time(NULL);
265:
266: /* opening package file */
267: file = fopen(path, "r");
268: if (!file)
269: {
270: DBG1(DBG_IMV, " could not open \"%s\"", path);
271: exit(EXIT_FAILURE);
272: }
273:
274: /* connect package database */
275: db_uri = lib->settings->get_str(lib->settings, "sec-updater.database", NULL);
276: if (!db_uri)
277: {
278: DBG1(DBG_IMV, "database URI sec-updater.database not set");
279: fclose(file);
280: exit(EXIT_FAILURE);
281: }
282: db = lib->db->create(lib->db, db_uri);
283: if (!db)
284: {
285: DBG1(DBG_IMV, "could not connect to database '%s'", db_uri);
286: fclose(file);
287: exit(EXIT_FAILURE);
288: }
289:
290: /* form product name by concatenating os and arch strings */
291: snprintf(product, BUF_LEN, "%s %s", os, arch);
292:
293: /* check if product is already in database */
294: e = db->query(db, "SELECT id FROM products WHERE name = ?",
295: DB_TEXT, product, DB_INT);
296: if (e)
297: {
298: if (e->enumerate(e, &pid))
299: {
300: stats.product = pid;
301: }
302: e->destroy(e);
303: }
304: if (!stats.product)
305: {
306: if (db->execute(db, &pid, "INSERT INTO products (name) VALUES (?)",
307: DB_TEXT, product) != 1)
308: {
309: DBG1(DBG_IMV, "could not store product '%s' to database",
310: product);
311: fclose(file);
312: db->destroy(db);
313: exit(EXIT_FAILURE);
314: }
315: stats.product = pid;
316: }
317:
318: /* get settings for the loop */
319: swid_regid = lib->settings->get_str(lib->settings,
320: "sec-updater.swid_gen.tag_creator.regid",
321: "strongswan.org");
322: swid_entity = lib->settings->get_str(lib->settings,
323: "sec-updater.swid_gen.tag_creator.name",
324: "strongSwan Project");
325: swid_gen_cmd = lib->settings->get_str(lib->settings,
326: "sec-updater.swid_gen.command", SWID_GEN_CMD);
327: tnc_manage_cmd = lib->settings->get_str(lib->settings,
328: "sec-updater.tnc_manage_command", TNC_MANAGE_CMD);
329: tmp_deb_file = lib->settings->get_str(lib->settings,
330: "sec-updater.tmp.deb_file", TMP_DEB_FILE);
331: tmp_tag_file = lib->settings->get_str(lib->settings,
332: "sec-updater.tmp.tag_file", TMP_TAG_FILE);
333:
334: state = SEC_UPDATE_STATE_BEGIN_PACKAGE;
335:
336: while (fgets(line, sizeof(line), file))
337: {
338: /* set read pointer to beginning of line */
339: pos = line;
340:
341: switch (state)
342: {
343: case SEC_UPDATE_STATE_BEGIN_PACKAGE:
344: pos = strstr(pos, "Package: ");
345: if (!pos)
346: {
347: continue;
348: }
349: pos += 9;
350: package = pos;
351: pos = strchr(pos, '\n');
352: if (pos)
353: {
354: package = strndup(package, pos - package);
355: state = SEC_UPDATE_STATE_VERSION;
356: }
357: break;
358: case SEC_UPDATE_STATE_VERSION:
359: pos = strstr(pos, "Version: ");
360: if (!pos)
361: {
362: continue;
363: }
364: pos += 9;
365: version = pos;
366: pos = strchr(pos, '\n');
367: if (pos)
368: {
369: version = strndup(version, pos - version);
370: success = update_database(db, package, version, security,
371: &stats, &new);
372: state = (success && new) ? SEC_UPDATE_STATE_FILENAME :
373: SEC_UPDATE_STATE_END_PACKAGE;
374: }
375: break;
376: case SEC_UPDATE_STATE_FILENAME:
377: pos = strstr(pos, "Filename: ");
378: if (!pos)
379: {
380: continue;
381: }
382: state = SEC_UPDATE_STATE_END_PACKAGE;
383:
384: pos += 10;
385: filename = pos;
386: pos = strchr(pos, '\n');
387: if (!pos)
388: {
389: break;
390: }
391: len = pos - filename;
392: if (asprintf(&download_uri, "%s/%.*s", uri, len, filename) == -1)
393: {
394: break;
395: }
396:
397: /* retrieve deb package file from linux repository */
398: if (lib->fetcher->fetch(lib->fetcher, download_uri,
399: &deb, FETCH_END) != SUCCESS)
400: {
401: DBG1(DBG_IMV, " %s failed", download_uri);
402: break;
403: }
404: DBG1(DBG_IMV, " %s (%u bytes)", download_uri, deb.len);
405:
406: /* store deb package file to temporary location */
407: if (!chunk_write(deb, tmp_deb_file, 0022, TRUE))
408: {
409: DBG1(DBG_IMV, " save to '%s' failed", tmp_deb_file);
410: break;
411: }
412:
413: /* generate SWID tag for downloaded deb package */
414: snprintf(command, BUF_LEN, "%s swid --full --package-file %s "
415: "--regid %s --entity-name '%s' --os '%s' --arch '%s' "
416: ">> %s", swid_gen_cmd, tmp_deb_file, swid_regid,
417: swid_entity, os, arch, tmp_tag_file);
418: if (system(command) != 0)
419: {
420: DBG1(DBG_IMV, " tag generation failed");
421: break;
422: }
423: break;
424: case SEC_UPDATE_STATE_END_PACKAGE:
425: if (*pos != '\n')
426: {
427: continue;
428: }
429: free(package);
430: free(version);
431: free(download_uri);
432: chunk_free(&deb);
433: package = version = download_uri = NULL;
434:
435: if (!success)
436: {
437: fclose(file);
438: db->destroy(db);
439: exit(EXIT_FAILURE);
440: }
441: state = SEC_UPDATE_STATE_BEGIN_PACKAGE;
442: }
443: }
444:
445: free(package);
446: free(version);
447: free(download_uri);
448: fclose(file);
449: db->destroy(db);
450:
451: /* import swid tags into strongTNC */
452: if (stats.new_versions > 0)
453: {
454: snprintf(command, BUF_LEN, "%s importswid %s",
455: tnc_manage_cmd, tmp_tag_file);
456: if (system(command) != 0)
457: {
458: DBG1(DBG_IMV, "tag import failed");
459: }
460: snprintf(command, BUF_LEN, "rm %s %s",
461: tmp_deb_file, tmp_tag_file);
462: if (system(command) != 0)
463: {
464: DBG1(DBG_IMV, "removing temporary files failed");
465: }
466: }
467:
468: DBG1(DBG_IMV, "processed \"%s\": %d packages, %d new versions, "
469: "%d updated versions", path, stats.packages,
470: stats.new_versions, stats.updated_versions);
471:
472: return (stats.new_versions + stats.updated_versions) ?
473: EXIT_SUCCESS : EXIT_NO_UPDATES;
474: }
475:
476: static int do_args(int argc, char *argv[])
477: {
478: char *filename = NULL, *arch = NULL, *os = NULL, *uri = NULL;
479: bool security = FALSE;
480:
481: /* reinit getopt state */
482: optind = 0;
483:
484: while (TRUE)
485: {
486: int c;
487:
488: struct option long_opts[] = {
489: { "help", no_argument, NULL, 'h' },
490: { "arch", required_argument, NULL, 'a' },
491: { "debug", required_argument, NULL, 'd' },
492: { "file", required_argument, NULL, 'f' },
493: { "os", required_argument, NULL, 'o' },
494: { "quiet", no_argument, NULL, 'q' },
495: { "security", no_argument, NULL, 's' },
496: { "uri", required_argument, NULL, 'u' },
497: { 0,0,0,0 }
498: };
499:
500: c = getopt_long(argc, argv, "ha:d:f:o:qsu:", long_opts, NULL);
501: switch (c)
502: {
503: case EOF:
504: break;
505: case 'h':
506: usage();
507: exit(EXIT_SUCCESS);
508: case 'a':
509: arch = optarg;
510: continue;
511: case 'd':
512: debug_level = atoi(optarg);
513: continue;
514: case 'f':
515: filename = optarg;
516: continue;
517: case 'o':
518: os = optarg;
519: continue;
520: case 'q':
521: stderr_quiet = TRUE;
522: continue;
523: case 's':
524: security = TRUE;
525: continue;
526: case 'u':
527: uri = optarg;
528: continue;
529: }
530: break;
531: }
532:
533: if (filename && os && arch && uri)
534: {
535: return process_packages(filename, os, arch, uri, security);
536: }
537: else
538: {
539: usage();
540: exit(EXIT_FAILURE);
541: }
542: }
543:
544: int main(int argc, char *argv[])
545: {
546: /* enable attest debugging hook */
547: dbg = sec_updater_dbg;
548: openlog("sec-updater", 0, LOG_DEBUG);
549:
550: atexit(cleanup);
551:
552: /* initialize library */
553: if (!library_init(NULL, "sec-updater"))
554: {
555: exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
556: }
557: if (!lib->plugins->load(lib->plugins,
558: lib->settings->get_str(lib->settings, "sec-updater.load",
559: "sqlite curl")))
560: {
561: exit(SS_RC_INITIALIZATION_FAILED);
562: }
563: exit(do_args(argc, argv));
564: }
565:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>