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