diff --git a/src/agent/communicator/CMakeLists.txt b/src/agent/communicator/CMakeLists.txt index 579a84681e..e87c2d49ba 100644 --- a/src/agent/communicator/CMakeLists.txt +++ b/src/agent/communicator/CMakeLists.txt @@ -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 @@ -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) diff --git a/src/agent/communicator/src/https_socket_verify_utils_mac.cpp b/src/agent/communicator/src/https_socket_verify_utils_mac.cpp new file mode 100644 index 0000000000..e3b602391e --- /dev/null +++ b/src/agent/communicator/src/https_socket_verify_utils_mac.cpp @@ -0,0 +1,127 @@ +#include +#include + +#include +#include +#include + +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