Subject: sender_agents modifier

From: Mark Shewmaker <[email-masked]>
Date: Fri, 06 Aug 2004 19:45:03 -0400

(I've made suggestions for a modifier of this sort a few times on the
spf-discuss list, and even mentioned it during the latest marid talks on
#spf, (though not on the jabber channel) but given the timing, I'm told
I should suggest it again here for it to have any hope of making it into
the final marid specs.)

Goal:
-----

I want Senders to be able to publish an additional modifier that would
allow for the reliable detection of a specific type of forgery that is
currently not reliably detectable.

Background:
----------

Currently recipients can white-list their known forwarders, in other
words they can white-list specific PRAs.

White-listing a PRA effectively makes that PRA a trusted agent of the
recipient.  The recipient would then trust that PRA not to lie to him in
the same way he trusts his own MTA not to lie to him.

This is all fine and good--by white-listing specific PRAs, the recipient
can trust body headers backwards a little bit.

But only backwards--there's currently no reliable way to re-examine
trust going forwards.

But...

If a body-header "From:"/PRA-going-forwards address could publish *it's*
authorized sending agents (PRAs), you could examine trust going
forwards.

So then you'd be in a situation where the senders could publish their
forward-looking-PRA sending agents, and recipients would know their
normal-backward-looking-PRA recipient agents.

