ikev1: Handle queued TRANSACTION messages only after processing replies
[strongswan.git] / src / libimcv / pts / pts_database.c
1 /*
2 * Copyright (C) 2011-2012 Sansar Choinyambuu
3 * Copyright (C) 2012-2014 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <libgen.h>
20
21 #include "pts_database.h"
22
23 #include <utils/debug.h>
24 #include <crypto/hashers/hasher.h>
25
26
27 typedef struct private_pts_database_t private_pts_database_t;
28
29 /**
30 * Private data of a pts_database_t object.
31 *
32 */
33 struct private_pts_database_t {
34
35 /**
36 * Public pts_database_t interface.
37 */
38 pts_database_t public;
39
40 /**
41 * database instance
42 */
43 database_t *db;
44
45 };
46
47 METHOD(pts_database_t, get_pathname, char*,
48 private_pts_database_t *this, bool is_dir, int id)
49 {
50 enumerator_t *e;
51 char *path, *name, *sep, *pathname = NULL;
52
53 if (is_dir)
54 {
55 e = this->db->query(this->db,
56 "SELECT path FROM directories WHERE id = ?",
57 DB_INT, id, DB_TEXT);
58 if (!e || !e->enumerate(e, &path))
59 {
60 pathname = NULL;
61 }
62 else
63 {
64 pathname = strdup(path);
65 }
66 }
67 else
68 {
69 e = this->db->query(this->db,
70 "SELECT d.path, f.name FROM files AS f "
71 "JOIN directories AS d ON d.id = f.dir WHERE f.id = ?",
72 DB_INT, id, DB_TEXT, DB_TEXT);
73 if (e && e->enumerate(e, &path, &name))
74 {
75 if (path[0] == '/')
76 { /* Unix style absolute path */
77 sep = "/";
78 }
79 else
80 { /* Windows absolute path */
81 sep = "\\";
82 }
83 if (asprintf(&pathname, "%s%s%s",
84 path, streq(path, "/") ? "" : sep, name) == -1)
85 {
86 pathname = NULL;
87 }
88 }
89 }
90 DESTROY_IF(e);
91
92 return pathname;
93 }
94
95 METHOD(pts_database_t, create_file_hash_enumerator, enumerator_t*,
96 private_pts_database_t *this, int pid, pts_meas_algorithms_t algo,
97 bool is_dir, int id)
98 {
99 enumerator_t *e;
100
101 if (is_dir)
102 {
103 e = this->db->query(this->db,
104 "SELECT f.id, f.name, fh.hash FROM file_hashes AS fh "
105 "JOIN files AS f ON f.id = fh.file "
106 "JOIN directories as d ON d.id = f.dir "
107 "WHERE fh.product = ? AND fh.algo = ? AND d.id = ? "
108 "ORDER BY f.name",
109 DB_INT, pid, DB_INT, algo, DB_INT, id, DB_INT, DB_TEXT, DB_BLOB);
110 }
111 else
112 {
113 e = this->db->query(this->db,
114 "SELECT f.id, f.name, fh.hash FROM file_hashes AS fh "
115 "JOIN files AS f ON f.id = fh.file "
116 "WHERE fh.product = ? AND fh.algo = ? AND fh.file = ?",
117 DB_INT, pid, DB_INT, algo, DB_INT, id, DB_INT, DB_TEXT, DB_BLOB);
118 }
119 return e;
120 }
121
122 METHOD(pts_database_t, add_file_measurement, status_t,
123 private_pts_database_t *this, int pid, pts_meas_algorithms_t algo,
124 chunk_t measurement, char *filename, bool is_dir, int id)
125 {
126 enumerator_t *e;
127 char *name;
128 chunk_t hash_value;
129 int hash_id, fid;
130 status_t status = SUCCESS;
131
132 if (is_dir)
133 {
134 /* does filename entry already exist? */
135 e = this->db->query(this->db,
136 "SELECT id FROM files WHERE name = ? AND dir = ?",
137 DB_TEXT, filename, DB_INT, id, DB_INT);
138 if (!e)
139 {
140 return FAILED;
141 }
142 if (!e->enumerate(e, &fid))
143 {
144 /* create filename entry */
145 if (this->db->execute(this->db, &fid,
146 "INSERT INTO files (name, dir) VALUES (?, ?)",
147 DB_TEXT, filename, DB_INT, id) != 1)
148 {
149 DBG1(DBG_PTS, "could not insert filename into database");
150 status = FAILED;
151 }
152 }
153 e->destroy(e);
154 }
155 else
156 {
157 fid = id;
158
159 /* verify filename */
160 e = this->db->query(this->db,
161 "SELECT name FROM files WHERE id = ?", DB_INT, fid, DB_TEXT);
162 if (!e)
163 {
164 return FAILED;
165 }
166 if (!e->enumerate(e, &name) || !streq(name, filename))
167 {
168 DBG1(DBG_PTS, "filename of reference measurement does not match");
169 status = FAILED;
170 }
171 e->destroy(e);
172 }
173
174 if (status != SUCCESS)
175 {
176 return status;
177 }
178
179 /* does hash measurement value already exist? */
180 e = this->db->query(this->db,
181 "SELECT fh.id, fh.hash FROM file_hashes AS fh "
182 "WHERE fh.product = ? AND fh.algo = ? AND fh.file = ?",
183 DB_INT, pid, DB_INT, algo, DB_INT, fid, DB_INT, DB_BLOB);
184 if (!e)
185 {
186 return FAILED;
187 }
188 if (e->enumerate(e, &hash_id, &hash_value))
189 {
190 if (!chunk_equals_const(measurement, hash_value))
191 {
192 /* update hash measurement value */
193 if (this->db->execute(this->db, &hash_id,
194 "UPDATE file_hashes SET hash = ? WHERE id = ?",
195 DB_BLOB, measurement, DB_INT, hash_id) != 1)
196 {
197 status = FAILED;
198 }
199 }
200 }
201 else
202 {
203 /* insert hash measurement value */
204 if (this->db->execute(this->db, &hash_id,
205 "INSERT INTO file_hashes (file, product, algo, hash) "
206 "VALUES (?, ?, ?, ?)", DB_INT, fid, DB_INT, pid,
207 DB_INT, algo, DB_BLOB, measurement) != 1)
208 {
209 status = FAILED;
210 }
211 }
212 e->destroy(e);
213
214 return status;
215 }
216
217 METHOD(pts_database_t, create_file_meas_enumerator, enumerator_t*,
218 private_pts_database_t *this, int pid, pts_meas_algorithms_t algo,
219 char *filename)
220 {
221 enumerator_t *e;
222 char *dir, *file;
223
224 if (strlen(filename) < 1)
225 {
226 return NULL;
227 }
228
229 /* separate filename into directory and basename components */
230 dir = path_dirname(filename);
231 file = path_basename(filename);
232
233 if (*dir == '.')
234 { /* relative pathname */
235 e = this->db->query(this->db,
236 "SELECT fh.hash FROM file_hashes AS fh "
237 "JOIN files AS f ON f.id = fh.file "
238 "WHERE fh.product = ? AND f.name = ? AND fh.algo = ?",
239 DB_INT, pid, DB_TEXT, file, DB_INT, algo, DB_BLOB);
240 }
241 else
242 { /* absolute pathname */
243 int did;
244
245 /* find directory entry first */
246 e = this->db->query(this->db,
247 "SELECT id FROM directories WHERE path = ?",
248 DB_TEXT, dir, DB_INT);
249
250 if (!e || !e->enumerate(e, &did))
251 {
252 goto err;
253 }
254 e->destroy(e);
255
256 e = this->db->query(this->db,
257 "SELECT fh.hash FROM file_hashes AS fh "
258 "JOIN files AS f ON f.id = fh.file "
259 "WHERE fh.product = ? AND f.dir = ? AND f.name = ? AND fh.algo = ?",
260 DB_INT, pid, DB_INT, did, DB_TEXT, file, DB_INT, algo, DB_BLOB);
261 }
262
263 err:
264 free(file);
265 free(dir);
266
267 return e;
268 }
269
270 METHOD(pts_database_t, check_comp_measurement, status_t,
271 private_pts_database_t *this, chunk_t measurement, int cid, int aik_id,
272 int seq_no, int pcr, pts_meas_algorithms_t algo)
273 {
274 enumerator_t *e;
275 chunk_t hash;
276 status_t status = NOT_FOUND;
277
278 e = this->db->query(this->db,
279 "SELECT hash FROM component_hashes "
280 "WHERE component = ? AND key = ? "
281 "AND seq_no = ? AND pcr = ? AND algo = ? ",
282 DB_INT, cid, DB_INT, aik_id, DB_INT, seq_no,
283 DB_INT, pcr, DB_INT, algo, DB_BLOB);
284 if (!e)
285 {
286 DBG1(DBG_PTS, "no database query enumerator returned");
287 return FAILED;
288 }
289
290 while (e->enumerate(e, &hash))
291 {
292 if (chunk_equals_const(hash, measurement))
293 {
294 status = SUCCESS;
295 break;
296 }
297 else
298 {
299 DBG1(DBG_PTS, "PCR %2d no matching component measurement #%d "
300 "found in database", pcr, seq_no);
301 DBG1(DBG_PTS, " expected: %#B", &hash);
302 DBG1(DBG_PTS, " received: %#B", &measurement);
303 status = VERIFY_ERROR;
304 break;
305 }
306 }
307 e->destroy(e);
308
309 if (status == NOT_FOUND)
310 {
311 DBG1(DBG_PTS, "PCR %2d no measurement #%d "
312 "found in database", pcr, seq_no);
313 }
314
315 return status;
316 }
317
318 METHOD(pts_database_t, insert_comp_measurement, status_t,
319 private_pts_database_t *this, chunk_t measurement, int cid, int aik_id,
320 int seq_no, int pcr, pts_meas_algorithms_t algo)
321 {
322 int id;
323
324 if (this->db->execute(this->db, &id,
325 "INSERT INTO component_hashes "
326 "(component, key, seq_no, pcr, algo, hash) "
327 "VALUES (?, ?, ?, ?, ?, ?)",
328 DB_INT, cid, DB_INT, aik_id, DB_INT, seq_no, DB_INT, pcr,
329 DB_INT, algo, DB_BLOB, measurement) == 1)
330 {
331 return SUCCESS;
332 }
333
334 DBG1(DBG_PTS, "could not insert component measurement into database");
335 return FAILED;
336 }
337
338 METHOD(pts_database_t, delete_comp_measurements, int,
339 private_pts_database_t *this, int cid, int aik_id)
340 {
341 return this->db->execute(this->db, NULL,
342 "DELETE FROM component_hashes "
343 "WHERE component = ? AND key = ?",
344 DB_INT, cid, DB_INT, aik_id);
345 }
346
347 METHOD(pts_database_t, get_comp_measurement_count, status_t,
348 private_pts_database_t *this, pts_comp_func_name_t *comp_name,
349 int aik_id, pts_meas_algorithms_t algo, int *cid, int *count)
350 {
351 enumerator_t *e;
352 status_t status = SUCCESS;
353
354 /* Initialize count */
355 *count = 0;
356
357 /* Get the primary key of the Component Functional Name */
358 e = this->db->query(this->db,
359 "SELECT id FROM components "
360 " WHERE vendor_id = ? AND name = ? AND qualifier = ?",
361 DB_INT, comp_name->get_vendor_id(comp_name),
362 DB_INT, comp_name->get_name(comp_name),
363 DB_INT, comp_name->get_qualifier(comp_name),
364 DB_INT);
365 if (!e)
366 {
367 DBG1(DBG_PTS, "no database query enumerator returned");
368 return FAILED;
369 }
370 if (!e->enumerate(e, cid))
371 {
372 DBG1(DBG_PTS, "component functional name not found in database");
373 e->destroy(e);
374 return FAILED;
375 }
376 e->destroy(e);
377
378 /* Get the number of stored measurements for a given AIK and component */
379 e = this->db->query(this->db,
380 "SELECT COUNT(*) FROM component_hashes AS ch "
381 "WHERE component = ? AND key = ? AND algo = ?",
382 DB_INT, *cid, DB_INT, aik_id, DB_INT, algo, DB_INT);
383 if (!e)
384 {
385 DBG1(DBG_PTS, "no database query enumerator returned");
386 return FAILED;
387 }
388 if (!e->enumerate(e, count))
389 {
390 DBG1(DBG_PTS, "no component measurement count returned from database");
391 status = FAILED;
392 }
393 e->destroy(e);
394
395 return status;
396 }
397
398 METHOD(pts_database_t, destroy, void,
399 private_pts_database_t *this)
400 {
401 free(this);
402 }
403
404 /**
405 * See header
406 */
407 pts_database_t *pts_database_create(imv_database_t *imv_db)
408 {
409 private_pts_database_t *this;
410
411 if (!imv_db)
412 {
413 return NULL;
414 }
415
416 INIT(this,
417 .public = {
418 .get_pathname = _get_pathname,
419 .create_file_hash_enumerator = _create_file_hash_enumerator,
420 .add_file_measurement = _add_file_measurement,
421 .create_file_meas_enumerator = _create_file_meas_enumerator,
422 .check_comp_measurement = _check_comp_measurement,
423 .insert_comp_measurement = _insert_comp_measurement,
424 .delete_comp_measurements = _delete_comp_measurements,
425 .get_comp_measurement_count = _get_comp_measurement_count,
426 .destroy = _destroy,
427 },
428 .db = imv_db->get_database(imv_db),
429 );
430
431 return &this->public;
432 }