Line data Source code
1 : /*
2 : * Copyright (C) 2010-2016 Free Software Foundation, Inc.
3 : * Copyright (C) 2015-2016 Red Hat, Inc.
4 : *
5 : * Author: Nikos Mavrogiannopoulos
6 : *
7 : * This file is part of GNUTLS.
8 : *
9 : * The GNUTLS library is free software; you can redistribute it and/or
10 : * modify it under the terms of the GNU Lesser General Public License
11 : * as published by the Free Software Foundation; either version 2.1 of
12 : * the License, or (at your option) any later version.
13 : *
14 : * This library is distributed in the hope that it will be useful, but
15 : * WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : * Lesser General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU Lesser General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>
21 : *
22 : */
23 :
24 : /* The Linux style system random generator: That is,
25 : * getrandom() -> /dev/urandom, where "->" indicates fallback.
26 : */
27 :
28 : #ifndef RND_NO_INCLUDES
29 : # include "gnutls_int.h"
30 : # include "errors.h"
31 : # include <num.h>
32 : # include <errno.h>
33 : # include <rnd-common.h>
34 : #endif
35 :
36 : #include <sys/types.h>
37 : #include <sys/stat.h>
38 : #include <unistd.h>
39 :
40 : /* gnulib wants to claim strerror even if it cannot provide it. WTF */
41 : #undef strerror
42 :
43 : #include <time.h>
44 : #include <sys/types.h>
45 : #include <sys/stat.h>
46 : #include <sys/time.h>
47 : #include <fcntl.h>
48 :
49 : static int _gnutls_urandom_fd = -1;
50 : static ino_t _gnutls_urandom_fd_ino = 0;
51 : static dev_t _gnutls_urandom_fd_rdev = 0;
52 :
53 : get_entropy_func _rnd_get_system_entropy = NULL;
54 :
55 : #if defined(__linux__)
56 : # ifdef HAVE_GETRANDOM
57 : # include <sys/random.h>
58 : # else
59 : # include <sys/syscall.h>
60 : # undef getrandom
61 : # if defined(SYS_getrandom)
62 : # define getrandom(dst,s,flags) syscall(SYS_getrandom, (void*)dst, (size_t)s, (unsigned int)flags)
63 : # else
64 : static ssize_t _getrandom0(void *buf, size_t buflen, unsigned int flags)
65 : {
66 : errno = ENOSYS;
67 : return -1;
68 : }
69 : # define getrandom(dst,s,flags) _getrandom0(dst,s,flags)
70 : # endif
71 : # endif
72 :
73 :
74 3431 : static unsigned have_getrandom(void)
75 : {
76 3431 : char c;
77 3431 : int ret;
78 3431 : ret = getrandom(&c, 1, 1/*GRND_NONBLOCK*/);
79 3431 : if (ret == 1 || (ret == -1 && errno == EAGAIN))
80 3431 : return 1;
81 : return 0;
82 : }
83 :
84 : /* returns exactly the amount of bytes requested */
85 4207 : static int force_getrandom(void *buf, size_t buflen, unsigned int flags)
86 : {
87 4207 : int left = buflen;
88 4207 : int ret;
89 4207 : uint8_t *p = buf;
90 :
91 8414 : while (left > 0) {
92 4207 : ret = getrandom(p, left, flags);
93 4207 : if (ret == -1) {
94 0 : if (errno != EINTR)
95 : return ret;
96 : }
97 :
98 4207 : if (ret > 0) {
99 4207 : left -= ret;
100 4207 : p += ret;
101 : }
102 : }
103 :
104 : return buflen;
105 : }
106 :
107 4207 : static int _rnd_get_system_entropy_getrandom(void* _rnd, size_t size)
108 : {
109 4207 : int ret;
110 4207 : ret = force_getrandom(_rnd, size, 0);
111 4207 : if (ret == -1) {
112 0 : int e = errno;
113 0 : gnutls_assert();
114 0 : _gnutls_debug_log
115 : ("Failed to use getrandom: %s\n",
116 : strerror(e));
117 0 : return GNUTLS_E_RANDOM_DEVICE_ERROR;
118 : }
119 :
120 : return 0;
121 : }
122 : #else /* not linux */
123 : # define have_getrandom() 0
124 : #endif
125 :
126 0 : static int _rnd_get_system_entropy_urandom(void* _rnd, size_t size)
127 : {
128 0 : uint8_t* rnd = _rnd;
129 0 : uint32_t done;
130 :
131 0 : for (done = 0; done < size;) {
132 0 : int res;
133 0 : do {
134 0 : res = read(_gnutls_urandom_fd, rnd + done, size - done);
135 0 : } while (res < 0 && errno == EINTR);
136 :
137 0 : if (res <= 0) {
138 0 : int e = errno;
139 0 : if (res < 0) {
140 0 : _gnutls_debug_log
141 : ("Failed to read /dev/urandom: %s\n",
142 : strerror(e));
143 : } else {
144 0 : _gnutls_debug_log
145 : ("Failed to read /dev/urandom: end of file\n");
146 : }
147 :
148 0 : return GNUTLS_E_RANDOM_DEVICE_ERROR;
149 : }
150 :
151 0 : done += res;
152 : }
153 :
154 : return 0;
155 : }
156 :
157 : /* This is called when gnutls_global_init() is called for second time.
158 : * It must check whether any resources are still available.
159 : * The particular problem it solves is to verify that the urandom fd is still
160 : * open (for applications that for some reason closed all fds */
161 7560 : int _rnd_system_entropy_check(void)
162 : {
163 7560 : int ret;
164 7560 : struct stat st;
165 :
166 7560 : if (_gnutls_urandom_fd == -1) /* not using urandom */
167 : return 0;
168 :
169 0 : ret = fstat(_gnutls_urandom_fd, &st);
170 0 : if (ret < 0 || st.st_ino != _gnutls_urandom_fd_ino || st.st_rdev != _gnutls_urandom_fd_rdev) {
171 0 : return _rnd_system_entropy_init();
172 : }
173 : return 0;
174 : }
175 :
176 3431 : int _rnd_system_entropy_init(void)
177 : {
178 3431 : int old;
179 3431 : struct stat st;
180 :
181 : #if defined(__linux__)
182 : /* Enable getrandom() usage if available */
183 3431 : if (have_getrandom()) {
184 3431 : _rnd_get_system_entropy = _rnd_get_system_entropy_getrandom;
185 3431 : _gnutls_debug_log("getrandom random generator was detected\n");
186 3431 : return 0;
187 : }
188 : #endif
189 :
190 : /* First fallback: /dev/unrandom */
191 0 : _gnutls_urandom_fd = open("/dev/urandom", O_RDONLY);
192 0 : if (_gnutls_urandom_fd < 0) {
193 0 : _gnutls_debug_log("Cannot open urandom!\n");
194 0 : return gnutls_assert_val(GNUTLS_E_RANDOM_DEVICE_ERROR);
195 : }
196 :
197 0 : old = fcntl(_gnutls_urandom_fd, F_GETFD);
198 0 : if (old != -1)
199 0 : fcntl(_gnutls_urandom_fd, F_SETFD, old | FD_CLOEXEC);
200 :
201 0 : if (fstat(_gnutls_urandom_fd, &st) >= 0) {
202 0 : _gnutls_urandom_fd_ino = st.st_ino;
203 0 : _gnutls_urandom_fd_rdev = st.st_rdev;
204 : }
205 :
206 0 : _rnd_get_system_entropy = _rnd_get_system_entropy_urandom;
207 :
208 0 : return 0;
209 : }
210 :
211 2227 : void _rnd_system_entropy_deinit(void)
212 : {
213 2227 : if (_gnutls_urandom_fd >= 0) {
214 0 : close(_gnutls_urandom_fd);
215 0 : _gnutls_urandom_fd = -1;
216 : }
217 2227 : }
218 :
|