SASL authentication.

This commit is contained in:
Vincent Richard 2005-09-17 09:08:45 +00:00
parent e51fb0c931
commit 0edaa87860
55 changed files with 3581 additions and 821 deletions

View File

@ -2,6 +2,11 @@
VERSION 0.7.2cvs
================
2005-09-17 Vincent Richard <vincent@vincent-richard.net>
* Added SASL support, based on GNU SASL library. Slightly modified
auhenticator object; see 'example6' which has been updated.
2005-09-06 Vincent Richard <vincent@vincent-richard.net>
* Created 'vmime::security' and 'vmime::security::digest' namespaces.

View File

@ -170,12 +170,26 @@ libvmime_sources = [
# =============================== Misc ===============================
'misc/importanceHelper.cpp', 'misc/importanceHelper.hpp',
# ============================= Security =============================
'security/authenticator.hpp',
'security/defaultAuthenticator.cpp', 'security/defaultAuthenticator.hpp',
# -- digest
'security/digest/messageDigest.cpp', 'security/digest/messageDigest.hpp',
'security/digest/messageDigestFactory.cpp', 'security/digest/messageDigestFactory.hpp',
'security/digest/md5/md5MessageDigest.cpp', 'security/digest/md5/md5MessageDigest.hpp',
'security/digest/sha1/sha1MessageDigest.cpp', 'security/digest/sha1/sha1MessageDigest.hpp'
]
libvmime_security_sasl_sources = [
'security/sasl/SASLContext.cpp', 'security/sasl/SASLContext.hpp',
'security/sasl/SASLSession.cpp', 'security/sasl/SASLSession.hpp',
'security/sasl/SASLMechanism.hpp',
'security/sasl/SASLMechanismFactory.cpp', 'security/sasl/SASLMechanismFactory.hpp',
'security/sasl/SASLSocket.cpp', 'security/sasl/SASLSocket.hpp',
'security/sasl/SASLAuthenticator.hpp',
'security/sasl/defaultSASLAuthenticator.cpp', 'security/sasl/defaultSASLAuthenticator.hpp',
'security/sasl/builtinSASLMechanism.cpp', 'security/sasl/builtinSASLMechanism.hpp'
]
libvmime_examples_sources = [
'examples/README',
# 'examples/Makefile.am', # not generated
@ -190,11 +204,8 @@ libvmime_examples_sources = [
]
libvmime_messaging_sources = [
'net/authenticator.cpp', 'net/authenticator.hpp',
'net/authenticationInfos.cpp', 'net/authenticationInfos.hpp',
'net/authHelper.cpp', 'net/authHelper.hpp',
'net/builtinServices.inl',
'net/defaultAuthenticator.cpp', 'net/defaultAuthenticator.hpp',
'net/events.cpp', 'net/events.hpp',
'net/folder.cpp', 'net/folder.hpp',
'net/message.cpp', 'net/message.hpp',
@ -202,7 +213,6 @@ libvmime_messaging_sources = [
'net/serviceFactory.cpp', 'net/serviceFactory.hpp',
'net/serviceInfos.cpp', 'net/serviceInfos.hpp',
'net/session.cpp', 'net/session.hpp',
'net/simpleAuthenticator.cpp', 'net/simpleAuthenticator.hpp',
'net/socket.hpp',
'net/store.hpp',
'net/timeoutHandler.hpp',
@ -357,7 +367,7 @@ libvmime_autotools = [
'vmime/Makefile.in'
]
libvmime_all_sources = [] + libvmime_sources + libvmime_messaging_sources
libvmime_all_sources = [] + libvmime_sources + libvmime_messaging_sources + libvmime_security_sasl_sources
for i in range(len(libvmime_all_sources)):
f = libvmime_all_sources[i]
@ -463,6 +473,14 @@ opts.AddOptions(
+ 'Currently available platform handlers: posix.',
'"posix"'
),
EnumOption(
'with_sasl',
'Enable SASL support (requires GNU SASL library)',
'yes',
allowed_values = ('yes', 'no'),
map = { },
ignorecase = 1
),
(
'sendmail_path',
'Specifies the path to sendmail.',
@ -554,6 +572,8 @@ else:
#env.Append(LIBS = ['additional-lib-here'])
env.ParseConfig('pkg-config --cflags --libs libgsasl')
# Generate help text for command line options
Help(opts.GenerateHelpText(env))
@ -642,6 +662,7 @@ if env['with_messaging'] == 'yes':
print " * protocols : " + env['with_messaging_protocols']
print "File-system support : " + env['with_filesystem']
print "Platform handlers : " + env['with_platforms']
print "SASL support : " + env['with_sasl']
if IsProtocolSupported(messaging_protocols, 'sendmail'):
print "Sendmail path : " + env['sendmail_path']
@ -727,6 +748,12 @@ if env['with_filesystem'] == 'yes':
else:
config_hpp.write('#define VMIME_HAVE_FILESYSTEM_FEATURES 0\n')
config_hpp.write('// -- SASL support\n')
if env['with_sasl'] == 'yes':
config_hpp.write('#define VMIME_HAVE_SASL_SUPPORT 1\n')
else:
config_hpp.write('#define VMIME_HAVE_SASL_SUPPORT 0\n')
config_hpp.write('// -- Messaging support\n')
if env['with_messaging'] == 'yes':
config_hpp.write('#define VMIME_HAVE_MESSAGING_FEATURES 1\n')
@ -800,6 +827,11 @@ if env['with_messaging'] == 'yes':
for file in protosrc[1]:
libvmime_sel_sources.append(file)
# -- SASL support
if env['with_sasl'] == 'yes':
for file in libvmime_security_sasl_sources:
libvmime_sel_sources.append(file)
# -- platform handlers
for platform in platforms:
files = libvmime_platforms_sources[platform]
@ -852,12 +884,13 @@ Default(libVmime)
# Tests
if env['build_tests'] == 'yes':
if env['debug'] == 'yes':
env = env.Copy()
env.Append(LIBS = ['cppunit', 'dl', packageVersionedGenericName + '-debug'])
env.Append(LIBPATH=['.'])
Default(
env.Program(
target = 'run-tests',
source = libvmimetest_sources,
LIBS=['cppunit', 'dl', packageVersionedGenericName + '-debug'],
LIBPATH=['.']
source = libvmimetest_sources
)
)
else:
@ -888,6 +921,13 @@ env.Install(includeDir, 'vmime/config.hpp')
# Pkg-config support
vmime_pc = open(packageVersionedGenericName + ".pc", 'w')
vmime_pc_requires = ''
vmime_pc_libs = ''
if env['with_sasl'] == 'yes':
vmime_pc_requires = vmime_pc_requires + "libgsasl "
vmime_pc_libs = vmime_pc_libs + "-lgsasl "
vmime_pc.write("prefix=" + env['prefix'] + "\n")
vmime_pc.write("exec_prefix=" + env['prefix'] + "\n")
vmime_pc.write("libdir=" + env['prefix'] + "/lib\n")
@ -896,8 +936,8 @@ vmime_pc.write("\n")
vmime_pc.write("Name: " + packageRealName + "\n")
vmime_pc.write("Description: " + packageDescription + "\n")
vmime_pc.write("Version: " + packageVersion + "\n")
vmime_pc.write("Requires:\n")
vmime_pc.write("Libs: -L${libdir} -l" + packageVersionedGenericName + "\n")
vmime_pc.write("Requires: " + vmime_pc_requires + "\n")
vmime_pc.write("Libs: -L${libdir} -l" + packageVersionedGenericName + " " + vmime_pc_libs + "\n")
#vmime_pc.write("Cflags: -I${includedir}/" + packageVersionedGenericName + "\n")
vmime_pc.write("Cflags: -I${includedir}/" + "\n")
@ -969,8 +1009,8 @@ def generateAutotools(target, source, env):
vmime_pc_in.write("Name: @GENERIC_LIBRARY_NAME@\n")
vmime_pc_in.write("Description: " + packageDescription + "\n")
vmime_pc_in.write("Version: @VERSION@\n")
vmime_pc_in.write("Requires:\n")
vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@\n")
vmime_pc_in.write("Requires: @GSASL_REQUIRED@\n")
vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@ @GSASL_LIBS@\n")
#vmime_pc_in.write("Cflags: -I${includedir}/@GENERIC_VERSIONED_LIBRARY_NAME@\n")
vmime_pc_in.write("Cflags: -I${includedir}/\n")
vmime_pc_in.close()
@ -1074,6 +1114,15 @@ INCLUDES = -I$(top_srcdir) -I$(srcdir) @PKGCONFIG_CFLAGS@ @EXTRA_CFLAGS@
Makefile_am.write(packageVersionedName + "_la_SOURCES += " + buildMakefileFileList(x, 1) + "\n")
Makefile_am.write("endif\n")
# -- SASL support
x = selectFilesFromSuffixNot(libvmime_security_sasl_sources, '.hpp')
sourceFiles += x
Makefile_am.write("\n")
Makefile_am.write("if VMIME_HAVE_SASL_SUPPORT\n")
Makefile_am.write(packageVersionedName + "_la_SOURCES += " + buildMakefileFileList(x, 1) + "\n")
Makefile_am.write("endif\n")
# -- platform handlers
for platform in libvmime_platforms_sources:
Makefile_am.write("\n")
@ -1200,6 +1249,13 @@ else
AC_ERROR(no usable version of iconv has been found)
fi
# -- GNU SASL Library (http://www.gnu.org/software/gsasl/)
AC_CHECK_HEADER(gsasl.h,
AC_CHECK_LIB(gsasl, gsasl_check_version,
[have_gsasl=yes AC_SUBST(GSASL_AVAIL_LIBS, -lgsasl) AC_SUBST(GSASL_AVAIL_REQUIRED, libgsasl)],
have_gsasl=no),
have_gsasl=no)
# -- global constructors (stolen from 'configure.in' in libsigc++)
AC_MSG_CHECKING([if linker supports global constructors])
cat > mylib.$ac_ext <<EOF
@ -1391,7 +1447,7 @@ esac
# ** debug
AC_ARG_ENABLE(debug,
[ --enable-debug Turn on debugging (default: disabled)],
AC_HELP_STRING([--enable-debug], [Turn on debugging, default: disabled]),
[case "${enableval}" in
yes) conf_debug=yes ;;
no) conf_debug=no ;;
@ -1410,8 +1466,7 @@ fi
# ** messaging
AC_ARG_ENABLE(messaging,
[ --enable-messaging Enable messaging support\
(connection to mail servers, default: enabled)],
AC_HELP_STRING([--enable-messaging], [Enable messaging support and connection to mail servers, default: enabled]),
[case "${enableval}" in
yes) conf_messaging=yes ;;
no) conf_messaging=no ;;
@ -1427,6 +1482,38 @@ else
VMIME_HAVE_MESSAGING_FEATURES=0
fi
# ** SASL
AC_ARG_ENABLE(sasl,
AC_HELP_STRING([--enable-sasl], [Enable SASL support with GNU SASL, default: enabled]),
[case "${enableval}" in
yes) conf_sasl=yes ;;
no) conf_sasl=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-sasl) ;;
esac],
[conf_sasl=yes])
if test "x$conf_sasl" = "xyes"; then
if test "x$have_gsasl" = "xyes"; then
AM_CONDITIONAL(VMIME_HAVE_SASL_SUPPORT, true)
VMIME_HAVE_SASL_SUPPORT=1
GSASL_REQUIRED=${GSASL_AVAIL_REQUIRED}
GSASL_LIBS=${GSASL_AVAIL_LIBS}
else
AC_MSG_ERROR(can't find an usable version of GNU SASL library)
fi
else
AM_CONDITIONAL(VMIME_HAVE_SASL_SUPPORT, false)
VMIME_HAVE_SASL_SUPPORT=0
GSASL_REQUIRED=
GSASL_LIBS=
fi
AC_SUBST(GSASL_REQUIRED)
AC_SUBST(GSASL_LIBS)
# ** platform handlers
VMIME_BUILTIN_PLATFORMS=''
@ -1452,9 +1539,9 @@ VMIME_BUILTIN_MESSAGING_PROTOS=''
p = proto[0]
configure_in.write("AC_ARG_ENABLE(messaging-proto-" + p + ",\n")
configure_in.write(" [ --enable-messaging-proto-" + p
+ " Enable built-in support for protocol '" + p + "' "
+ " (default: enabled)],\n")
configure_in.write(" AC_HELP_STRING([--enable-messaging-proto-" + p
+ "], [Enable built-in support for protocol '" + p + "'"
+ ", default: enabled]),\n")
configure_in.write(' [case "${enableval}" in\n')
configure_in.write(' yes) conf_messaging_proto_' + p + '=yes ;;\n')
configure_in.write(' no) conf_messaging_proto_' + p + '=no ;;\n')
@ -1514,9 +1601,9 @@ fi
configure_in.write('fi\n\n')
configure_in.write("AC_ARG_ENABLE(platform-" + p + ",\n")
configure_in.write(" [ --enable-platform-" + p
+ " Compile built-in platform handler for '" + p + "' "
+ " (default: disabled, except if default for your platform)],\n")
configure_in.write(" AC_HELP_STRING([--enable-platform-" + p
+ "], [Compile built-in platform handler for '" + p + "' "
+ ", default: disabled, except if default for your platform]),\n")
configure_in.write(' [case "${enableval}" in\n')
configure_in.write(' yes) conf_platform_' + p + '=yes ;;\n')
configure_in.write(' no) conf_platform_' + p + '=no ;;\n')
@ -1551,7 +1638,7 @@ AC_SUBST(PKGCONFIG_CFLAGS)
AC_SUBST(PKGCONFIG_LIBS)
EXTRA_CFLAGS="$EXTRA_CFLAGS -D_REENTRANT=1"
EXTRA_LIBS=""
EXTRA_LIBS="$GSASL_LIBS"
CFLAGS=""
CXXFLAGS=""
@ -1666,6 +1753,8 @@ typedef unsigned ${VMIME_TYPE_INT32} vmime_uint32;
#define VMIME_WIDE_CHAR_SUPPORT 0
// -- File-system support
#define VMIME_HAVE_FILESYSTEM_FEATURES 1
// -- SASL support
#define VMIME_HAVE_SASL_SUPPORT ${VMIME_HAVE_SASL_SUPPORT}
// -- Messaging support
#define VMIME_HAVE_MESSAGING_FEATURES ${VMIME_HAVE_MESSAGING_FEATURES}
""")
@ -1719,6 +1808,9 @@ Messaging support : $conf_messaging
* protocols :$VMIME_BUILTIN_MESSAGING_PROTOS
File-system support : yes
Platform handlers :$VMIME_BUILTIN_PLATFORMS
SASL support : $conf_sasl
Please check 'vmime/config.hpp' to ensure the configuration is correct.
])
""")

View File

@ -20,6 +20,7 @@
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include "vmime/vmime.hpp"
#include "vmime/platforms/posix/posixHandler.hpp"
@ -31,27 +32,65 @@ static vmime::ref <vmime::net::session> g_session
// Authentification handler
class interactiveAuthenticator : public vmime::net::authenticator
class interactiveAuthenticator : public vmime::security::sasl::defaultSASLAuthenticator
{
const vmime::net::authenticationInfos requestAuthInfos() const
const std::vector <vmime::ref <vmime::security::sasl::SASLMechanism> > getAcceptableMechanisms
(const std::vector <vmime::ref <vmime::security::sasl::SASLMechanism> >& available,
vmime::ref <vmime::security::sasl::SASLMechanism> suggested) const
{
vmime::string username, password;
std::cout << std::endl << "Available SASL mechanisms:" << std::endl;
std::cout << std::endl;
std::cout << "Please authenticate yourself:" << std::endl;
for (unsigned int i = 0 ; i < available.size() ; ++i)
{
std::cout << " " << available[i]->getName();
std::cout << " Username: ";
std::cout.flush();
if (suggested && available[i]->getName() == suggested->getName())
std::cout << "(suggested)";
}
std::getline(std::cin, username);
std::cout << std::endl << std::endl;
std::cout << " Password: ";
std::cout.flush();
std::getline(std::cin, password);
return (vmime::net::authenticationInfos(username, password));
return defaultSASLAuthenticator::getAcceptableMechanisms(available, suggested);
}
void setSASLMechanism(vmime::ref <vmime::security::sasl::SASLMechanism> mech)
{
std::cout << "Trying " << mech->getName() << std::endl;
defaultSASLAuthenticator::setSASLMechanism(mech);
}
const vmime::string getUsername() const
{
if (m_username.empty())
m_username = getUserInput("Username");
return m_username;
}
const vmime::string getPassword() const
{
if (m_password.empty())
m_password = getUserInput("Password");
return m_password;
}
static const vmime::string getUserInput(const std::string& prompt)
{
std::cout << prompt << ": ";
std::cout.flush();
vmime::string res;
std::getline(std::cin, res);
return res;
}
private:
mutable vmime::string m_username;
mutable vmime::string m_password;
};
@ -215,7 +254,7 @@ static void sendMessage()
// You can also set some properties (see example7 to know the properties
// available for each service). For example, for SMTP:
// tr->setProperty("options.need-authentication", true);
tr->setProperty("options.need-authentication", true);
// Information about the mail
std::cout << "Enter email of the expeditor (eg. me@somewhere.com): ";

View File

@ -347,8 +347,7 @@ const char* net_exception::name() const throw() { return "net_exception"; }
socket_exception::~socket_exception() throw() {}
socket_exception::socket_exception(const string& what, const exception& other)
: net_exception(what.empty()
? "Socket error."
: "Socket error: '" + what + "'.", other) {}
? "Socket error." : what, other) {}
exception* socket_exception::clone() const { return new socket_exception(*this); }
const char* socket_exception::name() const throw() { return "socket_exception"; }
@ -361,8 +360,7 @@ const char* socket_exception::name() const throw() { return "socket_exception";
connection_error::~connection_error() throw() {}
connection_error::connection_error(const string& what, const exception& other)
: socket_exception(what.empty()
? "Connection error."
: "Connection error: '" + what + "'.", other) {}
? "Connection error." : what, other) {}
exception* connection_error::clone() const { return new connection_error(*this); }
const char* connection_error::name() const throw() { return "connection_error"; }
@ -675,6 +673,48 @@ const char* file_not_found::name() const throw() { return "file_not_found"; }
#endif // VMIME_HAVE_FILESYSTEM_FEATURES
#if VMIME_HAVE_SASL_SUPPORT
//
// sasl_exception
//
sasl_exception::~sasl_exception() throw() {}
sasl_exception::sasl_exception(const string& what, const exception& other)
: exception(what, other) {}
exception* sasl_exception::clone() const { return new sasl_exception(*this); }
const char* sasl_exception::name() const throw() { return "sasl_exception"; }
//
// no_such_mechanism
//
no_such_mechanism::~no_such_mechanism() throw() {}
no_such_mechanism::no_such_mechanism(const string& name, const exception& other)
: sasl_exception("No such SASL mechanism: '" + name + "'.", other) {}
exception* no_such_mechanism::clone() const { return new no_such_mechanism(*this); }
const char* no_such_mechanism::name() const throw() { return "no_such_mechanism"; }
//
// no_auth_information
//
no_auth_information::~no_auth_information() throw() {}
no_auth_information::no_auth_information(const exception& other)
: sasl_exception("Information cannot be provided.", other) {}
exception* no_auth_information::clone() const { return new no_auth_information(*this); }
const char* no_auth_information::name() const throw() { return "no_auth_information"; }
#endif // VMIME_HAVE_SASL_SUPPORT
} // exceptions

