1 /*++
2 /* NAME
3 /* smtpd_sasl_proto 3
4 /* SUMMARY
5 /* Postfix SMTP protocol support for SASL authentication
6 /* SYNOPSIS
7 /* #include "smtpd.h"
8 /* #include "smtpd_sasl_proto.h"
9 /*
10 /* int smtpd_sasl_auth_cmd(state, argc, argv)
11 /* SMTPD_STATE *state;
12 /* int argc;
13 /* SMTPD_TOKEN *argv;
14 /*
15 /* void smtpd_sasl_auth_extern(state, username, method)
16 /* SMTPD_STATE *state;
17 /* const char *username;
18 /* const char *method;
19 /*
20 /* void smtpd_sasl_auth_reset(state)
21 /* SMTPD_STATE *state;
22 /*
23 /* char *smtpd_sasl_mail_opt(state, sender)
24 /* SMTPD_STATE *state;
25 /* const char *sender;
26 /*
27 /* void smtpd_sasl_mail_reset(state)
28 /* SMTPD_STATE *state;
29 /*
30 /* static int permit_sasl_auth(state, authenticated, unauthenticated)
31 /* SMTPD_STATE *state;
32 /* int authenticated;
33 /* int unauthenticated;
34 /* DESCRIPTION
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.
38 /*
39 /* smtpd_sasl_auth_cmd() implements the AUTH command and updates
40 /* the following state structure members:
41 /* .IP sasl_method
42 /* The authentication method that was successfully applied.
43 /* This member is a null pointer in the absence of successful
44 /* authentication.
45 /* .IP sasl_username
46 /* The username that was successfully authenticated.
47 /* This member is a null pointer in the absence of successful
48 /* authentication.
49 /* .PP
50 /* smtpd_sasl_auth_reset() cleans up after the AUTH command.
51 /* This is required before smtpd_sasl_auth_cmd() can be used again.
52 /* This may be called even if SASL authentication is turned off
53 /* in main.cf.
54 /*
55 /* smtpd_sasl_auth_extern() records authentication information
56 /* that is received from an external source.
57 /* This may be called even if SASL authentication is turned off
58 /* in main.cf.
59 /*
60 /* smtpd_sasl_mail_opt() implements the SASL-specific AUTH=sender
61 /* option to the MAIL FROM command. The result is an error response
62 /* in case of problems.
63 /*
64 /* smtpd_sasl_mail_reset() performs cleanup for the SASL-specific
65 /* AUTH=sender option to the MAIL FROM command.
66 /*
67 /* permit_sasl_auth() permits access from an authenticated client.
68 /* This test fails for clients that use anonymous authentication.
69 /*
70 /* Arguments:
71 /* .IP state
72 /* SMTP session context.
73 /* .IP argc
74 /* Number of command line tokens.
75 /* .IP argv
76 /* The command line parsed into tokens.
77 /* .IP sender
78 /* Sender address from the AUTH=sender option in the MAIL FROM
79 /* command.
80 /* .IP authenticated
81 /* Result for authenticated client.
82 /* .IP unauthenticated
83 /* Result for unauthenticated client.
84 /* DIAGNOSTICS
85 /* All errors are fatal.
86 /* LICENSE
87 /* .ad
88 /* .fi
89 /* The Secure Mailer license must be distributed with this software.
90 /* AUTHOR(S)
91 /* Initial implementation by:
92 /* Till Franke
93 /* SuSE Rhein/Main AG
94 /* 65760 Eschborn, Germany
95 /*
96 /* Adopted by:
97 /* Wietse Venema
98 /* IBM T.J. Watson Research
99 /* P.O. Box 704
100 /* Yorktown Heights, NY 10598, USA
101 /*
102 /* Wietse Venema
103 /* Google, Inc.
104 /* 111 8th Avenue
105 /* New York, NY 10011, USA
106 /*
107 /* TLS support originally by:
108 /* Lutz Jaenicke
109 /* BTU Cottbus
110 /* Allgemeine Elektrotechnik
111 /* Universitaetsplatz 3-4
112 /* D-03044 Cottbus, Germany
113 /*--*/
114
115 /* System library. */
116
117 #include <sys_defs.h>
118 #include <string.h>
119
120 #ifdef STRCASECMP_IN_STRINGS_H
121 #include <strings.h>
122 #endif
123
124 /* Utility library. */
125
126 #include <msg.h>
127 #include <mymalloc.h>
128 #include <stringops.h>
129
130 /* Global library. */
131
132 #include <mail_params.h>
133 #include <mail_proto.h>
134 #include <mail_error.h>
135 #include <ehlo_mask.h>
136
137 /* Application-specific. */
138
139 #include "smtpd.h"
140 #include "smtpd_token.h"
141 #include "smtpd_chat.h"
142 #include "smtpd_sasl_proto.h"
143 #include "smtpd_sasl_glue.h"
144
145 #ifdef USE_SASL_AUTH
146
147 /* smtpd_sasl_auth_cmd - process AUTH command */
148
smtpd_sasl_auth_cmd(SMTPD_STATE * state,int argc,SMTPD_TOKEN * argv)149 int smtpd_sasl_auth_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
150 {
151 char *auth_mechanism;
152 char *initial_response;
153 const char *err;
154
155 if (var_helo_required && state->helo_name == 0) {
156 state->error_mask |= MAIL_ERROR_POLICY;
157 smtpd_chat_reply(state, "503 5.5.1 Error: send HELO/EHLO first");
158 return (-1);
159 }
160 if (SMTPD_STAND_ALONE(state) || !smtpd_sasl_is_active(state)
161 || (state->ehlo_discard_mask & EHLO_MASK_AUTH)) {
162 state->error_mask |= MAIL_ERROR_PROTOCOL;
163 smtpd_chat_reply(state, "503 5.5.1 Error: authentication not enabled");
164 return (-1);
165 }
166 if (SMTPD_IN_MAIL_TRANSACTION(state)) {
167 state->error_mask |= MAIL_ERROR_PROTOCOL;
168 smtpd_chat_reply(state, "503 5.5.1 Error: MAIL transaction in progress");
169 return (-1);
170 }
171 if (state->milters != 0 && (err = milter_other_event(state->milters)) != 0) {
172 if (err[0] == '5') {
173 state->error_mask |= MAIL_ERROR_POLICY;
174 smtpd_chat_reply(state, "%s", err);
175 return (-1);
176 }
177 /* Sendmail compatibility: map 4xx into 454. */
178 else if (err[0] == '4') {
179 state->error_mask |= MAIL_ERROR_POLICY;
180 smtpd_chat_reply(state, "454 4.3.0 Try again later");
181 return (-1);
182 }
183 }
184 #ifdef USE_TLS
185 if (var_smtpd_tls_auth_only && !state->tls_context) {
186 state->error_mask |= MAIL_ERROR_PROTOCOL;
187 /* RFC 4954, Section 4. */
188 smtpd_chat_reply(state, "504 5.5.4 Encryption required for requested authentication mechanism");
189 return (-1);
190 }
191 #endif
192 if (state->sasl_username) {
193 state->error_mask |= MAIL_ERROR_PROTOCOL;
194 smtpd_chat_reply(state, "503 5.5.1 Error: already authenticated");
195 return (-1);
196 }
197 if (argc < 2 || argc > 3) {
198 state->error_mask |= MAIL_ERROR_PROTOCOL;
199 smtpd_chat_reply(state, "501 5.5.4 Syntax: AUTH mechanism");
200 return (-1);
201 }
202 /* Don't reuse the SASL handle after authentication failure. */
203 #ifndef XSASL_TYPE_CYRUS
204 #define XSASL_TYPE_CYRUS "cyrus"
205 #endif
206 if (state->flags & SMTPD_FLAG_AUTH_USED) {
207 smtpd_sasl_deactivate(state);
208 #ifdef USE_TLS
209 if (state->tls_context != 0)
210 smtpd_sasl_activate(state, VAR_SMTPD_SASL_TLS_OPTS,
211 var_smtpd_sasl_tls_opts);
212 else
213 #endif
214 smtpd_sasl_activate(state, VAR_SMTPD_SASL_OPTS,
215 var_smtpd_sasl_opts);
216 } else if (strcmp(var_smtpd_sasl_type, XSASL_TYPE_CYRUS) == 0) {
217 state->flags |= SMTPD_FLAG_AUTH_USED;
218 }
219
220 /*
221 * All authentication failures shall be logged. The 5xx reply code from
222 * the SASL authentication routine triggers tar-pit delays, which help to
223 * slow down password guessing attacks.
224 */
225 auth_mechanism = argv[1].strval;
226 initial_response = (argc == 3 ? argv[2].strval : 0);
227 return (smtpd_sasl_authenticate(state, auth_mechanism, initial_response));
228 }
229
230 /* smtpd_sasl_mail_opt - SASL-specific MAIL FROM option */
231
smtpd_sasl_mail_opt(SMTPD_STATE * state,const char * addr)232 char *smtpd_sasl_mail_opt(SMTPD_STATE *state, const char *addr)
233 {
234
235 /*
236 * Do not store raw RFC2554 protocol data.
237 */
238 #if 0
239 if (state->sasl_username == 0) {
240 state->error_mask |= MAIL_ERROR_PROTOCOL;
241 return ("503 5.5.4 Error: send AUTH command first");
242 }
243 #endif
244 if (state->sasl_sender != 0) {
245 state->error_mask |= MAIL_ERROR_PROTOCOL;
246 return ("503 5.5.4 Error: multiple AUTH= options");
247 }
248 if (strcmp(addr, "<>") != 0) {
249 state->sasl_sender = mystrdup(addr);
250 printable(state->sasl_sender, '?');
251 }
252 return (0);
253 }
254
255 /* smtpd_sasl_mail_reset - SASL-specific MAIL FROM cleanup */
256
smtpd_sasl_mail_reset(SMTPD_STATE * state)257 void smtpd_sasl_mail_reset(SMTPD_STATE *state)
258 {
259 if (state->sasl_sender) {
260 myfree(state->sasl_sender);
261 state->sasl_sender = 0;
262 }
263 }
264
265 /* permit_sasl_auth - OK for authenticated connection */
266
permit_sasl_auth(SMTPD_STATE * state,int ifyes,int ifnot)267 int permit_sasl_auth(SMTPD_STATE *state, int ifyes, int ifnot)
268 {
269 if (state->sasl_method && strcasecmp(state->sasl_method, "anonymous"))
270 return (ifyes);
271 return (ifnot);
272 }
273
274 #endif
275