]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/random-util.c
Add SPDX license identifiers to source files under the LGPL
[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>
11c3a366
TA
24#include <stdbool.h>
25#include <stdlib.h>
26#include <sys/time.h>
3ffd4af2
LP
27#include <linux/random.h>
28#include <stdint.h>
11c3a366 29
349cc4a5 30#if HAVE_SYS_AUXV_H
5224c2c7
ZJS
31# include <sys/auxv.h>
32#endif
33
349cc4a5 34#if USE_SYS_RANDOM_H
5224c2c7
ZJS
35# include <sys/random.h>
36#else
37# include <linux/random.h>
a67dab34 38#endif
3df3e884 39
3ffd4af2 40#include "fd-util.h"
c004493c 41#include "io-util.h"
3ffd4af2 42#include "missing.h"
3df3e884
RC
43#include "random-util.h"
44#include "time-util.h"
3df3e884 45
f0d09059 46int acquire_random_bytes(void *p, size_t n, bool high_quality_required) {
3df3e884
RC
47 static int have_syscall = -1;
48
49 _cleanup_close_ int fd = -1;
f0d09059 50 unsigned already_done = 0;
7b54715d 51 int r;
3df3e884 52
f0d09059
ZJS
53 /* Gathers some randomness from the kernel. This call will never block. If
54 * high_quality_required, it will always return some data from the kernel,
55 * regardless of whether the random pool is fully initialized or not.
56 * Otherwise, it will return success if at least some random bytes were
57 * successfully acquired, and an error if the kernel has no entropy whatsover
58 * for us. */
3df3e884 59
f0d09059
ZJS
60 /* Use the getrandom() syscall unless we know we don't have it. */
61 if (have_syscall != 0) {
3df3e884 62 r = getrandom(p, n, GRND_NONBLOCK);
f0d09059 63 if (r > 0) {
3df3e884 64 have_syscall = true;
7b54715d 65 if ((size_t) r == n)
f0d09059
ZJS
66 return 0;
67 if (!high_quality_required) {
ed88a900 68 /* Fill in the remaining bytes using pseudorandom values */
f0d09059
ZJS
69 pseudorandom_bytes((uint8_t*) p + r, n - r);
70 return 0;
71 }
72
73 already_done = r;
74 } else if (errno == ENOSYS)
75 /* We lack the syscall, continue with reading from /dev/urandom. */
76 have_syscall = false;
77 else if (errno == EAGAIN) {
78 /* The kernel has no entropy whatsoever. Let's remember to
79 * use the syscall the next time again though.
80 *
81 * If high_quality_required is false, return an error so that
82 * random_bytes() can produce some pseudorandom
83 * bytes. Otherwise, fall back to /dev/urandom, which we know
84 * is empty, but the kernel will produce some bytes for us on
85 * a best-effort basis. */
86 have_syscall = true;
87
88 if (!high_quality_required)
89 return -ENODATA;
3df3e884 90 } else
f0d09059 91 return -errno;
3df3e884
RC
92 }
93
94 fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
95 if (fd < 0)
96 return errno == ENOENT ? -ENOSYS : -errno;
97
f0d09059 98 return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true);
3df3e884
RC
99}
100
101void initialize_srand(void) {
102 static bool srand_called = false;
103 unsigned x;
349cc4a5 104#if HAVE_SYS_AUXV_H
3df3e884
RC
105 void *auxv;
106#endif
107
108 if (srand_called)
109 return;
110
349cc4a5 111#if HAVE_SYS_AUXV_H
f0d09059
ZJS
112 /* The kernel provides us with 16 bytes of entropy in auxv, so let's
113 * try to make use of that to seed the pseudo-random generator. It's
114 * better than nothing... */
3df3e884
RC
115
116 auxv = (void*) getauxval(AT_RANDOM);
ad6b1fa2 117 if (auxv) {
b5fa4c77 118 assert_cc(sizeof(x) <= 16);
ad6b1fa2
LP
119 memcpy(&x, auxv, sizeof(x));
120 } else
3df3e884 121#endif
ad6b1fa2
LP
122 x = 0;
123
3df3e884
RC
124
125 x ^= (unsigned) now(CLOCK_REALTIME);
126 x ^= (unsigned) gettid();
127
128 srand(x);
129 srand_called = true;
130}
131
6a06b1a5
ZJS
132/* INT_MAX gives us only 31 bits, so use 24 out of that. */
133#if RAND_MAX >= INT_MAX
134# define RAND_STEP 3
135#else
136/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */
137# define RAND_STEP 1
138#endif
139
140void pseudorandom_bytes(void *p, size_t n) {
3df3e884 141 uint8_t *q;
6a06b1a5
ZJS
142
143 initialize_srand();
144
145 for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) {
146 unsigned rr;
147
148 rr = (unsigned) rand();
149
150#if RAND_STEP >= 3
7b54715d 151 if ((size_t) (q - (uint8_t*) p + 2) < n)
6a06b1a5
ZJS
152 q[2] = rr >> 16;
153#endif
154#if RAND_STEP >= 2
7b54715d 155 if ((size_t) (q - (uint8_t*) p + 1) < n)
6a06b1a5
ZJS
156 q[1] = rr >> 8;
157#endif
158 q[0] = rr;
159 }
160}
161
162void random_bytes(void *p, size_t n) {
3df3e884
RC
163 int r;
164
f0d09059 165 r = acquire_random_bytes(p, n, false);
3df3e884
RC
166 if (r >= 0)
167 return;
168
6a06b1a5
ZJS
169 /* If some idiot made /dev/urandom unavailable to us, or the
170 * kernel has no entropy, use a PRNG instead. */
171 return pseudorandom_bytes(p, n);
3df3e884 172}