X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Frand.c;h=28e4b8716022d292b3251a7b774e2b5ccbdf43a8;hb=b50dc8d84850e0452a7a4d742ed0a0fa46fe491e;hp=813f7fde36b35414001115156cb6ea5a05359eb0;hpb=15f43a1b88d55b0eda7a4ee52d0c81ebb7923dcd;p=mono.git diff --git a/mono/metadata/rand.c b/mono/metadata/rand.c index 813f7fde36b..28e4b871602 100644 --- a/mono/metadata/rand.c +++ b/mono/metadata/rand.c @@ -1,16 +1,15 @@ /* * rand.c: System.Security.Cryptography.RNGCryptoServiceProvider support * - * Author: + * Authors: * Mark Crichton (crichton@gimp.org) + * Patrik Torstensson (p@rxc.se) + * Sebastien Pouliot (sebastien@ximian.com) * * (C) 2001 Ximian, Inc. - * + * Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) */ - -/* Ok, the exception handling is bogus. I need to work on that */ - #include #include #include @@ -22,100 +21,244 @@ #include #include -#if defined (NAME_DEV_RANDOM) && defined (HAVE_CRYPT_RNG) - -#ifndef NAME_DEV_URANDOM -#define NAME_DEV_URANDOM "/dev/urandom" -#endif +#if !defined(PLATFORM_WIN32) +#include +#include +#include -void -ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_GetBytes (MonoObject *self, MonoArray *arry) +static void +get_entropy_from_server (const char *path, guchar *buf, int len) { - guint32 len; - gint file; - gint err; - gint count; - guchar *buf; + int file; + gint ret; + guint offset = 0; + struct sockaddr_un egd_addr; - len = mono_array_length(arry); - buf = mono_array_addr(arry, guchar, 0); + 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")); + } - file = open (NAME_DEV_URANDOM, O_RDONLY); + while (len > 0) { + guchar request[2]; + gint count = 0; - if (file < 0) - file = open (NAME_DEV_RANDOM, O_RDONLY); + 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")); + } + } - if (file < 0) { - g_warning ("Entropy problem! Can't open %s or %s", NAME_DEV_URANDOM, NAME_DEV_RANDOM); + 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")); + } + } - /* This needs to be a crypto exception */ - mono_raise_exception(mono_get_exception_not_implemented()); + len -= request [1]; } - /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */ - count = 0; - do { - err = read(file, buf + count, len - count); - count += err; - } while (err >= 0 && count < len); + close (file); +} +#endif - if (err < 0) { - g_warning("Entropy error! Error in read."); - mono_raise_exception(mono_get_exception_not_implemented()); - } +#if defined (PLATFORM_WIN32) + +#include +#include + +#ifndef PROV_INTEL_SEC +#define PROV_INTEL_SEC 22 +#endif +#ifndef CRYPT_VERIFY_CONTEXT +#define CRYPT_VERIFY_CONTEXT 0xF0000000 +#endif - close(file); +MonoBoolean +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void) +{ + /* FALSE == Local (instance) handle for randomness */ + return FALSE; } -void -ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_GetNonZeroBytes (MonoObject *self, MonoArray *arry) +gpointer +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed) { - guint32 len; - gint file, i; - gint err; - guchar byte = 0; + HCRYPTPROV provider = 0; - len = mono_array_length(arry); + /* 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 */ - file = open(NAME_DEV_RANDOM, O_RDONLY); + /* 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 */ + } + } - if (file < 0) { - g_warning("Entropy problem! Can't open %s", NAME_DEV_RANDOM); + /* 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); + } + } - /* This needs to be a crypto exception */ - mono_raise_exception(mono_get_exception_not_implemented()); - } + return (gpointer) provider; +} - for (i = 0; i < len; i++) { +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); - do { - err = read(file, &byte, 1); - } while (byte == 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; +} - if (err < 0) { - g_warning("Entropy error! Error in read."); - mono_raise_exception(mono_get_exception_not_implemented()); - } +void +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle) +{ + CryptReleaseContext ((HCRYPTPROV) handle, 0); +} - mono_array_set(arry, guchar, i, byte); - } +#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 - close(file); +#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; } -/* This needs to change when I do the Win32 support... */ -#else -#warning "No Entropy Source Found" -void ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_GetBytes(MonoObject *self, MonoArray *arry) +gpointer +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed) { - g_warning("0K problem. We have no entropy. Badness will occur."); - mono_raise_exception(mono_get_exception_not_implemented()); + /* if required exception will be thrown in managed code */ + return ((!egd && (file < 0)) ? NULL : GINT_TO_POINTER (file)); } -void ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_GetNonZeroBytes(MonoObject *self, MonoArray *arry) +gpointer +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry) { - g_warning("0K problem. We have no entropy. Badness will occur."); - mono_raise_exception(mono_get_exception_not_implemented()); + 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; } -#endif +void +ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle) +{ +} + +#endif /* OS definition */