Botan  2.4.0
Crypto and TLS for C++11
proc_walk.cpp
Go to the documentation of this file.
1 /*
2 * Entropy source based on reading files in /proc on the assumption
3 * that a remote attacker will have difficulty guessing some of them.
4 *
5 * (C) 1999-2008,2012 Jack Lloyd
6 *
7 * Botan is released under the Simplified BSD License (see license.txt)
8 */
9 
10 #include <botan/internal/proc_walk.h>
11 #include <deque>
12 
13 #ifndef _POSIX_C_SOURCE
14  #define _POSIX_C_SOURCE 199309
15 #endif
16 
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <dirent.h>
21 #include <fcntl.h>
22 
23 namespace Botan {
24 
25 namespace {
26 
27 class Directory_Walker final : public File_Descriptor_Source
28  {
29  public:
30  explicit Directory_Walker(const std::string& root) :
31  m_cur_dir(std::make_pair<DIR*, std::string>(nullptr, ""))
32  {
33  if(DIR* root_dir = ::opendir(root.c_str()))
34  m_cur_dir = std::make_pair(root_dir, root);
35  }
36 
37  ~Directory_Walker()
38  {
39  if(m_cur_dir.first)
40  ::closedir(m_cur_dir.first);
41  }
42 
43  int next_fd() override;
44  private:
45  std::pair<struct dirent*, std::string> get_next_dirent();
46 
47  std::pair<DIR*, std::string> m_cur_dir;
48  std::deque<std::string> m_dirlist;
49  };
50 
51 std::pair<struct dirent*, std::string> Directory_Walker::get_next_dirent()
52  {
53  while(m_cur_dir.first)
54  {
55  if(struct dirent* dir = ::readdir(m_cur_dir.first))
56  return std::make_pair(dir, m_cur_dir.second);
57 
58  ::closedir(m_cur_dir.first);
59  m_cur_dir = std::make_pair<DIR*, std::string>(nullptr, "");
60 
61  while(!m_dirlist.empty() && !m_cur_dir.first)
62  {
63  const std::string next_dir_name = m_dirlist[0];
64  m_dirlist.pop_front();
65 
66  if(DIR* next_dir = ::opendir(next_dir_name.c_str()))
67  m_cur_dir = std::make_pair(next_dir, next_dir_name);
68  }
69  }
70 
71  return std::make_pair<struct dirent*, std::string>(nullptr, ""); // nothing left
72  }
73 
74 int Directory_Walker::next_fd()
75  {
76  while(true)
77  {
78  std::pair<struct dirent*, std::string> entry = get_next_dirent();
79 
80  if(!entry.first)
81  break; // no more dirs
82 
83  const std::string filename = entry.first->d_name;
84 
85  if(filename == "." || filename == "..")
86  continue;
87 
88  const std::string full_path = entry.second + "/" + filename;
89 
90  struct stat stat_buf;
91  if(::lstat(full_path.c_str(), &stat_buf) == -1)
92  continue;
93 
94  if(S_ISDIR(stat_buf.st_mode))
95  {
96  m_dirlist.push_back(full_path);
97  }
98  else if(S_ISREG(stat_buf.st_mode) && (stat_buf.st_mode & S_IROTH))
99  {
100  int fd = ::open(full_path.c_str(), O_RDONLY | O_NOCTTY);
101 
102  if(fd >= 0)
103  return fd;
104  }
105  }
106 
107  return -1;
108  }
109 
110 }
111 
113  {
114  const size_t MAX_FILES_READ_PER_POLL = 2048;
115 
116  lock_guard_type<mutex_type> lock(m_mutex);
117 
118  if(!m_dir)
119  m_dir.reset(new Directory_Walker(m_path));
120 
121  m_buf.resize(4096);
122 
123  size_t bits = 0;
124 
125  for(size_t i = 0; i != MAX_FILES_READ_PER_POLL; ++i)
126  {
127  int fd = m_dir->next_fd();
128 
129  // If we've exhaused this walk of the directory, halt the poll
130  if(fd == -1)
131  {
132  m_dir.reset();
133  break;
134  }
135 
136  ssize_t got = ::read(fd, m_buf.data(), m_buf.size());
137  ::close(fd);
138 
139  if(got > 0)
140  {
141  rng.add_entropy(m_buf.data(), static_cast<size_t>(got));
142 
143  // Conservative estimate of 4 bits per file
144  bits += 4;
145  }
146 
147  if(bits > 128)
148  break;
149  }
150 
151  return bits;
152  }
153 
154 }
virtual void add_entropy(const uint8_t input[], size_t length)=0
#define O_NOCTTY
Definition: alg_id.cpp:13
size_t poll(RandomNumberGenerator &rng) override
Definition: proc_walk.cpp:112