for sanitize
[ghsmtp.git] / smtp.cpp
blob61898760beee1912d7e2cde5c976e8dad59c344c
1 #include <gflags/gflags.h>
2 namespace gflags {
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");
11 #include <seccomp.h>
13 #include <fstream>
15 #include "Session.hpp"
16 #include "esc.hpp"
17 #include "fs.hpp"
18 #include "iobuffer.hpp"
19 #include "osutil.hpp"
21 #include <cstdlib>
22 #include <memory>
24 #include <signal.h>
26 #include <tao/pegtl.hpp>
27 #include <tao/pegtl/contrib/abnf.hpp>
29 using namespace tao::pegtl;
30 using namespace tao::pegtl::abnf;
32 namespace RFC5321 {
34 struct Ctx {
35 Session session;
37 std::string mb_loc;
38 std::string mb_dom;
40 std::pair<std::string, std::string> param;
41 std::unordered_map<std::string, std::string> parameters;
43 std::streamsize chunk_size;
45 Ctx(fs::path config_path, std::function<void(void)> read_hook)
46 : session(config_path, read_hook)
51 // clang-format off
53 struct UTF8_tail : range<'\x80', '\xBF'> {};
55 struct UTF8_1 : range<'\x00', '\x7F'> {};
57 struct UTF8_2 : seq<range<'\xC2', '\xDF'>, UTF8_tail> {};
59 struct UTF8_3 : sor<seq<one<'\xE0'>, range<'\xA0', '\xBF'>, UTF8_tail>,
60 seq<range<'\xE1', '\xEC'>, rep<2, UTF8_tail>>,
61 seq<one<'\xED'>, range<'\x80', '\x9F'>, UTF8_tail>,
62 seq<range<'\xEE', '\xEF'>, rep<2, UTF8_tail>>> {};
64 struct UTF8_4 : sor<seq<one<'\xF0'>, range<'\x90', '\xBF'>, rep<2, UTF8_tail>>,
65 seq<range<'\xF1', '\xF3'>, rep<3, UTF8_tail>>,
66 seq<one<'\xF4'>, range<'\x80', '\x8F'>, rep<2, UTF8_tail>>> {};
68 struct UTF8_non_ascii : sor<UTF8_2, UTF8_3, UTF8_4> {};
70 struct quoted_pair : seq<one<'\\'>, sor<VCHAR, WSP>> {};
72 using dot = one<'.'>;
73 using colon = one<':'>;
74 using dash = one<'-'>;
76 struct u_let_dig : sor<ALPHA, DIGIT, UTF8_non_ascii> {};
78 struct u_ldh_tail : star<sor<seq<plus<one<'-'>>, u_let_dig>, u_let_dig>> {};
80 struct u_label : seq<u_let_dig, u_ldh_tail> {};
82 struct let_dig : sor<ALPHA, DIGIT> {};
84 struct ldh_tail : star<sor<seq<plus<one<'-'>>, let_dig>, let_dig>> {};
86 struct ldh_str : seq<let_dig, ldh_tail> {};
88 // struct label : seq<let_dig, opt<ldh_str>> {};
90 struct sub_domain : u_label {};
92 struct domain : list_tail<sub_domain, dot> {};
94 struct dec_octet : sor<seq<string<'2','5'>, range<'0','5'>>,
95 seq<one<'2'>, range<'0','4'>, DIGIT>,
96 seq<range<'0', '1'>, rep<2, DIGIT>>,
97 rep_min_max<1, 2, DIGIT>> {};
99 struct IPv4_address_literal
100 : seq<dec_octet, dot, dec_octet, dot, dec_octet, dot, dec_octet> {};
102 struct h16 : rep_min_max<1, 4, HEXDIG> {};
104 struct ls32 : sor<seq<h16, colon, h16>, IPv4_address_literal> {};
106 struct dcolon : two<':'> {};
108 struct IPv6address : sor<seq< rep<6, h16, colon>, ls32>,
109 seq< dcolon, rep<5, h16, colon>, ls32>,
110 seq<opt<h16 >, dcolon, rep<4, h16, colon>, ls32>,
111 seq<opt<h16, opt< colon, h16>>, dcolon, rep<3, h16, colon>, ls32>,
112 seq<opt<h16, rep_opt<2, colon, h16>>, dcolon, rep<2, h16, colon>, ls32>,
113 seq<opt<h16, rep_opt<3, colon, h16>>, dcolon, h16, colon, ls32>,
114 seq<opt<h16, rep_opt<4, colon, h16>>, dcolon, ls32>,
115 seq<opt<h16, rep_opt<5, colon, h16>>, dcolon, h16>,
116 seq<opt<h16, rep_opt<6, colon, h16>>, dcolon >> {};
118 struct IPv6_address_literal : seq<TAO_PEGTL_ISTRING("IPv6:"), IPv6address> {};
120 struct dcontent : ranges<33, 90, 94, 126> {};
122 struct standardized_tag : ldh_str {};
124 struct general_address_literal : seq<standardized_tag, colon, plus<dcontent>> {};
126 // See rfc 5321 Section 4.1.3
127 struct address_literal : seq<one<'['>,
128 sor<IPv4_address_literal,
129 IPv6_address_literal,
130 general_address_literal>,
131 one<']'>> {};
133 struct at_domain : seq<one<'@'>, domain> {};
135 // The qtextSMTP rule explained: it's ASCII...
136 // excluding all the control chars below SPACE
137 // 34 '"' the double quote
138 // 92 '\\' the back slash
139 // DEL
140 // So including:
141 // 32-33: ' ' and '!'
142 // 35-91: "#$%&'()*+,-./" 0-9 ":;<=>?@" A-Z '['
143 // 93-126: "]^_`" a-z "{|}~"
145 struct qtextSMTP : sor<ranges<32, 33, 35, 91, 93, 126>, UTF8_non_ascii> {};
147 struct graphic : range<32, 126> {};
149 struct quoted_pairSMTP : seq<one<'\\'>, graphic> {};
151 struct qcontentSMTP : sor<qtextSMTP, quoted_pairSMTP> {};
153 struct quoted_string : seq<one<'"'>, plus<qcontentSMTP>, one<'"'>> {};
155 // excluded from atext are the “specials”: "()<>[]:;@\\,."
157 struct atext : sor<ALPHA, DIGIT,
158 one<'!', '#',
159 '$', '%',
160 '&', '\'',
161 '*', '+',
162 '-', '/',
163 '=', '?',
164 '^', '_',
165 '`', '{',
166 '|', '}',
167 '~'>,
168 UTF8_non_ascii> {};
170 struct atom : plus<atext> {};
172 struct dot_string : list<atom, dot> {};
174 struct local_part : sor<dot_string, quoted_string> {};
176 struct non_local_part : sor<domain, address_literal> {};
178 struct mailbox : seq<local_part, one<'@'>, non_local_part> {};
180 struct path : seq<one<'<'>, mailbox, one<'>'>> {};
182 struct bounce_path : TAO_PEGTL_ISTRING("<>") {};
184 struct reverse_path : sor<path, bounce_path> {};
186 struct magic_postmaster : TAO_PEGTL_ISTRING("<Postmaster>") {};
188 struct forward_path : sor<path, magic_postmaster> {};
190 struct esmtp_keyword : seq<sor<ALPHA, DIGIT>, star<sor<ALPHA, DIGIT, dash>>> {};
192 struct esmtp_value : plus<sor<range<33, 60>, range<62, 126>, UTF8_non_ascii>> {};
194 struct esmtp_param : seq<esmtp_keyword, opt<seq<one<'='>, esmtp_value>>> {};
196 struct mail_parameters : list<esmtp_param, SP> {};
198 struct rcpt_parameters : list<esmtp_param, SP> {};
200 struct string : sor<quoted_string, atom> {};
202 struct helo : seq<TAO_PEGTL_ISTRING("HELO"),
204 sor<domain, address_literal>,
205 CRLF> {};
207 struct ehlo : seq<TAO_PEGTL_ISTRING("EHLO"),
209 sor<domain, address_literal>,
210 CRLF> {};
212 struct mail_from : seq<TAO_PEGTL_ISTRING("MAIL"),
213 TAO_PEGTL_ISTRING(" FROM:"),
214 opt<SP>, // common enough error, we'll allow it
215 reverse_path,
216 opt<seq<SP, mail_parameters>>,
217 CRLF> {};
219 struct rcpt_to : seq<TAO_PEGTL_ISTRING("RCPT"),
220 TAO_PEGTL_ISTRING(" TO:"),
221 opt<SP>, // common error
222 forward_path,
223 opt<seq<SP, rcpt_parameters>>,
224 CRLF> {};
226 struct chunk_size : plus<DIGIT> {};
228 struct last : TAO_PEGTL_ISTRING("LAST") {};
230 struct bdat : seq<TAO_PEGTL_ISTRING("BDAT"), SP, chunk_size, CRLF> {};
232 struct bdat_last : seq<TAO_PEGTL_ISTRING("BDAT"), SP, chunk_size, SP, last, CRLF> {};
234 struct data : seq<TAO_PEGTL_ISTRING("DATA"), CRLF> {};
236 // ## below: DATA sub parse ##
238 struct data_end : seq<dot, CRLF> {};
240 struct data_blank : CRLF {};
242 // This particular crud will trigger an error return with the "no bare
243 // LF" message.
245 struct data_not_end : seq<dot, LF> {};
247 struct data_also_not_end : seq<LF, dot, CRLF> {};
250 // RFC 5321 text line length section 4.5.3.1.6.
251 struct data_plain : seq<rep_min_max<1, 998, not_one<'\r', '\n'>>, CRLF> {};
253 struct data_dot : seq<one<'.'>, rep_min_max<1, 998, not_one<'\r', '\n'>>, CRLF> {};
255 struct data_long : seq<star<not_one<'\r', '\n'>>, CRLF> {};
257 struct data_line : seq<sor<data_blank,
258 data_not_end,
259 data_also_not_end,
260 data_dot,
261 data_plain,
262 data_long>,
263 discard> {};
265 struct data_grammar : until<data_end, data_line> {};
267 // ## above: DATA sub parse ##
269 struct rset : seq<TAO_PEGTL_ISTRING("RSET"), CRLF> {};
271 struct noop : seq<TAO_PEGTL_ISTRING("NOOP"), opt<seq<SP, string>>, CRLF> {};
273 struct vrfy : seq<TAO_PEGTL_ISTRING("VRFY"), opt<seq<SP, string>>, CRLF> {};
275 struct help : seq<TAO_PEGTL_ISTRING("HELP"), opt<seq<SP, string>>, CRLF> {};
277 struct starttls
278 : seq<TAO_PEGTL_ISTRING("STAR"), TAO_PEGTL_ISTRING("TTLS"), CRLF> {};
280 struct quit : seq<TAO_PEGTL_ISTRING("QUIT"), CRLF> {};
282 // Anti-AUTH support
284 // base64-char = ALPHA / DIGIT / "+" / "/"
285 // ;; Case-sensitive
287 struct base64_char : sor<ALPHA, DIGIT, one<'+', '/'>> {};
289 // base64-terminal = (2base64-char "==") / (3base64-char "=")
291 struct base64_terminal : sor<seq<rep<2, base64_char>, TAO_PEGTL_ISTRING("==")>,
292 seq<rep<3, base64_char>, one<'='>>
293 > {};
295 // base64 = base64-terminal /
296 // ( 1*(4base64-char) [base64-terminal] )
298 struct base64 : sor<base64_terminal,
299 seq<plus<rep<4, base64_char>>,
300 opt<base64_terminal>>
301 > {};
303 // initial-response= base64 / "="
305 struct initial_response : sor<base64, one<'='>> {};
307 // cancel-response = "*"
309 struct cancel_response : one<'*'> {};
311 struct UPPER_ALPHA : range<'A', 'Z'> {};
313 using HYPHEN = one<'-'>;
314 using UNDERSCORE = one<'_'>;
316 struct mech_char : sor<UPPER_ALPHA, DIGIT, HYPHEN, UNDERSCORE> {};
317 struct sasl_mech : rep_min_max<1, 20, mech_char> {};
319 // auth-command = "AUTH" SP sasl-mech [SP initial-response]
320 // *(CRLF [base64]) [CRLF cancel-response]
321 // CRLF
322 // ;; <sasl-mech> is defined in RFC 4422
324 struct auth : seq<TAO_PEGTL_ISTRING("AUTH"), SP, sasl_mech,
325 opt<seq<SP, initial_response>>,
326 // star<CRLF, opt<base64>>,
327 // opt<seq<CRLF, cancel_response>>,
328 CRLF> {};
329 // bad commands:
331 struct bogus_cmd_short : seq<rep_min_max<0, 3, not_one<'\r', '\n'>>, CRLF> {};
332 struct bogus_cmd_long : seq<rep_min_max<4, 1000, not_one<'\r', '\n'>>, CRLF> {};
334 // commands in size order
336 struct any_cmd : seq<sor<bogus_cmd_short,
337 data,
338 quit,
339 rset,
340 noop,
341 vrfy,
342 help,
343 helo,
344 ehlo,
345 bdat,
346 bdat_last,
347 starttls,
348 rcpt_to,
349 mail_from,
350 auth,
351 bogus_cmd_long>,
352 discard> {};
354 struct grammar : plus<any_cmd> {};
356 // clang-format on
358 template <typename Rule>
359 struct action : nothing<Rule> {
362 template <typename Rule>
363 struct data_action : nothing<Rule> {
366 template <>
367 struct action<bogus_cmd_short> {
368 template <typename Input>
369 static void apply(Input const& in, Ctx& ctx)
371 LOG(INFO) << "bogus_cmd_short";
372 ctx.session.cmd_unrecognized(in.string());
376 template <>
377 struct action<bogus_cmd_long> {
378 template <typename Input>
379 static void apply(Input const& in, Ctx& ctx)
381 LOG(INFO) << "bogus_cmd_long";
382 ctx.session.cmd_unrecognized(in.string());
386 template <>
387 struct action<esmtp_keyword> {
388 template <typename Input>
389 static void apply(Input const& in, Ctx& ctx)
391 ctx.param.first = in.string();
395 template <>
396 struct action<esmtp_value> {
397 template <typename Input>
398 static void apply(Input const& in, Ctx& ctx)
400 ctx.param.second = in.string();
404 template <>
405 struct action<esmtp_param> {
406 template <typename Input>
407 static void apply(Input const& in, Ctx& ctx)
409 ctx.parameters.insert(ctx.param);
410 ctx.param.first.clear();
411 ctx.param.second.clear();
415 template <>
416 struct action<local_part> {
417 template <typename Input>
418 static void apply(Input const& in, Ctx& ctx)
420 ctx.mb_loc = in.string();
421 // RFC 5321, section 4.5.3.1.1.
422 if (ctx.mb_loc.length() > 64) {
423 LOG(WARNING) << "local part length == " << ctx.mb_loc.length();
428 template <>
429 struct action<non_local_part> {
430 template <typename Input>
431 static void apply(Input const& in, Ctx& ctx)
433 ctx.mb_dom = in.string();
434 // RFC 5321, section 4.5.3.1.2.
435 if (ctx.mb_dom.length() > 255) {
436 LOG(WARNING) << "domain name or number too long " << ctx.mb_dom;
441 template <>
442 struct action<magic_postmaster> {
443 static void apply0(Ctx& ctx)
445 ctx.mb_loc = std::string{"Postmaster"};
446 ctx.mb_dom.clear();
450 template <>
451 struct action<helo> {
452 template <typename Input>
453 static void apply(Input const& in, Ctx& ctx)
455 auto const b = begin(in) + 5; // +5 for the length of "HELO "
456 auto const e = std::find(b, end(in) - 2, ' '); // -2 for the CRLF
457 ctx.session.helo(std::string_view(b, e - b));
461 template <>
462 struct action<ehlo> {
463 template <typename Input>
464 static void apply(Input const& in, Ctx& ctx)
466 auto const b = begin(in) + 5; // +5 for the length of "EHLO "
467 auto const e = std::find(b, end(in) - 2, ' '); // -2 for the CRLF
468 ctx.session.ehlo(std::string_view(b, e - b));
472 template <>
473 struct action<mail_from> {
474 static void apply0(Ctx& ctx)
476 ctx.session.mail_from(Mailbox{ctx.mb_loc, ctx.mb_dom}, ctx.parameters);
477 ctx.mb_loc.clear();
478 ctx.mb_dom.clear();
479 ctx.parameters.clear();
483 template <>
484 struct action<rcpt_to> {
485 static void apply0(Ctx& ctx)
487 ctx.session.rcpt_to(Mailbox{ctx.mb_loc, ctx.mb_dom}, ctx.parameters);
488 ctx.mb_loc.clear();
489 ctx.mb_dom.clear();
490 ctx.parameters.clear();
494 template <>
495 struct action<chunk_size> {
496 template <typename Input>
497 static void apply(Input const& in, Ctx& ctx)
499 ctx.chunk_size = std::strtoull(in.string().c_str(), nullptr, 10);
503 void bdat_act(Ctx& ctx, bool last)
505 auto status_returned = false;
507 if (!ctx.session.bdat_start(ctx.chunk_size))
508 status_returned = true;
510 auto to_xfer = ctx.chunk_size;
512 auto const bfr_size{std::min(to_xfer, std::streamsize(FLAGS_max_xfer_size))};
513 iobuffer<char> bfr(bfr_size);
515 while (to_xfer) {
516 auto const xfer_sz{std::min(to_xfer, bfr_size)};
518 ctx.session.in().read(bfr.data(), xfer_sz);
519 if (!ctx.session.in()) {
520 LOG(ERROR) << "attempt to read " << xfer_sz << " octets but only got "
521 << ctx.session.in().gcount();
522 if (ctx.session.maxed_out()) {
523 LOG(ERROR) << "input maxed out";
524 if (!status_returned)
525 ctx.session.bdat_size_error();
527 else if (ctx.session.timed_out()) {
528 LOG(ERROR) << "input timed out";
529 if (!status_returned)
530 ctx.session.bdat_io_error();
532 else if (ctx.session.in().eof()) {
533 LOG(ERROR) << "EOF in BDAT";
534 if (!status_returned)
535 ctx.session.bdat_io_error();
537 else {
538 LOG(ERROR) << "I/O error in BDAT";
539 if (!status_returned)
540 ctx.session.bdat_io_error();
542 return;
544 if (!status_returned && !ctx.session.msg_write(bfr.data(), xfer_sz)) {
545 status_returned = true;
548 to_xfer -= xfer_sz;
551 if (!status_returned) {
552 ctx.session.bdat_done(ctx.chunk_size, last);
556 template <>
557 struct action<bdat> {
558 static void apply0(Ctx& ctx)
560 bdat_act(ctx, false);
564 template <>
565 struct action<bdat_last> {
566 static void apply0(Ctx& ctx)
568 bdat_act(ctx, true);
572 template <>
573 struct data_action<data_end> {
574 static void apply0(Ctx& ctx) { ctx.session.data_done(); }
577 template <>
578 struct data_action<data_blank> {
579 static void apply0(Ctx& ctx)
581 constexpr char CRLF[]{'\r', '\n'};
582 ctx.session.msg_write(CRLF, sizeof(CRLF));
586 template <>
587 struct data_action<data_plain> {
588 template <typename Input>
589 static void apply(Input const& in, Ctx& ctx)
591 auto const len{end(in) - begin(in)};
592 ctx.session.msg_write(begin(in), len);
596 template <>
597 struct data_action<data_dot> {
598 template <typename Input>
599 static void apply(Input const& in, Ctx& ctx)
601 auto const len{end(in) - begin(in) - 1};
602 ctx.session.msg_write(begin(in) + 1, len);
606 template <>
607 struct data_action<data_long> {
608 template <typename Input>
609 static void apply(Input const& in, Ctx& ctx)
611 LOG(WARNING) << "garbage in data stream: \"" << esc(in.string()) << "\"";
612 auto const len{end(in) - begin(in)};
613 if (len)
614 ctx.session.msg_write(begin(in), len);
615 if (len > 1000) {
616 LOG(WARNING) << "line too long at " << len << " octets";
621 template <>
622 struct data_action<data_not_end> {
623 static void apply0(Ctx& ctx) __attribute__((noreturn))
625 ctx.session.bare_lf();
629 template <>
630 struct data_action<data_also_not_end> {
631 static void apply0(Ctx& ctx) __attribute__((noreturn))
633 ctx.session.bare_lf();
637 template <>
638 struct action<data> {
639 template <typename Input>
640 static void apply(Input const& in, Ctx& ctx)
642 if (ctx.session.data_start()) {
643 auto din = istream_input<eol::crlf, 1>(ctx.session.in(),
644 FLAGS_data_bfr_size, "data");
645 try {
646 if (!parse_nested<RFC5321::data_grammar, RFC5321::data_action>(in, din,
647 ctx)) {
648 ctx.session.log_stats();
649 if (!(ctx.session.maxed_out() || ctx.session.timed_out())) {
650 ctx.session.error("bad DATA syntax");
653 return;
655 catch (parse_error const& e) {
656 LOG(WARNING) << e.what();
657 ctx.session.error("unable to parse DATA stream");
659 catch (std::exception const& e) {
660 LOG(WARNING) << e.what();
661 ctx.session.error("unknown problem in DATA stream");
667 template <>
668 struct action<rset> {
669 static void apply0(Ctx& ctx) { ctx.session.rset(); }
672 template <typename Input>
673 std::string_view get_string_view(Input const& in)
675 auto const b = begin(in) + 4;
676 auto const len = end(in) - b;
677 auto str = std::string_view(b, len);
678 if (str.front() == ' ')
679 str.remove_prefix(1);
680 return str;
683 template <>
684 struct action<noop> {
685 template <typename Input>
686 static void apply(Input const& in, Ctx& ctx)
688 auto const str = get_string_view(in);
689 ctx.session.noop(str);
693 template <>
694 struct action<vrfy> {
695 template <typename Input>
696 static void apply(Input const& in, Ctx& ctx)
698 auto const str = get_string_view(in);
699 ctx.session.vrfy(str);
703 template <>
704 struct action<help> {
705 template <typename Input>
706 static void apply(Input const& in, Ctx& ctx)
708 auto const str = get_string_view(in);
709 ctx.session.help(str);
713 template <>
714 struct action<starttls> {
715 static void apply0(Ctx& ctx) { ctx.session.starttls(); }
718 template <>
719 struct action<quit> {
720 static void apply0(Ctx& ctx) __attribute__((noreturn)) { ctx.session.quit(); }
723 template <>
724 struct action<auth> {
725 static void apply0(Ctx& ctx) __attribute__((noreturn)) { ctx.session.auth(); }
727 } // namespace RFC5321
729 void timeout(int signum) __attribute__((noreturn));
730 void timeout(int signum)
732 const char errmsg[] = "421 4.4.2 time-out\r\n";
733 write(STDOUT_FILENO, errmsg, sizeof errmsg - 1);
734 _Exit(1);
737 void install_syscall_filter()
739 // scmp_filter_ctx ctx = CHECK_NOTNULL(seccomp_init(SCMP_ACT_ERRNO(EPERM)));
740 scmp_filter_ctx ctx = CHECK_NOTNULL(seccomp_init(SCMP_ACT_LOG));
742 auto rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
743 CHECK_EQ(rc, 0) << "seccomp_rule_add write failed";
745 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
746 CHECK_EQ(rc, 0) << "seccomp_rule_add read failed";
748 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
749 CHECK_EQ(rc, 0) << "seccomp_rule_add close failed";
751 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0);
752 CHECK_EQ(rc, 0) << "seccomp_rule_add rt_sigprocmask failed";
754 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
755 CHECK_EQ(rc, 0) << "seccomp_rule_add rt_sigreturn failed";
757 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), 0);
758 CHECK_EQ(rc, 0) << "seccomp_rule_add rt_sigaction failed";
760 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(newfstatat), 0);
761 CHECK_EQ(rc, 0) << "seccomp_rule_add newfstatat failed";
763 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
764 CHECK_EQ(rc, 0) << "seccomp_rule_add openat failed";
766 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0);
767 CHECK_EQ(rc, 0) << "seccomp_rule_add socket failed";
769 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(connect), 0);
770 CHECK_EQ(rc, 0) << "seccomp_rule_add connect failed";
772 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(poll), 0);
773 CHECK_EQ(rc, 0) << "seccomp_rule_add poll failed";
775 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendto), 0);
776 CHECK_EQ(rc, 0) << "seccomp_rule_add sendto failed";
778 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvfrom), 0);
779 CHECK_EQ(rc, 0) << "seccomp_rule_add recvfrom failed";
781 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigaltstack), 0);
782 CHECK_EQ(rc, 0) << "seccomp_rule_add sigaltstack failed";
784 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl), 0);
785 CHECK_EQ(rc, 0) << "seccomp_rule_add prctl failed";
787 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettid), 0);
788 CHECK_EQ(rc, 0) << "seccomp_rule_add gettid failed";
790 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
791 CHECK_EQ(rc, 0) << "seccomp_rule_add getpid failed";
793 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getppid), 0);
794 CHECK_EQ(rc, 0) << "seccomp_rule_add getppid failed";
796 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(newfstatat), 0);
797 CHECK_EQ(rc, 0) << "seccomp_rule_add newfstatat failed";
799 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(dup), 0);
800 CHECK_EQ(rc, 0) << "seccomp_rule_add dup failed";
802 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
803 CHECK_EQ(rc, 0) << "seccomp_rule_add mmap failed";
805 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
806 CHECK_EQ(rc, 0) << "seccomp_rule_add munmap failed";
808 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(pipe2), 0);
809 CHECK_EQ(rc, 0) << "seccomp_rule_add pipe2 failed";
811 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(readlinkat), 0);
812 CHECK_EQ(rc, 0) << "seccomp_rule_add readlinkat failed";
814 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(pselect6), 0);
815 CHECK_EQ(rc, 0) << "seccomp_rule_add pselect6 failed";
817 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(uname), 0);
818 CHECK_EQ(rc, 0) << "seccomp_rule_add uname failed";
820 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
821 CHECK_EQ(rc, 0) << "seccomp_rule_add fcntl failed";
823 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(unlink), 0);
824 CHECK_EQ(rc, 0) << "seccomp_rule_add unlink failed";
826 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(symlink), 0);
827 CHECK_EQ(rc, 0) << "seccomp_rule_add symlink failed";
829 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(madvise), 0);
830 CHECK_EQ(rc, 0) << "seccomp_rule_add madvise failed";
832 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
833 CHECK_EQ(rc, 0) << "seccomp_rule_add mprotect failed";
835 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
836 CHECK_EQ(rc, 0) << "seccomp_rule_add futex failed";
838 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 0);
839 CHECK_EQ(rc, 0) << "seccomp_rule_add writev failed";
841 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sysinfo), 0);
842 CHECK_EQ(rc, 0) << "seccomp_rule_add sysinfo failed";
844 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
845 CHECK_EQ(rc, 0) << "seccomp_rule_add brk failed";
847 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getrandom), 0);
848 CHECK_EQ(rc, 0) << "seccomp_rule_add getrandom failed";
850 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getdents64), 0);
851 CHECK_EQ(rc, 0) << "seccomp_rule_add getdents64 failed";
853 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
854 CHECK_EQ(rc, 0) << "seccomp_rule_add lseek failed";
856 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 0);
857 CHECK_EQ(rc, 0) << "seccomp_rule_add setsockopt failed";
859 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(alarm), 0);
860 CHECK_EQ(rc, 0) << "seccomp_rule_add alarm failed";
862 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rename), 0);
863 CHECK_EQ(rc, 0) << "seccomp_rule_add rename failed";
865 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clock_gettime), 0);
866 CHECK_EQ(rc, 0) << "seccomp_rule_add clock_gettime failed";
868 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
869 CHECK_EQ(rc, 0) << "seccomp_rule_add exit_group failed";
871 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
872 CHECK_EQ(rc, 0) << "seccomp_rule_add exit failed";
874 // for sanitize
876 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0);
877 CHECK_EQ(rc, 0) << "seccomp_rule_add clone failed";
879 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(wait4), 0);
880 CHECK_EQ(rc, 0) << "seccomp_rule_add wait4 failed";
882 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sched_yield), 0);
883 CHECK_EQ(rc, 0) << "seccomp_rule_add sched_yield failed";
885 rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ptrace), 0);
886 CHECK_EQ(rc, 0) << "seccomp_rule_add ptrace failed";
888 rc = seccomp_load(ctx);
889 CHECK_EQ(rc, 0) << "seccomp_load failed";
891 // seccomp_export_pfc(ctx, STDERR_FILENO);
893 seccomp_release(ctx);
896 int main(int argc, char* argv[])
898 std::ios::sync_with_stdio(false);
900 { // Need to work with either namespace.
901 using namespace gflags;
902 using namespace google;
903 ParseCommandLineFlags(&argc, &argv, true);
906 // Set timeout signal handler to limit total run time.
907 struct sigaction sact {};
908 PCHECK(sigemptyset(&sact.sa_mask) == 0);
909 sact.sa_flags = 0;
910 sact.sa_handler = timeout;
911 PCHECK(sigaction(SIGALRM, &sact, nullptr) == 0);
913 auto const log_dir{getenv("GOOGLE_LOG_DIR")};
914 if (log_dir) {
915 error_code ec;
916 fs::create_directories(log_dir, ec);
919 google::InitGoogleLogging(argv[0]);
921 std::unique_ptr<RFC5321::Ctx> ctx;
923 // Don't wait for STARTTLS to fail if no cert.
924 auto const config_path = osutil::get_config_dir();
925 auto const certs = osutil::list_directory(config_path, Config::cert_fn_re);
926 CHECK_GE(certs.size(), 1) << "no certs found";
928 auto const read_hook{[&ctx]() { ctx->session.flush(); }};
929 ctx = std::make_unique<RFC5321::Ctx>(config_path, read_hook);
931 ctx->session.greeting();
933 install_syscall_filter();
935 istream_input<eol::crlf, 1> in{ctx->session.in(), FLAGS_cmd_bfr_size,
936 "session"};
938 int ret = 0;
939 try {
940 ret = !parse<RFC5321::grammar, RFC5321::action>(in, *ctx);
942 catch (std::exception const& e) {
943 LOG(WARNING) << e.what();
946 if (ctx->session.maxed_out()) {
947 ctx->session.max_out();
949 else if (ctx->session.timed_out()) {
950 ctx->session.time_out();
952 // else {
953 // ctx->session.error("session end without QUIT command from client");
954 // }
956 return ret;