summaryrefslogtreecommitdiff
path: root/random/random-daemon.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2008-07-05 16:44:03 +0000
committerWerner Koch <wk@gnupg.org>2008-07-05 16:44:03 +0000
commitb43501cc3f5b760da701e48a48e00634ad06e461 (patch)
treedd6713ea5e42bb805074010c7bc4797d5153ffdc /random/random-daemon.c
parent40e1ff89408eaeb56d32068bc9c9551715f2deff (diff)
downloadlibgcrypt-b43501cc3f5b760da701e48a48e00634ad06e461.tar.gz
Moved random stuff into its own directory.
Diffstat (limited to 'random/random-daemon.c')
-rw-r--r--random/random-daemon.c360
1 files changed, 360 insertions, 0 deletions
diff --git a/random/random-daemon.c b/random/random-daemon.c
new file mode 100644
index 00000000..0b79e5c5
--- /dev/null
+++ b/random/random-daemon.c
@@ -0,0 +1,360 @@
+/* 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"
+#include "ath.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. */
+static ath_mutex_t daemon_lock = ATH_MUTEX_INITIALIZER;
+
+/* 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_INTERNAL); /* FIXME? */
+ 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)
+{
+ static int initialized;
+ int err;
+
+ if (!initialized)
+ {
+ initialized = 1;
+ err = ath_mutex_init (&daemon_lock);
+ if (err)
+ log_fatal ("failed to create the daemon lock: %s\n", strerror (err) );
+ }
+}
+
+
+
+/* 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;
+
+ ath_mutex_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");
+ ath_mutex_unlock (&daemon_lock);
+ return err;
+ }
+ }
+
+ /* Check that we have a valid socket descriptor. */
+ if ( daemon_socket == -1 )
+ {
+ ath_mutex_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)*/ /* FIXME, verbose */
+ /* 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);
+
+ ath_mutex_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;
+}
+
+
+/* Internal function to fill BUFFER with NBYTES of data usable for a
+ nonce. Returns 0 on success. */
+int
+_gcry_daemon_create_nonce (const char *socketname, void *buffer, size_t length)
+{
+ gcry_error_t err;
+
+ err = call_daemon (socketname, buffer, length, 1, 0);
+
+ return err ? -1 : 0;
+}
+
+/* END */