summary refs log tree commit diff stats
path: root/libotr/libgcrypt-1.8.7/random/random-daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'libotr/libgcrypt-1.8.7/random/random-daemon.c')
-rw-r--r--libotr/libgcrypt-1.8.7/random/random-daemon.c336
1 files changed, 336 insertions, 0 deletions
diff --git a/libotr/libgcrypt-1.8.7/random/random-daemon.c b/libotr/libgcrypt-1.8.7/random/random-daemon.c
new file mode 100644
index 0000000..8ea4df2
--- /dev/null
+++ b/libotr/libgcrypt-1.8.7/random/random-daemon.c
@@ -0,0 +1,336 @@
+/* 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 */