privsep: Handle all file IO in the Priviledged Actioneer

This allows us to move the database directory back into the
root of the filesystem.
While here, harden the files by denying any user read access to them.

As part of this change, init the DUID from any machine data and
cache the default DHCP vendor field before dropping priviledges as we
may lose access to this later.
This commit is contained in:
Roy Marples 2020-05-12 10:26:35 +01:00
parent 123d782925
commit cf85354d04
27 changed files with 511 additions and 563 deletions

View File

@ -26,17 +26,18 @@
* SUCH DAMAGE.
*/
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "dhcpcd.h"
#include "if-options.h"
#include "logerr.h"
const char *
hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)
@ -103,36 +104,58 @@ hwaddr_aton(uint8_t *buffer, const char *addr)
return len;
}
size_t
read_hwaddr_aton(uint8_t **data, const char *path)
ssize_t
readfile(const char *file, void *data, size_t len)
{
FILE *fp;
char *buf;
size_t buf_len, len;
int fd;
struct stat st;
ssize_t bytes = -1;
if ((fp = fopen(path, "r")) == NULL)
return 0;
fd = open(file, O_RDONLY);
if (fd == -1)
return -1;
buf = NULL;
buf_len = len = 0;
*data = NULL;
while (getline(&buf, &buf_len, fp) != -1) {
if ((len = hwaddr_aton(NULL, buf)) != 0) {
if (buf_len >= len)
*data = (uint8_t *)buf;
else {
if ((*data = malloc(len)) == NULL)
len = 0;
}
if (len != 0)
(void)hwaddr_aton(*data, buf);
if (buf_len < len)
free(buf);
break;
}
if (fstat(fd, &st) != 0)
goto out;
if (!S_ISREG(st.st_mode)) {
errno = EINVAL;
goto out;
}
fclose(fp);
return len;
if ((size_t)st.st_size > len) {
errno = E2BIG;
goto out;
}
bytes = read(fd, data, len);
out:
if (fd != -1)
close(fd);
return bytes;
}
ssize_t
writefile(const char *file, mode_t mode, const void *data, size_t len)
{
int fd;
ssize_t bytes;
fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
if (fd == -1)
return -1;
bytes = write(fd, data, len);
close(fd);
return bytes;
}
int
filemtime(const char *file, time_t *time)
{
struct stat st;
if (stat(file, &st) == -1)
return -1;
*time = st.st_mtime;
return 0;
}
int

View File

@ -147,6 +147,8 @@
const char *hwaddr_ntoa(const void *, size_t, char *, size_t);
size_t hwaddr_aton(uint8_t *, const char *);
size_t read_hwaddr_aton(uint8_t **, const char *);
ssize_t readfile(const char *, void *, size_t);
ssize_t writefile(const char *, mode_t, const void *, size_t);
int filemtime(const char *, time_t *);
int is_root_local(void);
#endif

View File

@ -26,7 +26,6 @@
* SUCH DAMAGE.
*/
#include <sys/stat.h>
#include <sys/utsname.h>
#include <ctype.h>
@ -176,9 +175,10 @@ dhcp_vendor(char *str, size_t len)
return -1;
p += l;
len -= (size_t)l;
l = if_machinearch(p, len);
l = if_machinearch(p + 1, len - 1);
if (l == -1 || (size_t)(l + 1) > len)
return -1;
*p = ':';
p += l;
return p - str;
}
@ -945,38 +945,85 @@ dhcp_zero_index(struct dhcp_opt *opt)
dhcp_zero_index(o);
}
size_t
dhcp_read_lease_fd(int fd, void **lease)
ssize_t
dhcp_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data, size_t len)
{
struct stat st;
size_t sz;
void *buf;
ssize_t len;
if (fstat(fd, &st) != 0)
goto out;
if (!S_ISREG(st.st_mode)) {
errno = EINVAL;
goto out;
}
if (st.st_size > UINT32_MAX) {
errno = E2BIG;
goto out;
}
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
return ps_root_readfile(ctx, file, data, len);
#else
UNUSED(ctx);
#endif
sz = (size_t)st.st_size;
if (sz == 0)
goto out;
if ((buf = malloc(sz)) == NULL)
goto out;
if ((len = read(fd, buf, sz)) == -1) {
free(buf);
goto out;
}
*lease = buf;
return (size_t)len;
out:
*lease = NULL;
return 0;
return readfile(file, data, len);
}
ssize_t
dhcp_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
const void *data, size_t len)
{
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
return ps_root_writefile(ctx, file, mode, data, len);
#else
UNUSED(ctx);
#endif
return writefile(file, mode, data, len);
}
int
dhcp_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
{
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
return ps_root_filemtime(ctx, file, time);
#else
UNUSED(ctx);
#endif
return filemtime(file, time);
}
int
dhcp_unlink(struct dhcpcd_ctx *ctx, const char *file)
{
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
return ps_root_unlink(ctx, file);
#else
UNUSED(ctx);
#endif
return unlink(file);
}
size_t
dhcp_read_hwaddr_aton(struct dhcpcd_ctx *ctx, uint8_t **data, const char *file)
{
char buf[BUFSIZ];
ssize_t bytes;
size_t len;
bytes = dhcp_readfile(ctx, file, buf, sizeof(buf));
if (bytes == -1 || bytes == sizeof(buf))
return 0;
bytes[buf] = '\0';
len = hwaddr_aton(NULL, buf);
if (len == 0)
return 0;
*data = malloc(len);
if (*data == NULL)
return 0;
hwaddr_aton(*data, buf);
return len;
}

View File

@ -136,6 +136,11 @@ void dhcp_envoption(struct dhcpcd_ctx *,
const uint8_t *, size_t, struct dhcp_opt **),
const uint8_t *od, size_t ol);
void dhcp_zero_index(struct dhcp_opt *);
size_t dhcp_read_lease_fd(int, void **);
ssize_t dhcp_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);
ssize_t dhcp_writefile(struct dhcpcd_ctx *, const char *, mode_t,
const void *, size_t);
int dhcp_filemtime(struct dhcpcd_ctx *, const char *, time_t *);
int dhcp_unlink(struct dhcpcd_ctx *, const char *);
size_t dhcp_read_hwaddr_aton(struct dhcpcd_ctx *, uint8_t **, const char *);
#endif

View File

