View Issue Details

IDProjectCategoryView StatusLast Update
0000678LDMud 3.3Compilation, Installationpublic2011-01-11 09:13
Reporterzesstra Assigned Tozesstra  
PriorityimmediateSeveritymajorReproducibilityN/A
Status resolvedResolutionfixed 
Product Version3.3.719 
Target Version3.3.720Fixed in Version3.3.720 
Summary0000678: Unreliable source of entropy for initializing the openssl PRNG
DescriptionI just had a look at tls_global_init() in pkg-openssl.c. The PRNG from openssl is initialized by using the following data:
        data1.uname_1 = uname(&data1.uname);
        data1.uname_2 = errno; /* Let's hope that uname fails randomly :-) */

        data1.uid = getuid();
        data1.euid = geteuid();
        data1.gid = getgid();
        data1.egid = getegid();

        RAND_seed((const void *)&data1, sizeof data1);

        data2.pid = getpid();
        data2.time = time(NULL);
        data2.stack = (void *)&data2;

        RAND_seed((const void *)&data2, sizeof data2);

This seems to me not a particularly good way to do this. data1.uid, data1.euid, data1.gid, data1.egid are constant and may be known. data2.stack, data1.uname_1 and data1.uname_2 are probably constant as well and maybe also guessable. data2.time can be easily guessed, even by players. So there remains the PID and data1.uname. One could hope, that in .uname is enough trailing, more or less random, garbage, but is that assumption right?

From three gdb sessions:
(gdb) print data1.uname
$1 = {sysname = "Linux", '\0' <repeats 59 times>,
  nodename = "mg\000ne)", '\0' <repeats 58 times>,
  release = "2.6.18-6-686", '\0' <repeats 52 times>,
  version = "0000001 SMP Tue Jun 17 21:31:27 UTC 2008", '\0' <repeats 29 times>,
  machine = "i686", '\0' <repeats 60 times>,
  __domainname = "(none)", '\0' <repeats 58 times>}

(gdb) print data1.uname
$1 = {sysname = "Linux", '\0' <repeats 59 times>,
  nodename = "mg\000ne)", '\0' <repeats 58 times>,
  release = "2.6.18-6-686", '\0' <repeats 52 times>,
  version = "0000001 SMP Tue Jun 17 21:31:27 UTC 2008", '\0' <repeats 29 times>,
  machine = "i686", '\0' <repeats 60 times>,
  __domainname = "(none)", '\0' <repeats 58 times>}

(gdb) print data1.uname
$1 = {sysname = "Linux", '\0' <repeats 59 times>,
  nodename = "mg\000ne)", '\0' <repeats 58 times>,
  release = "2.6.18-6-686", '\0' <repeats 52 times>,
  version = "0000001 SMP Tue Jun 17 21:31:27 UTC 2008", '\0' <repeats 29 times>,
  machine = "i686", '\0' <repeats 60 times>,
  __domainname = "(none)", '\0' <repeats 58 times>}

Seems not.

Am I just not seeing something? If not, we should change this ASAP.

TagsNo tags attached.

Activities

zesstra

2009-09-21 15:41

administrator   ~0001288

OK, the situation is a follows:

1) we add very little entropy to the state.
2) on platform with /dev/urandom, /dev/random, /dev/srandom OpenSSL implicitly uses /dev/urandom (with /dev/(s)random as fallback) to add 32 bytes of entropy to its state.
3) OpenSSL implicitly adds uid, pid and time to its state, so our addition of these three is redundant and does not help.

So, we are somehow OK (although 32 bytes seem not very much to me) on Linux & Co (MacOS included). On Windows OpenSSL is said to use something called CryptoAPI to find entropy which it adds automatically, but I don't know that for sure.
On some platforms (but which, is the question now), using TLS currently sucks!

There is one big nasty problem: OpenSSL warns and refuses to work, if there too little entropy in its state. BUT: It gathers the amount of entropy from RAND_add(buf, len, entropy), so it is user-supplied. Even worse: the RAND_seed() we use, is just an alias to RAND_add(buf, len, len), so it assumes every byte contains 8 bit of entropy. This is abviously wrong (nearly always), but especially in case of our very weak source of entropy. So by seeding the PRNG in this way, we disable OpenSSL's safeguard, because it always thinks, it has enough entropy. And on platforms with strong source of entropy which OpenSSL uses automatically, this puts our users at risk.

So, I see several possibilities:
1) Find a better source of entropy. This would be great and solve all our problems. But it is also very difficult to do so. (suggestions?)
2) Use RAND_add(buf, len, 0.0) in our initialization. This adds our very little entropy, but it won't cause OpenSSL to falsely think, it has enough, even if it has no other source. So it will fail, which is good.
3) Remove seeding/adding to OpenSSL's entropy pool altogether. Same as 2), it will fail, if there is no other strong source of entropy.

