summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorHenning Brauer <henning@cvs.openbsd.org>2004-05-04 12:52:06 +0000
committerHenning Brauer <henning@cvs.openbsd.org>2004-05-04 12:52:06 +0000
commitac1ddc2c99c6c92aa30be01bb5a0ea9b5c12dda9 (patch)
treeffc1e9db8fa5dcd3d585abc750224dfd0b404d62 /sbin
parent2daf03fc192c6120be9db3e0262049bb69a67354 (diff)
privilege seperate dhclient.
the privileged child's duty is the dhclient-script env setup & exection. the parent process now chroots to /var/empty and drops privileges to _dhcp mostly hacked at vancouver airport with some feedback from krw@ and otto@, tested to not break installs by krw@
Diffstat (limited to 'sbin')
-rw-r--r--sbin/dhclient/Makefile5
-rw-r--r--sbin/dhclient/dhclient.c203
-rw-r--r--sbin/dhclient/dhcpd.h13
-rw-r--r--sbin/dhclient/privsep.c228
-rw-r--r--sbin/dhclient/privsep.h45
5 files changed, 481 insertions, 13 deletions
diff --git a/sbin/dhclient/Makefile b/sbin/dhclient/Makefile
index 9bb9c8347ee..594c9aff1b4 100644
--- a/sbin/dhclient/Makefile
+++ b/sbin/dhclient/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.8 2004/02/25 14:22:12 henning Exp $
+# $OpenBSD: Makefile,v 1.9 2004/05/04 12:52:05 henning Exp $
#
# Copyright (c) 1996, 1997 The Internet Software Consortium.
# All rights reserved.
@@ -33,7 +33,8 @@
.include <bsd.own.mk>
SRCS= dhclient.c clparse.c alloc.c dispatch.c hash.c bpf.c options.c \
- tree.c conflex.c errwarn.c inet.c packet.c convert.c tables.c parse.c
+ tree.c conflex.c errwarn.c inet.c packet.c convert.c tables.c \
+ parse.c privsep.c
PROG= dhclient
MAN= dhclient.8 dhclient.conf.5 dhclient.leases.5 dhclient-script.8
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
index 522354b253c..be190856fbf 100644
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -1,6 +1,4 @@
-/* $OpenBSD: dhclient.c,v 1.37 2004/04/14 20:14:49 henning Exp $ */
-
-/* DHCP Client. */
+/* $OpenBSD: dhclient.c,v 1.38 2004/05/04 12:52:05 henning Exp $ */
/*
* Copyright 2004 Henning Brauer <henning@openbsd.org>
@@ -56,6 +54,7 @@
*/
#include "dhcpd.h"
+#include "privsep.h"
#define PERIOD 0x2e
#define hyphenchar(c) ((c) == 0x2d)
@@ -81,6 +80,7 @@ char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
char *path_dhclient_db = NULL;
int log_perror = 1;
+int privfd;
struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } };
@@ -111,6 +111,7 @@ int check_option(struct client_lease *l, int option);
int ipv4addrs(char * buf);
int res_hnok(const char *dn);
char *option_as_string(unsigned int code, unsigned char *data, int len);
+int fork_privchld(int, int);
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
@@ -210,6 +211,8 @@ main(int argc, char *argv[])
{
extern char *__progname;
int ch, fd, quiet = 0, i = 0;
+ int pipe_fd[2];
+ struct passwd *pw;
/* Initially, log errors to stderr as well as to syslogd. */
openlog(__progname, LOG_NDELAY, DHCPD_LOG_FACILITY);
@@ -286,10 +289,10 @@ main(int argc, char *argv[])
fprintf(stderr, "got link\n");
}
- script_init(ifi, "PREINIT", NULL);
+ priv_script_init("PREINIT", NULL);
if (ifi->client->alias)
- script_write_params(ifi, "alias_", ifi->client->alias);
- script_go(ifi);
+ priv_script_write_params("alias_", ifi->client->alias);
+ priv_script_go();
if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1)
add_protocol("AF_ROUTE", routefd, routehandler, ifi);
@@ -297,6 +300,31 @@ main(int argc, char *argv[])
/* set up the interface */
discover_interfaces(ifi);
+ if ((pw = getpwnam("_dhcp")) == NULL)
+ error("no such user: _dhcp");
+
+ if (pipe(pipe_fd) == -1)
+ error("pipe");
+
+ fork_privchld(pipe_fd[0], pipe_fd[1]);
+
+ close(pipe_fd[0]);
+ privfd = pipe_fd[1];
+
+ if (chroot(_PATH_VAREMPTY) == -1)
+ error("chroot");
+ if (chdir("/") == -1)
+ error("chdir(\"/\")");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
+ seteuid(pw->pw_uid) || setuid(pw->pw_uid))
+ error("can't drop privileges: %m");
+
+ endpwent();
+
+ setproctitle("%s", ifi->name);
+
ifi->client->state = S_INIT;
state_reboot(ifi);
@@ -1739,6 +1767,44 @@ FILE *scriptFile;
void
script_init(struct interface_info *ip, char *reason, struct string_list *medium)
{
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int errs;
+ size_t len;
+ size_t mediumlen = 0;
+
+ if (medium != NULL && medium->string != NULL)
+ mediumlen = strlen(medium->string);
+
+ hdr.code = IMSG_SCRIPT_INIT;
+ hdr.len = sizeof(struct imsg_hdr) +
+ sizeof(size_t) + mediumlen +
+ sizeof(size_t) + strlen(reason);
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ errs = 0;
+ errs += buf_add(buf, &hdr, sizeof(hdr));
+ errs += buf_add(buf, &mediumlen, sizeof(mediumlen));
+ if (mediumlen > 0)
+ errs += buf_add(buf, medium->string, mediumlen);
+ len = strlen(reason);
+ errs += buf_add(buf, &len, sizeof(len));
+ errs += buf_add(buf, reason, len);
+
+ if (errs)
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+}
+
+void
+priv_script_init(char *reason, char *medium)
+{
+ struct interface_info *ip = ifi;
+
if (ip) {
ip->client->scriptEnvsize = 100;
if (ip->client->scriptEnv == NULL)
@@ -1756,21 +1822,20 @@ script_init(struct interface_info *ip, char *reason, struct string_list *medium)
script_set_env(ip->client, "", "interface", ip->name);
if (medium)
- script_set_env(ip->client, "", "medium",
- medium->string);
+ script_set_env(ip->client, "", "medium", medium);
script_set_env(ip->client, "", "reason", reason);
}
}
void
-script_write_params(struct interface_info *ip, char *prefix,
- struct client_lease *lease)
+priv_script_write_params(char *prefix, struct client_lease *lease)
{
int i;
u_int8_t dbuf[1500];
int len = 0;
char tbuf[128];
+ struct interface_info *ip = ifi;
script_set_env(ip->client, prefix, "ip_address",
piaddr(lease->address));
@@ -1889,9 +1954,91 @@ supersede:
script_set_env(ip->client, prefix, "expiry", tbuf);
}
+void
+script_write_params(struct interface_info *ip, char *prefix,
+ struct client_lease *lease)
+{
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int errs, i;
+ size_t fn_len = 0, sn_len = 0, pr_len= 0;
+
+ if (lease->filename != NULL)
+ fn_len = strlen(lease->filename);
+ if (lease->server_name != NULL)
+ sn_len = strlen(lease->server_name);
+ if (prefix != NULL)
+ pr_len = strlen(prefix);
+
+ hdr.code = IMSG_SCRIPT_WRITE_PARAMS;
+ hdr.len = sizeof(hdr) + sizeof(struct client_lease) +
+ sizeof(size_t) + fn_len +
+ sizeof(size_t) + sn_len +
+ sizeof(size_t) + pr_len;
+
+ for (i = 0; i < 256; i++)
+ hdr.len += sizeof(int) + lease->options[i].len;
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ errs = 0;
+ errs += buf_add(buf, &hdr, sizeof(hdr));
+ errs += buf_add(buf, lease, sizeof(struct client_lease));
+ errs += buf_add(buf, &fn_len, sizeof(fn_len));
+ errs += buf_add(buf, lease->filename, fn_len);
+ errs += buf_add(buf, &sn_len, sizeof(sn_len));
+ errs += buf_add(buf, lease->server_name, sn_len);
+ errs += buf_add(buf, &pr_len, sizeof(pr_len));
+ errs += buf_add(buf, prefix, pr_len);
+
+ for (i = 0; i < 256; i++) {
+ errs += buf_add(buf, &lease->options[i].len,
+ sizeof(lease->options[i].len));
+ errs += buf_add(buf, lease->options[i].data,
+ lease->options[i].len);
+ }
+
+ if (errs)
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+}
+
int
script_go(struct interface_info *ip)
{
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int ret;
+
+ hdr.code = IMSG_SCRIPT_GO;
+ hdr.len = sizeof(struct imsg_hdr);
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ if (buf_add(buf, &hdr, sizeof(hdr)))
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+
+ bzero(&hdr, sizeof(hdr));
+ buf_read(privfd, &hdr, sizeof(hdr));
+ if (hdr.code != IMSG_SCRIPT_GO_RET)
+ error("unexpected msg type %u", hdr.code);
+ if (hdr.len != sizeof(hdr) + sizeof(int))
+ error("received corrupted message");
+ buf_read(privfd, &ret, sizeof(ret));
+
+ return (ret);
+}
+
+int
+priv_script_go(void)
+{
char *scriptName;
char *argv[2];
char **envp;
@@ -1899,6 +2046,7 @@ script_go(struct interface_info *ip)
char reason[] = "REASON=NBI";
static char client_path[] = CLIENT_PATH;
int pid, wpid, wstatus;
+ struct interface_info *ip = ifi;
if (ip) {
scriptName = ip->client->config->script_name;
@@ -2218,3 +2366,38 @@ toobig:
warn("dhcp option too large");
return "<error>";
}
+
+
+int
+fork_privchld(int fd, int fd2)
+{
+ int nfds;
+ struct pollfd pfd[1];
+
+ switch (fork()) {
+ case -1:
+ error("cannot fork");
+ case 0:
+ break;
+ default:
+ return (0);
+ }
+
+ setproctitle("%s [priv]", ifi->name);
+
+ close(fd2);
+
+ for (;;) {
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+ if ((nfds = poll(pfd, 1, INFTIM)) == -1)
+ if (errno != EINTR)
+ error("poll error");
+
+ if (nfds == 0 || !(pfd[0].revents & POLLIN))
+ continue;
+
+ dispatch_imsg(fd);
+ }
+}
+
diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h
index f2533d81feb..3c6783247b0 100644
--- a/sbin/dhclient/dhcpd.h
+++ b/sbin/dhclient/dhcpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcpd.h,v 1.28 2004/04/14 20:22:27 henning Exp $ */
+/* $OpenBSD: dhcpd.h,v 1.29 2004/05/04 12:52:05 henning Exp $ */
/*
* Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
@@ -387,6 +387,10 @@ void free_client_lease(struct client_lease *);
void rewrite_client_leases(void);
void write_client_lease(struct interface_info *, struct client_lease *, int);
+void priv_script_init(char *, char *);
+void priv_script_write_params(char *, struct client_lease *);
+int priv_script_go(void);
+
void script_init(struct interface_info *, char *, struct string_list *);
void script_write_params(struct interface_info *,
char *, struct client_lease *);
@@ -438,3 +442,10 @@ void parse_client_lease_declaration(FILE *, struct client_lease *,
struct option *parse_option_decl(FILE *, struct option_data *);
void parse_string_list(FILE *, struct string_list **, int);
void parse_reject_statement(FILE *, struct client_config *);
+
+/* privsep.c */
+struct buf *buf_open(size_t);
+int buf_add(struct buf *, void *, size_t);
+int buf_close(int, struct buf *);
+ssize_t buf_read(int, void *, size_t);
+void dispatch_imsg(int);
diff --git a/sbin/dhclient/privsep.c b/sbin/dhclient/privsep.c
new file mode 100644
index 00000000000..aa7efddf9db
--- /dev/null
+++ b/sbin/dhclient/privsep.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "dhcpd.h"
+#include "privsep.h"
+
+struct buf *
+buf_open(size_t len)
+{
+ struct buf *buf;
+
+ if ((buf = calloc(1, sizeof(struct buf))) == NULL)
+ return (NULL);
+ if ((buf->buf = malloc(len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf->size = len;
+
+ return (buf);
+}
+
+int
+buf_add(struct buf *buf, void *data, size_t len)
+{
+ if (buf->wpos + len > buf->size)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+int
+buf_close(int sock, struct buf *buf)
+{
+ ssize_t n;
+
+ do {
+ n = write(sock, buf->buf + buf->rpos, buf->size - buf->rpos);
+ if (n != -1)
+ buf->rpos += n;
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (-1);
+ }
+ } while (n == -1 && (errno == EAGAIN || errno == EINTR));
+
+ if (buf->rpos < buf->size)
+ error("short write: wanted %u got %d bytes", buf->size,
+ buf->rpos);
+
+ return (n);
+}
+
+ssize_t
+buf_read(int sock, void *buf, size_t nbytes)
+{
+ ssize_t n, r = 0;
+
+ do {
+ n = read(sock, buf, nbytes);
+ if (n == 0)
+ error("connection closed");
+ if (n != -1) {
+ r += n;
+ buf += n;
+ nbytes -= n;
+ }
+ } while (n == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (n == -1)
+ error("buf_read: %m");
+
+ if (r < nbytes)
+ error("short read: wanted %u got %d bytes", nbytes, r);
+
+ return (r);
+}
+
+void
+dispatch_imsg(int fd)
+{
+ struct imsg_hdr hdr;
+ char *medium, *reason, *filename,
+ *servername, *prefix;
+ size_t medium_len, reason_len, filename_len,
+ servername_len, prefix_len, totlen;
+ struct client_lease lease;
+ int ret, i, optlen;
+ struct buf *buf;
+
+ buf_read(fd, &hdr, sizeof(hdr));
+
+ switch (hdr.code) {
+ case IMSG_SCRIPT_INIT:
+ if (hdr.len < sizeof(hdr) + sizeof(size_t))
+ error("corrupted message received");
+ buf_read(fd, &medium_len, sizeof(medium_len));
+ if (hdr.len < medium_len + sizeof(size_t) + sizeof(hdr)
+ + sizeof(size_t))
+ error("corrupted message received");
+ if (medium_len > 0) {
+ if ((medium = calloc(1, medium_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, medium, medium_len);
+ } else
+ medium = NULL;
+
+ buf_read(fd, &reason_len, sizeof(reason_len));
+ if (hdr.len < medium_len + reason_len + sizeof(hdr))
+ error("corrupted message received");
+ if (reason_len > 0) {
+ if ((reason = calloc(1, reason_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, reason, reason_len);
+ } else
+ reason = NULL;
+
+ priv_script_init(reason, medium);
+ free(reason);
+ free(medium);
+ break;
+ case IMSG_SCRIPT_WRITE_PARAMS:
+ totlen = sizeof(hdr) + sizeof(lease) + sizeof(size_t);
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ buf_read(fd, &lease, sizeof(lease));
+
+ buf_read(fd, &filename_len, sizeof(filename_len));
+ totlen += filename_len + sizeof(size_t);
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ if (filename_len > 0) {
+ if ((filename = calloc(1, filename_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, filename, filename_len);
+ } else
+ filename = NULL;
+
+ buf_read(fd, &servername_len, sizeof(servername_len));
+ totlen += servername_len + sizeof(size_t);
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ if (servername_len > 0) {
+ if ((servername =
+ calloc(1, servername_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, servername, servername_len);
+ } else
+ servername = NULL;
+
+ buf_read(fd, &prefix_len, sizeof(prefix_len));
+ totlen += prefix_len;
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ if (prefix_len > 0) {
+ if ((prefix = calloc(1, prefix_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, prefix, prefix_len);
+ } else
+ prefix = NULL;
+
+ for (i = 0; i < 256; i++) {
+ totlen += sizeof(optlen);
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ buf_read(fd, &optlen, sizeof(optlen));
+ lease.options[i].data = NULL;
+ lease.options[i].len = optlen;
+ if (optlen > 0) {
+ totlen += optlen;
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ lease.options[i].data =
+ calloc(1, optlen + 1);
+ if (lease.options[i].data == NULL)
+ error("%m");
+ buf_read(fd, lease.options[i].data, optlen);
+ }
+ }
+ lease.server_name = servername;
+ lease.filename = filename;
+
+ priv_script_write_params(prefix, &lease);
+
+ free(servername);
+ free(filename);
+ free(prefix);
+ for (i = 0; i < 256; i++)
+ if (lease.options[i].len > 0)
+ free(lease.options[i].data);
+
+ break;
+ case IMSG_SCRIPT_GO:
+ if (hdr.len != sizeof(hdr))
+ error("corrupted message received");
+
+ ret = priv_script_go();
+
+ hdr.code = IMSG_SCRIPT_GO_RET;
+ hdr.len = sizeof(struct imsg_hdr) + sizeof(int);
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+ if (buf_add(buf, &hdr, sizeof(hdr)))
+ error("buf_add: %m");
+ if (buf_add(buf, &ret, sizeof(ret)))
+ error("buf_add: %m");
+ if (buf_close(fd, buf) == -1)
+ error("buf_close: %m");
+ break;
+ default:
+ error("received unknown message, code %d", hdr.code);
+ }
+} \ No newline at end of file
diff --git a/sbin/dhclient/privsep.h b/sbin/dhclient/privsep.h
new file mode 100644
index 00000000000..5a2f0fae0e7
--- /dev/null
+++ b/sbin/dhclient/privsep.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <poll.h>
+#include <pwd.h>
+
+struct buf {
+ u_char *buf;
+ size_t size;
+ size_t wpos;
+ size_t rpos;
+};
+
+enum imsg_code {
+ IMSG_NONE,
+ IMSG_SCRIPT_INIT,
+ IMSG_SCRIPT_WRITE_PARAMS,
+ IMSG_SCRIPT_GO,
+ IMSG_SCRIPT_GO_RET
+};
+
+struct imsg_hdr {
+ enum imsg_code code;
+ size_t len;
+};
+
+struct buf *buf_open(size_t);
+int buf_add(struct buf *, void *, size_t);
+int buf_close(int, struct buf *);
+ssize_t buf_read(int sock, void *, size_t);