/src/botan/src/lib/entropy/proc_walk/proc_walk.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
33 | 0 | if(DIR* root_dir = ::opendir(root.c_str())) |
34 | 0 | m_cur_dir = std::make_pair(root_dir, root); |
35 | 0 | } |
36 | | |
37 | | ~Directory_Walker() |
38 | 0 | { |
39 | 0 | if(m_cur_dir.first) |
40 | 0 | ::closedir(m_cur_dir.first); |
41 | 0 | } |
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 | 0 | { |
53 | 0 | while(m_cur_dir.first) |
54 | 0 | { |
55 | 0 | if(struct dirent* dir = ::readdir(m_cur_dir.first)) |
56 | 0 | return std::make_pair(dir, m_cur_dir.second); |
57 | 0 | |
58 | 0 | ::closedir(m_cur_dir.first); |
59 | 0 | m_cur_dir = std::make_pair<DIR*, std::string>(nullptr, ""); |
60 | 0 |
|
61 | 0 | while(!m_dirlist.empty() && !m_cur_dir.first) |
62 | 0 | { |
63 | 0 | const std::string next_dir_name = m_dirlist[0]; |
64 | 0 | m_dirlist.pop_front(); |
65 | 0 |
|
66 | 0 | if(DIR* next_dir = ::opendir(next_dir_name.c_str())) |
67 | 0 | m_cur_dir = std::make_pair(next_dir, next_dir_name); |
68 | 0 | } |
69 | 0 | } |
70 | 0 |
|
71 | 0 | return std::make_pair<struct dirent*, std::string>(nullptr, ""); // nothing left |
72 | 0 | } |
73 | | |
74 | | int Directory_Walker::next_fd() |
75 | 0 | { |
76 | 0 | while(true) |
77 | 0 | { |
78 | 0 | std::pair<struct dirent*, std::string> entry = get_next_dirent(); |
79 | 0 |
|
80 | 0 | if(!entry.first) |
81 | 0 | break; // no more dirs |
82 | 0 | |
83 | 0 | const std::string filename = entry.first->d_name; |
84 | 0 |
|
85 | 0 | if(filename == "." || filename == "..") |
86 | 0 | continue; |
87 | 0 | |
88 | 0 | const std::string full_path = entry.second + "/" + filename; |
89 | 0 |
|
90 | 0 | struct stat stat_buf; |
91 | 0 | if(::lstat(full_path.c_str(), &stat_buf) == -1) |
92 | 0 | continue; |
93 | 0 | |
94 | 0 | if(S_ISDIR(stat_buf.st_mode)) |
95 | 0 | { |
96 | 0 | m_dirlist.push_back(full_path); |
97 | 0 | } |
98 | 0 | else if(S_ISREG(stat_buf.st_mode) && (stat_buf.st_mode & S_IROTH)) |
99 | 0 | { |
100 | 0 | int fd = ::open(full_path.c_str(), O_RDONLY | O_NOCTTY); |
101 | 0 |
|
102 | 0 | if(fd >= 0) |
103 | 0 | return fd; |
104 | 0 | } |
105 | 0 | } |
106 | 0 |
|
107 | 0 | return -1; |
108 | 0 | } |
109 | | |
110 | | } |
111 | | |
112 | | size_t ProcWalking_EntropySource::poll(RandomNumberGenerator& rng) |
113 | 0 | { |
114 | 0 | const size_t MAX_FILES_READ_PER_POLL = 2048; |
115 | 0 |
|
116 | 0 | lock_guard_type<mutex_type> lock(m_mutex); |
117 | 0 |
|
118 | 0 | if(!m_dir) |
119 | 0 | m_dir.reset(new Directory_Walker(m_path)); |
120 | 0 |
|
121 | 0 | m_buf.resize(4096); |
122 | 0 |
|
123 | 0 | size_t bits = 0; |
124 | 0 |
|
125 | 0 | for(size_t i = 0; i != MAX_FILES_READ_PER_POLL; ++i) |
126 | 0 | { |
127 | 0 | int fd = m_dir->next_fd(); |
128 | 0 |
|
129 | | // If we've exhaused this walk of the directory, halt the poll |
130 | 0 | if(fd == -1) |
131 | 0 | { |
132 | 0 | m_dir.reset(); |
133 | 0 | break; |
134 | 0 | } |
135 | 0 | |
136 | 0 | ssize_t got = ::read(fd, m_buf.data(), m_buf.size()); |
137 | 0 | ::close(fd); |
138 | 0 |
|
139 | 0 | if(got > 0) |
140 | 0 | { |
141 | 0 | rng.add_entropy(m_buf.data(), static_cast<size_t>(got)); |
142 | 0 |
|
143 | | // Conservative estimate of 4 bits per file |
144 | 0 | bits += 4; |
145 | 0 | } |
146 | 0 |
|
147 | 0 | if(bits > 128) |
148 | 0 | break; |
149 | 0 | } |
150 | 0 |
|
151 | 0 | return bits; |
152 | 0 | } |
153 | | |
154 | | } |