1 /* $NetBSD: veriexecgen.c,v 1.16 2008/04/29 06:53:04 martin Exp $ */
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #if HAVE_NBTOOL_CONFIG_H
32 #include "nbtool_config.h"
35 #include <sys/cdefs.h>
39 __RCSID("$NetBSD: veriexecgen.c,v 1.16 2008/04/29 06:53:04 martin Exp $");
43 #include <sys/param.h>
44 #include <sys/types.h>
45 #include <sys/queue.h>
47 #include <sys/dirent.h>
48 #include <sys/verified_exec.h>
65 #define IS_EXEC(mode) ((mode) & (S_IXUSR | S_IXGRP | S_IXOTH))
67 #define DEFAULT_DBFILE "/etc/signatures"
68 #define DEFAULT_HASH "sha256"
69 #define DEFAULT_SYSPATHS { "/bin", "/sbin", "/usr/bin", "/usr/sbin", \
70 "/lib", "/usr/lib", "/libexec", "/usr/libexec", \
73 /* this struct defines a hash algorithm */
74 typedef struct hash_t
{
75 const char *hashname
; /* algorithm name */
76 char *(*filefunc
)(const char *, char *); /* function */
79 /* this struct encapsulates various diverse options and arguments */
80 typedef struct veriexecgen_t
{
81 int all_files
; /* scan also for non-executable files */
82 int append_output
; /* append output to existing sigs file */
83 char *dbfile
; /* name of signatures database file */
84 int exit_on_error
; /* exit if we can't create a hash */
85 char *prefix
; /* any prefix to be discarded on output */
86 int recursive_scan
;/* perform scan for files recursively */
87 int scan_system_dirs
; /* just scan system directories */
88 int verbose
; /* verbosity level */
89 int stamp
; /* put a timestamp */
92 /* this struct describes a directory entry to generate a hash for */
94 char filename
[MAXPATHLEN
]; /* name of entry */
95 char *hash_val
; /* its associated hash value */
96 int flags
; /* any associated flags */
97 TAILQ_ENTRY(fentry
) f
; /* its place in the queue */
99 TAILQ_HEAD(, fentry
) fehead
;
101 /* define the possible hash algorithms */
102 static hash_t hashes
[] = {
104 { "SHA1", SHA1File
},
105 { "SHA256", SHA256_File
},
106 { "SHA384", SHA384_File
},
107 { "SHA512", SHA512_File
},
108 { "RMD160", RMD160File
},
114 static int make_immutable
; /* set immutable flag on signatures file */
116 /* warn about a problem - exit if exit_on_error is set */
118 gripe(veriexecgen_t
*vp
, const char *fmt
, const char *filename
)
121 if (vp
->exit_on_error
) {
122 /* error out on problematic files */
127 /* print usage message */
131 (void)fprintf(stderr
,
132 "usage: %s [-AaDrSTvW] [-d dir] [-o fingerprintdb] [-p prefix]\n"
133 "\t\t [-t algorithm]\n"
134 "\t%s [-h]\n", getprogname(), getprogname());
137 /* tell people what we're doing - scan dirs, fingerprint etc */
139 banner(veriexecgen_t
*vp
, hash_t
*hash_type
, char **search_path
)
143 (void)printf("Fingerprinting ");
145 for (j
= 0; search_path
[j
] != NULL
; j
++)
146 (void)printf("%s ", search_path
[j
]);
148 (void)printf("(%s) (%s) using %s\n",
149 vp
->all_files
? "all files" : "executables only",
150 vp
->recursive_scan
? "recursive" : "non-recursive",
151 hash_type
->hashname
);
154 /* find a hash algorithm, given its name */
156 find_hash(char *hash_type
)
160 for (hash
= hashes
; hash
->hashname
!= NULL
; hash
++)
161 if (strcasecmp(hash_type
, hash
->hashname
) == 0)
166 /* perform the hashing operation on `filename' */
168 do_hash(char *filename
, hash_t
* h
)
170 return h
->filefunc(filename
, NULL
);
173 /* return flags for `path' */
175 figure_flags(char *path
, mode_t mode
)
179 /* Try to figure out right flag(s). */
180 return VERIEXEC_DIRECT
;
184 return (IS_EXEC(mode
)) ? 0 : VERIEXEC_FILE
;
187 /* check to see that we don't have a duplicate entry */
189 check_dup(char *filename
)
191 struct fentry
*lwalk
;
193 TAILQ_FOREACH(lwalk
, &fehead
, f
) {
194 if (strcmp(lwalk
->filename
, filename
) == 0)
201 /* add a new entry to the list for `file' */
203 add_new_entry(veriexecgen_t
*vp
, FTSENT
*file
, hash_t
*hash
)
208 if (file
->fts_info
== FTS_SL
) {
209 /* we have a symbolic link */
210 if (stat(file
->fts_path
, &sb
) == -1) {
211 gripe(vp
, "Cannot stat symlink `%s'", file
->fts_path
);
215 sb
= *file
->fts_statp
;
217 if (!vp
->all_files
&& !vp
->scan_system_dirs
&& !IS_EXEC(sb
.st_mode
))
220 e
= ecalloc(1UL, sizeof(*e
));
222 if (realpath(file
->fts_accpath
, e
->filename
) == NULL
) {
223 gripe(vp
, "Cannot find absolute path `%s'", file
->fts_accpath
);
226 if (check_dup(e
->filename
)) {
230 if ((e
->hash_val
= do_hash(e
->filename
, hash
)) == NULL
) {
231 gripe(vp
, "Cannot calculate hash `%s'", e
->filename
);
234 e
->flags
= figure_flags(e
->filename
, sb
.st_mode
);
236 TAILQ_INSERT_TAIL(&fehead
, e
, f
);
239 /* walk through a directory */
241 walk_dir(veriexecgen_t
*vp
, char **search_path
, hash_t
*hash
)
246 if ((fh
= fts_open(search_path
, FTS_PHYSICAL
, NULL
)) == NULL
) {
247 gripe(vp
, "fts_open `%s'", (const char *)search_path
);
251 while ((file
= fts_read(fh
)) != NULL
) {
252 if (!vp
->recursive_scan
&& file
->fts_level
> 1) {
253 fts_set(fh
, file
, FTS_SKIP
);
257 switch (file
->fts_info
) {
266 if (file
->fts_errno
) {
267 if (vp
->exit_on_error
) {
268 errx(EXIT_FAILURE
, "%s: %s", file
->fts_path
,
269 strerror(file
->fts_errno
));
272 add_new_entry(vp
, file
, hash
);
279 /* return a string representation of the flags */
283 return (flags
== 0) ? "" : "file, indirect";
287 escape(const char *s
)
293 if (len
>= MAXPATHLEN
)
297 q
= p
= calloc(1, len
+ 1);
300 if (*s
== ' ' || *s
== '\t')
309 /* store the list in the signatures file */
311 store_entries(veriexecgen_t
*vp
, hash_t
*hash
)
315 char old_dbfile
[MAXPATHLEN
];
321 if (stat(vp
->dbfile
, &sb
) != 0) {
325 err(EXIT_FAILURE
, "could not stat %s", vp
->dbfile
);
327 if (move
&& !vp
->append_output
) {
329 (void)printf("\nBacking up existing fingerprint file "
330 "to \"%s.old\"\n\n", vp
->dbfile
);
332 if (snprintf(old_dbfile
, sizeof(old_dbfile
), "%s.old",
333 vp
->dbfile
) < strlen(vp
->dbfile
) + 4) {
334 err(EXIT_FAILURE
, "%s", old_dbfile
);
336 if (rename(vp
->dbfile
, old_dbfile
) == -1)
337 err(EXIT_FAILURE
, "could not rename file");
340 prefixc
= (vp
->prefix
== NULL
) ? -1 : strlen(vp
->prefix
);
342 fp
= efopen(vp
->dbfile
, vp
->append_output
? "a" : "w+");
346 (void)fprintf(fp
, "# Generated by %s, %.24s\n",
347 getlogin(), ctime(&ct
));
350 TAILQ_FOREACH(e
, &fehead
, f
) {
354 (void)printf("Adding %s.\n", e
->filename
);
356 file
= (prefixc
< 0) ? e
->filename
: &e
->filename
[prefixc
];
359 (void)fprintf(fp
, "%s %s %s %s\n", file
, hash
->hashname
,
360 e
->hash_val
, flags2str(e
->flags
));
369 "#############################################################\n"
370 " PLEASE VERIFY CONTENTS OF %s AND FINE-TUNE THE\n"
371 " FLAGS WHERE APPROPRIATE AFTER READING veriexecctl(8)\n"
372 "#############################################################\n",
378 main(int argc
, char **argv
)
381 char **search_path
= NULL
;
385 (void) memset(&v
, 0x0, sizeof(v
));
389 /* error out if we have a dangling symlink or other fs problem */
392 while ((ch
= getopt(argc
, argv
, "AaDd:ho:p:rSTt:vW")) != -1) {
401 v
.scan_system_dirs
= 1;
404 search_path
= erealloc(search_path
, sizeof(char *) *
406 search_path
[total
] = optarg
;
407 search_path
[++total
] = NULL
;
424 v
.recursive_scan
= 1;
433 if ((hash
= find_hash(optarg
)) == NULL
) {
435 "No such hash algorithm (%s)",
451 if (v
.dbfile
== NULL
)
452 v
.dbfile
= DEFAULT_DBFILE
;
455 if ((hash
= find_hash(DEFAULT_HASH
)) == NULL
)
456 errx(EXIT_FAILURE
, "No hash algorithm");
461 if (search_path
== NULL
)
462 v
.scan_system_dirs
= 1;
464 if (v
.scan_system_dirs
) {
465 char *sys_paths
[] = DEFAULT_SYSPATHS
;
468 banner(&v
, hash
, sys_paths
);
469 walk_dir(&v
, sys_paths
, hash
);
472 if (search_path
!= NULL
) {
474 banner(&v
, hash
, search_path
);
475 walk_dir(&v
, search_path
, hash
);
478 store_entries(&v
, hash
);
480 if (make_immutable
&& chflags(v
.dbfile
, SF_IMMUTABLE
) != 0)
481 err(EXIT_FAILURE
, "Can't set immutable flag");