chunk: Externalize error reporting in chunk_write()
[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 * 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 METHOD(integrity_checker_t, build_file, u_int32_t,
61 private_integrity_checker_t *this, char *file, size_t *len)
62 {
63 u_int32_t checksum;
64 chunk_t contents;
65 struct stat sb;
66 void *addr;
67 int fd;
68
69 fd = open(file, O_RDONLY);
70 if (fd == -1)
71 {
72 DBG1(DBG_LIB, " opening '%s' failed: %s", file, strerror(errno));
73 return 0;
74 }
75
76 if (fstat(fd, &sb) == -1)
77 {
78 DBG1(DBG_LIB, " getting file size of '%s' failed: %s", file,
79 strerror(errno));
80 close(fd);
81 return 0;
82 }
83
84 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
85 if (addr == MAP_FAILED)
86 {
87 DBG1(DBG_LIB, " mapping '%s' failed: %s", file, strerror(errno));
88 close(fd);
89 return 0;
90 }
91
92 *len = sb.st_size;
93 contents = chunk_create(addr, sb.st_size);
94 checksum = chunk_hash_static(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 METHOD(integrity_checker_t, build_segment, u_int32_t,
137 private_integrity_checker_t *this, void *sym, size_t *len)
138 {
139 chunk_t segment;
140 Dl_info dli;
141
142 if (dladdr(sym, &dli) == 0)
143 {
144 DBG1(DBG_LIB, " unable to locate symbol: %s", dlerror());
145 return 0;
146 }
147 /* we reuse the Dl_info struct as in/out parameter */
148 if (!dl_iterate_phdr((void*)callback, &dli))
149 {
150 DBG1(DBG_LIB, " executable section not found");
151 return 0;
152 }
153
154 segment = chunk_create(dli.dli_fbase, dli.dli_saddr - dli.dli_fbase);
155 *len = segment.len;
156 return chunk_hash_static(segment);
157 }
158
159 /**
160 * Find a checksum by its name
161 */
162 static integrity_checksum_t *find_checksum(private_integrity_checker_t *this,
163 char *name)
164 {
165 int i;
166
167 for (i = 0; i < this->checksum_count; i++)
168 {
169 if (streq(this->checksums[i].name, name))
170 {
171 return &this->checksums[i];
172 }
173 }
174 return NULL;
175 }
176
177 METHOD(integrity_checker_t, check_file, bool,
178 private_integrity_checker_t *this, char *name, char *file)
179 {
180 integrity_checksum_t *cs;
181 u_int32_t sum;
182 size_t len = 0;
183
184 cs = find_checksum(this, name);
185 if (!cs)
186 {
187 DBG1(DBG_LIB, " '%s' file checksum not found", name);
188 return FALSE;
189 }
190 sum = build_file(this, file, &len);
191 if (!sum)
192 {
193 return FALSE;
194 }
195 if (cs->file_len != len)
196 {
197 DBG1(DBG_LIB, " invalid '%s' file size: %u bytes, expected %u bytes",
198 name, len, cs->file_len);
199 return FALSE;
200 }
201 if (cs->file != sum)
202 {
203 DBG1(DBG_LIB, " invalid '%s' file checksum: %08x, expected %08x",
204 name, sum, cs->file);
205 return FALSE;
206 }
207 DBG2(DBG_LIB, " valid '%s' file checksum: %08x", name, sum);
208 return TRUE;
209 }
210
211 METHOD(integrity_checker_t, check_segment, bool,
212 private_integrity_checker_t *this, char *name, void *sym)
213 {
214 integrity_checksum_t *cs;
215 u_int32_t sum;
216 size_t len = 0;
217
218 cs = find_checksum(this, name);
219 if (!cs)
220 {
221 DBG1(DBG_LIB, " '%s' segment checksum not found", name);
222 return FALSE;
223 }
224 sum = build_segment(this, sym, &len);
225 if (!sum)
226 {
227 return FALSE;
228 }
229 if (cs->segment_len != len)
230 {
231 DBG1(DBG_LIB, " invalid '%s' segment size: %u bytes,"
232 " expected %u bytes", name, len, cs->segment_len);
233 return FALSE;
234 }
235 if (cs->segment != sum)
236 {
237 DBG1(DBG_LIB, " invalid '%s' segment checksum: %08x, expected %08x",
238 name, sum, cs->segment);
239 return FALSE;
240 }
241 DBG2(DBG_LIB, " valid '%s' segment checksum: %08x", name, sum);
242 return TRUE;
243 }
244
245 METHOD(integrity_checker_t, check, bool,
246 private_integrity_checker_t *this, char *name, void *sym)
247 {
248 Dl_info dli;
249
250 if (dladdr(sym, &dli) == 0)
251 {
252 DBG1(DBG_LIB, "unable to locate symbol: %s", dlerror());
253 return FALSE;
254 }
255 if (!check_file(this, name, (char*)dli.dli_fname))
256 {
257 return FALSE;
258 }
259 if (!check_segment(this, name, sym))
260 {
261 return FALSE;
262 }
263 return TRUE;
264 }
265
266 METHOD(integrity_checker_t, destroy, void,
267 private_integrity_checker_t *this)
268 {
269 if (this->handle)
270 {
271 dlclose(this->handle);
272 }
273 free(this);
274 }
275
276 /**
277 * See header
278 */
279 integrity_checker_t *integrity_checker_create(char *checksum_library)
280 {
281 private_integrity_checker_t *this;
282
283 INIT(this,
284 .public = {
285 .check_file = _check_file,
286 .build_file = _build_file,
287 .check_segment = _check_segment,
288 .build_segment = _build_segment,
289 .check = _check,
290 .destroy = _destroy,
291 },
292 );
293
294 if (checksum_library)
295 {
296 this->handle = dlopen(checksum_library, RTLD_LAZY);
297 if (this->handle)
298 {
299 int *checksum_count;
300
301 this->checksums = dlsym(this->handle, "checksums");
302 checksum_count = dlsym(this->handle, "checksum_count");
303 if (this->checksums && checksum_count)
304 {
305 this->checksum_count = *checksum_count;
306 }
307 else
308 {
309 DBG1(DBG_LIB, "checksum library '%s' invalid",
310 checksum_library);
311 }
312 }
313 else
314 {
315 DBG1(DBG_LIB, "loading checksum library '%s' failed",
316 checksum_library);
317 }
318 }
319 return &this->public;
320 }
321