If those two threads meet in the middle, then you know you can trust the
message to be authentic, (in ways you currently can't now.)

BUT, if the sender *says* he participates and there's a hole somewhere,
then you know the message is a forgery and can reject it.

Practical gain:
---------------

The current standards don't do anything to prevent someone sending an
email with a body From: of "[email-masked]", and a
body Sender: of "[email-masked]".

However, if you could look up the spf record of
"[email-masked]", and make a query to see if it
allows "[email-masked]" to speak on it's behalf, and find out
that it doesn't, then you could reject that message at MTA-receipt time.

There would then be:

  o  no need to depend on alert recipients to notice oddities
     in the email headers

  o  no need to depend on educated recipients to understand warnings
     that updated MUAs might give if they suspect that there's something
     fishy (heh) going on, and

  o  no need to depend on domain-name-based block lists quickly saying
     that all mail from "thief.example.com" is unreliable.

The actual suggestion:
======================

Preliminary:
------------

For the purpose of this modifier only, imagine that the PRA test
algorithm puts "List-ID" in between the body header From and the body
header Sender:

Were the actual PRA algorithm changed, (which I'm not suggesting), it
could be done by moving the "From:" item from number 5 to 6, inserting
the following as number 5:

     5. Locate all the non-empty List-ID headers in the message.  If
        there are no such headers, continue to step 6.  If there are
        multiple such headers, exit without returning a PRA.  If the
        single non-empty List-ID header is hopelessly malformed (e.g. if
        it appears to contain multiple mailboxes, or if the single
        mailbox is hopelessly malformed), exit without returning a PRA.
        Otherwise, exit returning the mailbox from the List-ID header as
        the PRA.

Suggested new modifier:
-----------------------

Imagine a new modifier:

 sender_agents=mycheck.example.com

that recipients could use to see whether a sender authorizes another
address as their sending agent by checking:

sha1(full_address_of_purported_agent).sender_localpart.mycheck.example.com

If the result is an NXDOMAIN, then that sender doesn't list any
authorized agent addresses, so no implicit sent-on-behalf-of
authorization claims can be validated or invalidated.  (Or you could run
the test without the sha section and check for NXDOMAIN or not--allowing
the sha1() to return NXDOMAIN means one fewer DNS lookups for the case
where the sender participates.)

If there is a result, then look at the last bit of the resulting
address, with "1" meaning the sender authorizes the agent to send on his
behalf, or "0" if not.

(You'd have to check the unwrapped-SRS address.  Also, I'm listing
sha1() as a convenience for this description for now.  I would ask that
if you're responding with alternate encoding suggestions, to make a
separate thread on that part of things, since it's mostly uncoupled with
the rest of the idea.)

(The modifier name should probably be something shorter than
"sender_agents".)

Details:
--------

(Somewhat repetitive--this is taken from my previous spf-discuss
message.  I'm leaving the repetitive bits in because I'm unsure if I'm
explaining things clearly.)

A recipient can decide whether or not he trusts the PRA to not be lying
about the previous resending hop's claims by looking at his personal PRA
whitelist.  But without the sender_agents modifier, he can't explicitly
backtrack the same trust tests any further.

For instance, if I write a message on behalf of my friend using
"From: [email-masked]" and "Sender: [email-masked]" in the body
headers, then if the recipient hasn't white-listed me, there's no way
for him to know if [email-masked] is okay with my making these
claims.

The most the recipient can tell is that it was really me,
"[email-masked]" who most recently sent him this message.

That's useful to know, but we can do better.

Suppose a particularly dastardly fellow had taken to putting lies into
my friend's mouth, by sending mail on his supposed behalf, using the
exact same method that I'm using to honestly send messages on my
friend's behalf.

Using the sender_agents modifier, or something close, my friend could
then publish a comprehensive list of allowed sending agents, obviously
not including the dastardly person on the list.

When this person then sends a message to someone with body headers
including "From: [email-masked]" and "Sender: [email-masked]",
then the recipient, in looking up [email-masked]'s sender_agents
modifier, can see that the Sender is not on the allowed list, and reject
the message.

(Yes, this requires body checks--but these can be done after all other
checks.  Also, they can even be done by the user--if the rest of the spf
checks have passed, then the user can even safely bounce the mail at
that point.)

So, with a sender_agents modifier, a recipient can know if the original
From: address trusts any successive PRA, and how any successive PRA
trusts subsequent ones.

This lets you have a potentially testable line of trust in the direction
of sender to recipient.

 sender->agent->agent->agent->...->agent->agent->agent->recipient

The sender_agents modifier can help validate that line of trust.

If the line of trust can be made into an unbroken link of permitted
agents between the sender and recipient, then, well, we know that the
recipient can trust that the message was sent via agents that either the
sender or the recipient trusts.

This has more or less been discussed earlier on the spf list multiple
times.

However, I would now like to make the startling claim that in
determining trust, that we can skip agents going forward *and*
backwards. (!!!)

For a forward-skip example, imagine:
------------------------------------

 sender->agent1->agent2->agent3->agent4->recipient.

If sender trusts agent1 not to misrepresent him,
   agent1 trusts agent3 not to misrepresent him, and
   recipient trusts agent4 to honestly represent received messages,
   (ie, agent4 is a PRA that the recipient has whitelisted),
then this is the same as:

 (sender->agent3)->(agent4->recipient)
 ^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^
 Agents of sender  Agents of recipient

There are no holes between the agents of the sender and agents of the
recipient, so I claim that we can trust the whole authentication chain.

I posit that we can ignore the "skipped" agent 2, because agent1 claims
to trust agent3.  This allows for there to be some internal MTA
structure or forwarding between agent1 and agent3.  By definition agent1
says he trusts agent3's judgement, so we can assume that agent3 knows
some reason why agent2 is trustworthy enough to be in the middle.

For a backward-skip example, imagine:
-------------------------------------

 sender->agent1->agent2->agent3->agent4->recipient.

If sender trusts agent1 not to misrepresent him,
   recipient trusts agent2 to honestly represent received messages, and
   agent2 trusts agent4 not to represent him.

then this is the same as:

 (sender->agent1)->(agent2->recipient)
 ^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^
 Agents of sender  Agents of recipient

So again I'd claim that since there are no holes between the agents of
the sender and agents of the recipient, that again in this case we can
trust the whole authentication chain.

I posit that we can ignore the "skipped" agents 3 and 4, because the
recipient trusts agent2 and agent2 trusts agent4.  This allows for there
to be some internal MTA/forwarding structure between agent2 and agent4. 
By definition, the recipient he trusts agent2's judgement, so we can
assume that agent2 knows some reason why agent4 is trustworthy enough
that the recipient can trust him to have properly validated the chain of
trust, (agent4 presumably has his own PRA whitelist, but in any event
agent2 trusts agent4's judgement).

Backward compatibility:
-----------------------

No one currently uses sender_agent modifiers, so how can a recipient go
about implementing this functionality without rejecting lots of
legitimate mail?

I would suggest that since each sender would have the option of doing
nothing, (which is the current setup), or participating, that a sensible
rule to have here, (maybe there are better ones), for any recipient
wanting to take advantage of this functionality could be:

  "If the claimed original sender (body header From:) participates
  in sender_agent functionality, then there *must* exist an unbroken
  and verifiable chain of PRA trust between the original sender and
  the end recipient, else the message is rejected/bounced."

If the claimed original sender (body header From:) does not participate
in sender_agent functionality, then you can't really reject immediately
on that basis.

However, any sender who wants to participate could prevent forgeries of
their body header From: addresses fro affecting participating
recipients.

(I would suggest some sort of body header info added into the topmost
Received-SPF: line detailing the result of these tests at
message-receipt time.  The result would basically be a binary result of
PRA-chain validated or not validated.)

Handwaving psuedocode:
----------------------

I've only gotten partway through handwaving pseudocode.

1.  make a list "Senders", whose reverse is the current PRA and its
    successive ancestor PRA's going backwards through to the From:
    body header.

2.  Create a list "agents_for", of the same length as senders,
    each element initialized as an empty list.

3.  Create a list "participating_senders" of the same length as senders.

4.  Create a list recipient_agents of the recipient's trusted PRAs.

5.  Do the following:

    For each sender in the list senders:
      If the domain of sender has a sender_agents modifier:
        test the sender against itself according to the modifier:
          if the result was success or failure:
            set corresponding element of participating_senders to TRUE.
          else:  (on nxdomain)
            set corresponding element of participating_senders to FALSE.
      else:  (if sender domain doesn't have a sender_agents modifier)
        set corresponding element of participating_senders to FALSE.

    For each sender in list of senders that are participating_senders:
      test each successive address in senders against the modifier
        on success (last bit of result is a "1"):
          add sender in the corresponding agents_for list
        on nxdomain:
          error out

    For each sender in senders:
      if the sender is in recipient_agents:
        add recipient into the corresponding agents_for list

That gives us a list of senders, and a list of list of agents in
agents_for

I'm not sure how to clearly write up the logic of:
pass-if-Body_From:-participates-and-there's-a-trusted-link,
fail/reject/bounce-if-Body_From:-participates-and-there's-not,
error/test_impossible-if-Body_From:-doesn't-participate bit.

(So I'm sending without this part really being done.)

Side effects/Implications:
--------------------------

1.  Since this implementation includes mailing lists as potential
    sender agents, (or I suppose recipient agents, though I don't think 
    recipients would want to whitelist mailing lists like that), that 
    implies that senders who wish to participate in this scheme would 
    have to whitelist all the mailing lists they participate in.

    Also, since some senders will forget to do this but try to 
    participate, mailing lists who want to prevent rejects/bounces this 
    will cause can query the sender's sender_agent record and then 
    rejecting or bouncing any messages the sender attempts to send, 
    reminding him to edit his sender_agent record to include the mailing
list.

    (Recipients can also whitelist the mailing lists they subscribe 
    to--so this whitelisting can be done on both sides, since mailing 
    lists are oddly enough both agents of the senders and recipients.  
    Future tools and improvements to MUAs on subscribing/unsubscribing 
    to mailing lists might include functionality to help do this 
    whitelisting & whitelist publishing.)

2.  MUA's can look at nonvalidated-sender messages, (ie, where the
    sender's don't participate so the message can't be outright
    rejected at receipt), do queries itself, and show the user how far
    back in the chain the message can be trusted.

-- 
Mark Shewmaker
[email-masked]