On Tue, Jul 11, 2017 at 11:48:47AM +0000, Florian Obser wrote: > On Tue, Jul 11, 2017 at 11:08:23AM +0100, Stuart Henderson wrote: > > On 2017/07/11 07:45, Florian Obser wrote: > > > The way I want to move forward with this is: > > > > > > 1) generate a random key at boot if it's not present yet (like we do > > > for ssh host keys and ipsec) > > > 2) if /etc/netstart brings up an interface set the key there by > > > enabling the feature per default like we do with privacy addresses. > > > NOTE that this does NOT enable v6. > > > If you do not want this you have to put -soiikey into the hostname.if > > > file (like -autoconfprivacy) > > > > It's slightly different to autoconfprivacy as that is enabled by > > default in the kernel for new interfaces, whereas with this it depends > > on the key being configured. So there are implications for dynamically > > created interfaces (USB, vlan etc) which may not always be handled by > > netstart. > > > > I'm wondering if we should have a "system soiikey" (e.g. in a sysctl) > > that is automatically applied to newly created interfaces? (Does the > > key even need to be configurable per-interface or would a per-system > > one be enough by itself?) > > I was also considering this. A per system key is probably enough. I > went with the per-interface key because that made it easier to disable > the feature on an interface. If we go with per-system we need a > interface flag to disable the feature (like -autoconfprivacy). > > The rfc seems to assume a per system secret. > > hmm... are there sysctls that are only readable as root?
yeah. see below for example code for a net.inet6.ip6.soiikey sysctl. it looks big cos it teaches sysctl how to handle binary strings represented by hex. are we tied to sha512 for this? siphash makes a nice 8 byte output... > > > > > > 3) if v6 is enabled in hostname.if you get a random but stable link > > > local address according to RFC 7217. > > > 4) if autoconf6 is enabled on the interface slaacd will generate a > > > random but stable global address according to RFC 7217 > > > > > > Also note that RFC 8064 is a thing and we are kinda supposed to do this. > > > Also I think it's a good idea[tm]. > > > > > > Comments, OKs? > > > > A couple of other comments: > > > > - system administrators should know in advance of updating that their > > IP address may change in case they need to prepare for it (adjust > > firewalls/nat-pt/proxies). > > sure, /me glances at current.html > > > > > - ramdisk upgrades may need to load the key from the installed system > > (e.g. in presence of restrictive firewall policies). > > > > good point > > > - a half-related idea came up while pondering this, > > "block in inet6 to autoconfprivacy" (similar UI to urpf-failed) > > (I'm meant to be doing something else and likely to forget so I'd > > like to at least plant the idea :) > > heh, interesting > > -- > I'm not entirely sure you are real. Index: sbin/sysctl/sysctl.c =================================================================== RCS file: /cvs/src/sbin/sysctl/sysctl.c,v retrieving revision 1.226 diff -u -p -r1.226 sysctl.c --- sbin/sysctl/sysctl.c 25 Apr 2017 17:33:16 -0000 1.226 +++ sbin/sysctl/sysctl.c 12 Jul 2017 03:39:33 -0000 @@ -212,7 +212,7 @@ int sysctl_chipset(char *, char **, int #endif void vfsinit(void); -char *equ = "="; +const char *equ = "="; int main(int argc, char *argv[]) @@ -286,6 +286,52 @@ listall(char *prefix, struct list *lp) } } +int +parse_hex_char(char ch) +{ + if (ch >= '0' && ch <= '9') + return (ch - '0'); + if (ch >= 'a' && ch <= 'f') + return (ch - 'a'); + if (ch >= 'A' && ch <= 'F') + return (ch - 'A'); + + return (-1); +} + +ssize_t +parse_hex_string(unsigned char *dst, size_t dstlen, const char *src) +{ + ssize_t len = 0; + int digit; + + while (len < dstlen) { + if (*src == '\0') + return (len); + + digit = parse_hex_char(*src++); + if (digit == -1) + return (-1); + dst[len] = digit << 4; + + digit = parse_hex_char(*src++); + if (digit == -1) + return (-1); + + dst[len] |= digit; + len++; + } + + while (*src != '\0') { + if (parse_hex_char(*src++) == -1 || + parse_hex_char(*src++) == -1) + return (-1); + len++; + } + + return (len); +} + /* * Parse a name into a MIB entry. * Lookup and print out the MIB entry if it exists. @@ -566,6 +612,9 @@ parse(char *string, int flags) len = sysctl_inet6(string, &bufp, mib, flags, &type); if (len < 0) return; + if (mib[2] == IPPROTO_IPV6 && + mib[3] == IPV6CTL_SOIIKEY) + special |= HEX; if ((mib[2] == IPPROTO_IPV6 && mib[3] == IPV6CTL_MRTMFC) || (mib[2] == IPPROTO_IPV6 && mib[3] == IPV6CTL_MRTMIF) || @@ -717,6 +766,29 @@ parse(char *string, int flags) newval = &quadval; newsize = sizeof(quadval); break; + case CTLTYPE_STRING: + if (special & HEX) { + ssize_t len; + + len = parse_hex_string(buf, sizeof(buf), + newval); + if (len == -1) { + warnx("%s: hex string %s: invalid", + string, newval); + return; + } + if (len > sizeof(buf)) { + warnx("%s: hex string %s: too long", + string, newval); + return; + } + + newval = buf; + newsize = len; + + printf("newsize = %zu\n", newsize); + } + break; } } size = (special & SMALLBUF) ? 512 : SYSCTL_BUFSIZ; @@ -936,13 +1008,26 @@ parse(char *string, int flags) if (newval == NULL) { if (!nflag) (void)printf("%s%s", string, equ); - (void)puts(buf); - } else { - if (!qflag) { - if (!nflag) - (void)printf("%s: %s -> ", string, buf); - (void)puts((char *)newval); + if (special & HEX) { + size_t i; + for (i = 0; i < size; i++) + (void)printf("%02x", buf[i]); + (void)printf("\n"); + } else + (void)puts(buf); + } else if (!qflag) { + if (!nflag) { + (void)printf("%s: ", string); + if (special & HEX) { + size_t i; + for (i = 0; i < size; i++) + (void)printf("%02x", buf[i]); + } else + (void)printf("%s", cp); + + (void)printf(" -> "); } + (void)puts(cp); } return; Index: sys/netinet6/in6.h =================================================================== RCS file: /cvs/src/sys/netinet6/in6.h,v retrieving revision 1.96 diff -u -p -r1.96 in6.h --- sys/netinet6/in6.h 30 May 2017 09:10:49 -0000 1.96 +++ sys/netinet6/in6.h 12 Jul 2017 03:39:33 -0000 @@ -592,7 +592,8 @@ ifatoia6(struct ifaddr *ifa) #define IPV6CTL_IFQUEUE 51 #define IPV6CTL_MRTMIF 52 #define IPV6CTL_MRTMFC 53 -#define IPV6CTL_MAXID 54 +#define IPV6CTL_SOIIKEY 54 +#define IPV6CTL_MAXID 55 /* New entries should be added here from current IPV6CTL_MAXID value. */ /* to define items, should talk with KAME guys first, for *BSD compatibility */ @@ -652,6 +653,7 @@ ifatoia6(struct ifaddr *ifa) { "ifq", CTLTYPE_NODE }, \ { "mrtmif", CTLTYPE_STRUCT }, \ { "mrtmfc", CTLTYPE_STRUCT }, \ + { "soiikey", CTLTYPE_STRING }, /* binary string */ \ } #define IPV6CTL_VARS { \ Index: sys/netinet6/ip6_input.c =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_input.c,v retrieving revision 1.198 diff -u -p -r1.198 ip6_input.c --- sys/netinet6/ip6_input.c 5 Jul 2017 11:34:10 -0000 1.198 +++ sys/netinet6/ip6_input.c 12 Jul 2017 03:39:34 -0000 @@ -1375,6 +1375,56 @@ ip6_sysctl_ip6stat(void *oldp, size_t *o return (ret); } +struct ip6_soiikey { + unsigned int len; + unsigned char key[32]; +}; + +struct ip6_soiikey ip6_soiikey = { .len = 0 }; + +int +ip6_sysctl_soiikey(void *oldp, size_t *oldlenp, void *newp, size_t newlen) +{ + unsigned char key[32]; + int error; + + error = suser(curproc, 0); + if (error != 0) + return (error); + + if (oldp != NULL) { + if (newp == NULL && ip6_soiikey.len == 0) + return (EOPNOTSUPP); + if (*oldlenp < ip6_soiikey.len) + return (ENOMEM); + } + if (newp != NULL) { + /* check securelevel? */ + if (newlen > sizeof(ip6_soiikey.key)) + return (EINVAL); + if (newlen > 0) { + error = copyin(newp, key, newlen); + if (error != 0) + return (error); + } + } + if (oldp != NULL) { + error = copyout(ip6_soiikey.key, oldp, *oldlenp); + if (error != 0) + return (error); + *oldlenp = ip6_soiikey.len; + } + + if (newp != NULL) { + /* commit */ + if (newlen > 0) + memcpy(ip6_soiikey.key, key, newlen); + ip6_soiikey.len = newlen; + } + + return (0); +} + int ip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) @@ -1429,6 +1479,8 @@ ip6_sysctl(int *name, u_int namelen, voi case IPV6CTL_IFQUEUE: return (sysctl_niq(name + 1, namelen - 1, oldp, oldlenp, newp, newlen, &ip6intrq)); + case IPV6CTL_SOIIKEY: + return (ip6_sysctl_soiikey(oldp, oldlenp, newp, newlen)); default: if (name[0] < IPV6CTL_MAXID) return (sysctl_int_arr(ipv6ctl_vars, name, namelen,