Next: , Previous: , Up: Tutorial   [Contents][Index]


3.13 Greylisting

Greylisting is a simple method of defending against the spam proposed by Evan Harris. In few words, it consists in recording the ‘sender IP’-‘sender email’-‘recipient email’ triplet of mail transactions. Each time the unknown triplet is seen, the corresponding message is rejected with the tempfail code. If the mail is legitimate, this will make the originating server retry the delivery later, until the destination eventually accepts it. If, however, the mail is a spam, it will probably never be retried, so the users will not be bothered by it. Even if the spammer will retry the delivery, the greylisting period will give spam-detection systems, such as DNSBLs, enough time to detect and blacklist it, so by the time the destination host starts accepting emails from this triplet, it will already be blocked by other means.

You will find the detailed description of the method in The Next Step in the Spam Control War: Greylisting, the original whitepaper by Evan Harris.

The mailfromd implementation of greylisting is based on greylist function. The function takes two arguments: the key, identifying the greylisting triplet, and the interval. The function looks up the key in the greylisting database. If such a key is not found, a new entry is created for it and the function returns true. If the key is found, greylist returns false, if it was inserted to the database more than interval seconds ago, and true otherwise. In other words, from the point of view of the greylisting algorithm, the function returns true when the message delivery should be blocked. Thus, the simplest implementation of the algorithm would be:

prog envrcpt
do
 if greylist("${client_addr}-$f-${rcpt_addr}", interval("1 hour"))
   tempfail 451 4.7.1 "You are greylisted"
 fi
done

However, the message returned by this example, is not informative enough. In particular, it does not tell when the message will be accepted. To help you produce more informative messages, greylist function stores the number of seconds left to the end of the greylisting period in the global variable greylist_seconds_left, so the above example could be enhanced as follows:

prog envrcpt
do
  set gltime interval("1 hour")
  if greylist("${client_addr}-$f-${rcpt_addr}", gltime)
    if greylist_seconds_left = gltime
      tempfail 451 4.7.1
         "You are greylisted for %gltime seconds"
    else
      tempfail 451 4.7.1
         "Still greylisted for %greylist_seconds_left seconds"
    fi
  fi
done

In real life you will have to avoid greylisting some messages, in particular those coming from the ‘<>’ address and from the IP addresses in your relayed domain. It can easily be done using the techniques described in previous sections and is left as an exercise to the reader.

Mailfromd provides two implementations of greylisting primitives, which differ in the information stored in the database. The one described above is called traditional. It keeps in the database the time when the greylisting was activated for the given key, so the greylisting function uses its second argument (interval) and the current timestamp to decide whether the key is still greylisted.

The second implementation is called by the name of its inventor Con Tassios. This implementation stores in the database the time when the greylisting period is set to expire, computed by the greylist when it is first called for the given key, using the formula ‘current_timestamp + interval’. Subsequent calls to greylist compare the current timestamp with the one stored in the database and ignore their second argument. This implementation is enabled by one of the following pragmas:

#pragma greylist con-tassios

or

#pragma greylist ct

When Con Tassios implementation is used, yet another function becomes available. The function is_greylisted (see is_greylisted) returns ‘True’ if its argument is greylisted and ‘False’ otherwise. It can be used to check for the greylisting status without actually updating the database:

  if is_greylisted("${client_addr}-$f-${rcpt_addr}")
    …
  fi

One special case is whitelisting, which is often used together with greylisting. To implement it, mailfromd provides the function dbmap, which takes two mandatory arguments: dbmap(file, key) (it also allows an optional third argument, see dbmap, for more information on it). The first argument is the name of the DBM file where to search for the key, the second one is the key to be searched. Assuming you keep your whitelist database in file /var/run/whitelist.db, a more practical example will be:

prog envrcpt
do
  set gltime interval("1 hour")

  if not ($f = "" or relayed(hostname(${client_addr}))
         or dbmap("/var/run/whitelist.db", ${client_addr}))
    if greylist("${client_addr}-$f-${rcpt_addr}", gltime)
      if greylist_seconds_left = gltime
        tempfail 451 4.7.1
           "You are greylisted for %gltime seconds"
      else
        tempfail 451 4.7.1
           "Still greylisted for %greylist_seconds_left seconds"
      fi
    fi
  fi
done

Next: , Previous: , Up: Tutorial   [Contents][Index]