Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge branch 'pkg-sig-sandbox'
Vsevolod Stakhov committed 12 years ago
commit 6b7434928bf35b580bcde89592da0b4595e8b975
parent 97fa605
2 files changed +357 -97
modified libpkg/pkg_repo.c
@@ -352,6 +352,294 @@ pkg_repo_signatures_free(struct sig_cert *sc)
}

static int
+
pkg_repo_meta_extract_signature_pubkey(int fd, void *ud)
+
{
+
	struct archive *a = NULL;
+
	struct archive_entry *ae = NULL;
+
	int afd = *(int *)ud;
+
	int siglen;
+
	void *sig;
+
	int rc = EPKG_FATAL;
+

+
	pkg_debug(1, "PkgRepo: extracting signature of repo in a sandbox");
+

+
	a = archive_read_new();
+
	archive_read_support_filter_all(a);
+
	archive_read_support_format_tar(a);
+

+
	archive_read_open_fd(a, afd, 4096);
+

+
	while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
+
		if (strcmp(archive_entry_pathname(ae), "signature") == 0) {
+
			siglen = archive_entry_size(ae);
+
			sig = malloc(siglen);
+
			if (sig == NULL) {
+
				pkg_emit_errno("pkg_repo_meta_extract_signature",
+
						"malloc failed");
+
				return (EPKG_FATAL);
+
			}
+
			if (archive_read_data(a, sig, siglen) == -1) {
+
				pkg_emit_errno("pkg_repo_meta_extract_signature",
+
						"archive_read_data failed");
+
				free(sig);
+
				return (EPKG_FATAL);
+
			}
+
			if (write(fd, sig, siglen) == -1) {
+
				pkg_emit_errno("pkg_repo_meta_extract_signature",
+
						"write failed");
+
				free(sig);
+
				return (EPKG_FATAL);
+
			}
+
			free(sig);
+
			rc = EPKG_OK;
+
			break;
+
		}
+
	}
+
	/*
+
	 * XXX: do not free resources here since the sandbox is terminated anyway
+
	 */
+
	return (rc);
+
}
+

+
/*
+
 * We use here the following format:
+
 * <type(0|1)><namelen(int)><name><datalen(int)><data>
+
 */
+
static int
+
pkg_repo_meta_extract_signature_fingerprints(int fd, void *ud)
+
{
+
	struct archive *a = NULL;
+
	struct archive_entry *ae = NULL;
+
	int afd = *(int *)ud;
+
	int siglen, keylen;
+
	void *sig;
+
	int rc = EPKG_FATAL;
+
	char key[MAXPATHLEN], t;
+
	struct iovec iov[5];
+

+
	pkg_debug(1, "PkgRepo: extracting signature of repo in a sandbox");
+

+
	a = archive_read_new();
+
	archive_read_support_filter_all(a);
+
	archive_read_support_format_tar(a);
+

+
	archive_read_open_fd(a, afd, 4096);
+

+
	while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
+
		if (pkg_repo_file_has_ext(archive_entry_pathname(ae), ".sig")) {
+
			snprintf(key, sizeof(key), "%.*s",
+
					(int) strlen(archive_entry_pathname(ae)) - 4,
+
					archive_entry_pathname(ae));
+
			siglen = archive_entry_size(ae);
+
			sig = malloc(siglen);
+
			if (sig == NULL) {
+
				pkg_emit_errno("pkg_repo_meta_extract_signature",
+
						"malloc failed");
+
				return (EPKG_FATAL);
+
			}
+
			if (archive_read_data(a, sig, siglen) == -1) {
+
				pkg_emit_errno("pkg_repo_meta_extract_signature",
+
						"archive_read_data failed");
+
				free(sig);
+
				return (EPKG_FATAL);
+
			}
+
			/* Signature type */
+
			t = 0;
+
			keylen = strlen(key);
+
			iov[0].iov_base = &t;
+
			iov[0].iov_len = sizeof(t);
+
			iov[1].iov_base = &keylen;
+
			iov[1].iov_len = sizeof(keylen);
+
			iov[2].iov_base = key;
+
			iov[2].iov_len = keylen;
+
			iov[3].iov_base = &siglen;
+
			iov[3].iov_len = sizeof(siglen);
+
			iov[4].iov_base = sig;
+
			iov[4].iov_len = siglen;
+
			if (writev(fd, iov, sizeof(iov) / sizeof(iov[0])) == -1) {
+
				pkg_emit_errno("pkg_repo_meta_extract_signature",
+
						"writev failed");
+
				free(sig);
+
				return (EPKG_FATAL);
+
			}
+
			free(sig);
+
			rc = EPKG_OK;
+
		}
+
		else if (pkg_repo_file_has_ext(archive_entry_pathname(ae), ".pub")) {
+
			snprintf(key, sizeof(key), "%.*s",
+
					(int) strlen(archive_entry_pathname(ae)) - 4,
+
					archive_entry_pathname(ae));
+
			siglen = archive_entry_size(ae);
+
			sig = malloc(siglen);
+
			if (sig == NULL) {
+
				pkg_emit_errno("pkg_repo_meta_extract_signature",
+
						"malloc failed");
+
				return (EPKG_FATAL);
+
			}
+
			if (archive_read_data(a, sig, siglen) == -1) {
+
				pkg_emit_errno("pkg_repo_meta_extract_signature",
+
						"archive_read_data failed");
+
				free(sig);
+
				return (EPKG_FATAL);
+
			}
+
			/* Pubkey type */
+
			t = 1;
+
			keylen = strlen(key);
+
			iov[0].iov_base = &t;
+
			iov[0].iov_len = sizeof(t);
+
			iov[1].iov_base = &keylen;
+
			iov[1].iov_len = sizeof(keylen);
+
			iov[2].iov_base = key;
+
			iov[2].iov_len = keylen;
+
			iov[3].iov_base = &siglen;
+
			iov[3].iov_len = sizeof(siglen);
+
			iov[4].iov_base = sig;
+
			iov[4].iov_len = siglen;
+
			if (writev(fd, iov, sizeof(iov) / sizeof(iov[0])) == -1) {
+
				pkg_emit_errno("pkg_repo_meta_extract_signature",
+
						"writev failed");
+
				free(sig);
+
				return (EPKG_FATAL);
+
			}
+
			free(sig);
+
			rc = EPKG_OK;
+
		}
+
		else {
+
			/* Pubkeys and signatures *MUST* be at the beginnig of an archive */
+
			break;
+
		}
+
	}
