From bdb8a0a993163256b4e3c6d10ea9e03140c474b4 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Wed, 6 Mar 2002 00:33:37 +0000 Subject: Start the ssl stream off in non-ssl mode (useful for STARTTLS). 2002-03-05 Jeffrey Stedfast * camel-tcp-stream-openssl.c (camel_tcp_stream_openssl_new_raw): Start the ssl stream off in non-ssl mode (useful for STARTTLS). (camel_tcp_stream_openssl_enable_ssl): New function to toggle an ssl stream into ssl mode. (open_ssl_connection): Close the sockfd on fail so our caller doesn't have to - this also allows us to save the original errno. (stream_connect): If we want ssl mode, do our ssl stuff. (camel_tcp_stream_openssl_class_init): Init some SSL stuff here instead of in open_ssl_connection since these only ever need to be called once. (stream_read): Only use SSL_read if we are in ssl mode. (stream_write): Only use SSL_write if we are in ssl mode. * providers/smtp/camel-smtp-transport.c (smtp_helo): Check for the STARTTLS extension. (connect_to_server): Try to use STARTTLS whenever possible rather than the old way of doing things. (connect_to_server_wrapper): Wrapper around connect_to_server() to first try STARTTLS and then attempt normal SSL mode if we can't connect via STARTTLS. * camel-tcp-stream-ssl.c (camel_tcp_stream_ssl_enable_ssl): New function to toggle an ssl stream into ssl mode. (camel_tcp_stream_ssl_new_raw): Start the ssl stream off in non-ssl mode (useful for STARTTLS). (stream_connect): Only connect in SSL mode if required. svn path=/trunk/; revision=15937 --- camel/providers/smtp/camel-smtp-provider.c | 36 +++---- camel/providers/smtp/camel-smtp-transport.c | 141 +++++++++++++++++++++++++--- camel/providers/smtp/camel-smtp-transport.h | 13 ++- 3 files changed, 157 insertions(+), 33 deletions(-) (limited to 'camel/providers') diff --git a/camel/providers/smtp/camel-smtp-provider.c b/camel/providers/smtp/camel-smtp-provider.c index 13f0cb6369..48e9bcc6d1 100644 --- a/camel/providers/smtp/camel-smtp-provider.c +++ b/camel/providers/smtp/camel-smtp-provider.c @@ -1,27 +1,27 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* camel-smtp-provider.c: smtp provider registration code */ - -/* - * Authors : - * Jeffrey Stedfast +/* + * Authors: Jeffrey Stedfast + * + * Copyright 2002 Ximian, Inc. (www.ximian.com) * - * Copyright (C) 2000 Ximian, Inc. (www.ximian.com) + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. + * This program 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 General Public License for more details. * - * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA */ + #ifdef HAVE_CONFIG_H #include #endif @@ -53,8 +53,8 @@ camel_provider_module_init (CamelSession *session) { smtp_provider.object_types[CAMEL_PROVIDER_TRANSPORT] = camel_smtp_transport_get_type (); - smtp_provider.authtypes = g_list_append(camel_sasl_authtype_list(TRUE), camel_sasl_authtype ("LOGIN")); - smtp_provider.authtypes = g_list_append(smtp_provider.authtypes, camel_sasl_authtype ("POPB4SMTP")); + smtp_provider.authtypes = g_list_append (camel_sasl_authtype_list (TRUE), camel_sasl_authtype ("LOGIN")); + smtp_provider.authtypes = g_list_append (smtp_provider.authtypes, camel_sasl_authtype ("POPB4SMTP")); smtp_provider.service_cache = g_hash_table_new (camel_url_hash, camel_url_equal); smtp_provider.url_hash = camel_url_hash; smtp_provider.url_equal = camel_url_equal; diff --git a/camel/providers/smtp/camel-smtp-transport.c b/camel/providers/smtp/camel-smtp-transport.c index b06b3cb329..c5bf11971e 100644 --- a/camel/providers/smtp/camel-smtp-transport.c +++ b/camel/providers/smtp/camel-smtp-transport.c @@ -92,6 +92,9 @@ static gboolean smtp_data (CamelSmtpTransport *transport, CamelMedium *message, static gboolean smtp_rset (CamelSmtpTransport *transport, CamelException *ex); static gboolean smtp_quit (CamelSmtpTransport *transport, CamelException *ex); +static void smtp_set_exception (CamelSmtpTransport *transport, const char *respbuf, + const char *message, CamelException *ex); + /* private data members */ static CamelTransportClass *parent_class = NULL; @@ -151,11 +154,16 @@ smtp_construct (CamelService *service, CamelSession *session, CamelException *ex) { CamelSmtpTransport *smtp_transport = CAMEL_SMTP_TRANSPORT (service); + const char *use_ssl; CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex); - if (camel_url_get_param (url, "use_ssl")) - smtp_transport->flags |= CAMEL_SMTP_TRANSPORT_USE_SSL; + if ((use_ssl = camel_url_get_param (url, "use_ssl"))) { + if (!strcmp (use_ssl, "always")) + smtp_transport->flags |= CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS; + else if (!strcmp (use_ssl, "when-possible")) + smtp_transport->flags |= CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE; + } } static const char * @@ -228,11 +236,11 @@ smtp_error_string (int error) } static gboolean -connect_to_server (CamelService *service, CamelException *ex) +connect_to_server (CamelService *service, int try_starttls, CamelException *ex) { CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service); CamelStream *tcp_stream; - gchar *respbuf = NULL; + char *respbuf = NULL; struct hostent *h; guint32 addrlen; int port, ret; @@ -247,20 +255,28 @@ connect_to_server (CamelService *service, CamelException *ex) /* set some smtp transport defaults */ transport->flags &= ~(CAMEL_SMTP_TRANSPORT_IS_ESMTP | CAMEL_SMTP_TRANSPORT_8BITMIME | + CAMEL_SMTP_TRANSPORT_STARTTLS | CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES); transport->authtypes = NULL; port = service->url->port ? service->url->port : SMTP_PORT; -#if defined(HAVE_NSS) || defined(HAVE_OPENSSL) +#if defined (HAVE_NSS) || defined (HAVE_OPENSSL) if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL) { - port = service->url->port ? service->url->port : 465; + if (!try_starttls) + port = service->url->port ? service->url->port : 465; #ifdef HAVE_NSS /* use the preferred implementation - NSS */ - tcp_stream = camel_tcp_stream_ssl_new (service, service->url->host); + if (try_starttls) + tcp_stream = camel_tcp_stream_ssl_new_raw (service, service->url->host); + else + tcp_stream = camel_tcp_stream_ssl_new (service, service->url->host); #else - tcp_stream = camel_tcp_stream_openssl_new (service, service->url->host); + if (try_starttls) + tcp_stream = camel_tcp_stream_openssl_new_raw (service, service->url->host); + else + tcp_stream = camel_tcp_stream_openssl_new (service, service->url->host); #endif /* HAVE_NSS */ } else { tcp_stream = camel_tcp_stream_raw_new (); @@ -340,7 +356,107 @@ connect_to_server (CamelService *service, CamelException *ex) smtp_helo (transport, ex); } +#if defined (HAVE_NSS) || defined (HAVE_OPENSSL) + if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE) { + /* try_starttls is always TRUE here */ + if (transport->flags & CAMEL_SMTP_TRANSPORT_STARTTLS) + goto starttls; + } else if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS) { + if (try_starttls) { + if (transport->flags & CAMEL_SMTP_TRANSPORT_STARTTLS) { + goto starttls; + } else { + /* server doesn't support STARTTLS, abort */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to connect to SMTP server %s in secure mode: %s"), + service->url->host, _("server does not appear to support SSL")); + goto exception_cleanup; + } + } + } +#endif /* HAVE_NSS || HAVE_OPENSSL */ + return TRUE; + +#if defined (HAVE_NSS) || defined (HAVE_OPENSSL) + starttls: + d(fprintf (stderr, "sending : STARTTLS\r\n")); + if (camel_stream_write (tcp_stream, "STARTTLS\r\n", 10) == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("STARTTLS request timed out: %s"), + g_strerror (errno)); + goto exception_cleanup; + } + + respbuf = NULL; + + do { + /* Check for "220 Ready for TLS" */ + g_free (respbuf); + respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream)); + + d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)")); + + if (!respbuf || strncmp (respbuf, "220", 3)) { + smtp_set_exception (transport, respbuf, _("STARTTLS response error"), ex); + g_free (respbuf); + goto exception_cleanup; + } + } while (*(respbuf+3) == '-'); /* if we got "220-" then loop again */ + + /* Okay, now toggle SSL/TLS mode */ +#ifdef HAVE_NSS + ret = camel_tcp_stream_ssl_enable_ssl (CAMEL_TCP_STREAM_SSL (tcp_stream)); +#else /* HAVE_OPENSSL */ + ret = camel_tcp_stream_openssl_enable_ssl (CAMEL_TCP_STREAM_OPENSSL (tcp_stream)); +#endif + if (ret != -1) + return TRUE; + + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to connect to SMTP server %s in secure mode: %s"), + service->url->host, g_strerror (errno)); + + exception_cleanup: + camel_object_unref (CAMEL_OBJECT (transport->istream)); + transport->istream = NULL; + camel_object_unref (CAMEL_OBJECT (transport->ostream)); + transport->ostream = NULL; + + return FALSE; +#endif /* HAVE_NSS || HAVE_OPENSSL */ +} + +#define EXCEPTION_RETRY(ex) (camel_exception_get_id (ex) != CAMEL_EXCEPTION_USER_CANCEL && \ + camel_exception_get_id (ex) != CAMEL_EXCEPTION_SERVICE_UNAVAILABLE) + +static gboolean +connect_to_server_wrapper (CamelService *service, CamelException *ex) +{ +#if defined (HAVE_NSS) || defined (HAVE_OPENSSL) + CamelSmtpTransport *transport = (CamelSmtpTransport *) service; + + if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS) { + /* First try STARTTLS */ + if (!connect_to_server (service, TRUE, ex) && + !transport->flags & CAMEL_SMTP_TRANSPORT_STARTTLS && + EXCEPTION_RETRY (ex)) { + /* STARTTLS is unavailable - okay, now try port 465 */ + camel_exception_clear (ex); + return connect_to_server (service, FALSE, ex); + } + + return TRUE; + } else if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE) { + /* If the server supports STARTTLS, use it */ + return connect_to_server (service, TRUE, ex); + } else { + /* User doesn't care about SSL */ + return connect_to_server (service, FALSE, ex); + } +#else + return connect_to_server (service, FALSE, ex); +#endif } static gboolean @@ -364,10 +480,10 @@ smtp_connect (CamelService *service, CamelException *ex) if (!truth) return FALSE; - return connect_to_server (service, ex); + return connect_to_server_wrapper (service, ex); } - if (!connect_to_server (service, ex)) + if (!connect_to_server_wrapper (service, ex)) return FALSE; /* check to see if AUTH is required, if so...then AUTH ourselves */ @@ -539,7 +655,7 @@ query_auth_types (CamelService *service, CamelException *ex) CamelServiceAuthType *authtype; GList *types, *t, *next; - if (!connect_to_server (service, ex)) + if (!connect_to_server_wrapper (service, ex)) return NULL; types = g_list_copy (service->provider->authtypes); @@ -852,6 +968,9 @@ smtp_helo (CamelSmtpTransport *transport, CamelException *ex) } else if (!strncmp (token, "ENHANCEDSTATUSCODES", 19)) { d(fprintf (stderr, "This server supports enhanced status codes\n")); transport->flags |= CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES; + } else if (!strncmp (token, "STARTTLS", 8)) { + d(fprintf (stderr, "This server supports STARTTLS\n")); + transport->flags |= CAMEL_SMTP_TRANSPORT_STARTTLS; } else if (!transport->authtypes && !strncmp (token, "AUTH", 4)) { /* Don't bother parsing any authtypes if we already have a list. * Some servers will list AUTH twice, once the standard way and diff --git a/camel/providers/smtp/camel-smtp-transport.h b/camel/providers/smtp/camel-smtp-transport.h index 8bba00e987..2f6ac261f4 100644 --- a/camel/providers/smtp/camel-smtp-transport.h +++ b/camel/providers/smtp/camel-smtp-transport.h @@ -47,11 +47,16 @@ extern "C" { #define CAMEL_IS_SMTP_TRANSPORT(o) (CAMEL_CHECK_TYPE((o), CAMEL_SMTP_TRANSPORT_TYPE)) -#define CAMEL_SMTP_TRANSPORT_IS_ESMTP (1 << 0) -#define CAMEL_SMTP_TRANSPORT_8BITMIME (1 << 1) -#define CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES (1 << 2) +#define CAMEL_SMTP_TRANSPORT_IS_ESMTP (1 << 0) +#define CAMEL_SMTP_TRANSPORT_8BITMIME (1 << 1) +#define CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES (1 << 2) +#define CAMEL_SMTP_TRANSPORT_STARTTLS (1 << 3) -#define CAMEL_SMTP_TRANSPORT_USE_SSL (1 << 3) +#define CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS (1 << 4) +#define CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE (1 << 5) + +#define CAMEL_SMTP_TRANSPORT_USE_SSL (CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS | \ + CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE) typedef struct { CamelTransport parent_object; -- cgit v1.2.3