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
- Authenticate a user with ( username, password ) pair against
a Postgresql
database before sending a mail.
- 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.
- 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.
- Unzipped the files into the source directory of qmail by the
command
gunzip -c qesmtp.tar.gz | tar xvf -
- Execute make to obtain all object files.
- Edit Makefile.smtpd to reflect your postgresql directories.
- Execute make -f Makefile.smtpd to generate qmail-smtpd.
- 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:
- Click Tool, then select Account.
- Select your email account, then click Properties.
- From the Properties manual, click Servers.
- In the category Outgoing Mail Server, check
My server requires authentication, and then
click Settings.
- 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