Line data Source code
1 : /*-
2 : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
3 : * Copyright (c) 2014-2015 Matthew Seaman <matthew@FreeBSD.org>
4 : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
5 : * All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer
12 : * in this position and unchanged.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : */
28 :
29 : #include "pkg_config.h"
30 :
31 : #include <sys/param.h>
32 : #include <sys/queue.h>
33 : #include <sys/stat.h>
34 : #include <sys/mman.h>
35 :
36 : #define _WITH_GETLINE
37 :
38 : #include <archive.h>
39 : #include <err.h>
40 : #include <errno.h>
41 : #include <fcntl.h>
42 : #include <fnmatch.h>
43 : #include <getopt.h>
44 : #include <stdbool.h>
45 : #include <stdio.h>
46 : #include <string.h>
47 : #include <unistd.h>
48 : #include <sysexits.h>
49 : #include <khash.h>
50 :
51 : #ifdef HAVE_SYS_CAPSICUM_H
52 : #include <sys/capsicum.h>
53 : #endif
54 :
55 : #ifdef HAVE_CAPSICUM
56 : #include <sys/capability.h>
57 : #endif
58 :
59 : #include <pkg.h>
60 : #include "pkgcli.h"
61 :
62 : void
63 0 : usage_audit(void)
64 : {
65 0 : fprintf(stderr, "Usage: pkg audit [-Fqr] [-f file] <pattern>\n\n");
66 0 : fprintf(stderr, "For more information see 'pkg help audit'.\n");
67 0 : }
68 :
69 0 : KHASH_MAP_INIT_STR(pkgs, struct pkg *);
70 :
71 : static void
72 0 : add_to_check(kh_pkgs_t *check, struct pkg *pkg)
73 : {
74 : const char *uid;
75 : int ret;
76 : khint_t k;
77 :
78 0 : pkg_get(pkg, PKG_UNIQUEID, &uid);
79 :
80 0 : k = kh_put_pkgs(check, uid, &ret);
81 0 : if (ret != 0)
82 0 : kh_value(check, k) = pkg;
83 0 : }
84 :
85 : static void
86 0 : print_recursive_rdeps(kh_pkgs_t *head, struct pkg *p, struct sbuf *sb,
87 : kh_pkgs_t *seen, bool top)
88 : {
89 0 : struct pkg_dep *dep = NULL;
90 : int ret;
91 : khint_t k, h;
92 :
93 0 : while(pkg_rdeps(p, &dep) == EPKG_OK) {
94 0 : const char *name = pkg_dep_get(dep, PKG_DEP_NAME);
95 :
96 0 : k = kh_get_pkgs(seen, name);
97 0 : if (k == kh_end(seen)) {
98 0 : h = kh_get_pkgs(head, name);
99 0 : if (h != kh_end(head)) {
100 0 : k = kh_put_pkgs(seen, name, &ret);
101 0 : if (!top)
102 0 : sbuf_cat(sb, ", ");
103 :
104 0 : sbuf_cat(sb, name);
105 :
106 0 : print_recursive_rdeps(head, kh_val(head, h), sb, seen, false);
107 :
108 0 : top = false;
109 : }
110 : }
111 : }
112 0 : }
113 :
114 : int
115 0 : exec_audit(int argc, char **argv)
116 : {
117 : struct pkg_audit *audit;
118 0 : struct pkgdb *db = NULL;
119 0 : struct pkgdb_it *it = NULL;
120 0 : struct pkg *pkg = NULL;
121 : const char *db_dir;
122 : char *name;
123 : char *version;
124 : char audit_file_buf[MAXPATHLEN];
125 0 : char *audit_file = audit_file_buf;
126 0 : unsigned int vuln = 0;
127 0 : bool fetch = false, recursive = false;
128 : int ch, i;
129 0 : int ret = EX_OK;
130 0 : const char *portaudit_site = NULL;
131 : struct sbuf *sb;
132 : const char *key;
133 0 : kh_pkgs_t *check = NULL;
134 :
135 0 : db_dir = pkg_object_string(pkg_config_get("PKG_DBDIR"));
136 0 : snprintf(audit_file_buf, sizeof(audit_file_buf), "%s/vuln.xml", db_dir);
137 :
138 0 : struct option longopts[] = {
139 : { "fetch", no_argument, NULL, 'F' },
140 : { "file", required_argument, NULL, 'f' },
141 : { "recursive", no_argument, NULL, 'r' },
142 : { "quiet", no_argument, NULL, 'q' },
143 : { NULL, 0, NULL, 0 },
144 : };
145 :
146 0 : while ((ch = getopt_long(argc, argv, "+Ff:qr", longopts, NULL)) != -1) {
147 0 : switch (ch) {
148 : case 'F':
149 0 : fetch = true;
150 0 : break;
151 : case 'f':
152 0 : audit_file = optarg;
153 0 : break;
154 : case 'q':
155 0 : quiet = true;
156 0 : break;
157 : case 'r':
158 0 : recursive = true;
159 0 : break;
160 : default:
161 0 : usage_audit();
162 0 : return(EX_USAGE);
163 : }
164 : }
165 0 : argc -= optind;
166 0 : argv += optind;
167 :
168 0 : audit = pkg_audit_new();
169 :
170 0 : if (fetch == true) {
171 0 : portaudit_site = pkg_object_string(pkg_config_get("VULNXML_SITE"));
172 0 : if (pkg_audit_fetch(portaudit_site, audit_file) != EPKG_OK) {
173 0 : pkg_audit_free(audit);
174 0 : return (EX_IOERR);
175 : }
176 : }
177 :
178 0 : if (pkg_audit_load(audit, audit_file) != EPKG_OK) {
179 0 : if (errno == ENOENT)
180 0 : warnx("vulnxml file %s does not exist. "
181 : "Try running 'pkg audit -F' first",
182 : audit_file);
183 : else
184 0 : warn("unable to open vulnxml file %s",
185 : audit_file);
186 :
187 0 : pkg_audit_free(audit);
188 0 : return (EX_DATAERR);
189 : }
190 :
191 0 : check = kh_init_pkgs();
192 0 : if (argc >= 1) {
193 0 : for (i = 0; i < argc; i ++) {
194 0 : name = argv[i];
195 0 : version = strrchr(name, '-');
196 0 : if (version != NULL) {
197 0 : version[0] = '\0';
198 0 : version++;
199 : }
200 0 : if (pkg_new(&pkg, PKG_FILE) != EPKG_OK)
201 0 : err(EX_OSERR, "malloc");
202 0 : if (version != NULL)
203 0 : pkg_set(pkg, PKG_NAME, name, PKG_VERSION, version);
204 : else
205 0 : pkg_set(pkg, PKG_NAME, name);
206 : /* Fake uniqueid */
207 0 : pkg_set(pkg, PKG_UNIQUEID, name);
208 0 : add_to_check(check, pkg);
209 0 : pkg = NULL;
210 : }
211 : }
212 : else {
213 :
214 : /*
215 : * if the database doesn't exist it just means there are no
216 : * packages to audit.
217 : */
218 :
219 0 : ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
220 0 : if (ret == EPKG_ENODB)
221 0 : return (EX_OK);
222 0 : else if (ret == EPKG_ENOACCESS) {
223 0 : warnx("Insufficient privileges to read the package database");
224 0 : return (EX_NOPERM);
225 0 : } else if (ret != EPKG_OK) {
226 0 : warnx("Error accessing the package database");
227 0 : return (EX_IOERR);
228 : }
229 :
230 0 : if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
231 0 : pkg_audit_free(audit);
232 0 : return (EX_IOERR);
233 : }
234 :
235 0 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
236 0 : pkgdb_close(db);
237 0 : pkg_audit_free(audit);
238 0 : warnx("Cannot get a read lock on a database, it is locked by another process");
239 0 : return (EX_TEMPFAIL);
240 : }
241 :
242 0 : if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
243 0 : warnx("Error accessing the package database");
244 0 : ret = EX_IOERR;
245 : }
246 : else {
247 0 : while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_RDEPS))
248 : == EPKG_OK) {
249 0 : add_to_check(check, pkg);
250 0 : pkg = NULL;
251 : }
252 0 : ret = EX_OK;
253 : }
254 0 : if (db != NULL) {
255 0 : if (it != NULL)
256 0 : pkgdb_it_free(it);
257 0 : pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
258 0 : pkgdb_close(db);
259 : }
260 0 : if (ret != EX_OK) {
261 0 : pkg_audit_free(audit);
262 0 : return (ret);
263 : }
264 : }
265 :
266 : /* Now we have vulnxml loaded and check list formed */
267 : #ifdef HAVE_CAPSICUM
268 0 : if (cap_enter() < 0 && errno != ENOSYS) {
269 0 : warn("cap_enter() failed");
270 0 : pkg_audit_free(audit);
271 0 : return (EPKG_FATAL);
272 : }
273 : #endif
274 :
275 0 : if (pkg_audit_process(audit) == EPKG_OK) {
276 0 : kh_foreach(check, key, pkg, {
277 : if (pkg_audit_is_vulnerable(audit, pkg, quiet, &sb)) {
278 : vuln ++;
279 : printf("%s", sbuf_data(sb));
280 :
281 : if (recursive) {
282 : const char *name;
283 : kh_pkgs_t *seen = kh_init_pkgs();
284 :
285 : pkg_get(pkg, PKG_NAME, &name);
286 : sbuf_clear(sb);
287 : sbuf_printf(sb, "Packages that depend on %s: ", name);
288 : print_recursive_rdeps(check, pkg , sb, seen, true);
289 : sbuf_finish(sb);
290 : printf("%s\n\n", sbuf_data(sb));
291 :
292 : kh_destroy_pkgs(seen);
293 : }
294 : sbuf_delete(sb);
295 : }
296 : pkg_free(pkg);
297 : });
298 0 : kh_destroy_pkgs(check);
299 :
300 0 : if (ret == EPKG_END && vuln == 0)
301 0 : ret = EX_OK;
302 :
303 0 : if (!quiet)
304 0 : printf("%u problem(s) in the installed packages found.\n", vuln);
305 : }
306 : else {
307 0 : warnx("cannot process vulnxml");
308 0 : ret = EX_SOFTWARE;
309 : }
310 :
311 0 : pkg_audit_free(audit);
312 0 : if (vuln != 0)
313 0 : ret = EXIT_FAILURE;
314 :
315 0 : return (ret);
316 : }
|