]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Implement Stable Private Addresses for SLAAC as per RFC7217.
authorRoy Marples <roy@marples.name>
Mon, 2 Jun 2014 14:48:33 +0000 (14:48 +0000)
committerRoy Marples <roy@marples.name>
Mon, 2 Jun 2014 14:48:33 +0000 (14:48 +0000)
Add a SHA256 implementation by Collin Percival if one in libc/libmd not found.

14 files changed:
Makefile
common.h
configure
crypt/sha256.c [new file with mode: 0755]
crypt/sha256.h [new file with mode: 0755]
defs.h
dhcpcd.8.in
dhcpcd.conf.5.in
dhcpcd.h
if-options.c
if-options.h
ipv6.c
ipv6.h
ipv6nd.c

index b68ec718588ba11c6da28ac345538ef41225c986..2eb46b2b5b00e93d3cb4443e461f0fb8498f39fe 100644 (file)
--- 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}
 
index 5a5450275334ccc0fdee2197248e07ff2e9b845e..577b96cc97898d132cace85a7d02bdc0f7ec8eb2 100644 (file)
--- a/common.h
+++ b/common.h
 #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 {                                          \
index 9f2a0d6fb123f5c92d4ac04a52f2385c42c628f9..2d49f86d3a5550c912532d3019e35745c9966cd8 100755 (executable)
--- 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 <<EOF >_sha256.c
+#include <sys/types.h>
+EOF
+       [ -n "$SHA2_H" ] && echo "#include <$SHA2_H>">>_sha256.c
+       cat <<EOF >>_sha256.c
+#include <sha2.h>
+#include <stdlib.h>
+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 (executable)
index 0000000..24faa38
--- /dev/null
@@ -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 <sys/endian.h>
+#include <sys/types.h>
+
+#include <string.h>
+
+#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 (executable)
index 0000000..34025e3
--- /dev/null
@@ -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 <sys/types.h>
+
+#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 e9de7fa9fe1831d34199ad70d307e7a1e6dd96bb..8e69bbd643bdb1a1c628a3aa26f174229ea69910 100644 (file)
--- 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
index fcd475ad4bb93d8b8bfe9141196a825672c45d8a..29285f1ba94521609ba4b7e5e00ddd533d81c289 100644 (file)
@@ -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
index 327fea3d988728c43c92847e1f993882699124b2..e61b771ace36b1f173cae62a7fcdbdeac9468590 100644 (file)
@@ -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 .
index 7eadaa3b22d456aaba7d09be75c030bdbef95fe6..105da431c1307c2e792f2cc7c8058d597033166f 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
 #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;
index b7f5c56f33112a29296f1f8780685248a52a9921..b3f255b02d4e232f7c050032e5e044a4a4148471 100644 (file)
@@ -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;
        }
index abab3f2ec3a442d1834328695722d2aa4daef405..4288479bd7c62c3b51a4fbab4b0101bf845d0fb2 100644 (file)
 #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 191e263bf8831e60e837ec3084da87d064a1f9bb..a09788dc127750b90cec0ccf3018ad872bffdbce 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
 #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 3c234557ce6a17fcba36266106011bf8f398793e..de9fc42b5cdd7880386110d6683a33f36cdb10bf 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
 #  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);
index a277889e4d1268eeeaf77b9a25278cc4f7972627..0e6f7a95cac67548b8342b9f4504272cc06de0c2 100644 (file)
--- 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;