eb0bc1407dfa1b523c5f72d22ec3fc8ec84ff7dc
[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 {
65 u_int32_t checksum;
66 chunk_t contents;
67 struct stat sb;
68 void *addr;
69 int fd;
70
71 fd = open(file, O_RDONLY);
72 if (fd == -1)
73 {
74 DBG1("opening '%s' failed: %s", file, strerror(errno));
75 return 0;
76 }
77
78 if (fstat(fd, &sb) == -1)
79 {
80 DBG1("getting file size of '%s' failed: %s", file, strerror(errno));
81 close(fd);
82 return 0;
83 }
84
85 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
86 if (addr == MAP_FAILED)
87 {
88 DBG1("mapping '%s' failed: %s", file, strerror(errno));
89 close(fd);
90 return 0;
91 }
92
93 contents = chunk_create(addr, sb.st_size);
94 checksum = chunk_hash(contents);
95
96 munmap(addr, sb.st_size);
97 close(fd);
98
99 return checksum;
100 }
101
102 /**
103 * dl_iterate_phdr callback function
104 */
105 static int callback(struct dl_phdr_info *dlpi, size_t size, Dl_info *dli)
106 {
107 /* We are looking for the dlpi_addr matching the address of our dladdr().
108 * dl_iterate_phdr() returns such an address for other (unknown) objects
109 * in very rare cases (e.g. in a chrooted gentoo, but only if
110 * the checksum_builder is invoked by 'make'). As a workaround, we filter
111 * objects by dlpi_name; valid objects have a library name.
112 */
113 if (dli->dli_fbase == (void*)dlpi->dlpi_addr &&
114 dlpi->dlpi_name && *dlpi->dlpi_name)
115 {
116 int i;
117
118 for (i = 0; i < dlpi->dlpi_phnum; i++)
119 {
120 const ElfW(Phdr) *sgmt = &dlpi->dlpi_phdr[i];
121
122 /* we are interested in the executable LOAD segment */
123 if (sgmt->p_type == PT_LOAD && (sgmt->p_flags & PF_X))
124 {
125 /* safe begin of segment in dli_fbase */
126 dli->dli_fbase = (void*)sgmt->p_vaddr + dlpi->dlpi_addr;
127 /* safe end of segment in dli_saddr */
128 dli->dli_saddr = dli->dli_fbase + sgmt->p_memsz;
129 return 1;
130 }
131 }
132 }
133 return 0;
134 }
135
136 /**
137 * Implementation of integrity_checker_t.build_segment
138 */
139 static u_int32_t build_segment(private_integrity_checker_t *this, void *sym)
140 {
141 chunk_t segment;
142 Dl_info dli;
143
144 if (dladdr(sym, &dli) == 0)
145 {
146 DBG1("unable to locate symbol: %s", dlerror());
147 return 0;
148 }
149 /* we reuse the Dl_info struct as in/out parameter */
150 if (!dl_iterate_phdr((void*)callback, &dli))
151 {
152 DBG1("executable section not found");
153 return 0;
154 }
155
156 segment = chunk_create(dli.dli_fbase, dli.dli_saddr - dli.dli_fbase);
157 return chunk_hash(segment);
158 }
159
160 /**
161 * Find a checksum by its name
162 */
163 static integrity_checksum_t *find_checksum(private_integrity_checker_t *this,
164 char *name)
165 {
166 int i;
167
168 for (i = 0; i < this->checksum_count; i++)
169 {
170 if (streq(this->checksums[i].name, name))
171 {
172 return &this->checksums[i];
173 }
174 }
175 return NULL;
176 }
177
178 /**
179 * Implementation of integrity_checker_t.check_file
180 */
181 static bool check_file(private_integrity_checker_t *this,
182 char *name, char *file)
183 {
184 integrity_checksum_t *cs;
185 u_int32_t sum;
186
187 cs = find_checksum(this, name);
188 if (!cs)
189 {
190 DBG1(" '%s' file checksum not found", name);
191 return FALSE;
192 }
193 sum = build_file(this, file);
194 if (!sum || cs->file != sum)
195 {
196 DBG1(" invalid '%s' file checksum: %08x, expected %08x",
197 name, sum, cs->file);
198 return FALSE;
199 }
200 DBG2(" valid '%s' file checksum: %08x", name, sum);
201 return TRUE;
202 }
203
204 /**
205 * Implementation of integrity_checker_t.check_segment
206 */
207 static bool check_segment(private_integrity_checker_t *this,
208 char *name, void *sym)
209 {
210 integrity_checksum_t *cs;
211 u_int32_t sum;
212
213 cs = find_checksum(this, name);
214 if (!cs)
215 {
216 DBG1(" '%s' segment checksum not found", name);
217 return FALSE;
218 }
219 sum = build_segment(this, sym);
220 if (!sum || cs->segment != sum)
221 {
222 DBG1(" invalid '%s' segment checksum: %08x, expected %08x",
223 name, sum, cs->segment);
224 return FALSE;
225 }
226 DBG2(" valid '%s' segment checksum: %08x", name, sum);
227 return TRUE;
228 }
229
230 /**
231 * Implementation of integrity_checker_t.check
232 */
233 static bool check(private_integrity_checker_t *this, char *name, void *sym)
234 {
235 Dl_info dli;
236
237 if (dladdr(sym, &dli) == 0)
238 {
239 DBG1("unable to locate symbol: %s", dlerror());
240 return FALSE;
241 }
242 if (!check_file(this, name, (char*)dli.dli_fname))
243 {
244 return FALSE;
245 }
246 if (!check_segment(this, name, sym))
247 {
248 return FALSE;
249 }
250 return TRUE;
251 }
252
253 /**
254 * Implementation of integrity_checker_t.destroy.
255 */
256 static void destroy(private_integrity_checker_t *this)
257 {
258 if (this->handle)
259 {
260 dlclose(this->handle);
261 }
262 free(this);
263 }
264
265 /**
266 * See header
267 */
268 integrity_checker_t *integrity_checker_create(char *checksum_library)
269 {
270 private_integrity_checker_t *this = malloc_thing(private_integrity_checker_t);
271
272 this->public.check_file = (bool(*)(integrity_checker_t*, char *name, char *file))check_file;
273 this->public.build_file = (u_int32_t(*)(integrity_checker_t*, char *file))build_file;
274 this->public.check_segment = (bool(*)(integrity_checker_t*, char *name, void *sym))check_segment;
275 this->public.build_segment = (u_int32_t(*)(integrity_checker_t*, void *sym))build_segment;
276 this->public.check = (bool(*)(integrity_checker_t*, char *name, void *sym))check;
277 this->public.destroy = (void(*)(integrity_checker_t*))destroy;
278
279 this->checksum_count = 0;
280 this->handle = NULL;
281 if (checksum_library)
282 {
283 this->handle = dlopen(checksum_library, RTLD_LAZY);
284 if (this->handle)
285 {
286 int *checksum_count;
287
288 this->checksums = dlsym(this->handle, "checksums");
289 checksum_count = dlsym(this->handle, "checksum_count");
290 if (this->checksums && checksum_count)
291 {
292 this->checksum_count = *checksum_count;
293 }
294 else
295 {
296 DBG1("checksum library '%s' invalid", checksum_library);
297 }
298 }
299 else
300 {
301 DBG1("loading checksum library '%s' failed", checksum_library);
302 }
303 }
304 return &this->public;
305 }
306