diff options
Diffstat (limited to 'smtp-auth.c')
-rw-r--r-- | smtp-auth.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/smtp-auth.c b/smtp-auth.c new file mode 100644 index 0000000..9398367 --- /dev/null +++ b/smtp-auth.c @@ -0,0 +1,306 @@ +/* + * This file is part of libESMTP, a library for submission of RFC 2822 + * formatted electronic mail messages using the SMTP protocol described + * in RFC 2821. + * + * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> + +#ifdef USE_SASL +/* Support for the SMTP AUTH verb. + */ +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdarg.h> +#include <unistd.h> +#include <errno.h> + +#include <missing.h> /* declarations for missing library functions */ + +#include "auth-client.h" +#include "libesmtp-private.h" +#include "api.h" + +#include "message-source.h" +#include "siobuf.h" +#include "tokens.h" +#include "base64.h" +#include "protocol.h" + +int +smtp_auth_set_context (smtp_session_t session, auth_context_t context) +{ + SMTPAPI_CHECK_ARGS (session != NULL, 0); + + session->auth_context = context; + return 1; +} + +struct mechanism + { + struct mechanism *next; + char *name; + }; + +void +destroy_auth_mechanisms (smtp_session_t session) +{ + struct mechanism *mech, *next; + + for (mech = session->auth_mechanisms; mech != NULL; mech = next) + { + next = mech->next; + if (mech->name != NULL) /* or assert (mech->name != NULL); */ + free (mech->name); + free (mech); + } + session->current_mechanism = session->auth_mechanisms = NULL; +} + +void +set_auth_mechanisms (smtp_session_t session, const char *mechanisms) +{ + char buf[64]; + struct mechanism *mech; + + while (read_atom (skipblank (mechanisms), &mechanisms, buf, sizeof buf)) + { + /* scan existing list to avoid duplicates */ + for (mech = session->auth_mechanisms; mech != NULL; mech = mech->next) + if (strcasecmp (buf, mech->name) == 0) + break; + if (mech != NULL) + continue; + + /* new mechanism, so add to the list */ + mech = malloc (sizeof (struct mechanism)); + if (mech == NULL) + { + /* FIXME: propagate ENOMEM to app. */ + continue; + } + mech->name = strdup (buf); + if (mech->name == NULL) + { + /* FIXME: propagate ENOMEM to app. */ + free (mech); + continue; + } + APPEND_LIST (session->auth_mechanisms, session->current_mechanism, mech); + } +} + +int +select_auth_mechanism (smtp_session_t session) +{ + if (session->authenticated) + return 0; + if (session->auth_context == NULL) + return 0; + if (!auth_client_enabled (session->auth_context)) + return 0; + /* find the first usable auth mechanism */ + for (session->current_mechanism = session->auth_mechanisms; + session->current_mechanism != NULL; + session->current_mechanism = session->current_mechanism->next) + if (auth_set_mechanism (session->auth_context, + session->current_mechanism->name)) + return 1; + return 0; +} + +static int +next_auth_mechanism (smtp_session_t session) +{ + /* find the next usable auth mechanism */ + while ((session->current_mechanism = session->current_mechanism->next) != NULL) + if (auth_set_mechanism (session->auth_context, + session->current_mechanism->name)) + return 1; + return 0; +} + +void +cmd_auth (siobuf_t conn, smtp_session_t session) +{ + char buf[2048]; + const char *response; + int len; + + assert (session != NULL && session->auth_context != NULL); + + sio_printf (conn, "AUTH %s", auth_mechanism_name (session->auth_context)); + + /* Ask SASL for the initial response (if there is one). */ + response = auth_response (session->auth_context, NULL, &len); + if (response != NULL) + { + /* Encode the response and send it back to the server */ + len = b64_encode (buf, sizeof buf, response, len); + if (len == 0) + sio_write (conn, " =", 2); + else if (len > 0) + { + sio_write (conn, " ", 1); + sio_write (conn, buf, len); + } + } + + sio_write (conn, "\r\n", 2); + session->cmd_state = -1; +} + +void +rsp_auth (siobuf_t conn, smtp_session_t session) +{ + int code; + + code = read_smtp_response (conn, session, &session->mta_status, NULL); + if (code < 0) + { + session->rsp_state = S_quit; + return; + } + if (code == 4 || code == 5) + { + /* If auth mechanism is too weak or encryption is required, give up. + Otherwise try the next mechanism. */ + if (session->mta_status.code == 534 || session->mta_status.code == 538) + session->rsp_state = S_quit; + else + { + /* If another mechanism cannot be selected, move on to the + mail command since the MTA is required to accept mail for + its own domain. */ + if (next_auth_mechanism (session)) + session->rsp_state = S_auth; +#ifdef USE_ETRN + else if (check_etrn (session)) + session->rsp_state = S_etrn; +#endif + else + session->rsp_state = initial_transaction_state (session); + } + } + else if (code == 2) + { + session->authenticated = 1; + if (auth_get_ssf (session->auth_context) != 0) + { + /* Add SASL mechanism's encoder/decoder to siobuf. This is + done now, since subsequent commands and responses will be + encoded and remain so until the connection to the server + is closed. Auth_encode/decode() call through to the + mechanism's coders. */ + sio_set_securitycb (conn, auth_encode, auth_decode, + session->auth_context); + session->auth_context = NULL; /* Don't permit AUTH */ + session->extensions = 0; /* Turn off extensions */ + session->rsp_state = S_ehlo; /* Restart protocol */ + } +#ifdef USE_ETRN + else if (check_etrn (session)) + session->rsp_state = S_etrn; +#endif + else + session->rsp_state = initial_transaction_state (session); + } + else if (code == 3) + session->rsp_state = S_auth2; + else + { + set_error (SMTP_ERR_INVALID_RESPONSE_STATUS); + session->rsp_state = S_quit; + } +} + +void +cmd_auth2 (siobuf_t conn, smtp_session_t session) +{ + char buf[2048]; + const char *response; + int len; + + /* Decode the text from the server to get the challenge. */ + len = b64_decode (buf, sizeof buf, session->mta_status.text, -1); + if (len >= 0) + { + /* Send it through SASL and get the response. */ + response = auth_response (session->auth_context, buf, &len); + + /* Encode the response and send it back to the server */ + len = (response != NULL) ? b64_encode (buf, sizeof buf, response, len) + : -1; + } + + /* Abort the AUTH command if base 64 encode/decode fails. */ + if (len < 0) + sio_write (conn, "*\r\n", 3); + else + { + if (len > 0) + sio_write (conn, buf, len); + sio_write (conn, "\r\n", 2); + } + session->cmd_state = -1; +} + +void +rsp_auth2 (siobuf_t conn, smtp_session_t session) +{ + rsp_auth (conn, session); +} + +#else + +/* Define stubs for some of the SMTP AUTH support. */ +#include <stdlib.h> +#include "auth-client.h" +#include "libesmtp-private.h" + +int +smtp_auth_set_context (smtp_session_t session, auth_context_t context) +{ + SMTPAPI_CHECK_ARGS (session != NULL, 0); + + return 0; +} + +void +set_auth_mechanisms (smtp_session_t session, const char *mechanisms) +{ +} + +int +select_auth_mechanism (smtp_session_t session) +{ + return 0; +} + +void +destroy_auth_mechanisms (smtp_session_t session) +{ +} + +#endif |