Sieve redirect does not collect the sendmail child process correctly (Dovecot 2.3.4.1, Pigeonhole 0.5.4)

Delian Krustev krustev at krustev.net
Sat Nov 2 04:05:30 EET 2019


	Hi,

I got this simple test Sieve config:

#############################################
# Sieve Filter
# Generated by Ingo (http://www.horde.org/apps/ingo/) (11/01/2019, 10:46:06 PM)

# Forwards
if true {
    redirect "krustev at krustev.net";
}
# Forward Keep Action
if true {
    keep;
    stop;
}
#############################################


Which used to work fine with:

  # 2.2.27 (c0f36b0): /etc/dovecot/dovecot.conf
  # Pigeonhole version 0.4.16 (fed8554)
  # OS: Linux 4.9.0-8-amd64 x86_64 Debian 9.11

Then I've tried migrating to:

  # 2.3.4.1 (f79e8e7e4): /etc/dovecot/dovecot.conf
  # Pigeonhole version 0.5.4 ()
  # OS: Linux 4.19.0-6-amd64 x86_64 Debian 10.1

And started getting the following errors:

  # cat .dovecot.sieve.log
  sieve: info: started log at Nov 01 22:55:50.
  error: msgid=<4221584.AlbrL4BjWX at home>: failed to redirect message to <krustev at krustev.net>: Failed to execute sendmail (temporary failure).

A google search lead me to:

  https://bugs.archlinux.org/task/56933

which blames systemd service changes:

  NoNewPrivileges=true

, however I'm not running dovecot via systemd but use DJB's daemontools for
service supervision .

The page above suggested to change from:

  sendmail_path = /usr/sbin/sendmail

to

  submission_host = localhost

in "conf.d/15-lda.conf", which worked fine.
But since I don't have other problems with "sendmail" execution I've decided
to debug this further.

I have dovecot integrated with Qmail:

# apt-cache policy qmail
qmail:
  Installed: 1.06-6+b1
  Candidate: 1.06-6+b1
  Version table:
 *** 1.06-6+b1 900
        900 http://deb.debian.org/debian buster/main amd64 Packages

so sendmail's implementation comes from Qmail:

  # ls -al `which sendmail`
  lrwxrwxrwx 1 root root 14 Feb 23  2015 /usr/sbin/sendmail -> qmail-sendmail


I've strace-d the qmail-lspawn process which executes dovecot's LDA, which
processes the Sieve rules and executes "sendmail".
The sendmail process executes and returns successfully, the mail is delivered,
however the Sieve code does not wait for the child process correctly and tries
to kill the already exited process after 30 seconds timeout.

Here are the problematic Sieve syscalls:

22:55:15.283482 execve("/usr/lib/dovecot/deliver", ["/usr/lib/dovecot/deliver", "-e", "-a", "tester at krustev.net"], 0x5620b4330c40 /* 61 vars */) = 0
...
22:55:15.286845 pipe([5, 6])            = 0
22:55:15.286870 fcntl(5, F_GETFL)       = 0 (flags O_RDONLY)
22:55:15.286889 fcntl(5, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
22:55:15.286909 fcntl(6, F_GETFL)       = 0x1 (flags O_WRONLY)
22:55:15.286928 fcntl(6, F_SETFL, O_WRONLY|O_NONBLOCK) = 0
22:55:15.286947 fcntl(5, F_GETFD)       = 0
22:55:15.286966 fcntl(5, F_SETFD, FD_CLOEXEC) = 0
22:55:15.286985 fcntl(6, F_GETFD)       = 0
22:55:15.287003 fcntl(6, F_SETFD, FD_CLOEXEC) = 0
22:55:15.287024 epoll_create(128)       = 7
22:55:15.287043 fcntl(7, F_GETFD)       = 0
22:55:15.287062 fcntl(7, F_SETFD, FD_CLOEXEC) = 0
22:55:15.287084 epoll_ctl(7, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP, {u32=47201248, u64=94699781110752}}) = 0
...
22:55:15.294625 epoll_ctl(7, EPOLL_CTL_DEL, 5, 0x7ffd59156b8c) = 0
22:55:15.294647 epoll_create(128)       = 12
22:55:15.294668 fcntl(12, F_GETFD)      = 0
22:55:15.294687 fcntl(12, F_SETFD, FD_CLOEXEC) = 0
22:55:15.294708 epoll_ctl(12, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP, {u32=47455536, u64=94699781365040}}) = 0

  Dovecot/Sieve will wait for this pipe FD 5 later, but no event will arrive.

22:55:15.294740 rt_sigaction(SIGCHLD, {sa_handler=0x7f15404b3640, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f1540229840}, NULL, 8) = 0
22:55:15.294763 pipe([13, 14])          = 0
22:55:15.294788 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f15401ec2d0) = 6929


  The sendmail child process with PID 6929 is forked here.


