Commit ff6b18f831d03b1f4944716195089ced3e9b9fd8

Stefan Sperling 2018-04-24T12:50:21

read blobs through privsep

diff --git a/lib/got_lib_privsep.h b/lib/got_lib_privsep.h
index 76986d6..c594b65 100644
--- a/lib/got_lib_privsep.h
+++ b/lib/got_lib_privsep.h
@@ -21,9 +21,8 @@
  * This behaviour is transparent to users of the library.
  *
  * We generally transmit data in imsg buffers, split across several messages
- * if necessary. File descriptor passing is used in cases where this is
- * impractical, such as when accessing pack files or when transferring
- * large blobs.
+ * if necessary. File descriptors are used in cases where this is impractical,
+ * such as when accessing pack files or when transferring large blobs.
  *
  * We currently do not exec(2) after a fork(2).
  * To achieve fork+exec, relevant parts of our library functionality could
@@ -137,5 +136,7 @@ const struct got_error *got_privsep_recv_tree(struct got_tree_object **,
     struct imsgbuf *);
 const struct got_error *got_privsep_send_tree(struct imsgbuf *,
     struct got_tree_object *);
+const struct got_error *got_privsep_send_blob(struct imsgbuf *);
+const struct got_error *got_privsep_recv_blob(struct imsgbuf *);
 
 /* TODO: Implement the above, and then add more message data types here. */
diff --git a/lib/object.c b/lib/object.c
index 3a874e6..f3845e1 100644
--- a/lib/object.c
+++ b/lib/object.c
@@ -1023,6 +1023,71 @@ read_blob_object(int outfd, int infd)
 	return got_inflate_to_fd(&size, infd, outfd);
 }
 
+static const struct got_error *
+read_blob_object_privsep_child(int outfd, int infd, int imsg_fds[2])
+{
+	const struct got_error *err = NULL;
+	struct imsgbuf ibuf;
+	int status = 0;
+
+	setproctitle("got: read blob object");
+	close(imsg_fds[0]);
+	imsg_init(&ibuf, imsg_fds[1]);
+
+	/* revoke access to most system calls */
+	if (pledge("stdio", NULL) == -1) {
+		err = got_error_from_errno();
+		goto done;
+	}
+
+	err = read_blob_object(outfd, infd);
+	close(infd);
+	if (err)
+		goto done;
+
+	err = got_privsep_send_blob(&ibuf);
+done:
+	if (err) {
+		got_privsep_send_error(&ibuf, err);
+		status = 1;
+	}
+	close(outfd);
+	imsg_clear(&ibuf);
+	close(imsg_fds[1]);
+	_exit(status);
+}
+
+static const struct got_error *
+read_blob_object_privsep(int outfd, int infd)
+{
+	struct imsgbuf parent_ibuf;
+	int imsg_fds[2];
+	const struct got_error *err = NULL;
+	pid_t pid;
+	int child_status;
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1)
+		return got_error_from_errno();
+
+	pid = fork();
+	if (pid == -1)
+		return got_error_from_errno();
+	else if (pid == 0) {
+		read_blob_object_privsep_child(outfd, infd, imsg_fds);
+		/* not reached */
+	}
+
+	close(imsg_fds[1]);
+	imsg_init(&parent_ibuf, imsg_fds[0]);
+	err = got_privsep_recv_blob(&parent_ibuf);
+	imsg_clear(&parent_ibuf);
+	waitpid(pid, &child_status, 0);
+	close(imsg_fds[0]);
+	if (lseek(outfd, SEEK_SET, 0) == -1)
+		err = got_error_from_errno();
+	return err;
+}
+
 const struct got_error *
 got_object_blob_open(struct got_blob_object **blob,
     struct got_repository *repo, struct got_object *obj, size_t blocksize)
@@ -1063,7 +1128,7 @@ got_object_blob_open(struct got_blob_object **blob,
 			goto done;
 		}
 
-		err = read_blob_object(outfd, infd);
+		err = read_blob_object_privsep(outfd, infd);
 		close(infd);
 		if (err)
 			goto done;
diff --git a/lib/privsep.c b/lib/privsep.c
index 71bf646..da7628f 100644
--- a/lib/privsep.c
+++ b/lib/privsep.c
@@ -607,3 +607,45 @@ done:
 
 	return err;
 }
+
+const struct got_error *
+got_privsep_send_blob(struct imsgbuf *ibuf)
+{
+	/* Data has already been written to file descriptor. */
+	if (imsg_compose(ibuf, GOT_IMSG_BLOB, 0, 0, -1, NULL, 0) == -1)
+		return got_error_from_errno();
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_recv_blob(struct imsgbuf *ibuf)
+{
+	const struct got_error *err = NULL;
+	struct imsg imsg;
+	size_t datalen;
+
+	err = recv_one_imsg(&imsg, ibuf, 0);
+	if (err)
+		return err;
+
+	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+	switch (imsg.hdr.type) {
+	case GOT_IMSG_ERROR:
+		err = recv_imsg_error(&imsg, datalen);
+		break;
+	case GOT_IMSG_BLOB:
+		if (datalen != 0)
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+		/* Data has been written to file descriptor. */
+		break;
+	default:
+		err = got_error(GOT_ERR_PRIVSEP_MSG);
+		break;
+	}
+
+	imsg_free(&imsg);
+
+	return err;
+}