1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/random.h> 34 #include <sys/sysctl.h> 35 36 #include <errno.h> 37 #include <signal.h> 38 #include <stdbool.h> 39 #include <stdlib.h> 40 41 #include "libc_private.h" 42 43 /* First __FreeBSD_version bump after introduction of getrandom(2) (r331279) */ 44 #define GETRANDOM_FIRST 1200061 45 46 extern int __sysctl(int *, u_int, void *, size_t *, void *, size_t); 47 48 static inline void 49 _getentropy_fail(void) 50 { 51 raise(SIGKILL); 52 } 53 54 static size_t 55 arnd_sysctl(u_char *buf, size_t size) 56 { 57 int mib[2]; 58 size_t len, done; 59 60 mib[0] = CTL_KERN; 61 mib[1] = KERN_ARND; 62 done = 0; 63 64 do { 65 len = size; 66 if (__sysctl(mib, 2, buf, &len, NULL, 0) == -1) 67 return (done); 68 done += len; 69 buf += len; 70 size -= len; 71 } while (size > 0); 72 73 return (done); 74 } 75 76 /* 77 * If a newer libc is accidentally installed on an older kernel, provide high 78 * quality random data anyway. The sysctl interface is not as fast and does 79 * not block by itself, but is provided by even very old kernels. 80 */ 81 static int 82 getentropy_fallback(void *buf, size_t buflen) 83 { 84 /* 85 * oldp (buf) == NULL has a special meaning for sysctl that results in 86 * no EFAULT. For compatibility with the kernel getrandom(2), detect 87 * this case and return the appropriate error. 88 */ 89 if (buf == NULL && buflen > 0) { 90 errno = EFAULT; 91 return (-1); 92 } 93 if (arnd_sysctl(buf, buflen) != buflen) { 94 if (errno == EFAULT) 95 return (-1); 96 /* 97 * This cannot happen. arnd_sysctl() spins until the random 98 * device is seeded and then repeatedly reads until the full 99 * request is satisfied. The only way for this to return a zero 100 * byte or short read is if sysctl(2) on the kern.arandom MIB 101 * fails. In this case, excepting the user-provided-a-bogus- 102 * buffer EFAULT, give up (like for arc4random(3)'s arc4_stir). 103 */ 104 _getentropy_fail(); 105 } 106 return (0); 107 } 108 109 int 110 getentropy(void *buf, size_t buflen) 111 { 112 ssize_t rd; 113 bool have_getrandom; 114 115 if (buflen > 256) { 116 errno = EIO; 117 return (-1); 118 } 119 120 have_getrandom = (__getosreldate() >= GETRANDOM_FIRST); 121 122 while (buflen > 0) { 123 if (have_getrandom) { 124 rd = getrandom(buf, buflen, 0); 125 if (rd == -1) { 126 switch (errno) { 127 case ECAPMODE: 128 /* 129 * Kernel >= r331280 and < r337999 130 * will return ECAPMODE when the 131 * caller is already in capability 132 * mode, fallback to traditional 133 * method in this case. 134 */ 135 have_getrandom = false; 136 continue; 137 case EINTR: 138 continue; 139 case EFAULT: 140 return (-1); 141 default: 142 _getentropy_fail(); 143 } 144 } 145 } else { 146 return (getentropy_fallback(buf, buflen)); 147 } 148 149 /* This cannot happen. */ 150 if (rd == 0) 151 _getentropy_fail(); 152 153 buf = (char *)buf + rd; 154 buflen -= rd; 155 } 156 157 return (0); 158 } 159