Request missing SWID tags in a directed PA-TNC message
[strongswan.git] / src / libimcv / 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_const(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 int fid, fid_last = 0;
188 char *filename;
189 chunk_t measurement;
190 entry_t *entry;
191 enumerator_t *enumerator = NULL;
192 bool found = FALSE, match = FALSE, success = TRUE;
193
194 while (e_hash->enumerate(e_hash, &fid, &filename, &measurement))
195 {
196 if (fid != fid_last)
197 {
198 if (found && !match)
199 {
200 /* no matching hash value found for last filename */
201 success = FALSE;
202 DBG1(DBG_PTS, " %#B for '%s' is incorrect",
203 &entry->measurement, entry->filename);
204 enumerator->destroy(enumerator);
205 }
206
207 /* get a new filename from the database */
208 found = FALSE;
209 match = FALSE;
210 fid_last = fid;
211
212 /**
213 * check if we find an entry for this filename
214 * in the PTS measurement list
215 */
216 enumerator = this->list->create_enumerator(this->list);
217 while (enumerator->enumerate(enumerator, &entry))
218 {
219 if (!is_dir || streq(filename, entry->filename))
220 {
221 found = TRUE;
222 break;
223 }
224 }
225
226 /* no PTS measurement returned for this filename */
227 if (!found)
228 {
229 success = FALSE;
230 DBG1(DBG_PTS, " no measurement found for '%s'", filename);
231 enumerator->destroy(enumerator);
232 }
233 }
234
235 if (found && !match)
236 {
237 if (chunk_equals_const(measurement, entry->measurement))
238 {
239 match = TRUE;
240 DBG2(DBG_PTS, " %#B for '%s' is ok",
241 &entry->measurement, entry->filename);
242 enumerator->destroy(enumerator);
243 }
244 }
245 }
246
247 if (found && !match)
248 {
249 /* no matching hash value found for the very last filename */
250 success = FALSE;
251 DBG1(DBG_PTS, " %#B for '%s' is incorrect",
252 &entry->measurement, entry->filename);
253 enumerator->destroy(enumerator);
254 }
255
256 return success;
257 }
258
259 METHOD(pts_file_meas_t, destroy, void,
260 private_pts_file_meas_t *this)
261 {
262 this->list->destroy_function(this->list, (void *)free_entry);
263 free(this);
264 }
265
266 /**
267 * See header
268 */
269 pts_file_meas_t *pts_file_meas_create(u_int16_t request_id)
270 {
271 private_pts_file_meas_t *this;
272
273 INIT(this,
274 .public = {
275 .get_request_id = _get_request_id,
276 .get_file_count = _get_file_count,
277 .add = _add,
278 .create_enumerator = _create_enumerator,
279 .check = _check,
280 .verify = _verify,
281 .destroy = _destroy,
282 },
283 .request_id = request_id,
284 .list = linked_list_create(),
285 );
286
287 return &this->public;
288 }
289
290 /**
291 * Hash a file with a given absolute pathname
292 */
293 static bool hash_file(hasher_t *hasher, char *pathname, u_char *hash)
294 {
295 u_char buffer[4096];
296 size_t bytes_read;
297 bool success = TRUE;
298 FILE *file;
299
300 file = fopen(pathname, "rb");
301 if (!file)
302 {
303 DBG1(DBG_PTS," file '%s' can not be opened, %s", pathname,
304 strerror(errno));
305 return FALSE;
306 }
307 while (TRUE)
308 {
309 bytes_read = fread(buffer, 1, sizeof(buffer), file);
310 if (bytes_read > 0)
311 {
312 if (!hasher->get_hash(hasher, chunk_create(buffer, bytes_read), NULL))
313 {
314 DBG1(DBG_PTS, " hasher increment error");
315 success = FALSE;
316 break;
317 }
318 }
319 else
320 {
321 if (!hasher->get_hash(hasher, chunk_empty, hash))
322 {
323 DBG1(DBG_PTS, " hasher finalize error");
324 success = FALSE;
325 }
326 break;
327 }
328 }
329 fclose(file);
330
331 return success;
332 }
333
334 /**
335 * See header
336 */
337 pts_file_meas_t *pts_file_meas_create_from_path(u_int16_t request_id,
338 char *pathname, bool is_dir, bool use_rel_name,
339 pts_meas_algorithms_t alg)
340 {
341 private_pts_file_meas_t *this;
342 hash_algorithm_t hash_alg;
343 hasher_t *hasher;
344 u_char hash[HASH_SIZE_SHA384];
345 chunk_t measurement;
346 char* filename;
347 bool success = TRUE;
348
349 /* Create a hasher and a hash measurement buffer */
350 hash_alg = pts_meas_algo_to_hash(alg);
351 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
352 if (!hasher)
353 {
354 DBG1(DBG_PTS, "hasher %N not available", hash_algorithm_names, hash_alg);
355 return NULL;
356 }
357 measurement = chunk_create(hash, hasher->get_hash_size(hasher));
358 this = (private_pts_file_meas_t*)pts_file_meas_create(request_id);
359
360 if (is_dir)
361 {
362 enumerator_t *enumerator;
363 char *rel_name, *abs_name;
364 struct stat st;
365
366 enumerator = enumerator_create_directory(pathname);
367 if (!enumerator)
368 {
369 DBG1(DBG_PTS, " directory '%s' can not be opened, %s", pathname,
370 strerror(errno));
371 success = FALSE;
372 goto end;
373 }
374 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
375 {
376 /* measure regular files only */
377 if (S_ISREG(st.st_mode) && *rel_name != '.')
378 {
379 if (!hash_file(hasher, abs_name, hash))
380 {
381 continue;
382 }
383 filename = use_rel_name ? rel_name : abs_name;
384 DBG2(DBG_PTS, " %#B for '%s'", &measurement, filename);
385 add(this, filename, measurement);
386 }
387 }
388 enumerator->destroy(enumerator);
389 }
390 else
391 {
392 if (!hash_file(hasher, pathname, hash))
393 {
394 success = FALSE;
395 goto end;
396 }
397 filename = use_rel_name ? path_basename(pathname) : strdup(pathname);
398 DBG2(DBG_PTS, " %#B for '%s'", &measurement, filename);
399 add(this, filename, measurement);
400 free(filename);
401 }
402
403 end:
404 hasher->destroy(hasher);
405 if (success)
406 {
407 return &this->public;
408 }
409 else
410 {
411 destroy(this);
412 return NULL;
413 }
414 }