1 /* $NetBSD: rand.c,v 1.2 2017/01/28 21:31:47 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include <config.h> 39 #include <krb5/roken.h> 40 41 #include <rand.h> 42 #include <randi.h> 43 44 #ifndef O_BINARY 45 #define O_BINARY 0 46 #endif 47 48 #ifdef _WIN32 49 #include<shlobj.h> 50 #endif 51 52 /** 53 * @page page_rand RAND - random number 54 * 55 * See the library functions here: @ref hcrypto_rand 56 */ 57 58 static const RAND_METHOD *selected_meth = NULL; 59 static ENGINE *selected_engine = NULL; 60 61 static void 62 init_method(void) 63 { 64 if (selected_meth != NULL) 65 return; 66 #if defined(_WIN32) 67 selected_meth = &hc_rand_w32crypto_method; 68 #elif defined(__APPLE__) 69 selected_meth = &hc_rand_unix_method; 70 #else 71 selected_meth = &hc_rand_fortuna_method; 72 #endif 73 } 74 75 /** 76 * Seed that random number generator. Secret material can securely be 77 * feed into the function, they will never be returned. 78 * 79 * @param indata seed data 80 * @param size length seed data 81 * 82 * @ingroup hcrypto_rand 83 */ 84 85 void 86 RAND_seed(const void *indata, size_t size) 87 { 88 init_method(); 89 (*selected_meth->seed)(indata, size); 90 } 91 92 /** 93 * Get a random block from the random generator, can be used for key material. 94 * 95 * @param outdata random data 96 * @param size length random data 97 * 98 * @return 1 on success, 0 on failure. 99 * 100 * @ingroup hcrypto_rand 101 */ 102 int 103 RAND_bytes(void *outdata, size_t size) 104 { 105 if (size == 0) 106 return 1; 107 init_method(); 108 return (*selected_meth->bytes)(outdata, size); 109 } 110 111 /** 112 * Reset and free memory used by the random generator. 113 * 114 * @ingroup hcrypto_rand 115 */ 116 117 void 118 RAND_cleanup(void) 119 { 120 const RAND_METHOD *meth = selected_meth; 121 ENGINE *engine = selected_engine; 122 123 selected_meth = NULL; 124 selected_engine = NULL; 125 126 if (meth) 127 (*meth->cleanup)(); 128 if (engine) 129 ENGINE_finish(engine); 130 } 131 132 /** 133 * Seed that random number generator. Secret material can securely be 134 * feed into the function, they will never be returned. 135 * 136 * @param indata the input data. 137 * @param size size of in data. 138 * @param entropi entropi in data. 139 * 140 * 141 * @ingroup hcrypto_rand 142 */ 143 144 void 145 RAND_add(const void *indata, size_t size, double entropi) 146 { 147 init_method(); 148 (*selected_meth->add)(indata, size, entropi); 149 } 150 151 /** 152 * Get a random block from the random generator, should NOT be used for key material. 153 * 154 * @param outdata random data 155 * @param size length random data 156 * 157 * @return 1 on success, 0 on failure. 158 * 159 * @ingroup hcrypto_rand 160 */ 161 162 int 163 RAND_pseudo_bytes(void *outdata, size_t size) 164 { 165 init_method(); 166 return (*selected_meth->pseudorand)(outdata, size); 167 } 168 169 /** 170 * Return status of the random generator 171 * 172 * @return 1 if the random generator can deliver random data. 173 * 174 * @ingroup hcrypto_rand 175 */ 176 177 int 178 RAND_status(void) 179 { 180 init_method(); 181 return (*selected_meth->status)(); 182 } 183 184 /** 185 * Set the default random method. 186 * 187 * @param meth set the new default method. 188 * 189 * @return 1 on success. 190 * 191 * @ingroup hcrypto_rand 192 */ 193 194 int 195 RAND_set_rand_method(const RAND_METHOD *meth) 196 { 197 const RAND_METHOD *old = selected_meth; 198 selected_meth = meth; 199 if (old) 200 (*old->cleanup)(); 201 if (selected_engine) { 202 ENGINE_finish(selected_engine); 203 selected_engine = NULL; 204 } 205 return 1; 206 } 207 208 /** 209 * Get the default random method. 210 * 211 * @return Returns a RAND_METHOD 212 * 213 * @ingroup hcrypto_rand 214 */ 215 216 const RAND_METHOD * 217 RAND_get_rand_method(void) 218 { 219 init_method(); 220 return selected_meth; 221 } 222 223 /** 224 * Set the default random method from engine. 225 * 226 * @param engine use engine, if NULL is passed it, old method and engine is cleared. 227 * 228 * @return 1 on success, 0 on failure. 229 * 230 * @ingroup hcrypto_rand 231 */ 232 233 int 234 RAND_set_rand_engine(ENGINE *engine) 235 { 236 const RAND_METHOD *meth, *old = selected_meth; 237 238 if (engine) { 239 ENGINE_up_ref(engine); 240 meth = ENGINE_get_RAND(engine); 241 if (meth == NULL) { 242 ENGINE_finish(engine); 243 return 0; 244 } 245 } else { 246 meth = NULL; 247 } 248 249 if (old) 250 (*old->cleanup)(); 251 252 if (selected_engine) 253 ENGINE_finish(selected_engine); 254 255 selected_engine = engine; 256 selected_meth = meth; 257 258 return 1; 259 } 260 261 #define RAND_FILE_SIZE 1024 262 263 /** 264 * Load a a file and feed it into RAND_seed(). 265 * 266 * @param filename name of file to read. 267 * @param size minimum size to read. 268 * 269 * @return Returns the number of seed bytes loaded (0 indicates failure) 270 * 271 * @ingroup hcrypto_rand 272 */ 273 274 int 275 RAND_load_file(const char *filename, size_t size) 276 { 277 unsigned char buf[128]; 278 size_t len; 279 ssize_t slen; 280 int fd; 281 282 fd = open(filename, O_RDONLY | O_BINARY, 0600); 283 if (fd < 0) 284 return 0; 285 rk_cloexec(fd); 286 len = 0; 287 while(len < size) { 288 slen = read(fd, buf, sizeof(buf)); 289 if (slen <= 0) 290 break; 291 RAND_seed(buf, slen); 292 len += slen; 293 } 294 close(fd); 295 296 return len ? 1 : 0; 297 } 298 299 /** 300 * Write of random numbers to a file to store for later initiation with RAND_load_file(). 301 * 302 * @param filename name of file to write. 303 * 304 * @return 1 on success and non-one on failure. 305 * @ingroup hcrypto_rand 306 */ 307 308 int 309 RAND_write_file(const char *filename) 310 { 311 unsigned char buf[128]; 312 size_t len; 313 int res = 0, fd; 314 315 fd = open(filename, O_WRONLY | O_CREAT | O_BINARY, 0600); 316 if (fd < 0) 317 return 0; 318 rk_cloexec(fd); 319 320 len = 0; 321 while(len < RAND_FILE_SIZE) { 322 res = RAND_bytes(buf, sizeof(buf)); 323 if (res != 1) 324 break; 325 if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { 326 res = 0; 327 break; 328 } 329 len += sizeof(buf); 330 } 331 332 close(fd); 333 334 return res; 335 } 336 337 /** 338 * Return the default random state filename for a user to use for 339 * RAND_load_file(), and RAND_write_file(). 340 * 341 * @param filename buffer to hold file name. 342 * @param size size of buffer filename. 343 * 344 * @return the buffer filename or NULL on failure. 345 * 346 * @ingroup hcrypto_rand 347 */ 348 349 const char * 350 RAND_file_name(char *filename, size_t size) 351 { 352 const char *e = NULL; 353 int pathp = 0, ret; 354 355 if (!issuid()) { 356 e = getenv("RANDFILE"); 357 if (e == NULL) 358 e = getenv("HOME"); 359 if (e) 360 pathp = 1; 361 } 362 363 #ifndef _WIN32 364 /* 365 * Here we really want to call getpwuid(getuid()) but this will 366 * cause recursive lookups if the nss library uses 367 * gssapi/krb5/hcrypto to authenticate to the ldap servers. 368 * 369 * So at least return the unix /dev/random if we have one 370 */ 371 if (e == NULL) { 372 int fd; 373 374 fd = _hc_unix_device_fd(O_RDONLY, &e); 375 if (fd >= 0) 376 close(fd); 377 } 378 #else /* Win32 */ 379 380 if (e == NULL) { 381 char profile[MAX_PATH]; 382 383 if (SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 384 SHGFP_TYPE_CURRENT, profile) == S_OK) { 385 ret = snprintf(filename, size, "%s\\.rnd", profile); 386 387 if (ret > 0 && ret < size) 388 return filename; 389 } 390 } 391 392 #endif 393 394 if (e == NULL) 395 return NULL; 396 397 if (pathp) 398 ret = snprintf(filename, size, "%s/.rnd", e); 399 else 400 ret = snprintf(filename, size, "%s", e); 401 402 if (ret <= 0 || ret >= size) 403 return NULL; 404 405 return filename; 406 } 407