Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / rand.c
index 4ed1203f85df3390b9d75bd5c597ec955c0c6dea..1dcfc8d4077884a2d28723ed5c0331ff7d85180f 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * rand.c: System.Security.Cryptography.RNGCryptoServiceProvider support
+/**
+ * \file
+ * System.Security.Cryptography.RNGCryptoServiceProvider support
  *
  * Authors:
  *      Mark Crichton (crichton@gimp.org)
  *
  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
-#include <config.h>
 #include <glib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
 
-#include <mono/metadata/object.h>
-#include <mono/metadata/rand.h>
-#include <mono/metadata/exception.h>
-
-#if !defined(__native_client__) && !defined(HOST_WIN32)
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <errno.h>
-
-static void
-get_entropy_from_server (const char *path, guchar *buf, int len)
-{
-    int file;
-    gint ret;
-    guint offset = 0;
-    struct sockaddr_un egd_addr;
-
-    file = socket (PF_UNIX, SOCK_STREAM, 0);
-    if (file < 0)
-        ret = -1;
-    else {
-        egd_addr.sun_family = AF_UNIX;
-        strncpy (egd_addr.sun_path, path, MONO_SIZEOF_SUNPATH - 1);
-        egd_addr.sun_path [MONO_SIZEOF_SUNPATH-1] = '\0';
-        ret = connect (file, (struct sockaddr *)&egd_addr, sizeof(egd_addr));
-    }
-    if (ret == -1) {
-        if (file >= 0)
-            close (file);
-       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"));
-    }
-
-    while (len > 0) {
-        guchar request[2];
-        gint count = 0;
-
-        request [0] = 2; /* block until daemon can return enough entropy */
-        request [1] = len < 255 ? len : 255;
-        while (count < 2) {
-            int sent = write (file, request + count, 2 - count);
-            if (sent >= 0)
-                count += sent;
-            else if (errno == 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"));
-            }
-        }
-
-        count = 0;
-        while (count != request [1]) {
-            int received;
-            received = read(file, buf + offset, request [1] - count);
-            if (received > 0) {
-                count += received;
-                offset += received;
-            } else if (received < 0 && errno == 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"));
-            }
-        }
-
-        len -= request [1];
-    }
-
-    close (file);
-}
-#endif
-
-#if defined (HOST_WIN32)
-
-#include <windows.h>
-#include <wincrypt.h>
-
-#ifndef PROV_INTEL_SEC
-#define PROV_INTEL_SEC         22
-#endif
-#ifndef CRYPT_VERIFY_CONTEXT
-#define CRYPT_VERIFY_CONTEXT   0xF0000000
-#endif
-
-MonoBoolean
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
-{
-       /* FALSE == Local (instance) handle for randomness */
-       return FALSE;
-}
-
-gpointer
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
-{
-       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)) {
-                       provider = 0;
-                       /* exception will be thrown in managed code */
-               }
-       }
-
-       /* seed the CSP with the supplied buffer (if present) */
-       if ((provider != 0) && (seed)) {
-               guint32 len = mono_array_length (seed);
-               guchar *buf = mono_array_addr (seed, guchar, 0);
-               /* the call we replace the seed with random - this isn't what is
-                  expected from the class library user */
-               guchar *data = g_malloc (len);
-               if (data) {
-                       memcpy (data, buf, len);
-                       /* add seeding material to the RNG */
-                       CryptGenRandom (provider, len, data);
-                       /* zeroize and free */
-                       memset (data, 0, len);
-                       g_free (data);
-               }
-       }
-
-       return (gpointer) provider;     
-}
-
-gpointer
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
-{
-       HCRYPTPROV provider = (HCRYPTPROV) handle;
-       guint32 len = mono_array_length (arry);
-       guchar *buf = mono_array_addr (arry, guchar, 0);
-
-       if (!CryptGenRandom (provider, len, buf)) {
-               CryptReleaseContext (provider, 0);
-               /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
-               provider = (HCRYPTPROV) ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (NULL);
-               if (!CryptGenRandom (provider, len, buf)) {
-                       CryptReleaseContext (provider, 0);
-                       provider = 0;
-                       /* exception will be thrown in managed code */
-               }
-       }
-       return (gpointer) provider;
-}
-
-void
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle) 
-{
-       CryptReleaseContext ((HCRYPTPROV) handle, 0);
-}
-
-#elif defined (__native_client__)
-
-#include <time.h>
+#include "object.h"
+#include "object-internals.h"
+#include "rand.h"
+#include "utils/mono-rand.h"
 
 MonoBoolean
 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
 {
-       srand (time (NULL));
-       return TRUE;
+       return (MonoBoolean) mono_rand_open ();
 }
 
 gpointer
 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
 {
-       return -1;
-}
-
-gpointer 
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
-{      
-       guint32 len = mono_array_length (arry);
-       guchar *buf = mono_array_addr (arry, guchar, 0);
-
-       /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
-       gint count = 0;
-       gint err;
-
-       do {
-               if (len - count >= sizeof (long))
-               {
-                       *(long*)buf = rand();
-                       count += sizeof (long);
-               }
-               else if (len - count >= sizeof (short))
-               {
-                       *(short*)buf = rand();
-                       count += sizeof (short);
-               }
-               else if (len - count >= sizeof (char))
-               {
-                       *buf = rand();
-                       count += sizeof (char);
-               }
-       } while (count < len);
-
-       return (gpointer)-1L;
-}
-
-void
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle) 
-{
-}
-#else
-
-#ifndef NAME_DEV_URANDOM
-#define NAME_DEV_URANDOM "/dev/urandom"
-#endif
-
-static gboolean egd = FALSE;
-static gint file = -1;
-
-MonoBoolean
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
-{
-       if (egd || (file >= 0))
-               return TRUE;
-
-#if defined (NAME_DEV_URANDOM)
-       file = open (NAME_DEV_URANDOM, O_RDONLY);
-#endif
-
-#if defined (NAME_DEV_RANDOM)
-       if (file < 0)
-               file = open (NAME_DEV_RANDOM, O_RDONLY);
-#endif
-
-       if (file < 0) {
-               const char *socket_path = g_getenv("MONO_EGD_SOCKET");
-               egd = (socket_path != NULL);
-       }
-
-       /* TRUE == Global handle for randomness */
-       return TRUE;
+       
+       return mono_rand_init (seed ? mono_array_addr (seed, guchar, 0) : NULL, seed ? mono_array_length (seed) : 0);
 }
 
 gpointer
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
-{
-       /* if required exception will be thrown in managed code */
-       return ((!egd && (file < 0)) ? NULL : GINT_TO_POINTER (file));
-}
-
-gpointer 
 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
 {
-       gint file = GPOINTER_TO_INT (handle);
-       guint32 len = mono_array_length (arry);
-       guchar *buf = mono_array_addr (arry, guchar, 0);
-
-       if (egd) {
-               const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
-               /* exception will be thrown in managed code */
-               if (socket_path == NULL)
-                       return NULL; 
-               get_entropy_from_server (socket_path, mono_array_addr (arry, guchar, 0), mono_array_length (arry));
-               return (gpointer) -1;
-       } else {
-               /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
-               gint count = 0;
-               gint err;
-
-               do {
-                       err = read (file, buf + count, len - count);
-                       if (err < 0) {
-                               if (errno == EINTR)
-                                       continue;
-                               break;
-                       }
-                       count += err;
-               } while (count < len);
-
-               if (err < 0) {
-                       g_warning("Entropy error! Error in read (%s).", strerror (errno));
-                       /* exception will be thrown in managed code */
-                       return NULL;
-               }
-       }
-
-       /* We do not support PRNG seeding right now but the class library is this */
-
-       return handle;  
+       MonoError error;
+       g_assert (arry);
+       mono_rand_try_get_bytes (&handle, mono_array_addr (arry, guchar, 0), mono_array_length (arry), &error);
+       mono_error_set_pending_exception (&error);
+       return handle;
 }
 
 void
-ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle) 
+ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle)
 {
+       mono_rand_close (handle);
 }
-
-#endif /* OS definition */