moved measurement and metadata flags to product_file table
[strongswan.git] / src / libimcv / plugins / imv_attestation / attest_db.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 #include "attest_db.h"
17
18 #include "libpts.h"
19 #include "pts/components/pts_comp_func_name.h"
20
21 typedef struct private_attest_db_t private_attest_db_t;
22
23 /**
24 * Private data of an attest_db_t object.
25 */
26 struct private_attest_db_t {
27
28 /**
29 * Public members of attest_db_state_t
30 */
31 attest_db_t public;
32
33 /**
34 * Software product to be queried
35 */
36 char *product;
37
38 /**
39 * Primary key of software product to be queried
40 */
41 int pid;
42
43 /**
44 * TRUE if product has been set
45 */
46 bool product_set;
47
48 /**
49 * Measurement file to be queried
50 */
51 char *file;
52
53 /**
54 * Primary key of measurement file to be queried
55 */
56 int fid;
57
58 /**
59 * TRUE if file has been set
60 */
61 bool file_set;
62
63 /**
64 * Directory containing the Measurement file to be queried
65 */
66 char *dir;
67
68 /**
69 * Primary key of the directory to be queried
70 */
71 int did;
72
73 /**
74 * TRUE if directory has been set
75 */
76 bool dir_set;
77
78 /**
79 * File measurement hash algorithm
80 */
81 pts_meas_algorithms_t algo;
82
83 /**
84 * Attestation database
85 */
86 database_t *db;
87
88 };
89
90 METHOD(attest_db_t, set_product, bool,
91 private_attest_db_t *this, char *product, bool create)
92 {
93 enumerator_t *e;
94
95 if (this->product_set)
96 {
97 printf("product has already been set\n");
98 return FALSE;
99 }
100 this->product = strdup(product);
101
102 e = this->db->query(this->db, "SELECT id FROM products WHERE name = ?",
103 DB_TEXT, product, DB_INT);
104 if (e)
105 {
106 if (e->enumerate(e, &this->pid))
107 {
108 this->product_set = TRUE;
109 }
110 e->destroy(e);
111 }
112 if (this->product_set)
113 {
114 return TRUE;
115 }
116
117 if (!create)
118 {
119 printf("product '%s' not found in database\n", product);
120 return FALSE;
121 }
122
123 /* Add a new database entry */
124 this->product_set = this->db->execute(this->db, &this->pid,
125 "INSERT INTO products (name) VALUES (?)",
126 DB_TEXT, product) == 1;
127
128 printf("product '%s' %sinserted into database\n", product,
129 this->product_set ? "" : "could not be ");
130
131 return this->product_set;
132 }
133
134 METHOD(attest_db_t, set_pid, bool,
135 private_attest_db_t *this, int pid)
136 {
137 enumerator_t *e;
138 char *product;
139
140 if (this->product_set)
141 {
142 printf("product has already been set\n");
143 return FALSE;
144 }
145 this->pid = pid;
146
147 e = this->db->query(this->db, "SELECT name FROM products WHERE id = ?",
148 DB_INT, pid, DB_TEXT);
149 if (e)
150 {
151 if (e->enumerate(e, &product))
152 {
153 this->product = strdup(product);
154 this->product_set = TRUE;
155 }
156 else
157 {
158 printf("no product found with pid %d in database\n", pid);
159 }
160 e->destroy(e);
161 }
162 return this->product_set;
163 }
164
165 METHOD(attest_db_t, set_file, bool,
166 private_attest_db_t *this, char *file, bool create)
167 {
168 enumerator_t *e;
169
170 if (this->file_set)
171 {
172 printf("file has already been set\n");
173 return FALSE;
174 }
175 this->file = strdup(file);
176
177 e = this->db->query(this->db, "SELECT id FROM files WHERE path = ?",
178 DB_TEXT, file, DB_INT);
179 if (e)
180 {
181 if (e->enumerate(e, &this->fid))
182 {
183 this->file_set = TRUE;
184 }
185 e->destroy(e);
186 }
187 if (this->file_set)
188 {
189 return TRUE;
190 }
191
192 if (!create)
193 {
194 printf("file '%s' not found in database\n", file);
195 return FALSE;
196 }
197
198 /* Add a new database entry */
199 this->file_set = this->db->execute(this->db, &this->fid,
200 "INSERT INTO files (type, path) VALUES (0, ?)",
201 DB_TEXT, file) == 1;
202
203 printf("file '%s' %sinserted into database\n", file,
204 this->file_set ? "" : "could not be ");
205
206 return this->file_set;
207 }
208
209 METHOD(attest_db_t, set_fid, bool,
210 private_attest_db_t *this, int fid)
211 {
212 enumerator_t *e;
213 char *file;
214
215 if (this->file_set)
216 {
217 printf("file has already been set\n");
218 return FALSE;
219 }
220 this->fid = fid;
221
222 e = this->db->query(this->db, "SELECT path FROM files WHERE id = ?",
223 DB_INT, fid, DB_TEXT);
224 if (e)
225 {
226 if (e->enumerate(e, &file))
227 {
228 this->file = strdup(file);
229 this->file_set = TRUE;
230 }
231 else
232 {
233 printf("no file found with fid %d\n", fid);
234 }
235 e->destroy(e);
236 }
237 return this->file_set;
238 }
239
240 METHOD(attest_db_t, set_directory, bool,
241 private_attest_db_t *this, char *dir, bool create)
242 {
243 enumerator_t *e;
244
245 if (this->dir_set)
246 {
247 printf("directory has already been set\n");
248 return FALSE;
249 }
250 free(this->dir);
251 this->dir = strdup(dir);
252
253 e = this->db->query(this->db,
254 "SELECT id FROM files WHERE type = 1 AND path = ?",
255 DB_TEXT, dir, DB_INT);
256 if (e)
257 {
258 if (e->enumerate(e, &this->did))
259 {
260 this->dir_set = TRUE;
261 }
262 e->destroy(e);
263 }
264 if (this->dir_set)
265 {
266 return TRUE;
267 }
268
269 if (!create)
270 {
271 printf("directory '%s' not found in database\n", dir);
272 return FALSE;
273 }
274
275 /* Add a new database entry */
276 this->dir_set = this->db->execute(this->db, &this->did,
277 "INSERT INTO files (type, path) VALUES (1, ?)",
278 DB_TEXT, dir) == 1;
279
280 printf("directory '%s' %sinserted into database\n", dir,
281 this->dir_set ? "" : "could not be ");
282
283 return this->dir_set;
284 }
285
286 METHOD(attest_db_t, set_did, bool,
287 private_attest_db_t *this, int did)
288 {
289 enumerator_t *e;
290 char *dir;
291
292 if (this->dir_set)
293 {
294 printf("directory has already been set\n");
295 return FALSE;
296 }
297 this->did = did;
298
299 e = this->db->query(this->db, "SELECT path FROM files WHERE id = ?",
300 DB_INT, did, DB_TEXT);
301 if (e)
302 {
303 if (e->enumerate(e, &dir))
304 {
305 free(this->dir);
306 this->dir = strdup(dir);
307 this->dir_set = TRUE;
308 }
309 else
310 {
311 printf("no directory found with did %d\n", did);
312 }
313 e->destroy(e);
314 }
315 return this->dir_set;
316 }
317
318 METHOD(attest_db_t, set_algo, void,
319 private_attest_db_t *this, pts_meas_algorithms_t algo)
320 {
321 this->algo = algo;
322 }
323
324 METHOD(attest_db_t, list_components, void,
325 private_attest_db_t *this)
326 {
327 enumerator_t *e;
328 enum_name_t *names, *types;
329 pts_comp_func_name_t *cfn;
330 int type, cid, vid, name, qualifier, count = 0;
331 char flags[8];
332
333 if (this->pid)
334 {
335 e = this->db->query(this->db,
336 "SELECT c.id, c.vendor_id, c.name, c.qualifier "
337 "FROM components AS c "
338 "JOIN product_component AS pc ON c.id = pc.component "
339 "WHERE pc.product = ? ORDER BY c.vendor_id, c.name, c.qualifier",
340 DB_INT, this->pid, DB_INT, DB_INT, DB_INT, DB_INT);
341 }
342 else
343 {
344 e = this->db->query(this->db,
345 "SELECT id, vendor_id, name, qualifier FROM components "
346 "ORDER BY vendor_id, name, qualifier",
347 DB_INT, DB_INT, DB_INT, DB_INT);
348 }
349 if (e)
350 {
351 while (e->enumerate(e, &cid, &vid, &name, &qualifier))
352 {
353 printf("%3d: 0x%06x/0x%08x-0x%02x", cid, vid, name, qualifier);
354
355 cfn = pts_comp_func_name_create(vid, name, qualifier);
356 names = pts_components->get_comp_func_names(pts_components, vid);
357 types = pts_components->get_qualifier_type_names(pts_components, vid);
358 type = pts_components->get_qualifier(pts_components, cfn, flags);
359 if (names && types)
360 {
361 printf(" %N '%N' [%s] '%N'", pen_names, vid, names, name, flags,
362 types, type);
363 }
364 printf("\n");
365 cfn->destroy(cfn);
366
367 count++;
368 }
369 e->destroy(e);
370
371 printf("%d component%s found", count, (count == 1) ? "" : "s");
372 if (this->product)
373 {
374 printf(" for product '%s'", this->product);
375 }
376 printf("\n");
377 }
378 }
379
380 METHOD(attest_db_t, list_files, void,
381 private_attest_db_t *this)
382 {
383 enumerator_t *e;
384 char *file;
385 int fid, is_dir, meas, meta, count = 0;
386
387 if (this->pid)
388 {
389 e = this->db->query(this->db,
390 "SELECT f.id, f.type, f.path, pf.measurement, pf.metadata "
391 "FROM files AS f "
392 "JOIN product_file AS pf ON f.id = pf.file "
393 "WHERE pf.product = ? ORDER BY f.path",
394 DB_INT, this->pid, DB_INT, DB_INT, DB_TEXT, DB_INT, DB_INT);
395 if (e)
396 {
397 while (e->enumerate(e, &fid, &is_dir, &file, &meas, &meta))
398 {
399 printf("%3d: |%s%s| %s %s\n", fid, meas ? "M":" ", meta ? "T":" ",
400 is_dir ? "d":" ", file);
401 count++;
402 }
403 e->destroy(e);
404 }
405 }
406 else
407 {
408 e = this->db->query(this->db,
409 "SELECT id, type, path FROM files "
410 "ORDER BY path",
411 DB_INT, DB_INT, DB_TEXT);
412 if (e)
413 {
414 while (e->enumerate(e, &fid, &is_dir, &file))
415 {
416 printf("%3d: %s %s\n", fid, is_dir ? "d":" ", file);
417 count++;
418 }
419 e->destroy(e);
420 }
421 }
422
423 printf("%d file%s found", count, (count == 1) ? "" : "s");
424 if (this->product)
425 {
426 printf(" for product '%s'", this->product);
427 }
428 printf("\n");
429 }
430
431 METHOD(attest_db_t, list_products, void,
432 private_attest_db_t *this)
433 {
434 enumerator_t *e;
435 char *product;
436 int pid, meas, meta, count = 0;
437
438 if (this->fid)
439 {
440 e = this->db->query(this->db,
441 "SELECT p.id, p.name, pf.measurement, pf.metadata "
442 "FROM products AS p "
443 "JOIN product_file AS pf ON p.id = pf.product "
444 "WHERE pf.file = ? ORDER BY p.name",
445 DB_INT, this->fid, DB_INT, DB_TEXT, DB_INT, DB_INT);
446 if (e)
447 {
448 while (e->enumerate(e, &pid, &product, &meas, &meta))
449 {
450 printf("%3d: |%s%s| %s\n", pid, meas ? "M":" ", meta ? "T":" ",
451 product);
452 count++;
453 }
454 e->destroy(e);
455 }
456 }
457 else
458 {
459 e = this->db->query(this->db, "SELECT id, name FROM products "
460 "ORDER BY name",
461 DB_INT, DB_TEXT);
462 if (e)
463 {
464 while (e->enumerate(e, &pid, &product))
465 {
466 printf("%3d: %s\n", pid, product);
467 count++;
468 }
469 e->destroy(e);
470 }
471 }
472
473 printf("%d product%s found", count, (count == 1) ? "" : "s");
474 if (this->file)
475 {
476 printf(" for file '%s'", this->file);
477 }
478 printf("\n");
479 }
480
481 /**
482 * get the directory if there is one from the files tables
483 */
484 static void get_directory(private_attest_db_t *this, int did, char **directory)
485 {
486 enumerator_t *e;
487 char *dir;
488
489 free(*directory);
490 *directory = strdup("");
491
492 if (did)
493 {
494 e = this->db->query(this->db,
495 "SELECT path from files WHERE id = ?",
496 DB_INT, did, DB_TEXT);
497 if (e)
498 {
499 if (e->enumerate(e, &dir))
500 {
501 free(*directory);
502 *directory = strdup(dir);
503 }
504 e->destroy(e);
505 }
506 }
507 }
508
509 static bool slash(char *directory, char *file)
510 {
511 return *file != '/' && directory[max(0, strlen(directory)-1)] != '/';
512 }
513
514 METHOD(attest_db_t, list_hashes, void,
515 private_attest_db_t *this)
516 {
517 enumerator_t *e;
518 chunk_t hash;
519 char *file, *dir, *product;
520 int fid, fid_old = 0, did, did_old = 0, count = 0;
521
522 dir = strdup("");
523
524 if (this->pid && this->fid)
525 {
526 e = this->db->query(this->db,
527 "SELECT hash FROM file_hashes "
528 "WHERE algo = ? AND file = ? AND directory = ? AND product = ?",
529 DB_INT, this->algo, DB_INT, this->fid, DB_INT, this->did,
530 DB_INT, this->pid, DB_BLOB);
531 if (e)
532 {
533 while (e->enumerate(e, &hash))
534 {
535 if (this->fid != fid_old)
536 {
537 printf("%3d: %s%s%s\n", this->fid, this->dir,
538 slash(this->dir, this->file) ? "/" : "", this->file);
539 fid_old = this->fid;
540 }
541 printf(" %#B\n", &hash);
542 count++;
543 }
544 e->destroy(e);
545
546 printf("%d %N value%s found for product '%s'\n", count,
547 hash_algorithm_names, pts_meas_algo_to_hash(this->algo),
548 (count == 1) ? "" : "s", this->product);
549 }
550 }
551 else if (this->pid)
552 {
553 e = this->db->query(this->db,
554 "SELECT f.id, f. f.path, fh.hash, fh.directory "
555 "FROM file_hashes AS fh "
556 "JOIN files AS f ON f.id = fh.file "
557 "WHERE fh.algo = ? AND fh.product = ? "
558 "ORDER BY fh.directory, f.path",
559 DB_INT, this->algo, DB_INT, this->pid,
560 DB_INT, DB_TEXT, DB_BLOB, DB_INT);
561 if (e)
562 {
563 while (e->enumerate(e, &fid, &file, &hash, &did))
564 {
565 if (fid != fid_old || did != did_old)
566 {
567 if (did != did_old)
568 {
569 get_directory(this, did, &dir);
570 }
571 printf("%3d: %s%s%s\n", fid,
572 dir, slash(dir, file) ? "/" : "", file);
573 fid_old = fid;
574 did_old = did;
575 }
576 printf(" %#B\n", &hash);
577 count++;
578 }
579 e->destroy(e);
580
581 printf("%d %N value%s found for product '%s'\n", count,
582 hash_algorithm_names, pts_meas_algo_to_hash(this->algo),
583 (count == 1) ? "" : "s", this->product);
584 }
585 }
586 else if (this->fid)
587 {
588 e = this->db->query(this->db,
589 "SELECT p.name, fh.hash, fh.directory "
590 "FROM file_hashes AS fh "
591 "JOIN products AS p ON p.id = fh.product "
592 "WHERE fh.algo = ? AND fh.file = ? AND fh.directory = ?"
593 "ORDER BY p.name",
594 DB_INT, this->algo, DB_INT, this->fid, DB_INT, this->did,
595 DB_TEXT, DB_BLOB, DB_INT);
596 if (e)
597 {
598 while (e->enumerate(e, &product, &hash, &did))
599 {
600 printf("%#B '%s'\n", &hash, product);
601 count++;
602 }
603 e->destroy(e);
604
605 printf("%d %N value%s found for file '%s%s%s'\n",
606 count, hash_algorithm_names, pts_meas_algo_to_hash(this->algo),
607 (count == 1) ? "" : "s", this->dir,
608 slash(this->dir, this->file) ? "/" : "", this->file);
609 }
610 }
611 else
612 {
613 e = this->db->query(this->db,
614 "SELECT f.id, f.path, p.name, fh.hash, fh.directory "
615 "FROM file_hashes AS fh "
616 "JOIN files AS f ON f.id = fh.file "
617 "JOIN products AS p ON p.id = fh.product "
618 "WHERE fh.algo = ? "
619 "ORDER BY fh.directory, f.path, p.name",
620 DB_INT, this->algo,
621 DB_INT, DB_TEXT, DB_TEXT, DB_BLOB, DB_INT);
622 if (e)
623 {
624 while (e->enumerate(e, &fid, &file, &product, &hash, &did))
625 {
626 if (fid != fid_old || did != did_old)
627 {
628 if (did != did_old)
629 {
630 get_directory(this, did, &dir);
631 did_old = did;
632 }
633 printf("%3d: %s%s%s\n", fid,
634 dir, slash(dir, file) ? "/" : "", file);
635 fid_old = fid;
636 }
637 printf(" %#B '%s'\n", &hash, product);
638 count++;
639 }
640 e->destroy(e);
641
642 printf("%d %N value%s found\n", count, hash_algorithm_names,
643 pts_meas_algo_to_hash(this->algo), (count == 1) ? "" : "s");
644 }
645 }
646 free(dir);
647 }
648
649 METHOD(attest_db_t, add, bool,
650 private_attest_db_t *this)
651 {
652 return FALSE;
653 }
654
655 METHOD(attest_db_t, delete, bool,
656 private_attest_db_t *this)
657 {
658 bool success;
659
660 if (this->pid && (this->fid || this->did))
661 {
662 printf("deletion of product/file entries not supported yet\n");
663 return FALSE;
664 }
665
666 if (this->pid)
667 {
668 success = this->db->execute(this->db, NULL,
669 "DELETE FROM products WHERE id = ?",
670 DB_UINT, this->pid) > 0;
671
672 printf("product '%s' %sdeleted from database\n", this->product,
673 success ? "" : "could not be ");
674 return success;
675 }
676
677 if (this->fid)
678 {
679 success = this->db->execute(this->db, NULL,
680 "DELETE FROM files WHERE id = ?",
681 DB_UINT, this->fid) > 0;
682
683 printf("file '%s' %sdeleted from database\n", this->file,
684 success ? "" : "could not be ");
685 return success;
686 }
687
688 if (this->did)
689 {
690 success = this->db->execute(this->db, NULL,
691 "DELETE FROM files WHERE type = 1 AND id = ?",
692 DB_UINT, this->did) > 0;
693
694 printf("directory '%s' %sdeleted from database\n", this->dir,
695 success ? "" : "could not be ");
696 return success;
697 }
698
699 printf("empty delete command\n");
700 return FALSE;
701 }
702
703 METHOD(attest_db_t, destroy, void,
704 private_attest_db_t *this)
705 {
706 DESTROY_IF(this->db);
707 free(this->product);
708 free(this->file);
709 free(this->dir);
710 free(this);
711 }
712
713 /**
714 * Described in header.
715 */
716 attest_db_t *attest_db_create(char *uri)
717 {
718 private_attest_db_t *this;
719
720 INIT(this,
721 .public = {
722 .set_product = _set_product,
723 .set_pid = _set_pid,
724 .set_file = _set_file,
725 .set_fid = _set_fid,
726 .set_directory = _set_directory,
727 .set_did = _set_did,
728 .set_algo = _set_algo,
729 .list_products = _list_products,
730 .list_files = _list_files,
731 .list_components = _list_components,
732 .list_hashes = _list_hashes,
733 .add = _add,
734 .delete = _delete,
735 .destroy = _destroy,
736 },
737 .dir = strdup(""),
738 .algo = PTS_MEAS_ALGO_SHA256,
739 .db = lib->db->create(lib->db, uri),
740 );
741
742 if (!this->db)
743 {
744 fprintf(stderr, "opening database failed.\n");
745 destroy(this);
746 return NULL;
747 }
748
749 return &this->public;
750 }