veriexecgen.c revision 1.1 1 /* $NetBSD: veriexecgen.c,v 1.1 2006/09/16 20:54:42 elad Exp $ */
2
3 /*-
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Fleming.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/param.h>
40 #include <sys/types.h>
41 #include <sys/queue.h>
42 #include <sys/stat.h>
43 #include <sys/dirent.h>
44 #include <sys/verified_exec.h>
45
46 #include <err.h>
47 #include <errno.h>
48 #include <fts.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <time.h>
53 #include <unistd.h>
54 #include <util.h>
55
56 #include <md5.h>
57 #include <crypto/sha1.h>
58 #include <crypto/sha2.h>
59 #include <crypto/rmd160.h>
60
61 #define IS_EXEC(mode) ((mode) & (S_IXUSR | S_IXGRP | S_IXOTH))
62
63 #define DEFAULT_DBFILE "/etc/signatures"
64 #define DEFAULT_HASH "sha256"
65 #define DEFAULT_SYSPATHS { "/bin", "/sbin", "/usr/bin", "/usr/sbin", \
66 "/usr/lib", NULL }
67
68 struct fentry {
69 char filename[MAXPATHLEN];
70 char *hash_val;
71 int flags;
72 TAILQ_ENTRY(fentry) f;
73 };
74 TAILQ_HEAD(, fentry) fehead;
75
76 struct hash {
77 const char *hashname;
78 char *(*filefunc) (const char *, char *);
79 } hashes[] = {
80 { "MD5", MD5File },
81 { "SHA1", SHA1File },
82 { "SHA256", SHA256_File },
83 { "SHA384", SHA384_File },
84 { "SHA512", SHA512_File },
85 { "RMD160", RMD160File },
86 { NULL, NULL },
87 };
88
89 int Aflag, aflag, Dflag, Fflag, rflag, vflag;
90
91 static void
92 usage(void)
93 {
94 (void)fprintf(stderr,
95 "usage: %s [-Aarv] [-D] [-d dir] [-o fingerprintdb]"
96 " [-t algorithm]\n", getprogname());
97 }
98
99 static void
100 banner(struct hash *hash_type, char **search_path)
101 {
102 int j;
103
104 (void)printf("Fingerprinting ");
105
106 for (j = 0; search_path[j] != NULL; j++)
107 (void)printf("%s ", search_path[j]);
108
109 (void)printf("(%s) (%s) using %s\n",
110 aflag ? "all files" : "executables only",
111 rflag ? "recursive" : "non-recursive",
112 hash_type->hashname);
113 }
114
115 static struct hash *
116 find_hash(char *hash_type)
117 {
118 struct hash *hash;
119
120 for (hash = hashes; hash->hashname != NULL; hash++)
121 if (strcasecmp(hash_type, hash->hashname) == 0)
122 return hash;
123 return NULL;
124 }
125
126 static char *
127 do_hash(char *filename, struct hash * h)
128 {
129 return h->filefunc(filename, NULL);
130 }
131
132 static int
133 figure_flags(char *path, mode_t mode)
134 {
135 #ifdef notyet
136 if (Fflag) {
137 /* Try to figure out right flag(s). */
138 return VERIEXEC_DIRECT;
139 }
140 #endif /* notyet */
141
142 if (!IS_EXEC(mode))
143 return VERIEXEC_FILE;
144 else
145 return 0;
146 }
147
148 static int
149 check_dup(char *filename)
150 {
151 struct fentry *lwalk;
152
153 TAILQ_FOREACH(lwalk, &fehead, f) {
154 if (strncmp(lwalk->filename, filename,
155 (unsigned long) MAXPATHLEN) == 0)
156 return 1;
157 }
158
159 return 0;
160 }
161
162 static void
163 add_new_entry(FTSENT *file, struct hash *hash)
164 {
165 struct fentry *e;
166 struct stat sb;
167
168 if (file->fts_info == FTS_SL) {
169 if (stat(file->fts_path, &sb) == -1)
170 err(1, "Cannot stat symlink");
171 } else
172 sb = *file->fts_statp;
173
174 if (!aflag && !Dflag && IS_EXEC(sb.st_mode))
175 return;
176
177 e = ecalloc(1UL, sizeof(*e));
178
179 if (realpath(file->fts_accpath, e->filename) == NULL)
180 err(1, "Cannot find absolute path");
181 if (check_dup(e->filename)) {
182 free(e);
183 return;
184 }
185 if ((e->hash_val = do_hash(e->filename, hash)) == NULL)
186 errx(1, "Cannot calculate hash");
187 e->flags = figure_flags(e->filename, sb.st_mode);
188
189 TAILQ_INSERT_TAIL(&fehead, e, f);
190 }
191
192 static void
193 walk_dir(char **search_path, struct hash *hash)
194 {
195 FTS *fh;
196 FTSENT *file;
197
198 if ((fh = fts_open(search_path, FTS_PHYSICAL, NULL)) == NULL)
199 err(1, "fts_open");
200
201 while ((file = fts_read(fh)) != NULL) {
202 if (!rflag && file->fts_level > 1) {
203 fts_set(fh, file, FTS_SKIP);
204 continue;
205 }
206
207 switch (file->fts_info) {
208 case FTS_D:
209 case FTS_DC:
210 case FTS_DP:
211 continue;
212 default:
213 break;
214 }
215
216 if (file->fts_errno) {
217 errx(1, "%s: %s", file->fts_path,
218 strerror(file->fts_errno));
219 }
220
221 add_new_entry(file, hash);
222 }
223
224 fts_close(fh);
225 }
226
227 static char *
228 flags2str(int flags)
229 {
230 if (flags != 0)
231 return "FILE";
232 else
233 return "";
234 }
235
236 static void
237 store_entries(char *dbfile, struct hash *hash)
238 {
239 FILE *fp;
240 int move = 1;
241 char old_dbfile[MAXPATHLEN];
242 time_t ct;
243 struct stat sb;
244 struct fentry *e;
245
246 if (stat(dbfile, &sb) != 0) {
247 if (errno == ENOENT)
248 move = 0;
249 else
250 err(1, "could not stat %s", dbfile);
251 }
252 if (move && !Aflag) {
253 if (vflag)
254 (void)printf("\nBacking up existing fingerprint file "
255 "to \"%s.old\"\n\n", dbfile);
256
257 if (snprintf(old_dbfile, MAXPATHLEN, "%s.old", dbfile) <
258 strlen(dbfile) + 4) {
259 err(1, "%s", old_dbfile);
260 }
261 if (rename(dbfile, old_dbfile) == -1)
262 err(1, "could not rename file");
263 }
264
265 fp = efopen(dbfile, Aflag ? "a" : "w+");
266
267 time(&ct);
268 (void)fprintf(fp, "# Generated by %s, %.24s\n",
269 getlogin(), ctime(&ct));
270
271 TAILQ_FOREACH(e, &fehead, f) {
272 if (vflag)
273 (void)printf("Adding %s.\n", e->filename);
274
275 (void)fprintf(fp, "%s %s %s %s\n", e->filename,
276 hash->hashname, e->hash_val, flags2str(e->flags));
277 }
278
279 (void)fclose(fp);
280
281 if (!vflag)
282 return;
283
284 (void)printf("\n\n"
285 "#############################################################\n"
286 " PLEASE VERIFY CONTENTS OF %s AND FINE-TUNE THE\n"
287 " FLAGS WHERE APPROPRIATE AFTER READING veriexecctl(8)\n"
288 "#############################################################\n",
289 dbfile);
290 }
291
292 int
293 main(int argc, char **argv)
294 {
295 int ch, total = 0;
296 char *dbfile = NULL;
297 char **search_path = NULL;
298 struct hash *hash = NULL;
299
300 Aflag = aflag = Dflag = Fflag = rflag = vflag = 0;
301
302 while ((ch = getopt(argc, argv, "AaDd:ho:rt:v")) != -1) {
303 switch (ch) {
304 case 'A':
305 Aflag = 1;
306 break;
307 case 'a':
308 aflag = 1;
309 break;
310 case 'D':
311 Dflag = 1;
312 break;
313 case 'd':
314 search_path = erealloc(search_path, sizeof(char *) *
315 (total + 1));
316 search_path[total] = optarg;
317 search_path[++total] = NULL;
318 break;
319 #ifdef notyet
320 case 'F':
321 Fflag = 1;
322 break;
323 #endif /* notyet */
324 case 'h':
325 usage();
326 return 0;
327 case 'o':
328 dbfile = optarg;
329 break;
330 case 'r':
331 rflag = 1;
332 break;
333 case 't':
334 hash = find_hash(optarg);
335 break;
336 case 'v':
337 vflag = 1;
338 break;
339 default:
340 usage();
341 return 1;
342 }
343 }
344
345 if (dbfile == NULL)
346 dbfile = DEFAULT_DBFILE;
347
348 if (hash == NULL) {
349 if ((hash = find_hash(DEFAULT_HASH)) == NULL)
350 errx(1, "No hash algorithm");
351 }
352
353 TAILQ_INIT(&fehead);
354
355 if (search_path == NULL)
356 Dflag = 1;
357
358 if (Dflag) {
359 char *sys_paths[] = DEFAULT_SYSPATHS;
360
361 if (vflag)
362 banner(hash, sys_paths);
363 walk_dir(sys_paths, hash);
364 }
365
366 if (search_path != NULL) {
367 if (vflag)
368 banner(hash, search_path);
369 walk_dir(search_path, hash);
370 }
371
372 store_entries(dbfile, hash);
373
374 return 0;
375 }
376