Skip to content

Commit

Permalink
Add preliminary implementation of upgrade vulnerable
Browse files Browse the repository at this point in the history
With this option, pkg will download audit file and check vulnerable
packages to perform their upgrade.
  • Loading branch information
vstakhov authored and bapt committed Dec 22, 2016
1 parent 364fbb6 commit 2181d87
Showing 1 changed file with 210 additions and 2 deletions.
212 changes: 210 additions & 2 deletions src/upgrade.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,26 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <err.h>
#include <getopt.h>
#include <stdio.h>
#include <sysexits.h>
#include <unistd.h>

#include <errno.h>
#include <khash.h>
#include <utstring.h>
#include <pkg.h>

#ifdef HAVE_SYS_CAPSICUM_H
#include <sys/capsicum.h>
#endif

#ifdef HAVE_CAPSICUM
#include <sys/capability.h>
#endif
#include "pkgcli.h"

void
Expand All @@ -44,6 +56,191 @@ usage_upgrade(void)
fprintf(stderr, "For more information see 'pkg help upgrade'.\n");
}

KHASH_MAP_INIT_STR(pkgs, struct pkg *);

static void
add_to_check(kh_pkgs_t *check, struct pkg *pkg)
{
const char *uid;
int ret;
khint_t k;

pkg_get(pkg, PKG_UNIQUEID, &uid);

k = kh_put_pkgs(check, uid, &ret);
if (ret != 0)
kh_value(check, k) = pkg;
}

static void
check_vulnerable(struct pkg_audit *audit, struct pkgdb *db, int sock)
{
struct pkgdb_it *it = NULL;
struct pkg *pkg = NULL;
kh_pkgs_t *check = NULL;
const char *uid;
UT_string *sb;
int ret;
FILE *out;

out = fdopen (sock, "w");
if (out == NULL) {
warn("unable to open stream");
return;
}

drop_privileges();

if (pkg_audit_load(audit, NULL) != EPKG_OK) {
warn("unable to open vulnxml file");
fclose(out);
pkg_audit_free(audit);
return;
}

if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
warnx("Error accessing the package database");
pkg_audit_free(audit);
fclose(out);
return;
}
else {
while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_RDEPS))
== EPKG_OK) {
add_to_check(check, pkg);
pkg = NULL;
}
ret = EX_OK;
}

pkg_audit_free(audit);
if (db != NULL) {
pkgdb_it_free(it);
pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
pkgdb_close(db);
fclose(out);
}

if (ret != EX_OK) {
pkg_audit_free(audit);
kh_destroy_pkgs(check);
fclose(out);
return;
}

#ifdef HAVE_CAPSICUM
if (cap_enter() < 0 && errno != ENOSYS) {
warn("cap_enter() failed");
pkg_audit_free(audit);
kh_destroy_pkgs(check);
fclose(out);
return;
}
#endif

if (pkg_audit_process(audit) == EPKG_OK) {
kh_foreach_value(check, pkg, {
if (pkg_audit_is_vulnerable(audit, pkg, true, &sb)) {
pkg_get(pkg, PKG_UNIQUEID, &uid);
fprintf(out, "%s\n", uid);
utstring_free(sb);
}
pkg_free(pkg);
});
kh_destroy_pkgs(check);
}
else {
warnx("cannot process vulnxml");
kh_destroy_pkgs(check);
}

fclose(out);
pkg_audit_free(audit);
}

static int
add_vulnerable_upgrades(struct pkg_jobs *jobs, struct pkgdb *db)
{
int sp[2], retcode;
pid_t cld;
FILE *in;
struct pkg_audit *audit;
char *line = NULL;
size_t linecap = 0;
ssize_t linelen;

/* Fetch audit file */
/* TODO: maybe, we can skip it somethimes? */
audit = pkg_audit_new();

if (pkg_audit_fetch(NULL, NULL) != EPKG_OK) {
pkg_audit_free(audit);
return (EX_IOERR);
}

/* Create socketpair to execute audit check in a detached mode */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1) {
warnx("Cannot create socketpair");

return (EPKG_FATAL);
}

cld = fork();

switch (cld) {
case 0:
close(sp[1]);
check_vulnerable(audit, db, sp[0]);
close(sp[0]);
exit(EXIT_SUCCESS);
break;
case -1:
warnx("Cannot fork");
return (EPKG_FATAL);
default:
/* Parent code */
close(sp[0]);
pkg_audit_free(audit);
in = fdopen(sp[1], "r");

if (in == NULL) {
warnx("Cannot create stream");
close(sp[1]);

return (EPKG_FATAL);
}
break;
}

while ((linelen = getline(&line, &linecap, in)) > 0) {
if (line[linelen - 1] == '\n') {
line[linelen - 1] = '\0';
}

if (pkg_jobs_add(jobs, MATCH_EXACT, &line, 1) == EPKG_FATAL) {
warnx("Cannot update %s which is vulnerable", line);
/* TODO: assume it non-fatal for now */
}
}

fclose(in);
close(sp[1]);
pkg_audit_free(audit);

while (waitpid(cld, &retcode, 0) == -1) {
if (errno == EINTR) {
continue;
}
else {
warnx("Cannot wait");

return (EPKG_FATAL);
}
}

return (EPKG_OK);
}

int
exec_upgrade(int argc, char **argv)
{
Expand Down Expand Up @@ -72,12 +269,13 @@ exec_upgrade(int argc, char **argv)
{ "no-repo-update", no_argument, NULL, 'U' },
{ "regex", no_argument, NULL, 'x' },
{ "yes", no_argument, NULL, 'y' },
{ "vulnerable", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 },
};

nbactions = nbdone = 0;

while ((ch = getopt_long(argc, argv, "+CfFgiInqr:Uxy", longopts, NULL)) != -1) {
while ((ch = getopt_long(argc, argv, "+CfFgiInqr:Uxyv", longopts, NULL)) != -1) {
switch (ch) {
case 'C':
pkgdb_set_case_sensitivity(true);
Expand Down Expand Up @@ -118,6 +316,9 @@ exec_upgrade(int argc, char **argv)
case 'y':
yes = true;
break;
case 'v':
f |= PKG_FLAG_UPGRADE_VULNERABLE;
break;
default:
usage_upgrade();
return (EX_USAGE);
Expand Down Expand Up @@ -175,6 +376,13 @@ exec_upgrade(int argc, char **argv)
if (pkg_jobs_add(jobs, match, argv, argc) == EPKG_FATAL)
goto cleanup;

if (f & PKG_FLAG_UPGRADE_VULNERABLE) {
/* We need to load audit info and add packages that are vulnerable */
if (add_vulnerable_upgrades(jobs, db) != EPKG_OK) {
goto cleanup;
}
}

if (pkg_jobs_solve(jobs) != EPKG_OK)
goto cleanup;

Expand Down

0 comments on commit 2181d87

Please sign in to comment.