(root)/
util-linux-2.39/
lib/
randutils.c
       1  /*
       2   * SPDX-License-Identifier: BSD-3-Clause
       3   *
       4   * General purpose random utilities. Based on libuuid code.
       5   *
       6   * This code is free software; you can redistribute it and/or modify it under
       7   * the terms of the Modified BSD License. The complete text of the license is
       8   * available in the Documentation/licenses/COPYING.BSD-3-Clause file.
       9   */
      10  #include <stdio.h>
      11  #include <unistd.h>
      12  #include <fcntl.h>
      13  #include <stdlib.h>
      14  #include <string.h>
      15  #include <sys/time.h>
      16  #ifdef HAVE_SYS_SYSCALL_H
      17  #include <sys/syscall.h>
      18  #endif
      19  #include "c.h"
      20  #include "randutils.h"
      21  #include "nls.h"
      22  
      23  #ifdef HAVE_TLS
      24  #define THREAD_LOCAL static __thread
      25  #else
      26  #define THREAD_LOCAL static
      27  #endif
      28  
      29  #ifdef HAVE_GETRANDOM
      30  # include <sys/random.h>
      31  #elif defined (__linux__)
      32  # if !defined(SYS_getrandom) && defined(__NR_getrandom)
      33     /* usable kernel-headers, but old glibc-headers */
      34  #  define SYS_getrandom __NR_getrandom
      35  # endif
      36  #endif
      37  
      38  #if !defined(HAVE_GETRANDOM) && defined(SYS_getrandom)
      39  /* libc without function, but we have syscall */
      40  #define GRND_NONBLOCK 0x01
      41  #define GRND_RANDOM 0x02
      42  static int getrandom(void *buf, size_t buflen, unsigned int flags)
      43  {
      44  	return (syscall(SYS_getrandom, buf, buflen, flags));
      45  }
      46  # define HAVE_GETRANDOM
      47  #endif
      48  
      49  #if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
      50  #define DO_JRAND_MIX
      51  THREAD_LOCAL unsigned short ul_jrand_seed[3];
      52  #endif
      53  
      54  int rand_get_number(int low_n, int high_n)
      55  {
      56  	return rand() % (high_n - low_n + 1) + low_n;
      57  }
      58  
      59  static void crank_random(void)
      60  {
      61  	int i;
      62  	struct timeval tv;
      63  	unsigned int n_pid, n_uid;
      64  
      65  	gettimeofday(&tv, NULL);
      66  	n_pid = getpid();
      67  	n_uid = getuid();
      68  	srand((n_pid << 16) ^ n_uid ^ tv.tv_sec ^ tv.tv_usec);
      69  
      70  #ifdef DO_JRAND_MIX
      71  	ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
      72  	ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
      73  	ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
      74  #endif
      75  	/* Crank the random number generator a few times */
      76  	gettimeofday(&tv, NULL);
      77  	for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
      78  		rand();
      79  }
      80  
      81  int random_get_fd(void)
      82  {
      83  	int fd;
      84  
      85  	fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
      86  	if (fd == -1)
      87  		fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
      88  	crank_random();
      89  	return fd;
      90  }
      91  
      92  /*
      93   * Generate a stream of random nbytes into buf.
      94   * Use /dev/urandom if possible, and if not,
      95   * use glibc pseudo-random functions.
      96   */
      97  #define UL_RAND_READ_ATTEMPTS	8
      98  #define UL_RAND_READ_DELAY	125000	/* microseconds */
      99  
     100  /*
     101   * Write @nbytes random bytes into @buf.
     102   *
     103   * Returns 0 for good quality of random bytes or 1 for weak quality.
     104   */
     105  int ul_random_get_bytes(void *buf, size_t nbytes)
     106  {
     107  	unsigned char *cp = (unsigned char *)buf;
     108  	size_t i, n = nbytes;
     109  	int lose_counter = 0;
     110  
     111  #ifdef HAVE_GETRANDOM
     112  	while (n > 0) {
     113  		int x;
     114  
     115  		errno = 0;
     116  		x = getrandom(cp, n, GRND_NONBLOCK);
     117  		if (x > 0) {			/* success */
     118  		       n -= x;
     119  		       cp += x;
     120  		       lose_counter = 0;
     121  		       errno = 0;
     122  		} else if (errno == ENOSYS) {	/* kernel without getrandom() */
     123  			break;
     124  
     125  		} else if (errno == EAGAIN && lose_counter < UL_RAND_READ_ATTEMPTS) {
     126  			xusleep(UL_RAND_READ_DELAY);	/* no entropy, wait and try again */
     127  			lose_counter++;
     128  		} else
     129  			break;
     130  	}
     131  
     132  	if (errno == ENOSYS)
     133  #endif
     134  	/*
     135  	 * We've been built against headers that support getrandom, but the
     136  	 * running kernel does not.  Fallback to reading from /dev/{u,}random
     137  	 * as before
     138  	 */
     139  	{
     140  		int fd = random_get_fd();
     141  
     142  		lose_counter = 0;
     143  		if (fd >= 0) {
     144  			while (n > 0) {
     145  				ssize_t x = read(fd, cp, n);
     146  				if (x <= 0) {
     147  					if (lose_counter++ > UL_RAND_READ_ATTEMPTS)
     148  						break;
     149  					xusleep(UL_RAND_READ_DELAY);
     150  					continue;
     151  				}
     152  				n -= x;
     153  				cp += x;
     154  				lose_counter = 0;
     155  			}
     156  
     157  			close(fd);
     158  		}
     159  	}
     160  	/*
     161  	 * We do this all the time, but this is the only source of
     162  	 * randomness if /dev/random/urandom is out to lunch.
     163  	 */
     164  	crank_random();
     165  	for (cp = buf, i = 0; i < nbytes; i++)
     166  		*cp++ ^= (rand() >> 7) & 0xFF;
     167  
     168  #ifdef DO_JRAND_MIX
     169  	{
     170  		unsigned short tmp_seed[3];
     171  
     172  		memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
     173  		ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
     174  		for (cp = buf, i = 0; i < nbytes; i++)
     175  			*cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
     176  		memcpy(ul_jrand_seed, tmp_seed,
     177  		       sizeof(ul_jrand_seed)-sizeof(unsigned short));
     178  	}
     179  #endif
     180  
     181  	return n != 0;
     182  }
     183  
     184  
     185  /*
     186   * Tell source of randomness.
     187   */
     188  const char *random_tell_source(void)
     189  {
     190  #ifdef HAVE_GETRANDOM
     191  	return _("getrandom() function");
     192  #else
     193  	size_t i;
     194  	static const char *random_sources[] = {
     195  		"/dev/urandom",
     196  		"/dev/random"
     197  	};
     198  
     199  	for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
     200  		if (!access(random_sources[i], R_OK))
     201  			return random_sources[i];
     202  	}
     203  #endif
     204  	return _("libc pseudo-random functions");
     205  }
     206  
     207  #ifdef TEST_PROGRAM_RANDUTILS
     208  #include <inttypes.h>
     209  
     210  int main(int argc, char *argv[])
     211  {
     212  	size_t i, n;
     213  	int64_t *vp, v;
     214  	char *buf;
     215  	size_t bufsz;
     216  
     217  	n = argc == 1 ? 16 : atoi(argv[1]);
     218  
     219  	printf("Multiple random calls:\n");
     220  	for (i = 0; i < n; i++) {
     221  		ul_random_get_bytes(&v, sizeof(v));
     222  		printf("#%02zu: %25"PRIu64"\n", i, v);
     223  	}
     224  
     225  
     226  	printf("One random call:\n");
     227  	bufsz = n * sizeof(*vp);
     228  	buf = malloc(bufsz);
     229  	if (!buf)
     230  		err(EXIT_FAILURE, "failed to allocate buffer");
     231  
     232  	ul_random_get_bytes(buf, bufsz);
     233  	for (i = 0; i < n; i++) {
     234  		vp = (int64_t *) (buf + (i * sizeof(*vp)));
     235  		printf("#%02zu: %25"PRIu64"\n", i, *vp);
     236  	}
     237  
     238  	return EXIT_SUCCESS;
     239  }
     240  #endif /* TEST_PROGRAM_RANDUTILS */