1 /* $OpenBSD: getentropy_solaris.c,v 1.13 2018/11/20 08:04:28 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 * Emulation of getentropy(2) as documented at: 20 * http://man.openbsd.org/getentropy.2 21 */ 22 23 #include "config.h" 24 #include <sys/types.h> 25 #include <sys/param.h> 26 #include <sys/ioctl.h> 27 #include <sys/resource.h> 28 #include <sys/syscall.h> 29 #include <sys/statvfs.h> 30 #include <sys/socket.h> 31 #include <sys/mount.h> 32 #include <sys/mman.h> 33 #include <sys/stat.h> 34 #include <sys/time.h> 35 #include <stdlib.h> 36 #ifdef HAVE_STDINT_H 37 #include <stdint.h> 38 #endif 39 #include <stdio.h> 40 #include <link.h> 41 #include <termios.h> 42 #include <fcntl.h> 43 #include <signal.h> 44 #include <string.h> 45 #include <errno.h> 46 #include <unistd.h> 47 #include <time.h> 48 #ifdef HAVE_SYS_SHA2_H 49 #include <sys/sha2.h> 50 #define SHA512_Init SHA512Init 51 #define SHA512_Update SHA512Update 52 #define SHA512_Final SHA512Final 53 #else 54 #include "openssl/sha.h" 55 #endif 56 57 #include <sys/vfs.h> 58 #include <sys/statfs.h> 59 #include <sys/loadavg.h> 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 76 int getentropy(void *buf, size_t len); 77 78 static int getentropy_urandom(void *buf, size_t len, const char *path, 79 int devfscheck); 80 static int getentropy_fallback(void *buf, size_t len); 81 static int getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data); 82 83 int 84 getentropy(void *buf, size_t len) 85 { 86 int ret = -1; 87 88 if (len > 256) { 89 errno = EIO; 90 return (-1); 91 } 92 93 /* 94 * Try to get entropy with /dev/urandom 95 * 96 * Solaris provides /dev/urandom as a symbolic link to 97 * /devices/pseudo/random@0:urandom which is provided by 98 * a devfs filesystem. Best practice is to use O_NOFOLLOW, 99 * so we must try the unpublished name directly. 100 * 101 * This can fail if the process is inside a chroot which lacks 102 * the devfs mount, or if file descriptors are exhausted. 103 */ 104 ret = getentropy_urandom(buf, len, 105 "/devices/pseudo/random@0:urandom", 1); 106 if (ret != -1) 107 return (ret); 108 109 /* 110 * Unfortunately, chroot spaces on Solaris are sometimes setup 111 * with direct device node of the well-known /dev/urandom name 112 * (perhaps to avoid dragging all of devfs into the space). 113 * 114 * This can fail if the process is inside a chroot or if file 115 * descriptors are exhausted. 116 */ 117 ret = getentropy_urandom(buf, len, "/dev/urandom", 0); 118 if (ret != -1) 119 return (ret); 120 121 /* 122 * Entropy collection via /dev/urandom has failed. 123 * 124 * No other API exists for collecting entropy, and we have 125 * no failsafe way to get it on Solaris that is not sensitive 126 * to resource exhaustion. 127 * 128 * We have very few options: 129 * - Even syslog_r is unsafe to call at this low level, so 130 * there is no way to alert the user or program. 131 * - Cannot call abort() because some systems have unsafe 132 * corefiles. 133 * - Could raise(SIGKILL) resulting in silent program termination. 134 * - Return EIO, to hint that arc4random's stir function 135 * should raise(SIGKILL) 136 * - Do the best under the circumstances.... 137 * 138 * This code path exists to bring light to the issue that Solaris 139 * does not provide a failsafe API for entropy collection. 140 * 141 * We hope this demonstrates that Solaris should consider 142 * providing a new failsafe API which works in a chroot or 143 * when file descriptors are exhausted. 144 */ 145 #undef FAIL_INSTEAD_OF_TRYING_FALLBACK 146 #ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK 147 raise(SIGKILL); 148 #endif 149 ret = getentropy_fallback(buf, len); 150 if (ret != -1) 151 return (ret); 152 153 errno = EIO; 154 return (ret); 155 } 156 157 static int 158 getentropy_urandom(void *buf, size_t len, const char *path, int devfscheck) 159 { 160 struct stat st; 161 size_t i; 162 int fd, flags; 163 int save_errno = errno; 164 165 start: 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(path, 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 (devfscheck && (strcmp(st.st_fstype, "devfs") != 0))) { 187 close(fd); 188 goto nodevrandom; 189 } 190 for (i = 0; i < len; ) { 191 size_t wanted = len - i; 192 ssize_t ret = read(fd, (char *)buf + i, wanted); 193 194 if (ret == -1) { 195 if (errno == EAGAIN || errno == EINTR) 196 continue; 197 close(fd); 198 goto nodevrandom; 199 } 200 i += ret; 201 } 202 close(fd); 203 errno = save_errno; 204 return (0); /* satisfied */ 205 nodevrandom: 206 errno = EIO; 207 return (-1); 208 } 209 210 static const int cl[] = { 211 CLOCK_REALTIME, 212 #ifdef CLOCK_MONOTONIC 213 CLOCK_MONOTONIC, 214 #endif 215 #ifdef CLOCK_MONOTONIC_RAW 216 CLOCK_MONOTONIC_RAW, 217 #endif 218 #ifdef CLOCK_TAI 219 CLOCK_TAI, 220 #endif 221 #ifdef CLOCK_VIRTUAL 222 CLOCK_VIRTUAL, 223 #endif 224 #ifdef CLOCK_UPTIME 225 CLOCK_UPTIME, 226 #endif 227 #ifdef CLOCK_PROCESS_CPUTIME_ID 228 CLOCK_PROCESS_CPUTIME_ID, 229 #endif 230 #ifdef CLOCK_THREAD_CPUTIME_ID 231 CLOCK_THREAD_CPUTIME_ID, 232 #endif 233 }; 234 235 static int 236 getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data) 237 { 238 SHA512_CTX *ctx = data; 239 240 SHA512_Update(ctx, &info->dlpi_addr, sizeof (info->dlpi_addr)); 241 return (0); 242 } 243 244 static int 245 getentropy_fallback(void *buf, size_t len) 246 { 247 uint8_t results[SHA512_DIGEST_LENGTH]; 248 int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat; 249 static int cnt; 250 struct timespec ts; 251 struct timeval tv; 252 double loadavg[3]; 253 struct rusage ru; 254 sigset_t sigset; 255 struct stat st; 256 SHA512_CTX ctx; 257 static pid_t lastpid; 258 pid_t pid; 259 size_t i, ii, m; 260 char *p; 261 262 pid = getpid(); 263 if (lastpid == pid) { 264 faster = 1; 265 repeat = 2; 266 } else { 267 faster = 0; 268 lastpid = pid; 269 repeat = REPEAT; 270 } 271 for (i = 0; i < len; ) { 272 int j; 273 SHA512_Init(&ctx); 274 for (j = 0; j < repeat; j++) { 275 HX((e = gettimeofday(&tv, NULL)) == -1, tv); 276 if (e != -1) { 277 cnt += (int)tv.tv_sec; 278 cnt += (int)tv.tv_usec; 279 } 280 281 dl_iterate_phdr(getentropy_phdr, &ctx); 282 283 for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); ii++) 284 HX(clock_gettime(cl[ii], &ts) == -1, ts); 285 286 HX((pid = getpid()) == -1, pid); 287 HX((pid = getsid(pid)) == -1, pid); 288 HX((pid = getppid()) == -1, pid); 289 HX((pid = getpgid(0)) == -1, pid); 290 HX((e = getpriority(0, 0)) == -1, e); 291 HX((getloadavg(loadavg, 3) == -1), loadavg); 292 293 if (!faster) { 294 ts.tv_sec = 0; 295 ts.tv_nsec = 1; 296 (void) nanosleep(&ts, NULL); 297 } 298 299 HX(sigpending(&sigset) == -1, sigset); 300 HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1, 301 sigset); 302 303 HF(getentropy); /* an addr in this library */ 304 HF(printf); /* an addr in libc */ 305 p = (char *)&p; 306 HD(p); /* an addr on stack */ 307 p = (char *)&errno; 308 HD(p); /* the addr of errno */ 309 310 if (i == 0) { 311 struct sockaddr_storage ss; 312 struct statvfs stvfs; 313 struct termios tios; 314 socklen_t ssl; 315 off_t off; 316 317 /* 318 * Prime-sized mappings encourage fragmentation; 319 * thus exposing some address entropy. 320 */ 321 struct mm { 322 size_t npg; 323 void *p; 324 } mm[] = { 325 { 17, MAP_FAILED }, { 3, MAP_FAILED }, 326 { 11, MAP_FAILED }, { 2, MAP_FAILED }, 327 { 5, MAP_FAILED }, { 3, MAP_FAILED }, 328 { 7, MAP_FAILED }, { 1, MAP_FAILED }, 329 { 57, MAP_FAILED }, { 3, MAP_FAILED }, 330 { 131, MAP_FAILED }, { 1, MAP_FAILED }, 331 }; 332 333 for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { 334 HX(mm[m].p = mmap(NULL, 335 mm[m].npg * pgs, 336 PROT_READ|PROT_WRITE, 337 MAP_PRIVATE|MAP_ANON, -1, 338 (off_t)0), mm[m].p); 339 if (mm[m].p != MAP_FAILED) { 340 size_t mo; 341 342 /* Touch some memory... */ 343 p = mm[m].p; 344 mo = cnt % 345 (mm[m].npg * pgs - 1); 346 p[mo] = 1; 347 cnt += (int)((long)(mm[m].p) 348 / pgs); 349 } 350 351 /* Check cnts and times... */ 352 for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); 353 ii++) { 354 HX((e = clock_gettime(cl[ii], 355 &ts)) == -1, ts); 356 if (e != -1) 357 cnt += (int)ts.tv_nsec; 358 } 359 360 HX((e = getrusage(RUSAGE_SELF, 361 &ru)) == -1, ru); 362 if (e != -1) { 363 cnt += (int)ru.ru_utime.tv_sec; 364 cnt += (int)ru.ru_utime.tv_usec; 365 } 366 } 367 368 for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { 369 if (mm[m].p != MAP_FAILED) 370 munmap(mm[m].p, mm[m].npg * pgs); 371 mm[m].p = MAP_FAILED; 372 } 373 374 HX(stat(".", &st) == -1, st); 375 HX(statvfs(".", &stvfs) == -1, stvfs); 376 377 HX(stat("/", &st) == -1, st); 378 HX(statvfs("/", &stvfs) == -1, stvfs); 379 380 HX((e = fstat(0, &st)) == -1, st); 381 if (e == -1) { 382 if (S_ISREG(st.st_mode) || 383 S_ISFIFO(st.st_mode) || 384 S_ISSOCK(st.st_mode)) { 385 HX(fstatvfs(0, &stvfs) == -1, 386 stvfs); 387 HX((off = lseek(0, (off_t)0, 388 SEEK_CUR)) < 0, off); 389 } 390 if (S_ISCHR(st.st_mode)) { 391 HX(tcgetattr(0, &tios) == -1, 392 tios); 393 } else if (S_ISSOCK(st.st_mode)) { 394 memset(&ss, 0, sizeof ss); 395 ssl = sizeof(ss); 396 HX(getpeername(0, 397 (void *)&ss, &ssl) == -1, 398 ss); 399 } 400 } 401 402 HX((e = getrusage(RUSAGE_CHILDREN, 403 &ru)) == -1, ru); 404 if (e != -1) { 405 cnt += (int)ru.ru_utime.tv_sec; 406 cnt += (int)ru.ru_utime.tv_usec; 407 } 408 } else { 409 /* Subsequent hashes absorb previous result */ 410 HD(results); 411 } 412 413 HX((e = gettimeofday(&tv, NULL)) == -1, tv); 414 if (e != -1) { 415 cnt += (int)tv.tv_sec; 416 cnt += (int)tv.tv_usec; 417 } 418 419 HD(cnt); 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 explicit_bzero(&ctx, sizeof ctx); 426 explicit_bzero(results, sizeof results); 427 errno = save_errno; 428 return (0); /* satisfied */ 429 } 430