/*++
/* NAME
/* smtp_sasl_glue 3
/* SUMMARY
/* Postfix SASL interface for SMTP client
/* SYNOPSIS
/* #include smtp_sasl.h
/*
/* void smtp_sasl_initialize()
/*
/* void smtp_sasl_connect(session)
/* SMTP_SESSION *session;
/*
/* void smtp_sasl_start(session, sasl_opts_name, sasl_opts_val)
/* SMTP_SESSION *session;
/*
/* int smtp_sasl_passwd_lookup(session)
/* SMTP_SESSION *session;
/*
/* int smtp_sasl_authenticate(session, why)
/* SMTP_SESSION *session;
/* DSN_BUF *why;
/*
/* void smtp_sasl_cleanup(session)
/* SMTP_SESSION *session;
/*
/* void smtp_sasl_passivate(session, buf)
/* SMTP_SESSION *session;
/* VSTRING *buf;
/*
/* int smtp_sasl_activate(session, buf)
/* SMTP_SESSION *session;
/* char *buf;
/* DESCRIPTION
/* smtp_sasl_initialize() initializes the SASL library. This
/* routine must be called once at process startup, before any
/* chroot operations.
/*
/* smtp_sasl_connect() performs per-session initialization. This
/* routine must be called once at the start of each connection.
/*
/* smtp_sasl_start() performs per-session initialization. This
/* routine must be called once per session before doing any SASL
/* authentication. The sasl_opts_name and sasl_opts_val parameters are
/* the postfix configuration parameters setting the security
/* policy of the SASL authentication.
/*
/* smtp_sasl_passwd_lookup() looks up the username/password
/* for the current SMTP server. The result is zero in case
/* of failure, a long jump in case of error.
/*
/* smtp_sasl_authenticate() implements the SASL authentication
/* dialog. The result is < 0 in case of protocol failure, zero in
/* case of unsuccessful authentication, > 0 in case of success.
/* The why argument is updated with a reason for failure.
/* This routine must be called only when smtp_sasl_passwd_lookup()
/* succeeds.
/*
/* smtp_sasl_cleanup() cleans up. It must be called at the
/* end of every SMTP session that uses SASL authentication.
/* This routine is a noop for non-SASL sessions.
/*
/* smtp_sasl_passivate() appends flattened SASL attributes to the
/* specified buffer. The SASL attributes are not destroyed.
/*
/* smtp_sasl_activate() restores SASL attributes from the
/* specified buffer. The buffer is modified. A result < 0
/* means there was an error.
/*
/* Arguments:
/* .IP session
/* Session context.
/* .IP mech_list
/* String of SASL mechanisms (separated by blanks)
/* DIAGNOSTICS
/* All errors are fatal.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Original author:
/* Till Franke
/* SuSE Rhein/Main AG
/* 65760 Eschborn, Germany
/*
/* Adopted by:
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*--*/
/*
* System library .
*/
#include <sys_defs.h>
#include <stdlib.h>
#include <string.h>
/*
* Utility library
*/
#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
#include <split_at.h>
/*
* Global library
*/
#include <mail_params.h>
#include <string_list.h>
#include <maps.h>
#include <mail_addr_find.h>
#include <smtp_stream.h>
/*
* XSASL library .
*/
#include <xsasl.h>
/*
* Application - specific
*/
#include "smtp.h"
#include "smtp_sasl.h"
#include "smtp_sasl_auth_cache.h"
#ifdef USE_SASL_AUTH
/*
* Per - host login / password information .
*/
static MAPS *smtp_sasl_passwd_map;
/*
* Supported SASL mechanisms .
*/
STRING_LIST *smtp_sasl_mechs;
/*
* SASL implementation handle .
*/
static XSASL_CLIENT_IMPL *smtp_sasl_impl;
/*
* The 535 SASL authentication failure cache .
*/
#ifdef HAVE_SASL_AUTH_CACHE
static SMTP_SASL_AUTH_CACHE *smtp_sasl_auth_cache;
#endif
/* smtp_sasl_passwd_lookup - password lookup routine */
int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
{
const char *myname = "smtp_sasl_passwd_lookup" ;
SMTP_STATE *state = session->state;
SMTP_ITERATOR *iter = session->iterator;
const char *value;
char *passwd;
/*
* Sanity check .
*/
if (smtp_sasl_passwd_map == 0 )
msg_panic("%s: passwd map not initialized" , myname);
/*
* Look up the per - server password information . Try the hostname first ,
* then try the destination .
*
* XXX Instead of using nexthop ( the intended destination ) we use dest
* ( either the intended destination , or a fall - back destination ) .
*
* XXX SASL authentication currently depends on the host / domain but not on
* the TCP port . If the port is not : 25 , we should append it to the table
* lookup key . Code for this was briefly introduced into 2 . 2 snapshots ,
* but didn ' t canonicalize the TCP port , and did not append the port to
* the MX hostname .
*/
smtp_sasl_passwd_map->error = 0 ;
if ((smtp_mode
&& var_smtp_sender_auth && state->request->sender[0 ]
&& (value = mail_addr_find(smtp_sasl_passwd_map,
state->request->sender, (char **) 0 )) != 0 )
|| (smtp_sasl_passwd_map->error == 0
&& (value = maps_find(smtp_sasl_passwd_map,
STR(iter->host), 0 )) != 0 )
|| (smtp_sasl_passwd_map->error == 0
&& (value = maps_find(smtp_sasl_passwd_map,
STR(iter->dest), 0 )) != 0 )) {
if (session->sasl_username)
myfree(session->sasl_username);
session->sasl_username = mystrdup(value);
/* Historically, the delimiter may appear in the password. */
passwd = split_at(session->sasl_username,
*var_smtp_sasl_passwd_res_delim);
if (session->sasl_passwd)
myfree(session->sasl_passwd);
session->sasl_passwd = mystrdup(passwd ? passwd : "" );
if (msg_verbose)
msg_info("%s: host `%s' user `%s' pass `%s'" ,
myname, STR(iter->host),
session->sasl_username, session->sasl_passwd);
return (1 );
} else if (smtp_sasl_passwd_map->error) {
msg_warn("%s: %s lookup error" ,
state->request->queue_id, smtp_sasl_passwd_map->title);
vstream_longjmp(session->stream, SMTP_ERR_DATA);
} else {
if (msg_verbose)
msg_info("%s: no auth info found (sender=`%s', host=`%s')" ,
myname, state->request->sender, STR(iter->host));
return (0 );
}
}
/* smtp_sasl_initialize - per-process initialization (pre jail) */
void smtp_sasl_initialize(void )
{
/*
* Sanity check .
*/
if (smtp_sasl_passwd_map || smtp_sasl_impl)
msg_panic("smtp_sasl_initialize: repeated call" );
if (*var_smtp_sasl_passwd == 0 )
msg_fatal("specify a password table via the `%s' configuration parameter" ,
VAR_LMTP_SMTP(SASL_PASSWD));
/*
* Open the per - host password table and initialize the SASL library . Use
* shared locks for reading , just in case someone updates the table .
*/
smtp_sasl_passwd_map = maps_create(VAR_LMTP_SMTP(SASL_PASSWD),
var_smtp_sasl_passwd,
DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
| DICT_FLAG_UTF8_REQUEST);
if ((smtp_sasl_impl = xsasl_client_init(var_smtp_sasl_type,
var_smtp_sasl_path)) == 0 )
msg_fatal("SASL library initialization" );
/*
* Initialize optional supported mechanism matchlist
*/
if (*var_smtp_sasl_mechs)
smtp_sasl_mechs = string_list_init(VAR_SMTP_SASL_MECHS,
MATCH_FLAG_NONE,
var_smtp_sasl_mechs);
/*
* Initialize the 535 SASL authentication failure cache .
*/
if (*var_smtp_sasl_auth_cache_name) {
#ifdef HAVE_SASL_AUTH_CACHE
smtp_sasl_auth_cache =
smtp_sasl_auth_cache_init(var_smtp_sasl_auth_cache_name,
var_smtp_sasl_auth_cache_time);
#else
msg_warn("not compiled with TLS support -- "
"ignoring the %s setting" , VAR_LMTP_SMTP(SASL_AUTH_CACHE_NAME));
#endif
}
}
/* smtp_sasl_connect - per-session client initialization */
void smtp_sasl_connect(SMTP_SESSION *session)
{
/*
* This initialization happens whenever we instantiate an SMTP session
* object . We don ' t instantiate a SASL client until we actually need one .
*/
session->sasl_mechanism_list = 0 ;
session->sasl_username = 0 ;
session->sasl_passwd = 0 ;
session->sasl_client = 0 ;
session->sasl_reply = 0 ;
}
/* smtp_sasl_start - per-session SASL initialization */
void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
const char *sasl_opts_val)
{
XSASL_CLIENT_CREATE_ARGS create_args;
SMTP_ITERATOR *iter = session->iterator;
if (msg_verbose)
msg_info("starting new SASL client" );
if ((session->sasl_client =
XSASL_CLIENT_CREATE(smtp_sasl_impl, &create_args,
stream = session->stream,
service = var_procname,
server_name = STR(iter->host),
security_options = sasl_opts_val)) == 0 )
msg_fatal("SASL per-connection initialization failed" );
session->sasl_reply = vstring_alloc(20 );
}
/* smtp_sasl_authenticate - run authentication protocol */
int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
{
const char *myname = "smtp_sasl_authenticate" ;
SMTP_ITERATOR *iter = session->iterator;
SMTP_RESP *resp;
const char *mechanism;
int result;
char *line;
int steps = 0 ;
/*
* Sanity check .
*/
if (session->sasl_mechanism_list == 0 )
msg_panic("%s: no mechanism list" , myname);
if (msg_verbose)
msg_info("%s: %s: SASL mechanisms %s" ,
myname, session->namaddrport, session->sasl_mechanism_list);
/*
* Avoid repeated login failures after a recent 535 error .
*/
#ifdef HAVE_SASL_AUTH_CACHE
if (smtp_sasl_auth_cache
&& smtp_sasl_auth_cache_find(smtp_sasl_auth_cache, session)) {
char *resp_dsn = smtp_sasl_auth_cache_dsn(smtp_sasl_auth_cache);
char *resp_str = smtp_sasl_auth_cache_text(smtp_sasl_auth_cache);
if (var_smtp_sasl_auth_soft_bounce && resp_dsn[0 ] == '5' )
resp_dsn[0 ] = '4' ;
dsb_update(why, resp_dsn, DSB_DEF_ACTION, DSB_MTYPE_DNS,
STR(iter->host), var_procname, resp_str,
"SASL [CACHED] authentication failed; server %s said: %s" ,
STR(iter->host), resp_str);
return (0 );
}
#endif
/*
* Start the client side authentication protocol .
*/
result = xsasl_client_first(session->sasl_client,
session->sasl_mechanism_list,
session->sasl_username,
session->sasl_passwd,
&mechanism, session->sasl_reply);
if (result != XSASL_AUTH_OK) {
dsb_update(why, "4.7.0" , DSB_DEF_ACTION, DSB_SKIP_RMTA,
DSB_DTYPE_SASL, STR(session->sasl_reply),
"SASL authentication failed; "
"cannot authenticate to server %s: %s" ,
session->namaddr, STR(session->sasl_reply));
return (-1 );
}
/*-
* Send the AUTH command and the optional initial client response .
*
* https : //tools.ietf.org/html/rfc4954#page-4
* Note that the AUTH command is still subject to the line length
* limitations defined in [ SMTP ] . If use of the initial response argument
* would cause the AUTH command to exceed this length , the client MUST NOT
* use the initial response parameter . . .
*
* https : //tools.ietf.org/html/rfc5321#section-4.5.3.1.4
* The maximum total length of a command line including the command word
* and the < CRLF > is 512 octets .
*
* Defer the initial response if the resulting command exceeds the limit .
*/
if (LEN(session->sasl_reply) > 0
&& strlen(mechanism) + LEN(session->sasl_reply) + 8 <= 512 ) {
smtp_chat_cmd(session, "AUTH %s %s" , mechanism,
STR(session->sasl_reply));
VSTRING_RESET(session->sasl_reply); /* no deferred initial reply */
} else {
smtp_chat_cmd(session, "AUTH %s" , mechanism);
}
/*
* Step through the authentication protocol until the server tells us
* that we are done . If session - > sasl_reply is non - empty we have a
* deferred initial reply and expect an empty initial challenge from the
* server . If the server ' s initial challenge is non - empty we have a SASL
* protocol violation with both sides wanting to go first .
*/
while ((resp = smtp_chat_resp(session))->code / 100 == 3 ) {
/*
* Sanity check .
*/
if (++steps > 100 ) {
dsb_simple(why, "4.3.0" , "SASL authentication failed; "
"authentication protocol loop with server %s" ,
session->namaddr);
return (-1 );
}
/*
* Process a server challenge .
*/
line = resp->str;
(void ) mystrtok(&line, "- \t\n" ); /* skip over result code */
if (LEN(session->sasl_reply) > 0 ) {
/*
* Deferred initial response , the server challenge must be empty .
* Cleared after actual transmission to the server .
*/
if (*line) {
dsb_update(why, "4.7.0" , DSB_DEF_ACTION,
DSB_SKIP_RMTA, DSB_DTYPE_SASL, "protocol error" ,
"SASL authentication failed; non-empty initial "
"%s challenge from server %s: %s" , mechanism,
session->namaddr, STR(session->sasl_reply));
return (-1 );
}
} else {
result = xsasl_client_next(session->sasl_client, line,
session->sasl_reply);
if (result != XSASL_AUTH_OK) {
dsb_update(why, "4.7.0" , DSB_DEF_ACTION, /* Fix 200512 */
DSB_SKIP_RMTA, DSB_DTYPE_SASL, STR(session->sasl_reply),
"SASL authentication failed; "
"cannot authenticate to server %s: %s" ,
session->namaddr, STR(session->sasl_reply));
return (-1 ); /* Fix 200512 */
}
}
/*
* Send a client response .
*/
smtp_chat_cmd(session, "%s" , STR(session->sasl_reply));
VSTRING_RESET(session->sasl_reply); /* clear initial reply */
}
/*
* We completed the authentication protocol .
*/
if (resp->code / 100 != 2 ) {
#ifdef HAVE_SASL_AUTH_CACHE
/* Update the 535 authentication failure cache. */
if (smtp_sasl_auth_cache && resp->code == 535 )
smtp_sasl_auth_cache_store(smtp_sasl_auth_cache, session, resp);
#endif
if (var_smtp_sasl_auth_soft_bounce && resp->code / 100 == 5 )
STR(resp->dsn_buf)[0 ] = '4' ;
dsb_update(why, resp->dsn, DSB_DEF_ACTION,
DSB_MTYPE_DNS, STR(iter->host),
var_procname, resp->str,
"SASL authentication failed; server %s said: %s" ,
session->namaddr, resp->str);
return (0 );
}
return (1 );
}
/* smtp_sasl_cleanup - per-session cleanup */
void smtp_sasl_cleanup(SMTP_SESSION *session)
{
if (session->sasl_username) {
myfree(session->sasl_username);
session->sasl_username = 0 ;
}
if (session->sasl_passwd) {
myfree(session->sasl_passwd);
session->sasl_passwd = 0 ;
}
if (session->sasl_mechanism_list) {
/* allocated in smtp_sasl_helo_auth */
myfree(session->sasl_mechanism_list);
session->sasl_mechanism_list = 0 ;
}
if (session->sasl_client) {
if (msg_verbose)
msg_info("disposing SASL state information" );
xsasl_client_free(session->sasl_client);
session->sasl_client = 0 ;
}
if (session->sasl_reply) {
vstring_free(session->sasl_reply);
session->sasl_reply = 0 ;
}
}
/* smtp_sasl_passivate - append serialized SASL attributes */
void smtp_sasl_passivate(SMTP_SESSION *session, VSTRING *buf)
{
}
/* smtp_sasl_activate - de-serialize SASL attributes */
int smtp_sasl_activate(SMTP_SESSION *session, char *buf)
{
return (0 );
}
#endif
Messung V0.5 in Prozent C=72 H=94 G=83
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland