Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Sink PKG_EVENT_SANDBOX_* handlers into libpkg.
Gleb Popov committed 2 years ago
commit 7a630cf9e6e154b7eb1bd89c17ed839b2b635b68
parent 2e70b6c
12 files changed +269 -206
modified libpkg/Makefile.autosetup
@@ -36,6 +36,7 @@ SRCS= backup_lib.c \
	pkg_status.c \
	plugins.c \
	utils.c fetch.c \
+
	pkg_sandbox.c \
	pkg_attributes.c \
	pkg_delete.c \
	pkg_jobs.c \
modified libpkg/libpkg.ver
@@ -61,6 +61,8 @@ global:
	pkg_get_dir;
	pkg_get_element;
	pkg_get_file;
+
	pkg_handle_sandboxed_call;
+
	pkg_handle_sandboxed_get_string;
	pkg_has_dir;
	pkg_has_file;
	pkg_has_message;
modified libpkg/pkg.h.in
@@ -1626,6 +1626,19 @@ int pkg_namecmp(struct pkg *, struct pkg *);
void pkg_emit_error(const char *fmt, ...) PKG_FORMAT_ATTRIBUTE(1, 2);
void pkg_emit_notice(const char *fmt, ...) PKG_FORMAT_ATTRIBUTE(1, 2);

+
/**
+
 * Default implementation for handling the PKG_EVENT_SANDBOX_CALL event.
+
 */
+
int pkg_handle_sandboxed_call(pkg_sandbox_cb func, int fd, void *ud);
+
/**
+
 * Default implementation for handling the PKG_EVENT_SANDBOX_GET_STRING event.
+
 */
+
int pkg_handle_sandboxed_get_string(pkg_sandbox_cb func, char **result, int64_t *len, void *ud);
+
/**
+
 * A helper function that switches the process to the "nobody" user.
+
 */
+
void pkg_drop_privileges(void);
+

struct pkg_create *pkg_create_new(void);
void pkg_create_free(struct pkg_create *);
bool pkg_create_set_format(struct pkg_create *, const char *);
added libpkg/pkg_sandbox.c
@@ -0,0 +1,236 @@
+
/*-
+
 * Copyright (c) 2011-2014 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
+
 * All rights reserved.
+
 *
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions
+
 * are met:
+
 * 1. Redistributions of source code must retain the above copyright
+
 *    notice, this list of conditions and the following disclaimer
+
 *    in this position and unchanged.
+
 * 2. Redistributions in binary form must reproduce the above copyright
+
 *    notice, this list of conditions and the following disclaimer in the
+
 *    documentation and/or other materials provided with the distribution.
+
 *
+
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

+
#include <sys/resource.h>
+
#include <sys/types.h>
+
#include <sys/wait.h>
+
#include <sys/socket.h>
+

+
#ifdef HAVE_CAPSICUM
+
#include <sys/capsicum.h>
+
#endif
+

+
#include <err.h>
+
#include <string.h>
+
#include <unistd.h>
+
#include <errno.h>
+
#include <time.h>
+
#include <signal.h>
+
#include <pwd.h>
+

+
#include "pkg.h"
+

+
int
+
pkg_handle_sandboxed_call(pkg_sandbox_cb func, int fd, void *ud)
+
{
+
	pid_t pid;
+
	int status, ret;
+
	struct rlimit rl_zero;
+

+
	ret = -1;
+
	pid = fork();
+

+
	switch(pid) {
+
	case -1:
+
		warn("fork failed");
+
		return (EPKG_FATAL);
+
		break;
+
	case 0:
+
		break;
+
	default:
+
		/* Parent process */
+
		while (waitpid(pid, &status, 0) == -1) {
+
			if (errno != EINTR) {
+
				warn("Sandboxed process pid=%d", (int)pid);
+
				ret = -1;
+
				break;
+
			}
+
		}
+

