E-MailRelay

guserverifier.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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 guserverifier.cpp
19///
20
21#include "gdef.h"
22#include "guserverifier.h"
23#include "gidentity.h"
24#include "grange.h"
25#include "gstr.h"
26#include "gstringtoken.h"
27#include "glog.h"
28#include <sstream>
29
30 GVerifiers::UserVerifier::UserVerifier( GNet::EventState es ,
31 const GSmtp::Verifier::Config & config , const std::string & spec ) :
32 m_config(config) ,
33 m_timer(*this,&UserVerifier::onTimeout,es) ,
34 m_result(GSmtp::VerifierStatus::invalid({})) ,
35 m_range(G::Range::range(1000,32767))
36{
37 std::string_view spec_view( spec ) ;
38 for( G::StringTokenView t( spec_view , ";" , 1U ) ; t ; ++t )
39 {
40 if( !t().empty() && G::Str::isNumeric(t().substr(0U,1U)) )
41 m_range = G::Range::range( t() ) ;
42 else if( ( t().size() <= 3U && t().find('l') != std::string::npos ) || t() == "lowercase"_sv )
43 m_config_lc = true ;
44 else if( ( t().size() <= 3U && t().find('r') != std::string::npos ) || t() == "remote"_sv )
45 m_config_remote = true ;
46 else if( ( t().size() <= 3U && t().find('c') != std::string::npos ) || t() == "check"_sv )
47 m_config_check = true ;
48 }
49 G_DEBUG( "GVerifiers::UserVerifier: uid range " << G::Range::str(m_range) ) ;
50}
51
52 void GVerifiers::UserVerifier::verify( const GSmtp::Verifier::Request & request )
53{
54 m_command = request.command ;
55
56 std::string_view request_address = dequote( request.address ) ;
57
58 std::size_t at_pos = request_address.rfind( '@' ) ;
59 std::string_view user = dequote( G::Str::headView( request_address , at_pos , request_address ) ) ;
60 std::string_view domain = G::Str::tailView( request_address , at_pos ) ;
61
62 std::string reason ;
63 std::string mailbox ;
64 if( user == "postmaster" && domain.empty() )
65 m_result = GSmtp::VerifierStatus::local( request.address , {} , "postmaster" ) ;
66 else if( lookup(user,domain,&reason,&mailbox) )
67 m_result =
68 m_config_remote ?
69 GSmtp::VerifierStatus::remote( request.address ) :
70 GSmtp::VerifierStatus::local( request.address , {} , m_config_lc?G::Str::lower(mailbox):mailbox ) ;
71 else if( m_config_check )
72 m_result = GSmtp::VerifierStatus::remote( request.address ) ;
73 else
74 m_result = GSmtp::VerifierStatus::invalid( request.address , false , "rejected" , reason ) ;
75
76 m_timer.startTimer( 0U ) ;
77}
78
79 bool GVerifiers::UserVerifier::lookup( std::string_view user , std::string_view domain ,
80 std::string * reason_p , std::string * mailbox_p ) const
81{
82 bool result = false ;
83 std::ostringstream ss ;
84 if( !G::Str::imatch( domain , m_config.domain ) )
85 {
86 ss << "[" << domain << "] does not match [" << m_config.domain << "]" ;
87 }
88 else
89 {
90 auto pair = G::Identity::lookup( user , std::nothrow ) ;
91
92 if( pair.first == G::Identity::invalid() && G::Str::isPrintableAscii(user) && !G::is_windows() )
93 pair = G::Identity::lookup( G::Str::lower(user) , std::nothrow ) ;
94
95 if( pair.first == G::Identity::invalid() || pair.second.empty() )
96 {
97 ss << "[" << user << "] is not a valid account name" ;
98 }
99 else if( !pair.first.match( m_range ) )
100 {
101 ss << "uid " << pair.first.userid() << " is not in the range " << G::Range::str(m_range) ;
102 }
103 else
104 {
105 if( mailbox_p )
106 *mailbox_p = pair.second ;
107 result = true ;
108 }
109 }
110 if( !result && reason_p ) *reason_p = ss.str() ;
111 return result ;
112}
113
114 GVerifiers::UserVerifier::Signal & GVerifiers::UserVerifier::doneSignal()
115{
116 return m_done_signal ;
117}
118
119 void GVerifiers::UserVerifier::cancel()
120{
121 m_timer.cancelTimer() ;
122}
123
124 void GVerifiers::UserVerifier::onTimeout()
125{
126 m_done_signal.emit( m_command , m_result ) ;
127}
128
129std::string_view GVerifiers::UserVerifier::dequote( std::string_view s )
130{
131 if( s.size() >= 2U && s.at(0) == '"' && s.at(s.size()-1U) == '"' )
132 return s.substr( 1U , s.size()-2U ) ;
133 else
134 return s ;
135}
136
GNet::EventState
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
Definition: geventstate.h:131
GSmtp::VerifierStatus::local
static VerifierStatus local(const std::string &recipient, const std::string &full_name, const std::string &mbox)
Constructor for a valid local mailbox.
Definition: gverifierstatus.cpp:54
GSmtp::VerifierStatus::invalid
static VerifierStatus invalid(const std::string &recipient, bool temporary=false, const std::string &response={}, const std::string &reason={})
Factory function for an invalid address.
Definition: gverifierstatus.cpp:31
GSmtp::VerifierStatus::remote
static VerifierStatus remote(const std::string &recipient, const std::string &address={})
Constructor for a valid remote mailbox.
Definition: gverifierstatus.cpp:43
GVerifiers::UserVerifier
A concrete Verifier class that verifies against the password database (ie.
Definition: guserverifier.h:56
GVerifiers::UserVerifier::UserVerifier
UserVerifier(GNet::EventState es, const GSmtp::Verifier::Config &config, const std::string &spec)
Constructor.
Definition: guserverifier.cpp:30
G::Identity::invalid
static Identity invalid() noexcept
Returns an invalid identity.
Definition: gidentity_unix.cpp:80
G::Identity::lookup
static std::pair< Identity, std::string > lookup(std::string_view user)
Does a username lookup returning the identity and the canonical name.
Definition: gidentity_unix.cpp:132
G::Str::lower
static std::string lower(std::string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
Definition: gstr.cpp:824
G::Str::imatch
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
Definition: gstr.cpp:1415
G::Str::isNumeric
static bool isNumeric(std::string_view s, bool allow_minus_sign=false) noexcept
Returns true if every character is a decimal digit.
Definition: gstr.cpp:400
G::Str::tailView
static std::string_view tailView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like tail() but returning a view into the input string.
Definition: gstr.cpp:1337
G::Str::headView
static std::string_view headView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like head() but returning a view into the input string.
Definition: gstr.cpp:1308
G::Str::isPrintableAscii
static bool isPrintableAscii(std::string_view s) noexcept
Returns true if every character is between 0x20 and 0x7e inclusive.
Definition: gstr.cpp:413
G::StringTokenT
A zero-copy string token iterator where the token separators are runs of whitespace characters,...
Definition: gstringtoken.h:54
gidentity.h
glog.h
grange.h
gstr.h
gstringtoken.h
guserverifier.h
GSmtp
SMTP classes.
Definition: gadminserver.h:42
GSmtp::Verifier::Config
Configuration passed to address verifier constructors.
Definition: gverifier.h:57
GSmtp::Verifier::Request
Verification request passed to various GSmtp::Verifier::verify() overrides.
Definition: gverifier.h:47
G::Slot::Signal< GSmtp::Verifier::Command, const GSmtp::VerifierStatus & >