Rick van Rein

do 24 juni 2021


Access 4: ARPA2 Access Control for Mail

We just demonstrated how you can configure access control in a local database. The next step is to actually put it to use. Lets control our email!

This article is part of a series on access control and is related to another series on identity.

As previously shown we have made tools for easily managing Access Control, which is setup in a system-central database. Now we demonstrate how you can apply these rules in an application. We demonstrate it for email, but there is nothing special about SMTP/email; the same ideas could be applied to XMPP/chat, SIP/telephony and so on.

Email usually passes through a number of stages of filtering and scrubbing. Even if we leave out the virus scanners and spam filters it may look a bit daunting. We'll talk you through though.

Access Control in an email server

  • On the left, you can see email coming in. Lots of terrible things are being tried at that point, so a fair degree of effort goes into fencing off the most blatant attackers. This is what a program like postscreen, part of the Postfix MTA, does nicely.

  • Soon after, we enter a program axesmtp-in that employs ARPA2 Access Control to learn about the communication rights for the sender to the intended recipient. Email classified as black listed gets bounced, and white listed email may pass.

  • Grey listed email should be challenged a little, which fights spam quite effectively and programs like postgrey do this well. Note how convenient it is that explicitly white listed contacts can pass through immediately, while those unknown are filtered with a bit more backpressure (by incurring a delay).

  • Email that is marked as honeypotted may be treated like they were on the black list or, if you want to discover more about it, sent astray into a honeypot where they hopefully tell us all the tricks that spammers might think of.

  • White listed email normally passes into the mail queue, which is central to mail servers, and the cornerstone of their solid delivery. From the mail queue, delivery to local users via their mailboxes is among the options. Users who send mail may also send it out, but the habit of doing this for incoming mail is considered offensive, if no further processing takes place.

  • One form of processing that may be useful is that of group iteration. Since the ARPA2 Rules DB also stores group members and allows their iteration, this has been built into the axesmtp-group program. Mail is directed here when ARPA2 Access Control told us that the sender address should be changed into a so-called Actor Identity. This new identity is often a group member, and is triggered by a Rule when it permits sending to a group address. Group iteration then completes the task by iterating over the members in the group that should receive an email. It delivers to their registered email address (or identity).

  • Finally, mail that is not group mail may be sent out through the axesmtp-out program, which makes an ARPA2 Signed Identity when it seens a "recipe" for such a signature pass through. The idea is that a reply to such an email address will be recognised by the axesmtp-in program. Normal email replies should not notice any changes, but there may be exceptions for unintended uses that you pinned down with the signature as improper conduct. Sending you email a month after you ordered something in a webshop, for example, could be rejected due to an expiration of the email address.

About AxeSMTP

The AxeSMPT programs are ours. They are built on top of an efficient, event-driven program that passes email as a kind of SMTP pass-through server. It may intervene anywhere, chop up the traffic and paste the chips back together in another form.

The program as shown below is very young, and certainly needs to mature. The simplicity of the current code does however make it a perfect example of the simple ideas. So, the links below point at a fixed, young code version for explanatory reasons.

You are more than welcome to apply these ideas to your own programs, and we love to hear about such work!

Filtering Incoming Communication

Blocks similar to axesmtp-in in the diagram can be imagined for other application protocols, but email serves as a good example. The purpose here is to recognise the things we consider proper communication, and weed out anything else.

The program code processes callbacks when the commands MAIL FROM (with a sender address) and RCPT TO (with an intended recipient address) are sent.

  • During MAIL FROM, the sender address is parsed with a2id_parse_remote() to know that it has at least somewhat reasonable grammar. The form does not assume that the sender uses an ARPA2 Identity, which makes this check more lenient than it would be for local users.

  • During RCPT TO, the local recipient address is provided. Since this is a local address, it is possible to more stringently parse the recipient address with a2id_parse() that does require proper ARPA2 Identity forms.

  • Once parsed, the recipient address is subjected to Signed Identity testing with a2id_verify(). If it has a signature that does not pass, for instance because it has expired or because it does not permit the sender address, then it will lead to the refusal of the email. A fine point to note is that email without a signature always passes; it is up to the next phase to put up any minimum requirements through signature flags.

  • What now follows is a check of Communication Access with access_comm() which returns a level that allows diversified actions further down. Not all the ideas of the schema above are already implemented here, but the structures are clearly shown. Also note the actor output and the check if it was not set with a2act_isempty(). This is how the two whitelisted paths can be distinguished; whether to delivery the email to the queue or to group iteration.

  • Only after all this scrutiny will the email get passed to the backend for further handling. The level and whether or not an Actor Identity was returned provide timely information to distribute the email to various possible backends.

As you can see, all this logic fits into 174 lines of code, not counting generic SMTP support code in the directory above it. We believe that the ideas for ARPA2 Access Control are very powerful, but at the same time really simple to embed into any bit of software, for any protocol.

Processing Outgoing Communication

The only task currently handled in the program code for outgoing traffic is to add signatures into ARPA2 Identity forms that ask for it. This applies to sender addresses whose username part ends in + but not as the end of a complete signature.

  • This work cannot be done during the MAIL FROM command, because some of the data hashed into the signature may depend on the recipient address. So all that is done here is to check the ARPA2 Identity grammer with a2id_parse() and store the result.

  • When the destination address arrives during RCPT TO, the first thing being done is avoid trouble with basic parsing as a remote, non-ARPA2 address, using lenient parsing with a2id_parse_remote().

  • Next up, the email is signed with a2id_sign() if it holds a recipe; otherwise, the function returns success without having done a thing. As with a2id_verify() this choice was made to allow a consistent flow without separate checks and code paths for signing.

  • As with the input filter, the backend delivery is started for the recipient.

This all in just 169 lines, of which quite a bit is patched out. You should feel no hesitation in adding this to your own software, as far as we're concerned. Offering your users control over their own identity may help them escape from the cognitive dissonance of being a second-class citizen in a world that controls their online behaviour.

Delivering to Group Members

The last bit to cover is group iteration, and the delivery of mail to multiple members. This takes only slightly more code than the foregoing bits.

  • During MAIL FROM, the sender address is assumed to be an Actor Identity, which in the case of a Group Member means that it has a local domain and a username that consists of at least two parts separated by a + symbol. The part after the last + is considered a Member Alias and the part before is the address of the ARPA2 Group. This explains the parsing as an Actor Identity with a2act_parse() with 1 level of + being stripped off.

  • The recipient address provided during RCPT TO is also assumed to be a Group Address, though it may have any number of Member Aliases added; none would deliver to the group and any higher number would only deliver to the named members. This explains parsing as an Actor Identity with a2act_parse() with 0 levels of + stripped off, followed by a manual check that the group matches the group from the MAIL FROM phase.

  • This filter is larger because it also has a callback for DATA, and distributes the actual email to multiple backends. Also, it stores all recipient addresses for one call to member iteration in group_iterate() to avoid double delivery to Group Members.

  • Group Iteration will repeatedly start a callback procedure for Group Members to start backends for each. Further content is then forked to all these backends.

Well, that was a lot of logic in only 312 line, once more a sign of careful design and an API that wants to be easy to use and allow you to benefit from it.

Now go and grind your own Axe!

Go Top