ipsec attest now can measure all files in a directory
[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, insert, bool,
115 private_pts_file_meas_t *this, pts_database_t *pts_db, char *product)
116 {
117 enumerator_t *enumerator;
118 entry_t *entry;
119
120 enumerator = this->list->create_enumerator(this->list);
121 while (enumerator->enumerate(enumerator, &entry))
122 {
123 DBG2(DBG_PTS, " %#B for '%s'", &entry->measurement, entry->filename);
124 }
125 enumerator->destroy(enumerator);
126
127 return TRUE;
128 }
129
130 METHOD(pts_file_meas_t, verify, bool,
131 private_pts_file_meas_t *this, enumerator_t *e_hash, bool is_dir)
132 {
133 char *filename;
134 chunk_t measurement;
135 entry_t *entry;
136 enumerator_t *enumerator;
137 bool found, success = TRUE;
138
139 while (e_hash->enumerate(e_hash, &filename, &measurement))
140 {
141 found = FALSE;
142
143 enumerator = this->list->create_enumerator(this->list);
144 while (enumerator->enumerate(enumerator, &entry))
145 {
146 if (!is_dir || streq(filename, entry->filename))
147 {
148 found = TRUE;
149 break;
150 }
151 }
152 enumerator->destroy(enumerator);
153
154 if (!found)
155 {
156 DBG1(DBG_PTS, " no measurement found for '%s'", filename);
157 success = FALSE;
158 continue;
159 }
160 if (chunk_equals(measurement, entry->measurement))
161 {
162 DBG2(DBG_PTS, " %#B for '%s' is ok", &measurement, filename);
163 }
164 else
165 {
166 DBG1(DBG_PTS, " %#B for '%s' is incorrect", &measurement, filename);
167 success = FALSE;
168 }
169 if (!is_dir)
170 {
171 break;
172 }
173 }
174 return success;
175 }
176
177 METHOD(pts_file_meas_t, destroy, void,
178 private_pts_file_meas_t *this)
179 {
180 this->list->destroy_function(this->list, (void *)free_entry);
181 free(this);
182 }
183
184 /**
185 * See header
186 */
187 pts_file_meas_t *pts_file_meas_create(u_int16_t request_id)
188 {
189 private_pts_file_meas_t *this;
190
191 INIT(this,
192 .public = {
193 .get_request_id = _get_request_id,
194 .get_file_count = _get_file_count,
195 .add = _add,
196 .create_enumerator = _create_enumerator,
197 .insert = _insert,
198 .verify = _verify,
199 .destroy = _destroy,
200 },
201 .request_id = request_id,
202 .list = linked_list_create(),
203 );
204
205 return &this->public;
206 }
207
208 /**
209 * Hash a file with a given absolute pathname
210 */
211 static bool hash_file(hasher_t *hasher, char *pathname, u_char *hash)
212 {
213 u_char buffer[4096];
214 size_t bytes_read;
215 FILE *file;
216
217 file = fopen(pathname, "rb");
218 if (!file)
219 {
220 DBG1(DBG_PTS," file '%s' can not be opened, %s", pathname,
221 strerror(errno));
222 return FALSE;
223 }
224 while (TRUE)
225 {
226 bytes_read = fread(buffer, 1, sizeof(buffer), file);
227 if (bytes_read > 0)
228 {
229 hasher->get_hash(hasher, chunk_create(buffer, bytes_read), NULL);
230 }
231 else
232 {
233 hasher->get_hash(hasher, chunk_empty, hash);
234 break;
235 }
236 }
237 fclose(file);
238
239 return TRUE;
240 }
241
242 /**
243 * See header
244 */
245 pts_file_meas_t *pts_file_meas_create_from_path(u_int16_t request_id,
246 char *pathname, bool is_dir, bool use_rel_name,
247 pts_meas_algorithms_t alg)
248 {
249 private_pts_file_meas_t *this;
250 hash_algorithm_t hash_alg;
251 hasher_t *hasher;
252 u_char hash[HASH_SIZE_SHA384];
253 chunk_t measurement;
254 char* filename;
255 bool success = TRUE;
256
257 /* Create a hasher and a hash measurement buffer */
258 hash_alg = pts_meas_algo_to_hash(alg);
259 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
260 if (!hasher)
261 {
262 DBG1(DBG_PTS, "hasher %N not available", hash_algorithm_names, hash_alg);
263 return NULL;
264 }
265 measurement = chunk_create(hash, hasher->get_hash_size(hasher));
266
267 INIT(this,
268 .public = {
269 .get_request_id = _get_request_id,
270 .get_file_count = _get_file_count,
271 .add = _add,
272 .create_enumerator = _create_enumerator,
273 .insert = _insert,
274 .verify = _verify,
275 .destroy = _destroy,
276 },
277 .request_id = request_id,
278 .list = linked_list_create(),
279 );
280
281 if (is_dir)
282 {
283 enumerator_t *enumerator;
284 char *rel_name, *abs_name;
285 struct stat st;
286
287 enumerator = enumerator_create_directory(pathname);
288 if (!enumerator)
289 {
290 DBG1(DBG_PTS, " directory '%s' can not be opened, %s", pathname,
291 strerror(errno));
292 success = FALSE;
293 goto end;
294 }
295 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
296 {
297 /* measure regular files only */
298 if (S_ISREG(st.st_mode) && *rel_name != '.')
299 {
300 if (!hash_file(hasher, abs_name, hash))
301 {
302 success = FALSE;
303 break;
304 }
305 filename = use_rel_name ? rel_name : abs_name;
306 DBG2(DBG_PTS, " %#B for '%s'", &measurement, filename);
307 add(this, filename, measurement);
308 }
309 }
310 enumerator->destroy(enumerator);
311 }
312 else
313 {
314 if (!hash_file(hasher, pathname, hash))
315 {
316 success = FALSE;
317 goto end;
318 }
319 filename = use_rel_name ? basename(pathname) : pathname;
320 DBG2(DBG_PTS, " %#B for '%s'", &measurement, filename);
321 add(this, filename, measurement);
322 }
323
324 end:
325 hasher->destroy(hasher);
326 if (success)
327 {
328 return &this->public;
329 }
330 else
331 {
332 destroy(this);
333 return NULL;
334 }
335 }