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

Remove bias from functions that get randomness, simplify their implementation, and move to separate file #624

Merged
merged 11 commits into from
Jan 28, 2023
Merged
1 change: 1 addition & 0 deletions lib/defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ static inline void memzero(void *ptr, size_t size)
#define SCALE DAY
#endif

#define WIDTHOF(x) (sizeof(x) * CHAR_BIT)
#define NITEMS(arr) (sizeof((arr)) / sizeof((arr)[0]))

/* Copy string pointed by B to array A with size checking. It was originally
Expand Down
5 changes: 5 additions & 0 deletions lib/prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ extern /*@dependent@*/ /*@null@*/struct commonio_entry *__pw_get_head (void);
extern /*@null@*/ /*@only@*/struct passwd *__pw_dup (const struct passwd *pwent);
extern void pw_free (/*@out@*/ /*@only@*/struct passwd *pwent);

/* csrand.c */
unsigned long csrand (void);
unsigned long csrand_uniform (unsigned long n);
unsigned long csrand_interval (unsigned long min, unsigned long max);

/* remove_tree.c */
extern int remove_tree (const char *root, bool remove_root);

Expand Down
1 change: 1 addition & 0 deletions libmisc/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ libmisc_la_SOURCES = \
pwd2spwd.c \
pwdcheck.c \
pwd_init.c \
csrand.c \
remove_tree.c \
rlogin.c \
root_flag.c \
Expand Down
107 changes: 107 additions & 0 deletions libmisc/csrand.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* SPDX-FileCopyrightText: Alejandro Colomar <alx@kernel.org>
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <config.h>

#ident "$Id$"

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#if HAVE_SYS_RANDOM_H
#include <sys/random.h>
#endif
#include "defines.h"
#include "prototypes.h"
#include "shadowlog.h"


/*
* Return a uniformly-distributed CS random u_long value.
*/
unsigned long
csrand(void)
{
FILE *fp;
unsigned long r;

#ifdef HAVE_GETENTROPY
/* getentropy may exist but lack kernel support. */
if (getentropy(&r, sizeof(r)) == 0)
return r;
#endif

#ifdef HAVE_GETRANDOM
/* Likewise getrandom. */
if (getrandom(&r, sizeof(r), 0) == sizeof(r))
return r;
#endif

#ifdef HAVE_ARC4RANDOM_BUF
/* arc4random_buf can never fail. */
arc4random_buf(&r, sizeof(r));
return r;
#endif

/* Use /dev/urandom as a last resort. */
fp = fopen("/dev/urandom", "r");
if (NULL == fp) {
goto fail;
}

if (fread(&r, sizeof(r), 1, fp) != 1) {
fclose(fp);
goto fail;
}

fclose(fp);
return r;

fail:
fprintf(log_get_logfd(), _("Unable to obtain random bytes.\n"));
exit(1);
}


/*
* Return a uniformly-distributed CS random value in the interval [0, n-1].
*
* Fast Random Integer Generation in an Interval
* ACM Transactions on Modeling and Computer Simulation 29 (1), 2019
* <https://arxiv.org/abs/1805.10941>
*/
alejandro-colomar marked this conversation as resolved.
Show resolved Hide resolved
unsigned long
csrand_uniform(unsigned long n)
{
unsigned long bound, rem;
unsigned __int128 r, mult;

if (n == 0)
return csrand();

bound = -n % n; // analogous to `2^64 % n`, since `x % y == (x-y) % y`

do {
r = csrand();
mult = r * n;
rem = mult; // analogous to `mult % 2^64`
} while (rem < bound); // p = (2^64 % n) / 2^64; W.C.: n=2^63+1, p=0.5

r = mult >> WIDTHOF(n); // analogous to `mult / 2^64`

return r;
}


/*
* Return a uniformly-distributed CS random value in the interval [min, max].
*/
unsigned long
csrand_interval(unsigned long min, unsigned long max)
{
return csrand_uniform(max - min + 1) + min;
}
2 changes: 1 addition & 1 deletion libmisc/idmapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ struct map_range *get_map_ranges(int ranges, int argc, char **argv)
* 8bytes --> 21 ascii estimated -> 18446744073709551616 (20 real)
* 16bytes --> 39 ascii estimated -> 340282366920938463463374607431768211456 (39 real)
*/
#define ULONG_DIGITS ((((sizeof(unsigned long) * CHAR_BIT) + 9)/10)*3)
#define ULONG_DIGITS (((WIDTHOF(unsigned long) + 9)/10)*3)

