1 /* $NetBSD: rand.c,v 1.1.1.1 2011/04/13 18:14:50 elric 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 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <rand.h> 43 #include <randi.h> 44 45 #include <krb5/roken.h> 46 47 #ifndef O_BINARY 48 #define O_BINARY 0 49 #endif 50 51 #ifdef _WIN32 52 #include<shlobj.h> 53 #endif 54 55 /** 56 * @page page_rand RAND - random number 57 * 58 * See the library functions here: @ref hcrypto_rand 59 */ 60 61 const static RAND_METHOD *selected_meth = NULL; 62 static ENGINE *selected_engine = NULL; 63 64 static void 65 init_method(void) 66 { 67 if (selected_meth != NULL) 68 return; 69 #if defined(_WIN32) 70 selected_meth = &hc_rand_w32crypto_method; 71 #elif defined(__APPLE__) 72 selected_meth = &hc_rand_unix_method; 73 #else 74 selected_meth = &hc_rand_fortuna_method; 75 #endif 76 } 77 78 /** 79 * Seed that random number generator. Secret material can securely be 80 * feed into the function, they will never be returned. 81 * 82 * @param indata seed data 83 * @param size length seed data 84 * 85 * @ingroup hcrypto_rand 86 */ 87 88 void 89 RAND_seed(const void *indata, size_t size) 90 { 91 init_method(); 92 (*selected_meth->seed)(indata, size); 93 } 94 95 /** 96 * Get a random block from the random generator, can be used for key material. 97 * 98 * @param outdata random data 99 * @param size length random data 100 * 101 * @return 1 on success, 0 on failure. 102 * 103 * @ingroup hcrypto_rand 104 */ 105 int 106 RAND_bytes(void *outdata, size_t size) 107 { 108 if (size == 0) 109 return 1; 110 init_method(); 111 return (*selected_meth->bytes)(outdata, size); 112 } 113 114 /** 115 * Reset and free memory used by the random generator. 116 * 117 * @ingroup hcrypto_rand 118 */ 119 120 void 121 RAND_cleanup(void) 122 { 123 const RAND_METHOD *meth = selected_meth; 124 ENGINE *engine = selected_engine; 125 126 selected_meth = NULL; 127 selected_engine = NULL; 128 129 if (meth) 130 (*meth->cleanup)(); 131 if (engine) 132 ENGINE_finish(engine); 133 } 134 135 /** 136 * Seed that random number generator. Secret material can securely be 137 * feed into the function, they will never be returned. 138 * 139 * @param indata the input data. 140 * @param size size of in data. 141 * @param entropi entropi in data. 142 * 143 * 144 * @ingroup hcrypto_rand 145 */ 146 147 void 148 RAND_add(const void *indata, size_t size, double entropi) 149 { 150 init_method(); 151 (*selected_meth->add)(indata, size, entropi); 152 } 153 154 /** 155 * Get a random block from the random generator, should NOT be used for key material. 156 * 157 * @param outdata random data 158 * @param size length random data 159 * 160 * @return 1 on success, 0 on failure. 161 * 162 * @ingroup hcrypto_rand 163 */ 164 165 int 166 RAND_pseudo_bytes(void *outdata, size_t size) 167 { 168 init_method(); 169 return (*selected_meth->pseudorand)(outdata, size); 170 } 171 172 /** 173 * Return status of the random generator 174 * 175 * @return 1 if the random generator can deliver random data. 176 * 177 * @ingroup hcrypto_rand 178 */ 179 180 int 181 RAND_status(void) 182 { 183 init_method(); 184 return (*selected_meth->status)(); 185 } 186 187 /** 188 * Set the default random method. 189 * 190 * @param meth set the new default method. 191 * 192 * @return 1 on success. 193 * 194 * @ingroup hcrypto_rand 195 */ 196 197 int 198 RAND_set_rand_method(const RAND_METHOD *meth) 199 { 200 const RAND_METHOD *old = selected_meth; 201 selected_meth = meth; 202 if (old) 203 (*old->cleanup)(); 204 if (selected_engine) { 205 ENGINE_finish(selected_engine); 206 selected_engine = NULL; 207 } 208 return 1; 209 } 210 211 /** 212 * Get the default random method. 213 * 214 * @ingroup hcrypto_rand 215 */ 216 217 const RAND_METHOD * 218 RAND_get_rand_method(void) 219 { 220 init_method(); 221 return selected_meth; 222 } 223 224 /** 225 * Set the default random method from engine. 226 * 227 * @param engine use engine, if NULL is passed it, old method and engine is cleared. 228 * 229 * @return 1 on success, 0 on failure. 230 * 231 * @ingroup hcrypto_rand 232 */ 233 234 int 235 RAND_set_rand_engine(ENGINE *engine) 236 { 237 const RAND_METHOD *meth, *old = selected_meth; 238 239 if (engine) { 240 ENGINE_up_ref(engine); 241 meth = ENGINE_get_RAND(engine); 242 if (meth == NULL) { 243 ENGINE_finish(engine); 244 return 0; 245 } 246 } else { 247 meth = NULL; 248 } 249 250 if (old) 251 (*old->cleanup)(); 252 253 if (selected_engine) 254 ENGINE_finish(selected_engine); 255 256 selected_engine = engine; 257 selected_meth = meth; 258 259 return 1; 260 } 261 262 #define RAND_FILE_SIZE 1024 263 264 /** 265 * Load a a file and feed it into RAND_seed(). 266 * 267 * @param filename name of file to read. 268 * @param size minimum size to read. 269 * 270 * @ingroup hcrypto_rand 271 */ 272 273 int 274 RAND_load_file(const char *filename, size_t size) 275 { 276 unsigned char buf[128]; 277 size_t len; 278 ssize_t slen; 279 int fd; 280 281 fd = open(filename, O_RDONLY | O_BINARY, 0600); 282 if (fd < 0) 283 return 0; 284 rk_cloexec(fd); 285 len = 0; 286 while(len < size) { 287 slen = read(fd, buf, sizeof(buf)); 288 if (slen <= 0) 289 break; 290 RAND_seed(buf, slen); 291 len += slen; 292 } 293 close(fd); 294 295 return len ? 1 : 0; 296 } 297 298 /** 299 * Write of random numbers to a file to store for later initiation with RAND_load_file(). 300 * 301 * @param filename name of file to write. 302 * 303 * @return 1 on success and non-one on failure. 304 * @ingroup hcrypto_rand 305 */ 306 307 int 308 RAND_write_file(const char *filename) 309 { 310 unsigned char buf[128]; 311 size_t len; 312 int res = 0, fd; 313 314 fd = open(filename, O_WRONLY | O_CREAT | O_BINARY, 0600); 315 if (fd < 0) 316 return 0; 317 rk_cloexec(fd); 318 319 len = 0; 320 while(len < RAND_FILE_SIZE) { 321 res = RAND_bytes(buf, sizeof(buf)); 322 if (res != 1) 323 break; 324 if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { 325 res = 0; 326 break; 327 } 328 len += sizeof(buf); 329 } 330 331 close(fd); 332 333 return res; 334 } 335 336 /** 337 * Return the default random state filename for a user to use for 338 * RAND_load_file(), and RAND_write_file(). 339 * 340 * @param filename buffer to hold file name. 341 * @param size size of buffer filename. 342 * 343 * @return the buffer filename or NULL on failure. 344 * 345 * @ingroup hcrypto_rand 346 */ 347 348 const char * 349 RAND_file_name(char *filename, size_t size) 350 { 351 const char *e = NULL; 352 int pathp = 0, ret; 353 354 if (!issuid()) { 355 e = getenv("RANDFILE"); 356 if (e == NULL) 357 e = getenv("HOME"); 358 if (e) 359 pathp = 1; 360 } 361 362 #ifndef _WIN32 363 /* 364 * Here we really want to call getpwuid(getuid()) but this will 365 * cause recursive lookups if the nss library uses 366 * gssapi/krb5/hcrypto to authenticate to the ldap servers. 367 * 368 * So at least return the unix /dev/random if we have one 369 */ 370 if (e == NULL) { 371 int fd; 372 373 fd = _hc_unix_device_fd(O_RDONLY, &e); 374 if (fd >= 0) 375 close(fd); 376 } 377 #else /* Win32 */ 378 379 if (e == NULL) { 380 char profile[MAX_PATH]; 381 382 if (SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 383 SHGFP_TYPE_CURRENT, profile) == S_OK) { 384 ret = snprintf(filename, size, "%s\\.rnd", profile); 385 386 if (ret > 0 && ret < size) 387 return filename; 388 } 389 } 390 391 #endif 392 393 if (e == NULL) 394 return NULL; 395 396 if (pathp) 397 ret = snprintf(filename, size, "%s/.rnd", e); 398 else 399 ret = snprintf(filename, size, "%s", e); 400 401 if (ret <= 0 || ret >= size) 402 return NULL; 403 404 return filename; 405 } 406