diff options
21 files changed, 456 insertions, 30 deletions
@@ -18,7 +18,7 @@ </dictionary> <dictionary> <key>org.eclipse.cdt.make.core.autoBuildTarget</key> - <value>dist=1 Swift</value> + <value>check=1 QA</value> </dictionary> <dictionary> <key>org.eclipse.cdt.make.core.buildArguments</key> @@ -54,7 +54,7 @@ </dictionary> <dictionary> <key>org.eclipse.cdt.make.core.fullBuildTarget</key> - <value>dist=1 Swift</value> + <value>check=1 QA</value> </dictionary> <dictionary> <key>org.eclipse.cdt.make.core.stopOnError</key> diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp index a199a84..9e6db5d 100644 --- a/Swiften/Client/ClientSession.cpp +++ b/Swiften/Client/ClientSession.cpp @@ -37,6 +37,7 @@ #include "Swiften/SASL/DIGESTMD5ClientAuthenticator.h" #include "Swiften/Session/SessionStream.h" #include "Swiften/TLS/CertificateTrustChecker.h" +#include "Swiften/TLS/ServerIdentityVerifier.h" namespace Swift { @@ -330,16 +331,27 @@ void ClientSession::handleTLSEncrypted() { Certificate::ref certificate = stream->getPeerCertificate(); boost::shared_ptr<CertificateVerificationError> verificationError = stream->getPeerCertificateVerificationError(); if (verificationError) { - if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificate, localJID.getDomain())) { + checkTrustOrFinish(certificate, verificationError); + } + else { + ServerIdentityVerifier identityVerifier(localJID); + if (identityVerifier.certificateVerifies(certificate)) { continueAfterTLSEncrypted(); } else { - finishSession(verificationError); + boost::shared_ptr<CertificateVerificationError> identityError(new CertificateVerificationError(CertificateVerificationError::InvalidServerIdentity)); + checkTrustOrFinish(certificate, identityError); } } - else { +} + +void ClientSession::checkTrustOrFinish(Certificate::ref certificate, boost::shared_ptr<CertificateVerificationError> error) { + if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificate, localJID.getDomain())) { continueAfterTLSEncrypted(); } + else { + finishSession(error); + } } void ClientSession::continueAfterTLSEncrypted() { diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h index 6acd9a3..20573a0 100644 --- a/Swiften/Client/ClientSession.h +++ b/Swiften/Client/ClientSession.h @@ -121,6 +121,7 @@ namespace Swift { void handleStanzaAcked(boost::shared_ptr<Stanza> stanza); void ack(unsigned int handledStanzasCount); void continueAfterTLSEncrypted(); + void checkTrustOrFinish(Certificate::ref certificate, boost::shared_ptr<CertificateVerificationError> error); private: JID localJID; diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp index 11e4992..74f3376 100644 --- a/Swiften/Client/UnitTest/ClientSessionTest.cpp +++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp @@ -24,6 +24,8 @@ #include "Swiften/Elements/EnableStreamManagement.h" #include "Swiften/Elements/IQ.h" #include "Swiften/Elements/ResourceBind.h" +#include "Swiften/TLS/SimpleCertificate.h" +#include "Swiften/TLS/BlindCertificateTrustChecker.h" using namespace Swift; @@ -33,6 +35,7 @@ class ClientSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testStartTLS); CPPUNIT_TEST(testStartTLS_ServerError); CPPUNIT_TEST(testStartTLS_ConnectError); + CPPUNIT_TEST(testStartTLS_InvalidIdentity); CPPUNIT_TEST(testAuthenticate); CPPUNIT_TEST(testAuthenticate_Unauthorized); CPPUNIT_TEST(testAuthenticate_NoValidAuthMechanisms); @@ -57,6 +60,11 @@ class ClientSessionTest : public CppUnit::TestFixture { server = boost::shared_ptr<MockSessionStream>(new MockSessionStream()); sessionFinishedReceived = false; needCredentials = false; + blindCertificateTrustChecker = new BlindCertificateTrustChecker(); + } + + void tearDown() { + delete blindCertificateTrustChecker; } void testStart_Error() { @@ -71,6 +79,7 @@ class ClientSessionTest : public CppUnit::TestFixture { void testStartTLS() { boost::shared_ptr<ClientSession> session(createSession()); + session->setCertificateTrustChecker(blindCertificateTrustChecker); session->start(); server->receiveStreamStart(); server->sendStreamStart(); @@ -116,6 +125,24 @@ class ClientSessionTest : public CppUnit::TestFixture { CPPUNIT_ASSERT(sessionFinishedError); } + void testStartTLS_InvalidIdentity() { + boost::shared_ptr<ClientSession> session(createSession()); + session->start(); + server->receiveStreamStart(); + server->sendStreamStart(); + server->sendStreamFeaturesWithStartTLS(); + server->receiveStartTLS(); + CPPUNIT_ASSERT(!server->tlsEncrypted); + server->sendTLSProceed(); + CPPUNIT_ASSERT(server->tlsEncrypted); + server->onTLSEncrypted(); + + CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); + CPPUNIT_ASSERT(sessionFinishedReceived); + CPPUNIT_ASSERT(sessionFinishedError); + CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::InvalidServerIdentity, boost::dynamic_pointer_cast<CertificateVerificationError>(sessionFinishedError)->getType()); + } + void testAuthenticate() { boost::shared_ptr<ClientSession> session(createSession()); session->start(); @@ -284,7 +311,7 @@ class ClientSessionTest : public CppUnit::TestFixture { } virtual Certificate::ref getPeerCertificate() const { - return Certificate::ref(); + return Certificate::ref(new SimpleCertificate()); } virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const { @@ -429,6 +456,7 @@ class ClientSessionTest : public CppUnit::TestFixture { bool sessionFinishedReceived; bool needCredentials; boost::shared_ptr<Error> sessionFinishedError; + BlindCertificateTrustChecker* blindCertificateTrustChecker; }; CPPUNIT_TEST_SUITE_REGISTRATION(ClientSessionTest); diff --git a/Swiften/IDN/IDNA.cpp b/Swiften/IDN/IDNA.cpp new file mode 100644 index 0000000..a21ca63 --- /dev/null +++ b/Swiften/IDN/IDNA.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/IDN/IDNA.h" + +#include <stringprep.h> +#include <vector> +#include <idna.h> + +namespace Swift { + +String IDNA::getEncoded(const String& domain) { + char* output; + if (idna_to_ascii_8z(domain.getUTF8Data(), &output, 0) == IDNA_SUCCESS) { + String result(output); + free(output); + return result; + } + else { + return domain; + } +} + +} diff --git a/Swiften/IDN/IDNA.h b/Swiften/IDN/IDNA.h new file mode 100644 index 0000000..cc4144b --- /dev/null +++ b/Swiften/IDN/IDNA.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swiften/Base/String.h" + +namespace Swift { + class IDNA { + public: + static String getEncoded(const String& s); + }; +} diff --git a/Swiften/StringPrep/SConscript b/Swiften/IDN/SConscript index 480d81a..bfdb42c 100644 --- a/Swiften/StringPrep/SConscript +++ b/Swiften/IDN/SConscript @@ -4,6 +4,7 @@ myenv = swiften_env.Clone() myenv.MergeFlags(swiften_env["LIBIDN_FLAGS"]) objects = myenv.StaticObject([ - "StringPrep.cpp" + "StringPrep.cpp", + "IDNA.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/StringPrep/StringPrep.cpp b/Swiften/IDN/StringPrep.cpp index ea084a5..d9e061e 100644 --- a/Swiften/StringPrep/StringPrep.cpp +++ b/Swiften/IDN/StringPrep.cpp @@ -4,7 +4,7 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/StringPrep/StringPrep.h" +#include "Swiften/IDN/StringPrep.h" #include <stringprep.h> #include <vector> diff --git a/Swiften/StringPrep/StringPrep.h b/Swiften/IDN/StringPrep.h index 3b27efa..3b27efa 100644 --- a/Swiften/StringPrep/StringPrep.h +++ b/Swiften/IDN/StringPrep.h diff --git a/Swiften/JID/JID.cpp b/Swiften/JID/JID.cpp index f89ba29..cf5317e 100644 --- a/Swiften/JID/JID.cpp +++ b/Swiften/JID/JID.cpp @@ -9,7 +9,7 @@ #include <iostream> #include "Swiften/JID/JID.h" -#include "Swiften/StringPrep/StringPrep.h" +#include "Swiften/IDN/StringPrep.h" namespace Swift { diff --git a/Swiften/Network/DomainNameResolver.cpp b/Swiften/Network/DomainNameResolver.cpp index 854b97f..96c5165 100644 --- a/Swiften/Network/DomainNameResolver.cpp +++ b/Swiften/Network/DomainNameResolver.cpp @@ -6,23 +6,9 @@ #include "Swiften/Network/DomainNameResolver.h" -#include <idna.h> - namespace Swift { DomainNameResolver::~DomainNameResolver() { } -String DomainNameResolver::getNormalized(const String& domain) { - char* output; - if (idna_to_ascii_8z(domain.getUTF8Data(), &output, 0) == IDNA_SUCCESS) { - String result(output); - free(output); - return result; - } - else { - return domain; - } -} - } diff --git a/Swiften/Network/DomainNameResolver.h b/Swiften/Network/DomainNameResolver.h index 4f8829c..cf9d521 100644 --- a/Swiften/Network/DomainNameResolver.h +++ b/Swiften/Network/DomainNameResolver.h @@ -21,8 +21,5 @@ namespace Swift { virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const String& name) = 0; virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const String& name) = 0; - - protected: - static String getNormalized(const String& domain); }; } diff --git a/Swiften/Network/PlatformDomainNameResolver.cpp b/Swiften/Network/PlatformDomainNameResolver.cpp index 44c87e0..3f72466 100644 --- a/Swiften/Network/PlatformDomainNameResolver.cpp +++ b/Swiften/Network/PlatformDomainNameResolver.cpp @@ -18,6 +18,7 @@ #include <algorithm> #include "Swiften/Base/String.h" +#include "Swiften/IDN/IDNA.h" #include "Swiften/Network/HostAddress.h" #include "Swiften/EventLoop/EventLoop.h" #include "Swiften/Network/HostAddressPort.h" @@ -95,11 +96,11 @@ PlatformDomainNameResolver::PlatformDomainNameResolver(EventLoop* eventLoop) : e } boost::shared_ptr<DomainNameServiceQuery> PlatformDomainNameResolver::createServiceQuery(const String& name) { - return boost::shared_ptr<DomainNameServiceQuery>(new PlatformDomainNameServiceQuery(getNormalized(name), eventLoop)); + return boost::shared_ptr<DomainNameServiceQuery>(new PlatformDomainNameServiceQuery(IDNA::getEncoded(name), eventLoop)); } boost::shared_ptr<DomainNameAddressQuery> PlatformDomainNameResolver::createAddressQuery(const String& name) { - return boost::shared_ptr<DomainNameAddressQuery>(new AddressQuery(getNormalized(name), eventLoop)); + return boost::shared_ptr<DomainNameAddressQuery>(new AddressQuery(IDNA::getEncoded(name), eventLoop)); } } diff --git a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp index 5d0ee9a..551afd5 100644 --- a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp +++ b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp @@ -14,7 +14,7 @@ #include "Swiften/StringCodecs/Base64.h" #include "Swiften/StringCodecs/HMACSHA1.h" #include "Swiften/StringCodecs/PBKDF2.h" -#include "Swiften/StringPrep/StringPrep.h" +#include "Swiften/IDN/StringPrep.h" namespace Swift { diff --git a/Swiften/SConscript b/Swiften/SConscript index df34e8b..1f762cb 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -125,7 +125,7 @@ if env["SCONS_STAGE"] == "build" : SConscript(dirs = [ "Avatars", "Base", - "StringPrep", + "IDN", "SASL", "TLS", "EventLoop", @@ -266,6 +266,7 @@ if env["SCONS_STAGE"] == "build" : File("StringCodecs/UnitTest/HexifyTest.cpp"), File("StringCodecs/UnitTest/HMACSHA1Test.cpp"), File("StringCodecs/UnitTest/PBKDF2Test.cpp"), + File("TLS/UnitTest/ServerIdentityVerifierTest.cpp"), File("VCards/UnitTest/VCardManagerTest.cpp"), ]) diff --git a/Swiften/TLS/CertificateVerificationError.h b/Swiften/TLS/CertificateVerificationError.h index f1bd091..807df03 100644 --- a/Swiften/TLS/CertificateVerificationError.h +++ b/Swiften/TLS/CertificateVerificationError.h @@ -22,6 +22,7 @@ namespace Swift { PathLengthExceeded, InvalidSignature, InvalidCA, + InvalidServerIdentity, }; CertificateVerificationError(Type type = UnknownError) : type(type) {} diff --git a/Swiften/TLS/SConscript b/Swiften/TLS/SConscript index f83e383..43f7db6 100644 --- a/Swiften/TLS/SConscript +++ b/Swiften/TLS/SConscript @@ -4,6 +4,7 @@ objects = swiften_env.StaticObject([ "Certificate.cpp", "CertificateFactory.cpp", "CertificateTrustChecker.cpp", + "ServerIdentityVerifier.cpp", "TLSContext.cpp", "TLSContextFactory.cpp", ]) diff --git a/Swiften/TLS/ServerIdentityVerifier.cpp b/Swiften/TLS/ServerIdentityVerifier.cpp new file mode 100644 index 0000000..05efd31 --- /dev/null +++ b/Swiften/TLS/ServerIdentityVerifier.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/TLS/ServerIdentityVerifier.h" + +#include "Swiften/Base/foreach.h" +#include "Swiften/IDN/IDNA.h" + +namespace Swift { + +ServerIdentityVerifier::ServerIdentityVerifier(const JID& jid) { + domain = jid.getDomain(); + encodedDomain = IDNA::getEncoded(domain); +} + +bool ServerIdentityVerifier::certificateVerifies(Certificate::ref certificate) { + bool hasSAN = false; + + // DNS names + std::vector<String> dnsNames = certificate->getDNSNames(); + foreach (const String& dnsName, dnsNames) { + if (matchesDomain(dnsName)) { + return true; + } + } + hasSAN |= !dnsNames.empty(); + + // SRV names + std::vector<String> srvNames = certificate->getSRVNames(); + foreach (const String& srvName, srvNames) { + // Only match SRV names that begin with the service; this isn't required per + // spec, but we're being purist about this. + if (srvName.beginsWith("_xmpp-client.") && matchesDomain(srvName.getSubstring(String("_xmpp-client.").getUTF8Size(), srvName.npos()))) { + return true; + } + } + hasSAN |= !srvNames.empty(); + + // XmppAddr + std::vector<String> xmppAddresses = certificate->getXMPPAddresses(); + foreach (const String& xmppAddress, xmppAddresses) { + if (matchesAddress(xmppAddress)) { + return true; + } + } + hasSAN |= !xmppAddresses.empty(); + + // CommonNames. Only check this if there was no SAN (according to spec). + if (!hasSAN) { + std::vector<String> commonNames = certificate->getCommonNames(); + foreach (const String& commonName, commonNames) { + if (matchesDomain(commonName)) { + return true; + } + } + } + + return false; +} + +bool ServerIdentityVerifier::matchesDomain(const String& s) { + if (s.beginsWith("*.")) { + String matchString(s.getSubstring(2, s.npos())); + String matchDomain = encodedDomain; + int dotIndex = matchDomain.find('.'); + if (dotIndex >= 0) { + matchDomain = matchDomain.getSubstring(dotIndex + 1, matchDomain.npos()); + } + return matchString == matchDomain; + } + else { + return s == encodedDomain; + } +} + +bool ServerIdentityVerifier::matchesAddress(const String& s) { + return s == domain; +} + +} diff --git a/Swiften/TLS/ServerIdentityVerifier.h b/Swiften/TLS/ServerIdentityVerifier.h new file mode 100644 index 0000000..a001a5e --- /dev/null +++ b/Swiften/TLS/ServerIdentityVerifier.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> + +#include "Swiften/Base/String.h" +#include "Swiften/JID/JID.h" +#include "Swiften/TLS/Certificate.h" + +namespace Swift { + class ServerIdentityVerifier { + public: + ServerIdentityVerifier(const JID& jid); + + bool certificateVerifies(Certificate::ref); + + private: + bool matchesDomain(const String&); + bool matchesAddress(const String&); + + private: + String domain; + String encodedDomain; + }; +} diff --git a/Swiften/TLS/SimpleCertificate.h b/Swiften/TLS/SimpleCertificate.h new file mode 100644 index 0000000..2db8291 --- /dev/null +++ b/Swiften/TLS/SimpleCertificate.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swiften/Base/String.h" +#include "Swiften/TLS/Certificate.h" + +namespace Swift { + class SimpleCertificate : public Certificate { + public: + typedef boost::shared_ptr<SimpleCertificate> ref; + + void setSubjectName(const String& name) { + subjectName = name; + } + + String getSubjectName() const { + return subjectName; + } + + std::vector<String> getCommonNames() const { + return commonNames; + } + + void addCommonName(const String& name) { + commonNames.push_back(name); + } + + void addSRVName(const String& name) { + srvNames.push_back(name); + } + + void addDNSName(const String& name) { + dnsNames.push_back(name); + } + + void addXMPPAddress(const String& addr) { + xmppAddresses.push_back(addr); + } + + std::vector<String> getSRVNames() const { + return srvNames; + } + + std::vector<String> getDNSNames() const { + return dnsNames; + } + + std::vector<String> getXMPPAddresses() const { + return xmppAddresses; + } + + ByteArray toDER() const { + return ByteArray(); + } + + private: + void parse(); + + private: + String subjectName; + std::vector<String> commonNames; + std::vector<String> dnsNames; + std::vector<String> xmppAddresses; + std::vector<String> srvNames; + }; +} diff --git a/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp b/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp new file mode 100644 index 0000000..a7fdbad --- /dev/null +++ b/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Base/ByteArray.h" + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <vector> + +#include "Swiften/TLS/ServerIdentityVerifier.h" +#include "Swiften/TLS/SimpleCertificate.h" + +using namespace Swift; + +class ServerIdentityVerifierTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(ServerIdentityVerifierTest); + CPPUNIT_TEST(testCertificateVerifies_WithoutMatchingDNSName); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingDNSName); + CPPUNIT_TEST(testCertificateVerifies_WithSecondMatchingDNSName); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingInternationalDNSName); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingDNSNameWithWildcard); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingDNSNameWithWildcardMatchingNoComponents); + CPPUNIT_TEST(testCertificateVerifies_WithDNSNameWithWildcardMatchingTwoComponents); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithoutService); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithService); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithServiceAndWildcard); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithDifferentService); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingXmppAddr); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingXmppAddrWithWildcard); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingInternationalXmppAddr); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithoutSAN); + CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithMatchingSAN); + CPPUNIT_TEST_SUITE_END(); + + public: + void testCertificateVerifies_WithoutMatchingDNSName() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addDNSName("foo.com"); + + CPPUNIT_ASSERT(!testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingDNSName() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addDNSName("bar.com"); + + CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithSecondMatchingDNSName() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addDNSName("foo.com"); + certificate->addDNSName("bar.com"); + + CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingInternationalDNSName() { + ServerIdentityVerifier testling(JID("foo@tron\xc3\xa7on.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addDNSName("xn--tronon-zua.com"); + + CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingDNSNameWithWildcard() { + ServerIdentityVerifier testling(JID("foo@im.bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addDNSName("*.bar.com"); + + CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingDNSNameWithWildcardMatchingNoComponents() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addDNSName("*.bar.com"); + + CPPUNIT_ASSERT(!testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithDNSNameWithWildcardMatchingTwoComponents() { + ServerIdentityVerifier testling(JID("foo@xmpp.im.bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addDNSName("*.bar.com"); + + CPPUNIT_ASSERT(!testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingSRVNameWithoutService() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addSRVName("bar.com"); + + CPPUNIT_ASSERT(!testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingSRVNameWithService() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addSRVName("_xmpp-client.bar.com"); + + CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingSRVNameWithServiceAndWildcard() { + ServerIdentityVerifier testling(JID("foo@im.bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addSRVName("_xmpp-client.*.bar.com"); + + CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingSRVNameWithDifferentService() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addSRVName("_xmpp-server.bar.com"); + + CPPUNIT_ASSERT(!testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingXmppAddr() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addXMPPAddress("bar.com"); + + CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingXmppAddrWithWildcard() { + ServerIdentityVerifier testling(JID("foo@im.bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addXMPPAddress("*.bar.com"); + + CPPUNIT_ASSERT(!testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingInternationalXmppAddr() { + ServerIdentityVerifier testling(JID("foo@tron\xc3\xa7.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addXMPPAddress("tron\xc3\xa7.com"); + + CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingCNWithoutSAN() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addCommonName("bar.com"); + + CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); + } + + void testCertificateVerifies_WithMatchingCNWithMatchingSAN() { + ServerIdentityVerifier testling(JID("foo@bar.com/baz")); + SimpleCertificate::ref certificate(new SimpleCertificate()); + certificate->addSRVName("foo.com"); + certificate->addCommonName("bar.com"); + + CPPUNIT_ASSERT(!testling.certificateVerifies(certificate)); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ServerIdentityVerifierTest); |