Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Crash] RAND_load_file() with specific file-sizes #7449

Closed
gvanem opened this issue Oct 20, 2018 · 12 comments
Closed

[Crash] RAND_load_file() with specific file-sizes #7449

gvanem opened this issue Oct 20, 2018 · 12 comments
Assignees
Milestone

Comments

@gvanem
Copy link

gvanem commented Oct 20, 2018

Some file-sizes given to this command:

openssl.exe rand -rand rand-file -hex 10

crashes in some mysterious ways while freeing the associated RAND_POOL. The call-stack from WinDbg:

ntdll!RtlReportCriticalFailure+0x88
ntdll!RtlpReportHeapFailure+0x2f
ntdll!RtlpHpHeapHandleError+0x6e
ntdll!RtlpLogHeapFailure+0x41
ntdll!RtlFreeHeap+0x4cbbc
ucrtbase!_free_base+0x1b
ucrtbase!free+0x18
libcrypto_1_1!CRYPTO_free(void * str = 0x011d0000, char * file = 0x6b77f9c4 "crypto/rand/rand_lib.c", int line = 0n506)+0x45 [f:\mingw32\src\inet\crypto\openssl\crypto\mem.c @ 307] 
libcrypto_1_1!rand_pool_free(struct rand_pool_st * pool = 0x075a84b0)+0x36 [f:\mingw32\src\inet\crypto\openssl\crypto\rand\rand_lib.c @ 506] 
libcrypto_1_1!rand_drbg_restart(struct rand_drbg_st * drbg = 0x0804daf8, unsigned char * buffer = 0x010fe4f8 "", unsigned int len = 0x16, unsigned int entropy = 0xb0)+0x1cd [f:\mingw32\src\inet\crypto\openssl\crypto\rand\drbg_lib.c @ 632] 
libcrypto_1_1!drbg_add(void * buf = 0x010fe4f8, int num = 0n22, double randomness = 22)+0x8b [f:\mingw32\src\inet\crypto\openssl\crypto\rand\drbg_lib.c @ 1062] 
libcrypto_1_1!RAND_add(void * buf = 0x010fe4f8, int num = 0n22, double randomness = 22)+0x2b [f:\mingw32\src\inet\crypto\openssl\crypto\rand\rand_lib.c @ 783] 
libcrypto_1_1!RAND_load_file(char * file = 0x076233c1 "rand-file", long bytes = 0n-1)+0x186 [f:\mingw32\src\inet\crypto\openssl\crypto\rand\randfile.c @ 142] 
openssl!loadfiles+0x2b [f:\mingw32\src\inet\crypto\openssl\apps\app_rand.c @ 46] 
openssl!opt_rand(int opt = 0n1501)+0x7b [f:\mingw32\src\inet\crypto\openssl\apps\app_rand.c @ 85] 
openssl!rand_main(int argc = 0n5, char ** argv = 0x0762338c)+0xb3 [f:\mingw32\src\inet\crypto\openssl\apps\rand.c @ 68] 
openssl!do_cmd(struct lhash_st_FUNCTION * prog = 0x07624cc0, int argc = 0n5, char ** argv = 0x0762338c)+0x68 [f:\mingw32\src\inet\crypto\openssl\apps\openssl.c @ 620] 
openssl!main(int argc = 0n0, char ** argv = 0x011e45b8)+0x24d 

I created the rand-file using CygWin's truncate:

Command Result
truncate.exe -s 10000000 rand-file OKAY
truncate.exe -s 10000 rand-file OKAY
truncate.exe -s 1000 rand-file OKAY
truncate.exe -s 1046 rand-file not OKAY
truncate.exe -s 2048 rand-file OKAY

I'm on Windows-10 using MSVC-2017.
Some other discussion of this issue is here too:
3064b55#commitcomment-30980301

gvanem referenced this issue Oct 20, 2018
In pull request #4328 the seeding of the DRBG via RAND_add()/RAND_seed()
was implemented by buffering the data in a random pool where it is
picked up later by the rand_drbg_get_entropy() callback. This buffer
was limited to the size of 4096 bytes.

