Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge branch 'master' of github.com:freebsd/pkg
Matthew Seaman committed 12 years ago
commit a10094dab39b876202d785bd046c013a0287ade0
parent 0a5e13d
11 files changed +396 -30
modified libpkg/pkg.h.in
@@ -1368,6 +1368,7 @@ typedef enum {
	PKG_EVENT_QUERY_YESNO,
	PKG_EVENT_QUERY_SELECT,
	PKG_EVENT_SANDBOX_CALL,
+
	PKG_EVENT_SANDBOX_GET_STRING,
	/* errors */
	PKG_EVENT_ERROR,
	PKG_EVENT_ERRNO,
@@ -1510,6 +1511,12 @@ struct pkg_event {
			int fd;
			void *userdata;
		} e_sandbox_call;
+
		struct {
+
			pkg_sandbox_cb call;
+
			void *userdata;
+
			char **result;
+
			int64_t *len;
+
		} e_sandbox_call_str;
	};
};

modified libpkg/pkg_event.c
@@ -824,6 +824,22 @@ pkg_emit_query_select(const char *msg, const char **items, int ncnt, int deft)
}

int
+
pkg_emit_sandbox_get_string(pkg_sandbox_cb call, void *ud, char **str, int64_t *len)
+
{
+
	struct pkg_event ev;
+
	int ret;
+

+
	ev.type = PKG_EVENT_SANDBOX_GET_STRING;
+
	ev.e_sandbox_call_str.call = call;
+
	ev.e_sandbox_call_str.userdata = ud;
+
	ev.e_sandbox_call_str.result = str;
+
	ev.e_sandbox_call_str.len = len;
+

+
	ret = pkg_emit_event(&ev);
+
	return ret;
+
}
+

+
int
pkg_emit_sandbox_call(pkg_sandbox_cb call, int fd, void *ud)
{
	struct pkg_event ev;
modified libpkg/pkg_jobs.c
@@ -2157,9 +2157,9 @@ pkg_jobs_fetch(struct pkg_jobs *j)
		}
	}

