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