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. 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) 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? 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 +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.