]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/randutils.c
Merge branch 'dmesg/kmsg-file' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / lib / randutils.c
CommitLineData
fe72459e 1/*
dde4b593 2 * SPDX-License-Identifier: BSD-3-Clause
0f23ee0c 3 *
dde4b593 4 * General purpose random utilities. Based on libuuid code.
0f23ee0c 5 *
dde4b593
KZ
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.
fe72459e 9 */
fe72459e
DB
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>
319563bf 16#ifdef HAVE_SYS_SYSCALL_H
fe72459e 17#include <sys/syscall.h>
811351ac 18#endif
0720d60c 19#include "c.h"
fe72459e 20#include "randutils.h"
0720d60c 21#include "nls.h"
fe72459e
DB
22
23#ifdef HAVE_TLS
24#define THREAD_LOCAL static __thread
25#else
26#define THREAD_LOCAL static
27#endif
28
cc01c2dc 29#ifdef HAVE_GETRANDOM
b192dd69 30# include <sys/random.h>
cc01c2dc 31#elif defined (__linux__)
cc01c2dc
SK
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)
bd0f347f 39/* libc without function, but we have syscall */
a9cf659e
CC
40#define GRND_NONBLOCK 0x01
41#define GRND_RANDOM 0x02
cc01c2dc
SK
42static 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
fe72459e
DB
49#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
50#define DO_JRAND_MIX
51THREAD_LOCAL unsigned short ul_jrand_seed[3];
52#endif
53
a55d646b
KZ
54int rand_get_number(int low_n, int high_n)
55{
56 return rand() % (high_n - low_n + 1) + low_n;
57}
58
cc01c2dc 59static void crank_random(void)
fe72459e 60{
cc01c2dc 61 int i;
a55d646b 62 struct timeval tv;
3534b2c7 63 unsigned int n_pid, n_uid;
fe72459e 64
87918040 65 gettimeofday(&tv, NULL);
3534b2c7
KZ
66 n_pid = getpid();
67 n_uid = getuid();
68 srand((n_pid << 16) ^ n_uid ^ tv.tv_sec ^ tv.tv_usec);
fe72459e
DB
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 */
87918040 76 gettimeofday(&tv, NULL);
fe72459e
DB
77 for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
78 rand();
fe72459e
DB
79}
80
cc01c2dc
SK
81int random_get_fd(void)
82{
261888f1 83 int fd;
cc01c2dc
SK
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);
cc01c2dc
SK
88 crank_random();
89 return fd;
90}
fe72459e
DB
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 */
edc1c90c
KZ
97#define UL_RAND_READ_ATTEMPTS 8
98#define UL_RAND_READ_DELAY 125000 /* microseconds */
99
e4be3ee0
SN
100/*
101 * Write @nbytes random bytes into @buf.
102 *
103 * Returns 0 for good quality of random bytes or 1 for weak quality.
104 */
105int ul_random_get_bytes(void *buf, size_t nbytes)
fe72459e 106{
cc01c2dc 107 unsigned char *cp = (unsigned char *)buf;
5264aebb
KZ
108 size_t i, n = nbytes;
109 int lose_counter = 0;
cc01c2dc
SK
110
111#ifdef HAVE_GETRANDOM
5264aebb
KZ
112 while (n > 0) {
113 int x;
114
115 errno = 0;
a9cf659e 116 x = getrandom(cp, n, GRND_NONBLOCK);
5264aebb
KZ
117 if (x > 0) { /* success */
118 n -= x;
119 cp += x;
120 lose_counter = 0;
e4be3ee0 121 errno = 0;
edc1c90c 122 } else if (errno == ENOSYS) { /* kernel without getrandom() */
a9cf659e 123 break;
edc1c90c
KZ
124
125 } else if (errno == EAGAIN && lose_counter < UL_RAND_READ_ATTEMPTS) {
311e33af 126 xusleep(UL_RAND_READ_DELAY); /* no entropy, wait and try again */
edc1c90c
KZ
127 lose_counter++;
128 } else
5264aebb 129 break;
cc01c2dc 130 }
5264aebb 131
a7df0f5f 132 if (errno == ENOSYS)
5264aebb 133#endif
a7df0f5f 134 /*
5264aebb
KZ
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
a7df0f5f 138 */
a7df0f5f 139 {
a7df0f5f 140 int fd = random_get_fd();
a7df0f5f 141
51013d3e 142 lose_counter = 0;
a7df0f5f
CJHR
143 if (fd >= 0) {
144 while (n > 0) {
145 ssize_t x = read(fd, cp, n);
146 if (x <= 0) {
edc1c90c 147 if (lose_counter++ > UL_RAND_READ_ATTEMPTS)
a7df0f5f 148 break;
edc1c90c 149 xusleep(UL_RAND_READ_DELAY);
a7df0f5f
CJHR
150 continue;
151 }
152 n -= x;
153 cp += x;
154 lose_counter = 0;
fe72459e 155 }
fe72459e 156
a7df0f5f
CJHR
157 close(fd);
158 }
fe72459e 159 }
fe72459e
DB
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 */
cc01c2dc 164 crank_random();
fe72459e
DB
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
e4be3ee0
SN
180
181 return n != 0;
fe72459e
DB
182}
183
0720d60c
SK
184
185/*
186 * Tell source of randomness.
187 */
188const char *random_tell_source(void)
189{
cc01c2dc
SK
190#ifdef HAVE_GETRANDOM
191 return _("getrandom() function");
192#else
0720d60c
SK
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 }
cc01c2dc 203#endif
0720d60c
SK
204 return _("libc pseudo-random functions");
205}
206
e8f7acb0 207#ifdef TEST_PROGRAM_RANDUTILS
e282116f
RM
208#include <inttypes.h>
209
5264aebb 210int main(int argc, char *argv[])
fe72459e 211{
5264aebb
KZ
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]);
fe72459e 218
5264aebb
KZ
219 printf("Multiple random calls:\n");
220 for (i = 0; i < n; i++) {
364de8f4 221 ul_random_get_bytes(&v, sizeof(v));
e282116f 222 printf("#%02zu: %25"PRIu64"\n", i, v);
5264aebb
KZ
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
364de8f4 232 ul_random_get_bytes(buf, bufsz);
5264aebb
KZ
233 for (i = 0; i < n; i++) {
234 vp = (int64_t *) (buf + (i * sizeof(*vp)));
e282116f 235 printf("#%02zu: %25"PRIu64"\n", i, *vp);
fe72459e
DB
236 }
237
238 return EXIT_SUCCESS;
239}
e8f7acb0 240#endif /* TEST_PROGRAM_RANDUTILS */