various PTS fixes
[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 #include "pts_database.h"
17
18 #include <debug.h>
19 #include <crypto/hashers/hasher.h>
20
21
22 typedef struct private_pts_database_t private_pts_database_t;
23
24 /**
25 * Private data of a pts_database_t object.
26 *
27 */
28 struct private_pts_database_t {
29
30 /**
31 * Public pts_database_t interface.
32 */
33 pts_database_t public;
34
35 /**
36 * database instance
37 */
38 database_t *db;
39
40 };
41
42 METHOD(pts_database_t, create_file_meas_enumerator, enumerator_t*,
43 private_pts_database_t *this, char *product)
44 {
45 enumerator_t *e;
46
47 /* look for all entries belonging to a product in the files table */
48 e = this->db->query(this->db,
49 "SELECT f.id, f.type, f.path FROM files AS f "
50 "JOIN product_file AS pf ON f.id = pf.file "
51 "JOIN products AS p ON p.id = pf.product "
52 "WHERE p.name = ? AND pf.measurement = 1",
53 DB_TEXT, product, DB_INT, DB_INT, DB_TEXT);
54 return e;
55 }
56
57 METHOD(pts_database_t, create_file_meta_enumerator, enumerator_t*,
58 private_pts_database_t *this, char *product)
59 {
60 enumerator_t *e;
61
62 /* look for all entries belonging to a product in the files table */
63 e = this->db->query(this->db,
64 "SELECT f.type, f.path FROM files AS f "
65 "JOIN product_file AS pf ON f.id = pf.file "
66 "JOIN products AS p ON p.id = pf.product "
67 "WHERE p.name = ? AND pf.metadata = 1",
68 DB_TEXT, product, DB_INT, DB_TEXT);
69 return e;
70 }
71
72 METHOD(pts_database_t, create_file_hash_enumerator, enumerator_t*,
73 private_pts_database_t *this, char *product, pts_meas_algorithms_t algo,
74 int id, bool is_dir)
75 {
76 enumerator_t *e;
77
78 if (is_dir)
79 {
80 e = this->db->query(this->db,
81 "SELECT f.path, fh.hash FROM file_hashes AS fh "
82 "JOIN files AS f ON fh.file = f.id "
83 "JOIN products AS p ON fh.product = p.id "
84 "WHERE p.name = ? AND fh.directory = ? AND fh.algo = ? "
85 "ORDER BY f.path",
86 DB_TEXT, product, DB_INT, id, DB_INT, algo, DB_TEXT, DB_BLOB);
87 }
88 else
89 {
90 e = this->db->query(this->db,
91 "SELECT f.path, fh.hash FROM file_hashes AS fh "
92 "JOIN files AS f ON fh.file = f.id "
93 "JOIN products AS p ON fh.product = p.id "
94 "WHERE p.name = ? AND fh.file = ? AND fh.algo = ?",
95 DB_TEXT, product, DB_INT, id, DB_INT, algo, DB_TEXT, DB_BLOB);
96 }
97 return e;
98 }
99
100 METHOD(pts_database_t, check_aik_keyid, status_t,
101 private_pts_database_t *this, chunk_t keyid, int *kid)
102 {
103 enumerator_t *e;
104
105 /* If the AIK is registered get the primary key */
106 e = this->db->query(this->db,
107 "SELECT id FROM keys WHERE keyid = ?", DB_BLOB, keyid, DB_INT);
108 if (!e)
109 {
110 DBG1(DBG_PTS, "no database query enumerator returned");
111 return FAILED;
112 }
113 if (!e->enumerate(e, kid))
114 {
115 DBG1(DBG_PTS, "AIK %#B is not registered in database", &keyid);
116 e->destroy(e);
117 return FAILED;
118 }
119 e->destroy(e);
120
121 return SUCCESS;
122 }
123
124 METHOD(pts_database_t, check_file_measurement, status_t,
125 private_pts_database_t *this, char *product, pts_meas_algorithms_t algo,
126 chunk_t measurement, char *filename)
127 {
128 enumerator_t *e;
129 chunk_t hash;
130 status_t status;
131
132 e = this->db->query(this->db,
133 "SELECT fh.hash FROM file_hashes AS fh "
134 "JOIN files AS f ON f.id = fh.file "
135 "JOIN products AS p ON p.id = fh.product "
136 "WHERE p.name = ? AND f.path = ? AND fh.algo = ?",
137 DB_TEXT, product, DB_TEXT, filename, DB_INT, algo, DB_BLOB);
138 if (!e)
139 {
140 return FAILED;
141 }
142 if (e->enumerate(e, &hash))
143 {
144 status = chunk_equals(measurement, hash) ?
145 SUCCESS : VERIFY_ERROR;
146 }
147 else
148 {
149 status = NOT_FOUND;
150 }
151 e->destroy(e);
152
153 return status;
154 }
155
156 METHOD(pts_database_t, create_comp_evid_enumerator, enumerator_t*,
157 private_pts_database_t *this, int kid)
158 {
159 enumerator_t *e;
160
161 /* look for all entries belonging to an AIK in the components table */
162 e = this->db->query(this->db,
163 "SELECT c.vendor_id, c.name, c.qualifier, kc.depth "
164 "FROM components AS c "
165 "JOIN key_component AS kc ON c.id = kc.component "
166 "WHERE kc.key = ? ORDER BY kc.seq_no",
167 DB_INT, kid, DB_INT, DB_INT, DB_INT, DB_INT);
168 return e;
169 }
170
171 METHOD(pts_database_t, check_comp_measurement, status_t,
172 private_pts_database_t *this, chunk_t measurement, int cid, int kid,
173 int seq_no, int pcr, pts_meas_algorithms_t algo)
174 {
175 enumerator_t *e;
176 chunk_t hash;
177 status_t status = NOT_FOUND;
178
179 e = this->db->query(this->db,
180 "SELECT hash FROM component_hashes "
181 "WHERE component = ? AND key = ? "
182 "AND seq_no = ? AND pcr = ? AND algo = ? ",
183 DB_INT, cid, DB_INT, kid, DB_INT, seq_no,
184 DB_INT, pcr, DB_INT, algo, DB_BLOB);
185 if (!e)
186 {
187 DBG1(DBG_PTS, "no database query enumerator returned");
188 return FAILED;
189 }
190
191 while (e->enumerate(e, &hash))
192 {
193 if (chunk_equals(hash, measurement))
194 {
195 status = SUCCESS;
196 break;
197 }
198 else
199 {
200 DBG1(DBG_PTS, "PCR %2d no matching component measurement #%d "
201 "found in database", pcr, seq_no);
202 DBG1(DBG_PTS, " expected: %#B", &hash);
203 DBG1(DBG_PTS, " received: %#B", &measurement);
204 status = VERIFY_ERROR;
205 break;
206 }
207 }
208 e->destroy(e);
209
210 if (status == NOT_FOUND)
211 {
212 DBG1(DBG_PTS, "PCR %2d no measurement #%d "
213 "found in database", pcr, seq_no);
214 }
215
216 return status;
217 }
218
219 METHOD(pts_database_t, insert_comp_measurement, status_t,
220 private_pts_database_t *this, chunk_t measurement, int cid, int kid,
221 int seq_no, int pcr, pts_meas_algorithms_t algo)
222 {
223 int id;
224
225 if (this->db->execute(this->db, &id,
226 "INSERT INTO component_hashes "
227 "(component, key, seq_no, pcr, algo, hash) "
228 "VALUES (?, ?, ?, ?, ?, ?)",
229 DB_INT, cid, DB_INT, kid, DB_INT, seq_no, DB_INT, pcr,
230 DB_INT, algo, DB_BLOB, measurement) == 1)
231 {
232 return SUCCESS;
233 }
234
235 DBG1(DBG_PTS, "could not insert component measurement into database");
236 return FAILED;
237 }
238
239 METHOD(pts_database_t, delete_comp_measurements, int,
240 private_pts_database_t *this, int cid, int kid)
241 {
242 return this->db->execute(this->db, NULL,
243 "DELETE FROM component_hashes "
244 "WHERE component = ? AND key = ?",
245 DB_INT, cid, DB_INT, kid);
246 }
247
248 METHOD(pts_database_t, get_comp_measurement_count, status_t,
249 private_pts_database_t *this, pts_comp_func_name_t *comp_name,
250 chunk_t keyid, pts_meas_algorithms_t algo, int *cid, int *kid, int *count)
251 {
252 enumerator_t *e;
253 status_t status = SUCCESS;
254
255 /* Initialize count */
256 *count = 0;
257
258 if (_check_aik_keyid(this, keyid, kid) != SUCCESS)
259 {
260 return FAILED;
261 }
262
263 /* Get the primary key of the Component Functional Name */
264 e = this->db->query(this->db,
265 "SELECT id FROM components "
266 " WHERE vendor_id = ? AND name = ? AND qualifier = ?",
267 DB_INT, comp_name->get_vendor_id(comp_name),
268 DB_INT, comp_name->get_name(comp_name),
269 DB_INT, comp_name->get_qualifier(comp_name),
270 DB_INT);
271 if (!e)
272 {
273 DBG1(DBG_PTS, "no database query enumerator returned");
274 return FAILED;
275 }
276 if (!e->enumerate(e, cid))
277 {
278 DBG1(DBG_PTS, "component functional name not found in database");
279 e->destroy(e);
280 return FAILED;
281 }
282 e->destroy(e);
283
284 /* Get the number of stored measurements for a given AIK and component */
285 e = this->db->query(this->db,
286 "SELECT COUNT(*) FROM component_hashes AS ch "
287 "WHERE component = ? AND key = ? AND algo = ?",
288 DB_INT, *cid, DB_INT, *kid, DB_INT, algo, DB_INT);
289 if (!e)
290 {
291 DBG1(DBG_PTS, "no database query enumerator returned");
292 return FAILED;
293 }
294 if (!e->enumerate(e, count))
295 {
296 DBG1(DBG_PTS, "no component measurement count returned from database");
297 status = FAILED;
298 }
299 e->destroy(e);
300
301 return status;
302 }
303
304 METHOD(pts_database_t, destroy, void,
305 private_pts_database_t *this)
306 {
307 this->db->destroy(this->db);
308 free(this);
309 }
310
311 /**
312 * See header
313 */
314 pts_database_t *pts_database_create(char *uri)
315 {
316 private_pts_database_t *this;
317
318 INIT(this,
319 .public = {
320 .create_file_meas_enumerator = _create_file_meas_enumerator,
321 .create_file_meta_enumerator = _create_file_meta_enumerator,
322 .create_comp_evid_enumerator = _create_comp_evid_enumerator,
323 .create_file_hash_enumerator = _create_file_hash_enumerator,
324 .check_aik_keyid = _check_aik_keyid,
325 .check_file_measurement = _check_file_measurement,
326 .check_comp_measurement = _check_comp_measurement,
327 .insert_comp_measurement = _insert_comp_measurement,
328 .delete_comp_measurements = _delete_comp_measurements,
329 .get_comp_measurement_count = _get_comp_measurement_count,
330 .destroy = _destroy,
331 },
332 .db = lib->db->create(lib->db, uri),
333 );
334
335 if (!this->db)
336 {
337 DBG1(DBG_PTS,
338 "failed to connect to PTS file measurement database '%s'", uri);
339 free(this);
340 return NULL;
341 }
342
343 return &this->public;
344 }
345