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