spf-discuss
[Top] [All Lists]

sender_agents modifier

2004-07-14 22:06:58
Re-introduction:
----------------

I've previously posted suggestions for a modifier that senders could use
to list authorized PRA sending agents, (usually using ONBEHALFOF
terminology.)  With such a modifier, you could know whether the PRA
you're receiving an email from is authorized by, say,
yourbank(_at_)bank(_dot_)example(_dot_)com to actually send mail on its behalf.

This would address some types of mail forgeries.

It's horribly late to be re-suggesting the whole thing yet again, but
here goes another version of it:

(I'm still having problems explaining the idea succinctly, but it's
probably better to suggest it once again that wait even longer given
it's already past the 11th hour here.)

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

If the definition of the PRA could be changed to move 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.

Then it would be possible to have a modifier that could be used to
protect against the particular type of email forgery of a body header
claiming to be sending mail on behalf of someone else where that's not
done with the permission of the sender.

(Technically you don't need to change the PRA to do this.  I kind of
think it makes sense to change the PRA that way, but at least for the
scope of discussing this modifier, it's easiest to think things through
assumming it were changed.)

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.)

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 as that
if you're responding with alternate 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".)

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

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: myfriend(_at_)example(_dot_)com" and "Sender: me(_at_)example(_dot_)com" 
in the body
headers, then if the recipient hasn't whitelisted me, there's no way for
him to know if myfriend(_at_)example(_dot_)com is okay with my making these 
claims.

The most the recipient can tell is that it was really me,
"me(_at_)example(_dot_)com" 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: myfriend(_at_)example(_dot_)com" and "Sender: 
forger(_at_)example(_dot_)com",
then the recipient, in looking up myfriend(_at_)example(_dot_)com'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 safetly 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 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:
--------------------------

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.

MUA's can look at nonvalidated-sender messages, do queries itself, and
show the user how far back in the chain the message can be trusted.

-- 
Mark Shewmaker
mark(_at_)primefactor(_dot_)com


<Prev in Thread] Current Thread [Next in Thread>
  • sender_agents modifier, Mark Shewmaker <=