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

Use something better than /dev/urandom on FreeBSD #326

Closed
briansmith opened this issue Nov 1, 2016 · 20 comments
Closed

Use something better than /dev/urandom on FreeBSD #326

briansmith opened this issue Nov 1, 2016 · 20 comments

Comments

@briansmith
Copy link
Owner

The main drivers here are performance and the ability to work correctly and automatically in a chroot/jail.

This was split off from #316, which is now OpenBSD-only. Note that iOS and MacOS are issue #149.

See:

From reading various FreeBSD mailing list messages, it seems like FreeBSD doesn't have a good way to guarantee fork-safety, which is the same problem that Linux has. Therefore, it seems like we should always be getting random values from the OS. That means, AFAICT, either reading from /dev/[u]random or the KERN_ARND sysctl. Note the potential problems with KERN_ARND mentioned in the linked-to comments above.

I don't understand this issue fully, but it seems that FreeBSD has a RANDOM kernel module that can be disabled, and in that disabled state it may be problematic to do anything better than reading from /dev/[u]random: "RW mentioned kernels without RANDOM, being an awkward situation for which it seems necessary to fall back to the PRNG in userland." - https://lists.freebsd.org/pipermail/freebsd-security/2014-July/007869.html.

@briansmith
Copy link
Owner Author

@valpackett
Copy link

LibreSSL just uses arc4random. The rationale was, basically, "deal with it".

But they have added locking and fork detection using pthread_atfork on FreeBSD. The comment says that atfork fails silently "if a program does not link to -lthr". That's not a problem for a Rust library — just link to thr in build.rs! Cargo is nice :) So we do have a good way to guarantee fork-safety.

They also have an emulation of getentropy using KERN_ARND.

@briansmith
Copy link
Owner Author

Thanks!

They also have an emulation of getentropy using KERN_ARND.

This looks the most promising to me.

Probably we should check what libsodium does too.

@valpackett
Copy link

They have a complicated file full of #ifdefs, but this seems like the part we're interested in:

#if defined(__OpenBSD__) || defined(__CloudABI__)
# define HAVE_SAFE_ARC4RANDOM 1
#endif

It's interesting that CloudABI's arc4random is considered safe, but not FreeBSD's. CloudABI runs on FreeBSD using, I think, the same arc4random as FreeBSD ABI programs. But on CloudABI you can't just open /dev/random, so it's not like they have a choice :D

@briansmith
Copy link
Owner Author

https://svnweb.freebsd.org/base?view=revision&revision=317015

Replace the RC4 algorithm for generating in-kernel secure random
numbers with Chacha20. Keep the API, though, as that is what the
other *BSD's have done.

Use the boot-time entropy stash (if present) to bootstrap the
in-kernel entropy source.

@briansmith
Copy link
Owner Author

@valpackett
Copy link

Somehow I only just discovered this: the rand crate uses KERN_ARND sysctl!

@valpackett
Copy link

FreeBSD 12 (current dev branch) now has all the interfaces: getentropy(3), getrandom(2).

The getentropy implementation uses the getrandom syscall, but falls back to KERN_ARND if you install a new libc but keep an old kernel.

The getrandom syscall is faster than KERN_ARND but equivalent in quality (and thread/fork safety I guess??).

So I think dynamically probing for getentropy would make sense, but just using KERN_ARND like the rand crate does will still be fine.

@briansmith
Copy link
Owner Author

So I think dynamically probing for getentropy would make sense, but just using KERN_ARND like the rand crate does will still be fine.

I think it would be good to try to use getentropy if it is available (using weak linkage) and fallback to the /dev/urandom fallback if that default feature is enabled. That's what we'd have to do on macOS, IIUC, and I'd like to minimize the amount of BSD-specific code we have to maintain, since we have so few BSD users.

Remember:

The main drivers here are performance and the ability to work correctly and automatically in a chroot/jail.

IMO, it is OK if we only get these benefits on newer versions of BSD.

@briansmith
Copy link
Owner Author

That's what we'd have to do on macOS, IIUC

Oops, I forgot that we already use SecRandomCopyBytes on macOS to avoid this stuff. So, yeah, I agree that your suggestion sounds reasonable.

@briansmith
Copy link
Owner Author

FreeBSD now has getrandom() like Linux: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=194204#c17. See the documentation at https://www.freebsd.org/cgi/man.cgi?query=getrandom&sektion=2&manpath=freebsd-release-ports. So I think we can just use the Linux implementation on FreeBSD, without the /dev/urandom fallback.

