0

Disclaimer: I have read some of the questions and articles here and elsewhere which deal with Sendmail rewriting headers. I haven't found an answer to the question below because the other question mostly relate to rewriting headers (instead of adding them), and nearly all of them relate to outbound messages and the envelope sender (instead of the envelope recipient).

Having said this:

I am running a mail server, using Sendmail 8.14.4 under Debian jessie.

There are some email recipient addresses which are mapped to the same O/S user account. When the respective O/S user reads the messages, he can't determine to what email address the messages originally have been sent.

Therefore, I would like to have Sendmail add a custom header containing the envelope recipient(s) to all inbound email messages.

I think I could better explain my problem by example:

Suppose I have two email addresses, offer1@example.com and offer2@example.com. Both are mapped to the O/S user account offers. This works so far: The O/S user offers, when fetching his email, gets all messages sent to offer1@example.com and offer2@example.com.

The problem now is that he can't determine to which email address each message originally has been sent. That means: Although he is seeing all messages which have been sent to both email addresses, he can't tell if a certain message has been originally sent to offer1@example.com or to offer2@example.com.

Therefore, I would like to add a custom header to each inbound message, perhaps something like that: X-Envelope-Recipient: <Original envelope recipient>.

What would be the easiest way to do this?

Once upon a time, I have written some simple custom rules for Sendmail. But nearly 15 years have passed since then, so I'd like to avoid that, and thus I hope that there is an easy solution or that somebody could point me in the right direction. To be honest, writing a milter to solve my problem currently seems easier to me than re-learning Sendmail's rule syntax ...

EDIT 1

As requested by @AnFi, here is the local mailer definition from sendmail.cf:

Mlocal,         P=/usr/lib/sm.bin/mail.local, F=lsDFMAw5:/|@qPSXnz9, S=EnvFromSMTP/HdrFromL, R=EnvToL/HdrToL,
                T=DNS/RFC822/SMTP,
                A=mail.local -l -h inbox
Binarus
  • 519
  • 3
  • 15

2 Answers2

1

What you suggest would be against the SMTP protocol: there's legitimate reasons for adding addresses to RCPT TO command despite they do not exist in the To: or Cc: headers, i.e. RFC 5321 7.2. "Blind" Copies (emphasis is mine):

Addresses that do not appear in the message header section may appear in the RCPT commands to an SMTP server for a number of reasons. The two most common involve the use of a mailing address as a "list exploder" (a single address that resolves into multiple addresses) and the appearance of "blind copies". Especially when more than one RCPT command is present, and in order to avoid defeating some of the purpose of these mechanisms, SMTP clients and servers SHOULD NOT copy the full set of RCPT command arguments into the header section, either as part of trace header fields or as informational or private-extension header fields. Since this rule is often violated in practice, and cannot be enforced, sending SMTP systems that are aware of "bcc" use MAY find it helpful to send each blind copy as a separate message transaction containing only a single RCPT command.

There is no inherent relationship between either "reverse" (from MAIL, SAML, etc., commands) or "forward" (RCPT) addresses in the SMTP transaction ("envelope") and the addresses in the header section. Receiving systems SHOULD NOT attempt to deduce such relationships and use them to alter the header section of the message for delivery. The popular Apparently-to header field is a violation of this principle as well as a common source of unintended information disclosure and SHOULD NOT be used.

The non-recommended Apparently-to header is controlled with option NoRecipientAction=action.

Set the behaviour when there are no recipient headers (To:, Cc: or Bcc:) in the message to action:

  • none leaves the message unchanged,
  • add-to adds a To: header with the envelope recipients,
  • add-apparently-to adds an Apparently-To: header with the envelope recipients,
  • add-bcc adds an empty Bcc: header, and
  • add-to-undisclosed adds a header reading 'To: undisclosed-recipients:;'.

Please note that normally the original recipient address is already in the To: or Cc: header. It should not be modified to the user account name offers, so it's either offer1@example.com, offer2@example.com or hidden. Rewriting headers has become even more dangerous as it might also broke DKIM signatures. The only reasonable use case is address rewriting for mail with a local origin (user to user@example.com).


Adding a list of all RCPT TO addresses would violate the protocol, but you actually simply need the single original RCPT TO address for the user the mail was delivered to. I don't know how to achieve this with Sendmail, but Postfix (with the default configuration) adds an X-Original-To: header containing exactly that, and additionally a Delivered-To: header containing the internal destination mailbox (user@FQDN.example.com).