When a larger input was added via RAND_add() or RAND_seed() to the DRBG,
the reseeding failed, but the error returned by the DRBG was ignored
by the two calling functions, which both don't return an error code.
As a consequence, the data provided by the application was effectively
ignored.

This commit fixes the problem by a more efficient implementation which
does not copy the data in memory and by raising the buffer the size limit
to INT32_MAX (2 gigabytes). This is less than the NIST limit of 2^35 bits
but it was chosen intentionally to avoid platform dependent problems
like integer sizes and/or signed/unsigned conversion.

Additionally, the DRBG is now less permissive on errors: In addition to
pushing a message to the openssl error stack, it enters the error state,
which forces a reinstantiation on next call.

Thanks go to Dr. Falko Strenzke for reporting this issue to the
openssl-security mailing list. After internal discussion the issue
has been categorized as not being security relevant, because the DRBG
reseeds automatically and is fully functional even without additional
randomness provided by the application.

Fixes #7381

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #7382)
@mspncp
Copy link
Contributor

mspncp commented Oct 20, 2018

I can now reproduce the problem with Visual Studio on Windows.

@mspncp mspncp self-assigned this Oct 20, 2018
@mspncp
Copy link
Contributor

mspncp commented Oct 20, 2018

This should fix your crash, but it's only half of the problem. I will open a pull request, but it will take little while because I have to leave now.

diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c
index f40c513ce8..52e2621948 100644
--- a/crypto/rand/rand_lib.c
+++ b/crypto/rand/rand_lib.c
@@ -200,6 +200,9 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
     }

  err:
+    if (ret == 0 && drbg->pool != NULL)
+        drbg->pool = NULL;
+
     rand_pool_free(pool);
     return ret;
 }

@gvanem
Copy link
Author

gvanem commented Oct 20, 2018

That fix works fine (no crashes with any sizes I've tried). Why is it only half of the problem?

@mspncp
Copy link
Contributor

mspncp commented Oct 20, 2018

Actually the first patch was only part one of three:

  • Part one fixes the memory corruption after the reseeding failed
  • Part two adds an error message (see patch below)
  • Part three fixes the true problem: RAND_add() fails if the buffer size is less than 32 (work in progress...)

More details in the upcoming pull request. Please be patient, I'm a hobby committer... ;-)

For the moment, I would ask you to apply this second patch and try whether openssl rand now returns a useful error message. You should get it whenever the randfile size is k*1024 + r with r<32, in particular for all sizes < 32.

commit bafbab69b7cb43a0e13feeded86f47e703a40ab8 (HEAD -> master)
Author: Dr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Date:   Sat Oct 20 16:53:57 2018 +0200

    RAND_load_file(): return error if reseeding failed

diff --git a/crypto/rand/randfile.c b/crypto/rand/randfile.c
index 89720eb5cf..028c1281c0 100644
--- a/crypto/rand/randfile.c
+++ b/crypto/rand/randfile.c
@@ -148,6 +148,12 @@ int RAND_load_file(const char *file, long bytes)

     OPENSSL_cleanse(buf, sizeof(buf));
     fclose(in);
+    if (!RAND_status()) {
+        RANDerr(RAND_F_RAND_LOAD_FILE, RAND_R_RESEED_ERROR);
+        ERR_add_error_data(2, "Filename=", file);
+        return -1;
+    }
+
     return ret;
 }

@mspncp
Copy link
Contributor

mspncp commented Oct 20, 2018

Note: although the seeding fails in your case, the DRBG will automatically recover from the error in the next RAND_bytes() call. So the output of openssl rand can be trusted. But of course the failure needs to be fixed.

@gvanem
Copy link
Author

gvanem commented Oct 20, 2018

For the moment, I would ask you to apply this second patch and try whether openssl rand now
returns a useful error message.

After applying all your patches and trying with a

truncate -s 2055 rand-file
openssl rand rand-file -hex 10

gives:

Can't load rand-file into RNG
105724:error:2407307D:random number generator:rand_pool_bytes_needed:random pool overflow:crypto/rand/rand_lib.c:609:
105724:error:2406E06E:random number generator:RAND_DRBG_reseed:error retrieving entropy:crypto/rand/drbg_lib.c:508:
105724:error:2406F076:random number generator:RAND_load_file:reseed error:crypto/rand/randfile.c:157:Filename=rand-file

