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

Reply via email to