]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/random-util.c
Merge pull request #7042 from vcaputo/iteratedcache
[thirdparty/systemd.git] / src / basic / random-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
3df3e884
RC
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
11c3a366 21#include <elf.h>
3df3e884 22#include <errno.h>
3df3e884 23#include <fcntl.h>
dccca82b 24#include <linux/random.h>
11c3a366 25#include <stdbool.h>
dccca82b 26#include <stdint.h>
11c3a366 27#include <stdlib.h>
dccca82b 28#include <string.h>
11c3a366 29#include <sys/time.h>
11c3a366 30
349cc4a5 31#if HAVE_SYS_AUXV_H
5224c2c7
ZJS
32# include <sys/auxv.h>
33#endif
34
349cc4a5 35#if USE_SYS_RANDOM_H
5224c2c7
ZJS
36# include <sys/random.h>
37#else
38# include <linux/random.h>
a67dab34 39#endif
3df3e884 40
3ffd4af2 41#include "fd-util.h"
c004493c 42#include "io-util.h"
3ffd4af2 43#include "missing.h"
3df3e884
RC
44#include "random-util.h"
45#include "time-util.h"
3df3e884 46
f0d09059 47int acquire_random_bytes(void *p, size_t n, bool high_quality_required) {
3df3e884
RC
48 static int have_syscall = -1;
49
50 _cleanup_close_ int fd = -1;
f0d09059 51 unsigned already_done = 0;
7b54715d 52 int r;
3df3e884 53
f0d09059
ZJS
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. */
3df3e884 60
f0d09059
ZJS
61 /* Use the getrandom() syscall unless we know we don't have it. */
62 if (have_syscall != 0) {
3df3e884 63 r = getrandom(p, n, GRND_NONBLOCK);
f0d09059 64 if (r > 0) {
3df3e884 65 have_syscall = true;
7b54715d 66 if ((size_t) r == n)
f0d09059
ZJS
67 return 0;
68 if (!high_quality_required) {
ed88a900 69 /* Fill in the remaining bytes using pseudorandom values */
f0d09059
ZJS
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;
3df3e884 91 } else
f0d09059 92 return -errno;
3df3e884
RC
93 }
94
95 fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
96 if (fd < 0)
97 return errno == ENOENT ? -ENOSYS : -errno;
98
f0d09059 99 return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true);
3df3e884
RC
100}
101
102void initialize_srand(void) {
103 static bool srand_called = false;
104 unsigned x;
349cc4a5 105#if HAVE_SYS_AUXV_H
3df3e884
RC
106 void *auxv;
107#endif
108
109 if (srand_called)
110 return;
111
349cc4a5 112#if HAVE_SYS_AUXV_H
f0d09059
ZJS
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... */
3df3e884
RC
116
117 auxv = (void*) getauxval(AT_RANDOM);
ad6b1fa2 118 if (auxv) {
b5fa4c77 119 assert_cc(sizeof(x) <= 16);
ad6b1fa2
LP
120 memcpy(&x, auxv, sizeof(x));
121 } else
3df3e884 122#endif
ad6b1fa2
LP
123 x = 0;
124
3df3e884
RC
125
126 x ^= (unsigned) now(CLOCK_REALTIME);
127 x ^= (unsigned) gettid();
128
129 srand(x);
130 srand_called = true;
131}
132
6a06b1a5
ZJS
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
141void pseudorandom_bytes(void *p, size_t n) {
3df3e884 142 uint8_t *q;
6a06b1a5
ZJS
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
7b54715d 152 if ((size_t) (q - (uint8_t*) p + 2) < n)
6a06b1a5
ZJS
153 q[2] = rr >> 16;
154#endif
155#if RAND_STEP >= 2
7b54715d 156 if ((size_t) (q - (uint8_t*) p + 1) < n)
6a06b1a5
ZJS
157 q[1] = rr >> 8;
158#endif
159 q[0] = rr;
160 }
161}
162
163void random_bytes(void *p, size_t n) {
3df3e884
RC
164 int r;
165
f0d09059 166 r = acquire_random_bytes(p, n, false);
3df3e884
RC
167 if (r >= 0)
168 return;
169
6a06b1a5
ZJS
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);
3df3e884 173}