f2a3d3fe4a029069f2f6c2e58a2d6f848869da7c
[libgcrypt.git] / cipher / rndegd.c
1 /* rndegd.c  -  interface to the EGD
2  *      Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of Libgcrypt.
5  *
6  * Libgcrypt is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Libgcrypt is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <assert.h>
25 #include <errno.h>
26 #include <sys/time.h>
27 #include <sys/stat.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33 #include "types.h"
34 #include "g10lib.h"
35 #include "dynload.h"
36 #include "cipher.h"
37
38 #ifndef offsetof
39 #define offsetof(type, member) ((size_t) &((type *)0)->member)
40 #endif
41
42
43 /* FIXME: this is duplicated code from util/fileutil
44  * I don't think that this code should go into libgcrypt anyway.
45  */
46 char *
47 my_make_filename( const char *first_part, ... )
48 {
49     va_list arg_ptr ;
50     size_t n;
51     const char *s;
52     char *name, *home, *p;
53
54     va_start( arg_ptr, first_part ) ;
55     n = strlen(first_part)+1;
56     while( (s=va_arg(arg_ptr, const char *)) )
57         n += strlen(s) + 1;
58     va_end(arg_ptr);
59
60     home = NULL;
61     if( *first_part == '~' && first_part[1] == '/'
62                            && (home = getenv("HOME")) && *home )
63         n += strlen(home);
64
65     name = gcry_malloc(n);
66     p = home ? stpcpy(stpcpy(name,home), first_part+1)
67              : stpcpy(name, first_part);
68     va_start( arg_ptr, first_part ) ;
69     while( (s=va_arg(arg_ptr, const char *)) )
70         p = stpcpy(stpcpy(p,"/"), s);
71     va_end(arg_ptr);
72
73     return name;
74 }
75
76
77
78
79
80 static int
81 do_write( int fd, void *buf, size_t nbytes )
82 {
83     size_t nleft = nbytes;
84     int nwritten;
85
86     while( nleft > 0 ) {
87         nwritten = write( fd, buf, nleft);
88         if( nwritten < 0 ) {
89             if( errno == EINTR )
90                 continue;
91             return -1;
92         }
93         nleft -= nwritten;
94         buf = (char*)buf + nwritten;
95     }
96     return 0;
97 }
98
99 static int
100 do_read( int fd, void *buf, size_t nbytes )
101 {
102     int n, nread = 0;
103
104     do {
105         do {
106             n = read(fd, (char*)buf + nread, nbytes );
107         } while( n == -1 && errno == EINTR );
108         if( n == -1 )
109             return -1;
110         nread += n;
111     } while( nread < nbytes );
112     return nbytes;
113 }
114
115
116
117 /****************
118  * Note: we always use the highest level.
119  * TO boost the performance we may want to add some
120  * additional code for level 1
121  *
122  * Using a level of 0 should never block and better add nothing
123  * to the pool.  So this is just a dummy for EGD.
124  */
125 static int
126 gather_random( void (*add)(const void*, size_t, int), int requester,
127                                           size_t length, int level )
128 {
129     static int fd = -1;
130     int n;
131     byte buffer[256+2];
132     int nbytes;
133     int do_restart = 0;
134
135     if( !length )
136         return 0;
137     if( !level )
138         return 0;
139
140   restart:
141     if( do_restart ) {
142         if( fd != -1 ) {
143             close( fd );
144             fd = -1;
145         }
146     }
147     if( fd == -1 ) {
148 #if __GNUC__ >= 2
149         #warning Fixme: make the filename configurable
150 #endif
151         char *name = my_make_filename( "~/.gnupg-test", "entropy", NULL );
152         struct sockaddr_un addr;
153         int addr_len;
154
155         memset( &addr, 0, sizeof addr );
156         addr.sun_family = AF_UNIX;
157         strcpy( addr.sun_path, name );    /* fixme: check that it is long enough */
158         addr_len = offsetof( struct sockaddr_un, sun_path )
159                    + strlen( addr.sun_path );
160
161         fd = socket(AF_UNIX, SOCK_STREAM, 0);
162         if( fd == -1 )
163             log_fatal("can't create unix domain socket: %s\n",
164                                                             strerror(errno) );
165         if( connect( fd, (struct sockaddr*)&addr, addr_len) == -1 )
166             log_fatal("can't connect to `%s': %s\n",
167                                                     name, strerror(errno) );
168         gcry_free(name);
169     }
170     do_restart = 0;
171
172     nbytes = length < 255? length : 255;
173     /* first time we do it with a non blocking request */
174     buffer[0] = 1; /* non blocking */
175     buffer[1] = nbytes;
176     if( do_write( fd, buffer, 2 ) == -1 )
177         log_fatal("can't write to the EGD: %s\n", strerror(errno) );
178     n = do_read( fd, buffer, 1 );
179     if( n == -1 ) {
180         log_error("read error on EGD: %s\n", strerror(errno));
181         do_restart = 1;
182         goto restart;
183     }
184     n = buffer[0];
185     if( n ) {
186         n = do_read( fd, buffer, n );
187         if( n == -1 ) {
188             log_error("read error on EGD: %s\n", strerror(errno));
189             do_restart = 1;
190             goto restart;
191         }
192         (*add)( buffer, n, requester );
193         length -= n;
194     }
195
196     if( length ) {
197         log_info (
198          _("Please wait, entropy is being gathered. Do some work if it would\n"
199            "keep you from getting bored, because it will improve the quality\n"
200            "of the entropy.\n") );
201     }
202     while( length ) {
203         nbytes = length < 255? length : 255;
204
205         buffer[0] = 2; /* blocking */
206         buffer[1] = nbytes;
207         if( do_write( fd, buffer, 2 ) == -1 )
208             log_fatal("can't write to the EGD: %s\n", strerror(errno) );
209         n = do_read( fd, buffer, nbytes );
210         if( n == -1 ) {
211             log_error("read error on EGD: %s\n", strerror(errno));
212             do_restart = 1;
213             goto restart;
214         }
215         (*add)( buffer, n, requester );
216         length -= n;
217     }
218     memset(buffer, 0, sizeof(buffer) );
219
220     return 0; /* success */
221 }
222
223
224
225 #ifndef IS_MODULE
226 static
227 #endif
228 const char * const gnupgext_version = "RNDEGD ($Revision$)";
229
230 static struct {
231     int class;
232     int version;
233     void *func;
234 } func_table[] = {
235     { 40, 1, gather_random },
236 };
237
238
239 #ifndef IS_MODULE
240 static
241 #endif
242 void *
243 gnupgext_enum_func( int what, int *sequence, int *class, int *vers )
244 {
245     void *ret;
246     int i = *sequence;
247
248     do {
249         if ( i >= DIM(func_table) || i < 0 ) {
250             return NULL;
251         }
252         *class = func_table[i].class;
253         *vers  = func_table[i].version;
254         ret = func_table[i].func;
255         i++;
256     } while ( what && what != *class );
257
258     *sequence = i;
259     return ret;
260 }
261
262 #ifndef IS_MODULE
263 void
264 _gcry_rndegd_constructor(void)
265 {
266   _gcry_register_internal_cipher_extension (gnupgext_version,
267                                             gnupgext_enum_func);
268 }
269 #endif
270
271
272
273
274
275
276
277
278
279
280