1276541Sdes/* $OpenBSD: getentropy_osx.c,v 1.3 2014/07/12 14:48:00 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. 18276541Sdes */ 19276541Sdes#include "config.h" 20276541Sdes 21276541Sdes#include <sys/types.h> 22276541Sdes#include <sys/param.h> 23276541Sdes#include <sys/ioctl.h> 24276541Sdes#include <sys/resource.h> 25276541Sdes#include <sys/syscall.h> 26276541Sdes#include <sys/sysctl.h> 27276541Sdes#include <sys/statvfs.h> 28276541Sdes#include <sys/socket.h> 29276541Sdes#include <sys/mount.h> 30276541Sdes#include <sys/mman.h> 31276541Sdes#include <sys/stat.h> 32276541Sdes#include <sys/time.h> 33276541Sdes#include <stdlib.h> 34276541Sdes#include <stdint.h> 35276541Sdes#include <stdio.h> 36276541Sdes#include <termios.h> 37276541Sdes#include <fcntl.h> 38276541Sdes#include <signal.h> 39276541Sdes#include <string.h> 40276541Sdes#include <errno.h> 41276541Sdes#include <unistd.h> 42276541Sdes#include <time.h> 43276541Sdes#include <mach/mach_time.h> 44276541Sdes#include <mach/mach_host.h> 45276541Sdes#include <mach/host_info.h> 46276541Sdes#include <sys/socketvar.h> 47276541Sdes#include <sys/vmmeter.h> 48276541Sdes#include <netinet/in.h> 49276541Sdes#include <netinet/tcp.h> 50276541Sdes#include <netinet/udp.h> 51276541Sdes#include <netinet/ip_var.h> 52276541Sdes#include <netinet/tcp_var.h> 53276541Sdes#include <netinet/udp_var.h> 54276541Sdes#include <CommonCrypto/CommonDigest.h> 55276541Sdes#define SHA512_Update(a, b, c) (CC_SHA512_Update((a), (b), (c))) 56276541Sdes#define SHA512_Init(xxx) (CC_SHA512_Init((xxx))) 57276541Sdes#define SHA512_Final(xxx, yyy) (CC_SHA512_Final((xxx), (yyy))) 58276541Sdes#define SHA512_CTX CC_SHA512_CTX 59276541Sdes#define SHA512_DIGEST_LENGTH CC_SHA512_DIGEST_LENGTH 60276541Sdes 61276541Sdes#define REPEAT 5 62276541Sdes#define min(a, b) (((a) < (b)) ? (a) : (b)) 63276541Sdes 64276541Sdes#define HX(a, b) \ 65276541Sdes do { \ 66276541Sdes if ((a)) \ 67276541Sdes HD(errno); \ 68276541Sdes else \ 69276541Sdes HD(b); \ 70276541Sdes } while (0) 71276541Sdes 72276541Sdes#define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l))) 73276541Sdes#define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x))) 74276541Sdes#define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*))) 75276541Sdes 76276541Sdesint getentropy(void *buf, size_t len); 77276541Sdes 78276541Sdes#ifdef CAN_REFERENCE_MAIN 79276541Sdesextern int main(int, char *argv[]); 80276541Sdes#endif 81276541Sdesstatic int gotdata(char *buf, size_t len); 82276541Sdesstatic int getentropy_urandom(void *buf, size_t len); 83276541Sdesstatic int getentropy_fallback(void *buf, size_t len); 84276541Sdes 85276541Sdesint 86276541Sdesgetentropy(void *buf, size_t len) 87276541Sdes{ 88276541Sdes int ret = -1; 89276541Sdes 90276541Sdes if (len > 256) { 91276541Sdes errno = EIO; 92276541Sdes return -1; 93276541Sdes } 94276541Sdes 95276541Sdes /* 96276541Sdes * Try to get entropy with /dev/urandom 97276541Sdes * 98276541Sdes * This can fail if the process is inside a chroot or if file 99276541Sdes * descriptors are exhausted. 100276541Sdes */ 101276541Sdes ret = getentropy_urandom(buf, len); 102276541Sdes if (ret != -1) 103276541Sdes return (ret); 104276541Sdes 105276541Sdes /* 106276541Sdes * Entropy collection via /dev/urandom and sysctl have failed. 107276541Sdes * 108276541Sdes * No other API exists for collecting entropy, and we have 109276541Sdes * no failsafe way to get it on OSX that is not sensitive 110276541Sdes * to resource exhaustion. 111276541Sdes * 112276541Sdes * We have very few options: 113276541Sdes * - Even syslog_r is unsafe to call at this low level, so 114276541Sdes * there is no way to alert the user or program. 115276541Sdes * - Cannot call abort() because some systems have unsafe 116276541Sdes * corefiles. 117276541Sdes * - Could raise(SIGKILL) resulting in silent program termination. 118276541Sdes * - Return EIO, to hint that arc4random's stir function 119276541Sdes * should raise(SIGKILL) 120276541Sdes * - Do the best under the circumstances.... 121276541Sdes * 122276541Sdes * This code path exists to bring light to the issue that OSX 123276541Sdes * does not provide a failsafe API for entropy collection. 124276541Sdes * 125276541Sdes * We hope this demonstrates that OSX should consider 126276541Sdes * providing a new failsafe API which works in a chroot or 127276541Sdes * when file descriptors are exhausted. 128276541Sdes */ 129276541Sdes#undef FAIL_INSTEAD_OF_TRYING_FALLBACK 130276541Sdes#ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK 131276541Sdes raise(SIGKILL); 132276541Sdes#endif 133276541Sdes ret = getentropy_fallback(buf, len); 134276541Sdes if (ret != -1) 135276541Sdes return (ret); 136276541Sdes 137276541Sdes errno = EIO; 138276541Sdes return (ret); 139276541Sdes} 140276541Sdes 141276541Sdes/* 142276541Sdes * Basic sanity checking; wish we could do better. 143276541Sdes */ 144276541Sdesstatic int 145276541Sdesgotdata(char *buf, size_t len) 146276541Sdes{ 147276541Sdes char any_set = 0; 148276541Sdes size_t i; 149276541Sdes 150276541Sdes for (i = 0; i < len; ++i) 151276541Sdes any_set |= buf[i]; 152276541Sdes if (any_set == 0) 153276541Sdes return -1; 154276541Sdes return 0; 155276541Sdes} 156276541Sdes 157276541Sdesstatic int 158276541Sdesgetentropy_urandom(void *buf, size_t len) 159276541Sdes{ 160276541Sdes struct stat st; 161276541Sdes size_t i; 162276541Sdes int fd, flags; 163276541Sdes int save_errno = errno; 164276541Sdes 165276541Sdesstart: 166276541Sdes 167276541Sdes flags = O_RDONLY; 168276541Sdes#ifdef O_NOFOLLOW 169276541Sdes flags |= O_NOFOLLOW; 170276541Sdes#endif 171276541Sdes#ifdef O_CLOEXEC 172276541Sdes flags |= O_CLOEXEC; 173276541Sdes#endif 174276541Sdes fd = open("/dev/urandom", flags, 0); 175276541Sdes if (fd == -1) { 176276541Sdes if (errno == EINTR) 177276541Sdes goto start; 178276541Sdes goto nodevrandom; 179276541Sdes } 180276541Sdes#ifndef O_CLOEXEC 181276541Sdes fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 182276541Sdes#endif 183276541Sdes 184276541Sdes /* Lightly verify that the device node looks sane */ 185276541Sdes if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) { 186276541Sdes close(fd); 187276541Sdes goto nodevrandom; 188276541Sdes } 189276541Sdes for (i = 0; i < len; ) { 190276541Sdes size_t wanted = len - i; 191276541Sdes ssize_t ret = read(fd, (char*)buf + i, wanted); 192276541Sdes 193276541Sdes if (ret == -1) { 194276541Sdes if (errno == EAGAIN || errno == EINTR) 195276541Sdes continue; 196276541Sdes close(fd); 197276541Sdes goto nodevrandom; 198276541Sdes } 199276541Sdes i += ret; 200276541Sdes } 201276541Sdes close(fd); 202276541Sdes if (gotdata(buf, len) == 0) { 203276541Sdes errno = save_errno; 204276541Sdes return 0; /* satisfied */ 205276541Sdes } 206276541Sdesnodevrandom: 207276541Sdes errno = EIO; 208276541Sdes return -1; 209276541Sdes} 210276541Sdes 211276541Sdesstatic int tcpmib[] = { CTL_NET, AF_INET, IPPROTO_TCP, TCPCTL_STATS }; 212276541Sdesstatic int udpmib[] = { CTL_NET, AF_INET, IPPROTO_UDP, UDPCTL_STATS }; 213276541Sdesstatic int ipmib[] = { CTL_NET, AF_INET, IPPROTO_IP, IPCTL_STATS }; 214276541Sdesstatic int kmib[] = { CTL_KERN, KERN_USRSTACK }; 215276541Sdesstatic int hwmib[] = { CTL_HW, HW_USERMEM }; 216276541Sdes 217276541Sdesstatic int 218276541Sdesgetentropy_fallback(void *buf, size_t len) 219276541Sdes{ 220276541Sdes uint8_t results[SHA512_DIGEST_LENGTH]; 221276541Sdes int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat; 222276541Sdes static int cnt; 223276541Sdes struct timespec ts; 224276541Sdes struct timeval tv; 225276541Sdes struct rusage ru; 226276541Sdes sigset_t sigset; 227276541Sdes struct stat st; 228276541Sdes SHA512_CTX ctx; 229276541Sdes static pid_t lastpid; 230276541Sdes pid_t pid; 231276541Sdes size_t i, ii, m; 232276541Sdes char *p; 233276541Sdes struct tcpstat tcpstat; 234276541Sdes struct udpstat udpstat; 235276541Sdes struct ipstat ipstat; 236276541Sdes u_int64_t mach_time; 237276541Sdes unsigned int idata; 238276541Sdes void *addr; 239276541Sdes 240276541Sdes pid = getpid(); 241276541Sdes if (lastpid == pid) { 242276541Sdes faster = 1; 243276541Sdes repeat = 2; 244276541Sdes } else { 245276541Sdes faster = 0; 246276541Sdes lastpid = pid; 247276541Sdes repeat = REPEAT; 248276541Sdes } 249276541Sdes for (i = 0; i < len; ) { 250276541Sdes int j; 251276541Sdes SHA512_Init(&ctx); 252276541Sdes for (j = 0; j < repeat; j++) { 253276541Sdes HX((e = gettimeofday(&tv, NULL)) == -1, tv); 254276541Sdes if (e != -1) { 255276541Sdes cnt += (int)tv.tv_sec; 256276541Sdes cnt += (int)tv.tv_usec; 257276541Sdes } 258276541Sdes 259276541Sdes mach_time = mach_absolute_time(); 260276541Sdes HD(mach_time); 261276541Sdes 262276541Sdes ii = sizeof(addr); 263276541Sdes HX(sysctl(kmib, sizeof(kmib) / sizeof(kmib[0]), 264276541Sdes &addr, &ii, NULL, 0) == -1, addr); 265276541Sdes 266276541Sdes ii = sizeof(idata); 267276541Sdes HX(sysctl(hwmib, sizeof(hwmib) / sizeof(hwmib[0]), 268276541Sdes &idata, &ii, NULL, 0) == -1, idata); 269276541Sdes 270276541Sdes ii = sizeof(tcpstat); 271276541Sdes HX(sysctl(tcpmib, sizeof(tcpmib) / sizeof(tcpmib[0]), 272276541Sdes &tcpstat, &ii, NULL, 0) == -1, tcpstat); 273276541Sdes 274276541Sdes ii = sizeof(udpstat); 275276541Sdes HX(sysctl(udpmib, sizeof(udpmib) / sizeof(udpmib[0]), 276276541Sdes &udpstat, &ii, NULL, 0) == -1, udpstat); 277276541Sdes 278276541Sdes ii = sizeof(ipstat); 279276541Sdes HX(sysctl(ipmib, sizeof(ipmib) / sizeof(ipmib[0]), 280276541Sdes &ipstat, &ii, NULL, 0) == -1, ipstat); 281276541Sdes 282276541Sdes HX((pid = getpid()) == -1, pid); 283276541Sdes HX((pid = getsid(pid)) == -1, pid); 284276541Sdes HX((pid = getppid()) == -1, pid); 285276541Sdes HX((pid = getpgid(0)) == -1, pid); 286276541Sdes HX((e = getpriority(0, 0)) == -1, e); 287276541Sdes 288276541Sdes if (!faster) { 289276541Sdes ts.tv_sec = 0; 290276541Sdes ts.tv_nsec = 1; 291276541Sdes (void) nanosleep(&ts, NULL); 292276541Sdes } 293276541Sdes 294276541Sdes HX(sigpending(&sigset) == -1, sigset); 295276541Sdes HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1, 296276541Sdes sigset); 297276541Sdes 298276541Sdes#ifdef CAN_REFERENCE_MAIN 299276541Sdes HF(main); /* an addr in program */ 300276541Sdes#endif 301276541Sdes HF(getentropy); /* an addr in this library */ 302276541Sdes HF(printf); /* an addr in libc */ 303276541Sdes p = (char *)&p; 304276541Sdes HD(p); /* an addr on stack */ 305276541Sdes p = (char *)&errno; 306276541Sdes HD(p); /* the addr of errno */ 307276541Sdes 308276541Sdes if (i == 0) { 309276541Sdes struct sockaddr_storage ss; 310276541Sdes struct statvfs stvfs; 311276541Sdes struct termios tios; 312276541Sdes struct statfs stfs; 313276541Sdes socklen_t ssl; 314276541Sdes off_t off; 315276541Sdes 316276541Sdes /* 317276541Sdes * Prime-sized mappings encourage fragmentation; 318276541Sdes * thus exposing some address entropy. 319276541Sdes */ 320276541Sdes struct mm { 321276541Sdes size_t npg; 322276541Sdes void *p; 323276541Sdes } mm[] = { 324276541Sdes { 17, MAP_FAILED }, { 3, MAP_FAILED }, 325276541Sdes { 11, MAP_FAILED }, { 2, MAP_FAILED }, 326276541Sdes { 5, MAP_FAILED }, { 3, MAP_FAILED }, 327276541Sdes { 7, MAP_FAILED }, { 1, MAP_FAILED }, 328276541Sdes { 57, MAP_FAILED }, { 3, MAP_FAILED }, 329276541Sdes { 131, MAP_FAILED }, { 1, MAP_FAILED }, 330276541Sdes }; 331276541Sdes 332276541Sdes for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { 333276541Sdes HX(mm[m].p = mmap(NULL, 334276541Sdes mm[m].npg * pgs, 335276541Sdes PROT_READ|PROT_WRITE, 336276541Sdes MAP_PRIVATE|MAP_ANON, -1, 337276541Sdes (off_t)0), mm[m].p); 338276541Sdes if (mm[m].p != MAP_FAILED) { 339276541Sdes size_t mo; 340276541Sdes 341276541Sdes /* Touch some memory... */ 342276541Sdes p = mm[m].p; 343276541Sdes mo = cnt % 344276541Sdes (mm[m].npg * pgs - 1); 345276541Sdes p[mo] = 1; 346276541Sdes cnt += (int)((long)(mm[m].p) 347276541Sdes / pgs); 348276541Sdes } 349276541Sdes 350276541Sdes /* Check cnts and times... */ 351276541Sdes mach_time = mach_absolute_time(); 352276541Sdes HD(mach_time); 353276541Sdes cnt += (int)mach_time; 354276541Sdes 355276541Sdes HX((e = getrusage(RUSAGE_SELF, 356276541Sdes &ru)) == -1, ru); 357276541Sdes if (e != -1) { 358276541Sdes cnt += (int)ru.ru_utime.tv_sec; 359276541Sdes cnt += (int)ru.ru_utime.tv_usec; 360276541Sdes } 361276541Sdes } 362276541Sdes 363276541Sdes for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { 364276541Sdes if (mm[m].p != MAP_FAILED) 365276541Sdes munmap(mm[m].p, mm[m].npg * pgs); 366276541Sdes mm[m].p = MAP_FAILED; 367276541Sdes } 368276541Sdes 369276541Sdes HX(stat(".", &st) == -1, st); 370276541Sdes HX(statvfs(".", &stvfs) == -1, stvfs); 371276541Sdes HX(statfs(".", &stfs) == -1, stfs); 372276541Sdes 373276541Sdes HX(stat("/", &st) == -1, st); 374276541Sdes HX(statvfs("/", &stvfs) == -1, stvfs); 375276541Sdes HX(statfs("/", &stfs) == -1, stfs); 376276541Sdes 377276541Sdes HX((e = fstat(0, &st)) == -1, st); 378276541Sdes if (e == -1) { 379276541Sdes if (S_ISREG(st.st_mode) || 380276541Sdes S_ISFIFO(st.st_mode) || 381276541Sdes S_ISSOCK(st.st_mode)) { 382276541Sdes HX(fstatvfs(0, &stvfs) == -1, 383276541Sdes stvfs); 384276541Sdes HX(fstatfs(0, &stfs) == -1, 385276541Sdes stfs); 386276541Sdes HX((off = lseek(0, (off_t)0, 387276541Sdes SEEK_CUR)) < 0, off); 388276541Sdes } 389276541Sdes if (S_ISCHR(st.st_mode)) { 390276541Sdes HX(tcgetattr(0, &tios) == -1, 391276541Sdes tios); 392276541Sdes } else if (S_ISSOCK(st.st_mode)) { 393276541Sdes memset(&ss, 0, sizeof ss); 394276541Sdes ssl = sizeof(ss); 395276541Sdes HX(getpeername(0, 396276541Sdes (void *)&ss, &ssl) == -1, 397276541Sdes ss); 398276541Sdes } 399276541Sdes } 400276541Sdes 401276541Sdes HX((e = getrusage(RUSAGE_CHILDREN, 402276541Sdes &ru)) == -1, ru); 403276541Sdes if (e != -1) { 404276541Sdes cnt += (int)ru.ru_utime.tv_sec; 405276541Sdes cnt += (int)ru.ru_utime.tv_usec; 406276541Sdes } 407276541Sdes } else { 408276541Sdes /* Subsequent hashes absorb previous result */ 409276541Sdes HD(results); 410276541Sdes } 411276541Sdes 412276541Sdes HX((e = gettimeofday(&tv, NULL)) == -1, tv); 413276541Sdes if (e != -1) { 414276541Sdes cnt += (int)tv.tv_sec; 415276541Sdes cnt += (int)tv.tv_usec; 416276541Sdes } 417276541Sdes 418276541Sdes HD(cnt); 419276541Sdes } 420276541Sdes 421276541Sdes SHA512_Final(results, &ctx); 422276541Sdes memcpy((char*)buf + i, results, min(sizeof(results), len - i)); 423276541Sdes i += min(sizeof(results), len - i); 424276541Sdes } 425276541Sdes memset(results, 0, sizeof results); 426276541Sdes if (gotdata(buf, len) == 0) { 427276541Sdes errno = save_errno; 428276541Sdes return 0; /* satisfied */ 429276541Sdes } 430276541Sdes errno = EIO; 431276541Sdes return -1; 432276541Sdes} 433