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