Hey, On Tue, Jan 01, 2019 at 12:30:04PM -0700, Theo de Raadt wrote: > In particular, specific pledges open up various system files, so those > do not need to be opened via unveil.
Gah, you're right! There's no need to unveil /etc/resolv.conf or /etc/pwd.db. > The lack of pledge "dns" but opening of resolv.conf shows a further > misunderstanding, and also a lack of testing. It turns out DNS is not required at all! So that's why it was working when I was testing. I also realised that none of 'tmppath', 'wpath' or 'cpath' pledges are required either. This brings us to the diff below. Anything else fishy? (Does anyone on-list host a real gopher site that they could test with?) Cheers! Index: Makefile =================================================================== RCS file: /cvs/ports/net/gophernicus/Makefile,v retrieving revision 1.17 diff -u -p -r1.17 Makefile --- Makefile 4 Sep 2018 12:46:17 -0000 1.17 +++ Makefile 31 Dec 2018 19:30:19 -0000 @@ -3,7 +3,7 @@ COMMENT= modern gopher server DISTNAME= gophernicus-2.5 CATEGORIES= net -REVISION= 0 +REVISION= 1 HOMEPAGE= gopher://gophernicus.org/ MAINTAINER = Brian Callahan <bcal...@openbsd.org> Index: patches/patch-README =================================================================== RCS file: patches/patch-README diff -N patches/patch-README --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-README 1 Jan 2019 16:01:17 -0000 @@ -0,0 +1,17 @@ +$OpenBSD$ + +Pledge and unveil. Based upon: +https://github.com/0x16h/gophernicus/commit/1f61d46a5ebb061d5862a6a61e296b473c169ec6 + +Index: README +--- README.orig ++++ README +@@ -43,6 +43,8 @@ Command line options: + -nm Disable shared memory use (for debugging) + -nr Disable root user checking (for debugging) + -np Disable HAproxy proxy protocol ++ -ne Disable executable gophermaps ++ -nu Disable personal gopherspaces + + -d Debug to syslog (not for production use) + -v Display version number and build date Index: patches/patch-gophernicus_c =================================================================== RCS file: patches/patch-gophernicus_c diff -N patches/patch-gophernicus_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-gophernicus_c 1 Jan 2019 21:26:28 -0000 @@ -0,0 +1,155 @@ +$OpenBSD$ + +Pledge and unveil. Based upon: +https://github.com/0x16h/gophernicus/commit/1f61d46a5ebb061d5862a6a61e296b473c169ec6 + +Index: gophernicus.c +--- gophernicus.c.orig ++++ gophernicus.c +@@ -219,7 +219,8 @@ void selector_to_path(state *st) + + #ifdef HAVE_PASSWD + /* Virtual userdir (~user -> /home/user/public_gopher)? */ +- if (*(st->user_dir) && sstrncmp(st->req_selector, "/~") == MATCH) { ++ if (st->opt_personal_spaces && *(st->user_dir) && ++ sstrncmp(st->req_selector, "/~") == MATCH) { + + /* Parse userdir login name & path */; + sstrlcpy(buf, st->req_selector + 2); +@@ -453,6 +454,8 @@ void init_state(state *st) + st->opt_shm = TRUE; + st->opt_root = TRUE; + st->opt_proxy = TRUE; ++ st->opt_execmaps = TRUE; ++ st->opt_personal_spaces = TRUE; + st->debug = FALSE; + + /* Load default suffix -> filetype mappings */ +@@ -488,6 +491,9 @@ int main(int argc, char *argv[]) + char local[BUFSIZE]; + int dummy; + #endif ++#ifdef __OpenBSD__ ++ char pledges[256]; ++#endif + + /* Get the name of this binary */ + if ((c = strrchr(argv[0], '/'))) sstrlcpy(self, c + 1); +@@ -506,6 +512,67 @@ int main(int argc, char *argv[]) + /* Open syslog() */ + if (st.opt_syslog) openlog(self, LOG_PID, LOG_DAEMON); + ++#ifdef __OpenBSD__ ++ /* unveil(2) support */ ++ if (st.opt_execmaps) { ++ /* ++ * We can't really unveil(2) if the user is expecting to shell-out ++ * to an arbitrary command. ++ */ ++ if (st.opt_syslog) { ++ syslog(LOG_WARNING, ++ "executable gophermaps enabled, can't unveil(2)"); ++ } ++ } else { ++ /* Always unveil the server root */ ++ if (unveil(st.server_root, "r") == -1) ++ die(&st, NULL, "unveil"); ++ ++ /* ++ * Personal spaces serve "gopher://host/1/~user" from ++ * "/home/user/public_gopher". ++ */ ++ if (st.opt_personal_spaces) { ++ syslog(LOG_WARNING, ++ "personal gopherspaces enabled, unveil(2) /home"); ++ if (unveil("/home", "r") == -1) ++ die(&st, NULL, "unveil"); ++ } ++ } ++ ++ /* pledge(2) support */ ++ if (st.opt_shm) { ++ /* ++ * pledge(2) never allows shared memory. ++ */ ++ if (st.opt_syslog) { ++ syslog(LOG_WARNING, ++ "shared-memory enabled, can't pledge(2)"); ++ } ++ } else { ++ strlcpy(pledges, ++ "stdio rpath inet sendfd recvfd tty proc", ++ sizeof(pledges)); ++ ++ /* Executable maps shell-out using popen(3) */ ++ if (st.opt_execmaps) { ++ strlcat(pledges, " exec", sizeof(pledges)); ++ syslog(LOG_WARNING, ++ "executable gophermaps enabled, adding 'exec' to pledge(2)"); ++ } ++ ++ /* Personal spaces require getpwnam(3) and getpwent(3) */ ++ if (st.opt_personal_spaces) { ++ strlcat(pledges, " getpw", sizeof(pledges)); ++ syslog(LOG_WARNING, ++ "personal gopherspaces enabled, adding 'getpw' to pledge(2)"); ++ } ++ ++ if (pledge(pledges, NULL) == -1) ++ die(&st, NULL, "pledge"); ++ } ++#endif ++ + /* Check if TCP wrappers have something to say about this connection */ + #ifdef HAVE_LIBWRAP + if (sstrncmp(st.req_remote_addr, UNKNOWN_ADDR) != MATCH && +@@ -527,30 +594,31 @@ int main(int argc, char *argv[]) + + /* Try to get shared memory */ + #ifdef HAVE_SHMEM +- if ((shmid = shmget(SHM_KEY, sizeof(shm_state), IPC_CREAT | SHM_MODE)) == ERROR) { ++ if (st.opt_shm) { ++ if ((shmid = shmget(SHM_KEY, sizeof(shm_state), IPC_CREAT | SHM_MODE)) == ERROR) { + +- /* Getting memory failed -> delete the old allocation */ +- shmctl(shmid, IPC_RMID, &shm_ds); +- shm = NULL; +- } +- else { +- /* Map shared memory */ +- if ((shm = (shm_state *) shmat(shmid, (void *) 0, 0)) == (void *) ERROR) ++ /* Getting memory failed -> delete the old allocation */ ++ shmctl(shmid, IPC_RMID, &shm_ds); + shm = NULL; ++ } ++ else { ++ /* Map shared memory */ ++ if ((shm = (shm_state *) shmat(shmid, (void *) 0, 0)) == (void *) ERROR) ++ shm = NULL; + +- /* Initialize mapped shared memory */ +- if (shm && shm->start_time == 0) { +- shm->start_time = time(NULL); ++ /* Initialize mapped shared memory */ ++ if (shm && shm->start_time == 0) { ++ shm->start_time = time(NULL); + +- /* Keep server platform & description in shm */ +- platform(&st); +- sstrlcpy(shm->server_platform, st.server_platform); +- sstrlcpy(shm->server_description, st.server_description); ++ /* Keep server platform & description in shm */ ++ platform(&st); ++ sstrlcpy(shm->server_platform, st.server_platform); ++ sstrlcpy(shm->server_description, st.server_description); ++ } + } ++ } else { ++ shm = NULL; + } +- +- /* For debugging shared memory issues */ +- if (!st.opt_shm) shm = NULL; + + /* Get server platform and description */ + if (shm) { Index: patches/patch-gophernicus_h =================================================================== RCS file: patches/patch-gophernicus_h diff -N patches/patch-gophernicus_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-gophernicus_h 1 Jan 2019 15:58:48 -0000 @@ -0,0 +1,17 @@ +$OpenBSD$ + +Pledge and unveil. Based upon: +https://github.com/0x16h/gophernicus/commit/1f61d46a5ebb061d5862a6a61e296b473c169ec6 + +Index: gophernicus.h +--- gophernicus.h.orig ++++ gophernicus.h +@@ -351,6 +351,8 @@ typedef struct { + char opt_shm; + char opt_root; + char opt_proxy; ++ char opt_execmaps; ++ char opt_personal_spaces; + char debug; + } state; + Index: patches/patch-menu_c =================================================================== RCS file: patches/patch-menu_c diff -N patches/patch-menu_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-menu_c 1 Jan 2019 15:58:49 -0000 @@ -0,0 +1,26 @@ +$OpenBSD$ + +Pledge and unveil. Based upon: +https://github.com/0x16h/gophernicus/commit/1f61d46a5ebb061d5862a6a61e296b473c169ec6 + +Index: menu.c +--- menu.c.orig ++++ menu.c +@@ -306,8 +306,15 @@ int gophermap(state *st, char *mapfile, int depth) + + /* Debug output */ + if (st->debug) { +- if (exe) syslog(LOG_INFO, "parsing executable gophermap \"%s\"", mapfile); +- else syslog(LOG_INFO, "parsing static gophermap \"%s\"", mapfile); ++ if (exe) { ++ if (st->opt_execmaps) ++ syslog(LOG_INFO, "parsing executable gophermap: %s", mapfile); ++ else { ++ syslog(LOG_INFO, "ignoring executable gophermap: %s", mapfile); ++ return OK; ++ } ++ } else ++ syslog(LOG_INFO, "parsing static gophermap: %s", mapfile); + } + + /* Try to execute or open the mapfile */ Index: patches/patch-options_c =================================================================== RCS file: patches/patch-options_c diff -N patches/patch-options_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-options_c 1 Jan 2019 15:58:52 -0000 @@ -0,0 +1,17 @@ +$OpenBSD$ + +Pledge and unveil. Based upon: +https://github.com/0x16h/gophernicus/commit/1f61d46a5ebb061d5862a6a61e296b473c169ec6 + +Index: options.c +--- options.c.orig ++++ options.c +@@ -143,6 +143,8 @@ void parse_args(state *st, int argc, char *argv[]) + if (*optarg == 'm') { st->opt_shm = FALSE; break; } + if (*optarg == 'r') { st->opt_root = FALSE; break; } + if (*optarg == 'p') { st->opt_proxy = FALSE; break; } ++ if (*optarg == 'e') { st->opt_execmaps = FALSE; break; } ++ if (*optarg == 'u') { st->opt_personal_spaces = FALSE; break; } + break; + + case 'd': st->debug = TRUE; break; Index: pkg/README =================================================================== RCS file: /cvs/ports/net/gophernicus/pkg/README,v retrieving revision 1.5 diff -u -p -r1.5 README --- pkg/README 4 Sep 2018 12:46:17 -0000 1.5 +++ pkg/README 1 Jan 2019 21:20:00 -0000 @@ -10,7 +10,7 @@ Setting up a gopher site After installing the gophernicus package, edit /etc/inetd.conf and add the following, all on one line: -gopher stream tcp nowait _gophernicus ${TRUEPREFIX}/libexec/in.gophernicus in.gophernicus -h "hostname" +gopher stream tcp nowait _gophernicus ${TRUEPREFIX}/libexec/in.gophernicus in.gophernicus -ne -nm -nu -h hostname Replace "hostname" with the system's hostname, this should be valid and resolvable as it is used to construct links. @@ -24,3 +24,24 @@ only serve files which are world-readabl server process is not enough. For more information, see ${TRUEPREFIX}/share/doc/gophernicus/README. + +pledge(2) / unveil(2) +===================== + +The above inetd.conf(8) line starts ${PKGSTEM} in the most secure mode of +operation with regards to pledge(2) and unveil(2). In this configuration, some +features are disabled in favour of tighter security: + + * The `-ne` argument disables executable gophermaps. If this argument is + removed, unveil(2) is totally disabled, since we cannot know what paths an + arbitrary popen(3)ed shell command might require. Additionally 'exec' is + passed to pledge(2). + + * The `-nm` argument disables shared-memory usage. If this argument is + removed, pledge(2) will be totally disabled, as pledge(2) never allows + shared memory. + + * The `-nu` argument disables personal gopher spaces. If this argument is + removed, we unveil(2) /home and 'getpw' is passed to pledge(2). + +If any of these arguments are not present, ${PKGSTEM} will warn via syslog(3). -- Best Regards Edd Barrett http://www.theunixzoo.co.uk