Modify access to EGD socket to deal with EINTR etc that can appear
[openssl.git] / crypto / rand / rand_egd.c
1 /* crypto/rand/rand_egd.c */
2 /* Written by Ulf Moeller and Lutz Jaenicke for the OpenSSL project. */
3 /* ====================================================================
4  * Copyright (c) 1998-2000 The OpenSSL Project.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * 3. All advertising materials mentioning features or use of this
19  *    software must display the following acknowledgment:
20  *    "This product includes software developed by the OpenSSL Project
21  *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
22  *
23  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
24  *    endorse or promote products derived from this software without
25  *    prior written permission. For written permission, please contact
26  *    openssl-core@openssl.org.
27  *
28  * 5. Products derived from this software may not be called "OpenSSL"
29  *    nor may "OpenSSL" appear in their names without prior written
30  *    permission of the OpenSSL Project.
31  *
32  * 6. Redistributions of any form whatsoever must retain the following
33  *    acknowledgment:
34  *    "This product includes software developed by the OpenSSL Project
35  *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
38  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
40  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
43  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
46  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
48  * OF THE POSSIBILITY OF SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This product includes cryptographic software written by Eric Young
52  * (eay@cryptsoft.com).  This product includes software written by Tim
53  * Hudson (tjh@cryptsoft.com).
54  *
55  */
56
57 #include <openssl/rand.h>
58
59 /*
60  * Query the EGD <URL: http://www.lothar.com/tech/crypto/>.
61  *
62  * This module supplies three routines:
63  *
64  * RAND_query_egd_bytes(path, buf, bytes)
65  *   will actually query "bytes" bytes of entropy form the egd-socket located
66  *   at path and will write them to buf (if supplied) or will directly feed
67  *   it to RAND_seed() if buf==NULL.
68  *   The number of bytes is not limited by the maximum chunk size of EGD,
69  *   which is 255 bytes. If more than 255 bytes are wanted, several chunks
70  *   of entropy bytes are requested. The connection is left open until the
71  *   query is competed.
72  *   RAND_query_egd_bytes() returns with
73  *     -1  if an error occured during connection or communication.
74  *     num the number of bytes read from the EGD socket. This number is either
75  *         the number of bytes requested or smaller, if the EGD pool is
76  *         drained and the daemon signals that the pool is empty.
77  *   This routine does not touch any RAND_status(). This is necessary, since
78  *   PRNG functions may call it during initialization.
79  *
80  * RAND_egd_bytes(path, bytes) will query "bytes" bytes and have them
81  *   used to seed the PRNG.
82  *   RAND_egd_bytes() is a wrapper for RAND_query_egd_bytes() with buf=NULL.
83  *   Unlike RAND_query_egd_bytes(), RAND_status() is used to test the
84  *   seed status so that the return value can reflect the seed state:
85  *     -1  if an error occured during connection or communication _or_
86  *         if the PRNG has still not received the required seeding.
87  *     num the number of bytes read from the EGD socket. This number is either
88  *         the number of bytes requested or smaller, if the EGD pool is
89  *         drained and the daemon signals that the pool is empty.
90  *
91  * RAND_egd(path) will query 255 bytes and use the bytes retreived to seed
92  *   the PRNG.
93  *   RAND_egd() is a wrapper for RAND_egd_bytes() with numbytes=255.
94  */
95
96 #if defined(WIN32) || defined(VMS) || defined(__VMS)
97 int RAND_query_egd_bytes(const char *path, unsigned char *buf, int bytes)
98         {
99         return(-1);
100         }
101 int RAND_egd(const char *path)
102         {
103         return(-1);
104         }
105
106 int RAND_egd_bytes(const char *path,int bytes)
107         {
108         return(-1);
109         }
110 #else
111 #include <openssl/opensslconf.h>
112 #include OPENSSL_UNISTD
113 #include <sys/types.h>
114 #include <sys/socket.h>
115 #include <sys/un.h>
116 #include <string.h>
117 #include <errno.h>
118
119 #ifndef offsetof
120 #  define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
121 #endif
122
123 int RAND_query_egd_bytes(const char *path, unsigned char *buf, int bytes)
124         {
125         int ret = 0;
126         struct sockaddr_un addr;
127         int len, num, numbytes;
128         int fd = -1;
129         int success;
130         unsigned char egdbuf[2], tempbuf[255], *retrievebuf;
131
132         memset(&addr, 0, sizeof(addr));
133         addr.sun_family = AF_UNIX;
134         if (strlen(path) > sizeof(addr.sun_path))
135                 return (-1);
136         strcpy(addr.sun_path,path);
137         len = offsetof(struct sockaddr_un, sun_path) + strlen(path);
138         fd = socket(AF_UNIX, SOCK_STREAM, 0);
139         if (fd == -1) return (-1);
140         success = 0;
141         while (!success)
142             {
143             if (connect(fd, (struct sockaddr *)&addr, len) == 0)
144                success = 1;
145             else
146                 {
147                 switch (errno)
148                     {
149 #ifdef EINTR
150                     case EINTR:
151 #endif
152 #ifdef EAGAIN
153                     case EAGAIN:
154 #endif
155 #ifdef EINPROGRESS
156                     case EINPROGRESS:
157 #endif
158 #ifdef EALREADY
159                     case EALREADY:
160 #endif
161                         /* No error, try again */
162                         break;
163 #ifdef EISCONN
164                     case EISCONN:
165                         success = 1;
166                         break;
167 #endif
168                     default:
169                         goto err;       /* failure */
170                     }
171                 }
172             }
173
174         while(bytes > 0)
175             {
176             egdbuf[0] = 1;
177             egdbuf[1] = bytes < 255 ? bytes : 255;
178             numbytes = 0;
179             while (numbytes != 2)
180                 {
181                 num = write(fd, egdbuf + numbytes, 2 - numbytes);
182                 if (num >= 0)
183                     numbytes += num;
184                 else
185                     {
186                     switch (errno)
187                         {
188 #ifdef EINTR
189                         case EINTR:
190 #endif
191 #ifdef EAGAIN
192                         case EAGAIN:
193 #endif
194                             /* No error, try again */
195                             break;
196                         default:
197                             ret = -1;
198                             goto err;   /* failure */
199                         }
200                     }
201                 }
202             numbytes = 0;
203             while (numbytes != 1)
204                 {
205                 num = read(fd, egdbuf, 1);
206                 if (num >= 0)
207                     numbytes += num;
208                 else
209                     {
210                     switch (errno)
211                         {
212 #ifdef EINTR
213                         case EINTR:
214 #endif
215 #ifdef EAGAIN
216                         case EAGAIN:
217 #endif
218                             /* No error, try again */
219                             break;
220                         default:
221                             ret = -1;
222                             goto err;   /* failure */
223                         }
224                     }
225                 }
226             if(egdbuf[0] == 0)
227                 goto err;
228             if (buf)
229                 retrievebuf = buf + ret;
230             else
231                 retrievebuf = tempbuf;
232             numbytes = 0;
233             while (numbytes != egdbuf[0])
234                 {
235                 num = read(fd, retrievebuf + numbytes, egdbuf[0] - numbytes);
236                 if (num >= 0)
237                     numbytes += num;
238                 else
239                     {
240                     switch (errno)
241                         {
242 #ifdef EINTR
243                         case EINTR:
244 #endif
245 #ifdef EAGAIN
246                         case EAGAIN:
247 #endif
248                             /* No error, try again */
249                             break;
250                         default:
251                             ret = -1;
252                             goto err;   /* failure */
253                         }
254                     }
255                 }
256             ret += egdbuf[0];
257             bytes -= egdbuf[0];
258             if (!buf)
259                 RAND_seed(tempbuf, egdbuf[0]);
260             }
261  err:
262         if (fd != -1) close(fd);
263         return(ret);
264         }
265
266
267 int RAND_egd_bytes(const char *path, int bytes)
268         {
269         int num, ret = 0;
270
271         num = RAND_query_egd_bytes(path, NULL, bytes);
272         if (num < 1) goto err;
273         if (RAND_status() == 1)
274             ret = num;
275  err:
276         return(ret);
277         }
278
279
280 int RAND_egd(const char *path)
281         {
282         return (RAND_egd_bytes(path, 255));
283         }
284
285
286 #endif