removed
[ghsmtp.git] / smtp.cpp
blob4f27235f48554b423274031400a18474fcdb247d
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 using namespace std::string_literals;
34 namespace RFC5321 {
36 struct Ctx {
37 Session session;
39 std::string mb_loc;
40 std::string mb_dom;
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)
53 #include "UTF8.hpp"
55 // clang-format off
57 struct quoted_pair : seq<one<'\\'>, sor<VCHAR, WSP>> {};
59 using dot = one<'.'>;
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>,
85 DIGIT
86 > {};
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>,
119 one<']'>> {};
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
127 // DEL
128 // So including:
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,
146 one<'!', '#',
147 '$', '%',
148 '&', '\'',
149 '*', '+',
150 '-', '/',
151 '=', '?',
152 '^', '_',
153 '`', '{',
154 '|', '}',
155 '~'>,
156 UTF8_non_ascii> {};
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>,
193 CRLF> {};
195 struct ehlo : seq<TAO_PEGTL_ISTRING("EHLO"),
197 sor<domain, address_literal>,
198 CRLF> {};
200 struct mail_from : seq<TAO_PEGTL_ISTRING("MAIL"),
201 TAO_PEGTL_ISTRING(" FROM:"),
202 opt<SP>, // common enough error, we'll allow it
203 reverse_path,
204 opt<seq<SP, mail_parameters>>,
205 CRLF> {};
207 struct rcpt_to : seq<TAO_PEGTL_ISTRING("RCPT"),
208 TAO_PEGTL_ISTRING(" TO:"),
209 opt<SP>, // common error
210 forward_path,
211 opt<seq<SP, rcpt_parameters>>,
212 CRLF> {};
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
231 // LF" message.
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,
246 data_not_end,
247 data_also_not_end,
248 data_dot,
249 data_plain,
250 data_long>,
251 discard> {};
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> {};
265 struct starttls
266 : seq<TAO_PEGTL_ISTRING("STAR"), TAO_PEGTL_ISTRING("TTLS"), CRLF> {};
268 struct quit : seq<TAO_PEGTL_ISTRING("QUIT"), CRLF> {};
270 // Anti-AUTH support
272 // base64-char = ALPHA / DIGIT / "+" / "/"
273 // ;; Case-sensitive
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<'='>>
281 > {};
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>>
289 > {};
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]
309 // CRLF
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>>,
316 CRLF> {};
317 // bad commands:
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,
325 data,
326 quit,
327 rset,
328 noop,
329 vrfy,
330 help,
331 helo,
332 ehlo,
333 bdat,
334 bdat_last,
335 starttls,
336 rcpt_to,
337 mail_from,
338 auth,
339 bogus_cmd_long>,
340 discard> {};
342 struct grammar : plus<any_cmd> {};
344 // clang-format on
346 template <typename Rule>
347 struct action : nothing<Rule> {
350 template <typename Rule>
351 struct data_action : nothing<Rule> {
354 template <>
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());
364 template <>
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());
374 template <>
375 struct action<esmtp_keyword> {
376 template <typename Input>
377 static void apply(Input const& in, Ctx& ctx)
379 ctx.param.first = in.string();
383 template <>
384 struct action<esmtp_value> {
385 template <typename Input>
386 static void apply(Input const& in, Ctx& ctx)
388 ctx.param.second = in.string();
392 template <>
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();
403 template <>
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();
417 template <>
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;
430 template <>
431 struct action<bounce_path> {
432 static void apply0(Ctx& ctx)
434 ctx.mb_loc.clear();
435 ctx.mb_dom.clear();
439 template <>
440 struct action<magic_postmaster> {
441 static void apply0(Ctx& ctx)
443 ctx.mb_loc = std::string{"Postmaster"};
444 ctx.mb_dom.clear();
448 template <>
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));
459 template <>
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));
470 template <>
471 struct action<mail_from> {
472 static void apply0(Ctx& ctx)
474 Mailbox mbx;
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);
480 ctx.mb_loc.clear();
481 ctx.mb_dom.clear();
482 ctx.parameters.clear();
486 template <>
487 struct action<rcpt_to> {
488 static void apply0(Ctx& ctx)
490 Mailbox mbx;
492 if (ctx.mb_loc == "Postmaster"s) {
493 mbx = Mailbox{"Postmaster"};
495 else {
496 mbx = Mailbox{ctx.mb_loc, Domain{ctx.mb_dom}};
498 ctx.session.rcpt_to(std::move(mbx), ctx.parameters);
499 ctx.mb_loc.clear();
500 ctx.mb_dom.clear();
501 ctx.parameters.clear();
505 template <>
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);
526 while (to_xfer) {
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();
548 else {
549 LOG(ERROR) << "I/O error in BDAT";
550 if (!status_returned)
551 ctx.session.bdat_io_error();
553 return;
555 if (!status_returned && !ctx.session.msg_write(bfr.data(), xfer_sz)) {
556 status_returned = true;
559 to_xfer -= xfer_sz;
562 if (!status_returned) {
563 ctx.session.bdat_done(ctx.chunk_size, last);
567 template <>
568 struct action<bdat> {
569 static void apply0(Ctx& ctx)
571 bdat_act(ctx, false);
575 template <>
576 struct action<bdat_last> {
577 static void apply0(Ctx& ctx)
579 bdat_act(ctx, true);
583 template <>
584 struct data_action<data_end> {
585 static void apply0(Ctx& ctx) { ctx.session.data_done(); }
588 template <>
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));
597 template <>
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);
607 template <>
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);
617 template <>
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)};
624 if (len)
625 ctx.session.msg_write(begin(in), len);
626 if (len > 1000) {
627 LOG(WARNING) << "line too long at " << len << " octets";
632 template <>
633 struct data_action<data_not_end> {
634 static void apply0(Ctx& ctx) __attribute__((noreturn))
636 ctx.session.bare_lf();
640 template <>
641 struct data_action<data_also_not_end> {
642 static void apply0(Ctx& ctx) __attribute__((noreturn))
644 ctx.session.bare_lf();
648 template <>
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");
656 try {
657 if (!parse_nested<RFC5321::data_grammar, RFC5321::data_action>(in, din,
658 ctx)) {
659 ctx.session.log_stats();
660 if (!(ctx.session.maxed_out() || ctx.session.timed_out())) {
661 ctx.session.error("bad DATA syntax");
664 return;
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");
678 template <>
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);
691 return str;
694 template <>
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);
704 template <>
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);
714 template <>
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);
724 template <>
725 struct action<starttls> {
726 static void apply0(Ctx& ctx) { ctx.session.starttls(); }
729 template <>
730 struct action<quit> {
731 static void apply0(Ctx& ctx) __attribute__((noreturn)) { ctx.session.quit(); }
734 template <>
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);
745 _Exit(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";
888 // for sanitize
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);
923 sact.sa_flags = 0;
924 sact.sa_handler = timeout;
925 PCHECK(sigaction(SIGALRM, &sact, nullptr) == 0);
927 auto const log_dir{getenv("GOOGLE_LOG_DIR")};
928 if (log_dir) {
929 error_code ec;
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,
950 "session"};
952 int ret = 0;
953 try {
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();
966 // else {
967 // ctx->session.error("session end without QUIT command from client");
968 // }
970 return ret;