Extending Qmail

Dec. 2002

Presented by Fore June
Author of Windows Fan, Linux Fan

( This document is best viewed with NS 6.0 or higher versions. )

Qmail is a contemporary Internet Mail Transfer Agent (MTA) designed for high security. It is written in a modular way that users can easily extend or modify its functions. We discuss here how to extend the smtp server and the integration of the POP3 server with a database. In this article, when a term is presented in italics, it means that the term is not a terminal but should be substituted by actual names.

1. The Extended Simple Mail Transfer Protocol

The Programs

Mail User Agents ( MUAs ) such as Eudora and Outlook use the Simple Mail Transfer Protocol ( SMTP ) to send mail messages to Mail Transfer Agents ( MTAs ) such as qmail and sendmail. MTAs in turn use SMTP to transfer messages to other MTAs. The many extensions to SMTP have become known as the Extended Simple Mail Transfer Protocol ( ESTMP ). Basically, qmail supports SMTP but not ESMTP. For various reasons, many email service providers find that there is a need to authenticate users when delivering emails for them. The authentication is a function of the ESTMP but not SMTP. We discuss here how to incorporate this function in the qmail package. We shall modify or add a couple qmail files to achieve the following

  1. Authenticate a user with ( username, password ) pair against a Postgresql database before sending a mail.
  2. Increment the counter of a user when the user sends a message.
     
The modified or new files are available for download at this site. The qmail package put all the source files in the same directory which makes the modifications straight forward. At this point, you may need to first execute make to generate all the required object files and libraries for subsequent linking if you have cleared all those files before. In general, each file can be compiled independently. For example, the command
  cc -c env.c
 
should generate the object file env.o. You can always ignore the warning errors in the compilation process. In this project, the only file that we need to modify is qmail-smtpd.c which is responsible for communications with MUAs. We copied this file to qmail-esmtpd.c and added a few lines of code. The new function int authenticated( char *login, char *pw); is used to carry out the database authentication and its actual implementation is in the new file smtp-auth.c. The function smtp_auth ( char *arg ); is added in the file qmail-esmtpd.c. If an MUA like Outlook or Eudora has been set to request SMTP authentication, this function will be called, otherwise it will not be called and everything works as the original qmail-smtpd, which utilizes IP addresses saved in the file tcp.smtp.cdb to determine if a client is allowed to relay messages. This function greps the login name and password from the MUA and passes them to authenticated() to carry out the authentication.

As mentioned earlier, the function authenticated() is implemented in the file smtp-auth.c. When you examine the file smtp-auth.c you will find that it includes two header files, namely, libpq-fe.h which comes with the Postgresql package, and db.h which contains the lines:
#define DB_NAME        "qmail"
#define AUTH_TABLE_NAME        "poppass"
#define COUNT_TABLE_NAME        "sendcounts"
#define MAX_COUNT        50
where DB_NAME is the name of the database, and AUTH_TABLE_NAME is the name of the database table containing the ( username, password ) pair. The names we used are qmail and poppass respectively; you must change the names in this header file to the ones that you use in your system. Also, COUNT_TABLE_NAME is the name of the table containing the number of times the user sends emails and MAX_COUNT is the maximum number of times a user is allowed to send emails in one day; these two parameters are used in the file checkcounts.c. Note that you have to grant select right of the tables to the system user qmaild for the programs to work properly. We shall discuss these tables in more detail later. We shall use the table names poppass and sendcounts as examples in our discussion. The idea of authentication is very simple. A valid user name and her encrypted password are saved in the table poppass. The function authenticated( char *login, char *pw) obtains the plain username,and password from the routine smtp_auth(). It encrypts the password and checks against poppass to see if the pair ( username, encrypted password ) is in the table. If not, it returns 0 to signify the failures in authentication. If yes, it further checks the table sendcounts to see if the MAX_COUNT has reached; if it has, it again returns 0 otherwise it returns 1 to signify the success in authentication. The table poppass is also used in POP3 authentication that we shall discuss later. It can be defined as follows,
create table poppass (
  id   		int4,   		--user id relates to other tables
  name 		varchar(20) not null,   --user name
  pw   		varchar(20) not null,   --encrypted password
  sysuser       varchar(20) not null,   --System user
  hostname      varchar(40) not null,   --hostname of email server
  ip   	        varchar(20) not null,   --IP address of email server
  dir           varchar(80) not null,   --directory where incoming emails are saved
);
Here, only the three entities, id, name, pw have been used. Other entries are for POP3 authentication, which is irrelevant at the moment. If you do not need the POP authentication you can simply reduce the table to three entries ( id, name, pw ). Moreover, if you prefer to authenticate with a plain password instead of an encrypted password, you can simply replace in the program smtp-auth.c, the line strcpy( encr_pass, sp ) by strcpy( encr_pass, pass ). You can compile the enhanced qmail-esmtpd.c file by
cc   -c    qmail-esmtpd.c
 
