chiark / gitweb /
b216be579d28c0fe2d250b263dfe0f49de2ef5fb
[elogind.git] / src / basic / random-util.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2010 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <elf.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include <sys/time.h>
26 #include <linux/random.h>
27 #include <stdint.h>
28
29 #ifdef HAVE_SYS_AUXV_H
30 #  include <sys/auxv.h>
31 #endif
32
33 #ifdef USE_SYS_RANDOM_H
34 #  include <sys/random.h>
35 #else
36 #  include <linux/random.h>
37 #endif
38
39 #include "fd-util.h"
40 #include "io-util.h"
41 #include "missing.h"
42 #include "random-util.h"
43 #include "time-util.h"
44
45 int dev_urandom(void *p, size_t n) {
46         static int have_syscall = -1;
47
48         _cleanup_close_ int fd = -1;
49         int r;
50
51         /* Gathers some randomness from the kernel. This call will
52          * never block, and will always return some data from the
53          * kernel, regardless if the random pool is fully initialized
54          * or not. It thus makes no guarantee for the quality of the
55          * returned entropy, but is good enough for our usual usecases
56          * of seeding the hash functions for hashtable */
57
58         /* Use the getrandom() syscall unless we know we don't have
59          * it, or when the requested size is too large for it. */
60         if (have_syscall != 0 || (size_t) (int) n != n) {
61                 r = getrandom(p, n, GRND_NONBLOCK);
62                 if (r == (int) n) {
63                         have_syscall = true;
64                         return 0;
65                 }
66
67                 if (r < 0) {
68                         if (errno == ENOSYS)
69                                 /* we lack the syscall, continue with
70                                  * reading from /dev/urandom */
71                                 have_syscall = false;
72                         else if (errno == EAGAIN)
73                                 /* not enough entropy for now. Let's
74                                  * remember to use the syscall the
75                                  * next time, again, but also read
76                                  * from /dev/urandom for now, which
77                                  * doesn't care about the current
78                                  * amount of entropy.  */
79                                 have_syscall = true;
80                         else
81                                 return -errno;
82                 } else
83                         /* too short read? */
84                         return -ENODATA;
85         }
86
87         fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
88         if (fd < 0)
89                 return errno == ENOENT ? -ENOSYS : -errno;
90
91         return loop_read_exact(fd, p, n, true);
92 }
93
94 void initialize_srand(void) {
95         static bool srand_called = false;
96         unsigned x;
97 #ifdef HAVE_SYS_AUXV_H
98         void *auxv;
99 #endif
100
101         if (srand_called)
102                 return;
103
104 #ifdef HAVE_SYS_AUXV_H
105         /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed the
106          * pseudo-random generator. It's better than nothing... */
107
108         auxv = (void*) getauxval(AT_RANDOM);
109         if (auxv) {
110                 assert_cc(sizeof(x) < 16);
111                 memcpy(&x, auxv, sizeof(x));
112         } else
113 #endif
114                 x = 0;
115
116
117         x ^= (unsigned) now(CLOCK_REALTIME);
118         x ^= (unsigned) gettid();
119
120         srand(x);
121         srand_called = true;
122 }
123
124 void random_bytes(void *p, size_t n) {
125         uint8_t *q;
126         int r;
127
128         r = dev_urandom(p, n);
129         if (r >= 0)
130                 return;
131
132         /* If some idiot made /dev/urandom unavailable to us, he'll
133          * get a PRNG instead. */
134
135         initialize_srand();
136
137         for (q = p; q < (uint8_t*) p + n; q ++)
138                 *q = rand();
139 }