Commit Graph

597 Commits

Author SHA1 Message Date
Samanta Navarro dcc90658fd xgetXXbyYY: Avoid duplicated error handling block
The error handling is performed after the loop. By just calling break it
is possible to reuse the error handling if status is not ERANGE.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-05-26 16:03:27 -05:00
Samanta Navarro e73a2194b3 xgetXXbyYY: Handle DUP_FUNCTION failure
A failure of DUP_FUNCTION is already handled for non-reentrant
function wrapper. Perform the check for reentrant version as well.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-05-26 16:03:27 -05:00
Samanta Navarro 6491fef1e0 libmisc: Use safer chroot/chdir sequence
OpenSSH and coreutils' chroot call chroot first and then chdir. Doing it
this way is a bit safer because otherwise something could happen between
chdir and chroot to the specified path (like exchange of links) so the
working directory would not end up within the chroot environment.

This is a purely defensive measure.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-05-25 08:25:42 -05:00
Samanta Navarro 4ad359ccc6 subsystem: Prevent endless loop
If a user has home directory "/" and login shell "*" then login and su
enter an endless loop by constantly switching to the next subsystem.

This could also be achieved with a layered approach so just checking
for "/" as home directory is not enough to protect against such a
misconfiguration.

Just break the loop if it progressed too far. I doubt that this has
negative impact on any real setup.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-05-25 08:25:42 -05:00
Samanta Navarro 812f934e77 process_prefix_flag: Drop privileges
Using --prefix in a setuid binary is quite dangerous. An unprivileged
user could prepare a custom shadow file in home directory. During a data
race the user could exchange directories with links which could lead to
exchange of shadow file in system's /etc directory.

This could be used for local privilege escalation.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-05-18 15:36:59 +02:00
Samanta Navarro 666468cc36 Remove some static char arrays
Some strings are first written into static char arrays before passed to
functions which expect a const char pointer anyway.

It is easier to pass these strings directly as arguments.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-05-11 11:05:29 -05:00
Samanta Navarro 7109b7c066 login_prompt: Simplify login_prompt API
The only user of login_prompt is the login tool. This implies that the
first argument is always the same.

It is much easier to verify printf's format string and its argument if
both are next to each other.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-05-03 07:54:28 -05:00
Samanta Navarro 3010ec11ae login_prompt: Use _exit in signal handler
Calling exit is not signal safe.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-05-03 07:54:28 -05:00
Samanta Navarro 8fc8de382a login_prompt: Do not parse environment variables
Parsing optional environment variables after a login name is a feature
which is neither documented nor available in util-linux or busybox
login which are other wide spread login utilities used in Linux
distributions as reference.

Removing this feature resolves two issues:

- A memory leak exists if variables without an equal sign are used,
  because set_env creates copies on its own. This could lead to OOM
  situations in privileged part of login or may lead to heap spraying.
- Environment variables are not reset between login attempts. This
  could lead to additional environment variables set for a user who
  never intended to do so.

Proof of Concept on a system with shadow login without PAM and
util-linux agetty:

1. Provoke an invalid login, e.g. user `noone` and password `invalid`.
   This starts shadow login and subsequent inputs are passed through
   the function login_prompt.
2. Provoke an invalid login with environment variables, e.g.
   user `noone HISTFILE=/tmp/owo` and password `invalid`.
3. Log in correctly with user `root`.

Now you can see with `echo $HISTFILE` that `/tmp/owo` has been set for
the root user.

This requires a malicious failed login attempt and a successful login
within the configured login timeout (default 60 seconds).

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-05-03 07:54:28 -05:00
Samanta Navarro c0fc4d2122 libmisc/yesno.c: Fix regression
The getline function does not return a pointer but the amount of read
characters. The error return value to check for is -1.

Set buf to NULL to avoid dereference of an uninitialized stack value.