+
	/*
+
	 * XXX: do not free resources here since the sandbox is terminated anyway
+
	 */
+
	return (rc);
+
}
+

+
static int
+
pkg_repo_parse_sigkeys(const char *in, int inlen, struct sig_cert **sc)
+
{
+
	const char *p = in, *end = in + inlen;
+
	int rc = EPKG_OK;
+
	enum {
+
		fp_parse_type,
+
		fp_parse_flen,
+
		fp_parse_file,
+
		fp_parse_siglen,
+
		fp_parse_sig
+
	} state = fp_parse_type;
+
	char type;
+
	unsigned char *sig;
+
	int len = 0;
+
	struct sig_cert *s;
+
	bool new = false;
+

+
	while (p < end) {
+
		switch (state) {
+
		case fp_parse_type:
+
			type = *p;
+
			if (type != 0 && type != 1) {
+
				/* Invalid type */
+
				pkg_emit_error("%d is not a valid type for signature_fingerprints"
+
						"output", type);
+
				return (EPKG_FATAL);
+
			}
+
			state = fp_parse_flen;
+
			s = NULL;
+
			p ++;
+
			break;
+
		case fp_parse_flen:
+
			if (end - p < sizeof (int)) {
+
				pkg_emit_error("truncated reply for signature_fingerprints"
+
						"output", type);
+
				return (EPKG_FATAL);
+
			}
+
			len = *(int *)p;
+
			state = fp_parse_file;
+
			p += sizeof(int);
+
			s = NULL;
+
			break;
+
		case fp_parse_file:
+
			if (end - p < len || len <= 0) {
+
				pkg_emit_error("truncated reply for signature_fingerprints"
+
						"output, wanted %d bytes", type, len);
+
				return (EPKG_FATAL);
+
			}
+
			else if (len >= MAXPATHLEN) {
+
				pkg_emit_error("filename is incorrect for signature_fingerprints"
+
						"output: %d, wanted 5..%d bytes", type, len, MAXPATHLEN);
+
				return (EPKG_FATAL);
+
			}
+
			HASH_FIND(hh, *sc, p, len, s);
+
			if (s == NULL) {
+
				s = calloc(1, sizeof(struct sig_cert));
+
				if (s == NULL) {
+
					pkg_emit_errno("pkg_repo_parse_sigkeys", "calloc failed");
+
					return (EPKG_FATAL);
+
				}
+
				strlcpy(s->name, p, MIN(len + 1, sizeof(s->name)));
+
				new = true;
+
			}
+
			else {
+
				new = false;
+
			}
+
			state = fp_parse_siglen;
+
			p += len;
+
			break;
+
		case fp_parse_siglen:
+
			if (s == NULL) {
+
				pkg_emit_error("fatal state machine failure at pkg_repo_parse_sigkeys");
+
				return (EPKG_FATAL);
+
			}
+
			if (end - p < sizeof (int)) {
+
				pkg_emit_error("truncated reply for signature_fingerprints"
+
						"output", type);
+
				return (EPKG_FATAL);
+
			}
+
			len = *(int *)p;
+
			state = fp_parse_sig;
+
			p += sizeof(int);
+
			break;
+
		case fp_parse_sig:
+
			if (s == NULL) {
+
				pkg_emit_error("fatal state machine failure at pkg_repo_parse_sigkeys");
+
				return (EPKG_FATAL);
+
			}
+
			if (end - p < len || len <= 0) {
+
				pkg_emit_error("truncated reply for signature_fingerprints"
+
						"output, wanted %d bytes", type, len);
+
				free(s);
+
				return (EPKG_FATAL);
+
			}
+
			sig = malloc(len);
+
			if (sig == NULL) {
+
				pkg_emit_errno("pkg_repo_parse_sigkeys", "malloc failed");
+
				free(s);
+
				return (EPKG_FATAL);
+
			}
+
			memcpy(sig, p, len);
+
			if (type == 0) {
+
				s->sig = sig;
+
				s->siglen = len;
+
			}
+
			else {
+
				s->cert = sig;
+
				s->certlen = len;
+
				s->cert_allocated = true;
+
			}
+
			state = fp_parse_type;
+
			p += len;
+

+
			if (new)
+
				HASH_ADD_STR(*sc, name, s);
+

+
			break;
+
		}
+
	}
+

+
	return (rc);
+
}
+

