diff --git a/Makefile.am b/Makefile.am index d2b3992..5f12222 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,4 +40,7 @@ if INSTALL_FREEBSD_STARTUP endif endif - +if BUILD_FOR_OPENBSD +# NQPTP starts as root on OpenBSD to access ports 319 and 320 +# and drops privileges to the user shairport is running as. +endif diff --git a/configure.ac b/configure.ac index ff1947f..503b805 100644 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,7 @@ AC_CANONICAL_HOST build_linux=no build_freebsd=no +build_openbsd=no # Detect the target system case "${host_os}" in @@ -16,7 +17,10 @@ case "${host_os}" in ;; freebsd*) build_freebsd=yes - ;; + ;; + openbsd*) + build_openbsd=yes + ;; *) AC_MSG_ERROR(["OS $host_os is not supported"]) ;; @@ -25,6 +29,7 @@ esac # Pass the conditionals to automake AM_CONDITIONAL([BUILD_FOR_LINUX], [test "$build_linux" = "yes"]) AM_CONDITIONAL([BUILD_FOR_FREEBSD], [test "$build_freebsd" = "yes"]) +AM_CONDITIONAL([BUILD_FOR_OPENBSD], [test "$build_openbsd" = "yes"]) if test "x$build_linux" = "xyes" ; then AC_DEFINE([CONFIG_FOR_LINUX], 1, [Build for Linux.]) @@ -32,6 +37,9 @@ fi if test "x$build_freebsd" = "xyes" ; then AC_DEFINE([CONFIG_FOR_FREEBSD], 1, [Build for FreeBSD.]) fi +if test "x$build_openbsd" = "xyes" ; then + AC_DEFINE([CONFIG_FOR_OPENBSD], 1, [Build for OpenBSD.]) +fi AC_CHECK_PROGS([GIT], [git]) if test -n "$GIT" && test -e ".git/index" ; then @@ -44,7 +52,7 @@ AM_CONDITIONAL([USE_GIT_VERSION], [test -n "$GIT" && test -e ".git/index" ]) AC_ARG_WITH([systemd-startup],[AS_HELP_STRING([--with-systemd-startup],[install a systemd startup script during a make install])]) AM_CONDITIONAL([INSTALL_SYSTEMD_STARTUP], [test "x$with_systemd_startup" = "xyes"]) -# Check to see if we should include the systemd stuff to define it as a service +# Check to see if we should include the FreeBSD stuff to define it as a service AC_ARG_WITH([freebsd-startup],[AS_HELP_STRING([--with-freebsd-startup],[install a FreeBSD startup script during a make install])]) AM_CONDITIONAL([INSTALL_FREEBSD_STARTUP], [test "x$with_freebsd_startup" = "xyes"]) @@ -58,7 +66,10 @@ AC_PROG_INSTALL # Checks for libraries. AC_CHECK_LIB([pthread],[pthread_create], , AC_MSG_ERROR(pthread library needed)) -AC_CHECK_LIB([rt],[clock_gettime], , AC_MSG_ERROR(librt needed for shared memory library)) +if test "x$build_openbsd" = "xno" ; then + # part of libc + AC_CHECK_LIB([rt],[clock_gettime], , AC_MSG_ERROR(librt needed for shared memory library)) +fi # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h inttypes.h netdb.h stdlib.h string.h sys/socket.h unistd.h]) diff --git a/nqptp-utilities.c b/nqptp-utilities.c index 7c30877..18e5a9c 100644 --- a/nqptp-utilities.c +++ b/nqptp-utilities.c @@ -28,7 +28,7 @@ #include // sockaddr_ll #endif -#ifdef CONFIG_FOR_FREEBSD +#if defined(CONFIG_FOR_FREEBSD) || defined(CONFIG_FOR_OPENBSD) #include #include #include @@ -106,7 +106,7 @@ void open_sockets_at_port(const char *node, uint16_t port, freeaddrinfo(info); if (sockets_opened == 0) { if (errno == EACCES) { - die("nqptp does not have permission to access port %u. It must (a) [Linux only] have been given CAP_NET_BIND_SERVICE capabilities using e.g. setcap or systemd's AmbientCapabilities, or (b) run as root.", port); + die("nqptp does not have permission to access port %u. It must (a) [Linux only] have been given CAP_NET_BIND_SERVICE capabilities using e.g. setcap or systemd's AmbientCapabilities, or (b) start as root.", port); } else { die("nqptp is unable to listen on port %u. The error is: %d, \"%s\".", port, errno, strerror(errno)); } diff --git a/nqptp.c b/nqptp.c index a1a3c76..73b0d66 100644 --- a/nqptp.c +++ b/nqptp.c @@ -48,11 +48,17 @@ #include #include -#ifdef CONFIG_FOR_FREEBSD +#if defined(CONFIG_FOR_FREEBSD) || defined(CONFIG_FOR_OPENBSD) #include #include #endif +#ifdef CONFIG_FOR_OPENBSD +#include +#include +#include +#endif + #ifndef FIELD_SIZEOF #define FIELD_SIZEOF(t, f) (sizeof(((t *)0)->f)) #endif @@ -125,6 +131,11 @@ void termHandler(__attribute__((unused)) int k) { } int main(int argc, char **argv) { +#ifdef CONFIG_FOR_OPENBSD + if (pledge("stdio rpath tmppath inet dns id", NULL) == -1) { + die("pledge: %s", strerror(errno)); + } +#endif int debug_level = 0; int i; @@ -177,6 +188,10 @@ int main(int argc, char **argv) { sockets_open_stuff.sockets_open = 0; + // open PTP sockets + open_sockets_at_port(NULL, 319, &sockets_open_stuff); + open_sockets_at_port(NULL, 320, &sockets_open_stuff); + epoll_fd = -1; // control-c (SIGINT) cleanly @@ -191,6 +206,26 @@ int main(int argc, char **argv) { act2.sa_handler = termHandler; sigaction(SIGTERM, &act2, NULL); +#ifdef CONFIG_FOR_OPENBSD + // shm_open(3) prohibits sharing between different UIDs, so nqptp must run as + // the same user shairport-sync does. + struct passwd *pw; + const char *shairport_user = "_shairport"; + pw = getpwnam(shairport_user); + if (pw == NULL) { + die("unknown user %s", shairport_user); + } + if (setgroups(1, &pw->pw_gid) == -1 || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) { + die("cannot drop privileges to %s", shairport_user); + } + + if (pledge("stdio tmppath inet dns", NULL) == -1) { + die("pledge: %s", strerror(errno)); + } +#endif + // open the SMI shm_fd = -1; @@ -206,7 +241,7 @@ int main(int argc, char **argv) { die("failed to set size of shared memory \"%s\".", NQPTP_INTERFACE_NAME); } -#ifdef CONFIG_FOR_FREEBSD +#if defined(CONFIG_FOR_FREEBSD) || defined(CONFIG_FOR_OPENBSD) shared_memory = (struct shm_structure *)mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); #endif @@ -233,10 +268,7 @@ int main(int argc, char **argv) { char buf[BUFLEN]; - // open sockets 319 and 320 - - open_sockets_at_port(NULL, 319, &sockets_open_stuff); - open_sockets_at_port(NULL, 320, &sockets_open_stuff); + // open control socket open_sockets_at_port("localhost", NQPTP_CONTROL_PORT, &sockets_open_stuff); // this for messages from the client