Files
Alvar Penning ea81780895 strip_exif support for the OpenBSD sandbox
Change the strip_exif logic to work with the already existing OpenBSD
sandbox and allow ffmpeg and mogrify to be executed.

The previous strip_exif implementation relied on system(3), effectively
starting "/bin/sh" and executing the required tool within a shell
session. Making this work in the sandbox would require to allow
executing "/bin/sh", rendering the sandbox useless.

Thus, the code now starts determining the absolute path of the tools -
unless they are given as ffmpeg_path or mogrify_path - and allowing them
to be executed via unveil(2). Then, instead of the system(3) call, the
good old fork(2) and execve(2) dance is performed.

The sbox_enter code was made aware of strip_exif, which resulted in a
pledge(2) violation before when disable_email_notifications was set to
false. Furthermore, the detected paths of the tools are now allowed.
2026-02-05 21:17:44 +01:00

175 lines
4.8 KiB
C

#include "xs.h"
#include "snac.h"
#if defined(__OpenBSD__)
void sbox_enter(const char *basedir)
{
const char *address = xs_dict_get(srv_config, "address");
if (xs_is_true(xs_dict_get(srv_config, "disable_openbsd_security"))) {
srv_log(xs_dup("OpenBSD security disabled by admin"));
return;
}
const xs_val *strip_exif = xs_dict_get(srv_config, "strip_exif");
int smail;
const char *url = xs_dict_get(srv_config, "smtp_url");
if (xs_is_string(url) && *url)
smail = 0;
else
smail = !xs_is_true(xs_dict_get(srv_config, "disable_email_notifications"));
srv_debug(1, xs_fmt("Calling unveil()"));
unveil(basedir, "rwc");
unveil("/tmp", "rwc");
unveil("/etc/resolv.conf", "r");
unveil("/etc/hosts", "r");
unveil("/etc/ssl/openssl.cnf", "r");
unveil("/etc/ssl/cert.pem", "r");
unveil("/usr/share/zoneinfo", "r");
if (*address == '/')
unveil(address, "rwc");
if (strip_exif) {
unveil(xs_dict_get(srv_config, "ffmpeg_path"), "x");
unveil(xs_dict_get(srv_config, "mogrify_path"), "x");
}
if (smail)
unveil("/usr/sbin/sendmail", "x");
unveil(NULL, NULL);
srv_debug(1, xs_fmt("Calling pledge()"));
xs *p = xs_str_new("stdio rpath wpath cpath flock inet proc dns fattr");
if (*address == '/')
p = xs_str_cat(p, " unix");
if (smail || strip_exif)
p = xs_str_cat(p, " exec");
pledge(p, NULL);
}
#elif defined(__linux__)
#if defined(WITH_LINUX_SANDBOX)
#include <unistd.h>
#define LL_PRINTERR(fmt, ...) srv_debug(0, xs_fmt(fmt, __VA_ARGS__))
#include "landloc.h"
static
LL_BEGIN(sbox_enter_linux_, const char* basedir, const char *address, int smtp_port) {
const unsigned long long
rd = LANDLOCK_ACCESS_FS_READ_DIR,
rf = LANDLOCK_ACCESS_FS_READ_FILE,
w = LANDLOCK_ACCESS_FS_WRITE_FILE |
LANDLOCK_ACCESS_FS_TRUNCATE_COMPAT,
c = LANDLOCK_ACCESS_FS_MAKE_DIR |
LANDLOCK_ACCESS_FS_MAKE_REG |
LANDLOCK_ACCESS_FS_TRUNCATE_COMPAT |
LANDLOCK_ACCESS_FS_MAKE_SYM |
LANDLOCK_ACCESS_FS_REMOVE_DIR |
LANDLOCK_ACCESS_FS_REMOVE_FILE |
LANDLOCK_ACCESS_FS_REFER_COMPAT,
s = LANDLOCK_ACCESS_FS_MAKE_SOCK,
x = LANDLOCK_ACCESS_FS_EXECUTE;
char *resolved_path = NULL;
LL_PATH(basedir, rf|rd|w|c);
LL_PATH("/tmp", rf|rd|w|c);
#ifndef WITHOUT_SHM
LL_PATH("/dev/shm", rf|w|c );
#endif
LL_PATH("/dev/urandom", rf );
LL_PATH("/etc/resolv.conf", rf );
LL_PATH("/etc/hosts", rf );
LL_PATH("/etc/ssl", rf|rd );
if ((resolved_path = realpath("/etc/ssl/cert.pem", NULL))) {
/* some distros like cert.pem to be a symlink */
LL_PATH(resolved_path, rf );
free(resolved_path);
}
LL_PATH("/usr/share/zoneinfo", rf );
if (mtime("/etc/pki") > 0)
LL_PATH("/etc/pki", rf );
if (*address == '/') {
/* the directory holding the socket must be allowed */
xs *l = xs_split(address, "/");
l = xs_list_del(l, -1);
xs *sdir = xs_join(l, "/");
LL_PATH(sdir, s);
}
if (*address != '/') {
unsigned short listen_port = xs_number_get(xs_dict_get(srv_config, "port"));
LL_PORT(listen_port, LANDLOCK_ACCESS_NET_BIND_TCP_COMPAT);
}
LL_PORT(80, LANDLOCK_ACCESS_NET_CONNECT_TCP_COMPAT);
LL_PORT(443, LANDLOCK_ACCESS_NET_CONNECT_TCP_COMPAT);
if (smtp_port > 0)
LL_PORT((unsigned short)smtp_port, LANDLOCK_ACCESS_NET_CONNECT_TCP_COMPAT);
} LL_END
void sbox_enter(const char *basedir)
{
const char *errstr;
const char *address = xs_dict_get(srv_config, "address");
const char *smtp_url = xs_dict_get(srv_config, "smtp_url");
int smtp_port = -1;
if (xs_is_true(xs_dict_get(srv_config, "disable_sandbox"))) {
srv_debug(1, xs_dup("Linux sandbox disabled by admin"));
return;
}
if (xs_is_string(smtp_url) && *smtp_url != '\0') {
smtp_port = parse_port(smtp_url, &errstr);
if (errstr)
srv_debug(0, xs_fmt("Couldn't determine port from '%s': %s", smtp_url, errstr));
}
if (sbox_enter_linux_(basedir, address, smtp_port) == 0)
srv_debug(1, xs_dup("Linux sandbox enabled"));
else
srv_debug(0, xs_dup("Linux sandbox failed"));
}
#else /* defined(WITH_LINUX_SANDBOX) */
void sbox_enter(const char *basedir)
{
(void)basedir;
srv_debug(1, xs_fmt("Linux sandbox not compiled in"));
}
#endif
#else
/* other OSs: dummy sbox_enter() */
void sbox_enter(const char *basedir)
{
(void)basedir;
}
#endif /* __OpenBSD__ */