diff --git a/.gitignore b/.gitignore index bc1d959..4fb8602 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ debian/substvars debian/utils-substvars debian/trees/ debian/build/ +ID +tags +*~ diff --git a/Makefile b/Makefile index 3fb7cbe..f762d94 100644 --- a/Makefile +++ b/Makefile @@ -24,11 +24,14 @@ MANDIR = $(PREFIX)/share/man LOCALEDIR = $(PREFIX)/share/locale BUILDDIR = $(SRC) DESTDIR = -CFLAGS = -Wall -W -O2 -LDFLAGS = +CFLAGS = -Wall -W -O3 -march=native -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC -fstack-protector-strong -fstack-clash-protection -fcf-protection=full --param=ssp-buffer-size=4 -flto +#ASANFLAGS = +#ASANFLAGS = -fsanitize=address -fsanitize=bounds-strict -fsanitize=object-size -fsanitize=builtin +#CFLAGS = -Wall -W -O0 -march=native -gdwarf-4 -fno-omit-frame-pointer -fPIC ${ASANFLAGS} +LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now -flto ${ASANFLAGS} COPTS = RPM_OPT_FLAGS = -LIBS = +LIBS = `pkg-config --libs libsystemd` ################################################################# @@ -72,12 +75,12 @@ sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | sum!=$(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ' copts_conf = .copts_$(sum) -objs = cache.o rfc1035.o util.o option.o forward.o network.o \ +objs = cache.o rfc1035.o util.o charcrandom.o option.o forward.o network.o \ dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \ helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \ - poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o metrics.o + poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o metrics.o siphash.o hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ dns-protocol.h radv-protocol.h ip6addr.h metrics.h diff --git a/src/cache.c b/src/cache.c index 44c13e4..2ff84e3 100644 --- a/src/cache.c +++ b/src/cache.c @@ -158,22 +158,17 @@ static void rehash(int size) free(old); } } - + +siphash_key_t sipkey; + static struct crec **hash_bucket(char *name) { - unsigned int c, val = 017465; /* Barker code - minimum self-correlation in cyclic shift */ - const unsigned char *mix_tab = (const unsigned char*)typestr; + char *end = name; - while((c = (unsigned char) *name++)) - { - /* don't use tolower and friends here - they may be messed up by LOCALE */ - if (c >= 'A' && c <= 'Z') - c += 'a' - 'A'; - val = ((val << 7) | (val >> (32 - 7))) + (mix_tab[(val + c) & 0x3F] ^ c); - } - - /* hash_size is a power of two */ - return hash_table + ((val ^ (val >> 16)) & (hash_size - 1)); + while (*end++) + ; + end--; + return hash_table + (siphash_case(&sipkey, (const unsigned char*)name, end-name) & (hash_size - 1)); } static void cache_hash(struct crec *crecp) diff --git a/src/charcrandom-chacha.h b/src/charcrandom-chacha.h new file mode 100644 index 0000000..1ea5ac2 --- /dev/null +++ b/src/charcrandom-chacha.h @@ -0,0 +1,179 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */ + +#include +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; + +typedef struct +{ + u32 input[16]; /* could be compressed */ +} chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define U32TO8_CPU(p, v) memcpy(p, &v, 4) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[] = "expand 32-byte k"; +static const char tau[] = "expand 16-byte k"; + +static void chacha_keysetup(chacha_ctx *x, const u8 *k, size_t kbits) +{ + const char *constants; + + /* little-endian and big-endian random bits are just as good! :-D */ + memcpy(&x->input[4], k, 16); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + memcpy(&x->input[8], k, 16); + memcpy(&x->input[0], constants, 16); +} + +static void chacha_ivsetup(chacha_ctx *x, const u8 *iv) +{ + x->input[12] = 0; + x->input[13] = 0; + memcpy(&x->input[14], iv, 8); +} + +static void chacha_encrypt_bytes(chacha_ctx *x, const u8 *m, u8 *c, size_t bytes) +{ + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = NULL; + u8 tmp[64]; + unsigned int i; + + if (!bytes) return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS(x0,j0); + x1 = PLUS(x1,j1); + x2 = PLUS(x2,j2); + x3 = PLUS(x3,j3); + x4 = PLUS(x4,j4); + x5 = PLUS(x5,j5); + x6 = PLUS(x6,j6); + x7 = PLUS(x7,j7); + x8 = PLUS(x8,j8); + x9 = PLUS(x9,j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_CPU(c + 0,x0); + U32TO8_CPU(c + 4,x1); + U32TO8_CPU(c + 8,x2); + U32TO8_CPU(c + 12,x3); + U32TO8_CPU(c + 16,x4); + U32TO8_CPU(c + 20,x5); + U32TO8_CPU(c + 24,x6); + U32TO8_CPU(c + 28,x7); + U32TO8_CPU(c + 32,x8); + U32TO8_CPU(c + 36,x9); + U32TO8_CPU(c + 40,x10); + U32TO8_CPU(c + 44,x11); + U32TO8_CPU(c + 48,x12); + U32TO8_CPU(c + 52,x13); + U32TO8_CPU(c + 56,x14); + U32TO8_CPU(c + 60,x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; + } +} diff --git a/src/charcrandom.c b/src/charcrandom.c new file mode 100644 index 0000000..fa87b98 --- /dev/null +++ b/src/charcrandom.c @@ -0,0 +1,268 @@ +/* OPENBSD ORIGINAL: lib/libc/crypto/arc4random.c */ + +/* $OpenBSD: arc4random.c,v 1.51 2015/01/15 06:57:18 deraadt Exp $ */ + +/* + * Copyright (c) 1996, David Mazieres + * Copyright (c) 2008, Damien Miller + * Copyright (c) 2013, Markus Friedl + * Copyright (c) 2014, Theo de Raadt + * Copyright (c) 2015, Sami Farin + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * ChaCha based random number generator for OpenBSD. + * Sami Farin edition Thread-safe without locking. + */ + +#include +#include +#include +#include +#include +#include /* For SYS_xxx definitions */ +#include +#include +#include + +#include "charcrandom.h" +#include "charcrandom-chacha.h" + +#ifdef __GNUC__ +#define inline __inline +#else /* !__GNUC__ */ +#define inline +#endif /* !__GNUC__ */ + +#define KEYSZ ((size_t)32) +#define IVSZ ((size_t)8) +#define BLOCKSZ 64 +#define RSBUFSZ (16*BLOCKSZ) + +#define min(x,y) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) + +#define max(x,y) ({ \ + typeof(x) _max1 = (x); \ + typeof(y) _max2 = (y); \ + (void) (&_max1 == &_max2); \ + _max1 > _max2 ? _max1 : _max2; }) + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +typedef struct { + bool rs_initialized; + pid_t rs_stir_pid; + chacha_ctx rs; /* chacha context for random keystream */ + uint8_t rs_buf[RSBUFSZ]; /* keystream blocks */ + size_t rs_have; /* valid bytes at end of rs_buf */ + size_t rs_count; /* bytes till reseed */ +} charc_state; +static __thread charc_state charc __attribute__((aligned(128))); + +static inline void _rs_rekey(uint8_t *dat, size_t datlen); + +static inline void _rs_init(uint8_t *buf, size_t n) +{ + if (n < KEYSZ + IVSZ) + return; + chacha_keysetup(&charc.rs, buf, KEYSZ * 8); + chacha_ivsetup(&charc.rs, buf + KEYSZ); +} + +static bool _rs_random_bytes(void *p, size_t n) +{ + static FILE *frandom; + long ret; + + if (!n) return true; + +#if defined(SYS_getrandom) && defined(__linux__) +//#warning getrandom supported + do { + /* <=256 byte requests always succeed */ + ret = syscall(SYS_getrandom, p, n, 0, 0, 0, 0); + } while ((ret == -1) && (errno == EINTR)); + if ((size_t)ret == n) return true; +#endif + if (frandom == NULL) { + frandom = fopen("/dev/urandom", "rb"); + if (frandom == NULL) { + return false; + } + setbuf(frandom, NULL); + } + if (fread(p, 1, n, frandom) != n) { + fclose(frandom); + frandom = NULL; + return false; + } + return true; +} + +static void _rs_stir(void) +{ + uint8_t rnd[KEYSZ + IVSZ]; + + if (_rs_random_bytes(rnd, sizeof(rnd)) == false) { + fprintf(stderr, "Couldn't obtain random bytes\n"); + _exit(1); + } + + if (!charc.rs_initialized) { + charc.rs_initialized = 1; + _rs_init(rnd, sizeof(rnd)); + } else { + _rs_rekey(rnd, sizeof(rnd)); + } + secure_memset(rnd, sizeof(rnd)); + + /* invalidate rs_buf */ + charc.rs_have = 0; + memset(charc.rs_buf, 0, RSBUFSZ); + + charc.rs_count = 1600000; +} + +static inline void _rs_stir_if_needed(size_t len) +{ + pid_t pid = getpid(); /* maybe user does fork() without immediately doing exec() */ + + if (unlikely(charc.rs_count <= len || !charc.rs_initialized || charc.rs_stir_pid != pid)) { + charc.rs_stir_pid = pid; + _rs_stir(); + } + if (charc.rs_count <= len) { + charc.rs_count = 0; + } else { + charc.rs_count -= len; + } +} + +static inline void _rs_rekey(uint8_t *dat, size_t datlen) +{ + /* fill rs_buf with the keystream */ + chacha_encrypt_bytes(&charc.rs, charc.rs_buf, charc.rs_buf, RSBUFSZ); + /* mix in optional user provided data */ + if (dat) { + size_t i, m; + + m = min(datlen, KEYSZ + IVSZ); + for (i = 0; i < m; i++) + charc.rs_buf[i] ^= dat[i]; + } + /* immediately reinit for backtracking resistance */ + _rs_init(charc.rs_buf, KEYSZ + IVSZ); + memset(charc.rs_buf, 0, KEYSZ + IVSZ); + charc.rs_have = RSBUFSZ - KEYSZ - IVSZ; +} + +static void _rs_random_buf(void *_buf, size_t n) +{ + uint8_t *buf = (uint8_t*)_buf; + uint8_t *keystream; + size_t m; + + _rs_stir_if_needed(n); + while (n > 0) { + if (likely(charc.rs_have > 0)) { + m = min(n, charc.rs_have); + keystream = charc.rs_buf + RSBUFSZ - charc.rs_have; + memcpy(buf, keystream, m); + memset(keystream, 0, m); + buf += m; + n -= m; + charc.rs_have -= m; + } + if (charc.rs_have == 0) + _rs_rekey(NULL, 0); + } +} + +/* API functions start */ +uint16_t rand16(void) +{ + uint16_t res; + + _rs_random_buf(&res, sizeof(res)); + return res; +} + +uint32_t rand32(void) +{ + uint32_t res; + + _rs_random_buf(&res, sizeof(res)); + return res; +} + +uint64_t rand64(void) +{ + uint64_t res; + + _rs_random_buf(&res, sizeof(res)); + return res; +} + +void random_seed(uint8_t *dat, size_t datlen) +{ + size_t m; + + if (!charc.rs_initialized) + _rs_stir(); + while (datlen > 0) { + m = min(datlen, KEYSZ + IVSZ); + _rs_rekey(dat, m); + dat += m; + datlen -= m; + } +} + +void random_buf(void *buf, size_t n) +{ + _rs_random_buf(buf, n); +} + +/* + * Calculate a uniformly distributed random number less than upper_bound + * avoiding "modulo bias". + * + * Uniformity is achieved by generating new random numbers until the one + * returned is outside the range [0, 2**64 % upper_bound). This + * guarantees the selected random number will be inside + * [2**64 % upper_bound, 2**64) which maps back to [0, upper_bound) + * after reduction modulo upper_bound. + */ +uint64_t rand_uniform64(uint64_t upper_bound) +{ + uint64_t r, mini; + + if (upper_bound < 2) + return 0; + + mini = -upper_bound % upper_bound; + + for (;;) { + r = rand64(); + if (likely(r >= mini)) + break; + } + + return r % upper_bound; +} diff --git a/src/charcrandom.h b/src/charcrandom.h new file mode 100644 index 0000000..b63e1ce --- /dev/null +++ b/src/charcrandom.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#ifdef barrier +# undef barrier +#endif +#define barrier() asm volatile("": : :"memory") + +#if defined(__cplusplus) +extern "C" { +#endif + +extern uint16_t rand16(void); +extern uint32_t rand32(void); +extern uint64_t rand64(void); +extern void random_seed(uint8_t *dat, size_t datlen); +extern void random_buf(void *buf, size_t n); +extern uint64_t rand_uniform64(uint64_t upper_bound); + +static inline void* secure_memset(void* p, size_t n) +{ + void* retp = memset(p, 0, n); + barrier(); + return retp; +} + +#if defined(__cplusplus) +} +#endif + diff --git a/src/config.h b/src/config.h index 203d69e..e73e910 100644 --- a/src/config.h +++ b/src/config.h @@ -182,7 +182,7 @@ RESOLVFILE /* #define HAVE_LUASCRIPT */ /* #define HAVE_DBUS */ /* #define HAVE_IDN */ -/* #define HAVE_LIBIDN2 */ +#define HAVE_LIBIDN2 /* #define HAVE_CONNTRACK */ /* #define HAVE_DNSSEC */ diff --git a/src/dhcp.c b/src/dhcp.c index ee74f06..beb01d2 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -651,7 +651,7 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i This wrapper handles a cache and load-limiting. Return is NULL is address in use, or a pointer to a cache entry recording that it isn't. */ -struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash, int loopback) +struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, u32 hash, int loopback) { static struct ping_result dummy; struct ping_result *r, *victim = NULL; @@ -707,6 +707,8 @@ struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int h } } +siphash_key_t machine_key; /* specific to this machine only (systemd machine-id) */ + int address_allocate(struct dhcp_context *context, struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct dhcp_netid *netids, time_t now, int loopback) @@ -717,18 +719,12 @@ int address_allocate(struct dhcp_context *context, struct in_addr start, addr; struct dhcp_context *c, *d; - int i, pass; - unsigned int j; - - /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good - dispersal even with similarly-valued "strings". */ - for (j = 0, i = 0; i < hw_len; i++) - j = hwaddr[i] + (j << 6) + (j << 16) - j; + int pass; + u32 hwhash, j; + unsigned char perturb[12]; - /* j == 0 is marker */ - if (j == 0) - j = 1; - + hwhash = siphash(&machine_key, hwaddr, hw_len); + for (pass = 0; pass <= 1; pass++) for (c = context; c; c = c->current) if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) @@ -737,13 +733,19 @@ int address_allocate(struct dhcp_context *context, continue; else { - if (option_bool(OPT_CONSEC_ADDR)) + if (option_bool(OPT_CONSEC_ADDR)) { /* seed is largest extant lease addr in this context */ start = lease_find_max_addr(c); - else + } else { /* pick a seed based on hwaddr */ + memcpy(&perturb[0], &(c->start.s_addr), 4); + memcpy(&perturb[4], &(c->end.s_addr), 4); + memcpy(&perturb[8], &hwhash, 4); + j = siphash(&machine_key, perturb, sizeof(perturb)); + if (j == 0) j++; start.s_addr = htonl(ntohl(c->start.s_addr) + - ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr)))); + ((j + c->addr_epoch) % (1U + ntohl(c->end.s_addr) - ntohl(c->start.s_addr)))); + } /* iterate until we find a free address. */ addr = start; diff --git a/src/dhcp6.c b/src/dhcp6.c index 7e6e3df..782c6bb 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -410,18 +410,20 @@ struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned c u64 start, addr; struct dhcp_context *c, *d; - int i, pass; - u64 j; + int pass; + u64 j; + siphash_key_t clid_key; - /* hash hwaddr: use the SDBM hashing algorithm. This works - for MAC addresses, let's see how it manages with client-ids! + /* hash hwaddr: use system-unique seed. For temporary addresses, we generate a new random one each time. */ - if (temp_addr) + if (temp_addr) { j = rand64(); - else - for (j = iaid, i = 0; i < clid_len; i++) - j = clid[i] + (j << 6) + (j << 16) - j; - + } else { + clid_key.key[0] = machine_key.key[0]; + clid_key.key[1] = machine_key.key[1] ^ iaid; + j = siphash(&clid_key, clid, clid_len); + } + for (pass = 0; pass <= plain_range ? 1 : 0; pass++) for (c = context; c; c = c->current) if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED)) diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 4036914..a360964 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -19,6 +19,10 @@ #include "dnsmasq.h" +#include + +#define OUR_APPLICATION_ID SD_ID128_MAKE(c7,1f,bf,4d,50,28,42,83,9e,00,13,b4,65,fd,90,e2) + struct daemon *daemon; static volatile pid_t pid = 0; @@ -62,6 +66,7 @@ int main (int argc, char **argv) #ifdef HAVE_TFTP int tftp_prefix_missing = 0; #endif + sd_id128_t dhcp_id; #ifdef LOCALEDIR setlocale(LC_ALL, ""); @@ -86,7 +91,10 @@ int main (int argc, char **argv) umask(022); /* known umask, create leases and pid files as 0644 */ - rand_init(); /* Must precede read_opts() */ + init_siphash_seed(&sipkey); + sd_id128_get_machine_app_specific(OUR_APPLICATION_ID, &dhcp_id); + machine_key.key[0] = dhcp_id.qwords[0]; + machine_key.key[1] = dhcp_id.qwords[1]; read_opts(argc, argv, compile_opts); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 0dc1de9..8f31797 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -36,6 +36,8 @@ # include #endif +#include + /* Need these defined early */ #if defined(__sun) || defined(__sun__) # define _XPG4_2 @@ -58,17 +60,20 @@ #endif #include +#include /* Also needed before config.h. */ #include #include "config.h" #include "ip6addr.h" +#include "siphash.h" #include "metrics.h" +#include "charcrandom.h" -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; #define countof(x) (long)(sizeof(x) / sizeof(x[0])) #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -932,7 +937,7 @@ struct dhcp_context { struct ping_result { struct in_addr addr; time_t time; - unsigned int hash; + u32 hash; struct ping_result *next; }; @@ -1162,6 +1167,8 @@ char *cache_get_cname_target(struct crec *crecp); struct crec *cache_enumerate(int init); int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz); +extern siphash_key_t sipkey; +extern siphash_key_t machine_key; /* blockdata.c */ void blockdata_init(void); @@ -1240,10 +1247,7 @@ char *algo_digest_name(int algo); char *nsec3_digest_name(int digest); /* util.c */ -void rand_init(void); -unsigned short rand16(void); -u32 rand32(void); -u64 rand64(void); +void init_siphash_seed(siphash_key_t *key); int legal_hostname(char *name); char *canonicalise(char *in, int *nomem); unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit); @@ -1359,7 +1363,7 @@ struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr, struct dhcp_netid *netids); struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, - unsigned int hash, int loopback); + u32 hash, int loopback); int address_allocate(struct dhcp_context *context, struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct dhcp_netid *netids, time_t now, int loopback); diff --git a/src/domain.c b/src/domain.c index 711fe60..839e2d5 100644 --- a/src/domain.c +++ b/src/domain.c @@ -212,7 +212,7 @@ int is_rev_synth(int flag, union all_addr *addr, char *name) if (c->indexed) { u64 index = addr6part(&addr->addr6) - addr6part(&c->start6); - snprintf(name, MAXDNAME, "%s%llu", c->prefix ? c->prefix : "", index); + snprintf(name, MAXDNAME, "%s%" PRIu64, c->prefix ? c->prefix : "", index); } else { diff --git a/src/radv.c b/src/radv.c index 4f31457..3bdca09 100644 --- a/src/radv.c +++ b/src/radv.c @@ -128,7 +128,7 @@ void ra_start_unsolicited(time_t now, struct dhcp_context *context) for (context = daemon->dhcp6; context; context = context->next) if (!(context->flags & CONTEXT_TEMPLATE)) { - context->ra_time = now + (rand16()/13000); /* range 0 - 5 */ + context->ra_time = now + rand_uniform64(6); /* re-do frequently for a minute or so, in case the first gets lost. */ context->ra_short_period_start = now; } @@ -935,12 +935,12 @@ static void new_timeout(struct dhcp_context *context, char *iface_name, time_t n { if (difftime(now, context->ra_short_period_start) < 60.0) /* range 5 - 20 */ - context->ra_time = now + 5 + (rand16()/4400); + context->ra_time = now + 5 + rand_uniform64(16); else { /* range 3/4 - 1 times MaxRtrAdvInterval */ unsigned int adv_interval = calc_interval(find_iface_param(iface_name)); - context->ra_time = now + (3 * adv_interval)/4 + ((adv_interval * (unsigned int)rand16()) >> 18); + context->ra_time = now + (3 * adv_interval)/4 + rand_uniform64(adv_interval / 4); } } diff --git a/src/siphash.c b/src/siphash.c new file mode 100644 index 0000000..f210bc1 --- /dev/null +++ b/src/siphash.c @@ -0,0 +1,186 @@ +#include + +#include "siphash.h" + +#if defined(_MSC_VER) + #include + + #define INLINE __forceinline + #define NOINLINE __declspec(noinline) + #define ROTL64(a,b) _rotl64(a,b) + #define MM16 __declspec(align(16)) + + typedef unsigned int uint32_t; + typedef unsigned __int64 uint64_t; + + #if (_MSC_VER >= 1500) + #define __SSSE3__ + #endif + #if (_MSC_VER > 1200) || defined(_mm_free) + #define __SSE2__ + #endif +#else + #include + #include + + #define INLINE __attribute__((always_inline)) + #define NOINLINE __attribute__((noinline)) + #define ROTL64(a,b) (((a)<<(b))|((a)>>(64-b))) + #define MM16 __attribute__((aligned(16))) +#endif + +uint64_t siphash(const siphash_key_t *key, const unsigned char *m, size_t len) { + uint64_t v0, v1, v2, v3; + uint64_t mi, k0, k1; + uint64_t last7; + size_t i, blocks; + + k0 = key->key[0]; + k1 = key->key[1]; + v0 = k0 ^ 0x736f6d6570736575ull; + v1 = k1 ^ 0x646f72616e646f6dull; + v2 = k0 ^ 0x6c7967656e657261ull; + v3 = k1 ^ 0x7465646279746573ull; + + last7 = (uint64_t)(len & 0xff) << 56; + +#define sipcompress() \ + v0 += v1; v2 += v3; \ + v1 = ROTL64(v1,13); v3 = ROTL64(v3,16); \ + v1 ^= v0; v3 ^= v2; \ + v0 = ROTL64(v0,32); \ + v2 += v1; v0 += v3; \ + v1 = ROTL64(v1,17); v3 = ROTL64(v3,21); \ + v1 ^= v2; v3 ^= v0; \ + v2 = ROTL64(v2,32); + + for (i = 0, blocks = (len & ~7); i < blocks; i += 8) { + memcpy(&mi, m + i, sizeof(mi)); + v3 ^= mi; + sipcompress() + sipcompress() + v0 ^= mi; + } + + switch (len - blocks) { + case 7: last7 |= (uint64_t)m[i + 6] << 48; + // fall through + case 6: last7 |= (uint64_t)m[i + 5] << 40; + // fall through + case 5: last7 |= (uint64_t)m[i + 4] << 32; + // fall through + case 4: last7 |= (uint64_t)m[i + 3] << 24; + // fall through + case 3: last7 |= (uint64_t)m[i + 2] << 16; + // fall through + case 2: last7 |= (uint64_t)m[i + 1] << 8; + // fall through + case 1: last7 |= (uint64_t)m[i + 0] ; + // fall through + case 0: + // fall through + default:; + }; + v3 ^= last7; + sipcompress() + sipcompress() + v0 ^= last7; + v2 ^= 0xff; + sipcompress() + sipcompress() + sipcompress() + sipcompress() + return v0 ^ v1 ^ v2 ^ v3; +} + +/* case-insensitive for chars A-Za-z */ +uint64_t siphash_case(const siphash_key_t *key, const unsigned char *m, size_t len) { + uint64_t v0, v1, v2, v3; + uint64_t mi, mitmp, miout, k0, k1; + uint64_t last7, last1; + size_t i, blocks, lowlyhack; + + k0 = key->key[0] ^ 0xaa; + k1 = key->key[1]; + v0 = k0 ^ 0x736f6d6570736575ull; + v1 = k1 ^ 0x646f72616e646f6dull; + v2 = k0 ^ 0x6c7967656e657261ull; + v3 = k1 ^ 0x7465646279746573ull; + + last7 = (uint64_t)(len & 0xff) << 56; + +#define sipcompress() \ + v0 += v1; v2 += v3; \ + v1 = ROTL64(v1,13); v3 = ROTL64(v3,16); \ + v1 ^= v0; v3 ^= v2; \ + v0 = ROTL64(v0,32); \ + v2 += v1; v0 += v3; \ + v1 = ROTL64(v1,17); v3 = ROTL64(v3,21); \ + v1 ^= v2; v3 ^= v0; \ + v2 = ROTL64(v2,32); + + for (i = 0, blocks = (len & ~7); i < blocks; i += 8) { + memcpy(&mi, m + i, sizeof(mi)); + for (miout = 0, lowlyhack = 0; lowlyhack < 64; lowlyhack += 8) { + mitmp = (mi << lowlyhack) & 255; + if (mitmp >= 'A' && mitmp <= 'Z') + mitmp += 'a' - 'A'; + miout |= (mitmp << lowlyhack); + } + v3 ^= miout; + sipcompress() + sipcompress() + v0 ^= miout; + } + + switch (len - blocks) { + case 7: last1 = (uint64_t)m[i + 6]; + if (last1 >= 'A' && last1 <= 'Z') + last1 += 'a' - 'A'; + last7 |= last1 << 48; + // fall through + case 6: last1 = (uint64_t)m[i + 5]; + if (last1 >= 'A' && last1 <= 'Z') + last1 += 'a' - 'A'; + last7 |= last1 << 40; + // fall through + case 5: last1 = (uint64_t)m[i + 4]; + if (last1 >= 'A' && last1 <= 'Z') + last1 += 'a' - 'A'; + last7 |= last1 << 32; + // fall through + case 4: last1 = (uint64_t)m[i + 3]; + if (last1 >= 'A' && last1 <= 'Z') + last1 += 'a' - 'A'; + last7 |= last1 << 24; + // fall through + case 3: last1 = (uint64_t)m[i + 2]; + if (last1 >= 'A' && last1 <= 'Z') + last1 += 'a' - 'A'; + last7 |= last1 << 16; + // fall through + case 2: last1 = (uint64_t)m[i + 1]; + if (last1 >= 'A' && last1 <= 'Z') + last1 += 'a' - 'A'; + last7 |= last1 << 8; + // fall through + case 1: last1 = (uint64_t)m[i + 0]; + if (last1 >= 'A' && last1 <= 'Z') + last1 += 'a' - 'A'; + last7 |= last1; + // fall through + case 0: + default:; + }; + v3 ^= last7; + sipcompress() + sipcompress() + v0 ^= last7; + v2 ^= 0xff; + sipcompress() + sipcompress() + sipcompress() + sipcompress() + return v0 ^ v1 ^ v2 ^ v3; +} + diff --git a/src/siphash.h b/src/siphash.h new file mode 100644 index 0000000..2ed32da --- /dev/null +++ b/src/siphash.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +typedef struct { + uint64_t key[2]; +} siphash_key_t; + +uint64_t siphash(const siphash_key_t *key, const unsigned char *m, size_t len); +uint64_t siphash_case(const siphash_key_t *key, const unsigned char *m, size_t len); + diff --git a/src/slaac.c b/src/slaac.c index 3042bb8..282c328 100644 --- a/src/slaac.c +++ b/src/slaac.c @@ -171,9 +171,9 @@ time_t periodic_slaac(time_t now, struct dhcp_lease *leases) slaac->ping_time = 0; /* Give up */ else { - slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */ + slaac->ping_time += (1 << (slaac->backoff - 1)) + rand_uniform64(4); if (slaac->backoff > 4) - slaac->ping_time += rand16()/4000; /* 0 - 15 */ + slaac->ping_time += rand_uniform64(16); if (slaac->backoff < 12) slaac->backoff++; } diff --git a/src/util.c b/src/util.c index 079fe44..6e0acf1 100644 --- a/src/util.c +++ b/src/util.c @@ -19,6 +19,7 @@ #include "dnsmasq.h" +#include "charcrandom.h" #ifdef HAVE_BROKEN_RTC #include @@ -30,85 +31,9 @@ #include #endif -/* SURF random number generator */ - -static u32 seed[32]; -static u32 in[12]; -static u32 out[8]; -static int outleft = 0; - -void rand_init() -{ - int fd = open(RANDFILE, O_RDONLY); - - if (fd == -1 || - !read_write(fd, (unsigned char *)&seed, sizeof(seed), 1) || - !read_write(fd, (unsigned char *)&in, sizeof(in), 1)) - die(_("failed to seed the random number generator: %s"), NULL, EC_MISC); - - close(fd); -} - -#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b)))) -#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b)); - -static void surf(void) +void init_siphash_seed(siphash_key_t *key) { - u32 t[12]; u32 x; u32 sum = 0; - int r; int i; int loop; - - for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i]; - for (i = 0;i < 8;++i) out[i] = seed[24 + i]; - x = t[11]; - for (loop = 0;loop < 2;++loop) { - for (r = 0;r < 16;++r) { - sum += 0x9e3779b9; - MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13) - MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13) - MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13) - } - for (i = 0;i < 8;++i) out[i] ^= t[i + 4]; - } -} - -unsigned short rand16(void) -{ - if (!outleft) - { - if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; - surf(); - outleft = 8; - } - - return (unsigned short) out[--outleft]; -} - -u32 rand32(void) -{ - if (!outleft) - { - if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; - surf(); - outleft = 8; - } - - return out[--outleft]; -} - -u64 rand64(void) -{ - static int outleft = 0; - - if (outleft < 2) - { - if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; - surf(); - outleft = 8; - } - - outleft -= 2; - - return (u64)out[outleft+1] + (((u64)out[outleft]) << 32); + random_buf(key, sizeof(*key)); } /* returns 2 if names is OK but contains one or more underscores */