1 /*	$NetBSD: rand-egd.c,v 1.1.1.2 2014/04/24 12:45:30 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <config.h>
37 
38 #include <sys/types.h>
39 #ifdef HAVE_SYS_UN_H
40 #include <sys/un.h>
41 #endif
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 #include <assert.h>
49 
50 #include <rand.h>
51 #include <randi.h>
52 
53 #include <krb5/roken.h>
54 
55 static const char *egd_path = "/var/run/egd-pool";
56 
57 #define MAX_EGD_DATA 255
58 
59 static int
connect_egd(const char * path)60 connect_egd(const char *path)
61 {
62     struct sockaddr_un addr;
63     int fd;
64 
65     memset(&addr, 0, sizeof(addr));
66 
67     if (strlen(path) > sizeof(addr.sun_path))
68 	return -1;
69 
70     addr.sun_family = AF_UNIX;
71     strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
72 
73     fd = socket(AF_UNIX, SOCK_STREAM, 0);
74     if (fd < 0)
75 	return -1;
76 
77     rk_cloexec(fd);
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
get_entropy(int fd,void * data,size_t len)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
put_entropy(int fd,const void * data,size_t len)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
egd_seed(const void * indata,int size)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
get_bytes(const char * path,unsigned char * outdata,int size)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
egd_bytes(unsigned char * outdata,int size)182 egd_bytes(unsigned char *outdata, int size)
183 {
184     return get_bytes(NULL, outdata, size);
185 }
186 
187 static void
egd_cleanup(void)188 egd_cleanup(void)
189 {
190 }
191 
192 static void
egd_add(const void * indata,int size,double entropi)193 egd_add(const void *indata, int size, double entropi)
194 {
195     egd_seed(indata, size);
196 }
197 
198 static int
egd_pseudorand(unsigned char * outdata,int size)199 egd_pseudorand(unsigned char *outdata, int size)
200 {
201     return get_bytes(NULL, outdata, size);
202 }
203 
204 static int
egd_status(void)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 *
RAND_egd_method(void)225 RAND_egd_method(void)
226 {
227     return &hc_rand_egd_method;
228 }
229 
230 
231 int
RAND_egd(const char * filename)232 RAND_egd(const char *filename)
233 {
234     return RAND_egd_bytes(filename, 128);
235 }
236 
237 int
RAND_egd_bytes(const char * filename,int size)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, size);
259     free(data);
260 
261     return 1;
262 }
263