1 From: Mark Morley <mark@islandnet.com>
2 Date: Sat, 11 Dec 1999 10:45:38 -0800 (PST)
4 This is a HOW-TO for setting up Exim to support SMTP authentication under
5 different environments, including regular password files, PAM and NIS.
7 The goal is to allow local users to relay without requiring authentication,
8 and disallow relaying by remote users UNLESS they authenticate. If a user
9 authenticates then their username is included in the log file entries so
10 they can't abuse your server without incurring your wrath.
12 The first thing you need to do is make sure you enabled the right things in
13 the Local/Makefile before compiling. You will need the following line:
17 And possibly this line:
21 If your server uses PAM then you will also need this line:
25 Next you need to edit your configure file. To achieve our goals we need
28 host_accept_relay should list those hosts that are allowed to relay
29 without needing to authenticate. This is normally a list of your
32 host_auth_accept_relay should list those hosts that are allowed to
33 relay so long as they authenticate first. We just set this to "*"
34 to allow authentication from anywhere.
36 If there are any hosts that you REQUIRE authentication from, even if
37 they are listed in host_accept_relay, then list them in the auth_hosts
38 setting. In my case I only have one IP listed, and that's the one I'm
39 using to test authentication with (ie: my personal static IP number).
41 Next comes the potentially tricky part. You need to edit the AUTH section
42 of the configure file (it's the section right after REWRITE). You need to
43 create an entry for each authentication method you wish to support.
45 The first authentication method we'll create is called AUTH PLAIN. It is
46 the method used by Netscape Messenger for example. With the PLAIN method
47 the client sends a command like this:
49 AUTH PLAIN AHVzZXJuYW1lAHBhc3N3b3Jk
51 That third item there is actually three strings, separated by nul characters,
52 and then base64 encoded. The first string is not used here. The second
53 string will be the username, and the third string will be the password.
55 The entry for AUTH PLAIN will look something like this:
60 server_condition = ????
63 The tricky bit is deciding what the server_condition should be, and that
64 depends on whether you are using PAM, NIS, plain password files, etc. The
65 server_condition string will be expanded, and if the result is "1" then
66 authentication is successful - if it's "0" then authentication failed.
68 At the point where the string is expanded, the username is stored in $2
69 and the password in $3, so we just need to perform whatever lookups and
70 comparisons are necessary to validate the user.
72 For example, here's a server_condition that works for a specific user
73 named "bloggs" with the password "freddy":
75 server_condition = "${if and{ {eq{$2}{bloggs}} {eq{$3}{freddy}} } {1}{0}}"
77 But a single hardcoded example isn't all that useful (unless you only want
78 a specific user to be able to authenticate). Here's one that works with a
79 non-shadowed NIS based password file:
81 server_condition = "${if and {{!eq{$2}{}}{!eq{$3}{}} \
82 {crypteq{$3}{${extract{2}{:} \
83 {${lookup{$2}nis{passwd.byname}{$value}{*:*}}}}}}}{1}{0}}"
85 That's a tad more complicated! At the heart of it it performs an NIS lookup
86 on the "passwd.byname" map using $2 (the username) as the key. If the
87 lookup is successful then we get a typical passwd file entry, otherwise
88 we get the bogus entry "*:*". From the result it extracts the second field
89 using a colon as the delimiter. This results in either the user's encrypted
90 password or "*". It then encrypts $3 (the plaintext password) and compares
91 that against the extracted value. It also checks both $2 and $3 to ensure
92 that neither is a null string. If the whole condition is true then it
93 resolves to "1", otherwise it resolves to "0".
95 If your server uses a shadow passwd file with NIS, then you simply need to
96 change the map from "passwd.byname" to "passwd.adjunct.byname" or whatever
97 name your system uses.
99 If your mail server uses a traditional passwd file, you could probably do
100 something like this (untested):
102 server_condition = "${if and {{!eq{$2}{}}{!eq{$3}{}} \
103 {crypteq{$3}{${extract{2}{:} \
104 {${lookup{$2}lsearch{/etc/passwd}{$value}{*:*}}}}}}}{1}{0}}"
106 If you use shadow passwords you could change the "/etc/passwd" to
107 "/etc/shadow" or "/etc/security/passwd.adjunct", etc.
109 If your mail server uses PAM, then the condition is much simpler:
111 server_condition = "${if pam{$2:$3}{1}{0}}"
113 Since Exim has built-in PAM support you don't need such a complicated
116 So now we want to add a second authentication method. This one is called
117 AUTH LOGIN and is used by Outlook Express, among others. This is similar
118 to the PLAIN method, except that the client expects the server to prompt
119 it for the username and password one at a time.
121 Here's the basic entry:
126 server_prompts = "Username:: : Password::"
127 server_condition = ????
130 The primary difference from the PLAIN method is the server_prompts setting,
131 which is a colon-separated list of prompts to issue to the client. According
132 to the AUTH LOGIN specification, there should be two prompts and they should
133 always be "User Name" and "Password". But Microsoft is never content to
134 leave things be and this will only work with Outlook Express if you use
135 "Username:" and "Password:". The double "::" is needed to escape the
138 After the prompts are issued and the client has submitted it's responses,
139 the username will be stored in $1 and the password in $2. You can use the
140 same server condition that you used for the PLAIN method, just change the
141 $2's to $1's, and the $3's to $2's.
143 Both the PLAIN and LOGIN methods transfer unencrypted copies of the username
144 and password over the 'net. This is not the most secure way of doing things
145 (although the fact that it's base64 encoded makes it a bit more secure than
146 the way the same data is sent for a standard POP session). So there is a
147 third method you may want to support called CRAM-MD5. This is the method
148 used by Eudora for example.
150 CRAM-MD5 never sends the password at all. The server issues a challenge,
151 which the client encrypts using the user's password, and returns to the
152 server. The server performs the same encryption. If the two strings
153 match then the client must have used the same password and therefore it's
154 safe to authenticate them.
156 The problem with CRAM-MD5 is that in order for it to work, the server must
157 have an UNENCRYPTED copy of the user's password. With most typical servers
158 this isn't possible, since the passwords in a UNIX passwd file are normally
161 So unless you are willing to maintain a separate database of plaintext
162 username/password pairs for those users who want to use CRAM-MD5, it's
165 In our case we use Qualcomm's POP server software which allows Eudora
166 users to send email via the POP server itself (via the POP XMIT command),
167 so SMTP authentication isn't really needed for them anyway.
169 So that's that. Whether it's via SMTP authentication or POP XMIT, the
170 majority of our users can now relay through our mail server regardless
171 of where they are connecting from.
173 Now there is one problem, in my opinion, with this whole setup. Certain
174 email clients (eg: Netscape Messenger) will notice that your server now
175 accepts authentication, and will assume that it's required. That means
176 that even users on your local network will suddenly have to enter their
177 password whenever they send a message.
179 While this works, it causes a lot of confusion for people who have never
180 had to do that before. To my way of thinking it would be better to hide
181 the fact that authentication is supported when the client is connecting
182 from a local IP number.
184 To do this you must make a simple modification to the Exim source code.
185 On line 1943 of src/smtp_in.c (Exim 3.12) you will find the following
192 if (auths != NULL && (host_must_authenticate ||
193 !verify_check_host(&host_accept_relay, FALSE)))
195 And recompile. Your server will now only advertise the AUTH capability
196 to clients that are required to authenticate (ie: they are listed in
197 the auth_hosts setting) or those that are NOT listed in host_accept_relay.