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