mirror of
https://github.com/gophernicus/gophernicus.git
synced 2024-11-03 04:27:17 -05:00
Add OpenBSD pledge(2) and unveil(2) support.
See the README for semantics.
This commit is contained in:
parent
403d031507
commit
5d05d61ba9
29
README.md
29
README.md
@ -53,6 +53,9 @@ are made.
|
||||
-L text|file Set or load server location for caps.txt
|
||||
-A admin Set admin email for caps.txt
|
||||
|
||||
-U paths Specify a colon-separated list of extra unveil(2) paths
|
||||
(OpenBSD only).
|
||||
|
||||
-nv Disable virtual hosting
|
||||
-nl Disable parent directory links
|
||||
-nh Disable menu header (title)
|
||||
@ -108,6 +111,32 @@ The `-nx` option prevents execution of any script or external file,
|
||||
and the `-nu` option suppresses scanning for and serving of `~user`
|
||||
directories (which are normally at `~/public_html/` for each user).
|
||||
|
||||
### OpenBSD-specific Security
|
||||
|
||||
If you are running Gophernicus on OpenBSD, you may (depending on what features
|
||||
you want to use) be able to take advantage of unveil(2) and pledge(2).
|
||||
|
||||
If you run without executable map support (i.e. you run with `-nx`) then
|
||||
unveil(2) will be enabled and the server root will automatically be unveiled.
|
||||
If run with personal gopherspaces enabled (i.e. you run without `-nu`), then
|
||||
the password database (`/etc/pwd.db`) will automatically be unveiled, but you
|
||||
will have to manually unveil the filesystem path(s) from which to serve
|
||||
personal gopherspaces (see `-U`).
|
||||
|
||||
Running with `-nm -nu -nx` results in the strictest set of pledge(2) promises.
|
||||
If you have executable maps enabled (i.e. you run without `-nx`), then the
|
||||
promises are relaxed to allow `exec`. If you have personal gopherspaces enabled
|
||||
(i.e. you run without `-nu`), then the promises are relaxed to allow `getpw`.
|
||||
If you have shared memory enabled (i.e. you run without `-nm`), then pledge(2)
|
||||
support cannot be used at all.
|
||||
|
||||
In short, you probably want to run Gophernicus with `-nm -nu -nx` and then
|
||||
remove the flags that would otherwise disable the features you want.
|
||||
|
||||
To see what is going on with regards to pledge(2) and unveil(2), run
|
||||
Gophernicus with `-d` (to turn on debug logging) and look in your system logs.
|
||||
|
||||
|
||||
## Gophermaps
|
||||
|
||||
By default all gopher menus are automatically generated from the
|
||||
|
135
gophernicus.c
135
gophernicus.c
@ -448,6 +448,11 @@ void init_state(state *st)
|
||||
strclear(st->server_platform);
|
||||
strclear(st->server_admin);
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
st->extra_unveil_paths = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
/* Session */
|
||||
st->session_timeout = DEFAULT_SESSION_TIMEOUT;
|
||||
st->session_max_kbytes = DEFAULT_SESSION_MAX_KBYTES;
|
||||
@ -498,13 +503,17 @@ int main(int argc, char *argv[])
|
||||
#ifdef HAVE_SHMEM
|
||||
struct shmid_ds shm_ds;
|
||||
shm_state *shm;
|
||||
int shmid;
|
||||
int shmid = -1;
|
||||
#endif
|
||||
#ifdef ENABLE_HAPROXY1
|
||||
char remote[BUFSIZE];
|
||||
char local[BUFSIZE];
|
||||
int dummy;
|
||||
#endif
|
||||
#ifdef __OpenBSD__
|
||||
char pledges[256];
|
||||
char *extra_unveil;
|
||||
#endif
|
||||
|
||||
/* Get the name of this binary */
|
||||
if ((c = strrchr(argv[0], '/'))) sstrlcpy(self, c + 1);
|
||||
@ -523,6 +532,87 @@ int main(int argc, char *argv[])
|
||||
/* Open syslog() */
|
||||
if (st.opt_syslog) openlog(self, LOG_PID, LOG_DAEMON);
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
/* unveil(2) support.
|
||||
*
|
||||
* We only enable unveil(2) if the user isn't expecting to shell-out to
|
||||
* arbitrary commands.
|
||||
*/
|
||||
if (st.opt_exec) {
|
||||
if (st.extra_unveil_paths != NULL) {
|
||||
die(&st, NULL, "-U and executable maps cannot co-exist");
|
||||
}
|
||||
if (st.debug)
|
||||
syslog(LOG_INFO, "executable gophermaps are enabled, no unveil(2)");
|
||||
} else {
|
||||
if (unveil(st.server_root, "r") == -1)
|
||||
die(&st, NULL, "unveil");
|
||||
|
||||
/*
|
||||
* If we want personal gopherspaces, then we have to unveil(2) the user
|
||||
* database. This isn't actually needed if pledge(2) is enabled, as the
|
||||
* 'getpw' promise will ensure access to this file, but it doesn't hurt
|
||||
* to unveil it anyway.
|
||||
*/
|
||||
if (st.opt_personal_spaces) {
|
||||
if (st.debug)
|
||||
syslog(LOG_INFO, "unveiling /etc/pwd.db");
|
||||
if (unveil("/etc/pwd.db", "r") == -1)
|
||||
die(&st, NULL, "unveil");
|
||||
}
|
||||
|
||||
/* Any extra unveil paths that the user has specified */
|
||||
char *p = st.extra_unveil_paths;
|
||||
while (p != NULL) {
|
||||
extra_unveil = strsep(&p, ":");
|
||||
if (*extra_unveil == '\0')
|
||||
continue; /* empty path */
|
||||
|
||||
if (st.debug)
|
||||
syslog(LOG_INFO, "unveiling extra path: %s\n", extra_unveil);
|
||||
if (unveil(extra_unveil, "r") == -1)
|
||||
die(&st, NULL, "unveil");
|
||||
}
|
||||
|
||||
if (unveil(NULL, NULL) == -1)
|
||||
die(&st, NULL, "unveil");
|
||||
}
|
||||
|
||||
/* pledge(2) support */
|
||||
if (st.opt_shm) {
|
||||
/* pledge(2) never allows shared memory */
|
||||
if (st.debug)
|
||||
syslog(LOG_INFO, "shared-memory enabled, can't pledge(2)");
|
||||
} else {
|
||||
strlcpy(pledges,
|
||||
"stdio rpath inet sendfd recvfd proc",
|
||||
sizeof(pledges));
|
||||
|
||||
/* Executable maps shell-out using popen(3) */
|
||||
if (st.opt_exec) {
|
||||
strlcat(pledges, " exec", sizeof(pledges));
|
||||
if (st.debug) {
|
||||
syslog(LOG_INFO,
|
||||
"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));
|
||||
if (st.debug) {
|
||||
syslog(LOG_INFO,
|
||||
"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 &&
|
||||
@ -544,30 +634,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);
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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);
|
||||
|
||||
/* Keep server platform & description in shm */
|
||||
platform(&st);
|
||||
sstrlcpy(shm->server_platform, st.server_platform);
|
||||
sstrlcpy(shm->server_description, st.server_description);
|
||||
}
|
||||
}
|
||||
|
||||
/* For debugging shared memory issues */
|
||||
if (!st.opt_shm) shm = NULL;
|
||||
|
||||
/* Get server platform and description */
|
||||
if (shm) {
|
||||
|
@ -342,6 +342,10 @@ typedef struct {
|
||||
srewrite rewrite[MAX_REWRITE];
|
||||
int rewrite_count;
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
char *extra_unveil_paths;
|
||||
#endif
|
||||
|
||||
/* Session */
|
||||
int session_timeout;
|
||||
int session_max_kbytes;
|
||||
|
10
options.c
10
options.c
@ -101,7 +101,11 @@ void parse_args(state *st, int argc, char *argv[])
|
||||
int opt;
|
||||
|
||||
/* Parse args */
|
||||
while ((opt = getopt(argc, argv, "h:p:T:r:t:g:a:c:u:m:l:w:o:s:i:k:f:e:R:D:L:A:P:n:dbv?-")) != ERROR) {
|
||||
while ((opt = getopt(argc, argv,
|
||||
#ifdef __OpenBSD__
|
||||
"U:" /* extra unveil(2) paths are OpenBSD only */
|
||||
#endif
|
||||
"h:p:T:r:t:g:a:c:u:m:l:w:o:s:i:k:f:e:R:D:L:A:P:n:dbv?-")) != ERROR) {
|
||||
switch(opt) {
|
||||
case 'h': sstrlcpy(st->server_host, optarg); break;
|
||||
case 'p': st->server_port = atoi(optarg); break;
|
||||
@ -133,7 +137,9 @@ void parse_args(state *st, int argc, char *argv[])
|
||||
case 'D': sstrlcpy(st->server_description, optarg); break;
|
||||
case 'L': sstrlcpy(st->server_location, optarg); break;
|
||||
case 'A': sstrlcpy(st->server_admin, optarg); break;
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
case 'U': st->extra_unveil_paths = optarg; break;
|
||||
#endif
|
||||
case 'n':
|
||||
if (*optarg == 'v') { st->opt_vhost = FALSE; break; }
|
||||
if (*optarg == 'l') { st->opt_parent = FALSE; break; }
|
||||
|
Loading…
Reference in New Issue
Block a user