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 : : const char *uid;
68 : :
69 : 0 : pkg_get(pkg, PKG_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 : pkg_audit_free(audit);
110 : 0 : pkghash_destroy(check);
111 : 0 : fclose(out);
112 : 0 : return;
113 : : }
114 : :
115 : :
116 [ # # ]: 0 : if (pkg_audit_load(audit, NULL) != EPKG_OK) {
117 : 0 : warn("unable to open vulnxml file");
118 : 0 : fclose(out);
119 : 0 : pkg_audit_free(audit);
120 : 0 : pkghash_destroy(check);
121 : 0 : return;
122 : : }
123 : :
124 : 0 : drop_privileges();
125 : :
126 : : #ifdef HAVE_CAPSICUM
127 : : #ifndef PKG_COVERAGE
128 : : if (cap_enter() < 0 && errno != ENOSYS) {
129 : : warn("cap_enter() failed");
130 : : pkg_audit_free(audit);
131 : : pkghash_destroy(check);
132 : : fclose(out);
133 : : return;
134 : : }
135 : : #endif
136 : : #endif
137 : :
138 [ # # ]: 0 : if (pkg_audit_process(audit) == EPKG_OK) {
139 : 0 : hit = pkghash_iterator(check);
140 [ # # ]: 0 : while (pkghash_next(&hit)) {
141 : 0 : issues = NULL;
142 : 0 : pkg = (struct pkg *)hit.value;
143 : 0 : if (pkg_audit_is_vulnerable(audit, pkg, &issues, true)) {
144 : 0 : pkg_get(pkg, PKG_UNIQUEID, &uid);
145 : 0 : fprintf(out, "%s\n", uid);
146 : 0 : fflush(out);
147 : 0 : }
148 : 0 : pkg_audit_issues_free(issues);
149 : 0 : pkg_free(pkg);
150 : : }
151 : :
152 : 0 : fprintf(out, "%s\n", vuln_end_lit);
153 : 0 : fflush(out);
154 : 0 : } else {
155 : 0 : warnx("cannot process vulnxml");
156 : : }
157 : :
158 : 0 : pkg_audit_free(audit);
159 : 0 : pkghash_destroy(check);
160 : 0 : fclose(out);
161 : 0 : }
162 : :
163 : : static int
164 : 0 : add_vulnerable_upgrades(struct pkg_jobs *jobs, struct pkgdb *db)
165 : : {
166 : 0 : int sp[2], retcode, ret = EPKG_FATAL;
167 : : pid_t cld;
168 : : FILE *in;
169 : : struct pkg_audit *audit;
170 : 0 : char *line = NULL;
171 : 0 : size_t linecap = 0;
172 : : ssize_t linelen;
173 : :
174 : : /* Fetch audit file */
175 : : /* TODO: maybe, we can skip it somethimes? */
176 : 0 : audit = pkg_audit_new();
177 : :
178 [ # # ]: 0 : if (pkg_audit_fetch(NULL, NULL) != EPKG_OK) {
179 : 0 : pkg_audit_free(audit);
180 : 0 : return (EXIT_FAILURE);
181 : : }
182 : :
183 : : /* Create socketpair to execute audit check in a detached mode */
184 [ # # ]: 0 : if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1) {
185 : 0 : warnx("Cannot create socketpair");
186 : :
187 : 0 : return (EPKG_FATAL);
188 : : }
189 : :
190 : 0 : cld = fork();
191 : :
192 [ # # # ]: 0 : switch (cld) {
193 : : case 0:
194 : 0 : close(sp[1]);
195 : 0 : check_vulnerable(audit, db, sp[0]);
196 : 0 : close(sp[0]);
197 : 0 : _exit(EXIT_SUCCESS);
198 : : break;
199 : : case -1:
200 : 0 : warnx("Cannot fork");
201 : 0 : return (EPKG_FATAL);
202 : : default:
203 : : /* Parent code */
204 : 0 : close(sp[0]);
205 : 0 : pkg_audit_free(audit);
206 : 0 : in = fdopen(sp[1], "r");
207 : :
208 [ # # ]: 0 : if (in == NULL) {
209 : 0 : warnx("Cannot create stream");
210 : 0 : close(sp[1]);
211 : :
212 : 0 : return (EPKG_FATAL);
213 : : }
214 : 0 : break;
215 : : }
216 : :
217 : 0 : while ((linelen = getline(&line, &linecap, in)) > 0) {
218 : 0 : if (line[linelen - 1] == '\n') {
219 : 0 : line[linelen - 1] = '\0';
220 : 0 : }
221 : :
222 [ # # ]: 0 : if (strcmp(line, vuln_end_lit) == 0) {
223 : 0 : ret = EPKG_OK;
224 : 0 : break;
225 : : }
226 : :
227 : 0 : if (pkg_jobs_add(jobs, MATCH_EXACT, &line, 1) == EPKG_FATAL) {
228 : 0 : warnx("Cannot update %s which is vulnerable", line);
229 : : /* TODO: assume it non-fatal for now */
230 : 0 : }
231 : : }
232 : :
233 : 0 : fclose(in);
234 : :
235 [ # # ]: 0 : while (waitpid(cld, &retcode, 0) == -1) {
236 [ # # ]: 0 : if (errno == EINTR) {
237 : 0 : continue;
238 : : }
239 : : else {
240 : 0 : warnx("Cannot wait");
241 : :
242 : 0 : return (EPKG_FATAL);
243 : : }
244 : : }
245 : :
246 : 0 : if (ret != EPKG_OK) {
247 : 0 : warn("Cannot get the complete list of vulnerable packages");
248 : 0 : }
249 : :
250 : 0 : return (ret);
251 : 0 : }
252 : :
253 : : int
254 : 144 : exec_upgrade(int argc, char **argv)
255 : : {
256 : 144 : struct pkgdb *db = NULL;
257 : 144 : struct pkg_jobs *jobs = NULL;
258 : 144 : const char *reponame = NULL;
259 : : int retcode;
260 : : int updcode;
261 : : int ch;
262 : 144 : int lock_type = PKGDB_LOCK_ADVISORY;
263 : 144 : match_t match = MATCH_EXACT;
264 : 144 : int done = 0;
265 : 144 : bool rc = true;
266 : 144 : pkg_flags f = PKG_FLAG_NONE | PKG_FLAG_PKG_VERSION_TEST;
267 : :
268 : 144 : struct option longopts[] = {
269 : : { "case-sensitive", no_argument, NULL, 'C' },
270 : : { "force", no_argument, NULL, 'f' },
271 : : { "fetch-only", no_argument, NULL, 'F' },
272 : : { "glob", no_argument, NULL, 'g' },
273 : : { "case-insensitive", no_argument, NULL, 'i' },
274 : : { "no-scripts", no_argument, NULL, 'I' },
275 : : { "dry-run", no_argument, NULL, 'n' },
276 : : { "quiet", no_argument, NULL, 'q' },
277 : : { "repository", required_argument, NULL, 'r' },
278 : : { "no-repo-update", no_argument, NULL, 'U' },
279 : : { "regex", no_argument, NULL, 'x' },
280 : : { "yes", no_argument, NULL, 'y' },
281 : : { "vulnerable", no_argument, NULL, 'v' },
282 : : { NULL, 0, NULL, 0 },
283 : : };
284 : :
285 : 144 : nbactions = nbdone = 0;
286 : :
287 [ + + ]: 328 : while ((ch = getopt_long(argc, argv, "+CfFgiInqr:Uxyv", longopts, NULL)) != -1) {
288 [ - + - - : 184 : switch (ch) {
- - + + -
- + + -
- ]
289 : : case 'C':
290 : 0 : pkgdb_set_case_sensitivity(true);
291 : 0 : break;
292 : : case 'f':
293 : 4 : f |= PKG_FLAG_FORCE;
294 : 4 : break;
295 : : case 'F':
296 : 0 : f |= PKG_FLAG_SKIP_INSTALL;
297 : 0 : lock_type = PKGDB_LOCK_READONLY;
298 : 0 : break;
299 : : case 'g':
300 : 0 : match = MATCH_GLOB;
301 : 0 : break;
302 : : case 'i':
303 : 0 : pkgdb_set_case_sensitivity(false);
304 : 0 : break;
305 : : case 'I':
306 : 0 : f |= PKG_FLAG_NOSCRIPT;
307 : 0 : break;
308 : : case 'n':
309 : 8 : f |= PKG_FLAG_DRY_RUN;
310 : 8 : lock_type = PKGDB_LOCK_READONLY;
311 : 8 : dry_run = true;
312 : 8 : break;
313 : : case 'q':
314 : 20 : quiet = true;
315 : 20 : break;
316 : : case 'r':
317 : 0 : reponame = optarg;
318 : 0 : break;
319 : : case 'U':
320 : 0 : auto_update = false;
321 : 0 : break;
322 : : case 'x':
323 : 12 : match = MATCH_REGEX;
324 : 12 : break;
325 : : case 'y':
326 : 140 : yes = true;
327 : 140 : break;
328 : : case 'v':
329 : 0 : f |= PKG_FLAG_UPGRADE_VULNERABLE;
330 : 0 : break;
331 : : default:
332 : 0 : usage_upgrade();
333 : 0 : return (EXIT_FAILURE);
334 : : /* NOTREACHED */
335 : : }
336 : : }
337 : 144 : argc -= optind;
338 : 144 : argv += optind;
339 : :
340 : 144 : if (dry_run && !auto_update)
341 : 0 : retcode = pkgdb_access(PKGDB_MODE_READ,
342 : : PKGDB_DB_LOCAL|PKGDB_DB_REPO);
343 : : else
344 : 144 : retcode = pkgdb_access(PKGDB_MODE_READ |
345 : : PKGDB_MODE_WRITE |
346 : : PKGDB_MODE_CREATE,
347 : : PKGDB_DB_LOCAL|PKGDB_DB_REPO);
348 : 144 : if (retcode == EPKG_ENOACCESS && dry_run) {
349 : 0 : auto_update = false;
350 : 0 : retcode = pkgdb_access(PKGDB_MODE_READ,
351 : : PKGDB_DB_LOCAL|PKGDB_DB_REPO);
352 : 0 : }
353 : :
354 [ - + ]: 144 : if (retcode == EPKG_ENOACCESS) {
355 : 0 : warnx("Insufficient privilege to upgrade packages");
356 : 0 : return (EXIT_FAILURE);
357 [ - + ]: 144 : } else if (retcode != EPKG_OK)
358 : 0 : return (EXIT_FAILURE);
359 : : else
360 : 144 : retcode = EXIT_FAILURE;
361 : :
362 : : /* first update the remote repositories if needed */
363 : 144 : if (auto_update &&
364 : 144 : (updcode = pkgcli_update(false, false, reponame)) != EPKG_OK)
365 : 0 : return (updcode);
366 : :
367 [ - + ]: 144 : if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
368 : 0 : return (EXIT_FAILURE);
369 : :
370 [ - + ]: 144 : if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
371 : 0 : pkgdb_close(db);
372 : 0 : warnx("Cannot get an advisory lock on a database, it is locked by another process");
373 : 0 : return (EXIT_FAILURE);
374 : : }
375 : :
376 [ - + ]: 144 : if (pkg_jobs_new(&jobs, PKG_JOBS_UPGRADE, db) != EPKG_OK)
377 : 0 : goto cleanup;
378 : :
379 : 144 : if (reponame != NULL && pkg_jobs_set_repository(jobs, reponame) != EPKG_OK)
380 : 0 : goto cleanup;
381 : :
382 : 36 : pkg_jobs_set_flags(jobs, f);
383 : :
384 : 36 : if (argc > 0)
385 [ - + ]: 36 : if (pkg_jobs_add(jobs, match, argv, argc) == EPKG_FATAL)
386 : 0 : goto cleanup;
387 : :
388 : 72 : if (f & PKG_FLAG_UPGRADE_VULNERABLE) {
389 : : /* We need to load audit info and add packages that are vulnerable */
390 [ # # ]: 0 : if (add_vulnerable_upgrades(jobs, db) != EPKG_OK) {
391 : 0 : goto cleanup;
392 : : }
393 : 0 : }
394 : :
395 [ - + ]: 144 : if (pkg_jobs_solve(jobs) != EPKG_OK)
396 : 0 : goto cleanup;
397 : :
398 : 144 : while ((nbactions = pkg_jobs_count(jobs)) > 0) {
399 : : /* print a summary before applying the jobs */
400 : 119 : rc = yes;
401 : 119 : if (!quiet || dry_run) {
402 : 198 : print_jobs_summary(jobs,
403 : : "The following %d package(s) will be affected (of %d checked):\n\n",
404 : 99 : nbactions, pkg_jobs_total(jobs));
405 : :
406 [ + + ]: 99 : if (!dry_run) {
407 : 91 : rc = query_yesno(false, "\nProceed with this "
408 : : "action? ");
409 : 91 : } else {
410 : 8 : rc = false;
411 : : }
412 : 99 : }
413 : :
414 : 111 : if (rc) {
415 : 111 : retcode = pkg_jobs_apply(jobs);
416 : 111 : done = 1;
417 [ - + ]: 111 : if (retcode == EPKG_CONFLICT) {
418 : 0 : printf("Conflicts with the existing packages "
419 : : "have been found.\nOne more solver "
420 : : "iteration is needed to resolve them.\n");
421 : 0 : continue;
422 : : }
423 [ - + ]: 111 : else if (retcode != EPKG_OK)
424 : 0 : goto cleanup;
425 : 111 : }
426 : :
427 : 202 : if (messages != NULL) {
428 : 20 : fflush(messages->fp);
429 : 20 : printf("%s", messages->buf);
430 : 20 : }
431 : 119 : break;
432 : : }
433 : :
434 : 205 : if (done == 0 && rc && !quiet)
435 : 25 : printf("Your packages are up to date.\n");
436 : :
437 [ + + ]: 288 : if (rc)
438 : 136 : retcode = EXIT_SUCCESS;
439 : : else
440 : 8 : retcode = EXIT_FAILURE;
441 : :
442 : : cleanup:
443 : 144 : pkg_jobs_free(jobs);
444 : 144 : pkgdb_release_lock(db, lock_type);
445 : 144 : pkgdb_close(db);
446 : :
447 : 144 : if (!dry_run)
448 : 136 : pkg_cache_full_clean();
449 : :
450 : 264 : if (!rc && newpkgversion)
451 : 0 : newpkgversion = false;
452 : :
453 : 144 : return (retcode);
454 : 144 : }
|