+
static int
pkg_repo_archive_extract_archive(int fd, const char *file,
		const char *dest, struct pkg_repo *repo, int dest_fd,
		struct sig_cert **signatures)
@@ -361,8 +649,9 @@ pkg_repo_archive_extract_archive(int fd, const char *file,
	struct sig_cert *sc = NULL, *s;

	unsigned char *sig = NULL;
-
	int siglen = 0, rc = EPKG_OK;
-
	char key[MAXPATHLEN];
+
	int rc = EPKG_OK;
+
	int64_t siglen = 0;
+


	pkg_debug(1, "PkgRepo: extracting %s of repo %s", file, pkg_repo_name(repo));

@@ -372,6 +661,33 @@ pkg_repo_archive_extract_archive(int fd, const char *file,

	/* Seek to the begin of file */
	(void)lseek(fd, 0, SEEK_SET);
+

+
	if (pkg_repo_signature_type(repo) == SIG_PUBKEY) {
+
		if (pkg_emit_sandbox_get_string(pkg_repo_meta_extract_signature_pubkey,
+
				&fd, (char **)&sig, &siglen) == EPKG_OK && sig != NULL) {
+
			s = calloc(1, sizeof(struct sig_cert));
+
			if (s == NULL) {
+
				pkg_emit_errno("pkg_repo_archive_extract_archive",
+
						"malloc failed");
+
				rc = EPKG_FATAL;
+
				goto cleanup;
+
			}
+
			s->sig = sig;
+
			s->siglen = siglen;
+
			strlcpy(s->name, "signature", sizeof(s->name));
+
			HASH_ADD_STR(sc, name, s);
+
		}
+
	}
+
	else if (pkg_repo_signature_type(repo) == SIG_FINGERPRINT) {
+
		if (pkg_emit_sandbox_get_string(pkg_repo_meta_extract_signature_fingerprints,
+
				&fd, (char **)&sig, &siglen) == EPKG_OK && sig != NULL) {
+
			if (pkg_repo_parse_sigkeys(sig, siglen, &sc) == EPKG_FATAL) {
+
				return (EPKG_FATAL);
+
			}
+
			free(sig);
+
		}
+
	}
+
	(void)lseek(fd, 0, SEEK_SET);
	archive_read_open_fd(a, fd, 4096);

	while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
@@ -399,73 +715,6 @@ pkg_repo_archive_extract_archive(int fd, const char *file,
				(void)lseek(dest_fd, 0, SEEK_SET);
			}
		}
-
		if (pkg_repo_signature_type(repo) == SIG_PUBKEY &&
-
				strcmp(archive_entry_pathname(ae), "signature") == 0) {
-
			siglen = archive_entry_size(ae);
-
			sig = malloc(siglen);
-
			archive_read_data(a, sig, siglen);
-
			s = calloc(1, sizeof(struct sig_cert));
-
			s->sig = sig;
-
			s->siglen = siglen;
-
			strlcpy(s->name, "signature", sizeof(s->name));
-
			HASH_ADD_STR(sc, name, s);
-
		}
-

-
		if (pkg_repo_signature_type(repo) == SIG_FINGERPRINT) {
-
			if (pkg_repo_file_has_ext(archive_entry_pathname(ae), ".sig")) {
-
				snprintf(key, sizeof(key), "%.*s",
-
						(int) strlen(archive_entry_pathname(ae)) - 4,
-
						archive_entry_pathname(ae));
-
				HASH_FIND_STR(sc, key, s);
-
				if (s == NULL) {
-
					s = calloc(1, sizeof(struct sig_cert));
-
					if (s == NULL) {
-
						pkg_emit_errno("pkg_repo_archive_extract_file",
-
								"calloc failed for struct sig_cert");
-
						rc = EPKG_FATAL;
-
						goto cleanup;
-
					}
-
					strlcpy(s->name, key, sizeof(s->name));
-
					HASH_ADD_STR(sc, name, s);
-
				}
-
				s->siglen = archive_entry_size(ae);
-
				s->sig = malloc(s->siglen);
-
				if (s->sig == NULL) {
-
					pkg_emit_errno("pkg_repo_archive_extract_file",
-
							"calloc failed for signature data");
-
					rc = EPKG_FATAL;
-
					goto cleanup;
-
				}
-
				archive_read_data(a, s->sig, s->siglen);
-
			}
-
			if (pkg_repo_file_has_ext(archive_entry_pathname(ae), ".pub")) {
-
				snprintf(key, sizeof(key), "%.*s",
-
						(int) strlen(archive_entry_pathname(ae)) - 4,
-
						archive_entry_pathname(ae));
-
				HASH_FIND_STR(sc, key, s);
-
				if (s == NULL) {
-
					s = calloc(1, sizeof(struct sig_cert));
-
					if (s == NULL) {
-
						pkg_emit_errno("pkg_repo_archive_extract_file",
-
								"calloc failed for struct sig_cert");
-
						rc = EPKG_FATAL;
-
						goto cleanup;
-
					}
-
					strlcpy(s->name, key, sizeof(s->name));
-
					HASH_ADD_STR(sc, name, s);
-
				}
-
				s->certlen = archive_entry_size(ae);
-
				s->cert = malloc(s->certlen);
-
				if (s->cert == NULL) {
-
					pkg_emit_errno("pkg_repo_archive_extract_file",
-
							"calloc failed for signature data");
-
					rc = EPKG_FATAL;
-
					goto cleanup;
-
				}
-
				s->cert_allocated = true;
-
				archive_read_data(a, s->cert, s->certlen);
-
			}
-
		}
	}