+
		if (WIFEXITED(status)) {
+
			ret = WEXITSTATUS(status);
+
		}
+
		if (WIFSIGNALED(status)) {
+
			/* Process got some terminating signal, hence stop the loop */
+
			fprintf(stderr, "Sandboxed process pid=%d terminated abnormally by signal: %d\n",
+
					(int)pid, WTERMSIG(status));
+
			ret = -1;
+
		}
+
		return (ret);
+
	}
+

+
	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
+
	if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
+
		err(EXIT_FAILURE, "Unable to setrlimit(RLIMIT_NPROC)");
+

+
	/* Here comes child process */
+
#ifdef HAVE_CAPSICUM
+
#ifndef PKG_COVERAGE
+
	if (cap_enter() < 0 && errno != ENOSYS) {
+
		warn("cap_enter() failed");
+
		_exit(EXIT_FAILURE);
+
	}
+
#endif
+
#endif
+

+
	ret = func(fd, ud);
+

+
	_exit(ret);
+
}
+

+
int
+
pkg_handle_sandboxed_get_string(pkg_sandbox_cb func, char **result, int64_t *len,
+
		void *ud)
+
{
+
	pid_t pid;
+
	struct rlimit rl_zero;
+
	int	status, ret = EPKG_OK;
+
	int pair[2], r, allocated_len = 0, off = 0;
+
	char *buf = NULL;
+

+
	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
+
		warn("socketpair failed");
+
		return (EPKG_FATAL);
+
	}
+

+
	pid = fork();
+

+
	switch(pid) {
+
	case -1:
+
		warn("fork failed");
+
		return (EPKG_FATAL);
+
		break;
+
	case 0:
+
		break;
+
	default:
+
		/* Parent process */
+
		close(pair[0]);
+
		/*
+
		 * We use blocking IO here as if the child is terminated we would have
+
		 * EINTR here
+
		 */
+
		buf = malloc(BUFSIZ);
+
		if (buf == NULL) {
+
			warn("malloc failed");
+
			return (EPKG_FATAL);
+
		}
+
		allocated_len = BUFSIZ;
+
		do {
+
			if (off >= allocated_len) {
+
				allocated_len *= 2;
+
				buf = realloc(buf, allocated_len);
+
				if (buf == NULL) {
+
					warn("realloc failed");
+
					return (EPKG_FATAL);
+
				}
+
			}
+

+
			r = read(pair[1], buf + off, allocated_len - off);
+
			if (r == -1 && errno != EINTR) {
+
				free(buf);
+
				warn("read failed");
+
				return (EPKG_FATAL);
+
			}
+
			else if (r > 0) {
+
				off += r;
+
			}
+
		} while (r > 0);
+

+
		/* Fill the result buffer */
+
		*len = off;
+
		*result = buf;
+
		if (*result == NULL) {
+
			warn("malloc failed");
+
			kill(pid, SIGTERM);
+
			ret = EPKG_FATAL;
+
		}
+
		while (waitpid(pid, &status, 0) == -1) {
+
			if (errno != EINTR) {
+
				warn("Sandboxed process pid=%d", (int)pid);
+
				ret = -1;
+
				break;
+
			}
+
		}
+

+
		if (WIFEXITED(status)) {
+
			ret = WEXITSTATUS(status);
+
		}
+
		if (WIFSIGNALED(status)) {
+
			/* Process got some terminating signal, hence stop the loop */
+
			fprintf(stderr, "Sandboxed process pid=%d terminated abnormally by signal: %d\n",
+
					(int)pid, WTERMSIG(status));
+
			ret = -1;
+
		}
+
		return (ret);
+
	}
+

+
	/* Here comes child process */
+
	close(pair[1]);
+

+
	pkg_drop_privileges();
+

+
	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
+
	if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
+
		err(EXIT_FAILURE, "Unable to setrlimit(RLIMIT_NPROC)");
+