No crash anymore, but I've no idea why the size should matter!?

@mspncp
Copy link
Contributor

mspncp commented Oct 20, 2018

I've no idea why the size should matter!?

Because it's a bug which I introduced in 3064b55 :-)

@mspncp
Copy link
Contributor

mspncp commented Oct 20, 2018

(Hint: 32 bytes corresponds to 256 bits which is the security strength of the DRBG.)

mspncp added a commit to mspncp/openssl that referenced this issue Oct 21, 2018
This bug was introduced by openssl#7382 which enhanced RAND_add() to
accept large buffer sizes. As a consequence, RAND_add() now fails
for buffer sizes less than 32 bytes (i.e. less than 256 bits).
In addition, rand_drbg_get_entropy() forgets to reset the attached
drbg->pool in the case of an error, which leads to the heap corruption.

The problem occurred with RAND_load_file(), which reads the file in
chunks of 1024 bytes each. If the size of the final chunk is less than
32 bytes, then RAND_add() fails, whence RAND_load_file() fails
silently for buffer sizes n = k * 1024 + r with r = 1,...,31.

This commit fixes the heap corruption only. The other issues will
be addressed in a separate pull request.

Thanks to Gisle Vanem for reporting this issue.

Fixes openssl#7449
@mspncp
Copy link
Contributor

mspncp commented Oct 21, 2018

See #7455 for the immediate fix and #7456 for the full story...

@gvanem
Copy link
Author

gvanem commented Oct 22, 2018

Good job. Feel free to close this issue when you wish.

@mspncp
Copy link
Contributor

mspncp commented Oct 22, 2018

Thank you very much for your appreciation. But actually I can return the compliment because your report pointed me to a whole bunch of minor problems.

Don't worry about the issue, it will be closed automatically when the PR is merged.

Regards,
Matthias

levitte pushed a commit that referenced this issue Oct 22, 2018
This bug was introduced by #7382 which enhanced RAND_add() to
accept large buffer sizes. As a consequence, RAND_add() now fails
for buffer sizes less than 32 bytes (i.e. less than 256 bits).
In addition, rand_drbg_get_entropy() forgets to reset the attached
drbg->pool in the case of an error, which leads to the heap corruption.

The problem occurred with RAND_load_file(), which reads the file in
chunks of 1024 bytes each. If the size of the final chunk is less than
32 bytes, then RAND_add() fails, whence RAND_load_file() fails
silently for buffer sizes n = k * 1024 + r with r = 1,...,31.

This commit fixes the heap corruption only. The other issues will
be addressed in a separate pull request.

Thanks to Gisle Vanem for reporting this issue.

Fixes #7449

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #7455)

(cherry picked from commit 5b4cb38)
@mspncp
Copy link
Contributor

mspncp commented Oct 22, 2018

Okay... the notification page tells me that the closing hook was issued, but it's still open. Probably because GitHub is still in confusion... Well, then I'll close it myself.

@mspncp mspncp added this to the 1.1.1a milestone Oct 23, 2018
mspncp added a commit to mspncp/openssl that referenced this issue Oct 25, 2018
The failure of RAND_load_file was only noticed because of the
heap corruption which was reported in openssl#7499 and fixed in commit
5b4cb38. To prevent this in the future, RAND_load_file()
now explicitly checks RAND_status() and reports an error if it
fails.

