Commit a8ebe469 authored by Lutz Jänicke's avatar Lutz Jänicke
Browse files

Modify access to EGD socket to deal with EINTR etc that can appear

during connect() and other calls. First seen on Unixware-7.

Unify access to EGD-socket for all RAND_egd_*() methods.
parent deb2c1a1
Loading
Loading
Loading
Loading
+156 −67
Original line number Diff line number Diff line
/* crypto/rand/rand_egd.c */
/* Written by Ulf Moeller for the OpenSSL project. */
/* Written by Ulf Moeller and Lutz Jaenicke for the OpenSSL project. */
/* ====================================================================
 * Copyright (c) 1998-2000 The OpenSSL Project.  All rights reserved.
 *
@@ -56,7 +56,41 @@

#include <openssl/rand.h>

/* Query the EGD <URL: http://www.lothar.com/tech/crypto/>.
/*
 * Query the EGD <URL: http://www.lothar.com/tech/crypto/>.
 *
 * This module supplies three routines:
 *
 * RAND_query_egd_bytes(path, buf, bytes)
 *   will actually query "bytes" bytes of entropy form the egd-socket located
 *   at path and will write them to buf (if supplied) or will directly feed
 *   it to RAND_seed() if buf==NULL.
 *   The number of bytes is not limited by the maximum chunk size of EGD,
 *   which is 255 bytes. If more than 255 bytes are wanted, several chunks
 *   of entropy bytes are requested. The connection is left open until the
 *   query is competed.
 *   RAND_query_egd_bytes() returns with
 *     -1  if an error occured during connection or communication.
 *     num the number of bytes read from the EGD socket. This number is either
 *         the number of bytes requested or smaller, if the EGD pool is
 *         drained and the daemon signals that the pool is empty.
 *   This routine does not touch any RAND_status(). This is necessary, since
 *   PRNG functions may call it during initialization.
 *
 * RAND_egd_bytes(path, bytes) will query "bytes" bytes and have them
 *   used to seed the PRNG.
 *   RAND_egd_bytes() is a wrapper for RAND_query_egd_bytes() with buf=NULL.
 *   Unlike RAND_query_egd_bytes(), RAND_status() is used to test the
 *   seed status so that the return value can reflect the seed state:
 *     -1  if an error occured during connection or communication _or_
 *         if the PRNG has still not received the required seeding.
 *     num the number of bytes read from the EGD socket. This number is either
 *         the number of bytes requested or smaller, if the EGD pool is
 *         drained and the daemon signals that the pool is empty.
 *
 * RAND_egd(path) will query 255 bytes and use the bytes retreived to seed
 *   the PRNG.
 *   RAND_egd() is a wrapper for RAND_egd_bytes() with numbytes=255.
 */

#if defined(WIN32) || defined(VMS) || defined(__VMS)
@@ -80,6 +114,7 @@ int RAND_egd_bytes(const char *path,int bytes)
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <errno.h>

