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