privsep: Allow Linux to work without needing any mounts

This commit is contained in:
Roy Marples 2020-05-24 05:47:14 +00:00
parent c572835e98
commit 12b0db43b2
12 changed files with 110 additions and 192 deletions

View File

@ -161,9 +161,3 @@ copied to `$(libexecdir)/dhcpcd-hooks` for use.
The configure program attempts to find hooks for systems you have installed.
To add more simply
`./configure -with-hook=ntp.conf`
If running privilege separation and on Linux then the `00-linux` hook is
**mandatory**.
If you choose not to run it, then you are responsible for setting up the
needed mount points: `/dev`, `/proc`, `/sys`, `/run/udev`
as well as sorting out `/dev/log` if it points to something outside of `/dev`.

3
configure vendored
View File

@ -1766,9 +1766,6 @@ if cd hooks; then
done
cd ..
fi
if [ "$OS" = linux ]; then
HOOKS="$HOOKS${HOOKS:+ }00-linux"
fi
echo "HOOKSCRIPTS= $HOOKS" >>$CONFIG_MK
echo "EGHOOKSCRIPTS= $EGHOOKS" >>$CONFIG_MK

View File

@ -1,17 +0,0 @@
# setup chroot mounts
if [ "$reason" = CHROOT ] && [ -n "$chroot" ]; then
# Special case /dev/log
if [ -h /dev/log ]; then
devlogdir=$(dirname $(readlink /dev/log))
else
devlogdir=
fi
for d in /dev /proc /sys /run/udev $devlogdir; do
[ -d "$d" ] || continue
if ! mountpoint -q "$chroot$d"; then
mkdir -p "$chroot$d"
mount --bind $d "$chroot$d"
fi
done
fi

View File

@ -109,28 +109,17 @@ ssize_t
readfile(const char *file, void *data, size_t len)
{
int fd;
struct stat st;
ssize_t bytes = -1;
ssize_t bytes;
fd = open(file, O_RDONLY);
if (fd == -1)
return -1;
if (fstat(fd, &st) != 0)
goto out;
if (!S_ISREG(st.st_mode)) {
errno = EINVAL;
goto out;
}
if ((size_t)st.st_size > len) {
errno = E2BIG;
goto out;
}
bytes = read(fd, data, len);
out:
if (fd != -1)
close(fd);
close(fd);
if ((size_t)bytes == len) {
errno = ENOBUFS;
return -1;
}
return bytes;
}

View File

