1 #include <gflags/gflags.h>
5 // These need to be at least the length of any string it's trying to match.
6 DEFINE_uint64(cmd_bfr_size
, 4 * 1024, "command parser buffer size");
7 DEFINE_uint64(data_bfr_size
, 64 * 1024, "data parser buffer size");
9 DEFINE_uint64(max_xfer_size
, 64 * 1024, "maximum BDAT transfer size");
15 #include "Session.hpp"
18 #include "iobuffer.hpp"
26 #include <tao/pegtl.hpp>
27 #include <tao/pegtl/contrib/abnf.hpp>
29 using namespace tao::pegtl
;
30 using namespace tao::pegtl::abnf
;
32 using namespace std::string_literals
;
42 std::pair
<std::string
, std::string
> param
;
43 std::unordered_map
<std::string
, std::string
> parameters
;
45 std::streamsize chunk_size
;
47 Ctx(fs::path config_path
, std::function
<void(void)> read_hook
)
48 : session(config_path
, read_hook
)
57 struct quoted_pair
: seq
<one
<'\\'>, sor
<VCHAR
, WSP
>> {};
60 using colon
= one
<':'>;
61 using dash
= one
<'-'>;
63 struct u_let_dig
: sor
<ALPHA
, DIGIT
, UTF8_non_ascii
> {};
65 struct u_ldh_tail
: star
<sor
<seq
<plus
<one
<'-'>>, u_let_dig
>, u_let_dig
>> {};
67 struct u_label
: seq
<u_let_dig
, u_ldh_tail
> {};
69 struct let_dig
: sor
<ALPHA
, DIGIT
> {};
71 struct ldh_tail
: star
<sor
<seq
<plus
<one
<'-'>>, let_dig
>, let_dig
>> {};
73 struct ldh_str
: seq
<let_dig
, ldh_tail
> {};
75 // struct label : seq<let_dig, opt<ldh_str>> {};
77 struct sub_domain
: u_label
{};
79 struct domain
: list_tail
<sub_domain
, dot
> {};
81 struct dec_octet
: sor
<seq
<string
<'2','5'>, range
<'0','5'>>,
82 seq
<one
<'2'>, range
<'0','4'>, DIGIT
>,
83 seq
<one
<'1'>, rep
<2, DIGIT
>>,
84 seq
<range
<'1', '9'>, DIGIT
>,
87 struct IPv4_address_literal
88 : seq
<dec_octet
, dot
, dec_octet
, dot
, dec_octet
, dot
, dec_octet
> {};
90 struct h16
: rep_min_max
<1, 4, HEXDIG
> {};
92 struct ls32
: sor
<seq
<h16
, colon
, h16
>, IPv4_address_literal
> {};
94 struct dcolon
: two
<':'> {};
96 struct IPv6address
: sor
<seq
< rep
<6, h16
, colon
>, ls32
>,
97 seq
< dcolon
, rep
<5, h16
, colon
>, ls32
>,
98 seq
<opt
<h16
>, dcolon
, rep
<4, h16
, colon
>, ls32
>,
99 seq
<opt
<h16
, opt
< colon
, h16
>>, dcolon
, rep
<3, h16
, colon
>, ls32
>,
100 seq
<opt
<h16
, rep_opt
<2, colon
, h16
>>, dcolon
, rep
<2, h16
, colon
>, ls32
>,
101 seq
<opt
<h16
, rep_opt
<3, colon
, h16
>>, dcolon
, h16
, colon
, ls32
>,
102 seq
<opt
<h16
, rep_opt
<4, colon
, h16
>>, dcolon
, ls32
>,
103 seq
<opt
<h16
, rep_opt
<5, colon
, h16
>>, dcolon
, h16
>,
104 seq
<opt
<h16
, rep_opt
<6, colon
, h16
>>, dcolon
>> {};
106 struct IPv6_address_literal
: seq
<TAO_PEGTL_ISTRING("IPv6:"), IPv6address
> {};
108 struct dcontent
: ranges
<33, 90, 94, 126> {};
110 struct standardized_tag
: ldh_str
{};
112 struct general_address_literal
: seq
<standardized_tag
, colon
, plus
<dcontent
>> {};
114 // See rfc 5321 Section 4.1.3
115 struct address_literal
: seq
<one
<'['>,
116 sor
<IPv4_address_literal
,
117 IPv6_address_literal
,
118 general_address_literal
>,
121 struct at_domain
: seq
<one
<'@'>, domain
> {};
123 // The qtextSMTP rule explained: it's ASCII...
124 // excluding all the control chars below SPACE
125 // 34 '"' the double quote
126 // 92 '\\' the back slash
129 // 32-33: ' ' and '!'
130 // 35-91: "#$%&'()*+,-./" 0-9 ":;<=>?@" A-Z '['
131 // 93-126: "]^_`" a-z "{|}~"
133 struct qtextSMTP
: sor
<ranges
<32, 33, 35, 91, 93, 126>, UTF8_non_ascii
> {};
135 struct graphic
: range
<32, 126> {};
137 struct quoted_pairSMTP
: seq
<one
<'\\'>, graphic
> {};
139 struct qcontentSMTP
: sor
<qtextSMTP
, quoted_pairSMTP
> {};
141 struct quoted_string
: seq
<one
<'"'>, plus
<qcontentSMTP
>, one
<'"'>> {};
143 // excluded from atext are the “specials”: "()<>[]:;@\\,."
145 struct atext
: sor
<ALPHA
, DIGIT
,
158 struct atom
: plus
<atext
> {};
160 struct dot_string
: list
<atom
, dot
> {};
162 struct local_part
: sor
<dot_string
, quoted_string
> {};
164 struct non_local_part
: sor
<domain
, address_literal
> {};
166 struct mailbox
: seq
<local_part
, one
<'@'>, non_local_part
> {};
168 struct path
: seq
<one
<'<'>, mailbox
, one
<'>'>> {};
170 struct bounce_path
: TAO_PEGTL_ISTRING("<>") {};
172 struct reverse_path
: sor
<path
, bounce_path
> {};
174 struct magic_postmaster
: TAO_PEGTL_ISTRING("<Postmaster>") {};
176 struct forward_path
: sor
<path
, magic_postmaster
> {};
178 struct esmtp_keyword
: seq
<sor
<ALPHA
, DIGIT
>, star
<sor
<ALPHA
, DIGIT
, dash
>>> {};
180 struct esmtp_value
: plus
<sor
<range
<33, 60>, range
<62, 126>, UTF8_non_ascii
>> {};
182 struct esmtp_param
: seq
<esmtp_keyword
, opt
<seq
<one
<'='>, esmtp_value
>>> {};
184 struct mail_parameters
: list
<esmtp_param
, SP
> {};
186 struct rcpt_parameters
: list
<esmtp_param
, SP
> {};
188 struct string
: sor
<quoted_string
, atom
> {};
190 struct helo
: seq
<TAO_PEGTL_ISTRING("HELO"),
192 sor
<domain
, address_literal
>,
195 struct ehlo
: seq
<TAO_PEGTL_ISTRING("EHLO"),
197 sor
<domain
, address_literal
>,
200 struct mail_from
: seq
<TAO_PEGTL_ISTRING("MAIL"),
201 TAO_PEGTL_ISTRING(" FROM:"),
202 opt
<SP
>, // common enough error, we'll allow it
204 opt
<seq
<SP
, mail_parameters
>>,
207 struct rcpt_to
: seq
<TAO_PEGTL_ISTRING("RCPT"),
208 TAO_PEGTL_ISTRING(" TO:"),
209 opt
<SP
>, // common error
211 opt
<seq
<SP
, rcpt_parameters
>>,
214 struct chunk_size
: plus
<DIGIT
> {};
216 struct last
: TAO_PEGTL_ISTRING("LAST") {};
218 struct bdat
: seq
<TAO_PEGTL_ISTRING("BDAT"), SP
, chunk_size
, CRLF
> {};
220 struct bdat_last
: seq
<TAO_PEGTL_ISTRING("BDAT"), SP
, chunk_size
, SP
, last
, CRLF
> {};
222 struct data
: seq
<TAO_PEGTL_ISTRING("DATA"), CRLF
> {};
224 // ## below: DATA sub parse ##
226 struct data_end
: seq
<dot
, CRLF
> {};
228 struct data_blank
: CRLF
{};
230 // This particular crud will trigger an error return with the "no bare
233 struct data_not_end
: seq
<dot
, LF
> {};
235 struct data_also_not_end
: seq
<LF
, dot
, CRLF
> {};
238 // RFC 5321 text line length section 4.5.3.1.6.
239 struct data_plain
: seq
<rep_min_max
<1, 998, not_one
<'\r', '\n'>>, CRLF
> {};
241 struct data_dot
: seq
<one
<'.'>, rep_min_max
<1, 998, not_one
<'\r', '\n'>>, CRLF
> {};
243 struct data_long
: seq
<star
<not_one
<'\r', '\n'>>, CRLF
> {};
245 struct data_line
: seq
<sor
<data_blank
,
253 struct data_grammar
: until
<data_end
, data_line
> {};
255 // ## above: DATA sub parse ##
257 struct rset
: seq
<TAO_PEGTL_ISTRING("RSET"), CRLF
> {};
259 struct noop
: seq
<TAO_PEGTL_ISTRING("NOOP"), opt
<seq
<SP
, string
>>, CRLF
> {};
261 struct vrfy
: seq
<TAO_PEGTL_ISTRING("VRFY"), opt
<seq
<SP
, string
>>, CRLF
> {};
263 struct help
: seq
<TAO_PEGTL_ISTRING("HELP"), opt
<seq
<SP
, string
>>, CRLF
> {};
266 : seq
<TAO_PEGTL_ISTRING("STAR"), TAO_PEGTL_ISTRING("TTLS"), CRLF
> {};
268 struct quit
: seq
<TAO_PEGTL_ISTRING("QUIT"), CRLF
> {};
272 // base64-char = ALPHA / DIGIT / "+" / "/"
275 struct base64_char
: sor
<ALPHA
, DIGIT
, one
<'+', '/'>> {};
277 // base64-terminal = (2base64-char "==") / (3base64-char "=")
279 struct base64_terminal
: sor
<seq
<rep
<2, base64_char
>, TAO_PEGTL_ISTRING("==")>,
280 seq
<rep
<3, base64_char
>, one
<'='>>
283 // base64 = base64-terminal /
284 // ( 1*(4base64-char) [base64-terminal] )
286 struct base64
: sor
<base64_terminal
,
287 seq
<plus
<rep
<4, base64_char
>>,
288 opt
<base64_terminal
>>
291 // initial-response= base64 / "="
293 struct initial_response
: sor
<base64
, one
<'='>> {};
295 // cancel-response = "*"
297 struct cancel_response
: one
<'*'> {};
299 struct UPPER_ALPHA
: range
<'A', 'Z'> {};
301 using HYPHEN
= one
<'-'>;
302 using UNDERSCORE
= one
<'_'>;
304 struct mech_char
: sor
<UPPER_ALPHA
, DIGIT
, HYPHEN
, UNDERSCORE
> {};
305 struct sasl_mech
: rep_min_max
<1, 20, mech_char
> {};
307 // auth-command = "AUTH" SP sasl-mech [SP initial-response]
308 // *(CRLF [base64]) [CRLF cancel-response]
310 // ;; <sasl-mech> is defined in RFC 4422
312 struct auth
: seq
<TAO_PEGTL_ISTRING("AUTH"), SP
, sasl_mech
,
313 opt
<seq
<SP
, initial_response
>>,
314 // star<CRLF, opt<base64>>,
315 // opt<seq<CRLF, cancel_response>>,
319 struct bogus_cmd_short
: seq
<rep_min_max
<0, 3, not_one
<'\r', '\n'>>, CRLF
> {};
320 struct bogus_cmd_long
: seq
<rep_min_max
<4, 1000, not_one
<'\r', '\n'>>, CRLF
> {};
322 // commands in size order
324 struct any_cmd
: seq
<sor
<bogus_cmd_short
,
342 struct grammar
: plus
<any_cmd
> {};
346 template <typename Rule
>
347 struct action
: nothing
<Rule
> {
350 template <typename Rule
>
351 struct data_action
: nothing
<Rule
> {
355 struct action
<bogus_cmd_short
> {
356 template <typename Input
>
357 static void apply(Input
const& in
, Ctx
& ctx
)
359 LOG(INFO
) << "bogus_cmd_short";
360 ctx
.session
.cmd_unrecognized(in
.string());
365 struct action
<bogus_cmd_long
> {
366 template <typename Input
>
367 static void apply(Input
const& in
, Ctx
& ctx
)
369 LOG(INFO
) << "bogus_cmd_long";
370 ctx
.session
.cmd_unrecognized(in
.string());
375 struct action
<esmtp_keyword
> {
376 template <typename Input
>
377 static void apply(Input
const& in
, Ctx
& ctx
)
379 ctx
.param
.first
= in
.string();
384 struct action
<esmtp_value
> {
385 template <typename Input
>
386 static void apply(Input
const& in
, Ctx
& ctx
)
388 ctx
.param
.second
= in
.string();
393 struct action
<esmtp_param
> {
394 template <typename Input
>
395 static void apply(Input
const& in
, Ctx
& ctx
)
397 ctx
.parameters
.insert(ctx
.param
);
398 ctx
.param
.first
.clear();
399 ctx
.param
.second
.clear();
404 struct action
<local_part
> {
405 template <typename Input
>
406 static void apply(Input
const& in
, Ctx
& ctx
)
408 ctx
.mb_loc
= in
.string();
409 // RFC 5321, section 4.5.3.1.1.
410 if (ctx
.mb_loc
.length() > 64) {
411 LOG(INFO
) << "local part «" << ctx
.mb_loc
412 << "» length == " << ctx
.mb_loc
.length();
418 struct action
<non_local_part
> {
419 template <typename Input
>
420 static void apply(Input
const& in
, Ctx
& ctx
)
422 ctx
.mb_dom
= in
.string();
423 // RFC 5321, section 4.5.3.1.2.
424 if (ctx
.mb_dom
.length() > 253) {
425 LOG(WARNING
) << "domain name too long " << ctx
.mb_dom
;
431 struct action
<bounce_path
> {
432 static void apply0(Ctx
& ctx
)
440 struct action
<magic_postmaster
> {
441 static void apply0(Ctx
& ctx
)
443 ctx
.mb_loc
= std::string
{"Postmaster"};
449 struct action
<helo
> {
450 template <typename Input
>
451 static void apply(Input
const& in
, Ctx
& ctx
)
453 auto const b
= begin(in
) + 5; // +5 for the length of "HELO "
454 auto const e
= std::find(b
, end(in
) - 2, ' '); // -2 for the CRLF
455 ctx
.session
.helo(std::string_view(b
, e
- b
));
460 struct action
<ehlo
> {
461 template <typename Input
>
462 static void apply(Input
const& in
, Ctx
& ctx
)
464 auto const b
= begin(in
) + 5; // +5 for the length of "EHLO "
465 auto const e
= std::find(b
, end(in
) - 2, ' '); // -2 for the CRLF
466 ctx
.session
.ehlo(std::string_view(b
, e
- b
));
471 struct action
<mail_from
> {
472 static void apply0(Ctx
& ctx
)
476 if (ctx
.mb_loc
!= ""s
&& ctx
.mb_dom
!= ""s
) {
477 mbx
= Mailbox
{ctx
.mb_loc
, Domain
{ctx
.mb_dom
}};
479 ctx
.session
.mail_from(std::move(mbx
), ctx
.parameters
);
482 ctx
.parameters
.clear();
487 struct action
<rcpt_to
> {
488 static void apply0(Ctx
& ctx
)
492 if (ctx
.mb_loc
== "Postmaster"s
) {
493 mbx
= Mailbox
{"Postmaster"};
496 mbx
= Mailbox
{ctx
.mb_loc
, Domain
{ctx
.mb_dom
}};
498 ctx
.session
.rcpt_to(std::move(mbx
), ctx
.parameters
);
501 ctx
.parameters
.clear();
506 struct action
<chunk_size
> {
507 template <typename Input
>
508 static void apply(Input
const& in
, Ctx
& ctx
)
510 ctx
.chunk_size
= std::strtoull(in
.string().c_str(), nullptr, 10);
514 void bdat_act(Ctx
& ctx
, bool last
)
516 auto status_returned
= false;
518 if (!ctx
.session
.bdat_start(ctx
.chunk_size
))
519 status_returned
= true;
521 auto to_xfer
= ctx
.chunk_size
;
523 auto const bfr_size
{std::min(to_xfer
, std::streamsize(FLAGS_max_xfer_size
))};
524 iobuffer
<char> bfr(bfr_size
);
527 auto const xfer_sz
{std::min(to_xfer
, bfr_size
)};
529 ctx
.session
.in().read(bfr
.data(), xfer_sz
);
530 if (!ctx
.session
.in()) {
531 LOG(ERROR
) << "attempt to read " << xfer_sz
<< " octets but only got "
532 << ctx
.session
.in().gcount();
533 if (ctx
.session
.maxed_out()) {
534 LOG(ERROR
) << "input maxed out";
535 if (!status_returned
)
536 ctx
.session
.bdat_size_error();
538 else if (ctx
.session
.timed_out()) {
539 LOG(ERROR
) << "input timed out";
540 if (!status_returned
)
541 ctx
.session
.bdat_io_error();
543 else if (ctx
.session
.in().eof()) {
544 LOG(ERROR
) << "EOF in BDAT";
545 if (!status_returned
)
546 ctx
.session
.bdat_io_error();
549 LOG(ERROR
) << "I/O error in BDAT";
550 if (!status_returned
)
551 ctx
.session
.bdat_io_error();
555 if (!status_returned
&& !ctx
.session
.msg_write(bfr
.data(), xfer_sz
)) {
556 status_returned
= true;
562 if (!status_returned
) {
563 ctx
.session
.bdat_done(ctx
.chunk_size
, last
);
568 struct action
<bdat
> {
569 static void apply0(Ctx
& ctx
)
571 bdat_act(ctx
, false);
576 struct action
<bdat_last
> {
577 static void apply0(Ctx
& ctx
)
584 struct data_action
<data_end
> {
585 static void apply0(Ctx
& ctx
) { ctx
.session
.data_done(); }
589 struct data_action
<data_blank
> {
590 static void apply0(Ctx
& ctx
)
592 constexpr char CRLF
[]{'\r', '\n'};
593 ctx
.session
.msg_write(CRLF
, sizeof(CRLF
));
598 struct data_action
<data_plain
> {
599 template <typename Input
>
600 static void apply(Input
const& in
, Ctx
& ctx
)
602 auto const len
{end(in
) - begin(in
)};
603 ctx
.session
.msg_write(begin(in
), len
);
608 struct data_action
<data_dot
> {
609 template <typename Input
>
610 static void apply(Input
const& in
, Ctx
& ctx
)
612 auto const len
{end(in
) - begin(in
) - 1};
613 ctx
.session
.msg_write(begin(in
) + 1, len
);
618 struct data_action
<data_long
> {
619 template <typename Input
>
620 static void apply(Input
const& in
, Ctx
& ctx
)
622 LOG(WARNING
) << "garbage in data stream: \"" << esc(in
.string()) << "\"";
623 auto const len
{end(in
) - begin(in
)};
625 ctx
.session
.msg_write(begin(in
), len
);
627 LOG(WARNING
) << "line too long at " << len
<< " octets";
633 struct data_action
<data_not_end
> {
634 static void apply0(Ctx
& ctx
) __attribute__((noreturn
))
636 ctx
.session
.bare_lf();
641 struct data_action
<data_also_not_end
> {
642 static void apply0(Ctx
& ctx
) __attribute__((noreturn
))
644 ctx
.session
.bare_lf();
649 struct action
<data
> {
650 template <typename Input
>
651 static void apply(Input
const& in
, Ctx
& ctx
)
653 if (ctx
.session
.data_start()) {
654 auto din
= istream_input
<eol::crlf
, 1>(ctx
.session
.in(),
655 FLAGS_data_bfr_size
, "data");
657 if (!parse_nested
<RFC5321::data_grammar
, RFC5321::data_action
>(in
, din
,
659 ctx
.session
.log_stats();
660 if (!(ctx
.session
.maxed_out() || ctx
.session
.timed_out())) {
661 ctx
.session
.error("bad DATA syntax");
666 catch (parse_error
const& e
) {
667 LOG(WARNING
) << e
.what();
668 ctx
.session
.error("unable to parse DATA stream");
670 catch (std::exception
const& e
) {
671 LOG(WARNING
) << e
.what();
672 ctx
.session
.error("unknown problem in DATA stream");
679 struct action
<rset
> {
680 static void apply0(Ctx
& ctx
) { ctx
.session
.rset(); }
683 template <typename Input
>
684 std::string_view
get_string_view(Input
const& in
)
686 auto const b
= begin(in
) + 4;
687 auto const len
= end(in
) - b
;
688 auto str
= std::string_view(b
, len
);
689 if (str
.front() == ' ')
690 str
.remove_prefix(1);
695 struct action
<noop
> {
696 template <typename Input
>
697 static void apply(Input
const& in
, Ctx
& ctx
)
699 auto const str
= get_string_view(in
);
700 ctx
.session
.noop(str
);
705 struct action
<vrfy
> {
706 template <typename Input
>
707 static void apply(Input
const& in
, Ctx
& ctx
)
709 auto const str
= get_string_view(in
);
710 ctx
.session
.vrfy(str
);
715 struct action
<help
> {
716 template <typename Input
>
717 static void apply(Input
const& in
, Ctx
& ctx
)
719 auto const str
= get_string_view(in
);
720 ctx
.session
.help(str
);
725 struct action
<starttls
> {
726 static void apply0(Ctx
& ctx
) { ctx
.session
.starttls(); }
730 struct action
<quit
> {
731 static void apply0(Ctx
& ctx
) __attribute__((noreturn
)) { ctx
.session
.quit(); }
735 struct action
<auth
> {
736 static void apply0(Ctx
& ctx
) __attribute__((noreturn
)) { ctx
.session
.auth(); }
738 } // namespace RFC5321
740 void timeout(int signum
) __attribute__((noreturn
));
741 void timeout(int signum
)
743 const char errmsg
[] = "421 4.4.2 time-out\r\n";
744 write(STDOUT_FILENO
, errmsg
, sizeof errmsg
- 1);
748 void install_syscall_filter()
750 /// scmp_filter_ctx ctx = CHECK_NOTNULL(seccomp_init(SCMP_ACT_ERRNO(EPERM)));
751 scmp_filter_ctx ctx
= CHECK_NOTNULL(seccomp_init(SCMP_ACT_LOG
));
753 auto rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(write
), 0);
754 CHECK_EQ(rc
, 0) << "seccomp_rule_add write failed";
756 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(read
), 0);
757 CHECK_EQ(rc
, 0) << "seccomp_rule_add read failed";
759 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(close
), 0);
760 CHECK_EQ(rc
, 0) << "seccomp_rule_add close failed";
762 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(rt_sigprocmask
), 0);
763 CHECK_EQ(rc
, 0) << "seccomp_rule_add rt_sigprocmask failed";
765 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(rt_sigreturn
), 0);
766 CHECK_EQ(rc
, 0) << "seccomp_rule_add rt_sigreturn failed";
768 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(rt_sigaction
), 0);
769 CHECK_EQ(rc
, 0) << "seccomp_rule_add rt_sigaction failed";
771 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(fstat
), 0);
772 CHECK_EQ(rc
, 0) << "seccomp_rule_add fstat failed";
774 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(openat
), 0);
775 CHECK_EQ(rc
, 0) << "seccomp_rule_add openat failed";
777 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(socket
), 0);
778 CHECK_EQ(rc
, 0) << "seccomp_rule_add socket failed";
780 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(connect
), 0);
781 CHECK_EQ(rc
, 0) << "seccomp_rule_add connect failed";
783 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(poll
), 0);
784 CHECK_EQ(rc
, 0) << "seccomp_rule_add poll failed";
786 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(sendto
), 0);
787 CHECK_EQ(rc
, 0) << "seccomp_rule_add sendto failed";
789 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(recvfrom
), 0);
790 CHECK_EQ(rc
, 0) << "seccomp_rule_add recvfrom failed";
792 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(sigaltstack
), 0);
793 CHECK_EQ(rc
, 0) << "seccomp_rule_add sigaltstack failed";
795 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(prctl
), 0);
796 CHECK_EQ(rc
, 0) << "seccomp_rule_add prctl failed";
798 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(gettid
), 0);
799 CHECK_EQ(rc
, 0) << "seccomp_rule_add gettid failed";
801 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(getpid
), 0);
802 CHECK_EQ(rc
, 0) << "seccomp_rule_add getpid failed";
804 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(getppid
), 0);
805 CHECK_EQ(rc
, 0) << "seccomp_rule_add getppid failed";
807 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(newfstatat
), 0);
808 CHECK_EQ(rc
, 0) << "seccomp_rule_add newfstatat failed";
810 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(tgkill
), 0);
811 CHECK_EQ(rc
, 0) << "seccomp_rule_add tgkill failed";
813 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(dup
), 0);
814 CHECK_EQ(rc
, 0) << "seccomp_rule_add dup failed";
816 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(mmap
), 0);
817 CHECK_EQ(rc
, 0) << "seccomp_rule_add mmap failed";
819 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(munmap
), 0);
820 CHECK_EQ(rc
, 0) << "seccomp_rule_add munmap failed";
822 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(pipe2
), 0);
823 CHECK_EQ(rc
, 0) << "seccomp_rule_add pipe2 failed";
825 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(readlinkat
), 0);
826 CHECK_EQ(rc
, 0) << "seccomp_rule_add readlinkat failed";
828 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(pselect6
), 0);
829 CHECK_EQ(rc
, 0) << "seccomp_rule_add pselect6 failed";
831 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(uname
), 0);
832 CHECK_EQ(rc
, 0) << "seccomp_rule_add uname failed";
834 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(fcntl
), 0);
835 CHECK_EQ(rc
, 0) << "seccomp_rule_add fcntl failed";
837 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(unlink
), 0);
838 CHECK_EQ(rc
, 0) << "seccomp_rule_add unlink failed";
840 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(symlink
), 0);
841 CHECK_EQ(rc
, 0) << "seccomp_rule_add symlink failed";
843 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(madvise
), 0);
844 CHECK_EQ(rc
, 0) << "seccomp_rule_add madvise failed";
846 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(mprotect
), 0);
847 CHECK_EQ(rc
, 0) << "seccomp_rule_add mprotect failed";
849 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(futex
), 0);
850 CHECK_EQ(rc
, 0) << "seccomp_rule_add futex failed";
852 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(writev
), 0);
853 CHECK_EQ(rc
, 0) << "seccomp_rule_add writev failed";
855 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(sysinfo
), 0);
856 CHECK_EQ(rc
, 0) << "seccomp_rule_add sysinfo failed";
858 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(brk
), 0);
859 CHECK_EQ(rc
, 0) << "seccomp_rule_add brk failed";
861 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(getrandom
), 0);
862 CHECK_EQ(rc
, 0) << "seccomp_rule_add getrandom failed";
864 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(getdents64
), 0);
865 CHECK_EQ(rc
, 0) << "seccomp_rule_add getdents64 failed";
867 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(lseek
), 0);
868 CHECK_EQ(rc
, 0) << "seccomp_rule_add lseek failed";
870 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(setsockopt
), 0);
871 CHECK_EQ(rc
, 0) << "seccomp_rule_add setsockopt failed";
873 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(alarm
), 0);
874 CHECK_EQ(rc
, 0) << "seccomp_rule_add alarm failed";
876 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(rename
), 0);
877 CHECK_EQ(rc
, 0) << "seccomp_rule_add rename failed";
879 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(clock_gettime
), 0);
880 CHECK_EQ(rc
, 0) << "seccomp_rule_add clock_gettime failed";
882 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(exit_group
), 0);
883 CHECK_EQ(rc
, 0) << "seccomp_rule_add exit_group failed";
885 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(exit
), 0);
886 CHECK_EQ(rc
, 0) << "seccomp_rule_add exit failed";
890 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(clone
), 0);
891 CHECK_EQ(rc
, 0) << "seccomp_rule_add clone failed";
893 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(wait4
), 0);
894 CHECK_EQ(rc
, 0) << "seccomp_rule_add wait4 failed";
896 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(sched_yield
), 0);
897 CHECK_EQ(rc
, 0) << "seccomp_rule_add sched_yield failed";
899 rc
= seccomp_rule_add(ctx
, SCMP_ACT_ALLOW
, SCMP_SYS(ptrace
), 0);
900 CHECK_EQ(rc
, 0) << "seccomp_rule_add ptrace failed";
902 rc
= seccomp_load(ctx
);
903 CHECK_EQ(rc
, 0) << "seccomp_load failed";
905 // seccomp_export_pfc(ctx, STDERR_FILENO);
907 seccomp_release(ctx
);
910 int main(int argc
, char* argv
[])
912 std::ios::sync_with_stdio(false);
914 { // Need to work with either namespace.
915 using namespace gflags
;
916 using namespace google
;
917 ParseCommandLineFlags(&argc
, &argv
, true);
920 // Set timeout signal handler to limit total run time.
921 struct sigaction sact
{};
922 PCHECK(sigemptyset(&sact
.sa_mask
) == 0);
924 sact
.sa_handler
= timeout
;
925 PCHECK(sigaction(SIGALRM
, &sact
, nullptr) == 0);
927 auto const log_dir
{getenv("GOOGLE_LOG_DIR")};
930 fs::create_directories(log_dir
, ec
);
933 google::InitGoogleLogging(argv
[0]);
935 std::unique_ptr
<RFC5321::Ctx
> ctx
;
937 // Don't wait for STARTTLS to fail if no cert.
938 auto const config_path
= osutil::get_config_dir();
939 auto const certs
= osutil::list_directory(config_path
, Config::cert_fn_re
);
940 CHECK_GE(certs
.size(), 1) << "no certs found";
942 auto const read_hook
{[&ctx
]() { ctx
->session
.flush(); }};
943 ctx
= std::make_unique
<RFC5321::Ctx
>(config_path
, read_hook
);
945 ctx
->session
.greeting();
947 install_syscall_filter();
949 istream_input
<eol::crlf
, 1> in
{ctx
->session
.in(), FLAGS_cmd_bfr_size
,
954 ret
= !parse
<RFC5321::grammar
, RFC5321::action
>(in
, *ctx
);
956 catch (std::exception
const& e
) {
957 LOG(WARNING
) << e
.what();
960 if (ctx
->session
.maxed_out()) {
961 ctx
->session
.max_out();
963 else if (ctx
->session
.timed_out()) {
964 ctx
->session
.time_out();
967 // ctx->session.error("session end without QUIT command from client");