diff options
Diffstat (limited to 'comm/third_party/libgcrypt/random/random-daemon.c')
-rw-r--r-- | comm/third_party/libgcrypt/random/random-daemon.c | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/comm/third_party/libgcrypt/random/random-daemon.c b/comm/third_party/libgcrypt/random/random-daemon.c new file mode 100644 index 0000000000..8ea4df285b --- /dev/null +++ b/comm/third_party/libgcrypt/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 */ |