@ -28,7 +28,6 @@
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <net/if.h>
@ -1142,31 +1141,16 @@ toobig:
return -1;
}
static ssize_t
write_lease(const struct interface *ifp, const struct bootp *bootp, size_t len)
{
int fd;
ssize_t bytes;
const struct dhcp_state *state = D_CSTATE(ifp);
logdebugx("%s: writing lease `%s'", ifp->name, state->leasefile);
fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
return -1;
bytes = write(fd, bootp, len);
close(fd);
return bytes;
}
static size_t
read_lease(struct interface *ifp, struct bootp **bootp)
{
int fd;
bool fd_opened;
union {
struct bootp bootp;
uint8_t buf[FRAMELEN_MAX];
} buf;
struct dhcp_state *state = D_STATE(ifp);
struct bootp *lease;
size_t bytes;
ssize_t bytes;
uint8_t type;
#ifdef AUTH
const uint8_t *auth;
@ -1177,37 +1161,26 @@ read_lease(struct interface *ifp, struct bootp **bootp)
*bootp = NULL;
if (state->leasefile[0] == '\0') {
fd = fileno(stdin);
fd_opened = false;
} else {
fd = open(state->leasefile, O_RDONLY);
fd_opened = true;
}
if (fd == -1) {
if (errno != ENOENT)
logerr("%s: open `%s'",
ifp->name, state->leasefile);
return 0;
}
if (state->leasefile[0] == '\0')
logdebugx("reading standard input");
else
bytes = read(fileno(stdin), buf.buf, sizeof(buf.buf));
} else {
logdebugx("%s: reading lease `%s'",
ifp->name, state->leasefile);
bytes = dhcp_read_lease_fd(fd, (void **)&lease);
if (fd_opened)
close(fd);
if (bytes == 0)
bytes = dhcp_readfile(ifp->ctx, state->leasefile,
buf.buf, sizeof(buf.buf));
}
if (bytes == -1) {
if (errno != ENOENT)
logerr("%s: %s", ifp->name, state->leasefile);
return 0;
}
/* Ensure the packet is at lease BOOTP sized
* with a vendor area of 4 octets
* (it should be more, and our read packet enforces this so this
* code should not be needed, but of course people could
* scribble whatever in the stored lease file. */
if (bytes < DHCP_MIN_LEN) {
free(lease);
if ((size_t)bytes < DHCP_MIN_LEN) {
logerrx("%s: %s: truncated lease", ifp->name, __func__);
return 0;
}
@ -1216,20 +1189,19 @@ read_lease(struct interface *ifp, struct bootp **bootp)
goto out;
/* We may have found a BOOTP server */
if (get_option_uint8(ifp->ctx, &type, (struct bootp *)lease, bytes,
if (get_option_uint8(ifp->ctx, &type, &buf.bootp, bytes,
DHO_MESSAGETYPE) == -1)
type = 0;
#ifdef AUTH
/* Authenticate the message */
auth = get_option(ifp->ctx, (struct bootp *)lease, bytes,
auth = get_option(ifp->ctx, &buf.bootp, bytes,
DHO_AUTHENTICATION, &auth_len);
if (auth) {
if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
lease, bytes, 4, type, auth, auth_len) == NULL)
{
logerr("%s: authentication failed", ifp->name);
free(lease);
return 0;
}
if (state->auth.token)
@ -1241,14 +1213,18 @@ read_lease(struct interface *ifp, struct bootp **bootp)
DHCPCD_AUTH_SENDREQUIRE)
{
logerrx("%s: authentication now required", ifp->name);
free(lease);
return 0;
}
#endif
out:
*bootp = (struct bootp *)lease;
return bytes;
*bootp = malloc(bytes);
if (*bootp == NULL) {
logerr(__func__);
return 0;
}
memcpy(*bootp, buf.buf, bytes);
return (size_t)bytes;
}
static const struct dhcp_opt *
@ -1929,7 +1905,7 @@ dhcp_expire1(struct interface *ifp)
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
dhcp_drop(ifp, "EXPIRE");
unlink(state->leasefile);
dhcp_unlink(ifp->ctx, state->leasefile);
state->interval = 0;
if (!(ifp->options->options & DHCPCD_LINK) || ifp->carrier > LINK_DOWN)
dhcp_discover(ifp);
@ -2072,7 +2048,7 @@ dhcp_addr_duplicated(struct interface *ifp, struct in_addr *ia)
/* RFC 2131 3.1.5, Client-server interaction */
logerrx("%s: DAD detected %s", ifp->name, inet_ntoa(*ia));
unlink(state->leasefile);
dhcp_unlink(ifp->ctx, state->leasefile);
if (!(opts & DHCPCD_STATIC) && !state->lease.frominfo)
dhcp_decline(ifp);
#ifdef IN_IFF_DUPLICATED
@ -2299,9 +2275,13 @@ dhcp_bind(struct interface *ifp)
}
state->state = DHS_BOUND;
if (!state->lease.frominfo &&
!(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
if (write_lease(ifp, state->new, state->new_len) == -1)
logerr("write_lease: %s", state->leasefile);
!(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))) {
logdebugx("%s: writing lease `%s'",
ifp->name, state->leasefile);
if (dhcp_writefile(ifp->ctx, state->leasefile, 0640,
state->new, state->new_len) == -1)
logerr("dhcp_writefile: %s", state->leasefile);
}
/* Close the BPF filter as we can now receive DHCP messages
* on a UDP socket. */
@ -2749,7 +2729,7 @@ dhcp_drop(struct interface *ifp, const char *reason)
return;
state->state = DHS_RELEASE;
unlink(state->leasefile);
dhcp_unlink(ifp->ctx, state->leasefile);
if (ifp->carrier > LINK_DOWN &&
state->new != NULL &&
state->lease.server.s_addr != INADDR_ANY)
@ -3114,7 +3094,7 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
return;
if (!(ifp->ctx->options & DHCPCD_TEST)) {
dhcp_drop(ifp, "NAK");
unlink(state->leasefile);
dhcp_unlink(ifp->ctx, state->leasefile);
}
/* If we constantly get NAKS then we should slowly back off */
@ -3788,7 +3768,7 @@ dhcp_init(struct interface *ifp)
/* We need to drop the leasefile so that dhcp_start
* doesn't load it. */
if (ifo->options & DHCPCD_REQUEST)
unlink(state->leasefile);
dhcp_unlink(ifp->ctx, state->leasefile);
free(state->clientid);
state->clientid = NULL;
@ -3862,7 +3842,6 @@ dhcp_start1(void *arg)
struct dhcpcd_ctx *ctx = ifp->ctx;
struct if_options *ifo = ifp->options;
struct dhcp_state *state;
struct stat st;
uint32_t l;
int nolease;
@ -3947,6 +3926,7 @@ dhcp_start1(void *arg)
}
if (state->offer) {
struct ipv4_addr *ia;
time_t mtime;
get_lease(ifp, &state->lease, state->offer, state->offer_len);
state->lease.frominfo = 1;
@ -3974,14 +3954,14 @@ dhcp_start1(void *arg)
state->offer_len = 0;
} else if (!(ifo->options & DHCPCD_LASTLEASE_EXTEND) &&
state->lease.leasetime != DHCP_INFINITE_LIFETIME &&
stat(state->leasefile, &st) == 0)
dhcp_filemtime(ifp->ctx, state->leasefile, &mtime) == 0)
{
time_t now;
/* Offset lease times and check expiry */
now = time(NULL);
if (now == -1 ||
(time_t)state->lease.leasetime < now - st.st_mtime)
(time_t)state->lease.leasetime < now - mtime)
{
logdebugx("%s: discarding expired lease",
ifp->name);
@ -4006,7 +3986,7 @@ dhcp_start1(void *arg)
dhcp_drop(ifp, "EXPIRE");
#endif
} else {
l = (uint32_t)(now - st.st_mtime);
l = (uint32_t)(now - mtime);
state->lease.leasetime -= l;
state->lease.renewaltime -= l;
state->lease.rebindtime -= l;

View File

@ -26,7 +26,6 @@
* SUCH DAMAGE.
*/
#include <sys/stat.h>
#include <sys/utsname.h>
#include <netinet/in.h>
@ -258,9 +257,8 @@ static size_t
dhcp6_makevendor(void *data, const struct interface *ifp)
{
const struct if_options *ifo;
size_t len, i;
size_t len, vlen, i;
uint8_t *p;
ssize_t vlen;
const struct vivco *vivco;
char vendor[VENDORCLASSID_MAX_LEN];
struct dhcp6_option o;
@ -274,11 +272,8 @@ dhcp6_makevendor(void *data, const struct interface *ifp)
len += sizeof(uint16_t) + vivco->len;
vlen = 0; /* silence bogus gcc warning */
} else {
vlen = dhcp_vendor(vendor, sizeof(vendor));
if (vlen == -1)
vlen = 0;
else
len += sizeof(uint16_t) + (size_t)vlen;
vlen = strlcpy(vendor, ifp->ctx->vendor, sizeof(vendor));
len += sizeof(uint16_t) + vlen;
}
if (len > UINT16_MAX) {
@ -1748,7 +1743,7 @@ dhcp6_fail(struct interface *ifp)
state->new_len = 0;
if (state->old != NULL)
script_runreason(ifp, "EXPIRE6");
unlink(state->leasefile);
dhcp_unlink(ifp->ctx, state->leasefile);
}
if (!dhcp6_startdiscoinform(ifp)) {
@ -2590,106 +2585,77 @@ dhcp6_validatelease(struct interface *ifp,
}
static ssize_t
dhcp6_writelease(const struct interface *ifp)
{
const struct dhcp6_state *state;
int fd;
ssize_t bytes;
state = D6_CSTATE(ifp);
logdebugx("%s: writing lease `%s'", ifp->name, state->leasefile);
fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
return -1;
bytes = write(fd, state->new, state->new_len);
close(fd);
return bytes;
}
static int
dhcp6_readlease(struct interface *ifp, int validate)
{
union {
struct dhcp6_message dhcp6;
uint8_t buf[UDPLEN_MAX];
} buf;
struct dhcp6_state *state;
struct stat st;
ssize_t bytes;
int fd;
time_t now;
int retval;
bool read_stdin, fd_opened;
time_t mtime, now;
#ifdef AUTH
uint8_t *o;
uint16_t ol;
#endif
state = D6_STATE(ifp);
read_stdin = state->leasefile[0] == '\0';
if (read_stdin) {
if (state->leasefile[0] == '\0') {
logdebugx("reading standard input");
fd = fileno(stdin);
fd_opened = false;
bytes = read(fileno(stdin), buf.buf, sizeof(buf.buf));
} else {
logdebugx("%s: reading lease `%s'", ifp->name,state->leasefile);
fd = open(state->leasefile, O_RDONLY);
if (fd != -1 && fstat(fd, &st) == -1) {
close(fd);
fd = -1;
}
fd_opened = true;
logdebugx("%s: reading lease `%s'",
ifp->name, state->leasefile);
bytes = dhcp_readfile(ifp->ctx, state->leasefile,
buf.buf, sizeof(buf.buf));
}
if (fd == -1)
return -1;
retval = -1;
free(state->new);
state->new_len = dhcp_read_lease_fd(fd, (void **)&state->new);
if (fd_opened)
close(fd);
if (ifp->ctx->options & DHCPCD_DUMPLEASE || read_stdin)
return 0;
if (state->new_len == 0) {
retval = 0;
if (bytes == -1)
goto ex;
if (ifp->ctx->options & DHCPCD_DUMPLEASE || state->leasefile[0] == '\0')
goto out;
if (bytes == 0)
goto ex;
}
/* If not validating IA's and if they have expired,
* skip to the auth check. */
if (!validate) {
fd = 0;
if (!validate)
goto auth;
}
if (dhcp_filemtime(ifp->ctx, state->leasefile, &mtime) == -1)
goto ex;
clock_gettime(CLOCK_MONOTONIC, &state->acquired);
if ((now = time(NULL)) == -1)
goto ex;
state->acquired.tv_sec -= now - st.st_mtime;
state->acquired.tv_sec -= now - mtime;
/* Check to see if the lease is still valid */
fd = dhcp6_validatelease(ifp, state->new, state->new_len, NULL,
fd = dhcp6_validatelease(ifp, &buf.dhcp6, bytes, NULL,
&state->acquired);
if (fd == -1)
goto ex;
if (state->expire != ND6_INFINITE_LIFETIME &&
(time_t)state->expire < now - st.st_mtime &&
(time_t)state->expire < now - mtime &&
!(ifp->options->options & DHCPCD_LASTLEASE_EXTEND))
{
logdebugx("%s: discarding expired lease", ifp->name);
retval = 0;
bytes = 0;
goto ex;
}
auth:
retval = 0;
#ifdef AUTH
/* Authenticate the message */
o = dhcp6_findmoption(state->new, state->new_len, D6_OPTION_AUTH, &ol);
o = dhcp6_findmoption(&buf.dhcp6, bytes, D6_OPTION_AUTH, &ol);
if (o) {
if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
(uint8_t *)state->new, state->new_len, 6, state->new->type,
o, ol) == NULL)
buf.buf, bytes, 6, buf.dhcp6.type, o, ol) == NULL)
{
logerr("%s: authentication failed", ifp->name);
bytes = 0;
goto ex;
}
if (state->auth.token)
@ -2705,22 +2671,32 @@ auth:
}
#endif
return fd;
out:
free(state->new);
state->new = malloc(bytes);
if (state->new == NULL) {
logerr(__func__);
goto ex;
}
memcpy(state->new, buf.buf, bytes);
state->new_len = bytes;
return bytes;
ex:
dhcp6_freedrop_addrs(ifp, 0, NULL);
unlink(state->leasefile);
dhcp_unlink(ifp->ctx, state->leasefile);
free(state->new);
state->new = NULL;
state->new_len = 0;
return retval;
return bytes == 0 ? 0 : -1;
}
static void
dhcp6_startinit(struct interface *ifp)
{
struct dhcp6_state *state;
int r;
ssize_t r;
uint8_t has_ta, has_non_ta;
size_t i;
@ -3259,9 +3235,13 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
logmessage(loglevel, "%s: expire in %"PRIu32" seconds",
ifp->name, state->expire);
rt_build(ifp->ctx, AF_INET6);
if (!confirmed && !timedout)
if (dhcp6_writelease(ifp) == -1)
logerr("dhcp6_writelease: %s",state->leasefile);
if (!confirmed && !timedout) {
logdebugx("%s: writing lease `%s'",
ifp->name, state->leasefile);
if (dhcp_writefile(ifp->ctx, state->leasefile, 0640,
state->new, state->new_len) == -1)
logerr("dhcp_writefile: %s",state->leasefile);
}
#ifndef SMALL
dhcp6_delegate_prefix(ifp);
#endif
@ -3688,7 +3668,7 @@ dhcp6_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, struct ipv6_addr *ia)
* replay it in my code.
*/
static int replyn = 0;
char fname[PATH_MAX], tbuf[64 * 1024];
char fname[PATH_MAX], tbuf[UDPLEN_MAX];
int fd;
ssize_t tlen;
uint8_t *si1, *si2;
@ -3725,7 +3705,7 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
struct sockaddr_in6 from;
union {
struct dhcp6_message dhcp6;
uint8_t buf[64 * 1024]; /* Maximum UDP message size */
uint8_t buf[UDPLEN_MAX]; /* Maximum UDP message size */
} iovbuf;
struct iovec iov = {
.iov_base = iovbuf.buf, .iov_len = sizeof(iovbuf.buf),
@ -4044,7 +4024,7 @@ dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
dhcp6_startrelease(ifp);
return;
}
unlink(state->leasefile);
dhcp_unlink(ifp->ctx, state->leasefile);
}
dhcp6_freedrop_addrs(ifp, drop, NULL);
free(state->old);

