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