Implemented IMA-NG support
[strongswan.git] / src / libpts / 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, *pathname;
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 asprintf(&pathname, "%s%s%s",
75 path, streq(path, "/") ? "" : "/", name) == -1)
76 {
77 pathname = NULL;
78 }
79 }
80 DESTROY_IF(e);
81
82 return pathname;
83 }
84
85 METHOD(pts_database_t, create_file_hash_enumerator, enumerator_t*,
86 private_pts_database_t *this, int pid, pts_meas_algorithms_t algo,
87 bool is_dir, int id)
88 {
89 enumerator_t *e;
90
91 if (is_dir)
92 {
93 e = this->db->query(this->db,
94 "SELECT f.name, fh.hash FROM file_hashes AS fh "
95 "JOIN files AS f ON f.id = fh.file "
96 "JOIN directories as d ON d.id = f.dir "
97 "WHERE fh.product = ? AND fh.algo = ? AND d.id = ? "
98 "ORDER BY f.name",
99 DB_INT, pid, 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 "WHERE fh.product = ? AND fh.algo = ? AND fh.file = ?",
107 DB_INT, pid, DB_INT, algo, DB_INT, id, DB_TEXT, DB_BLOB);
108 }
109 return e;
110 }
111
112 METHOD(pts_database_t, add_file_measurement, status_t,
113 private_pts_database_t *this, int pid, pts_meas_algorithms_t algo,
114 chunk_t measurement, char *filename, bool is_dir, int id)
115 {
116 enumerator_t *e;
117 char *name;
118 chunk_t hash_value;
119 int hash_id, fid;
120 status_t status = SUCCESS;
121
122 if (is_dir)
123 {
124 /* does filename entry already exist? */
125 e = this->db->query(this->db,
126 "SELECT id FROM files WHERE name = ? AND dir = ?",
127 DB_TEXT, filename, DB_INT, id, DB_INT);
128 if (!e)
129 {
130 return FAILED;
131 }
132 if (!e->enumerate(e, &fid))
133 {
134 /* create filename entry */
135 if (this->db->execute(this->db, &fid,
136 "INSERT INTO files (name, dir) VALUES (?, ?)",
137 DB_TEXT, filename, DB_INT, id) != 1)
138 {
139 DBG1(DBG_PTS, "could not insert filename into database");
140 status = FAILED;
141 }
142 }
143 e->destroy(e);
144 }
145 else
146 {
147 fid = id;
148
149 /* verify filename */
150 e = this->db->query(this->db,
151 "SELECT name FROM files WHERE id = ?", DB_INT, fid, DB_TEXT);
152 if (!e)
153 {
154 return FAILED;
155 }
156 if (!e->enumerate(e, &name) || !streq(name, filename))
157 {
158 DBG1(DBG_PTS, "filename of reference measurement does not match");
159 status = FAILED;
160 }
161 e->destroy(e);
162 }
163
164 if (status != SUCCESS)
165 {
166 return status;
167 }
168
169 /* does hash measurement value already exist? */
170 e = this->db->query(this->db,
171 "SELECT fh.id, fh.hash FROM file_hashes AS fh "
172 "WHERE fh.product = ? AND fh.algo = ? AND fh.file = ?",
173 DB_INT, pid, DB_INT, algo, DB_INT, fid, DB_INT, DB_BLOB);
174 if (!e)
175 {
176 return FAILED;
177 }
178 if (e->enumerate(e, &hash_id, &hash_value))
179 {
180 if (!chunk_equals(measurement, hash_value))
181 {
182 /* update hash measurement value */
183 if (this->db->execute(this->db, &hash_id,
184 "UPDATE file_hashes SET hash = ? WHERE id = ?",
185 DB_BLOB, measurement, DB_INT, hash_id) != 1)
186 {
187 status = FAILED;
188 }
189 }
190 }
191 else
192 {
193 /* insert hash measurement value */
194 if (this->db->execute(this->db, &hash_id,
195 "INSERT INTO file_hashes (file, product, algo, hash) "
196 "VALUES (?, ?, ?, ?)", DB_INT, fid, DB_INT, pid,
197 DB_INT, algo, DB_BLOB, measurement) != 1)
198 {
199 status = FAILED;
200 }
201 }
202 e->destroy(e);
203
204 return status;
205 }
206
207 METHOD(pts_database_t, create_file_meas_enumerator, enumerator_t*,
208 private_pts_database_t *this, int pid, pts_meas_algorithms_t algo,
209 char *filename)
210 {
211 enumerator_t *e;
212 char *dir, *file;
213
214 if (strlen(filename) < 1)
215 {
216 return NULL;
217 }
218
219 /* separate filename into directory and basename components */
220 dir = path_dirname(filename);
221 file = path_basename(filename);
222
223 if (*dir == '.')
224 { /* relative pathname */
225 e = this->db->query(this->db,
226 "SELECT fh.hash FROM file_hashes AS fh "
227 "JOIN files AS f ON f.id = fh.file "
228 "WHERE fh.product = ? AND f.name = ? AND fh.algo = ?",
229 DB_INT, pid, DB_TEXT, file, DB_INT, algo, DB_BLOB);
230 }
231 else
232 { /* absolute pathname */
233 int did;
234
235 /* find directory entry first */
236 e = this->db->query(this->db,
237 "SELECT id FROM directories WHERE path = ?",
238 DB_TEXT, dir, DB_INT);
239
240 if (!e || !e->enumerate(e, &did))
241 {
242 goto err;
243 }
244 e->destroy(e);
245
246 e = this->db->query(this->db,
247 "SELECT fh.hash FROM file_hashes AS fh "
248 "JOIN files AS f ON f.id = fh.file "
249 "WHERE fh.product = ? AND f.dir = ? AND f.name = ? AND fh.algo = ?",
250 DB_INT, pid, DB_INT, did, DB_TEXT, file, DB_INT, algo, DB_BLOB);
251 }
252
253 err:
254 free(file);
255 free(dir);
256
257 return e;
258 }
259
260 METHOD(pts_database_t, check_comp_measurement, status_t,
261 private_pts_database_t *this, chunk_t measurement, int cid, int aik_id,
262 int seq_no, int pcr, pts_meas_algorithms_t algo)
263 {
264 enumerator_t *e;
265 chunk_t hash;
266 status_t status = NOT_FOUND;
267
268 e = this->db->query(this->db,
269 "SELECT hash FROM component_hashes "
270 "WHERE component = ? AND key = ? "
271 "AND seq_no = ? AND pcr = ? AND algo = ? ",
272 DB_INT, cid, DB_INT, aik_id, DB_INT, seq_no,
273 DB_INT, pcr, DB_INT, algo, DB_BLOB);
274 if (!e)
275 {
276 DBG1(DBG_PTS, "no database query enumerator returned");
277 return FAILED;
278 }
279
280 while (e->enumerate(e, &hash))
281 {
282 if (chunk_equals(hash, measurement))
283 {
284 status = SUCCESS;
285 break;
286 }
287 else
288 {
289 DBG1(DBG_PTS, "PCR %2d no matching component measurement #%d "
290 "found in database", pcr, seq_no);
291 DBG1(DBG_PTS, " expected: %#B", &hash);
292 DBG1(DBG_PTS, " received: %#B", &measurement);
293 status = VERIFY_ERROR;
294 break;
295 }
296 }
297 e->destroy(e);
298
299 if (status == NOT_FOUND)
300 {
301 DBG1(DBG_PTS, "PCR %2d no measurement #%d "
302 "found in database", pcr, seq_no);
303 }
304
305 return status;
306 }
307
308 METHOD(pts_database_t, insert_comp_measurement, status_t,
309 private_pts_database_t *this, chunk_t measurement, int cid, int aik_id,
310 int seq_no, int pcr, pts_meas_algorithms_t algo)
311 {
312 int id;
313
314 if (this->db->execute(this->db, &id,
315 "INSERT INTO component_hashes "
316 "(component, key, seq_no, pcr, algo, hash) "
317 "VALUES (?, ?, ?, ?, ?, ?)",
318 DB_INT, cid, DB_INT, aik_id, DB_INT, seq_no, DB_INT, pcr,
319 DB_INT, algo, DB_BLOB, measurement) == 1)
320 {
321 return SUCCESS;
322 }
323
324 DBG1(DBG_PTS, "could not insert component measurement into database");
325 return FAILED;
326 }
327
328 METHOD(pts_database_t, delete_comp_measurements, int,
329 private_pts_database_t *this, int cid, int aik_id)
330 {
331 return this->db->execute(this->db, NULL,
332 "DELETE FROM component_hashes "
333 "WHERE component = ? AND key = ?",
334 DB_INT, cid, DB_INT, aik_id);
335 }
336
337 METHOD(pts_database_t, get_comp_measurement_count, status_t,
338 private_pts_database_t *this, pts_comp_func_name_t *comp_name,
339 int aik_id, pts_meas_algorithms_t algo, int *cid, int *count)
340 {
341 enumerator_t *e;
342 status_t status = SUCCESS;
343
344 /* Initialize count */
345 *count = 0;
346
347 /* Get the primary key of the Component Functional Name */
348 e = this->db->query(this->db,
349 "SELECT id FROM components "
350 " WHERE vendor_id = ? AND name = ? AND qualifier = ?",
351 DB_INT, comp_name->get_vendor_id(comp_name),
352 DB_INT, comp_name->get_name(comp_name),
353 DB_INT, comp_name->get_qualifier(comp_name),
354 DB_INT);
355 if (!e)
356 {
357 DBG1(DBG_PTS, "no database query enumerator returned");
358 return FAILED;
359 }
360 if (!e->enumerate(e, cid))
361 {
362 DBG1(DBG_PTS, "component functional name not found in database");
363 e->destroy(e);
364 return FAILED;
365 }
366 e->destroy(e);
367
368 /* Get the number of stored measurements for a given AIK and component */
369 e = this->db->query(this->db,
370 "SELECT COUNT(*) FROM component_hashes AS ch "
371 "WHERE component = ? AND key = ? AND algo = ?",
372 DB_INT, *cid, DB_INT, aik_id, DB_INT, algo, DB_INT);
373 if (!e)
374 {
375 DBG1(DBG_PTS, "no database query enumerator returned");
376 return FAILED;
377 }
378 if (!e->enumerate(e, count))
379 {
380 DBG1(DBG_PTS, "no component measurement count returned from database");
381 status = FAILED;
382 }
383 e->destroy(e);
384
385 return status;
386 }
387
388 METHOD(pts_database_t, destroy, void,
389 private_pts_database_t *this)
390 {
391 free(this);
392 }
393
394 /**
395 * See header
396 */
397 pts_database_t *pts_database_create(imv_database_t *imv_db)
398 {
399 private_pts_database_t *this;
400
401 if (!imv_db)
402 {
403 return NULL;
404 }
405
406 INIT(this,
407 .public = {
408 .get_pathname = _get_pathname,
409 .create_file_hash_enumerator = _create_file_hash_enumerator,
410 .add_file_measurement = _add_file_measurement,
411 .create_file_meas_enumerator = _create_file_meas_enumerator,
412 .check_comp_measurement = _check_comp_measurement,
413 .insert_comp_measurement = _insert_comp_measurement,
414 .delete_comp_measurements = _delete_comp_measurements,
415 .get_comp_measurement_count = _get_comp_measurement_count,
416 .destroy = _destroy,
417 },
418 .db = imv_db->get_database(imv_db),
419 );
420
421 return &this->public;
422 }
423