/* 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 #include #include #include #include #include #include #include #include #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 */