337 lines
8.2 KiB
C
337 lines
8.2 KiB
C
/* 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 */
|