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