1*b7041c07Sderaadt /* $OpenBSD: getentropy_solaris.c,v 1.15 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
233f84b207Sbeck #include <sys/types.h>
243f84b207Sbeck #include <sys/param.h>
253f84b207Sbeck #include <sys/ioctl.h>
263f84b207Sbeck #include <sys/resource.h>
273f84b207Sbeck #include <sys/syscall.h>
283f84b207Sbeck #include <sys/statvfs.h>
293f84b207Sbeck #include <sys/socket.h>
303f84b207Sbeck #include <sys/mount.h>
313f84b207Sbeck #include <sys/mman.h>
323f84b207Sbeck #include <sys/stat.h>
333f84b207Sbeck #include <sys/time.h>
343f84b207Sbeck #include <stdlib.h>
353f84b207Sbeck #include <stdint.h>
363f84b207Sbeck #include <stdio.h>
375a30a5b2Sderaadt #include <link.h>
383f84b207Sbeck #include <termios.h>
393f84b207Sbeck #include <fcntl.h>
403f84b207Sbeck #include <signal.h>
413f84b207Sbeck #include <string.h>
423f84b207Sbeck #include <errno.h>
433f84b207Sbeck #include <unistd.h>
443f84b207Sbeck #include <time.h>
453f84b207Sbeck #include <sys/sha2.h>
463f84b207Sbeck #define SHA512_Init SHA512Init
473f84b207Sbeck #define SHA512_Update SHA512Update
483f84b207Sbeck #define SHA512_Final SHA512Final
493f84b207Sbeck
503f84b207Sbeck #include <sys/vfs.h>
513f84b207Sbeck #include <sys/statfs.h>
523f84b207Sbeck #include <sys/loadavg.h>
533f84b207Sbeck
543f84b207Sbeck #define REPEAT 5
55557f50beSderaadt #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
563f84b207Sbeck
573f84b207Sbeck #define HX(a, b) \
583f84b207Sbeck do { \
593f84b207Sbeck if ((a)) \
603f84b207Sbeck HD(errno); \
613f84b207Sbeck else \
623f84b207Sbeck HD(b); \
633f84b207Sbeck } while (0)
643f84b207Sbeck
653f84b207Sbeck #define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l)))
663f84b207Sbeck #define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x)))
6754dec340Swouter #define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*)))
683f84b207Sbeck
693f84b207Sbeck int getentropy(void *buf, size_t len);
703f84b207Sbeck
71399b1dc5Sbeck static int getentropy_urandom(void *buf, size_t len, const char *path,
72399b1dc5Sbeck int devfscheck);
733f84b207Sbeck static int getentropy_fallback(void *buf, size_t len);
745a30a5b2Sderaadt static int getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data);
753f84b207Sbeck
763f84b207Sbeck int
getentropy(void * buf,size_t len)773f84b207Sbeck getentropy(void *buf, size_t len)
783f84b207Sbeck {
793f84b207Sbeck int ret = -1;
803f84b207Sbeck
813f84b207Sbeck if (len > 256) {
823f84b207Sbeck errno = EIO;
83044fc755Sderaadt return (-1);
843f84b207Sbeck }
853f84b207Sbeck
863f84b207Sbeck /*
87190c328cSderaadt * Try to get entropy with /dev/urandom
88399b1dc5Sbeck *
89399b1dc5Sbeck * Solaris provides /dev/urandom as a symbolic link to
90399b1dc5Sbeck * /devices/pseudo/random@0:urandom which is provided by
91399b1dc5Sbeck * a devfs filesystem. Best practice is to use O_NOFOLLOW,
92399b1dc5Sbeck * so we must try the unpublished name directly.
93399b1dc5Sbeck *
94399b1dc5Sbeck * This can fail if the process is inside a chroot which lacks
95399b1dc5Sbeck * the devfs mount, or if file descriptors are exhausted.
96399b1dc5Sbeck */
97399b1dc5Sbeck ret = getentropy_urandom(buf, len,
98399b1dc5Sbeck "/devices/pseudo/random@0:urandom", 1);
99399b1dc5Sbeck if (ret != -1)
100399b1dc5Sbeck return (ret);
101399b1dc5Sbeck
102399b1dc5Sbeck /*
103399b1dc5Sbeck * Unfortunately, chroot spaces on Solaris are sometimes setup
104399b1dc5Sbeck * with direct device node of the well-known /dev/urandom name
105399b1dc5Sbeck * (perhaps to avoid dragging all of devfs into the space).
1063f84b207Sbeck *
1073f84b207Sbeck * This can fail if the process is inside a chroot or if file
1083f84b207Sbeck * descriptors are exhausted.
1093f84b207Sbeck */
110399b1dc5Sbeck ret = getentropy_urandom(buf, len, "/dev/urandom", 0);
1113f84b207Sbeck if (ret != -1)
1123f84b207Sbeck return (ret);
113399b1dc5Sbeck
1143f84b207Sbeck /*
115399b1dc5Sbeck * Entropy collection via /dev/urandom has failed.
1163f84b207Sbeck *
1173f84b207Sbeck * No other API exists for collecting entropy, and we have
1183f84b207Sbeck * no failsafe way to get it on Solaris that is not sensitive
1193f84b207Sbeck * to resource exhaustion.
1203f84b207Sbeck *
1213f84b207Sbeck * We have very few options:
1223f84b207Sbeck * - Even syslog_r is unsafe to call at this low level, so
1233f84b207Sbeck * there is no way to alert the user or program.
1243f84b207Sbeck * - Cannot call abort() because some systems have unsafe
1253f84b207Sbeck * corefiles.
1263f84b207Sbeck * - Could raise(SIGKILL) resulting in silent program termination.
1273f84b207Sbeck * - Return EIO, to hint that arc4random's stir function
1283f84b207Sbeck * should raise(SIGKILL)
1293f84b207Sbeck * - Do the best under the circumstances....
1303f84b207Sbeck *
1313f84b207Sbeck * This code path exists to bring light to the issue that Solaris
1323f84b207Sbeck * does not provide a failsafe API for entropy collection.
1333f84b207Sbeck *
1343f84b207Sbeck * We hope this demonstrates that Solaris should consider
1353f84b207Sbeck * providing a new failsafe API which works in a chroot or
1363f84b207Sbeck * when file descriptors are exhausted.
1373f84b207Sbeck */
138190c328cSderaadt #undef FAIL_INSTEAD_OF_TRYING_FALLBACK
139190c328cSderaadt #ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK
1403f84b207Sbeck raise(SIGKILL);
1413f84b207Sbeck #endif
1423f84b207Sbeck ret = getentropy_fallback(buf, len);
1433f84b207Sbeck if (ret != -1)
1443f84b207Sbeck return (ret);
1453f84b207Sbeck
1463f84b207Sbeck errno = EIO;
1473f84b207Sbeck return (ret);
1483f84b207Sbeck }
1493f84b207Sbeck
1503f84b207Sbeck static int
getentropy_urandom(void * buf,size_t len,const char * path,int devfscheck)151399b1dc5Sbeck getentropy_urandom(void *buf, size_t len, const char *path, int devfscheck)
1523f84b207Sbeck {
1533f84b207Sbeck struct stat st;
1543f84b207Sbeck size_t i;
1553f84b207Sbeck int fd, flags;
1563f84b207Sbeck int save_errno = errno;
1573f84b207Sbeck
1583f84b207Sbeck start:
1593f84b207Sbeck
1603f84b207Sbeck flags = O_RDONLY;
1613f84b207Sbeck #ifdef O_NOFOLLOW
1623f84b207Sbeck flags |= O_NOFOLLOW;
1633f84b207Sbeck #endif
1643f84b207Sbeck #ifdef O_CLOEXEC
1653f84b207Sbeck flags |= O_CLOEXEC;
1663f84b207Sbeck #endif
167*b7041c07Sderaadt fd = open(path, flags);
1683f84b207Sbeck if (fd == -1) {
1693f84b207Sbeck if (errno == EINTR)
1703f84b207Sbeck goto start;
1713f84b207Sbeck goto nodevrandom;
1723f84b207Sbeck }
1733f84b207Sbeck #ifndef O_CLOEXEC
1743f84b207Sbeck fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
1753f84b207Sbeck #endif
1763f84b207Sbeck
1773f84b207Sbeck /* Lightly verify that the device node looks sane */
178399b1dc5Sbeck if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode) ||
179399b1dc5Sbeck (devfscheck && (strcmp(st.st_fstype, "devfs") != 0))) {
1803f84b207Sbeck close(fd);
1813f84b207Sbeck goto nodevrandom;
1823f84b207Sbeck }
1833f84b207Sbeck for (i = 0; i < len; ) {
1843f84b207Sbeck size_t wanted = len - i;
18554dec340Swouter ssize_t ret = read(fd, (char *)buf + i, wanted);
1863f84b207Sbeck
1873f84b207Sbeck if (ret == -1) {
1883f84b207Sbeck if (errno == EAGAIN || errno == EINTR)
1893f84b207Sbeck continue;
1903f84b207Sbeck close(fd);
1913f84b207Sbeck goto nodevrandom;
1923f84b207Sbeck }
1933f84b207Sbeck i += ret;
1943f84b207Sbeck }
1953f84b207Sbeck close(fd);
1963f84b207Sbeck errno = save_errno;
197044fc755Sderaadt return (0); /* satisfied */
1983f84b207Sbeck nodevrandom:
1993f84b207Sbeck errno = EIO;
200044fc755Sderaadt return (-1);
2013f84b207Sbeck }
2023f84b207Sbeck
203190c328cSderaadt static const int cl[] = {
2043f84b207Sbeck CLOCK_REALTIME,
2053f84b207Sbeck #ifdef CLOCK_MONOTONIC
2063f84b207Sbeck CLOCK_MONOTONIC,
2073f84b207Sbeck #endif
2083f84b207Sbeck #ifdef CLOCK_MONOTONIC_RAW
2093f84b207Sbeck CLOCK_MONOTONIC_RAW,
2103f84b207Sbeck #endif
2113f84b207Sbeck #ifdef CLOCK_TAI
2123f84b207Sbeck CLOCK_TAI,
2133f84b207Sbeck #endif
2143f84b207Sbeck #ifdef CLOCK_VIRTUAL
2153f84b207Sbeck CLOCK_VIRTUAL,
2163f84b207Sbeck #endif
2173f84b207Sbeck #ifdef CLOCK_UPTIME
2183f84b207Sbeck CLOCK_UPTIME,
2193f84b207Sbeck #endif
2203f84b207Sbeck #ifdef CLOCK_PROCESS_CPUTIME_ID
2213f84b207Sbeck CLOCK_PROCESS_CPUTIME_ID,
2223f84b207Sbeck #endif
2233f84b207Sbeck #ifdef CLOCK_THREAD_CPUTIME_ID
2243f84b207Sbeck CLOCK_THREAD_CPUTIME_ID,
2253f84b207Sbeck #endif
2263f84b207Sbeck };
2273f84b207Sbeck
2283f84b207Sbeck static int
getentropy_phdr(struct dl_phdr_info * info,size_t size,void * data)2295a30a5b2Sderaadt getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data)
2305a30a5b2Sderaadt {
2315a30a5b2Sderaadt SHA512_CTX *ctx = data;
2325a30a5b2Sderaadt
2335a30a5b2Sderaadt SHA512_Update(ctx, &info->dlpi_addr, sizeof (info->dlpi_addr));
234044fc755Sderaadt return (0);
2355a30a5b2Sderaadt }
2365a30a5b2Sderaadt
2375a30a5b2Sderaadt static int
getentropy_fallback(void * buf,size_t len)2383f84b207Sbeck getentropy_fallback(void *buf, size_t len)
2393f84b207Sbeck {
2403f84b207Sbeck uint8_t results[SHA512_DIGEST_LENGTH];
24154dec340Swouter int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat;
2423f84b207Sbeck static int cnt;
2433f84b207Sbeck struct timespec ts;
2443f84b207Sbeck struct timeval tv;
2453f84b207Sbeck double loadavg[3];
2463f84b207Sbeck struct rusage ru;
2473f84b207Sbeck sigset_t sigset;
2483f84b207Sbeck struct stat st;
2493f84b207Sbeck SHA512_CTX ctx;
2503f84b207Sbeck static pid_t lastpid;
2513f84b207Sbeck pid_t pid;
25254dec340Swouter size_t i, ii, m;
2533f84b207Sbeck char *p;
2543f84b207Sbeck
2553f84b207Sbeck pid = getpid();
2563f84b207Sbeck if (lastpid == pid) {
2573f84b207Sbeck faster = 1;
2583f84b207Sbeck repeat = 2;
2593f84b207Sbeck } else {
2603f84b207Sbeck faster = 0;
2613f84b207Sbeck lastpid = pid;
2623f84b207Sbeck repeat = REPEAT;
2633f84b207Sbeck }
2643f84b207Sbeck for (i = 0; i < len; ) {
2653f84b207Sbeck int j;
2663f84b207Sbeck SHA512_Init(&ctx);
2673f84b207Sbeck for (j = 0; j < repeat; j++) {
2683f84b207Sbeck HX((e = gettimeofday(&tv, NULL)) == -1, tv);
2693f84b207Sbeck if (e != -1) {
2703f84b207Sbeck cnt += (int)tv.tv_sec;
2713f84b207Sbeck cnt += (int)tv.tv_usec;
2723f84b207Sbeck }
2733f84b207Sbeck
2745a30a5b2Sderaadt dl_iterate_phdr(getentropy_phdr, &ctx);
2755a30a5b2Sderaadt
2763f84b207Sbeck for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); ii++)
2773f84b207Sbeck HX(clock_gettime(cl[ii], &ts) == -1, ts);
278190c328cSderaadt
2793f84b207Sbeck HX((pid = getpid()) == -1, pid);
2803f84b207Sbeck HX((pid = getsid(pid)) == -1, pid);
2813f84b207Sbeck HX((pid = getppid()) == -1, pid);
2823f84b207Sbeck HX((pid = getpgid(0)) == -1, pid);
28354dec340Swouter HX((e = getpriority(0, 0)) == -1, e);
2843f84b207Sbeck HX((getloadavg(loadavg, 3) == -1), loadavg);
2853f84b207Sbeck
2863f84b207Sbeck if (!faster) {
2873f84b207Sbeck ts.tv_sec = 0;
2883f84b207Sbeck ts.tv_nsec = 1;
2893f84b207Sbeck (void) nanosleep(&ts, NULL);
2903f84b207Sbeck }
2913f84b207Sbeck
2923f84b207Sbeck HX(sigpending(&sigset) == -1, sigset);
2933f84b207Sbeck HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1,
2943f84b207Sbeck sigset);
2953f84b207Sbeck
29654dec340Swouter HF(getentropy); /* an addr in this library */
29754dec340Swouter HF(printf); /* an addr in libc */
2983f84b207Sbeck p = (char *)&p;
2993f84b207Sbeck HD(p); /* an addr on stack */
3003f84b207Sbeck p = (char *)&errno;
3013f84b207Sbeck HD(p); /* the addr of errno */
3023f84b207Sbeck
3033f84b207Sbeck if (i == 0) {
3043f84b207Sbeck struct sockaddr_storage ss;
3053f84b207Sbeck struct statvfs stvfs;
3063f84b207Sbeck struct termios tios;
3073f84b207Sbeck socklen_t ssl;
3083f84b207Sbeck off_t off;
3093f84b207Sbeck
3103f84b207Sbeck /*
3113f84b207Sbeck * Prime-sized mappings encourage fragmentation;
3123f84b207Sbeck * thus exposing some address entropy.
3133f84b207Sbeck */
3143f84b207Sbeck struct mm {
3153f84b207Sbeck size_t npg;
3163f84b207Sbeck void *p;
3173f84b207Sbeck } mm[] = {
3183f84b207Sbeck { 17, MAP_FAILED }, { 3, MAP_FAILED },
3193f84b207Sbeck { 11, MAP_FAILED }, { 2, MAP_FAILED },
3203f84b207Sbeck { 5, MAP_FAILED }, { 3, MAP_FAILED },
3213f84b207Sbeck { 7, MAP_FAILED }, { 1, MAP_FAILED },
3223f84b207Sbeck { 57, MAP_FAILED }, { 3, MAP_FAILED },
3233f84b207Sbeck { 131, MAP_FAILED }, { 1, MAP_FAILED },
3243f84b207Sbeck };
3253f84b207Sbeck
3263f84b207Sbeck for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
3273f84b207Sbeck HX(mm[m].p = mmap(NULL,
3283f84b207Sbeck mm[m].npg * pgs,
3293f84b207Sbeck PROT_READ|PROT_WRITE,
3303f84b207Sbeck MAP_PRIVATE|MAP_ANON, -1,
3313f84b207Sbeck (off_t)0), mm[m].p);
3323f84b207Sbeck if (mm[m].p != MAP_FAILED) {
3333f84b207Sbeck size_t mo;
3343f84b207Sbeck
3353f84b207Sbeck /* Touch some memory... */
3363f84b207Sbeck p = mm[m].p;
3373f84b207Sbeck mo = cnt %
3383f84b207Sbeck (mm[m].npg * pgs - 1);
3393f84b207Sbeck p[mo] = 1;
3403f84b207Sbeck cnt += (int)((long)(mm[m].p)
3413f84b207Sbeck / pgs);
3423f84b207Sbeck }
3433f84b207Sbeck
3443f84b207Sbeck /* Check cnts and times... */
3453f84b207Sbeck for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]);
3463f84b207Sbeck ii++) {
3473f84b207Sbeck HX((e = clock_gettime(cl[ii],
3483f84b207Sbeck &ts)) == -1, ts);
3493f84b207Sbeck if (e != -1)
3503f84b207Sbeck cnt += (int)ts.tv_nsec;
3513f84b207Sbeck }
3523f84b207Sbeck
3533f84b207Sbeck HX((e = getrusage(RUSAGE_SELF,
3543f84b207Sbeck &ru)) == -1, ru);
3553f84b207Sbeck if (e != -1) {
3563f84b207Sbeck cnt += (int)ru.ru_utime.tv_sec;
3573f84b207Sbeck cnt += (int)ru.ru_utime.tv_usec;
3583f84b207Sbeck }
3593f84b207Sbeck }
3603f84b207Sbeck
3613f84b207Sbeck for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) {
3623f84b207Sbeck if (mm[m].p != MAP_FAILED)
3633f84b207Sbeck munmap(mm[m].p, mm[m].npg * pgs);
3643f84b207Sbeck mm[m].p = MAP_FAILED;
3653f84b207Sbeck }
3663f84b207Sbeck
3673f84b207Sbeck HX(stat(".", &st) == -1, st);
3683f84b207Sbeck HX(statvfs(".", &stvfs) == -1, stvfs);
3693f84b207Sbeck
3703f84b207Sbeck HX(stat("/", &st) == -1, st);
3713f84b207Sbeck HX(statvfs("/", &stvfs) == -1, stvfs);
3723f84b207Sbeck
3733f84b207Sbeck HX((e = fstat(0, &st)) == -1, st);
3743f84b207Sbeck if (e == -1) {
3753f84b207Sbeck if (S_ISREG(st.st_mode) ||
3763f84b207Sbeck S_ISFIFO(st.st_mode) ||
3773f84b207Sbeck S_ISSOCK(st.st_mode)) {
3783f84b207Sbeck HX(fstatvfs(0, &stvfs) == -1,
3793f84b207Sbeck stvfs);
3803f84b207Sbeck HX((off = lseek(0, (off_t)0,
3813f84b207Sbeck SEEK_CUR)) < 0, off);
3823f84b207Sbeck }
3833f84b207Sbeck if (S_ISCHR(st.st_mode)) {
3843f84b207Sbeck HX(tcgetattr(0, &tios) == -1,
3853f84b207Sbeck tios);
3863f84b207Sbeck } else if (S_ISSOCK(st.st_mode)) {
3873f84b207Sbeck memset(&ss, 0, sizeof ss);
3883f84b207Sbeck ssl = sizeof(ss);
3893f84b207Sbeck HX(getpeername(0,
3903f84b207Sbeck (void *)&ss, &ssl) == -1,
3913f84b207Sbeck ss);
3923f84b207Sbeck }
3933f84b207Sbeck }
3943f84b207Sbeck
3953f84b207Sbeck HX((e = getrusage(RUSAGE_CHILDREN,
3963f84b207Sbeck &ru)) == -1, ru);
3973f84b207Sbeck if (e != -1) {
3983f84b207Sbeck cnt += (int)ru.ru_utime.tv_sec;
3993f84b207Sbeck cnt += (int)ru.ru_utime.tv_usec;
4003f84b207Sbeck }
4013f84b207Sbeck } else {
4023f84b207Sbeck /* Subsequent hashes absorb previous result */
4033f84b207Sbeck HD(results);
4043f84b207Sbeck }
4053f84b207Sbeck
4063f84b207Sbeck HX((e = gettimeofday(&tv, NULL)) == -1, tv);
4073f84b207Sbeck if (e != -1) {
4083f84b207Sbeck cnt += (int)tv.tv_sec;
4093f84b207Sbeck cnt += (int)tv.tv_usec;
4103f84b207Sbeck }
4113f84b207Sbeck
4123f84b207Sbeck HD(cnt);
4133f84b207Sbeck }
4143f84b207Sbeck SHA512_Final(results, &ctx);
415557f50beSderaadt memcpy((char *)buf + i, results, MINIMUM(sizeof(results), len - i));
416557f50beSderaadt i += MINIMUM(sizeof(results), len - i);
4173f84b207Sbeck }
4185fd8226cSguenther explicit_bzero(&ctx, sizeof ctx);
4195fd8226cSguenther explicit_bzero(results, sizeof results);
4203f84b207Sbeck errno = save_errno;
421044fc755Sderaadt return (0); /* satisfied */
4223f84b207Sbeck }
423