get measurement hashes for a given file
[strongswan.git] / src / libimcv / plugins / imv_attestation / attest.c
1 /*
2 * Copyright (C) 2011 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
23 #include <debug.h>
24 #include <library.h>
25
26 #include <pts/pts_meas_algo.h>
27
28 #include "attest_usage.h"
29
30 /**
31 * global database handle
32 */
33 database_t *db;
34
35 /**
36 * forward declarations
37 */
38 static void do_args(int argc, char *argv[]);
39
40 /**
41 * ipsec attest --files - show files
42 */
43 static void list_files(char *product, int pid)
44 {
45 enumerator_t *e;
46 char *file;
47 bool select = TRUE;
48 int fid, is_dir, count = 0;
49
50 if (pid)
51 {
52 e = db->query(db,
53 "SELECT f.id, f.type, f.path FROM files AS f "
54 "JOIN product_file AS pf ON f.id = pf.file "
55 "JOIN products AS p ON p.id = pf.product "
56 "WHERE p.id = ? ORDER BY f.path",
57 DB_INT, pid, DB_INT, DB_INT, DB_TEXT);
58 }
59 else if (!product || *product == '\0')
60 {
61 select = FALSE;
62 e = db->query(db,
63 "SELECT id, type, path FROM files "
64 "ORDER BY path",
65 DB_INT, DB_INT, DB_TEXT);
66 }
67 else
68 {
69 e = db->query(db,
70 "SELECT f.id, f.type, f.path FROM files AS f "
71 "JOIN product_file AS pf ON f.id = pf.file "
72 "JOIN products AS p ON p.id = pf.product "
73 "WHERE p.name = ? ORDER BY f.path",
74 DB_TEXT, product, DB_INT, DB_INT, DB_TEXT);
75 }
76 if (e)
77 {
78 while (e->enumerate(e, &fid, &is_dir, &file))
79 {
80 printf("%3d: %s %s\n", fid, is_dir ? "d":" ", file);
81 count++;
82 }
83 e->destroy(e);
84
85 printf("%d file%s found", count, (count == 1) ? "" : "s");
86 if (select)
87 {
88 printf(" for product '%s'", product);
89 }
90 printf("\n");
91 }
92 }
93
94 /**
95 * ipsec attest --products - show products
96 */
97 static void list_products(char *file, int fid)
98 {
99 enumerator_t *e;
100 char *product;
101 bool select = TRUE;
102 int pid, count = 0;
103
104 if (fid)
105 {
106 e = db->query(db,
107 "SELECT p.id, p.name FROM products AS p "
108 "JOIN product_file AS pf ON p.id = pf.product "
109 "JOIN files AS f ON f.id = pf.file "
110 "WHERE f.id = ? ORDER BY p.name",
111 DB_INT, fid, DB_INT, DB_TEXT);
112 }
113 else if (!file || *file == '\0')
114 {
115 select = FALSE;
116 e = db->query(db, "SELECT id, name FROM products "
117 "ORDER BY name",
118 DB_INT, DB_TEXT);
119 }
120 else
121 {
122 e = db->query(db,
123 "SELECT p.id, p.name FROM products AS p "
124 "JOIN product_file AS pf ON p.id = pf.product "
125 "JOIN files AS f ON f.id = pf.file "
126 "WHERE f.path = ? ORDER BY p.name",
127 DB_TEXT, file, DB_INT, DB_TEXT);
128 }
129 if (e)
130 {
131 while (e->enumerate(e, &pid, &product))
132 {
133 printf("%3d: %s\n", pid, product);
134 count++;
135 }
136 e->destroy(e);
137
138 printf("%d product%s found", count, (count == 1) ? "" : "s");
139 if (select)
140 {
141 printf(" for file '%s'", file);
142 }
143 printf("\n");
144 }
145 }
146
147 /**
148 * get the directory if there is one from the files tables
149 */
150 static void get_directory(int did, char **directory)
151 {
152 enumerator_t *e;
153 char *dir;
154
155 free(*directory);
156 *directory = strdup("");
157
158 if (did)
159 {
160 e = db->query(db, "SELECT path from files WHERE id = ?",
161 DB_INT, did, DB_TEXT);
162 if (e)
163 {
164 if (e->enumerate(e, &dir))
165 {
166 free(*directory);
167 *directory = strdup(dir);
168 }
169 e->destroy(e);
170 }
171 }
172 }
173
174 static bool slash(char *directory, char *file)
175 {
176 return *file != '/' && directory[max(0, strlen(directory)-1)] != '/';
177 }
178
179 /**
180 * ipsec attest --hashes - show all file measurement hashes
181 */
182 static void list_hashes(pts_meas_algorithms_t algo)
183 {
184 enumerator_t *e;
185 chunk_t hash;
186 char *file, *dir, *product;
187 int fid, fid_old = 0, did, did_old = 0, count = 0;
188
189 dir = strdup("");
190
191 e = db->query(db,
192 "SELECT f.id, f.path, p.name, fh.hash, fh.directory "
193 "FROM files AS f, products AS p, file_hashes AS fh "
194 "WHERE fh.algo = ? AND f.id = fh.file AND p.id = fh.product "
195 "ORDER BY fh.directory, f.path, p.name",
196 DB_INT, algo, DB_INT, DB_TEXT, DB_TEXT, DB_BLOB, DB_INT);
197 if (e)
198 {
199 while (e->enumerate(e, &fid, &file, &product, &hash, &did))
200 {
201 if (fid != fid_old || did != did_old)
202 {
203 if (did != did_old)
204 {
205 get_directory(did, &dir);
206 }
207 printf("%3d: %s%s%s\n", fid,
208 dir, slash(dir, file) ? "/" : "", file);
209 fid_old = fid;
210 did_old = did;
211 }
212 printf(" %#B '%s'\n", &hash, product);
213 count++;
214 }
215 e->destroy(e);
216
217 printf("%d %N value%s found\n", count, hash_algorithm_names,
218 pts_meas_algo_to_hash(algo), (count == 1) ? "" : "s");
219 free(dir);
220 }
221 }
222
223 /**
224 * ipsec attest --hashes - show file measurement hashes for a given file
225 */
226 static void list_hashes_for_file(pts_meas_algorithms_t algo, char *file, int fid)
227 {
228 enumerator_t *e;
229 chunk_t hash;
230 char *product, *dir;
231 int did, count = 0;
232
233 dir = strdup("");
234
235 if (fid)
236 {
237 e = db->query(db,
238 "SELECT p.name, fh.hash, fh.directory "
239 "FROM products AS p, file_hashes AS fh "
240 "JOIN files AS f ON f.id = fh.file "
241 "WHERE fh.algo = ? AND f.id = ? AND p.id = fh.product "
242 "ORDER BY p.name",
243 DB_INT, algo, DB_INT, fid, DB_TEXT, DB_BLOB, DB_INT);
244 }
245 else
246 {
247 e = db->query(db,
248 "SELECT p.name, fh.hash, fh.directory "
249 "FROM products AS p, file_hashes AS fh "
250 "JOIN files AS f ON f.id = fh.file "
251 "WHERE fh.algo = ? AND f.path = ? AND p.id = fh.product "
252 "ORDER BY p.name",
253 DB_INT, algo, DB_TEXT, file, DB_TEXT, DB_BLOB, DB_INT);
254 }
255 if (e)
256 {
257 while (e->enumerate(e, &product, &hash, &did))
258 {
259 printf("%#B '%s'\n", &hash, product);
260 count++;
261 }
262 e->destroy(e);
263
264 get_directory(did, &dir);
265 printf("%d %N value%s found for file '%s%s%s'\n",
266 count, hash_algorithm_names, pts_meas_algo_to_hash(algo),
267 (count == 1) ? "" : "s",
268 dir, slash(dir, file) ? "/" : "", file);
269 free(dir);
270 }
271 }
272
273 /**
274 * ipsec attest --hashes - show file measurement hashes for a given product
275 */
276 static void list_hashes_for_product(pts_meas_algorithms_t algo,
277 char *product, int pid)
278 {
279 enumerator_t *e;
280 chunk_t hash;
281 char *file, *dir;
282 int fid, fid_old = 0, did, did_old = 0, count = 0;
283
284 dir = strdup("");
285
286 if (pid)
287 {
288 e = db->query(db,
289 "SELECT f.id, f. f.path, fh.hash, fh.directory "
290 "FROM files AS f, file_hashes AS fh "
291 "JOIN products AS p ON p.id = fh.product "
292 "WHERE fh.algo = ? AND p.id = ? AND f.id = fh.file "
293 "ORDER BY fh.directory, f.path",
294 DB_INT, algo, DB_INT, pid, DB_INT, DB_TEXT, DB_BLOB, DB_INT);
295 }
296 else
297 {
298 e = db->query(db,
299 "SELECT f.id, f.path, fh.hash, fh.directory "
300 "FROM files AS f, file_hashes AS fh "
301 "JOIN products AS p ON p.id = fh.product "
302 "WHERE fh.algo = ? AND p.name = ? AND f.id = fh.file "
303 "ORDER BY fh.directory, f.path",
304 DB_INT, algo, DB_TEXT, product, DB_INT, DB_TEXT, DB_BLOB, DB_INT);
305 }
306 if (e)
307 {
308 while (e->enumerate(e, &fid, &file, &hash, &did))
309 {
310 if (fid != fid_old || did != did_old)
311 {
312 if (did != did_old)
313 {
314 get_directory(did, &dir);
315 }
316 printf("%3d: %s%s%s\n", fid,
317 dir, slash(dir, file) ? "/" : "", file);
318 fid_old = fid;
319 did_old = did;
320 }
321 printf(" %#B\n", &hash);
322 count++;
323 }
324 e->destroy(e);
325
326 printf("%d %N value%s found for product '%s'\n",
327 count, hash_algorithm_names, pts_meas_algo_to_hash(algo),
328 (count == 1) ? "" : "s", product);
329 free(dir);
330 }
331 }
332
333 /**
334 * find file corresponding to primary key fid
335 */
336 static bool fid_to_file(int fid, char **file)
337 {
338 enumerator_t *e;
339 bool found = FALSE;
340 char *f;
341
342 e = db->query(db, "SELECT path FROM files WHERE id = ?",
343 DB_INT, fid, DB_TEXT);
344 if (e)
345 {
346 if (e->enumerate(e, &f))
347 {
348 found = TRUE;
349 *file = strdup(f);
350 }
351 else
352 {
353 printf("no file found with fid %d\n", fid);
354 }
355 e->destroy(e);
356 }
357 return found;
358 }
359
360 /**
361 * find product corresponding to primary key pid
362 */
363 static bool pid_to_product(int pid, char **product)
364 {
365 enumerator_t *e;
366 bool found = FALSE;
367 char *p;
368
369 e = db->query(db, "SELECT name FROM products WHERE id = ?",
370 DB_INT, pid, DB_TEXT);
371 if (e)
372 {
373 if (e->enumerate(e, &p))
374 {
375 found = TRUE;
376 *product = strdup(p);
377 }
378 else
379 {
380 printf("no product found with pid %d\n", pid);
381 }
382 e->destroy(e);
383 }
384 return found;
385 }
386
387 /**
388 * atexit handler to close db on shutdown
389 */
390 static void cleanup(void)
391 {
392 db->destroy(db);
393 }
394
395 static void do_args(int argc, char *argv[])
396 {
397 char *product = NULL, *file = NULL;
398 int fid = 0, pid = 0;
399 pts_meas_algorithms_t algo = PTS_MEAS_ALGO_SHA256;
400
401 enum {
402 OP_UNDEF,
403 OP_USAGE,
404 OP_FILES,
405 OP_PRODUCTS,
406 OP_HASHES,
407 } operation = OP_UNDEF;
408
409 /* reinit getopt state */
410 optind = 0;
411
412 while (TRUE)
413 {
414 int c;
415
416 struct option long_opts[] = {
417 { "help", no_argument, NULL, 'h' },
418 { "files", no_argument, NULL, 'f' },
419 { "products", no_argument, NULL, 'p' },
420 { "hashes", no_argument, NULL, 'H' },
421 { "file", required_argument, NULL, 'F' },
422 { "product", required_argument, NULL, 'P' },
423 { "sha1", no_argument, NULL, '1' },
424 { "sha256", no_argument, NULL, '2' },
425 { "sha384", no_argument, NULL, '3' },
426 { "fid", required_argument, NULL, '4' },
427 { "pid", required_argument, NULL, '5' },
428 { 0,0,0,0 }
429 };
430
431 c = getopt_long(argc, argv, "", long_opts, NULL);
432 switch (c)
433 {
434 case EOF:
435 break;
436 case 'h':
437 operation = OP_USAGE;
438 break;
439 case 'f':
440 operation = OP_FILES;
441 continue;
442 case 'p':
443 operation = OP_PRODUCTS;
444 continue;
445 case 'H':
446 operation = OP_HASHES;
447 continue;
448 case 'F':
449 file = optarg;
450 continue;
451 case 'P':
452 product = optarg;
453 continue;
454 case '1':
455 algo = PTS_MEAS_ALGO_SHA1;
456 continue;
457 case '2':
458 algo = PTS_MEAS_ALGO_SHA256;
459 continue;
460 case '3':
461 algo = PTS_MEAS_ALGO_SHA384;
462 continue;
463 case '4':
464 fid = atoi(optarg);
465 if (!fid_to_file(fid, &file))
466 {
467 exit(EXIT_FAILURE);
468 }
469 continue;
470 case '5':
471 pid = atoi(optarg);
472 if (!pid_to_product(pid, &product))
473 {
474 exit(EXIT_FAILURE);
475 }
476 continue;
477 }
478 break;
479 }
480
481 switch (operation)
482 {
483 case OP_USAGE:
484 usage();
485 break;
486 case OP_PRODUCTS:
487 list_products(file, fid);
488 break;
489 case OP_FILES:
490 list_files(product, pid);
491 break;
492 case OP_HASHES:
493 if ((!product || *product == '\0') && (!file || *file == '\0'))
494 {
495 list_hashes(algo);
496 }
497 else if (product)
498 {
499 list_hashes_for_product(algo, product, pid);
500 }
501 else
502 {
503 list_hashes_for_file(algo, file, fid);
504 }
505 break;
506 default:
507 usage();
508 exit(EXIT_FAILURE);
509 }
510
511 if (fid)
512 {
513 free(file);
514 }
515 if (pid)
516 {
517 free(product);
518 }
519
520 }
521
522 int main(int argc, char *argv[])
523 {
524 char *uri;
525
526 atexit(library_deinit);
527
528 /* initialize library */
529 if (!library_init(NULL))
530 {
531 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
532 }
533 if (!lib->plugins->load(lib->plugins, NULL,
534 lib->settings->get_str(lib->settings, "attest.load", "sqlite")))
535 {
536 exit(SS_RC_INITIALIZATION_FAILED);
537 }
538
539 uri = lib->settings->get_str(lib->settings, "attest.database", NULL);
540 if (!uri)
541 {
542 fprintf(stderr, "database URI attest.database not set.\n");
543 exit(SS_RC_INITIALIZATION_FAILED);
544 }
545 db = lib->db->create(lib->db, uri);
546 if (!db)
547 {
548 fprintf(stderr, "opening database failed.\n");
549 exit(SS_RC_INITIALIZATION_FAILED);
550 }
551 atexit(cleanup);
552
553 do_args(argc, argv);
554
555 exit(EXIT_SUCCESS);
556 }
557