summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorFlorian Obser <florian@cvs.openbsd.org>2021-02-26 16:16:38 +0000
committerFlorian Obser <florian@cvs.openbsd.org>2021-02-26 16:16:38 +0000
commit87b6b1e326363b77fdfebb2f0f408d1ddcbfcd7d (patch)
tree943590807b633a498b4df729199d8d3f9d6069df /usr.sbin
parentcdcc4ae80c374e157b12e9fa2bd8311b1bf8d8b8 (diff)
Import dhcpleased(8) - a dhcp daemon to acquire IPv4 address leases
from servers. dhcpleased(8) follows the well known three process design of all our privsep daemons. It uses pledge(2) and unveil(2) to restrict access further. In particular the "engine" process, responsible for parsing of untrusted data, is pledge'd "stdio". It cannot access the outside world nor the filesystem at all. Like slaacd(8) for IPv6 it will be always running and acquire addresses for all interface with the autoconf4 flag set. The flag can be set by "ifconfig $if inet autoconf" or by adding "inet autoconf" to /etc/hostname.if. An existing "dhcp" line should be removed. Various iterations tested by deraadt@ The hardest part, finding a name, was handled by jmatthew@ & otto@ "get to it :)" deraadt@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/dhcpleasectl/Makefile17
-rw-r--r--usr.sbin/dhcpleasectl/dhcpleasectl.880
-rw-r--r--usr.sbin/dhcpleasectl/dhcpleasectl.c258
-rw-r--r--usr.sbin/dhcpleasectl/parser.c207
-rw-r--r--usr.sbin/dhcpleasectl/parser.h34
5 files changed, 596 insertions, 0 deletions
diff --git a/usr.sbin/dhcpleasectl/Makefile b/usr.sbin/dhcpleasectl/Makefile
new file mode 100644
index 00000000000..af564dda3cc
--- /dev/null
+++ b/usr.sbin/dhcpleasectl/Makefile
@@ -0,0 +1,17 @@
+# $OpenBSD: Makefile,v 1.1 2021/02/26 16:16:37 florian Exp $
+
+PROG= dhcpleasectl
+SRCS= dhcpleasectl.c parser.c
+
+MAN= dhcpleasectl.8
+
+CFLAGS+= -Wall
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../../sbin/dhcpleased
+LDADD= -lutil
+DPADD= ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dhcpleasectl/dhcpleasectl.8 b/usr.sbin/dhcpleasectl/dhcpleasectl.8
new file mode 100644
index 00000000000..b68d592a25d
--- /dev/null
+++ b/usr.sbin/dhcpleasectl/dhcpleasectl.8
@@ -0,0 +1,80 @@
+.\" $OpenBSD: dhcpleasectl.8,v 1.1 2021/02/26 16:16:37 florian Exp $
+.\"
+.\" Copyright (c) 2021 Florian Obser <florian@openbsd.org>
+.\" Copyright (c) 2016 Kenneth R Westerback <kwesterback@gmail.com>
+.\" Copyright (c) 2004, 2005 Esben Norby <norby@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 USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: February 26 2021 $
+.Dt DHCPLEASECTL 8
+.Os
+.Sh NAME
+.Nm dhcpleasectl
+.Nd control the dhcpleased daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl s Ar socket
+.Ar command
+.Op Ar argument ...
+.Sh DESCRIPTION
+The
+.Nm
+program controls the
+.Xr dhcpleased 8
+daemon.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl s Ar socket
+Use
+.Ar socket
+instead of the default
+.Pa /dev/dhcpleased.sock
+to communicate with
+.Xr dhcpleased 8 .
+.El
+.Pp
+The following commands are available:
+.Bl -tag -width Ds
+.It Cm log brief
+Disable verbose debug logging.
+.It Cm log verbose
+Enable verbose debug logging.
+.It Cm send request Ar interfacename
+Send a DHCP request on interface
+.Ar interfacename
+to force a renew of the lease.
+.It Cm show interface Op Ar interfacename
+Display status about network interfaces.
+If
+.Ar interfacename
+is specified, only information relative to
+.Ar interfacename
+is shown.
+Otherwise information on all interfaces is shown.
+.El
+.Sh FILES
+.Bl -tag -width "/dev/dhcpleased.sockXX" -compact
+.It Pa /dev/dhcpleased.sock
+.Ux Ns -domain
+socket used for communication with
+.Xr dhcpleased 8 .
+.El
+.Sh SEE ALSO
+.Xr dhcpleased 8
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 6.9 .
diff --git a/usr.sbin/dhcpleasectl/dhcpleasectl.c b/usr.sbin/dhcpleasectl/dhcpleasectl.c
new file mode 100644
index 00000000000..e05b70fe558
--- /dev/null
+++ b/usr.sbin/dhcpleasectl/dhcpleasectl.c
@@ -0,0 +1,258 @@
+/* $OpenBSD: dhcpleasectl.c,v 1.1 2021/02/26 16:16:37 florian Exp $ */
+
+/*
+ * Copyright (c) 2021 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003 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 USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet6/nd6.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dhcpleased.h"
+#include "frontend.h"
+#include "parser.h"
+
+__dead void usage(void);
+int show_interface_msg(struct imsg *);
+
+struct imsgbuf *ibuf;
+
+__dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
+ __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_un sun;
+ struct parse_result *res;
+ struct imsg imsg;
+ int ctl_sock;
+ int done = 0;
+ int n, verbose = 0;
+ int ch;
+ char *sockname;
+
+ sockname = DHCPLEASED_SOCKET;
+ while ((ch = getopt(argc, argv, "s:")) != -1) {
+ switch (ch) {
+ case 's':
+ sockname = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (pledge("stdio unix", NULL) == -1)
+ err(1, "pledge");
+
+ /* Parse command line. */
+ if ((res = parse(argc, argv)) == NULL)
+ exit(1);
+
+ /* Connect to control socket. */
+ if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ err(1, "socket");
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
+
+ if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
+ err(1, "connect: %s", sockname);
+
+ if (pledge("stdio", NULL) == -1)
+ err(1, "pledge");
+
+ if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
+ err(1, NULL);
+ imsg_init(ibuf, ctl_sock);
+ done = 0;
+
+ /* Process user request. */
+ switch (res->action) {
+ case LOG_VERBOSE:
+ verbose = 1;
+ /* FALLTHROUGH */
+ case LOG_BRIEF:
+ imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
+ &verbose, sizeof(verbose));
+ printf("logging request sent.\n");
+ done = 1;
+ break;
+ case SHOW_INTERFACE:
+ imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1,
+ &res->if_index, sizeof(res->if_index));
+ break;
+ case SEND_REQUEST:
+ imsg_compose(ibuf, IMSG_CTL_SEND_REQUEST, 0, 0, -1,
+ &res->if_index, sizeof(res->if_index));
+ done = 1;
+ break;
+ default:
+ usage();
+ }
+
+ while (ibuf->w.queued)
+ if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
+ err(1, "write error");
+
+ while (!done) {
+ if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+ errx(1, "imsg_read error");
+ if (n == 0)
+ errx(1, "pipe closed");
+
+ while (!done) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ errx(1, "imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (res->action) {
+ case SHOW_INTERFACE:
+ done = show_interface_msg(&imsg);
+ break;
+ default:
+ break;
+ }
+
+ imsg_free(&imsg);
+ }
+ }
+ close(ctl_sock);
+ free(ibuf);
+
+ return (0);
+}
+
+int
+show_interface_msg(struct imsg *imsg)
+{
+ static int if_count = 0;
+ struct ctl_engine_info *cei;
+ struct timespec now, diff;
+ int i, h, m, s;
+ char buf[IF_NAMESIZE], *bufp;
+ char ipbuf[INET_ADDRSTRLEN];
+ char maskbuf[INET_ADDRSTRLEN];
+ char routerbuf[INET_ADDRSTRLEN];
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_INTERFACE_INFO:
+ cei = imsg->data;
+
+ if (if_count++ > 0)
+ printf("\n");
+
+ bufp = if_indextoname(cei->if_index, buf);
+ printf("%s [%s]:\n", bufp != NULL ? bufp : "unknown",
+ cei->state);
+ memset(ipbuf, 0, sizeof(ipbuf));
+ if (cei->server_identifier.s_addr != INADDR_ANY) {
+ if (inet_ntop(AF_INET, &cei->server_identifier, ipbuf,
+ sizeof(ipbuf)) == NULL)
+ ipbuf[0] = '\0';
+ } else if (cei->dhcp_server.s_addr != INADDR_ANY) {
+ if (inet_ntop(AF_INET, &cei->dhcp_server, ipbuf,
+ sizeof(ipbuf)) == NULL)
+ ipbuf[0] = '\0';
+ }
+ if (ipbuf[0] != '\0')
+ printf("\tserver: %s\n", ipbuf);
+ if (cei->requested_ip.s_addr != INADDR_ANY) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecsub(&now, &cei->request_time, &diff);
+ memset(ipbuf, 0, sizeof(ipbuf));
+ memset(maskbuf, 0, sizeof(maskbuf));
+ memset(routerbuf, 0, sizeof(routerbuf));
+ if (inet_ntop(AF_INET, &cei->requested_ip, ipbuf,
+ sizeof(ipbuf)) == NULL)
+ ipbuf[0] = '\0';
+ if (inet_ntop(AF_INET, &cei->mask, maskbuf,
+ sizeof(maskbuf)) == NULL)
+ maskbuf[0] = '\0';
+ if (inet_ntop(AF_INET, &cei->router, routerbuf,
+ sizeof(routerbuf)) == NULL)
+ routerbuf[0] = '\0';
+ printf("\t IP: %s/%s\n", ipbuf, maskbuf);
+ if (cei->router.s_addr != INADDR_ANY)
+ printf("\trouter: %s\n", routerbuf);
+ if (cei->nameservers[0].s_addr != INADDR_ANY) {
+ printf("\t DNS:");
+ for (i = 0; i < MAX_RDNS_COUNT &&
+ cei->nameservers[i].s_addr != INADDR_ANY;
+ i++) {
+ if (inet_ntop(AF_INET,
+ &cei->nameservers[i], ipbuf,
+ sizeof(ipbuf)) == NULL)
+ continue;
+ printf(" %s", ipbuf);
+ }
+ printf("\n");
+ }
+ s = cei->lease_time - diff.tv_sec;
+ if (s < 0)
+ s = 0;
+ h = s / 3600; s -= h * 3600;
+ m = s / 60; s -= m * 60;
+ if (h > 0)
+ printf("\t lease: %dh%dm%ds\n", h, m ,s);
+ else if (m > 0)
+ printf("\t lease: %dm%ds\n", m ,s);
+ else
+ printf("\t lease: %ds\n", s);
+ }
+ break;
+ case IMSG_CTL_END:
+ return (1);
+ default:
+ break;
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/dhcpleasectl/parser.c b/usr.sbin/dhcpleasectl/parser.c
new file mode 100644
index 00000000000..eb9a0d88505
--- /dev/null
+++ b/usr.sbin/dhcpleasectl/parser.c
@@ -0,0 +1,207 @@
+/* $OpenBSD: parser.c,v 1.1 2021/02/26 16:16:37 florian Exp $ */
+
+/*
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 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 USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dhcpleased.h"
+#include "parser.h"
+
+enum token_type {
+ NOTOKEN,
+ ENDTOKEN,
+ INTERFACENAME,
+ KEYWORD
+};
+
+struct token {
+ enum token_type type;
+ const char *keyword;
+ int value;
+ const struct token *next;
+};
+
+static const struct token t_main[];
+static const struct token t_log[];
+static const struct token t_show[];
+static const struct token t_show_interface[];
+static const struct token t_send[];
+static const struct token t_send_request[];
+
+static const struct token t_main[] = {
+ {KEYWORD, "show", SHOW, t_show},
+ {KEYWORD, "log", NONE, t_log},
+ {KEYWORD, "send", NONE, t_send},
+ {ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_log[] = {
+ {KEYWORD, "verbose", LOG_VERBOSE, NULL},
+ {KEYWORD, "brief", LOG_BRIEF, NULL},
+ {ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_show[] = {
+ {KEYWORD, "interface", SHOW_INTERFACE, t_show_interface},
+ {ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_send[] = {
+ {KEYWORD, "request", SEND_REQUEST, t_send_request},
+ {ENDTOKEN, "", NONE, NULL}
+};
+static const struct token t_send_request[] = {
+ {INTERFACENAME, "", SEND_REQUEST, NULL},
+ {ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_show_interface[] = {
+ {NOTOKEN, "", NONE, NULL},
+ {INTERFACENAME, "", SHOW_INTERFACE, NULL},
+ {ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token *match_token(const char *, const struct token *,
+ struct parse_result *);
+static void show_valid_args(const struct token *);
+
+struct parse_result *
+parse(int argc, char *argv[])
+{
+ static struct parse_result res;
+ const struct token *table = t_main;
+ const struct token *match;
+
+ memset(&res, 0, sizeof(res));
+
+ while (argc >= 0) {
+ if ((match = match_token(argv[0], table, &res)) == NULL) {
+ fprintf(stderr, "valid commands/args:\n");
+ show_valid_args(table);
+ return (NULL);
+ }
+
+ argc--;
+ argv++;
+
+ if (match->type == NOTOKEN || match->next == NULL)
+ break;
+
+ table = match->next;
+ }
+
+ if (argc > 0) {
+ fprintf(stderr, "superfluous argument: %s\n", argv[0]);
+ return (NULL);
+ }
+
+ return (&res);
+}
+
+static const struct token *
+match_token(const char *word, const struct token *table,
+ struct parse_result *res)
+{
+ u_int i, match;
+ const struct token *t = NULL;
+
+ match = 0;
+
+ for (i = 0; table[i].type != ENDTOKEN; i++) {
+ switch (table[i].type) {
+ case NOTOKEN:
+ if (word == NULL || strlen(word) == 0) {
+ match++;
+ t = &table[i];
+ }
+ break;
+ case INTERFACENAME:
+ if (!match && word != NULL && strlen(word) > 0) {
+ if ((res->if_index = if_nametoindex(word)) == 0)
+ errx(1, "unknown interface");
+ match++;
+ t = &table[i];
+ if (t->value)
+ res->action = t->value;
+ }
+ break;
+ case KEYWORD:
+ if (word != NULL && strncmp(word, table[i].keyword,
+ strlen(word)) == 0) {
+ match++;
+ t = &table[i];
+ if (t->value)
+ res->action = t->value;
+ }
+ break;
+ case ENDTOKEN:
+ break;
+ }
+ }
+
+ if (match != 1) {
+ if (word == NULL)
+ fprintf(stderr, "missing argument:\n");
+ else if (match > 1)
+ fprintf(stderr, "ambiguous argument: %s\n", word);
+ else if (match < 1)
+ fprintf(stderr, "unknown argument: %s\n", word);
+ return (NULL);
+ }
+
+ return (t);
+}
+
+static void
+show_valid_args(const struct token *table)
+{
+ int i;
+
+ for (i = 0; table[i].type != ENDTOKEN; i++) {
+ switch (table[i].type) {
+ case NOTOKEN:
+ fprintf(stderr, " <cr>\n");
+ break;
+ case INTERFACENAME:
+ fprintf(stderr, " <interface>\n");
+ break;
+ case KEYWORD:
+ fprintf(stderr, " %s\n", table[i].keyword);
+ break;
+ case ENDTOKEN:
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/dhcpleasectl/parser.h b/usr.sbin/dhcpleasectl/parser.h
new file mode 100644
index 00000000000..dad595b313e
--- /dev/null
+++ b/usr.sbin/dhcpleasectl/parser.h
@@ -0,0 +1,34 @@
+/* $OpenBSD: parser.h,v 1.1 2021/02/26 16:16:37 florian Exp $ */
+
+/*
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 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 USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+enum actions {
+ NONE,
+ LOG_VERBOSE,
+ LOG_BRIEF,
+ SHOW,
+ SHOW_INTERFACE,
+ SEND_REQUEST
+};
+
+struct parse_result {
+ enum actions action;
+ uint32_t if_index;
+};
+
+struct parse_result *parse(int, char *[]);