1 /*
2  *  Platform-specific and custom entropy polling functions
3  *
4  *  Copyright The Mbed TLS Contributors
5  *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6  */
7 
8 #if defined(__linux__) || defined(__midipix__)
9 /* Ensure that syscall() is available even when compiling with -std=c99 */
10 #if !defined(_GNU_SOURCE)
11 #define _GNU_SOURCE
12 #endif
13 #endif
14 
15 #include "common.h"
16 
17 #include <string.h>
18 
19 #if defined(MBEDTLS_ENTROPY_C)
20 
21 #include "mbedtls/entropy.h"
22 #include "entropy_poll.h"
23 #include "mbedtls/error.h"
24 
25 #if defined(MBEDTLS_TIMING_C)
26 #include "mbedtls/timing.h"
27 #endif
28 #include "mbedtls/platform.h"
29 
30 #if !defined(MBEDTLS_NO_PLATFORM_ENTROPY)
31 
32 #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \
33     !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \
34     !defined(__HAIKU__) && !defined(__midipix__) && !defined(__MVS__)
35 #error \
36     "Platform entropy sources only work on Unix and Windows, see MBEDTLS_NO_PLATFORM_ENTROPY in mbedtls_config.h"
37 #endif
38 
39 #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
40 
41 #include <windows.h>
42 #include <bcrypt.h>
43 #include <intsafe.h>
44 
mbedtls_platform_entropy_poll(void * data,unsigned char * output,size_t len,size_t * olen)45 int mbedtls_platform_entropy_poll(void *data, unsigned char *output, size_t len,
46                                   size_t *olen)
47 {
48     ((void) data);
49     *olen = 0;
50 
51     /*
52      * BCryptGenRandom takes ULONG for size, which is smaller than size_t on
53      * 64-bit Windows platforms. Extract entropy in chunks of len (dependent
54      * on ULONG_MAX) size.
55      */
56     while (len != 0) {
57         unsigned long ulong_bytes =
58             (len > ULONG_MAX) ? ULONG_MAX : (unsigned long) len;
59 
60         if (!BCRYPT_SUCCESS(BCryptGenRandom(NULL, output, ulong_bytes,
61                                             BCRYPT_USE_SYSTEM_PREFERRED_RNG))) {
62             return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
63         }
64 
65         *olen += ulong_bytes;
66         len -= ulong_bytes;
67     }
68 
69     return 0;
70 }
71 #else /* _WIN32 && !EFIX64 && !EFI32 */
72 
73 /*
74  * Test for Linux getrandom() support.
75  * Since there is no wrapper in the libc yet, use the generic syscall wrapper
76  * available in GNU libc and compatible libc's (eg uClibc).
77  */
78 #if ((defined(__linux__) && defined(__GLIBC__)) || defined(__midipix__))
79 #include <unistd.h>
80 #include <sys/syscall.h>
81 #if defined(SYS_getrandom)
82 #define HAVE_GETRANDOM
83 #include <errno.h>
84 
getrandom_wrapper(void * buf,size_t buflen,unsigned int flags)85 static int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags)
86 {
87     /* MemSan cannot understand that the syscall writes to the buffer */
88 #if defined(__has_feature)
89 #if __has_feature(memory_sanitizer)
90     memset(buf, 0, buflen);
91 #endif
92 #endif
93     return (int) syscall(SYS_getrandom, buf, buflen, flags);
94 }
95 #endif /* SYS_getrandom */
96 #endif /* __linux__ || __midipix__ */
97 
98 #if defined(__FreeBSD__) || defined(__DragonFly__)
99 #include <sys/param.h>
100 #if (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || \
101     (defined(__DragonFly__) && __DragonFly_version >= 500700)
102 #include <errno.h>
103 #include <sys/random.h>
104 #define HAVE_GETRANDOM
getrandom_wrapper(void * buf,size_t buflen,unsigned int flags)105 static int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags)
106 {
107     return (int) getrandom(buf, buflen, flags);
108 }
109 #endif /* (__FreeBSD__ && __FreeBSD_version >= 1200000) ||
110           (__DragonFly__ && __DragonFly_version >= 500700) */
111 #endif /* __FreeBSD__ || __DragonFly__ */
112 
113 /*
114  * Some BSD systems provide KERN_ARND.
115  * This is equivalent to reading from /dev/urandom, only it doesn't require an
116  * open file descriptor, and provides up to 256 bytes per call (basically the
117  * same as getentropy(), but with a longer history).
118  *
119  * Documentation: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7
120  */
121 #if (defined(__FreeBSD__) || defined(__NetBSD__)) && !defined(HAVE_GETRANDOM)
122 #include <sys/param.h>
123 #include <sys/sysctl.h>
124 #if defined(KERN_ARND)
125 #define HAVE_SYSCTL_ARND
126 
sysctl_arnd_wrapper(unsigned char * buf,size_t buflen)127 static int sysctl_arnd_wrapper(unsigned char *buf, size_t buflen)
128 {
129     int name[2];
130     size_t len;
131 
132     name[0] = CTL_KERN;
133     name[1] = KERN_ARND;
134 
135     while (buflen > 0) {
136         len = buflen > 256 ? 256 : buflen;
137         if (sysctl(name, 2, buf, &len, NULL, 0) == -1) {
138             return -1;
139         }
140         buflen -= len;
141         buf += len;
142     }
143     return 0;
144 }
145 #endif /* KERN_ARND */
146 #endif /* __FreeBSD__ || __NetBSD__ */
147 
148 #include <stdio.h>
149 
mbedtls_platform_entropy_poll(void * data,unsigned char * output,size_t len,size_t * olen)150 int mbedtls_platform_entropy_poll(void *data,
151                                   unsigned char *output, size_t len, size_t *olen)
152 {
153     FILE *file;
154     size_t read_len;
155     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
156     ((void) data);
157 
158 #if defined(HAVE_GETRANDOM)
159     ret = getrandom_wrapper(output, len, 0);
160     if (ret >= 0) {
161         *olen = (size_t) ret;
162         return 0;
163     } else if (errno != ENOSYS) {
164         return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
165     }
166     /* Fall through if the system call isn't known. */
167 #else
168     ((void) ret);
169 #endif /* HAVE_GETRANDOM */
170 
171 #if defined(HAVE_SYSCTL_ARND)
172     ((void) file);
173     ((void) read_len);
174     if (sysctl_arnd_wrapper(output, len) == -1) {
175         return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
176     }
177     *olen = len;
178     return 0;
179 #else
180 
181     *olen = 0;
182 
183     file = fopen("/dev/urandom", "rb");
184     if (file == NULL) {
185         return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
186     }
187 
188     /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
189     mbedtls_setbuf(file, NULL);
190 
191     read_len = fread(output, 1, len, file);
192     if (read_len != len) {
193         fclose(file);
194         return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
195     }
196 
197     fclose(file);
198     *olen = len;
199 
200     return 0;
201 #endif /* HAVE_SYSCTL_ARND */
202 }
203 #endif /* _WIN32 && !EFIX64 && !EFI32 */
204 #endif /* !MBEDTLS_NO_PLATFORM_ENTROPY */
205 
206 #if defined(MBEDTLS_ENTROPY_NV_SEED)
mbedtls_nv_seed_poll(void * data,unsigned char * output,size_t len,size_t * olen)207 int mbedtls_nv_seed_poll(void *data,
208                          unsigned char *output, size_t len, size_t *olen)
209 {
210     unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE];
211     size_t use_len = MBEDTLS_ENTROPY_BLOCK_SIZE;
212     ((void) data);
213 
214     memset(buf, 0, MBEDTLS_ENTROPY_BLOCK_SIZE);
215 
216     if (mbedtls_nv_seed_read(buf, MBEDTLS_ENTROPY_BLOCK_SIZE) < 0) {
217         return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
218     }
219 
220     if (len < use_len) {
221         use_len = len;
222     }
223 
224     memcpy(output, buf, use_len);
225     *olen = use_len;
226 
227     return 0;
228 }
229 #endif /* MBEDTLS_ENTROPY_NV_SEED */
230 
231 #endif /* MBEDTLS_ENTROPY_C */
232