The getline function returns -1 if size argument is NULL. Always use
a valid pointer even if size is unimportant.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-04-28 11:22:48 -05:00
Alejandro Colomar 0c4fa6ee0a libmisc, man: Drop old check and advice for complex character sets in passwords
Add the relevant XKCD to the passwd(1) manual page.  It already explains
most of the rationale behind this patch.

Add also reference to makepasswd(1), which is a good way to generate
strong passwords.  Personally, I commonly run `makepasswd --chars 64` to
create my passwords, or 32 for passwords I need to type interactively
often.

The strength of a password is an exponential formula, where the base is
the size of the character set, and the exponent is the length of the
password.  That already shows why long passwords of just lowercase
letters are better than short Pa$sw0rdZ3.  But an even more important
point is that humans, when forced to use symbols in a password, are more
likely to do trivial substitutions on simple passwords, which doesn't
increase strength, and can instead give a false sense of strength, which
is dangerous.

Closes: <https://github.com/shadow-maint/shadow/issues/688>
Link: <https://xkcd.com/936/>
Cc: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-04-27 09:16:08 +02:00
Alejandro Colomar e28deeb8e9 libmisc/yesno.c: Use getline(3) and rpmatch(3)
getline(3) is much more readable than manually looping.  It has some
overhead due to the allocation of a buffer, but that shouldn't be a
problem here.  If that was a problem, we could reuse the buffer (thus
making the function non-reentrant), but I don't think that's worth the
extra complexity.

Using rpmatch(3) instead of a simple y/n test provides i18n to the
response checking.  We have a fall-back minimalistic implementation for
systems that lack this function (e.g., musl libc).

While we're at it, apply some other minor improvements to this file:

-  Remove comment saying which files use this function.  That's likely
   to get outdated.  And anyway, it's just a grep(1) away, so it doesn't
   really add any value.

-  Remove unnecessary casts to (void) that were used to verbosely ignore
   errors from stdio calls.  They add clutter without really adding much
   value to the code (or I don't see it).

-  Remove comments from the function body.  They make the function less
   readable.  Instead, centralize the description of the function into a
   man-page-like comment before the function definition.  This keeps the
   function body short and sweet.

-  Add '#include <stdbool.h>', which was missing.

-  Minor whitespace style changes (it doesn't hurt the diff at this
   point, since most of the affected lines were already touched by other
   changes, so I applied my preferred style :).

Acked-by: Samanta Navarro <ferivoz@riseup.net>
Cc: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-04-26 17:32:47 -05:00
Samanta Navarro 0c83b98105 Read whole line in yes_or_no
Do not stop after 79 characters. Read the complete line to avoid
arbitrary limitations.

Proof of Concept:

```
cat > passwd-poc << EOF
root0:0:root:/root:/bin/bash
root0:0:root:/root:/bin/bash
root0:0:root:/root:/bin/bash
EOF
python -c "print(80*'y')" | pwck passwd-poc
```

Two lines should still be within the file because we agreed only once
to remove a duplicated line.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
2023-04-21 18:12:56 -05:00
Alejandro Colomar 1d7d94ed7d Simplify is_my_tty()
This commit will serve to document why we shouldn't worry about the
truncation in the call to strlcpy(3).  Since we have one more byte in
tmptty than in full_tty, truncation will produce a string that is at
least one byte longer than full_tty.  Such a string could never compare
equal, so we're actually handling the truncation in a clever way.  Maybe
too clever, but that's why I'm documenting it here.

Now, about the simplification itself:

Since we made sure that both full_tty and tmptty are null-terminated, we
can call strcmp(3) instead of strncmp(3).  We can also simplify the
return logic avoiding one branch.

Cc: Paul Eggert <eggert@cs.ucla.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
2023-03-28 13:00:38 +02:00
Alejandro Colomar e27ca53091 Fix is_my_tty() buffer overrun
*  libmisc/utmp.c (is_my_tty): Declare the parameter as a char array,
   not char *, as it is not necessarily null-terminated.
   Avoid a read overrun when reading 'tty', which comes from
   'ut_utname'.

Reported-by: Paul Eggert <eggert@cs.ucla.edu>
Co-developed-by: Paul Eggert <eggert@cs.ucla.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
2023-03-28 13:00:38 +02:00
Alejandro Colomar 03af2940f7 Fix crash with large timestamps
*  libmisc/date_to_str.c (date_to_str): Do not crash if gmtime(3)
   returns NULL because the timestamp is far in the future.

Reported-by: Paul Eggert <eggert@cs.ucla.edu>
Co-developed-by: Paul Eggert <eggert@cs.ucla.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
2023-03-28 13:00:38 +02:00
skyler-ferrante c089196e15 Fix null dereference in basename
On older kernels (<=linux-5.17), argv[0] can be null. Basename would
call strrchr with null if argc==0. Fixes issue #680
2023-03-27 10:10:37 -05:00
Christian Göttsche 37bf59067f Use strict prototype in definition
gettime.c:25:30: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes]
    /*@observer@*/time_t gettime ()
                                 ^
                                  void
