1/*	$NetBSD$	*/
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
55static const char *egd_path = "/var/run/egd-pool";
56
57#define MAX_EGD_DATA 255
58
59static int
60connect_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
87static int
88get_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
106static int
107put_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
130static void
131egd_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
153static int
154get_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
181static int
182egd_bytes(unsigned char *outdata, int size)
183{
184    return get_bytes(NULL, outdata, size);
185}
186
187static void
188egd_cleanup(void)
189{
190}
191
192static void
193egd_add(const void *indata, int size, double entropi)
194{
195    egd_seed(indata, size);
196}
197
198static int
199egd_pseudorand(unsigned char *outdata, int size)
200{
201    return get_bytes(NULL, outdata, size);
202}
203
204static int
205egd_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
215const 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
224const RAND_METHOD *
225RAND_egd_method(void)
226{
227    return &hc_rand_egd_method;
228}
229
230
231int
232RAND_egd(const char *filename)
233{
234    return RAND_egd_bytes(filename, 128);
235}
236
237int
238RAND_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