]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/random-util.c
Merge pull request #7042 from vcaputo/iteratedcache
[thirdparty/systemd.git] / src / basic / random-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <elf.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <linux/random.h>
25 #include <stdbool.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/time.h>
30
31 #if HAVE_SYS_AUXV_H
32 # include <sys/auxv.h>
33 #endif
34
35 #if USE_SYS_RANDOM_H
36 # include <sys/random.h>
37 #else
38 # include <linux/random.h>
39 #endif
40
41 #include "fd-util.h"
42 #include "io-util.h"
43 #include "missing.h"
44 #include "random-util.h"
45 #include "time-util.h"
46
47 int acquire_random_bytes(void *p, size_t n, bool high_quality_required) {
48 static int have_syscall = -1;
49
50 _cleanup_close_ int fd = -1;
51 unsigned already_done = 0;
52 int r;
53
54 /* Gathers some randomness from the kernel. This call will never block. If
55 * high_quality_required, it will always return some data from the kernel,
56 * regardless of whether the random pool is fully initialized or not.
57 * Otherwise, it will return success if at least some random bytes were
58 * successfully acquired, and an error if the kernel has no entropy whatsover
59 * for us. */
60
61 /* Use the getrandom() syscall unless we know we don't have it. */
62 if (have_syscall != 0) {
63 r = getrandom(p, n, GRND_NONBLOCK);
64 if (r > 0) {
65 have_syscall = true;
66 if ((size_t) r == n)
67 return 0;
68 if (!high_quality_required) {
69 /* Fill in the remaining bytes using pseudorandom values */
70 pseudorandom_bytes((uint8_t*) p + r, n - r);
71 return 0;
72 }
73
74 already_done = r;
75 } else if (errno == ENOSYS)
76 /* We lack the syscall, continue with reading from /dev/urandom. */
77 have_syscall = false;
78 else if (errno == EAGAIN) {
79 /* The kernel has no entropy whatsoever. Let's remember to
80 * use the syscall the next time again though.
81 *
82 * If high_quality_required is false, return an error so that
83 * random_bytes() can produce some pseudorandom
84 * bytes. Otherwise, fall back to /dev/urandom, which we know
85 * is empty, but the kernel will produce some bytes for us on
86 * a best-effort basis. */
87 have_syscall = true;
88
89 if (!high_quality_required)
90 return -ENODATA;
91 } else
92 return -errno;
93 }
94
95 fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
96 if (fd < 0)
97 return errno == ENOENT ? -ENOSYS : -errno;
98
99 return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true);
100 }
101
102 void initialize_srand(void) {
103 static bool srand_called = false;
104 unsigned x;
105 #if HAVE_SYS_AUXV_H
106 void *auxv;
107 #endif
108
109 if (srand_called)
110 return;
111
112 #if HAVE_SYS_AUXV_H
113 /* The kernel provides us with 16 bytes of entropy in auxv, so let's
114 * try to make use of that to seed the pseudo-random generator. It's
115 * better than nothing... */
116
117 auxv = (void*) getauxval(AT_RANDOM);
118 if (auxv) {
119 assert_cc(sizeof(x) <= 16);
120 memcpy(&x, auxv, sizeof(x));
121 } else
122 #endif
123 x = 0;
124
125
126 x ^= (unsigned) now(CLOCK_REALTIME);
127 x ^= (unsigned) gettid();
128
129 srand(x);
130 srand_called = true;
131 }
132
133 /* INT_MAX gives us only 31 bits, so use 24 out of that. */
134 #if RAND_MAX >= INT_MAX
135 # define RAND_STEP 3
136 #else
137 /* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */
138 # define RAND_STEP 1
139 #endif
140
141 void pseudorandom_bytes(void *p, size_t n) {
142 uint8_t *q;
143
144 initialize_srand();
145
146 for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) {
147 unsigned rr;
148
149 rr = (unsigned) rand();
150
151 #if RAND_STEP >= 3
152 if ((size_t) (q - (uint8_t*) p + 2) < n)
153 q[2] = rr >> 16;
154 #endif
155 #if RAND_STEP >= 2
156 if ((size_t) (q - (uint8_t*) p + 1) < n)
157 q[1] = rr >> 8;
158 #endif
159 q[0] = rr;
160 }
161 }
162
163 void random_bytes(void *p, size_t n) {
164 int r;
165
166 r = acquire_random_bytes(p, n, false);
167 if (r >= 0)
168 return;
169
170 /* If some idiot made /dev/urandom unavailable to us, or the
171 * kernel has no entropy, use a PRNG instead. */
172 return pseudorandom_bytes(p, n);
173 }