Botan 2.19.3
Crypto and TLS for C&
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
23namespace Botan {
24
25namespace {
26
27class 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
51std::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
74int 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}
size_t poll(RandomNumberGenerator &rng) override
virtual void add_entropy(const uint8_t input[], size_t length)=0
int(* final)(unsigned char *, CTX *)
#define O_NOCTTY