Sendmail 8.16.0.29 with PostgreSQL support

NOTE THAT SENDMAIL 8.16.0.29 IS NOT AN OFFICIAL RELEASE. THIS IS A TESTING VERSION AND THE POSTGRESQL DATABASE MAP SUPPORT WAS ADDED BY KALEVI KOLTTONEN <kalevi@kolttonen.fi>. SO PLEASE DO NOT SEND BUG REPORTS ABOUT THE POSTGRESQL FEATURE TO THE SENDMAIL FOLKS.

I CHOSE SENDMAIL 8.16.0.29 AS A DEVELOPMENT TARGET BECAUSE AS OF THIS WRITING (JANUARY 19TH, 2019), IT IS THE LATEST SNAPSHOT THAT PROOFPOINT HAS RELEASED.

YOU MUST UNDERSTAND THAT THE POSTGRESQL MAP SUPPORT IS NOT GOING TO BE IN THE OFFICIAL SENDMAIL 8.16. THIS PROJECT IS JUST MY PERSONAL EXPERIMENT.

Background

It is now Saturday, January 19th, 2019. Back in 2018 I got the idea of trying to add PostgreSQL database support for Sendmail. A stock Sendmail already supports LDAP, Berkeley DB, socket maps, and so on.

Like many old-school Unix users know, Sendmail is a famous MTA written by Eric Allman. PostgreSQL is a well-known object-relational_database that supports SQL and has features comparable to the Oracle databases. Both Sendmail and PostgreSQL are released under a liberal, open source licensing, so it is possible to study their sources and make changes to them.

Personally, I would prefer to always release my own code under Richard Stallman's GNU General Public License, but in this case I think it is best to make an exception. So the experimental PostgreSQL map code presented here is released under the same license as Sendmail itself. I am not a lawyer, but I guess the Sendmail license is a kind of a BSD license.

Sendmail rulesets and database maps

When Sendmail does key-value lookups using its pretty weird configuration language, those calls are largely database agnostic. It means that the rulesets refer to the databases using a unique name that is defined using a K-line directive such as:

Kaccess hash -T<TMPF> -o /etc/mail/access.db

The definition above tells Sendmail that there is a Berkeley DB hash type database in the optional file /etc/mail/access.db and the name of that database map is access.

The rulesets do not know, or care about, the fact that access is implemented using a Berkeley DB hash. It could as well be an LDAP-based map and the rulesets would continue to work just the same.

I have added an experimental support for PostgreSQL datamaps maps for the Sendmail testing release 8.16.0.29. You can identify this test version using the following command as root:

[root@localhost ~]# /usr/sbin/sendmail -bt -d0.1 < /dev/null

Below you can see that it is Version 8.16.0.29-pgmap and that PGMAP appears in the Compiled with: section:

Version 8.16.0.29-pgmap
Compiled with: DNSMAP IPV6_FULL LDAPMAP LOG MAP_REGEX MATCHGECOS MILTER
MIME7TO8 MIME8TO7 NAMED_BIND NETINET NETUNIX NEWDB PGMAP
PIPELINING SASLv2 SCANF SOCKETMAP STARTTLS TLS_VRFY_PER_CTX
USERDB USE_LDAP_INIT XDEBUG

============ SYSTEM IDENTITY (after readcf) ============
(short domain name) $w = localhost
(canonical domain name) $j = localhost.localdomain
(subdomain name) $m = localdomain
(node name) $k = localhost.localdomain
========================================================

ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>

Downloading and installing

Here are the RPMs for Fedora Linux 29.

If you need just the PostgreSQL map support patch, you can download it here:

To install the binary RPM and tools for creating configurations, you can do:

sudo dnf -y install sendmail-8.16.0.29-1.fc29.x86_64.rpm sendmail-cf-8.16.0.29-1.fc29.noarch.rpm

In case you are not using my RPMs, you could compile Sendmail from the tar.gz with the following in the devtools/Site/site.config.m4:

