s4:heimdal: import lorikeet-heimdal-200908052208 (commit 370a73a74199a5a55188340906e1...
[ira/wip.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 #include <config.h>
35
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_UN_H
38 #include <sys/un.h>
39 #endif
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46 #include <assert.h>
47
48 #include <rand.h>
49 #include <randi.h>
50
51 #include <roken.h>
52
53 static const char *egd_path = "/var/run/egd-pool";
54
55 #define MAX_EGD_DATA 255
56
57 static int
58 connect_egd(const char *path)
59 {
60     struct sockaddr_un addr;
61     int fd;
62
63     memset(&addr, 0, sizeof(addr));
64
65     if (strlen(path) > sizeof(addr.sun_path))
66         return -1;
67
68     addr.sun_family = AF_UNIX;
69     strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
70
71     fd = socket(AF_UNIX, SOCK_STREAM, 0);
72     if (fd < 0)
73         return -1;
74
75     rk_cloexec(fd);
76
77     if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
78         close(fd);
79         return -1;
80     }
81
82     return fd;
83 }
84
85 static int
86 get_entropy(int fd, void *data, size_t len)
87 {
88     unsigned char msg[2];
89
90     assert(len <= MAX_EGD_DATA);
91
92     msg[0] = 0x02; /* read blocking data */
93     msg[1] = len; /* wanted length */
94
95     if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
96         return 0;
97
98     if (net_read(fd, data, len) != len)
99         return 0;
100
101     return 1;
102 }
103
104 static int
105 put_entropy(int fd, const void *data, size_t len)
106 {
107     unsigned char msg[4];
108
109     assert (len <= MAX_EGD_DATA);
110
111     msg[0] = 0x03; /* write data */
112     msg[1] = 0; /* dummy */
113     msg[2] = 0; /* entropy */
114     msg[3] = len; /* length */
115
116     if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
117         return 0;
118     if (net_write(fd, data, len) != len)
119         return 0;
120
121     return 1;
122 }
123
124 /*
125  *
126  */
127
128 static void
129 egd_seed(const void *indata, int size)
130 {
131     size_t len;
132     int fd, ret = 1;
133
134     fd = connect_egd(egd_path);
135     if (fd < 0)
136         return;
137
138     while(size) {
139         len = size;
140         if (len > MAX_EGD_DATA)
141             len = MAX_EGD_DATA;
142         ret = put_entropy(fd, indata, len);
143         if (ret != 1)
144             break;
145         indata = ((unsigned char *)indata) + len;
146         size -= len;
147     }   
148     close(fd);
149 }
150
151 static int
152 get_bytes(const char *path, unsigned char *outdata, int size)
153 {
154     size_t len;
155     int fd, ret = 1;
156
157     if (path == NULL)
158         path = egd_path;
159
160     fd = connect_egd(path);
161     if (fd < 0)
162         return 0;
163
164     while(size) {
165         len = size;
166         if (len > MAX_EGD_DATA)
167             len = MAX_EGD_DATA;
168         ret = get_entropy(fd, outdata, len);
169         if (ret != 1)
170             break;
171         outdata += len;
172         size -= len;
173     }   
174     close(fd);
175
176     return ret;
177 }
178
179 static int
180 egd_bytes(unsigned char *outdata, int size)
181 {
182     return get_bytes(NULL, outdata, size);
183 }
184
185 static void
186 egd_cleanup(void)
187 {
188 }
189
190 static void
191 egd_add(const void *indata, int size, double entropi)
192 {
193     egd_seed(indata, size);
194 }
195
196 static int
197 egd_pseudorand(unsigned char *outdata, int size)
198 {
199     return get_bytes(NULL, outdata, size);
200 }
201
202 static int
203 egd_status(void)
204 {
205     int fd;
206     fd = connect_egd(egd_path);
207     if (fd < 0)
208         return 0;
209     close(fd);
210     return 1;
211 }
212
213 const RAND_METHOD hc_rand_egd_method = {
214     egd_seed,
215     egd_bytes,
216     egd_cleanup,
217     egd_add,
218     egd_pseudorand,
219     egd_status
220 };
221
222 const RAND_METHOD *
223 RAND_egd_method(void)
224 {
225     return &hc_rand_egd_method;
226 }
227
228
229 int
230 RAND_egd(const char *filename)
231 {
232     return RAND_egd_bytes(filename, 128);
233 }
234
235 int
236 RAND_egd_bytes(const char *filename, int size)
237 {
238     void *data;
239     int ret;
240
241     if (size <= 0)
242         return 0;
243
244     data = malloc(size);
245     if (data == NULL)
246         return 0;
247
248     ret = get_bytes(filename, data, size);
249     if (ret != 1) {
250         free(data);
251         return ret;
252     }
253
254     RAND_seed(data, size);
255
256     memset(data, 0, size);
257     free(data);
258
259     return 1;
260 }