]>
Commit | Line | Data |
---|---|---|
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 |
46 | static 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 | 56 | static int urandom_fd = -1; |
4b3fbfd3 | 57 | |
e97cb679 AT |
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; | |
e06db596 | 62 | |
e97cb679 AT |
63 | static void |
64 | kiss_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 | ||
71 | static unsigned int | |
72 | kiss_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 | 84 | static 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 |
193 | void 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 | 213 | uint32_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 | } |