]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dns_random.cc
auth: switch circleci mssql image
[thirdparty/pdns.git] / pdns / dns_random.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
4b3fbfd3 25#include <sys/types.h>
e97cb679
AT
26#include <sys/stat.h>
27#include <fcntl.h>
4b3fbfd3 28#include <unistd.h>
e97cb679
AT
29#include <stdlib.h>
30#include <string>
0b1143db 31#include "dns_random.hh"
e97cb679
AT
32#include "arguments.hh"
33#include "logger.hh"
34#include "boost/lexical_cast.hpp"
35
36#if defined(HAVE_RANDOMBYTES_STIR)
37#include <sodium.h>
38#endif
39#if defined(HAVE_RAND_BYTES)
40#include <openssl/rand.h>
41#endif
42#if defined(HAVE_GETRANDOM)
1d2a703d 43#include <sys/random.h>
e97cb679 44#endif
4b3fbfd3 45
e97cb679
AT
46static enum DNS_RNG {
47 RNG_UNINITIALIZED = 0,
48 RNG_SODIUM,
49 RNG_OPENSSL,
50 RNG_GETRANDOM,
51 RNG_ARC4RANDOM,
52 RNG_URANDOM,
53 RNG_KISS,
54} chosen_rng = RNG_UNINITIALIZED;
4b3fbfd3 55
e97cb679 56static int urandom_fd = -1;
4b3fbfd3 57
e97cb679
AT
58#if defined(HAVE_KISS_RNG)
59/* KISS is intended for development use only */
60static unsigned int kiss_seed;
61static uint32_t kiss_z, kiss_w, kiss_jsr, kiss_jcong;
e06db596 62
e97cb679
AT
63static void
64kiss_init(unsigned int seed)
4b3fbfd3 65{
e97cb679
AT
66 kiss_seed = seed;
67 kiss_jsr = 0x5eed5eed; /* simply musn't be 0 */
68 kiss_z = 1 ^ (kiss_w = kiss_jcong = seed); /* w=z=0 is bad, see Rose */
69}
70
71static unsigned int
72kiss_rand(void)
73{
74 kiss_z = 36969 * (kiss_z&65535) + (kiss_z>>16);
75 kiss_w = 18000 * (kiss_w&65535) + (kiss_w>>16);
76 kiss_jcong = 69069 * kiss_jcong + 1234567;
77 kiss_jsr^=(kiss_jsr<<13); /* <<17, >>13 gives cycle length 2^28.2 max */
78 kiss_jsr^=(kiss_jsr>>17); /* <<13, >>17 gives maximal cycle length */
79 kiss_jsr^=(kiss_jsr<<5);
80 return (((kiss_z<<16) + kiss_w) ^ kiss_jcong) + kiss_jsr;
81}
82#endif
83
05739b0e 84static void dns_random_setup(bool force=false)
e97cb679
AT
85{
86 string rdev;
87 string rng;
88 /* check if selection has been done */
05739b0e 89 if (chosen_rng > RNG_UNINITIALIZED && !force)
e97cb679
AT
90 return;
91
92/* XXX: A horrible hack to allow using dns_random in places where arguments are not available.
93 Forces /dev/urandom usage
94*/
95#if defined(USE_URANDOM_ONLY)
96 chosen_rng = RNG_URANDOM;
97 rdev = "/dev/urandom";
98#else
99 rng = ::arg()["rng"];
100 rdev = ::arg()["entropy-source"];
101 if (rng == "auto") {
102# if defined(HAVE_GETRANDOM)
103 chosen_rng = RNG_GETRANDOM;
104# elif defined(HAVE_ARC4RANDOM)
105 chosen_rng = RNG_ARC4RANDOM;
106# elif defined(HAVE_RANDOMBYTES_STIR)
107 chosen_rng = RNG_SODIUM;
108# elif defined(HAVE_RAND_BYTES)
109 chosen_rng = RNG_OPENSSL;
110# else
111 chosen_rng = RNG_URANDOM;
112# endif
113# if defined(HAVE_RANDOMBYTES_STIR)
114 } else if (rng == "sodium") {
115 chosen_rng = RNG_SODIUM;
116# endif
117# if defined(HAVE_RAND_BYTES)
118 } else if (rng == "openssl") {
119 chosen_rng = RNG_OPENSSL;
120# endif
121# if defined(HAVE_GETRANDOM)
122 } else if (rng == "getrandom") {
123 chosen_rng = RNG_GETRANDOM;
124# endif
125# if defined(HAVE_ARC4RANDOM)
126 } else if (rng == "arc4random") {
127 chosen_rng = RNG_ARC4RANDOM;
128# endif
129 } else if (rng == "urandom") {
130 chosen_rng = RNG_URANDOM;
131#if defined(HAVE_KISS_RNG)
132 } else if (rng == "kiss") {
133 chosen_rng = RNG_KISS;
1d2a703d 134 g_log<<Logger::Warning<<"kiss rng should not be used in production environment"<<std::endl;
e97cb679
AT
135#endif
136 } else {
05739b0e 137 throw std::runtime_error("Unsupported rng '" + rng + "'");
ba6cbc80 138 }
4b3fbfd3 139
e97cb679
AT
140# if defined(HAVE_RANDOMBYTES_STIR)
141 if (chosen_rng == RNG_SODIUM) {
142 if (sodium_init() == -1)
05739b0e 143 throw std::runtime_error("Unable to initialize sodium crypto library");
e97cb679
AT
144 /* make sure it's set up */
145 randombytes_stir();
146 }
147# endif
4b3fbfd3 148
e97cb679
AT
149# if defined(HAVE_GETRANDOM)
150 if (chosen_rng == RNG_GETRANDOM) {
151 char buf[1];
152 // some systems define getrandom but it does not really work, e.g. because it's
153 // not present in kernel.
154 if (getrandom(buf, sizeof(buf), 0) == -1) {
1d2a703d 155 g_log<<Logger::Warning<<"getrandom() failed: "<<strerror(errno)<<", falling back to " + rdev<<std::endl;
e97cb679
AT
156 chosen_rng = RNG_URANDOM;
157 }
158 }
159# endif
e22d9b4e 160
e97cb679
AT
161# if defined(HAVE_RAND_BYTES)
162 if (chosen_rng == RNG_OPENSSL) {
163 int ret;
164 unsigned char buf[1];
165 if ((ret = RAND_bytes(buf, sizeof(buf))) == -1)
05739b0e 166 throw std::runtime_error("RAND_bytes not supported by current SSL engine");
e97cb679 167 if (ret == 0)
05739b0e 168 throw std::runtime_error("Openssl RNG was not seeded");
e97cb679
AT
169 }
170# endif
171#endif /* USE_URANDOM_ONLY */
172 if (chosen_rng == RNG_URANDOM) {
173 urandom_fd = open(rdev.c_str(), O_RDONLY);
174 if (urandom_fd == -1)
05739b0e 175 throw std::runtime_error("Cannot open " + rdev + ": " + std::string(strerror(errno)));
e97cb679
AT
176 }
177#if defined(HAVE_KISS_RNG)
178 if (chosen_rng == RNG_KISS) {
179 unsigned int seed;
180 urandom_fd = open(rdev.c_str(), O_RDONLY);
181 if (urandom_fd == -1)
05739b0e 182 throw std::runtime_error("Cannot open " + rdev + ": " + std::string(strerror(errno)));
e97cb679
AT
183 if (read(urandom_fd, &seed, sizeof(seed)) < 0) {
184 (void)close(urandom_fd);
05739b0e 185 throw std::runtime_error("Cannot read random device");
e97cb679
AT
186 }
187 kiss_init(seed);
188 (void)close(urandom_fd);
189 }
190#endif
4b3fbfd3
BH
191}
192
05739b0e
RG
193void dns_random_init(const string& data __attribute__((unused)), bool force) {
194 dns_random_setup(force);
e97cb679
AT
195 (void)dns_random(1);
196 // init should occur already in dns_random_setup
197 // this interface is only for KISS
198#if defined(HAVE_KISS_RNG)
199 unsigned int seed;
200 if (chosen_rng != RNG_KISS)
201 return;
202 if (data.size() != 16)
05739b0e 203 throw std::runtime_error("invalid seed");
e97cb679
AT
204 seed = (data[0] + (data[1]<<8) + (data[2]<<16) + (data[3]<<24)) ^
205 (data[4] + (data[5]<<8) + (data[6]<<16) + (data[7]<<24)) ^
206 (data[8] + (data[9]<<8) + (data[10]<<16) + (data[11]<<24)) ^
207 (data[12] + (data[13]<<8) + (data[14]<<16) + (data[15]<<24));
208 kiss_init(seed);
e06a74d5 209#endif
4b3fbfd3
BH
210}
211
e97cb679 212/* Parts of this code come from arc4random_uniform */
a49c8752 213uint32_t dns_random(uint32_t upper_bound) {
e97cb679
AT
214 if (chosen_rng == RNG_UNINITIALIZED)
215 dns_random_setup();
4b3fbfd3 216
e97cb679
AT
217 unsigned int min;
218 if (upper_bound < 2)
219 return 0;
220 /* To avoid "modulo bias" for some methods, calculate
221 minimum acceptable value for random number to improve
222 uniformity.
223
224 On applicable rngs, we loop until the rng spews out
225 value larger than min, and then take modulo out of that.
226 */
764d0fbe 227#if (ULONG_MAX > 0xffffffffUL)
e97cb679
AT
228 min = 0x100000000UL % upper_bound;
229#else
230 /* Calculate (2**32 % upper_bound) avoiding 64-bit math */
231 if (upper_bound > 0x80000000)
232 min = 1 + ~upper_bound; /* 2**32 - upper_bound */
233 else {
234 /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
235 min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
236 }
4b3fbfd3 237#endif
e97cb679
AT
238
239 switch(chosen_rng) {
240 case RNG_UNINITIALIZED:
05739b0e 241 throw std::runtime_error("Unreachable at " __FILE__ ":" + boost::lexical_cast<std::string>(__LINE__)); // cannot be reached
e97cb679
AT
242 case RNG_SODIUM:
243#if defined(HAVE_RANDOMBYTES_STIR) && !defined(USE_URANDOM_ONLY)
a49c8752 244 return randombytes_uniform(upper_bound);
e97cb679 245#else
05739b0e 246 throw std::runtime_error("Unreachable at " __FILE__ ":" + boost::lexical_cast<std::string>(__LINE__)); // cannot be reached
e97cb679
AT
247#endif /* RND_SODIUM */
248 case RNG_OPENSSL: {
249#if defined(HAVE_RAND_BYTES) && !defined(USE_URANDOM_ONLY)
7c9e9190 250 uint32_t num = 0;
df6d5d8c 251 do {
e97cb679 252 if (RAND_bytes(reinterpret_cast<unsigned char*>(&num), sizeof(num)) < 1)
05739b0e 253 throw std::runtime_error("Openssl RNG was not seeded");
e97cb679 254 }
df6d5d8c
RG
255 while(num < min);
256
e97cb679
AT
257 return num % upper_bound;
258#else
05739b0e 259 throw std::runtime_error("Unreachable at " __FILE__ ":" + boost::lexical_cast<std::string>(__LINE__)); // cannot be reached
e97cb679
AT
260#endif /* RNG_OPENSSL */
261 }
262 case RNG_GETRANDOM: {
263#if defined(HAVE_GETRANDOM) && !defined(USE_URANDOM_ONLY)
7c9e9190 264 uint32_t num = 0;
df6d5d8c 265 do {
1d2a703d 266 if (getrandom(&num, sizeof(num), 0) != sizeof(num))
05739b0e 267 throw std::runtime_error("getrandom() failed: " + std::string(strerror(errno)));
e97cb679 268 }
df6d5d8c
RG
269 while(num < min);
270
e97cb679
AT
271 return num % upper_bound;
272#else
05739b0e 273 throw std::runtime_error("Unreachable at " __FILE__ ":" + boost::lexical_cast<std::string>(__LINE__)); // cannot be reached
e97cb679
AT
274#endif
275 }
276 case RNG_ARC4RANDOM:
277#if defined(HAVE_ARC4RANDOM) && !defined(USE_URANDOM_ONLY)
a49c8752 278 return arc4random_uniform(upper_bound);
e97cb679 279#else
05739b0e 280 throw std::runtime_error("Unreachable at " __FILE__ ":" + boost::lexical_cast<std::string>(__LINE__)); // cannot be reached
e97cb679
AT
281#endif
282 case RNG_URANDOM: {
7c9e9190 283 uint32_t num = 0;
144c9852 284 size_t attempts = 5;
df6d5d8c 285 do {
144c9852
RG
286 ssize_t got = read(urandom_fd, &num, sizeof(num));
287 if (got < 0) {
b973ba4d
RG
288 if (errno == EINTR) {
289 continue;
290 }
291
e97cb679 292 (void)close(urandom_fd);
05739b0e 293 throw std::runtime_error("Cannot read random device");
e97cb679 294 }
144c9852
RG
295 else if (static_cast<size_t>(got) != sizeof(num)) {
296 /* short read, let's retry */
297 if (attempts == 0) {
298 throw std::runtime_error("Too many short reads on random device");
299 }
300 attempts--;
301 continue;
302 }
e97cb679 303 }
df6d5d8c
RG
304 while(num < min);
305
e97cb679
AT
306 return num % upper_bound;
307 }
308#if defined(HAVE_KISS_RNG)
309 case RNG_KISS: {
7c9e9190 310 uint32_t num = 0;
df6d5d8c 311 do {
e97cb679 312 num = kiss_rand();
df6d5d8c
RG
313 }
314 while(num < min);
315
e97cb679
AT
316 return num % upper_bound;
317 }
318#endif
319 default:
05739b0e 320 throw std::runtime_error("Unreachable at " __FILE__ ":" + boost::lexical_cast<std::string>(__LINE__)); // cannot be reached
e97cb679
AT
321 };
322}