diff options
-rw-r--r-- | sbin/dhclient/Makefile | 5 | ||||
-rw-r--r-- | sbin/dhclient/dhclient.c | 203 | ||||
-rw-r--r-- | sbin/dhclient/dhcpd.h | 13 | ||||
-rw-r--r-- | sbin/dhclient/privsep.c | 228 | ||||
-rw-r--r-- | sbin/dhclient/privsep.h | 45 |
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); |