View File

@ -33,4 +33,4 @@
#include <unistd.h>
const char * const dhcpcd_embedded_conf[] = {
const char dhcpcd_embedded_conf[] =

View File

@ -35,4 +35,4 @@
#define INITDEFINE6S @INITDEFINE6S@
#endif
extern const char * const dhcpcd_embedded_conf[];
extern const char dhcpcd_embedded_conf[];

View File

@ -357,8 +357,17 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
eloop_event_delete(ctx->eloop, ctx->fork_fd);
close(ctx->fork_fd);
ctx->fork_fd = -1;
(void)freopen(_PATH_DEVNULL, "w", stdout);
(void)freopen(_PATH_DEVNULL, "w", stderr);
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP) {
/* Aside from Linux, we don't have access to /dev/null */
fclose(stdout);
fclose(stderr);
} else
#endif
{
(void)freopen(_PATH_DEVNULL, "w", stdout);
(void)freopen(_PATH_DEVNULL, "w", stderr);
}
#endif
}
@ -825,12 +834,27 @@ warn_iaid_conflict(struct interface *ifp, uint16_t ia_type, uint8_t *iaid)
ifp->name, ifn->name);
}
static void
dhcpcd_initduid(struct dhcpcd_ctx *ctx, struct interface *ifp)
{
char buf[DUID_LEN * 3];
if (ctx->duid != NULL)
return;
duid_init(ctx, ifp);
if (ctx->duid == NULL)
return;
loginfox("DUID %s",
hwaddr_ntoa(ctx->duid, ctx->duid_len, buf, sizeof(buf)));
}
void
dhcpcd_startinterface(void *arg)
{
struct interface *ifp = arg;
struct if_options *ifo = ifp->options;
char buf[DUID_LEN * 3];
int carrier;
if (ifo->options & DHCPCD_LINK) {
@ -863,26 +887,21 @@ dhcpcd_startinterface(void *arg)
if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6) &&
!(ifo->options & DHCPCD_ANONYMOUS))
{
char buf[sizeof(ifo->iaid) * 3];
#ifdef INET6
size_t i;
struct if_ia *ia;
#endif
/* Report client DUID */
if (ifp->ctx->duid == NULL) {
if (duid_init(ifp) == 0)
return;
loginfox("DUID %s",
hwaddr_ntoa(ifp->ctx->duid,
ifp->ctx->duid_len,
buf, sizeof(buf)));
}
/* Try and init DUID from the interface hardware address */
dhcpcd_initduid(ifp->ctx, ifp);
/* Report IAIDs */
loginfox("%s: IAID %s", ifp->name,
hwaddr_ntoa(ifo->iaid, sizeof(ifo->iaid),
buf, sizeof(buf)));
warn_iaid_conflict(ifp, 0, ifo->iaid);
#ifdef INET6
for (i = 0; i < ifo->ia_len; i++) {
ia = &ifo->ia[i];
@ -2251,8 +2270,16 @@ printpidfile:
dhcpcd_setlinkrcvbuf(&ctx);
#endif
/* Try and create DUID from the machine UUID. */
dhcpcd_initduid(&ctx, NULL);
/* Cache the default vendor option. */
if (dhcp_vendor(ctx.vendor, sizeof(ctx.vendor)) == -1)
logerrx("dhcp_vendor");
#ifdef PRIVSEP
if (ctx.options & DHCPCD_PRIVSEP) {
/*
* PSF_CAP_ENTER is not set because the following functions
* won't work in it:

View File

@ -125,6 +125,7 @@ struct passwd;
struct dhcpcd_ctx {
char pidfile[sizeof(PIDFILE) + IF_NAMESIZE + 1];
char vendor[256];
int fork_fd; /* FD for the fork init signal pipe */
const char *cffile;
unsigned long long options;

View File

@ -149,20 +149,18 @@ duid_make(void *d, const struct interface *ifp, uint16_t type)
#define DUID_STRLEN DUID_LEN * 3
static size_t
duid_get(uint8_t **d, const struct interface *ifp)
duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp)
{
FILE *fp;
uint8_t *data;
size_t len;
int x = 0;
size_t len, slen;
char line[DUID_STRLEN];
const struct interface *ifp2;
/* If we already have a DUID then use it as it's never supposed
* to change once we have one even if the interfaces do */
if ((len = read_hwaddr_aton(&data, DUID)) != 0) {
if ((len = dhcp_read_hwaddr_aton(ctx, &data, DUID)) != 0) {
if (len <= DUID_LEN) {
*d = data;
ctx->duid = data;
return len;
}
logerrx("DUID too big (max %u): %s", DUID_LEN, DUID);
@ -176,13 +174,18 @@ duid_get(uint8_t **d, const struct interface *ifp)
}
}
/* Regardless of what happens we will create a DUID to use. */
*d = data;
/* No file? OK, lets make one based the machines UUID */
len = duid_make_uuid(data);
if (len > 0)
if (ifp == NULL) {
len = duid_make_uuid(data);
if (len == 0)
free(data);
else
ctx->duid = data;
return len;
}
/* Regardless of what happens we will create a DUID to use. */
ctx->duid = data;
/* No UUID? OK, lets make one based on our interface */
if (ifp->hwlen == 0) {
@ -202,27 +205,25 @@ duid_get(uint8_t **d, const struct interface *ifp)
}
}
if (!(fp = fopen(DUID, "w"))) {
logerr("%s", DUID);
return duid_make(data, ifp, DUID_LL);
}
len = duid_make(data, ifp, DUID_LLT);
x = fprintf(fp, "%s\n", hwaddr_ntoa(data, len, line, sizeof(line)));
if (fclose(fp) == EOF)
x = -1;
/* Failed to write the duid? scrub it, we cannot use it */
if (x < 1) {
logerr("%s", DUID);
unlink(DUID);
hwaddr_ntoa(data, len, line, sizeof(line));
slen = strlen(line);
if (slen < sizeof(line) - 2) {
line[slen++] = '\n';
line[slen] = '\0';
}
if (dhcp_writefile(ctx, DUID, 0640, line, slen) == -1) {
logerr("%s: cannot write duid", __func__);
return duid_make(data, ifp, DUID_LL);
}
return len;
}
size_t duid_init(const struct interface *ifp)
size_t
duid_init(struct dhcpcd_ctx *ctx, const struct interface *ifp)
{
if (ifp->ctx->duid == NULL)
ifp->ctx->duid_len = duid_get(&ifp->ctx->duid, ifp);
return ifp->ctx->duid_len;
if (ctx->duid == NULL)
ctx->duid_len = duid_get(ctx, ifp);
return ctx->duid_len;
}

View File

@ -35,6 +35,6 @@
#define DUID_UUID 4
size_t duid_make(void *, const struct interface *, uint16_t);
size_t duid_init(const struct interface *);
size_t duid_init(struct dhcpcd_ctx *, const struct interface *);
#endif

View File

@ -13,7 +13,7 @@ $TOOL_SED \
-e 's/#.*$//' \
-e '/^$/d' \
-e 's/^/"/g' \
-e 's/$/\",/g' \
-e 's/$/\\n\"/g' \
-e 's/ [ ]*/ /g' \
-e 's/ [ ]*/ /g' \
$CONF_SMALL
@ -22,9 +22,9 @@ $TOOL_SED \
-e 's/#.*$//' \
-e '/^$/d' \
-e 's/^/"/g' \
-e 's/$/\",/g' \
-e 's/$/\\n"/g' \
-e 's/ [ ]*/ /g' \
-e 's/ [ ]*/ /g' \
$CONF
echo "#endif"
printf "%s\n%s\n" "NULL" "};"
printf "%s\n%s\n" '"\0";'

View File

@ -1555,7 +1555,7 @@ if_handlelink(struct dhcpcd_ctx *ctx)
}
#ifndef SYS_NMLN /* OSX */
# define SYS_NMLN 256
# define SYS_NMLN __SYS_NAMELEN
#endif
#ifndef HW_MACHINE_ARCH
# ifdef HW_MODEL /* OpenBSD */
@ -1566,14 +1566,8 @@ int
if_machinearch(char *str, size_t len)
{
int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
char march[SYS_NMLN];
size_t marchlen = sizeof(march);
return -1;
if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
march, &marchlen, NULL, 0) != 0)
return -1;
return snprintf(str, len, ":%s", march);
return sysctl(mib, sizeof(mib) / sizeof(mib[0]), str, &len, NULL, 0);
}
#ifdef INET6

