E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gsecretsfile.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gsecretsfile.cpp
19 //
20
21 #include "gdef.h"
22 #include "gsecretsfile.h"
23 #include "gsecrets.h"
24 #include "groot.h"
25 #include "gxtext.h"
26 #include "gstr.h"
27 #include "gdatetime.h"
28 #include "gfile.h"
29 #include "gassert.h"
30 #include <map>
31 #include <set>
32 #include <fstream>
33 #include <string>
34 #include <algorithm> // std::swap
35 #include <utility> // std::swap, std::pair
36 #include <sstream>
37
38 GAuth::SecretsFile::SecretsFile( const G::Path & path , bool auto_reread , const std::string & debug_name ,
39 const std::string & server_type ) :
40 m_path(path) ,
41 m_auto(auto_reread) ,
42 m_debug_name(debug_name) ,
43 m_server_type(server_type) ,
44 m_file_time(0) ,
45 m_check_time(G::DateTime::now())
46 {
47 m_server_type = m_server_type.empty() ? std::string("server") : m_server_type ;
48 G_DEBUG( "GAuth::Secrets: " << m_debug_name << ": \"" << path << "\"" ) ;
49 m_valid = ! path.str().empty() ;
50 if( m_valid )
51 read( path ) ;
52 }
53
54 GAuth::SecretsFile::~SecretsFile()
55 {
56 }
57
58 bool GAuth::SecretsFile::valid() const
59 {
60 return m_valid ;
61 }
62
63 void GAuth::SecretsFile::reread() const
64 {
65 (const_cast<SecretsFile*>(this))->reread(0) ;
66 }
67
68 void GAuth::SecretsFile::reread( int )
69 {
70 if( m_auto )
71 {
72 G::EpochTime now = G::DateTime::now() ;
73 G_DEBUG( "GAuth::SecretsFile::reread: file time checked at " << m_check_time << ": now " << now ) ;
74 if( now != m_check_time ) // at most once a second
75 {
76 m_check_time = now ;
77 G::EpochTime t = readFileTime( m_path ) ;
78 G_DEBUG( "GAuth::SecretsFile::reread: current file time " << t << ": saved file time " << m_file_time ) ;
79 if( t != m_file_time )
80 {
81 G_LOG_S( "GAuth::Secrets: re-reading secrets file: " << m_path ) ;
82 read( m_path ) ;
83 }
84 }
85 }
86 }
87
88 void GAuth::SecretsFile::read( const G::Path & path )
89 {
90 // open the file
91 unique_ptr<std::ifstream> file ;
92 {
93 G::Root claim_root ;
94 file.reset( new std::ifstream( path.str().c_str() ) ) ;
95 }
96 if( !file->good() )
97 {
98 std::ostringstream ss ;
99 ss << "reading \"" << path << "\" for " << m_debug_name << " secrets" ;
100 throw Secrets::OpenError( ss.str() ) ;
101 }
102 m_file_time = readFileTime( path ) ;
103
104 // read the file
105 m_map.clear() ;
106 m_types.clear() ;
107 m_warnings.clear() ;
108 read( *file.get() ) ;
109
110 // show warnings
111 G_DEBUG( "GAuth::SecretsFile::read: processed " << m_map.size() << " records" ) ;
112 if( m_warnings.size() )
113 {
114 std::string prefix = path.basename() ;
115 G_WARNING( "GAuth::SecretsFile::read: problems reading " << m_debug_name
116 << " secrets file [" << path.str() << "]..." ) ;
117 for( Warnings::iterator p = m_warnings.begin() ; p != m_warnings.end() ; ++p )
118 {
119 G_WARNING( "GAuth::SecretsFile::read: " << prefix << "(" << (*p).first << "): " << (*p).second ) ;
120 }
121 }
122 }
123
124 G::EpochTime GAuth::SecretsFile::readFileTime( const G::Path & path )
125 {
126 G::Root claim_root ;
127 return G::File::time( path ) ;
128 }
129
130 void GAuth::SecretsFile::read( std::istream & file )
131 {
132 for( unsigned int line_number = 1U ; file.good() ; ++line_number )
133 {
134 std::string line = G::Str::readLineFrom( file ) ;
135 if( !file )
136 break ;
137
138 G::Str::trim( line , G::Str::ws() ) ;
139 if( !line.empty() && line.at(0U) != '#' )
140 {
141 G::StringArray word_array ;
142 G::Str::splitIntoTokens( line , word_array , " \t" ) ;
143 if( word_array.size() >= 4U )
144 {
145 // 0=<client-server> 1=<encoding-type> 2=<userid-or-ipaddress> 3=<secret-or-verifier-hint>
146 process( line_number , word_array[0U] , word_array[1U] , word_array[2U] , word_array[3U] ) ;
147 }
148 else
149 {
150 warning( line_number , "line ignored: too few fields" , "" ) ;
151 }
152 }
153 }
154 }
155
156 void GAuth::SecretsFile::process( unsigned int line_number , std::string side ,
157 std::string encoding_type , std::string id , std::string secret )
158 {
159 G_ASSERT( !side.empty() && !encoding_type.empty() && !id.empty() && !secret.empty() ) ;
160 G::Str::toLower( encoding_type ) ;
161 G::Str::toLower( side ) ;
162
163 // allow columns 1 and 2 to switch around - TODO remove backwards compatibility
164 if( encoding_type == G::Str::lower(m_server_type) || encoding_type == "client" )
165 std::swap( encoding_type , side ) ;
166
167 if( side == G::Str::lower(m_server_type) )
168 {
169 // server-side
170 std::string key = serverKey( encoding_type , id ) ;
171 Value value( secret , line_number ) ;
172 bool inserted = m_map.insert(std::make_pair(key,value)).second ;
173 if( inserted )
174 m_types.insert( canonical(encoding_type) ) ;
175 else
176 warning( line_number , "line ignored: duplicate key" , key ) ;
177 }
178 else if( side == "client" )
179 {
180 // client-side
181 std::string key = clientKey( encoding_type ) ; // not including user id
182 Value value( id + " " + secret , line_number ) ;
183 bool inserted = m_map.insert(std::make_pair(key,value)).second ;
184 if( !inserted )
185 warning( line_number , "line ignored: duplicate key" , key ) ;
186 }
187 else
188 {
189 G_DEBUG( "GAuth::SecretsFile::process: ignoring line number " << line_number << ": not 'client' or '" << m_server_type << "'" ) ;
190 }
191 }
192
193 void GAuth::SecretsFile::warning( unsigned int line_number , const std::string & message , const std::string & more )
194 {
195 m_warnings.push_back( Warnings::value_type(line_number,message+(more.empty()?std::string():": ")+more) ) ;
196 }
197
198 std::string GAuth::SecretsFile::canonical( const std::string & encoding_type )
199 {
200 std::string type = G::Str::lower( encoding_type ) ;
201 if( type == "cram-md5" ) type = "md5" ;
202 if( type == "apop" ) type = "md5" ;
203 if( type == "login" ) type = "plain" ;
204 return type ;
205 }
206
207 std::string GAuth::SecretsFile::serverKey( std::string encoding_type , const std::string & id )
208 {
209 // eg. key -> value
210 // "server plain bob" -> "e+3Dmc2"
211 // "server md5 bob" -> "xbase64x=="
212 // "server none 192.168.0.0/24" -> "trustee"
213 // "server none ::1/128" -> "trustee"
214 return "server " + canonical(encoding_type) + " " + id ;
215 }
216
217 std::string GAuth::SecretsFile::clientKey( std::string encoding_type )
218 {
219 // eg. key -> value...
220 // "client plain" -> "alice secret+21"
221 // "client md5" -> "alice xbase64x=="
222 return "client " + canonical(encoding_type) ;
223 }
224
225 GAuth::Secret GAuth::SecretsFile::clientSecret( const std::string & encoding_type ) const
226 {
227 reread() ;
228
229 Map::const_iterator p = m_map.find( clientKey(encoding_type) ) ;
230 if( p == m_map.end() )
231 {
232 return Secret::none() ;
233 }
234 else
235 {
236 std::string id = G::Xtext::decode( G::Str::head( (*p).second.s , " " ) ) ;
237 std::string str_secret = G::Str::tail( (*p).second.s , " " ) ;
238 return Secret( str_secret , canonical(encoding_type) , id , context((*p).second.n) ) ;
239 }
240 }
241
242 GAuth::Secret GAuth::SecretsFile::serverSecret( const std::string & encoding_type , const std::string & id ) const
243 {
244 if( id.empty() )
245 return Secret::none() ;
246
247 reread() ;
248
249 Map::const_iterator p = m_map.find( serverKey(encoding_type,G::Xtext::encode(id)) ) ;
250 if( p == m_map.end() )
251 {
252 return Secret::none( id ) ;
253 }
254 else
255 {
256 return Secret( (*p).second.s , canonical(encoding_type) , id , context((*p).second.n) ) ;
257 }
258 }
259
260 std::pair<std::string,std::string> GAuth::SecretsFile::serverTrust( const std::string & address_range ) const
261 {
262 std::pair<std::string,std::string> result ;
263 std::string encoding_type = "none" ;
264 std::string id = address_range ; // the address-range lives in the id field
265 Map::const_iterator p = m_map.find( serverKey(encoding_type,G::Xtext::encode(id)) ) ;
266 if( p != m_map.end() )
267 {
268 result.first = (*p).second.s ; // the trustee name lives in the shared-secret field
269 result.second = context( (*p).second.n ) ;
270 }
271 return result ;
272 }
273
274 std::string GAuth::SecretsFile::path() const
275 {
276 return m_path.str() ;
277 }
278
279 bool GAuth::SecretsFile::contains( const std::string & encoding_type ) const
280 {
281 return m_types.find(canonical(encoding_type)) != m_types.end() ;
282 }
283
284 std::string GAuth::SecretsFile::context( unsigned int line_number )
285 {
286 return " from line " + G::Str::fromUInt(line_number) ;
287 }
288 /// \file gsecretsfile.cpp
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.cpp:290
G::EpochTime
A subsecond-resolution timestamp based on a time_t.
Definition: gdatetime.h:39
GAuth::SecretsFile::serverTrust
std::pair< std::string, std::string > serverTrust(const std::string &address_range) const
Returns a non-empty trustee name if the server trusts clients in the given address range...
Definition: gsecretsfile.cpp:260
GAuth::SecretsFile::SecretsFile
SecretsFile(const G::Path &path, bool auto_reread, const std::string &debug_name, const std::string &server_type=std::string())
Constructor to read "client" and "" records from the named file.
Definition: gsecretsfile.cpp:38
G::Str::tail
static std::string tail(const std::string &in, size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1102
G::Xtext::decode
static std::string decode(const std::string &)
Decodes the given string.
Definition: gxtext.cpp:107
G::Path::basename
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:310
G::Str::head
static std::string head(const std::string &in, size_type pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1088
GAuth::SecretsFile::valid
bool valid() const
Returns true if the file path was supplied in the ctor.
Definition: gsecretsfile.cpp:58
G::Xtext::encode
static std::string encode(const std::string &)
Encodes the given string.
Definition: gxtext.cpp:86
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
G::Root
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:49
G::Str::splitIntoTokens
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:908
GAuth::SecretsFile::~SecretsFile
~SecretsFile()
Destructor.
Definition: gsecretsfile.cpp:54
groot.h
gsecretsfile.h
G::Str::trim
static void trim(std::string &s, const std::string &ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:246
G::DateTime::now
static EpochTime now()
Returns the current epoch time.
Definition: gdatetime_unix.cpp:26
GAuth::SecretsFile::serverSecret
Secret serverSecret(const std::string &encoding_type, const std::string &id) const
Returns the server secret for the given id and encoding-type.
Definition: gsecretsfile.cpp:242
G::Str::lower
static std::string lower(const std::string &s)
Returns a copy of 's' in which all Latin-1 uppercase characters have been replaced by lowercase chara...
Definition: gstr.cpp:599
G::Str::toLower
static void toLower(std::string &s)
Replaces all Latin-1 uppercase characters in string 's' by lowercase characters.
Definition: gstr.cpp:594
GAuth::SecretsFile
A class to read authentication secrets from file, used by GAuth::Secrets.
Definition: gsecretsfile.h:44
G::Str::fromUInt
static std::string fromUInt(unsigned int ui)
Converts unsigned int 'ui' to a string.
Definition: gstr.cpp:353
gstr.h
gsecrets.h
gassert.h
G
Low-level classes.
Definition: garg.h:29
GAuth::SecretsFile::path
std::string path() const
Returns the file path, as supplied to the ctor.
Definition: gsecretsfile.cpp:274
GAuth::SecretsFile::clientSecret
Secret clientSecret(const std::string &encoding_type) const
Returns the client id and secret for the given encoding-type.
Definition: gsecretsfile.cpp:225
gfile.h
GAuth::SecretsFile::contains
bool contains(const std::string &server_encoding_type) const
Returns true if the given server encoding-type is represented.
Definition: gsecretsfile.cpp:279
G::Str::readLineFrom
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:733
gxtext.h
gdatetime.h
GAuth::Secret::none
static Secret none()
Factory function that returns a secret that is not valid() and has an empty id(). ...
Definition: gsecret.cpp:90
G::File::time
static EpochTime time(const Path &file)
Returns the file's timestamp.
Definition: gfile_unix.cpp:107
GAuth::Secret
Encapsulates a shared secret from the secrets file plus the associated userid.
Definition: gsecret.h:42
G::Path
A Path object represents a file system path.
Definition: gpath.h:72
G::Str::ws
static std::string ws()
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1078