Botan
1.11.15
|
00001 /* 00002 * Entropy source based on reading files in /proc on the assumption 00003 * that a remote attacker will have difficulty guessing some of them. 00004 * 00005 * (C) 1999-2008,2012 Jack Lloyd 00006 * 00007 * Botan is released under the Simplified BSD License (see license.txt) 00008 */ 00009 00010 #include <botan/internal/proc_walk.h> 00011 #include <botan/secmem.h> 00012 #include <deque> 00013 00014 #ifndef _POSIX_C_SOURCE 00015 #define _POSIX_C_SOURCE 199309 00016 #endif 00017 00018 #include <sys/types.h> 00019 #include <sys/stat.h> 00020 #include <unistd.h> 00021 #include <dirent.h> 00022 #include <fcntl.h> 00023 00024 namespace Botan { 00025 00026 namespace { 00027 00028 class Directory_Walker : public File_Descriptor_Source 00029 { 00030 public: 00031 Directory_Walker(const std::string& root) : 00032 m_cur_dir(std::make_pair<DIR*, std::string>(nullptr, "")) 00033 { 00034 if(DIR* root_dir = ::opendir(root.c_str())) 00035 m_cur_dir = std::make_pair(root_dir, root); 00036 } 00037 00038 ~Directory_Walker() 00039 { 00040 if(m_cur_dir.first) 00041 ::closedir(m_cur_dir.first); 00042 } 00043 00044 int next_fd(); 00045 private: 00046 std::pair<struct dirent*, std::string> get_next_dirent(); 00047 00048 std::pair<DIR*, std::string> m_cur_dir; 00049 std::deque<std::string> m_dirlist; 00050 }; 00051 00052 std::pair<struct dirent*, std::string> Directory_Walker::get_next_dirent() 00053 { 00054 while(m_cur_dir.first) 00055 { 00056 if(struct dirent* dir = ::readdir(m_cur_dir.first)) 00057 return std::make_pair(dir, m_cur_dir.second); 00058 00059 ::closedir(m_cur_dir.first); 00060 m_cur_dir = std::make_pair<DIR*, std::string>(nullptr, ""); 00061 00062 while(!m_dirlist.empty() && !m_cur_dir.first) 00063 { 00064 const std::string next_dir_name = m_dirlist[0]; 00065 m_dirlist.pop_front(); 00066 00067 if(DIR* next_dir = ::opendir(next_dir_name.c_str())) 00068 m_cur_dir = std::make_pair(next_dir, next_dir_name); 00069 } 00070 } 00071 00072 return std::make_pair<struct dirent*, std::string>(nullptr, ""); // nothing left 00073 } 00074 00075 int Directory_Walker::next_fd() 00076 { 00077 while(true) 00078 { 00079 std::pair<struct dirent*, std::string> entry = get_next_dirent(); 00080 00081 if(!entry.first) 00082 break; // no more dirs 00083 00084 const std::string filename = entry.first->d_name; 00085 00086 if(filename == "." || filename == "..") 00087 continue; 00088 00089 const std::string full_path = entry.second + '/' + filename; 00090 00091 struct stat stat_buf; 00092 if(::lstat(full_path.c_str(), &stat_buf) == -1) 00093 continue; 00094 00095 if(S_ISDIR(stat_buf.st_mode)) 00096 { 00097 m_dirlist.push_back(full_path); 00098 } 00099 else if(S_ISREG(stat_buf.st_mode) && (stat_buf.st_mode & S_IROTH)) 00100 { 00101 int fd = ::open(full_path.c_str(), O_RDONLY | O_NOCTTY); 00102 00103 if(fd >= 0) 00104 return fd; 00105 } 00106 } 00107 00108 return -1; 00109 } 00110 00111 } 00112 00113 void ProcWalking_EntropySource::poll(Entropy_Accumulator& accum) 00114 { 00115 const size_t MAX_FILES_READ_PER_POLL = 2048; 00116 const double ENTROPY_ESTIMATE = 1.0 / (8*1024); 00117 00118 std::lock_guard<std::mutex> lock(m_mutex); 00119 00120 if(!m_dir) 00121 m_dir.reset(new Directory_Walker(m_path)); 00122 00123 secure_vector<byte>& io_buffer = accum.get_io_buffer(4096); 00124 00125 for(size_t i = 0; i != MAX_FILES_READ_PER_POLL; ++i) 00126 { 00127 int fd = m_dir->next_fd(); 00128 00129 // If we've exhaused this walk of the directory, halt the poll 00130 if(fd == -1) 00131 { 00132 m_dir.reset(); 00133 break; 00134 } 00135 00136 ssize_t got = ::read(fd, &io_buffer[0], io_buffer.size()); 00137 ::close(fd); 00138 00139 if(got > 0) 00140 accum.add(&io_buffer[0], got, ENTROPY_ESTIMATE); 00141 00142 if(accum.polling_goal_achieved()) 00143 break; 00144 } 00145 } 00146 00147 }