-
	if (dlsize > ((int64_t)fs.f_bsize * (int64_t)fs.f_bfree)) {
-
		int64_t fsize = (int64_t)fs.f_bsize * (int64_t)fs.f_bfree;
-
		char dlsz[7], fsz[7];
+
	if (dlsize > ((int64_t)fs.f_bsize * (int64_t)fs.f_bavail)) {
+
		int64_t fsize = (int64_t)fs.f_bsize * (int64_t)fs.f_bavail;
+
		char dlsz[8], fsz[8];

		humanize_number(dlsz, sizeof(dlsz), dlsize, "B", HN_AUTOSCALE, 0);
		humanize_number(fsz, sizeof(fsz), fsize, "B", HN_AUTOSCALE, 0);
modified libpkg/pkg_repo.c
@@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
+
#include <sys/uio.h>

#include <archive_entry.h>
#include <assert.h>
@@ -46,6 +47,8 @@
#include <sysexits.h>
#include <unistd.h>
#include <errno.h>
+
#include <sys/mman.h>
+
#include <fcntl.h>

#include "pkg.h"
#include "private/event.h"
@@ -59,9 +62,9 @@
struct sig_cert {
	char name[MAXPATHLEN];
	unsigned char *sig;
-
	int siglen;
+
	int64_t siglen;
	unsigned char *cert;
-
	int certlen;
+
	int64_t certlen;
	bool cert_allocated;
	UT_hash_handle hh;
	bool trusted;
@@ -268,6 +271,7 @@ pkg_repo_check_fingerprint(struct pkg_repo *repo, struct sig_cert *sc, bool fata
	char hash[SHA256_DIGEST_LENGTH * 2 + 1];
	int nbgood = 0;
	struct sig_cert *s = NULL, *stmp = NULL;
+
	struct pkg_repo_meta_key *mk = NULL;

	if (HASH_COUNT(sc) == 0) {
		if (fatal)
@@ -282,13 +286,29 @@ pkg_repo_check_fingerprint(struct pkg_repo *repo, struct sig_cert *sc, bool fata
	}

	HASH_ITER(hh, sc, s, stmp) {
-
		if (s->sig == NULL || s->cert == NULL) {
+
		if (s->sig != NULL && s->cert == NULL) {
+
			/*
+
			 * We may want to check meta
+
			 */
+
			if (repo->meta != NULL && repo->meta->keys != NULL)
+
				HASH_FIND_STR(repo->meta->keys, s->name, mk);
+

+
			if (mk != NULL && mk->pubkey != NULL) {
+
				s->cert = mk->pubkey;
+
				s->certlen = strlen(mk->pubkey);
+
			}
+
			else {
+
				if (fatal)
+
					pkg_emit_error("No key with name %s has been found", s->name);
+
				return (false);
+
			}
+
		}
+
		else if (s->sig == NULL) {
			if (fatal)
-
				pkg_emit_error("Number of signatures and certificates "
-
					"mismatch");
-

+
				pkg_emit_error("No signature with name %s has been found", s->name);
			return (false);
		}
+

		s->trusted = false;
		sha256_buf(s->cert, s->certlen, hash);
		HASH_FIND_STR(repo->revoked_fp, hash, f);
@@ -591,14 +611,87 @@ cleanup:
	return (res);
}

+
struct pkg_repo_check_cbdata {
+
	unsigned char *map;
+
	size_t len;
+
	const char *name;
+
};
+

+
static int
+
pkg_repo_meta_extract_pubkey(int fd, void *ud)
+
{
+
	struct pkg_repo_check_cbdata *cbdata = ud;
+
	struct ucl_parser *parser;
+
	ucl_object_t *top;
+
	const ucl_object_t *obj, *cur, *elt;
+
	ucl_object_iter_t iter = NULL;
+
	struct iovec iov[2];
+
	int rc = EPKG_OK;
+
	int64_t res_len = 0;
+
	bool found = false;
+

+
	parser = ucl_parser_new(0);
+
	if (!ucl_parser_add_chunk(parser, cbdata->map, cbdata->len)) {
+
		pkg_emit_error("cannot parse repository meta from %s",
+
				ucl_parser_get_error(parser));
+
		ucl_parser_free(parser);
+
		return (EPKG_FATAL);
+
	}
+

+
	top = ucl_parser_get_object(parser);
+
	ucl_parser_free(parser);
+

+
	/* Now search for the required key */
+
	obj = ucl_object_find_key(top, "cert");
+
	if (obj == NULL) {
+
		pkg_emit_error("cannot find key for signature %s in meta",
+
				cbdata->name);
+
		rc = EPKG_FATAL;
+
	}
+
	else {
+
		while(!found && (cur = ucl_iterate_object(obj, &iter, false)) != NULL) {
+
			elt = ucl_object_find_key(cur, "name");
+
			if (elt != NULL && elt->type == UCL_STRING) {
+
				if (strcmp(ucl_object_tostring(elt), cbdata->name) == 0) {
+
					elt = ucl_object_find_key(cur, "data");
+
					if (elt == NULL || elt->type != UCL_STRING)
+
						continue;
+

+
					/* +1 to include \0 at the end */
+
					res_len = elt->len + 1;
+
					iov[0].iov_base = &res_len;
+
					iov[0].iov_len = sizeof(res_len);
+
					iov[1].iov_base = (void *)ucl_object_tostring(elt);
+
					iov[1].iov_len = res_len;
+
					if (writev(fd, iov, 2) == -1) {
+
						pkg_emit_errno("pkg_repo_meta_extract_pubkey",
+
								"writev error");
+
						rc = EPKG_FATAL;
+
						break;
+
					}
+
					found = true;
+
				}
+
			}
+
		}
+
	}
+

+
	ucl_object_unref(top);
+

+
	return (rc);
+
}
+

int
pkg_repo_fetch_meta(struct pkg_repo *repo, time_t *t)
{
	char filepath[MAXPATHLEN];
	struct pkg_repo_meta *nmeta;
+
	struct stat st;
	const char *dbdir = NULL;
+
	unsigned char *map = NULL;
	int fd;
-
	int rc = EPKG_OK;
+
	int rc = EPKG_OK, ret;
+
	struct sig_cert *sc = NULL, *s, *stmp;
+
	struct pkg_repo_check_cbdata cbdata;

	dbdir = pkg_object_string(pkg_config_get("PKG_DBDIR"));

@@ -614,13 +707,90 @@ pkg_repo_fetch_meta(struct pkg_repo *repo, time_t *t)
		return (EPKG_FATAL);
	}

-
	if ((rc = pkg_repo_archive_extract_check_archive(fd, "meta", filepath, repo, -1)) != EPKG_OK) {
+
	if (pkg_repo_signature_type(repo) == SIG_PUBKEY) {
+
		if ((rc = pkg_repo_archive_extract_check_archive(fd, "meta", filepath, repo, -1)) != EPKG_OK) {
+
			close (fd);
+
			return (rc);
+
		}
+
		goto load_meta;
+
	}
+

+
	/*
+
	 * For fingerprints we cannot just load pubkeys as they could be in metafile itself
+
	 * To do it, we parse meta in sandbox and for each unloaded pubkey we try to return
+
	 * a corresponding key from meta file.
+
	 */
+

+
	if ((rc = pkg_repo_archive_extract_archive(fd, "meta", filepath, repo, -1, &sc)) != EPKG_OK) {
		close (fd);
		return (rc);
	}

	close(fd);

+
	if (repo->trusted_fp == NULL) {
+
		if (pkg_repo_load_fingerprints(repo) != EPKG_OK)
+
			return (EPKG_FATAL);
+
	}
+

+
	/* Map meta file for extracting pubkeys from it */
+
	if (stat(filepath, &st) == -1) {
+
		pkg_emit_errno("pkg_repo_fetch_meta", "cannot stat meta fetched");
+
		rc = EPKG_FATAL;
+
		goto cleanup;
+
	}
+
	if ((fd = open(filepath, O_RDONLY)) == -1) {
+
		pkg_emit_errno("pkg_repo_fetch_meta", "cannot open meta fetched");
+
		rc = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

+
	map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+
	close(fd);
+
	if (map == MAP_FAILED) {
+
		pkg_emit_errno("pkg_repo_fetch_meta", "cannot mmap meta fetched");
+
		rc = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

+
	cbdata.len = st.st_size;
+
	cbdata.map = map;
+
	HASH_ITER(hh, sc, s, stmp) {
+
		if (s->siglen != 0 && s->certlen == 0) {
+
			/*
+
			 * We need to load this pubkey from meta
+
			 */
+
			cbdata.name = s->name;
+
			if (pkg_emit_sandbox_get_string(pkg_repo_meta_extract_pubkey, &cbdata,
+
					(char **)&s->cert, &s->certlen) != EPKG_OK) {
+
				rc = EPKG_FATAL;
+
				goto cleanup;
+
			}
+
			s->cert_allocated = true;
+
		}
+
	}
+

+
	if (!pkg_repo_check_fingerprint(repo, sc, true)) {
+
		rc = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

+
	HASH_ITER(hh, sc, s, stmp) {
+
		ret = rsa_verify_cert(filepath, s->cert, s->certlen, s->sig, s->siglen,
+
				-1);
+
		if (ret == EPKG_OK && s->trusted)
+
			break;
+

+
		ret = EPKG_FATAL;
+
	}
+
	if (ret != EPKG_OK) {
+
		pkg_emit_error("No trusted certificate has been used "
+
				"to sign the repository");
+
		rc = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

+
load_meta:
	if ((rc = pkg_repo_meta_load(filepath, &nmeta)) != EPKG_OK)
		return (rc);

@@ -629,6 +799,16 @@ pkg_repo_fetch_meta(struct pkg_repo *repo, time_t *t)

	repo->meta = nmeta;

+
cleanup:
+
	if (map != NULL)
+
		munmap(map, st.st_size);
+

+
	if (sc != NULL)
+
		pkg_repo_signatures_free(sc);
+

+
	if (rc != EPKG_OK)
+
		unlink(filepath);
+

	return (rc);
}

modified libpkg/pkgdb.c
@@ -4186,6 +4186,13 @@ pkgdb_try_lock(struct pkgdb *db, const char *lock_sql,
				/* No live processes found, so we can safely reset lock */
				pkg_debug(1, "no concurrent processes found, cleanup the lock");
				pkgdb_reset_lock(db);
+
				if (upgrade) {
+
					/*
+
					 * In case of upgrade we should obtain a lock from the beginning,
+
					 * hence switch upgrade to retain
+
					 */
+
					return pkgdb_obtain_lock(db, type, delay, retries - tries);
+
				}
				continue;
			}
			else if (delay > 0) {
modified libpkg/private/event.h
@@ -58,6 +58,7 @@ void pkg_emit_package_not_found(const char *);
void pkg_emit_incremental_update(int updated, int removed, int added, int processed);
void pkg_debug(int level, const char *fmt, ...);
int pkg_emit_sandbox_call(pkg_sandbox_cb call, int fd, void *ud);
+
int pkg_emit_sandbox_get_string(pkg_sandbox_cb call, void *ud, char **str, int64_t *len);


#endif
modified libpkg/rsa.c
@@ -25,11 +25,17 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#include <sys/stat.h>
+
#include <sys/param.h>
+

+
#include <fcntl.h>
+

#include <openssl/err.h>
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>

+

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
@@ -98,44 +104,80 @@ _load_rsa_public_key_buf(unsigned char *cert, int certlen)
	return (rsa);
}

-
int
-
rsa_verify_cert(const char *path, unsigned char *key, int keylen,
-
    unsigned char *sig, int siglen, int fd)
+
struct rsa_verify_cbdata {
+
	unsigned char *key;
+
	size_t keylen;
+
	unsigned char *sig;
+
	size_t siglen;
+
};
+

+
static int
+
rsa_verify_cert_cb(int fd, void *ud)
{
+
	struct rsa_verify_cbdata *cbdata = ud;
	char sha256[SHA256_DIGEST_LENGTH *2 +1];
	char hash[SHA256_DIGEST_LENGTH];
	char errbuf[1024];
	RSA *rsa = NULL;
	int ret;

-
	if (fd != -1)
-
		sha256_fd(fd, sha256);
-
	else
-
		sha256_file(path, sha256);
-

-
	SSL_load_error_strings();
-
	OpenSSL_add_all_algorithms();
-
	OpenSSL_add_all_ciphers();
+
	sha256_fd(fd, sha256);

	sha256_buf_bin(sha256, strlen(sha256), hash);

-
	rsa = _load_rsa_public_key_buf(key, keylen);
+
	rsa = _load_rsa_public_key_buf(cbdata->key, cbdata->keylen);
	if (rsa == NULL)
		return (EPKG_FATAL);
-
	ret = RSA_verify(NID_sha256, hash, sizeof(hash), sig, siglen, rsa);
+
	ret = RSA_verify(NID_sha256, hash, sizeof(hash), cbdata->sig,
+
			cbdata->siglen, rsa);
	if (ret == 0) {
		pkg_emit_error("rsa verify failed: %s",
-
		    ERR_error_string(ERR_get_error(), errbuf));
+
				ERR_error_string(ERR_get_error(), errbuf));
		return (EPKG_FATAL);
	}

	RSA_free(rsa);
-
	ERR_free_strings();

	return (EPKG_OK);
}

int
+
rsa_verify_cert(const char *path, unsigned char *key, int keylen,
+
    unsigned char *sig, int siglen, int fd)
+
{
+
	int ret;
+
	bool need_close = false;
+
	struct rsa_verify_cbdata cbdata;
+

+
	if (fd == -1) {
+
		if ((fd = open(path, O_RDONLY)) == -1) {
+
			pkg_emit_errno("fopen", path);
+
			return (EPKG_FATAL);
+
		}
+
		need_close = true;
+
	}
+
	(void)lseek(fd, 0, SEEK_SET);
+

+
	cbdata.key = key;
+
	cbdata.keylen = keylen;
+
	cbdata.sig = sig;
+
	cbdata.siglen = siglen;
+

+
	SSL_load_error_strings();
+
	OpenSSL_add_all_algorithms();
+
	OpenSSL_add_all_ciphers();
+

+
	ret = pkg_emit_sandbox_call(rsa_verify_cert_cb, fd, &cbdata);
+
	if (need_close)
+
		close(fd);
+

+
	return (ret);
+
}
+

+
/*
+
 * XXX: this function is deprecated and should be removed in the next pkg releases
+
 */
+
int
rsa_verify(const char *path, const char *key, unsigned char *sig,
    unsigned int sig_len, int fd)
{
modified src/annotate.c
@@ -24,6 +24,10 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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

#include <sys/types.h>
#include <sys/sbuf.h>

modified src/create.c
@@ -26,6 +26,10 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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

#include <sys/param.h>
#include <sys/queue.h>

modified src/event.c
@@ -28,9 +28,14 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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

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

#ifdef HAVE_CAPSICUM
#include <sys/capability.h>
@@ -41,6 +46,7 @@
#include <unistd.h>
#include <errno.h>
#include <sysexits.h>
+
#include <signal.h>

#include "pkg.h"
#include "progressmeter.h"
@@ -106,12 +112,99 @@ event_sandboxed_call(pkg_sandbox_cb func, int fd, void *ud)

	/* Here comes child process */
#ifdef HAVE_CAPSICUM
-
	cap_rights_init(&rights, CAP_READ|CAP_MMAP_R|CAP_SEEK|CAP_LOOKUP|CAP_FSTAT);
-
	if (cap_rights_limit(fileno(fd), &rights) < 0 && errno != ENOSYS ) {
-
		warn("cap_rights_limit() failed");
+
	if (cap_enter() < 0 && errno != ENOSYS) {
+
		warn("cap_enter() failed");
+
		return (EPKG_FATAL);
+
	}
+
#endif
+

+
	/*
+
	 * XXX: if capsicum is not enabled we basically have no idea of how to
+
	 * make a sandbox
+
	 */
+
	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;
+
	int	status, ret = EPKG_OK;
+
	int pair[2], r;
+
	int64_t res_len = 0;
+

+
	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:
+
		close(pair[0]);
+
		/*
+
		 * We use blocking IO here as if the child is terminated we would have
+
		 * EINTR here
+
		 */
+
		if (read(pair[1], &res_len, sizeof(res_len)) == -1) {
+
			ret = EPKG_FATAL;
+
		}
+
		else {
+
			/* Fill the result buffer */
+
			*len = res_len;
+
			*result = malloc(res_len + 1);
+
			if (*result == NULL) {
+
				warn("malloc failed");
+
				kill(pid, SIGTERM);
+
				ret = EPKG_FATAL;
+
			}
+
			else {
+
				if ((r = read(pair[1], *result, res_len)) == -1) {
+
					ret = EPKG_FATAL;
+
					free(*result);
+
					kill(pid, SIGTERM);
+
				}
+
				else {
+
					/* Null terminate string */
+
					*result[r] = '\0';
+
				}
+
			}
+
		}
+
		/* 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);
+
	}
+

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

+
#ifdef HAVE_CAPSICUM
	if (cap_enter() < 0 && errno != ENOSYS) {
		warn("cap_enter() failed");
		return (EPKG_FATAL);
@@ -122,7 +215,7 @@ event_sandboxed_call(pkg_sandbox_cb func, int fd, void *ud)
	 * XXX: if capsicum is not enabled we basically have no idea of how to
	 * make a sandbox
	 */
-
	ret = func(fd, ud);
+
	ret = func(pair[0], ud);

	_exit(ret);
}
@@ -426,6 +519,13 @@ event_callback(void *data, struct pkg_event *ev)
		return ( event_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,
+
				ev->e_sandbox_call_str.result,
+
				ev->e_sandbox_call_str.len,
+
				ev->e_sandbox_call_str.userdata) );
+
		break;
	default:
		break;
	}
modified src/main.c
@@ -4,6 +4,7 @@
 * Copyright (c) 2011 Will Andrews <will@FreeBSD.org>
 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
 * Copyright (c) 2014 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
@@ -28,6 +29,10 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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

#include <sys/param.h>

#include <sys/stat.h>