]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dns_random.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
25 #include <sys/types.h>
31 #include "dns_random.hh"
32 #include "arguments.hh"
34 #include "boost/lexical_cast.hpp"
36 #if defined(HAVE_RANDOMBYTES_STIR)
39 #if defined(HAVE_RAND_BYTES)
40 #include <openssl/rand.h>
42 #if defined(HAVE_GETRANDOM)
43 #include <sys/random.h>
47 RNG_UNINITIALIZED
= 0,
54 } chosen_rng
= RNG_UNINITIALIZED
;
56 static int urandom_fd
= -1;
58 #if defined(HAVE_KISS_RNG)
59 /* KISS is intended for development use only */
60 static unsigned int kiss_seed
;
61 static uint32_t kiss_z
, kiss_w
, kiss_jsr
, kiss_jcong
;
64 kiss_init(unsigned int 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 */
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
;
84 static void dns_random_setup(bool force
=false)
88 /* check if selection has been done */
89 if (chosen_rng
> RNG_UNINITIALIZED
&& !force
)
92 /* XXX: A horrible hack to allow using dns_random in places where arguments are not available.
93 Forces /dev/urandom usage
95 #if defined(USE_URANDOM_ONLY)
96 chosen_rng
= RNG_URANDOM
;
97 rdev
= "/dev/urandom";
100 rdev
= ::arg()["entropy-source"];
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
;
111 chosen_rng
= RNG_URANDOM
;
113 # if defined(HAVE_RANDOMBYTES_STIR)
114 } else if (rng
== "sodium") {
115 chosen_rng
= RNG_SODIUM
;
117 # if defined(HAVE_RAND_BYTES)
118 } else if (rng
== "openssl") {
119 chosen_rng
= RNG_OPENSSL
;
121 # if defined(HAVE_GETRANDOM)
122 } else if (rng
== "getrandom") {
123 chosen_rng
= RNG_GETRANDOM
;
125 # if defined(HAVE_ARC4RANDOM)
126 } else if (rng
== "arc4random") {
127 chosen_rng
= RNG_ARC4RANDOM
;
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
;
134 g_log
<<Logger::Warning
<<"kiss rng should not be used in production environment"<<std::endl
;
137 throw std::runtime_error("Unsupported rng '" + rng
+ "'");
140 # if defined(HAVE_RANDOMBYTES_STIR)
141 if (chosen_rng
== RNG_SODIUM
) {
142 if (sodium_init() == -1)
143 throw std::runtime_error("Unable to initialize sodium crypto library");
144 /* make sure it's set up */
149 # if defined(HAVE_GETRANDOM)
150 if (chosen_rng
== RNG_GETRANDOM
) {
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) {
155 g_log
<<Logger::Warning
<<"getrandom() failed: "<<strerror(errno
)<<", falling back to " + rdev
<<std::endl
;
156 chosen_rng
= RNG_URANDOM
;
161 # if defined(HAVE_RAND_BYTES)
162 if (chosen_rng
== RNG_OPENSSL
) {
164 unsigned char buf
[1];
165 if ((ret
= RAND_bytes(buf
, sizeof(buf
))) == -1)
166 throw std::runtime_error("RAND_bytes not supported by current SSL engine");
168 throw std::runtime_error("Openssl RNG was not seeded");
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)
175 throw std::runtime_error("Cannot open " + rdev
+ ": " + std::string(strerror(errno
)));
177 #if defined(HAVE_KISS_RNG)
178 if (chosen_rng
== RNG_KISS
) {
180 urandom_fd
= open(rdev
.c_str(), O_RDONLY
);
181 if (urandom_fd
== -1)
182 throw std::runtime_error("Cannot open " + rdev
+ ": " + std::string(strerror(errno
)));
183 if (read(urandom_fd
, &seed
, sizeof(seed
)) < 0) {
184 (void)close(urandom_fd
);
185 throw std::runtime_error("Cannot read random device");
188 (void)close(urandom_fd
);
193 void dns_random_init(const string
& data
__attribute__((unused
)), bool force
) {
194 dns_random_setup(force
);
196 // init should occur already in dns_random_setup
197 // this interface is only for KISS
198 #if defined(HAVE_KISS_RNG)
200 if (chosen_rng
!= RNG_KISS
)
202 if (data
.size() != 16)
203 throw std::runtime_error("invalid seed");
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));
212 /* Parts of this code come from arc4random_uniform */
213 uint32_t dns_random(uint32_t upper_bound
) {
214 if (chosen_rng
== RNG_UNINITIALIZED
)
220 /* To avoid "modulo bias" for some methods, calculate
221 minimum acceptable value for random number to improve
224 On applicable rngs, we loop until the rng spews out
225 value larger than min, and then take modulo out of that.
227 #if (ULONG_MAX > 0xffffffffUL)
228 min
= 0x100000000UL
% upper_bound
;
230 /* Calculate (2**32 % upper_bound) avoiding 64-bit math */
231 if (upper_bound
> 0x80000000)
232 min
= 1 + ~upper_bound
; /* 2**32 - upper_bound */
234 /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
235 min
= ((0xffffffff - (upper_bound
* 2)) + 1) % upper_bound
;
240 case RNG_UNINITIALIZED
:
241 throw std::runtime_error("Unreachable at " __FILE__
":" + boost::lexical_cast
<std::string
>(__LINE__
)); // cannot be reached
243 #if defined(HAVE_RANDOMBYTES_STIR) && !defined(USE_URANDOM_ONLY)
244 return randombytes_uniform(upper_bound
);
246 throw std::runtime_error("Unreachable at " __FILE__
":" + boost::lexical_cast
<std::string
>(__LINE__
)); // cannot be reached
247 #endif /* RND_SODIUM */
249 #if defined(HAVE_RAND_BYTES) && !defined(USE_URANDOM_ONLY)
252 if (RAND_bytes(reinterpret_cast<unsigned char*>(&num
), sizeof(num
)) < 1)
253 throw std::runtime_error("Openssl RNG was not seeded");
257 return num
% upper_bound
;
259 throw std::runtime_error("Unreachable at " __FILE__
":" + boost::lexical_cast
<std::string
>(__LINE__
)); // cannot be reached
260 #endif /* RNG_OPENSSL */
262 case RNG_GETRANDOM
: {
263 #if defined(HAVE_GETRANDOM) && !defined(USE_URANDOM_ONLY)
266 if (getrandom(&num
, sizeof(num
), 0) != sizeof(num
))
267 throw std::runtime_error("getrandom() failed: " + std::string(strerror(errno
)));
271 return num
% upper_bound
;
273 throw std::runtime_error("Unreachable at " __FILE__
":" + boost::lexical_cast
<std::string
>(__LINE__
)); // cannot be reached
277 #if defined(HAVE_ARC4RANDOM) && !defined(USE_URANDOM_ONLY)
278 return arc4random_uniform(upper_bound
);
280 throw std::runtime_error("Unreachable at " __FILE__
":" + boost::lexical_cast
<std::string
>(__LINE__
)); // cannot be reached
286 ssize_t got
= read(urandom_fd
, &num
, sizeof(num
));
288 if (errno
== EINTR
) {
292 (void)close(urandom_fd
);
293 throw std::runtime_error("Cannot read random device");
295 else if (static_cast<size_t>(got
) != sizeof(num
)) {
296 /* short read, let's retry */
298 throw std::runtime_error("Too many short reads on random device");
306 return num
% upper_bound
;
308 #if defined(HAVE_KISS_RNG)
316 return num
% upper_bound
;
320 throw std::runtime_error("Unreachable at " __FILE__
":" + boost::lexical_cast
<std::string
>(__LINE__
)); // cannot be reached