+
#ifdef HAVE_CAPSICUM
+
#ifndef PKG_COVERAGE
+
	if (cap_enter() < 0 && errno != ENOSYS) {
+
		warn("cap_enter() failed");
+
		return (EPKG_FATAL);
+
	}
+
#endif
+
#endif
+

+
	ret = func(pair[0], ud);
+

+
	close(pair[0]);
+

+
	_exit(ret);
+
}
+

+
void
+
pkg_drop_privileges(void)
+
{
+
	struct passwd *nobody;
+

+
	if (geteuid() == 0) {
+
		nobody = getpwnam("nobody");
+
		if (nobody == NULL)
+
			errx(EXIT_FAILURE, "Unable to drop privileges: no 'nobody' user");
+
		setgroups(1, &nobody->pw_gid);
+
		/* setgid also sets egid and setuid also sets euid */
+
		if (setgid(nobody->pw_gid) == -1)
+
			err(EXIT_FAILURE, "Unable to setgid");
+
		if (setuid(nobody->pw_uid) == -1)
+
			err(EXIT_FAILURE, "Unable to setuid");
+
	}
+
}
modified src/audit.c
@@ -3,7 +3,7 @@
 * Copyright (c) 2014-2015 Matthew Seaman <matthew@FreeBSD.org>
 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
 * All rights reserved.
-
 * 
+
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
@@ -13,7 +13,7 @@
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
-
 * 
+
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -367,7 +367,7 @@ exec_audit(int argc, char **argv)
		}
	}

-
	drop_privileges();
+
	pkg_drop_privileges();

	/* Now we have vulnxml loaded and check list formed */
#ifdef HAVE_CAPSICUM
@@ -468,7 +468,7 @@ exec_audit(int argc, char **argv)
			if (!quiet)
				printf("%u problem(s) in %u installed package(s) found.\n",
				   affected, vuln);
-
	
+

		} else {
			ucl_object_insert_key(top, ucl_object_fromint(vuln), "pkg_count", 9, false );
			ucl_object_insert_key(top, vuln_objs, "packages", 8, false);
modified src/event.c
@@ -6,7 +6,7 @@
 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
 * Copyright (c) 2015 Matthew Seaman <matthew@FreeBSD.org>
 * All rights reserved.
-
 * 
+
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
@@ -16,7 +16,7 @@
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
-
 * 
+
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -179,175 +179,6 @@ job_status_begin(xstring *msg)
	}
}

-
static int
-
event_sandboxed_call(pkg_sandbox_cb func, int fd, void *ud)
-
{
-
	pid_t pid;
-
	int status, ret;
-
	struct rlimit rl_zero;
-

-
	ret = -1;
-
	pid = fork();
-

-
	switch(pid) {
-
	case -1:
-
		warn("fork failed");
-
		return (EPKG_FATAL);
-
		break;
-
	case 0:
-
		break;
-
	default:
-
		/* Parent process */
-
		while (waitpid(pid, &status, 0) == -1) {
-
			if (errno != EINTR) {
-
				warn("Sandboxed process pid=%d", (int)pid);
-
				ret = -1;
-
				break;
-
			}
-
		}
-

-
		if (WIFEXITED(status)) {
-
			ret = WEXITSTATUS(status);
-
		}
-
		if (WIFSIGNALED(status)) {
-
			/* Process got some terminating signal, hence stop the loop */
-
			fprintf(stderr, "Sandboxed process pid=%d terminated abnormally by signal: %d\n",
-
					(int)pid, WTERMSIG(status));
-
			ret = -1;
-
		}
-
		return (ret);
-
	}
-

-
	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
-
	if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
-
		err(EXIT_FAILURE, "Unable to setrlimit(RLIMIT_NPROC)");
-

-
	/* Here comes child process */
-
#ifdef HAVE_CAPSICUM
-
#ifndef PKG_COVERAGE
-
	if (cap_enter() < 0 && errno != ENOSYS) {
-
		warn("cap_enter() failed");
-
		_exit(EXIT_FAILURE);
-
	}
-
#endif
-
#endif
-

-
	ret = func(fd, ud);
-

-
	_exit(ret);
-
}
-

