be2faf2ce683abe1c190f2208071eda8ad127851
[strongswan.git] / src / libstrongswan / utils / 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 * Key used to calculate MACs (128-bit)
36 */
37 static char key[] = "IntegrityChecker";
38
39 /**
40 * Private data of an integrity_checker_t object.
41 */
42 struct private_integrity_checker_t {
43
44 /**
45 * Public integrity_checker_t interface.
46 */
47 integrity_checker_t public;
48
49 /**
50 * dlopen handle to checksum library
51 */
52 void *handle;
53
54 /**
55 * checksum array
56 */
57 integrity_checksum_t *checksums;
58
59 /**
60 * number of checksums in array
61 */
62 int checksum_count;
63 };
64
65 METHOD(integrity_checker_t, build_file, u_int32_t,
66 private_integrity_checker_t *this, char *file, size_t *len)
67 {
68 u_int32_t checksum;
69 chunk_t contents;
70 struct stat sb;
71 void *addr;
72 int fd;
73
74 fd = open(file, O_RDONLY);
75 if (fd == -1)
76 {
77 DBG1(DBG_LIB, " opening '%s' failed: %s", file, strerror(errno));
78 return 0;
79 }
80
81 if (fstat(fd, &sb) == -1)
82 {
83 DBG1(DBG_LIB, " getting file size of '%s' failed: %s", file,
84 strerror(errno));
85 close(fd);
86 return 0;
87 }
88
89 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
90 if (addr == MAP_FAILED)
91 {
92 DBG1(DBG_LIB, " mapping '%s' failed: %s", file, strerror(errno));
93 close(fd);
94 return 0;
95 }
96
97 *len = sb.st_size;
98 contents = chunk_create(addr, sb.st_size);
99 checksum = chunk_mac(contents, key);
100
101 munmap(addr, sb.st_size);
102 close(fd);
103
104 return checksum;
105 }
106
107 /**
108 * dl_iterate_phdr callback function
109 */
110 static int callback(struct dl_phdr_info *dlpi, size_t size, Dl_info *dli)
111 {
112 /* We are looking for the dlpi_addr matching the address of our dladdr().
113 * dl_iterate_phdr() returns such an address for other (unknown) objects
114 * in very rare cases (e.g. in a chrooted gentoo, but only if
115 * the checksum_builder is invoked by 'make'). As a workaround, we filter
116 * objects by dlpi_name; valid objects have a library name.
117 */
118 if (dli->dli_fbase == (void*)dlpi->dlpi_addr &&
119 dlpi->dlpi_name && *dlpi->dlpi_name)
120 {
121 int i;
122
123 for (i = 0; i < dlpi->dlpi_phnum; i++)
124 {
125 const ElfW(Phdr) *sgmt = &dlpi->dlpi_phdr[i];
126
127 /* we are interested in the executable LOAD segment */
128 if (sgmt->p_type == PT_LOAD && (sgmt->p_flags & PF_X))
129 {
130 /* safe begin of segment in dli_fbase */
131 dli->dli_fbase = (void*)sgmt->p_vaddr + dlpi->dlpi_addr;
132 /* safe end of segment in dli_saddr */
133 dli->dli_saddr = dli->dli_fbase + sgmt->p_memsz;
134 return 1;
135 }
136 }
137 }
138 return 0;
139 }
140
141 METHOD(integrity_checker_t, build_segment, u_int32_t,
142 private_integrity_checker_t *this, void *sym, size_t *len)
143 {
144 chunk_t segment;
145 Dl_info dli;
146
147 if (dladdr(sym, &dli) == 0)
148 {
149 DBG1(DBG_LIB, " 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(DBG_LIB, " 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_mac(segment, key);
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 METHOD(integrity_checker_t, check_file, bool,
183 private_integrity_checker_t *this, char *name, char *file)
184 {
185 integrity_checksum_t *cs;
186 u_int32_t sum;
187 size_t len = 0;
188
189 cs = find_checksum(this, name);
190 if (!cs)
191 {
192 DBG1(DBG_LIB, " '%s' file checksum not found", name);
193 return FALSE;
194 }
195 sum = build_file(this, file, &len);
196 if (!sum)
197 {
198 return FALSE;
199 }
200 if (cs->file_len != len)
201 {
202 DBG1(DBG_LIB, " invalid '%s' file size: %u bytes, expected %u bytes",
203 name, len, cs->file_len);
204 return FALSE;
205 }
206 if (cs->file != sum)
207 {
208 DBG1(DBG_LIB, " invalid '%s' file checksum: %08x, expected %08x",
209 name, sum, cs->file);
210 return FALSE;
211 }
212 DBG2(DBG_LIB, " valid '%s' file checksum: %08x", name, sum);
213 return TRUE;
214 }
215
216 METHOD(integrity_checker_t, check_segment, bool,
217 private_integrity_checker_t *this, char *name, void *sym)
218 {
219 integrity_checksum_t *cs;
220 u_int32_t sum;
221 size_t len = 0;
222
223 cs = find_checksum(this, name);
224 if (!cs)
225 {
226 DBG1(DBG_LIB, " '%s' segment checksum not found", name);
227 return FALSE;
228 }
229 sum = build_segment(this, sym, &len);
230 if (!sum)
231 {
232 return FALSE;
233 }
234 if (cs->segment_len != len)
235 {
236 DBG1(DBG_LIB, " invalid '%s' segment size: %u bytes,"
237 " expected %u bytes", name, len, cs->segment_len);
238 return FALSE;
239 }
240 if (cs->segment != sum)
241 {
242 DBG1(DBG_LIB, " invalid '%s' segment checksum: %08x, expected %08x",
243 name, sum, cs->segment);
244 return FALSE;
245 }
246 DBG2(DBG_LIB, " valid '%s' segment checksum: %08x", name, sum);
247 return TRUE;
248 }
249
250 METHOD(integrity_checker_t, check, bool,
251 private_integrity_checker_t *this, char *name, void *sym)
252 {
253 Dl_info dli;
254
255 if (dladdr(sym, &dli) == 0)
256 {
257 DBG1(DBG_LIB, "unable to locate symbol: %s", dlerror());
258 return FALSE;
259 }
260 if (!check_file(this, name, (char*)dli.dli_fname))
261 {
262 return FALSE;
263 }
264 if (!check_segment(this, name, sym))
265 {
266 return FALSE;
267 }
268 return TRUE;
269 }
270
271 METHOD(integrity_checker_t, destroy, void,
272 private_integrity_checker_t *this)
273 {
274 if (this->handle)
275 {
276 dlclose(this->handle);
277 }
278 free(this);
279 }
280
281 /**
282 * See header
283 */
284 integrity_checker_t *integrity_checker_create(char *checksum_library)
285 {
286 private_integrity_checker_t *this;
287
288 INIT(this,
289 .public = {
290 .check_file = _check_file,
291 .build_file = _build_file,
292 .check_segment = _check_segment,
293 .build_segment = _build_segment,
294 .check = _check,
295 .destroy = _destroy,
296 },
297 );
298
299 if (checksum_library)
300 {
301 this->handle = dlopen(checksum_library, RTLD_LAZY);
302 if (this->handle)
303 {
304 int *checksum_count;
305
306 this->checksums = dlsym(this->handle, "checksums");
307 checksum_count = dlsym(this->handle, "checksum_count");
308 if (this->checksums && checksum_count)
309 {
310 this->checksum_count = *checksum_count;
311 }
312 else
313 {
314 DBG1(DBG_LIB, "checksum library '%s' invalid",
315 checksum_library);
316 }
317 }
318 else
319 {
320 DBG1(DBG_LIB, "loading checksum library '%s' failed",
321 checksum_library);
322 }
323 }
324 return &this->public;
325 }
326