First set of licensing changes
[mono.git] / mono / utils / mono-rand.c
1 /*
2  * mono-rand.c: 
3  *
4  * Authors:
5  *      Mark Crichton (crichton@gimp.org)
6  *      Patrik Torstensson (p@rxc.se)
7  *      Sebastien Pouliot (sebastien@ximian.com)
8  *      Ludovic Henry (ludovic.henry@xamarin.com)
9  *
10  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
11  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
12  * Copyright 2001 Xamarin, Inc (http://www.novell.com)
13  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14  */
15
16
17 #include <glib.h>
18 #include <config.h>
19
20 #include "atomic.h"
21 #include "mono-error.h"
22 #include "mono-error-internals.h"
23 #include "mono-rand.h"
24 #include "mono-threads.h"
25 #include "metadata/exception.h"
26 #include "metadata/object.h"
27
28 #ifdef HOST_WIN32
29
30 #include <windows.h>
31 #include <wincrypt.h>
32
33 #ifndef PROV_INTEL_SEC
34 #define PROV_INTEL_SEC          22
35 #endif
36 #ifndef CRYPT_VERIFY_CONTEXT
37 #define CRYPT_VERIFY_CONTEXT    0xF0000000
38 #endif
39
40 /**
41  * mono_rand_open:
42  *
43  * Returns: True if random source is global, false if mono_rand_init can be called repeatedly to get randomness instances.
44  *
45  * Initializes entire RNG system. Must be called once per process before calling mono_rand_init.
46  */
47 gboolean
48 mono_rand_open (void)
49 {
50         return FALSE;
51 }
52
53 /**
54  * mono_rand_init:
55  * @seed: A string containing seed data
56  * @seed_size: Length of seed string
57  *
58  * Returns: On success, a non-NULL handle which can be used to fetch random data from mono_rand_try_get_bytes. On failure, NULL.
59  *
60  * Initializes an RNG client.
61  */
62 gpointer
63 mono_rand_init (guchar *seed, gint seed_size)
64 {
65         HCRYPTPROV provider = 0;
66
67         /* There is no need to create a container for just random data,
68          * so we can use CRYPT_VERIFY_CONTEXT (one call) see: 
69          * http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */
70
71         /* We first try to use the Intel PIII RNG if drivers are present */
72         if (!CryptAcquireContext (&provider, NULL, NULL, PROV_INTEL_SEC, CRYPT_VERIFY_CONTEXT)) {
73                 /* not a PIII or no drivers available, use default RSA CSP */
74                 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT)) {
75                         /* exception will be thrown in managed code */
76                         provider = 0;
77                 }
78         }
79
80         /* seed the CSP with the supplied buffer (if present) */
81         if (provider != 0 && seed) {
82                 /* the call we replace the seed with random - this isn't what is
83                  * expected from the class library user */
84                 guchar *data = g_malloc (seed_size);
85                 if (data) {
86                         memcpy (data, seed, seed_size);
87                         /* add seeding material to the RNG */
88                         CryptGenRandom (provider, seed_size, data);
89                         /* zeroize and free */
90                         memset (data, 0, seed_size);
91                         g_free (data);
92                 }
93         }
94
95         return (gpointer) provider;
96 }
97
98 /**
99  * mono_rand_try_get_bytes:
100  * @handle: A pointer to an RNG handle. Handle is set to NULL on failure.
101  * @buffer: A buffer into which to write random data.
102  * @buffer_size: Number of bytes to write into buffer.
103  * @error: Set on error.
104  *
105  * Returns: FALSE on failure and sets @error, TRUE on success.
106  *
107  * Extracts bytes from an RNG handle.
108  */
109 gboolean
110 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
111 {
112         HCRYPTPROV provider;
113
114         mono_error_init (error);
115
116         g_assert (handle);
117         provider = (HCRYPTPROV) *handle;
118
119         if (!CryptGenRandom (provider, buffer_size, buffer)) {
120                 CryptReleaseContext (provider, 0);
121                 /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
122                 provider = (HCRYPTPROV) mono_rand_init (NULL, 0);
123                 if (!CryptGenRandom (provider, buffer_size, buffer)) {
124                         /* exception will be thrown in managed code */
125                         CryptReleaseContext (provider, 0);
126                         *handle = 0;
127                         mono_error_set_execution_engine (error, "Failed to gen random bytes (%d)", GetLastError ());
128                         return FALSE;
129                 }
130         }
131         return TRUE;
132 }
133
134 /**
135  * mono_rand_close:
136  * @handle: An RNG handle.
137  * @buffer: A buffer into which to write random data.
138  * @buffer_size: Number of bytes to write into buffer.
139  *
140  * Releases an RNG handle.
141  */
142 void
143 mono_rand_close (gpointer handle)
144 {
145         CryptReleaseContext ((HCRYPTPROV) handle, 0);
146 }
147
148 #elif defined (HAVE_SYS_UN_H) && !defined(__native_client__)
149
150 #include <errno.h>
151 #include <fcntl.h>
152 #include <unistd.h>
153 #include <sys/socket.h>
154 #include <sys/types.h>
155 #include <sys/un.h>
156
157 #ifndef NAME_DEV_URANDOM
158 #define NAME_DEV_URANDOM "/dev/urandom"
159 #endif
160
161 static gboolean use_egd = FALSE;
162 static gint file = -1;
163
164 static void
165 get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size, MonoError *error)
166 {
167         struct sockaddr_un egd_addr;
168         gint file;
169         gint ret;
170         guint offset = 0;
171         int err = 0;
172
173         mono_error_init (error);
174         
175         file = socket (PF_UNIX, SOCK_STREAM, 0);
176         if (file < 0) {
177                 ret = -1;
178                 err = errno;
179         } else {
180                 egd_addr.sun_family = AF_UNIX;
181                 strncpy (egd_addr.sun_path, path, sizeof (egd_addr.sun_path) - 1);
182                 egd_addr.sun_path [sizeof (egd_addr.sun_path) - 1] = '\0';
183                 ret = connect (file, (struct sockaddr*) &egd_addr, sizeof (egd_addr));
184                 err = errno;
185         }
186         if (ret == -1) {
187                 if (file >= 0)
188                         close (file);
189                 g_warning ("Entropy problem! Can't create or connect to egd socket %s", path);
190                 mono_error_set_execution_engine (error, "Failed to open egd socket %s: %s", path, strerror (err));
191                 return;
192         }
193
194         while (buffer_size > 0) {
195                 guchar request [2];
196                 gint count = 0;
197
198                 /* block until daemon can return enough entropy */
199                 request [0] = 2;
200                 request [1] = buffer_size < 255 ? buffer_size : 255;
201                 while (count < 2) {
202                         int sent = write (file, request + count, 2 - count);
203                         err = errno;
204                         if (sent >= 0) {
205                                 count += sent;
206                         } else if (err == EINTR) {
207                                 continue;
208                         } else {
209                                 close (file);
210                                 g_warning ("Send egd request failed %d", err);
211                                 mono_error_set_execution_engine (error, "Failed to send request to egd socket: %s", strerror (err));
212                                 return;
213                         }
214                 }
215
216                 count = 0;
217                 while (count != request [1]) {
218                         int received;
219                         received = read (file, buffer + offset, request [1] - count);
220                         err = errno;
221                         if (received > 0) {
222                                 count += received;
223                                 offset += received;
224                         } else if (received < 0 && err == EINTR) {
225                                 continue;
226                         } else {
227                                 close (file);
228                                 g_warning ("Receive egd request failed %d", err);
229                                 mono_error_set_execution_engine (error, "Failed to get response from egd socket: %s", strerror(err));
230                                 return;
231                         }
232                 }
233
234                 buffer_size -= request [1];
235         }
236
237         close (file);
238 }
239
240 gboolean
241 mono_rand_open (void)
242 {
243         static gint32 status = 0;
244         if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) {
245                 while (status != 2)
246                         mono_thread_info_yield ();
247                 return TRUE;
248         }
249
250 #ifdef NAME_DEV_URANDOM
251         file = open (NAME_DEV_URANDOM, O_RDONLY);
252 #endif
253 #ifdef NAME_DEV_RANDOM
254         if (file < 0)
255                 file = open (NAME_DEV_RANDOM, O_RDONLY);
256 #endif
257         if (file < 0)
258                 use_egd = g_getenv("MONO_EGD_SOCKET") != NULL;
259
260         status = 2;
261
262         return TRUE;
263 }
264
265 gpointer
266 mono_rand_init (guchar *seed, gint seed_size)
267 {
268         // file < 0 is expected in the egd case
269         return (!use_egd && file < 0) ? NULL : GINT_TO_POINTER (file);
270 }
271
272 gboolean
273 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
274 {
275         g_assert (handle);
276
277         mono_error_init (error);
278
279         if (use_egd) {
280                 const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
281                 /* exception will be thrown in managed code */
282                 if (socket_path == NULL) {
283                         *handle = NULL;
284                         return FALSE;
285                 }
286                 get_entropy_from_egd (socket_path, buffer, buffer_size, error);
287         } else {
288                 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
289                 gint count = 0;
290                 gint err;
291
292                 do {
293                         err = read (file, buffer + count, buffer_size - count);
294                         if (err < 0) {
295                                 if (errno == EINTR)
296                                         continue;
297                                 g_warning("Entropy error! Error in read (%s).", strerror (errno));
298                                 /* exception will be thrown in managed code */
299                                 mono_error_set_execution_engine (error, "Entropy error! Error in read (%s).", strerror (errno));
300                                 return FALSE;
301                         }
302                         count += err;
303                 } while (count < buffer_size);
304         }
305         return TRUE;
306 }
307
308 void
309 mono_rand_close (gpointer provider)
310 {
311 }
312
313 #else
314
315 #include <stdlib.h>
316 #include <time.h>
317
318 gboolean
319 mono_rand_open (void)
320 {
321         static gint32 status = 0;
322         if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) {
323                 while (status != 2)
324                         mono_thread_info_yield ();
325                 return TRUE;
326         }
327
328         srand (time (NULL));
329
330         status = 2;
331
332         return TRUE;
333 }
334
335 gpointer
336 mono_rand_init (guchar *seed, gint seed_size)
337 {
338         return "srand"; // NULL will be interpreted as failure; return arbitrary nonzero pointer
339 }
340
341 gboolean
342 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
343 {
344         gint count = 0;
345
346         mono_error_init (error);
347         
348         do {
349                 if (buffer_size - count >= sizeof (gint32) && RAND_MAX >= 0xFFFFFFFF) {
350                         *(gint32*) buffer = rand();
351                         count += sizeof (gint32);
352                         buffer += sizeof (gint32) / sizeof (guchar);
353                 } else if (buffer_size - count >= sizeof (gint16) && RAND_MAX >= 0xFFFF) {
354                         *(gint16*) buffer = rand();
355                         count += sizeof (gint16);
356                         buffer += sizeof (gint16) / sizeof (guchar);
357                 } else if (buffer_size - count >= sizeof (gint8) && RAND_MAX >= 0xFF) {
358                         *(gint8*) buffer = rand();
359                         count += sizeof (gint8);
360                         buffer += sizeof (gint8) / sizeof (guchar);
361                 }
362         } while (count < buffer_size);
363
364         return TRUE;
365 }
366
367 void
368 mono_rand_close (gpointer provider)
369 {
370 }
371
372 #endif
373
374 /**
375  * mono_rand_try_get_uint32:
376  * @handle: A pointer to an RNG handle. Handle is set to NULL on failure.
377  * @val: A pointer to a 32-bit unsigned int, to which the result will be written.
378  * @min: Result will be greater than or equal to this value.
379  * @max: Result will be less than or equal to this value.
380  *
381  * Returns: FALSE on failure, TRUE on success.
382  *
383  * Extracts one 32-bit unsigned int from an RNG handle.
384  */
385 gboolean
386 mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max, MonoError *error)
387 {
388         g_assert (val);
389         if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32), error))
390                 return FALSE;
391
392         double randomDouble = ((gdouble) *val) / ( ((double)G_MAXUINT32) + 1 ); // Range is [0,1)
393         *val = (guint32) (randomDouble * (max - min + 1) + min);
394
395         g_assert (*val >= min);
396         g_assert (*val <= max);
397
398         return TRUE;
399 }