added file and segment lengths to checksum.c
[strongswan.git] / src / libstrongswan / integrity_checker.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * 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 #define _GNU_SOURCE
17
18 #include "integrity_checker.h"
19
20 #include <dlfcn.h>
21 #include <link.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28
29 #include <debug.h>
30 #include <library.h>
31
32 typedef struct private_integrity_checker_t private_integrity_checker_t;
33
34 /**
35 * Private data of an integrity_checker_t object.
36 */
37 struct private_integrity_checker_t {
38
39 /**
40 * Public integrity_checker_t interface.
41 */
42 integrity_checker_t public;
43
44 /**
45 * dlopen handle to checksum library
46 */
47 void *handle;
48
49 /**
50 * checksum array
51 */
52 integrity_checksum_t *checksums;
53
54 /**
55 * number of checksums in array
56 */
57 int checksum_count;
58 };
59
60 /**
61 * Implementation of integrity_checker_t.build_file
62 */
63 static u_int32_t build_file(private_integrity_checker_t *this, char *file,
64 size_t *len)
65 {
66 u_int32_t checksum;
67 chunk_t contents;
68 struct stat sb;
69 void *addr;
70 int fd;
71
72 fd = open(file, O_RDONLY);
73 if (fd == -1)
74 {
75 DBG1(" opening '%s' failed: %s", file, strerror(errno));
76 return 0;
77 }
78
79 if (fstat(fd, &sb) == -1)
80 {
81 DBG1(" getting file size of '%s' failed: %s", file, strerror(errno));
82 close(fd);
83 return 0;
84 }
85
86 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
87 if (addr == MAP_FAILED)
88 {
89 DBG1(" mapping '%s' failed: %s", file, strerror(errno));
90 close(fd);
91 return 0;
92 }
93
94 *len = sb.st_size;
95 contents = chunk_create(addr, sb.st_size);
96 checksum = chunk_hash(contents);
97
98 munmap(addr, sb.st_size);
99 close(fd);
100
101 return checksum;
102 }
103
104 /**
105 * dl_iterate_phdr callback function
106 */
107 static int callback(struct dl_phdr_info *dlpi, size_t size, Dl_info *dli)
108 {
109 /* We are looking for the dlpi_addr matching the address of our dladdr().
110 * dl_iterate_phdr() returns such an address for other (unknown) objects
111 * in very rare cases (e.g. in a chrooted gentoo, but only if
112 * the checksum_builder is invoked by 'make'). As a workaround, we filter
113 * objects by dlpi_name; valid objects have a library name.
114 */
115 if (dli->dli_fbase == (void*)dlpi->dlpi_addr &&
116 dlpi->dlpi_name && *dlpi->dlpi_name)
117 {
118 int i;
119
120 for (i = 0; i < dlpi->dlpi_phnum; i++)
121 {
122 const ElfW(Phdr) *sgmt = &dlpi->dlpi_phdr[i];
123
124 /* we are interested in the executable LOAD segment */
125 if (sgmt->p_type == PT_LOAD && (sgmt->p_flags & PF_X))
126 {
127 /* safe begin of segment in dli_fbase */
128 dli->dli_fbase = (void*)sgmt->p_vaddr + dlpi->dlpi_addr;
129 /* safe end of segment in dli_saddr */
130 dli->dli_saddr = dli->dli_fbase + sgmt->p_memsz;
131 return 1;
132 }
133 }
134 }
135 return 0;
136 }
137
138 /**
139 * Implementation of integrity_checker_t.build_segment
140 */
141 static u_int32_t build_segment(private_integrity_checker_t *this, void *sym,
142 size_t *len)
143 {
144 chunk_t segment;
145 Dl_info dli;
146
147 if (dladdr(sym, &dli) == 0)
148 {
149 DBG1(" unable to locate symbol: %s", dlerror());
150 return 0;
151 }
152 /* we reuse the Dl_info struct as in/out parameter */
153 if (!dl_iterate_phdr((void*)callback, &dli))
154 {
155 DBG1(" executable section not found");
156 return 0;
157 }
158
159 segment = chunk_create(dli.dli_fbase, dli.dli_saddr - dli.dli_fbase);
160 *len = segment.len;
161 return chunk_hash(segment);
162 }
163
164 /**
165 * Find a checksum by its name
166 */
167 static integrity_checksum_t *find_checksum(private_integrity_checker_t *this,
168 char *name)
169 {
170 int i;
171
172 for (i = 0; i < this->checksum_count; i++)
173 {
174 if (streq(this->checksums[i].name, name))
175 {
176 return &this->checksums[i];
177 }
178 }
179 return NULL;
180 }
181
182 /**
183 * Implementation of integrity_checker_t.check_file
184 */
185 static bool check_file(private_integrity_checker_t *this,
186 char *name, char *file)
187 {
188 integrity_checksum_t *cs;
189 u_int32_t sum;
190 size_t len = 0;
191
192 cs = find_checksum(this, name);
193 if (!cs)
194 {
195 DBG1(" '%s' file checksum not found", name);
196 return FALSE;
197 }
198 sum = build_file(this, file, &len);
199 if (!sum)
200 {
201 return FALSE;
202 }
203 if (cs->file_len != len)
204 {
205 DBG1(" invalid '%s' file size: %u bytes, expected %u bytes",
206 name, len, cs->file_len);
207 return FALSE;
208 }
209 if (cs->file != sum)
210 {
211 DBG1(" invalid '%s' file checksum: %08x, expected %08x",
212 name, sum, cs->file);
213 return FALSE;
214 }
215 DBG2(" valid '%s' file checksum: %08x", name, sum);
216 return TRUE;
217 }
218
219 /**
220 * Implementation of integrity_checker_t.check_segment
221 */
222 static bool check_segment(private_integrity_checker_t *this,
223 char *name, void *sym)
224 {
225 integrity_checksum_t *cs;
226 u_int32_t sum;
227 size_t len = 0;
228
229 cs = find_checksum(this, name);
230 if (!cs)
231 {
232 DBG1(" '%s' segment checksum not found", name);
233 return FALSE;
234 }
235 sum = build_segment(this, sym, &len);
236 if (!sum)
237 {
238 return FALSE;
239 }
240 if (cs->segment_len != len)
241 {
242 DBG1(" invalid '%s' segment size: %u bytes, expected %u bytes",
243 name, len, cs->segment_len);
244 return FALSE;
245 }
246 if (cs->segment != sum)
247 {
248 DBG1(" invalid '%s' segment checksum: %08x, expected %08x",
249 name, sum, cs->segment);
250 return FALSE;
251 }
252 DBG2(" valid '%s' segment checksum: %08x", name, sum);
253 return TRUE;
254 }
255
256 /**
257 * Implementation of integrity_checker_t.check
258 */
259 static bool check(private_integrity_checker_t *this, char *name, void *sym)
260 {
261 Dl_info dli;
262
263 if (dladdr(sym, &dli) == 0)
264 {
265 DBG1("unable to locate symbol: %s", dlerror());
266 return FALSE;
267 }
268 if (!check_file(this, name, (char*)dli.dli_fname))
269 {
270 return FALSE;
271 }
272 if (!check_segment(this, name, sym))
273 {
274 return FALSE;
275 }
276 return TRUE;
277 }
278
279 /**
280 * Implementation of integrity_checker_t.destroy.
281 */
282 static void destroy(private_integrity_checker_t *this)
283 {
284 if (this->handle)
285 {
286 dlclose(this->handle);
287 }
288 free(this);
289 }
290
291 /**
292 * See header
293 */
294 integrity_checker_t *integrity_checker_create(char *checksum_library)
295 {
296 private_integrity_checker_t *this = malloc_thing(private_integrity_checker_t);
297
298 this->public.check_file = (bool(*)(integrity_checker_t*, char *name, char *file))check_file;
299 this->public.build_file = (u_int32_t(*)(integrity_checker_t*, char *file, size_t *len))build_file;
300 this->public.check_segment = (bool(*)(integrity_checker_t*, char *name, void *sym))check_segment;
301 this->public.build_segment = (u_int32_t(*)(integrity_checker_t*, void *sym, size_t *len))build_segment;
302 this->public.check = (bool(*)(integrity_checker_t*, char *name, void *sym))check;
303 this->public.destroy = (void(*)(integrity_checker_t*))destroy;
304
305 this->checksum_count = 0;
306 this->handle = NULL;
307 if (checksum_library)
308 {
309 this->handle = dlopen(checksum_library, RTLD_LAZY);
310 if (this->handle)
311 {
312 int *checksum_count;
313
314 this->checksums = dlsym(this->handle, "checksums");
315 checksum_count = dlsym(this->handle, "checksum_count");
316 if (this->checksums && checksum_count)
317 {
318 this->checksum_count = *checksum_count;
319 }
320 else
321 {
322 DBG1("checksum library '%s' invalid", checksum_library);
323 }
324 }
325 else
326 {
327 DBG1("loading checksum library '%s' failed", checksum_library);
328 }
329 }
330 return &this->public;
331 }
332