#include <err.h>
#include <pthread.h>
#include <priv.h>
#include <stdlib.h>
#include <string.h>
#include <upanic.h>
#include <sys/debug.h>
#include "config.h"
#include "debug.h"
#include "privileges.h"
static pthread_t priv_thread;
static priv_set_t *bhyve_priv_init;
static priv_set_t *bhyve_priv_min;
static priv_set_t *bhyve_priv_max;
static bool priv_debug = false;
#define DPRINTF(params) \
if (priv_debug) do { PRINTLN params; fflush(stdout); } while (0)
static void
illumos_priv_printset(const char *tag, priv_set_t *set)
{
char *s;
s = priv_set_to_str(set, ',', PRIV_STR_LIT);
if (s == NULL) {
warn("priv_set_to_str(%s) failed", tag);
return;
}
DPRINTF((" + %s: %s", tag, s));
free(s);
}
void
illumos_priv_reduce(void)
{
VERIFY3S(pthread_equal(pthread_self(), priv_thread), !=, 0);
DPRINTF((" + Reducing privileges to minimum"));
if (setppriv(PRIV_SET, PRIV_EFFECTIVE, bhyve_priv_min) != 0)
err(4, "failed to reduce privileges");
}
static void
illumos_priv_add_set(priv_set_t *set, const char *priv, const char *src)
{
VERIFY3S(pthread_equal(pthread_self(), priv_thread), !=, 0);
DPRINTF((" + Adding privilege %s (%s)", priv, src));
if (!priv_ismember(bhyve_priv_init, priv))
errx(4, "Privilege %s (for %s) not in initial set", priv, src);
if (priv_addset(set, priv) != 0)
err(4, "Failed to add %s privilege for %s", priv, src);
}
void
illumos_priv_add(const char *priv, const char *src)
{
illumos_priv_add_set(bhyve_priv_max, priv, src);
}
void
illumos_priv_add_min(const char *priv, const char *src)
{
illumos_priv_add_set(bhyve_priv_min, priv, src);
illumos_priv_add_set(bhyve_priv_max, priv, src);
}
void
illumos_priv_init(void)
{
priv_debug = get_config_bool_default("privileges.debug", false);
DPRINTF((" + Initialising privileges."));
if (priv_debug)
(void) setpflags(PRIV_DEBUG, 1);
priv_thread = pthread_self();
if ((bhyve_priv_init = priv_allocset()) == NULL)
err(4, "failed to allocate memory for initial priv set");
if ((bhyve_priv_min = priv_allocset()) == NULL)
err(4, "failed to allocate memory for minimum priv set");
if ((bhyve_priv_max = priv_allocset()) == NULL)
err(4, "failed to allocate memory for maximum priv set");
if (getppriv(PRIV_EFFECTIVE, bhyve_priv_init) != 0)
err(4, "failed to fetch current privileges");
if (priv_debug)
illumos_priv_printset("initial", bhyve_priv_init);
priv_basicset(bhyve_priv_min);
VERIFY0(priv_delset(bhyve_priv_min, PRIV_FILE_LINK_ANY));
VERIFY0(priv_delset(bhyve_priv_min, PRIV_FILE_WRITE));
VERIFY0(priv_delset(bhyve_priv_min, PRIV_NET_ACCESS));
VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_EXEC));
VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_FORK));
VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_INFO));
VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_SECFLAGS));
VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_SESSION));
priv_intersect(bhyve_priv_init, bhyve_priv_min);
priv_copyset(bhyve_priv_min, bhyve_priv_max);
illumos_priv_add(PRIV_FILE_WRITE, "init");
if (priv_ismember(bhyve_priv_init, PRIV_PROC_CLOCK_HIGHRES))
illumos_priv_add_min(PRIV_PROC_CLOCK_HIGHRES, "init");
else
warnx("The 'proc_clock_highres' privilege is not available");
}
void
illumos_priv_lock(void)
{
VERIFY3S(pthread_equal(pthread_self(), priv_thread), !=, 0);
if (bhyve_priv_init == NULL) {
const char *msg = "attempted to re-lock privileges";
upanic(msg, strlen(msg));
}
priv_intersect(bhyve_priv_init, bhyve_priv_max);
if (priv_debug) {
DPRINTF((" + Locking privileges"));
illumos_priv_printset("min", bhyve_priv_min);
illumos_priv_printset("max", bhyve_priv_max);
}
if (setppriv(PRIV_SET, PRIV_PERMITTED, bhyve_priv_max) != 0) {
const char *fail = "failed to reduce permitted privileges";
upanic(fail, strlen(fail));
}
if (setppriv(PRIV_SET, PRIV_LIMIT, bhyve_priv_max) != 0) {
const char *fail = "failed to reduce limit privileges";
upanic(fail, strlen(fail));
}
illumos_priv_reduce();
priv_freeset(bhyve_priv_init);
bhyve_priv_init = NULL;
}