E-MailRelay

gsecret.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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/// \file gsecret.cpp
19///
20
21#include "gdef.h"
22#include "gsecret.h"
23#include "gstr.h"
24#include "gbase64.h"
25#include "gxtext.h"
26#include "gmd5.h"
27#include "gassert.h"
28#include <sstream>
29
30 GAuth::Secret::Secret()
31{
32 G_ASSERT( !valid() ) ;
33}
34
35 GAuth::Secret::Secret( const std::string & id ) :
36 m_id(id)
37{
38 G_ASSERT( !valid() ) ;
39}
40
41 GAuth::Secret::Secret( const std::string & secret , const std::string & secret_encoding ,
42 const std::string & id , bool id_encoding_xtext , const std::string & context ) :
43 m_id(id) ,
44 m_context(context)
45{
46 G_ASSERT( secret_encoding == G::Str::lower(secret_encoding) ) ;
47 std::string reason = check( secret , secret_encoding , id , id_encoding_xtext ) ;
48 if( !reason.empty() )
49 throw context.empty() ? Error(reason) : Error(context,reason) ;
50
51 if( secret_encoding == "plain" )
52 {
53 G_ASSERT( G::Xtext::valid(secret) ) ;
54 m_key = G::Xtext::decode( secret ) ;
55 }
56 else if( secret_encoding == "md5" && isDotted(secret) )
57 {
58 m_key = undotted( secret ) ;
59 m_mask_type = secret_encoding ;
60 }
61 else
62 {
63 G_ASSERT( G::Base64::valid(secret) ) ; // check()ed
64 m_key = G::Base64::decode( secret ) ;
65 m_mask_type = secret_encoding ;
66 }
67}
68
69std::string GAuth::Secret::check( const std::string & secret , const std::string & secret_encoding ,
70 const std::string & id , bool id_encoding_xtext )
71{
72 if( secret.empty() )
73 return "empty secret" ;
74 if( secret_encoding.empty() || secret_encoding != G::Str::lower(secret_encoding) || !G::Str::isPrintableAscii(secret_encoding) )
75 return "invalid encoding type" ;
76 if( id.empty() )
77 return "empty id" ;
78 if( id_encoding_xtext && !G::Xtext::valid(id) )
79 return "invalid xtext encoding of id" ;
80 if( secret_encoding == "plain" && !G::Xtext::valid(secret) )
81 return "invalid xtext encoding of secret" ;
82 if( secret_encoding == "md5" && !( isDotted(secret) || G::Base64::valid(secret) ) )
83 return "invalid encoding of md5 secret" ;
84 if( secret_encoding != "md5" && secret_encoding != "plain" && !G::Base64::valid(secret) )
85 return "invalid base64 encoding of secret" ;
86 return std::string() ;
87}
88
89 GAuth::Secret GAuth::Secret::none( const std::string & id )
90{
91 return Secret( id ) ;
92}
93
94 GAuth::Secret GAuth::Secret::none()
95{
96 return Secret() ;
97}
98
99 bool GAuth::Secret::valid() const
100{
101 return !m_key.empty() ;
102}
103
104std::string GAuth::Secret::key() const
105{
106 if( !valid() ) throw Error() ;
107 return m_key ;
108}
109
110 bool GAuth::Secret::masked() const
111{
112 return !m_mask_type.empty() ;
113}
114
115std::string GAuth::Secret::id() const
116{
117 if( !valid() ) throw Error() ;
118 return m_id ;
119}
120
121std::string GAuth::Secret::maskType() const
122{
123 if( !valid() ) throw Error() ;
124 return m_mask_type ;
125}
126
127std::string GAuth::Secret::info( const std::string & id_in ) const
128{
129 std::ostringstream ss ;
130 ss << (valid()?(masked()?maskType():std::string("plaintext")):std::string("missing")) << " secret" ;
131 std::string id_ = ( id_in.empty() && valid() ) ? m_id : id_in ;
132 if( !id_.empty() )
133 {
134 ss << " for [" << G::Str::printable(id_) << "]" ;
135 }
136 if( !m_context.empty() )
137 {
138 ss << " from " << m_context ;
139 }
140 return ss.str() ;
141}
142
143 bool GAuth::Secret::isDotted( const std::string & s )
144{
145 return
146 s.length() >= 15U &&
147 s.find_first_not_of("0123456789.") == std::string::npos &&
148 G::Str::splitIntoFields(s,".").size() == 8U ;
149}
150
151std::string GAuth::Secret::undotted( const std::string & s )
152{
153 G::StringArray decimals = G::Str::splitIntoFields( s , "." ) ;
154 decimals.resize( 8U ) ;
155 std::string result ;
156 for( std::size_t i = 0U ; i < 8U ; i++ )
157 {
158 G::Md5::big_t n = 0U ;
159 for( const char & c : decimals[i] )
160 {
161 n *= 10U ;
162 n += (c-'0') ;
163 }
164 for( int j = 0 ; j < 4 ; j++ )
165 {
166 unsigned char uc = ( n & 0xffU ) ;
167 n >>= 8 ;
168 result.push_back( static_cast<char>(uc) ) ;
169 }
170 }
171 return result ;
172}
173
GAuth::Secret
Encapsulates a shared secret from the secrets file plus the associated userid.
Definition: gsecret.h:42
GAuth::Secret::masked
bool masked() const
Returns true if key() is masked.
Definition: gsecret.cpp:110
GAuth::Secret::none
static Secret none()
Factory function that returns a secret that is not valid() and has an empty id().
Definition: gsecret.cpp:94
GAuth::Secret::valid
bool valid() const
Returns true if the secret is valid.
Definition: gsecret.cpp:99
GAuth::Secret::Secret
Secret(const std::string &secret, const std::string &secret_encoding, const std::string &id, bool id_encoding_xtext, const std::string &context=std::string())
Constructor used by the SecretsFile class.
Definition: gsecret.cpp:41
GAuth::Secret::check
static std::string check(const std::string &secret, const std::string &secret_encoding, const std::string &id, bool id_encoding_xtext)
Does a non-throwing check of the constructor parameters, returning an error message or the empty stri...
Definition: gsecret.cpp:69
GAuth::Secret::info
std::string info(const std::string &id=std::string()) const
Returns information for logging, excluding anything sensitive.
Definition: gsecret.cpp:127
GAuth::Secret::maskType
std::string maskType() const
Returns the masking function name, such as "MD5", or the empty string if not masked().
Definition: gsecret.cpp:121
GAuth::Secret::id
std::string id() const
Returns the associated identity. Throws if not valid().
Definition: gsecret.cpp:115
GAuth::Secret::key
std::string key() const
Returns the key. Throws if not valid().
Definition: gsecret.cpp:104
G::Base64::decode
static std::string decode(const std::string &, bool throw_on_invalid=false, bool strict=true)
Decodes the given string.
Definition: gbase64.cpp:89
G::Base64::valid
static bool valid(const std::string &, bool strict=true)
Returns true if the string is a valid base64 encoding, possibly allowing for embedded newlines,...
Definition: gbase64.cpp:94
G::Str::splitIntoFields
static void splitIntoFields(const std::string &in, StringArray &out, string_view ws, char escape='\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:1146
G::Str::isPrintableAscii
static bool isPrintableAscii(const std::string &s)
Returns true if every character is a 7-bit, non-control character (ie.
Definition: gstr.cpp:420
G::Str::printable
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
Definition: gstr.cpp:885
G::Str::lower
static std::string lower(const std::string &s)
Returns a copy of 's' in which all Latin-1 upper-case characters have been replaced by lower-case cha...
Definition: gstr.cpp:741
G::Xtext::decode
static std::string decode(const std::string &)
Decodes the given string.
Definition: gxtext.cpp:117
G::Xtext::valid
static bool valid(const std::string &, bool strict=false)
Returns true if a valid encoding.
Definition: gxtext.cpp:75
gassert.h
gbase64.h
gmd5.h
gsecret.h
gstr.h
gxtext.h
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:31