/src/resiprocate/rutil/Random.cxx
Line | Count | Source (jump to first uncovered line) |
1 | | #if defined(HAVE_CONFIG_H) |
2 | | #include "config.h" |
3 | | #endif |
4 | | |
5 | | #include "rutil/ResipAssert.h" |
6 | | #include <stdlib.h> |
7 | | |
8 | | #ifdef WIN32 |
9 | | #include "rutil/Socket.hxx" |
10 | | #include "rutil/DataStream.hxx" |
11 | | #include "rutil/Data.hxx" |
12 | | #else |
13 | | #include <unistd.h> |
14 | | #include <sys/types.h> |
15 | | #include <sys/stat.h> |
16 | | #include <fcntl.h> |
17 | | #endif |
18 | | |
19 | | #include "rutil/Random.hxx" |
20 | | #include "rutil/Timer.hxx" |
21 | | #include "rutil/Mutex.hxx" |
22 | | #include "rutil/Lock.hxx" |
23 | | #include "rutil/Logger.hxx" |
24 | | |
25 | | |
26 | | #ifdef USE_SSL |
27 | | #ifdef WIN32 |
28 | | //hack for name collision of OCSP_RESPONSE and wincrypt.h in latest openssl release 0.9.8h |
29 | | //http://www.google.com/search?q=OCSP%5fRESPONSE+wincrypt%2eh |
30 | | //continue to watch this issue for a real fix. |
31 | | #undef OCSP_RESPONSE |
32 | | #endif |
33 | | #include "rutil/ssl/OpenSSLInit.hxx" |
34 | | # define USE_OPENSSL 1 |
35 | | #else |
36 | | # define USE_OPENSSL 0 |
37 | | #endif |
38 | | |
39 | | #if ( USE_OPENSSL == 1 ) |
40 | | # include <openssl/opensslv.h> |
41 | | #if !defined(LIBRESSL_VERSION_NUMBER) |
42 | | # include <openssl/e_os2.h> |
43 | | #endif |
44 | | # include <openssl/rand.h> |
45 | | # include <openssl/err.h> |
46 | | #endif |
47 | | |
48 | | using namespace resip; |
49 | | #define RESIPROCATE_SUBSYSTEM Subsystem::SIP |
50 | | |
51 | | Mutex Random::mMutex; |
52 | | bool Random::mIsInitialized = false; |
53 | | |
54 | | #ifdef WIN32 |
55 | | Random::Initializer Random::mInitializer; |
56 | | #ifdef RESIP_RANDOM_WIN32_RTL |
57 | | BOOLEAN (APIENTRY *Random::RtlGenRandom)(void*, ULONG) = 0; |
58 | | #endif |
59 | | #endif //WIN32 |
60 | | |
61 | | #ifdef RESIP_RANDOM_THREAD_MUTEX |
62 | | struct random_data* Random::sRandomState = 0; |
63 | | #endif |
64 | | |
65 | | #ifdef RESIP_RANDOM_THREAD_LOCAL |
66 | | ThreadIf::TlsKey Random::sRandomStateKey = 0; |
67 | | #endif |
68 | | |
69 | | #define RANDOM_STATE_SIZE 128 |
70 | | |
71 | | const char* |
72 | | Random::getImplName() |
73 | 0 | { |
74 | | #ifdef WIN32 |
75 | | #if defined(RESIP_RANDOM_WIN32_RTL) |
76 | | return "win32_rtl"; |
77 | | #else |
78 | | return "win32_rand"; |
79 | | #endif |
80 | | #else // WIN32 |
81 | | #if defined(RESIP_RANDOM_THREAD_LOCAL) |
82 | | return "posix_thread_local"; |
83 | | #elif defined(RESIP_RANDOM_THREAD_MUTEX) |
84 | | return "posix_thread_mutex"; |
85 | | #else |
86 | 0 | return "posix_random"; |
87 | 0 | #endif |
88 | 0 | #endif // not WIN32 |
89 | 0 | } |
90 | | |
91 | | /** |
92 | | Key goal is to make sure that each thread has distinct seed. |
93 | | **/ |
94 | | unsigned |
95 | | Random::getSimpleSeed() |
96 | 1 | { |
97 | | // !cj! need to find a better way - use pentium random commands? |
98 | 1 | Data buffer; |
99 | 1 | { |
100 | 1 | DataStream strm(buffer); |
101 | | #ifdef WIN32 |
102 | | strm << GetTickCount() << ":"; |
103 | | strm << GetCurrentProcessId() << ":"; |
104 | | strm << GetCurrentThreadId(); |
105 | | #else |
106 | | // .kw. previously just used the lower 32bits of getTimeMs() |
107 | 1 | strm << ResipClock::getTimeMicroSec() << ":"; |
108 | 1 | strm << getpid(); |
109 | | #if defined(RESIP_RANDOM_THREAD_LOCAL) |
110 | | strm << ":" << ThreadIf::selfId(); |
111 | | #endif |
112 | 1 | #endif |
113 | 1 | } |
114 | 1 | return (unsigned int)buffer.hash(); |
115 | 1 | } |
116 | | |
117 | | void |
118 | | Random::initialize() |
119 | 19.5k | { |
120 | | #ifdef WIN32 |
121 | | //#if defined(USE_SSL) |
122 | | #if 0 //!dcm! - this shouldn't be per thread for win32, and this is slow. Going |
123 | | //to re-work openssl initialization |
124 | | if ( !Random::mIsInitialized) |
125 | | { |
126 | | Lock lock(mMutex); |
127 | | if (!Random::mIsInitialized) |
128 | | { |
129 | | mIsInitialized = true; |
130 | | RAND_screen (); |
131 | | } |
132 | | } |
133 | | #else |
134 | | if (!Random::mInitializer.isInitialized()) |
135 | | { |
136 | | Lock lock(mMutex); |
137 | | if (!Random::mInitializer.isInitialized()) |
138 | | { |
139 | | Random::mInitializer.setInitialized(); |
140 | | |
141 | | unsigned seed = getSimpleSeed(); |
142 | | srand(seed); |
143 | | |
144 | | #ifdef RESIP_RANDOM_WIN32_RTL |
145 | | // .jjg. from http://blogs.msdn.com/michael_howard/archive/2005/01/14/353379.aspx |
146 | | // srand(..) and rand() have proven to be insufficient sources of randomness, |
147 | | // leading to transaction id collisions in resip. |
148 | | // SystemFunction036 maps to RtlGenRandom, which is used by rand_s() (which is available |
149 | | // only with the VC 8.0 runtime or later) and is the Microsoft-recommended way of getting |
150 | | // a random number. This code allows that functionality to be accessed even from VC 7.1. |
151 | | // However, SystemFunction036 only exists in Windows XP and later, so we may need to fallback |
152 | | // to the old method using rand(). |
153 | | HMODULE hLib = LoadLibrary("ADVAPI32.DLL"); |
154 | | if (hLib) |
155 | | { |
156 | | Random::RtlGenRandom = |
157 | | (BOOLEAN (APIENTRY *)(void*,ULONG))GetProcAddress(hLib,"SystemFunction036"); |
158 | | |
159 | | if (!Random::RtlGenRandom) |
160 | | { |
161 | | WarningLog(<< "Using srand(..) and rand() for random numbers"); |
162 | | } |
163 | | } |
164 | | #endif // RESIP_RANDOM_WIN32_RTL |
165 | | |
166 | | mIsInitialized = true; |
167 | | |
168 | | } |
169 | | } |
170 | | #endif // not dead code |
171 | | |
172 | | #else // WIN32 |
173 | | // ?dcm? -- OpenSSL will transparently initialize PRNG if /dev/urandom is |
174 | | // present. In any case, will move into OpenSSLInit |
175 | 19.5k | if ( !Random::mIsInitialized) |
176 | 1 | { |
177 | 1 | Lock lock(mMutex); |
178 | 1 | if (!Random::mIsInitialized) |
179 | 1 | { |
180 | 1 | mIsInitialized = true; |
181 | 1 | Timer::setupTimeOffsets(); |
182 | | |
183 | 1 | unsigned seed = getSimpleSeed(); |
184 | | |
185 | | #if defined(RESIP_RANDOM_THREAD_LOCAL) |
186 | | ThreadIf::tlsKeyCreate(sRandomStateKey, ::free); |
187 | | #elif defined(RESIP_RANDOM_THREAD_MUTEX) |
188 | | struct random_data *buf; |
189 | | size_t sz = sizeof(*buf)+RANDOM_STATE_SIZE; |
190 | | buf = (struct random_data*) ::malloc(sz); |
191 | | memset( buf, 0, sz); // .kw. strange segfaults without this |
192 | | initstate_r(seed, ((char*)buf)+sizeof(*buf), RANDOM_STATE_SIZE, buf); |
193 | | sRandomState = buf; |
194 | | #else |
195 | 1 | srandom(seed); |
196 | 1 | #endif |
197 | | |
198 | | |
199 | 1 | int fd = open("/dev/urandom", O_RDONLY); |
200 | | // !ah! blocks on embedded devices -- not enough entropy. |
201 | 1 | if ( fd != -1 ) |
202 | 1 | { |
203 | 1 | int s = read( fd,&seed,sizeof(seed) ); //!ah! blocks if /dev/random on embedded sys |
204 | | |
205 | 1 | if ( s != sizeof(seed) ) |
206 | 0 | { |
207 | 0 | ErrLog( << "System is short of randomness" ); // !ah! never prints |
208 | 0 | } |
209 | 1 | } |
210 | 0 | else |
211 | 0 | { |
212 | 0 | ErrLog( << "Could not open /dev/urandom" ); |
213 | 0 | } |
214 | | |
215 | | #if defined(USE_SSL) |
216 | | if (fd == -1 ) |
217 | | { |
218 | | // really bad sign - /dev/random does not exist so need to intialize |
219 | | // OpenSSL some other way |
220 | | |
221 | | // !cj! need to fix assert(0); |
222 | | } |
223 | | else |
224 | | { |
225 | | char buf[1024/8]; // size is number byes used for OpenSSL init |
226 | | |
227 | | int s = read( fd,&buf,sizeof(buf) ); |
228 | | |
229 | | if ( s != sizeof(buf) ) |
230 | | { |
231 | | ErrLog( << "System is short of randomness" ); |
232 | | } |
233 | | |
234 | | RAND_add(buf,sizeof(buf),double(s*8)); |
235 | | } |
236 | | #endif // SSL |
237 | 1 | if (fd != -1 ) |
238 | 1 | { |
239 | 1 | ::close(fd); |
240 | 1 | } |
241 | 1 | } |
242 | 1 | } |
243 | 19.5k | #endif // not WIN32 |
244 | 19.5k | } |
245 | | |
246 | | int |
247 | | Random::getRandom() |
248 | 13.0k | { |
249 | 13.0k | initialize(); |
250 | | |
251 | | #ifdef WIN32 |
252 | | |
253 | | int ret = 0; |
254 | | |
255 | | #ifdef RESIP_RANDOM_WIN32_RTL |
256 | | // see comment in initialize() |
257 | | if (Random::RtlGenRandom) |
258 | | { |
259 | | unsigned long buff[1]; |
260 | | ULONG ulCbBuff = sizeof(buff); |
261 | | if (Random::RtlGenRandom(buff,ulCbBuff)) |
262 | | { |
263 | | // .kw. all other impls here return positive number, so do the same... |
264 | | ret = buff[0] & (~(1<<31)); |
265 | | return ret; |
266 | | } |
267 | | } |
268 | | // fallback to using rand() if this is a Windows version previous to XP |
269 | | #endif // RESIP_RANDOM_WIN32_RTL |
270 | | { |
271 | | // rand() returns [0,RAND_MAX], which on Windows is 15 bits and positive |
272 | | // code below gets 30bits of randomness; with bit31 and bit15 |
273 | | // always zero; result is always positive |
274 | | resip_assert( RAND_MAX == 0x7fff ); |
275 | | // WATCHOUT: on Linux, rand() returns 31bits, and assert above will fail |
276 | | int r1 = rand(); |
277 | | int r2 = rand(); |
278 | | ret = (r1<<16) + r2; |
279 | | } |
280 | | |
281 | | return ret; |
282 | | #else // WIN32 |
283 | | |
284 | | #if defined(RESIP_RANDOM_THREAD_LOCAL) |
285 | | struct random_data *buf = (struct random_data*) ThreadIf::tlsGetValue(sRandomStateKey); |
286 | | if ( buf==NULL ) { |
287 | | size_t sz = sizeof(*buf)+RANDOM_STATE_SIZE; |
288 | | buf = (struct random_data*) ::malloc(sz); |
289 | | memset( buf, 0, sz); // .kw. strange segfaults without this |
290 | | unsigned seed = getSimpleSeed(); |
291 | | initstate_r(seed, ((char*)buf)+sizeof(*buf), RANDOM_STATE_SIZE, buf); |
292 | | ThreadIf::tlsSetValue(sRandomStateKey, buf); |
293 | | } |
294 | | int32_t ret; |
295 | | random_r(buf, &ret); |
296 | | return ret; |
297 | | #elif defined(RESIP_RANDOM_THREAD_MUTEX) |
298 | | int32_t ret; |
299 | | { |
300 | | Lock statelock(mMutex); |
301 | | random_r(sRandomState, &ret); |
302 | | } |
303 | | return ret; |
304 | | #else |
305 | | // random returns [0,RAN_MAX]. On Linux, this is 31 bits and positive. |
306 | | // On some platforms it might be on 15 bits, and will need to do something. |
307 | | // assert( RAND_MAX == ((1<<31)-1) ); // ?slg? commented out assert since, RAND_MAX is not used in random(), it applies to rand() only |
308 | 13.0k | return random(); |
309 | 13.0k | #endif // THREAD_LOCAL |
310 | 13.0k | #endif // WIN32 |
311 | 13.0k | } |
312 | | |
313 | | int |
314 | | Random::getCryptoRandom() |
315 | 0 | { |
316 | 0 | initialize(); |
317 | |
|
318 | | #if USE_OPENSSL |
319 | | int ret; |
320 | | int e = RAND_bytes( (unsigned char*)&ret , sizeof(ret) ); |
321 | | if ( e < 0 ) |
322 | | { |
323 | | // error of some type - likely not enough rendomness to dod this |
324 | | long err = ERR_get_error(); |
325 | | |
326 | | char buf[1024]; |
327 | | ERR_error_string_n(err,buf,sizeof(buf)); |
328 | | |
329 | | ErrLog( << buf ); |
330 | | resip_assert(0); |
331 | | } |
332 | | return ret; |
333 | | #else |
334 | 0 | return getRandom(); |
335 | 0 | #endif |
336 | 0 | } |
337 | | |
338 | | Data |
339 | | Random::getRandom(unsigned int len) |
340 | 6.52k | { |
341 | 6.52k | initialize(); |
342 | 6.52k | resip_assert(len < Random::maxLength+1); |
343 | | |
344 | 6.52k | union |
345 | 6.52k | { |
346 | 6.52k | char cbuf[Random::maxLength+1]; |
347 | 6.52k | unsigned int ibuf[(Random::maxLength+1)/sizeof(int)]; |
348 | 6.52k | }; |
349 | | |
350 | 19.5k | for (unsigned int count=0; count<(len+sizeof(int)-1)/sizeof(int); ++count) |
351 | 13.0k | { |
352 | 13.0k | ibuf[count] = Random::getRandom(); |
353 | 13.0k | } |
354 | 6.52k | return Data(cbuf, len); |
355 | 6.52k | } |
356 | | |
357 | | Data |
358 | | Random::getCryptoRandom(unsigned int len) |
359 | 0 | { |
360 | 0 | unsigned char* buf = new unsigned char[len]; |
361 | 0 | getCryptoRandom(buf, len); // USE_SSL check is in here |
362 | 0 | return Data(Data::Take, (char*)buf, len); |
363 | 0 | } |
364 | | |
365 | | Data |
366 | | Random::getRandomHex(unsigned int numBytes) |
367 | 6.52k | { |
368 | 6.52k | return Random::getRandom(numBytes).hex(); |
369 | 6.52k | } |
370 | | |
371 | | Data |
372 | | Random::getRandomBase64(unsigned int numBytes) |
373 | 0 | { |
374 | 0 | return Random::getRandom(numBytes).base64encode(); |
375 | 0 | } |
376 | | |
377 | | Data |
378 | | Random::getCryptoRandomHex(unsigned int numBytes) |
379 | 0 | { |
380 | 0 | return Random::getCryptoRandom(numBytes).hex(); |
381 | 0 | } |
382 | | |
383 | | Data |
384 | | Random::getCryptoRandomBase64(unsigned int numBytes) |
385 | 0 | { |
386 | 0 | return Random::getCryptoRandom(numBytes).base64encode(); |
387 | 0 | } |
388 | | |
389 | | /* |
390 | | [From RFC 4122] |
391 | | |
392 | | The version 4 UUID is meant for generating UUIDs from truly-random or |
393 | | pseudo-random numbers. |
394 | | |
395 | | The algorithm is as follows: |
396 | | |
397 | | o Set the two most significant bits (bits 6 and 7) of the |
398 | | clock_seq_hi_and_reserved to zero and one, respectively. |
399 | | |
400 | | o Set the four most significant bits (bits 12 through 15) of the |
401 | | time_hi_and_version field to the 4-bit version number from |
402 | | Section 4.1.3. (0 1 0 0) |
403 | | |
404 | | o Set all the other bits to randomly (or pseudo-randomly) chosen |
405 | | values. |
406 | | |
407 | | UUID = time-low "-" time-mid "-" |
408 | | time-high-and-version "-" |
409 | | clock-seq-and-reserved |
410 | | clock-seq-low "-" node |
411 | | time-low = 4hexOctet |
412 | | time-mid = 2hexOctet |
413 | | time-high-and-version = 2hexOctet |
414 | | clock-seq-and-reserved = hexOctet |
415 | | clock-seq-low = hexOctet |
416 | | node = 6hexOctet |
417 | | hexOctet = hexDigit hexDigit |
418 | | */ |
419 | | Data |
420 | | Random::getVersion4UuidUrn() |
421 | 0 | { |
422 | 0 | Data urn ("urn:uuid:"); |
423 | 0 | urn += getCryptoRandomHex(4); // time-low |
424 | 0 | urn += "-"; |
425 | 0 | urn += getCryptoRandomHex(2); // time-mid |
426 | 0 | urn += "-"; |
427 | |
|
428 | 0 | Data time_hi_and_version = Random::getCryptoRandom(2); |
429 | 0 | time_hi_and_version[0] &= 0x0f; |
430 | 0 | time_hi_and_version[0] |= 0x40; |
431 | 0 | urn += time_hi_and_version.hex(); |
432 | |
|
433 | 0 | urn += "-"; |
434 | |
|
435 | 0 | Data clock_seq_hi_and_reserved = Random::getCryptoRandom(1); |
436 | 0 | clock_seq_hi_and_reserved[0] &= 0x3f; |
437 | 0 | clock_seq_hi_and_reserved[0] |= 0x40; |
438 | 0 | urn += clock_seq_hi_and_reserved.hex(); |
439 | |
|
440 | 0 | urn += getCryptoRandomHex(1); // clock-seq-low |
441 | 0 | urn += "-"; |
442 | 0 | urn += getCryptoRandomHex(6); // node |
443 | 0 | return urn; |
444 | 0 | } |
445 | | |
446 | | void |
447 | | Random::getCryptoRandom(unsigned char* buf, unsigned int numBytes) |
448 | 0 | { |
449 | 0 | resip_assert(numBytes < Random::maxLength+1); |
450 | |
|
451 | | #if USE_OPENSSL |
452 | | initialize(); |
453 | | int e = RAND_bytes( (unsigned char*)buf , numBytes ); |
454 | | if ( e < 0 ) |
455 | | { |
456 | | // error of some type - likely not enough rendomness to dod this |
457 | | long err = ERR_get_error(); |
458 | | |
459 | | char buf[1024]; |
460 | | ERR_error_string_n(err,buf,sizeof(buf)); |
461 | | |
462 | | ErrLog( << buf ); |
463 | | resip_assert(0); |
464 | | } |
465 | | #else |
466 | | // !bwc! Should optimize this. |
467 | 0 | Data temp=Random::getRandom(numBytes); |
468 | 0 | memcpy(buf, temp.data(), numBytes); |
469 | 0 | #endif |
470 | 0 | } |
471 | | |
472 | | #ifdef WIN32 |
473 | | Random::Initializer::Initializer() : mThreadStorage(::TlsAlloc()) |
474 | | { |
475 | | resip_assert(mThreadStorage != TLS_OUT_OF_INDEXES); |
476 | | } |
477 | | |
478 | | Random::Initializer::~Initializer() |
479 | | { |
480 | | ::TlsFree(mThreadStorage); |
481 | | } |
482 | | |
483 | | void |
484 | | Random::Initializer::setInitialized() |
485 | | { |
486 | | ::TlsSetValue(mThreadStorage, (LPVOID) TRUE); |
487 | | } |
488 | | |
489 | | bool |
490 | | Random::Initializer::isInitialized() |
491 | | { |
492 | | // Note: if value is not set yet then 0 (false) is returned |
493 | | return (BOOL) ::TlsGetValue(mThreadStorage) == TRUE; |
494 | | } |
495 | | #endif |
496 | | |
497 | | |
498 | | /* ==================================================================== |
499 | | * The Vovida Software License, Version 1.0 |
500 | | * |
501 | | * Copyright (c) 2005. All rights reserved. |
502 | | * |
503 | | * Redistribution and use in source and binary forms, with or without |
504 | | * modification, are permitted provided that the following conditions |
505 | | * are met: |
506 | | * |
507 | | * 1. Redistributions of source code must retain the above copyright |
508 | | * notice, this list of conditions and the following disclaimer. |
509 | | * |
510 | | * 2. Redistributions in binary form must reproduce the above copyright |
511 | | * notice, this list of conditions and the following disclaimer in |
512 | | * the documentation and/or other materials provided with the |
513 | | * distribution. |
514 | | * |
515 | | * 3. The names "VOCAL", "Vovida Open Communication Application Library", |
516 | | * and "Vovida Open Communication Application Library (VOCAL)" must |
517 | | * not be used to endorse or promote products derived from this |
518 | | * software without prior written permission. For written |
519 | | * permission, please contact vocal@vovida.org. |
520 | | * |
521 | | * 4. Products derived from this software may not be called "VOCAL", nor |
522 | | * may "VOCAL" appear in their name, without prior written |
523 | | * permission of Vovida Networks, Inc. |
524 | | * |
525 | | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
526 | | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
527 | | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND |
528 | | * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA |
529 | | * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES |
530 | | * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, |
531 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
532 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
533 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
534 | | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
535 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
536 | | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
537 | | * DAMAGE. |
538 | | * |
539 | | * ==================================================================== |
540 | | * |
541 | | * This software consists of voluntary contributions made by Vovida |
542 | | * Networks, Inc. and many individuals on behalf of Vovida Networks, |
543 | | * Inc. For more information on Vovida Networks, Inc., please see |
544 | | * <http://www.vovida.org/>. |
545 | | * |
546 | | * vi: set shiftwidth=3 expandtab: |
547 | | */ |