1356345Scy/* $OpenBSD: getentropy_osx.c,v 1.12 2018/11/20 08:04:28 deraadt Exp $ */ 2276541Sdes 3276541Sdes/* 4276541Sdes * Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org> 5276541Sdes * Copyright (c) 2014 Bob Beck <beck@obtuse.com> 6276541Sdes * 7276541Sdes * Permission to use, copy, modify, and distribute this software for any 8276541Sdes * purpose with or without fee is hereby granted, provided that the above 9276541Sdes * copyright notice and this permission notice appear in all copies. 10276541Sdes * 11276541Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12276541Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13276541Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14276541Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15276541Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16276541Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17276541Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18356345Scy * 19356345Scy * Emulation of getentropy(2) as documented at: 20356345Scy * http://man.openbsd.org/getentropy.2 21276541Sdes */ 22276541Sdes 23356345Scy#include <TargetConditionals.h> 24276541Sdes#include <sys/types.h> 25276541Sdes#include <sys/param.h> 26276541Sdes#include <sys/ioctl.h> 27276541Sdes#include <sys/resource.h> 28276541Sdes#include <sys/syscall.h> 29276541Sdes#include <sys/sysctl.h> 30276541Sdes#include <sys/statvfs.h> 31276541Sdes#include <sys/socket.h> 32276541Sdes#include <sys/mount.h> 33276541Sdes#include <sys/mman.h> 34276541Sdes#include <sys/stat.h> 35276541Sdes#include <sys/time.h> 36276541Sdes#include <stdlib.h> 37276541Sdes#include <stdint.h> 38276541Sdes#include <stdio.h> 39276541Sdes#include <termios.h> 40276541Sdes#include <fcntl.h> 41276541Sdes#include <signal.h> 42276541Sdes#include <string.h> 43276541Sdes#include <errno.h> 44276541Sdes#include <unistd.h> 45276541Sdes#include <time.h> 46276541Sdes#include <mach/mach_time.h> 47276541Sdes#include <mach/mach_host.h> 48276541Sdes#include <mach/host_info.h> 49356345Scy#if TARGET_OS_OSX 50276541Sdes#include <sys/socketvar.h> 51276541Sdes#include <sys/vmmeter.h> 52356345Scy#endif 53276541Sdes#include <netinet/in.h> 54276541Sdes#include <netinet/tcp.h> 55356345Scy#if TARGET_OS_OSX 56276541Sdes#include <netinet/udp.h> 57276541Sdes#include <netinet/ip_var.h> 58276541Sdes#include <netinet/tcp_var.h> 59276541Sdes#include <netinet/udp_var.h> 60356345Scy#endif 61276541Sdes#include <CommonCrypto/CommonDigest.h> 62276541Sdes#define SHA512_Update(a, b, c) (CC_SHA512_Update((a), (b), (c))) 63276541Sdes#define SHA512_Init(xxx) (CC_SHA512_Init((xxx))) 64276541Sdes#define SHA512_Final(xxx, yyy) (CC_SHA512_Final((xxx), (yyy))) 65276541Sdes#define SHA512_CTX CC_SHA512_CTX 66276541Sdes#define SHA512_DIGEST_LENGTH CC_SHA512_DIGEST_LENGTH 67276541Sdes 68276541Sdes#define REPEAT 5 69276541Sdes#define min(a, b) (((a) < (b)) ? (a) : (b)) 70276541Sdes 71276541Sdes#define HX(a, b) \ 72276541Sdes do { \ 73276541Sdes if ((a)) \ 74276541Sdes HD(errno); \ 75276541Sdes else \ 76276541Sdes HD(b); \ 77276541Sdes } while (0) 78276541Sdes 79276541Sdes#define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l))) 80276541Sdes#define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x))) 81276541Sdes#define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*))) 82276541Sdes 83276541Sdesint getentropy(void *buf, size_t len); 84276541Sdes 85276541Sdesstatic int getentropy_urandom(void *buf, size_t len); 86276541Sdesstatic int getentropy_fallback(void *buf, size_t len); 87276541Sdes 88276541Sdesint 89276541Sdesgetentropy(void *buf, size_t len) 90276541Sdes{ 91276541Sdes int ret = -1; 92276541Sdes 93276541Sdes if (len > 256) { 94276541Sdes errno = EIO; 95356345Scy return (-1); 96276541Sdes } 97276541Sdes 98276541Sdes /* 99276541Sdes * Try to get entropy with /dev/urandom 100276541Sdes * 101276541Sdes * This can fail if the process is inside a chroot or if file 102276541Sdes * descriptors are exhausted. 103276541Sdes */ 104276541Sdes ret = getentropy_urandom(buf, len); 105276541Sdes if (ret != -1) 106276541Sdes return (ret); 107276541Sdes 108276541Sdes /* 109276541Sdes * Entropy collection via /dev/urandom and sysctl have failed. 110276541Sdes * 111276541Sdes * No other API exists for collecting entropy, and we have 112276541Sdes * no failsafe way to get it on OSX that is not sensitive 113276541Sdes * to resource exhaustion. 114276541Sdes * 115276541Sdes * We have very few options: 116276541Sdes * - Even syslog_r is unsafe to call at this low level, so 117276541Sdes * there is no way to alert the user or program. 118276541Sdes * - Cannot call abort() because some systems have unsafe 119276541Sdes * corefiles. 120276541Sdes * - Could raise(SIGKILL) resulting in silent program termination. 121276541Sdes * - Return EIO, to hint that arc4random's stir function 122276541Sdes * should raise(SIGKILL) 123276541Sdes * - Do the best under the circumstances.... 124276541Sdes * 125276541Sdes * This code path exists to bring light to the issue that OSX 126276541Sdes * does not provide a failsafe API for entropy collection. 127276541Sdes * 128276541Sdes * We hope this demonstrates that OSX should consider 129276541Sdes * providing a new failsafe API which works in a chroot or 130276541Sdes * when file descriptors are exhausted. 131276541Sdes */ 132276541Sdes#undef FAIL_INSTEAD_OF_TRYING_FALLBACK 133276541Sdes#ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK 134276541Sdes raise(SIGKILL); 135276541Sdes#endif 136276541Sdes ret = getentropy_fallback(buf, len); 137276541Sdes if (ret != -1) 138276541Sdes return (ret); 139276541Sdes 140276541Sdes errno = EIO; 141276541Sdes return (ret); 142276541Sdes} 143276541Sdes 144276541Sdesstatic int 145276541Sdesgetentropy_urandom(void *buf, size_t len) 146276541Sdes{ 147276541Sdes struct stat st; 148276541Sdes size_t i; 149276541Sdes int fd, flags; 150276541Sdes int save_errno = errno; 151276541Sdes 152276541Sdesstart: 153276541Sdes 154276541Sdes flags = O_RDONLY; 155276541Sdes#ifdef O_NOFOLLOW 156276541Sdes flags |= O_NOFOLLOW; 157276541Sdes#endif 158276541Sdes#ifdef O_CLOEXEC 159276541Sdes flags |= O_CLOEXEC; 160276541Sdes#endif 161276541Sdes fd = open("/dev/urandom", flags, 0); 162276541Sdes if (fd == -1) { 163276541Sdes if (errno == EINTR) 164276541Sdes goto start; 165276541Sdes goto nodevrandom; 166276541Sdes } 167276541Sdes#ifndef O_CLOEXEC 168276541Sdes fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 169276541Sdes#endif 170276541Sdes 171276541Sdes /* Lightly verify that the device node looks sane */ 172276541Sdes if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) { 173276541Sdes close(fd); 174276541Sdes goto nodevrandom; 175276541Sdes } 176276541Sdes for (i = 0; i < len; ) { 177276541Sdes size_t wanted = len - i; 178356345Scy ssize_t ret = read(fd, (char *)buf + i, wanted); 179276541Sdes 180276541Sdes if (ret == -1) { 181276541Sdes if (errno == EAGAIN || errno == EINTR) 182276541Sdes continue; 183276541Sdes close(fd); 184276541Sdes goto nodevrandom; 185276541Sdes } 186276541Sdes i += ret; 187276541Sdes } 188276541Sdes close(fd); 189356345Scy errno = save_errno; 190356345Scy return (0); /* satisfied */ 191276541Sdesnodevrandom: 192276541Sdes errno = EIO; 193356345Scy return (-1); 194276541Sdes} 195276541Sdes 196356345Scy#if TARGET_OS_OSX 197276541Sdesstatic int tcpmib[] = { CTL_NET, AF_INET, IPPROTO_TCP, TCPCTL_STATS }; 198276541Sdesstatic int udpmib[] = { CTL_NET, AF_INET, IPPROTO_UDP, UDPCTL_STATS }; 199276541Sdesstatic int ipmib[] = { CTL_NET, AF_INET, IPPROTO_IP, IPCTL_STATS }; 200356345Scy#endif 201276541Sdesstatic int kmib[] = { CTL_KERN, KERN_USRSTACK }; 202276541Sdesstatic int hwmib[] = { CTL_HW, HW_USERMEM }; 203276541Sdes 204276541Sdesstatic int 205276541Sdesgetentropy_fallback(void *buf, size_t len) 206276541Sdes{ 207276541Sdes uint8_t results[SHA512_DIGEST_LENGTH]; 208276541Sdes int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat; 209276541Sdes static int cnt; 210276541Sdes struct timespec ts; 211276541Sdes struct timeval tv; 212276541Sdes struct rusage ru; 213276541Sdes sigset_t sigset; 214276541Sdes struct stat st; 215276541Sdes SHA512_CTX ctx; 216276541Sdes static pid_t lastpid; 217276541Sdes pid_t pid; 218276541Sdes size_t i, ii, m; 219276541Sdes char *p; 220356345Scy#if TARGET_OS_OSX 221276541Sdes struct tcpstat tcpstat; 222276541Sdes struct udpstat udpstat; 223276541Sdes struct ipstat ipstat; 224356345Scy#endif 225276541Sdes u_int64_t mach_time; 226276541Sdes unsigned int idata; 227276541Sdes void *addr; 228276541Sdes 229276541Sdes pid = getpid(); 230276541Sdes if (lastpid == pid) { 231276541Sdes faster = 1; 232276541Sdes repeat = 2; 233276541Sdes } else { 234276541Sdes faster = 0; 235276541Sdes lastpid = pid; 236276541Sdes repeat = REPEAT; 237276541Sdes } 238276541Sdes for (i = 0; i < len; ) { 239276541Sdes int j; 240276541Sdes SHA512_Init(&ctx); 241276541Sdes for (j = 0; j < repeat; j++) { 242276541Sdes HX((e = gettimeofday(&tv, NULL)) == -1, tv); 243276541Sdes if (e != -1) { 244276541Sdes cnt += (int)tv.tv_sec; 245276541Sdes cnt += (int)tv.tv_usec; 246276541Sdes } 247276541Sdes 248276541Sdes mach_time = mach_absolute_time(); 249276541Sdes HD(mach_time); 250276541Sdes 251276541Sdes ii = sizeof(addr); 252276541Sdes HX(sysctl(kmib, sizeof(kmib) / sizeof(kmib[0]), 253276541Sdes &addr, &ii, NULL, 0) == -1, addr); 254276541Sdes 255276541Sdes ii = sizeof(idata); 256276541Sdes HX(sysctl(hwmib, sizeof(hwmib) / sizeof(hwmib[0]), 257276541Sdes &idata, &ii, NULL, 0) == -1, idata); 258276541Sdes 259356345Scy#if TARGET_OS_OSX 260276541Sdes ii = sizeof(tcpstat); 261276541Sdes HX(sysctl(tcpmib, sizeof(tcpmib) / sizeof(tcpmib[0]), 262276541Sdes &tcpstat, &ii, NULL, 0) == -1, tcpstat); 263276541Sdes 264276541Sdes ii = sizeof(udpstat); 265276541Sdes HX(sysctl(udpmib, sizeof(udpmib) / sizeof(udpmib[0]), 266276541Sdes &udpstat, &ii, NULL, 0) == -1, udpstat); 267276541Sdes 268276541Sdes ii = sizeof(ipstat); 269276541Sdes HX(sysctl(ipmib, sizeof(ipmib) / sizeof(ipmib[0]), 270276541Sdes &ipstat, &ii, NULL, 0) == -1, ipstat); 271356345Scy#endif 272276541Sdes 273276541Sdes HX((pid = getpid()) == -1, pid); 274276541Sdes HX((pid = getsid(pid)) == -1, pid); 275276541Sdes HX((pid = getppid()) == -1, pid); 276276541Sdes HX((pid = getpgid(0)) == -1, pid); 277276541Sdes HX((e = getpriority(0, 0)) == -1, e); 278276541Sdes 279276541Sdes if (!faster) { 280276541Sdes ts.tv_sec = 0; 281276541Sdes ts.tv_nsec = 1; 282276541Sdes (void) nanosleep(&ts, NULL); 283276541Sdes } 284276541Sdes 285276541Sdes HX(sigpending(&sigset) == -1, sigset); 286276541Sdes HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1, 287276541Sdes sigset); 288276541Sdes 289276541Sdes HF(getentropy); /* an addr in this library */ 290276541Sdes HF(printf); /* an addr in libc */ 291276541Sdes p = (char *)&p; 292276541Sdes HD(p); /* an addr on stack */ 293276541Sdes p = (char *)&errno; 294276541Sdes HD(p); /* the addr of errno */ 295276541Sdes 296276541Sdes if (i == 0) { 297276541Sdes struct sockaddr_storage ss; 298276541Sdes struct statvfs stvfs; 299276541Sdes struct termios tios; 300276541Sdes struct statfs stfs; 301276541Sdes socklen_t ssl; 302276541Sdes off_t off; 303276541Sdes 304276541Sdes /* 305276541Sdes * Prime-sized mappings encourage fragmentation; 306276541Sdes * thus exposing some address entropy. 307276541Sdes */ 308276541Sdes struct mm { 309276541Sdes size_t npg; 310276541Sdes void *p; 311276541Sdes } mm[] = { 312276541Sdes { 17, MAP_FAILED }, { 3, MAP_FAILED }, 313276541Sdes { 11, MAP_FAILED }, { 2, MAP_FAILED }, 314276541Sdes { 5, MAP_FAILED }, { 3, MAP_FAILED }, 315276541Sdes { 7, MAP_FAILED }, { 1, MAP_FAILED }, 316276541Sdes { 57, MAP_FAILED }, { 3, MAP_FAILED }, 317276541Sdes { 131, MAP_FAILED }, { 1, MAP_FAILED }, 318276541Sdes }; 319276541Sdes 320276541Sdes for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { 321276541Sdes HX(mm[m].p = mmap(NULL, 322276541Sdes mm[m].npg * pgs, 323276541Sdes PROT_READ|PROT_WRITE, 324276541Sdes MAP_PRIVATE|MAP_ANON, -1, 325276541Sdes (off_t)0), mm[m].p); 326276541Sdes if (mm[m].p != MAP_FAILED) { 327276541Sdes size_t mo; 328276541Sdes 329276541Sdes /* Touch some memory... */ 330276541Sdes p = mm[m].p; 331276541Sdes mo = cnt % 332276541Sdes (mm[m].npg * pgs - 1); 333276541Sdes p[mo] = 1; 334276541Sdes cnt += (int)((long)(mm[m].p) 335276541Sdes / pgs); 336276541Sdes } 337276541Sdes 338276541Sdes /* Check cnts and times... */ 339276541Sdes mach_time = mach_absolute_time(); 340276541Sdes HD(mach_time); 341276541Sdes cnt += (int)mach_time; 342276541Sdes 343276541Sdes HX((e = getrusage(RUSAGE_SELF, 344276541Sdes &ru)) == -1, ru); 345276541Sdes if (e != -1) { 346276541Sdes cnt += (int)ru.ru_utime.tv_sec; 347276541Sdes cnt += (int)ru.ru_utime.tv_usec; 348276541Sdes } 349276541Sdes } 350276541Sdes 351276541Sdes for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { 352276541Sdes if (mm[m].p != MAP_FAILED) 353276541Sdes munmap(mm[m].p, mm[m].npg * pgs); 354276541Sdes mm[m].p = MAP_FAILED; 355276541Sdes } 356276541Sdes 357276541Sdes HX(stat(".", &st) == -1, st); 358276541Sdes HX(statvfs(".", &stvfs) == -1, stvfs); 359276541Sdes HX(statfs(".", &stfs) == -1, stfs); 360276541Sdes 361276541Sdes HX(stat("/", &st) == -1, st); 362276541Sdes HX(statvfs("/", &stvfs) == -1, stvfs); 363276541Sdes HX(statfs("/", &stfs) == -1, stfs); 364276541Sdes 365276541Sdes HX((e = fstat(0, &st)) == -1, st); 366276541Sdes if (e == -1) { 367276541Sdes if (S_ISREG(st.st_mode) || 368276541Sdes S_ISFIFO(st.st_mode) || 369276541Sdes S_ISSOCK(st.st_mode)) { 370276541Sdes HX(fstatvfs(0, &stvfs) == -1, 371276541Sdes stvfs); 372276541Sdes HX(fstatfs(0, &stfs) == -1, 373276541Sdes stfs); 374276541Sdes HX((off = lseek(0, (off_t)0, 375276541Sdes SEEK_CUR)) < 0, off); 376276541Sdes } 377276541Sdes if (S_ISCHR(st.st_mode)) { 378276541Sdes HX(tcgetattr(0, &tios) == -1, 379276541Sdes tios); 380276541Sdes } else if (S_ISSOCK(st.st_mode)) { 381276541Sdes memset(&ss, 0, sizeof ss); 382276541Sdes ssl = sizeof(ss); 383276541Sdes HX(getpeername(0, 384276541Sdes (void *)&ss, &ssl) == -1, 385276541Sdes ss); 386276541Sdes } 387276541Sdes } 388276541Sdes 389276541Sdes HX((e = getrusage(RUSAGE_CHILDREN, 390276541Sdes &ru)) == -1, ru); 391276541Sdes if (e != -1) { 392276541Sdes cnt += (int)ru.ru_utime.tv_sec; 393276541Sdes cnt += (int)ru.ru_utime.tv_usec; 394276541Sdes } 395276541Sdes } else { 396276541Sdes /* Subsequent hashes absorb previous result */ 397276541Sdes HD(results); 398276541Sdes } 399276541Sdes 400276541Sdes HX((e = gettimeofday(&tv, NULL)) == -1, tv); 401276541Sdes if (e != -1) { 402276541Sdes cnt += (int)tv.tv_sec; 403276541Sdes cnt += (int)tv.tv_usec; 404276541Sdes } 405276541Sdes 406276541Sdes HD(cnt); 407276541Sdes } 408276541Sdes 409276541Sdes SHA512_Final(results, &ctx); 410356345Scy memcpy((char *)buf + i, results, min(sizeof(results), len - i)); 411276541Sdes i += min(sizeof(results), len - i); 412276541Sdes } 413356345Scy explicit_bzero(&ctx, sizeof ctx); 414356345Scy explicit_bzero(results, sizeof results); 415356345Scy errno = save_errno; 416356345Scy return (0); /* satisfied */ 417276541Sdes} 418