On 2017/07/11 07:45, Florian Obser wrote: > > Here is the first step in implementing RFC 7217 "A Method for > Generating Semantically Opaque Interface Identifiers with IPv6 > Stateless Address Autoconfiguration (SLAAC)" > > This is orthogonal to privacy extension. The idea is to replace the > stable slaac addresses formed from the MAC address of your ethernet > device with a random but stable over reboots address. This address can > be used for server type services to connect in since the address is > stable over reboots. It defends against certain scanning and > enumeration attacks since it's random. See the RFC for details.
Thank you - we definitely want RFC 7217. - compared to L2-based addresses (or typical user-chosen static addresses) this increases search space for scanners, often significantly. see https://tools.ietf.org/html/rfc7707#section-4.1.1.1 - it is particularly bad if it's known that VMs of a certain type are used. - besides servers, it's a halfway house between autoconfprivacy and static addresses, useful for networks with L3-address-based access controls. (yes that's a broken concept anyway, but it doesn't stop people using it). > 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?) > 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). - ramdisk upgrades may need to load the key from the installed system (e.g. in presence of restrictive firewall policies). - 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 :) > diff --git sbin/ifconfig/ifconfig.8 sbin/ifconfig/ifconfig.8 > index cac8eafc2cb..584a90911f4 100644 > --- sbin/ifconfig/ifconfig.8 > +++ sbin/ifconfig/ifconfig.8 > @@ -1070,6 +1070,7 @@ protocol when supported by the access point. > .Op Oo Fl Oc Ns Cm autoconfprivacy > .Op Cm eui64 > .Op Cm pltime Ar n > +.Op Oo Fl Oc Ns Cm soiikey Ar hexkey > .Op Oo Fl Oc Ns Cm tentative > .Op Cm vltime Ar n > .Ek > @@ -1127,6 +1128,18 @@ Fill the interface index > automatically. > .It Cm pltime Ar n > Set preferred lifetime for the address. > +.It Cm soiikey Ar hexkey - users probably need guidance on how long "hexkey" should be. (Same for "hexkey" in wpakey actually - the manual there just says "full length"). > +Set the secret to generate random but stable Semantically Opaque > +Interface Identifiers (RFC 7217) for IPv6 Stateless Address > +Autoconfiguration. > +This is orthogonal to > +.Cm autoconfprivacy . > +It is meant to improve the security and privacy properties of the > +stable addresses formed from the layer 2 hardware address by replacing > +them with a random but stable address that depends on the advertised > +prefix. > +.It Cm -soikey > +Disable generation of Semantically Opaque Interface Identifiers. > .It Cm tentative > Set the IPv6 tentative address bit. > .It Cm -tentative > diff --git sbin/ifconfig/ifconfig.c sbin/ifconfig/ifconfig.c > index d99bcb34871..b08dbe57d83 100644 > --- sbin/ifconfig/ifconfig.c > +++ sbin/ifconfig/ifconfig.c > @@ -198,6 +198,7 @@ void setia6pltime(const char *, int); > void setia6vltime(const char *, int); > void setia6lifetime(const char *, const char *); > void setia6eui64(const char *, int); > +void setifsoiikey(const char *, int); > void setkeepalive(const char *, const char *); > void unsetkeepalive(const char *, int); > void setmedia(const char *, int); > @@ -376,6 +377,8 @@ const struct cmd { > { "-group", NEXTARG, 0, unsetifgroup }, > { "autoconf", 1, 0, setautoconf }, > { "-autoconf", -1, 0, setautoconf }, > + { "soiikey", NEXTARG, 0, setifsoiikey }, > + { "-soiikey", -1, 0, setifsoiikey }, > { "trunkport", NEXTARG, 0, settrunkport }, > { "-trunkport", NEXTARG, 0, unsettrunkport }, > { "trunkproto", NEXTARG, 0, settrunkproto }, > @@ -1418,6 +1421,36 @@ setautoconf(const char *cmd, int val) > } > } > > +void > +setifsoiikey(const char *val, int d) > +{ > + struct if_soiikey soiikey; > + int passlen; > + > + memset(&soiikey, 0, sizeof(soiikey)); > + if (d != -1) { > + passlen = strlen(val); > + > + if (passlen < 4 || (passlen - 2) % 2 != 0 || > + val[0] != '0' || val[1] != 'x') > + errx(1, "soiikey: invalid key"); > + > + soiikey.len = (passlen - 2) / 2; > + > + val = get_string(val, NULL, soiikey.key, &passlen); > + if (val == NULL) > + errx(1, "soiikey: invalid soii key"); > + > + soiikey.enabled = 1; > + } else > + soiikey.enabled = 0; > + > + ifr.ifr_data = (caddr_t)&soiikey; > + > + if (ioctl(s, SIOCSIFSOIIKEY, (caddr_t)&ifr) < 0) > + err(1, "SIOCSIFSOIIKEY"); > +} > + > #ifndef SMALL > /* ARGSUSED */ > void > @@ -2838,6 +2871,7 @@ status(int link, struct sockaddr_dl *sdl, int ls) > { > const struct afswtch *p = afp; > struct ifmediareq ifmr; > + struct if_soiikey soiikey; > #ifndef SMALL > struct ifreq ifrdesc; > struct ifkalivereq ikardesc; > @@ -2982,6 +3016,24 @@ status(int link, struct sockaddr_dl *sdl, int ls) > putchar('\n'); > } > #endif > + > + memset(&soiikey, 0, sizeof(soiikey)); > + ifr.ifr_data = (caddr_t)&soiikey; > + if (ioctl(s, SIOCGIFSOIIKEY, (caddr_t)&ifr) == -1) > + err(1, "SIOCGIFSOIIKEY: %lu", SIOCGIFSOIIKEY); > + if (soiikey.enabled) { > + printf("\tsoiikey: "); > + if (soiikey.enabled == 2) > + fputs("<not displayed>", stdout); > + else { > + if (soiikey.len <= sizeof(soiikey)) > + print_string(soiikey.key, soiikey.len); > + else > + fputs("<invalid>", stdout); > + } > + printf("\n"); > + } > + > ieee80211_status(); > > if (showmediaflag) { > diff --git sys/net/if.c sys/net/if.c > index 6354484e477..1e323a37fb2 100644 > --- sys/net/if.c > +++ sys/net/if.c > @@ -163,6 +163,10 @@ void ifa_print_all(void); > > void if_qstart_compat(struct ifqueue *); > > +#ifdef INET6 > +void ifnewsoiikey(struct ifnet *); > +#endif > + > /* > * interface index map > * > @@ -1824,6 +1828,9 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, > struct proc *p) > struct sockaddr_dl *sdl; > struct ifgroupreq *ifgr; > struct if_afreq *ifar; > +#ifdef INET6 > + struct if_soiikey *soiikey; > +#endif > char ifdescrbuf[IFDESCRSIZE]; > char ifrtlabelbuf[RTLABEL_LEN]; > int s, error = 0; > @@ -2185,6 +2192,40 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, > struct proc *p) > ifp->if_llprio = ifr->ifr_llprio; > break; > > +#ifdef INET6 > + case SIOCGIFSOIIKEY: > + soiikey = (struct if_soiikey *)ifr->ifr_data; > + if (ifp->if_soiikey.enabled) { > + soiikey->enabled = 1; > + if (suser(curproc, 0) != 0) { > + soiikey->enabled = 2; > + memset(soiikey->key, 0, sizeof(soiikey->key)); > + break; /* return ok but w/o key */ > + } > + memcpy(soiikey->key, ifp->if_soiikey.key, > + sizeof(soiikey->key)); > + soiikey->len = ifp->if_soiikey.len; > + } else > + soiikey->enabled = 0; > + break; > + case SIOCSIFSOIIKEY: > + if ((error = suser(curproc, 0)) != 0) > + break; > + soiikey = (struct if_soiikey *)ifr->ifr_data; > + if (soiikey->enabled) { > + if (soiikey->len > sizeof(soiikey->key)) { > + error = EINVAL; > + break; > + } > + ifp->if_soiikey.len = soiikey->len; > + memcpy(&ifp->if_soiikey, soiikey, > + sizeof(ifp->if_soiikey)); > + } else > + memset(&ifp->if_soiikey, 0, sizeof(ifp->if_soiikey)); > + ifnewsoiikey(ifp); > + break; > +#endif /* INET6 */ > + > default: > if (so->so_proto == 0) > return (EOPNOTSUPP); > @@ -2798,6 +2839,28 @@ ifnewlladdr(struct ifnet *ifp) > splx(s); > } > > +#ifdef INET6 > +void > +ifnewsoiikey(struct ifnet *ifp) > +{ > + struct ifaddr *ifa; > + int s; > + > + s = splnet(); > + > + /* > + * Update the link-local address. > + */ > + ifa = &in6ifa_ifpforlinklocal(ifp, 0)->ia_ifa; > + if (ifa) { > + in6_purgeaddr(ifa); > + dohooks(ifp->if_addrhooks, 0); > + in6_ifattach(ifp); > + } > + splx(s); > +} > +#endif /* INET6 */ > + > int net_ticks; > u_int net_livelocks; > > diff --git sys/net/if.h sys/net/if.h > index 89867eac340..5ab6f2f5417 100644 > --- sys/net/if.h > +++ sys/net/if.h > @@ -452,6 +452,13 @@ struct if_parent { > char ifp_parent[IFNAMSIZ]; > }; > > +/* SIOC[SG]IFSOIIKEY */ > +struct if_soiikey { > + int enabled; > + int len; > + uint8_t key[32]; > +}; > + > #include <net/if_arp.h> > > #ifdef _KERNEL > diff --git sys/net/if_var.h sys/net/if_var.h > index 14e118bcbef..5e33fa2a59f 100644 > --- sys/net/if_var.h > +++ sys/net/if_var.h > @@ -130,6 +130,9 @@ struct ifnet { /* and the > entries */ > unsigned short if_flags; /* up/down, broadcast, etc. */ > int if_xflags; /* extra softnet flags */ > struct if_data if_data; /* stats and other data about if */ > +#ifdef INET6 > + struct if_soiikey if_soiikey; > +#endif > u_int32_t if_hardmtu; /* maximum MTU device supports */ > char if_description[IFDESCRSIZE]; /* interface description */ > u_short if_rtlabelid; /* next route label */ > diff --git sys/netinet6/in6_ifattach.c sys/netinet6/in6_ifattach.c > index fad1eb130b6..d8af7e69ccc 100644 > --- sys/netinet6/in6_ifattach.c > +++ sys/netinet6/in6_ifattach.c > @@ -58,6 +58,7 @@ > > int get_last_resort_ifid(struct ifnet *, struct in6_addr *); > int get_hw_ifid(struct ifnet *, struct in6_addr *); > +int get_soii_ifid(struct ifnet *, struct in6_addr *); > int get_ifid(struct ifnet *, struct in6_addr *); > int in6_ifattach_loopback(struct ifnet *); > > @@ -231,6 +232,55 @@ get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6) > } > > /* > + * Generate a Semantically Opaque Interface Identifier according to RFC 7217 > + * > + * in6 - upper 64bits are preserved > + */ > +int > +get_soii_ifid(struct ifnet *ifp, struct in6_addr *in6) > +{ > + SHA2_CTX ctx; > + struct in6_addr prefix; > + struct sockaddr_dl *sdl; > + int dad_counter = 0; /* XXX not used */ > + char *addr; > + u_int8_t digest[SHA512_DIGEST_LENGTH]; > + > + if (!ifp->if_soiikey.enabled || ifp->if_soiikey.len <= 0) > + return -1; > + > + sdl = ifp->if_sadl; > + if (sdl == NULL || sdl->sdl_alen == 0) > + return -1; > + > + memset(&prefix, 0, sizeof(prefix)); > + prefix.s6_addr16[0] = htons(0xfe80); > + addr = LLADDR(sdl); > + > + SHA512Init(&ctx); > + SHA512Update(&ctx, &prefix, sizeof(prefix)); > + SHA512Update(&ctx, addr, sdl->sdl_alen); > + SHA512Update(&ctx, &dad_counter, sizeof(dad_counter)); > + SHA512Update(&ctx, &dad_counter, sizeof(dad_counter)); > + SHA512Update(&ctx, ifp->if_soiikey.key, ifp->if_soiikey.len); > + > + SHA512Final(digest, &ctx); > + > + /* assumes sizeof(digest) > sizeof(ifid) */ > + bcopy(digest, &in6->s6_addr[8], 8); > + > + /* make sure to set "u" bit to local, and "g" bit to individual. */ > + in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ > + in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ > + > + /* convert EUI64 into IPv6 interface identifier */ > + EUI64_TO_IFID(in6); > + > + return 0; > +} > + > + > +/* > * Get interface identifier for the specified interface. If it is not > * available on ifp0, borrow interface identifier from other information > * sources. > @@ -240,7 +290,15 @@ get_ifid(struct ifnet *ifp0, struct in6_addr *in6) > { > struct ifnet *ifp; > > - /* first, try to get it from the interface itself */ > + /* first, try to generate a Semantically Opaque Interface Identifier */ > + if (get_soii_ifid(ifp0, in6) == 0) { > + nd6log((LOG_DEBUG, "%s: got Semantically Opaque Interface " > + "Identifier\n", > + ifp0->if_xname)); > + goto success; > + } > + > + /* next, try to get it from the interface itself */ > if (get_hw_ifid(ifp0, in6) == 0) { > nd6log((LOG_DEBUG, "%s: got interface identifier from itself\n", > ifp0->if_xname)); > diff --git sys/sys/sockio.h sys/sys/sockio.h > index 216c2d5ba31..0fa64195c0a 100644 > --- sys/sys/sockio.h > +++ sys/sys/sockio.h > @@ -208,6 +208,9 @@ > #define SIOCSUMBPARAM _IOW('i', 191, struct ifreq) /* set MBIM > param */ > #define SIOCGUMBPARAM _IOWR('i', 192, struct ifreq) /* get MBIM > param */ > > +#define SIOCSIFSOIIKEY _IOW('i', 193, struct ifreq) /* set soiikey > */ > +#define SIOCGIFSOIIKEY _IOWR('i', 194, struct ifreq) /* get soiikey > */ > + > #define SIOCSVH _IOWR('i', 245, struct ifreq) /* set carp > param */ > #define SIOCGVH _IOWR('i', 246, struct ifreq) /* get carp > param */ > > > > -- > I'm not entirely sure you are real. >