1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright (C) 2001, 2002, 2004, 2007, 2008, 2010 by the Massachusetts Institute of Technology. 4 * All rights reserved. 5 * 6 * 7 * Export of this software from the United States of America may require 8 * a specific license from the United States Government. It is the 9 * responsibility of any person or organization contemplating export to 10 * obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 #include "crypto_int.h" 28 29 krb5_error_code KRB5_CALLCONV 30 krb5_c_random_seed(krb5_context context, krb5_data *data) 31 { 32 return krb5_c_random_add_entropy(context, KRB5_C_RANDSOURCE_OLDAPI, data); 33 } 34 35 /* Routines to get entropy from the OS. */ 36 #if defined(_WIN32) 37 38 static krb5_boolean 39 get_os_entropy(unsigned char *buf, size_t len) 40 { 41 krb5_boolean result; 42 HCRYPTPROV provider; 43 44 if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, 45 CRYPT_VERIFYCONTEXT)) 46 return FALSE; 47 result = CryptGenRandom(provider, len, buf); 48 (void)CryptReleaseContext(provider, 0); 49 return result; 50 } 51 52 #else /* not Windows */ 53 #ifdef HAVE_UNISTD_H 54 #include <unistd.h> 55 #endif 56 #ifdef HAVE_SYS_STAT_H 57 #include <sys/stat.h> 58 #endif 59 #ifdef HAVE_SYS_RANDOM_H 60 #include <sys/random.h> 61 #endif 62 #ifdef __linux__ 63 #include <sys/syscall.h> 64 #endif /* __linux__ */ 65 66 /* Open device, ensure that it is not a regular file, and read entropy. Return 67 * true on success, false on failure. */ 68 static krb5_boolean 69 read_entropy_from_device(const char *device, unsigned char *buf, size_t len) 70 { 71 struct stat sb; 72 int fd; 73 unsigned char *bp; 74 size_t left; 75 ssize_t count; 76 krb5_boolean result = FALSE; 77 78 fd = open(device, O_RDONLY); 79 if (fd == -1) 80 return FALSE; 81 set_cloexec_fd(fd); 82 if (fstat(fd, &sb) == -1 || S_ISREG(sb.st_mode)) 83 goto cleanup; 84 85 for (bp = buf, left = len; left > 0;) { 86 count = read(fd, bp, left); 87 if (count <= 0) 88 goto cleanup; 89 left -= count; 90 bp += count; 91 } 92 result = TRUE; 93 94 cleanup: 95 close(fd); 96 return result; 97 } 98 99 static krb5_boolean 100 get_os_entropy(unsigned char *buf, size_t len) 101 { 102 #if defined(HAVE_GETENTROPY) 103 int r; 104 size_t seg; 105 106 /* getentropy() has a maximum length of 256. */ 107 while (len > 0) { 108 seg = (len > 256) ? 256 : len; 109 r = getentropy(buf, seg); 110 if (r != 0) 111 break; 112 len -= seg; 113 buf += seg; 114 } 115 if (len == 0) 116 return TRUE; 117 #elif defined(__linux__) && defined(SYS_getrandom) 118 /* 119 * Linux added SYS_getrandom in 3.17 (2014), but glibc did not have a 120 * wrapper until 2.25 (2017). This block can be deleted when that interval 121 * is far in the past, along with the conditional include of 122 * <sys/syscall.h> above. 123 */ 124 int r; 125 126 while (len > 0) { 127 /* 128 * Pull from the /dev/urandom pool, but require it to have been seeded. 129 * This ensures strong randomness while only blocking during first 130 * system boot. 131 */ 132 errno = 0; 133 r = syscall(SYS_getrandom, buf, len, 0); 134 if (r <= 0) { 135 if (errno == EINTR) 136 continue; 137 138 /* ENOSYS or other unrecoverable failure */ 139 break; 140 } 141 len -= r; 142 buf += r; 143 } 144 if (len == 0) 145 return TRUE; 146 #endif /* defined(__linux__) && defined(SYS_getrandom) */ 147 148 return read_entropy_from_device("/dev/urandom", buf, len); 149 } 150 151 #endif /* not Windows */ 152 153 krb5_error_code KRB5_CALLCONV 154 krb5_c_random_make_octets(krb5_context context, krb5_data *outdata) 155 { 156 krb5_boolean res; 157 158 res = get_os_entropy((uint8_t *)outdata->data, outdata->length); 159 return res ? 0 : KRB5_CRYPTO_INTERNAL; 160 } 161 162 krb5_error_code KRB5_CALLCONV 163 krb5_c_random_add_entropy(krb5_context context, unsigned int randsource, 164 const krb5_data *indata) 165 { 166 return 0; 167 } 168 169 krb5_error_code KRB5_CALLCONV 170 krb5_c_random_os_entropy(krb5_context context, int strong, int *success) 171 { 172 *success = 0; 173 return 0; 174 } 175