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