7 /* Postfix SMTP protocol support for SASL authentication
10 /* #include "smtpd_sasl_proto.h"
12 /* void smtpd_sasl_auth_cmd(state, argc, argv)
13 /* SMTPD_STATE *state;
17 /* void smtpd_sasl_auth_reset(state)
18 /* SMTPD_STATE *state;
20 /* char *smtpd_sasl_mail_opt(state, sender)
21 /* SMTPD_STATE *state;
22 /* const char *sender;
24 /* void smtpd_sasl_mail_log(state)
25 /* SMTPD_STATE *state;
27 /* void smtpd_sasl_mail_reset(state)
28 /* SMTPD_STATE *state;
30 /* static int permit_sasl_auth(state, authenticated, unauthenticated)
31 /* SMTPD_STATE *state;
33 /* int unauthenticated;
35 /* This module contains random chunks of code that implement
36 /* the SMTP protocol interface for SASL negotiation. The goal
37 /* is to reduce clutter of the main SMTP server source code.
39 /* smtpd_sasl_auth_cmd() implements the AUTH command and updates
40 /* the following state structure members:
42 /* The authentication method that was successfully applied.
43 /* This member is a null pointer in the absence of successful
46 /* The username that was successfully authenticated.
47 /* This member is a null pointer in the absence of successful
50 /* smtpd_sasl_auth_reset() cleans up after the AUTH command.
51 /* This is required before smtpd_sasl_auth_cmd() can be used again.
53 /* smtpd_sasl_mail_opt() implements the SASL-specific AUTH=sender
54 /* option to the MAIL FROM command. The result is an error response
55 /* in case of problems.
57 /* smtpd_sasl_mail_log() logs SASL-specific information after
58 /* processing the MAIL FROM command.
60 /* smtpd_sasl_mail_reset() performs cleanup for the SASL-specific
61 /* AUTH=sender option to the MAIL FROM command.
63 /* permit_sasl_auth() permits access from an authenticated client.
64 /* This test fails for clients that use anonymous authentication.
68 /* SMTP session context.
70 /* Number of command line tokens.
72 /* The command line parsed into tokens.
74 /* Sender address from the AUTH=sender option in the MAIL FROM
77 /* Result for authenticated client.
78 /* .IP unauthenticated
79 /* Result for unauthenticated client.
81 /* All errors are fatal.
85 /* The Secure Mailer license must be distributed with this software.
87 /* Initial implementation by:
90 /* 65760 Eschborn, Germany
94 /* IBM T.J. Watson Research
96 /* Yorktown Heights, NY 10598, USA
98 /* TLS support originally by:
101 /* Allgemeine Elektrotechnik
102 /* Universitaetsplatz 3-4
103 /* D-03044 Cottbus, Germany
106 /* System library. */
108 #include <sys_defs.h>
111 #ifdef STRCASECMP_IN_STRINGS_H
115 /* Utility library. */
118 #include <mymalloc.h>
119 #include <stringops.h>
121 /* Global library. */
123 #include <mail_params.h>
124 #include <mail_proto.h>
125 #include <mail_error.h>
126 #include <ehlo_mask.h>
128 /* Application-specific. */
131 #include "smtpd_token.h"
132 #include "smtpd_chat.h"
133 #include "smtpd_sasl_proto.h"
134 #include "smtpd_sasl_glue.h"
138 /* smtpd_sasl_auth_cmd - process AUTH command */
140 int smtpd_sasl_auth_cmd(SMTPD_STATE
*state
, int argc
, SMTPD_TOKEN
*argv
)
142 char *auth_mechanism
;
143 char *initial_response
;
146 if (var_helo_required
&& state
->helo_name
== 0) {
147 state
->error_mask
|= MAIL_ERROR_POLICY
;
148 smtpd_chat_reply(state
, "503 5.5.1 Error: send HELO/EHLO first");
151 if (SMTPD_STAND_ALONE(state
) || !smtpd_sasl_is_active(state
)
152 || (state
->ehlo_discard_mask
& EHLO_MASK_AUTH
)) {
153 state
->error_mask
|= MAIL_ERROR_PROTOCOL
;
154 smtpd_chat_reply(state
, "503 5.5.1 Error: authentication not enabled");
157 if (smtpd_milters
!= 0 && (err
= milter_other_event(smtpd_milters
)) != 0) {
159 state
->error_mask
|= MAIL_ERROR_POLICY
;
160 smtpd_chat_reply(state
, "%s", err
);
163 /* Sendmail compatibility: map 4xx into 454. */
164 else if (err
[0] == '4') {
165 state
->error_mask
|= MAIL_ERROR_POLICY
;
166 smtpd_chat_reply(state
, "454 4.3.0 Try again later");
171 if (state
->tls_auth_only
&& !state
->tls_context
) {
172 state
->error_mask
|= MAIL_ERROR_PROTOCOL
;
173 /* RFC 4954, Section 4. */
174 smtpd_chat_reply(state
, "504 5.5.4 Encryption required for requested authentication mechanism");
178 if (state
->sasl_username
) {
179 state
->error_mask
|= MAIL_ERROR_PROTOCOL
;
180 smtpd_chat_reply(state
, "503 5.5.1 Error: already authenticated");
183 if (argc
< 2 || argc
> 3) {
184 state
->error_mask
|= MAIL_ERROR_PROTOCOL
;
185 smtpd_chat_reply(state
, "501 5.5.4 Syntax: AUTH mechanism");
190 * All authentication failures shall be logged. The 5xx reply code from
191 * the SASL authentication routine triggers tar-pit delays, which help to
192 * slow down password guessing attacks.
194 auth_mechanism
= argv
[1].strval
;
195 initial_response
= (argc
== 3 ? argv
[2].strval
: 0);
196 return (smtpd_sasl_authenticate(state
, auth_mechanism
, initial_response
));
199 /* smtpd_sasl_auth_reset - clean up after AUTH command */
201 void smtpd_sasl_auth_reset(SMTPD_STATE
*state
)
203 smtpd_sasl_logout(state
);
206 /* smtpd_sasl_mail_opt - SASL-specific MAIL FROM option */
208 char *smtpd_sasl_mail_opt(SMTPD_STATE
*state
, const char *addr
)
212 * Do not store raw RFC2554 protocol data.
214 if (!smtpd_sasl_is_active(state
)) {
215 state
->error_mask
|= MAIL_ERROR_PROTOCOL
;
216 return ("503 5.5.4 Error: authentication disabled");
219 if (state
->sasl_username
== 0) {
220 state
->error_mask
|= MAIL_ERROR_PROTOCOL
;
221 return ("503 5.5.4 Error: send AUTH command first");
224 if (state
->sasl_sender
!= 0) {
225 state
->error_mask
|= MAIL_ERROR_PROTOCOL
;
226 return ("503 5.5.4 Error: multiple AUTH= options");
228 if (strcmp(addr
, "<>") != 0) {
229 state
->sasl_sender
= mystrdup(addr
);
230 printable(state
->sasl_sender
, '?');
235 /* smtpd_sasl_mail_log - SASL-specific MAIL FROM logging */
237 void smtpd_sasl_mail_log(SMTPD_STATE
*state
)
239 #define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
241 msg_info("%s: client=%s%s%s%s%s%s%s",
242 state
->queue_id
? state
->queue_id
: "NOQUEUE", FORWARD_NAMADDR(state
),
243 IFELSE(state
->sasl_method
, ", sasl_method=", ""),
244 IFELSE(state
->sasl_method
, state
->sasl_method
, ""),
245 IFELSE(state
->sasl_username
, ", sasl_username=", ""),
246 IFELSE(state
->sasl_username
, state
->sasl_username
, ""),
247 IFELSE(state
->sasl_sender
, ", sasl_sender=", ""),
248 IFELSE(state
->sasl_sender
, state
->sasl_sender
, ""));
251 /* smtpd_sasl_mail_reset - SASL-specific MAIL FROM cleanup */
253 void smtpd_sasl_mail_reset(SMTPD_STATE
*state
)
255 if (state
->sasl_sender
) {
256 myfree(state
->sasl_sender
);
257 state
->sasl_sender
= 0;
261 /* permit_sasl_auth - OK for authenticated connection */
263 int permit_sasl_auth(SMTPD_STATE
*state
, int ifyes
, int ifnot
)
265 if (state
->sasl_method
&& strcasecmp(state
->sasl_method
, "anonymous"))