View File

@ -211,7 +211,7 @@ if_machinearch(char *str, size_t len)
fscanf(fp, "%255s", buf) == 1)
{
fclose(fp);
return snprintf(str, len, ":%s", buf);
return snprintf(str, len, "%s", buf);
}
}
fclose(fp);

View File

@ -27,7 +27,6 @@
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <arpa/inet.h>
@ -2265,25 +2264,31 @@ finish_config(struct if_options *ifo)
* We strip leading space and avoid comment lines, making the code that calls
* us smaller. */
static char *
get_line(char ** __restrict buf, size_t * __restrict buflen,
FILE * __restrict fp)
get_line(char ** __restrict buf, ssize_t * __restrict buflen)
{
char *p, *c;
ssize_t bytes;
int quoted;
bool quoted;
do {
bytes = getline(buf, buflen, fp);
if (bytes == -1)
return NULL;
for (p = *buf; *p == ' ' || *p == '\t'; p++)
p = *buf;
c = memchr(*buf, '\n', *buflen);
if (c == NULL) {
c = memchr(*buf, '\0', *buflen);
if (c == NULL)
return NULL;
*buflen = c - *buf;
*buf = NULL;
} else {
*c++ = '\0';
*buflen -= c - *buf;
*buf = c;
}
for (; *p == ' ' || *p == '\t'; p++)
;
} while (*p == '\0' || *p == '\n' || *p == '#' || *p == ';');
if ((*buf)[--bytes] == '\n')
(*buf)[bytes] = '\0';
/* Strip embedded comments unless in a quoted string or escaped */
quoted = 0;
quoted = false;
for (c = p; *c != '\0'; c++) {
if (*c == '\\') {
c++; /* escaped */
@ -2334,16 +2339,11 @@ read_config(struct dhcpcd_ctx *ctx,
const char *ifname, const char *ssid, const char *profile)
{
struct if_options *ifo;
FILE *fp;
struct stat sb;
char *line, *buf, *option, *p;
size_t buflen;
char buf[UDPLEN_MAX], *bp; /* 64k max config file size */
char *line, *option, *p;
ssize_t buflen;
ssize_t vlen;
int skip, have_profile, new_block, had_block;
#ifndef EMBEDDED_CONFIG
const char * const *e;
size_t ol;
#endif
#if !defined(INET) || !defined(INET6)
size_t i;
struct dhcp_opt *opt;
@ -2366,12 +2366,9 @@ read_config(struct dhcpcd_ctx *ctx,
ifo->options |= DHCPCD_DHCP6;
#endif
vlen = dhcp_vendor((char *)ifo->vendorclassid + 1,
sizeof(ifo->vendorclassid) - 1);
ifo->vendorclassid[0] = (uint8_t)(vlen == -1 ? 0 : vlen);
buf = NULL;
buflen = 0;
vlen = strlcpy((char *)ifo->vendorclassid + 1, ctx->vendor,
sizeof(ifo->vendorclassid) - 1);
ifo->vendorclassid[0] = (uint8_t)(vlen > 255 ? 0 : vlen);
/* Reset route order */
ctx->rt_order = 0;
@ -2407,38 +2404,26 @@ read_config(struct dhcpcd_ctx *ctx,
/* Now load our embedded config */
#ifdef EMBEDDED_CONFIG
fp = fopen(EMBEDDED_CONFIG, "r");
if (fp == NULL)
logerr("%s: fopen `%s'", __func__, EMBEDDED_CONFIG);
while (fp && (line = get_line(&buf, &buflen, fp))) {
#else
buflen = 80;
buf = malloc(buflen);
if (buf == NULL) {
logerr(__func__);
free_options(ctx, ifo);
return NULL;
buflen = dhcp_readfile(ctx, EMBEDDED_CONFIG, buf, sizeof(buf));
if (buflen == -1) {
logerr("%s: %s", __func__, EMBEDDED_CONFIG);
return ifo;
}
ldop = edop = NULL;
for (e = dhcpcd_embedded_conf; *e; e++) {
ol = strlen(*e) + 1;
if (ol > buflen) {
char *nbuf;
buflen = ol;
nbuf = realloc(buf, buflen);
if (nbuf == NULL) {
logerr(__func__);
free(buf);
free_options(ctx, ifo);
return NULL;
}
buf = nbuf;
}
memcpy(buf, *e, ol);
line = buf;
if (buf[buflen] != '\0') {
if (buflen < sizeof(buf) - 1)
bulen++;
buf[buflen] = '\0';
}
#else
buflen = strlcpy(buf, dhcpcd_embedded_conf, sizeof(buf));
if ((size_t)buflen >= sizeof(buf)) {
logerrx("%s: embedded config too big", __func__);
return ifo;
}
/* Our embedded config is NULL terminated */
#endif
bp = buf;
while ((line = get_line(&bp, &buflen)) != NULL) {
option = strsep(&line, " \t");
if (line)
line = strskipwhite(line);
@ -2452,13 +2437,8 @@ read_config(struct dhcpcd_ctx *ctx,
}
parse_config_line(ctx, NULL, ifo, option, line,
&ldop, &edop);
}
#ifdef EMBEDDED_CONFIG
if (fp)
fclose(fp);
#endif
#ifdef INET
ctx->dhcp_opts = ifo->dhcp_override;
ctx->dhcp_opts_len = ifo->dhcp_override_len;
@ -2503,26 +2483,25 @@ read_config(struct dhcpcd_ctx *ctx,
}
/* Parse our options file */
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP &&
ps_root_copychroot(ctx, ctx->cffile) == -1)
logwarn("%s: ps_root_copychroot `%s'", __func__, ctx->cffile);
#endif
fp = fopen(ctx->cffile, "r");
if (fp == NULL) {
buflen = dhcp_readfile(ctx, ctx->cffile, buf, sizeof(buf));
if (buflen == -1) {
/* dhcpcd can continue without it, but no DNS options
* would be requested ... */
logwarn("%s: fopen `%s'", __func__, ctx->cffile);
free(buf);
logerr("%s: %s", __func__, ctx->cffile);
return ifo;
}
if (stat(ctx->cffile, &sb) == 0)
ifo->mtime = sb.st_mtime;
if (buf[buflen] != '\0') {
if ((size_t)buflen < sizeof(buf) - 1)
buflen++;
buf[buflen] = '\0';
}
dhcp_filemtime(ctx, ctx->cffile, &ifo->mtime);
ldop = edop = NULL;
skip = have_profile = new_block = 0;
had_block = ifname == NULL ? 1 : 0;
while ((line = get_line(&buf, &buflen, fp))) {
bp = buf;
while ((line = get_line(&bp, &buflen)) != NULL) {
option = strsep(&line, " \t");
if (line)
line = strskipwhite(line);
@ -2596,10 +2575,9 @@ read_config(struct dhcpcd_ctx *ctx,
continue;
if (skip)
continue;
parse_config_line(ctx, ifname, ifo, option, line, &ldop, &edop);
}
fclose(fp);
free(buf);
if (profile && !have_profile) {
free_options(ctx, ifo);

View File

@ -120,6 +120,7 @@
#define DHCPCD_ONESHOT (1ULL << 60)
#define DHCPCD_INACTIVE (1ULL << 61)
#define DHCPCD_SLAACTEMP (1ULL << 62)
#define DHCPCD_PRIVSEPROOT (1ULL << 63)
#define DHCPCD_NODROP (DHCPCD_EXITING | DHCPCD_PERSISTENT)

View File

@ -95,6 +95,8 @@ typedef unsigned long ioctl_request_t;
#define FRAMEHDRLEN_MAX 14 /* only ethernet support */
#define FRAMELEN_MAX (FRAMEHDRLEN_MAX + 9216)
#define UDPLEN_MAX 64 * 1024
/* Work out if we have a private address or not
* 10/8
* 172.16/12

View File

@ -147,18 +147,17 @@ ipv6_init(struct dhcpcd_ctx *ctx)
static ssize_t
ipv6_readsecret(struct dhcpcd_ctx *ctx)
{
FILE *fp;
char line[1024];
unsigned char *p;
size_t len;
uint32_t r;
int x;
if ((ctx->secret_len = read_hwaddr_aton(&ctx->secret, SECRET)) != 0)
ctx->secret_len = dhcp_read_hwaddr_aton(ctx, &ctx->secret, SECRET);
if (ctx->secret_len != 0)
return (ssize_t)ctx->secret_len;
if (errno != ENOENT)
logerr("%s: %s", __func__, SECRET);
logerr("%s: cannot read secret", __func__);
/* Chaining arc4random should be good enough.
* RFC7217 section 5.1 states the key SHOULD be at least 128 bits.
@ -178,27 +177,18 @@ ipv6_readsecret(struct dhcpcd_ctx *ctx)
p += sizeof(r);
}
/* Ensure that only the dhcpcd user can read the secret.
* Write permission is also denied as changing it would remove
* it's stability. */
if ((fp = fopen(SECRET, "w")) == NULL ||
chmod(SECRET, S_IRUSR) == -1)
goto eexit;
x = fprintf(fp, "%s\n",
hwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line)));
if (fclose(fp) == EOF)
x = -1;
fp = NULL;
if (x > 0)
return (ssize_t)ctx->secret_len;
eexit:
logerr("%s: %s", __func__, SECRET);
if (fp != NULL)
fclose(fp);
unlink(SECRET);
ctx->secret_len = 0;
return -1;
hwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line));
len = strlen(line);
if (len < sizeof(line) - 2) {
line[len++] = '\n';
line[len] = '\0';
}
if (dhcp_writefile(ctx, SECRET, S_IRUSR, line, len) == -1) {
logerr("%s: cannot write secret", __func__);
ctx->secret_len = 0;
return -1;
}
return (ssize_t)ctx->secret_len;
}
/* http://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml

View File

@ -104,7 +104,6 @@ logprintdate(FILE *stream)
return -1;
now = tv.tv_sec;
tzset();
if (localtime_r(&now, &tmnow) == NULL)
return -1;
if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0)
@ -366,6 +365,9 @@ logopen(const char *path)
{
struct logctx *ctx = &_logctx;
/* Cache timezone */
tzset();
if (path == NULL) {
int opts = 0;

View File

@ -91,7 +91,7 @@ ps_bpf_recvbpf(void *arg)
#ifdef ARP
static ssize_t
ps_bpf_arp_addr(uint8_t cmd, struct ps_process *psp, struct msghdr *msg)
ps_bpf_arp_addr(uint16_t cmd, struct ps_process *psp, struct msghdr *msg)
{
struct interface *ifp = &psp->psp_ifp;
struct iovec *iov = msg->msg_iov;
@ -197,7 +197,7 @@ ps_bpf_signal_bpfcb(int sig, void *arg)
ssize_t
ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
{
uint8_t cmd;
uint16_t cmd;
struct ps_process *psp;
pid_t start;
struct iovec *iov = msg->msg_iov;
@ -205,7 +205,7 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
struct ipv4_state *istate;
unsigned int flags = PSF_DROPPRIVS | PSF_CAP_ENTER;
cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
psp = ps_findprocess(ctx, &psm->ps_id);
#ifdef PRIVSEP_DEBUG
@ -261,7 +261,7 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
* Pledge is currently useless for BPF ARP because we cannot
* change the filter:
* http://openbsd-archive.7691.n7.nabble.com/ \
* pledge-bpf-32bit-arch-unbreak-td299901.html
* pledge-bpf-32bit-arch-unbreak-td299901.html
*/
break;
#endif
@ -321,7 +321,7 @@ ps_bpf_dispatch(struct dhcpcd_ctx *ctx,
}
static ssize_t
ps_bpf_send(const struct interface *ifp, uint8_t cmd,
ps_bpf_send(const struct interface *ifp, uint16_t cmd,
const void *data, size_t len)
{
struct dhcpcd_ctx *ctx = ifp->ctx;

View File

@ -114,7 +114,7 @@ ps_root_os(struct ps_msghdr *psm, struct msghdr *msg)
}
static ssize_t
ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint8_t domain, unsigned long request,
ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint16_t domain, unsigned long request,
void *data, size_t len)
{
@ -126,7 +126,7 @@ ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint8_t domain, unsigned long request,
ssize_t
ps_root_ioctllink(struct dhcpcd_ctx *ctx, unsigned long request,
void *data,size_t len)
void *data, size_t len)
{
return ps_root_ioctldom(ctx, PS_IOCTLLINK, request, data, len);

View File

@ -498,12 +498,12 @@ ssize_t
ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,
__unused struct msghdr *msg)
{
uint8_t cmd;
uint16_t cmd;
struct ps_process *psp;
int (*start_func)(void *);
pid_t start;
cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
psp = ps_findprocess(ctx, &psm->ps_id);
#ifdef PRIVSEP_DEBUG
@ -570,7 +570,7 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,
#ifdef INET
static ssize_t
ps_inet_in_docmd(struct ipv4_addr *ia, uint8_t cmd, const struct msghdr *msg)
ps_inet_in_docmd(struct ipv4_addr *ia, uint16_t cmd, const struct msghdr *msg)
{
assert(ia != NULL);
struct dhcpcd_ctx *ctx = ia->iface->ctx;
@ -614,7 +614,7 @@ ps_inet_sendbootp(struct ipv4_addr *ia, const struct msghdr *msg)
#ifdef INET6
#ifdef __sun
static ssize_t
ps_inet_ifp_docmd(struct interface *ifp, uint8_t cmd, const struct msghdr *msg)
ps_inet_ifp_docmd(struct interface *ifp, uint16_t cmd, const struct msghdr *msg)
{
struct dhcpcd_ctx *ctx = ifp->ctx;
struct ps_msghdr psm = {
@ -659,7 +659,7 @@ ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)
#ifdef DHCP6
static ssize_t
ps_inet_in6_docmd(struct ipv6_addr *ia, uint8_t cmd, const struct msghdr *msg)
ps_inet_in6_docmd(struct ipv6_addr *ia, uint16_t cmd, const struct msghdr *msg)
{
struct dhcpcd_ctx *ctx = ia->iface->ctx;
struct ps_msghdr psm = {

View File

@ -217,124 +217,35 @@ ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
return status;
}
#if defined(__linux__) && !defined(st_mtimespec)
#define st_atimespec st_atim
#define st_mtimespec st_mtim
#endif
ssize_t
ps_root_docopychroot(struct dhcpcd_ctx *ctx, const char *file)
{
char path[PATH_MAX], buf[BUFSIZ], *slash;
struct stat from_sb, to_sb;
int from_fd, to_fd;
ssize_t rcount, wcount, total;
#if defined(BSD) || defined(__linux__)
struct timespec ts[2];
#else
struct timeval tv[2];
#endif
if (snprintf(path, sizeof(path), "%s/%s", ctx->ps_chroot, file) == -1)
return -1;
if (stat(file, &from_sb) == -1)
return -1;
if (stat(path, &to_sb) == 0) {
#if defined(BSD) || defined(__linux__)
if (from_sb.st_mtimespec.tv_sec == to_sb.st_mtimespec.tv_sec &&
from_sb.st_mtimespec.tv_nsec == to_sb.st_mtimespec.tv_nsec)
return 0;
#else
if (from_sb.st_mtime == to_sb.st_mtime)
return 0;
#endif
} else {
/* Ensure directory exists */
slash = strrchr(path, '/');
if (slash != NULL) {
*slash = '\0';
ps_mkdir(path);
*slash = '/';
}
}
if (unlink(path) == -1 && errno != ENOENT)
return -1;
if ((from_fd = open(file, O_RDONLY, 0)) == -1)
return -1;
if ((to_fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0555)) == -1)
return -1;
total = 0;
while ((rcount = read(from_fd, buf, sizeof(buf))) > 0) {
wcount = write(to_fd, buf, (size_t)rcount);
if (wcount != rcount) {
total = -1;
break;
}
total += wcount;
}
#if defined(BSD) || defined(__linux__)
ts[0] = from_sb.st_atimespec;
ts[1] = from_sb.st_mtimespec;
if (futimens(to_fd, ts) == -1)
total = -1;
#else
tv[0].tv_sec = from_sb.st_atime;
tv[0].tv_usec = 0;
tv[1].tv_sec = from_sb.st_mtime;
tv[1].tv_usec = 0;
if (futimes(to_fd, tv) == -1)
total = -1;
#endif
close(from_fd);
close(to_fd);
return total;
}
static ssize_t
ps_root_dofileop(struct dhcpcd_ctx *ctx, void *data, size_t len, uint8_t op)
ps_root_dowritefile(mode_t mode, void *data, size_t len)
{
char *path = data;
size_t plen;
char *file = data, *nc;
if (len < sizeof(plen)) {
nc = memchr(file, '\0', len);
if (nc == NULL) {
errno = EINVAL;
return -1;
}
memcpy(&plen, path, sizeof(plen));
path += sizeof(plen);
if (sizeof(plen) + plen > len) {
errno = EINVAL;
return -1;
}
switch(op) {
case PS_COPY:
return ps_root_docopychroot(ctx, path);
case PS_UNLINK:
return (ssize_t)unlink(path);
default:
errno = ENOTSUP;
return -1;
}
nc++;
return writefile(file, mode, nc, len - (nc - file));
}
static ssize_t
ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
{
struct dhcpcd_ctx *ctx = arg;
uint8_t cmd;
uint16_t cmd;
struct ps_process *psp;
struct iovec *iov = msg->msg_iov;
void *data = iov->iov_base;
size_t len = iov->iov_len;
uint8_t buf[PS_BUFLEN];
time_t mtime;
ssize_t err;
bool retdata = false;
cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP | PS_DELETE));
cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP | PS_DELETE));
psp = ps_findprocess(ctx, &psm->ps_id);
#ifdef PRIVSEP_DEBUG
@ -388,20 +299,40 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
switch (psm->ps_cmd) {
case PS_IOCTL:
err = ps_root_doioctl(psm->ps_flags, data, len);
retdata = true;
break;
case PS_SCRIPT:
err = ps_root_run_script(ctx, data, len);
break;
case PS_COPY: /* FALLTHROUGH */
case PS_UNLINK:
err = ps_root_dofileop(ctx, data, len, psm->ps_cmd);
err = unlink(data);
break;
case PS_READFILE:
errno = 0;
err = readfile(data, buf, sizeof(buf));
if (err != -1) {
data = buf;
len = (size_t)err;
retdata = true;
}
break;
case PS_WRITEFILE:
err = ps_root_dowritefile(psm->ps_flags, data, len);
break;
case PS_FILEMTIME:
err = filemtime(data, &mtime);
if (err != -1) {
data = &mtime;
len = sizeof(mtime);
retdata = true;
}
break;
default:
err = ps_root_os(psm, msg);
break;
}
return ps_root_writeerror(ctx, err, data, len);
return ps_root_writeerror(ctx, err, data, retdata ? len : 0);
}
/* Receive from state engine, do an action. */
@ -428,6 +359,7 @@ ps_root_startcb(void *arg)
ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
ctx->ps_root_pid = getpid();
ctx->options |= DHCPCD_PRIVSEPROOT;
return 0;
}
@ -574,38 +506,53 @@ ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
return ps_root_readerror(ctx, data, len);
}
static ssize_t
ps_root_fileop(struct dhcpcd_ctx *ctx, const char *path, uint8_t op)
ssize_t
ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file)
{
char buf[PATH_MAX], *p = buf;
size_t plen = strlen(path) + 1;
size_t len = sizeof(plen) + plen;
if (len > sizeof(buf)) {
errno = ENOBUFS;
return -1;
}
memcpy(p, &plen, sizeof(plen));
p += sizeof(plen);
memcpy(p, path, plen);
if (ps_sendcmd(ctx, ctx->ps_root_fd, op, 0, buf, len) == -1)
if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_UNLINK, 0,
file, strlen(file) + 1) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
ssize_t
ps_root_copychroot(struct dhcpcd_ctx *ctx, const char *path)
ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file,
void *data, size_t len)
{
return ps_root_fileop(ctx, path, PS_COPY);
if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_READFILE, 0,
file, strlen(file) + 1) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
}
ssize_t
ps_root_unlink(struct dhcpcd_ctx *ctx, const char *path)
ps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
const void *data, size_t len)
{
char buf[PS_BUFLEN];
size_t flen;
flen = strlcpy(buf, file, sizeof(buf));
flen += 1;
if (flen > sizeof(buf) || flen + len > sizeof(buf)) {
errno = ENOBUFS;
return -1;
}
memcpy(buf + flen, data, len);
if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_WRITEFILE, mode,
buf, flen + len) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
int
ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
{
return ps_root_fileop(ctx, path, PS_UNLINK);
if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_FILEMTIME, 0,
file, strlen(file) + 1) == -1)
return -1;
return ps_root_readerror(ctx, time, sizeof(*time));
}

View File

@ -35,10 +35,12 @@ pid_t ps_root_start(struct dhcpcd_ctx *ctx);
int ps_root_stop(struct dhcpcd_ctx *ctx);
ssize_t ps_root_readerror(struct dhcpcd_ctx *, void *, size_t);
ssize_t ps_root_docopychroot(struct dhcpcd_ctx *, const char *);
ssize_t ps_root_copychroot(struct dhcpcd_ctx *, const char *);
ssize_t ps_root_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);
ssize_t ps_root_unlink(struct dhcpcd_ctx *, const char *);
int ps_root_filemtime(struct dhcpcd_ctx *, const char *, time_t *);
ssize_t ps_root_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);
ssize_t ps_root_writefile(struct dhcpcd_ctx *, const char *, mode_t,
const void *, size_t);
ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *);
#if defined(BSD) || defined(__sun)
ssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t);

