Page MenuHomeFreeBSD

D60594.diff
No OneTemporary

D60594.diff

diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c
--- a/usr.sbin/rtsold/rtsol.c
+++ b/usr.sbin/rtsold/rtsol.c
@@ -58,6 +58,7 @@
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
+#include <stdbool.h>
#include <stdio.h>
#include <time.h>
#include <err.h>
@@ -71,6 +72,7 @@
struct ifinfo_head_t ifinfo_head = TAILQ_HEAD_INITIALIZER(ifinfo_head);
static void call_script(const char *const *, struct script_msg_head_t *);
+static void captive_portal_set(struct ifinfo *, const char *);
static size_t dname_labeldec(char *, size_t, const char *);
static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t);
static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *,
@@ -171,7 +173,7 @@
struct nd_router_advert *nd_ra;
struct cmsghdr *cm;
struct rainfo *rai;
- char *p, *raoptp;
+ char *p, *raoptp, *cp;
struct in6_addr *addr;
struct nd_opt_hdr *ndo;
struct nd_opt_rdnss *rdnss;
@@ -180,6 +182,8 @@
char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1];
char dname[NI_MAXHOST];
struct timespec lifetime, now;
+ char *capurl;
+ bool complete;
int newent_rai, newent_rao;
memset(&hdr, 0, sizeof(hdr));
@@ -339,6 +343,8 @@
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
newent_rai = 0;
+ capurl = NULL;
+ complete = true;
rai = find_rainfo(ifi, &from);
if (rai == NULL) {
ELM_MALLOC(rai, exit(1));
@@ -366,10 +372,12 @@
if (ndo->nd_opt_len == 0) {
warnmsg(LOG_INFO, __func__, "invalid option length 0.");
+ complete = false;
break;
}
if ((char *)RA_OPT_NEXT_HDR(raoptp) > (char *)icp + msglen) {
warnmsg(LOG_INFO, __func__, "option length overflow.");
+ complete = false;
break;
}
@@ -515,12 +523,64 @@
p += len;
}
break;
+ case ND_OPT_CAPTIVE_PORTAL:
+ /*
+ * RFC 8910: the option carries the captive portal
+ * API URL, zero padded to the 8-octet boundary.
+ * It has no lifetime of its own: the URL is
+ * considered present as long as the router keeps
+ * advertising it, so it is recorded per router and
+ * replaced once the whole RA has been parsed.
+ */
+ len = ndo->nd_opt_len * 8 - sizeof(*ndo);
+ if (len == 0) {
+ warnmsg(LOG_INFO, __func__,
+ "empty captive portal option in RA "
+ "from %s was ignored.",
+ inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, sizeof(ntopbuf)));
+ break;
+ }
+ if ((cp = malloc(len + 1)) == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "malloc failed: %s", strerror(errno));
+ break;
+ }
+ memcpy(cp, ndo + 1, len);
+ cp[len] = '\0';
+ while (len > 0 && cp[len - 1] == '\0')
+ cp[--len] = '\0';
+ if (len == 0) {
+ free(cp);
+ break;
+ }
+ warnmsg(LOG_DEBUG, __func__, "captive portal = %s",
+ cp);
+ if (capurl == NULL)
+ capurl = cp;
+ else
+ free(cp);
+ break;
default:
/* nothing to do for other options */
break;
}
raoptp = (char *)RA_OPT_NEXT_HDR(raoptp);
}
+
+ /*
+ * The RA just parsed is authoritative for the captive portal URL
+ * of this router: replace the previously stored one, or drop it
+ * when this RA does not carry the option. A truncated RA tells
+ * nothing about a missing option, so keep the current value then.
+ */
+ if (complete) {
+ free(rai->rai_captive_url);
+ rai->rai_captive_url = capurl;
+ capurl = NULL;
+ }
+ free(capurl);
+
if (newent_rai)
TAILQ_INSERT_TAIL(&ifi->ifi_rainfo, rai, rai_next);
@@ -555,9 +615,11 @@
TAILQ_HEAD_INITIALIZER(sm_rdnss_head);
struct script_msg_head_t sm_dnssl_head =
TAILQ_HEAD_INITIALIZER(sm_dnssl_head);
+ char *captive;
int dcount, dlen;
+ captive = NULL;
dcount = 0;
dlen = strlen(resstr_sh_prefix) + strlen(resstr_nl);
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
@@ -568,6 +630,8 @@
* handled, not overridden. [RFC 4861 6.3.4]
*/
TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) {
+ if (captive == NULL)
+ captive = rai->rai_captive_url;
TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
switch (rao->rao_type) {
case ND_OPT_RDNSS:
@@ -647,6 +711,7 @@
if (!uflag)
ra_opt_rdnss_dispatch(ifi, NULL, &sm_rdnss_head,
&sm_dnssl_head);
+ captive_portal_set(ifi, captive);
return (0);
}
@@ -776,6 +841,73 @@
argv[0], status);
}
+/*
+ * Advertise the captive portal API URL received in a Router
+ * Advertisement (RFC 8910).
+ */
+static void
+captive_portal_set(struct ifinfo *ifi, const char *url)
+{
+ char name[IFNAMSIZ + 32], tmp[MAXPATHLEN];
+ int fd;
+ ssize_t len;
+
+ if (captivefd < 0)
+ return;
+
+ if (url == NULL) {
+ if (ifi->ifi_captive_url == NULL)
+ return;
+ snprintf(name, sizeof(name), "captive-portal6.%s",
+ ifi->ifname);
+ (void)unlinkat(captivefd, name, 0);
+ free(ifi->ifi_captive_url);
+ ifi->ifi_captive_url = NULL;
+ return;
+ }
+
+ if (ifi->ifi_captive_url != NULL &&
+ strcmp(ifi->ifi_captive_url, url) == 0)
+ return;
+
+ /*
+ * Remove a stale temporary file left by an interrupted run, then
+ * create it with O_EXCL so a symlink cannot be followed.
+ */
+ snprintf(name, sizeof(name), "captive-portal6.%s", ifi->ifname);
+ snprintf(tmp, sizeof(tmp), "%s.tmp", name);
+ (void)unlinkat(captivefd, tmp, 0);
+ fd = openat(captivefd, tmp, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if (fd < 0) {
+ warnmsg(LOG_ERR, __func__, "openat(%s): %s", tmp,
+ strerror(errno));
+ return;
+ }
+ len = strlen(url);
+ if (write(fd, url, len) != len || write(fd, "\n", 1) != 1) {
+ warnmsg(LOG_ERR, __func__, "write(%s): %s", tmp,
+ strerror(errno));
+ (void)close(fd);
+ (void)unlinkat(captivefd, tmp, 0);
+ return;
+ }
+ (void)close(fd);
+ if (renameat(captivefd, tmp, captivefd, name) < 0) {
+ warnmsg(LOG_ERR, __func__, "renameat(%s): %s", name,
+ strerror(errno));
+ (void)unlinkat(captivefd, tmp, 0);
+ return;
+ }
+
+ free(ifi->ifi_captive_url);
+ ifi->ifi_captive_url = strdup(url);
+ if (ifi->ifi_captive_url == NULL)
+ return;
+
+ warnmsg(LOG_INFO, __func__, "New Captive Portal (%s): %s",
+ ifi->ifname, url);
+}
+
#define PERIOD 0x2e
#define hyphenchar(c) ((c) == 0x2d)
#define periodchar(c) ((c) == PERIOD)
diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h
--- a/usr.sbin/rtsold/rtsold.h
+++ b/usr.sbin/rtsold/rtsold.h
@@ -56,6 +56,8 @@
struct ifinfo *rai_ifinfo;
struct sockaddr_in6 rai_saddr;
TAILQ_HEAD(, ra_opt) rai_ra_opt;
+ char *rai_captive_url; /* captive portal API URL,
+ * or NULL */
};
/* Per-interface tracking info. */
@@ -84,6 +86,8 @@
int ifi_rdnss; /* RDNSS option state */
int ifi_dnssl; /* DNSSL option state */
+ char *ifi_captive_url; /* captive portal API URL, or NULL */
+
int racnt; /* total # of valid RAs it have got */
TAILQ_HEAD(, rainfo) ifi_rainfo;
@@ -161,6 +165,7 @@
extern const char *otherconf_script;
extern const char *alwaysconf_script;
extern const char *resolvconf_script;
+extern int captivefd;
extern struct cap_channel *capllflags, *capscript, *capsendmsg, *capsyslog;
struct ifinfo *find_ifinfo(int);
diff --git a/usr.sbin/rtsold/rtsold.8 b/usr.sbin/rtsold/rtsold.8
--- a/usr.sbin/rtsold/rtsold.8
+++ b/usr.sbin/rtsold/rtsold.8
@@ -294,6 +294,10 @@
.Pp
Otherwise it will be
.Ql ifname:slaac .
+.Pp
+When a Router Advertisement carries a Captive Portal option (option 37,
+RFC 8910), the advertised API URL is written to
+.Pa /var/run/captive-portal6. Ns Ar interface
.El
.Sh FILES
.Bl -tag -width /var/run/rtsold.dump -compact
@@ -302,6 +306,11 @@
.Nm .
.It Pa /var/run/rtsold.dump
Internal state dump file.
+.It Pa /var/run/captive-portal6. Ns Ar interface
+The Captive Portal API URL received in a Router Advertisement,
+terminated by a newline.
+The file is removed when a subsequent Router Advertisement from the
+same router no longer carries the Captive Portal option.
.El
.\"
.Sh EXIT STATUS
diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c
--- a/usr.sbin/rtsold/rtsold.c
+++ b/usr.sbin/rtsold/rtsold.c
@@ -83,6 +83,8 @@
const char *alwaysconf_script;
const char *resolvconf_script = "/sbin/resolvconf";
+int captivefd = -1;
+
cap_channel_t *capllflags, *capscript, *capsendmsg, *capsyslog;
/* protocol constants */
@@ -282,6 +284,24 @@
exit(1);
}
+ /*
+ * Open the directory where the captive portal API URL (RFC 8910)
+ * is published before entering capability mode. The feature is
+ * disabled if the descriptor cannot be opened or limited.
+ */
+ captivefd = open("/var/run", O_RDONLY | O_DIRECTORY);
+ if (captivefd >= 0) {
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_LOOKUP, CAP_CREATE, CAP_WRITE,
+ CAP_FTRUNCATE, CAP_RENAMEAT_SOURCE, CAP_RENAMEAT_TARGET,
+ CAP_UNLINKAT);
+ if (caph_rights_limit(captivefd, &rights) < 0) {
+ (void)close(captivefd);
+ captivefd = -1;
+ }
+ }
+
/* Enter capability mode. */
caph_cache_catpages();
if (caph_enter_casper() != 0) {

File Metadata

Mime Type
text/plain
Expires
Tue, Oct 13, 4:32 PM (12 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40624642
Default Alt Text
D60594.diff (8 KB)

Event Timeline