]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/randutils.c
lib/env: fix function name remote_entry -> remove_entry
[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 #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 */