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