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