From 10f9e6ece2602b50815963ebb011256a66f55566 Mon Sep 17 00:00:00 2001 From: Richard Steele Date: Tue, 7 Jan 2025 14:11:11 +0100 Subject: [PATCH] Implementation of initial responses with GNU SASL (resolves #319) --- src/vmime/net/imap/IMAPConnection.cpp | 16 ++++++++++-- .../security/sasl/builtinSASLMechanism.cpp | 25 ++++++++++++++++--- .../security/sasl/builtinSASLMechanism.hpp | 3 +++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/vmime/net/imap/IMAPConnection.cpp b/src/vmime/net/imap/IMAPConnection.cpp index 0e4020f1..f8113815 100644 --- a/src/vmime/net/imap/IMAPConnection.cpp +++ b/src/vmime/net/imap/IMAPConnection.cpp @@ -331,12 +331,24 @@ void IMAPConnection::authenticateSASL() { const std::vector capa = getCapabilities(); std::vector saslMechs; + bool saslIR = false; for (unsigned int i = 0 ; i < capa.size() ; ++i) { const string& x = capa[i]; - if (x.length() > 5 && + if (x.length() == 7 && + (x[0] == 'S' || x[0] == 's') && + (x[1] == 'A' || x[1] == 'a') && + (x[2] == 'S' || x[2] == 's') && + (x[3] == 'L' || x[3] == 'l') && + x[4] == '-' && + (x[5] == 'I' || x[5] == 'i') && + (x[6] == 'R' || x[6] == 'r')) { + + saslIR = true; + } + else if (x.length() > 5 && (x[0] == 'A' || x[0] == 'a') && (x[1] == 'U' || x[1] == 'u') && (x[2] == 'T' || x[2] == 't') && @@ -397,7 +409,7 @@ void IMAPConnection::authenticateSASL() { shared_ptr authCmd; - if (saslSession->getMechanism()->hasInitialResponse()) { + if (saslIR && saslSession->getMechanism()->hasInitialResponse()) { byte_t* initialResp = 0; size_t initialRespLen = 0; diff --git a/src/vmime/security/sasl/builtinSASLMechanism.cpp b/src/vmime/security/sasl/builtinSASLMechanism.cpp index 020cae95..170a2a8d 100644 --- a/src/vmime/security/sasl/builtinSASLMechanism.cpp +++ b/src/vmime/security/sasl/builtinSASLMechanism.cpp @@ -38,6 +38,25 @@ #include #include +#include + +namespace { + const std::unordered_set CLIENT_FIRST_MECHANISMS{ + "ANONYMOUS", + "EXTERNAL", + "GS2-KRB5", + "GSSAPI", + "LOGIN", + "NTLM", + "OPENID20", + "PLAIN", + "SAML20", + "SCRAM-SHA-1", + "SCRAM-SHA-256", + "SECURID" + }; + const auto CLIENT_FIRST_MECHANISMS_END = CLIENT_FIRST_MECHANISMS.cend(); +} namespace vmime { @@ -51,7 +70,8 @@ builtinSASLMechanism::builtinSASLMechanism( ) : m_context(ctx), m_name(name), - m_complete(false) { + m_complete(false), + m_initialResponse(CLIENT_FIRST_MECHANISMS.find(name) != CLIENT_FIRST_MECHANISMS_END) { } @@ -140,8 +160,7 @@ bool builtinSASLMechanism::isComplete() const { bool builtinSASLMechanism::hasInitialResponse() const { - // It seems GNU SASL does not support initial response - return false; + return m_initialResponse; } diff --git a/src/vmime/security/sasl/builtinSASLMechanism.hpp b/src/vmime/security/sasl/builtinSASLMechanism.hpp index 2e412d6c..56373a84 100644 --- a/src/vmime/security/sasl/builtinSASLMechanism.hpp +++ b/src/vmime/security/sasl/builtinSASLMechanism.hpp @@ -92,6 +92,9 @@ class VMIME_EXPORT builtinSASLMechanism : public SASLMechanism { /** Authentication process status. */ bool m_complete; + + /** Whether the mechanism is client-first, and thus supports an initial response. */ + bool m_initialResponse; };