summaryrefslogtreecommitdiff
path: root/entropy.c
diff options
context:
space:
mode:
Diffstat (limited to 'entropy.c')
-rw-r--r--entropy.c238
1 files changed, 139 insertions, 99 deletions
diff --git a/entropy.c b/entropy.c
index a82166258..2d6d3ec52 100644
--- a/entropy.c
+++ b/entropy.c
@@ -25,19 +25,19 @@
25#include "includes.h" 25#include "includes.h"
26 26
27#include <sys/types.h> 27#include <sys/types.h>
28#include <sys/wait.h> 28#include <sys/socket.h>
29 29#ifdef HAVE_SYS_UN_H
30#ifdef HAVE_SYS_STAT_H 30# include <sys/un.h>
31# include <sys/stat.h>
32#endif 31#endif
33 32
34#ifdef HAVE_FCNTL_H 33#include <netinet/in.h>
35# include <fcntl.h> 34#include <arpa/inet.h>
36#endif 35
37#include <stdarg.h> 36#include <errno.h>
38#include <string.h>
39#include <signal.h> 37#include <signal.h>
38#include <string.h>
40#include <unistd.h> 39#include <unistd.h>
40#include <stddef.h> /* for offsetof */
41 41
42#include <openssl/rand.h> 42#include <openssl/rand.h>
43#include <openssl/crypto.h> 43#include <openssl/crypto.h>
@@ -54,118 +54,128 @@
54/* 54/*
55 * Portable OpenSSH PRNG seeding: 55 * Portable OpenSSH PRNG seeding:
56 * If OpenSSL has not "internally seeded" itself (e.g. pulled data from 56 * If OpenSSL has not "internally seeded" itself (e.g. pulled data from
57 * /dev/random), then we execute a "ssh-rand-helper" program which 57 * /dev/random), then collect RANDOM_SEED_SIZE bytes of randomness from
58 * collects entropy and writes it to stdout. The child program must 58 * PRNGd.
59 * write at least RANDOM_SEED_SIZE bytes. The child is run with stderr
60 * attached, so error/debugging output should be visible.
61 *
62 * XXX: we should tell the child how many bytes we need.
63 */ 59 */
64
65#ifndef OPENSSL_PRNG_ONLY 60#ifndef OPENSSL_PRNG_ONLY
61
66#define RANDOM_SEED_SIZE 48 62#define RANDOM_SEED_SIZE 48
67static uid_t original_uid, original_euid;
68#endif
69 63
70void 64/*
71seed_rng(void) 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 */
72int
73get_random_bytes_prngd(unsigned char *buf, int len,
74 unsigned short tcp_port, char *socket_path)
72{ 75{
73#ifndef OPENSSL_PRNG_ONLY 76 int fd, addr_len, rval, errors;
74 int devnull; 77 u_char msg[2];
75 int p[2]; 78 struct sockaddr_storage addr;
76 pid_t pid; 79 struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr;
77 int ret; 80 struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr;
78 unsigned char buf[RANDOM_SEED_SIZE]; 81 mysig_t old_sigpipe;
79 mysig_t old_sigchld; 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 }
80 106
81 if (RAND_status() == 1) { 107 old_sigpipe = mysignal(SIGPIPE, SIG_IGN);
82 debug3("RNG is ready, skipping seeding"); 108
83 return; 109 errors = 0;
110 rval = -1;
111reopen:
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;
84 } 116 }
85 117
86 debug3("Seeding PRNG from %s", SSH_RAND_HELPER); 118 if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) {
87 119 if (tcp_port != 0) {
88 if ((devnull = open("/dev/null", O_RDWR)) == -1) 120 error("Couldn't connect to PRNGD port %d: %s",
89 fatal("Couldn't open /dev/null: %s", strerror(errno)); 121 tcp_port, strerror(errno));
90 if (pipe(p) == -1) 122 } else {
91 fatal("pipe: %s", strerror(errno)); 123 error("Couldn't connect to PRNGD socket \"%s\": %s",
92 124 addr_un->sun_path, strerror(errno));
93 old_sigchld = signal(SIGCHLD, SIG_DFL);
94 if ((pid = fork()) == -1)
95 fatal("Couldn't fork: %s", strerror(errno));
96 if (pid == 0) {
97 dup2(devnull, STDIN_FILENO);
98 dup2(p[1], STDOUT_FILENO);
99 /* Keep stderr open for errors */
100 close(p[0]);
101 close(p[1]);
102 close(devnull);
103
104 if (original_uid != original_euid &&
105 ( seteuid(getuid()) == -1 ||
106 setuid(original_uid) == -1) ) {
107 fprintf(stderr, "(rand child) setuid(%li): %s\n",
108 (long int)original_uid, strerror(errno));
109 _exit(1);
110 } 125 }
111 126 goto done;
112 execl(SSH_RAND_HELPER, "ssh-rand-helper", NULL);
113 fprintf(stderr, "(rand child) Couldn't exec '%s': %s\n",
114 SSH_RAND_HELPER, strerror(errno));
115 _exit(1);
116 } 127 }
117 128
118 close(devnull); 129 /* Send blocking read request to PRNGD */
119 close(p[1]); 130 msg[0] = 0x02;
131 msg[1] = len;
120 132
121 memset(buf, '\0', sizeof(buf)); 133 if (atomicio(vwrite, fd, msg, sizeof(msg)) != sizeof(msg)) {
122 ret = atomicio(read, p[0], buf, sizeof(buf)); 134 if (errno == EPIPE && errors < 10) {
123 if (ret == -1) 135 close(fd);
124 fatal("Couldn't read from ssh-rand-helper: %s", 136 errors++;
137 goto reopen;
138 }
139 error("Couldn't write to PRNGD socket: %s",
125 strerror(errno)); 140 strerror(errno));
126 if (ret != sizeof(buf)) 141 goto done;
127 fatal("ssh-rand-helper child produced insufficient data"); 142 }
128
129 close(p[0]);
130 143
131 if (waitpid(pid, &ret, 0) == -1) 144 if (atomicio(read, fd, buf, len) != (size_t)len) {
132 fatal("Couldn't wait for ssh-rand-helper completion: %s", 145 if (errno == EPIPE && errors < 10) {
146 close(fd);
147 errors++;
148 goto reopen;
149 }
150 error("Couldn't read from PRNGD socket: %s",
133 strerror(errno)); 151 strerror(errno));
134 signal(SIGCHLD, old_sigchld); 152 goto done;
135 153 }
136 /* We don't mind if the child exits upon a SIGPIPE */
137 if (!WIFEXITED(ret) &&
138 (!WIFSIGNALED(ret) || WTERMSIG(ret) != SIGPIPE))
139 fatal("ssh-rand-helper terminated abnormally");
140 if (WEXITSTATUS(ret) != 0)
141 fatal("ssh-rand-helper exit with exit status %d", ret);
142
143 RAND_add(buf, sizeof(buf), sizeof(buf));
144 memset(buf, '\0', sizeof(buf));
145 154
146#endif /* OPENSSL_PRNG_ONLY */ 155 rval = 0;
147 if (RAND_status() != 1) 156done:
148 fatal("PRNG is not seeded"); 157 mysignal(SIGPIPE, old_sigpipe);
158 if (fd != -1)
159 close(fd);
160 return rval;
149} 161}
150 162
151void 163static int
152init_rng(void) 164seed_from_prngd(unsigned char *buf, size_t bytes)
153{ 165{
154 /* 166#ifdef PRNGD_PORT
155 * OpenSSL version numbers: MNNFFPPS: major minor fix patch status 167 debug("trying egd/prngd port %d", PRNGD_PORT);
156 * We match major, minor, fix and status (not patch) 168 if (get_random_bytes_prngd(buf, bytes, PRNGD_PORT, NULL) == 0)
157 */ 169 return 0;
158 if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) 170#endif
159 fatal("OpenSSL version mismatch. Built against %lx, you " 171#ifdef PRNGD_SOCKET
160 "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay()); 172 debug("trying egd/prngd socket %s", PRNGD_SOCKET);
161 173 if (get_random_bytes_prngd(buf, bytes, 0, PRNGD_SOCKET) == 0)
162#ifndef OPENSSL_PRNG_ONLY 174 return 0;
163 original_uid = getuid();
164 original_euid = geteuid();
165#endif 175#endif
176 return -1;
166} 177}
167 178
168#ifndef OPENSSL_PRNG_ONLY
169void 179void
170rexec_send_rng_seed(Buffer *m) 180rexec_send_rng_seed(Buffer *m)
171{ 181{
@@ -191,4 +201,34 @@ rexec_recv_rng_seed(Buffer *m)
191 RAND_add(buf, len, len); 201 RAND_add(buf, len, len);
192 } 202 }
193} 203}
204#endif /* OPENSSL_PRNG_ONLY */
205
206void
207seed_rng(void)
208{
209#ifndef OPENSSL_PRNG_ONLY
210 unsigned char buf[RANDOM_SEED_SIZE];
194#endif 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}