View File

@ -1,52 +0,0 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "vmime/net/authenticationInfos.hpp"
namespace vmime {
namespace net {
authenticationInfos::authenticationInfos(const string& username, const string& password)
: m_username(username), m_password(password)
{
}
authenticationInfos::authenticationInfos(const authenticationInfos& infos)
: object(), m_username(infos.m_username), m_password(infos.m_password)
{
}
const string& authenticationInfos::getUsername() const
{
return (m_username);
}
const string& authenticationInfos::getPassword() const
{
return (m_password);
}
} // net
} // vmime

View File

@ -1,33 +0,0 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "vmime/net/authenticator.hpp"
namespace vmime {
namespace net {
authenticator::~authenticator()
{
}
} // net
} // vmime

View File

@ -1,43 +0,0 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "vmime/net/defaultAuthenticator.hpp"
#include "vmime/net/session.hpp"
namespace vmime {
namespace net {
defaultAuthenticator::defaultAuthenticator(weak_ref <session> sess, const string& prefix)
: m_session(sess), m_prefix(prefix)
{
}
const authenticationInfos defaultAuthenticator::requestAuthInfos() const
{
return (authenticationInfos
(m_session->getProperties()[m_prefix + "auth.username"],
m_session->getProperties()[m_prefix + "auth.password"]));
}
} // net
} // vmime

View File