View File

@ -77,31 +77,9 @@
#include <util.h>
#endif
int
ps_mkdir(char *path)
{
char *slash;
bool done;
slash = path;
for (;;) {
slash += strspn(slash, "/");
slash += strcspn(slash, "/");
done = (*slash == '\0');
*slash = '\0';
if (mkdir(path, 0755) == -1 && errno != EEXIST)
return -1;
if (done)
break;
*slash = '/';
}
return 0;
}
int
ps_init(struct dhcpcd_ctx *ctx)
{
char path[PATH_MAX];
struct passwd *pw;
errno = 0;
@ -120,9 +98,7 @@ ps_init(struct dhcpcd_ctx *ctx)
ctx->ps_chroot = pw->pw_dir;
/* If we pickup the _dhcp user refuse the default directory */
if (*ctx->ps_chroot != '/' ||
strcmp(ctx->ps_chroot, "/var/empty") == 0)
{
if (*ctx->ps_chroot != '/') {
ctx->options &= ~DHCPCD_PRIVSEP;
logerrx("refusing chroot: %s: %s",
PRIVSEP_USER, ctx->ps_chroot);
@ -130,17 +106,6 @@ ps_init(struct dhcpcd_ctx *ctx)
return -1;
}
/* Create the database directory. */
if (snprintf(path, sizeof(path), "%s%s", ctx->ps_chroot, DBDIR) == -1 ||
ps_mkdir(path) == -1 ||
chown(path, pw->pw_uid, pw->pw_gid) == -1 ||
chmod(path, 0755) == -1)
logerr("%s: %s", __func__, path);
/* Ensure we have a localtime to correctly format dates. */
if (ps_root_docopychroot(ctx, "/etc/localtime") == -1 && errno!=ENOENT)
logerr("%s: %s", __func__, "/etc/localtime");
ctx->options |= DHCPCD_PRIVSEP;
return 0;
}
@ -152,7 +117,6 @@ ps_dropprivs(struct dhcpcd_ctx *ctx, unsigned int flags)
if (!(ctx->options & DHCPCD_FORKED))
logdebugx("chrooting to `%s'", ctx->ps_chroot);
if (chroot(ctx->ps_chroot) == -1)
logerr("%s: chroot `%s'", __func__, ctx->ps_chroot);
if (chdir("/") == -1)
@ -184,9 +148,8 @@ ps_dropprivs(struct dhcpcd_ctx *ctx, unsigned int flags)
if (ctx->options & DHCPCD_UNPRIV)
promises = "stdio dns bpf";
else
/* SIOCGIFGROUP requries inet
* lease files and foo require rpath, wpath and cpath */
promises = "stdio dns inet route rpath wpath cpath";
/* SIOCGIFGROUP requires inet */
promises = "stdio dns inet route";
if (pledge(promises, NULL) == -1) {
logerr("%s: pledge", __func__);
return -1;
@ -568,7 +531,7 @@ ps_sendpsmdata(struct dhcpcd_ctx *ctx, int fd,
ssize_t
ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags,
ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
const struct msghdr *msg)
{
assert(msg->msg_iovlen == 1);
@ -610,7 +573,7 @@ ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags,
}
ssize_t
ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags,
ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
const void *data, size_t len)
{
struct ps_msghdr psm = {
@ -628,7 +591,7 @@ ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags,
}
static ssize_t
ps_sendcmdmsg(int fd, uint8_t cmd, const struct msghdr *msg)
ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg)
{
struct ps_msghdr psm = { .ps_cmd = cmd };
uint8_t data[PS_BUFLEN], *p = data;
@ -671,7 +634,7 @@ nobufs:
}
ssize_t
ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint8_t cmd, int wfd)
ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
{
struct sockaddr_storage ss = { .ss_family = AF_UNSPEC };
uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 };

