Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use pledge(2) on OpenBSD to restrict system calls #1801

Closed
wants to merge 8 commits into from
95 changes: 95 additions & 0 deletions shairport.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@
#include <FFTConvolver/convolver.h>
#endif

#ifdef CONFIG_OPENBSD
#include <string.h> // strerror
#endif

pid_t pid;
#ifdef CONFIG_LIBDAEMON
int this_is_the_daemon_process = 0;
Expand Down Expand Up @@ -1929,6 +1933,12 @@ void _display_config(const char *filename, const int linenumber, __attribute__((
#define display_config(argc, argv) _display_config(__FILE__, __LINE__, argc, argv)

int main(int argc, char **argv) {
#ifdef COMPILE_FOR_OPENBSD
/* Start with the superset of all potentially required promises. */
if (pledge("stdio rpath wpath cpath dpath inet unix dns proc exec unveil audio", NULL) == -1)
die("pledge: %s", strerror(errno));
#endif

memset(&config, 0, sizeof(config)); // also clears all strings, BTW
/* Check if we are called with -V or --version parameter */
if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) {
Expand Down Expand Up @@ -2102,6 +2112,24 @@ int main(int argc, char **argv) {
// parse arguments into config -- needed to locate pid_dir
int audio_arg = parse_options(argc, argv);

#ifdef COMPILE_FOR_OPENBSD
int run_cmds =
config.cmd_active_start != NULL ||
config.cmd_active_stop != NULL ||
config.cmd_set_volume != NULL ||
config.cmd_start != NULL ||
config.cmd_stop != NULL;

/* Drop "proc exec" immediately, if possible. */
# if CONFIG_LIBDAEMON
if (!run_cmds && !config.daemonise)
# else
if (!run_cmds)
# endif
if (pledge("stdio rpath wpath cpath dpath inet unix dns unveil audio", NULL) == -1)
die("pledge: %s", strerror(errno));
#endif
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To limit the dangerous "proc exec" set to only those commands, one had to hoist poptParseArgvString() out of common.c`s command handlers, so the following becomes possible:

  } else {
    unveil(parsed_active_start_argv0, "x");
    ....
    unveil("/", "rwc");
    unveil(NULL, NULL);  // or pledge("..." /* no "unveil" */, NULL);
  }


// mDNS supports maximum of 63-character names (we append 13).
if (strlen(config.service_name) > 50) {
warn("The service name \"%s\" is too long (max 50 characters) and has been truncated.",
Expand Down Expand Up @@ -2235,6 +2263,13 @@ int main(int argc, char **argv) {
/* end libdaemon stuff */
}

# ifdef COMPILE_FOR_OPENBSD
/* Drop "proc exec", if possible. */
if (!run_cmds)
if (pledge("stdio rpath wpath cpath dpath inet unix dns unveil audio", NULL) == -1)
die("pledge: %s", strerror(errno));
# endif

#endif

#ifdef CONFIG_AIRPLAY_2
Expand Down Expand Up @@ -2352,6 +2387,66 @@ int main(int argc, char **argv) {
}
config.output->init(argc - audio_arg, argv + audio_arg);

#ifdef COMPILE_FOR_OPENBSD
/*
* At this point, the first and last sio_open(3) call was made, i.e.
* the sndio(7) cookie is dealt with and only "audio" is needed.
*/

if (run_cmds) {
/* Do not bother with "*path" as long as "proc exec" can do everything. */
} else {
/*
* unveil(2) TODO:
* - assume system D-Bus, hoist setup/defer unveil
* - glib2 locale (not critical!)
* - MQTT?
*/
# if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
if (unveil("/var/run/dbus/system_bus_socket", "rw") == -1)
die("unveil D-Bus: %s", strerror(errno));
if (unveil("/usr/local/share/locale", "r") == -1)
die("unveil locale: %s", strerror(errno));
# endif

/*
* Only coverart cache is created/written.
* Only metadata pipe is special.
*/
int need_cpath_dpath = 0;
# ifdef CONFIG_METADATA
if (config.metadata_enabled) {
need_cpath_dpath = 1;
# ifdef CONFIG_METADATA_HUB
/*
* XXX unveiling default /tmp/shairport-sync/.cache/coverart may fail
* as parent directories do not exist.
*/
int do_cache =
config.cover_art_cache_dir != NULL &&
config.cover_art_cache_dir[0] != '\0';

if (do_cache)
if (unveil(config.cover_art_cache_dir, "wc") == -1)
die("unveil %s: %s", config.cover_art_cache_dir, strerror(errno));
# endif
if (unveil(config.metadata_pipename, "wc") == -1)
die("unveil %s: %s", config.metadata_pipename, strerror(errno));
}
# endif

/* Drop "unveil". */
if (need_cpath_dpath) {
if (pledge("stdio rpath wpath cpath dpath inet unix dns audio", NULL) == -1)
die("pledge: %s", strerror(errno));
} else {
/* Drop "cpath dpath". */
if (pledge("stdio rpath wpath inet unix dns audio", NULL) == -1)
die("pledge: %s", strerror(errno));
}
}
#endif

// pthread_cleanup_push(main_cleanup_handler, NULL);

// daemon_log(LOG_NOTICE, "startup");
Expand Down
Loading