Botan
1.11.15
|
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 }