Esa Jokinen
  • 43,252
  • 2
  • 75
  • 122
  • Thanks for the insight! *...is already in the `To:` or `Cc:` header...* But this is exactly my problem: This is not the case. There is no `Cc:` header at all, and the `To:` header contains something like `verysillynonsensehere`. The senders of those messages often "fake" the `To:` header by intention. Exactly in such cases, I would like to know to which recipient(s) the message has been sent. I explicitly do *not* want to rewrite `offer1@...` or `offer2@...` to `offers`; I just would like to find out the message's recipient(s) if they are not in the `To:` header field. – Binarus Sep 10 '18 at 10:58
  • That part was just to rule out that if you have such rewriting somewhere, you should remove it first. For rare use cases like this I personally find Postfix more flexible. My expertise is mail systems in general, and as an implementation I mostly use Postfix and Exchange. Hopefully someone else can answer the question *how* using *Sendmail*. – Esa Jokinen Sep 10 '18 at 11:04
  • I could live with violating SMTP in this case. First, I can't imagine that anything bad would happen if such a header would be added (if choosing a GUID as header name so that it is unique .-)); second, in my case, after Sendmail has received that messages, *no further SMTP transactions will happen* (the messages will be delivered to `offers`'s inbox by using mechanisms other than SMTP instead of being relayed further). – Binarus Sep 10 '18 at 11:09
  • Thanks for your additional comment. Could you please give me a hint where I should place that `NoRecipientAction=...` line? Placing it into `sendmail.mc` won't work ... – Binarus Sep 10 '18 at 11:11
  • The `sendmail.mc` is a m4 macro file that is recommended for building the `sendmail.cf` master configuration file. There, the options are specified with `define('option','value')` that creates the `0 option=value` entry to the `sendmail.cf`. – Esa Jokinen Sep 10 '18 at 12:02
  • 1
    OK, thanks, The macro to use in `sendmail.mc` would be `confNO_RCPT_ACTION`. However, I think this won't help either because this macro strikes only if "if there are no legal recipient fields (To:, Cc: or Bcc:) in the message" (from the docs); in my case, there are such headers, but they obviously contain "fake" contents. – Binarus Sep 10 '18 at 12:07
  • Yes. Well, Postfix adds that as an `X-Original-To:` header by default. So, if you keep having problems implementing this with Sendmail, that's one solution. Added that to my answer. – Esa Jokinen Sep 10 '18 at 12:22
1

Your question is addresses by Sendmail.org FAQ 3.29

3.29 How can I add a header specifying the actual recipient when having multiple users in a virtual domain go to a single mailbox?

Short version: Use virtusertable and ~offers/.procmailrc

virtusertable:

offer1@example.com  offers+offer1
offer2@example.com  offers+offer2

~offers/.procmailrc should get "plus detail" in $1.
OR
You can use $h (set to +detail) in custom headers

Warning: you will get/process two copies of a message addressed to both offer1@example.com and offer2@example.com

AnFi
  • 5,883
  • 1
  • 12
  • 26
  • Thank you very much, and +1, but: Actually, I have been searching for a solution which does not involve "external" tools. That is why I wrote that *Sendmail* should add the header. However, since I eventually have not been clear enough about that, I'll give the answer to you after waiting a few more days ... – Binarus Sep 11 '18 at 17:41
  • 1
    1) You can make sendmail.cf add custom header(s). You can use `$h` (+detail in case of many local mailers) in such headers. 2) Post your local mailer definition from `/etc/mail/sendmail.cf` [A few lines after `Mlocal`] – AnFi Sep 11 '18 at 19:00
  • Thanks for bothering. Please see section EDIT 1 in my original question where I am showing the local mailer definition from `sendmail.cf`. I'll try to find out more about `mail.local` in the meantime. Are you suggesting using `.forward` files instead? The reason why I would like to have this done as deep in Sendmail as possible is that I would like to avoid playing around with files users have crafted, or to urge users to play around with them, i.e. I would like to avoid changing anything in users' home directories. – Binarus Sep 11 '18 at 21:03
  • 1
    Could also post what is reported by `sendmail -bv offers+offer1` executed by root? Local mailer definition does not pass `$h` (+detail) to local mailer program but sendmail.cf may set it anyway. – AnFi Sep 12 '18 at 05:10
  • Thanks again - since the output is short, here we go: `offers+offer1... deliverable: mailer local, host offer1, user offers`. – Binarus Sep 13 '18 at 05:53
  • So your sendmail,cf sets `$h` to +detail. [ `$h` keeps "next hop host" for typical mailer] – AnFi Sep 13 '18 at 07:37
  • I am giving the answer to you because your solution would probably work. However, the method is very illogical (why do I have to use plussed addresses to achieve such a simple thing?) and worrying (why does this work at all given that `$h` usually contains the *host* part?). Additionally, I wanted a solution which would show me *all* RCPT-TO addresses in that header, not just the one(s) which map to the respective (O/S-level) user. Therefore, I now simply have written a milter which does exactly what I want, transparently for *all* users, without fiddling with any configuration files. – Binarus Sep 15 '18 at 15:45
  • In fact, it already has been tested thoroughly the last two days. It works as expected. Understanding the `libmilter` API is not that hard, but I admit that I already have written my first milter some months ago, which made writing this one much easier :-) – Binarus Sep 15 '18 at 19:15