getentropy_osx.c revision 285206
1/*	$OpenBSD: getentropy_osx.c,v 1.3 2014/07/12 14:48:00 deraadt Exp $	*/
2
3/*
4 * Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
5 * Copyright (c) 2014 Bob Beck <beck@obtuse.com>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19#include "config.h"
20
21#include <sys/types.h>
22#include <sys/param.h>
23#include <sys/ioctl.h>
24#include <sys/resource.h>
25#include <sys/syscall.h>
26#include <sys/sysctl.h>
27#include <sys/statvfs.h>
28#include <sys/socket.h>
29#include <sys/mount.h>
30#include <sys/mman.h>
31#include <sys/stat.h>
32#include <sys/time.h>
33#include <stdlib.h>
34#include <stdint.h>
35#include <stdio.h>
36#include <termios.h>
37#include <fcntl.h>
38#include <signal.h>
39#include <string.h>
40#include <errno.h>
41#include <unistd.h>
42#include <time.h>
43#include <mach/mach_time.h>
44#include <mach/mach_host.h>
45#include <mach/host_info.h>
46#include <sys/socketvar.h>
47#include <sys/vmmeter.h>
48#include <netinet/in.h>
49#include <netinet/tcp.h>
50#include <netinet/udp.h>
51#include <netinet/ip_var.h>
52#include <netinet/tcp_var.h>
53#include <netinet/udp_var.h>
54#include <CommonCrypto/CommonDigest.h>
55#define SHA512_Update(a, b, c)	(CC_SHA512_Update((a), (b), (c)))
56#define SHA512_Init(xxx) (CC_SHA512_Init((xxx)))
57#define SHA512_Final(xxx, yyy) (CC_SHA512_Final((xxx), (yyy)))
58#define SHA512_CTX CC_SHA512_CTX
59#define SHA512_DIGEST_LENGTH CC_SHA512_DIGEST_LENGTH
60
61#define REPEAT 5
62#define min(a, b) (((a) < (b)) ? (a) : (b))
63
64#define HX(a, b) \
65	do { \
66		if ((a)) \
67			HD(errno); \
68		else \
69			HD(b); \
70	} while (0)
71
72#define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l)))
73#define HD(x)	 (SHA512_Update(&ctx, (char *)&(x), sizeof (x)))
74#define HF(x)    (SHA512_Update(&ctx, (char *)&(x), sizeof (void*)))
75
76int	getentropy(void *buf, size_t len);
77
78#ifdef CAN_REFERENCE_MAIN
79extern int main(int, char *argv[]);
80#endif
81static int gotdata(char *buf, size_t len);
82static int getentropy_urandom(void *buf, size_t len);
83static int getentropy_fallback(void *buf, size_t len);
84
85int
86getentropy(void *buf, size_t len)
87{
88	int ret = -1;
89
90	if (len > 256) {
91		errno = EIO;
92		return -1;
93	}
94
95	/*
96	 * Try to get entropy with /dev/urandom
97	 *
98	 * This can fail if the process is inside a chroot or if file
99	 * descriptors are exhausted.
100	 */
101	ret = getentropy_urandom(buf, len);
102	if (ret != -1)
103		return (ret);
104
105	/*
106	 * Entropy collection via /dev/urandom and sysctl have failed.
107	 *
108	 * No other API exists for collecting entropy, and we have
109	 * no failsafe way to get it on OSX that is not sensitive
110	 * to resource exhaustion.
111	 *
112	 * We have very few options:
113	 *     - Even syslog_r is unsafe to call at this low level, so
114	 *	 there is no way to alert the user or program.
115	 *     - Cannot call abort() because some systems have unsafe
116	 *	 corefiles.
117	 *     - Could raise(SIGKILL) resulting in silent program termination.
118	 *     - Return EIO, to hint that arc4random's stir function
119	 *       should raise(SIGKILL)
120	 *     - Do the best under the circumstances....
121	 *
122	 * This code path exists to bring light to the issue that OSX
123	 * does not provide a failsafe API for entropy collection.
124	 *
125	 * We hope this demonstrates that OSX should consider
126	 * providing a new failsafe API which works in a chroot or
127	 * when file descriptors are exhausted.
128	 */
129#undef FAIL_INSTEAD_OF_TRYING_FALLBACK
130#ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK
131	raise(SIGKILL);
132#endif
133	ret = getentropy_fallback(buf, len);
134	if (ret != -1)
135		return (ret);
136
137	errno = EIO;
138	return (ret);
139}
140
141/*
142 * Basic sanity checking; wish we could do better.
143 */
144static int
145gotdata(char *buf, size_t len)
146{
147	char	any_set = 0;
148	size_t	i;
149
150	for (i = 0; i < len; ++i)
151		any_set |= buf[i];
152	if (any_set == 0)
153		return -1;
154	return 0;
155}
156
157static int
158getentropy_urandom(void *buf, size_t len)
159{
160	struct stat st;
161	size_t i;
162	int fd, flags;
163	int save_errno = errno;
164
165start:
166
167	flags = O_RDONLY;
168#ifdef O_NOFOLLOW
169	flags |= O_NOFOLLOW;
170#endif
171#ifdef O_CLOEXEC
172	flags |= O_CLOEXEC;
173#endif
174	fd = open("/dev/urandom", flags, 0);
175	if (fd == -1) {
176		if (errno == EINTR)
177			goto start;
178		goto nodevrandom;
179	}
180#ifndef O_CLOEXEC
181	fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
182#endif
183
184	/* Lightly verify that the device node looks sane */
185	if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) {
186		close(fd);
187		goto nodevrandom;
188	}
189	for (i = 0; i < len; ) {
190		size_t wanted = len - i;
191		ssize_t ret = read(fd, (char*)buf + i, wanted);
192
193		if (ret == -1) {
194			if (errno == EAGAIN || errno == EINTR)
195				continue;
196			close(fd);
197			goto nodevrandom;
198		}
199		i += ret;
200	}
201	close(fd);
202	if (gotdata(buf, len) == 0) {
203		errno = save_errno;
204		return 0;		/* satisfied */
205	}
206nodevrandom:
207	errno = EIO;
208	return -1;
209}
210
211static int tcpmib[] = { CTL_NET, AF_INET, IPPROTO_TCP, TCPCTL_STATS };
212static int udpmib[] = { CTL_NET, AF_INET, IPPROTO_UDP, UDPCTL_STATS };
213static int ipmib[] = { CTL_NET, AF_INET, IPPROTO_IP, IPCTL_STATS };
214static int kmib[] = { CTL_KERN, KERN_USRSTACK };
215static int hwmib[] = { CTL_HW, HW_USERMEM };
216
217static int
218getentropy_fallback(void *buf, size_t len)
219{
220	uint8_t results[SHA512_DIGEST_LENGTH];
221	int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat;
222	static int cnt;
223	struct timespec ts;
224	struct timeval tv;
225	struct rusage ru;
226	sigset_t sigset;
227	struct stat st;
228	SHA512_CTX ctx;
229	static pid_t lastpid;
230	pid_t pid;
231	size_t i, ii, m;
232	char *p;
233	struct tcpstat tcpstat;
234	struct udpstat udpstat;
235	struct ipstat ipstat;
236	u_int64_t mach_time;
237	unsigned int idata;
238	void *addr;
239
240	pid = getpid();
241	if (lastpid == pid) {
242		faster = 1;
243		repeat = 2;
244	} else {
245		faster = 0;
246		lastpid = pid;
247		repeat = REPEAT;
248	}
249	for (i = 0; i < len; ) {
250		int j;
251		SHA512_Init(&ctx);
252		for (j = 0; j < repeat; j++) {
253			HX((e = gettimeofday(&tv, NULL)) == -1, tv);
254			if (e != -1) {
255				cnt += (int)tv.tv_sec;
256				cnt += (int)tv.tv_usec;
257			}
258
259			mach_time = mach_absolute_time();
260			HD(mach_time);
261
262			ii = sizeof(addr);
263			HX(sysctl(kmib, sizeof(kmib) / sizeof(kmib[0]),
264			    &addr, &ii, NULL, 0) == -1, addr);
265
266			ii = sizeof(idata);
267			HX(sysctl(hwmib, sizeof(hwmib) / sizeof(hwmib[0]),
268			    &idata, &ii, NULL, 0) == -1, idata);
269
270			ii = sizeof(tcpstat);
271			HX(sysctl(tcpmib, sizeof(tcpmib) / sizeof(tcpmib[0]),
272			    &tcpstat, &ii, NULL, 0) == -1, tcpstat);
273
274			ii = sizeof(udpstat);
275			HX(sysctl(udpmib, sizeof(udpmib) / sizeof(udpmib[0]),
276			    &udpstat, &ii, NULL, 0) == -1, udpstat);
277
278			ii = sizeof(ipstat);
279			HX(sysctl(ipmib, sizeof(ipmib) / sizeof(ipmib[0]),
280			    &ipstat, &ii, NULL, 0) == -1, ipstat);
281
282			HX((pid = getpid()) == -1, pid);
283			HX((pid = getsid(pid)) == -1, pid);
284			HX((pid = getppid()) == -1, pid);
285			HX((pid = getpgid(0)) == -1, pid);
286			HX((e = getpriority(0, 0)) == -1, e);
287
288			if (!faster) {
289				ts.tv_sec = 0;
290				ts.tv_nsec = 1;
291				(void) nanosleep(&ts, NULL);
292			}
293
294			HX(sigpending(&sigset) == -1, sigset);
295			HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1,
296			    sigset);
297
298#ifdef CAN_REFERENCE_MAIN
299			HF(main);		/* an addr in program */
300#endif
301			HF(getentropy);	/* an addr in this library */
302			HF(printf);		/* an addr in libc */
303			p = (char *)&p;
304			HD(p);		/* an addr on stack */
305			p = (char *)&errno;
306			HD(p);		/* the addr of errno */
307
308			if (i == 0) {
309				struct sockaddr_storage ss;
310				struct statvfs stvfs;
311				struct termios tios;
312				struct statfs stfs;
313				socklen_t ssl;
314				off_t off;
315
316				/*
317				 * Prime-sized mappings encourage fragmentation;
318				 * thus exposing some address entropy.
319				 */
320				struct mm {
321					size_t	npg;
322					void	*p;
323				} mm[] =	 {
324					{ 17, MAP_FAILED }, { 3, MAP_FAILED },
325					{ 11, MAP_FAILED }, { 2, MAP_FAILED },
326					{ 5, MAP_FAILED }, { 3, MAP_FAILED },
327					{ 7, MAP_FAILED }, { 1, MAP_FAILED },
328					{ 57, MAP_FAILED }, { 3, MAP_FAILED },
329					{ 131, MAP_FAILED }, { 1, MAP_FAILED },
330				};
331
332				for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
333					HX(mm[m].p = mmap(NULL,
334					    mm[m].npg * pgs,
335					    PROT_READ|PROT_WRITE,
336					    MAP_PRIVATE|MAP_ANON, -1,
337					    (off_t)0), mm[m].p);
338					if (mm[m].p != MAP_FAILED) {
339						size_t mo;
340
341						/* Touch some memory... */
342						p = mm[m].p;
343						mo = cnt %
344						    (mm[m].npg * pgs - 1);
345						p[mo] = 1;
346						cnt += (int)((long)(mm[m].p)
347						    / pgs);
348					}
349
350					/* Check cnts and times... */
351					mach_time = mach_absolute_time();
352					HD(mach_time);
353					cnt += (int)mach_time;
354
355					HX((e = getrusage(RUSAGE_SELF,
356					    &ru)) == -1, ru);
357					if (e != -1) {
358						cnt += (int)ru.ru_utime.tv_sec;
359						cnt += (int)ru.ru_utime.tv_usec;
360					}
361				}
362
363				for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
364					if (mm[m].p != MAP_FAILED)
365						munmap(mm[m].p, mm[m].npg * pgs);
366					mm[m].p = MAP_FAILED;
367				}
368
369				HX(stat(".", &st) == -1, st);
370				HX(statvfs(".", &stvfs) == -1, stvfs);
371				HX(statfs(".", &stfs) == -1, stfs);
372
373				HX(stat("/", &st) == -1, st);
374				HX(statvfs("/", &stvfs) == -1, stvfs);
375				HX(statfs("/", &stfs) == -1, stfs);
376
377				HX((e = fstat(0, &st)) == -1, st);
378				if (e == -1) {
379					if (S_ISREG(st.st_mode) ||
380					    S_ISFIFO(st.st_mode) ||
381					    S_ISSOCK(st.st_mode)) {
382						HX(fstatvfs(0, &stvfs) == -1,
383						    stvfs);
384						HX(fstatfs(0, &stfs) == -1,
385						    stfs);
386						HX((off = lseek(0, (off_t)0,
387						    SEEK_CUR)) < 0, off);
388					}
389					if (S_ISCHR(st.st_mode)) {
390						HX(tcgetattr(0, &tios) == -1,
391						    tios);
392					} else if (S_ISSOCK(st.st_mode)) {
393						memset(&ss, 0, sizeof ss);
394						ssl = sizeof(ss);
395						HX(getpeername(0,
396						    (void *)&ss, &ssl) == -1,
397						    ss);
398					}
399				}
400
401				HX((e = getrusage(RUSAGE_CHILDREN,
402				    &ru)) == -1, ru);
403				if (e != -1) {
404					cnt += (int)ru.ru_utime.tv_sec;
405					cnt += (int)ru.ru_utime.tv_usec;
406				}
407			} else {
408				/* Subsequent hashes absorb previous result */
409				HD(results);
410			}
411
412			HX((e = gettimeofday(&tv, NULL)) == -1, tv);
413			if (e != -1) {
414				cnt += (int)tv.tv_sec;
415				cnt += (int)tv.tv_usec;
416			}
417
418			HD(cnt);
419		}
420
421		SHA512_Final(results, &ctx);
422		memcpy((char*)buf + i, results, min(sizeof(results), len - i));
423		i += min(sizeof(results), len - i);
424	}
425	memset(results, 0, sizeof results);
426	if (gotdata(buf, len) == 0) {
427		errno = save_errno;
428		return 0;		/* satisfied */
429	}
430	errno = EIO;
431	return -1;
432}
433