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