From 881f66b0c2526bff6a2a51b30d314c512df813b4 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Wed, 22 Aug 2018 11:45:53 +0200 Subject: [PATCH] odhcpd: detect broken hostnames Check hostnames contain valid characters as defined in RFC 952 and RFC 1123. Invalid hostnames in uci configured host entries will result into a refusal to create the static lease. In case a client received hostname contains an invalid character no entry will be added to the lease file. In such case the leaseinfo description in the lease file will still contain the hostname but preceded by the string broken\x20 Signed-off-by: Hans Dedecker --- src/config.c | 5 ++++- src/dhcpv4.c | 5 +++++ src/dhcpv6-ia.c | 16 ++++++++++++---- src/odhcpd.c | 35 +++++++++++++++++++++++++++++++++++ src/odhcpd.h | 8 +++++--- src/ubus.c | 3 +++ 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/config.c b/src/config.c index 6556eac..b36afb1 100644 --- a/src/config.c +++ b/src/config.c @@ -344,8 +344,11 @@ static int set_lease(struct uci_section *s) if (!lease) goto err; - if (hostlen > 1) + if (hostlen > 1) { memcpy(lease->hostname, blobmsg_get_string(c), hostlen); + if (!odhcpd_valid_hostname(lease->hostname)) + goto err; + } if ((c = tb[LEASE_ATTR_IP])) if (inet_pton(AF_INET, blobmsg_get_string(c), &lease->ipaddr) < 0) diff --git a/src/dhcpv4.c b/src/dhcpv4.c index 3386abb..2cc6278 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -1095,6 +1095,11 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface, if (a->hostname) { memcpy(a->hostname, hostname, hostname_len); a->hostname[hostname_len] = 0; + + if (odhcpd_valid_hostname(a->hostname)) + a->flags &= ~OAF_BROKEN_HOSTNAME; + else + a->flags |= OAF_BROKEN_HOSTNAME; } } diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 4ee6dd2..fb1c228 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -331,7 +331,8 @@ void dhcpv6_write_ia_addr(struct in6_addr *addr, int prefix, _unused uint32_t pr inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf) - 1); - if (ctxt->c->length == 128 && ctxt->c->hostname) { + if (ctxt->c->length == 128 && ctxt->c->hostname && + !(ctxt->c->flags & OAF_BROKEN_HOSTNAME)) { fputs(ipbuf, ctxt->fp); char b[256]; @@ -394,8 +395,9 @@ void dhcpv6_write_statefile(void) odhcpd_hexlify(duidbuf, ctxt.c->clid_data, ctxt.c->clid_len); /* iface DUID iaid hostname lifetime assigned length [addrs...] */ - ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s %x %s %ld %x %u ", + ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s %x %s%s %ld %x %u ", ctxt.iface->ifname, duidbuf, ntohl(ctxt.c->iaid), + (ctxt.c->flags & OAF_BROKEN_HOSTNAME) ? "broken\\x20" : "", (ctxt.c->hostname ? ctxt.c->hostname : "-"), (ctxt.c->valid_until > now ? (ctxt.c->valid_until - now + wall_time) : @@ -423,8 +425,9 @@ void dhcpv6_write_statefile(void) odhcpd_hexlify(duidbuf, c->hwaddr, sizeof(c->hwaddr)); /* iface DUID iaid hostname lifetime assigned length [addrs...] */ - ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s ipv4 %s %ld %x 32 ", + ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s ipv4 %s%s %ld %x 32 ", ctxt.iface->ifname, duidbuf, + (c->flags & OAF_BROKEN_HOSTNAME) ? "broken\\x20" : "", (c->hostname ? c->hostname : "-"), (c->valid_until > now ? (c->valid_until - now + wall_time) : @@ -434,7 +437,7 @@ void dhcpv6_write_statefile(void) struct in_addr addr = {.s_addr = c->addr}; inet_ntop(AF_INET, &addr, ipbuf, sizeof(ipbuf) - 1); - if (c->hostname) { + if (c->hostname && !(c->flags & OAF_BROKEN_HOSTNAME)) { fputs(ipbuf, ctxt.fp); char b[256]; @@ -1343,6 +1346,11 @@ ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, if (a->hostname) { memcpy(a->hostname, hostname, hostname_len); a->hostname[hostname_len] = 0; + + if (odhcpd_valid_hostname(a->hostname)) + a->flags &= ~OAF_BROKEN_HOSTNAME; + else + a->flags |= OAF_BROKEN_HOSTNAME; } } a->accept_reconf = accept_reconf; diff --git a/src/odhcpd.c b/src/odhcpd.c index fe4dd73..c1d51e7 100644 --- a/src/odhcpd.c +++ b/src/odhcpd.c @@ -581,3 +581,38 @@ bool odhcpd_bitlen2netmask(bool inet6, unsigned int bits, void *mask) return true; } + +bool odhcpd_valid_hostname(const char *name) +{ +#define MAX_LABEL 63 + const char *c, *label, *label_end; + int label_sz = 0; + + for (c = name, label_sz = 0, label = name, label_end = name + strcspn(name, ".") - 1; + *c && label_sz <= MAX_LABEL; c++) { + if ((*c >= '0' && *c <= '9') || + (*c >= 'A' && *c <= 'Z') || + (*c >= 'a' && *c <= 'z')) { + label_sz++; + continue; + } + + if ((*c == '_' || *c == '-') && c != label && c != label_end) { + label_sz++; + continue; + } + + if (*c == '.') { + if (*(c + 1)) { + label = c + 1; + label_end = label + strcspn(label, ".") - 1; + label_sz = 0; + } + continue; + } + + return false; + } + + return (label_sz && label_sz <= MAX_LABEL ? true : false); +} diff --git a/src/odhcpd.h b/src/odhcpd.h index 91fdcbf..ef94cfc 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -129,9 +129,10 @@ enum odhcpd_mode { enum odhcpd_assignment_flags { - OAF_TENTATIVE = (1 << 0), - OAF_BOUND = (1 << 1), - OAF_STATIC = (1 << 2), + OAF_TENTATIVE = (1 << 0), + OAF_BOUND = (1 << 1), + OAF_STATIC = (1 << 2), + OAF_BROKEN_HOSTNAME = (1 << 3), }; struct config { @@ -288,6 +289,7 @@ void odhcpd_bmemcpy(void *av, const void *bv, size_t bits); int odhcpd_netmask2bitlen(bool v6, void *mask); bool odhcpd_bitlen2netmask(bool v6, unsigned int bits, void *mask); +bool odhcpd_valid_hostname(const char *name); int config_parse_interface(void *data, size_t len, const char *iname, bool overwrite); diff --git a/src/ubus.c b/src/ubus.c index 5be6bbe..1aec590 100644 --- a/src/ubus.c +++ b/src/ubus.c @@ -53,6 +53,9 @@ static int handle_dhcpv4_leases(struct ubus_context *ctx, _unused struct ubus_ob if (c->flags & OAF_STATIC) blobmsg_add_string(&b, NULL, "static"); + + if (c->flags & OAF_BROKEN_HOSTNAME) + blobmsg_add_string(&b, NULL, "broken-hostname"); blobmsg_close_array(&b, m); buf = blobmsg_alloc_string_buffer(&b, "address", INET_ADDRSTRLEN); -- 2.30.2