X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Futils%2Fmono-rand.c;h=1ac91179382d2ffb4ee7f5e826bca141e7cc4bb5;hb=8e6f129087207e14b5bc411554229441a7b522e6;hp=71be40b92bf7fc5abc22553512baf33f3c7c835a;hpb=28f473c41df72b278eaf9784c29c2b8fa2cbe06a;p=mono.git diff --git a/mono/utils/mono-rand.c b/mono/utils/mono-rand.c index 71be40b92bf..1ac91179382 100644 --- a/mono/utils/mono-rand.c +++ b/mono/utils/mono-rand.c @@ -10,101 +10,22 @@ * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) * Copyright 2004-2009 Novell, Inc (http://www.novell.com) * Copyright 2001 Xamarin, Inc (http://www.novell.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ - #include #include #include "atomic.h" +#include "mono-error.h" +#include "mono-error-internals.h" #include "mono-rand.h" #include "mono-threads.h" #include "metadata/exception.h" #include "metadata/object.h" #ifdef HOST_WIN32 - -#include -#include - -#ifndef PROV_INTEL_SEC -#define PROV_INTEL_SEC 22 -#endif -#ifndef CRYPT_VERIFY_CONTEXT -#define CRYPT_VERIFY_CONTEXT 0xF0000000 -#endif - -gboolean -mono_rand_open (void) -{ - /* FALSE == Local (instance) handle for randomness */ - return FALSE; -} - -gpointer -mono_rand_init (guchar *seed, gint seed_size) -{ - HCRYPTPROV provider = 0; - - /* There is no need to create a container for just random data, - * so we can use CRYPT_VERIFY_CONTEXT (one call) see: - * http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */ - - /* We first try to use the Intel PIII RNG if drivers are present */ - if (!CryptAcquireContext (&provider, NULL, NULL, PROV_INTEL_SEC, CRYPT_VERIFY_CONTEXT)) { - /* not a PIII or no drivers available, use default RSA CSP */ - if (!CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT)) { - /* exception will be thrown in managed code */ - provider = 0; - } - } - - /* seed the CSP with the supplied buffer (if present) */ - if (provider != 0 && seed) { - /* the call we replace the seed with random - this isn't what is - * expected from the class library user */ - guchar *data = g_malloc (seed_size); - if (data) { - memcpy (data, seed, seed_size); - /* add seeding material to the RNG */ - CryptGenRandom (provider, seed_size, data); - /* zeroize and free */ - memset (data, 0, seed_size); - g_free (data); - } - } - - return (gpointer) provider; -} - -gboolean -mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size) -{ - HCRYPTPROV provider; - - g_assert (handle); - provider = (HCRYPTPROV) *handle; - - if (!CryptGenRandom (provider, buffer_size, buffer)) { - CryptReleaseContext (provider, 0); - /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */ - provider = (HCRYPTPROV) mono_rand_init (NULL, 0); - if (!CryptGenRandom (provider, buffer_size, buffer)) { - /* exception will be thrown in managed code */ - CryptReleaseContext (provider, 0); - *handle = 0; - return FALSE; - } - } - return TRUE; -} - -void -mono_rand_close (gpointer handle) -{ - CryptReleaseContext ((HCRYPTPROV) handle, 0); -} - +// Windows specific implementation in mono-rand-windows.c #elif defined (HAVE_SYS_UN_H) && !defined(__native_client__) #include @@ -122,27 +43,33 @@ static gboolean use_egd = FALSE; static gint file = -1; static void -get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size) +get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size, MonoError *error) { struct sockaddr_un egd_addr; - gint file; + gint socket_fd; gint ret; guint offset = 0; + int err = 0; - file = socket (PF_UNIX, SOCK_STREAM, 0); - if (file < 0) { + mono_error_init (error); + + socket_fd = socket (PF_UNIX, SOCK_STREAM, 0); + if (socket_fd < 0) { ret = -1; + err = errno; } else { egd_addr.sun_family = AF_UNIX; strncpy (egd_addr.sun_path, path, sizeof (egd_addr.sun_path) - 1); egd_addr.sun_path [sizeof (egd_addr.sun_path) - 1] = '\0'; - ret = connect (file, (struct sockaddr*) &egd_addr, sizeof (egd_addr)); + ret = connect (socket_fd, (struct sockaddr*) &egd_addr, sizeof (egd_addr)); + err = errno; } if (ret == -1) { - if (file >= 0) - close (file); + if (socket_fd >= 0) + close (socket_fd); g_warning ("Entropy problem! Can't create or connect to egd socket %s", path); - mono_raise_exception (mono_get_exception_execution_engine ("Failed to open egd socket")); + mono_error_set_execution_engine (error, "Failed to open egd socket %s: %s", path, strerror (err)); + return; } while (buffer_size > 0) { @@ -153,38 +80,42 @@ get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size) request [0] = 2; request [1] = buffer_size < 255 ? buffer_size : 255; while (count < 2) { - int sent = write (file, request + count, 2 - count); + int sent = write (socket_fd, request + count, 2 - count); + err = errno; if (sent >= 0) { count += sent; - } else if (errno == EINTR) { + } else if (err == EINTR) { continue; } else { - close (file); - g_warning ("Send egd request failed %d", errno); - mono_raise_exception (mono_get_exception_execution_engine ("Failed to send request to egd socket")); + close (socket_fd); + g_warning ("Send egd request failed %d", err); + mono_error_set_execution_engine (error, "Failed to send request to egd socket: %s", strerror (err)); + return; } } count = 0; while (count != request [1]) { int received; - received = read (file, buffer + offset, request [1] - count); + received = read (socket_fd, buffer + offset, request [1] - count); + err = errno; if (received > 0) { count += received; offset += received; - } else if (received < 0 && errno == EINTR) { + } else if (received < 0 && err == EINTR) { continue; } else { - close (file); - g_warning ("Receive egd request failed %d", errno); - mono_raise_exception (mono_get_exception_execution_engine ("Failed to get response from egd socket")); + close (socket_fd); + g_warning ("Receive egd request failed %d", err); + mono_error_set_execution_engine (error, "Failed to get response from egd socket: %s", strerror(err)); + return; } } buffer_size -= request [1]; } - close (file); + close (socket_fd); } gboolean @@ -215,15 +146,17 @@ mono_rand_open (void) gpointer mono_rand_init (guchar *seed, gint seed_size) { - /* if required exception will be thrown in managed code */ + // file < 0 is expected in the egd case return (!use_egd && file < 0) ? NULL : GINT_TO_POINTER (file); } gboolean -mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size) +mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error) { g_assert (handle); + mono_error_init (error); + if (use_egd) { const char *socket_path = g_getenv ("MONO_EGD_SOCKET"); /* exception will be thrown in managed code */ @@ -231,7 +164,7 @@ mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size) *handle = NULL; return FALSE; } - get_entropy_from_egd (socket_path, buffer, buffer_size); + get_entropy_from_egd (socket_path, buffer, buffer_size, error); } else { /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */ gint count = 0; @@ -244,6 +177,7 @@ mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size) continue; g_warning("Entropy error! Error in read (%s).", strerror (errno)); /* exception will be thrown in managed code */ + mono_error_set_execution_engine (error, "Entropy error! Error in read (%s).", strerror (errno)); return FALSE; } count += err; @@ -282,14 +216,16 @@ mono_rand_open (void) gpointer mono_rand_init (guchar *seed, gint seed_size) { - return NULL; + return "srand"; // NULL will be interpreted as failure; return arbitrary nonzero pointer } gboolean -mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size) +mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error) { gint count = 0; + mono_error_init (error); + do { if (buffer_size - count >= sizeof (gint32) && RAND_MAX >= 0xFFFFFFFF) { *(gint32*) buffer = rand(); @@ -316,16 +252,29 @@ mono_rand_close (gpointer provider) #endif +/** + * mono_rand_try_get_uint32: + * @handle: A pointer to an RNG handle. Handle is set to NULL on failure. + * @val: A pointer to a 32-bit unsigned int, to which the result will be written. + * @min: Result will be greater than or equal to this value. + * @max: Result will be less than or equal to this value. + * + * Returns: FALSE on failure, TRUE on success. + * + * Extracts one 32-bit unsigned int from an RNG handle. + */ gboolean -mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max) +mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max, MonoError *error) { g_assert (val); - if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32))) + if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32), error)) return FALSE; - *val = (guint32) (((gdouble) *val) / G_MAXUINT32 * (max - min) + min); + double randomDouble = ((gdouble) *val) / ( ((double)G_MAXUINT32) + 1 ); // Range is [0,1) + *val = (guint32) (randomDouble * (max - min + 1) + min); + g_assert (*val >= min); g_assert (*val <= max); return TRUE; -} \ No newline at end of file +}