-
Notifications
You must be signed in to change notification settings - Fork 1k
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
random_seed.c: prefer getrandom() / /dev/{,u}random over arc4random() #832
base: master
Are you sure you want to change the base?
Conversation
arc4random(3) is problematic to use during early boot, as it (as implemented by glibc or libbsd) uses getrandom(2) without the GRND_NONBLOCK flag, causing it to block until the kernel entropy pool is initialized, which may take a very long time. One example of this is cryptsetup(8), which may be used for bringing up a LUKS encrypted root filesystem during early boot. So instead prefer getrandom(.., GRND_NONBLOCK) (which will fail before the pool is initialized) and /dev/urandom (which will succeed, but cause the kernel to print a warning about access before the pool is initialized) before falling back to arc4random(). Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
Rather than fiddling around with the order in json_c_get_random_seed() to try to guess at what will work for all users of the json-c API, I feel like it's be better to provide a way for programs that know they're going to run in environments where random number generation is broken to explicitly adjust the behavior. e.g. add something like a |
That is also fine, but significantly more effort (E.G. new json-c release, affected reverse dependencies have to (conditionally) add the call, new releases of those projects and integration in distributions / build systems. What was the reason exactly for adding (and preferring) arc4random() in the first place? From the https://github.com/json-c/json-c/blob/master/ChangeLog#L83-L85 I see:
So there was already focus on the cryptsetup use case when it was added. How important is the quality of the random seed? From a quick look it seems as if it is only used for the hash table lookups, E.G. unlikely to be an issue for early userspace. |
Yes, it's "just" to initialize the hash function to make it more difficult for an attacker to generate content that would be subject to collisions, thus making a DoS more likely. Actually, looking at what arc4random actually does, it seems like the right fix for There's not much point in moving arc4random to the end of the list of things we try in json-c, because we'd effectively never end up using it. Perhaps we can detect whether the arc4random that's available is from a "too early" libbsd, and just not try to use it at all in that case, though I don't actually know a reliable way to do that offhand. |
Sorry, but I don't quite get that. All the different interfaces to random numbers (on Linux at least) boil down to the same thing (the kernel random number generator). They do not return "better" or "worse" random numbers, they only differ in the behaviour when the kernel entropy pool is not yet initialized or empty:
So I don't quite get why we should prefer arc4random(). If it is for non-Linux setups, then they won't have the getrandom() and /dev/{,u}random interfaces, so the ordering doesn't matter. Using arc4random() from a newer version of libbsd on Linux would just mean that it would end up calling getrandom(,, GRND_NONBLOCK) which would return -1, and it would fall back to using /dev/urandom, exactly as my proposed patch. |
@hawicz ping? |
@hawicz ping? How can we make progress on this? |
@hawicz it would be great to get this merged, thanks |
@hawicz ping? |
The reason I prefer to keep arc4random first is so json-c makes use of the code that is purpose built to do randomness generation correctly, and thus users of json-c gain a bit of system-wide consistency in how random numbers are generated, and I don't need to think as much about fallbacks or relative suitability and order of the other mechanisms that random_seed.c might call. |
On Linux, arc4random, getrandom, /dev/{u,}random all get their data from the same entropy pool, so there is no difference in the "quality" of the output. In Glibc and libbsd, arc4random is implemented using getrandom(). The big problem with arc4random() is that it is isn't suitable for early user space because it doesn't take any flags / blocks for a (potentially very long time) for the kernel entropy pool to be filled (E.G. it calls getrandom() without the GRND_NONBLOCK flags):
For a lot (most?) JSON-C users during early boot, calling getrandom(, GRND_NONBLOCK) (which fails fast with -1) and then falling back to /dev/urandom (which succeeds, even though theoretically the "randomness" cannot be guaranteed yet) is very much preferably to hanging for a very long time until arc4random() returns, as the hash collision problem the random seed is supposed to protect against is not an issue (E.G. cryptsetup uses JSON-C to read a JSON formatted configuration file of a few KB). Once early boot is completed and the kernel entropy pool is initialized, calling getrandom() or arc4random() is completely equivalent. The difference is only about early user space where arc4random() is not suitable and getrandom() / dev/urandom is. arc4random() is arguably a legacy function from BSD, so I am surprised to hear that you think it is the popular choice, except that it is a bit more portable given libbsd. |
@hawicz ping? |
@hawicz Can we make progress on this issue please? |
what can I do to make progress on this? |
@hawicz ping? |
arc4random(3) is problematic to use during early boot, as it (as implemented by glibc or libbsd) uses getrandom(2) without the GRND_NONBLOCK flag, causing it to block until the kernel entropy pool is initialized, which may take a very long time.
One example of this is cryptsetup(8), which may be used for bringing up a LUKS encrypted root filesystem during early boot.
So instead prefer getrandom(.., GRND_NONBLOCK) (which will fail before the pool is initialized) and /dev/urandom (which will succeed, but cause the kernel to print a warning about access before the pool is initialized) before falling back to arc4random().