2009-12-01 Jb Evain <jbevain@novell.com>
[mono.git] / mono / metadata / rand.c
blob9b657343ca4eb028dfb144777d745d7486479227
1 /*
2 * rand.c: System.Security.Cryptography.RNGCryptoServiceProvider support
4 * Authors:
5 * Mark Crichton (crichton@gimp.org)
6 * Patrik Torstensson (p@rxc.se)
7 * Sebastien Pouliot (sebastien@ximian.com)
9 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
13 #include <config.h>
14 #include <glib.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #ifdef HAVE_STRING_H
22 #include <string.h>
23 #endif
25 #include <mono/metadata/object.h>
26 #include <mono/metadata/rand.h>
27 #include <mono/metadata/exception.h>
29 #if !defined(HOST_WIN32)
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <errno.h>
34 static void
35 get_entropy_from_server (const char *path, guchar *buf, int len)
37 int file;
38 gint ret;
39 guint offset = 0;
40 struct sockaddr_un egd_addr;
42 file = socket (PF_UNIX, SOCK_STREAM, 0);
43 if (file < 0)
44 ret = -1;
45 else {
46 egd_addr.sun_family = AF_UNIX;
47 strncpy (egd_addr.sun_path, path, MONO_SIZEOF_SUNPATH - 1);
48 egd_addr.sun_path [MONO_SIZEOF_SUNPATH-1] = '\0';
49 ret = connect (file, (struct sockaddr *)&egd_addr, sizeof(egd_addr));
51 if (ret == -1) {
52 if (file >= 0)
53 close (file);
54 g_warning ("Entropy problem! Can't create or connect to egd socket %s", path);
55 mono_raise_exception (mono_get_exception_execution_engine ("Failed to open egd socket"));
58 while (len > 0) {
59 guchar request[2];
60 gint count = 0;
62 request [0] = 2; /* block until daemon can return enough entropy */
63 request [1] = len < 255 ? len : 255;
64 while (count < 2) {
65 int sent = write (file, request + count, 2 - count);
66 if (sent >= 0)
67 count += sent;
68 else if (errno == EINTR)
69 continue;
70 else {
71 close (file);
72 g_warning ("Send egd request failed %d", errno);
73 mono_raise_exception (mono_get_exception_execution_engine ("Failed to send request to egd socket"));
77 count = 0;
78 while (count != request [1]) {
79 int received;
80 received = read(file, buf + offset, request [1] - count);
81 if (received > 0) {
82 count += received;
83 offset += received;
84 } else if (received < 0 && errno == EINTR) {
85 continue;
86 } else {
87 close (file);
88 g_warning ("Receive egd request failed %d", errno);
89 mono_raise_exception (mono_get_exception_execution_engine ("Failed to get response from egd socket"));
93 len -= request [1];
96 close (file);
98 #endif
100 #if defined (HOST_WIN32)
102 #include <windows.h>
103 #include <wincrypt.h>
105 #ifndef PROV_INTEL_SEC
106 #define PROV_INTEL_SEC 22
107 #endif
108 #ifndef CRYPT_VERIFY_CONTEXT
109 #define CRYPT_VERIFY_CONTEXT 0xF0000000
110 #endif
112 MonoBoolean
113 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
115 /* FALSE == Local (instance) handle for randomness */
116 return FALSE;
119 gpointer
120 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
122 HCRYPTPROV provider = 0;
124 /* There is no need to create a container for just random data,
125 so we can use CRYPT_VERIFY_CONTEXT (one call) see:
126 http://blogs.msdn.com/dangriff/archive/2003/11/19/51709.aspx */
128 /* We first try to use the Intel PIII RNG if drivers are present */
129 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_INTEL_SEC, CRYPT_VERIFY_CONTEXT)) {
130 /* not a PIII or no drivers available, use default RSA CSP */
131 if (!CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT)) {
132 provider = 0;
133 /* exception will be thrown in managed code */
137 /* seed the CSP with the supplied buffer (if present) */
138 if ((provider != 0) && (seed)) {
139 guint32 len = mono_array_length (seed);
140 guchar *buf = mono_array_addr (seed, guchar, 0);
141 /* the call we replace the seed with random - this isn't what is
142 expected from the class library user */
143 guchar *data = g_malloc (len);
144 if (data) {
145 memcpy (data, buf, len);
146 /* add seeding material to the RNG */
147 CryptGenRandom (provider, len, data);
148 /* zeroize and free */
149 memset (data, 0, len);
150 g_free (data);
154 return (gpointer) provider;
157 gpointer
158 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
160 HCRYPTPROV provider = (HCRYPTPROV) handle;
161 guint32 len = mono_array_length (arry);
162 guchar *buf = mono_array_addr (arry, guchar, 0);
164 if (!CryptGenRandom (provider, len, buf)) {
165 CryptReleaseContext (provider, 0);
166 /* we may have lost our context with CryptoAPI, but all hope isn't lost yet! */
167 provider = (HCRYPTPROV) ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (NULL);
168 if (!CryptGenRandom (provider, len, buf)) {
169 CryptReleaseContext (provider, 0);
170 provider = 0;
171 /* exception will be thrown in managed code */
174 return (gpointer) provider;
177 void
178 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle)
180 CryptReleaseContext ((HCRYPTPROV) handle, 0);
183 #else
185 #ifndef NAME_DEV_URANDOM
186 #define NAME_DEV_URANDOM "/dev/urandom"
187 #endif
189 static gboolean egd = FALSE;
190 static gint file = -1;
192 MonoBoolean
193 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngOpen (void)
195 if (egd || (file >= 0))
196 return TRUE;
198 #if defined (NAME_DEV_URANDOM)
199 file = open (NAME_DEV_URANDOM, O_RDONLY);
200 #endif
202 #if defined (NAME_DEV_RANDOM)
203 if (file < 0)
204 file = open (NAME_DEV_RANDOM, O_RDONLY);
205 #endif
207 if (file < 0) {
208 const char *socket_path = g_getenv("MONO_EGD_SOCKET");
209 egd = (socket_path != NULL);
212 /* TRUE == Global handle for randomness */
213 return TRUE;
216 gpointer
217 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngInitialize (MonoArray *seed)
219 /* if required exception will be thrown in managed code */
220 return ((!egd && (file < 0)) ? NULL : GINT_TO_POINTER (file));
223 gpointer
224 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngGetBytes (gpointer handle, MonoArray *arry)
226 gint file = GPOINTER_TO_INT (handle);
227 guint32 len = mono_array_length (arry);
228 guchar *buf = mono_array_addr (arry, guchar, 0);
230 if (egd) {
231 const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
232 /* exception will be thrown in managed code */
233 if (socket_path == NULL)
234 return NULL;
235 get_entropy_from_server (socket_path, mono_array_addr (arry, guchar, 0), mono_array_length (arry));
236 return (gpointer) -1;
237 } else {
238 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
239 gint count = 0;
240 gint err;
242 do {
243 err = read (file, buf + count, len - count);
244 if (err < 0) {
245 if (errno == EINTR)
246 continue;
247 break;
249 count += err;
250 } while (count < len);
252 if (err < 0) {
253 g_warning("Entropy error! Error in read (%s).", strerror (errno));
254 /* exception will be thrown in managed code */
255 return NULL;
259 /* We do not support PRNG seeding right now but the class library is this */
261 return handle;
264 void
265 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_RngClose (gpointer handle)
269 #endif /* OS definition */