]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/randutils.c
login: keep default MOTD_FILE= backwardly compatible
[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 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 i, 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 if (fd >= 0) {
89 i = fcntl(fd, F_GETFD);
90 if (i >= 0)
91 fcntl(fd, F_SETFD, i | FD_CLOEXEC);
92 }
93 crank_random();
94 return fd;
95 }
96
97 /*
98 * Generate a stream of random nbytes into buf.
99 * Use /dev/urandom if possible, and if not,
100 * use glibc pseudo-random functions.
101 */
102 #define UL_RAND_READ_ATTEMPTS 8
103 #define UL_RAND_READ_DELAY 125000 /* microseconds */
104
105 void 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
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 etropy, 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
182
183 /*
184 * Tell source of randomness.
185 */
186 const char *random_tell_source(void)
187 {
188 #ifdef HAVE_GETRANDOM
189 return _("getrandom() function");
190 #else
191 size_t i;
192 static const char *random_sources[] = {
193 "/dev/urandom",
194 "/dev/random"
195 };
196
197 for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
198 if (!access(random_sources[i], R_OK))
199 return random_sources[i];
200 }
201 #endif
202 return _("libc pseudo-random functions");
203 }
204
205 #ifdef TEST_PROGRAM_RANDUTILS
206 #include <inttypes.h>
207
208 int main(int argc, char *argv[])
209 {
210 size_t i, n;
211 int64_t *vp, v;
212 char *buf;
213 size_t bufsz;
214
215 n = argc == 1 ? 16 : atoi(argv[1]);
216
217 printf("Multiple random calls:\n");
218 for (i = 0; i < n; i++) {
219 random_get_bytes(&v, sizeof(v));
220 printf("#%02zu: %25"PRIu64"\n", i, v);
221 }
222
223
224 printf("One random call:\n");
225 bufsz = n * sizeof(*vp);
226 buf = malloc(bufsz);
227 if (!buf)
228 err(EXIT_FAILURE, "failed to allocate buffer");
229
230 random_get_bytes(buf, bufsz);
231 for (i = 0; i < n; i++) {
232 vp = (int64_t *) (buf + (i * sizeof(*vp)));
233 printf("#%02zu: %25"PRIu64"\n", i, *vp);
234 }
235
236 return EXIT_SUCCESS;
237 }
238 #endif /* TEST_PROGRAM_RANDUTILS */