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