Related-to openssl#7449
mspncp added a commit to mspncp/openssl that referenced this issue Oct 25, 2018
Increase the load buffer size such that it exceeds the chunk
size by a comfortable amount. This is done to avoid calling
RAND_add() with a small final chunk. Instead, such a small
final chunk will be added together with the previous chunk
(unless it's the only one).

Related-to openssl#7449
mspncp added a commit to mspncp/openssl that referenced this issue Oct 25, 2018
Commit 5b4cb38 (openssl#7382) introduced a bug which had the effect
that RAND_add()/RAND_seed() failed for buffer sizes less than
32 bytes. The reason was that now the added random data was used
exlusively as entropy source for reseeding. When the random input
was too short or contained not enough entropy, the DRBG failed
without querying the available entropy sources.

This commit makes drbg_add() act smarter: it checks the entropy
requirements explicitely. If the random input fails this check,
it won't be added as entropy input, but only as additional data.
More precisely, the behaviour depends on whether an os entropy
source was configured (which is the default on most os):

- If an os entropy source is avaible then we declare the buffer
  content as additional data by setting randomness to zero and
  trigger a regular   reseeding.

- If no os entropy source is available, a reseeding will fail
  inevitably. So drbg_add() uses a trick to mix the buffer contents
  into the DRBG state without forcing a reseeding: it generates a
  dummy random byte, using the buffer content as additional data.

Related-to openssl#7449
levitte pushed a commit that referenced this issue Oct 26, 2018
The failure of RAND_load_file was only noticed because of the
heap corruption which was reported in #7499 and fixed in commit
5b4cb38. To prevent this in the future, RAND_load_file()
now explicitly checks RAND_status() and reports an error if it
fails.

Related-to: #7449

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #7456)
levitte pushed a commit that referenced this issue Oct 26, 2018
Increase the load buffer size such that it exceeds the chunk
size by a comfortable amount. This is done to avoid calling
RAND_add() with a small final chunk. Instead, such a small
final chunk will be added together with the previous chunk
(unless it's the only one).

Related-to: #7449

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #7456)
levitte pushed a commit that referenced this issue Oct 26, 2018
Commit 5b4cb38 (#7382) introduced a bug which had the effect
that RAND_add()/RAND_seed() failed for buffer sizes less than
32 bytes. The reason was that now the added random data was used
exlusively as entropy source for reseeding. When the random input
was too short or contained not enough entropy, the DRBG failed
without querying the available entropy sources.

This commit makes drbg_add() act smarter: it checks the entropy
requirements explicitely. If the random input fails this check,
it won't be added as entropy input, but only as additional data.
More precisely, the behaviour depends on whether an os entropy
source was configured (which is the default on most os):

- If an os entropy source is avaible then we declare the buffer
  content as additional data by setting randomness to zero and
  trigger a regular   reseeding.

- If no os entropy source is available, a reseeding will fail
  inevitably. So drbg_add() uses a trick to mix the buffer contents
  into the DRBG state without forcing a reseeding: it generates a
  dummy random byte, using the buffer content as additional data.

Related-to: #7449

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #7456)
mspncp added a commit to mspncp/openssl that referenced this issue Oct 26, 2018
The failure of RAND_load_file was only noticed because of the
heap corruption which was reported in openssl#7499 and fixed in commit
5b4cb38. To prevent this in the future, RAND_load_file()
now explicitly checks RAND_status() and reports an error if it
fails.

Related-to: openssl#7449

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from openssl#7456)
levitte pushed a commit that referenced this issue Oct 26, 2018
Increase the load buffer size such that it exceeds the chunk
size by a comfortable amount. This is done to avoid calling
RAND_add() with a small final chunk. Instead, such a small
final chunk will be added together with the previous chunk
(unless it's the only one).

Related-to: #7449

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #7456)
levitte pushed a commit that referenced this issue Oct 27, 2018
Commit 5b4cb38 (#7382) introduced a bug which had the effect
that RAND_add()/RAND_seed() failed for buffer sizes less than
32 bytes. The reason was that now the added random data was used
exlusively as entropy source for reseeding. When the random input
was too short or contained not enough entropy, the DRBG failed
without querying the available entropy sources.

This commit makes drbg_add() act smarter: it checks the entropy
requirements explicitely. If the random input fails this check,
it won't be added as entropy input, but only as additional data.
More precisely, the behaviour depends on whether an os entropy
source was configured (which is the default on most os):

- If an os entropy source is avaible then we declare the buffer
  content as additional data by setting randomness to zero and
  trigger a regular   reseeding.

- If no os entropy source is available, a reseeding will fail
  inevitably. So drbg_add() uses a trick to mix the buffer contents
  into the DRBG state without forcing a reseeding: it generates a
  dummy random byte, using the buffer content as additional data.

Related-to: #7449

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #7456)

(cherry picked from commit 8817215)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants