Fix potential leak
[mono.git] / mono / metadata / rand.c
1 /*
2  * rand.c: System.Security.Cryptography.RNGCryptoServiceProvider support
3  *
4  * Author:
5  *      Mark Crichton (crichton@gimp.org)
6  *      Patrik Torstensson (p@rxc.se)
7  *
8  * (C) 2001 Ximian, Inc.
9  *
10  */
11
12 #include <config.h>
13 #include <glib.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18
19 #include <mono/metadata/object.h>
20 #include <mono/metadata/rand.h>
21 #include <mono/metadata/exception.h>
22
23 #if !defined(PLATFORM_WIN32)
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <errno.h>
27
28 static void
29 get_entropy_from_server (const char *path, guchar *buf, int len)
30 {
31     int file;
32     gint ret;
33     guint offset = 0;
34     struct sockaddr_un egd_addr;
35
36     file = socket (PF_UNIX, SOCK_STREAM, 0);
37     if (file < 0)
38         ret = -1;
39     else {
40         egd_addr.sun_family = AF_UNIX;
41         strncpy (egd_addr.sun_path, path, MONO_SIZEOF_SUNPATH - 1);
42         egd_addr.sun_path [MONO_SIZEOF_SUNPATH-1] = '\0';
43         ret = connect (file, (struct sockaddr *)&egd_addr, sizeof(egd_addr));
44     }
45     if (ret == -1) {
46         if (file >= 0)
47             close (file);
48         g_warning ("Entropy problem! Can't create or connect to egd socket %s", path);
49         mono_raise_exception (mono_get_exception_execution_engine ("Failed to open egd socket"));
50     }
51
52     while (len > 0) {
53         guchar request[2];
54         gint count = 0;
55
56         request [0] = 2; /* block until daemon can return enough entropy */
57         request [1] = len < 255 ? len : 255;
58         while (count < 2) {
59             int sent = write (file, request + count, 2 - count);
60             if (sent >= 0)
61                 count += sent;
62             else if (errno == EINTR)
63                 continue;
64             else {
65                 close (file);
66                 g_warning ("Send egd request failed %d", errno);
67                 mono_raise_exception (mono_get_exception_execution_engine ("Failed to send request to egd socket"));
68             }
69         }
70
71         count = 0;
72         while (count != request [1]) {
73             int received;
74             received = read(file, buf + offset, request [1] - count);
75             if (received > 0) {
76                 count += received;
77                 offset += received;
78             } else if (received < 0 && errno == EINTR) {
79                 continue;
80             } else {
81                 close (file);
82                 g_warning ("Receive egd request failed %d", errno);
83                 mono_raise_exception (mono_get_exception_execution_engine ("Failed to get response from egd socket"));
84             }
85         }
86
87         len -= request [1];
88     }
89
90     close (file);
91 }
92 #endif
93
94 #if defined (PLATFORM_WIN32)
95
96 #include <WinCrypt.h>
97 \r
98 static int s_providerInitialized = 0;\r
99 static HCRYPTPROV s_provider;\r
100
101 static HCRYPTPROV GetProvider()\r
102 {\r
103     if (s_providerInitialized == 1)\r
104         return s_provider;\r
105 \r
106     if (!CryptAcquireContext (&s_provider, NULL, NULL, PROV_RSA_FULL, 0))  \r
107     {\r
108         if (GetLastError() != NTE_BAD_KEYSET)\r
109             mono_raise_exception (mono_get_exception_execution_engine ("Failed to acquire crypt context"));\r
110 \r
111                 // Generate a new keyset if needed\r
112         if (!CryptAcquireContext (&s_provider, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))\r
113             mono_raise_exception (mono_get_exception_execution_engine ("Failed to acquire crypt context (new keyset)"));\r
114     }\r
115     \r
116     s_providerInitialized =  1;\r
117 \r
118     return s_provider;\r
119 }\r
120
121 void ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_InternalGetBytes(MonoObject *self, MonoArray *arry)
122 {
123     guint32 len;
124     guchar *buf;
125
126     len = mono_array_length (arry);
127     buf = mono_array_addr (arry, guchar, 0);
128
129     if (0 == CryptGenRandom (GetProvider(), len, buf))
130        mono_raise_exception (mono_get_exception_execution_engine ("Failed to generate random bytes from CryptoAPI"));\r
131 }
132
133 #else
134
135 #ifndef NAME_DEV_URANDOM
136 #define NAME_DEV_URANDOM "/dev/urandom"
137 #endif
138
139 void 
140 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_InternalGetBytes (MonoObject *self, MonoArray *arry)
141 {
142     guint32 len;
143     gint file = -1;
144     gint err;
145     gint count;
146     guchar *buf;
147
148     len = mono_array_length(arry);
149     buf = mono_array_addr(arry, guchar, 0);
150
151 #if defined (NAME_DEV_URANDOM)
152     file = open (NAME_DEV_URANDOM, O_RDONLY);
153 #endif
154
155 #if defined (NAME_DEV_RANDOM)
156     if (file < 0)
157             file = open (NAME_DEV_RANDOM, O_RDONLY);
158 #endif
159
160     if (file < 0) {
161         const char *socket_path = getenv("MONO_EGD_SOCKET");
162
163         if (socket_path == NULL)
164             mono_raise_exception (mono_get_exception_execution_engine ("Failed to open /dev/urandom or /dev/random device, or find egd socket"));
165
166         get_entropy_from_server (socket_path, mono_array_addr(arry, guchar, 0), mono_array_length(arry));
167         return;
168     }
169
170     /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
171     count = 0;
172     do {
173             err = read(file, buf + count, len - count);
174             count += err;
175     } while (err >= 0 && count < len);
176     close(file);
177
178     if (err < 0) {
179         g_warning("Entropy error! Error in read (%s).", strerror (errno));
180         mono_raise_exception (mono_get_exception_execution_engine ("Failed to read a random byte from /dev/urandom or /dev/random device"));\r
181     }
182
183 }
184
185 #endif /* OS definition */
186
187 void
188 ves_icall_System_Security_Cryptography_RNGCryptoServiceProvider_Seed (MonoArray *seed)
189 {
190         /* actually we do not support any PRNG requiring seeding right now but
191         the class library is ready for such possibility - so this empty 
192         function is needed (e.g. a new or modified runtime) */
193 }
194