2 #include "SockBuffer.hpp"
4 #include <boost/iostreams/concepts.hpp>
5 #include <boost/iostreams/device/file_descriptor.hpp>
6 #include <boost/iostreams/stream.hpp>
10 #include <unordered_map>
12 #include <fmt/format.h>
15 #include <sys/socket.h>
18 #include <sys/types.h>
20 #include <glog/logging.h>
22 #include <tao/pegtl.hpp>
23 #include <tao/pegtl/contrib/abnf.hpp>
25 #include "test-credentials.ipp"
27 using namespace std::string_literals
;
29 using namespace tao::pegtl
;
30 using namespace tao::pegtl::abnf
;
35 using HYPHEN
= one
<'-'>;
36 using UNDERSCORE
= one
<'_'>;
38 struct id
: plus
<DIGIT
> {};
39 struct pid
: plus
<DIGIT
> {};
40 struct cookie
: rep
<32, HEXDIG
> {};
42 struct base64_char
: sor
<ALPHA
, DIGIT
, one
<'+'>, one
<'/'>> {};
43 struct base64_data
: seq
<plus
<base64_char
>, rep_min_max
<0, 2, one
<'='>>> {};
45 struct param_char
: sor
<ALPHA
, DIGIT
, HYPHEN
, UNDERSCORE
> {};
46 struct param_name
: rep_min_max
<1, 20, param_char
> {};
48 struct param_vchar
: not_one
<'\t'> {};
49 struct param_val
: rep_min_max
<1, 200, param_vchar
> {};
51 struct parameter
: sor
<param_name
, seq
<param_name
, one
<'='>, param_val
>> {};
53 struct UPPER_ALPHA
: range
<'A', 'Z'> {};
55 struct mech_char
: sor
<UPPER_ALPHA
, DIGIT
, HYPHEN
, UNDERSCORE
> {};
56 struct sasl_mech
: rep_min_max
<1, 20, mech_char
> {};
58 struct vers
: seq
<TAO_PEGTL_STRING("VERSION"), HTAB
, one
<'1'>, HTAB
, DIGIT
, LF
> {};
59 struct mech
: seq
<TAO_PEGTL_STRING("MECH"), HTAB
, sasl_mech
, star
<seq
<HTAB
, parameter
>>, LF
> {};
60 struct spid
: seq
<TAO_PEGTL_STRING("SPID"), HTAB
, pid
, LF
> {};
61 struct cuid
: seq
<TAO_PEGTL_STRING("CUID"), HTAB
, pid
, LF
> {};
62 struct cook
: seq
<TAO_PEGTL_STRING("COOKIE"), HTAB
, cookie
, LF
> {};
63 struct done
: seq
<TAO_PEGTL_STRING("DONE"), LF
> {};
65 struct resp
: seq
<vers
, star
<mech
>, spid
, cuid
, cook
, done
, discard
> {};
67 struct auth_ok
: seq
<TAO_PEGTL_STRING("OK"), HTAB
, id
, star
<seq
<HTAB
, parameter
>>> {};
68 struct auth_cont
: seq
<TAO_PEGTL_STRING("CONT"), HTAB
, id
, HTAB
, base64_data
> {};
69 struct auth_fail
: seq
<TAO_PEGTL_STRING("FAIL"), HTAB
, id
, star
<seq
<HTAB
, parameter
>>> {};
71 struct auth_resp
: seq
<sor
<auth_ok
, auth_cont
, auth_fail
>, discard
> {};
76 using parameters_t
= std::vector
<std::string
>;
77 using mechs_t
= std::unordered_map
<std::string
, parameters_t
>;
81 std::string sasl_mech
;
83 parameters_t parameters
;
86 enum class auth_response
{ none
, ok
, cont
, fail
};
89 static constexpr auto none
= auth_response::none
;
90 static constexpr auto ok
= auth_response::ok
;
91 static constexpr auto cont
= auth_response::cont
;
92 static constexpr auto fail
= auth_response::fail
;
94 static constexpr char const* c_str(auth_response rsp
)
97 case none
: return "none";
99 case cont
: return "cont";
100 case fail
: return "fail";
102 return "** unknown **";
106 auth_response auth_resp
{none
};
109 std::ostream
& operator<<(std::ostream
& os
, Context::auth_response rsp
)
111 return os
<< Context::c_str(rsp
);
114 template <typename Rule
>
115 struct action
: nothing
<Rule
> {
120 template <typename Input
>
121 static void apply(Input
const& in
, Context
& ctx
)
123 ctx
.id
= strtoul(in
.string().c_str(), nullptr, 10);
128 struct action
<cookie
> {
129 template <typename Input
>
130 static void apply(Input
const& in
, Context
& ctx
)
132 ctx
.cookie
= in
.string();
137 struct action
<parameter
> {
138 template <typename Input
>
139 static void apply(Input
const& in
, Context
& ctx
)
141 ctx
.parameters
.push_back(in
.string());
146 struct action
<sasl_mech
> {
147 template <typename Input
>
148 static void apply(Input
const& in
, Context
& ctx
)
150 ctx
.sasl_mech
= in
.string();
155 struct action
<mech
> {
156 template <typename Input
>
157 static void apply(Input
const& in
, Context
& ctx
)
159 ctx
.mechs
.emplace(std::move(ctx
.sasl_mech
), std::move(ctx
.parameters
));
164 struct action
<auth_ok
> {
165 static void apply0(Context
& ctx
)
167 ctx
.auth_resp
= Context::auth_response::ok
;
172 struct action
<auth_cont
> {
173 static void apply0(Context
& ctx
)
175 ctx
.auth_resp
= Context::auth_response::cont
;
180 struct action
<auth_fail
> {
181 static void apply0(Context
& ctx
)
183 ctx
.auth_resp
= Context::auth_response::fail
;
186 } // namespace dovecot
189 constexpr char const* defined_params
[]{
202 auto const fd
{socket(AF_UNIX
, SOCK_STREAM
, 0)};
203 PCHECK(fd
>= 0) << "socket failed";
205 sockaddr_un addr
= {.sun_family
= AF_UNIX
};
207 constexpr char socket_path
[]{"/var/spool/postfix/private/auth"};
208 static_assert(sizeof(socket_path
) <= sizeof(addr
.sun_path
));
209 strcpy(addr
.sun_path
, socket_path
);
211 PCHECK(connect(fd
, reinterpret_cast<sockaddr
*>(&addr
), sizeof(addr
)) == 0)
212 << "connect to " << socket_path
<< " failed";
214 auto ios
{boost::iostreams::stream
<SockBuffer
>{fd
, fd
}};
216 ios
<< "VERSION\t1\t1\n"
217 << "CPID\t" << getpid() << "\n"
220 auto ctx
= dovecot::Context
{};
221 auto in
= istream_input
<eol::lf
, 1>{ios
, 8 * 1024, "sasl"};
222 if (!parse
<dovecot::resp
, dovecot::action
>(in
, ctx
)) {
223 LOG(WARNING
) << "handshake response parse failed";
226 for (auto const& m
: ctx
.mechs
) {
227 LOG(INFO
) << m
.first
;
230 auto const tok
{fmt::format("\0{}\0{}"s
, test::username
, test::password
)};
231 auto const init
{Base64::enc(tok
)};
233 if (ctx
.mechs
.find("PLAIN") != end(ctx
.mechs
)) {
234 auto id
{uint32_t{0x12345678}};
236 ios
<< "AUTH\t" << id
<< "\tPLAIN"
238 << "\tresp=" << init
<< '\n'
241 if (!parse
<dovecot::auth_resp
, dovecot::action
>(in
, ctx
)) {
242 LOG(WARNING
) << "auth response parse failed";
245 CHECK_EQ(ctx
.id
, id
);
246 LOG(INFO
) << "AUTH: " << ctx
.auth_resp
;