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 }