From: Roy Marples Date: Mon, 2 Jun 2014 14:48:33 +0000 (+0000) Subject: Implement Stable Private Addresses for SLAAC as per RFC7217. X-Git-Tag: v6.4.0~34 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3ed12ab824919fda7cfae98b6779841e8651984e;p=thirdparty%2Fdhcpcd.git Implement Stable Private Addresses for SLAAC as per RFC7217. Add a SHA256 implementation by Collin Percival if one in libc/libmd not found. --- diff --git a/Makefile b/Makefile index b68ec718..2eb46b2b 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ VPATH= . ./crypt SRCS+= auth.c CPPFLAGS+= -I./crypt -CRYPT_SRCS= hmac_md5.c ${MD5_SRC} +CRYPT_SRCS= hmac_md5.c ${MD5_SRC} ${SHA256_SRC} OBJS+= ${SRCS:.c=.o} ${COMPAT_SRCS:.c=.o} ${CRYPT_SRCS:.c=.o} diff --git a/common.h b/common.h index 5a545027..577b96cc 100644 --- a/common.h +++ b/common.h @@ -47,12 +47,13 @@ #define STRINGIFY(a) #a #define TOSTRING(a) STRINGIFY(a) +#define USECINSEC 1000000 #define timeval_to_double(tv) \ ((double)(tv)->tv_sec + (double)((tv)->tv_usec) * 1.0e-6) #define timernorm(tv) do { \ - while ((tv)->tv_usec >= 1000000) { \ + while ((tv)->tv_usec >= USECINSEC) { \ (tv)->tv_sec++; \ - (tv)->tv_usec -= 1000000; \ + (tv)->tv_usec -= USECINSEC; \ } \ } while (0 /* CONSTCOND */); #define tv_to_ms(ms, tv) do { \ diff --git a/configure b/configure index 9f2a0d6f..2d49f86d 100755 --- a/configure +++ b/configure @@ -68,6 +68,8 @@ for x do --without-pollts) POLLTS=no;; --with-pollts) POLLTS=$var;; --without-md5) MD5=no;; + --without-sha2) SHA2=no;; + --without-sha256) SHA2=no;; --without-dev) DEV=no;; --without-udev) UDEV=no;; --serviceexists) SERVICEEXISTS=$var;; @@ -783,6 +785,57 @@ else [ -n "$MD5_LIB" ] && echo "LDADD+= $MD5_LIB" >>$CONFIG_MK fi +if [ -z "$SHA2_H" -a -z "$SHA2" -o "$SHA2" != no ]; then + printf "Testing for sha2.h ... " + if [ -e /usr/include/sha2.h ]; then + SHA2_H=sha2.h + elif [ -e /usr/inclde/sha256.h ]; then + SHA2_H=sha256.h + fi + if [ -n "$SHA2_H" ]; then + echo "$SHA2_H" + else + echo "no" + fi +fi + +if [ -z "$SHA2" ]; then + SHA2_LIB= + printf "Testing for SHA256_Init ... " + cat <_sha256.c +#include +EOF + [ -n "$SHA2_H" ] && echo "#include <$SHA2_H>">>_sha256.c + cat <>_sha256.c +#include +#include +int main(void) { + SHA256_CTX context; + SHA256_Init(&context); + return 0; +} +EOF + # We only want to link to libmd if it exists in /lib + set -- $(ls /lib/libmd.so.* 2>/dev/null) + if $XCC _sha256.c -o _sha256 2>/dev/null; then + SHA2=yes + elif [ -e "$1" ] && $XCC _sha256.c -lmd -o _sha256 2>/dev/null; then + SHA2="yes (-lmd)" + SHA2_LIB=-lmd + else + MD5=no + fi + echo "$SHA2" + rm -f _sha256.c _sha256 +fi +if [ "$SHA2" = no ]; then + echo "SHA256_SRC= sha256.c" >>$CONFIG_MK +else + echo "SHA256_SRC=" >>$CONFIG_MK + echo "#define SHA2_H <$SHA2_H>" >>$CONFIG_H + [ -n "$SHA2_LIB" ] && echo "LDADD+= $SHA2_LIB" >>$CONFIG_MK +fi + if [ "$DEV" != no -a "$UDEV" != no ]; then printf "Checking for libudev ... " LIBUDEV_CFLAGS=$(pkg-config --cflags libudev 2>/dev/null) diff --git a/crypt/sha256.c b/crypt/sha256.c new file mode 100755 index 00000000..24faa38d --- /dev/null +++ b/crypt/sha256.c @@ -0,0 +1,294 @@ +/*- + * Copyright 2005 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include + +#include "sha256.h" + +#if BYTE_ORDER == BIG_ENDIAN + +/* Copy a vector of big-endian uint32_t into a vector of bytes */ +#define be32enc_vect(dst, src, len) \ + memcpy((void *)dst, (const void *)src, (size_t)len) + +/* Copy a vector of bytes into a vector of big-endian uint32_t */ +#define be32dec_vect(dst, src, len) \ + memcpy((void *)dst, (const void *)src, (size_t)len) + +#else /* BYTE_ORDER != BIG_ENDIAN */ + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (unsigned char) in big-endian form. Assumes len is a multiple of 4. + */ +static void +be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + be32enc(dst + i * 4, src[i]); +} + +/* + * Decode a big-endian length len vector of (unsigned char) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +static void +be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + dst[i] = be32dec(src + i * 4); +} + +#endif /* BYTE_ORDER != BIG_ENDIAN */ + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +/* SHA256 round function */ +#define RND(a, b, c, d, e, f, g, h, k) \ + t0 = h + S1(e) + Ch(e, f, g) + k; \ + t1 = S0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +/* Adjusted round function for rotating state */ +#define RNDr(S, W, i, k) \ + RND(S[(64 - i) % 8], S[(65 - i) % 8], \ + S[(66 - i) % 8], S[(67 - i) % 8], \ + S[(68 - i) % 8], S[(69 - i) % 8], \ + S[(70 - i) % 8], S[(71 - i) % 8], \ + W[i] + k) + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void +SHA256_Transform(uint32_t * state, const unsigned char block[64]) +{ + uint32_t W[64]; + uint32_t S[8]; + uint32_t t0, t1; + int i; + + /* 1. Prepare message schedule W. */ + be32dec_vect(W, block, 64); + for (i = 16; i < 64; i++) + W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + RNDr(S, W, 0, 0x428a2f98); + RNDr(S, W, 1, 0x71374491); + RNDr(S, W, 2, 0xb5c0fbcf); + RNDr(S, W, 3, 0xe9b5dba5); + RNDr(S, W, 4, 0x3956c25b); + RNDr(S, W, 5, 0x59f111f1); + RNDr(S, W, 6, 0x923f82a4); + RNDr(S, W, 7, 0xab1c5ed5); + RNDr(S, W, 8, 0xd807aa98); + RNDr(S, W, 9, 0x12835b01); + RNDr(S, W, 10, 0x243185be); + RNDr(S, W, 11, 0x550c7dc3); + RNDr(S, W, 12, 0x72be5d74); + RNDr(S, W, 13, 0x80deb1fe); + RNDr(S, W, 14, 0x9bdc06a7); + RNDr(S, W, 15, 0xc19bf174); + RNDr(S, W, 16, 0xe49b69c1); + RNDr(S, W, 17, 0xefbe4786); + RNDr(S, W, 18, 0x0fc19dc6); + RNDr(S, W, 19, 0x240ca1cc); + RNDr(S, W, 20, 0x2de92c6f); + RNDr(S, W, 21, 0x4a7484aa); + RNDr(S, W, 22, 0x5cb0a9dc); + RNDr(S, W, 23, 0x76f988da); + RNDr(S, W, 24, 0x983e5152); + RNDr(S, W, 25, 0xa831c66d); + RNDr(S, W, 26, 0xb00327c8); + RNDr(S, W, 27, 0xbf597fc7); + RNDr(S, W, 28, 0xc6e00bf3); + RNDr(S, W, 29, 0xd5a79147); + RNDr(S, W, 30, 0x06ca6351); + RNDr(S, W, 31, 0x14292967); + RNDr(S, W, 32, 0x27b70a85); + RNDr(S, W, 33, 0x2e1b2138); + RNDr(S, W, 34, 0x4d2c6dfc); + RNDr(S, W, 35, 0x53380d13); + RNDr(S, W, 36, 0x650a7354); + RNDr(S, W, 37, 0x766a0abb); + RNDr(S, W, 38, 0x81c2c92e); + RNDr(S, W, 39, 0x92722c85); + RNDr(S, W, 40, 0xa2bfe8a1); + RNDr(S, W, 41, 0xa81a664b); + RNDr(S, W, 42, 0xc24b8b70); + RNDr(S, W, 43, 0xc76c51a3); + RNDr(S, W, 44, 0xd192e819); + RNDr(S, W, 45, 0xd6990624); + RNDr(S, W, 46, 0xf40e3585); + RNDr(S, W, 47, 0x106aa070); + RNDr(S, W, 48, 0x19a4c116); + RNDr(S, W, 49, 0x1e376c08); + RNDr(S, W, 50, 0x2748774c); + RNDr(S, W, 51, 0x34b0bcb5); + RNDr(S, W, 52, 0x391c0cb3); + RNDr(S, W, 53, 0x4ed8aa4a); + RNDr(S, W, 54, 0x5b9cca4f); + RNDr(S, W, 55, 0x682e6ff3); + RNDr(S, W, 56, 0x748f82ee); + RNDr(S, W, 57, 0x78a5636f); + RNDr(S, W, 58, 0x84c87814); + RNDr(S, W, 59, 0x8cc70208); + RNDr(S, W, 60, 0x90befffa); + RNDr(S, W, 61, 0xa4506ceb); + RNDr(S, W, 62, 0xbef9a3f7); + RNDr(S, W, 63, 0xc67178f2); + + /* 4. Mix local working variables into global state */ + for (i = 0; i < 8; i++) + state[i] += S[i]; +} + +static unsigned char PAD[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Add padding and terminating bit-count. */ +static void +SHA256_Pad(SHA256_CTX * ctx) +{ + unsigned char len[8]; + uint32_t r, plen; + + /* + * Convert length to a vector of bytes -- we do this now rather + * than later because the length will change after we pad. + */ + be64enc(len, ctx->count); + + /* Add 1--64 bytes so that the resulting length is 56 mod 64 */ + r = (ctx->count >> 3) & 0x3f; + plen = (r < 56) ? (56 - r) : (120 - r); + SHA256_Update(ctx, PAD, (size_t)plen); + + /* Add the terminating bit-count */ + SHA256_Update(ctx, len, 8); +} + +/* SHA-256 initialization. Begins a SHA-256 operation. */ +void +SHA256_Init(SHA256_CTX * ctx) +{ + + /* Zero bits processed so far */ + ctx->count = 0; + + /* Magic initialization constants */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +/* Add bytes into the hash */ +void +SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len) +{ + uint64_t bitlen; + uint32_t r; + const unsigned char *src = in; + + /* Number of bytes left in the buffer from previous updates */ + r = (ctx->count >> 3) & 0x3f; + + /* Convert the length into a number of bits */ + bitlen = len << 3; + + /* Update number of bits */ + ctx->count += bitlen; + + /* Handle the case where we don't need to perform any transforms */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], src, len); + return; + } + + /* Finish the current block */ + memcpy(&ctx->buf[r], src, 64 - r); + SHA256_Transform(ctx->state, ctx->buf); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks */ + while (len >= 64) { + SHA256_Transform(ctx->state, src); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer */ + memcpy(ctx->buf, src, len); +} + +/* + * SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + */ +void +SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + + /* Add padding */ + SHA256_Pad(ctx); + + /* Write the hash */ + be32enc_vect(digest, ctx->state, 32); + + /* Clear the context state */ + memset((void *)ctx, 0, sizeof(*ctx)); +} diff --git a/crypt/sha256.h b/crypt/sha256.h new file mode 100755 index 00000000..34025e3d --- /dev/null +++ b/crypt/sha256.h @@ -0,0 +1,50 @@ +/*- + * Copyright 2005 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef SHA256_H_ +#define SHA256_H_ + +#include + +#define SHA256_DIGEST_LENGTH 32 + +typedef struct SHA256Context { + uint32_t state[8]; + uint64_t count; + unsigned char buf[64]; +} SHA256_CTX; + +void SHA256_Init(SHA256_CTX *); +void SHA256_Update(SHA256_CTX *, const void *, size_t); +void SHA256_Final(unsigned char [32], SHA256_CTX *); +char *SHA256_End(SHA256_CTX *, char *); +char *SHA256_File(const char *, char *); +char *SHA256_FileChunk(const char *, char *, off_t, off_t); +char *SHA256_Data(const void *, unsigned int, char *); + +#endif diff --git a/defs.h b/defs.h index e9de7fa9..8e69bbd6 100644 --- a/defs.h +++ b/defs.h @@ -42,6 +42,9 @@ #ifndef DUID # define DUID SYSCONFDIR "/" PACKAGE ".duid" #endif +#ifndef SECRET +# define SECRET SYSCONFDIR "/" PACKAGE ".secret" +#endif #ifndef LEASEFILE # define LEASEFILE DBDIR "/" PACKAGE "-%s.lease" #endif diff --git a/dhcpcd.8.in b/dhcpcd.8.in index fcd475ad..29285f1b 100644 --- a/dhcpcd.8.in +++ b/dhcpcd.8.in @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 29, 2014 +.Dd June 2, 2014 .Dt DHCPCD 8 .Os .Sh NAME @@ -112,7 +112,8 @@ and .Li RFC 6106 . .Nm can optionally handle address and route management itself, -and will do so by default if Router Solicitation is disabled in the kernel. +and will do so by default if Router Solicitation is disabled in the kernel +or if stable private addresses are enabled in the configuration. If .Nm is managing routes, @@ -630,6 +631,8 @@ Configuration file for dhcpcd. If you always use the same options, put them here. .It Pa @SYSCONFDIR@/dhcpcd.duid Text file that holds the DUID used to identify the host. +.It Pa @SYSCONFDIR@/dhcpcd.secret +Text file that holds a secret key known only to the host. .It Pa @SCRIPT@ Bourne shell script that is run to configure or de-configure an interface. .It Pa @LIBDIR@/dhcpcd/dev @@ -678,7 +681,7 @@ RFC\ 951, RFC\ 1534, RFC\ 2104, RFC\ 2131, RFC\ 2132, RFC\ 2563, RFC\ 2855, RFC\ 3004, RFC\ 3118, RFC\ 3203, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396, RFC\ 3397, RFC\ 3442, RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075, RFC\ 4242, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833, -RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106, RFC\ 6334, RFC\ 6704. +RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106, RFC\ 6334, RFC\ 6704, RFC\ 7217. .Sh AUTHORS .An Roy Marples Aq Mt roy@marples.name .Sh BUGS diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in index 327fea3d..e61b771a 100644 --- a/dhcpcd.conf.5.in +++ b/dhcpcd.conf.5.in @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 9, 2014 +.Dd June 2, 2014 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -453,6 +453,9 @@ instead of the default .It Ic ssid Ar ssid Subsequent options are only parsed for this wireless .Ar ssid . +.It Ic stableprivate +Configure stable private IPv6 addresses as per RFC7217 for SLAAC instead of +ones generated from hardware addresses for SLAAC. .It Ic static Ar value Configures a static .Ar value . diff --git a/dhcpcd.h b/dhcpcd.h index 7eadaa3b..105da431 100644 --- a/dhcpcd.h +++ b/dhcpcd.h @@ -36,9 +36,10 @@ #include "control.h" #include "if-options.h" -#define HWADDR_LEN 20 -#define IF_SSIDSIZE 33 -#define PROFILE_LEN 64 +#define HWADDR_LEN 20 +#define IF_SSIDSIZE 33 +#define PROFILE_LEN 64 +#define SECRET_LEN 64 #define LINK_UP 1 #define LINK_UNKNOWN 0 @@ -116,6 +117,9 @@ struct dhcpcd_ctx { uint8_t *opt_buffer; #endif #ifdef INET6 + unsigned char secret[SECRET_LEN]; + size_t secret_len; + struct dhcp_opt *dhcp6_opts; size_t dhcp6_opts_len; struct ipv6_ctx *ipv6; diff --git a/if-options.c b/if-options.c index b7f5c56f..b3f255b0 100644 --- a/if-options.c +++ b/if-options.c @@ -91,6 +91,7 @@ #define O_IPV4 O_BASE + 32 #define O_IPV6 O_BASE + 33 #define O_CONTROLGRP O_BASE + 34 +#define O_STABLEPRIVATE O_BASE + 35 const struct option cf_options[] = { {"background", no_argument, NULL, 'b'}, @@ -175,6 +176,7 @@ const struct option cf_options[] = { {"dhcp6", no_argument, NULL, O_DHCP6}, {"nodhcp6", no_argument, NULL, O_NODHCP6}, {"controlgroup", required_argument, NULL, O_CONTROLGRP}, + {"stableprivate", no_argument, NULL, O_STABLEPRIVATE}, {NULL, 0, NULL, '\0'} }; @@ -1835,6 +1837,11 @@ err_sla: ctx->control_group = grp->gr_gid; #endif break; + case O_STABLEPRIVATE: + ifo->options |= DHCPCD_STABLEPRIVATE; + /* This option implies that we steal SLAAC from the kernel */ + ifo->options |= DHCPCD_IPV6RA_OWN; + break; default: return 0; } diff --git a/if-options.h b/if-options.h index abab3f2e..4288479b 100644 --- a/if-options.h +++ b/if-options.h @@ -102,6 +102,7 @@ #define DHCPCD_IAID (1ULL << 48) #define DHCPCD_DHCP (1ULL << 49) #define DHCPCD_DHCP6 (1ULL << 50) +#define DHCPCD_STABLEPRIVATE (1ULL << 51) extern const struct option cf_options[]; diff --git a/ipv6.c b/ipv6.c index 191e263b..a09788dc 100644 --- a/ipv6.c +++ b/ipv6.c @@ -73,6 +73,16 @@ #include "ipv6.h" #include "ipv6nd.h" +#ifdef SHA2_H +# include SHA2_H +#else +# include "sha256.h" +#endif + +#ifndef SHA256_DIGEST_LENGTH +# define SHA256_DIGEST_LENGTH 32 +#endif + #ifdef IPV6_POLLADDRFLAG # warning kernel does not report IPv6 address flag changes # warning polling tentative address flags periodically instead @@ -181,13 +191,149 @@ ipv6_printaddr(char *s, size_t sl, const uint8_t *d, const char *ifname) return (ssize_t)l; } +static ssize_t +ipv6_readsecret(struct dhcpcd_ctx *ctx) +{ + FILE *fp; + char line[1024]; + unsigned char *p; + size_t len; + uint32_t r; + int x; + + if ((fp = fopen(SECRET, "r"))) { + while (fgets(line, sizeof(line), fp)) { + len = strlen(line); + if (len) { + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + } + len = hwaddr_aton(NULL, line); + if (len) { + ctx->secret_len = hwaddr_aton(ctx->secret, + line); + break; + } + len = 0; + } + fclose(fp); + if (len) + return (ssize_t)len; + } else { + if (errno != ENOENT) + syslog(LOG_ERR, "error reading secret: %s: %m", SECRET); + } + + /* Chaining arc4random should be good enough. + * RFC7217 section 5.1 states the key SHOULD be at least 128 bits. + * To attempt and future proof ourselves, we'll generate a key of + * 512 bits (64 bytes). */ + p = ctx->secret; + ctx->secret_len = 0; + for (len = 0; len < 512 / NBBY; len += sizeof(r)) { + r = arc4random(); + memcpy(p, &r, sizeof(r)); + p += sizeof(r); + ctx->secret_len += sizeof(r); + + } + + if (!(fp = fopen(SECRET, "w"))) + goto eexit; + x = fprintf(fp, "%s\n", + hwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line))); + fclose(fp); + if (x > 0) + return (ssize_t)ctx->secret_len; + +eexit: + syslog(LOG_ERR, "error writing secret: %s: %m", SECRET); + unlink(SECRET); + ctx->secret_len = 0; + return -1; +} + +/* RFC7217 */ +int +ipv6_makestableprivate(struct in6_addr *addr, + const struct in6_addr *prefix, int prefix_len, + const unsigned char *netiface, size_t netiface_len, + const char *netid, size_t netid_len, + uint32_t dad_counter, + const unsigned char *secret, size_t secret_len) +{ + unsigned char buf[2048], *p, digest[SHA256_DIGEST_LENGTH]; + size_t len, l; + SHA256_CTX ctx; + + if (prefix_len < 0 || prefix_len > 120) { + errno = EINVAL; + return -1; + } + + l = (size_t)(ROUNDUP8(prefix_len) / NBBY); + len = l + netiface_len + netid_len + sizeof(dad_counter) + secret_len; + if (len > sizeof(buf)) { + errno = ENOBUFS; + return -1; + } + + /* Combine all parameters into one buffer */ + p = buf; + memcpy(p, prefix, l); + p += l; + memcpy(p, netiface, netiface_len); + p += netiface_len; + memcpy(p, netid, netid_len); + p += netid_len; + memcpy(p, &dad_counter, sizeof(dad_counter)); + p += sizeof(dad_counter); + memcpy(p, secret, secret_len); + + /* Make an address using the prefix and the digest of the above. + * RFC7217 Section 5.1 states that we shouldn't use MD5. + * Pity as we use that for HMAC-MD5 which is still deemed OK. + * SHA-256 is recommended */ + SHA256_Init(&ctx); + SHA256_Update(&ctx, buf, len); + SHA256_Final(digest, &ctx); + + p = addr->s6_addr; + memcpy(p, prefix, l); + /* RFC7217 section 5.2 says we need to start taking the id from + * the least significant bit */ + len = sizeof(addr->s6_addr) - l; + memcpy(p + l, digest + (sizeof(digest) - len), len); + + return 0; +} + int ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp, const struct in6_addr *prefix, int prefix_len) { const struct ipv6_addr *ap; - if (prefix_len < 0 || prefix_len > 64) { + if (prefix_len < 0 || prefix_len > 120) { + errno = EINVAL; + return -1; + } + + if (ifp->options->options & DHCPCD_STABLEPRIVATE) { + if (ifp->ctx->secret_len == 0) { + if (ipv6_readsecret(ifp->ctx) == -1) + return -1; + } + if (ipv6_makestableprivate(addr, prefix, prefix_len, + ifp->options->iaid, sizeof(ifp->options->iaid), + ifp->ssid, strlen(ifp->ssid), + 0, /* DAD counter starts at 0 */ + ifp->ctx->secret, ifp->ctx->secret_len) == -1) + return -1; + return 0; + } + + if (prefix_len > 64) { errno = EINVAL; return -1; } diff --git a/ipv6.h b/ipv6.h index 3c234557..de9fc42b 100644 --- a/ipv6.h +++ b/ipv6.h @@ -49,6 +49,10 @@ # define ND6_INFINITE_LIFETIME ((uint32_t)~0) #endif +/* RFC7217 constants */ +#define IDGEN_RETRIES 3 +#define IDGEN_DELAY 1 /* second */ + /* * BSD kernels don't inform userland of DAD results. * See the discussion here: @@ -84,6 +88,7 @@ struct ipv6_addr { struct interface *delegating_iface; void (*dadcallback)(void *); + uint32_t dadcounter; uint8_t *ns; size_t nslen; int nsprobes; @@ -160,6 +165,12 @@ struct ipv6_ctx { #ifdef INET6 struct ipv6_ctx *ipv6_init(struct dhcpcd_ctx *); ssize_t ipv6_printaddr(char *, size_t, const uint8_t *, const char *); +int ipv6_makestableprivate(struct in6_addr *addr, + const struct in6_addr *prefix, int prefix_len, + const unsigned char *netiface, size_t netiface_len, + const char *netid, size_t netid_len, + uint32_t dad_counter, + const unsigned char *secret, size_t secret_len); int ipv6_makeaddr(struct in6_addr *, const struct interface *, const struct in6_addr *, int); int ipv6_makeprefix(struct in6_addr *, const struct in6_addr *, int); diff --git a/ipv6nd.c b/ipv6nd.c index a277889e..0e6f7a95 100644 --- a/ipv6nd.c +++ b/ipv6nd.c @@ -571,6 +571,14 @@ ipv6nd_scriptrun(struct ra *rap) return pid; } +static void +ipv6nd_addaddr(void *arg) +{ + struct ipv6_addr *ap = arg; + + ipv6_addaddr(ap); +} + static void ipv6nd_dadcallback(void *arg) { @@ -578,17 +586,64 @@ ipv6nd_dadcallback(void *arg) struct interface *ifp; struct ra *rap; int wascompleted, found; + struct timeval tv; + char buf[INET6_ADDRSTRLEN]; + const char *p; + ifp = ap->iface; wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED); ap->flags |= IPV6_AF_DADCOMPLETED; - if (ap->flags & IPV6_AF_DUPLICATED) - /* No idea what how to try and make another address :( */ + if (ap->flags & IPV6_AF_DUPLICATED) { + ap->dadcounter++; syslog(LOG_WARNING, "%s: DAD detected %s", ap->iface->name, ap->saddr); - if (!wascompleted) { - ifp = ap->iface; + /* Try and make another stable private address. + * Because ap->dadcounter is always increamented, + * a different address is generated. */ + /* XXX Cache DAD counter per prefix/id/ssid? */ + if (ifp->options->options & DHCPCD_STABLEPRIVATE && + ap->dadcounter < IDGEN_RETRIES) + { + syslog(LOG_INFO, "%s: deleting address %s", + ifp->name, ap->saddr); + if (if_deladdress6(ap) == -1 && + errno != EADDRNOTAVAIL && errno != ENXIO) + syslog(LOG_ERR, "if_deladdress6: %m"); + if (ipv6_makestableprivate(&ap->addr, + &ap->prefix, ap->prefix_len, + ifp->options->iaid, sizeof(ifp->options->iaid), + ifp->ssid, strlen(ifp->ssid), + ap->dadcounter, + ifp->ctx->secret, ifp->ctx->secret_len) == -1) + { + syslog(LOG_ERR, + "%s: ipv6_makestableprivate: %m", + ifp->name); + return; + } + ap->flags &= ~(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED); + ap->flags |= IPV6_AF_NEW; + p = inet_ntop(AF_INET6, ap->addr.s6_addr, + buf, sizeof(buf)); + if (p) + snprintf(ap->saddr, + sizeof(ap->saddr), + "%s/%d", + p, ap->prefix_len); + else + ap->saddr[0] = '\0'; + tv.tv_sec = 0; + tv.tv_usec = (suseconds_t)arc4random_uniform( + IDGEN_DELAY * USECINSEC); + timernorm(&tv); + eloop_timeout_add_tv(ifp->ctx->eloop, &tv, + ipv6nd_addaddr, ap); + return; + } + } + if (!wascompleted) { TAILQ_FOREACH(rap, ifp->ctx->ipv6->ra_routers, next) { if (rap->iface != ifp) continue;