View File

@ -36,32 +36,36 @@
#define PSF_CAP_ENTER 0x02
#define PSF_PLEDGE 0x04
/* Commands */
#define PS_BOOTP 0x01
#define PS_ND 0x02
#define PS_DHCP6 0x03
#define PS_BPF_BOOTP 0x04
#define PS_BPF_ARP 0x05
#define PS_BPF_ARP_ADDR 0x06
/* Protocols */
#define PS_BOOTP 0x0001
#define PS_ND 0x0002
#define PS_DHCP6 0x0003
#define PS_BPF_BOOTP 0x0004
#define PS_BPF_ARP 0x0005
#define PS_BPF_ARP_ADDR 0x0006
#define PS_IOCTL 0x10
#define PS_ROUTE 0x11 /* Also used for NETLINK */
#define PS_SCRIPT 0x12
#define PS_UNLINK 0x13
#define PS_COPY 0x14
/* Generic commands */
#define PS_IOCTL 0x0010
#define PS_ROUTE 0x0011 /* Also used for NETLINK */
#define PS_SCRIPT 0x0012
#define PS_UNLINK 0x0013
#define PS_READFILE 0x0014
#define PS_WRITEFILE 0x0015
#define PS_FILEMTIME 0x0016
/* BSD Commands */
#define PS_IOCTLLINK 0x15
#define PS_IOCTL6 0x16
#define PS_IOCTLINDIRECT 0x17
#define PS_IP6FORWARDING 0x18
#define PS_IOCTLLINK 0x0101
#define PS_IOCTL6 0x0102
#define PS_IOCTLINDIRECT 0x0103
#define PS_IP6FORWARDING 0x0104
/* Linux commands */
#define PS_WRITEPATHUINT 0x19
#define PS_WRITEPATHUINT 0x0201
#define PS_DELETE 0x20
#define PS_START 0x40
#define PS_STOP 0x80
/* Process commands */
#define PS_DELETE 0x2000
#define PS_START 0x4000
#define PS_STOP 0x8000
/* Max INET message size + meta data for IPC */
#define PS_BUFLEN ((64 * 1024) + \
@ -96,13 +100,13 @@ struct ps_addr {
struct ps_id {
struct ps_addr psi_addr;
unsigned int psi_ifindex;
uint8_t psi_cmd;
uint8_t psi_pad[3];
uint16_t psi_cmd;
uint8_t psi_pad[2];
};
struct ps_msghdr {
uint8_t ps_cmd;
uint8_t ps_pad[sizeof(unsigned long) - 1];
uint16_t ps_cmd;
uint8_t ps_pad[sizeof(unsigned long) - sizeof(uint16_t)];
unsigned long ps_flags;
struct ps_id ps_id;
socklen_t ps_namelen;
@ -141,7 +145,6 @@ TAILQ_HEAD(ps_process_head, ps_process);
#include "privsep-bpf.h"
#endif
int ps_mkdir(char *);
int ps_init(struct dhcpcd_ctx *);
int ps_dropprivs(struct dhcpcd_ctx *, unsigned int);
int ps_start(struct dhcpcd_ctx *);
@ -152,11 +155,11 @@ ssize_t ps_sendpsmmsg(struct dhcpcd_ctx *, int,
struct ps_msghdr *, const struct msghdr *);
ssize_t ps_sendpsmdata(struct dhcpcd_ctx *, int,
struct ps_msghdr *, const void *, size_t);
ssize_t ps_sendmsg(struct dhcpcd_ctx *, int, uint8_t, unsigned long,
ssize_t ps_sendmsg(struct dhcpcd_ctx *, int, uint16_t, unsigned long,
const struct msghdr *);
ssize_t ps_sendcmd(struct dhcpcd_ctx *, int, uint8_t, unsigned long,
ssize_t ps_sendcmd(struct dhcpcd_ctx *, int, uint16_t, unsigned long,
const void *data, size_t len);
ssize_t ps_recvmsg(struct dhcpcd_ctx *, int, uint8_t, int);
ssize_t ps_recvmsg(struct dhcpcd_ctx *, int, uint16_t, int);
ssize_t ps_recvpsmsg(struct dhcpcd_ctx *, int,
ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), void *);