which generates the object file qmail-esmtpd.o. To compile the new file smtp-auth.c, you must include the path where the header file libpq-fe.h is located. In my system, it is located at /apps/postgres/include. Therefore, I used the command,
  cc    -c    -I/apps/postgres/include    smtp-auth.c
 
to generate the object file smtp-auth.o.

The function checkcounts( int id ) is implemented in the new file checkcounts.c. Its mechanism is also very straight forward. The table sendcounts is created in the database as follows,
create table sendcounts (
  id  		int4 not null PRIMARY KEY,	--id of user, relates to poppass
  last_send	date,		--last date the user sends email
  count		int4,		--today's count
  total_count	int4		--accumulated counts
);
The function checks the row of the table with the user's id. It then reads the values of last_send, count, and total_count. last_send contains the last date that the user has sent an email. If it does not match the current date, count is reset to 1 and the value 1 is returned by the function to signify that the email is allowed to be sent. Otherwise, it is incremented by 1 and compared with MAX_COUNT. If it exceeds the MAX_COUNT value, the function returns 0 to signify that the email is not allowed to send. In the former case, total_count is incremented by 1 and both the new count and total_count values are written back to the sendcounts table. As discussed above, to compile the file, I used the command,
  cc    -c    -I/apps/postgres/include    checkcounts.c
 
to generate checkcounts.o. Now you need to link the object files together to generate the executable qmail-smtpd. The only extra library that you need to link is the postgresql pq library. In my system, it is located at /apps/postgres/7.3.1/lib. Therefore, I need to add the statement -L/apps/postgres/7.3.1/lib -lpq in the linking. Again, you have to locate yours and put in the appropriate path. In my system, the following command generates the executable qmail-smtpd:
        cc -s -o qmail-smtpd  qmail-esmtpd.o checkcounts.o smtp-auth.o \
        rcpthosts.o commands.o timeoutread.o \
        timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
        received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
        datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ 
        alloc.a substdio.a error.a str.a fs.a auto_qmail.o cat \
        socket.lib -L/apps/postgres/7.3.1/lib -lpq


 
Now you need to login as root and copy qmail-smtpd to its working directory. For example, you should use the command
	cp  qmail-smtpd   /var/qmail/bin

if you have installed your qmail system in the directory /var/qmail. If the file is locked, you need to first turn off qmail and copy again.

I have combined the above compilation steps and put them in a makefile called Makefile.smtpd. The steps to obtain the enhanced executable qmail-smtpd that supports smtp authentication against a postgresql database is summarized as follows.
  1. Obtain the zipped file qesmtp.tar.gz which contains the source files, qmail-esmtpd.c, smtp-auth.c, checkcounts.c ,db.h and Makefile.smtpd.
  2. Unzipped the files into the source directory of qmail by the command gunzip -c qesmtp.tar.gz | tar xvf -
  3. Execute make to obtain all object files.
  4. Edit Makefile.smtpd to reflect your postgresql directories.
  5. Execute make -f Makefile.smtpd to generate qmail-smtpd.
  6. Login as root to copy qmail-smtpd to its working directory.
Of course the database and tables have to be created as described above. Do not forget to grant select right of the tables to the system user qmaild. I have also added syslog to the programs. When sending an email, you may examine the log by tail /var/log/messages.

Mail Client Settings

Many MUAs, which are also referred to as mail clients support SMTP authentication. As an example, you can enable the SMTP authentication of Outlook Express by the following sequence of actions:
  1. Click Tool, then select Account.
  2. Select your email account, then click Properties.
  3. From the Properties manual, click Servers.
  4. In the category Outgoing Mail Server, check My server requires authentication, and then click Settings.
  5. Enter the logon information and click OK.


Download

You may download the file by clicking the link below:
qesmtp.tar.gz

2. POP Authentication with Database

coming soon.

Acknowlegments

Many thanks to Cula.net for allowing me to post materials on its site, Mr. Richard Friedman for giving valuable pointers, and the open source community for making the wonderful sharing of ideas possible.

Fore June, 2002.
Author of Windows Fan, Linux Fan