22:55:15.294928 close(13)               = 0
22:55:15.294969 fcntl(14, F_GETFL)      = 0x1 (flags O_WRONLY)
22:55:15.295006 fcntl(14, F_SETFL, O_WRONLY|O_NONBLOCK) = 0
22:55:15.295039 lseek(14, 0, SEEK_CUR)  = -1 ESPIPE (Illegal seek)
22:55:15.295065 getsockname(14, 0x7ffd59156a40, [28]) = -1 ENOTSOCK (Socket operation on non-socket)
22:55:15.295109 write(14, "EDITTED LONG MESSAGE DATA\n", 1461) = 1461
22:55:15.295150 close(14)               = 0
22:55:15.295175 wait4(6929, 0x562102d4fee4, WNOHANG, NULL) = 0
22:55:15.295211 epoll_wait(12, [], 1, 30000) = 0


  Here's where the 30 second's timeout is requested and waited.


22:55:45.325580 kill(6929, SIGTERM)     = 0
22:55:45.325769 epoll_wait(12, [], 1, 5000) = 0
22:55:50.331108 kill(6929, SIGKILL)     = 0
22:55:50.332867 wait4(6929, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 6929
22:55:50.333020 epoll_ctl(12, EPOLL_CTL_DEL, 5, 0x7ffd59156b6c) = 0
22:55:50.333060 epoll_ctl(7, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP, {u32=47201248, u64=94699781110752}}) = 0
22:55:50.333097 close(12)               = 0
22:55:50.333240 sendto(9, "<19>Nov  1 22:55:50 dovecot: lda(krustev)<6926><IlpXEdO3vF0OGwAA4EGeoA>: Error: sieve: msgid=<4221584.AlbrL4BjWX at home>: failed to redirect message to <krustev at krustev.net>: Failed to execute sendmail (temporary failure)", 220, MSG_NOSIGNAL, NULL, 0) = 220
22:55:50.333321 openat(AT_FDCWD, "/home/krustev/mail/domains/krustev.net/tester/.dovecot.sieve.log", O_WRONLY|O_CREAT|O_APPEND, 0600) = 12
22:55:50.333557 fstat(12, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
22:55:50.333613 lseek(12, 0, SEEK_CUR)  = 0
22:55:50.333643 fstat(12, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
22:55:50.333678 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=127, ...}) = 0
22:55:50.333726 write(12, "sieve: info: started log at Nov 01 22:55:50.\n", 45) = 45
22:55:50.333788 write(12, "error: msgid=<4221584.AlbrL4BjWX at home>: failed to redirect message to <krustev at krustev.net>: Failed to execute sendmail (temporary failure).\n", 141) = 141
22:55:50.333857 sendto(9, "<19>Nov  1 22:55:50 dovecot: lda(krustev)<6926><IlpXEdO3vF0OGwAA4EGeoA>: Error: sieve: Execution of script /home/krustev/mail/domains/krustev.net/tester/.dovecot.sieve was aborted due to temporary failure (user logfile /home/krustev/mail/domains/krustev.net/tester/.dovecot.sieve.log may reveal additional details)", 317, MSG_NOSIGNAL, NULL, 0) = 317
22:55:50.333941 close(10)               = 0
22:55:50.333996 close(12)               = 0
22:55:50.334057 lstat("/home/krustev/mail/domains/krustev.net/tester/.dovecot.lda-dupes.lock", {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
22:55:50.334095 unlink("/home/krustev/mail/domains/krustev.net/tester/.dovecot.lda-dupes.lock") = 0
22:55:50.334188 close(11)               = 0
22:55:50.334245 write(2, "Execution of Sieve filters was aborted due to temporary failure\n", 64) = 64
22:55:50.334418 munmap(0x7f15401bc000, 103680) = 0
22:55:50.334481 munmap(0x7f15403b3000, 32800) = 0
22:55:50.334516 munmap(0x7f15400fb000, 788352) = 0
22:55:50.334610 epoll_ctl(7, EPOLL_CTL_DEL, 8, 0x7ffd5915706c) = 0
22:55:50.334640 close(8)                = 0
22:55:50.334685 epoll_ctl(7, EPOLL_CTL_DEL, 5, 0x7ffd5915706c) = 0
22:55:50.334738 close(5)                = 0
22:55:50.334785 close(6)                = 0
22:55:50.334813 close(7)                = 0
22:55:50.334841 close(4)                = 0
22:55:50.334881 exit_group(75)          = ?
22:55:50.335077 +++ exited with 75 +++


Here's the interesting part of sendmail's (PID 6929) execution with timestamps:
..
22:55:15.295268 execve("/usr/sbin/sendmail", ["/usr/sbin/sendmail", "-i", "-f", "tester at krustev.net", "--", "krustev at krustev.net"], NULL) = 0
...
22:55:15.296024 chdir("/var/lib/qmail") = 0
22:55:15.296055 execve("bin/qmail-inject", ["bin/qmail-inject", "-a", "-f", "tester at krustev.net", "--", "krustev at krustev.net"], 0x55741b1890e0 /* 2 vars */) = 0
...
22:55:15.297264 vfork()                 = 6930
..
22:55:15.297930 wait4(6930, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 6930
22:55:15.305391 exit_group(0)           = ?
22:55:15.305472 +++ exited with 0 +++

The PID 6930 is a call to the setuid qmail-queue, which also executes and returns normally.


With all the above stated - It seems that the problem is in the "epoll" calls.
Either it is not set-up correctly or dovecot-LDA does not receive the
information it waits for.

I've tried to make my way trough Dovecot's source code but have concluded that
someone more familiar should be able to spot the problem much quicker than me.


Let me know if you need some additional information.
Sorry for the long mail.



Dovecot config follows:

# 2.3.4.1 (f79e8e7e4): /etc/dovecot/dovecot.conf
# Pigeonhole version 0.5.4 ()
# OS: Linux 4.19.0-6-amd64 x86_64 Debian 10.1
# Hostname: pinko.livehostserver.com
auth_mechanisms = plain login
disable_plaintext_auth = no
lda_mailbox_autocreate = yes
lda_mailbox_autosubscribe = yes
log_path = /dev/stderr
log_timestamp =
mail_plugins = " quota"
managesieve_notify_capability = mailto
managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext
namespace inbox {
  inbox = yes
  location =
  mailbox Archive {
    special_use = \Archive
  }
  mailbox Archives {
    special_use = \Archive
  }
  mailbox "Deleted Items" {
    special_use = \Trash
  }
  mailbox "Deleted Messages" {
    special_use = \Trash
  }
  mailbox Drafts {
    auto = subscribe
    special_use = \Drafts
  }
  mailbox Junk {
    auto = subscribe
    special_use = \Junk
  }
  mailbox "Junk E-mail" {
    special_use = \Junk
  }
  mailbox Sent {
    auto = subscribe
    special_use = \Sent
  }
  mailbox "Sent Items" {
    special_use = \Sent
  }
  mailbox "Sent Mail" {
    special_use = \Sent
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
  mailbox Spam {
    special_use = \Junk
  }
  mailbox Trash {
    auto = subscribe
    special_use = \Trash
  }
  mailbox sent-mail {
    special_use = \Sent
  }
  prefix =
}
passdb {
  args = /usr/local/sbin/vchkpw-dovecot
  driver = checkpassword
}
plugin {
  antispam_allow_append_to_spam = yes
  antispam_backend = pipe
  antispam_pipe_program = /bin/sh
  antispam_pipe_program_notspam_args = -c;HOME=$PWD exec nice sa-learn --ham
  antispam_pipe_program_spam_args = -c;HOME=$PWD exec nice sa-learn --spam
  antispam_pipe_tmpdir = /tmp
  antispam_spam_pattern_ignorecase = spam;junk;junk *
  antispam_trash_pattern_ignorecase = trash;Deleted *
  quota = maildir:User quota
  sieve = ~/.dovecot.sieve
  sieve_dir = ~/sieve
  sieve_redirect_envelope_from = recipient
}
protocols = " imap sieve pop3"
service anvil {
  client_limit = 1536
}
service auth {
  client_limit = 1536
}
service imap-login {
  process_limit = 512
}
service managesieve-login {
  inet_listener sieve {
    port = 4190
  }
  process_min_avail = 1
  service_count = 1
}
service managesieve {
  process_limit = 50
}
service pop3-login {
  process_limit = 512
}
service stats {
  client_limit = 1024
  unix_listener stats-writer {
    mode = 0666
  }
}
ssl_ca = </scm/deb-global/etc/ssl/intermediate-CAs.crt
ssl_cert = </etc/ssl/default-fullchain.pem
ssl_client_ca_dir = /etc/ssl/certs
ssl_dh = # hidden, use -P to show it
ssl_key = # hidden, use -P to show it
userdb {
  driver = prefetch
}
protocol lda {
  info_log_path =
  log_path =
  mail_plugins = " quota sieve"
}
protocol imap {
  mail_max_userip_connections = 64
  mail_plugins = " quota imap_quota antispam"
}
protocol sieve {
  mail_max_userip_connections = 10
  managesieve_implementation_string = Dovecot Pigeonhole
  managesieve_max_line_length = 65536
}


Best regards
--
Delian


More information about the dovecot mailing list