Coverage Report

Created: 2023-06-07 06:03

/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
 */