-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(VerifyCertificateMacOS): moves the HttpsVerifier class to separa…
…te files
- Loading branch information
Showing
4 changed files
with
211 additions
and
156 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
#include <icertificate_utils.hpp> | ||
|
||
#include <CoreFoundation/CoreFoundation.h> | ||
#include <Security/Security.h> | ||
#include <boost/asio/ssl.hpp> | ||
|
||
#include <memory> | ||
#include <string> | ||
|
||
namespace https_socket_verify_utils | ||
{ | ||
class HttpsVerifier | ||
{ | ||
public: | ||
/// @brief Constructor to initialize the verifier object. | ||
/// @param mode The verification mode to use | ||
/// @param host The hostname to verify against | ||
/// @param utils The certificate utilities object to use | ||
HttpsVerifier(const std::string& mode, const std::string& host, std::unique_ptr<ICertificateUtils>& utils) | ||
: m_mode(mode) | ||
, m_host(host) | ||
, m_utils(utils) | ||
{ | ||
} | ||
|
||
/// @brief Verifies the certificate of the HTTPS connection | ||
/// @param ctx The verification context | ||
/// @return True if the certificate is valid, false otherwise | ||
bool Verify(boost::asio::ssl::verify_context& ctx); | ||
|
||
private: | ||
/// @brief Extracts the certificate from the verification context | ||
/// @param ctx The verification context | ||
/// @param certData The extracted certificate | ||
/// @return True if the certificate was extracted successfully, false otherwise | ||
bool ExtractCertificate(boost::asio::ssl::verify_context& ctx, CFDataRef& certData); | ||
|
||
/// @brief Creates a trust object from the certificate | ||
/// @param certData The certificate data | ||
/// @param trust The created trust object | ||
/// @return True if the trust object was created successfully, false otherwise | ||
bool CreateTrustObject(CFDataRef certData, SecTrustRef& trust); | ||
|
||
/// @brief Evaluates the trust status of the trust object | ||
/// @param trust The trust object to evaluate | ||
/// @return True if the trust evaluation succeeded, false otherwise | ||
bool EvaluateTrust(SecTrustRef trust); | ||
|
||
/// @brief Validates the hostname of the server certificate | ||
/// @param cert The server certificate | ||
/// @return True if the hostname is valid, false otherwise | ||
bool ValidateHostname(SecCertificateRef cert); | ||
|
||
/// @brief The verification mode to use | ||
std::string m_mode; | ||
|
||
/// @brief The hostname to verify against | ||
std::string m_host; | ||
|
||
/// @brief The certificate utilities object to use | ||
std::unique_ptr<ICertificateUtils>& m_utils; | ||
}; | ||
} // namespace https_socket_verify_utils |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
#include "certificate_utils.hpp" | ||
#include <https_socket_verify_utils.hpp> | ||
#include <https_verifier_mac.hpp> | ||
#include <icertificate_utils.hpp> | ||
#include <logger.hpp> | ||
|
||
#include <CoreFoundation/CoreFoundation.h> | ||
#include <Security/Security.h> | ||
#include <boost/asio/ssl.hpp> | ||
|
||
#include <memory> | ||
#include <string> | ||
|
||
namespace https_socket_verify_utils | ||
{ | ||
bool HttpsVerifier::Verify(boost::asio::ssl::verify_context& ctx) | ||
{ | ||
CFDataRef certData = nullptr; | ||
if (!ExtractCertificate(ctx, certData)) | ||
{ | ||
return false; | ||
} | ||
|
||
SecTrustRef trust = nullptr; | ||
if (!CreateTrustObject(certData, trust)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (!EvaluateTrust(trust)) | ||
{ | ||
m_utils->ReleaseCFObject(certData); | ||
m_utils->ReleaseCFObject(trust); | ||
return false; | ||
} | ||
|
||
SecCertificateRef serverCert = m_utils->CreateCertificate(certData); | ||
if (m_mode == "full" && !ValidateHostname(serverCert)) | ||
{ | ||
m_utils->ReleaseCFObject(certData); | ||
m_utils->ReleaseCFObject(serverCert); | ||
m_utils->ReleaseCFObject(trust); | ||
return false; | ||
} | ||
|
||
m_utils->ReleaseCFObject(certData); | ||
m_utils->ReleaseCFObject(serverCert); | ||
m_utils->ReleaseCFObject(trust); | ||
return true; | ||
} | ||
|
||
bool HttpsVerifier::ExtractCertificate(boost::asio::ssl::verify_context& ctx, CFDataRef& certData) | ||
{ | ||
STACK_OF(X509)* certChain = m_utils->GetCertChain(ctx.native_handle()); | ||
if (!certChain || m_utils->GetCertificateCount(certChain) == 0) | ||
{ | ||
LogError("No certificates in the chain."); | ||
return false; | ||
} | ||
|
||
X509* cert = m_utils->GetCertificateFromChain(certChain, 0); | ||
if (!cert) | ||
{ | ||
LogError("The server certificate could not be obtained."); | ||
return false; | ||
} | ||
|
||
unsigned char* certRawData = nullptr; | ||
const int certLen = m_utils->EncodeCertificateToDER(cert, &certRawData); | ||
if (certLen <= 0) | ||
{ | ||
LogError("Failed to encode certificate to DER."); | ||
return false; | ||
} | ||
|
||
certData = m_utils->CreateCFData(certRawData, certLen); | ||
OPENSSL_free(certRawData); | ||
|
||
return certData != nullptr; | ||
} | ||
|
||
bool HttpsVerifier::CreateTrustObject(CFDataRef certData, SecTrustRef& trust) | ||
{ | ||
SecCertificateRef serverCert = m_utils->CreateCertificate(certData); | ||
|
||
if (!serverCert) | ||
{ | ||
LogError("Failed to create SecCertificateRef."); | ||
return false; | ||
} | ||
|
||
const void* certArrayValues[] = {serverCert}; | ||
|
||
CFArrayRef certArray = m_utils->CreateCertArray(certArrayValues, 1); | ||
SecPolicyRef policy = m_utils->CreateSSLPolicy(true, ""); | ||
|
||
OSStatus status = m_utils->CreateTrustObject(certArray, policy, &trust); | ||
m_utils->ReleaseCFObject(certArray); | ||
m_utils->ReleaseCFObject(policy); | ||
m_utils->ReleaseCFObject(serverCert); | ||
|
||
return (status == errSecSuccess && trust != nullptr); | ||
} | ||
|
||
bool HttpsVerifier::EvaluateTrust(SecTrustRef trust) | ||
{ | ||
CFErrorRef error = nullptr; | ||
const bool trustResult = m_utils->EvaluateTrust(trust, &error); | ||
if (!trustResult && error) | ||
{ | ||
CFStringRef errorDesc = m_utils->CopyErrorDescription(error); | ||
std::string errorString = m_utils->GetStringCFString(errorDesc); | ||
LogError("Trust evaluation failed: {}", errorString); | ||
m_utils->ReleaseCFObject(errorDesc); | ||
m_utils->ReleaseCFObject(error); | ||
} | ||
return trustResult; | ||
} | ||
|
||
bool HttpsVerifier::ValidateHostname(SecCertificateRef serverCert) | ||
{ | ||
CFStringRef sanString = SecCertificateCopySubjectSummary(serverCert); | ||
if (!sanString) | ||
{ | ||
LogError("Failed to retrieve SAN or CN for hostname validation."); | ||
return false; | ||
} | ||
|
||
bool hostnameMatches = false; | ||
std::string sanStringStr = m_utils->GetStringCFString(sanString); | ||
if (!sanStringStr.empty()) | ||
{ | ||
hostnameMatches = (m_host == sanStringStr); | ||
} | ||
|
||
m_utils->ReleaseCFObject(sanString); | ||
if (!hostnameMatches) | ||
{ | ||
LogError("The hostname '{}' does not match the certificate's SAN or CN '{}'.", m_host, sanStringStr); | ||
} | ||
|
||
return hostnameMatches; | ||
} | ||
|
||
} // namespace https_socket_verify_utils |