2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
18 #include "integrity_checker.h"
27 #include <sys/types.h>
32 typedef struct private_integrity_checker_t private_integrity_checker_t
;
35 * Key used to calculate MACs (128-bit)
37 static char key
[] = "IntegrityChecker";
40 * Private data of an integrity_checker_t object.
42 struct private_integrity_checker_t
{
45 * Public integrity_checker_t interface.
47 integrity_checker_t
public;
50 * dlopen handle to checksum library
57 integrity_checksum_t
*checksums
;
60 * number of checksums in array
65 METHOD(integrity_checker_t
, build_file
, u_int32_t
,
66 private_integrity_checker_t
*this, char *file
, size_t *len
)
74 fd
= open(file
, O_RDONLY
);
77 DBG1(DBG_LIB
, " opening '%s' failed: %s", file
, strerror(errno
));
81 if (fstat(fd
, &sb
) == -1)
83 DBG1(DBG_LIB
, " getting file size of '%s' failed: %s", file
,
89 addr
= mmap(NULL
, sb
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
90 if (addr
== MAP_FAILED
)
92 DBG1(DBG_LIB
, " mapping '%s' failed: %s", file
, strerror(errno
));
98 contents
= chunk_create(addr
, sb
.st_size
);
99 checksum
= chunk_mac(contents
, key
);
101 munmap(addr
, sb
.st_size
);
108 * dl_iterate_phdr callback function
110 static int callback(struct dl_phdr_info
*dlpi
, size_t size
, Dl_info
*dli
)
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.
118 if (dli
->dli_fbase
== (void*)dlpi
->dlpi_addr
&&
119 dlpi
->dlpi_name
&& *dlpi
->dlpi_name
)
123 for (i
= 0; i
< dlpi
->dlpi_phnum
; i
++)
125 const ElfW(Phdr
) *sgmt
= &dlpi
->dlpi_phdr
[i
];
127 /* we are interested in the executable LOAD segment */
128 if (sgmt
->p_type
== PT_LOAD
&& (sgmt
->p_flags
& PF_X
))
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
;
141 METHOD(integrity_checker_t
, build_segment
, u_int32_t
,
142 private_integrity_checker_t
*this, void *sym
, size_t *len
)
147 if (dladdr(sym
, &dli
) == 0)
149 DBG1(DBG_LIB
, " unable to locate symbol: %s", dlerror());
152 /* we reuse the Dl_info struct as in/out parameter */
153 if (!dl_iterate_phdr((void*)callback
, &dli
))
155 DBG1(DBG_LIB
, " executable section not found");
159 segment
= chunk_create(dli
.dli_fbase
, dli
.dli_saddr
- dli
.dli_fbase
);
161 return chunk_mac(segment
, key
);
165 * Find a checksum by its name
167 static integrity_checksum_t
*find_checksum(private_integrity_checker_t
*this,
172 for (i
= 0; i
< this->checksum_count
; i
++)
174 if (streq(this->checksums
[i
].name
, name
))
176 return &this->checksums
[i
];
182 METHOD(integrity_checker_t
, check_file
, bool,
183 private_integrity_checker_t
*this, char *name
, char *file
)
185 integrity_checksum_t
*cs
;
189 cs
= find_checksum(this, name
);
192 DBG1(DBG_LIB
, " '%s' file checksum not found", name
);
195 sum
= build_file(this, file
, &len
);
200 if (cs
->file_len
!= len
)
202 DBG1(DBG_LIB
, " invalid '%s' file size: %u bytes, expected %u bytes",
203 name
, len
, cs
->file_len
);
208 DBG1(DBG_LIB
, " invalid '%s' file checksum: %08x, expected %08x",
209 name
, sum
, cs
->file
);
212 DBG2(DBG_LIB
, " valid '%s' file checksum: %08x", name
, sum
);
216 METHOD(integrity_checker_t
, check_segment
, bool,
217 private_integrity_checker_t
*this, char *name
, void *sym
)
219 integrity_checksum_t
*cs
;
223 cs
= find_checksum(this, name
);
226 DBG1(DBG_LIB
, " '%s' segment checksum not found", name
);
229 sum
= build_segment(this, sym
, &len
);
234 if (cs
->segment_len
!= len
)
236 DBG1(DBG_LIB
, " invalid '%s' segment size: %u bytes,"
237 " expected %u bytes", name
, len
, cs
->segment_len
);
240 if (cs
->segment
!= sum
)
242 DBG1(DBG_LIB
, " invalid '%s' segment checksum: %08x, expected %08x",
243 name
, sum
, cs
->segment
);
246 DBG2(DBG_LIB
, " valid '%s' segment checksum: %08x", name
, sum
);
250 METHOD(integrity_checker_t
, check
, bool,
251 private_integrity_checker_t
*this, char *name
, void *sym
)
255 if (dladdr(sym
, &dli
) == 0)
257 DBG1(DBG_LIB
, "unable to locate symbol: %s", dlerror());
260 if (!check_file(this, name
, (char*)dli
.dli_fname
))
264 if (!check_segment(this, name
, sym
))
271 METHOD(integrity_checker_t
, destroy
, void,
272 private_integrity_checker_t
*this)
276 dlclose(this->handle
);
284 integrity_checker_t
*integrity_checker_create(char *checksum_library
)
286 private_integrity_checker_t
*this;
290 .check_file
= _check_file
,
291 .build_file
= _build_file
,
292 .check_segment
= _check_segment
,
293 .build_segment
= _build_segment
,
299 if (checksum_library
)
301 this->handle
= dlopen(checksum_library
, RTLD_LAZY
);
306 this->checksums
= dlsym(this->handle
, "checksums");
307 checksum_count
= dlsym(this->handle
, "checksum_count");
308 if (this->checksums
&& checksum_count
)
310 this->checksum_count
= *checksum_count
;
314 DBG1(DBG_LIB
, "checksum library '%s' invalid",
320 DBG1(DBG_LIB
, "loading checksum library '%s' failed",
324 return &this->public;