Skip to content

Commit

Permalink
Merge pull request #476 from wazuh/enhancement/468-certificate-valida…
Browse files Browse the repository at this point in the history
…tion-on-macos

Add server certificate validation to MacOS wazuh agent
  • Loading branch information
TomasTurina authored Jan 6, 2025
2 parents 95b9665 + d8d6285 commit 9274bff
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 3 deletions.
8 changes: 5 additions & 3 deletions src/agent/communicator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ find_package(Boost REQUIRED COMPONENTS asio beast system url)
find_package(nlohmann_json CONFIG REQUIRED)
find_path(JWT_CPP_INCLUDE_DIRS "jwt-cpp/base.h")

add_library(Communicator src/communicator.cpp src/http_client.cpp src/http_request_params.cpp)

if(WIN32)
set(VERIFY_UTILS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/src/https_socket_verify_utils_win.cpp")
elseif(APPLE)
set(VERIFY_UTILS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/src/https_socket_verify_utils_mac.cpp")
else()
set(VERIFY_UTILS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/src/https_socket_verify_utils_lin.cpp")
endif()

target_sources(Communicator PRIVATE ${VERIFY_UTILS_FILE})
add_library(Communicator src/communicator.cpp src/http_client.cpp src/http_request_params.cpp ${VERIFY_UTILS_FILE})

target_include_directories(Communicator PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
Expand All @@ -33,6 +33,8 @@ target_link_libraries(Communicator PUBLIC Config Boost::asio Boost::beast Boost:

if(WIN32)
target_link_libraries(Communicator PRIVATE Crypt32)
elseif(APPLE)
target_link_libraries(Communicator PRIVATE "-framework Security" "-framework CoreFoundation")
endif()

include(../../cmake/ConfigureTarget.cmake)
Expand Down
127 changes: 127 additions & 0 deletions src/agent/communicator/src/https_socket_verify_utils_mac.cpp
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

0 comments on commit 9274bff

Please sign in to comment.