cleanup:
@@ -659,11 +908,9 @@ pkg_repo_meta_extract_pubkey(int fd, void *ud)

					/* +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) {
+
					iov[0].iov_base = (void *)ucl_object_tostring(elt);
+
					iov[0].iov_len = res_len;
+
					if (writev(fd, iov, 1) == -1) {
						pkg_emit_errno("pkg_repo_meta_extract_pubkey",
								"writev error");
						rc = EPKG_FATAL;
modified src/event.c
@@ -133,8 +133,8 @@ event_sandboxed_get_string(pkg_sandbox_cb func, char **result, int64_t *len,
{
	pid_t pid;
	int	status, ret = EPKG_OK;
-
	int pair[2], r;
-
	int64_t res_len = 0;
+
	int pair[2], r, allocated_len = 0, off = 0;
+
	char *buf = NULL;

	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
		warn("socketpair failed");
@@ -151,36 +151,47 @@ event_sandboxed_get_string(pkg_sandbox_cb func, char **result, int64_t *len,
	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
		 */
-
		if (read(pair[1], &res_len, sizeof(res_len)) == -1) {
-
			ret = EPKG_FATAL;
+
		buf = malloc(BUFSIZ);
+
		if (buf == NULL) {
+
			warn("malloc failed");
+
			return (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';
+
		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;
		}
-
		/* Parent process */
		while (waitpid(pid, &status, 0) == -1) {
			if (errno != EINTR) {
				warn("Sandboxed process pid=%d", (int)pid);
@@ -217,6 +228,8 @@ event_sandboxed_get_string(pkg_sandbox_cb func, char **result, int64_t *len,
	 */
	ret = func(pair[0], ud);

+
	close(pair[0]);
+

	_exit(ret);
}