1 /** 2 * EGD EntropySource 3 * 4 * Copyright: 5 * (C) 1999-2007 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.entropy.es_egd; 12 13 import botan.constants; 14 version(Posix): 15 static if (BOTAN_HAS_ENTROPY_SRC_EGD): 16 17 import botan.entropy.entropy_src; 18 // import string; 19 import botan.utils.types; 20 21 import botan.utils.parsing; 22 import botan.utils.exceptn; 23 import std.exception; 24 25 import core.sys.posix.sys.types; 26 import core.sys.posix.sys.stat; 27 import core.sys.posix.fcntl; 28 import core.sys.posix.unistd; 29 30 import core.sys.posix.sys.socket; 31 import core.sys.posix.sys.un; 32 33 import core.stdc.string; 34 import std.string : toStringz; 35 36 import std.algorithm : min; 37 enum PF_LOCAL = AF_UNIX; 38 39 /** 40 * EGD Entropy Source 41 */ 42 final class EGDEntropySource : EntropySource 43 { 44 public: 45 @property string name() const { return "EGD/PRNGD"; } 46 47 /** 48 * Gather Entropy from EGD 49 */ 50 void poll(ref EntropyAccumulator accum) 51 { 52 __gshared immutable size_t READ_ATTEMPT = 32; 53 54 SecureVector!ubyte* io_buffer = &accum.getIoBuffer(READ_ATTEMPT); 55 56 ubyte[] io_buf_mutable = io_buffer.ptr[0 .. READ_ATTEMPT]; 57 foreach (socket; m_sockets[]) 58 { 59 size_t got = socket.read(io_buf_mutable); 60 61 if (got) 62 { 63 accum.add(io_buffer.ptr, got, 6); 64 break; 65 } 66 } 67 } 68 69 /** 70 * EGD_EntropySource constructor 71 */ 72 this()(auto const ref Vector!string paths) 73 { 74 foreach (path; paths[]) 75 m_sockets.pushBack(EGDSocket(path)); 76 } 77 ~this() 78 { 79 foreach (socket; m_sockets[]) { 80 socket.close(); 81 } 82 m_sockets.clear(); 83 } 84 private: 85 struct EGDSocket 86 { 87 public: 88 this(in string path) 89 { 90 m_socket_path = path; 91 m_fd = -1; 92 } 93 94 95 void close() 96 { 97 if (m_fd > 0) 98 { 99 .close(m_fd); 100 m_fd = -1; 101 } 102 } 103 104 /** 105 * Attempt to read entropy from EGD 106 */ 107 size_t read(ref ubyte[] outbuf) 108 { 109 size_t length = outbuf.length; 110 if (length == 0) 111 return 0; 112 113 if (m_fd < 0) 114 { 115 m_fd = openSocket(m_socket_path); 116 if (m_fd < 0) 117 return 0; 118 } 119 120 try 121 { 122 // 1 == EGD command for non-blocking read 123 ubyte[2] egd_read_command = [ 1, cast(ubyte)(min(length, 255)) ]; 124 125 if (.write(m_fd, egd_read_command.ptr, 2) != 2) 126 throw new Exception("Writing entropy read command to EGD failed"); 127 128 ubyte out_len = 0; 129 if (.read(m_fd, &out_len, 1) != 1) 130 throw new Exception("Reading response length from EGD failed"); 131 132 if (out_len > egd_read_command[1]) 133 throw new Exception("Bogus length field received from EGD"); 134 135 ssize_t count = .read(m_fd, outbuf.ptr, out_len); 136 137 if (count != out_len) 138 throw new Exception("Reading entropy result from EGD failed"); 139 140 return cast(size_t)(count); 141 } 142 catch(Exception e) 143 { 144 this.close(); 145 // Will attempt to reopen next poll 146 } 147 148 return 0; 149 } 150 151 private: 152 /** 153 * Attempt a connection to an EGD/PRNGD socket 154 */ 155 static int openSocket(in string path) 156 { 157 logDebug("OpenSocket: ", path); 158 int fd = socket(PF_LOCAL, SOCK_STREAM, 0); 159 160 if (fd >= 0) 161 { 162 sockaddr_un addr; 163 memset(&addr, 0, (addr).sizeof); 164 addr.sun_family = PF_LOCAL; 165 166 if (addr.sun_path.sizeof < path.length + 1) 167 throw new InvalidArgument("EGD socket path is too long"); 168 169 strncpy(cast(char*) addr.sun_path.ptr, path.toStringz, addr.sun_path.sizeof); 170 171 int len = cast(int)( addr.sun_family.sizeof + strlen(cast(const(char*))addr.sun_path.ptr) + 1 ); 172 173 if (.connect(fd, cast(sockaddr*)(&addr), len) < 0) 174 { 175 .close(fd); 176 fd = -1; 177 } 178 } 179 180 return fd; 181 } 182 183 string m_socket_path; 184 int m_fd; // cached fd 185 } 186 187 Vector!EGDSocket m_sockets; 188 }