Botan  1.11.15
src/lib/entropy/proc_walk/proc_walk.cpp
Go to the documentation of this file.
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 }