Coverage Report

Created: 2020-09-16 07:52

/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
}