@ -149,7 +149,7 @@ static int if_addressexists(struct interface *, struct in_addr *);
#define PROC_INET6 "/proc/net/if_inet6"
#define PROC_PROMOTE "/proc/sys/net/ipv4/conf/%s/promote_secondaries"
#define SYS_BRIDGE "/sys/class/net/%s/bridge"
#define SYS_BRIDGE "/sys/class/net/%s/bridge/bridge_id"
#define SYS_LAYER2 "/sys/class/net/%s/device/layer2"
#define SYS_TUNTAP "/sys/class/net/%s/tun_flags"
@ -220,53 +220,44 @@ if_machinearch(char *str, size_t len)
}
static int
check_proc_int(const char *path)
check_proc_int(struct dhcpcd_ctx *ctx, const char *path)
{
FILE *fp;
int i;
char buf[64];
int error, i;
fp = fopen(path, "r");
if (fp == NULL)
if (dhcp_readfile(ctx, path, buf, sizeof(buf)) == -1)
return -1;
if (fscanf(fp, "%d", &i) != 1)
i = -1;
fclose(fp);
i = (int)strtoi(buf, NULL, 0, INT_MIN, INT_MAX, &error);
if (error != 0 && error != ENOTSUP) {
errno = error;
return -1;
}
return i;
}
static int
check_proc_hex(const char *path, unsigned int *value)
check_proc_uint(struct dhcpcd_ctx *ctx, const char *path, unsigned int *u)
{
FILE *fp;
int i;
char buf[64];
int error;
fp = fopen(path, "r");
if (fp == NULL)
if (dhcp_readfile(ctx, path, buf, sizeof(buf)) == -1)
return -1;
i = fscanf(fp, "%x", value) == 1 ? 0 : -1;
fclose(fp);
return i;
*u = (unsigned int)strtou(buf, NULL, 0, 0, UINT_MAX, &error);
if (error != 0 && error != ENOTSUP) {
errno = error;
return error;
}
return 0;
}
static ssize_t
if_writepathuint(struct dhcpcd_ctx *ctx, const char *path, unsigned int val)
{
FILE *fp;
ssize_t r;
char buf[64];
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP)
return ps_root_writepathuint(ctx, path, val);
#else
UNUSED(ctx);
#endif
fp = fopen(path, "w");
if (fp == NULL)
return -1;
r = fprintf(fp, "%u\n", val);
fclose(fp);
return r;
snprintf(buf, sizeof(buf), "%u\n", val);
return dhcp_writefile(ctx, path, 0664, buf, sizeof(buf));
}
int
@ -283,7 +274,7 @@ if_init(struct interface *ifp)
* This matches the behaviour of BSD which makes coding dhcpcd
* a little easier as there's just one behaviour. */
snprintf(path, sizeof(path), PROC_PROMOTE, ifp->name);
n = check_proc_int(path);
n = check_proc_int(ifp->ctx, path);
if (n == -1)
return errno == ENOENT ? 0 : -1;
if (n == 1)
@ -299,7 +290,7 @@ if_conf(struct interface *ifp)
/* Some qeth setups require the use of the broadcast flag. */
snprintf(path, sizeof(path), SYS_LAYER2, ifp->name);
n = check_proc_int(path);
n = check_proc_int(ifp->ctx, path);
if (n == -1)
return errno == ENOENT ? 0 : -1;
if (n == 0)
@ -308,34 +299,33 @@ if_conf(struct interface *ifp)
}
static bool
if_bridge(const char *ifname)
if_bridge(struct dhcpcd_ctx *ctx, const char *ifname)
{
char path[sizeof(SYS_BRIDGE) + IF_NAMESIZE];
struct stat sb;
char path[sizeof(SYS_BRIDGE) + IF_NAMESIZE], buf[64];
snprintf(path, sizeof(path), SYS_BRIDGE, ifname);
if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode))
return true;
return false;
if (dhcp_readfile(ctx, path, buf, sizeof(buf)) == -1)
return false;
return true;
}
static bool
if_tap(const char *ifname)
if_tap(struct dhcpcd_ctx *ctx, const char *ifname)
{
char path[sizeof(SYS_TUNTAP) + IF_NAMESIZE];
unsigned int n;
unsigned int u;
snprintf(path, sizeof(path), SYS_TUNTAP, ifname);
if (check_proc_hex(path, &n) == -1)
if (check_proc_uint(ctx, path, &u) == -1)
return false;
return n & IFF_TAP;
return u & IFF_TAP;
}
bool
if_ignore(__unused struct dhcpcd_ctx *ctx, const char *ifname)
if_ignore(struct dhcpcd_ctx *ctx, const char *ifname)
{
if (if_tap(ifname) || if_bridge(ifname))
if (if_tap(ctx, ifname) || if_bridge(ctx, ifname))
return true;
return false;
}
@ -1866,14 +1856,19 @@ int
if_addrflags6(const struct interface *ifp, const struct in6_addr *addr,
__unused const char *alias)
{
FILE *fp;
char buf[PS_BUFLEN], *bp = buf, *line;
ssize_t buflen;
char *p, ifaddress[33], address[33], name[IF_NAMESIZE + 1];
unsigned int ifindex;
int prefix, scope, flags, i;
fp = fopen(PROC_INET6, "r");
if (fp == NULL)
buflen = dhcp_readfile(ifp->ctx, PROC_INET6, buf, sizeof(buf));
if (buflen == -1)
return -1;
if ((size_t)buflen == sizeof(buf)) {
errno = ENOBUFS;
return -1;
}
p = ifaddress;
for (i = 0; i < (int)sizeof(addr->s6_addr); i++) {
@ -1881,23 +1876,20 @@ if_addrflags6(const struct interface *ifp, const struct in6_addr *addr,
}
*p = '\0';
while (fscanf(fp, "%32[a-f0-9] %x %x %x %x %"TOSTRING(IF_NAMESIZE)"s\n",
address, &ifindex, &prefix, &scope, &flags, name) == 6)
{
if (strlen(address) != 32) {
fclose(fp);
while ((line = get_line(&bp, &buflen)) != NULL) {
if (sscanf(line,
"%32[a-f0-9] %x %x %x %x %"TOSTRING(IF_NAMESIZE)"s\n",
address, &ifindex, &prefix, &scope, &flags, name) != 6 ||
strlen(address) != 32)
{
errno = EINVAL;
return -1;
}
if (strcmp(name, ifp->name) == 0 &&
strcmp(ifaddress, address) == 0)
{
fclose(fp);
return flags;
}
}
fclose(fp);
errno = ESRCH;
return -1;
}
@ -1972,11 +1964,12 @@ static const char *p_neigh = "/proc/sys/net/ipv6/neigh";
void
if_setup_inet6(const struct interface *ifp)
{
struct dhcpcd_ctx *ctx = ifp->ctx;
int ra;
char path[256];
/* The kernel cannot make stable private addresses. */
if (if_disable_autolinklocal(ifp->ctx, ifp->index) == -1)
if (if_disable_autolinklocal(ctx, ifp->index) == -1 && errno != ENODEV)
logdebug("%s: if_disable_autolinklocal", ifp->name);
/*
@ -1987,21 +1980,21 @@ if_setup_inet6(const struct interface *ifp)
return;
snprintf(path, sizeof(path), "%s/%s/autoconf", p_conf, ifp->name);
ra = check_proc_int(path);
ra = check_proc_int(ctx, path);
if (ra != 1 && ra != -1) {
if (if_writepathuint(ifp->ctx, path, 0) == -1)
if (if_writepathuint(ctx, path, 0) == -1)
logerr("%s: %s", __func__, path);
}
snprintf(path, sizeof(path), "%s/%s/accept_ra", p_conf, ifp->name);
ra = check_proc_int(path);
ra = check_proc_int(ctx, path);
if (ra == -1) {
/* The sysctl probably doesn't exist, but this isn't an
* error as such so just log it and continue */
if (errno != ENOENT)
logerr("%s: %s", __func__, path);
} else if (ra != 0) {
if (if_writepathuint(ifp->ctx, path, 0) == -1)
if (if_writepathuint(ctx, path, 0) == -1)
logerr("%s: %s", __func__, path);
}
}
@ -2040,14 +2033,18 @@ if_applyra(const struct ra *rap)
int
ip6_forwarding(const char *ifname)
{
char path[256];
int val;
char path[256], buf[64];
int error, i;
if (ifname == NULL)
ifname = "all";
snprintf(path, sizeof(path), "%s/%s/forwarding", p_conf, ifname);
val = check_proc_int(path);
return val == -1 ? 0 : val;
if (readfile(path, buf, sizeof(buf)) == -1)
return 0;
i = (int)strtoi(buf, NULL, 0, INT_MIN, INT_MAX, &error);
if (error != 0)
return -1;
return error != 0 ? 0 : i;
}
#endif /* INET6 */

View File

@ -1106,6 +1106,13 @@ ipv6_anyglobal(struct interface *sifp)
if (ifp != sifp && !forwarding)
continue;
#else
#if defined(PRIVSEP) && defined(__linux__)
if (IN_PRIVSEP(sifp->ctx)) {
if (ifp != sifp &&
ps_root_ip6forwarding(sifp->ctx, ifp->name) != 1)
continue;
} else
#endif
if (ifp != sifp && ip6_forwarding(ifp->name) != 1)
continue;
#endif

View File

@ -524,11 +524,13 @@ ipv6nd_advertise(struct ipv6_addr *ia)
na->nd_na_type = ND_NEIGHBOR_ADVERT;
na->nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE;
#ifdef HAVE_PLEDGE
if (ps_root_ip6_forwarding(ctx) == 1)
#else
if (ip6_forwarding(ifp->name) == 1)
#if defined(PRIVSEP) && (defined(__linux__) || defined(HAVE_PLEDGE))
if (IN_PRIVSEP(ctx)) {
if (ps_root_ip6forwarding(ctx, ifp->name) == 1)
na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
} else
#endif
if (ip6_forwarding(ifp->name) == 1)
na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
na->nd_na_target = ia->addr;

View File

@ -151,8 +151,6 @@ ps_root_os(struct ps_msghdr *psm, struct msghdr *msg)
#ifdef HAVE_PLEDGE
case PS_IOCTLINDIRECT:
return ps_root_doindirectioctl(psm->ps_flags, data, len);
case PS_IP6FORWARDING:
return ip6_forwarding(NULL);
#endif
default:
errno = ENOTSUP;
@ -210,14 +208,4 @@ ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request,
return -1;
return ps_root_readerror(ctx, data, len);
}
ssize_t
ps_root_ip6forwarding(struct dhcpcd_ctx *ctx)
{
if (ps_sendcmd(ctx, ctx->ps_root_fd,
PS_IP6FORWARDING, 0, NULL, 0) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
#endif

View File

@ -64,50 +64,13 @@ out:
return retval;
}
static ssize_t
ps_root_dowritepathuint(const void *data, size_t len)
{
const char *path = data;
size_t plen;
unsigned int val;
int fd;
ssize_t r;
if (len < sizeof(plen)) {
errno = EINVAL;
return -1;
}
memcpy(&plen, path, sizeof(plen));
path += sizeof(plen);
if (sizeof(plen) + plen + sizeof(val) > len) {
errno = EINVAL;
return -1;
}
memcpy(&val, path + plen, sizeof(val));
fd = open(path, O_WRONLY);
if (fd == -1)
return -1;
r = dprintf(fd, "%u", val);
close(fd);
return r;
}
ssize_t
ps_root_os(struct ps_msghdr *psm, struct msghdr *msg)
{
struct iovec *iov = msg->msg_iov;
void *data = iov->iov_base;
size_t len = iov->iov_len;
switch (psm->ps_cmd) {
case PS_ROUTE:
return ps_root_dosendnetlink((int)psm->ps_flags, msg);
case PS_WRITEPATHUINT:
return ps_root_dowritepathuint(data, len);
default:
errno = ENOTSUP;
return -1;
@ -123,28 +86,3 @@ ps_root_sendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
ssize_t
ps_root_writepathuint(struct dhcpcd_ctx *ctx, const char *path,
unsigned int val)
{
char buf[PS_BUFLEN], *p = buf;
size_t plen = strlen(path) + 1;
size_t len = sizeof(plen) + plen + sizeof(val);
if (len > sizeof(buf)) {
errno = ENOBUFS;
return -1;
}
memcpy(p, &plen, sizeof(plen));
p += sizeof(plen);
memcpy(p, path, plen);
p += plen;
memcpy(p, &val, sizeof(val));
if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_WRITEPATHUINT,
0, buf, len) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}

View File

@ -295,6 +295,14 @@ ps_root_validpath(const struct dhcpcd_ctx *ctx, uint16_t cmd, const char *path)
return true;
if (strncmp(RUNDIR, path, strlen(RUNDIR)) == 0)
return true;
#ifdef __linux__
if (strncmp("/proc/net/", path, strlen("/proc/net/")) == 0 ||
strncmp("/proc/sys/net/", path, strlen("/proc/sys/net/")) == 0 ||
strncmp("/sys/class/net/", path, strlen("/sys/class/net/")) == 0)
return true;
#endif
errno = EPERM;
return false;
}
@ -310,6 +318,7 @@ ps_root_dowritefile(const struct dhcpcd_ctx *ctx,
errno = EINVAL;
return -1;
}
if (!ps_root_validpath(ctx, PS_WRITEFILE, file))
return -1;
nc++;
@ -510,6 +519,11 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
err = ps_root_dogetifaddrs(&rdata, &rlen);
free_rdata = true;
break;
#endif
#if defined(__linux__) || defined(HAVE_PLEDGE)
case PS_IP6FORWARDING:
err = ip6_forwarding(data);
break;
#endif
default:
err = ps_root_os(psm, msg);
@ -800,3 +814,15 @@ err:
return -1;
}
#endif
#if defined(__linux__) || defined(HAVE_PLEDGE)
ssize_t
ps_root_ip6forwarding(struct dhcpcd_ctx *ctx, const char *ifname)
{
if (ps_sendcmd(ctx, ctx->ps_root_fd,
PS_IP6FORWARDING, 0, ifname, strlen(ifname) + 1) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
#endif

View File

@ -37,6 +37,7 @@ int ps_root_stop(struct dhcpcd_ctx *ctx);
ssize_t ps_root_readerror(struct dhcpcd_ctx *, void *, size_t);
ssize_t ps_root_mreaderror(struct dhcpcd_ctx *, void **, size_t *);
ssize_t ps_root_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);
ssize_t ps_root_ip6forwarding(struct dhcpcd_ctx *, const char *);
ssize_t ps_root_unlink(struct dhcpcd_ctx *, const char *);
ssize_t ps_root_filemtime(struct dhcpcd_ctx *, const char *, time_t *);
ssize_t ps_root_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);
@ -52,7 +53,6 @@ ssize_t ps_root_ioctllink(struct dhcpcd_ctx *, unsigned long, void *, size_t);
ssize_t ps_root_ioctl6(struct dhcpcd_ctx *, unsigned long, void *, size_t);
ssize_t ps_root_indirectioctl(struct dhcpcd_ctx *, unsigned long, const char *,
void *, size_t);
ssize_t ps_root_ip6forwarding(struct dhcpcd_ctx *);
#endif
#ifdef __linux__
ssize_t ps_root_sendnetlink(struct dhcpcd_ctx *, int, struct msghdr *);

View File

@ -57,9 +57,6 @@
#define PS_IP6FORWARDING 0x0104
#define PS_GETIFADDRS 0x0105
/* Linux commands */
#define PS_WRITEPATHUINT 0x0201
/* Process commands */
#define PS_START 0x4000
#define PS_STOP 0x8000