commit 2178c42edfccac6cf2793ba7ba5da36a0f28324e from: Stefan Sperling date: Sun Apr 22 12:51:49 2018 UTC read object headers with privsep commit - e6b1056ef3e2060a93683e3069bbc1bb48c40e87 commit + 2178c42edfccac6cf2793ba7ba5da36a0f28324e blob - 6523bd29cb23ef003060976305bef92050abacb5 blob + 0044a48fd54842d1cf8fc7bcc9f7c0144bf4be4a --- got/Makefile +++ got/Makefile @@ -2,7 +2,8 @@ PROG= got SRCS= got.c delta.c diff.c diffreg.c error.c fileindex.c object.c \ - path.c pack.c reference.c repository.c sha1.c worktree.c zbuf.c + path.c pack.c privsep.c reference.c repository.c sha1.c \ + worktree.c zbuf.c CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib LDADD = -lutil -lz blob - c9ea7a877089fb35835cb3d796c4f8efae413fbc blob + 3666d5bce24758194183e573ddf33cc2dd733953 --- got/got.c +++ got/got.c @@ -180,7 +180,7 @@ cmd_checkout(int argc, char *argv[]) argv += optind; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath flock", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc", NULL) == -1) err(1, "pledge"); #endif if (argc == 1) { @@ -449,7 +449,7 @@ cmd_log(int argc, char *argv[]) const char *errstr; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath", NULL) == -1) + if (pledge("stdio rpath wpath cpath proc", NULL) == -1) err(1, "pledge"); #endif @@ -623,7 +623,7 @@ cmd_diff(int argc, char *argv[]) int ch; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath", NULL) == -1) + if (pledge("stdio rpath wpath cpath proc", NULL) == -1) err(1, "pledge"); #endif blob - 86c785de07fb2604fc83ef040c4eef616c0598ad blob + 92dd8ec2b275504c93573219db0f65d317daf3f7 --- include/got_error.h +++ include/got_error.h @@ -46,6 +46,12 @@ #define GOT_ERR_DIR_OBSTRUCTED 30 #define GOT_ERR_FILE_OBSTRUCTED 31 #define GOT_ERR_RECURSION 32 +#define GOT_ERR_TIMEOUT 33 +#define GOT_ERR_INTERRUPT 34 +#define GOT_ERR_PRIVSEP_READ 35 +#define GOT_ERR_PRIVSEP_LEN 36 +#define GOT_ERR_PRIVSEP_PIPE 37 +#define GOT_ERR_PRIVSEP_NO_FD 38 static const struct got_error { int code; @@ -80,6 +86,13 @@ static const struct got_error { { GOT_ERR_WORKTREE_VERS,"unsupported worktree format version" }, { GOT_ERR_WORKTREE_BUSY,"worktree already locked" }, { GOT_ERR_RECURSION, "recursion limit reached" }, + { GOT_ERR_TIMEOUT, "operation timed out" }, + { GOT_ERR_INTERRUPT, "operation interrupted" }, + { GOT_ERR_PRIVSEP_READ, "no data received from unprivileged process" }, + { GOT_ERR_PRIVSEP_LEN, "unexpected amount of data received " + "from unprivileged process" }, + { GOT_ERR_PRIVSEP_PIPE, "unprivileged process closed pipe" }, + { GOT_ERR_PRIVSEP_NO_FD,"out of file descriptors for privsep" }, }; /* blob - ada9bf24224874127a2434200404a4aa622396ff blob + 01e6ece311a71c7f129dd8ac5cb7a2ce6573f663 --- lib/got_lib_delta.h +++ lib/got_lib_delta.h @@ -31,6 +31,8 @@ struct got_delta_chain { SIMPLEQ_HEAD(, got_delta) entries; }; +#define GOT_DELTA_CHAIN_RECURSION_MAX 100 + struct got_delta *got_delta_open(const char *, off_t, size_t, int, size_t, off_t, uint8_t *, size_t); void got_delta_close(struct got_delta *); blob - 79bae21ce2f62bd2ba6faca67f3b86668c23a5a5 blob + a5baf8992edfe2ecb99afd5b264e96c34a1bff05 --- lib/got_lib_privsep.h +++ lib/got_lib_privsep.h @@ -46,8 +46,7 @@ enum got_imsg_type { * separate process which runs under pledge("stdio"). * This sandboxes our own repository parsing code, as well as zlib. */ - GOT_IMSG_LOOSE_OBJECT_HEADER_REQUEST, - GOT_IMSG_LOOSE_OBJECT_HEADER_REPLY, + GOT_IMSG_OBJECT, GOT_IMSG_LOOSE_BLOB_OBJECT_REQUEST, GOT_IMSG_LOOSE_TREE_OBJECT_REQUEST, GOT_IMSG_LOOSE_COMMIT_OBJECT_REQUEST, @@ -64,6 +63,7 @@ enum got_imsg_type { /* Structure for GOT_IMSG_ERROR. */ struct got_imsg_error { int code; /* an error code from got_error.h */ + int errno_code; /* in case code equals GOT_ERR_ERRNO */ }; /* Structure for GOT_IMSG_DELTA data. */ @@ -92,15 +92,7 @@ struct got_imsg_delta_stream { */ }; -/* Structure for GOT_IMSG_LOOSE_OBJECT_HEADER_REQUEST data. */ -struct got_imsg_loose_object_header_request { - /* - * Empty since the following is implied: If imsg fd == -1 then - * read raw object data from imsg buffer, else read from fd. - */ -}; - -/* Structure for transmitting struct got_object data in an imsg. */ +/* Structure for GOT_IMSG_OBJECT data. */ struct got_imsg_object { /* These fields are the same as in struct got_object. */ int type; @@ -159,4 +151,10 @@ struct got_imsg_tree_object { int nentries; /* This many TREE_ENTRY messages follow. */ }; +void got_privsep_send_error(struct imsgbuf *, const struct got_error *); +const struct got_error *got_privsep_send_obj(struct imsgbuf *, + struct got_object *, int); +const struct got_error *got_privsep_recv_obj(struct got_object **, + struct imsgbuf *); + /* TODO: Implement the above, and then add more message data types here. */ blob - 76455bbdd7ca3d4884e66ff542a70b2d39bb3aae blob + a069e1adb81442a557e2dc9bceab6ed1ee868c24 --- lib/object.c +++ lib/object.c @@ -14,17 +14,24 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include +#include +#include +#include #include +#include #include #include #include +#include #include #include #include #include +#include #include "got_error.h" #include "got_object.h" @@ -33,8 +40,10 @@ #include "got_lib_sha1.h" #include "got_lib_delta.h" #include "got_lib_pack.h" +#include "got_lib_path.h" #include "got_lib_zbuf.h" #include "got_lib_object.h" +#include "got_lib_privsep.h" #ifndef MIN #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) @@ -166,8 +175,7 @@ parse_object_header(struct got_object **obj, char *buf } static const struct got_error * -read_object_header(struct got_object **obj, struct got_repository *repo, - FILE *f) +read_object_header(struct got_object **obj, FILE *f) { const struct got_error *err; struct got_zstream_buf zb; @@ -205,6 +213,72 @@ read_object_header(struct got_object **obj, struct got err = parse_object_header(obj, buf, totlen); done: got_inflate_end(&zb); + return err; +} + +static const struct got_error * +read_object_header_privsep(struct got_object **obj, int fd) +{ + 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) { + struct got_object *child_obj = NULL; + struct imsgbuf child_ibuf; + FILE *f = NULL; + int status = 0; + + setproctitle("got: read object header"); + close(imsg_fds[0]); + imsg_init(&child_ibuf, imsg_fds[1]); + if (err) + goto done; + + /* revoke access to most system calls */ + if (pledge("stdio", NULL) == -1) { + err = got_error_from_errno(); + goto done; + } + + f = fdopen(fd, "rb"); + if (f == NULL) { + err = got_error_from_errno(); + goto done; + } + + err = read_object_header(&child_obj, f); + if (err) + goto done; + + err = got_privsep_send_obj(&child_ibuf, child_obj, 0); +done: + if (child_obj) + got_object_close(child_obj); + if (err) { + got_privsep_send_error(&child_ibuf, err); + status = 1; + } + if (f) + fclose(f); + imsg_clear(&child_ibuf); + _exit(status); + } + + close(imsg_fds[1]); + imsg_init(&parent_ibuf, imsg_fds[0]); + err = got_privsep_recv_obj(obj, &parent_ibuf); + imsg_clear(&parent_ibuf); + waitpid(pid, &child_status, 0); + close(imsg_fds[0]); return err; } @@ -258,14 +332,14 @@ got_object_open(struct got_object **obj, struct got_re { const struct got_error *err = NULL; char *path; - FILE *f; + int fd; err = object_path(&path, id, repo); if (err) return err; - f = fopen(path, "rb"); - if (f == NULL) { + fd = open(path, O_RDONLY | O_NOFOLLOW, GOT_DEFAULT_FILE_MODE); + if (fd == -1) { if (errno != ENOENT) { err = got_error_from_errno(); goto done; @@ -276,15 +350,15 @@ got_object_open(struct got_object **obj, struct got_re if (*obj == NULL) err = got_error(GOT_ERR_NO_OBJ); } else { - err = read_object_header(obj, repo, f); + err = read_object_header_privsep(obj, fd); if (err) goto done; memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH); } done: free(path); - if (f) - fclose(f); + if (fd != -1) + close(fd); return err; } blob - 710d4edf7f4242a2bde28dbcbfcbdf99c9774b31 blob + a82e6faaf749168b298617bc5c024054687df83a --- lib/pack.c +++ lib/pack.c @@ -55,8 +55,6 @@ #define GOT_PACKIDX_NAMELEN (strlen(GOT_PACK_PREFIX) + \ SHA1_DIGEST_STRING_LENGTH - 1 + \ strlen(GOT_PACKIDX_SUFFIX)) - -#define GOT_DELTA_CHAIN_RECURSION_MAX 100 #ifndef MIN #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) blob - /dev/null blob + 4b6246c0fc9f2ca3eefc339b4243aa76058886e9 (mode 644) --- /dev/null +++ lib/privsep.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "got_object.h" +#include "got_error.h" + +#include "got_lib_sha1.h" +#include "got_lib_delta.h" +#include "got_lib_zbuf.h" +#include "got_lib_object.h" +#include "got_lib_privsep.h" + +#ifndef MIN +#define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) +#endif + +static const struct got_error * +poll_fd(int fd, int events, int timeout) +{ + struct pollfd pfd[1]; + int n; + + pfd[0].fd = fd; + pfd[0].events = events; + + n = poll(pfd, 1, timeout); + if (n == -1) + return got_error_from_errno(); + if (n == 0) + return got_error(GOT_ERR_TIMEOUT); + if (pfd[0].revents & (POLLERR | POLLNVAL)) + return got_error_from_errno(); + if (pfd[0].revents & (events | POLLHUP)) + return NULL; + + return got_error(GOT_ERR_INTERRUPT); +} + +/* Attempt to send an error in an imsg. Complain on stderr as a last resort. */ +void +got_privsep_send_error(struct imsgbuf *ibuf, const struct got_error *err) +{ + const struct got_error *poll_err; + struct got_imsg_error ierr; + int ret; + + ierr.code = err->code; + if (err->code == GOT_ERR_ERRNO) + ierr.errno_code = errno; + else + ierr.errno_code = 0; + ret = imsg_compose(ibuf, GOT_IMSG_ERROR, 0, 0, -1, &ierr, sizeof(ierr)); + if (ret != -1) { + fprintf(stderr, "%s: error %d \"%s\": imsg_compose: %s\n", + getprogname(), err->code, err->msg, strerror(errno)); + } + + poll_err = poll_fd(ibuf->fd, POLLOUT, INFTIM); + if (poll_err) + fprintf(stderr, "%s: error %d \"%s\": poll: %s\n", + getprogname(), err->code, err->msg, poll_err->msg); + + ret = imsg_flush(ibuf); + if (ret == -1) + fprintf(stderr, "%s: error %d \"%s\": imsg_flush: %s\n", + getprogname(), err->code, err->msg, strerror(errno)); +} + +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; + iobj.flags = obj->flags; + iobj.hdrlen = obj->hdrlen; + iobj.size = obj->size; + memcpy(iobj.id.sha1, obj->id.sha1, SHA1_DIGEST_LENGTH); + iobj.ndeltas = ndeltas; + + if (ndeltas > 0) { + /* TODO: Handle deltas */ + } + + if (imsg_compose(ibuf, GOT_IMSG_OBJECT, 0, 0, -1, &iobj, sizeof(iobj)) + == -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; +} + +const struct got_error * +got_privsep_recv_obj(struct got_object **obj, struct imsgbuf *ibuf) +{ + const struct got_error *err = NULL; + struct got_imsg_error ierr; + struct imsg imsg; + struct got_imsg_object iobj; + ssize_t n, m; + size_t datalen; + int i; + + *obj = NULL; + + err = poll_fd(ibuf->fd, POLLIN, INFTIM); + if (err) + return err; + + n = imsg_read(ibuf); + if (n == -1) { + if (errno == EAGAIN) /* Could be a file-descriptor leak. */ + return got_error(GOT_ERR_PRIVSEP_NO_FD); + return got_error(GOT_ERR_PRIVSEP_READ); + } + if (n == 0) + return got_error(GOT_ERR_PRIVSEP_PIPE); + + m = imsg_get(ibuf, &imsg); + if (m == 0) + return got_error(GOT_ERR_PRIVSEP_READ); + + if (imsg.hdr.len < IMSG_HEADER_SIZE + MIN(sizeof(ierr), sizeof(obj))) + return got_error(GOT_ERR_PRIVSEP_LEN); + + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + + switch (imsg.hdr.type) { + case GOT_IMSG_ERROR: + if (datalen != sizeof(ierr)) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + memcpy(&ierr, imsg.data, sizeof(ierr)); + if (ierr.code == GOT_ERR_ERRNO) { + static struct got_error err; + err.code = GOT_ERR_ERRNO; + err.msg = strerror(ierr.errno_code); + } else + err = got_error(ierr.code); + break; + case GOT_IMSG_OBJECT: + if (datalen != sizeof(iobj)) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + + memcpy(&iobj, imsg.data, sizeof(iobj)); + if (iobj.ndeltas < 0 || + iobj.ndeltas > GOT_DELTA_CHAIN_RECURSION_MAX) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + + *obj = calloc(1, sizeof(**obj)); + if (*obj == NULL) { + err = got_error_from_errno(); + break; + } + + (*obj)->type = iobj.type; + (*obj)->hdrlen = iobj.hdrlen; + (*obj)->size = iobj.size; + memcpy((*obj)->id.sha1, iobj.id.sha1, SHA1_DIGEST_LENGTH); + for (i = 0; i < iobj.ndeltas; i++) { + /* TODO: Handle deltas */ + } + break; + } + + imsg_free(&imsg); + + return err; +} blob - 452c4df4f034ed4c0e9ad4a3edf6185facac954e blob + 0058276d9ffc15cf54575813b1acc5eb96b1a99c --- regress/repository/Makefile +++ regress/repository/Makefile @@ -2,7 +2,7 @@ PROG = repository_test SRCS = path.c repository.c error.c reference.c object.c sha1.c diff.c \ - diffreg.c pack.c delta.c zbuf.c repository_test.c + diffreg.c pack.c privsep.c delta.c zbuf.c repository_test.c CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib LDADD = -lutil -lz blob - ee04dc00dd2a769d5216f1acd5271c554871c21c blob + 67c32399cf6d5ff610bbb6cfae01015915a78850 --- regress/repository/repository_test.c +++ regress/repository/repository_test.c @@ -71,9 +71,12 @@ print_parent_commits(struct got_commit_object *commit, if (err != NULL) return err; if (got_object_get_type(obj) != GOT_OBJ_TYPE_COMMIT) - return got_error(GOT_ERR_OBJ_TYPE); - err = print_commit_object(obj, repo); + err = got_error(GOT_ERR_OBJ_TYPE); + else + err = print_commit_object(obj, repo); got_object_close(obj); + if (err) + break; } return err; @@ -451,7 +454,7 @@ main(int argc, char *argv[]) const char *repo_path; int ch; - if (pledge("stdio rpath wpath cpath", NULL) == -1) + if (pledge("stdio rpath wpath cpath proc", NULL) == -1) err(1, "pledge"); while ((ch = getopt(argc, argv, "v")) != -1) { blob - 49d5b1d64b78f5f5f35942045f0799409ac76d7f blob + 7d7df3bd9b3f8e19311418e163aa4904b3ab7239 --- regress/worktree/Makefile +++ regress/worktree/Makefile @@ -2,7 +2,7 @@ PROG = worktree_test SRCS = worktree.c repository.c object.c path.c error.c reference.c sha1.c \ - pack.c delta.c zbuf.c fileindex.c worktree_test.c + pack.c privsep.c delta.c zbuf.c fileindex.c worktree_test.c CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib LDADD = -lutil -lz blob - 0e19e1cd770c8f3d68e25d54e5b72912ceaaac00 blob + eb723d0e2017c280c82d33e528302983a90378ab --- regress/worktree/worktree_test.c +++ regress/worktree/worktree_test.c @@ -389,7 +389,7 @@ main(int argc, char *argv[]) const char *repo_path; int ch; - if (pledge("stdio rpath wpath cpath flock", NULL) == -1) + if (pledge("stdio rpath wpath cpath flock proc", NULL) == -1) err(1, "pledge"); while ((ch = getopt(argc, argv, "v")) != -1) {