2d6d3ec5251c70c957b8f364ec8919d56cd73cf3
[dragonfly.git] / crypto / openssh / entropy.c
1 /*
2  * Copyright (c) 2001 Damien Miller.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "includes.h"
26
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #ifdef HAVE_SYS_UN_H
30 # include <sys/un.h>
31 #endif
32
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35
36 #include <errno.h>
37 #include <signal.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <stddef.h> /* for offsetof */
41
42 #include <openssl/rand.h>
43 #include <openssl/crypto.h>
44 #include <openssl/err.h>
45
46 #include "ssh.h"
47 #include "misc.h"
48 #include "xmalloc.h"
49 #include "atomicio.h"
50 #include "pathnames.h"
51 #include "log.h"
52 #include "buffer.h"
53
54 /*
55  * Portable OpenSSH PRNG seeding:
56  * If OpenSSL has not "internally seeded" itself (e.g. pulled data from
57  * /dev/random), then collect RANDOM_SEED_SIZE bytes of randomness from
58  * PRNGd.
59  */
60 #ifndef OPENSSL_PRNG_ONLY
61
62 #define RANDOM_SEED_SIZE 48
63
64 /*
65  * Collect 'len' bytes of entropy into 'buf' from PRNGD/EGD daemon
66  * listening either on 'tcp_port', or via Unix domain socket at *
67  * 'socket_path'.
68  * Either a non-zero tcp_port or a non-null socket_path must be
69  * supplied.
70  * Returns 0 on success, -1 on error
71  */
72 int
73 get_random_bytes_prngd(unsigned char *buf, int len,
74     unsigned short tcp_port, char *socket_path)
75 {
76         int fd, addr_len, rval, errors;
77         u_char msg[2];
78         struct sockaddr_storage addr;
79         struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr;
80         struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr;
81         mysig_t old_sigpipe;
82
83         /* Sanity checks */
84         if (socket_path == NULL && tcp_port == 0)
85                 fatal("You must specify a port or a socket");
86         if (socket_path != NULL &&
87             strlen(socket_path) >= sizeof(addr_un->sun_path))
88                 fatal("Random pool path is too long");
89         if (len <= 0 || len > 255)
90                 fatal("Too many bytes (%d) to read from PRNGD", len);
91
92         memset(&addr, '\0', sizeof(addr));
93
94         if (tcp_port != 0) {
95                 addr_in->sin_family = AF_INET;
96                 addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
97                 addr_in->sin_port = htons(tcp_port);
98                 addr_len = sizeof(*addr_in);
99         } else {
100                 addr_un->sun_family = AF_UNIX;
101                 strlcpy(addr_un->sun_path, socket_path,
102                     sizeof(addr_un->sun_path));
103                 addr_len = offsetof(struct sockaddr_un, sun_path) +
104                     strlen(socket_path) + 1;
105         }
106
107         old_sigpipe = mysignal(SIGPIPE, SIG_IGN);
108
109         errors = 0;
110         rval = -1;
111 reopen:
112         fd = socket(addr.ss_family, SOCK_STREAM, 0);
113         if (fd == -1) {
114                 error("Couldn't create socket: %s", strerror(errno));
115                 goto done;
116         }
117
118         if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) {
119                 if (tcp_port != 0) {
120                         error("Couldn't connect to PRNGD port %d: %s",
121                             tcp_port, strerror(errno));
122                 } else {
123                         error("Couldn't connect to PRNGD socket \"%s\": %s",
124                             addr_un->sun_path, strerror(errno));
125                 }
126                 goto done;
127         }
128
129         /* Send blocking read request to PRNGD */
130         msg[0] = 0x02;
131         msg[1] = len;
132
133         if (atomicio(vwrite, fd, msg, sizeof(msg)) != sizeof(msg)) {
134                 if (errno == EPIPE && errors < 10) {
135                         close(fd);
136                         errors++;
137                         goto reopen;
138                 }
139                 error("Couldn't write to PRNGD socket: %s",
140                     strerror(errno));
141                 goto done;
142         }
143
144         if (atomicio(read, fd, buf, len) != (size_t)len) {
145                 if (errno == EPIPE && errors < 10) {
146                         close(fd);
147                         errors++;
148                         goto reopen;
149                 }
150                 error("Couldn't read from PRNGD socket: %s",
151                     strerror(errno));
152                 goto done;
153         }
154
155         rval = 0;
156 done:
157         mysignal(SIGPIPE, old_sigpipe);
158         if (fd != -1)
159                 close(fd);
160         return rval;
161 }
162
163 static int
164 seed_from_prngd(unsigned char *buf, size_t bytes)
165 {
166 #ifdef PRNGD_PORT
167         debug("trying egd/prngd port %d", PRNGD_PORT);
168         if (get_random_bytes_prngd(buf, bytes, PRNGD_PORT, NULL) == 0)
169                 return 0;
170 #endif
171 #ifdef PRNGD_SOCKET
172         debug("trying egd/prngd socket %s", PRNGD_SOCKET);
173         if (get_random_bytes_prngd(buf, bytes, 0, PRNGD_SOCKET) == 0)
174                 return 0;
175 #endif
176         return -1;
177 }
178
179 void
180 rexec_send_rng_seed(Buffer *m)
181 {
182         u_char buf[RANDOM_SEED_SIZE];
183
184         if (RAND_bytes(buf, sizeof(buf)) <= 0) {
185                 error("Couldn't obtain random bytes (error %ld)",
186                     ERR_get_error());
187                 buffer_put_string(m, "", 0);
188         } else 
189                 buffer_put_string(m, buf, sizeof(buf));
190 }
191
192 void
193 rexec_recv_rng_seed(Buffer *m)
194 {
195         u_char *buf;
196         u_int len;
197
198         buf = buffer_get_string_ret(m, &len);
199         if (buf != NULL) {
200                 debug3("rexec_recv_rng_seed: seeding rng with %u bytes", len);
201                 RAND_add(buf, len, len);
202         }
203 }
204 #endif /* OPENSSL_PRNG_ONLY */
205
206 void
207 seed_rng(void)
208 {
209 #ifndef OPENSSL_PRNG_ONLY
210         unsigned char buf[RANDOM_SEED_SIZE];
211 #endif
212         /*
213          * OpenSSL version numbers: MNNFFPPS: major minor fix patch status
214          * We match major, minor, fix and status (not patch)
215          */
216         if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L)
217                 fatal("OpenSSL version mismatch. Built against %lx, you "
218                     "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay());
219
220 #ifndef OPENSSL_PRNG_ONLY
221         if (RAND_status() == 1) {
222                 debug3("RNG is ready, skipping seeding");
223                 return;
224         }
225
226         if (seed_from_prngd(buf, sizeof(buf)) == -1)
227                 fatal("Could not obtain seed from PRNGd");
228         RAND_add(buf, sizeof(buf), sizeof(buf));
229         memset(buf, '\0', sizeof(buf));
230
231 #endif /* OPENSSL_PRNG_ONLY */
232         if (RAND_status() != 1)
233                 fatal("PRNG is not seeded");
234 }