read blobs through privsep
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
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;
+}