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
init_method(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
RAND_seed(const void * indata,size_t size)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
RAND_bytes(void * outdata,size_t size)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
RAND_cleanup(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
RAND_add(const void * indata,size_t size,double entropi)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
RAND_pseudo_bytes(void * outdata,size_t size)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
RAND_status(void)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
RAND_set_rand_method(const RAND_METHOD * meth)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 *
RAND_get_rand_method(void)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
RAND_set_rand_engine(ENGINE * engine)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
RAND_load_file(const char * filename,size_t size)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
RAND_write_file(const char * filename)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 *
RAND_file_name(char * filename,size_t size)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