libgcrypt20/random/random-daemon.c

337 lines
8.2 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* random-daemon.c - Access to the external random daemon
* Copyright (C) 2006 Free Software Foundation, Inc.
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
/*
The functions here are used by random.c to divert calls to an
external random number daemon. The actual daemon we use is
gcryptrnd. Such a daemon is useful to keep a persistent pool in
memory over invocations of a single application and to allow
prioritizing access to the actual entropy sources. The drawback is
that we need to use IPC (i.e. unix domain socket) to convey
sensitive data.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <unistd.h>
#include "g10lib.h"
#include "random.h"
/* This is default socket name we use in case the provided socket name
is NULL. */
#define RANDOM_DAEMON_SOCKET "/var/run/libgcrypt/S.gcryptrnd"
/* The lock serializing access to the daemon. */
GPGRT_LOCK_DEFINE (daemon_lock);
/* The socket connected to the daemon. */
static int daemon_socket = -1;
/* Creates a socket connected to the daemon. On success, store the
socket fd in *SOCK. Returns error code. */
static gcry_error_t
connect_to_socket (const char *socketname, int *sock)
{
struct sockaddr_un *srvr_addr;
socklen_t addrlen;
gcry_error_t err;
int fd;
int rc;
srvr_addr = NULL;
/* Create a socket. */
fd = socket (AF_UNIX, SOCK_STREAM, 0);
if (fd == -1)
{
log_error ("can't create socket: %s\n", strerror (errno));
err = gcry_error_from_errno (errno);
goto out;
}
/* Set up address. */
srvr_addr = gcry_malloc (sizeof *srvr_addr);
if (! srvr_addr)
{
log_error ("malloc failed: %s\n", strerror (errno));
err = gcry_error_from_errno (errno);
goto out;
}
memset (srvr_addr, 0, sizeof *srvr_addr);
srvr_addr->sun_family = AF_UNIX;
if (strlen (socketname) + 1 >= sizeof (srvr_addr->sun_path))
{
log_error ("socket name `%s' too long\n", socketname);
err = gcry_error (GPG_ERR_ENAMETOOLONG);
goto out;
}
strcpy (srvr_addr->sun_path, socketname);
addrlen = (offsetof (struct sockaddr_un, sun_path)
+ strlen (srvr_addr->sun_path) + 1);
/* Connect socket. */
rc = connect (fd, (struct sockaddr *) srvr_addr, addrlen);
if (rc == -1)
{
log_error ("error connecting socket `%s': %s\n",
srvr_addr->sun_path, strerror (errno));
err = gcry_error_from_errno (errno);
goto out;
}
err = 0;
out:
gcry_free (srvr_addr);
if (err)
{
close (fd);
fd = -1;
}
*sock = fd;
return err;
}
/* Initialize basics of this module. This should be viewed as a
constructor to prepare locking. */
void
_gcry_daemon_initialize_basics (void)
{
/* Not anymore required. */
}
/* Send LENGTH bytes of BUFFER to file descriptor FD. Returns 0 on
success or another value on write error. */
static int
writen (int fd, const void *buffer, size_t length)
{
ssize_t n;
while (length)
{
do
n = ath_write (fd, buffer, length);
while (n < 0 && errno == EINTR);
if (n < 0)
{
log_error ("write error: %s\n", strerror (errno));
return -1; /* write error */
}
length -= n;
buffer = (const char*)buffer + n;
}
return 0; /* Okay */
}
static int
readn (int fd, void *buf, size_t buflen, size_t *ret_nread)
{
size_t nleft = buflen;
int nread;
char *p;
p = buf;
while (nleft > 0)
{
nread = ath_read (fd, buf, nleft);
if (nread < 0)
{
if (nread == EINTR)
nread = 0;
else
return -1;
}
else if (!nread)
break; /* EOF */
nleft -= nread;
buf = (char*)buf + nread;
}
if (ret_nread)
*ret_nread = buflen - nleft;
return 0;
}
/* This functions requests REQ_NBYTES from the daemon. If NONCE is
true, the data should be suited for a nonce. If NONCE is FALSE,
data of random level LEVEL will be generated. The retrieved random
data will be stored in BUFFER. Returns error code. */
static gcry_error_t
call_daemon (const char *socketname,
void *buffer, size_t req_nbytes, int nonce,
enum gcry_random_level level)
{
static int initialized;
unsigned char buf[255];
gcry_error_t err = 0;
size_t nbytes;
size_t nread;
int rc;
if (!req_nbytes)
return 0;
gpgrt_lock_lock (&daemon_lock);
/* Open the socket if that has not been done. */
if (!initialized)
{
initialized = 1;
err = connect_to_socket (socketname ? socketname : RANDOM_DAEMON_SOCKET,
&daemon_socket);
if (err)
{
daemon_socket = -1;
log_info ("not using random daemon\n");
gpgrt_lock_unlock (&daemon_lock);
return err;
}
}
/* Check that we have a valid socket descriptor. */
if ( daemon_socket == -1 )
{
gpgrt_lock_unlock (&daemon_lock);
return gcry_error (GPG_ERR_INTERNAL);
}
/* Do the real work. */
do
{
/* Process in chunks. */
nbytes = req_nbytes > sizeof (buf) ? sizeof (buf) : req_nbytes;
req_nbytes -= nbytes;
/* Construct request. */
buf[0] = 3;
if (nonce)
buf[1] = 10;
else if (level == GCRY_VERY_STRONG_RANDOM)
buf[1] = 12;
else if (level == GCRY_STRONG_RANDOM)
buf[1] = 11;
buf[2] = nbytes;
/* Send request. */
rc = writen (daemon_socket, buf, 3);
if (rc == -1)
{
err = gcry_error_from_errno (errno);
break;
}
/* Retrieve response. */
rc = readn (daemon_socket, buf, 2, &nread);
if (rc == -1)
{
err = gcry_error_from_errno (errno);
log_error ("read error: %s\n", _gcry_strerror (err));
break;
}
if (nread && buf[0])
{
log_error ("random daemon returned error code %d\n", buf[0]);
err = gcry_error (GPG_ERR_INTERNAL); /* ? */
break;
}
if (nread != 2)
{
log_error ("response too small\n");
err = gcry_error (GPG_ERR_PROTOCOL_VIOLATION); /* ? */
break;
}
/* if (1)*/ /* Do this in verbose mode? */
/* log_info ("received response with %d bytes of data\n", buf[1]);*/
if (buf[1] < nbytes)
{
log_error ("error: server returned less bytes than requested\n");
err = gcry_error (GPG_ERR_PROTOCOL_VIOLATION); /* ? */
break;
}
else if (buf[1] > nbytes)
{
log_error ("warning: server returned more bytes than requested\n");
err = gcry_error (GPG_ERR_PROTOCOL_VIOLATION); /* ? */
break;
}
assert (nbytes <= sizeof (buf));
rc = readn (daemon_socket, buf, nbytes, &nread);
if (rc == -1)
{
err = gcry_error_from_errno (errno);
log_error ("read error: %s\n", _gcry_strerror (err));
break;
}
if (nread != nbytes)
{
log_error ("too little random data read\n");
err = gcry_error (GPG_ERR_INTERNAL);
break;
}
/* Successfuly read another chunk of data. */
memcpy (buffer, buf, nbytes);
buffer = ((char *) buffer) + nbytes;
}
while (req_nbytes);
gpgrt_lock_unlock (&daemon_lock);
return err;
}
/* Internal function to fill BUFFER with LENGTH bytes of random. We
support GCRY_STRONG_RANDOM and GCRY_VERY_STRONG_RANDOM here.
Return 0 on success. */
int
_gcry_daemon_randomize (const char *socketname,
void *buffer, size_t length,
enum gcry_random_level level)
{
gcry_error_t err;
err = call_daemon (socketname, buffer, length, 0, level);
return err ? -1 : 0;
}
/* END */