@valpackett
Copy link

Yep, I mentioned that above :)

FreeBSD 12 (current dev branch) now has all the interfaces: getentropy(3), getrandom(2).

Now that 12 is RELEASE, more people have it, but some places are still on 11.x, so some fallback would be good

@briansmith
Copy link
Owner Author

Now that 12 is RELEASE, more people have it, but some places are still on 11.x, so some fallback would be good

I would like to eliminate the fallback as much as possible. It seems OK to me to treat OpenBSD and FreeBSD the same and use getentropy() for both now. If a FreeBSD 11 user shows up we can deal with that later.

@briansmith
Copy link
Owner Author

I would like to eliminate the fallback as much as possible. It seems OK to me to treat OpenBSD and FreeBSD the same and use getentropy() for both now. If a FreeBSD 11 user shows up we can deal with that later.

A customer asked for FreeBSD support and we don't know what versions of FreeBSD we need to support, so PR #874 restores the /dev/urandom-based implementation for FreeBSD.

@valpackett
Copy link

https://github.com/rust-random/getrandom currently uses getrandom with a fallback to sysctl(..,KERN_ARND) (which has been available for ages). Honestly it seems like that crate is the new way to go.

@briansmith
Copy link
Owner Author

@myfreeweb The first step to doing something better is getting CI set up so we can test FreeBSD. Any tips for getting FreeBSD running in an emulator that would work reasonably in CI?

@valpackett
Copy link

I think Rust itself uses qemu on travis, but it's much easier to set up native CI with https://builds.sr.ht or https://cirrus-ci.org/guide/FreeBSD/

@cemeyer
Copy link

cemeyer commented Nov 23, 2019

I can maybe add some context. I added getrandom() and getentropy() to FreeBSD.

The getrandom syscall, without NONBLOCK, is essentially the same as reading from the kernel /dev/[u]random device. On FreeBSD, getentropy() is just a thin libc shim around getrandom().

The userspace arc4random() API family was replaced by the OpenBSD Chacha implementation in 12.0. (The related OpenBSD issue had some confusion because the FreeBSD kernel arc4random was replaced with Chacha much earlier than our userspace API.) Fork detection is passive, using the same / similar minherit(INHERIT_ZERO) mechanism to have the kernel zero out RNG state in the fork child; there's no longer any use of pthread_atfork or getpid() to detect fork. The arc4random APIs periodically invoke getentropy() to rekey, but otherwise are just generating Chacha keystream. So on 12.0+ it's a good RNG, and fork-safe. But I wouldn't use it pre-12.0, when it was still based on rc4.

So, I'm not sure what the goals of this crate's SystemRandom are. getrandom() is a good method to access the system random device on 12.0+. It is intended to be API-compatible with Linux. It's a direct syscall. KERN_ARND and /dev/urandom (as fallbacks) are also direct syscalls and good sources of entropy. (urandom requires an fd, of course.)

It would be extremely straightforward, and correct, to just use the existing Linux code on FreeBSD. IMO, it's slightly preferable to the status quo of just using the /dev/urandom device. (FreeBSD's urandom doesn't have Linux's problem of yielding weak entropy before initial seeding.)

I'd consider 11.x more or less obsolete, even if it's still ostensibly supported by the FreeBSD project, so it's probably fine to just require FreeBSD to have getrandom().

Using getentropy() or arc4random() would require linking libc, which might not be desirable for a Rust crate. And I'm not sure you want to expose a userspace C RNG anyway; it probably makes more sense to expose the system random device and implement a faster seeded generator in Rust, if you need that kind of throughput.

As far as DEV_RANDOM concerns mentioned somewhere upthread, it is no longer optional to have a system entropy device (and was never the default configuration anyone downloading an image and installing it would get).

Please let me know if I can answer any other questions!

cemeyer added a commit to cemeyer/ring that referenced this issue Nov 23, 2019
FreeBSD added the Linux-compatible getrandom(2) API in 12.0.  Like Linux,
prefer it to the /dev/urandom device, but fallback to the earlier method if
the syscall is not available.
@briansmith
Copy link
Owner Author

See the draft PR #1531 where I propose we delegate all this to the getrandom crate.

@briansmith
Copy link
Owner Author

PR #1531 was merged, which switched to using getrandom on FreeBSD, so I'm closing this issue.

Also, FreeBSD users, please check out PR #1542, which further improves support for FreeBSD.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants