commit e033d8037031ecafbf4a99c69c25f1be778ef9d3 from: Stefan Sperling date: Mon Apr 23 16:30:26 2018 UTC read tree objects with privsep commit - 86acc5664b2eab0c8e696d98336235c2b027229a commit + e033d8037031ecafbf4a99c69c25f1be778ef9d3 blob - b1af4e2fa5b6fc7b4ebe696d93b8a7697eabff1e blob + 4cfa4a51368b015a532f56d4588e182ad78b9e69 --- lib/got_lib_object.h +++ lib/got_lib_object.h @@ -45,3 +45,4 @@ struct got_blob_object { }; struct got_commit_object *got_object_commit_alloc_partial(void); +struct got_tree_entry *got_alloc_tree_entry_partial(void); blob - 037cf653ad0247ae919e99902639f2ca2214069c blob + 43af9dc4a56b8d4092240a2fe7610e3e90df9dac --- lib/got_lib_privsep.h +++ lib/got_lib_privsep.h @@ -114,7 +114,7 @@ struct got_imsg_commit_object { /* Structure for GOT_IMSG_TREE_ENTRY. */ struct got_imsg_tree_entry { - struct got_object_id id; + char id[SHA1_DIGEST_LENGTH]; mode_t mode; /* Followed by entry's name in remaining data of imsg buffer. */ } __attribute__((__packed__)); @@ -133,5 +133,9 @@ const struct got_error *got_privsep_send_commit_obj(st struct got_commit_object *); const struct got_error *got_privsep_recv_commit_obj(struct got_commit_object **, struct imsgbuf *); +const struct got_error *got_privsep_recv_tree_obj(struct got_tree_object **, + struct imsgbuf *); +const struct got_error *got_privsep_send_tree_obj(struct imsgbuf *, + struct got_tree_object *); /* TODO: Implement the above, and then add more message data types here. */ blob - 94349e0a76159e5adea1b20e4fd51ce1bd252f6f blob + 98e4f11e71887b6117bf17e88675d07c1a274080 --- lib/object.c +++ lib/object.c @@ -567,6 +567,23 @@ tree_entry_close(struct got_tree_entry *te) free(te); } +struct got_tree_entry * +got_alloc_tree_entry_partial(void) +{ + struct got_tree_entry *te; + + te = calloc(1, sizeof(*te)); + if (te == NULL) + return NULL; + + te->id = calloc(1, sizeof(*te->id)); + if (te->id == NULL) { + free(te); + te = NULL; + } + return te; +} + static const struct got_error * parse_tree_entry(struct got_tree_entry **te, size_t *elen, char *buf, size_t maxlen) @@ -574,18 +591,10 @@ parse_tree_entry(struct got_tree_entry **te, size_t *e char *p = buf, *space; const struct got_error *err = NULL; - *te = calloc(1, sizeof(**te)); + *te = got_alloc_tree_entry_partial(); if (*te == NULL) return got_error_from_errno(); - (*te)->id = calloc(1, sizeof(*(*te)->id)); - if ((*te)->id == NULL) { - err = got_error_from_errno(); - free(*te); - *te = NULL; - return err; - } - *elen = strlen(buf) + 1; if (*elen > maxlen) { free(*te); @@ -627,8 +636,7 @@ done: } static const struct got_error * -parse_tree_object(struct got_tree_object **tree, struct got_repository *repo, - uint8_t *buf, size_t len) +parse_tree_object(struct got_tree_object **tree, uint8_t *buf, size_t len) { const struct got_error *err; size_t remain = len; @@ -862,8 +870,7 @@ got_object_commit_close(struct got_commit_object *comm } static const struct got_error * -read_tree_object(struct got_tree_object **tree, - struct got_repository *repo, struct got_object *obj, FILE *f) +read_tree_object(struct got_tree_object **tree, struct got_object *obj, FILE *f) { const struct got_error *err = NULL; size_t len; @@ -883,9 +890,85 @@ read_tree_object(struct got_tree_object **tree, /* Skip object header. */ len -= obj->hdrlen; - err = parse_tree_object(tree, repo, p + obj->hdrlen, len); + err = parse_tree_object(tree, p + obj->hdrlen, len); free(p); +done: + return err; +} + +static void +read_tree_object_privsep_child(struct got_object *obj, int obj_fd, + int imsg_fds[2]) +{ + const struct got_error *err = NULL; + struct got_tree_object *tree = NULL; + struct imsgbuf ibuf; + FILE *f = NULL; + int status = 0; + + setproctitle("got: read tree 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; + } + + f = fdopen(obj_fd, "rb"); + if (f == NULL) { + err = got_error_from_errno(); + close(obj_fd); + goto done; + } + + err = read_tree_object(&tree, obj, f); + if (err) + goto done; + + err = got_privsep_send_tree_obj(&ibuf, tree); done: + if (tree) + got_object_tree_close(tree); + if (err) { + got_privsep_send_error(&ibuf, err); + status = 1; + } + if (f) + fclose(f); + imsg_clear(&ibuf); + close(imsg_fds[1]); + _exit(status); +} + +static const struct got_error * +read_tree_object_privsep(struct got_tree_object **tree, struct got_object *obj, + int fd) +{ + const struct got_error *err = NULL; + struct imsgbuf parent_ibuf; + int imsg_fds[2]; + 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_tree_object_privsep_child(obj, fd, imsg_fds); + /* not reached */ + } + + close(imsg_fds[1]); + imsg_init(&parent_ibuf, imsg_fds[0]); + err = got_privsep_recv_tree_obj(tree, &parent_ibuf); + imsg_clear(&parent_ibuf); + waitpid(pid, &child_status, 0); + close(imsg_fds[0]); return err; } @@ -905,21 +988,14 @@ got_object_tree_open(struct got_tree_object **tree, if (err) return err; obj->size = len; - err = parse_tree_object(tree, repo, buf, len); + err = parse_tree_object(tree, buf, len); free(buf); } else { - FILE *f; int fd; err = open_loose_object(&fd, obj, repo); if (err) return err; - f = fdopen(fd, "rb"); - if (f == NULL) { - close(fd); - return got_error_from_errno(); - } - err = read_tree_object(tree, repo, obj, f); - fclose(f); + err = read_tree_object_privsep(tree, obj, fd); close(fd); } return err; blob - 85b125e761c9349c488108b05a63bb914fbb644f blob + 564d5af6f1afadb90372f6f97eba30bd81194107 --- lib/privsep.c +++ lib/privsep.c @@ -64,10 +64,10 @@ poll_fd(int fd, int events, int timeout) } static const struct got_error * -recv_one_imsg(struct imsg *imsg, struct imsgbuf *ibuf, size_t min_datalen) +read_imsg(struct imsgbuf *ibuf) { const struct got_error *err; - ssize_t n, m; + size_t n; err = poll_fd(ibuf->fd, POLLIN, INFTIM); if (err) @@ -82,8 +82,21 @@ recv_one_imsg(struct imsg *imsg, struct imsgbuf *ibuf, if (n == 0) return got_error(GOT_ERR_PRIVSEP_PIPE); - m = imsg_get(ibuf, imsg); - if (m == 0) + return NULL; +} + +static const struct got_error * +recv_one_imsg(struct imsg *imsg, struct imsgbuf *ibuf, size_t min_datalen) +{ + const struct got_error *err; + ssize_t n; + + err = read_imsg(ibuf); + if (err) + return err; + + n = imsg_get(ibuf, imsg); + if (n == 0) return got_error(GOT_ERR_PRIVSEP_READ); if (imsg->hdr.len < IMSG_HEADER_SIZE + min_datalen) @@ -144,12 +157,26 @@ got_privsep_send_error(struct imsgbuf *ibuf, const str getprogname(), err->code, err->msg, strerror(errno)); return; } +} + +static const struct got_error * +flush_imsg(struct imsgbuf *ibuf) +{ + const struct got_error *err; + + err = poll_fd(ibuf->fd, POLLOUT, INFTIM); + if (err) + return err; + + if (imsg_flush(ibuf) == -1) + return got_error_from_errno(); + + return NULL; } const struct got_error * got_privsep_send_obj(struct imsgbuf *ibuf, struct got_object *obj, int ndeltas) { - const struct got_error *err = NULL; struct got_imsg_object iobj; iobj.type = obj->type; @@ -166,14 +193,7 @@ got_privsep_send_obj(struct imsgbuf *ibuf, struct got_ == -1) return got_error_from_errno(); - err = poll_fd(ibuf->fd, POLLOUT, INFTIM); - if (err) - return err; - - if (imsg_flush(ibuf) == -1) - return got_error_from_errno(); - - return NULL; + return flush_imsg(ibuf); } const struct got_error * @@ -280,15 +300,7 @@ got_privsep_send_commit_obj(struct imsgbuf *ibuf, stru goto done; } - err = poll_fd(ibuf->fd, POLLOUT, INFTIM); - if (err) - goto done; - - if (imsg_flush(ibuf) == -1) { - err = got_error_from_errno(); - goto done; - } - + err = flush_imsg(ibuf); done: free(buf); return err; @@ -431,6 +443,168 @@ got_privsep_recv_commit_obj(struct got_commit_object * } imsg_free(&imsg); + + return err; +} + +const struct got_error * +got_privsep_send_tree_obj(struct imsgbuf *ibuf, struct got_tree_object *tree) +{ + const struct got_error *err = NULL; + struct got_imsg_tree_object itree; + struct got_tree_entry *te; + + itree.nentries = tree->nentries; + if (imsg_compose(ibuf, GOT_IMSG_TREE, 0, 0, -1, &itree, sizeof(itree)) + == -1) + return got_error_from_errno(); + + err = flush_imsg(ibuf); + if (err) + return err; + + SIMPLEQ_FOREACH(te, &tree->entries, entry) { + struct got_imsg_tree_entry ite; + uint8_t *buf = NULL; + size_t len = sizeof(ite) + strlen(te->name); + + if (len > MAX_IMSGSIZE) + return got_error(GOT_ERR_NO_SPACE); + + buf = malloc(len); + if (buf == NULL) + return got_error_from_errno(); + + memcpy(ite.id, te->id->sha1, sizeof(ite.id)); + ite.mode = te->mode; + memcpy(buf, &ite, sizeof(ite)); + memcpy(buf + sizeof(ite), te->name, strlen(te->name)); + + if (imsg_compose(ibuf, GOT_IMSG_TREE_ENTRY, 0, 0, -1, + buf, len) == -1) + err = got_error_from_errno(); + free(buf); + if (err) + return err; + + err = flush_imsg(ibuf); + if (err) + return err; + } + + return NULL; +} + +const struct got_error * +got_privsep_recv_tree_obj(struct got_tree_object **tree, struct imsgbuf *ibuf) +{ + const struct got_error *err = NULL; + const size_t min_datalen = + MIN(sizeof(struct got_imsg_error), + sizeof(struct got_imsg_tree_object)); + struct got_imsg_tree_object itree = { 0 }; + int nentries = 0; + *tree = NULL; +get_more: + err = read_imsg(ibuf); + if (err) + return err; + + while (1) { + struct imsg imsg; + size_t n; + uint8_t *data; + size_t datalen; + struct got_imsg_tree_entry ite; + struct got_tree_entry *te = NULL; + + n = imsg_get(ibuf, &imsg); + if (n == 0) { + if (*tree && (*tree)->nentries != nentries) + goto get_more; + break; + } + + if (imsg.hdr.len < IMSG_HEADER_SIZE + min_datalen) + return got_error(GOT_ERR_PRIVSEP_LEN); + + data = imsg.data; + 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_TREE: + /* This message should only appear once. */ + if (*tree != NULL) { + err = got_error(GOT_ERR_PRIVSEP_MSG); + break; + } + if (datalen != sizeof(itree)) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + memcpy(&itree, imsg.data, sizeof(itree)); + *tree = calloc(1, sizeof(**tree)); + if (*tree == NULL) { + err = got_error_from_errno(); + break; + } + (*tree)->nentries = itree.nentries; + SIMPLEQ_INIT(&(*tree)->entries); + break; + case GOT_IMSG_TREE_ENTRY: + /* This message should be preceeded by GOT_IMSG_TREE. */ + if (*tree == NULL) { + err = got_error(GOT_ERR_PRIVSEP_MSG); + break; + } + if (datalen < sizeof(ite) || datalen > MAX_IMSGSIZE) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + + /* Remaining data contains the entry's name. */ + datalen -= sizeof(ite); + memcpy(&ite, imsg.data, sizeof(ite)); + if (datalen == 0 || datalen > MAX_IMSGSIZE) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + te = got_alloc_tree_entry_partial(); + if (te == NULL) { + err = got_error_from_errno(); + break; + } + te->name = malloc(datalen + 1); + if (te->name == NULL) { + free(te); + err = got_error_from_errno(); + break; + } + memcpy(te->name, imsg.data, datalen); + te->name[datalen] = '\0'; + + memcpy(te->id->sha1, ite.id, SHA1_DIGEST_LENGTH); + te->mode = ite.mode; + SIMPLEQ_INSERT_TAIL(&(*tree)->entries, te, entry); + nentries++; + break; + default: + err = got_error(GOT_ERR_PRIVSEP_MSG); + break; + } + + imsg_free(&imsg); + } + + if (*tree && (*tree)->nentries != nentries) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + got_object_tree_close(*tree); + *tree = NULL; + } + return err; }