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