Botan  1.11.15
src/lib/entropy/egd/es_egd.cpp
Go to the documentation of this file.
00001 /*
00002 * EGD EntropySource
00003 * (C) 1999-2009 Jack Lloyd
00004 *
00005 * Botan is released under the Simplified BSD License (see license.txt)
00006 */
00007 
00008 #include <botan/internal/es_egd.h>
00009 #include <botan/parsing.h>
00010 #include <botan/exceptn.h>
00011 #include <botan/mem_ops.h>
00012 #include <stdexcept>
00013 
00014 #include <sys/types.h>
00015 #include <sys/stat.h>
00016 #include <fcntl.h>
00017 #include <unistd.h>
00018 
00019 #include <sys/socket.h>
00020 #include <sys/un.h>
00021 
00022 #ifndef PF_LOCAL
00023   #define PF_LOCAL PF_UNIX
00024 #endif
00025 
00026 namespace Botan {
00027 
00028 EGD_EntropySource::EGD_Socket::EGD_Socket(const std::string& path) :
00029    socket_path(path), m_fd(-1)
00030    {
00031    }
00032 
00033 /**
00034 * Attempt a connection to an EGD/PRNGD socket
00035 */
00036 int EGD_EntropySource::EGD_Socket::open_socket(const std::string& path)
00037    {
00038    int fd = ::socket(PF_LOCAL, SOCK_STREAM, 0);
00039 
00040    if(fd >= 0)
00041       {
00042       sockaddr_un addr;
00043       clear_mem(&addr, 1);
00044       addr.sun_family = PF_LOCAL;
00045 
00046       if(path.length() >= sizeof(addr.sun_path))
00047          throw std::invalid_argument("EGD socket path is too long");
00048 
00049       std::strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
00050 
00051       int len = sizeof(addr.sun_family) + std::strlen(addr.sun_path) + 1;
00052 
00053       if(::connect(fd, reinterpret_cast<struct ::sockaddr*>(&addr), len) < 0)
00054          {
00055          ::close(fd);
00056          fd = -1;
00057          }
00058       }
00059 
00060    return fd;
00061    }
00062 
00063 /**
00064 * Attempt to read entropy from EGD
00065 */
00066 size_t EGD_EntropySource::EGD_Socket::read(byte outbuf[], size_t length)
00067    {
00068    if(length == 0)
00069       return 0;
00070 
00071    if(m_fd < 0)
00072       {
00073       m_fd = open_socket(socket_path);
00074       if(m_fd < 0)
00075          return 0;
00076       }
00077 
00078    try
00079       {
00080       // 1 == EGD command for non-blocking read
00081       byte egd_read_command[2] = {
00082          1, static_cast<byte>(std::min<size_t>(length, 255)) };
00083 
00084       if(::write(m_fd, egd_read_command, 2) != 2)
00085          throw std::runtime_error("Writing entropy read command to EGD failed");
00086 
00087       byte out_len = 0;
00088       if(::read(m_fd, &out_len, 1) != 1)
00089          throw std::runtime_error("Reading response length from EGD failed");
00090 
00091       if(out_len > egd_read_command[1])
00092          throw std::runtime_error("Bogus length field received from EGD");
00093 
00094       ssize_t count = ::read(m_fd, outbuf, out_len);
00095 
00096       if(count != out_len)
00097          throw std::runtime_error("Reading entropy result from EGD failed");
00098 
00099       return static_cast<size_t>(count);
00100       }
00101    catch(std::exception)
00102       {
00103       this->close();
00104       // Will attempt to reopen next poll
00105       }
00106 
00107    return 0;
00108    }
00109 
00110 void EGD_EntropySource::EGD_Socket::close()
00111    {
00112    if(m_fd >= 0)
00113       {
00114       ::close(m_fd);
00115       m_fd = -1;
00116       }
00117    }
00118 
00119 /**
00120 * EGD_EntropySource constructor
00121 */
00122 EGD_EntropySource::EGD_EntropySource(const std::vector<std::string>& paths)
00123    {
00124    for(size_t i = 0; i != paths.size(); ++i)
00125       sockets.push_back(EGD_Socket(paths[i]));
00126    }
00127 
00128 EGD_EntropySource::~EGD_EntropySource()
00129    {
00130    for(size_t i = 0; i != sockets.size(); ++i)
00131       sockets[i].close();
00132    sockets.clear();
00133    }
00134 
00135 /**
00136 * Gather Entropy from EGD
00137 */
00138 void EGD_EntropySource::poll(Entropy_Accumulator& accum)
00139    {
00140    const size_t READ_ATTEMPT = 32;
00141 
00142    std::lock_guard<std::mutex> lock(m_mutex);
00143 
00144    secure_vector<byte>& io_buffer = accum.get_io_buffer(READ_ATTEMPT);
00145 
00146    for(size_t i = 0; i != sockets.size(); ++i)
00147       {
00148       size_t got = sockets[i].read(&io_buffer[0], io_buffer.size());
00149 
00150       if(got)
00151          {
00152          accum.add(&io_buffer[0], got, 6);
00153          break;
00154          }
00155       }
00156    }
00157 
00158 }