From: Mark Morley Date: Sat, 11 Dec 1999 10:45:38 -0800 (PST) This is a HOW-TO for setting up Exim to support SMTP authentication under different environments, including regular password files, PAM and NIS. The goal is to allow local users to relay without requiring authentication, and disallow relaying by remote users UNLESS they authenticate. If a user authenticates then their username is included in the log file entries so they can't abuse your server without incurring your wrath. The first thing you need to do is make sure you enabled the right things in the Local/Makefile before compiling. You will need the following line: AUTH_PLAINTEXT=yes And possibly this line: AUTH_CRAM_MD5=yes If your server uses PAM then you will also need this line: SUPPORT_PAM=yes Next you need to edit your configure file. To achieve our goals we need to set three entries: host_accept_relay should list those hosts that are allowed to relay without needing to authenticate. This is normally a list of your local IP numbers. host_auth_accept_relay should list those hosts that are allowed to relay so long as they authenticate first. We just set this to "*" to allow authentication from anywhere. If there are any hosts that you REQUIRE authentication from, even if they are listed in host_accept_relay, then list them in the auth_hosts setting. In my case I only have one IP listed, and that's the one I'm using to test authentication with (ie: my personal static IP number). Next comes the potentially tricky part. You need to edit the AUTH section of the configure file (it's the section right after REWRITE). You need to create an entry for each authentication method you wish to support. The first authentication method we'll create is called AUTH PLAIN. It is the method used by Netscape Messenger for example. With the PLAIN method the client sends a command like this: AUTH PLAIN AHVzZXJuYW1lAHBhc3N3b3Jk That third item there is actually three strings, separated by nul characters, and then base64 encoded. The first string is not used here. The second string will be the username, and the third string will be the password. The entry for AUTH PLAIN will look something like this: plain: driver = plaintext public_name = PLAIN server_condition = ???? server_set_id = $2 The tricky bit is deciding what the server_condition should be, and that depends on whether you are using PAM, NIS, plain password files, etc. The server_condition string will be expanded, and if the result is "1" then authentication is successful - if it's "0" then authentication failed. At the point where the string is expanded, the username is stored in $2 and the password in $3, so we just need to perform whatever lookups and comparisons are necessary to validate the user. For example, here's a server_condition that works for a specific user named "bloggs" with the password "freddy": server_condition = "${if and{ {eq{$2}{bloggs}} {eq{$3}{freddy}} } {1}{0}}" But a single hardcoded example isn't all that useful (unless you only want a specific user to be able to authenticate). Here's one that works with a non-shadowed NIS based password file: server_condition = "${if and {{!eq{$2}{}}{!eq{$3}{}} \ {crypteq{$3}{${extract{2}{:} \ {${lookup{$2}nis{passwd.byname}{$value}{*:*}}}}}}}{1}{0}}" That's a tad more complicated! At the heart of it it performs an NIS lookup on the "passwd.byname" map using $2 (the username) as the key. If the lookup is successful then we get a typical passwd file entry, otherwise we get the bogus entry "*:*". From the result it extracts the second field using a colon as the delimiter. This results in either the user's encrypted password or "*". It then encrypts $3 (the plaintext password) and compares that against the extracted value. It also checks both $2 and $3 to ensure that neither is a null string. If the whole condition is true then it resolves to "1", otherwise it resolves to "0". If your server uses a shadow passwd file with NIS, then you simply need to change the map from "passwd.byname" to "passwd.adjunct.byname" or whatever name your system uses. If your mail server uses a traditional passwd file, you could probably do something like this (untested): server_condition = "${if and {{!eq{$2}{}}{!eq{$3}{}} \ {crypteq{$3}{${extract{2}{:} \ {${lookup{$2}lsearch{/etc/passwd}{$value}{*:*}}}}}}}{1}{0}}" If you use shadow passwords you could change the "/etc/passwd" to "/etc/shadow" or "/etc/security/passwd.adjunct", etc. If your mail server uses PAM, then the condition is much simpler: server_condition = "${if pam{$2:$3}{1}{0}}" Since Exim has built-in PAM support you don't need such a complicated string expansion. So now we want to add a second authentication method. This one is called AUTH LOGIN and is used by Outlook Express, among others. This is similar to the PLAIN method, except that the client expects the server to prompt it for the username and password one at a time. Here's the basic entry: login: driver = plaintext public_name = LOGIN server_prompts = "Username:: : Password::" server_condition = ???? server_set_id = $1 The primary difference from the PLAIN method is the server_prompts setting, which is a colon-separated list of prompts to issue to the client. According to the AUTH LOGIN specification, there should be two prompts and they should always be "User Name" and "Password". But Microsoft is never content to leave things be and this will only work with Outlook Express if you use "Username:" and "Password:". The double "::" is needed to escape the trailing colons. After the prompts are issued and the client has submitted it's responses, the username will be stored in $1 and the password in $2. You can use the same server condition that you used for the PLAIN method, just change the $2's to $1's, and the $3's to $2's. Both the PLAIN and LOGIN methods transfer unencrypted copies of the username and password over the 'net. This is not the most secure way of doing things (although the fact that it's base64 encoded makes it a bit more secure than the way the same data is sent for a standard POP session). So there is a third method you may want to support called CRAM-MD5. This is the method used by Eudora for example. CRAM-MD5 never sends the password at all. The server issues a challenge, which the client encrypts using the user's password, and returns to the server. The server performs the same encryption. If the two strings match then the client must have used the same password and therefore it's safe to authenticate them. The problem with CRAM-MD5 is that in order for it to work, the server must have an UNENCRYPTED copy of the user's password. With most typical servers this isn't possible, since the passwords in a UNIX passwd file are normally one-way encrypted. So unless you are willing to maintain a separate database of plaintext username/password pairs for those users who want to use CRAM-MD5, it's of little value. In our case we use Qualcomm's POP server software which allows Eudora users to send email via the POP server itself (via the POP XMIT command), so SMTP authentication isn't really needed for them anyway. So that's that. Whether it's via SMTP authentication or POP XMIT, the majority of our users can now relay through our mail server regardless of where they are connecting from. Now there is one problem, in my opinion, with this whole setup. Certain email clients (eg: Netscape Messenger) will notice that your server now accepts authentication, and will assume that it's required. That means that even users on your local network will suddenly have to enter their password whenever they send a message. While this works, it causes a lot of confusion for people who have never had to do that before. To my way of thinking it would be better to hide the fact that authentication is supported when the client is connecting from a local IP number. To do this you must make a simple modification to the Exim source code. On line 1943 of src/smtp_in.c (Exim 3.12) you will find the following condition: if (auths != NULL) Change it to this: if (auths != NULL && (host_must_authenticate || !verify_check_host(&host_accept_relay, FALSE))) And recompile. Your server will now only advertise the AUTH capability to clients that are required to authenticate (ie: they are listed in the auth_hosts setting) or those that are NOT listed in host_accept_relay. mark@islandnet.com