1*b7041c07Sderaadt /* $OpenBSD: getentropy_osx.c,v 1.14 2021/10/24 21:24:20 deraadt Exp $ */
23f84b207Sbeck
33f84b207Sbeck /*
43f84b207Sbeck * Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
53f84b207Sbeck * Copyright (c) 2014 Bob Beck <beck@obtuse.com>
63f84b207Sbeck *
73f84b207Sbeck * Permission to use, copy, modify, and distribute this software for any
83f84b207Sbeck * purpose with or without fee is hereby granted, provided that the above
93f84b207Sbeck * copyright notice and this permission notice appear in all copies.
103f84b207Sbeck *
113f84b207Sbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
123f84b207Sbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
133f84b207Sbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
143f84b207Sbeck * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
153f84b207Sbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
163f84b207Sbeck * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
173f84b207Sbeck * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18b5f12122Sderaadt *
19b5f12122Sderaadt * Emulation of getentropy(2) as documented at:
20b8461433Stb * http://man.openbsd.org/getentropy.2
213f84b207Sbeck */
223f84b207Sbeck
23c283a6cdSbcook #include <TargetConditionals.h>
243f84b207Sbeck #include <sys/types.h>
253f84b207Sbeck #include <sys/param.h>
263f84b207Sbeck #include <sys/ioctl.h>
273f84b207Sbeck #include <sys/resource.h>
283f84b207Sbeck #include <sys/syscall.h>
293f84b207Sbeck #include <sys/sysctl.h>
303f84b207Sbeck #include <sys/statvfs.h>
313f84b207Sbeck #include <sys/socket.h>
323f84b207Sbeck #include <sys/mount.h>
333f84b207Sbeck #include <sys/mman.h>
343f84b207Sbeck #include <sys/stat.h>
353f84b207Sbeck #include <sys/time.h>
363f84b207Sbeck #include <stdlib.h>
373f84b207Sbeck #include <stdint.h>
383f84b207Sbeck #include <stdio.h>
393f84b207Sbeck #include <termios.h>
403f84b207Sbeck #include <fcntl.h>
413f84b207Sbeck #include <signal.h>
423f84b207Sbeck #include <string.h>
433f84b207Sbeck #include <errno.h>
443f84b207Sbeck #include <unistd.h>
453f84b207Sbeck #include <time.h>
463f84b207Sbeck #include <mach/mach_time.h>
473f84b207Sbeck #include <mach/mach_host.h>
483f84b207Sbeck #include <mach/host_info.h>
49c283a6cdSbcook #if TARGET_OS_OSX
503f84b207Sbeck #include <sys/socketvar.h>
513f84b207Sbeck #include <sys/vmmeter.h>
52c283a6cdSbcook #endif
533f84b207Sbeck #include <netinet/in.h>
543f84b207Sbeck #include <netinet/tcp.h>
55c283a6cdSbcook #if TARGET_OS_OSX
563f84b207Sbeck #include <netinet/udp.h>
573f84b207Sbeck #include <netinet/ip_var.h>
583f84b207Sbeck #include <netinet/tcp_var.h>
593f84b207Sbeck #include <netinet/udp_var.h>
60c283a6cdSbcook #endif
613f84b207Sbeck #include <CommonCrypto/CommonDigest.h>
623f84b207Sbeck #define SHA512_Update(a, b, c) (CC_SHA512_Update((a), (b), (c)))
633f84b207Sbeck #define SHA512_Init(xxx) (CC_SHA512_Init((xxx)))
643f84b207Sbeck #define SHA512_Final(xxx, yyy) (CC_SHA512_Final((xxx), (yyy)))
653f84b207Sbeck #define SHA512_CTX CC_SHA512_CTX
663f84b207Sbeck #define SHA512_DIGEST_LENGTH CC_SHA512_DIGEST_LENGTH
673f84b207Sbeck
683f84b207Sbeck #define REPEAT 5
69557f50beSderaadt #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
703f84b207Sbeck
713f84b207Sbeck #define HX(a, b) \
723f84b207Sbeck do { \
733f84b207Sbeck if ((a)) \
743f84b207Sbeck HD(errno); \
753f84b207Sbeck else \
763f84b207Sbeck HD(b); \
773f84b207Sbeck } while (0)
78fed6411aSderaadt
793f84b207Sbeck #define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l)))
803f84b207Sbeck #define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x)))
8154dec340Swouter #define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*)))
82fed6411aSderaadt
833f84b207Sbeck int getentropy(void *buf, size_t len);
843f84b207Sbeck
853f84b207Sbeck static int getentropy_urandom(void *buf, size_t len);
863f84b207Sbeck static int getentropy_fallback(void *buf, size_t len);
873f84b207Sbeck
883f84b207Sbeck int
getentropy(void * buf,size_t len)893f84b207Sbeck getentropy(void *buf, size_t len)
903f84b207Sbeck {
913f84b207Sbeck int ret = -1;
923f84b207Sbeck
933f84b207Sbeck if (len > 256) {
943f84b207Sbeck errno = EIO;
95044fc755Sderaadt return (-1);
963f84b207Sbeck }
973f84b207Sbeck
983f84b207Sbeck /*
993f84b207Sbeck * Try to get entropy with /dev/urandom
1003f84b207Sbeck *
1013f84b207Sbeck * This can fail if the process is inside a chroot or if file
1023f84b207Sbeck * descriptors are exhausted.
1033f84b207Sbeck */
1043f84b207Sbeck ret = getentropy_urandom(buf, len);
1053f84b207Sbeck if (ret != -1)
1063f84b207Sbeck return (ret);
1073f84b207Sbeck
1083f84b207Sbeck /*
1093f84b207Sbeck * Entropy collection via /dev/urandom and sysctl have failed.
1103f84b207Sbeck *
1113f84b207Sbeck * No other API exists for collecting entropy, and we have
1123f84b207Sbeck * no failsafe way to get it on OSX that is not sensitive
1133f84b207Sbeck * to resource exhaustion.
1143f84b207Sbeck *
1153f84b207Sbeck * We have very few options:
1163f84b207Sbeck * - Even syslog_r is unsafe to call at this low level, so
1173f84b207Sbeck * there is no way to alert the user or program.
1183f84b207Sbeck * - Cannot call abort() because some systems have unsafe
1193f84b207Sbeck * corefiles.
1203f84b207Sbeck * - Could raise(SIGKILL) resulting in silent program termination.
1213f84b207Sbeck * - Return EIO, to hint that arc4random's stir function
1223f84b207Sbeck * should raise(SIGKILL)
1233f84b207Sbeck * - Do the best under the circumstances....
1243f84b207Sbeck *
1253f84b207Sbeck * This code path exists to bring light to the issue that OSX
1263f84b207Sbeck * does not provide a failsafe API for entropy collection.
1273f84b207Sbeck *
1283f84b207Sbeck * We hope this demonstrates that OSX should consider
1293f84b207Sbeck * providing a new failsafe API which works in a chroot or
1303f84b207Sbeck * when file descriptors are exhausted.
1313f84b207Sbeck */
132fed6411aSderaadt #undef FAIL_INSTEAD_OF_TRYING_FALLBACK
133fed6411aSderaadt #ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK
1343f84b207Sbeck raise(SIGKILL);
1353f84b207Sbeck #endif
1363f84b207Sbeck ret = getentropy_fallback(buf, len);
1373f84b207Sbeck if (ret != -1)
1383f84b207Sbeck return (ret);
1393f84b207Sbeck
1403f84b207Sbeck errno = EIO;
1413f84b207Sbeck return (ret);
1423f84b207Sbeck }
1433f84b207Sbeck
1443f84b207Sbeck static int
getentropy_urandom(void * buf,size_t len)1453f84b207Sbeck getentropy_urandom(void *buf, size_t len)
1463f84b207Sbeck {
1473f84b207Sbeck struct stat st;
1483f84b207Sbeck size_t i;
149599b801fSbcook int fd, flags;
1503f84b207Sbeck int save_errno = errno;
1513f84b207Sbeck
1523f84b207Sbeck start:
1533f84b207Sbeck
1543f84b207Sbeck flags = O_RDONLY;
1553f84b207Sbeck #ifdef O_NOFOLLOW
1563f84b207Sbeck flags |= O_NOFOLLOW;
1573f84b207Sbeck #endif
1583f84b207Sbeck #ifdef O_CLOEXEC
1593f84b207Sbeck flags |= O_CLOEXEC;
1603f84b207Sbeck #endif
161*b7041c07Sderaadt fd = open("/dev/urandom", flags);
1623f84b207Sbeck if (fd == -1) {
1633f84b207Sbeck if (errno == EINTR)
1643f84b207Sbeck goto start;
1653f84b207Sbeck goto nodevrandom;
1663f84b207Sbeck }
1673f84b207Sbeck #ifndef O_CLOEXEC
1683f84b207Sbeck fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
1693f84b207Sbeck #endif
1703f84b207Sbeck
1713f84b207Sbeck /* Lightly verify that the device node looks sane */
1723f84b207Sbeck if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) {
1733f84b207Sbeck close(fd);
1743f84b207Sbeck goto nodevrandom;
1753f84b207Sbeck }
1763f84b207Sbeck for (i = 0; i < len; ) {
1773f84b207Sbeck size_t wanted = len - i;
17854dec340Swouter ssize_t ret = read(fd, (char *)buf + i, wanted);
1793f84b207Sbeck
1803f84b207Sbeck if (ret == -1) {
1813f84b207Sbeck if (errno == EAGAIN || errno == EINTR)
1823f84b207Sbeck continue;
1833f84b207Sbeck close(fd);
1843f84b207Sbeck goto nodevrandom;
1853f84b207Sbeck }
1863f84b207Sbeck i += ret;
1873f84b207Sbeck }
1883f84b207Sbeck close(fd);
1893f84b207Sbeck errno = save_errno;
190044fc755Sderaadt return (0); /* satisfied */
1913f84b207Sbeck nodevrandom:
1923f84b207Sbeck errno = EIO;
193044fc755Sderaadt return (-1);
1943f84b207Sbeck }
1953f84b207Sbeck
196c283a6cdSbcook #if TARGET_OS_OSX
197fed6411aSderaadt static int tcpmib[] = { CTL_NET, AF_INET, IPPROTO_TCP, TCPCTL_STATS };
198fed6411aSderaadt static int udpmib[] = { CTL_NET, AF_INET, IPPROTO_UDP, UDPCTL_STATS };
199fed6411aSderaadt static int ipmib[] = { CTL_NET, AF_INET, IPPROTO_IP, IPCTL_STATS };
200c283a6cdSbcook #endif
201fed6411aSderaadt static int kmib[] = { CTL_KERN, KERN_USRSTACK };
202fed6411aSderaadt static int hwmib[] = { CTL_HW, HW_USERMEM };
203fed6411aSderaadt
2043f84b207Sbeck static int
getentropy_fallback(void * buf,size_t len)2053f84b207Sbeck getentropy_fallback(void *buf, size_t len)
2063f84b207Sbeck {
2073f84b207Sbeck uint8_t results[SHA512_DIGEST_LENGTH];
20854dec340Swouter int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat;
209fed6411aSderaadt static int cnt;
2103f84b207Sbeck struct timespec ts;
2113f84b207Sbeck struct timeval tv;
2123f84b207Sbeck struct rusage ru;
2133f84b207Sbeck sigset_t sigset;
2143f84b207Sbeck struct stat st;
2153f84b207Sbeck SHA512_CTX ctx;
2163f84b207Sbeck static pid_t lastpid;
2173f84b207Sbeck pid_t pid;
21854dec340Swouter size_t i, ii, m;
2193f84b207Sbeck char *p;
220c283a6cdSbcook #if TARGET_OS_OSX
221fed6411aSderaadt struct tcpstat tcpstat;
222fed6411aSderaadt struct udpstat udpstat;
223fed6411aSderaadt struct ipstat ipstat;
224c283a6cdSbcook #endif
225fed6411aSderaadt u_int64_t mach_time;
226fed6411aSderaadt unsigned int idata;
227fed6411aSderaadt void *addr;
2283f84b207Sbeck
2293f84b207Sbeck pid = getpid();
2303f84b207Sbeck if (lastpid == pid) {
2313f84b207Sbeck faster = 1;
2323f84b207Sbeck repeat = 2;
2333f84b207Sbeck } else {
2343f84b207Sbeck faster = 0;
2353f84b207Sbeck lastpid = pid;
2363f84b207Sbeck repeat = REPEAT;
2373f84b207Sbeck }
2383f84b207Sbeck for (i = 0; i < len; ) {
2393f84b207Sbeck int j;
2403f84b207Sbeck SHA512_Init(&ctx);
2413f84b207Sbeck for (j = 0; j < repeat; j++) {
2423f84b207Sbeck HX((e = gettimeofday(&tv, NULL)) == -1, tv);
2433f84b207Sbeck if (e != -1) {
2443f84b207Sbeck cnt += (int)tv.tv_sec;
2453f84b207Sbeck cnt += (int)tv.tv_usec;
2463f84b207Sbeck }
2473f84b207Sbeck
2483f84b207Sbeck mach_time = mach_absolute_time();
2493f84b207Sbeck HD(mach_time);
2503f84b207Sbeck
251fed6411aSderaadt ii = sizeof(addr);
2523f84b207Sbeck HX(sysctl(kmib, sizeof(kmib) / sizeof(kmib[0]),
253fed6411aSderaadt &addr, &ii, NULL, 0) == -1, addr);
2543f84b207Sbeck
255fed6411aSderaadt ii = sizeof(idata);
2563f84b207Sbeck HX(sysctl(hwmib, sizeof(hwmib) / sizeof(hwmib[0]),
257fed6411aSderaadt &idata, &ii, NULL, 0) == -1, idata);
2583f84b207Sbeck
259c283a6cdSbcook #if TARGET_OS_OSX
260fed6411aSderaadt ii = sizeof(tcpstat);
2613f84b207Sbeck HX(sysctl(tcpmib, sizeof(tcpmib) / sizeof(tcpmib[0]),
262fed6411aSderaadt &tcpstat, &ii, NULL, 0) == -1, tcpstat);
2633f84b207Sbeck
264fed6411aSderaadt ii = sizeof(udpstat);
2653f84b207Sbeck HX(sysctl(udpmib, sizeof(udpmib) / sizeof(udpmib[0]),
266fed6411aSderaadt &udpstat, &ii, NULL, 0) == -1, udpstat);
2673f84b207Sbeck
268fed6411aSderaadt ii = sizeof(ipstat);
2693f84b207Sbeck HX(sysctl(ipmib, sizeof(ipmib) / sizeof(ipmib[0]),
270fed6411aSderaadt &ipstat, &ii, NULL, 0) == -1, ipstat);
271c283a6cdSbcook #endif
2723f84b207Sbeck
2733f84b207Sbeck HX((pid = getpid()) == -1, pid);
2743f84b207Sbeck HX((pid = getsid(pid)) == -1, pid);
2753f84b207Sbeck HX((pid = getppid()) == -1, pid);
2763f84b207Sbeck HX((pid = getpgid(0)) == -1, pid);
27754dec340Swouter HX((e = getpriority(0, 0)) == -1, e);
2783f84b207Sbeck
2793f84b207Sbeck if (!faster) {
2803f84b207Sbeck ts.tv_sec = 0;
2813f84b207Sbeck ts.tv_nsec = 1;
2823f84b207Sbeck (void) nanosleep(&ts, NULL);
2833f84b207Sbeck }
2843f84b207Sbeck
2853f84b207Sbeck HX(sigpending(&sigset) == -1, sigset);
2863f84b207Sbeck HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1,
2873f84b207Sbeck sigset);
2883f84b207Sbeck
28954dec340Swouter HF(getentropy); /* an addr in this library */
29054dec340Swouter HF(printf); /* an addr in libc */
2913f84b207Sbeck p = (char *)&p;
2923f84b207Sbeck HD(p); /* an addr on stack */
2933f84b207Sbeck p = (char *)&errno;
2943f84b207Sbeck HD(p); /* the addr of errno */
2953f84b207Sbeck
2963f84b207Sbeck if (i == 0) {
2973f84b207Sbeck struct sockaddr_storage ss;
2983f84b207Sbeck struct statvfs stvfs;
2993f84b207Sbeck struct termios tios;
3003f84b207Sbeck struct statfs stfs;
3013f84b207Sbeck socklen_t ssl;
3023f84b207Sbeck off_t off;
3033f84b207Sbeck
3043f84b207Sbeck /*
3053f84b207Sbeck * Prime-sized mappings encourage fragmentation;
3063f84b207Sbeck * thus exposing some address entropy.
3073f84b207Sbeck */
3083f84b207Sbeck struct mm {
3093f84b207Sbeck size_t npg;
3103f84b207Sbeck void *p;
3113f84b207Sbeck } mm[] = {
3123f84b207Sbeck { 17, MAP_FAILED }, { 3, MAP_FAILED },
3133f84b207Sbeck { 11, MAP_FAILED }, { 2, MAP_FAILED },
3143f84b207Sbeck { 5, MAP_FAILED }, { 3, MAP_FAILED },
3153f84b207Sbeck { 7, MAP_FAILED }, { 1, MAP_FAILED },
3163f84b207Sbeck { 57, MAP_FAILED }, { 3, MAP_FAILED },
3173f84b207Sbeck { 131, MAP_FAILED }, { 1, MAP_FAILED },
3183f84b207Sbeck };
3193f84b207Sbeck
3203f84b207Sbeck for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
3213f84b207Sbeck HX(mm[m].p = mmap(NULL,
3223f84b207Sbeck mm[m].npg * pgs,
3233f84b207Sbeck PROT_READ|PROT_WRITE,
3243f84b207Sbeck MAP_PRIVATE|MAP_ANON, -1,
3253f84b207Sbeck (off_t)0), mm[m].p);
3263f84b207Sbeck if (mm[m].p != MAP_FAILED) {
3273f84b207Sbeck size_t mo;
3283f84b207Sbeck
3293f84b207Sbeck /* Touch some memory... */
3303f84b207Sbeck p = mm[m].p;
3313f84b207Sbeck mo = cnt %
3323f84b207Sbeck (mm[m].npg * pgs - 1);
3333f84b207Sbeck p[mo] = 1;
3343f84b207Sbeck cnt += (int)((long)(mm[m].p)
3353f84b207Sbeck / pgs);
3363f84b207Sbeck }
3373f84b207Sbeck
3383f84b207Sbeck /* Check cnts and times... */
3393f84b207Sbeck mach_time = mach_absolute_time();
3403f84b207Sbeck HD(mach_time);
3413f84b207Sbeck cnt += (int)mach_time;
3423f84b207Sbeck
3433f84b207Sbeck HX((e = getrusage(RUSAGE_SELF,
3443f84b207Sbeck &ru)) == -1, ru);
3453f84b207Sbeck if (e != -1) {
3463f84b207Sbeck cnt += (int)ru.ru_utime.tv_sec;
3473f84b207Sbeck cnt += (int)ru.ru_utime.tv_usec;
3483f84b207Sbeck }
3493f84b207Sbeck }
3503f84b207Sbeck
3513f84b207Sbeck for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
3523f84b207Sbeck if (mm[m].p != MAP_FAILED)
3533f84b207Sbeck munmap(mm[m].p, mm[m].npg * pgs);
3543f84b207Sbeck mm[m].p = MAP_FAILED;
3553f84b207Sbeck }
3563f84b207Sbeck
3573f84b207Sbeck HX(stat(".", &st) == -1, st);
3583f84b207Sbeck HX(statvfs(".", &stvfs) == -1, stvfs);
3593f84b207Sbeck HX(statfs(".", &stfs) == -1, stfs);
3603f84b207Sbeck
3613f84b207Sbeck HX(stat("/", &st) == -1, st);
3623f84b207Sbeck HX(statvfs("/", &stvfs) == -1, stvfs);
3633f84b207Sbeck HX(statfs("/", &stfs) == -1, stfs);
3643f84b207Sbeck
3653f84b207Sbeck HX((e = fstat(0, &st)) == -1, st);
3663f84b207Sbeck if (e == -1) {
3673f84b207Sbeck if (S_ISREG(st.st_mode) ||
3683f84b207Sbeck S_ISFIFO(st.st_mode) ||
3693f84b207Sbeck S_ISSOCK(st.st_mode)) {
3703f84b207Sbeck HX(fstatvfs(0, &stvfs) == -1,
3713f84b207Sbeck stvfs);
3723f84b207Sbeck HX(fstatfs(0, &stfs) == -1,
3733f84b207Sbeck stfs);
3743f84b207Sbeck HX((off = lseek(0, (off_t)0,
3753f84b207Sbeck SEEK_CUR)) < 0, off);
3763f84b207Sbeck }
3773f84b207Sbeck if (S_ISCHR(st.st_mode)) {
3783f84b207Sbeck HX(tcgetattr(0, &tios) == -1,
3793f84b207Sbeck tios);
3803f84b207Sbeck } else if (S_ISSOCK(st.st_mode)) {
3813f84b207Sbeck memset(&ss, 0, sizeof ss);
3823f84b207Sbeck ssl = sizeof(ss);
3833f84b207Sbeck HX(getpeername(0,
3843f84b207Sbeck (void *)&ss, &ssl) == -1,
3853f84b207Sbeck ss);
3863f84b207Sbeck }
3873f84b207Sbeck }
3883f84b207Sbeck
3893f84b207Sbeck HX((e = getrusage(RUSAGE_CHILDREN,
3903f84b207Sbeck &ru)) == -1, ru);
3913f84b207Sbeck if (e != -1) {
3923f84b207Sbeck cnt += (int)ru.ru_utime.tv_sec;
3933f84b207Sbeck cnt += (int)ru.ru_utime.tv_usec;
3943f84b207Sbeck }
3953f84b207Sbeck } else {
3963f84b207Sbeck /* Subsequent hashes absorb previous result */
3973f84b207Sbeck HD(results);
3983f84b207Sbeck }
3993f84b207Sbeck
4003f84b207Sbeck HX((e = gettimeofday(&tv, NULL)) == -1, tv);
4013f84b207Sbeck if (e != -1) {
4023f84b207Sbeck cnt += (int)tv.tv_sec;
4033f84b207Sbeck cnt += (int)tv.tv_usec;
4043f84b207Sbeck }
4053f84b207Sbeck
4063f84b207Sbeck HD(cnt);
4073f84b207Sbeck }
4083f84b207Sbeck
4093f84b207Sbeck SHA512_Final(results, &ctx);
410557f50beSderaadt memcpy((char *)buf + i, results, MINIMUM(sizeof(results), len - i));
411557f50beSderaadt i += MINIMUM(sizeof(results), len - i);
4123f84b207Sbeck }
4135fd8226cSguenther explicit_bzero(&ctx, sizeof ctx);
4145fd8226cSguenther explicit_bzero(results, sizeof results);
4153f84b207Sbeck errno = save_errno;
416044fc755Sderaadt return (0); /* satisfied */
4173f84b207Sbeck }
418