]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/randutils.c
Merge branch 'fix-map-current-user-shortopt' of https://github.com/mat8913/util-linux
[thirdparty/util-linux.git] / 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
17 #include <sys/syscall.h>
18
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 syscal */
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
64 gettimeofday(&tv, NULL);
65 srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
66
67 #ifdef DO_JRAND_MIX
68 ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
69 ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
70 ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
71 #endif
72 /* Crank the random number generator a few times */
73 gettimeofday(&tv, NULL);
74 for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
75 rand();
76 }
77
78 int random_get_fd(void)
79 {
80 int i, fd;
81
82 fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
83 if (fd == -1)
84 fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
85 if (fd >= 0) {
86 i = fcntl(fd, F_GETFD);
87 if (i >= 0)
88 fcntl(fd, F_SETFD, i | FD_CLOEXEC);
89 }
90 crank_random();
91 return fd;
92 }
93
94 /*
95 * Generate a stream of random nbytes into buf.
96 * Use /dev/urandom if possible, and if not,
97 * use glibc pseudo-random functions.
98 */
99 #define UL_RAND_READ_ATTEMPTS 8
100 #define UL_RAND_READ_DELAY 125000 /* microseconds */
101
102 void random_get_bytes(void *buf, size_t nbytes)
103 {
104 unsigned char *cp = (unsigned char *)buf;
105 size_t i, n = nbytes;
106 int lose_counter = 0;
107
108 #ifdef HAVE_GETRANDOM
109 while (n > 0) {
110 int x;
111
112 errno = 0;
113 x = getrandom(cp, n, GRND_NONBLOCK);
114 if (x > 0) { /* success */
115 n -= x;
116 cp += x;
117 lose_counter = 0;
118
119 } else if (errno == ENOSYS) { /* kernel without getrandom() */
120 break;
121
122 } else if (errno == EAGAIN && lose_counter < UL_RAND_READ_ATTEMPTS) {
123 xusleep(UL_RAND_READ_DELAY); /* no etropy, wait and try again */
124 lose_counter++;
125 } else
126 break;
127 }
128
129 if (errno == ENOSYS)
130 #endif
131 /*
132 * We've been built against headers that support getrandom, but the
133 * running kernel does not. Fallback to reading from /dev/{u,}random
134 * as before
135 */
136 {
137 int fd = random_get_fd();
138
139 lose_counter = 0;
140 if (fd >= 0) {
141 while (n > 0) {
142 ssize_t x = read(fd, cp, n);
143 if (x <= 0) {
144 if (lose_counter++ > UL_RAND_READ_ATTEMPTS)
145 break;
146 xusleep(UL_RAND_READ_DELAY);
147 continue;
148 }
149 n -= x;
150 cp += x;
151 lose_counter = 0;
152 }
153
154 close(fd);
155 }
156 }
157 /*
158 * We do this all the time, but this is the only source of
159 * randomness if /dev/random/urandom is out to lunch.
160 */
161 crank_random();
162 for (cp = buf, i = 0; i < nbytes; i++)
163 *cp++ ^= (rand() >> 7) & 0xFF;
164
165 #ifdef DO_JRAND_MIX
166 {
167 unsigned short tmp_seed[3];
168
169 memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
170 ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
171 for (cp = buf, i = 0; i < nbytes; i++)
172 *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
173 memcpy(ul_jrand_seed, tmp_seed,
174 sizeof(ul_jrand_seed)-sizeof(unsigned short));
175 }
176 #endif
177 return;
178 }
179
180
181 /*
182 * Tell source of randomness.
183 */
184 const char *random_tell_source(void)
185 {
186 #ifdef HAVE_GETRANDOM
187 return _("getrandom() function");
188 #else
189 size_t i;
190 static const char *random_sources[] = {
191 "/dev/urandom",
192 "/dev/random"
193 };
194
195 for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
196 if (!access(random_sources[i], R_OK))
197 return random_sources[i];
198 }
199 #endif
200 return _("libc pseudo-random functions");
201 }
202
203 #ifdef TEST_PROGRAM_RANDUTILS
204 #include <inttypes.h>
205
206 int main(int argc, char *argv[])
207 {
208 size_t i, n;
209 int64_t *vp, v;
210 char *buf;
211 size_t bufsz;
212
213 n = argc == 1 ? 16 : atoi(argv[1]);
214
215 printf("Multiple random calls:\n");
216 for (i = 0; i < n; i++) {
217 random_get_bytes(&v, sizeof(v));
218 printf("#%02zu: %25"PRIu64"\n", i, v);
219 }
220
221
222 printf("One random call:\n");
223 bufsz = n * sizeof(*vp);
224 buf = malloc(bufsz);
225 if (!buf)
226 err(EXIT_FAILURE, "failed to allocate buffer");
227
228 random_get_bytes(buf, bufsz);
229 for (i = 0; i < n; i++) {
230 vp = (int64_t *) (buf + (i * sizeof(*vp)));
231 printf("#%02zu: %25"PRIu64"\n", i, *vp);
232 }
233
234 return EXIT_SUCCESS;
235 }
236 #endif /* TEST_PROGRAM_RANDUTILS */