Home | History | Annotate | Line # | Download | only in smtpd
      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 
    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 
    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 
    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 
    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