2023-03-20 08:47:52 +01:00
Alejandro Colomar 7668f77439 Fix use-after-free of pointer after realloc(3)
We can't use a pointer that was input to realloc(3), nor any pointers
that point to reallocated memory, without making sure that the memory
wasn't moved.  If we do, the Behavior is Undefined.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar efbbcade43 Use safer allocation macros
Use of these macros, apart from the benefits mentioned in the commit
that adds the macros, has some other good side effects:

-  Consistency in getting the size of the object from sizeof(type),
   instead of a mix of sizeof(type) sometimes and sizeof(*p) other
   times.

-  More readable code: no casts, and no sizeof(), so also shorter lines
   that we don't need to cut.

-  Consistency in using array allocation calls for allocations of arrays
   of objects, even when the object size is 1.

Cc: Valentin V. Bartenev <vbartenev@gmail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar f332379ea0 Use xreallocarray() instead of its pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar 190a702225 Use reallocarrayf() instead of its pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar 191f04f7dc Use *array() allocation functions where appropriate
This prevents overflow from multiplication.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar 727275a027 Use xcalloc(3) instead of its pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar d81506de1e libmisc: Add safer allocation functions
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar 881c1d63a1 libmisc: Move xmalloc.c to alloc.c
We'll expand the contents in a following commit, so let's move the file
to a more generic name, have a dedicated header, and update includes.

Signed-off-by: Alejandro Colomar <alx@kernel.org>

Use the new header for xstrdup()

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar a578617cc0 Use calloc(3) instead of its pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar 1aa22c1467 Use reallocarray(3) instead of its pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-23 20:28:43 -06:00
Alejandro Colomar 5c5dc75641 libmisc: agetpass(): Fix bug detecting truncation
On 2/19/23 18:09, David Mudrich wrote:
> I am working on a RAM based Linux OS from source, and try to use
> latest versions of all software.  I found shadow needs libbsd's
> readpassphrase(3) as superior alternative to getpass(3).  While
> considering if I a) include libbsd, or include libbsd's code of
> readpassphrase(3) into shadow, found, that libbsd's readpassphrase(3)
> never returns \n or \r
> <https://cgit.freedesktop.org/libbsd/tree/src/readpassphrase.c>
> line 122, while agetpass() uses a check for \n in agetpass.c line 108.
> I assume it always fails.

Indeed, it always failed.  I made a mistake when writing agetpass(),
assuming that readpassphrase(3) would keep newlines.

>
> I propose a check of len == PASS_MAX - 1, with false positive error for
> exactly PASS_MAX - 1 long passwords.

