Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-test
[kai/samba.git] / source / 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$");
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     rk_cloexec(fd);
80
81     if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
82         close(fd);
83         return -1;
84     }
85
86     return fd;
87 }
88
89 static int
90 get_entropy(int fd, void *data, size_t len)
91 {
92     unsigned char msg[2];
93
94     assert(len <= MAX_EGD_DATA);
95
96     msg[0] = 0x02; /* read blocking data */
97     msg[1] = len; /* wanted length */
98
99     if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
100         return 0;
101
102     if (net_read(fd, data, len) != len)
103         return 0;
104
105     return 1;
106 }
107
108 static int
109 put_entropy(int fd, const void *data, size_t len)
110 {
111     unsigned char msg[4];
112
113     assert (len <= MAX_EGD_DATA);
114
115     msg[0] = 0x03; /* write data */
116     msg[1] = 0; /* dummy */
117     msg[2] = 0; /* entropy */
118     msg[3] = len; /* length */
119
120     if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
121         return 0;
122     if (net_write(fd, data, len) != len)
123         return 0;
124
125     return 1;
126 }
127
128 /*
129  *
130  */
131
132 static void
133 egd_seed(const void *indata, int size)
134 {
135     size_t len;
136     int fd, ret = 1;
137
138     fd = connect_egd(egd_path);
139     if (fd < 0)
140         return;
141
142     while(size) {
143         len = size;
144         if (len > MAX_EGD_DATA)
145             len = MAX_EGD_DATA;
146         ret = put_entropy(fd, indata, len);
147         if (ret != 1)
148             break;
149         indata = ((unsigned char *)indata) + len;
150         size -= len;
151     }      
152     close(fd);
153 }
154
155 static int 
156 get_bytes(const char *path, unsigned char *outdata, int size)
157 {
158     size_t len;
159     int fd, ret = 1;
160
161     if (path == NULL)
162         path = egd_path;
163
164     fd = connect_egd(path);
165     if (fd < 0)
166         return 0;
167
168     while(size) {
169         len = size;
170         if (len > MAX_EGD_DATA)
171             len = MAX_EGD_DATA;
172         ret = get_entropy(fd, outdata, len);
173         if (ret != 1)
174             break;
175         outdata += len;
176         size -= len;
177     }      
178     close(fd);
179
180     return ret;
181 }
182
183 static int 
184 egd_bytes(unsigned char *outdata, int size)
185 {
186     return get_bytes(NULL, outdata, size);
187 }
188
189 static void
190 egd_cleanup(void)
191 {
192 }
193
194 static void
195 egd_add(const void *indata, int size, double entropi)
196 {
197     egd_seed(indata, size);
198 }
199
200 static int
201 egd_pseudorand(unsigned char *outdata, int size)
202 {
203     return get_bytes(NULL, outdata, size);
204 }
205
206 static int
207 egd_status(void)
208 {
209     int fd;
210     fd = connect_egd(egd_path);
211     if (fd < 0)
212         return 0;
213     close(fd);
214     return 1;
215 }
216
217 const RAND_METHOD hc_rand_egd_method = {
218     egd_seed,
219     egd_bytes,
220     egd_cleanup,
221     egd_add,
222     egd_pseudorand,
223     egd_status
224 };
225
226 const RAND_METHOD *
227 RAND_egd_method(void)
228 {
229     return &hc_rand_egd_method;
230 }
231
232
233 int
234 RAND_egd(const char *filename)
235 {
236     return RAND_egd_bytes(filename, 128);
237 }
238
239 int
240 RAND_egd_bytes(const char *filename, int size)
241 {
242     void *data;
243     int ret;
244
245     if (size <= 0)
246         return 0;
247
248     data = malloc(size);
249     if (data == NULL)
250         return 0;
251
252     ret = get_bytes(filename, data, size);
253     if (ret != 1) {
254         free(data);
255         return ret;
256     }
257
258     RAND_seed(data, size);
259
260     memset(data, 0, size);
261     free(data);
262
263     return 1;
264 }