Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/src/upgrade.c
2645 views
1
/*-
2
* Copyright (c) 2011-2012 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2013 Matthew Seaman <[email protected]>
4
* Copyright (c) 2012-2013 Bryan Drewery <[email protected]>
5
* Copyright (c) 2016 Vsevolod Stakhov <[email protected]>
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] [-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
bool rc = true;
248
pkg_flags f = PKG_FLAG_NONE | PKG_FLAG_PKG_VERSION_TEST;
249
c_charv_t reponames = vec_init();
250
251
struct option longopts[] = {
252
{ "case-sensitive", no_argument, NULL, 'C' },
253
{ "force", no_argument, NULL, 'f' },
254
{ "fetch-only", no_argument, NULL, 'F' },
255
{ "glob", no_argument, NULL, 'g' },
256
{ "case-insensitive", no_argument, NULL, 'i' },
257
{ "no-scripts", no_argument, NULL, 'I' },
258
{ "script-no-exec", no_argument, &scriptnoexec, 1 },
259
{ "dry-run", no_argument, NULL, 'n' },
260
{ "quiet", no_argument, NULL, 'q' },
261
{ "repository", required_argument, NULL, 'r' },
262
{ "no-repo-update", no_argument, NULL, 'U' },
263
{ "regex", no_argument, NULL, 'x' },
264
{ "yes", no_argument, NULL, 'y' },
265
{ "vulnerable", no_argument, NULL, 'v' },
266
{ NULL, 0, NULL, 0 },
267
};
268
269
while ((ch = getopt_long(argc, argv, "+CfFgiInqr:Uxyv", longopts, NULL)) != -1) {
270
switch (ch) {
271
case 'C':
272
pkgdb_set_case_sensitivity(true);
273
break;
274
case 'f':
275
f |= PKG_FLAG_FORCE;
276
break;
277
case 'F':
278
f |= PKG_FLAG_SKIP_INSTALL;
279
lock_type = PKGDB_LOCK_READONLY;
280
break;
281
case 'g':
282
match = MATCH_GLOB;
283
break;
284
case 'i':
285
pkgdb_set_case_sensitivity(false);
286
break;
287
case 'I':
288
f |= PKG_FLAG_NOSCRIPT;
289
break;
290
case 'n':
291
f |= PKG_FLAG_DRY_RUN;
292
lock_type = PKGDB_LOCK_READONLY;
293
dry_run = true;
294
break;
295
case 'q':
296
quiet = true;
297
break;
298
case 'r':
299
vec_push(&reponames, optarg);
300
break;
301
case 'U':
302
auto_update = false;
303
break;
304
case 'x':
305
match = MATCH_REGEX;
306
break;
307
case 'y':
308
yes = true;
309
break;
310
case 'v':
311
f |= PKG_FLAG_UPGRADE_VULNERABLE;
312
break;
313
case 0:
314
if (scriptnoexec == 1)
315
f |= PKG_FLAG_NOEXEC;
316
break;
317
default:
318
usage_upgrade();
319
return (EXIT_FAILURE);
320
/* NOTREACHED */
321
}
322
}
323
argc -= optind;
324
argv += optind;
325
326
if (dry_run && !auto_update)
327
retcode = pkgdb_access2(PKGDB_MODE_READ,
328
PKGDB_DB_LOCAL|PKGDB_DB_REPO, &reponames);
329
else
330
retcode = pkgdb_access2(PKGDB_MODE_READ |
331
PKGDB_MODE_WRITE |
332
PKGDB_MODE_CREATE,
333
PKGDB_DB_LOCAL|PKGDB_DB_REPO, &reponames);
334
if (retcode == EPKG_ENOACCESS && dry_run) {
335
auto_update = false;
336
retcode = pkgdb_access2(PKGDB_MODE_READ,
337
PKGDB_DB_LOCAL|PKGDB_DB_REPO, &reponames);
338
}
339
340
if (retcode == EPKG_ENOACCESS) {
341
warnx("Insufficient privilege to upgrade packages");
342
return (EXIT_FAILURE);
343
} else if (retcode != EPKG_OK)
344
return (EXIT_FAILURE);
345
else
346
retcode = EXIT_FAILURE;
347
348
/* first update the remote repositories if needed */
349
if (auto_update &&
350
(updcode = pkgcli_update(false, false, &reponames)) != EPKG_OK)
351
return (updcode);
352
353
if (pkgdb_open_all2(&db, PKGDB_REMOTE, &reponames) != EPKG_OK)
354
return (EXIT_FAILURE);
355
356
if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
357
pkgdb_close(db);
358
warnx("Cannot get an advisory lock on a database, it is locked by another process");
359
return (EXIT_FAILURE);
360
}
361
362
if (pkg_jobs_new(&jobs, PKG_JOBS_UPGRADE, db) != EPKG_OK)
363
goto cleanup;
364
365
if (reponames.len > 0 && pkg_jobs_set_repositories(jobs, &reponames) != EPKG_OK)
366
goto cleanup;
367
368
pkg_jobs_set_flags(jobs, f);
369
370
if (argc > 0)
371
if (pkg_jobs_add(jobs, match, argv, argc) == EPKG_FATAL)
372
goto cleanup;
373
374
if (f & PKG_FLAG_UPGRADE_VULNERABLE) {
375
/* We need to load audit info and add packages that are vulnerable */
376
if (add_vulnerable_upgrades(jobs, db) != EPKG_OK) {
377
goto cleanup;
378
}
379
}
380
381
if (pkg_jobs_solve(jobs) != EPKG_OK)
382
goto cleanup;
383
384
while ((nbactions = pkg_jobs_count(jobs)) > 0) {
385
/* print a summary before applying the jobs */
386
rc = yes;
387
if (!quiet || dry_run) {
388
print_jobs_summary(jobs,
389
"The following %d package(s) will be affected (of %d checked):\n\n",
390
nbactions, pkg_jobs_total(jobs));
391
392
if (!dry_run) {
393
rc = query_yesno(false, "\nProceed with this "
394
"action? ");
395
} else {
396
rc = false;
397
}
398
}
399
400
if (rc) {
401
retcode = pkg_jobs_apply(jobs);
402
done = 1;
403
if (retcode == EPKG_CONFLICT) {
404
printf("Conflicts with the existing packages "
405
"have been found.\nOne more solver "
406
"iteration is needed to resolve them.\n");
407
continue;
408
}
409
else if (retcode != EPKG_OK)
410
goto cleanup;
411
}
412
413
if (messages != NULL) {
414
fflush(messages->fp);
415
printf("%s", messages->buf);
416
}
417
break;
418
}
419
420
if (done == 0 && rc && !quiet)
421
printf("Your packages are up to date.\n");
422
423
if (rc || dry_run)
424
retcode = EXIT_SUCCESS;
425
else
426
retcode = EXIT_FAILURE;
427
428
cleanup:
429
pkg_jobs_free(jobs);
430
pkgdb_release_lock(db, lock_type);
431
pkgdb_close(db);
432
433
if (!dry_run)
434
pkg_cache_full_clean();
435
436
if (!rc && newpkgversion)
437
newpkgversion = false;
438
439
return (retcode);
440
}
441
442