veriexecgen.c revision 1.7 1 /* $NetBSD: veriexecgen.c,v 1.7 2006/10/27 22:14:17 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 <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 "/lib", "/usr/lib", "/libexec", "/usr/libexec", \
67 NULL }
68
69 struct fentry {
70 char filename[MAXPATHLEN];
71 char *hash_val;
72 int flags;
73 TAILQ_ENTRY(fentry) f;
74 };
75 TAILQ_HEAD(, fentry) fehead;
76
77 struct hash {
78 const char *hashname;
79 char *(*filefunc) (const char *, char *);
80 } hashes[] = {
81 { "MD5", MD5File },
82 { "SHA1", SHA1File },
83 { "SHA256", SHA256_File },
84 { "SHA384", SHA384_File },
85 { "SHA512", SHA512_File },
86 { "RMD160", RMD160File },
87 { NULL, NULL },
88 };
89
90 int Aflag, aflag, Dflag, Fflag, rflag, Sflag, vflag;
91
92 static void
93 usage(void)
94 {
95 (void)fprintf(stderr,
96 "usage: %s [-AaDrSv] [-d dir] [-o fingerprintdb]"
97 " [-t algorithm]\n", getprogname());
98 }
99
100 static void
101 banner(struct hash *hash_type, char **search_path)
102 {
103 int j;
104
105 (void)printf("Fingerprinting ");
106
107 for (j = 0; search_path[j] != NULL; j++)
108 (void)printf("%s ", search_path[j]);
109
110 (void)printf("(%s) (%s) using %s\n",
111 aflag ? "all files" : "executables only",
112 rflag ? "recursive" : "non-recursive",
113 hash_type->hashname);
114 }
115
116 static struct hash *
117 find_hash(char *hash_type)
118 {
119 struct hash *hash;
120
121 for (hash = hashes; hash->hashname != NULL; hash++)
122 if (strcasecmp(hash_type, hash->hashname) == 0)
123 return hash;
124 return NULL;
125 }
126
127 static char *
128 do_hash(char *filename, struct hash * h)
129 {
130 return h->filefunc(filename, NULL);
131 }
132
133 static int
134 figure_flags(char *path, mode_t mode)
135 {
136 #ifdef notyet
137 if (Fflag) {
138 /* Try to figure out right flag(s). */
139 return VERIEXEC_DIRECT;
140 }
141 #endif /* notyet */
142
143 if (!IS_EXEC(mode))
144 return VERIEXEC_FILE;
145 else
146 return 0;
147 }
148
149 static int
150 check_dup(char *filename)
151 {
152 struct fentry *lwalk;
153
154 TAILQ_FOREACH(lwalk, &fehead, f) {
155 if (strncmp(lwalk->filename, filename,
156 (unsigned long) MAXPATHLEN) == 0)
157 return 1;
158 }
159
160 return 0;
161 }
162
163 static void
164 add_new_entry(FTSENT *file, struct hash *hash)
165 {
166 struct fentry *e;
167 struct stat sb;
168
169 if (file->fts_info == FTS_SL) {
170 if (stat(file->fts_path, &sb) == -1)
171 err(1, "Cannot stat symlink");
172 } else
173 sb = *file->fts_statp;
174
175 if (!aflag && !Dflag && !IS_EXEC(sb.st_mode))
176 return;
177
178 e = ecalloc(1UL, sizeof(*e));
179
180 if (realpath(file->fts_accpath, e->filename) == NULL)
181 err(1, "Cannot find absolute path");
182 if (check_dup(e->filename)) {
183 free(e);
184 return;
185 }
186 if ((e->hash_val = do_hash(e->filename, hash)) == NULL)
187 errx(1, "Cannot calculate hash");
188 e->flags = figure_flags(e->filename, sb.st_mode);
189
190 TAILQ_INSERT_TAIL(&fehead, e, f);
191 }
192
193 static void
194 walk_dir(char **search_path, struct hash *hash)
195 {
196 FTS *fh;
197 FTSENT *file;
198
199 if ((fh = fts_open(search_path, FTS_PHYSICAL, NULL)) == NULL)
200 err(1, "fts_open");
201
202 while ((file = fts_read(fh)) != NULL) {
203 if (!rflag && file->fts_level > 1) {
204 fts_set(fh, file, FTS_SKIP);
205 continue;
206 }
207
208 switch (file->fts_info) {
209 case FTS_D:
210 case FTS_DC:
211 case FTS_DP:
212 continue;
213 default:
214 break;
215 }
216
217 if (file->fts_errno) {
218 errx(1, "%s: %s", file->fts_path,
219 strerror(file->fts_errno));
220 }
221
222 add_new_entry(file, hash);
223 }
224
225 fts_close(fh);
226 }
227
228 static char *
229 flags2str(int flags)
230 {
231 if (flags != 0)
232 return "FILE, INDIRECT";
233 else
234 return "";
235 }
236
237 static void
238 store_entries(char *dbfile, struct hash *hash)
239 {
240 FILE *fp;
241 int move = 1;
242 char old_dbfile[MAXPATHLEN];
243 time_t ct;
244 struct stat sb;
245 struct fentry *e;
246
247 if (stat(dbfile, &sb) != 0) {
248 if (errno == ENOENT)
249 move = 0;
250 else
251 err(1, "could not stat %s", dbfile);
252 }
253 if (move && !Aflag) {
254 if (vflag)
255 (void)printf("\nBacking up existing fingerprint file "
256 "to \"%s.old\"\n\n", dbfile);
257
258 if (snprintf(old_dbfile, MAXPATHLEN, "%s.old", dbfile) <
259 strlen(dbfile) + 4) {
260 err(1, "%s", old_dbfile);
261 }
262 if (rename(dbfile, old_dbfile) == -1)
263 err(1, "could not rename file");
264 }
265
266 fp = efopen(dbfile, Aflag ? "a" : "w+");
267
268 time(&ct);
269 (void)fprintf(fp, "# Generated by %s, %.24s\n",
270 getlogin(), ctime(&ct));
271
272 TAILQ_FOREACH(e, &fehead, f) {
273 if (vflag)
274 (void)printf("Adding %s.\n", e->filename);
275
276 (void)fprintf(fp, "%s %s %s %s\n", e->filename,
277 hash->hashname, e->hash_val, flags2str(e->flags));
278 }
279
280 (void)fclose(fp);
281
282 if (!vflag)
283 return;
284
285 (void)printf("\n\n"
286 "#############################################################\n"
287 " PLEASE VERIFY CONTENTS OF %s AND FINE-TUNE THE\n"
288 " FLAGS WHERE APPROPRIATE AFTER READING veriexecctl(8)\n"
289 "#############################################################\n",
290 dbfile);
291 }
292
293 int
294 main(int argc, char **argv)
295 {
296 int ch, total = 0;
297 char *dbfile = NULL;
298 char **search_path = NULL;
299 struct hash *hash = NULL;
300
301 Aflag = aflag = Dflag = Fflag = rflag = Sflag = vflag = 0;
302
303 while ((ch = getopt(argc, argv, "AaDd:ho:rSt:v")) != -1) {
304 switch (ch) {
305 case 'A':
306 Aflag = 1;
307 break;
308 case 'a':
309 aflag = 1;
310 break;
311 case 'D':
312 Dflag = 1;
313 break;
314 case 'd':
315 search_path = erealloc(search_path, sizeof(char *) *
316 (total + 1));
317 search_path[total] = optarg;
318 search_path[++total] = NULL;
319 break;
320 #ifdef notyet
321 case 'F':
322 Fflag = 1;
323 break;
324 #endif /* notyet */
325 case 'h':
326 usage();
327 return 0;
328 case 'o':
329 dbfile = optarg;
330 break;
331 case 'r':
332 rflag = 1;
333 break;
334 case 'S':
335 Sflag = 1;
336 break;
337 case 't':
338 hash = find_hash(optarg);
339 break;
340 case 'v':
341 vflag = 1;
342 break;
343 default:
344 usage();
345 return 1;
346 }
347 }
348
349 if (dbfile == NULL)
350 dbfile = DEFAULT_DBFILE;
351
352 if (hash == NULL) {
353 if ((hash = find_hash(DEFAULT_HASH)) == NULL)
354 errx(1, "No hash algorithm");
355 }
356
357 TAILQ_INIT(&fehead);
358
359 if (search_path == NULL)
360 Dflag = 1;
361
362 if (Dflag) {
363 char *sys_paths[] = DEFAULT_SYSPATHS;
364
365 if (vflag)
366 banner(hash, sys_paths);
367 walk_dir(sys_paths, hash);
368 }
369
370 if (search_path != NULL) {
371 if (vflag)
372 banner(hash, search_path);
373 walk_dir(search_path, hash);
374 }
375
376 store_entries(dbfile, hash);
377
378 if (Sflag && chflags(dbfile, SF_IMMUTABLE) != 0)
379 err(1, "Can't set immutable flag");
380
381 return 0;
382 }
383