/src/gnutls/lib/nettle/sysrng-linux.c
Line | Count | Source (jump to first uncovered line) |
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 | | get_entropy_func _rnd_get_system_entropy = NULL; |
50 | | |
51 | | #if defined(__linux__) |
52 | | # ifdef HAVE_GETRANDOM |
53 | | # include <sys/random.h> |
54 | | # else |
55 | | # include <sys/syscall.h> |
56 | | # undef getrandom |
57 | | # if defined(SYS_getrandom) |
58 | | # define getrandom(dst,s,flags) syscall(SYS_getrandom, (void*)dst, (size_t)s, (unsigned int)flags) |
59 | | # else |
60 | | static ssize_t _getrandom0(void *buf, size_t buflen, unsigned int flags) |
61 | | { |
62 | | errno = ENOSYS; |
63 | | return -1; |
64 | | } |
65 | | |
66 | | # define getrandom(dst,s,flags) _getrandom0(dst,s,flags) |
67 | | # endif |
68 | | # endif |
69 | | |
70 | | static unsigned have_getrandom(void) |
71 | 2 | { |
72 | 2 | char c; |
73 | 2 | int ret; |
74 | 2 | ret = getrandom(&c, 1, 1 /*GRND_NONBLOCK */ ); |
75 | 2 | if (ret == 1 || (ret == -1 && errno == EAGAIN)) |
76 | 2 | return 1; |
77 | 0 | return 0; |
78 | 2 | } |
79 | | |
80 | | /* returns exactly the amount of bytes requested */ |
81 | | static int force_getrandom(void *buf, size_t buflen, unsigned int flags) |
82 | 0 | { |
83 | 0 | int left = buflen; |
84 | 0 | int ret; |
85 | 0 | uint8_t *p = buf; |
86 | |
|
87 | 0 | while (left > 0) { |
88 | 0 | ret = getrandom(p, left, flags); |
89 | 0 | if (ret == -1) { |
90 | 0 | if (errno != EINTR) |
91 | 0 | return ret; |
92 | 0 | } |
93 | | |
94 | 0 | if (ret > 0) { |
95 | 0 | left -= ret; |
96 | 0 | p += ret; |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | return buflen; |
101 | 0 | } |
102 | | |
103 | | static int _rnd_get_system_entropy_getrandom(void *_rnd, size_t size) |
104 | 0 | { |
105 | 0 | int ret; |
106 | 0 | ret = force_getrandom(_rnd, size, 0); |
107 | 0 | if (ret == -1) { |
108 | 0 | int e = errno; |
109 | 0 | gnutls_assert(); |
110 | 0 | _gnutls_debug_log("Failed to use getrandom: %s\n", strerror(e)); |
111 | 0 | return GNUTLS_E_RANDOM_DEVICE_ERROR; |
112 | 0 | } |
113 | | |
114 | 0 | return 0; |
115 | 0 | } |
116 | | #else /* not linux */ |
117 | | # define have_getrandom() 0 |
118 | | #endif |
119 | | |
120 | | static int _rnd_get_system_entropy_urandom(void *_rnd, size_t size) |
121 | 0 | { |
122 | 0 | uint8_t *rnd = _rnd; |
123 | 0 | uint32_t done; |
124 | 0 | int urandom_fd; |
125 | |
|
126 | 0 | urandom_fd = open("/dev/urandom", O_RDONLY); |
127 | 0 | if (urandom_fd < 0) { |
128 | 0 | _gnutls_debug_log("Cannot open /dev/urandom!\n"); |
129 | 0 | return GNUTLS_E_RANDOM_DEVICE_ERROR; |
130 | 0 | } |
131 | | |
132 | 0 | for (done = 0; done < size;) { |
133 | 0 | int res; |
134 | 0 | do { |
135 | 0 | res = read(urandom_fd, rnd + done, size - done); |
136 | 0 | } while (res < 0 && errno == EINTR); |
137 | |
|
138 | 0 | if (res <= 0) { |
139 | 0 | int e = errno; |
140 | 0 | if (res < 0) { |
141 | 0 | _gnutls_debug_log |
142 | 0 | ("Failed to read /dev/urandom: %s\n", |
143 | 0 | strerror(e)); |
144 | 0 | } else { |
145 | 0 | _gnutls_debug_log |
146 | 0 | ("Failed to read /dev/urandom: end of file\n"); |
147 | 0 | } |
148 | |
|
149 | 0 | close(urandom_fd); |
150 | 0 | return GNUTLS_E_RANDOM_DEVICE_ERROR; |
151 | 0 | } |
152 | | |
153 | 0 | done += res; |
154 | 0 | } |
155 | | |
156 | 0 | close(urandom_fd); |
157 | 0 | return 0; |
158 | 0 | } |
159 | | |
160 | | int _rnd_system_entropy_init(void) |
161 | 2 | { |
162 | 2 | int urandom_fd; |
163 | | |
164 | 2 | #if defined(__linux__) |
165 | | /* Enable getrandom() usage if available */ |
166 | 2 | if (have_getrandom()) { |
167 | 2 | _rnd_get_system_entropy = _rnd_get_system_entropy_getrandom; |
168 | 2 | _gnutls_debug_log("getrandom random generator was selected\n"); |
169 | 2 | return 0; |
170 | 2 | } else { |
171 | 0 | _gnutls_debug_log("getrandom is not available\n"); |
172 | 0 | } |
173 | 0 | #endif |
174 | | |
175 | | /* Fallback: /dev/urandom */ |
176 | | |
177 | | /* Check that we can open it */ |
178 | 0 | urandom_fd = open("/dev/urandom", O_RDONLY); |
179 | 0 | if (urandom_fd < 0) { |
180 | 0 | _gnutls_debug_log |
181 | 0 | ("Cannot open /dev/urandom during initialization!\n"); |
182 | 0 | return gnutls_assert_val(GNUTLS_E_RANDOM_DEVICE_ERROR); |
183 | 0 | } |
184 | 0 | close(urandom_fd); |
185 | |
|
186 | 0 | _rnd_get_system_entropy = _rnd_get_system_entropy_urandom; |
187 | 0 | _gnutls_debug_log("/dev/urandom random generator was selected\n"); |
188 | |
|
189 | 0 | return 0; |
190 | 0 | } |
191 | | |
192 | | void _rnd_system_entropy_deinit(void) |
193 | 0 | { |
194 | | /* A no-op now when we open and close /dev/urandom every time */ |
195 | 0 | return; |
196 | 0 | } |