@ -25,6 +25,10 @@
#include "vmime/exception.hpp"
#include "vmime/platformDependant.hpp"
#if VMIME_HAVE_SASL_SUPPORT
#include "vmime/security/sasl/SASLContext.hpp"
#endif // VMIME_HAVE_SASL_SUPPORT
#include <sstream>
@ -42,7 +46,7 @@ namespace net {
namespace imap {
IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> auth)
IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <security::authenticator> auth)
: m_store(store), m_auth(auth), m_socket(NULL), m_parser(NULL), m_tag(NULL),
m_hierarchySeparator('\0'), m_state(STATE_NONE), m_timeoutHandler(NULL)
{
@ -51,10 +55,17 @@ IMAPConnection::IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> a
IMAPConnection::~IMAPConnection()
{
if (isConnected())
disconnect();
else if (m_socket)
internalDisconnect();
try
{
if (isConnected())
disconnect();
else if (m_socket)
internalDisconnect();
}
catch (vmime::exception&)
{
// Ignore
}
}
@ -107,25 +118,14 @@ void IMAPConnection::connect()
}
else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH)
{
const authenticationInfos auth = m_auth->requestAuthInfos();
// TODO: other authentication methods
send(true, "LOGIN " + IMAPUtils::quoteString(auth.getUsername())
+ " " + IMAPUtils::quoteString(auth.getPassword()), true);
utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
if (resp->isBad())
try
{
internalDisconnect();
throw exceptions::command_error("LOGIN", m_parser->lastLine());
authenticate();
}
else if (resp->response_done()->response_tagged()->
resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
catch (...)
{
internalDisconnect();
throw exceptions::authentication_error(m_parser->lastLine());
m_state = STATE_NONE;
throw;
}
}
@ -137,6 +137,278 @@ void IMAPConnection::connect()
}
void IMAPConnection::authenticate()
{
getAuthenticator()->setService(thisRef().dynamicCast <service>());
#if VMIME_HAVE_SASL_SUPPORT
// First, try SASL authentication
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
{
try
{
authenticateSASL();
return;
}
catch (exceptions::authentication_error& e)
{
if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
{
// Can't fallback on normal authentication
internalDisconnect();
throw e;
}
else
{
// Ignore, will try normal authentication
}
}
catch (exception& e)
{
internalDisconnect();
throw e;
}
}
#endif // VMIME_HAVE_SASL_SUPPORT
// Normal authentication
const string username = getAuthenticator()->getUsername();
const string password = getAuthenticator()->getPassword();
send(true, "LOGIN " + IMAPUtils::quoteString(username)
+ " " + IMAPUtils::quoteString(password), true);
utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
if (resp->isBad())
{
internalDisconnect();
throw exceptions::command_error("LOGIN", m_parser->lastLine());
}
else if (resp->response_done()->response_tagged()->
resp_cond_state()->status() != IMAPParser::resp_cond_state::OK)
{
internalDisconnect();
throw exceptions::authentication_error(m_parser->lastLine());
}
}
#if VMIME_HAVE_SASL_SUPPORT
void IMAPConnection::authenticateSASL()
{
if (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>())
throw exceptions::authentication_error("No SASL authenticator available.");
const std::vector <string> capa = getCapabilities();
std::vector <string> saslMechs;
for (unsigned int i = 0 ; i < capa.size() ; ++i)
{
const string& x = capa[i];
if (x.length() > 5 &&
(x[0] == 'A' || x[0] == 'a') &&
(x[1] == 'U' || x[1] == 'u') &&
(x[2] == 'T' || x[2] == 't') &&
(x[3] == 'H' || x[3] == 'h') &&
x[4] == '=')
{
saslMechs.push_back(string(x.begin() + 5, x.end()));
}
}
if (saslMechs.empty())
throw exceptions::authentication_error("No SASL mechanism available.");
std::vector <ref <security::sasl::SASLMechanism> > mechList;
ref <security::sasl::SASLContext> saslContext =
vmime::create <security::sasl::SASLContext>();
for (unsigned int i = 0 ; i < saslMechs.size() ; ++i)
{
try
{
mechList.push_back
(saslContext->createMechanism(saslMechs[i]));
}
catch (exceptions::no_such_mechanism&)
{
// Ignore mechanism
}
}
if (mechList.empty())
throw exceptions::authentication_error("No SASL mechanism available.");
// Try to suggest a mechanism among all those supported
ref <security::sasl::SASLMechanism> suggestedMech =
saslContext->suggestMechanism(mechList);
if (!suggestedMech)
throw exceptions::authentication_error("Unable to suggest SASL mechanism.");
// Allow application to choose which mechanisms to use
mechList = getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()->
getAcceptableMechanisms(mechList, suggestedMech);
if (mechList.empty())
throw exceptions::authentication_error("No SASL mechanism available.");
// Try each mechanism in the list in turn
for (unsigned int i = 0 ; i < mechList.size() ; ++i)
{
ref <security::sasl::SASLMechanism> mech = mechList[i];
ref <security::sasl::SASLSession> saslSession =
saslContext->createSession("imap", getAuthenticator(), mech);
saslSession->init();
send(true, "AUTHENTICATE " + mech->getName(), true);
for (bool cont = true ; cont ; )
{
utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
if (resp->response_done() &&
resp->response_done()->response_tagged() &&
resp->response_done()->response_tagged()->resp_cond_state()->
status() == IMAPParser::resp_cond_state::OK)
{
m_socket = saslSession->getSecuredSocket(m_socket);
return;
}
else
{
std::vector <IMAPParser::continue_req_or_response_data*>
respDataList = resp->continue_req_or_response_data();
string response;
for (unsigned int i = 0 ; i < respDataList.size() ; ++i)
{
if (respDataList[i]->continue_req())
{
response = respDataList[i]->continue_req()->resp_text()->text();
break;
}
}
if (response.empty())
{
cont = false;
continue;
}
byte* challenge = 0;
int challengeLen = 0;
byte* resp = 0;
int respLen = 0;
try
{
// Extract challenge
saslContext->decodeB64(response, &challenge, &challengeLen);
// Prepare response
saslSession->evaluateChallenge
(challenge, challengeLen, &resp, &respLen);
// Send response
send(false, saslContext->encodeB64(resp, respLen), true);
}
catch (exceptions::sasl_exception& e)
{
if (challenge)
{
delete [] challenge;
challenge = NULL;
}
if (resp)
{
delete [] resp;
resp = NULL;
}
// Cancel SASL exchange
send(false, "*", true);
}
catch (...)
{
if (challenge)
delete [] challenge;
if (resp)
delete [] resp;
throw;
}
if (challenge)
delete [] challenge;
if (resp)
delete [] resp;
}
}
}
throw exceptions::authentication_error
("Could not authenticate using SASL: all mechanisms failed.");
}
#endif // VMIME_HAVE_SASL_SUPPORT
const std::vector <string> IMAPConnection::getCapabilities()
{
send(true, "CAPABILITY", true);
utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse());
std::vector <string> res;
if (resp->response_done()->response_tagged()->
resp_cond_state()->status() == IMAPParser::resp_cond_state::OK)
{
const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList =
resp->continue_req_or_response_data();
for (unsigned int i = 0 ; i < respDataList.size() ; ++i)
{
if (respDataList[i]->response_data() == NULL)
continue;
const IMAPParser::capability_data* capaData =
respDataList[i]->response_data()->capability_data();
std::vector <IMAPParser::capability*> caps = capaData->capabilities();
for (unsigned int j = 0 ; j < caps.size() ; ++j)
{
if (caps[j]->auth_type())
res.push_back("AUTH=" + caps[j]->auth_type()->name());
else
res.push_back(caps[j]->atom()->value());
}
}
}
return res;
}
ref <security::authenticator> IMAPConnection::getAuthenticator()
{
return m_auth;
}
const bool IMAPConnection::isConnected() const
{
return (m_socket && m_socket->isConnected() &&

View File

@ -133,7 +133,7 @@ void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable)
// Open a connection for this folder
ref <IMAPConnection> connection =
vmime::create <IMAPConnection>(m_store, m_store->oneTimeAuthenticator());
vmime::create <IMAPConnection>(m_store, m_store->getAuthenticator());
try
{

View File

@ -32,67 +32,23 @@ namespace net {
namespace imap {
#ifndef VMIME_BUILDING_DOC
//
// IMAPauthenticator: private class used internally
//
// Used to request user credentials only in the first authentication
// and reuse this information the next times
//
class IMAPauthenticator : public authenticator
{
public:
IMAPauthenticator(ref <authenticator> auth)
: m_auth(auth), m_infos(NULL)
{
}
~IMAPauthenticator()
{
}
const authenticationInfos requestAuthInfos() const
{
if (m_infos == NULL)
m_infos = vmime::create <authenticationInfos>(m_auth->requestAuthInfos());
return (*m_infos);
}
private:
ref <authenticator> m_auth;
mutable ref <authenticationInfos> m_infos;
};
#endif // VMIME_BUILDING_DOC
//
// IMAPStore
//
IMAPStore::IMAPStore(ref <session> sess, ref <authenticator> auth)
: store(sess, getInfosInstance(), auth),
m_connection(NULL), m_oneTimeAuth(NULL)
IMAPStore::IMAPStore(ref <session> sess, ref <security::authenticator> auth)
: store(sess, getInfosInstance(), auth), m_connection(NULL)
{
}
IMAPStore::~IMAPStore()
{
if (isConnected())
disconnect();
}
ref <authenticator> IMAPStore::oneTimeAuthenticator()
{
return (m_oneTimeAuth);
try
{
if (isConnected())
disconnect();
}
catch (vmime::exception&)
{
// Ignore
}
}
@ -140,10 +96,8 @@ void IMAPStore::connect()
if (isConnected())
throw exceptions::already_connected();
m_oneTimeAuth = vmime::create <IMAPauthenticator>(getAuthenticator());
m_connection = vmime::create <IMAPConnection>
(thisWeakRef().dynamicCast <IMAPStore>(), m_oneTimeAuth);
(thisWeakRef().dynamicCast <IMAPStore>(), getAuthenticator());
try
{
@ -179,8 +133,6 @@ void IMAPStore::disconnect()
m_connection->disconnect();
m_oneTimeAuth = NULL;
m_connection = NULL;
}
@ -263,7 +215,10 @@ const IMAPStore::_infos::props& IMAPStore::_infos::getProperties() const
static props p =
{
// IMAP-specific options
// (none)
#if VMIME_HAVE_SASL_SUPPORT
property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"),
property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"),
#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED),
@ -286,7 +241,10 @@ const std::vector <serviceInfos::property> IMAPStore::_infos::getAvailableProper
const props& p = getProperties();
// IMAP-specific options
// (none)
#if VMIME_HAVE_SASL_SUPPORT
list.push_back(p.PROPERTY_OPTIONS_SASL);
list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
list.push_back(p.PROPERTY_AUTH_USERNAME);

View File

@ -39,7 +39,7 @@ namespace net {
namespace maildir {
maildirStore::maildirStore(ref <session> sess, ref <authenticator> auth)
maildirStore::maildirStore(ref <session> sess, ref <security::authenticator> auth)
: store(sess, getInfosInstance(), auth), m_connected(false)
{
}
@ -47,8 +47,15 @@ maildirStore::maildirStore(ref <session> sess, ref <authenticator> auth)
maildirStore::~maildirStore()
{
if (isConnected())
disconnect();
try
{
if (isConnected())
disconnect();
}
catch (vmime::exception&)
{
// Ignore
}
}
@ -96,7 +103,7 @@ const bool maildirStore::isValidFolderName(const folder::path::component& name)
const string& buf = name.getBuffer();
// Name cannot start/end with spaces
if (utility::stringUtils::trim(buf) != name.getBuffer())
if (utility::stringUtils::trim(buf) != buf)
return false;
// Name cannot start with '.'

View File

@ -25,6 +25,11 @@
#include "vmime/messageId.hpp"
#include "vmime/security/digest/messageDigestFactory.hpp"
#include "vmime/utility/filteredStream.hpp"
#include "vmime/utility/stringUtils.hpp"
#if VMIME_HAVE_SASL_SUPPORT
#include "vmime/security/sasl/SASLContext.hpp"
#endif // VMIME_HAVE_SASL_SUPPORT
#include <algorithm>
@ -41,7 +46,7 @@ namespace net {
namespace pop3 {
POP3Store::POP3Store(ref <session> sess, ref <authenticator> auth)
POP3Store::POP3Store(ref <session> sess, ref <security::authenticator> auth)
: store(sess, getInfosInstance(), auth), m_socket(NULL),
m_authentified(false), m_timeoutHandler(NULL)
{
@ -50,10 +55,17 @@ POP3Store::POP3Store(ref <session> sess, ref <authenticator> auth)
POP3Store::~POP3Store()
{
if (isConnected())
disconnect();
else if (m_socket)
internalDisconnect();
try
{
if (isConnected())
disconnect();
else if (m_socket)
internalDisconnect();
}
catch (vmime::exception&)
{
// Ignore
}
}
@ -128,98 +140,319 @@ void POP3Store::connect()
string response;
readResponse(response, false);
if (isSuccessResponse(response))
{
bool authentified = false;
const authenticationInfos auth = getAuthenticator()->requestAuthInfos();
// Secured authentication with APOP (if requested and if available)
//
// eg: C: APOP vincent <digest>
// --- S: +OK vincent is a valid mailbox
messageId mid(response);
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP))
{
if (mid.getLeft().length() && mid.getRight().length())
{
// <digest> is the result of MD5 applied to "<message-id>password"
ref <security::digest::messageDigest> md5 =
security::digest::messageDigestFactory::getInstance()->create("md5");
md5->update(mid.generate() + auth.getPassword());
md5->finalize();
sendRequest("APOP " + auth.getUsername() + " " + md5->getHexDigest());
readResponse(response, false);
if (isSuccessResponse(response))
{
authentified = true;
}
else
{
if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
{
internalDisconnect();
throw exceptions::authentication_error(response);
}
}
}
else
{
// APOP not supported
if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
{
// Can't fallback on basic authentification
internalDisconnect();
throw exceptions::unsupported_option();
}
}
}
if (!authentified)
{
// Basic authentication
//
// eg: C: USER vincent
// --- S: +OK vincent is a valid mailbox
//
// C: PASS couic
// S: +OK vincent's maildrop has 2 messages (320 octets)
sendRequest("USER " + auth.getUsername());
readResponse(response, false);
if (isSuccessResponse(response))
{
sendRequest("PASS " + auth.getPassword());
readResponse(response, false);
if (!isSuccessResponse(response))
{
internalDisconnect();
throw exceptions::authentication_error(response);
}
}
else
{
internalDisconnect();
throw exceptions::authentication_error(response);
}
}
}
else
if (!isSuccessResponse(response))
{
internalDisconnect();
throw exceptions::connection_greeting_error(response);
}
// Start authentication process
authenticate(messageId(response));
}
void POP3Store::authenticate(const messageId& randomMID)
{
getAuthenticator()->setService(thisRef().dynamicCast <service>());
#if VMIME_HAVE_SASL_SUPPORT
// First, try SASL authentication
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
{
try
{
authenticateSASL();
m_authentified = true;
return;
}
catch (exceptions::authentication_error& e)
{
if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
{
// Can't fallback on APOP/normal authentication
internalDisconnect();
throw e;
}
else
{
// Ignore, will try APOP/normal authentication
}
}
catch (exception& e)
{
internalDisconnect();
throw e;
}
}
#endif // VMIME_HAVE_SASL_SUPPORT
// Secured authentication with APOP (if requested and if available)
//
// eg: C: APOP vincent <digest>
// --- S: +OK vincent is a valid mailbox
const string username = getAuthenticator()->getUsername();
const string password = getAuthenticator()->getPassword();
string response;
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP))
{
if (randomMID.getLeft().length() != 0 &&
randomMID.getRight().length() != 0)
{
// <digest> is the result of MD5 applied to "<message-id>password"
ref <security::digest::messageDigest> md5 =
security::digest::messageDigestFactory::getInstance()->create("md5");
md5->update(randomMID.generate() + password);
md5->finalize();
sendRequest("APOP " + username + " " + md5->getHexDigest());
readResponse(response, false);
if (isSuccessResponse(response))
{
m_authentified = true;
return;
}
else
{
// Some servers close the connection after an
// unsuccessful APOP command, so the fallback
// may not always work...
//
// S: +OK Qpopper (version 4.0.5) at xxx starting. <30396.1126730747@xxx>
// C: APOP plop c5e0a87d088ec71d60e32692d4c5bdf4
// S: -ERR [AUTH] Password supplied for "o" is incorrect.
// S: +OK Pop server at xxx signing off.
// [Connection closed by foreign host.]
if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
{
// Can't fallback on basic authentication
internalDisconnect();
throw exceptions::authentication_error(response);
}
}
}
else
{
// APOP not supported
if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_APOP_FALLBACK))
{
// Can't fallback on basic authentication
internalDisconnect();
throw exceptions::authentication_error("APOP not supported");
}
}
}
// Basic authentication
//
// eg: C: USER vincent
// --- S: +OK vincent is a valid mailbox
//
// C: PASS couic
// S: +OK vincent's maildrop has 2 messages (320 octets)
sendRequest("USER " + username);
readResponse(response, false);
if (!isSuccessResponse(response))
{
internalDisconnect();
throw exceptions::authentication_error(response);
}
sendRequest("PASS " + password);
readResponse(response, false);
if (!isSuccessResponse(response))
{
internalDisconnect();
throw exceptions::authentication_error(response);
}
m_authentified = true;
}
#if VMIME_HAVE_SASL_SUPPORT
void POP3Store::authenticateSASL()
{
if (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>())
throw exceptions::authentication_error("No SASL authenticator available.");
std::vector <string> capa = getCapabilities();
std::vector <string> saslMechs;
for (unsigned int i = 0 ; i < capa.size() ; ++i)
{
const string& x = capa[i];
// C: CAPA
// S: +OK List of capabilities follows
// S: LOGIN-DELAY 0
// S: PIPELINING
// S: UIDL
// S: ...
// S: SASL DIGEST-MD5 CRAM-MD5 <-----
// S: EXPIRE NEVER
// S: ...
if (x.length() > 5 &&
(x[0] == 'S' || x[0] == 's') &&
(x[1] == 'A' || x[1] == 'a') &&
(x[2] == 'S' || x[2] == 's') &&
(x[3] == 'L' || x[3] == 'l') &&
std::isspace(x[4]))
{
const string list(x.begin() + 5, x.end());
std::istringstream iss(list);
string mech;
while (iss >> mech)
saslMechs.push_back(mech);
}
}
if (saslMechs.empty())
throw exceptions::authentication_error("No SASL mechanism available.");
std::vector <ref <security::sasl::SASLMechanism> > mechList;
ref <security::sasl::SASLContext> saslContext =
vmime::create <security::sasl::SASLContext>();
for (unsigned int i = 0 ; i < saslMechs.size() ; ++i)
{
try
{
mechList.push_back
(saslContext->createMechanism(saslMechs[i]));
}
catch (exceptions::no_such_mechanism&)
{
// Ignore mechanism
}
}
if (mechList.empty())
throw exceptions::authentication_error("No SASL mechanism available.");
// Try to suggest a mechanism among all those supported
ref <security::sasl::SASLMechanism> suggestedMech =
saslContext->suggestMechanism(mechList);
if (!suggestedMech)
throw exceptions::authentication_error("Unable to suggest SASL mechanism.");
// Allow application to choose which mechanisms to use
mechList = getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()->
getAcceptableMechanisms(mechList, suggestedMech);
if (mechList.empty())
throw exceptions::authentication_error("No SASL mechanism available.");
// Try each mechanism in the list in turn
for (unsigned int i = 0 ; i < mechList.size() ; ++i)
{
ref <security::sasl::SASLMechanism> mech = mechList[i];
ref <security::sasl::SASLSession> saslSession =
saslContext->createSession("pop3", getAuthenticator(), mech);
saslSession->init();
sendRequest("AUTH " + mech->getName());
for (bool cont = true ; cont ; )
{
string response;
readResponse(response, false);
switch (getResponseCode(response))
{
case RESPONSE_OK:
{
m_socket = saslSession->getSecuredSocket(m_socket);
return;
}
case RESPONSE_READY:
{
byte* challenge = 0;
int challengeLen = 0;
byte* resp = 0;
int respLen = 0;
try
{
// Extract challenge
stripResponseCode(response, response);
saslContext->decodeB64(response, &challenge, &challengeLen);
// Prepare response
saslSession->evaluateChallenge
(challenge, challengeLen, &resp, &respLen);
// Send response
sendRequest(saslContext->encodeB64(resp, respLen));
}
catch (exceptions::sasl_exception& e)
{
if (challenge)
{
delete [] challenge;
challenge = NULL;
}
if (resp)
{
delete [] resp;
resp = NULL;
}
// Cancel SASL exchange
sendRequest("*");
}
catch (...)
{
if (challenge)
delete [] challenge;
if (resp)
delete [] resp;
throw;
}
if (challenge)
delete [] challenge;
if (resp)
delete [] resp;
break;
}
default:
cont = false;
break;
}
}
}
throw exceptions::authentication_error
("Could not authenticate using SASL: all mechanisms failed.");
}
#endif // VMIME_HAVE_SASL_SUPPORT
const bool POP3Store::isConnected() const
{
return (m_socket && m_socket->isConnected() && m_authentified);
@ -259,7 +492,7 @@ void POP3Store::internalDisconnect()
void POP3Store::noop()
{
m_socket->send("NOOP");
sendRequest("NOOP");
string response;
readResponse(response, false);
@ -269,12 +502,33 @@ void POP3Store::noop()
}
const std::vector <string> POP3Store::getCapabilities()
{
sendRequest("CAPA");
string response;
readResponse(response, true);
std::vector <string> res;
if (isSuccessResponse(response))
{
stripFirstLine(response, response);
std::istringstream iss(response);
string line;
while (std::getline(iss, line, '\n'))
res.push_back(utility::stringUtils::trim(line));
}
return res;
}
const bool POP3Store::isSuccessResponse(const string& buffer)
{
static const string OK("+OK");
return (buffer.length() >= 3 &&
std::equal(buffer.begin(), buffer.begin() + 3, OK.begin()));
return getResponseCode(buffer) == RESPONSE_OK;
}
@ -296,6 +550,34 @@ const bool POP3Store::stripFirstLine(const string& buffer, string& result, strin
}
const int POP3Store::getResponseCode(const string& buffer)
{
if (buffer.length() >= 2)
{
// +[space]
if (buffer[0] == '+' &&
(buffer[1] == ' ' || buffer[1] == '\t'))
{
return RESPONSE_READY;
}
// +OK
if (buffer.length() >= 3)
{
if (buffer[0] == '+' &&
(buffer[1] == 'O' || buffer[1] == 'o') &&
(buffer[2] == 'K' || buffer[1] == 'k'))
{
return RESPONSE_OK;
}
}
}
// -ERR or whatever
return RESPONSE_ERR;
}
void POP3Store::stripResponseCode(const string& buffer, string& result)
{
const string::size_type pos = buffer.find_first_of(" \t");
@ -588,8 +870,12 @@ const POP3Store::_infos::props& POP3Store::_infos::getProperties() const
static props p =
{
// POP3-specific options
property("options.apop", serviceInfos::property::TYPE_BOOL, "false"),
property("options.apop.fallback", serviceInfos::property::TYPE_BOOL, "false"),
property("options.apop", serviceInfos::property::TYPE_BOOL, "true"),
property("options.apop.fallback", serviceInfos::property::TYPE_BOOL, "true"),
#if VMIME_HAVE_SASL_SUPPORT
property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"),
property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"),
#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED),
@ -614,6 +900,10 @@ const std::vector <serviceInfos::property> POP3Store::_infos::getAvailableProper
// POP3-specific options
list.push_back(p.PROPERTY_OPTIONS_APOP);
list.push_back(p.PROPERTY_OPTIONS_APOP_FALLBACK);
#if VMIME_HAVE_SASL_SUPPORT
list.push_back(p.PROPERTY_OPTIONS_SASL);
list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
list.push_back(p.PROPERTY_AUTH_USERNAME);

View File

@ -46,7 +46,7 @@ namespace net {
namespace sendmail {
sendmailTransport::sendmailTransport(ref <session> sess, ref <authenticator> auth)
sendmailTransport::sendmailTransport(ref <session> sess, ref <security::authenticator> auth)
: transport(sess, getInfosInstance(), auth), m_connected(false)
{
}
@ -54,8 +54,15 @@ sendmailTransport::sendmailTransport(ref <session> sess, ref <authenticator> aut
sendmailTransport::~sendmailTransport()
{
if (isConnected())
disconnect();
try
{
if (isConnected())
disconnect();
}
catch (vmime::exception&)
{
// Ignore
}
}

View File

@ -17,22 +17,33 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "vmime/config.hpp"
#include "vmime/net/service.hpp"
#include "vmime/net/defaultAuthenticator.hpp"
#if VMIME_HAVE_SASL_SUPPORT
#include "vmime/security/sasl/defaultSASLAuthenticator.hpp"
#else
#include "vmime/security/defaultAuthenticator.hpp"
#endif // VMIME_HAVE_SASL_SUPPORT
namespace vmime {
namespace net {
service::service(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth)
service::service(ref <session> sess, const serviceInfos& /* infos */,
ref <security::authenticator> auth)
: m_session(sess), m_auth(auth)
{
if (!auth)
{
m_auth = vmime::create <defaultAuthenticator>
(sess, infos.getPropertyPrefix());
#if VMIME_HAVE_SASL_SUPPORT
m_auth = vmime::create
<security::sasl::defaultSASLAuthenticator>();
#else
m_auth = vmime::create
<security::defaultAuthenticator>();
#endif // VMIME_HAVE_SASL_SUPPORT
}
}
@ -54,17 +65,23 @@ ref <session> service::getSession()
}
ref <const authenticator> service::getAuthenticator() const
ref <const security::authenticator> service::getAuthenticator() const
{
return (m_auth);
}
ref <authenticator> service::getAuthenticator()
ref <security::authenticator> service::getAuthenticator()
{
return (m_auth);
}
void service::setAuthenticator(ref <security::authenticator> auth)
{
m_auth = auth;
}
} // net
} // vmime

View File

@ -48,14 +48,16 @@ serviceFactory* serviceFactory::getInstance()
ref <service> serviceFactory::create
(ref <session> sess, const string& protocol, ref <authenticator> auth)
(ref <session> sess, const string& protocol,
ref <security::authenticator> auth)
{
return (getServiceByProtocol(protocol)->create(sess, auth));
}
ref <service> serviceFactory::create
(ref <session> sess, const utility::url& u, ref <authenticator> auth)
(ref <session> sess, const utility::url& u,
ref <security::authenticator> auth)
{
ref <service> serv = create(sess, u.getProtocol(), auth);

View File

@ -50,13 +50,14 @@ session::~session()
}
ref <transport> session::getTransport(ref <authenticator> auth)
ref <transport> session::getTransport(ref <security::authenticator> auth)
{
return (getTransport(m_props["transport.protocol"], auth));
}
ref <transport> session::getTransport(const string& protocol, ref <authenticator> auth)
ref <transport> session::getTransport
(const string& protocol, ref <security::authenticator> auth)
{
ref <session> sess = thisRef().dynamicCast <session>();
ref <service> sv = serviceFactory::getInstance()->create(sess, protocol, auth);
@ -68,7 +69,8 @@ ref <transport> session::getTransport(const string& protocol, ref <authenticator
}
ref <transport> session::getTransport(const utility::url& url, ref <authenticator> auth)
ref <transport> session::getTransport
(const utility::url& url, ref <security::authenticator> auth)
{
ref <session> sess = thisRef().dynamicCast <session>();
ref <service> sv = serviceFactory::getInstance()->create(sess, url, auth);
@ -80,13 +82,14 @@ ref <transport> session::getTransport(const utility::url& url, ref <authenticato
}
ref <store> session::getStore(ref <authenticator> auth)
ref <store> session::getStore(ref <security::authenticator> auth)
{
return (getStore(m_props["store.protocol"], auth));
}
ref <store> session::getStore(const string& protocol, ref <authenticator> auth)
ref <store> session::getStore
(const string& protocol, ref <security::authenticator> auth)
{
ref <session> sess = thisRef().dynamicCast <session>();
ref <service> sv = serviceFactory::getInstance()->create(sess, protocol, auth);
@ -98,7 +101,8 @@ ref <store> session::getStore(const string& protocol, ref <authenticator> auth)
}
ref <store> session::getStore(const utility::url& url, ref <authenticator> auth)
ref <store> session::getStore
(const utility::url& url, ref <security::authenticator> auth)
{
ref <session> sess = thisRef().dynamicCast <session>();
ref <service> sv = serviceFactory::getInstance()->create(sess, url, auth);

View File

@ -1,69 +0,0 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "vmime/net/simpleAuthenticator.hpp"
namespace vmime {
namespace net {
simpleAuthenticator::simpleAuthenticator()
{
}
simpleAuthenticator::simpleAuthenticator(const string& username, const string& password)
: m_username(username), m_password(password)
{
}
const authenticationInfos simpleAuthenticator::requestAuthInfos() const
{
return (authenticationInfos(m_username, m_password));
}
const string& simpleAuthenticator::getUsername() const
{
return (m_username);
}
void simpleAuthenticator::setUsername(const string& username)
{
m_username = username;
}
const string& simpleAuthenticator::getPassword() const
{
return (m_password);
}
void simpleAuthenticator::setPassword(const string& password)
{
m_password = password;
}
} // net
} // vmime

View File

@ -27,6 +27,11 @@
#include "vmime/net/authHelper.hpp"
#include "vmime/utility/filteredStream.hpp"
#include "vmime/utility/stringUtils.hpp"
#if VMIME_HAVE_SASL_SUPPORT
#include "vmime/security/sasl/SASLContext.hpp"
#endif // VMIME_HAVE_SASL_SUPPORT
// Helpers for service properties
@ -41,7 +46,7 @@ namespace net {
namespace smtp {
SMTPTransport::SMTPTransport(ref <session> sess, ref <authenticator> auth)
SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth)
: transport(sess, getInfosInstance(), auth), m_socket(NULL),
m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL)
{
@ -50,10 +55,17 @@ SMTPTransport::SMTPTransport(ref <session> sess, ref <authenticator> auth)
SMTPTransport::~SMTPTransport()
{
if (isConnected())
disconnect();
else if (m_socket)
internalDisconnect();
try
{
if (isConnected())
disconnect();
else if (m_socket)
internalDisconnect();
}
catch (vmime::exception&)
{
// Ignore
}
}
@ -87,15 +99,16 @@ void SMTPTransport::connect()
m_socket = sf->create();
m_socket->connect(address, port);
m_responseBuffer.clear();
// Connection
//
// eg: C: <connection to server>
// --- S: 220 smtp.domain.com Service ready
string response;
readResponse(response);
if (responseCode(response) != 220)
if (readAllResponses(response) != 220)
{
internalDisconnect();
throw exceptions::connection_greeting_error(response);
@ -105,12 +118,12 @@ void SMTPTransport::connect()
// First, try Extended SMTP (ESMTP)
//
// eg: C: EHLO thismachine.ourdomain.com
// S: 250 OK
// S: 250-smtp.theserver.com
// S: 250 AUTH CRAM-MD5 DIGEST-MD5
sendRequest("EHLO " + platformDependant::getHandler()->getHostName());
readResponse(response);
if (responseCode(response) != 250)
if (readAllResponses(response, true) != 250)
{
// Next, try "Basic" SMTP
//
@ -118,9 +131,8 @@ void SMTPTransport::connect()
// S: 250 OK
sendRequest("HELO " + platformDependant::getHandler()->getHostName());
readResponse(response);
if (responseCode(response) != 250)
if (readAllResponses(response) != 250)
{
internalDisconnect();
throw exceptions::connection_greeting_error(response);
@ -131,93 +143,231 @@ void SMTPTransport::connect()
else
{
m_extendedSMTP = true;
m_extendedSMTPResponse = response;
}
// Authentication
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_NEEDAUTH))
authenticate();
}
void SMTPTransport::authenticate()
{
if (!m_extendedSMTP)
{
if (!m_extendedSMTP)
internalDisconnect();
throw exceptions::command_error("AUTH", "ESMTP not supported.");
}
getAuthenticator()->setService(thisRef().dynamicCast <service>());
#if VMIME_HAVE_SASL_SUPPORT
// First, try SASL authentication
if (GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL))
{
try
{
authenticateSASL();
m_authentified = true;
return;
}
catch (exceptions::authentication_error& e)
{
if (!GET_PROPERTY(bool, PROPERTY_OPTIONS_SASL_FALLBACK))
{
// Can't fallback on normal authentication
internalDisconnect();
throw e;
}
else
{
// Ignore, will try normal authentication
}
}
catch (exception& e)
{
internalDisconnect();
throw exceptions::command_error("AUTH", "ESMTP not supported.");
throw e;
}
}
#endif // VMIME_HAVE_SASL_SUPPORT
const authenticationInfos auth = getAuthenticator()->requestAuthInfos();
bool authentified = false;
// No other authentication method is possible
throw exceptions::authentication_error("All authentication methods failed");
}
enum AuthMethods
#if VMIME_HAVE_SASL_SUPPORT
void SMTPTransport::authenticateSASL()
{
if (!getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>())
throw exceptions::authentication_error("No SASL authenticator available.");
// Obtain SASL mechanisms supported by server from EHLO response
std::vector <string> saslMechs;
std::istringstream iss(m_extendedSMTPResponse);
while (!iss.eof())
{
string line;
std::getline(iss, line);
std::istringstream liss(line);
string word;
bool inAuth = false;
while (liss >> word)
{
First = 0,
CRAM_MD5 = First,
// TODO: more authentication methods...
End
};
for (int currentMethod = First ; !authentified ; ++currentMethod)
{
switch (currentMethod)
if (word.length() == 4 &&
(word[0] == 'A' || word[0] == 'a') ||
(word[0] == 'U' || word[0] == 'u') ||
(word[0] == 'T' || word[0] == 't') ||
(word[0] == 'H' || word[0] == 'h'))
{
case CRAM_MD5:
{
sendRequest("AUTH CRAM-MD5");
readResponse(response);
if (responseCode(response) == 334)
{
encoderB64 base64;
string challengeB64 = responseText(response);
string challenge, challengeHex;
{
utility::inputStreamStringAdapter in(challengeB64);
utility::outputStreamStringAdapter out(challenge);
base64.decode(in, out);
}
hmac_md5(challenge, auth.getPassword(), challengeHex);
string decoded = auth.getUsername() + " " + challengeHex;
string encoded;
{
utility::inputStreamStringAdapter in(decoded);
utility::outputStreamStringAdapter out(encoded);
base64.encode(in, out);
}
sendRequest(encoded);
readResponse(response);
if (responseCode(response) == 235)
{
authentified = true;
}
else
{
internalDisconnect();
throw exceptions::authentication_error(response);
}
}
break;
inAuth = true;
}
case End:
else if (inAuth)
{
// All authentication methods have been tried and
// the server does not understand any.
throw exceptions::authentication_error(response);
}
saslMechs.push_back(word);
}
}
}
m_authentified = true;
if (saslMechs.empty())
throw exceptions::authentication_error("No SASL mechanism available.");
std::vector <ref <security::sasl::SASLMechanism> > mechList;
ref <security::sasl::SASLContext> saslContext =
vmime::create <security::sasl::SASLContext>();
for (unsigned int i = 0 ; i < saslMechs.size() ; ++i)
{
try
{
mechList.push_back
(saslContext->createMechanism(saslMechs[i]));
}
catch (exceptions::no_such_mechanism&)
{
// Ignore mechanism
}
}
if (mechList.empty())
throw exceptions::authentication_error("No SASL mechanism available.");
// Try to suggest a mechanism among all those supported
ref <security::sasl::SASLMechanism> suggestedMech =
saslContext->suggestMechanism(mechList);
if (!suggestedMech)
throw exceptions::authentication_error("Unable to suggest SASL mechanism.");
// Allow application to choose which mechanisms to use
mechList = getAuthenticator().dynamicCast <security::sasl::SASLAuthenticator>()->
getAcceptableMechanisms(mechList, suggestedMech);
if (mechList.empty())
throw exceptions::authentication_error("No SASL mechanism available.");
// Try each mechanism in the list in turn
for (unsigned int i = 0 ; i < mechList.size() ; ++i)
{
ref <security::sasl::SASLMechanism> mech = mechList[i];
ref <security::sasl::SASLSession> saslSession =
saslContext->createSession("smtp", getAuthenticator(), mech);
saslSession->init();
sendRequest("AUTH " + mech->getName());
for (bool cont = true ; cont ; )
{
string response;
switch (readAllResponses(response))
{
case 235:
{
m_socket = saslSession->getSecuredSocket(m_socket);
return;
}
case 334:
{
byte* challenge = 0;
int challengeLen = 0;
byte* resp = 0;
int respLen = 0;
try
{
// Extract challenge
saslContext->decodeB64(response, &challenge, &challengeLen);
// Prepare response
saslSession->evaluateChallenge
(challenge, challengeLen, &resp, &respLen);
// Send response
sendRequest(saslContext->encodeB64(resp, respLen));
}
catch (exceptions::sasl_exception& e)
{
if (challenge)
{
delete [] challenge;
challenge = NULL;
}
if (resp)
{
delete [] resp;
resp = NULL;
}
// Cancel SASL exchange
sendRequest("*");
}
catch (...)
{
if (challenge)
delete [] challenge;
if (resp)
delete [] resp;
throw;
}
if (challenge)
delete [] challenge;
if (resp)
delete [] resp;
break;
}
default:
cont = false;
break;
}
}
}
throw exceptions::authentication_error
("Could not authenticate using SASL: all mechanisms failed.");
}
#endif // VMIME_HAVE_SASL_SUPPORT
const bool SMTPTransport::isConnected() const
{
@ -250,12 +400,11 @@ void SMTPTransport::internalDisconnect()
void SMTPTransport::noop()
{
m_socket->send("NOOP");
sendRequest("NOOP");
string response;
readResponse(response);
if (responseCode(response) != 250)
if (readAllResponses(response) != 250)
throw exceptions::command_error("NOOP", response);
}
@ -274,9 +423,8 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
string response;
sendRequest("MAIL FROM: <" + expeditor.getEmail() + ">");
readResponse(response);
if (responseCode(response) != 250)
if (readAllResponses(response) != 250)
{
internalDisconnect();
throw exceptions::command_error("MAIL", response);
@ -288,9 +436,8 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
const mailbox& mbox = *recipients.getMailboxAt(i);
sendRequest("RCPT TO: <" + mbox.getEmail() + ">");
readResponse(response);
if (responseCode(response) != 250)
if (readAllResponses(response) != 250)
{
internalDisconnect();
throw exceptions::command_error("RCPT TO", response);
@ -299,9 +446,8 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
// Send the message data
sendRequest("DATA");
readResponse(response);
if (responseCode(response) != 354)
if (readAllResponses(response) != 354)
{
internalDisconnect();
throw exceptions::command_error("DATA", response);
@ -315,9 +461,8 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
// Send end-of-data delimiter
m_socket->sendRaw("\r\n.\r\n", 5);
readResponse(response);
if (responseCode(response) != 250)
if (readAllResponses(response) != 250)
{
internalDisconnect();
throw exceptions::command_error("DATA", response);
@ -332,7 +477,7 @@ void SMTPTransport::sendRequest(const string& buffer, const bool end)
}
const int SMTPTransport::responseCode(const string& response)
const int SMTPTransport::getResponseCode(const string& response)
{
int code = 0;
@ -347,35 +492,25 @@ const int SMTPTransport::responseCode(const string& response)
}
const string SMTPTransport::responseText(const string& response)
const string SMTPTransport::readResponseLine()
{
string text;
string currentBuffer = m_responseBuffer;
std::istringstream iss(response);
std::string line;
while (std::getline(iss, line))
while (true)
{
if (line.length() >= 4)
text += line.substr(4);
else
text += line;
// Get a line from the response buffer
string::size_type lineEnd = currentBuffer.find_first_of('\n');
text += "\n";
}
if (lineEnd != string::npos)
{
const string line(currentBuffer.begin(), currentBuffer.begin() + lineEnd);
return (text);
}
currentBuffer.erase(currentBuffer.begin(), currentBuffer.begin() + lineEnd + 1);
m_responseBuffer = currentBuffer;
return line;
}
void SMTPTransport::readResponse(string& buffer)
{
bool foundTerminator = false;
buffer.clear();
for ( ; !foundTerminator ; )
{
// Check whether the time-out delay is elapsed
if (m_timeoutHandler && m_timeoutHandler->isTimeOut())
{
@ -393,40 +528,61 @@ void SMTPTransport::readResponse(string& buffer)
continue;
}
// We have received data: reset the time-out counter
if (m_timeoutHandler)
m_timeoutHandler->resetTimeOut();
currentBuffer += receiveBuffer;
}
}
// Append the data to the response buffer
buffer += receiveBuffer;
// Check for terminator string (and strip it if present)
if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n')
const int SMTPTransport::readResponse(string& text)
{
string line = readResponseLine();
// Special case where CRLF occurs after response code
if (line.length() < 4)
line = line + '\n' + readResponseLine();
const int code = getResponseCode(line);
m_responseContinues = (line.length() >= 4 && line[3] == '-');
if (line.length() > 4)
text = utility::stringUtils::trim(line.substr(4));
else
text = utility::stringUtils::trim(line);
return code;
}
const int SMTPTransport::readAllResponses(string& outText, const bool allText)
{
string text;
const int firstCode = readResponse(outText);
if (allText)
text = outText;
while (m_responseContinues)
{
const int code = readResponse(outText);
if (allText)
text += '\n' + outText;
if (code != firstCode)
{
string::size_type p = buffer.length() - 2;
bool end = false;
if (allText)
outText = text;
for ( ; !end ; --p)
{
if (p == 0 || buffer[p] == '\n')
{
end = true;
if (p + 4 < buffer.length())
foundTerminator = true;
}
}
return 0;
}
}
// Remove [CR]LF at the end of the response
if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n')
{
if (buffer[buffer.length() - 2] == '\r')
buffer.resize(buffer.length() - 2);
else
buffer.resize(buffer.length() - 1);
}
if (allText)
outText = text;
return firstCode;
}
@ -460,6 +616,10 @@ const SMTPTransport::_infos::props& SMTPTransport::_infos::getProperties() const
{
// SMTP-specific options
property("options.need-authentication", serviceInfos::property::TYPE_BOOL, "false"),
#if VMIME_HAVE_SASL_SUPPORT
property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"),
property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "false"),
#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
property(serviceInfos::property::AUTH_USERNAME),
@ -483,6 +643,10 @@ const std::vector <serviceInfos::property> SMTPTransport::_infos::getAvailablePr
// SMTP-specific options
list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH);
#if VMIME_HAVE_SASL_SUPPORT
list.push_back(p.PROPERTY_OPTIONS_SASL);
list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK);
#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
list.push_back(p.PROPERTY_AUTH_USERNAME);

View File

@ -28,7 +28,7 @@ namespace vmime {
namespace net {
transport::transport(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth)
transport::transport(ref <session> sess, const serviceInfos& infos, ref <security::authenticator> auth)
: service(sess, infos, auth)
{
}

View File

@ -0,0 +1,98 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "vmime/security/defaultAuthenticator.hpp"
#include "vmime/net/service.hpp"
#include "vmime/platformDependant.hpp"
namespace vmime {
namespace security {
defaultAuthenticator::defaultAuthenticator()
{
}
defaultAuthenticator::~defaultAuthenticator()
{
}
const string defaultAuthenticator::getUsername() const
{
const string& prefix = m_service->getInfos().getPropertyPrefix();
const propertySet& props = m_service->getSession()->getProperties();
if (props.hasProperty(prefix + net::serviceInfos::property::AUTH_USERNAME.getName()))
return props[prefix + net::serviceInfos::property::AUTH_USERNAME.getName()];
throw exceptions::no_auth_information();
}
const string defaultAuthenticator::getPassword() const
{
const string& prefix = m_service->getInfos().getPropertyPrefix();
const propertySet& props = m_service->getSession()->getProperties();
if (props.hasProperty(prefix + net::serviceInfos::property::AUTH_PASSWORD.getName()))
return props[prefix + net::serviceInfos::property::AUTH_PASSWORD.getName()];
throw exceptions::no_auth_information();
}
const string defaultAuthenticator::getHostname() const
{
return platformDependant::getHandler()->getHostName();
}
const string defaultAuthenticator::getAnonymousToken() const
{
return "anonymous@" + platformDependant::getHandler()->getHostName();
}
const string defaultAuthenticator::getServiceName() const
{
// Information cannot be provided
throw exceptions::no_auth_information();
}
void defaultAuthenticator::setService(ref <net::service> serv)
{
m_service = serv;
}
weak_ref <net::service> defaultAuthenticator::getService() const
{
return m_service;
}
} // security
} // vmime

View File

@ -0,0 +1,189 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <sstream>
#include <gsasl.h>
#include "vmime/security/sasl/SASLContext.hpp"
#include "vmime/security/sasl/SASLMechanism.hpp"
#include "vmime/base.hpp"
#include "vmime/encoderFactory.hpp"
#include "vmime/utility/stream.hpp"
namespace vmime {
namespace security {
namespace sasl {
SASLContext::SASLContext()
{
if (gsasl_init(&m_gsaslContext) != GSASL_OK)
throw std::bad_alloc();
}
SASLContext::~SASLContext()
{
gsasl_done(m_gsaslContext);
}
ref <SASLSession> SASLContext::createSession
(const string& serviceName,
ref <authenticator> auth, ref <SASLMechanism> mech)
{
return vmime::create <SASLSession>
(serviceName, thisRef().dynamicCast <SASLContext>(), auth, mech);
}
ref <SASLMechanism> SASLContext::createMechanism(const string& name)
{
return SASLMechanismFactory::getInstance()->create
(thisRef().dynamicCast <SASLContext>(), name);
}
ref <SASLMechanism> SASLContext::suggestMechanism
(const std::vector <ref <SASLMechanism> >& mechs)
{
if (mechs.empty())
return 0;
std::ostringstream oss;
for (unsigned int i = 0 ; i < mechs.size() ; ++i)
oss << mechs[i]->getName() << " ";
const string mechList = oss.str();
const char* suggested = gsasl_client_suggest_mechanism
(m_gsaslContext, mechList.c_str());
if (suggested)
{
for (unsigned int i = 0 ; i < mechs.size() ; ++i)
{
if (mechs[i]->getName() == suggested)
return mechs[i];
}
}
return 0;
}
void SASLContext::decodeB64(const string& input, byte** output, int* outputLen)
{
string res;
utility::inputStreamStringAdapter is(input);
utility::outputStreamStringAdapter os(res);
ref <encoder> dec = encoderFactory::getInstance()->create("base64");
dec->decode(is, os);
byte* out = new byte[res.length()];
std::copy(res.begin(), res.end(), out);
*output = out;
*outputLen = res.length();
}
const string SASLContext::encodeB64(const byte* input, const int inputLen)
{
string res;
utility::inputStreamByteBufferAdapter is(input, inputLen);
utility::outputStreamStringAdapter os(res);
ref <encoder> enc = encoderFactory::getInstance()->create("base64");
enc->encode(is, os);
return res;
}
const string SASLContext::getErrorMessage(const string& fname, const int code)
{
string msg = fname + "() returned ";
#define ERROR(x) \
case x: msg += #x; break;
switch (code)
{
ERROR(GSASL_NEEDS_MORE)
ERROR(GSASL_UNKNOWN_MECHANISM)
ERROR(GSASL_MECHANISM_CALLED_TOO_MANY_TIMES)
ERROR(GSASL_MALLOC_ERROR)
ERROR(GSASL_BASE64_ERROR)
ERROR(GSASL_CRYPTO_ERROR)
ERROR(GSASL_SASLPREP_ERROR)
ERROR(GSASL_MECHANISM_PARSE_ERROR)
ERROR(GSASL_AUTHENTICATION_ERROR)
ERROR(GSASL_INTEGRITY_ERROR)
ERROR(GSASL_NO_CLIENT_CODE)
ERROR(GSASL_NO_SERVER_CODE)
ERROR(GSASL_NO_CALLBACK)
ERROR(GSASL_NO_ANONYMOUS_TOKEN)
ERROR(GSASL_NO_AUTHID)
ERROR(GSASL_NO_AUTHZID)
ERROR(GSASL_NO_PASSWORD)
ERROR(GSASL_NO_PASSCODE)
ERROR(GSASL_NO_PIN)
ERROR(GSASL_NO_SERVICE)
ERROR(GSASL_NO_HOSTNAME)
ERROR(GSASL_GSSAPI_RELEASE_BUFFER_ERROR)
ERROR(GSASL_GSSAPI_IMPORT_NAME_ERROR)
ERROR(GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR)
ERROR(GSASL_GSSAPI_ACCEPT_SEC_CONTEXT_ERROR)
ERROR(GSASL_GSSAPI_UNWRAP_ERROR)
ERROR(GSASL_GSSAPI_WRAP_ERROR)
ERROR(GSASL_GSSAPI_ACQUIRE_CRED_ERROR)
ERROR(GSASL_GSSAPI_DISPLAY_NAME_ERROR)
ERROR(GSASL_GSSAPI_UNSUPPORTED_PROTECTION_ERROR)
ERROR(GSASL_KERBEROS_V5_INIT_ERROR)
ERROR(GSASL_KERBEROS_V5_INTERNAL_ERROR)
ERROR(GSASL_SECURID_SERVER_NEED_ADDITIONAL_PASSCODE)
ERROR(GSASL_SECURID_SERVER_NEED_NEW_PIN)
default:
msg += "unknown error";
break;
}
#undef ERROR
return msg;
}
} // sasl
} // security
} // vmime

View File

@ -0,0 +1,131 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <stdexcept>
#include <new>
#include <gsasl.h>
#include "vmime/security/sasl/SASLMechanismFactory.hpp"
#include "vmime/security/sasl/builtinSASLMechanism.hpp"
#include "vmime/security/sasl/SASLContext.hpp"
#include "vmime/utility/stringUtils.hpp"
#include "vmime/base.hpp"
#include "vmime/exception.hpp"
namespace vmime {
namespace security {
namespace sasl {
SASLMechanismFactory::SASLMechanismFactory()
{
if (gsasl_init(&m_gsaslContext) != GSASL_OK)
throw std::bad_alloc();
}
SASLMechanismFactory::~SASLMechanismFactory()
{
gsasl_done(m_gsaslContext);
}
// static
SASLMechanismFactory* SASLMechanismFactory::getInstance()
{
static SASLMechanismFactory instance;
return &instance;
}
ref <SASLMechanism> SASLMechanismFactory::create
(ref <SASLContext> ctx, const string& name_)
{
const string name(utility::stringUtils::toUpper(name_));
// Check for built-in mechanisms
if (isMechanismSupported(name))
{
return vmime::create <builtinSASLMechanism>(ctx, name);
}
// Check for registered mechanisms
else
{
MapType::iterator it = m_mechs.find(name);
if (it != m_mechs.end())
return (*it).second->create(ctx, name);
}
throw exceptions::no_such_mechanism(name);
return 0;
}
const std::vector <string> SASLMechanismFactory::getSupportedMechanisms() const
{
std::vector <string> list;
// Registered mechanisms
for (MapType::const_iterator it = m_mechs.begin() ;
it != m_mechs.end() ; ++it)
{
list.push_back((*it).first);
}
// Built-in mechanisms
char* out = 0;
if (gsasl_client_mechlist(m_gsaslContext, &out) == GSASL_OK)
{
// 'out' contains SASL mechanism names, separated by spaces
for (char *start = out, *p = out ; ; ++p)
{
if (*p == ' ' || !*p)
{
list.push_back(string(start, p));
start = p + 1;
// End of string
if (!*p) break;
}
}
free(out);
}
return list;
}
const bool SASLMechanismFactory::isMechanismSupported(const string& name) const
{
return (gsasl_client_support_p(m_gsaslContext, name.c_str()) != 0 ||
m_mechs.find(name) != m_mechs.end());
}
} // sasl
} // security
} // vmime

View File

@ -0,0 +1,179 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <sstream>
#include <gsasl.h>
#include "vmime/security/sasl/SASLSession.hpp"
#include "vmime/security/sasl/SASLContext.hpp"
#include "vmime/security/sasl/SASLSocket.hpp"
#include "vmime/security/sasl/SASLAuthenticator.hpp"
namespace vmime {
namespace security {
namespace sasl {
SASLSession::SASLSession(const string& serviceName, ref <SASLContext> ctx,
ref <authenticator> auth, ref <SASLMechanism> mech)
: m_serviceName(serviceName), m_context(ctx), m_auth(auth),
m_mech(mech), m_gsaslContext(0), m_gsaslSession(0)
{
if (gsasl_init(&m_gsaslContext) != GSASL_OK)
throw std::bad_alloc();
gsasl_client_start(m_gsaslContext, mech->getName().c_str(), &m_gsaslSession);
gsasl_callback_set(m_gsaslContext, gsaslCallback);
gsasl_callback_hook_set(m_gsaslContext, this);
}
SASLSession::~SASLSession()
{
gsasl_finish(m_gsaslSession);
m_gsaslSession = 0;
gsasl_done(m_gsaslContext);
m_gsaslContext = 0;
}
void SASLSession::init()
{
ref <SASLAuthenticator> saslAuth = m_auth.dynamicCast <SASLAuthenticator>();
if (saslAuth)
{
saslAuth->setSASLMechanism(m_mech);
saslAuth->setSASLSession(thisRef().dynamicCast <SASLSession>());
}
}
ref <authenticator> SASLSession::getAuthenticator()
{
return m_auth;
}
ref <SASLMechanism> SASLSession::getMechanism()
{
return m_mech;
}
ref <SASLContext> SASLSession::getContext()
{
return m_context;
}
const bool SASLSession::evaluateChallenge
(const byte* challenge, const int challengeLen,
byte** response, int* responseLen)
{
return m_mech->step(thisRef().dynamicCast <SASLSession>(),
challenge, challengeLen, response, responseLen);
}
ref <net::socket> SASLSession::getSecuredSocket(ref <net::socket> sok)
{
return vmime::create <SASLSocket>(thisRef().dynamicCast <SASLSession>(), sok);
}
const string SASLSession::getServiceName() const
{
return m_serviceName;
}
// static
int SASLSession::gsaslCallback
(Gsasl* ctx, Gsasl_session* sctx, Gsasl_property prop)
{
SASLSession* sess = reinterpret_cast <SASLSession*>(gsasl_callback_hook_get(ctx));
if (!sess) return GSASL_AUTHENTICATION_ERROR;
ref <authenticator> auth = sess->getAuthenticator();
try
{
string res;
switch (prop)
{
case GSASL_AUTHID:
res = auth->getUsername();
break;
case GSASL_PASSWORD:
res = auth->getPassword();
break;
case GSASL_ANONYMOUS_TOKEN:
res = auth->getAnonymousToken();
break;
case GSASL_HOSTNAME:
res = auth->getHostname();
break;
case GSASL_SERVICE:
res = auth->getServiceName();
break;
case GSASL_AUTHZID:
case GSASL_GSSAPI_DISPLAY_NAME:
case GSASL_PASSCODE:
case GSASL_SUGGESTED_PIN:
case GSASL_PIN:
case GSASL_REALM:
default:
return GSASL_NO_CALLBACK;
}
gsasl_property_set(sctx, prop, res.c_str());
return GSASL_OK;
}
//catch (exceptions::no_auth_information&)
catch (...)
{
return GSASL_NO_CALLBACK;
}
}
} // sasl
} // security
} // vmime

View File

@ -0,0 +1,167 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "vmime/security/sasl/SASLSocket.hpp"
#include "vmime/security/sasl/SASLSession.hpp"
#include "vmime/exception.hpp"
#include <algorithm>
#include <gsasl.h>
namespace vmime {
namespace security {
namespace sasl {
SASLSocket::SASLSocket(ref <SASLSession> sess, ref <net::socket> wrapped)
: m_session(sess), m_wrapped(wrapped),
m_pendingBuffer(0), m_pendingPos(0), m_pendingLen(0)
{
}
SASLSocket::~SASLSocket()
{
if (m_pendingBuffer)
delete [] m_pendingBuffer;
}
void SASLSocket::connect(const string& address, const port_t port)
{
m_wrapped->connect(address, port);
}
void SASLSocket::disconnect()
{
m_wrapped->disconnect();
}
const bool SASLSocket::isConnected() const
{
return m_wrapped->isConnected();
}
void SASLSocket::receive(string& buffer)
{
const int n = receiveRaw(m_recvBuffer, sizeof(m_recvBuffer));
buffer = string(m_recvBuffer, n);
}
const int SASLSocket::receiveRaw(char* buffer, const int count)
{
if (m_pendingLen != 0)
{
const int copyLen =
(count >= m_pendingLen ? m_pendingLen : count);
std::copy(m_pendingBuffer + m_pendingPos,
m_pendingBuffer + m_pendingPos + copyLen,
buffer);
m_pendingLen -= copyLen;
m_pendingPos += copyLen;
if (m_pendingLen == 0)
{
delete [] m_pendingBuffer;
m_pendingBuffer = 0;
m_pendingPos = 0;
m_pendingLen = 0;
}
return copyLen;
}
const int n = m_wrapped->receiveRaw(buffer, count);
byte* output = 0;
int outputLen = 0;
m_session->getMechanism()->decode
(m_session, reinterpret_cast <const byte*>(buffer), n,
&output, &outputLen);
// If we can not copy all decoded data into the output buffer, put
// remaining data into a pending buffer for next calls to receive()
if (outputLen > count)
{
std::copy(output, output + count, buffer);
m_pendingBuffer = output;
m_pendingLen = outputLen;
m_pendingPos = count;
return count;
}
else
{
std::copy(output, output + outputLen, buffer);
delete [] output;
return outputLen;
}
}
void SASLSocket::send(const string& buffer)
{
sendRaw(buffer.data(), buffer.length());
}
void SASLSocket::sendRaw(const char* buffer, const int count)
{
byte* output = 0;
int outputLen = 0;
m_session->getMechanism()->encode
(m_session, reinterpret_cast <const byte*>(buffer), count,
&output, &outputLen);
try
{
m_wrapped->sendRaw
(reinterpret_cast <const char*>(output), outputLen);
}
catch (...)
{
delete [] output;
throw;
}
delete [] output;
}
} // sasl
} // security
} // vmime

View File

@ -0,0 +1,182 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <gsasl.h>
#include "vmime/security/sasl/builtinSASLMechanism.hpp"
#include "vmime/security/sasl/SASLContext.hpp"
#include "vmime/security/sasl/SASLSession.hpp"
#include "vmime/exception.hpp"
#include <stdexcept>
#include <new>
namespace vmime {
namespace security {
namespace sasl {
builtinSASLMechanism::builtinSASLMechanism(ref <SASLContext> ctx, const string& name)
: m_context(ctx), m_name(name), m_complete(false)
{
}
builtinSASLMechanism::~builtinSASLMechanism()
{
}
const string builtinSASLMechanism::getName() const
{
return m_name;
}
const bool builtinSASLMechanism::step
(ref <SASLSession> sess, const byte* challenge, const int challengeLen,
byte** response, int* responseLen)
{
char* output = 0;
size_t outputLen = 0;
const int result = gsasl_step(sess->m_gsaslSession,
reinterpret_cast <const char*>(challenge), challengeLen,
&output, &outputLen);
if (result == GSASL_OK || result == GSASL_NEEDS_MORE)
{
byte* res = new byte[outputLen];
for (size_t i = 0 ; i < outputLen ; ++i)
res[i] = output[i];
*response = res;
*responseLen = outputLen;
free(output);
}
else
{
*response = 0;
*responseLen = 0;
}
if (result == GSASL_OK)
{
// Authentication process completed
m_complete = true;
return true;
}
else if (result == GSASL_NEEDS_MORE)
{
// Continue authentication process
return false;
}
else if (result == GSASL_MALLOC_ERROR)
{
throw std::bad_alloc();
}
else
{
throw exceptions::sasl_exception("Error when processing challenge: "
+ SASLContext::getErrorMessage("gsasl_step", result));
}
}
const bool builtinSASLMechanism::isComplete() const
{
return m_complete;
}
void builtinSASLMechanism::encode
(ref <SASLSession> sess, const byte* input, const int inputLen,
byte** output, int* outputLen)
{
char* coutput = 0;
size_t coutputLen = 0;
if (gsasl_encode(sess->m_gsaslSession,
reinterpret_cast <const char*>(input), inputLen,
&coutput, &coutputLen) != GSASL_OK)
{
throw exceptions::sasl_exception("Encoding error.");
}
try
{
byte* res = new byte[coutputLen];
std::copy(coutput, coutput + coutputLen, res);
*output = res;
*outputLen = static_cast <int>(coutputLen);
}
catch (...)
{
free(coutput);
throw;
}
free(coutput);
}
void builtinSASLMechanism::decode
(ref <SASLSession> sess, const byte* input, const int inputLen,
byte** output, int* outputLen)
{
char* coutput = 0;
size_t coutputLen = 0;
try
{
if (gsasl_decode(sess->m_gsaslSession,
reinterpret_cast <const char*>(input), inputLen,
&coutput, &coutputLen) != GSASL_OK)
{
throw exceptions::sasl_exception("Decoding error.");
}
byte* res = new byte[coutputLen];
std::copy(coutput, coutput + coutputLen, res);
*output = res;
*outputLen = static_cast <int>(coutputLen);
}
catch (...)
{
free(coutput);
throw;
}
free(coutput);
}
} // sasl
} // security
} // vmime

View File

@ -0,0 +1,139 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "vmime/security/sasl/defaultSASLAuthenticator.hpp"
#include "vmime/security/sasl/SASLMechanism.hpp"
#include "vmime/security/sasl/SASLSession.hpp"
#include "vmime/net/service.hpp"
namespace vmime {
namespace security {
namespace sasl {
defaultSASLAuthenticator::defaultSASLAuthenticator()
{
}
defaultSASLAuthenticator::~defaultSASLAuthenticator()
{
}
const std::vector <ref <SASLMechanism> >
defaultSASLAuthenticator::getAcceptableMechanisms
(const std::vector <ref <SASLMechanism> >& available,
ref <SASLMechanism> suggested) const
{
if (suggested)
{
std::vector <ref <SASLMechanism> > res;
res.push_back(suggested);
for (unsigned int i = 0 ; i < available.size() ; ++i)
{
if (available[i]->getName() != suggested->getName())
res.push_back(available[i]);
}
return res;
}
else
{
return available;
}
}
const string defaultSASLAuthenticator::getUsername() const
{
return m_default.getUsername();
}
const string defaultSASLAuthenticator::getPassword() const
{
return m_default.getPassword();
}
const string defaultSASLAuthenticator::getHostname() const
{
return m_default.getHostname();
}
const string defaultSASLAuthenticator::getAnonymousToken() const
{
return m_default.getAnonymousToken();
}
const string defaultSASLAuthenticator::getServiceName() const
{
return m_saslSession->getServiceName();
}
void defaultSASLAuthenticator::setService(ref <net::service> serv)
{
m_service = serv;
m_default.setService(serv);
}
weak_ref <net::service> defaultSASLAuthenticator::getService() const
{
return m_service;
}
void defaultSASLAuthenticator::setSASLSession(ref <SASLSession> sess)
{
m_saslSession = sess;
}
ref <SASLSession> defaultSASLAuthenticator::getSASLSession() const
{
return m_saslSession;
}
void defaultSASLAuthenticator::setSASLMechanism(ref <SASLMechanism> mech)
{
m_saslMech = mech;
}
ref <SASLMechanism> defaultSASLAuthenticator::getSASLMechanism() const
{
return m_saslMech;
}
} // sasl
} // security
} // vmime

View File

@ -326,6 +326,67 @@ const stream::size_type inputStreamPointerAdapter::skip(const size_type count)
}
// inputStreamByteBufferAdapter
inputStreamByteBufferAdapter::inputStreamByteBufferAdapter(const byte* buffer, const size_type length)
: m_buffer(buffer), m_length(length), m_pos(0)
{
}
const bool inputStreamByteBufferAdapter::eof() const
{
return m_pos >= m_length;
}
void inputStreamByteBufferAdapter::reset()
{
m_pos = 0;
}
const stream::size_type inputStreamByteBufferAdapter::read
(value_type* const data, const size_type count)
{
const size_type remaining = m_length - m_pos;
if (remaining < count)
{
std::copy(m_buffer + m_pos, m_buffer + m_pos + remaining, data);
m_pos += remaining;
return remaining;
}
else
{
std::copy(m_buffer + m_pos, m_buffer + m_pos + count, data);
m_pos += count;
return count;
}
}
const stream::size_type inputStreamByteBufferAdapter::skip(const size_type count)
{
const size_type remaining = m_length - m_pos;
if (remaining < count)
{
m_pos += remaining;
return remaining;
}
else
{
m_pos += count;
return count;
}
}
#ifdef VMIME_HAVE_MESSAGING_FEATURES

View File

@ -811,6 +811,58 @@ public:
#endif // VMIME_HAVE_FILESYSTEM_FEATURES
#if VMIME_HAVE_SASL_SUPPORT
/** Base class for exceptions throw by SASL module.
*/
class sasl_exception : public vmime::exception
{
public:
sasl_exception(const string& what, const exception& other = NO_EXCEPTION);
~sasl_exception() throw();
exception* clone() const;
const char* name() const throw();
};
/** No mechanism is registered with the specified name.
*/
class no_such_mechanism : public sasl_exception
{
public:
no_such_mechanism(const string& name, const exception& other = NO_EXCEPTION);
~no_such_mechanism() throw();
exception* clone() const;
const char* name() const throw();
};
/** The requested information cannot be provided.
*/
class no_auth_information : public sasl_exception
{
public:
no_auth_information(const exception& other = NO_EXCEPTION);
~no_auth_information() throw();
exception* clone() const;
const char* name() const throw();
};
#endif // VMIME_HAVE_SASL_SUPPORT
} // exceptions

View File

@ -1,54 +0,0 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_NET_AUTHENTICATOR_HPP_INCLUDED
#define VMIME_NET_AUTHENTICATOR_HPP_INCLUDED
#include "vmime/types.hpp"
#include "vmime/net/authenticationInfos.hpp"
namespace vmime {
namespace net {
/** This class is used to obtain user credentials.
*/
class authenticator : public object
{
public:
virtual ~authenticator();
/** Called when the service needs to retrieve user credentials.
* It should return the user account name and password.
*
* @return user credentials (user name and password)
*/
virtual const authenticationInfos requestAuthInfos() const = 0;
};
} // net
} // vmime
#endif // VMIME_NET_AUTHENTICATOR_HPP_INCLUDED

View File

@ -23,13 +23,14 @@
#include "vmime/config.hpp"
#include "vmime/net/authenticator.hpp"
#include "vmime/net/socket.hpp"
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/net/session.hpp"
#include "vmime/net/imap/IMAPParser.hpp"
#include "vmime/security/authenticator.hpp"
namespace vmime {
namespace net {
@ -44,7 +45,7 @@ class IMAPConnection : public object
{
public:
IMAPConnection(weak_ref <IMAPStore> store, ref <authenticator> auth);
IMAPConnection(weak_ref <IMAPStore> store, ref <security::authenticator> auth);
~IMAPConnection();
@ -83,11 +84,21 @@ public:
ref <session> getSession();
const std::vector <string> getCapabilities();
ref <security::authenticator> getAuthenticator();
private:
void authenticate();
#if VMIME_HAVE_SASL_SUPPORT
void authenticateSASL();
#endif // VMIME_HAVE_SASL_SUPPORT
weak_ref <IMAPStore> m_store;
ref <authenticator> m_auth;
ref <security::authenticator> m_auth;
ref <socket> m_socket;

View File

@ -1401,22 +1401,32 @@ public:
string::size_type pos = *currentPos;
parser.check <one_char <'\\'> >(line, &pos);
if (parser.check <one_char <'\\'> >(line, &pos, true))
{
atom* at = parser.get <atom>(line, &pos);
const string name = utility::stringUtils::toLower(at->value());
delete (at);
atom* at = parser.get <atom>(line, &pos);
const string name = utility::stringUtils::toLower(at->value());
delete (at);
if (name == "marked")
m_type = MARKED;
else if (name == "noinferiors")
m_type = NOINFERIORS;
else if (name == "noselect")
m_type = NOSELECT;
else if (name == "unmarked")
m_type = UNMARKED;
if (name == "marked")
m_type = MARKED;
else if (name == "noinferiors")
m_type = NOINFERIORS;
else if (name == "noselect")
m_type = NOSELECT;
else if (name == "unmarked")
m_type = UNMARKED;
else
{
m_type = UNKNOWN;
m_name = "\\" + name;
}
}
else
{
atom* at = parser.get <atom>(line, &pos);
const string name = utility::stringUtils::toLower(at->value());
delete (at);
m_type = UNKNOWN;
m_name = name;
}
@ -1989,40 +1999,13 @@ public:
string::size_type pos = *currentPos;
parser.checkWithArg <special_atom>(line, &pos, "capability");
parser.check <SPACE>(line, &pos);
bool IMAP4rev1 = false;
for (bool end = false ; !end && !IMAP4rev1 ; )
while (parser.check <SPACE>(line, &pos, true))
{
if (parser.checkWithArg <special_atom>(line, &pos, "imap4rev1", true))
{
IMAP4rev1 = true;
}
else
{
capability* cap = parser.get <capability>(line, &pos);
end = (cap == NULL);
capability* cap = parser.get <capability>(line, &pos);
if (cap == NULL) break;
if (cap)
{
m_capabilities.push_back(cap);
}
}
parser.check <SPACE>(line, &pos);
}
if (parser.check <SPACE>(line, &pos, true))
{
for (capability* cap = NULL ;
(cap = parser.get <capability>(line, &pos)) != NULL ; )
{
m_capabilities.push_back(cap);
parser.check <SPACE>(line, &pos);
}
m_capabilities.push_back(cap);
}
*currentPos = pos;

View File

@ -52,7 +52,7 @@ class IMAPStore : public store
public:
IMAPStore(ref <session> sess, ref <authenticator> auth);
IMAPStore(ref <session> sess, ref <security::authenticator> auth);
~IMAPStore();
const string getProtocolName() const;
@ -79,13 +79,6 @@ private:
// Connection
ref <IMAPConnection> m_connection;
// Used to request the authentication informations only the
// first time, and reuse these informations the next time.
ref <class authenticator> m_oneTimeAuth;
ref <class authenticator> oneTimeAuthenticator();
ref <IMAPConnection> connection();
@ -106,7 +99,10 @@ private:
struct props
{
// IMAP-specific options
// (none)
#if VMIME_HAVE_SASL_SUPPORT
serviceInfos::property PROPERTY_OPTIONS_SASL;
serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK;
#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
serviceInfos::property PROPERTY_AUTH_USERNAME;

View File

@ -49,7 +49,7 @@ class maildirStore : public store
public:
maildirStore(ref <session> sess, ref <authenticator> auth);
maildirStore(ref <session> sess, ref <security::authenticator> auth);
~maildirStore();
const string getProtocolName() const;

View File

@ -48,7 +48,7 @@ class POP3Store : public store
public:
POP3Store(ref <session> sess, ref <authenticator> auth);
POP3Store(ref <session> sess, ref <security::authenticator> auth);
~POP3Store();
const string getProtocolName() const;
@ -72,9 +72,24 @@ public:
private:
enum ResponseCode
{
RESPONSE_OK = 0,
RESPONSE_READY,
RESPONSE_ERR
};
void authenticate(const messageId& randomMID);
#if VMIME_HAVE_SASL_SUPPORT
void authenticateSASL();
#endif // VMIME_HAVE_SASL_SUPPORT
const std::vector <string> getCapabilities();
static const bool isSuccessResponse(const string& buffer);
static const bool stripFirstLine(const string& buffer, string& result, string* firstLine = NULL);
static void stripResponseCode(const string& buffer, string& result);
static const int getResponseCode(const string& buffer);
void sendRequest(const string& buffer, const bool end = true);
void readResponse(string& buffer, const bool multiLine, utility::progressionListener* progress = NULL);
@ -108,6 +123,10 @@ private:
// POP3-specific options
serviceInfos::property PROPERTY_OPTIONS_APOP;
serviceInfos::property PROPERTY_OPTIONS_APOP_FALLBACK;
#if VMIME_HAVE_SASL_SUPPORT
serviceInfos::property PROPERTY_OPTIONS_SASL;
serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK;
#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
serviceInfos::property PROPERTY_AUTH_USERNAME;

View File

@ -43,7 +43,7 @@ class sendmailTransport : public transport
{
public:
sendmailTransport(ref <session> sess, ref <authenticator> auth);
sendmailTransport(ref <session> sess, ref <security::authenticator> auth);
~sendmailTransport();
const string getProtocolName() const;

View File

@ -24,7 +24,6 @@
#include "vmime/types.hpp"
#include "vmime/net/session.hpp"
#include "vmime/net/authenticator.hpp"
#include "vmime/net/serviceFactory.hpp"
#include "vmime/net/serviceInfos.hpp"
@ -43,7 +42,7 @@ class service : public object
{
protected:
service(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth);
service(ref <session> sess, const serviceInfos& infos, ref <security::authenticator> auth);
public:
@ -110,13 +109,19 @@ public:
*
* @return authenticator object
*/
ref <const authenticator> getAuthenticator() const;
ref <const security::authenticator> getAuthenticator() const;
/** Return the authenticator object used with this service instance.
*
* @return authenticator object
*/
ref <authenticator> getAuthenticator();
ref <security::authenticator> getAuthenticator();
/** Set the authenticator object used with this service instance.
*
* @param auth authenticator object
*/
void setAuthenticator(ref <security::authenticator> auth);
/** Set a property for this service (service prefix is added automatically).
*
@ -150,7 +155,7 @@ public:
private:
ref <session> m_session;
ref <authenticator> m_auth;
ref <security::authenticator> m_auth;
};

View File

@ -30,9 +30,10 @@
#include "vmime/utility/url.hpp"
#include "vmime/net/serviceInfos.hpp"
#include "vmime/net/authenticator.hpp"
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/security/authenticator.hpp"
#include "vmime/utility/progressionListener.hpp"
@ -69,7 +70,9 @@ public:
public:
virtual ref <service> create(ref <session> sess, ref <authenticator> auth) const = 0;
virtual ref <service> create
(ref <session> sess,
ref <security::authenticator> auth) const = 0;
virtual const string& getName() const = 0;
virtual const serviceInfos& getInfos() const = 0;
@ -92,7 +95,9 @@ private:
public:
ref <service> create(ref <session> sess, ref <authenticator> auth) const
ref <service> create
(ref <session> sess,
ref <security::authenticator> auth) const
{
return vmime::create <S>(sess, auth);
}
@ -137,7 +142,10 @@ public:
* @throw exceptions::no_service_available if no service is registered
* for this protocol
*/
ref <service> create(ref <session> sess, const string& protocol, ref <authenticator> auth = NULL);
ref <service> create
(ref <session> sess,
const string& protocol,
ref <security::authenticator> auth = NULL);
/** Create a new service instance from a URL.
*
@ -149,7 +157,10 @@ public:
* @throw exceptions::no_service_available if no service is registered
* for this protocol
*/
ref <service> create(ref <session> sess, const utility::url& u, ref <authenticator> auth = NULL);
ref <service> create
(ref <session> sess,
const utility::url& u,
ref <security::authenticator> auth = NULL);
/** Return information about a registered protocol.
*

View File

@ -21,7 +21,7 @@
#define VMIME_NET_SESSION_HPP_INCLUDED
#include "vmime/net/authenticator.hpp"
#include "vmime/security/authenticator.hpp"
#include "vmime/utility/url.hpp"
@ -60,7 +60,8 @@ public:
* credentials by reading the session properties "auth.username" and "auth.password".
* @return a new transport service
*/
ref <transport> getTransport(ref <authenticator> auth = NULL);
ref <transport> getTransport
(ref <security::authenticator> auth = NULL);
/** Return a transport service instance for the specified protocol.
*
@ -70,7 +71,9 @@ public:
* credentials by reading the session properties "auth.username" and "auth.password".
* @return a new transport service
*/
ref <transport> getTransport(const string& protocol, ref <authenticator> auth = NULL);
ref <transport> getTransport
(const string& protocol,
ref <security::authenticator> auth = NULL);
/** Return a transport service instance for the specified URL.
*
@ -80,7 +83,9 @@ public:
* credentials by reading the session properties "auth.username" and "auth.password".
* @return a new transport service
*/
ref <transport> getTransport(const utility::url& url, ref <authenticator> auth = NULL);
ref <transport> getTransport
(const utility::url& url,
ref <security::authenticator> auth = NULL);
/** Return a transport service instance for the protocol specified
* in the session properties.
@ -92,7 +97,7 @@ public:
* credentials by reading the session properties "auth.username" and "auth.password".
* @return a new store service
*/
ref <store> getStore(ref <authenticator> auth = NULL);
ref <store> getStore(ref <security::authenticator> auth = NULL);
/** Return a store service instance for the specified protocol.
*
@ -102,7 +107,9 @@ public:
* credentials by reading the session properties "auth.username" and "auth.password".
* @return a new store service
*/
ref <store> getStore(const string& protocol, ref <authenticator> auth = NULL);
ref <store> getStore
(const string& protocol,
ref <security::authenticator> auth = NULL);
/** Return a store service instance for the specified URL.
*
@ -112,7 +119,9 @@ public:
* credentials by reading the session properties "auth.username" and "auth.password".
* @return a new store service
*/
ref <store> getStore(const utility::url& url, ref <authenticator> auth = NULL);
ref <store> getStore
(const utility::url& url,
ref <security::authenticator> auth = NULL);
/** Properties for the session and for the services.
*/

View File

@ -1,62 +0,0 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_NET_SIMPLEAUTHENTICATOR_HPP_INCLUDED
#define VMIME_NET_SIMPLEAUTHENTICATOR_HPP_INCLUDED
#include "vmime/net/authenticator.hpp"
namespace vmime {
namespace net {
/** Basic implementation for an authenticator.
*/
class simpleAuthenticator : public authenticator
{
public:
simpleAuthenticator();
simpleAuthenticator(const string& username, const string& password);
public:
const string& getUsername() const;
void setUsername(const string& username);
const string& getPassword() const;
void setPassword(const string& password);
const authenticationInfos requestAuthInfos() const;
private:
string m_username;
string m_password;
};
} // net
} // vmime
#endif // VMIME_NET_SIMPLEAUTHENTICATOR_HPP_INCLUDED

View File

@ -40,7 +40,7 @@ class SMTPTransport : public transport
{
public:
SMTPTransport(ref <session> sess, ref <authenticator> auth);
SMTPTransport(ref <session> sess, ref <security::authenticator> auth);
~SMTPTransport();
const string getProtocolName() const;
@ -58,18 +58,30 @@ public:
private:
static const int responseCode(const string& response);
static const string responseText(const string& response);
static const int getResponseCode(const string& response);
void sendRequest(const string& buffer, const bool end = true);
void readResponse(string& buffer);
const string readResponseLine();
const int readResponse(string& text);
const int readAllResponses(string& text, const bool allText = false);
void internalDisconnect();
void authenticate();
#if VMIME_HAVE_SASL_SUPPORT
void authenticateSASL();
#endif // VMIME_HAVE_SASL_SUPPORT
ref <socket> m_socket;
bool m_authentified;
bool m_extendedSMTP;
string m_extendedSMTPResponse;
string m_responseBuffer;
bool m_responseContinues;
ref <timeoutHandler> m_timeoutHandler;
@ -83,6 +95,10 @@ private:
{
// SMTP-specific options
serviceInfos::property PROPERTY_OPTIONS_NEEDAUTH;
#if VMIME_HAVE_SASL_SUPPORT
serviceInfos::property PROPERTY_OPTIONS_SASL;
serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK;
#endif // VMIME_HAVE_SASL_SUPPORT
// Common properties
serviceInfos::property PROPERTY_AUTH_USERNAME;

View File

@ -37,7 +37,7 @@ class store : public service
{
protected:
store(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth)
store(ref <session> sess, const serviceInfos& infos, ref <security::authenticator> auth)
: service(sess, infos, auth) { }
public:

View File

@ -42,7 +42,7 @@ class transport : public service
{
protected:
transport(ref <session> sess, const serviceInfos& infos, ref <authenticator> auth);
transport(ref <session> sess, const serviceInfos& infos, ref <security::authenticator> auth);
public:

View File

@ -0,0 +1,116 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_SECURITY_AUTHENTICATOR_HPP_INCLUDED
#define VMIME_SECURITY_AUTHENTICATOR_HPP_INCLUDED
#include "vmime/types.hpp"
// Forward declarations
namespace vmime {
namespace net {
class service;
} // net
} // vmime
namespace vmime {
namespace security {
/** Provides required information for user authentication. The same
* information can be requested multiple time (eg. in IMAP, there is a
* new connection started each time a folder is open), so the object is
* responsible for caching the information to avoid useless interactions
* with the user.
*
* Usually, you should not inherit from this class, but instead from the
* more convenient defaultAuthenticator class.
*
* WARNING: an authenticator should be used with one and ONLY ONE messaging
* service at a time.
*/
class authenticator : public object
{
public:
/** Return the authentication identity (usually, this
* is the username).
*
* @return username
* @throw exceptions::no_auth_information if the information
* could not be provided
*/
virtual const string getUsername() const = 0;
/** Return the password of the authentication identity.
*
* @return password
* @throw exceptions::no_auth_information if the information
* could not be provided
*/
virtual const string getPassword() const = 0;
/** Return the local host name of the machine.
*
* @return hostname
* @throw exceptions::no_auth_information if the information
* could not be provided
*/
virtual const string getHostname() const = 0;
/** Return the anonymous token (usually, this is the user's
* email address).
*
* @return anonymous token
* @throw exceptions::no_auth_information if the information
* could not be provided
*/
virtual const string getAnonymousToken() const = 0;
/** Return the registered service name of the application
* service (eg: "imap"). This can be used by GSSAPI or DIGEST-MD5
* mechanisms with SASL.
*
* @return service name
* @throw exceptions::no_auth_information if the information
* could not be provided
*/
virtual const string getServiceName() const = 0;
/** Called by the messaging service to allow this authenticator to
* know which service is currently using it. This is called just
* before the service starts the authentication process.
*
* @param serv messaging service instance
*/
virtual void setService(ref <net::service> serv) = 0;
};
} // security
} // vmime
#endif // VMIME_SECURITY_AUTHENTICATOR_HPP_INCLUDED

View File

@ -17,44 +17,45 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_NET_DEFAULTAUTHENTICATOR_HPP_INCLUDED
#define VMIME_NET_DEFAULTAUTHENTICATOR_HPP_INCLUDED
#ifndef VMIME_SECURITY_DEFAULTAUTHENTICATOR_HPP_INCLUDED
#define VMIME_SECURITY_DEFAULTAUTHENTICATOR_HPP_INCLUDED
#include "vmime/net/authenticator.hpp"
#include "vmime/propertySet.hpp"
#include "vmime/security/authenticator.hpp"
namespace vmime {
namespace net {
namespace security {
class session;
/** Default implementation for authenticator. It simply returns
* the credentials set in the session properties (named 'username'
* and 'password'). This is the default implementation used if
* you do not write your own authenticator object.
/** An authenticator that can provide some basic information by
* reading in the messaging session properties.
*/
class defaultAuthenticator : public authenticator
{
public:
defaultAuthenticator(weak_ref <session> session, const string& prefix);
defaultAuthenticator();
~defaultAuthenticator();
const authenticationInfos requestAuthInfos() const;
const string getUsername() const;
const string getPassword() const;
const string getHostname() const;
const string getAnonymousToken() const;
const string getServiceName() const;
void setService(ref <net::service> serv);
weak_ref <net::service> getService() const;
private:
weak_ref <session> m_session;
const string m_prefix;
weak_ref <net::service> m_service;
};
} // net
} // security
} // vmime
#endif // VMIME_NET_DEFAULTAUTHENTICATOR_HPP_INCLUDED
#endif // VMIME_SECURITY_DEFAULTAUTHENTICATOR_HPP_INCLUDED

View File

@ -0,0 +1,84 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_SECURITY_SASL_SASLAUTHENTICATOR_HPP_INCLUDED
#define VMIME_SECURITY_SASL_SASLAUTHENTICATOR_HPP_INCLUDED
#include "vmime/types.hpp"
#include "vmime/security/authenticator.hpp"
namespace vmime {
namespace security {
namespace sasl {
class SASLMechanism;
class SASLSession;
/** SASL-aware authenticator.
*
* Usually, you should not inherit from this class, but instead from the
* more convenient defaultSASLAuthenticator class.
*/
class SASLAuthenticator : public authenticator
{
public:
/** This method is called to allow the client to choose the
* authentication mechanisms that will be used. By default,
* the more secure mechanisms are chosen.
*
* @param available available mechanisms
* @param suggested suggested mechanism (or NULL if the system
* could not suggest a mechanism)
* @return ordered list of mechanism to use among the available
* mechanisms (from the first to try to the last)
*/
virtual const std::vector <ref <SASLMechanism> > getAcceptableMechanisms
(const std::vector <ref <SASLMechanism> >& available,
ref <SASLMechanism> suggested) const = 0;
/** Set the SASL session which is using this authenticator.
*
* @param sess SASL session
*/
virtual void setSASLSession(ref <SASLSession> sess) = 0;
/** Set the SASL mechanism which has been selected for the
* SASL authentication process. This may be called several times
* if the multiple mechanisms are tried by the service which
* use this authentication.
*
* @param mech SASL mechanism
*/
virtual void setSASLMechanism(ref <SASLMechanism> mech) = 0;
};
} // sasl
} // security
} // vmime
#endif // VMIME_SECURITY_SASL_SASLAUTHENTICATOR_HPP_INCLUDED

View File

@ -0,0 +1,116 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_SECURITY_SASL_SASLCONTEXT_HPP_INCLUDED
#define VMIME_SECURITY_SASL_SASLCONTEXT_HPP_INCLUDED
#include "vmime/types.hpp"
#include "vmime/security/sasl/SASLSession.hpp"
#include "vmime/security/sasl/SASLMechanismFactory.hpp"
namespace vmime {
namespace security {
namespace sasl {
/** An SASL client context.
*/
class SASLContext : public object
{
friend class SASLSession;
friend class builtinSASLMechanism;
public:
~SASLContext();
/** Construct and initialize a new SASL context.
*/
SASLContext();
/** Create and initialize a new SASL session.
*
* @param serviceName name of the service which will use the session
* @param auth authenticator object to use during the session
* @param mech SASL mechanism
* @return a new SASL session
*/
ref <SASLSession> createSession
(const string& serviceName,
ref <authenticator> auth, ref <SASLMechanism> mech);
/** Create an instance of an SASL mechanism.
*
* @param name mechanism name
* @return a new instance of the specified SASL mechanism
* @throw exceptions::no_such_mechanism if no mechanism is
* registered for the specified name
*/
ref <SASLMechanism> createMechanism(const string& name);
/** Suggests an SASL mechanism among a set of mechanisms
* supported by the server.
*
* @param mechs list of mechanisms
* @return suggested mechanism (usually the safest mechanism
* supported by both the client and the server)
*/
ref <SASLMechanism> suggestMechanism
(const std::vector <ref <SASLMechanism> >& mechs);
/** Helper function for decoding Base64-encoded challenge.
*
* @param input input buffer
* @param output output buffer
* @param outputLen length of output buffer
*/
void decodeB64(const string& input, byte** output, int* outputLen);
/** Helper function for encoding challenge in Base64.
*
* @param input input buffer
* @param inputLen length of input buffer
* @return Base64-encoded challenge
*/
const string encodeB64(const byte* input, const int inputLen);
private:
static const string getErrorMessage(const string& fname, const int code);
#ifdef GSASL_VERSION
Gsasl* m_gsaslContext;
#else
void* m_gsaslContext;
#endif // GSASL_VERSION
};
} // sasl
} // security
} // vmime
#endif // VMIME_SECURITY_SASL_SASLCONTEXT_HPP_INCLUDED

View File

@ -0,0 +1,119 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_SECURITY_SASL_SASLMECHANISM_HPP_INCLUDED
#define VMIME_SECURITY_SASL_SASLMECHANISM_HPP_INCLUDED
#include "vmime/types.hpp"
namespace vmime {
namespace security {
namespace sasl {
class SASLSession;
/** An SASL mechanism.
*/
class SASLMechanism : public object
{
public:
/** Return the name of this mechanism.
*
* @return mechanism name
*/
virtual const string getName() const = 0;
/** Perform one step of SASL authentication. Accept data from the
* server (challenge), process it and return data to be returned
* in response to the server.
*
* @param sess SASL session
* @param challenge challenge sent from the server
* @param challengeLen length of challenge
* @param response response to send to the server (allocated by
* this function, free with delete[])
* @param responseLen length of response buffer
* @return true if authentication terminated successfully, or
* false if the authentication process should continue
* @throw exceptions::sasl_exception if an error occured during
* authentication (in this case, the values in 'response' and
* 'responseLen' are undetermined)
*/
virtual const bool step
(ref <SASLSession> sess,
const byte* challenge, const int challengeLen,
byte** response, int* responseLen) = 0;
/** Check whether authentication has completed. If false, more
* calls to evaluateChallenge() are needed to complete the
* authentication process).
*
* @return true if the authentication has finished, or false
* otherwise
*/
virtual const bool isComplete() const = 0;
/** Encode data according to negotiated SASL mechanism. This
* might mean that data is integrity or privacy protected.
*
* @param sess SASL session
* @param input input buffer
* @param inputLen length of input buffer
* @param output output buffer (allocated bu the function,
* free with delete[])
* @param outputLen length of output buffer
* @throw exceptions::sasl_exception if an error occured during
* the encoding of data (in this case, the values in 'output' and
* 'outputLen' are undetermined)
*/
virtual void encode(ref <SASLSession> sess,
const byte* input, const int inputLen,
byte** output, int* outputLen) = 0;
/** Decode data according to negotiated SASL mechanism. This
* might mean that data is integrity or privacy protected.
*
* @param sess SASL session
* @param input input buffer
* @param inputLen length of input buffer
* @param output output buffer (allocated bu the function,
* free with delete[])
* @param outputLen length of output buffer
* @throw exceptions::sasl_exception if an error occured during
* the encoding of data (in this case, the values in 'output' and
* 'outputLen' are undetermined)
*/
virtual void decode(ref <SASLSession> sess,
const byte* input, const int inputLen,
byte** output, int* outputLen) = 0;
};
} // sasl
} // security
} // vmime
#endif // VMIME_SECURITY_SASL_SASLMECHANISM_HPP_INCLUDED

View File

@ -0,0 +1,131 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_SECURITY_SASL_SASLMECHANISMFACTORY_HPP_INCLUDED
#define VMIME_SECURITY_SASL_SASLMECHANISMFACTORY_HPP_INCLUDED
#include "vmime/types.hpp"
#include "vmime/base.hpp"
#include "vmime/security/sasl/SASLMechanism.hpp"
#include <map>
namespace vmime {
namespace security {
namespace sasl {
class SASLContext;
/** Constructs SASL mechanism objects.
*/
class SASLMechanismFactory : public object
{
private:
SASLMechanismFactory();
~SASLMechanismFactory();
class registeredMechanism : public object
{
public:
virtual ref <SASLMechanism> create
(ref <SASLContext> ctx, const string& name) = 0;
};
template <typename T>
class registeredMechanismImpl : public registeredMechanism
{
public:
ref <SASLMechanism> create(ref <SASLContext> ctx, const string& name)
{
return vmime::create <T>(ctx, name);
}
};
typedef std::map <string, ref <registeredMechanism> > MapType;
MapType m_mechs;
public:
static SASLMechanismFactory* getInstance();
/** Register a mechanism into this factory, so that subsequent
* calls to create return a valid object for this mechanism.
*
* @param name mechanism name
*/
template <typename MECH_CLASS>
void registerMechanism(const string& name)
{
m_mechs.insert(MapType::value_type(name,
vmime::create <registeredMechanismImpl <MECH_CLASS> >()));
}
/** Create a mechanism object given its name.
*
* @param ctx SASL context
* @param name mechanism name
* @return a new mechanism object
* @throw exceptions::no_such_mechanism if no mechanism is
* registered for the specified name
*/
ref <SASLMechanism> create(ref <SASLContext> ctx, const string& name);
/** Return a list of supported mechanisms. This includes mechanisms
* registered using registerMechanism() as well as the ones that
* are built-in.
*
* @return list of supported mechanisms
*/
const std::vector <string> getSupportedMechanisms() const;
/** Test whether an authentication mechanism is supported.
*
* @param name mechanism name
* @return true if the specified mechanism is supported,
* false otherwise
*/
const bool isMechanismSupported(const string& name) const;
private:
#ifdef GSASL_VERSION
Gsasl* m_gsaslContext;
#else
void* m_gsaslContext;
#endif // GSASL_VERSION
};
} // sasl
} // security
} // vmime
#endif // VMIME_SECURITY_SASL_SASLMECHANISMFACTORY_HPP_INCLUDED

View File

@ -0,0 +1,150 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_SECURITY_SASL_SASLSESSION_HPP_INCLUDED
#define VMIME_SECURITY_SASL_SASLSESSION_HPP_INCLUDED
#include "vmime/types.hpp"
#include "vmime/security/sasl/SASLAuthenticator.hpp"
#include "vmime/security/sasl/SASLMechanism.hpp"
#include "vmime/security/sasl/SASLSocket.hpp"
namespace vmime {
namespace security {
namespace sasl {
class SASLContext;
/** An SASL client session.
*/
class SASLSession : public object
{
friend class builtinSASLMechanism;
friend class SASLSocket;
public:
~SASLSession();
/** Construct a new SASL session.
*
* @param serviceName name of the service using this session
* @param ctx SASL context
* @param auth authenticator to use for this session
* @param mech SASL mechanism
*/
SASLSession(const string& serviceName, ref <SASLContext> ctx,
ref <authenticator> auth, ref <SASLMechanism> mech);
/** Initialize this SASL session. This must be called before
* calling any other method on this object (except accessors).
*/
void init();
/** Return the authenticator used for this session. This is the
* authenticator which has been previously set with a call to
* setAuthenticator().
*
* @return authenticator object
*/
ref <authenticator> getAuthenticator();
/** Return the mechanism used for this session.
*
* @return SASL mechanism
*/
ref <SASLMechanism> getMechanism();
/** Return the SASL context.
*
* @return SASL context
*/
ref <SASLContext> getContext();
/** Perform one step of SASL authentication. Accept data from the
* server (challenge), process it and return data to be returned
* in response to the server.
*
* @param challenge challenge sent from the server
* @param challengeLen length of challenge
* @param response response to send to the server (allocated by
* this function, free with delete[])
* @param responseLen length of response buffer
* @return true if authentication terminated successfully, or
* false if the authentication process should continue
* @throw exceptions::sasl_exception if an error occured during
* authentication (in this case, the values in 'response' and
* 'responseLen' are undetermined)
*/
const bool evaluateChallenge
(const byte* challenge, const int challengeLen,
byte** response, int* responseLen);
/** Return a socket in which transmitted data is integrity
* and/or privacy protected, depending on the QOP (Quality of
* Protection) negotiated during the SASL authentication.
*
* @param sok socket to wrap
* @return secured socket
*/
ref <net::socket> getSecuredSocket(ref <net::socket> sok);
/** Return the name of the service which is using this
* SASL session (eg. "imap"). This value should be returned
* by the authenticator when INFO_SERVICE is requested.
*
* @return service name
*/
const string getServiceName() const;
private:
const string m_serviceName;
ref <SASLContext> m_context;
ref <authenticator> m_auth;
ref <SASLMechanism> m_mech;
#ifdef GSASL_VERSION
Gsasl* m_gsaslContext;
Gsasl_session* m_gsaslSession;
static int gsaslCallback(Gsasl* ctx, Gsasl_session* sctx, Gsasl_property prop);
#else
void* m_gsaslContext;
void* m_gsaslSession;
static int gsaslCallback(void* ctx, void* sctx, int prop);
#endif // GSASL_VERSION
};
} // sasl
} // security
} // vmime
#endif // VMIME_SECURITY_SASL_SASLSESSION_HPP_INCLUDED

View File

@ -17,48 +17,60 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_NET_AUTHENTICATIONINFOS_HPP_INCLUDED
#define VMIME_NET_AUTHENTICATIONINFOS_HPP_INCLUDED
#ifndef VMIME_SECURITY_SASL_SASLSOCKET_HPP_INCLUDED
#define VMIME_SECURITY_SASL_SASLSOCKET_HPP_INCLUDED
#include "vmime/types.hpp"
#include "vmime/net/socket.hpp"
namespace vmime {
namespace net {
namespace security {
namespace sasl {
/** This class encapsulates user credentials.
class SASLSession;
/** A socket which provides data integrity and/or privacy protection.
*/
class authenticationInfos : public object
class SASLSocket : public net::socket
{
public:
authenticationInfos(const string& username, const string& password);
authenticationInfos(const authenticationInfos& infos);
SASLSocket(ref <SASLSession> sess, ref <net::socket> wrapped);
~SASLSocket();
/** Return the user account name.
*
* @return account name
*/
const string& getUsername() const;
void connect(const string& address, const port_t port);
void disconnect();
/** Return the user account password.
*
* @return account password
*/
const string& getPassword() const;
const bool isConnected() const;
void receive(string& buffer);
const int receiveRaw(char* buffer, const int count);
void send(const string& buffer);
void sendRaw(const char* buffer, const int count);
private:
string m_username;
string m_password;
ref <SASLSession> m_session;
ref <net::socket> m_wrapped;
byte* m_pendingBuffer;
int m_pendingPos;
int m_pendingLen;
char m_recvBuffer[65536];
};
} // net
} // sasl
} // security
} // vmime
#endif // VMIME_NET_AUTHENTICATIONINFOS_HPP_INCLUDED
#endif // VMIME_SECURITY_SASL_SASLSOCKET_HPP_INCLUDED

View File

@ -0,0 +1,82 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_SECURITY_SASL_BUILTINSASLMECHANISM_HPP_INCLUDED
#define VMIME_SECURITY_SASL_BUILTINSASLMECHANISM_HPP_INCLUDED
#include "vmime/security/sasl/SASLMechanism.hpp"
namespace vmime {
namespace security {
namespace sasl {
class SASLContext;
/** A built-in authentication mechanism that relies on
* the GNU SASL library.
*/
class builtinSASLMechanism : public SASLMechanism
{
public:
builtinSASLMechanism(ref <SASLContext> ctx, const string& name);
~builtinSASLMechanism();
const string getName() const;
const bool step
(ref <SASLSession> sess,
const byte* challenge, const int challengeLen,
byte** response, int* responseLen);
const bool isComplete() const;
void encode(ref <SASLSession> sess,
const byte* input, const int inputLen,
byte** output, int* outputLen);
void decode(ref <SASLSession> sess,
const byte* input, const int inputLen,
byte** output, int* outputLen);
private:
/** SASL context */
ref <SASLContext> m_context;
/** Mechanism name */
const string m_name;
/** Authentication process status. */
bool m_complete;
};
} // sasl
} // security
} // vmime
#endif // VMIME_SECURITY_SASL_BUILTINSASLMECHANISM_HPP_INCLUDED

View File

@ -0,0 +1,80 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
//
// 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef VMIME_SECURITY_SASL_DEFAULTSASLAUTHENTICATOR_HPP_INCLUDED
#define VMIME_SECURITY_SASL_DEFAULTSASLAUTHENTICATOR_HPP_INCLUDED
#include "vmime/security/sasl/SASLAuthenticator.hpp"
#include "vmime/security/defaultAuthenticator.hpp"
namespace vmime {
namespace security {
namespace sasl {
/** An authenticator that is capable of providing information
* for simple authentication mechanisms (username and password).
*/
class defaultSASLAuthenticator : public SASLAuthenticator
{
public:
defaultSASLAuthenticator();
~defaultSASLAuthenticator();
const std::vector <ref <SASLMechanism> > getAcceptableMechanisms
(const std::vector <ref <SASLMechanism> >& available,
ref <SASLMechanism> suggested) const;
const string getUsername() const;
const string getPassword() const;
const string getHostname() const;
const string getAnonymousToken() const;
const string getServiceName() const;
void setService(ref <net::service> serv);
weak_ref <net::service> getService() const;
void setSASLSession(ref <SASLSession> sess);
ref <SASLSession> getSASLSession() const;
void setSASLMechanism(ref <SASLMechanism> mech);
ref <SASLMechanism> getSASLMechanism() const;
private:
defaultAuthenticator m_default;
weak_ref <net::service> m_service;
ref <SASLSession> m_saslSession;
ref <SASLMechanism> m_saslMech;
};
} // sasl
} // security
} // vmime
#endif // VMIME_SECURITY_SASL_DEFAULTSASLAUTHENTICATOR_HPP_INCLUDED

View File

@ -23,6 +23,7 @@
#include <limits>
#include <string>
#include <vector>
#include "vmime/config.hpp"
#include "vmime/utility/smartPtr.hpp"
@ -40,6 +41,7 @@ namespace vmime
typedef int char_t;
typedef vmime_uint8 byte;
typedef std::vector <byte> byteArray;
// Some aliases
namespace utils = utility;

View File

@ -326,6 +326,29 @@ private:
};
/** An adapter class for reading from an array of bytes.
*/
class inputStreamByteBufferAdapter : public inputStream
{
public:
inputStreamByteBufferAdapter(const byte* buffer, size_type length);
const bool eof() const;
void reset();
const size_type read(value_type* const data, const size_type count);
const size_type skip(const size_type count);
private:
const byte* m_buffer;
const size_type m_length;
size_type m_pos;
};
#if VMIME_HAVE_MESSAGING_FEATURES

View File

@ -86,6 +86,21 @@
#include "vmime/utility/datetimeUtils.hpp"
#include "vmime/utility/filteredStream.hpp"
// Security
#include "vmime/security/authenticator.hpp"
#include "vmime/security/defaultAuthenticator.hpp"
// Security/digest
#include "vmime/security/digest/messageDigestFactory.hpp"
// Security/SASL
#if VMIME_HAVE_SASL_SUPPORT
#include "vmime/security/sasl/SASLAuthenticator.hpp"
#include "vmime/security/sasl/defaultSASLAuthenticator.hpp"
#include "vmime/security/sasl/SASLContext.hpp"
#include "vmime/security/sasl/SASLSession.hpp"
#endif // VMIME_HAVE_SASL_SUPPORT
// Messaging features
#if VMIME_HAVE_MESSAGING_FEATURES
#include "vmime/net/socket.hpp"
@ -95,9 +110,6 @@
#include "vmime/net/transport.hpp"
#include "vmime/net/session.hpp"
#include "vmime/net/authenticator.hpp"
#include "vmime/net/defaultAuthenticator.hpp"
#include "vmime/net/simpleAuthenticator.hpp"
#include "vmime/net/folder.hpp"
#include "vmime/net/message.hpp"