4eb359ce17eb21a4d375b04bbe3e2df35202820b
[strongswan.git] / src / libpts / pts / pts_file_meas.c
1 /*
2 * Copyright (C) 2011 Sansar Choinyambuu
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_file_meas.h"
17
18 #include <utils/linked_list.h>
19 #include <debug.h>
20
21 #include <sys/stat.h>
22 #include <libgen.h>
23 #include <errno.h>
24
25 typedef struct private_pts_file_meas_t private_pts_file_meas_t;
26
27 /**
28 * Private data of a pts_file_meas_t object.
29 *
30 */
31 struct private_pts_file_meas_t {
32
33 /**
34 * Public pts_file_meas_t interface.
35 */
36 pts_file_meas_t public;
37
38 /**
39 * ID of PTS File Measurement Request
40 */
41 u_int16_t request_id;
42
43 /**
44 * List of File Measurements
45 */
46 linked_list_t *list;
47 };
48
49 typedef struct entry_t entry_t;
50
51 /**
52 * PTS File Measurement entry
53 */
54 struct entry_t {
55 char *filename;
56 chunk_t measurement;
57 };
58
59 /**
60 * Free an entry_t object
61 */
62 static void free_entry(entry_t *entry)
63 {
64 if (entry)
65 {
66 free(entry->filename);
67 free(entry->measurement.ptr);
68 free(entry);
69 }
70 }
71
72 METHOD(pts_file_meas_t, get_request_id, u_int16_t,
73 private_pts_file_meas_t *this)
74 {
75 return this->request_id;
76 }
77
78 METHOD(pts_file_meas_t, get_file_count, int,
79 private_pts_file_meas_t *this)
80 {
81 return this->list->get_count(this->list);
82 }
83
84 METHOD(pts_file_meas_t, add, void,
85 private_pts_file_meas_t *this, char *filename, chunk_t measurement)
86 {
87 entry_t *entry;
88
89 entry = malloc_thing(entry_t);
90 entry->filename = strdup(filename);
91 entry->measurement = chunk_clone(measurement);
92
93 this->list->insert_last(this->list, entry);
94 }
95
96 /**
97 * Enumerate file measurement entries
98 */
99 static bool entry_filter(void *null, entry_t **entry, char **filename,
100 void *i2, chunk_t *measurement)
101 {
102 *filename = (*entry)->filename;
103 *measurement = (*entry)->measurement;
104 return TRUE;
105 }
106
107 METHOD(pts_file_meas_t, create_enumerator, enumerator_t*,
108 private_pts_file_meas_t *this)
109 {
110 return enumerator_create_filter(this->list->create_enumerator(this->list),
111 (void*)entry_filter, NULL, NULL);
112 }
113
114 METHOD(pts_file_meas_t, check, bool,
115 private_pts_file_meas_t *this, pts_database_t *pts_db, char *product,
116 pts_meas_algorithms_t algo)
117 {
118 enumerator_t *enumerator;
119 entry_t *entry;
120 char *status_msg;
121 int count_ok = 0, count_not_found = 0, count_differ = 0;
122 status_t status;
123
124 enumerator = this->list->create_enumerator(this->list);
125 while (enumerator->enumerate(enumerator, &entry))
126 {
127 status = pts_db->check_file_measurement(pts_db, product, algo,
128 entry->measurement, entry->filename);
129 switch (status)
130 {
131 case SUCCESS:
132 status_msg = "ok";
133 count_ok++;
134 break;
135 case NOT_FOUND:
136 status_msg = "not found";
137 count_not_found++;
138 break;
139 case VERIFY_ERROR:
140 status_msg = "differs";
141 count_differ++;
142 break;
143 case FAILED:
144 default:
145 status_msg = "failed";
146 }
147 DBG2(DBG_PTS, " %#B for '%s' - %s", &entry->measurement,
148 entry->filename, status_msg);
149 }
150 enumerator->destroy(enumerator);
151
152 DBG1(DBG_PTS, "%d measurements, %d ok, %d not found, %d differ",
153 this->list->get_count(this->list),
154 count_ok, count_not_found, count_differ);
155 return TRUE;
156 }
157
158 METHOD(pts_file_meas_t, verify, bool,
159 private_pts_file_meas_t *this, enumerator_t *e_hash, bool is_dir)
160 {
161 char *filename;
162 chunk_t measurement;
163 entry_t *entry;
164 enumerator_t *enumerator;
165 bool found, success = TRUE;
166
167 while (e_hash->enumerate(e_hash, &filename, &measurement))
168 {
169 found = FALSE;
170
171 enumerator = this->list->create_enumerator(this->list);
172 while (enumerator->enumerate(enumerator, &entry))
173 {
174 if (!is_dir || streq(filename, entry->filename))
175 {
176 found = TRUE;
177 break;
178 }
179 }
180 enumerator->destroy(enumerator);
181
182 if (!found)
183 {
184 DBG1(DBG_PTS, " no measurement found for '%s'", filename);
185 success = FALSE;
186 continue;
187 }
188 if (chunk_equals(measurement, entry->measurement))
189 {
190 DBG2(DBG_PTS, " %#B for '%s' is ok", &measurement, filename);
191 }
192 else
193 {
194 DBG1(DBG_PTS, " %#B for '%s' is incorrect", &measurement, filename);
195 success = FALSE;
196 }
197 if (!is_dir)
198 {
199 break;
200 }
201 }
202 return success;
203 }
204
205 METHOD(pts_file_meas_t, destroy, void,
206 private_pts_file_meas_t *this)
207 {
208 this->list->destroy_function(this->list, (void *)free_entry);
209 free(this);
210 }
211
212 /**
213 * See header
214 */
215 pts_file_meas_t *pts_file_meas_create(u_int16_t request_id)
216 {
217 private_pts_file_meas_t *this;
218
219 INIT(this,
220 .public = {
221 .get_request_id = _get_request_id,
222 .get_file_count = _get_file_count,
223 .add = _add,
224 .create_enumerator = _create_enumerator,
225 .check = _check,
226 .verify = _verify,
227 .destroy = _destroy,
228 },
229 .request_id = request_id,
230 .list = linked_list_create(),
231 );
232
233 return &this->public;
234 }
235
236 /**
237 * Hash a file with a given absolute pathname
238 */
239 static bool hash_file(hasher_t *hasher, char *pathname, u_char *hash)
240 {
241 u_char buffer[4096];
242 size_t bytes_read;
243 bool success = TRUE;
244 FILE *file;
245
246 file = fopen(pathname, "rb");
247 if (!file)
248 {
249 DBG1(DBG_PTS," file '%s' can not be opened, %s", pathname,
250 strerror(errno));
251 return FALSE;
252 }
253 while (TRUE)
254 {
255 bytes_read = fread(buffer, 1, sizeof(buffer), file);
256 if (bytes_read > 0)
257 {
258 if (!hasher->get_hash(hasher, chunk_create(buffer, bytes_read), NULL))
259 {
260 DBG1(DBG_PTS, " hasher increment error");
261 success = FALSE;
262 break;
263 }
264 }
265 else
266 {
267 if (!hasher->get_hash(hasher, chunk_empty, hash))
268 {
269 DBG1(DBG_PTS, " hasher finalize error");
270 success = FALSE;
271 }
272 break;
273 }
274 }
275 fclose(file);
276
277 return success;
278 }
279
280 /**
281 * See header
282 */
283 pts_file_meas_t *pts_file_meas_create_from_path(u_int16_t request_id,
284 char *pathname, bool is_dir, bool use_rel_name,
285 pts_meas_algorithms_t alg)
286 {
287 private_pts_file_meas_t *this;
288 hash_algorithm_t hash_alg;
289 hasher_t *hasher;
290 u_char hash[HASH_SIZE_SHA384];
291 chunk_t measurement;
292 char* filename;
293 bool success = TRUE;
294
295 /* Create a hasher and a hash measurement buffer */
296 hash_alg = pts_meas_algo_to_hash(alg);
297 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
298 if (!hasher)
299 {
300 DBG1(DBG_PTS, "hasher %N not available", hash_algorithm_names, hash_alg);
301 return NULL;
302 }
303 measurement = chunk_create(hash, hasher->get_hash_size(hasher));
304
305 INIT(this,
306 .public = {
307 .get_request_id = _get_request_id,
308 .get_file_count = _get_file_count,
309 .add = _add,
310 .create_enumerator = _create_enumerator,
311 .check = _check,
312 .verify = _verify,
313 .destroy = _destroy,
314 },
315 .request_id = request_id,
316 .list = linked_list_create(),
317 );
318
319 if (is_dir)
320 {
321 enumerator_t *enumerator;
322 char *rel_name, *abs_name;
323 struct stat st;
324
325 enumerator = enumerator_create_directory(pathname);
326 if (!enumerator)
327 {
328 DBG1(DBG_PTS, " directory '%s' can not be opened, %s", pathname,
329 strerror(errno));
330 success = FALSE;
331 goto end;
332 }
333 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
334 {
335 /* measure regular files only */
336 if (S_ISREG(st.st_mode) && *rel_name != '.')
337 {
338 if (!hash_file(hasher, abs_name, hash))
339 {
340 success = FALSE;
341 break;
342 }
343 filename = use_rel_name ? rel_name : abs_name;
344 DBG2(DBG_PTS, " %#B for '%s'", &measurement, filename);
345 add(this, filename, measurement);
346 }
347 }
348 enumerator->destroy(enumerator);
349 }
350 else
351 {
352 if (!hash_file(hasher, pathname, hash))
353 {
354 success = FALSE;
355 goto end;
356 }
357 filename = use_rel_name ? basename(pathname) : pathname;
358 DBG2(DBG_PTS, " %#B for '%s'", &measurement, filename);
359 add(this, filename, measurement);
360 }
361
362 end:
363 hasher->destroy(hasher);
364 if (success)
365 {
366 return &this->public;
367 }
368 else
369 {
370 destroy(this);
371 return NULL;
372 }
373 }