openzfs / zfs

OpenZFS on Linux and FreeBSD
https://openzfs.github.io/openzfs-docs
Other
10.69k stars 1.75k forks source link

First Line of ZED Email Missing When Sending with Nullmailer sendmail #16656

Open scolby33 opened 1 month ago

scolby33 commented 1 month ago

System information

Type Version/Name
Distribution Name Debian
Distribution Version 12 (bookworm)
Kernel Version Linux 6.1.0-26-amd64
Architecture amd64
OpenZFS Version zfs-2.2.6-1~bpo12+3 zfs-kmod-2.2.6-1~bpo12+3

Describe the problem you're observing

I received the following email from zed:

acceptable levels. ZFS has marked the device as faulted.

 impact: Fault tolerance of the pool may be compromised.
    eid: 87
  class: statechange
*snip*

The first line of the message has been lost.

Describe how to reproduce the problem

In zed.rc, set

ZED_EMAIL_ADDR="root"
ZED_EMAIL_PROG="sendmail"  # sendmail from the Debian nullmailer package, version 1:2.2-4
ZED_EMAIL_OPTS="@ADDRESS@"

I believe the issue lies in zed-functions.sh at line 285.

https://github.com/openzfs/zfs/blob/e0bf43d64ed01285321bf6c3a308f699c5483efc/cmd/zed/zed.d/zed-functions.sh#L279-L290

If I add a second \n (adding a blank line between the end of the Subject: header and the message), Nullmailer sendmail no longer eats the first line of the message.

I would've proposed this as a PR, but I'm not sure how other versions of mail/sendmail handle this and if adding the extra newline would break things elsewhere. (Nullmailer is a bit idiosyncratic, I think.) However, I think it should be okay, given that RFC 822 and RFC 5322 specify that there should be a blank line between the end of the headers and the message body:

A message consists of header fields (collectively called "the header section of the message") followed, optionally, by a body. The header section is a sequence of lines of characters with special syntax as defined in this specification. The body is simply a sequence of characters that follows the header section and is separated from the header section by an empty line (i.e., a line with nothing preceding the CRLF).

Nullmailer sendmail does not have an option to specify a subject with a command line option. See: https://manpages.debian.org/bookworm/nullmailer/sendmail.1.en.html And: https://manpages.debian.org/bookworm/nullmailer/nullmailer-inject.1.en.html

Include any warning/errors/backtraces from the system logs

N/A

n0xena commented 1 month ago

in fact it has to be two \r\n as specified in both rfc822 and smtp just using (and accepting) non-correct line terminations (only \r or only \n) is what led to the smtp smuggle issue and should not longer be accepted by any updated mailer anyway

n0xena commented 1 month ago

after further reading of the code: it's specific written to use mail instead of sendmail as it makes use of -s to define the subject - which sendmail doesn't support as it requires the subject to be part of the headers inside the mail data https://github.com/openzfs/zfs/blob/aefc2da8a594d7a8059c862eab464d5f798393b3/cmd/zed/zed.d/zed-functions.sh#L256-L290 to make it sendmail compatible the entire command line has to be replaced with something similar how php uses it what has to be passed to sendmail has to look like this in its most basic form:

From: <from@example.invalid>\r\n
To: <to@example.invalid>\r\n
Subject: some subject\r\n
\r\n //empty line all by itself
here comes the actual message text - per rfc trimmed to 70 chars per line\r\n
all with proper line break\r\n
.\r\n //indication of END_OF_DATA_COMMAND - the rfc defines this as "<CRLF>.<CRLF>" - "a dot on a line on its own"

forcing "-s SUBJECT" as its own parameter for the mailer to inject it properly forces it to mailers compatible with mail syntax and by this makes it incompatible with sendmail syntax I'm not sure which one is the preferred one - but at least on opensuse the package dependencies always require a sendmail wrapper available - either sendmail itself or something that provides one like postfix

another idea that comes to my mind is to use sendmail as basis and write a wrapper that checks if mail is inplace in case sendmail is missing - as by its documentation the input mail can process seems to be compatible with what sendmail expects - which would result in only a small boilerplate code to extract the subject header field and pass it as an additional parameter with the rest of the code using sendmail standard anyway