Opinions?

BTW: Maybe we should write a short comment about this on ldmud-talk...? Just in case, somebody uses LDMud+OpenSSL for TLS on a vulnerable platform?

zesstra

2009-09-21 17:55

administrator   ~0001289

Newer versions of OpenSSL use besides /dev/urandom, /dev/random and /dev/srandom also the sockets of the EGD (entroy gathering daemon).
After a short discusson with Gnomi and Coogan I think, I will implement option 3, check the seed status during tls_global_init() and disable TLS, if OpenSSL didn't get enough entropy automagically.
On platforms where this happens, we can deal on demand with the bug reports.
At least in this way, there should not be a false sense of security.

zesstra

2009-09-22 15:48

administrator   ~0001291

Another request for comments:
RAND_poll() is the function OpenSSL has for automagically get entropy from /dev/random & EGD. It can also be called by applications. I thought about calling that every i TLS connections (e.g. 50), to add some fresh entropy to the mix. RAND_poll waits for maximal 10ms for each device (so 30ms) and stops once it got 32 bytes. And the non-blocking urandom is tried first. So I think, the impact would not be significant.
What do you think?

BTW: As far as I understand, if you create a symmetric key with 128 bits, it would be best to use 128 bit of real entropy for this. In practice we are safe with less I guess. OpenSSL uses a PRNG which it seeds with 32 bytes (ideally 256 bits). Now, the question is: should you add new entropy after generating two 128bit long symmetric keys...?

zesstra

2009-10-07 09:29

administrator   ~0001514

Gnomi and I discussed last week shortly about the possibility of DOS by many new connections to cause the driver to poll /dev/*random very often per second. Although you will need about 30*50 new TLS connections per second for the given example, we might introduce a time limit for adding entropy (e.g. not more often than every 300 seconds). Or forget it whole. But with uptimes of hundreds of days, I would feel more comfortable to add some entropy in between.

I had a look at postfix TLS documentation. They state, the initial entropy of 32 bytes (256 bits) is sufficient for at 128 or 168 bit session key. They add entropy from the external random device by default every random(3600) s, add the time of day everytime somebody uses tlsmgr and update it periodically with TLS session cache entries (don't know about that period).

zesstra

2011-01-11 00:23

administrator   ~0001948

I now introduced the following:
a) for each new connection the gettimeofday() is added to the entropy pool of OpenSSL before the TLS connection is initialized.
b) the first connection after PRNG_RESEED_PERIOD/2 + random(PRNG_RESEED_PERIOD) seconds since the last call to RAND_poll causes additionally a call to RAND_poll() from OpenSSL, which will use a system-dependent source of strong entropy to add fresh entropy to its pool.
PRNG_RESEED_PERIOD defaults to 1800. For now, it is a compile-time constant.

zesstra

2011-01-11 09:13

administrator   ~0001949

Ok, then this is fixed for 3.3 and 3.5.
If there platforms where RAND_poll() does not work properly or there are other problems, please file a report.

Issue History

Date Modified Username Field Change
2009-09-19 16:27 zesstra New Issue
2009-09-21 15:41 zesstra Note Added: 0001288
2009-09-21 15:41 zesstra View Status private => public
2009-09-21 17:55 zesstra Note Added: 0001289
2009-09-21 17:55 zesstra Status new => assigned
2009-09-21 17:55 zesstra Assigned To => zesstra
2009-09-22 15:48 zesstra Note Added: 0001291
2009-10-07 09:29 zesstra Note Added: 0001514
2011-01-11 00:23 zesstra Note Added: 0001948
2011-01-11 00:35 zesstra Source_changeset_attached => ldmud.git master ac643847
2011-01-11 00:35 zesstra Source_changeset_attached => ldmud.git master 5b01a5ee
2011-01-11 00:53 zesstra Source_changeset_attached => ldmud.git master-3.3 4519ceda
2011-01-11 00:54 zesstra Source_changeset_attached => ldmud.git master-3.3 a7db0267
2011-01-11 09:12 zesstra Project LDMud => LDMud 3.3
2011-01-11 09:13 zesstra Note Added: 0001949
2011-01-11 09:13 zesstra Status assigned => resolved
2011-01-11 09:13 zesstra Resolution open => fixed
2011-01-11 09:13 zesstra Category Implementation => Compilation, Installation
2011-01-11 09:13 zesstra Product Version => 3.3.719
2011-01-11 09:13 zesstra Fixed in Version => 3.3.720
2011-01-11 09:13 zesstra Target Version => 3.3.720