-
static int
-
event_sandboxed_get_string(pkg_sandbox_cb func, char **result, int64_t *len,
-
		void *ud)
-
{
-
	pid_t pid;
-
	struct rlimit rl_zero;
-
	int	status, ret = EPKG_OK;
-
	int pair[2], r, allocated_len = 0, off = 0;
-
	char *buf = NULL;
-

-
	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
-
		warn("socketpair failed");
-
		return (EPKG_FATAL);
-
	}
-

-
	pid = fork();
-

-
	switch(pid) {
-
	case -1:
-
		warn("fork failed");
-
		return (EPKG_FATAL);
-
		break;
-
	case 0:
-
		break;
-
	default:
-
		/* Parent process */
-
		close(pair[0]);
-
		/*
-
		 * We use blocking IO here as if the child is terminated we would have
-
		 * EINTR here
-
		 */
-
		buf = malloc(BUFSIZ);
-
		if (buf == NULL) {
-
			warn("malloc failed");
-
			return (EPKG_FATAL);
-
		}
-
		allocated_len = BUFSIZ;
-
		do {
-
			if (off >= allocated_len) {
-
				allocated_len *= 2;
-
				buf = realloc(buf, allocated_len);
-
				if (buf == NULL) {
-
					warn("realloc failed");
-
					return (EPKG_FATAL);
-
				}
-
			}
-

-
			r = read(pair[1], buf + off, allocated_len - off);
-
			if (r == -1 && errno != EINTR) {
-
				free(buf);
-
				warn("read failed");
-
				return (EPKG_FATAL);
-
			}
-
			else if (r > 0) {
-
				off += r;
-
			}
-
		} while (r > 0);
-

-
		/* Fill the result buffer */
-
		*len = off;
-
		*result = buf;
-
		if (*result == NULL) {
-
			warn("malloc failed");
-
			kill(pid, SIGTERM);
-
			ret = EPKG_FATAL;
-
		}
-
		while (waitpid(pid, &status, 0) == -1) {
-
			if (errno != EINTR) {
-
				warn("Sandboxed process pid=%d", (int)pid);
-
				ret = -1;
-
				break;
-
			}
-
		}
-

-
		if (WIFEXITED(status)) {
-
			ret = WEXITSTATUS(status);
-
		}
-
		if (WIFSIGNALED(status)) {
-
			/* Process got some terminating signal, hence stop the loop */
-
			fprintf(stderr, "Sandboxed process pid=%d terminated abnormally by signal: %d\n",
-
					(int)pid, WTERMSIG(status));
-
			ret = -1;
-
		}
-
		return (ret);
-
	}
-

-
	/* Here comes child process */
-
	close(pair[1]);
-

-
	drop_privileges();
-

-
	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
-
	if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
-
		err(EXIT_FAILURE, "Unable to setrlimit(RLIMIT_NPROC)");
-

-
#ifdef HAVE_CAPSICUM
-
#ifndef PKG_COVERAGE
-
	if (cap_enter() < 0 && errno != ENOSYS) {
-
		warn("cap_enter() failed");
-
		return (EPKG_FATAL);
-
	}
-
#endif
-
#endif
-

-
	ret = func(pair[0], ud);
-

-
	close(pair[0]);
-

-
	_exit(ret);
-
}
-

void
progressbar_start(const char *pmsg)
{
@@ -816,12 +647,12 @@ event_callback(void *data, struct pkg_event *ev)
			ev->e_query_select.ncnt, ev->e_query_select.deft);
		break;
	case PKG_EVENT_SANDBOX_CALL:
