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