Instead, I added an extra byte to the allocation to allow a maximum
password length of PASS_MAX (which is the maximum for getpass(3), which
we're replacing.

While doing that, I notice that my previous implementation also had
another bug (minor): The maximum password length was PASS_MAX - 1
instead of PASS_MAX.  That's also fixed in this commit.

Reported-by: David Mudrich <dmudrich@gmx.de>
Fixes: 155c9421b9 ("libmisc: agetpass(), erase_pass(): Add functions for getting passwords safely")
Cc: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-20 12:16:01 +01:00
Martin Kletzander baae5b4a06 find_new_[gu]id(): Skip over IDs that are reserved for legacy reasons
Some programs don't support `(uint16_t) -1` or `(uint32_t) -1` as user
or group IDs.  This is because `-1` is used as an error code or as an
unspecified ID, e.g. in `chown(2)` parameters, and in the past, `gid_t`
and `uid_t` have changed width.  For legacy reasons, those values have
been kept reserved in programs today (for example systemd does this; see
the documentation in the link below).

This should not be confused with catching overflow in the ID values,
since that is already caught by our ERANGE checks.  This is about not
using reserved values that have been reserved for legacy reasons.

Link: <https://systemd.io/UIDS-GIDS/>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
2023-02-20 12:10:02 +01:00
Samanta Navarro 0dfeb9e674 Fix comments
These comments should indicate which functions they really wrap.
An alternative would be to remove the line completely to avoid
future copy&paste mistakes.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-02-16 13:23:08 -06:00
Samanta Navarro c53b36fe85 Fix grammar
Use proper grammar (third-person singular).

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-02-16 13:23:08 -06:00
Samanta Navarro b8ea76ba72 Fix typo
It should be "if" not "is".

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-02-16 13:23:08 -06:00
Samanta Navarro d5d1932370 Fix typos
It is a user, not an user.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-02-16 13:23:08 -06:00
Alejandro Colomar 709e6b4497 Add stpecpy()
strncat(3), strlcpy(3), and many other functions are often misused for
catenating strings, when they should never be used for that.  strlcat(3)
is good.  However, there's no equivalent to strlcat(3) similar to
snprintf(3).  Let's add stpecpy(), which is similar to strlcat(3), but
it is also the only function compatible with stpeprintf(), which makes
it more useful than strlcat(3).

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-16 11:29:33 +01:00
Alejandro Colomar e0e9e57a72 Add mempcpy(3)
We'll use it for implementing stpecpy(), and may be interesting to have
it around.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-16 11:29:33 +01:00
Alejandro Colomar 46610792e9 Use stpeprintf() where appropriate
This function allows reducing error checking (since errors are
propagated across chained calls), and also simplifies the calculation of
the start and end of the buffer where the string should be written.

Moreover, the new code is more optimized, since many calls to strlen(3)
have been removed.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-16 11:29:33 +01:00
Alejandro Colomar 7e213cfb50 Add stpeprintf()
[v]stpeprintf() are similar to [v]snprintf(3), but they allow chaining.
[v]snprintf(3) are very dangerous for catenating strings, since the
obvious ways to do it invoke Undefined Behavior, and the ways that avoid
UB are very error-prone.

Cc: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-16 11:29:33 +01:00
Alejandro Colomar a187ad8e9e agetpass.c: Use SPDX tags
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-16 11:29:33 +01:00
Alejandro Colomar 5da8388fc6 ttytype(): Fix race
The intention of the code is just to not report an error message when
'typefile' doesn't exist.  If we call access(2) and then fopen(2),
there's a race.  It's not a huge problem, and the worst thing that can
happen is reporting an error when the file has been removed after
access(2).  It's not a problem, but we can fix the race and at the same
time clarify the intention of not warning about ENOENT and also remove
one syscall.  Seems like a win-win.

Suggested-by: Christian Göttsche <cgzones@googlemail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-09 10:03:03 -06:00
Alejandro Colomar bddcd9b095 Remove superfluous casts
-  Every non-const pointer converts automatically to void *.
-  Every pointer converts automatically to void *.
-  void * converts to any other pointer.
-  const void * converts to any other const pointer.
-  Integer variables convert to each other.

I changed the declaration of a few variables in order to allow removing
a cast.

However, I didn't attempt to edit casts inside comparisons, since they
are very delicate.  I also kept casts in variadic functions, since they
are necessary, and in allocation functions, because I have other plans
for them.

I also changed a few casts to int that are better as ptrdiff_t.

This change has triggered some warnings about const correctness issues,
which have also been fixed in this patch (see for example src/login.c).

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-09 10:03:03 -06:00
Alejandro Colomar 1f6f1669cf Remove superfluous casts to 'void*'
Every non-const pointer converts automatically to it.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-02 22:03:26 -06:00
Alejandro Colomar 62172f6fb5 Call NULL by its name
In variadic functions we still do the cast.  In POSIX, it's not
necessary, since NULL is required to be of type 'void *', and 'void *'
is guaranteed to have the same alignment and representation as 'char *'.
However, since ISO C still doesn't mandate that, and moreover they're
doing dubious stuff by adding nullptr, let's be on the cautious side.
Also, C++ requires that NULL is _not_ 'void *', but either plain 0 or
some magic stuff.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-02 13:08:30 -06:00
Alejandro Colomar 1482224c54 Use freezero(3) where suitable
It originated in OpenBSD, and is available in libbsd.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-02-02 12:04:28 +01:00
Samanta Navarro ffc480c2e9 Explicitly override only newlines
Override only newlines with '\0' to avoid undesired truncation of
actual line content.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-02-01 15:47:35 -06:00
Samanta Navarro 37ae232080 Correctly handle illegal system file in tz
If the file referenced by ENV_TZ has a zero length string, then an out
of boundary write occurs. Also the result can be wrong because it is
assumed that the file will always end with a newline.

Only override a newline character with '\0' to avoid these cases.

This cannot be considered to be security relevant because login.defs
and its contained references to system files should be trusted to begin
with.

Proof of Concept:

1. Compile shadow's su with address sanitizer and --without-libpam

2. Setup your /etc/login.defs to contain ENV_TZ=/etc/tzname

3. Prepare /etc/tzname to contain a '\0' byte at the beginning

`python -c "print('\x00')" > /etc/tzname`

4. Use su

`su -l`

You can see the following output:

`tz.c:45:8: runtime error: index 18446744073709551615 out of bounds for type 'char [8192]'`

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
2023-02-01 15:47:35 -06:00
Alejandro Colomar 2a61122b5e Unoptimize the higher part of the domain of csrand_uniform()
__int128, which is needed for optimizing that part of the range, is not
always available.  We need the unoptimized version for portability
reasons.

Closes: <https://github.com/shadow-maint/shadow/issues/634>
Fixes: 1a0e13f94e ("Optimize csrand_uniform()")
Reported-by: Adam Sampson <ats@offog.org>
Cc: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-01-30 18:24:15 +01:00
Alejandro Colomar 0712b236c3 Add bit manipulation functions
We do need the unoptimized version of csrand_uniform() for high values
of `n`, since the optimized version depends on having __int128, and it's
not available on several platforms, including ARMv7, IA32, and MK68k.

This reverts commit 848f53c1d3c1362c86d3baab6906e1e4419d2634; however,
I applied some tweaks to the reverted commit.

Reported-by: Adam Sampson <ats@offog.org>
Cc: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-01-30 18:24:15 +01:00
Alejandro Colomar 848f53c1d3 Revert "Add bit manipulation functions"
Now that we optimized csrand_uniform(), we don't need these functions.

This reverts commit 7c8fe291b1260e127c10562bfd7616961013730f.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-01-27 21:48:37 -06:00
Alejandro Colomar 1a0e13f94e Optimize csrand_uniform()
Use a different algorithm to minimize rejection.  This is essentially
the same algorithm implemented in the Linux kernel for
__get_random_u32_below(), but written in a more readable way, and
avoiding microopimizations that make it less readable.

Which (the Linux kernel implementation) is itself based on Daniel
Lemire's algorithm from "Fast Random Integer Generation in an Interval",
linked below.  However, I couldn't really understand that paper very
much, so I had to reconstruct the proofs from scratch, just from what I
could understand from the Linux kernel implementation source code.

I constructed some graphical explanation of how it works, and why it
is optimal, because I needed to visualize it to understand it.  It is
published in the GitHub pull request linked below.

Here goes a wordy explanation of why this algorithm based on
multiplication is better optimized than my original implementation based
on masking.

masking:

	It discards the extra bits of entropy that are not necessary for
	this operation.  This works as if dividing the entire space of
	possible csrand() values into smaller spaces of a size that is
	a smaller power of 2.  Each of those smaller spaces has a
	rejection band, so we get as many rejection bands as spaces
	there are.  For smaller values of 'n', the size of each
	rejection band is smaller, but having more rejection bands
	compensates for this, and results in the same inefficiency as
	for large values of 'n'.

multiplication:

	It divides the entire space of possible random numbers in
	chunks of size exactly 'n', so that there is only one rejection
	band that is the remainder of `2^64 % n`.  The worst case is
	still similar to the masking algorithm, a rejection band that is
	almost half the entire space (n = 2^63 + 1), but for lower
	values of 'n', by only having one small rejection band, it is
	much faster than the masking algorithm.

	This algorithm, however, has one caveat: the implementation
	is harder to read, since it relies on several bitwise tricky
	operations to perform operations like `2^64 % n`, `mult % 2^64`,
	and `mult / 2^64`.  And those operations are different depending
	on the number of bits of the maximum possible random number
	generated by the function.  This means that while this algorithm
	could also be applied to get uniform random numbers in the range
	[0, n-1] quickly from a function like rand(3), which only
	produces 31 bits of (non-CS) random numbers, it would need to be
	implemented differently.  However, that's not a concern for us,
	it's just a note so that nobody picks this code and expects it
	to just work with rand(3) (which BTW I tried for testing it, and
	got a bit confused until I realized this).

Finally, here's some light testing of this implementation, just to know
that I didn't goof it.  I pasted this function into a standalone
program, and run it many times to find if it has any bias (I tested also
to see how many iterations it performs, and it's also almost always 1,
but that test is big enough to not paste it here).

int main(int argc, char *argv[])
{
	printf("%lu\n", csrand_uniform(atoi(argv[1])));
}

$ seq 1 1000 | while read _; do ./a.out 3; done | grep 1 | wc -l
341
$ seq 1 1000 | while read _; do ./a.out 3; done | grep 1 | wc -l
339
$ seq 1 1000 | while read _; do ./a.out 3; done | grep 1 | wc -l
338
$ seq 1 1000 | while read _; do ./a.out 3; done | grep 2 | wc -l
336
$ seq 1 1000 | while read _; do ./a.out 3; done | grep 2 | wc -l
328
$ seq 1 1000 | while read _; do ./a.out 3; done | grep 2 | wc -l
335
$ seq 1 1000 | while read _; do ./a.out 3; done | grep 0 | wc -l
332
$ seq 1 1000 | while read _; do ./a.out 3; done | grep 0 | wc -l
331
$ seq 1 1000 | while read _; do ./a.out 3; done | grep 0 | wc -l
327

This isn't a complete test for a cryptographically-secure random number
generator, of course, but I leave that for interested parties.

Link: <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e9a688bcb19348862afe30d7c85bc37c4c293471>
Link: <https://github.com/shadow-maint/shadow/pull/624#discussion_r1059574358>
Link: <https://arxiv.org/abs/1805.10941>
Cc: "Jason A. Donenfeld" <Jason@zx2c4.com>
Cc: Cristian Rodríguez <crrodriguez@opensuse.org>
Cc: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Cc: Björn Esser <besser82@fedoraproject.org>
Cc: Yann Droneaud <ydroneaud@opteya.com>
Cc: Joseph Myers <joseph@codesourcery.com>
Cc: Sam James <sam@gentoo.org>
Cc: Serge Hallyn <serge@hallyn.com>
Cc: Iker Pedrosa <ipedrosa@redhat.com>
[Daniel Lemire: Added link to research paper in source code]
Cc: Daniel Lemire <daniel@lemire.me>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2023-01-27 21:48:37 -06:00