diff options
Diffstat (limited to 'Swiften/TLS')
-rw-r--r-- | Swiften/TLS/CertificateVerificationError.h | 1 | ||||
-rw-r--r-- | Swiften/TLS/SConscript | 1 | ||||
-rw-r--r-- | Swiften/TLS/ServerIdentityVerifier.cpp | 83 | ||||
-rw-r--r-- | Swiften/TLS/ServerIdentityVerifier.h | 30 | ||||
-rw-r--r-- | Swiften/TLS/SimpleCertificate.h | 71 | ||||
-rw-r--r-- | Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp | 170 |
6 files changed, 356 insertions, 0 deletions
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); |