Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2013 Matthew Seaman <matthew@FreeBSD.org>
4 : : * Copyright (c) 2012-2013 Bryan Drewery <bdrewery@FreeBSD.org>
5 : : * Copyright (c) 2016 Vsevolod Stakhov <vsevolod@FreeBSD.org>
6 : : * All rights reserved.
7 : : *
8 : : * Redistribution and use in source and binary forms, with or without
9 : : * modification, are permitted provided that the following conditions
10 : : * are met:
11 : : * 1. Redistributions of source code must retain the above copyright
12 : : * notice, this list of conditions and the following disclaimer
13 : : * in this position and unchanged.
14 : : * 2. Redistributions in binary form must reproduce the above copyright
15 : : * notice, this list of conditions and the following disclaimer in the
16 : : * documentation and/or other materials provided with the distribution.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
19 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
22 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 : : */
29 : :
30 : : #include "pkg_config.h"
31 : :
32 : : #include <sys/types.h>
33 : : #include <sys/socket.h>
34 : : #include <sys/wait.h>
35 : : #include <err.h>
36 : : #include <getopt.h>
37 : : #include <stdio.h>
38 : : #include <unistd.h>
39 : : #include <errno.h>
40 : : #include <signal.h>
41 : : #include <xstring.h>
42 : : #include <pkghash.h>
43 : : #include <pkg.h>
44 : :
45 : : #ifdef HAVE_SYS_CAPSICUM_H
46 : : #include <sys/capsicum.h>
47 : : #endif
48 : :
49 : : #ifdef HAVE_CAPSICUM
50 : : #include <sys/capsicum.h>
51 : : #endif
52 : : #include "pkgcli.h"
53 : : #include <pkg/audit.h>
54 : :
55 : : static const char vuln_end_lit[] = "**END**";
56 : :
57 : : void
58 : 0 : usage_upgrade(void)
59 : : {
60 : 0 : fprintf(stderr, "Usage: pkg upgrade [-fInFqUy] [-r reponame] [-Cgix] <pkg-name> ...\n\n");
61 : 0 : fprintf(stderr, "For more information see 'pkg help upgrade'.\n");
62 : 0 : }
63 : :
64 : : static void
65 : 0 : add_to_check(pkghash *check, struct pkg *pkg)
66 : : {
67 : 0 : const char *uid = NULL;
68 : :
69 : 0 : pkg_get(pkg, PKG_ATTR_UNIQUEID, &uid);
70 [ # # # # ]: 0 : pkghash_safe_add(check, uid, pkg, NULL);
71 : 0 : }
72 : :
73 : : static void
74 : 0 : check_vulnerable(struct pkg_audit *audit, struct pkgdb *db, int sock)
75 : : {
76 : : struct pkg_audit_issues *issues;
77 : 0 : struct pkgdb_it *it = NULL;
78 : 0 : struct pkg *pkg = NULL;
79 : 0 : pkghash *check = NULL;
80 : : pkghash_it hit;
81 : : const char *uid;
82 : : FILE *out;
83 : :
84 : 0 : out = fdopen(sock, "w");
85 [ # # ]: 0 : if (out == NULL) {
86 : 0 : warn("unable to open stream");
87 : 0 : return;
88 : : }
89 : :
90 [ # # ]: 0 : if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
91 : 0 : warnx("Error accessing the package database");
92 : 0 : pkg_audit_free(audit);
93 : 0 : fclose(out);
94 : 0 : return;
95 : : }
96 : 0 : check = pkghash_new();
97 : :
98 [ # # ]: 0 : while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_RDEPS) == EPKG_OK) {
99 : 0 : if (pkg_type(pkg) == PKG_INSTALLED) {
100 : 0 : add_to_check(check, pkg);
101 : 0 : pkg = NULL;
102 : 0 : }
103 : : }
104 : :
105 : 0 : pkgdb_it_free(it);
106 : 0 : pkgdb_close(db);
107 : :
108 [ # # ]: 0 : if (check == NULL)
109 : 0 : goto out_cleanup;
110 : :
111 [ # # ]: 0 : if (pkg_audit_load(audit, NULL) != EPKG_OK) {
112 : 0 : warn("unable to open vulnxml file");
113 : 0 : goto out_cleanup;
114 : : }
115 : :
116 : 0 : pkg_drop_privileges();
117 : :
118 : : #ifdef HAVE_CAPSICUM
119 : : #ifndef PKG_COVERAGE
120 : : if (cap_enter() < 0 && errno != ENOSYS) {
121 : : warn("cap_enter() failed");
122 : : goto out_cleanup;
123 : : }
124 : : #endif
125 : : #endif
126 : :
127 [ # # ]: 0 : if (pkg_audit_process(audit) == EPKG_OK) {
128 : 0 : hit = pkghash_iterator(check);
129 [ # # ]: 0 : while (pkghash_next(&hit)) {
130 : 0 : issues = NULL;
131 : 0 : pkg = (struct pkg *)hit.value;
132 : 0 : if (pkg_audit_is_vulnerable(audit, pkg, &issues, true)) {
133 : 0 : pkg_get(pkg, PKG_ATTR_UNIQUEID, &uid);
134 : 0 : fprintf(out, "%s\n", uid);
135 : 0 : fflush(out);
136 : 0 : }
137 : 0 : pkg_audit_issues_free(issues);
138 : 0 : pkg_free(pkg);
139 : : }
140 : :
141 : 0 : fprintf(out, "%s\n", vuln_end_lit);
142 : 0 : fflush(out);
143 : 0 : } else {
144 : 0 : warnx("cannot process vulnxml");
145 : : }
146 : :
147 : : out_cleanup:
148 : 0 : pkg_audit_free(audit);
149 : 0 : pkghash_destroy(check);
150 : 0 : fclose(out);
151 : 0 : }
152 : :
153 : : static int
154 : 0 : add_vulnerable_upgrades(struct pkg_jobs *jobs, struct pkgdb *db)
155 : : {
156 : 0 : int sp[2], retcode, ret = EPKG_FATAL;
157 : : pid_t cld;
158 : : FILE *in;
159 : : struct pkg_audit *audit;
160 : 0 : char *line = NULL;
161 : 0 : size_t linecap = 0;
162 : : ssize_t linelen;
163 : :
164 : : /* Fetch audit file */
165 [ # # ]: 0 : if (pkg_audit_fetch(NULL, NULL) != EPKG_OK)
166 : 0 : return (EXIT_FAILURE);
167 : :
168 : : /* Create socketpair to execute audit check in a detached mode */
169 [ # # ]: 0 : if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1) {
170 : 0 : warnx("Cannot create socketpair");
171 : :
172 : 0 : return (EPKG_FATAL);
173 : : }
174 : :
175 : 0 : audit = pkg_audit_new();
176 : 0 : cld = fork();
177 : :
178 [ # # # ]: 0 : switch (cld) {
179 : : case 0:
180 : 0 : close(sp[1]);
181 : 0 : check_vulnerable(audit, db, sp[0]);
182 : 0 : close(sp[0]);
183 : 0 : _exit(EXIT_SUCCESS);
184 : : break;
185 : : case -1:
186 : 0 : warnx("Cannot fork");
187 : 0 : pkg_audit_free(audit);
188 : 0 : return (EPKG_FATAL);
189 : : default:
190 : : /* Parent code */
191 : 0 : close(sp[0]);
192 : 0 : pkg_audit_free(audit);
193 : 0 : in = fdopen(sp[1], "r");
194 : :
195 [ # # ]: 0 : if (in == NULL) {
196 : 0 : warnx("Cannot create stream");
197 : 0 : close(sp[1]);
198 : :
199 : 0 : return (EPKG_FATAL);
200 : : }
201 : 0 : break;
202 : : }
203 : :
204 : 0 : while ((linelen = getline(&line, &linecap, in)) > 0) {
205 : 0 : if (line[linelen - 1] == '\n') {
206 : 0 : line[linelen - 1] = '\0';
207 : 0 : }
208 : :
209 [ # # ]: 0 : if (STREQ(line, vuln_end_lit)) {
210 : 0 : ret = EPKG_OK;
211 : 0 : break;
212 : : }
213 : :
214 : 0 : if (pkg_jobs_add(jobs, MATCH_EXACT, &line, 1) == EPKG_FATAL) {
215 : 0 : warnx("Cannot update %s which is vulnerable", line);
216 : : /* TODO: assume it non-fatal for now */
217 : 0 : }
218 : : }
219 : :
220 : 0 : free(line);
221 : :
222 : 0 : fclose(in);
223 : :
224 [ # # ]: 0 : while (waitpid(cld, &retcode, 0) == -1) {
225 [ # # ]: 0 : if (errno != EINTR) {
226 : 0 : warnx("Cannot wait");
227 : 0 : return (EPKG_FATAL);
228 : : }
229 : : }
230 : :
231 : 0 : if (ret != EPKG_OK) {
232 : 0 : warn("Cannot get the complete list of vulnerable packages");
233 : 0 : }
234 : :
235 : 0 : return (ret);
236 : 0 : }
237 : :
238 : : int
239 : 35 : exec_upgrade(int argc, char **argv)
240 : : {
241 : 35 : struct pkgdb *db = NULL;
242 : 35 : struct pkg_jobs *jobs = NULL;
243 : : int retcode;
244 : : int updcode;
245 : : int ch;
246 : 35 : int lock_type = PKGDB_LOCK_ADVISORY;
247 : 35 : match_t match = MATCH_EXACT;
248 : 35 : int done = 0;
249 : 35 : bool rc = true;
250 : 35 : pkg_flags f = PKG_FLAG_NONE | PKG_FLAG_PKG_VERSION_TEST;
251 : : c_charv_t reponames;
252 : :
253 : 35 : struct option longopts[] = {
254 : : { "case-sensitive", no_argument, NULL, 'C' },
255 : : { "force", no_argument, NULL, 'f' },
256 : : { "fetch-only", no_argument, NULL, 'F' },
257 : : { "glob", no_argument, NULL, 'g' },
258 : : { "case-insensitive", no_argument, NULL, 'i' },
259 : : { "no-scripts", no_argument, NULL, 'I' },
260 : : { "dry-run", no_argument, NULL, 'n' },
261 : : { "quiet", no_argument, NULL, 'q' },
262 : : { "repository", required_argument, NULL, 'r' },
263 : : { "no-repo-update", no_argument, NULL, 'U' },
264 : : { "regex", no_argument, NULL, 'x' },
265 : : { "yes", no_argument, NULL, 'y' },
266 : : { "vulnerable", no_argument, NULL, 'v' },
267 : : { NULL, 0, NULL, 0 },
268 : : };
269 : :
270 : 35 : nbactions = nbdone = 0;
271 : 35 : pkgvec_init(&reponames);
272 : :
273 [ + + ]: 81 : while ((ch = getopt_long(argc, argv, "+CfFgiInqr:Uxyv", longopts, NULL)) != -1) {
274 [ - + - - : 46 : switch (ch) {
- - + + -
- + + -
- ]
275 : : case 'C':
276 : 0 : pkgdb_set_case_sensitivity(true);
277 : 0 : break;
278 : : case 'f':
279 : 1 : f |= PKG_FLAG_FORCE;
280 : 1 : break;
281 : : case 'F':
282 : 0 : f |= PKG_FLAG_SKIP_INSTALL;
283 : 0 : lock_type = PKGDB_LOCK_READONLY;
284 : 0 : break;
285 : : case 'g':
286 : 0 : match = MATCH_GLOB;
287 : 0 : break;
288 : : case 'i':
289 : 0 : pkgdb_set_case_sensitivity(false);
290 : 0 : break;
291 : : case 'I':
292 : 0 : f |= PKG_FLAG_NOSCRIPT;
293 : 0 : break;
294 : : case 'n':
295 : 2 : f |= PKG_FLAG_DRY_RUN;
296 : 2 : lock_type = PKGDB_LOCK_READONLY;
297 : 2 : dry_run = true;
298 : 2 : break;
299 : : case 'q':
300 : 6 : quiet = true;
301 : 6 : break;
302 : : case 'r':
303 [ # # # # ]: 0 : pkgvec_push(&reponames, optarg);
304 : 0 : break;
305 : : case 'U':
306 : 0 : auto_update = false;
307 : 0 : break;
308 : : case 'x':
309 : 3 : match = MATCH_REGEX;
310 : 3 : break;
311 : : case 'y':
312 : 34 : yes = true;
313 : 34 : break;
314 : : case 'v':
315 : 0 : f |= PKG_FLAG_UPGRADE_VULNERABLE;
316 : 0 : break;
317 : : default:
318 : 0 : usage_upgrade();
319 : 0 : return (EXIT_FAILURE);
320 : : /* NOTREACHED */
321 : : }
322 : : }
323 : 35 : argc -= optind;
324 : 35 : argv += optind;
325 : :
326 : 35 : if (dry_run && !auto_update)
327 : 0 : retcode = pkgdb_access2(PKGDB_MODE_READ,
328 : : PKGDB_DB_LOCAL|PKGDB_DB_REPO, &reponames);
329 : : else
330 : 35 : retcode = pkgdb_access2(PKGDB_MODE_READ |
331 : : PKGDB_MODE_WRITE |
332 : : PKGDB_MODE_CREATE,
333 : : PKGDB_DB_LOCAL|PKGDB_DB_REPO, &reponames);
334 : 35 : if (retcode == EPKG_ENOACCESS && dry_run) {
335 : 0 : auto_update = false;
336 : 0 : retcode = pkgdb_access2(PKGDB_MODE_READ,
337 : : PKGDB_DB_LOCAL|PKGDB_DB_REPO, &reponames);
338 : 0 : }
339 : :
340 [ - + ]: 35 : if (retcode == EPKG_ENOACCESS) {
341 : 0 : warnx("Insufficient privilege to upgrade packages");
342 : 0 : return (EXIT_FAILURE);
343 [ - + ]: 35 : } else if (retcode != EPKG_OK)
344 : 0 : return (EXIT_FAILURE);
345 : : else
346 : 35 : retcode = EXIT_FAILURE;
347 : :
348 : : /* first update the remote repositories if needed */
349 : 35 : if (auto_update &&
350 : 35 : (updcode = pkgcli_update(false, false, &reponames)) != EPKG_OK)
351 : 0 : return (updcode);
352 : :
353 [ - + ]: 35 : if (pkgdb_open_all2(&db, PKGDB_REMOTE, &reponames) != EPKG_OK)
354 : 0 : return (EXIT_FAILURE);
355 : :
356 [ - + ]: 35 : if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
357 : 0 : pkgdb_close(db);
358 : 0 : warnx("Cannot get an advisory lock on a database, it is locked by another process");
359 : 0 : return (EXIT_FAILURE);
360 : : }
361 : :
362 [ - + ]: 35 : if (pkg_jobs_new(&jobs, PKG_JOBS_UPGRADE, db) != EPKG_OK)
363 : 0 : goto cleanup;
364 : :
365 : 35 : if (reponames.len > 0 && pkg_jobs_set_repositories(jobs, &reponames) != EPKG_OK)
366 : 0 : goto cleanup;
367 : :
368 : 8 : pkg_jobs_set_flags(jobs, f);
369 : :
370 : 8 : if (argc > 0)
371 [ - + ]: 8 : if (pkg_jobs_add(jobs, match, argv, argc) == EPKG_FATAL)
372 : 0 : goto cleanup;
373 : :
374 : 16 : if (f & PKG_FLAG_UPGRADE_VULNERABLE) {
375 : : /* We need to load audit info and add packages that are vulnerable */
376 [ # # ]: 0 : if (add_vulnerable_upgrades(jobs, db) != EPKG_OK) {
377 : 0 : goto cleanup;
378 : : }
379 : 0 : }
380 : :
381 [ - + ]: 35 : if (pkg_jobs_solve(jobs) != EPKG_OK)
382 : 0 : goto cleanup;
383 : :
384 : 35 : while ((nbactions = pkg_jobs_count(jobs)) > 0) {
385 : : /* print a summary before applying the jobs */
386 : 31 : rc = yes;
387 : 31 : if (!quiet || dry_run) {
388 : 50 : print_jobs_summary(jobs,
389 : : "The following %d package(s) will be affected (of %d checked):\n\n",
390 : 25 : nbactions, pkg_jobs_total(jobs));
391 : :
392 [ + + ]: 25 : if (!dry_run) {
393 : 23 : rc = query_yesno(false, "\nProceed with this "
394 : : "action? ");
395 : 23 : } else {
396 : 2 : rc = false;
397 : : }
398 : 25 : }
399 : :
400 : 31 : if (rc) {
401 : 29 : retcode = pkg_jobs_apply(jobs);
402 : 29 : done = 1;
403 [ - + ]: 29 : if (retcode == EPKG_CONFLICT) {
404 : 0 : printf("Conflicts with the existing packages "
405 : : "have been found.\nOne more solver "
406 : : "iteration is needed to resolve them.\n");
407 : 0 : continue;
408 : : }
409 [ + + ]: 29 : else if (retcode != EPKG_OK)
410 : 1 : goto cleanup;
411 : 28 : }
412 : :
413 : 51 : if (messages != NULL) {
414 : 5 : fflush(messages->fp);
415 : 5 : printf("%s", messages->buf);
416 : 5 : }
417 : 30 : break;
418 : : }
419 : :
420 : 54 : if (done == 0 && rc && !quiet)
421 : 4 : printf("Your packages are up to date.\n");
422 : :
423 [ + + ]: 68 : if (rc)
424 : 32 : retcode = EXIT_SUCCESS;
425 : : else
426 : 2 : retcode = EXIT_FAILURE;
427 : :
428 : : cleanup:
429 : 35 : pkg_jobs_free(jobs);
430 : 35 : pkgdb_release_lock(db, lock_type);
431 : 35 : pkgdb_close(db);
432 : :
433 : 35 : if (!dry_run)
434 : 33 : pkg_cache_full_clean();
435 : :
436 : 64 : if (!rc && newpkgversion)
437 : 0 : newpkgversion = false;
438 : :
439 : 35 : return (retcode);
440 : 35 : }
|