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