-
		return ( event_sandboxed_call(ev->e_sandbox_call.call,
+
		return ( pkg_handle_sandboxed_call(ev->e_sandbox_call.call,
				ev->e_sandbox_call.fd,
				ev->e_sandbox_call.userdata) );
		break;
	case PKG_EVENT_SANDBOX_GET_STRING:
-
		return ( event_sandboxed_get_string(ev->e_sandbox_call_str.call,
+
		return ( pkg_handle_sandboxed_get_string(ev->e_sandbox_call_str.call,
				ev->e_sandbox_call_str.result,
				ev->e_sandbox_call_str.len,
				ev->e_sandbox_call_str.userdata) );
modified src/info.c
@@ -5,7 +5,7 @@
 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
 * Copyright (c) 2013-2014 Matthew Seaman <matthew@FreeBSD.org>
 * All rights reserved.
-
 * 
+
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
@@ -15,7 +15,7 @@
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
-
 * 
+
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -267,7 +267,7 @@ exec_info(int argc, char **argv)
			return (EXIT_FAILURE);
		}

-
		drop_privileges();
+
		pkg_drop_privileges();
#ifdef HAVE_CAPSICUM
		cap_rights_init(&rights, CAP_READ, CAP_FSTAT);
		if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS ) {
@@ -319,7 +319,7 @@ exec_info(int argc, char **argv)
	if (ret != EPKG_OK)
		return (EXIT_FAILURE);

-
	drop_privileges();
+
	pkg_drop_privileges();
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
modified src/pkgcli.h
@@ -277,8 +277,6 @@ void progressbar_start(const char *pmsg);
void progressbar_tick(int64_t current, int64_t total);
void progressbar_stop(void);

-
void drop_privileges(void);
-

extern xstring *messages;


modified src/query.c
@@ -1023,7 +1023,7 @@ exec_query(int argc, char **argv)
	if (ret != EPKG_OK)
		return (EXIT_FAILURE);

-
	drop_privileges();
+
	pkg_drop_privileges();
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
modified src/rquery.c
@@ -4,7 +4,7 @@
 * Copyright (c) 2012-2013 Bryan Drewery <bdrewery@FreeBSD.org>
 * Copyright (c) 2013-2014 Matthew Seaman <matthew@FreeBSD.org>
 * All rights reserved.
-
 * 
+
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
@@ -14,7 +14,7 @@
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
-
 * 
+
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -229,7 +229,7 @@ exec_rquery(int argc, char **argv)
		xstring_free(sqlcond);
		return (EXIT_FAILURE);
	}
-
	drop_privileges();
+
	pkg_drop_privileges();

	if (index_output)
		query_flags = PKG_LOAD_BASIC|PKG_LOAD_CATEGORIES|PKG_LOAD_DEPS;
modified src/upgrade.c
@@ -121,7 +121,7 @@ check_vulnerable(struct pkg_audit *audit, struct pkgdb *db, int sock)
		return;
	}

-
	drop_privileges();
+
	pkg_drop_privileges();

#ifdef HAVE_CAPSICUM
#ifndef PKG_COVERAGE
modified src/utils.c
@@ -1029,24 +1029,6 @@ print_jobs_summary(struct pkg_jobs *jobs, const char *msg, ...)
	return (displayed);
}

-
void
-
drop_privileges(void)
-
{
-
	struct passwd *nobody;
-

-
	if (geteuid() == 0) {
-
		nobody = getpwnam("nobody");
-
		if (nobody == NULL)
-
			errx(EXIT_FAILURE, "Unable to drop privileges: no 'nobody' user");
-
		setgroups(1, &nobody->pw_gid);
-
		/* setgid also sets egid and setuid also sets euid */
-
		if (setgid(nobody->pw_gid) == -1)
-
			err(EXIT_FAILURE, "Unable to setgid");
-
		if (setuid(nobody->pw_uid) == -1)
-
			err(EXIT_FAILURE, "Unable to setuid");
-
	}
-
}
-

int
print_pkg(struct pkg *p, void *ctx)
{