#if HAVE_SYS_CAPABILITY_H
static inline bool maps_lower_root(int cap, int ranges, const struct map_range *mappings)
Expand Down
86 changes: 4 additions & 82 deletions libmisc/salt.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if HAVE_SYS_RANDOM_H
#include <sys/random.h>
#endif
#include "prototypes.h"
#include "defines.h"
#include "getdef.h"
Expand Down Expand Up @@ -89,13 +86,9 @@
#define GENSALT_SETTING_SIZE 100

/* local function prototypes */
static long read_random_bytes (void);
#if !USE_XCRYPT_GENSALT
static /*@observer@*/const char *gensalt (size_t salt_size);
#endif /* !USE_XCRYPT_GENSALT */
#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT)
static long shadow_random (long min, long max);
#endif /* USE_SHA_CRYPT || USE_BCRYPT */
#ifdef USE_SHA_CRYPT
static /*@observer@*/unsigned long SHA_get_salt_rounds (/*@null@*/const int *prefered_rounds);
static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, unsigned long rounds);
Expand All @@ -109,77 +102,6 @@ static /*@observer@*/unsigned long YESCRYPT_get_salt_cost (/*@null@*/const int *
static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, unsigned long cost);
#endif /* USE_YESCRYPT */

/* Read sizeof (long) random bytes from /dev/urandom. */
static long read_random_bytes (void)
{
long randval = 0;

#ifdef HAVE_GETENTROPY
/* getentropy may exist but lack kernel support. */
if (getentropy (&randval, sizeof (randval)) == 0) {
goto end;
}
#endif

#ifdef HAVE_GETRANDOM
/* Likewise getrandom. */
if ((size_t) getrandom (&randval, sizeof (randval), 0) == sizeof (randval)) {
goto end;
}
#endif

#ifdef HAVE_ARC4RANDOM_BUF
/* arc4random_buf, if it exists, can never fail. */
arc4random_buf (&randval, sizeof (randval));
goto end;
#endif

/* Use /dev/urandom as a last resort. */
FILE *f = fopen ("/dev/urandom", "r");
if (NULL == f) {
goto fail;
}

if (fread (&randval, sizeof (randval), 1, f) != 1) {
fclose(f);
goto fail;
}

fclose(f);
goto end;

fail:
fprintf (log_get_logfd(),
_("Unable to obtain random bytes.\n"));
exit (1);

end:
return randval;
}

#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT)
/*
* Return a random number between min and max (both included).
*
* It favors slightly the higher numbers.
*/
static long shadow_random (long min, long max)
{
double drand;
long ret;

drand = (double) (read_random_bytes () & RAND_MAX) / (double) RAND_MAX;
drand *= (double) (max - min + 1);
/* On systems were this is not random() range is lower, we favor
* higher numbers of salt. */
ret = (long) (max + 1 - drand);
/* And we catch limits, and use the highest number */
if ((ret < min) || (ret > max)) {
ret = max;
}
return ret;
}
#endif /* USE_SHA_CRYPT || USE_BCRYPT */

#ifdef USE_SHA_CRYPT
/* Return the the rounds number for the SHA crypt methods. */
Expand Down Expand Up @@ -207,7 +129,7 @@ static /*@observer@*/unsigned long SHA_get_salt_rounds (/*@null@*/const int *pre
max_rounds = min_rounds;
}

rounds = (unsigned long) shadow_random (min_rounds, max_rounds);
rounds = csrand_interval (min_rounds, max_rounds);
}
} else if (0 == *prefered_rounds) {
rounds = SHA_ROUNDS_DEFAULT;
Expand Down Expand Up @@ -280,7 +202,7 @@ static /*@observer@*/unsigned long BCRYPT_get_salt_rounds (/*@null@*/const int *
max_rounds = min_rounds;
}

rounds = (unsigned long) shadow_random (min_rounds, max_rounds);
rounds = csrand_interval (min_rounds, max_rounds);
}
} else if (0 == *prefered_rounds) {
rounds = B_ROUNDS_DEFAULT;
Expand Down Expand Up @@ -401,9 +323,9 @@ static /*@observer@*/const char *gensalt (size_t salt_size)

assert (salt_size >= MIN_SALT_SIZE &&
salt_size <= MAX_SALT_SIZE);
strcat (salt, l64a (read_random_bytes ()));
strcat (salt, l64a (csrand ()));
do {
strcat (salt, l64a (read_random_bytes ()));
strcat (salt, l64a (csrand ()));
} while (strlen (salt) < salt_size);

salt[salt_size] = '\0';
Expand Down