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