imv-os: Properly free strings for invalid input in pacman
[strongswan.git] / src / libimcv / plugins / imv_os / pacman.c
1 /*
2 * Copyright (C) 2012 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
26 #include "imv_os_state.h"
27
28 #include <library.h>
29 #include <utils/debug.h>
30
31 typedef enum pacman_state_t pacman_state_t;
32
33 enum pacman_state_t {
34 PACMAN_STATE_BEGIN_PACKAGE,
35 PACMAN_STATE_VERSION,
36 PACMAN_STATE_END_PACKAGE
37 };
38
39 typedef struct stats_t stats_t;
40
41 struct stats_t {
42 time_t release;
43 int product;
44 int packages;
45 int new_packages;
46 int new_versions;
47 int updated_versions;
48 int deleted_versions;
49 };
50
51 /**
52 * global debug output variables
53 */
54 static int debug_level = 1;
55 static bool stderr_quiet = TRUE;
56
57 /**
58 * pacman dbg function
59 */
60 static void pacman_dbg(debug_t group, level_t level, char *fmt, ...)
61 {
62 int priority = LOG_INFO;
63 char buffer[8192];
64 char *current = buffer, *next;
65 va_list args;
66
67 if (level <= debug_level)
68 {
69 if (!stderr_quiet)
70 {
71 va_start(args, fmt);
72 vfprintf(stderr, fmt, args);
73 fprintf(stderr, "\n");
74 va_end(args);
75 }
76
77 /* write in memory buffer first */
78 va_start(args, fmt);
79 vsnprintf(buffer, sizeof(buffer), fmt, args);
80 va_end(args);
81
82 /* do a syslog with every line */
83 while (current)
84 {
85 next = strchr(current, '\n');
86 if (next)
87 {
88 *(next++) = '\0';
89 }
90 syslog(priority, "%s\n", current);
91 current = next;
92 }
93 }
94 }
95
96 /**
97 * atexit handler to close everything on shutdown
98 */
99 static void cleanup(void)
100 {
101 closelog();
102 library_deinit();
103 }
104
105 static void usage(void)
106 {
107 printf("Usage:\n"
108 "ipsec pacman --product <name> --file <filename> [--update]\n");
109 }
110
111 /**
112 * Update the package database
113 */
114 static bool update_database(database_t *db, char *package, char *version,
115 bool security, stats_t *stats)
116 {
117 char *cur_version, *version_update = NULL, *version_delete = NULL;
118 int cur_security, security_update = 0, security_delete = 0;
119 int pac_id = 0, vid = 0, vid_update = 0, vid_delete = 0;
120 u_int cur_time;
121 bool add_version = TRUE;
122 enumerator_t *e;
123
124 /* increment package count */
125 stats->packages++;
126
127 /* check if package is already in database */
128 e = db->query(db, "SELECT id FROM packages WHERE name = ?",
129 DB_TEXT, package, DB_INT);
130 if (!e)
131 {
132 return FALSE;
133 }
134 if (!e->enumerate(e, &pac_id))
135 {
136 pac_id = 0;
137 }
138 e->destroy(e);
139
140 if (!pac_id && security)
141 {
142 if (db->execute(db, &pac_id, "INSERT INTO packages (name) VALUES (?)",
143 DB_TEXT, package) != 1)
144 {
145 fprintf(stderr, "could not store package '%s' to database\n",
146 package);
147 return FALSE;
148 }
149 stats->new_packages++;
150 }
151
152 /* check for package versions already in database */
153 e = db->query(db,
154 "SELECT id, release, security, time FROM versions "
155 "WHERE package = ? AND product = ?", DB_INT, pac_id,
156 DB_INT, stats->product, DB_INT, DB_TEXT, DB_INT, DB_UINT);
157 if (!e)
158 {
159 return FALSE;
160 }
161
162 while (e->enumerate(e, &vid, &cur_version, &cur_security, &cur_time))
163 {
164 if (streq(version, cur_version))
165 {
166 /* already in data base */
167 add_version = FALSE;
168 break;
169 }
170 else if (stats->release >= cur_time)
171 {
172 if (security)
173 {
174 if (cur_security)
175 {
176 vid_update = vid;
177 version_update = strdup(cur_version);
178 security_update = cur_security;
179 }
180 else
181 {
182 vid_delete = vid;
183 version_delete = strdup(cur_version);
184 security_delete = cur_security;
185 }
186 }
187 else
188 {
189 if (!cur_security)
190 {
191 vid_update = vid;
192 version_update = strdup(cur_version);
193 security_update = cur_security;
194 }
195 }
196 }
197 else
198 {
199 if (security == cur_security)
200 {
201 add_version = FALSE;
202 }
203 }
204 }
205 e->destroy(e);
206
207 if ((!vid && !security) || (vid && !add_version))
208 {
209 free(version_update);
210 free(version_delete);
211 return TRUE;
212 }
213
214 if ((!vid && security) || (vid && !vid_update))
215 {
216 printf("%s (%s) %s\n", package, version, security ? "[s]" : "");
217
218 if (db->execute(db, &vid,
219 "INSERT INTO versions "
220 "(package, product, release, security, time) "
221 "VALUES (?, ?, ?, ?, ?)", DB_INT, pac_id, DB_INT, stats->product,
222 DB_TEXT, version, DB_INT, security, DB_INT, stats->release) != 1)
223 {
224 fprintf(stderr, "could not store version '%s' to database\n",
225 version);
226 free(version_update);
227 free(version_delete);
228 return FALSE;
229 }
230 stats->new_versions++;
231 }
232 else
233 {
234 printf("%s (%s) %s updated by\n",
235 package, version_update, security_update ? "[s]" : "");
236 printf("%s (%s) %s\n", package, version, security ? "[s]" : "");
237
238 if (db->execute(db, NULL,
239 "UPDATE versions SET release = ?, time = ? WHERE id = ?",
240 DB_TEXT, version, DB_INT, stats->release, DB_INT, vid_update) <= 0)
241 {
242 fprintf(stderr, "could not update version '%s' to database\n",
243 version);
244 free(version_update);
245 free(version_delete);
246 return FALSE;
247 }
248 stats->updated_versions++;
249 }
250
251 if (vid_delete)
252 {
253 printf("%s (%s) %s deleted\n",
254 package, version_delete, security_delete ? "[s]" : "");
255 if (db->execute(db, NULL,
256 "DELETE FROM versions WHERE id = ?",
257 DB_INT, vid_delete) <= 0)
258 {
259 fprintf(stderr, "could not delete version '%s' from database\n",
260 version_delete);
261 free(version_update);
262 free(version_delete);
263 return FALSE;
264 }
265 stats->deleted_versions++;
266 }
267 free(version_update);
268 free(version_delete);
269
270 return TRUE;
271 }
272
273 /**
274 * Process a package file and store updates in the database
275 */
276 static void process_packages(char *filename, char *product, bool security)
277 {
278 char *uri, line[BUF_LEN], *pos, *package = NULL, *version = NULL;
279 pacman_state_t pacman_state;
280 enumerator_t *e;
281 database_t *db;
282 int pid;
283 FILE *file;
284 stats_t stats;
285 bool success;
286
287 /* initialize statistics */
288 memset(&stats, 0x00, sizeof(stats_t));
289
290 /* Set release date to current time */
291 stats.release = time(NULL);
292
293 /* opening package file */
294 printf("loading\"%s\"\n", filename);
295 file = fopen(filename, "r");
296 if (!file)
297 {
298 fprintf(stderr, "could not open \"%s\"\n", filename);
299 exit(EXIT_FAILURE);
300 }
301
302 /* connect package database */
303 uri = lib->settings->get_str(lib->settings, "pacman.database", NULL);
304 if (!uri)
305 {
306 fprintf(stderr, "database URI pacman.database not set\n");
307 fclose(file);
308 exit(EXIT_FAILURE);
309 }
310 db = lib->db->create(lib->db, uri);
311 if (!db)
312 {
313 fprintf(stderr, "could not connect to database '%s'\n", uri);
314 fclose(file);
315 exit(EXIT_FAILURE);
316 }
317
318 /* check if product is already in database */
319 e = db->query(db, "SELECT id FROM products WHERE name = ?",
320 DB_TEXT, product, DB_INT);
321 if (e)
322 {
323 if (e->enumerate(e, &pid))
324 {
325 stats.product = pid;
326 }
327 e->destroy(e);
328 }
329 if (!stats.product)
330 {
331 if (db->execute(db, &pid, "INSERT INTO products (name) VALUES (?)",
332 DB_TEXT, product) != 1)
333 {
334 fprintf(stderr, "could not store product '%s' to database\n",
335 product);
336 fclose(file);
337 db->destroy(db);
338 exit(EXIT_FAILURE);
339 }
340 stats.product = pid;
341 }
342
343 pacman_state = PACMAN_STATE_BEGIN_PACKAGE;
344
345 while (fgets(line, sizeof(line), file))
346 {
347 /* set read pointer to beginning of line */
348 pos = line;
349
350 switch (pacman_state)
351 {
352 case PACMAN_STATE_BEGIN_PACKAGE:
353 pos = strstr(pos, "Package: ");
354 if (!pos)
355 {
356 continue;
357 }
358 pos += 9;
359 package = pos;
360 pos = strchr(pos, '\n');
361 if (pos)
362 {
363 package = strndup(package, pos - package);
364 pacman_state = PACMAN_STATE_VERSION;
365 }
366 break;
367 case PACMAN_STATE_VERSION:
368 pos = strstr(pos, "Version: ");
369 if (!pos)
370 {
371 continue;
372 }
373 pos += 9;
374 version = pos;
375 pos = strchr(pos, '\n');
376 if (pos)
377 {
378 version = strndup(version, pos - version);
379 pacman_state = PACMAN_STATE_END_PACKAGE;
380 }
381 break;
382 case PACMAN_STATE_END_PACKAGE:
383 if (*pos != '\n')
384 {
385 continue;
386 }
387 success = update_database(db, package, version, security, &stats);
388 free(package);
389 free(version);
390 if (!success)
391 {
392 fclose(file);
393 db->destroy(db);
394 exit(EXIT_FAILURE);
395 }
396 pacman_state = PACMAN_STATE_BEGIN_PACKAGE;
397 }
398 }
399 switch (pacman_state)
400 {
401 case PACMAN_STATE_END_PACKAGE:
402 free(version);
403 /* fall-through */
404 case PACMAN_STATE_VERSION:
405 free(package);
406 break;
407 default:
408 break;
409 }
410 fclose(file);
411 db->destroy(db);
412
413 printf("processed %d packages, %d new packages, %d new versions, "
414 "%d updated versions, %d deleted versions\n",
415 stats.packages, stats.new_packages, stats.new_versions,
416 stats.updated_versions, stats.deleted_versions);
417 }
418
419 static void do_args(int argc, char *argv[])
420 {
421 char *filename = NULL, *product = NULL;
422 bool security = FALSE;
423
424 /* reinit getopt state */
425 optind = 0;
426
427 while (TRUE)
428 {
429 int c;
430
431 struct option long_opts[] = {
432 { "help", no_argument, NULL, 'h' },
433 { "file", required_argument, NULL, 'f' },
434 { "product", required_argument, NULL, 'p' },
435 { "security", no_argument, NULL, 's' },
436 { 0,0,0,0 }
437 };
438
439 c = getopt_long(argc, argv, "", long_opts, NULL);
440 switch (c)
441 {
442 case EOF:
443 break;
444 case 'h':
445 usage();
446 exit(EXIT_SUCCESS);
447 case 'f':
448 filename = optarg;
449 continue;
450 case 'p':
451 product = optarg;
452 continue;
453 case 's':
454 security = TRUE;
455 continue;
456 }
457 break;
458 }
459
460 if (filename && product)
461 {
462 process_packages(filename, product, security);
463 }
464 else
465 {
466 usage();
467 exit(EXIT_FAILURE);
468 }
469 }
470
471 int main(int argc, char *argv[])
472 {
473 /* enable attest debugging hook */
474 dbg = pacman_dbg;
475 openlog("pacman", 0, LOG_DEBUG);
476
477 atexit(cleanup);
478
479 /* initialize library */
480 if (!library_init(NULL, "pacman"))
481 {
482 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
483 }
484 if (!lib->plugins->load(lib->plugins,
485 lib->settings->get_str(lib->settings, "pacman.load", "sqlite")))
486 {
487 exit(SS_RC_INITIALIZATION_FAILED);
488 }
489 do_args(argc, argv);
490
491 exit(EXIT_SUCCESS);
492 }
493