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 * Private data of an integrity_checker_t object.
37 struct private_integrity_checker_t
{
40 * Public integrity_checker_t interface.
42 integrity_checker_t
public;
45 * dlopen handle to checksum library
52 integrity_checksum_t
*checksums
;
55 * number of checksums in array
60 METHOD(integrity_checker_t
, build_file
, u_int32_t
,
61 private_integrity_checker_t
*this, char *file
, size_t *len
)
69 fd
= open(file
, O_RDONLY
);
72 DBG1(DBG_LIB
, " opening '%s' failed: %s", file
, strerror(errno
));
76 if (fstat(fd
, &sb
) == -1)
78 DBG1(DBG_LIB
, " getting file size of '%s' failed: %s", file
,
84 addr
= mmap(NULL
, sb
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
85 if (addr
== MAP_FAILED
)
87 DBG1(DBG_LIB
, " mapping '%s' failed: %s", file
, strerror(errno
));
93 contents
= chunk_create(addr
, sb
.st_size
);
94 checksum
= chunk_hash_static(contents
);
96 munmap(addr
, sb
.st_size
);
103 * dl_iterate_phdr callback function
105 static int callback(struct dl_phdr_info
*dlpi
, size_t size
, Dl_info
*dli
)
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.
113 if (dli
->dli_fbase
== (void*)dlpi
->dlpi_addr
&&
114 dlpi
->dlpi_name
&& *dlpi
->dlpi_name
)
118 for (i
= 0; i
< dlpi
->dlpi_phnum
; i
++)
120 const ElfW(Phdr
) *sgmt
= &dlpi
->dlpi_phdr
[i
];
122 /* we are interested in the executable LOAD segment */
123 if (sgmt
->p_type
== PT_LOAD
&& (sgmt
->p_flags
& PF_X
))
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
;
136 METHOD(integrity_checker_t
, build_segment
, u_int32_t
,
137 private_integrity_checker_t
*this, void *sym
, size_t *len
)
142 if (dladdr(sym
, &dli
) == 0)
144 DBG1(DBG_LIB
, " unable to locate symbol: %s", dlerror());
147 /* we reuse the Dl_info struct as in/out parameter */
148 if (!dl_iterate_phdr((void*)callback
, &dli
))
150 DBG1(DBG_LIB
, " executable section not found");
154 segment
= chunk_create(dli
.dli_fbase
, dli
.dli_saddr
- dli
.dli_fbase
);
156 return chunk_hash_static(segment
);
160 * Find a checksum by its name
162 static integrity_checksum_t
*find_checksum(private_integrity_checker_t
*this,
167 for (i
= 0; i
< this->checksum_count
; i
++)
169 if (streq(this->checksums
[i
].name
, name
))
171 return &this->checksums
[i
];
177 METHOD(integrity_checker_t
, check_file
, bool,
178 private_integrity_checker_t
*this, char *name
, char *file
)
180 integrity_checksum_t
*cs
;
184 cs
= find_checksum(this, name
);
187 DBG1(DBG_LIB
, " '%s' file checksum not found", name
);
190 sum
= build_file(this, file
, &len
);
195 if (cs
->file_len
!= len
)
197 DBG1(DBG_LIB
, " invalid '%s' file size: %u bytes, expected %u bytes",
198 name
, len
, cs
->file_len
);
203 DBG1(DBG_LIB
, " invalid '%s' file checksum: %08x, expected %08x",
204 name
, sum
, cs
->file
);
207 DBG2(DBG_LIB
, " valid '%s' file checksum: %08x", name
, sum
);
211 METHOD(integrity_checker_t
, check_segment
, bool,
212 private_integrity_checker_t
*this, char *name
, void *sym
)
214 integrity_checksum_t
*cs
;
218 cs
= find_checksum(this, name
);
221 DBG1(DBG_LIB
, " '%s' segment checksum not found", name
);
224 sum
= build_segment(this, sym
, &len
);
229 if (cs
->segment_len
!= len
)
231 DBG1(DBG_LIB
, " invalid '%s' segment size: %u bytes,"
232 " expected %u bytes", name
, len
, cs
->segment_len
);
235 if (cs
->segment
!= sum
)
237 DBG1(DBG_LIB
, " invalid '%s' segment checksum: %08x, expected %08x",
238 name
, sum
, cs
->segment
);
241 DBG2(DBG_LIB
, " valid '%s' segment checksum: %08x", name
, sum
);
245 METHOD(integrity_checker_t
, check
, bool,
246 private_integrity_checker_t
*this, char *name
, void *sym
)
250 if (dladdr(sym
, &dli
) == 0)
252 DBG1(DBG_LIB
, "unable to locate symbol: %s", dlerror());
255 if (!check_file(this, name
, (char*)dli
.dli_fname
))
259 if (!check_segment(this, name
, sym
))
266 METHOD(integrity_checker_t
, destroy
, void,
267 private_integrity_checker_t
*this)
271 dlclose(this->handle
);
279 integrity_checker_t
*integrity_checker_create(char *checksum_library
)
281 private_integrity_checker_t
*this;
285 .check_file
= _check_file
,
286 .build_file
= _build_file
,
287 .check_segment
= _check_segment
,
288 .build_segment
= _build_segment
,
294 if (checksum_library
)
296 this->handle
= dlopen(checksum_library
, RTLD_LAZY
);
301 this->checksums
= dlsym(this->handle
, "checksums");
302 checksum_count
= dlsym(this->handle
, "checksum_count");
303 if (this->checksums
&& checksum_count
)
305 this->checksum_count
= *checksum_count
;
309 DBG1(DBG_LIB
, "checksum library '%s' invalid",
315 DBG1(DBG_LIB
, "loading checksum library '%s' failed",
319 return &this->public;