#ifndef offsetof
#  define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
@@ -89,9 +124,10 @@ int RAND_query_egd_bytes(const char *path, unsigned char *buf, int bytes)
	{
	int ret = 0;
	struct sockaddr_un addr;
	int len, num;
	int len, num, numbytes;
	int fd = -1;
	unsigned char egdbuf[2];
	int success;
	unsigned char egdbuf[2], tempbuf[255], *retrievebuf;

	memset(&addr, 0, sizeof(addr));
	addr.sun_family = AF_UNIX;
@@ -101,96 +137,149 @@ int RAND_query_egd_bytes(const char *path, unsigned char *buf, int bytes)
	len = offsetof(struct sockaddr_un, sun_path) + strlen(path);
	fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (fd == -1) return (-1);
	if (connect(fd, (struct sockaddr *)&addr, len) == -1) goto err;
	success = 0;
	while (!success)
	    {
	    if (connect(fd, (struct sockaddr *)&addr, len) == 0)
	       success = 1;
	    else
		{
		switch (errno)
		    {
#ifdef EINTR
		    case EINTR:
#endif
#ifdef EAGAIN
		    case EAGAIN:
#endif
#ifdef EINPROGRESS
		    case EINPROGRESS:
#endif
#ifdef EALREADY
		    case EALREADY:
#endif
			/* No error, try again */
			break;
#ifdef EISCONN
		    case EISCONN:
			success = 1;
			break;
#endif
		    default:
			goto err;	/* failure */
		    }
		}
	    }

	while(bytes > 0)
	    {
	    egdbuf[0] = 1;
	    egdbuf[1] = bytes < 255 ? bytes : 255;
	    write(fd, egdbuf, 2);
	    if (read(fd, egdbuf, 1) != 1)
	    numbytes = 0;
	    while (numbytes != 2)
		{
	        num = write(fd, egdbuf + numbytes, 2 - numbytes);
	        if (num >= 0)
		    numbytes += num;
	    	else
		    {
		    switch (errno)
		    	{
#ifdef EINTR
		    	case EINTR:
#endif
#ifdef EAGAIN
		    	case EAGAIN:
#endif
			    /* No error, try again */
			    break;
		    	default:
			    ret = -1;
		goto err;
			    goto err;	/* failure */
			}
		    }
		}
	    numbytes = 0;
	    while (numbytes != 1)
		{
	        num = read(fd, egdbuf, 1);
	        if (num >= 0)
		    numbytes += num;
	    	else
		    {
		    switch (errno)
		    	{
#ifdef EINTR
		    	case EINTR:
#endif
#ifdef EAGAIN
		    	case EAGAIN:
#endif
			    /* No error, try again */
			    break;
		    	default:
			    ret = -1;
			    goto err;	/* failure */
			}
		    }
		}
	    if(egdbuf[0] == 0)
		goto err;
	    num = read(fd, buf + ret, egdbuf[0]);
	    if (num < 1)
	    if (buf)
		retrievebuf = buf + ret;
	    else
		retrievebuf = tempbuf;
	    numbytes = 0;
	    while (numbytes != egdbuf[0])
		{
	        num = read(fd, retrievebuf + numbytes, egdbuf[0] - numbytes);
	        if (num >= 0)
		    numbytes += num;
	    	else
		    {
		    switch (errno)
		    	{
#ifdef EINTR
		    	case EINTR:
#endif
#ifdef EAGAIN
		    	case EAGAIN:
#endif
			    /* No error, try again */
			    break;
		    	default:
			    ret = -1;
		goto err;
			    goto err;	/* failure */
			}
		    }
	    ret += num;
	    bytes-=num;
		}
	    ret += egdbuf[0];
	    bytes -= egdbuf[0];
	    if (!buf)
		RAND_seed(tempbuf, egdbuf[0]);
	    }
 err:
	if (fd != -1) close(fd);
	return(ret);
	}

int RAND_egd(const char *path)

int RAND_egd_bytes(const char *path, int bytes)
	{
	int num, ret = 0;
	unsigned char buf[256];

	num = RAND_query_egd_bytes(path, buf, 255);
	num = RAND_query_egd_bytes(path, NULL, bytes);
	if (num < 1) goto err;
	RAND_seed(buf, num);
	if (RAND_status() == 1)
	    ret = num;
 err:
	return(ret);
	}

int RAND_egd_bytes(const char *path,int bytes)
	{
	int ret = 0;
	struct sockaddr_un addr;
	int len, num;
	int fd = -1;
	unsigned char buf[255];

	memset(&addr, 0, sizeof(addr));
	addr.sun_family = AF_UNIX;
	if (strlen(path) > sizeof(addr.sun_path))
		return (-1);
	strcpy(addr.sun_path,path);
	len = offsetof(struct sockaddr_un, sun_path) + strlen(path);
	fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (fd == -1) return (-1);
	if (connect(fd, (struct sockaddr *)&addr, len) == -1) goto err;

	while(bytes > 0)
	    {
	    buf[0] = 1;
	    buf[1] = bytes < 255 ? bytes : 255;
	    write(fd, buf, 2);
	    if (read(fd, buf, 1) != 1)
		{
		ret=-1;
		goto err;
		}
	    if(buf[0] == 0)
		goto err;
	    num = read(fd, buf, buf[0]);
	    if (num < 1)
		{
		ret=-1;
		goto err;
		}
	    RAND_seed(buf, num);
	    if (RAND_status() != 1)
int RAND_egd(const char *path)
	{
		ret=-1;
		goto err;
		}
	    ret += num;
	    bytes-=num;
	    }
 err:
	if (fd != -1) close(fd);
	return(ret);
	return (RAND_egd_bytes(path, 255));
	}