diff options
Diffstat (limited to 'src/smtpd/smtpd_sasl_glue.c')
-rw-r--r-- | src/smtpd/smtpd_sasl_glue.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/src/smtpd/smtpd_sasl_glue.c b/src/smtpd/smtpd_sasl_glue.c new file mode 100644 index 0000000..2dc6aad --- /dev/null +++ b/src/smtpd/smtpd_sasl_glue.c @@ -0,0 +1,396 @@ +/*++ +/* NAME +/* smtpd_sasl_glue 3 +/* SUMMARY +/* Postfix SMTP server, SASL support interface +/* SYNOPSIS +/* #include "smtpd_sasl_glue.h" +/* +/* void smtpd_sasl_state_init(state) +/* SMTPD_STATE *state; +/* +/* void smtpd_sasl_initialize() +/* +/* void smtpd_sasl_activate(state, sasl_opts_name, sasl_opts_val) +/* SMTPD_STATE *state; +/* const char *sasl_opts_name; +/* const char *sasl_opts_val; +/* +/* char *smtpd_sasl_authenticate(state, sasl_method, init_response) +/* SMTPD_STATE *state; +/* const char *sasl_method; +/* const char *init_response; +/* +/* void smtpd_sasl_logout(state) +/* SMTPD_STATE *state; +/* +/* void smtpd_sasl_login(state, sasl_username, sasl_method) +/* SMTPD_STATE *state; +/* const char *sasl_username; +/* const char *sasl_method; +/* +/* void smtpd_sasl_deactivate(state) +/* SMTPD_STATE *state; +/* +/* int smtpd_sasl_is_active(state) +/* SMTPD_STATE *state; +/* +/* int smtpd_sasl_set_inactive(state) +/* SMTPD_STATE *state; +/* DESCRIPTION +/* This module encapsulates most of the detail specific to SASL +/* authentication. +/* +/* smtpd_sasl_state_init() performs minimal server state +/* initialization to support external authentication (e.g., +/* XCLIENT) without having to enable SASL in main.cf. This +/* should always be called at process startup. +/* +/* smtpd_sasl_initialize() initializes the SASL library. This +/* routine should be called once at process start-up. It may +/* need access to the file system for run-time loading of +/* plug-in modules. There is no corresponding cleanup routine. +/* +/* smtpd_sasl_activate() performs per-connection initialization. +/* This routine should be called once at the start of every +/* connection. The sasl_opts_name and sasl_opts_val parameters +/* are the postfix configuration parameters setting the security +/* policy of the SASL authentication. +/* +/* smtpd_sasl_authenticate() implements the authentication +/* dialog. The result is zero in case of success, -1 in case +/* of failure. smtpd_sasl_authenticate() updates the following +/* state structure members: +/* .IP sasl_method +/* The authentication method that was successfully applied. +/* This member is a null pointer in the absence of successful +/* authentication. +/* .IP sasl_username +/* The username that was successfully authenticated. +/* This member is a null pointer in the absence of successful +/* authentication. +/* .PP +/* smtpd_sasl_login() records the result of successful external +/* authentication, i.e. without invoking smtpd_sasl_authenticate(), +/* but produces an otherwise equivalent result. +/* +/* smtpd_sasl_logout() cleans up after smtpd_sasl_authenticate(). +/* This routine exists for the sake of symmetry. +/* +/* smtpd_sasl_deactivate() performs per-connection cleanup. +/* This routine should be called at the end of every connection. +/* +/* smtpd_sasl_is_active() is a predicate that returns true +/* if the SMTP server session state is between smtpd_sasl_activate() +/* and smtpd_sasl_deactivate(). +/* +/* smtpd_sasl_set_inactive() initializes the SMTP session +/* state before the first smtpd_sasl_activate() call. +/* +/* Arguments: +/* .IP state +/* SMTP session context. +/* .IP sasl_opts_name +/* Security options parameter name. +/* .IP sasl_opts_val +/* Security options parameter value. +/* .IP sasl_method +/* A SASL mechanism name +/* .IP init_reply +/* An optional initial client response. +/* DIAGNOSTICS +/* All errors are fatal. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Initial implementation by: +/* 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> + +/* Global library. */ + +#include <mail_params.h> +#include <sasl_mech_filter.h> +#include <string_list.h> + +/* XSASL library. */ + +#include <xsasl.h> + +/* Application-specific. */ + +#include "smtpd.h" +#include "smtpd_sasl_glue.h" +#include "smtpd_chat.h" + +#ifdef USE_SASL_AUTH + + /* + * SASL mechanism filter. + */ +static STRING_LIST *smtpd_sasl_mech_filter; + +/* + * Silly little macros. + */ +#define STR(s) vstring_str(s) + + /* + * SASL server implementation handle. + */ +static XSASL_SERVER_IMPL *smtpd_sasl_impl; + +/* smtpd_sasl_initialize - per-process initialization */ + +void smtpd_sasl_initialize(void) +{ + + /* + * Sanity check. + */ + if (smtpd_sasl_impl) + msg_panic("smtpd_sasl_initialize: repeated call"); + + /* + * Initialize the SASL library. + */ + if ((smtpd_sasl_impl = xsasl_server_init(var_smtpd_sasl_type, + var_smtpd_sasl_path)) == 0) + msg_fatal("SASL per-process initialization failed"); + + /* + * Initialize the SASL mechanism filter. + */ + smtpd_sasl_mech_filter = string_list_init(VAR_SMTPD_SASL_MECH_FILTER, + MATCH_FLAG_NONE, + var_smtpd_sasl_mech_filter); +} + +/* smtpd_sasl_activate - per-connection initialization */ + +void smtpd_sasl_activate(SMTPD_STATE *state, const char *sasl_opts_name, + const char *sasl_opts_val) +{ + const char *mechanism_list; + const char *filtered_mechanism_list; + XSASL_SERVER_CREATE_ARGS create_args; + int tls_flag; + + /* + * Sanity check. + */ + if (smtpd_sasl_is_active(state)) + msg_panic("smtpd_sasl_activate: already active"); + + /* + * Initialize SASL-specific state variables. Use long-lived storage for + * base 64 conversion results, rather than local variables, to avoid + * memory leaks when a read or write routine returns abnormally after + * timeout or I/O error. + */ + state->sasl_reply = vstring_alloc(20); + state->sasl_mechanism_list = 0; + + /* + * Set up a new server context for this connection. + */ +#ifdef USE_TLS + tls_flag = state->tls_context != 0; +#else + tls_flag = 0; +#endif +#define ADDR_OR_EMPTY(addr, unknown) (strcmp(addr, unknown) ? addr : "") +#define REALM_OR_NULL(realm) (*(realm) ? (realm) : (char *) 0) + + if ((state->sasl_server = + XSASL_SERVER_CREATE(smtpd_sasl_impl, &create_args, + stream = state->client, + addr_family = state->addr_family, + server_addr = ADDR_OR_EMPTY(state->dest_addr, + SERVER_ADDR_UNKNOWN), + server_port = ADDR_OR_EMPTY(state->dest_port, + SERVER_PORT_UNKNOWN), + client_addr = ADDR_OR_EMPTY(state->addr, + CLIENT_ADDR_UNKNOWN), + client_port = ADDR_OR_EMPTY(state->port, + CLIENT_PORT_UNKNOWN), + service = var_smtpd_sasl_service, + user_realm = REALM_OR_NULL(var_smtpd_sasl_realm), + security_options = sasl_opts_val, + tls_flag = tls_flag)) == 0) + msg_fatal("SASL per-connection initialization failed"); + + /* + * Get the list of authentication mechanisms. + */ + if ((mechanism_list = + xsasl_server_get_mechanism_list(state->sasl_server)) == 0) + msg_fatal("no SASL authentication mechanisms"); + filtered_mechanism_list = + sasl_mech_filter(smtpd_sasl_mech_filter, mechanism_list); + if (*filtered_mechanism_list == 0) + msg_fatal("%s discards all mechanisms in '%s'", + VAR_SMTPD_SASL_MECH_FILTER, mechanism_list); + state->sasl_mechanism_list = mystrdup(filtered_mechanism_list); +} + +/* smtpd_sasl_state_init - initialize state to allow extern authentication. */ + +void smtpd_sasl_state_init(SMTPD_STATE *state) +{ + /* Initialization to support external authentication (e.g., XCLIENT). */ + state->sasl_username = 0; + state->sasl_method = 0; + state->sasl_sender = 0; +} + +/* smtpd_sasl_deactivate - per-connection cleanup */ + +void smtpd_sasl_deactivate(SMTPD_STATE *state) +{ + if (state->sasl_reply) { + vstring_free(state->sasl_reply); + state->sasl_reply = 0; + } + if (state->sasl_mechanism_list) { + myfree(state->sasl_mechanism_list); + state->sasl_mechanism_list = 0; + } + if (state->sasl_username) { + myfree(state->sasl_username); + state->sasl_username = 0; + } + if (state->sasl_method) { + myfree(state->sasl_method); + state->sasl_method = 0; + } + if (state->sasl_sender) { + myfree(state->sasl_sender); + state->sasl_sender = 0; + } + if (state->sasl_server) { + xsasl_server_free(state->sasl_server); + state->sasl_server = 0; + } +} + +/* smtpd_sasl_authenticate - per-session authentication */ + +int smtpd_sasl_authenticate(SMTPD_STATE *state, + const char *sasl_method, + const char *init_response) +{ + int status; + const char *sasl_username; + + /* + * SASL authentication protocol start-up. Process any initial client + * response that was sent along in the AUTH command. + */ + for (status = xsasl_server_first(state->sasl_server, sasl_method, + init_response, state->sasl_reply); + status == XSASL_AUTH_MORE; + status = xsasl_server_next(state->sasl_server, STR(state->buffer), + state->sasl_reply)) { + + /* + * Send a server challenge. + */ + smtpd_chat_reply(state, "334 %s", STR(state->sasl_reply)); + + /* + * Receive the client response. "*" means that the client gives up. + */ + if (!smtpd_chat_query_limit(state, var_smtpd_sasl_resp_limit)) { + smtpd_chat_reply(state, "500 5.5.6 SASL response limit exceeded"); + return (-1); + } + if (strcmp(STR(state->buffer), "*") == 0) { + msg_warn("%s: SASL %s authentication aborted", + state->namaddr, sasl_method); + smtpd_chat_reply(state, "501 5.7.0 Authentication aborted"); + return (-1); + } + } + if (status != XSASL_AUTH_DONE) { + sasl_username = xsasl_server_get_username(state->sasl_server); + msg_warn("%s: SASL %.100s authentication failed: %s, sasl_username=%.100s", + state->namaddr, sasl_method, *STR(state->sasl_reply) ? + STR(state->sasl_reply) : "(reason unavailable)", + sasl_username ? sasl_username : "(unavailable)"); + /* RFC 4954 Section 6. */ + if (status == XSASL_AUTH_TEMP) + smtpd_chat_reply(state, "454 4.7.0 Temporary authentication failure: %s", + STR(state->sasl_reply)); + else + smtpd_chat_reply(state, "535 5.7.8 Error: authentication failed: %s", + STR(state->sasl_reply)); + return (-1); + } + /* RFC 4954 Section 6. */ + smtpd_chat_reply(state, "235 2.7.0 Authentication successful"); + if ((sasl_username = xsasl_server_get_username(state->sasl_server)) == 0) + msg_panic("cannot look up the authenticated SASL username"); + state->sasl_username = mystrdup(sasl_username); + printable(state->sasl_username, '?'); + state->sasl_method = mystrdup(sasl_method); + printable(state->sasl_method, '?'); + + return (0); +} + +/* smtpd_sasl_logout - clean up after smtpd_sasl_authenticate */ + +void smtpd_sasl_logout(SMTPD_STATE *state) +{ + if (state->sasl_username) { + myfree(state->sasl_username); + state->sasl_username = 0; + } + if (state->sasl_method) { + myfree(state->sasl_method); + state->sasl_method = 0; + } +} + +/* smtpd_sasl_login - set login information */ + +void smtpd_sasl_login(SMTPD_STATE *state, const char *sasl_username, + const char *sasl_method) +{ + if (state->sasl_username) + myfree(state->sasl_username); + state->sasl_username = mystrdup(sasl_username); + if (state->sasl_method) + myfree(state->sasl_method); + state->sasl_method = mystrdup(sasl_method); +} + +#endif |