Fixed check_file_measurement method in pts_database_t
[strongswan.git] / src / libpts / pts / pts_database.c
1 /*
2 * Copyright (C) 2011-2012 Sansar Choinyambuu, 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 #define _GNU_SOURCE
17 #include <stdio.h>
18 #include <libgen.h>
19
20 #include "pts_database.h"
21
22 #include <utils/debug.h>
23 #include <crypto/hashers/hasher.h>
24
25
26 typedef struct private_pts_database_t private_pts_database_t;
27
28 /**
29 * Private data of a pts_database_t object.
30 *
31 */
32 struct private_pts_database_t {
33
34 /**
35 * Public pts_database_t interface.
36 */
37 pts_database_t public;
38
39 /**
40 * database instance
41 */
42 database_t *db;
43
44 };
45
46 METHOD(pts_database_t, get_pathname, char*,
47 private_pts_database_t *this, bool is_dir, int id)
48 {
49 enumerator_t *e;
50 char *path, *name, *pathname;
51
52 if (is_dir)
53 {
54 e = this->db->query(this->db,
55 "SELECT path FROM directories WHERE id = ?",
56 DB_INT, id, DB_TEXT);
57 if (!e || !e->enumerate(e, &path))
58 {
59 pathname = NULL;
60 }
61 else
62 {
63 pathname = strdup(path);
64 }
65 }
66 else
67 {
68 e = this->db->query(this->db,
69 "SELECT d.path, f.name FROM files AS f "
70 "JOIN directories AS d ON d.id = f.dir WHERE f.id = ?",
71 DB_INT, id, DB_TEXT, DB_TEXT);
72 if (!e || !e->enumerate(e, &path, &name) ||
73 asprintf(&pathname, "%s%s%s",
74 path, streq(path, "/") ? "" : "/", name) == -1)
75 {
76 pathname = NULL;
77 }
78 }
79 DESTROY_IF(e);
80
81 return pathname;
82 }
83
84 METHOD(pts_database_t, create_file_hash_enumerator, enumerator_t*,
85 private_pts_database_t *this, char *product, pts_meas_algorithms_t algo,
86 bool is_dir, int id)
87 {
88 enumerator_t *e;
89
90 if (is_dir)
91 {
92 e = this->db->query(this->db,
93 "SELECT f.name, fh.hash FROM file_hashes AS fh "
94 "JOIN files AS f ON f.id = fh.file "
95 "JOIN products AS p ON p.id = fh.product "
96 "JOIN directories as d ON d.id = f.dir "
97 "WHERE p.name = ? AND fh.algo = ? AND d.id = ? "
98 "ORDER BY f.name",
99 DB_TEXT, product, DB_INT, algo, DB_INT, id, DB_TEXT, DB_BLOB);
100 }
101 else
102 {
103 e = this->db->query(this->db,
104 "SELECT f.name, fh.hash FROM file_hashes AS fh "
105 "JOIN files AS f ON f.id = fh.file "
106 "JOIN products AS p ON p.id = fh.product "
107 "WHERE p.name = ? AND fh.algo = ? AND fh.file = ?",
108 DB_TEXT, product, DB_INT, algo, DB_INT, id, DB_TEXT, DB_BLOB);
109 }
110 return e;
111 }
112
113 METHOD(pts_database_t, check_aik_keyid, status_t,
114 private_pts_database_t *this, chunk_t keyid, int *kid)
115 {
116 enumerator_t *e;
117
118 /* If the AIK is registered get the primary key */
119 e = this->db->query(this->db,
120 "SELECT id FROM keys WHERE keyid = ?", DB_BLOB, keyid, DB_INT);
121 if (!e)
122 {
123 DBG1(DBG_PTS, "no database query enumerator returned");
124 return FAILED;
125 }
126 if (!e->enumerate(e, kid))
127 {
128 DBG1(DBG_PTS, "AIK %#B is not registered in database", &keyid);
129 e->destroy(e);
130 return FAILED;
131 }
132 e->destroy(e);
133
134 return SUCCESS;
135 }
136
137 METHOD(pts_database_t, add_file_measurement, status_t,
138 private_pts_database_t *this, char *product, pts_meas_algorithms_t algo,
139 chunk_t measurement, char *filename, bool is_dir, int id)
140 {
141 enumerator_t *e;
142 char *name;
143 chunk_t hash_value;
144 int hash_id, fid, pid = 0;
145 status_t status = SUCCESS;
146
147 /* get primary key of product string */
148 e = this->db->query(this->db,
149 "SELECT id FROM products WHERE name = ?", DB_TEXT, product, DB_INT);
150 if (e)
151 {
152 e->enumerate(e, &pid);
153 e->destroy(e);
154 }
155 if (pid == 0)
156 {
157 return FAILED;
158 }
159
160 if (is_dir)
161 {
162 /* does filename entry already exist? */
163 e = this->db->query(this->db,
164 "SELECT id FROM files WHERE name = ? AND dir = ?",
165 DB_TEXT, filename, DB_INT, id, DB_INT);
166 if (!e)
167 {
168 return FAILED;
169 }
170 if (!e->enumerate(e, &fid))
171 {
172 /* create filename entry */
173 if (this->db->execute(this->db, &fid,
174 "INSERT INTO files (name, dir) VALUES (?, ?)",
175 DB_TEXT, filename, DB_INT, id) != 1)
176 {
177 DBG1(DBG_PTS, "could not insert filename into database");
178 status = FAILED;
179 }
180 }
181 e->destroy(e);
182 }
183 else
184 {
185 fid = id;
186
187 /* verify filename */
188 e = this->db->query(this->db,
189 "SELECT name FROM files WHERE id = ?", DB_INT, fid, DB_TEXT);
190 if (!e)
191 {
192 return FAILED;
193 }
194 if (!e->enumerate(e, &name) || !streq(name, filename))
195 {
196 DBG1(DBG_PTS, "filename of reference measurement does not match");
197 status = FAILED;
198 }
199 e->destroy(e);
200 }
201
202 if (status != SUCCESS)
203 {
204 return status;
205 }
206
207 /* does hash measurement value already exist? */
208 e = this->db->query(this->db,
209 "SELECT fh.id, fh.hash FROM file_hashes AS fh "
210 "WHERE fh.product = ? AND fh.algo = ? AND fh.file = ?",
211 DB_INT, pid, DB_INT, algo, DB_INT, fid, DB_INT, DB_BLOB);
212 if (!e)
213 {
214 return FAILED;
215 }
216 if (e->enumerate(e, &hash_id, &hash_value))
217 {
218 if (!chunk_equals(measurement, hash_value))
219 {
220 /* update hash measurement value */
221 if (this->db->execute(this->db, &hash_id,
222 "UPDATE file_hashes SET hash = ? WHERE id = ?",
223 DB_BLOB, measurement, DB_INT, hash_id) != 1)
224 {
225 status = FAILED;
226 }
227 }
228 }
229 else
230 {
231 /* insert hash measurement value */
232 if (this->db->execute(this->db, &hash_id,
233 "INSERT INTO file_hashes (file, product, algo, hash) "
234 "VALUES (?, ?, ?, ?)", DB_INT, fid, DB_INT, pid,
235 DB_INT, algo, DB_BLOB, measurement) != 1)
236 {
237 status = FAILED;
238 }
239 }
240 e->destroy(e);
241
242 return status;
243 }
244
245 METHOD(pts_database_t, check_file_measurement, status_t,
246 private_pts_database_t *this, char *product, pts_meas_algorithms_t algo,
247 chunk_t measurement, char *filename)
248 {
249 enumerator_t *e;
250 chunk_t hash;
251 status_t status = NOT_FOUND;
252 char *path, *dir, *file;
253
254 if (strlen(filename) < 1)
255 {
256 return INVALID_ARG;
257 }
258
259 /* separate filename into directory and basename components */
260 path = strdup(filename);
261 dir = dirname(path);
262 file = basename(filename);
263
264 if (*dir == '.')
265 { /* relative pathname */
266 e = this->db->query(this->db,
267 "SELECT fh.hash FROM file_hashes AS fh "
268 "JOIN files AS f ON f.id = fh.file "
269 "JOIN products AS p ON p.id = fh.product "
270 "WHERE p.name = ? AND f.name = ? AND fh.algo = ?",
271 DB_TEXT, product, DB_TEXT, file, DB_INT, algo, DB_BLOB);
272 }
273 else
274 { /* absolute pathname */
275 bool dir_found;
276 int did;
277
278 /* find directory entry first */
279 e = this->db->query(this->db,
280 "SELECT id FROM directories WHERE path = ?",
281 DB_TEXT, dir, DB_INT);
282 if (!e)
283 {
284 free(path);
285 return FAILED;
286 }
287 dir_found = e->enumerate(e, &did);
288 e->destroy(e);
289
290 if (!dir_found)
291 {
292 free(path);
293 return NOT_FOUND;
294 }
295
296 e = this->db->query(this->db,
297 "SELECT fh.hash FROM file_hashes AS fh "
298 "JOIN files AS f ON f.id = fh.file "
299 "JOIN products AS p ON p.id = fh.product "
300 "WHERE p.name = ? AND f.dir = ? AND f.name = ? AND fh.algo = ?",
301 DB_TEXT, product, DB_INT, did, DB_TEXT, file, DB_INT, algo,
302 DB_BLOB);
303 }
304 free(path);
305
306 if (!e)
307 {
308 return FAILED;
309 }
310 while (e->enumerate(e, &hash))
311 {
312 /* with relative filenames there might be multiple entries */
313 if (chunk_equals(measurement, hash))
314 {
315 status = SUCCESS;
316 break;
317 }
318 else
319 {
320 status = VERIFY_ERROR;
321 }
322 }
323 e->destroy(e);
324
325 return status;
326 }
327
328 METHOD(pts_database_t, create_comp_evid_enumerator, enumerator_t*,
329 private_pts_database_t *this, int kid)
330 {
331 enumerator_t *e;
332
333 /* look for all entries belonging to an AIK in the components table */
334 e = this->db->query(this->db,
335 "SELECT c.vendor_id, c.name, c.qualifier, kc.depth "
336 "FROM components AS c "
337 "JOIN key_component AS kc ON c.id = kc.component "
338 "WHERE kc.key = ? ORDER BY kc.seq_no",
339 DB_INT, kid, DB_INT, DB_INT, DB_INT, DB_INT);
340 return e;
341 }
342
343 METHOD(pts_database_t, check_comp_measurement, status_t,
344 private_pts_database_t *this, chunk_t measurement, int cid, int kid,
345 int seq_no, int pcr, pts_meas_algorithms_t algo)
346 {
347 enumerator_t *e;
348 chunk_t hash;
349 status_t status = NOT_FOUND;
350
351 e = this->db->query(this->db,
352 "SELECT hash FROM component_hashes "
353 "WHERE component = ? AND key = ? "
354 "AND seq_no = ? AND pcr = ? AND algo = ? ",
355 DB_INT, cid, DB_INT, kid, DB_INT, seq_no,
356 DB_INT, pcr, DB_INT, algo, DB_BLOB);
357 if (!e)
358 {
359 DBG1(DBG_PTS, "no database query enumerator returned");
360 return FAILED;
361 }
362
363 while (e->enumerate(e, &hash))
364 {
365 if (chunk_equals(hash, measurement))
366 {
367 status = SUCCESS;
368 break;
369 }
370 else
371 {
372 DBG1(DBG_PTS, "PCR %2d no matching component measurement #%d "
373 "found in database", pcr, seq_no);
374 DBG1(DBG_PTS, " expected: %#B", &hash);
375 DBG1(DBG_PTS, " received: %#B", &measurement);
376 status = VERIFY_ERROR;
377 break;
378 }
379 }
380 e->destroy(e);
381
382 if (status == NOT_FOUND)
383 {
384 DBG1(DBG_PTS, "PCR %2d no measurement #%d "
385 "found in database", pcr, seq_no);
386 }
387
388 return status;
389 }
390
391 METHOD(pts_database_t, insert_comp_measurement, status_t,
392 private_pts_database_t *this, chunk_t measurement, int cid, int kid,
393 int seq_no, int pcr, pts_meas_algorithms_t algo)
394 {
395 int id;
396
397 if (this->db->execute(this->db, &id,
398 "INSERT INTO component_hashes "
399 "(component, key, seq_no, pcr, algo, hash) "
400 "VALUES (?, ?, ?, ?, ?, ?)",
401 DB_INT, cid, DB_INT, kid, DB_INT, seq_no, DB_INT, pcr,
402 DB_INT, algo, DB_BLOB, measurement) == 1)
403 {
404 return SUCCESS;
405 }
406
407 DBG1(DBG_PTS, "could not insert component measurement into database");
408 return FAILED;
409 }
410
411 METHOD(pts_database_t, delete_comp_measurements, int,
412 private_pts_database_t *this, int cid, int kid)
413 {
414 return this->db->execute(this->db, NULL,
415 "DELETE FROM component_hashes "
416 "WHERE component = ? AND key = ?",
417 DB_INT, cid, DB_INT, kid);
418 }
419
420 METHOD(pts_database_t, get_comp_measurement_count, status_t,
421 private_pts_database_t *this, pts_comp_func_name_t *comp_name,
422 chunk_t keyid, pts_meas_algorithms_t algo, int *cid, int *kid, int *count)
423 {
424 enumerator_t *e;
425 status_t status = SUCCESS;
426
427 /* Initialize count */
428 *count = 0;
429
430 if (_check_aik_keyid(this, keyid, kid) != SUCCESS)
431 {
432 return FAILED;
433 }
434
435 /* Get the primary key of the Component Functional Name */
436 e = this->db->query(this->db,
437 "SELECT id FROM components "
438 " WHERE vendor_id = ? AND name = ? AND qualifier = ?",
439 DB_INT, comp_name->get_vendor_id(comp_name),
440 DB_INT, comp_name->get_name(comp_name),
441 DB_INT, comp_name->get_qualifier(comp_name),
442 DB_INT);
443 if (!e)
444 {
445 DBG1(DBG_PTS, "no database query enumerator returned");
446 return FAILED;
447 }
448 if (!e->enumerate(e, cid))
449 {
450 DBG1(DBG_PTS, "component functional name not found in database");
451 e->destroy(e);
452 return FAILED;
453 }
454 e->destroy(e);
455
456 /* Get the number of stored measurements for a given AIK and component */
457 e = this->db->query(this->db,
458 "SELECT COUNT(*) FROM component_hashes AS ch "
459 "WHERE component = ? AND key = ? AND algo = ?",
460 DB_INT, *cid, DB_INT, *kid, DB_INT, algo, DB_INT);
461 if (!e)
462 {
463 DBG1(DBG_PTS, "no database query enumerator returned");
464 return FAILED;
465 }
466 if (!e->enumerate(e, count))
467 {
468 DBG1(DBG_PTS, "no component measurement count returned from database");
469 status = FAILED;
470 }
471 e->destroy(e);
472
473 return status;
474 }
475
476 METHOD(pts_database_t, destroy, void,
477 private_pts_database_t *this)
478 {
479 free(this);
480 }
481
482 /**
483 * See header
484 */
485 pts_database_t *pts_database_create(imv_database_t *imv_db)
486 {
487 private_pts_database_t *this;
488
489 if (!imv_db)
490 {
491 return NULL;
492 }
493
494 INIT(this,
495 .public = {
496 .get_pathname = _get_pathname,
497 .create_comp_evid_enumerator = _create_comp_evid_enumerator,
498 .create_file_hash_enumerator = _create_file_hash_enumerator,
499 .check_aik_keyid = _check_aik_keyid,
500 .add_file_measurement = _add_file_measurement,
501 .check_file_measurement = _check_file_measurement,
502 .check_comp_measurement = _check_comp_measurement,
503 .insert_comp_measurement = _insert_comp_measurement,
504 .delete_comp_measurements = _delete_comp_measurements,
505 .get_comp_measurement_count = _get_comp_measurement_count,
506 .destroy = _destroy,
507 },
508 .db = imv_db->get_database(imv_db),
509 );
510
511 return &this->public;
512 }
513