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