aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/entropy/egd/es_egd.cpp
blob: 728104b4422cf637d8859eed0fc179d39ce4e5ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
* EGD EntropySource
* (C) 1999-2009 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/es_egd.h>
#include <botan/parsing.h>
#include <botan/exceptn.h>
#include <botan/mem_ops.h>
#include <stdexcept>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/un.h>

#ifndef PF_LOCAL
  #define PF_LOCAL PF_UNIX
#endif

namespace Botan {

EGD_EntropySource::EGD_Socket::EGD_Socket(const std::string& path) :
   socket_path(path), m_fd(-1)
   {
   }

/**
* Attempt a connection to an EGD/PRNGD socket
*/
int EGD_EntropySource::EGD_Socket::open_socket(const std::string& path)
   {
   int fd = ::socket(PF_LOCAL, SOCK_STREAM, 0);

   if(fd >= 0)
      {
      sockaddr_un addr;
      clear_mem(&addr, 1);
      addr.sun_family = PF_LOCAL;

      if(path.length() >= sizeof(addr.sun_path))
         throw std::invalid_argument("EGD socket path is too long");

      std::strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));

      int len = sizeof(addr.sun_family) + std::strlen(addr.sun_path) + 1;

      if(::connect(fd, reinterpret_cast<struct ::sockaddr*>(&addr), len) < 0)
         {
         ::close(fd);
         fd = -1;
         }
      }

   return fd;
   }

/**
* Attempt to read entropy from EGD
*/
size_t EGD_EntropySource::EGD_Socket::read(byte outbuf[], size_t length)
   {
   if(length == 0)
      return 0;

   if(m_fd < 0)
      {
      m_fd = open_socket(socket_path);
      if(m_fd < 0)
         return 0;
      }

   try
      {
      // 1 == EGD command for non-blocking read
      byte egd_read_command[2] = {
         1, static_cast<byte>(std::min<size_t>(length, 255)) };

      if(::write(m_fd, egd_read_command, 2) != 2)
         throw std::runtime_error("Writing entropy read command to EGD failed");

      byte out_len = 0;
      if(::read(m_fd, &out_len, 1) != 1)
         throw std::runtime_error("Reading response length from EGD failed");

      if(out_len > egd_read_command[1])
         throw std::runtime_error("Bogus length field received from EGD");

      ssize_t count = ::read(m_fd, outbuf, out_len);

      if(count != out_len)
         throw std::runtime_error("Reading entropy result from EGD failed");

      return static_cast<size_t>(count);
      }
   catch(std::exception)
      {
      this->close();
      // Will attempt to reopen next poll
      }

   return 0;
   }

void EGD_EntropySource::EGD_Socket::close()
   {
   if(m_fd >= 0)
      {
      ::close(m_fd);
      m_fd = -1;
      }
   }

/**
* EGD_EntropySource constructor
*/
EGD_EntropySource::EGD_EntropySource(const std::vector<std::string>& paths)
   {
   for(size_t i = 0; i != paths.size(); ++i)
      sockets.push_back(EGD_Socket(paths[i]));
   }

EGD_EntropySource::~EGD_EntropySource()
   {
   for(size_t i = 0; i != sockets.size(); ++i)
      sockets[i].close();
   sockets.clear();
   }

/**
* Gather Entropy from EGD
*/
void EGD_EntropySource::poll(Entropy_Accumulator& accum)
   {
   const size_t READ_ATTEMPT = 32;

   std::lock_guard<std::mutex> lock(m_mutex);

   m_buf.resize(READ_ATTEMPT);

   for(size_t i = 0; i != sockets.size(); ++i)
      {
      size_t got = sockets[i].read(&m_buf[0], m_buf.size());

      if(got)
         {
         accum.add(&m_buf[0], got, 6);
         break;
         }
      }
   }

}