various PTS fixes
[strongswan.git] / src / libpts / plugins / imv_attestation / attest_db.c
1 /*
2 * Copyright (C) 2011-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 #include "attest_db.h"
17
18 #include "libpts.h"
19 #include "pts/pts_meas_algo.h"
20 #include "pts/pts_file_meas.h"
21 #include "pts/components/pts_comp_func_name.h"
22
23 #include <libgen.h>
24
25 #define IMA_MAX_NAME_LEN 255
26
27 typedef struct private_attest_db_t private_attest_db_t;
28
29 /**
30 * Private data of an attest_db_t object.
31 */
32 struct private_attest_db_t {
33
34 /**
35 * Public members of attest_db_state_t
36 */
37 attest_db_t public;
38
39 /**
40 * Component Functional Name to be queried
41 */
42 pts_comp_func_name_t *cfn;
43
44 /**
45 * Primary key of the Component Functional Name to be queried
46 */
47 int cid;
48
49 /**
50 * TRUE if Component Functional Name has been set
51 */
52 bool comp_set;
53
54 /**
55 * Directory containing the Measurement file to be queried
56 */
57 char *dir;
58
59 /**
60 * Primary key of the directory to be queried
61 */
62 int did;
63
64 /**
65 * TRUE if directory has been set
66 */
67 bool dir_set;
68
69 /**
70 * Measurement file to be queried
71 */
72 char *file;
73
74 /**
75 * Primary key of measurement file to be queried
76 */
77 int fid;
78
79 /**
80 * TRUE if file has been set
81 */
82 bool file_set;
83
84 /**
85 * AIK to be queried
86 */
87 chunk_t key;
88
89 /**
90 * Primary key of the AIK to be queried
91 */
92 int kid;
93
94 /**
95 * TRUE if AIK has been set
96 */
97 bool key_set;
98
99 /**
100 * Software product to be queried
101 */
102 char *product;
103
104 /**
105 * Primary key of software product to be queried
106 */
107 int pid;
108
109 /**
110 * TRUE if product has been set
111 */
112 bool product_set;
113
114 /**
115 * TRUE if relative filenames are to be used
116 */
117 bool relative;
118
119 /**
120 * File measurement hash algorithm
121 */
122 pts_meas_algorithms_t algo;
123
124 /**
125 * Optional owner (user/host name)
126 */
127 char *owner;
128
129 /**
130 * Attestation database
131 */
132 database_t *db;
133
134 };
135
136 char* print_cfn(pts_comp_func_name_t *cfn)
137 {
138 static char buf[BUF_LEN];
139 char flags[8];
140 int type, vid, name, qualifier, n;
141 enum_name_t *names, *types;
142
143 vid = cfn->get_vendor_id(cfn),
144 name = cfn->get_name(cfn);
145 qualifier = cfn->get_qualifier(cfn);
146 n = snprintf(buf, BUF_LEN, "0x%06x/0x%08x-0x%02x", vid, name, qualifier);
147
148 names = pts_components->get_comp_func_names(pts_components, vid);
149 types = pts_components->get_qualifier_type_names(pts_components, vid);
150 type = pts_components->get_qualifier(pts_components, cfn, flags);
151 if (names && types)
152 {
153 n = snprintf(buf + n, BUF_LEN - n, " %N/%N [%s] %N",
154 pen_names, vid, names, name, flags, types, type);
155 }
156 return buf;
157 }
158
159 METHOD(attest_db_t, set_component, bool,
160 private_attest_db_t *this, char *comp, bool create)
161 {
162 enumerator_t *e;
163 char *pos1, *pos2;
164 int vid, name, qualifier;
165 pts_comp_func_name_t *cfn;
166
167 if (this->comp_set)
168 {
169 printf("component has already been set\n");
170 return FALSE;
171 }
172
173 /* parse component string */
174 pos1 = strchr(comp, '/');
175 pos2 = strchr(comp, '-');
176 if (!pos1 || !pos2)
177 {
178 printf("component string must have the form \"vendor_id/name-qualifier\"\n");
179 return FALSE;
180 }
181 vid = atoi(comp);
182 name = atoi(pos1 + 1);
183 qualifier = atoi(pos2 + 1);
184 cfn = pts_comp_func_name_create(vid, name, qualifier);
185
186 e = this->db->query(this->db,
187 "SELECT id FROM components "
188 "WHERE vendor_id = ? AND name = ? AND qualifier = ?",
189 DB_INT, vid, DB_INT, name, DB_INT, qualifier, DB_INT);
190 if (e)
191 {
192 if (e->enumerate(e, &this->cid))
193 {
194 this->comp_set = TRUE;
195 this->cfn = cfn;
196 }
197 e->destroy(e);
198 }
199 if (this->comp_set)
200 {
201 return TRUE;
202 }
203
204 if (!create)
205 {
206 printf("component '%s' not found in database\n", print_cfn(cfn));
207 cfn->destroy(cfn);
208 return FALSE;
209 }
210
211 /* Add a new database entry */
212 this->comp_set = this->db->execute(this->db, &this->cid,
213 "INSERT INTO components (vendor_id, name, qualifier) "
214 "VALUES (?, ?, ?)",
215 DB_INT, vid, DB_INT, name, DB_INT, qualifier) == 1;
216
217 printf("component '%s' %sinserted into database\n", print_cfn(cfn),
218 this->comp_set ? "" : "could not be ");
219 if (this->comp_set)
220 {
221 this->cfn = cfn;
222 }
223 else
224 {
225 cfn->destroy(cfn);
226 }
227 return this->comp_set;
228 }
229
230 METHOD(attest_db_t, set_cid, bool,
231 private_attest_db_t *this, int cid)
232 {
233 enumerator_t *e;
234 int vid, name, qualifier;
235
236 if (this->comp_set)
237 {
238 printf("component has already been set\n");
239 return FALSE;
240 }
241 this->cid = cid;
242
243 e = this->db->query(this->db, "SELECT vendor_id, name, qualifier "
244 "FROM components WHERE id = ?",
245 DB_INT, cid, DB_INT, DB_INT, DB_INT);
246 if (e)
247 {
248 if (e->enumerate(e, &vid, &name, &qualifier))
249 {
250 this->cfn = pts_comp_func_name_create(vid, name, qualifier);
251 this->comp_set = TRUE;
252 }
253 else
254 {
255 printf("no component found with cid %d\n", cid);
256 }
257 e->destroy(e);
258 }
259 return this->comp_set;
260 }
261
262 METHOD(attest_db_t, set_directory, bool,
263 private_attest_db_t *this, char *dir, bool create)
264 {
265 enumerator_t *e;
266 size_t len;
267
268 if (this->dir_set)
269 {
270 printf("directory has already been set\n");
271 return FALSE;
272 }
273 free(this->dir);
274
275 /* remove trailing '/' character */
276 len = strlen(dir);
277 if (len && dir[len-1] == '/')
278 {
279 dir[len-1] = '\0';
280 }
281 this->dir = strdup(dir);
282
283 e = this->db->query(this->db,
284 "SELECT id FROM files WHERE type = 1 AND path = ?",
285 DB_TEXT, dir, DB_INT);
286 if (e)
287 {
288 if (e->enumerate(e, &this->did))
289 {
290 this->dir_set = TRUE;
291 }
292 e->destroy(e);
293 }
294 if (this->dir_set)
295 {
296 return TRUE;
297 }
298
299 if (!create)
300 {
301 printf("directory '%s' not found in database\n", dir);
302 return FALSE;
303 }
304
305 /* Add a new database entry */
306 this->dir_set = this->db->execute(this->db, &this->did,
307 "INSERT INTO files (type, path) VALUES (1, ?)",
308 DB_TEXT, dir) == 1;
309
310 printf("directory '%s' %sinserted into database\n", dir,
311 this->dir_set ? "" : "could not be ");
312
313 return this->dir_set;
314 }
315
316 METHOD(attest_db_t, set_did, bool,
317 private_attest_db_t *this, int did)
318 {
319 enumerator_t *e;
320 char *dir;
321
322 if (this->dir_set)
323 {
324 printf("directory has already been set\n");
325 return FALSE;
326 }
327 this->did = did;
328
329 e = this->db->query(this->db, "SELECT path FROM files WHERE id = ?",
330 DB_INT, did, DB_TEXT);
331 if (e)
332 {
333 if (e->enumerate(e, &dir))
334 {
335 free(this->dir);
336 this->dir = strdup(dir);
337 this->dir_set = TRUE;
338 }
339 else
340 {
341 printf("no directory found with did %d\n", did);
342 }
343 e->destroy(e);
344 }
345 return this->dir_set;
346 }
347
348 METHOD(attest_db_t, set_file, bool,
349 private_attest_db_t *this, char *file, bool create)
350 {
351 enumerator_t *e;
352
353 if (this->file_set)
354 {
355 printf("file has already been set\n");
356 return FALSE;
357 }
358 this->file = strdup(file);
359
360 e = this->db->query(this->db, "SELECT id FROM files WHERE path = ?",
361 DB_TEXT, file, DB_INT);
362 if (e)
363 {
364 if (e->enumerate(e, &this->fid))
365 {
366 this->file_set = TRUE;
367 }
368 e->destroy(e);
369 }
370 if (this->file_set)
371 {
372 return TRUE;
373 }
374
375 if (!create)
376 {
377 printf("file '%s' not found in database\n", file);
378 return FALSE;
379 }
380
381 /* Add a new database entry */
382 this->file_set = this->db->execute(this->db, &this->fid,
383 "INSERT INTO files (type, path) VALUES (0, ?)",
384 DB_TEXT, file) == 1;
385
386 printf("file '%s' %sinserted into database\n", file,
387 this->file_set ? "" : "could not be ");
388
389 return this->file_set;
390 }
391
392 METHOD(attest_db_t, set_fid, bool,
393 private_attest_db_t *this, int fid)
394 {
395 enumerator_t *e;
396 char *file;
397
398 if (this->file_set)
399 {
400 printf("file has already been set\n");
401 return FALSE;
402 }
403 this->fid = fid;
404
405 e = this->db->query(this->db, "SELECT path FROM files WHERE id = ?",
406 DB_INT, fid, DB_TEXT);
407 if (e)
408 {
409 if (e->enumerate(e, &file))
410 {
411 this->file = strdup(file);
412 this->file_set = TRUE;
413 }
414 else
415 {
416 printf("no file found with fid %d\n", fid);
417 }
418 e->destroy(e);
419 }
420 return this->file_set;
421 }
422
423 METHOD(attest_db_t, set_key, bool,
424 private_attest_db_t *this, chunk_t key, bool create)
425 {
426 enumerator_t *e;
427 char *owner;
428
429 if (this->key_set)
430 {
431 printf("key has already been set\n");
432 return FALSE;
433 }
434 this->key = key;
435
436 e = this->db->query(this->db, "SELECT id, owner FROM keys WHERE keyid= ?",
437 DB_BLOB, this->key, DB_INT, DB_TEXT);
438 if (e)
439 {
440 if (e->enumerate(e, &this->kid, &owner))
441 {
442 free(this->owner);
443 this->owner = strdup(owner);
444 this->key_set = TRUE;
445 }
446 e->destroy(e);
447 }
448 if (this->key_set)
449 {
450 return TRUE;
451 }
452
453 if (!create)
454 {
455 printf("key '%#B' not found in database\n", &this->key);
456 return FALSE;
457 }
458
459 /* Add a new database entry */
460 if (!this->owner)
461 {
462 this->owner = strdup("");
463 }
464 this->key_set = this->db->execute(this->db, &this->kid,
465 "INSERT INTO keys (keyid, owner) VALUES (?, ?)",
466 DB_BLOB, this->key, DB_TEXT, this->owner) == 1;
467
468 printf("key '%#B' %sinserted into database\n", &this->key,
469 this->key_set ? "" : "could not be ");
470
471 return this->key_set;
472
473 };
474
475 METHOD(attest_db_t, set_kid, bool,
476 private_attest_db_t *this, int kid)
477 {
478 enumerator_t *e;
479 chunk_t key;
480 char *owner;
481
482 if (this->key_set)
483 {
484 printf("key has already been set\n");
485 return FALSE;
486 }
487 this->kid = kid;
488
489 e = this->db->query(this->db, "SELECT keyid, owner FROM keys WHERE id = ?",
490 DB_INT, kid, DB_BLOB, DB_TEXT);
491 if (e)
492 {
493 if (e->enumerate(e, &key, &owner))
494 {
495 this->owner = strdup(owner);
496 this->key = chunk_clone(key);
497 this->key_set = TRUE;
498 }
499 else
500 {
501 printf("no key found with kid %d\n", kid);
502 }
503 e->destroy(e);
504 }
505 return this->key_set;
506
507 };
508
509 METHOD(attest_db_t, set_product, bool,
510 private_attest_db_t *this, char *product, bool create)
511 {
512 enumerator_t *e;
513
514 if (this->product_set)
515 {
516 printf("product has already been set\n");
517 return FALSE;
518 }
519 this->product = strdup(product);
520
521 e = this->db->query(this->db, "SELECT id FROM products WHERE name = ?",
522 DB_TEXT, product, DB_INT);
523 if (e)
524 {
525 if (e->enumerate(e, &this->pid))
526 {
527 this->product_set = TRUE;
528 }
529 e->destroy(e);
530 }
531 if (this->product_set)
532 {
533 return TRUE;
534 }
535
536 if (!create)
537 {
538 printf("product '%s' not found in database\n", product);
539 return FALSE;
540 }
541
542 /* Add a new database entry */
543 this->product_set = this->db->execute(this->db, &this->pid,
544 "INSERT INTO products (name) VALUES (?)",
545 DB_TEXT, product) == 1;
546
547 printf("product '%s' %sinserted into database\n", product,
548 this->product_set ? "" : "could not be ");
549
550 return this->product_set;
551 }
552
553 METHOD(attest_db_t, set_pid, bool,
554 private_attest_db_t *this, int pid)
555 {
556 enumerator_t *e;
557 char *product;
558
559 if (this->product_set)
560 {
561 printf("product has already been set\n");
562 return FALSE;
563 }
564 this->pid = pid;
565
566 e = this->db->query(this->db, "SELECT name FROM products WHERE id = ?",
567 DB_INT, pid, DB_TEXT);
568 if (e)
569 {
570 if (e->enumerate(e, &product))
571 {
572 this->product = strdup(product);
573 this->product_set = TRUE;
574 }
575 else
576 {
577 printf("no product found with pid %d in database\n", pid);
578 }
579 e->destroy(e);
580 }
581 return this->product_set;
582 }
583
584 METHOD(attest_db_t, set_algo, void,
585 private_attest_db_t *this, pts_meas_algorithms_t algo)
586 {
587 this->algo = algo;
588 }
589
590 METHOD(attest_db_t, set_relative, void,
591 private_attest_db_t *this)
592 {
593 this->relative = TRUE;
594 }
595
596 METHOD(attest_db_t, set_owner, void,
597 private_attest_db_t *this, char *owner)
598 {
599 free(this->owner);
600 this->owner = strdup(owner);
601 }
602
603 METHOD(attest_db_t, list_components, void,
604 private_attest_db_t *this)
605 {
606 enumerator_t *e;
607 pts_comp_func_name_t *cfn;
608 int cid, vid, name, qualifier, count = 0;
609
610 if (this->kid)
611 {
612 e = this->db->query(this->db,
613 "SELECT c.id, c.vendor_id, c.name, c.qualifier "
614 "FROM components AS c "
615 "JOIN key_component AS kc ON c.id = kc.component "
616 "WHERE kc.key = ? ORDER BY c.vendor_id, c.name, c.qualifier",
617 DB_INT, this->kid, DB_INT, DB_INT, DB_INT, DB_INT);
618 }
619 else
620 {
621 e = this->db->query(this->db,
622 "SELECT id, vendor_id, name, qualifier FROM components "
623 "ORDER BY vendor_id, name, qualifier",
624 DB_INT, DB_INT, DB_INT, DB_INT);
625 }
626 if (e)
627 {
628 while (e->enumerate(e, &cid, &vid, &name, &qualifier))
629 {
630 cfn = pts_comp_func_name_create(vid, name, qualifier);
631 printf("%4d: %s\n", cid, print_cfn(cfn));
632 cfn->destroy(cfn);
633 count++;
634 }
635 e->destroy(e);
636
637 printf("%d component%s found", count, (count == 1) ? "" : "s");
638 if (this->key_set)
639 {
640 printf(" for key %#B", &this->key);
641 }
642 printf("\n");
643 }
644 }
645
646 METHOD(attest_db_t, list_keys, void,
647 private_attest_db_t *this)
648 {
649 enumerator_t *e;
650 chunk_t keyid;
651 char *owner;
652 int kid, count = 0;
653
654 if (this->cid)
655 {
656 e = this->db->query(this->db,
657 "SELECT k.id, k.keyid, k.owner FROM keys AS k "
658 "JOIN key_component AS kc ON k.id = kc.key "
659 "WHERE kc.component = ? ORDER BY k.keyid",
660 DB_INT, this->cid, DB_INT, DB_BLOB, DB_TEXT);
661 if (e)
662 {
663 while (e->enumerate(e, &kid, &keyid, &owner))
664 {
665 printf("%4d: %#B '%s'\n", kid, &keyid, owner);
666 count++;
667 }
668 e->destroy(e);
669 }
670 }
671 else
672 {
673 e = this->db->query(this->db, "SELECT id, keyid, owner FROM keys "
674 "ORDER BY keyid",
675 DB_INT, DB_BLOB, DB_TEXT);
676 if (e)
677 {
678 while (e->enumerate(e, &kid, &keyid, &owner))
679 {
680 printf("%4d: %#B '%s'\n", kid, &keyid, owner);
681 count++;
682 }
683 e->destroy(e);
684 }
685 }
686
687 printf("%d key%s found", count, (count == 1) ? "" : "s");
688 if (this->comp_set)
689 {
690 printf(" for component '%s'", print_cfn(this->cfn));
691 }
692 printf("\n");
693 }
694
695 METHOD(attest_db_t, list_files, void,
696 private_attest_db_t *this)
697 {
698 enumerator_t *e;
699 char *file, *file_type[] = { " ", "d", "r" };
700 int fid, type, meas, meta, count = 0;
701
702 if (this->pid)
703 {
704 e = this->db->query(this->db,
705 "SELECT f.id, f.type, f.path, pf.measurement, pf.metadata "
706 "FROM files AS f "
707 "JOIN product_file AS pf ON f.id = pf.file "
708 "WHERE pf.product = ? ORDER BY f.path",
709 DB_INT, this->pid, DB_INT, DB_INT, DB_TEXT, DB_INT, DB_INT);
710 if (e)
711 {
712 while (e->enumerate(e, &fid, &type, &file, &meas, &meta))
713 {
714 type = (type < 0 || type > 2) ? 0 : type;
715 printf("%4d: |%s%s| %s %s\n", fid, meas ? "M":" ", meta ? "T":" ",
716 file_type[type], file);
717 count++;
718 }
719 e->destroy(e);
720 }
721 }
722 else
723 {
724 e = this->db->query(this->db,
725 "SELECT id, type, path FROM files "
726 "ORDER BY path",
727 DB_INT, DB_INT, DB_TEXT);
728 if (e)
729 {
730 while (e->enumerate(e, &fid, &type, &file))
731 {
732 type = (type < 0 || type > 2) ? 0 : type;
733 printf("%4d: %s %s\n", fid, file_type[type], file);
734 count++;
735 }
736 e->destroy(e);
737 }
738 }
739
740 printf("%d file%s found", count, (count == 1) ? "" : "s");
741 if (this->product_set)
742 {
743 printf(" for product '%s'", this->product);
744 }
745 printf("\n");
746 }
747
748 METHOD(attest_db_t, list_products, void,
749 private_attest_db_t *this)
750 {
751 enumerator_t *e;
752 char *product;
753 int pid, meas, meta, count = 0;
754
755 if (this->fid)
756 {
757 e = this->db->query(this->db,
758 "SELECT p.id, p.name, pf.measurement, pf.metadata "
759 "FROM products AS p "
760 "JOIN product_file AS pf ON p.id = pf.product "
761 "WHERE pf.file = ? ORDER BY p.name",
762 DB_INT, this->fid, DB_INT, DB_TEXT, DB_INT, DB_INT);
763 if (e)
764 {
765 while (e->enumerate(e, &pid, &product, &meas, &meta))
766 {
767 printf("%4d: |%s%s| %s\n", pid, meas ? "M":" ", meta ? "T":" ",
768 product);
769 count++;
770 }
771 e->destroy(e);
772 }
773 }
774 else
775 {
776 e = this->db->query(this->db, "SELECT id, name FROM products "
777 "ORDER BY name",
778 DB_INT, DB_TEXT);
779 if (e)
780 {
781 while (e->enumerate(e, &pid, &product))
782 {
783 printf("%4d: %s\n", pid, product);
784 count++;
785 }
786 e->destroy(e);
787 }
788 }
789
790 printf("%d product%s found", count, (count == 1) ? "" : "s");
791 if (this->file_set)
792 {
793 printf(" for file '%s'", this->file);
794 }
795 printf("\n");
796 }
797
798 /**
799 * get the directory if there is one from the files tables
800 */
801 static void get_directory(private_attest_db_t *this, int did, char **directory)
802 {
803 enumerator_t *e;
804 char *dir;
805
806 free(*directory);
807 *directory = strdup("");
808
809 if (did)
810 {
811 e = this->db->query(this->db,
812 "SELECT path from files WHERE id = ?",
813 DB_INT, did, DB_TEXT);
814 if (e)
815 {
816 if (e->enumerate(e, &dir))
817 {
818 free(*directory);
819 *directory = strdup(dir);
820 }
821 e->destroy(e);
822 }
823 }
824 }
825
826 static bool slash(char *directory, char *file)
827 {
828 return *file != '/' && directory[max(0, strlen(directory)-1)] != '/';
829 }
830
831 METHOD(attest_db_t, list_hashes, void,
832 private_attest_db_t *this)
833 {
834 enumerator_t *e;
835 chunk_t hash;
836 char *file, *dir, *product;
837 int fid, fid_old = 0, did, did_old = 0, count = 0;
838
839 dir = strdup("");
840
841 if (this->pid && this->fid)
842 {
843 e = this->db->query(this->db,
844 "SELECT hash FROM file_hashes "
845 "WHERE algo = ? AND file = ? AND directory = ? AND product = ?",
846 DB_INT, this->algo, DB_INT, this->fid, DB_INT, this->did,
847 DB_INT, this->pid, DB_BLOB);
848 if (e)
849 {
850 while (e->enumerate(e, &hash))
851 {
852 if (this->fid != fid_old)
853 {
854 printf("%4d: %s%s%s\n", this->fid, this->dir,
855 slash(this->dir, this->file) ? "/" : "", this->file);
856 fid_old = this->fid;
857 }
858 printf(" %#B\n", &hash);
859 count++;
860 }
861 e->destroy(e);
862
863 printf("%d %N value%s found for product '%s'\n", count,
864 pts_meas_algorithm_names, this->algo,
865 (count == 1) ? "" : "s", this->product);
866 }
867 }
868 else if (this->pid)
869 {
870 e = this->db->query(this->db,
871 "SELECT f.id, f. f.path, fh.hash, fh.directory "
872 "FROM file_hashes AS fh "
873 "JOIN files AS f ON f.id = fh.file "
874 "WHERE fh.algo = ? AND fh.product = ? "
875 "ORDER BY fh.directory, f.path",
876 DB_INT, this->algo, DB_INT, this->pid,
877 DB_INT, DB_TEXT, DB_BLOB, DB_INT);
878 if (e)
879 {
880 while (e->enumerate(e, &fid, &file, &hash, &did))
881 {
882 if (fid != fid_old || did != did_old)
883 {
884 if (did != did_old)
885 {
886 get_directory(this, did, &dir);
887 }
888 printf("%4d: %s%s%s\n", fid,
889 dir, slash(dir, file) ? "/" : "", file);
890 fid_old = fid;
891 did_old = did;
892 }
893 printf(" %#B\n", &hash);
894 count++;
895 }
896 e->destroy(e);
897
898 printf("%d %N value%s found for product '%s'\n", count,
899 pts_meas_algorithm_names, this->algo,
900 (count == 1) ? "" : "s", this->product);
901 }
902 }
903 else if (this->fid)
904 {
905 e = this->db->query(this->db,
906 "SELECT p.name, fh.hash, fh.directory "
907 "FROM file_hashes AS fh "
908 "JOIN products AS p ON p.id = fh.product "
909 "WHERE fh.algo = ? AND fh.file = ? AND fh.directory = ?"
910 "ORDER BY p.name",
911 DB_INT, this->algo, DB_INT, this->fid, DB_INT, this->did,
912 DB_TEXT, DB_BLOB, DB_INT);
913 if (e)
914 {
915 while (e->enumerate(e, &product, &hash, &did))
916 {
917 printf("%#B '%s'\n", &hash, product);
918 count++;
919 }
920 e->destroy(e);
921
922 printf("%d %N value%s found for file '%s%s%s'\n",
923 count, pts_meas_algorithm_names, this->algo,
924 (count == 1) ? "" : "s", this->dir,
925 slash(this->dir, this->file) ? "/" : "", this->file);
926 }
927 }
928 else
929 {
930 e = this->db->query(this->db,
931 "SELECT f.id, f.path, p.name, fh.hash, fh.directory "
932 "FROM file_hashes AS fh "
933 "JOIN files AS f ON f.id = fh.file "
934 "JOIN products AS p ON p.id = fh.product "
935 "WHERE fh.algo = ? "
936 "ORDER BY fh.directory, f.path, p.name",
937 DB_INT, this->algo,
938 DB_INT, DB_TEXT, DB_TEXT, DB_BLOB, DB_INT);
939 if (e)
940 {
941 while (e->enumerate(e, &fid, &file, &product, &hash, &did))
942 {
943 if (fid != fid_old || did != did_old)
944 {
945 if (did != did_old)
946 {
947 get_directory(this, did, &dir);
948 did_old = did;
949 }
950 printf("%4d: %s%s%s\n", fid,
951 dir, slash(dir, file) ? "/" : "", file);
952 fid_old = fid;
953 }
954 printf(" %#B '%s'\n", &hash, product);
955 count++;
956 }
957 e->destroy(e);
958
959 printf("%d %N value%s found\n", count, pts_meas_algorithm_names,
960 this->algo, (count == 1) ? "" : "s");
961 }
962 }
963 free(dir);
964 }
965
966 METHOD(attest_db_t, list_measurements, void,
967 private_attest_db_t *this)
968 {
969 enumerator_t *e;
970 chunk_t hash, keyid;
971 pts_comp_func_name_t *cfn;
972 char *owner;
973 int seq_no, pcr, vid, name, qualifier;
974 int cid, cid_old = 0, kid, kid_old = 0, count = 0;
975
976 if (this->kid && this->cid)
977 {
978 e = this->db->query(this->db,
979 "SELECT ch.seq_no, ch.pcr, ch.hash, k.owner "
980 "FROM component_hashes AS ch "
981 "JOIN keys AS k ON k.id = ch.key "
982 "WHERE ch.algo = ? AND ch.key = ? AND ch.component = ? "
983 "ORDER BY seq_no",
984 DB_INT, this->algo, DB_INT, this->kid, DB_INT, this->cid,
985 DB_INT, DB_INT, DB_BLOB, DB_TEXT);
986 if (e)
987 {
988 while (e->enumerate(e, &seq_no, &pcr, &hash, &owner))
989 {
990 if (this->kid != kid_old)
991 {
992 printf("%4d: %#B '%s'\n", this->kid, &this->key, owner);
993 kid_old = this->kid;
994 }
995 printf("%5d %02d %#B\n", seq_no, pcr, &hash);
996 count++;
997 }
998 e->destroy(e);
999
1000 printf("%d %N value%s found for component '%s'\n", count,
1001 pts_meas_algorithm_names, this->algo,
1002 (count == 1) ? "" : "s", print_cfn(this->cfn));
1003 }
1004 }
1005 else if (this->cid)
1006 {
1007 e = this->db->query(this->db,
1008 "SELECT ch.seq_no, ch.pcr, ch.hash, k.id, k.keyid, k.owner "
1009 "FROM component_hashes AS ch "
1010 "JOIN keys AS k ON k.id = ch.key "
1011 "WHERE ch.algo = ? AND ch.component = ? "
1012 "ORDER BY keyid, seq_no",
1013 DB_INT, this->algo, DB_INT, this->cid,
1014 DB_INT, DB_INT, DB_BLOB, DB_INT, DB_BLOB, DB_TEXT);
1015 if (e)
1016 {
1017 while (e->enumerate(e, &seq_no, &pcr, &hash, &kid, &keyid, &owner))
1018 {
1019 if (kid != kid_old)
1020 {
1021 printf("%4d: %#B '%s'\n", kid, &keyid, owner);
1022 kid_old = kid;
1023 }
1024 printf("%5d %02d %#B\n", seq_no, pcr, &hash);
1025 count++;
1026 }
1027 e->destroy(e);
1028
1029 printf("%d %N value%s found for component '%s'\n", count,
1030 pts_meas_algorithm_names, this->algo,
1031 (count == 1) ? "" : "s", print_cfn(this->cfn));
1032 }
1033
1034 }
1035 else if (this->kid)
1036 {
1037 e = this->db->query(this->db,
1038 "SELECT ch.seq_no, ch.pcr, ch.hash, "
1039 "c.id, c.vendor_id, c.name, c.qualifier "
1040 "FROM component_hashes AS ch "
1041 "JOIN components AS c ON c.id = ch.component "
1042 "WHERE ch.algo = ? AND ch.key = ? "
1043 "ORDER BY vendor_id, name, qualifier, seq_no",
1044 DB_INT, this->algo, DB_INT, this->kid, DB_INT, DB_INT, DB_BLOB,
1045 DB_INT, DB_INT, DB_INT, DB_INT);
1046 if (e)
1047 {
1048 while (e->enumerate(e, &seq_no, &pcr, &hash, &cid, &vid, &name,
1049 &qualifier))
1050 {
1051 if (cid != cid_old)
1052 {
1053 cfn = pts_comp_func_name_create(vid, name, qualifier);
1054 printf("%4d: %s\n", cid, print_cfn(cfn));
1055 cfn->destroy(cfn);
1056 cid_old = cid;
1057 }
1058 printf("%5d %02d %#B\n", seq_no, pcr, &hash);
1059 count++;
1060 }
1061 e->destroy(e);
1062
1063 printf("%d %N value%s found for key %#B '%s'\n", count,
1064 pts_meas_algorithm_names, this->algo,
1065 (count == 1) ? "" : "s", &this->key, this->owner);
1066 }
1067 }
1068 }
1069
1070 bool insert_file_hash(private_attest_db_t *this, pts_meas_algorithms_t algo,
1071 chunk_t measurement, int fid, int did, bool ima,
1072 int *hashes_added)
1073 {
1074 enumerator_t *e;
1075 chunk_t hash;
1076 char *label;
1077
1078 label = "could not be created";
1079
1080 e = this->db->query(this->db,
1081 "SELECT hash FROM file_hashes WHERE algo = ? "
1082 "AND file = ? AND directory = ? AND product = ? and key = 0",
1083 DB_INT, algo, DB_INT, fid, DB_INT, did, DB_INT, this->pid, DB_BLOB);
1084 if (!e)
1085 {
1086 printf("file_hashes query failed\n");
1087 return FALSE;
1088 }
1089 if (e->enumerate(e, &hash))
1090 {
1091 label = chunk_equals(measurement, hash) ?
1092 "exists and equals" : "exists and differs";
1093 }
1094 else
1095 {
1096 if (this->db->execute(this->db, NULL,
1097 "INSERT INTO file_hashes "
1098 "(file, directory, product, key, algo, hash) "
1099 "VALUES (?, ?, ?, 0, ?, ?)",
1100 DB_INT, fid, DB_INT, did, DB_INT, this->pid,
1101 DB_INT, algo, DB_BLOB, measurement) == 1)
1102 {
1103 label = "created";
1104 (*hashes_added)++;
1105 }
1106 }
1107 e->destroy(e);
1108
1109 printf(" %#B - %s%s\n", &measurement, ima ? "ima - " : "", label);
1110 return TRUE;
1111 }
1112
1113 METHOD(attest_db_t, add, bool,
1114 private_attest_db_t *this)
1115 {
1116 bool success = FALSE;
1117
1118 /* add key/component pair */
1119 if (this->kid && this->cid)
1120 {
1121 success = this->db->execute(this->db, NULL,
1122 "INSERT INTO key_component (key, component) VALUES (?, ?)",
1123 DB_UINT, this->kid, DB_UINT, this->cid) == 1;
1124
1125 printf("key/component pair (%d/%d) %sinserted into database\n",
1126 this->kid, this->cid, success ? "" : "could not be ");
1127 }
1128
1129 /* add directory or file measurement for a given product */
1130 if ((this->did || this->fid) && this->pid)
1131 {
1132 char *pathname, *filename, *label;
1133 char ima_buffer[IMA_MAX_NAME_LEN + 1];
1134 chunk_t measurement, ima_template;
1135 pts_file_meas_t *measurements;
1136 hasher_t *hasher = NULL;
1137 bool ima;
1138 int fid, did;
1139 int files_added = 0, hashes_added = 0, ima_hashes_added = 0;
1140 enumerator_t *enumerator, *e;
1141
1142 if (this->algo == PTS_MEAS_ALGO_SHA1_IMA)
1143 {
1144 ima = TRUE;
1145 this->algo = PTS_MEAS_ALGO_SHA1;
1146 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
1147 if (!hasher)
1148 {
1149 printf("could not create hasher\n");
1150 return FALSE;
1151 }
1152 }
1153
1154 pathname = this->did ? this->dir : this->file;
1155 measurements = pts_file_meas_create_from_path(0, pathname, this->did,
1156 this->relative, this->algo);
1157 if (!measurements)
1158 {
1159 printf("file measurement failed\n");
1160 hasher->destroy(hasher);
1161 return FALSE;
1162 }
1163 if (this->fid && this->relative)
1164 {
1165 set_directory(this, dirname(pathname), TRUE);
1166 }
1167 did = this->relative ? this->did : 0;
1168
1169 enumerator = measurements->create_enumerator(measurements);
1170 while (enumerator->enumerate(enumerator, &filename, &measurement))
1171 {
1172 /* retrieve or create filename */
1173 label = "could not be created";
1174
1175 e = this->db->query(this->db,
1176 "SELECT id FROM files WHERE path = ?",
1177 DB_TEXT, filename, DB_INT);
1178 if (!e)
1179 {
1180 printf("files query failed\n");
1181 break;
1182 }
1183 if (e->enumerate(e, &fid))
1184 {
1185 label = "exists";
1186 }
1187 else
1188 {
1189 if (this->db->execute(this->db, &fid,
1190 "INSERT INTO files (type, path) VALUES (0, ?)",
1191 DB_TEXT, filename) == 1)
1192 {
1193 label = "created";
1194 files_added++;
1195 }
1196 }
1197 e->destroy(e);
1198
1199 printf("%4d: %s - %s\n", fid, filename, label);
1200
1201 /* compute file measurement hash */
1202 if (!insert_file_hash(this, this->algo, measurement,
1203 fid, did, FALSE, &hashes_added))
1204 {
1205 break;
1206 }
1207
1208 if (!ima)
1209 {
1210 continue;
1211 }
1212
1213 /* compute IMA template hash */
1214 strncpy(ima_buffer, filename, IMA_MAX_NAME_LEN);
1215 ima_buffer[IMA_MAX_NAME_LEN] = '\0';
1216 ima_template = chunk_create(ima_buffer, sizeof(ima_buffer));
1217 if (!hasher->get_hash(hasher, measurement, NULL) ||
1218 !hasher->get_hash(hasher, ima_template, measurement.ptr))
1219 {
1220 printf("could not compute IMA template hash\n");
1221 break;
1222 }
1223 if (!insert_file_hash(this, PTS_MEAS_ALGO_SHA1_IMA, measurement,
1224 fid, did, TRUE, &ima_hashes_added))
1225 {
1226 break;
1227 }
1228 }
1229 enumerator->destroy(enumerator);
1230
1231 printf("%d measurements, added %d new files, %d new file hashes",
1232 measurements->get_file_count(measurements),
1233 files_added, hashes_added);
1234 if (ima)
1235 {
1236 printf(" , %d new ima hashes");
1237 hasher->destroy(hasher);
1238 }
1239 printf("\n");
1240 measurements->destroy(measurements);
1241 success = TRUE;
1242 }
1243 return success;
1244 }
1245
1246 METHOD(attest_db_t, delete, bool,
1247 private_attest_db_t *this)
1248 {
1249 bool success;
1250
1251 if (this->pid && (this->fid || this->did))
1252 {
1253 printf("deletion of product/file entries not supported yet\n");
1254 return FALSE;
1255 }
1256
1257 if (this->kid && this->cid)
1258 {
1259 success = this->db->execute(this->db, NULL,
1260 "DELETE FROM key_component "
1261 "WHERE key = ? AND component = ?",
1262 DB_UINT, this->kid, DB_UINT, this->cid) > 0;
1263
1264 printf("key/component pair (%d/%d) %sdeleted from database\n",
1265 this->kid, this->cid, success ? "" : "could not be ");
1266 return success;
1267 }
1268
1269 if (this->cid)
1270 {
1271 success = this->db->execute(this->db, NULL,
1272 "DELETE FROM components WHERE id = ?",
1273 DB_UINT, this->cid) > 0;
1274
1275 printf("component '%s' %sdeleted from database\n", print_cfn(this->cfn),
1276 success ? "" : "could not be ");
1277 return success;
1278 }
1279
1280 if (this->did)
1281 {
1282 success = this->db->execute(this->db, NULL,
1283 "DELETE FROM files WHERE type = 1 AND id = ?",
1284 DB_UINT, this->did) > 0;
1285
1286 printf("directory '%s' %sdeleted from database\n", this->dir,
1287 success ? "" : "could not be ");
1288 return success;
1289 }
1290
1291 if (this->fid)
1292 {
1293 success = this->db->execute(this->db, NULL,
1294 "DELETE FROM files WHERE id = ?",
1295 DB_UINT, this->fid) > 0;
1296
1297 printf("file '%s' %sdeleted from database\n", this->file,
1298 success ? "" : "could not be ");
1299 return success;
1300 }
1301
1302 if (this->kid)
1303 {
1304 success = this->db->execute(this->db, NULL,
1305 "DELETE FROM keys WHERE id = ?",
1306 DB_UINT, this->kid) > 0;
1307
1308 printf("key %#B %sdeleted from database\n", &this->key,
1309 success ? "" : "could not be ");
1310 return success;
1311 }
1312 if (this->pid)
1313 {
1314 success = this->db->execute(this->db, NULL,
1315 "DELETE FROM products WHERE id = ?",
1316 DB_UINT, this->pid) > 0;
1317
1318 printf("product '%s' %sdeleted from database\n", this->product,
1319 success ? "" : "could not be ");
1320 return success;
1321 }
1322
1323 printf("empty delete command\n");
1324 return FALSE;
1325 }
1326
1327 METHOD(attest_db_t, destroy, void,
1328 private_attest_db_t *this)
1329 {
1330 DESTROY_IF(this->db);
1331 DESTROY_IF(this->cfn);
1332 free(this->product);
1333 free(this->file);
1334 free(this->dir);
1335 free(this->owner);
1336 free(this->key.ptr);
1337 free(this);
1338 }
1339
1340 /**
1341 * Described in header.
1342 */
1343 attest_db_t *attest_db_create(char *uri)
1344 {
1345 private_attest_db_t *this;
1346
1347 INIT(this,
1348 .public = {
1349 .set_component = _set_component,
1350 .set_cid = _set_cid,
1351 .set_directory = _set_directory,
1352 .set_did = _set_did,
1353 .set_file = _set_file,
1354 .set_fid = _set_fid,
1355 .set_key = _set_key,
1356 .set_kid = _set_kid,
1357 .set_product = _set_product,
1358 .set_pid = _set_pid,
1359 .set_algo = _set_algo,
1360 .set_relative = _set_relative,
1361 .set_owner = _set_owner,
1362 .list_products = _list_products,
1363 .list_files = _list_files,
1364 .list_components = _list_components,
1365 .list_keys = _list_keys,
1366 .list_hashes = _list_hashes,
1367 .list_measurements = _list_measurements,
1368 .add = _add,
1369 .delete = _delete,
1370 .destroy = _destroy,
1371 },
1372 .dir = strdup(""),
1373 .algo = PTS_MEAS_ALGO_SHA256,
1374 .db = lib->db->create(lib->db, uri),
1375 );
1376
1377 if (!this->db)
1378 {
1379 fprintf(stderr, "opening database failed.\n");
1380 destroy(this);
1381 return NULL;
1382 }
1383
1384 return &this->public;
1385 }