Coverage Report

Created: 2026-09-14 06:22

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