chiark / gitweb /
Prep v228: Clean up the new src/basic/*-util-[hc] files:
[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 <errno.h>
21 #include <fcntl.h>
22 #include <linux/random.h>
23 #include <stdint.h>
24 #ifdef HAVE_SYS_AUXV_H
25 #include <sys/auxv.h>
26 #endif
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <time.h>
30
31 #include "fd-util.h"
32 #include "io-util.h"
33 #include "missing.h"
34 #include "random-util.h"
35 #include "time-util.h"
36 #include "util.h"
37
38 int dev_urandom(void *p, size_t n) {
39         static int have_syscall = -1;
40
41         _cleanup_close_ int fd = -1;
42         int r;
43
44         /* Gathers some randomness from the kernel. This call will
45          * never block, and will always return some data from the
46          * kernel, regardless if the random pool is fully initialized
47          * or not. It thus makes no guarantee for the quality of the
48          * returned entropy, but is good enough for or usual usecases
49          * of seeding the hash functions for hashtable */
50
51         /* Use the getrandom() syscall unless we know we don't have
52          * it, or when the requested size is too large for it. */
53         if (have_syscall != 0 || (size_t) (int) n != n) {
54                 r = getrandom(p, n, GRND_NONBLOCK);
55                 if (r == (int) n) {
56                         have_syscall = true;
57                         return 0;
58                 }
59
60                 if (r < 0) {
61                         if (errno == ENOSYS)
62                                 /* we lack the syscall, continue with
63                                  * reading from /dev/urandom */
64                                 have_syscall = false;
65                         else if (errno == EAGAIN)
66                                 /* not enough entropy for now. Let's
67                                  * remember to use the syscall the
68                                  * next time, again, but also read
69                                  * from /dev/urandom for now, which
70                                  * doesn't care about the current
71                                  * amount of entropy.  */
72                                 have_syscall = true;
73                         else
74                                 return -errno;
75                 } else
76                         /* too short read? */
77                         return -ENODATA;
78         }
79
80         fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
81         if (fd < 0)
82                 return errno == ENOENT ? -ENOSYS : -errno;
83
84         return loop_read_exact(fd, p, n, true);
85 }
86
87 void initialize_srand(void) {
88         static bool srand_called = false;
89         unsigned x;
90 #ifdef HAVE_SYS_AUXV_H
91         void *auxv;
92 #endif
93
94         if (srand_called)
95                 return;
96
97         x = 0;
98
99 #ifdef HAVE_SYS_AUXV_H
100         /* The kernel provides us with a bit of entropy in auxv, so
101          * let's try to make use of that to seed the pseudo-random
102          * generator. It's better than nothing... */
103
104         auxv = (void*) getauxval(AT_RANDOM);
105         if (auxv)
106                 x ^= *(unsigned*) auxv;
107 #endif
108
109         x ^= (unsigned) now(CLOCK_REALTIME);
110         x ^= (unsigned) gettid();
111
112         srand(x);
113         srand_called = true;
114 }
115
116 void random_bytes(void *p, size_t n) {
117         uint8_t *q;
118         int r;
119
120         r = dev_urandom(p, n);
121         if (r >= 0)
122                 return;
123
124         /* If some idiot made /dev/urandom unavailable to us, he'll
125          * get a PRNG instead. */
126
127         initialize_srand();
128
129         for (q = p; q < (uint8_t*) p + n; q ++)
130                 *q = rand();
131 }