/* * Entropy source based on reading files in /proc on the assumption * that a remote attacker will have difficulty guessing some of them. * * (C) 1999-2008,2012 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 199309 #endif #include #include #include #include #include namespace Botan { namespace { class Directory_Walker : public File_Descriptor_Source { public: Directory_Walker(const std::string& root) : m_cur_dir(std::make_pair(nullptr, "")) { if(DIR* root_dir = ::opendir(root.c_str())) m_cur_dir = std::make_pair(root_dir, root); } ~Directory_Walker() { if(m_cur_dir.first) ::closedir(m_cur_dir.first); } int next_fd(); private: std::pair get_next_dirent(); std::pair m_cur_dir; std::deque m_dirlist; }; std::pair Directory_Walker::get_next_dirent() { while(m_cur_dir.first) { if(struct dirent* dir = ::readdir(m_cur_dir.first)) return std::make_pair(dir, m_cur_dir.second); ::closedir(m_cur_dir.first); m_cur_dir = std::make_pair(nullptr, ""); while(!m_dirlist.empty() && !m_cur_dir.first) { const std::string next_dir_name = m_dirlist[0]; m_dirlist.pop_front(); if(DIR* next_dir = ::opendir(next_dir_name.c_str())) m_cur_dir = std::make_pair(next_dir, next_dir_name); } } return std::make_pair(nullptr, ""); // nothing left } int Directory_Walker::next_fd() { while(true) { std::pair entry = get_next_dirent(); if(!entry.first) break; // no more dirs const std::string filename = entry.first->d_name; if(filename == "." || filename == "..") continue; const std::string full_path = entry.second + '/' + filename; struct stat stat_buf; if(::lstat(full_path.c_str(), &stat_buf) == -1) continue; if(S_ISDIR(stat_buf.st_mode)) { m_dirlist.push_back(full_path); } else if(S_ISREG(stat_buf.st_mode) && (stat_buf.st_mode & S_IROTH)) { int fd = ::open(full_path.c_str(), O_RDONLY | O_NOCTTY); if(fd >= 0) return fd; } } return -1; } } void ProcWalking_EntropySource::poll(Entropy_Accumulator& accum) { const size_t MAX_FILES_READ_PER_POLL = 2048; const double ENTROPY_ESTIMATE = 1.0 / (8*1024); std::lock_guard lock(m_mutex); if(!m_dir) m_dir.reset(new Directory_Walker(m_path)); m_buf.resize(4096); for(size_t i = 0; i != MAX_FILES_READ_PER_POLL; ++i) { int fd = m_dir->next_fd(); // If we've exhaused this walk of the directory, halt the poll if(fd == -1) { m_dir.reset(); break; } ssize_t got = ::read(fd, m_buf.data(), m_buf.size()); ::close(fd); if(got > 0) accum.add(m_buf.data(), got, ENTROPY_ESTIMATE); if(accum.polling_finished()) break; } } }