r23456: Update Samba4 to current lorikeet-heimdal.
[sfrench/samba-autobuild/.git] / source4 / heimdal / lib / hcrypto / rand-egd.c
1 /*
2  * Copyright (c) 2007 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * 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 the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 RCSID("$Id: rand-egd.c 20093 2007-01-31 12:44:28Z lha $");
39
40 #include <sys/types.h>
41 #ifdef HAVE_SYS_UN_H
42 #include <sys/un.h>
43 #endif
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #include <assert.h>
51
52 #include <rand.h>
53 #include <randi.h>
54
55 #include <roken.h>
56
57 static const char *egd_path = "/var/run/egd-pool";
58
59 #define MAX_EGD_DATA 255
60
61 static int
62 connect_egd(const char *path)
63 {
64     struct sockaddr_un addr;
65     int fd;
66     
67     memset(&addr, 0, sizeof(addr));
68
69     if (strlen(path) > sizeof(addr.sun_path))
70         return -1;
71
72     addr.sun_family = AF_UNIX;
73     strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
74
75     fd = socket(AF_UNIX, SOCK_STREAM, 0);
76     if (fd < 0)
77         return -1;
78
79     if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
80         close(fd);
81         return -1;
82     }
83
84     return fd;
85 }
86
87 static int
88 get_entropy(int fd, void *data, size_t len)
89 {
90     unsigned char msg[2];
91
92     assert(len <= MAX_EGD_DATA);
93
94     msg[0] = 0x02; /* read blocking data */
95     msg[1] = len; /* wanted length */
96
97     if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
98         return 0;
99
100     if (net_read(fd, data, len) != len)
101         return 0;
102
103     return 1;
104 }
105
106 static int
107 put_entropy(int fd, const void *data, size_t len)
108 {
109     unsigned char msg[4];
110
111     assert (len <= MAX_EGD_DATA);
112
113     msg[0] = 0x03; /* write data */
114     msg[1] = 0; /* dummy */
115     msg[2] = 0; /* entropy */
116     msg[3] = len; /* length */
117
118     if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
119         return 0;
120     if (net_write(fd, data, len) != len)
121         return 0;
122
123     return 1;
124 }
125
126 /*
127  *
128  */
129
130 static void
131 egd_seed(const void *indata, int size)
132 {
133     size_t len;
134     int fd, ret = 1;
135
136     fd = connect_egd(egd_path);
137     if (fd < 0)
138         return;
139
140     while(size) {
141         len = size;
142         if (len > MAX_EGD_DATA)
143             len = MAX_EGD_DATA;
144         ret = put_entropy(fd, indata, len);
145         if (ret != 1)
146             break;
147         indata = ((unsigned char *)indata) + len;
148         size -= len;
149     }      
150     close(fd);
151 }
152
153 static int 
154 get_bytes(const char *path, unsigned char *outdata, int size)
155 {
156     size_t len;
157     int fd, ret = 1;
158
159     if (path == NULL)
160         path = egd_path;
161
162     fd = connect_egd(path);
163     if (fd < 0)
164         return 0;
165
166     while(size) {
167         len = size;
168         if (len > MAX_EGD_DATA)
169             len = MAX_EGD_DATA;
170         ret = get_entropy(fd, outdata, len);
171         if (ret != 1)
172             break;
173         outdata += len;
174         size -= len;
175     }      
176     close(fd);
177
178     return ret;
179 }
180
181 static int 
182 egd_bytes(unsigned char *outdata, int size)
183 {
184     return get_bytes(NULL, outdata, size);
185 }
186
187 static void
188 egd_cleanup(void)
189 {
190 }
191
192 static void
193 egd_add(const void *indata, int size, double entropi)
194 {
195     egd_seed(indata, size);
196 }
197
198 static int
199 egd_pseudorand(unsigned char *outdata, int size)
200 {
201     return get_bytes(NULL, outdata, size);
202 }
203
204 static int
205 egd_status(void)
206 {
207     int fd;
208     fd = connect_egd(egd_path);
209     if (fd < 0)
210         return 0;
211     close(fd);
212     return 1;
213 }
214
215 const RAND_METHOD hc_rand_egd_method = {
216     egd_seed,
217     egd_bytes,
218     egd_cleanup,
219     egd_add,
220     egd_pseudorand,
221     egd_status
222 };
223
224 const RAND_METHOD *
225 RAND_egd_method(void)
226 {
227     return &hc_rand_egd_method;
228 }
229
230
231 int
232 RAND_egd(const char *filename)
233 {
234     return RAND_egd_bytes(filename, 128);
235 }
236
237 int
238 RAND_egd_bytes(const char *filename, int size)
239 {
240     void *data;
241     int ret;
242
243     if (size <= 0)
244         return 0;
245
246     data = malloc(size);
247     if (data == NULL)
248         return 0;
249
250     ret = get_bytes(filename, data, size);
251     if (ret != 1) {
252         free(data);
253         return ret;
254     }
255
256     RAND_seed(data, size);
257
258     memset(data, 0, sizeof(data));
259     free(data);
260
261     return 1;
262 }