-
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.
Merge pull request #476 from wazuh/enhancement/468-certificate-valida…
…tion-on-macos Add server certificate validation to MacOS wazuh agent
- Loading branch information
Showing
2 changed files
with
132 additions
and
3 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
127 changes: 127 additions & 0 deletions
127
src/agent/communicator/src/https_socket_verify_utils_mac.cpp
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,127 @@ | ||
#include <https_socket_verify_utils.hpp> | ||
#include <logger.hpp> | ||
|
||
#include <CoreFoundation/CoreFoundation.h> | ||
#include <Security/Security.h> | ||
#include <boost/asio/ssl.hpp> | ||
|
||
namespace https_socket_verify_utils | ||
{ | ||
|
||
bool VerifyCertificate([[maybe_unused]] bool preverified, | ||
boost::asio::ssl::verify_context& ctx, | ||
const std::string& mode, | ||
const std::string& host) | ||
{ | ||
STACK_OF(X509)* certChain = X509_STORE_CTX_get_chain(ctx.native_handle()); | ||
if (!certChain || sk_X509_num(certChain) == 0) | ||
{ | ||
LogError("No certificates in the chain."); | ||
return false; | ||
} | ||
|
||
X509* cert = sk_X509_value(certChain, 0); | ||
if (!cert) | ||
{ | ||
LogError("The server certificate could not be obtained."); | ||
return false; | ||
} | ||
|
||
unsigned char* certData = nullptr; | ||
const int certLen = i2d_X509(cert, &certData); | ||
if (certLen <= 0) | ||
{ | ||
LogError("Failed to encode certificate to DER."); | ||
return false; | ||
} | ||
|
||
CFDataRef cfCertData = CFDataCreate(kCFAllocatorDefault, certData, certLen); | ||
OPENSSL_free(certData); | ||
if (!cfCertData) | ||
{ | ||
LogError("Failed to create CFData for certificate."); | ||
return false; | ||
} | ||
|
||
SecCertificateRef serverCert = SecCertificateCreateWithData(nullptr, cfCertData); | ||
CFRelease(cfCertData); | ||
if (!serverCert) | ||
{ | ||
LogError("Failed to create SecCertificateRef."); | ||
return false; | ||
} | ||
|
||
const void* certArrayValues[] = {serverCert}; | ||
|
||
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certArrayValues, 1, &kCFTypeArrayCallBacks); | ||
SecPolicyRef policy = SecPolicyCreateSSL(true, nullptr); | ||
SecTrustRef trust = nullptr; | ||
|
||
OSStatus status = SecTrustCreateWithCertificates(certArray, policy, &trust); | ||
CFRelease(certArray); | ||
CFRelease(policy); | ||
|
||
if (status != errSecSuccess || !trust) | ||
{ | ||
LogError("Failed to create SecTrust object."); | ||
CFRelease(serverCert); | ||
return false; | ||
} | ||
|
||
// Evaluate certificate trust using SecTrustEvaluateWithError | ||
CFErrorRef error = nullptr; | ||
|
||
const bool trustResult = SecTrustEvaluateWithError(trust, &error); | ||
if (!trustResult) | ||
{ | ||
if (error) | ||
{ | ||
CFStringRef errorDesc = CFErrorCopyDescription(error); | ||
char bufferError[256] = {0}; | ||
CFStringGetCString(errorDesc, bufferError, sizeof(bufferError), kCFStringEncodingUTF8); | ||
LogError("Trust evaluation failed: {}", bufferError); | ||
CFRelease(errorDesc); | ||
CFRelease(error); | ||
} | ||
CFRelease(trust); | ||
CFRelease(serverCert); | ||
return false; | ||
} | ||
|
||
// Validate the hostname if the mode is 'full'. | ||
if (mode == "full") | ||
{ | ||
CFStringRef sanString = SecCertificateCopySubjectSummary(serverCert); | ||
if (!sanString) | ||
{ | ||
LogError("Failed to retrieve SAN or CN for hostname validation."); | ||
CFRelease(trust); | ||
CFRelease(serverCert); | ||
return false; | ||
} | ||
|
||
bool hostnameMatches = false; | ||
char buffer[256] = {0}; | ||
if (CFStringGetCString(sanString, buffer, sizeof(buffer), kCFStringEncodingUTF8)) | ||
{ | ||
hostnameMatches = (host == buffer); | ||
} | ||
|
||
CFRelease(sanString); | ||
if (!hostnameMatches) | ||
{ | ||
LogError("The hostname does not match the certificate's SAN or CN."); | ||
CFRelease(trust); | ||
CFRelease(serverCert); | ||
return false; | ||
} | ||
} | ||
|
||
// Free resources | ||
CFRelease(trust); | ||
CFRelease(serverCert); | ||
|
||
return true; | ||
} | ||
|
||
} // namespace https_socket_verify_utils |