define(`confBLDVARIANT', `DEBUG')
define(`confMAPDEF', `-DNEWDB -DMAP_REGEX -DLDAPMAP -DLDAP_DEPRECATED -DSOCKETMAP -DNAMED_BIND=1 -DSASL=2 -DNIS=0')
define(`confCCOPTS', `-W -Wall -g3')
APPENDDEF(`confLIBDIRS', `-L/usr/lib64/libdb4')
APPENDDEF(`confENVDEF',`-DSTARTTLS')
APPENDDEF(`confLIBS', `-lssl -lcrypto -ldb -lldap -llber -lresolv -lpq -lsasl2')
APPENDDEF(`confINCDIRS', `-I/usr/include/openssl -I/usr/include/libdb4 -I/usr/include/sasl')
APPENDDEF(`confMAPDEF',`-DPGMAP')

Configuring Sendmail and PostgreSQL

I have told Sendmail that virtusertable and mailertable are PostgreSQL based database maps:

[root@localhost ~]# grep pg /etc/mail/sendmail.mc
FEATURE(`virtusertable', `pg -H postgresql://kalevi:password@127.0.0.1:5432/kalevidb -k vtable')dnl
FEATURE(`mailertable', `pg -H postgresql://kalevi:password@127.0.0.1:5432/kalevidb -k mtable')dnl

In addition I have:

VIRTUSER_DOMAIN_FILE(`/etc/mail/virtuserdomains')dnl

The file /etc/mail/sendmail.cf needs to be rebuilt:

cd /etc/mail
make sendmail.cf

I have hardcoded names key and value in the PostgreSQL SELECT statement in my C code. So you must create a PostgreSQL table having key and value fields.

Here is a sample of my test data in PostgreSQL 10.6 database called kalevidb:

kalevidb=# select * from vtable;
id | key | value
------+------------------------+--------------------------
1973 | david.bowie@leary.org | firstvalue@leary.org
2019 | firstvalue@leary.org | bowie@heaven.example.com
(2 rows)

kalevidb=# select * from mtable;
id | key | value
-----+--------------+--------------------
777 | .example.com | esmtp:[127.0.0.1]
(1 row)

I have id field having type INTEGER. The fields key and value are of type TEXT and they are what Sendmail is interested in. If you want to have extra fields (e.g. for including comments, or other metadata for the key), that is perfectly fine as long as you have key and value.

I inserted domain name leary.org into /etc/mail/virtuserdomains so that Sendmail will use virtusertable for addresses in that leary.org domain:

sudo echo leary.org >> /etc/mail/virtuserdomains

Testing PostgreSQL maps

Finally I tested the PostgreSQL based virtusertable with the address david.bowie@leary.org:

[root@localhost ~]# /usr/sbin/sendmail -d38.2 -bv david.bowie@leary.org
seq_map_parse(aliases.files, )
pg_map_open(virtuser, 0): opening new connection
sm_pg_resolve(0x1e88140, table='vtable' key='david.bowie@leary.org', value='firstvalue@leary.org'
sm_pg_resolve(0x1e88140, table='vtable' key='firstvalue@leary.org', value='bowie@heaven.example.com'
pg_map_open(mailertable, 0): using cached connection
sm_pg_resolve(0x1e88140, table='mtable' key='heaven.example.com', value='<NULL pointer>'
sm_pg_resolve(0x1e88140, table='mtable' key='.example.com', value='esmtp:[127.0.0.1]'
david.bowie@leary.org... deliverable: mailer esmtp, host [127.0.0.73], user bowie@heaven.example.com
pg_map_close(virtuser) entered
pg_map_close(virtuser) done
pg_map_close(virtuser): closed mailertable (shared pg connection)

As you can see, when using virtuser map, Sendmail opened the initial connection to PostgreSQL database. The second pg_map_open() concerning mailertable informed us that it is using a cached connection. That is exactly how Sendmail is supposed to work, i.e. it reuses connections whenever possible.

After the database lookups had been done, Sendmail closed the PostgreSQL database connection and marked all maps using it as closed (e.g. mailertable).

Conclusion

PostgreSQL based database maps seem to work with Sendmail 8.16.0.29. However, I have done only a few tests and cannot claim that the C code is flawless. This is just an experiment, nothing more, nothing less.

Please note that I have not implemented any kind of timeout mechanism for database connections or the queries. Despite that I am pretty happy with the way how this patch turned out to be. Many thanks to Unto Sten for valuable help and advice.

Have fun!

Kalevi Kolttonen <kalevi@kolttonen.fi>
Helsinki, Finland