1 /* Test the fallback logic in get_random_bytes.
2
3 Written by Zack Weinberg <zackw at panix.com> in 2018.
4 To the extent possible under law, Zack Weinberg has waived all
5 copyright and related or neighboring rights to this work.
6
7 See https://creativecommons.org/publicdomain/zero/1.0/ for further
8 details. */
9
10 #include "crypt-port.h"
11
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <errno.h>
16
17 #ifdef HAVE_FCNTL_H
18 #include <fcntl.h>
19 #endif
20 #if defined HAVE_SYS_SYSCALL_H
21 #include <sys/syscall.h>
22 #endif
23 #ifdef HAVE_SYS_STAT_H
24 #include <sys/stat.h>
25 #endif
26
27 /* If arc4random_buf is available, all of the fallback logic is compiled
28 out and this test is unnecessary. If ld --wrap is not available this
29 test will not work. */
30 #if defined HAVE_ARC4RANDOM_BUF || !defined HAVE_LD_WRAP
31
32 int
33 main (void)
34 {
35 return 77;
36 }
37
38 #else
39
40 /* All of the mock system primitives below fill in their buffer with
41 repeats of these bytes, so we can tell where the data came from. */
42 #define MOCK_getentropy 'e'
43 #define MOCK_getrandom 'r'
44 #define MOCK_sys_getentropy 'E'
45 #define MOCK_sys_getrandom 'R'
46 #define MOCK_urandom 'u'
47
48 #ifdef HAVE_GETENTROPY
49 static bool getentropy_should_fail = false;
50 extern int __wrap_getentropy (void *, size_t);
51 int
52 __wrap_getentropy (void *buf, size_t buflen)
53 {
54 if (getentropy_should_fail)
55 {
56 errno = ENOSYS;
57 return -1;
58 }
59 else
60 {
61 memset (buf, MOCK_getentropy, buflen);
62 return 0;
63 }
64 }
65 #endif
66
67 #ifdef HAVE_GETRANDOM
68 static bool getrandom_should_fail = false;
69 extern ssize_t __wrap_getrandom (void *, size_t, unsigned int);
70 ssize_t
71 __wrap_getrandom (void *buf, size_t buflen, unsigned int ARG_UNUSED(flags))
72 {
73 if (getrandom_should_fail)
74 {
75 errno = ENOSYS;
76 return -1;
77 }
78 else
79 {
80 buflen = MIN (buflen, INT16_MAX);
81 memset (buf, MOCK_getrandom, buflen);
82 return (ssize_t)buflen;
83 }
84 }
85 #endif
86
87 #ifdef HAVE_SYSCALL
88 #ifdef SYS_getentropy
89 static bool sys_getentropy_should_fail = false;
90 #endif
91 #ifdef SYS_getrandom
92 static bool sys_getrandom_should_fail = false;
93 #endif
94 static bool other_syscalls = false;
95 extern long __wrap_syscall (long, ...);
96 long
97 __wrap_syscall(long number, ...)
98 {
99 #ifdef SYS_getentropy
100 if (number == SYS_getentropy)
101 {
102 if (sys_getentropy_should_fail)
103 {
104 errno = ENOSYS;
105 return -1;
106 }
107 else
108 {
109 va_list ap;
110 va_start (ap, number);
111 void *buf = va_arg (ap, void *);
112 size_t buflen = va_arg (ap, size_t);
113 va_end (ap);
114 memset (buf, MOCK_sys_getentropy, buflen);
115 return 0;
116 }
117 }
118 #endif
119 #ifdef SYS_getrandom
120 if (number == SYS_getrandom)
121 {
122 if (sys_getrandom_should_fail)
123 {
124 errno = ENOSYS;
125 return -1;
126 }
127 else
128 {
129 va_list ap;
130 va_start (ap, number);
131 void *buf = va_arg (ap, void *);
132 size_t buflen = va_arg (ap, size_t);
133 buflen = MIN (buflen, INT16_MAX);
134 va_end (ap);
135 memset (buf, MOCK_sys_getrandom, buflen);
136 return (ssize_t)buflen;
137 }
138 }
139 #endif
140 /* There is no vsyscall. We just have to hope nobody in this test
141 program wants to use syscall() for anything else. */
142 other_syscalls = true;
143 fprintf (stderr, "ERROR: unexpected syscall(%ld)\n", number);
144 errno = ENOSYS;
145 return -1;
146 }
147 #endif /* HAVE_SYSCALL */
148
149 /* It is not possible to hit both of the code paths that can set the
150 "/dev/urandom doesn't work" flag in a single test program, because
151 there's no way to _clear_ that flag again. This test chooses to
152 exercise the read-failure path, not the open-failure path. */
153 #if defined HAVE_SYS_STAT_H && defined HAVE_FCNTL_H && defined HAVE_UNISTD_H
154 static bool urandom_should_fail = false;
155 static int urandom_fd = -1;
156 extern int __wrap_open (const char *, int, mode_t);
157 extern int __real_open (const char *, int, mode_t);
158 int
159 __wrap_open (const char *path, int flags, mode_t mode)
160 {
161 int ret = __real_open (path, flags, mode);
162 if (ret == -1)
163 return ret;
164 if (!strcmp (path, "/dev/urandom"))
165 urandom_fd = ret;
166 return ret;
167 }
168
169 #ifdef HAVE_OPEN64
170 extern int __wrap_open64 (const char *, int, mode_t);
171 extern int __real_open64 (const char *, int, mode_t);
172 int
173 __wrap_open64 (const char *path, int flags, mode_t mode)
174 {
175 int ret = __real_open64 (path, flags, mode);
176 if (ret == -1)
177 return ret;
178 if (!strcmp (path, "/dev/urandom"))
179 urandom_fd = ret;
180 return ret;
181 }
182 #endif
183
184 extern int __wrap_close (int);
185 extern int __real_close (int);
186 int
187 __wrap_close (int fd)
188 {
189 if (fd == urandom_fd)
190 urandom_fd = -1;
191 return __real_close (fd);
192 }
193
194 extern ssize_t __wrap_read (int, void *, size_t);
195 extern ssize_t __real_read (int, void *, size_t);
196 ssize_t
197 __wrap_read (int fd, void *buf, size_t count)
198 {
199 if (fd == urandom_fd)
200 {
201 if (urandom_should_fail)
202 {
203 errno = ENOSYS;
204 return -1;
205 }
206 else
207 {
208 count = MIN (count, INT16_MAX);
209 memset (buf, MOCK_urandom, count);
210 return (ssize_t)count;
211 }
212 }
213 else
214 return __real_read (fd, buf, count);
215 }
216
217 #endif
218
219 struct subtest
220 {
221 const char *what;
222 bool *make_fail;
223 char expected;
224 };
225 const struct subtest subtests[] =
226 {
227 { "initial", 0, 'x' },
228
229 #ifdef HAVE_GETENTROPY
230 { "getentropy", &getentropy_should_fail, MOCK_getentropy },
231 #endif
232 #ifdef HAVE_GETRANDOM
233 { "getrandom", &getrandom_should_fail, MOCK_getrandom },
234 #endif
235
236 #ifdef HAVE_SYSCALL
237 #ifdef SYS_getentropy
238 { "sys_getentropy", &sys_getentropy_should_fail, MOCK_sys_getentropy },
239 #endif
240 #ifdef SYS_getrandom
241 { "sys_getrandom", &sys_getrandom_should_fail, MOCK_sys_getrandom },
242 #endif
243 #endif
244
245 #if defined HAVE_SYS_STAT_H && defined HAVE_FCNTL_H && defined HAVE_UNISTD_H
246 { "/dev/urandom", &urandom_should_fail, MOCK_urandom },
247 #endif
248
249 { "final", 0, 0 }
250 };
251
252 int
253 main (void)
254 {
255 char buf[257];
256 char expected[2] = { 0, 0 };
257 memset (buf, 'x', sizeof buf - 1);
258 buf[sizeof buf - 1] = '\0';
259 bool failed = false;
260 const struct subtest *s;
261
262 for (s = subtests; s->expected;)
263 {
264 expected[0] = s->expected;
265 if (strspn (buf, expected) != 256)
266 {
267 printf ("FAIL: %s: buffer not filled with '%c'\n",
268 s->what, s->expected);
269 failed = true;
270 }
271 else
272 printf ("ok: %s (output)\n", s->what);
273
274 if (s->make_fail)
275 *(s->make_fail) = true;
276 s++;
277
278 bool r = get_random_bytes (buf, sizeof buf - 1);
279 buf[sizeof buf - 1] = '\0';
280 if ((s->expected && !r) || (!s->expected && r))
281 {
282 printf ("FAIL: %s: get_random_bytes: %s\n",
283 s->what, strerror (errno));
284 failed = true;
285 }
286 else
287 printf ("ok: %s (return)\n", s->what);
288 }
289 #if HAVE_SYSCALL
290 failed |= other_syscalls;
291 #endif
292 return failed;
293 }
294
295 #endif