Critical EGD handling in 2.1.1p1
Lutz Jaenicke
Lutz.Jaenicke at aet.TU-Cottbus.DE
Fri Jun 23 19:15:15 EST 2000
On Wed, Jun 21, 2000 at 12:03:46PM +0200, Lutz Jaenicke wrote:
> I am not completely happy with it. If it finds, that the EGD connection
> has been dropped, it tries to reconnect, that is a good one, but:
> - If EGD is down at this point for any reason whatsoever, the sshd server
> will die, even though enough entropy might have been collected over time.
> * This is especially bad, as without EGD you cannot even fire off an
> "emergency" sshd on another port from inetd. Hence you cannot recover
> this problem from remote (without using the root password over telnet).
>
> Hence, I have "reworked" your patch a bit :-)
>
> Best regards,
> Lutz
> PS. I have sshd dying very often by now, it just services the first request
> and once the connection is closed, the server process dies, too....
Following up to my own posting...
By now I have tracked into my problems a bit more, and I have found another
problem: when the connection is closed from the EGD side, the read()/write()
operations may fail with SIGPIPE, which kills sshd without even having the
possibility to check the return values :-(
I have hence now added a sigaction sequence around the EGD communication
to prevent this from happening. I have left in my other change to not
consider EGD problems fatal, when enough entropy to seed the PRNG has
already been collected.
With respect to the "EGD-down problem": entropy.c by now is structured
to only include one of the alternative PRNG-seeding methods:
EGD _or_ /dev/urandom _or_ builtin.
I would consider it better to have builtin always available (/dev/urandom
should be reliable anyway), so that when EGD fails, the builtin seeder
is called automatically. Otherwise an EGD-failure will prevent even the
start of an emergency sshd from inetd...
[Background: I am playing around with my own "prngd" which replaces EGD
and rather emulates an "urandom" device (non-blocking, never drained)
by having an internal openssl-PRNG that is continously reseeded and
can save seed on close and initialize itself from the seed-file on restart.
Hence I have slightly other behaviour of my daemon and a lot of restarts...]
Best regards,
Lutz
--
Lutz Jaenicke Lutz.Jaenicke at aet.TU-Cottbus.DE
BTU Cottbus http://www.aet.TU-Cottbus.DE/personen/jaenicke/
Lehrstuhl Allgemeine Elektrotechnik Tel. +49 355 69-4129
Universitaetsplatz 3-4, D-03044 Cottbus Fax. +49 355 69-4153
-------------- next part --------------
--- entropy.c.orig Wed Jun 7 14:20:23 2000
+++ entropy.c Fri Jun 23 10:59:50 2000
@@ -63,42 +63,101 @@
{
static int egd_socket = -1;
int c;
+ int egd_error;
+ int enough_entropy;
char egd_message[2] = { 0x02, 0x00 };
struct sockaddr_un addr;
+ struct sigaction sa, osa;
int addr_len;
+ egd_error = 0;
+
+ enough_entropy = RAND_status();
+
+ /*
+ * When we are losing the connection to EGD, we might receive a
+ * SIGPIPE when trying to write to or read from the socket. This
+ * SIGPIPE should be ignored and the respective error code returned
+ * by read()/write(), so we change the SIGPIPE handling during
+ * EGD connections.
+ */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ (void) sigaction(SIGPIPE, &sa, &osa);
+
+retry:
+
memset(&addr, '\0', sizeof(addr));
addr.sun_family = AF_UNIX;
-
- /* FIXME: compile time check? */
+
if (sizeof(EGD_SOCKET) > sizeof(addr.sun_path))
fatal("Random pool path is too long");
-
+
strlcpy(addr.sun_path, EGD_SOCKET, sizeof(addr.sun_path));
-
+
addr_len = offsetof(struct sockaddr_un, sun_path) + sizeof(EGD_SOCKET);
-
+
if (egd_socket == -1) {
egd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
- if (egd_socket == -1)
- fatal("Couldn't create AF_UNIX socket: %s", strerror(errno));
- if (connect(egd_socket, (struct sockaddr*)&addr, addr_len) == -1)
- fatal("Couldn't connect to EGD socket \"%s\": %s", addr.sun_path, strerror(errno));
+ if (egd_socket == -1) {
+ if (!enough_entropy)
+ fatal("Couldn't create AF_UNIX socket: %s", strerror(errno));
+ else
+ error("Couldn't create AF_UNIX socket: %s", strerror(errno));
+ }
+ if (connect(egd_socket, (struct sockaddr*)&addr, addr_len) == -1) {
+ if (!enough_entropy)
+ fatal("Couldn't connect to EGD socket \"%s\": %s", addr.sun_path, strerror(errno));
+ else
+ error("Couldn't connect to EGD socket \"%s\": %s", addr.sun_path, strerror(errno));
+ }
}
if (len > 255)
fatal("Too many bytes to read from EGD");
-
+
/* Send blocking read request to EGD */
egd_message[1] = len;
c = atomicio(write, egd_socket, egd_message, sizeof(egd_message));
- if (c == -1)
- fatal("Couldn't write to EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
+ if (c == -1) {
+ if (egd_error) {
+ if (!enough_entropy)
+ fatal("Couldn't write to EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
+ else
+ error("Couldn't write to EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
+ } else {
+ egd_error = 1;
+ close(egd_socket);
+ egd_socket = -1;
+ error("Couldn't write to EGD socket \"%s\": %s",
+ EGD_SOCKET, strerror(errno));
+ goto retry;
+ }
+ }
c = atomicio(read, egd_socket, buf, len);
- if (c <= 0)
- fatal("Couldn't read from EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
+ if (c == -1) {
+ if (egd_error) {
+ if (!enough_entropy)
+ fatal("Couldn't read from EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
+ else
+ error("Couldn't read from EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
+ } else {
+ egd_error = 1;
+ close(egd_socket);
+ egd_socket = -1;
+ error("Couldn't read from EGD socket \"%s\": %s",
+ EGD_SOCKET, strerror(errno));
+ goto retry;
+ }
+ }
+
+ /*
+ * Restore the original signal response
+ */
+ (void) sigaction(SIGPIPE, &osa, NULL);
+
}
#else /* !EGD_SOCKET */
#ifdef RANDOM_POOL
More information about the openssh-unix-dev
mailing list