mirror of
https://github.com/google/santa.git
synced 2026-01-19 19:19:38 -05:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0ede18bf4 | ||
|
|
6d223aea03 | ||
|
|
f7986b0a05 | ||
|
|
629e70287c | ||
|
|
3c2a88144c | ||
|
|
3651f18566 | ||
|
|
472fea75b1 | ||
|
|
e1b5438865 | ||
|
|
fbbf523333 | ||
|
|
15fa53d744 | ||
|
|
9595f80fde | ||
|
|
61a67e45c1 | ||
|
|
143e690dab | ||
|
|
ebd507f143 | ||
|
|
f71bc0a8f7 | ||
|
|
edc0c72464 | ||
|
|
c3ce4f718b | ||
|
|
40ee482973 | ||
|
|
a5d2e6fdd2 |
133
CMakeLists.txt
Normal file
133
CMakeLists.txt
Normal file
@@ -0,0 +1,133 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
|
||||
execute_process(
|
||||
COMMAND git fetch --tags --all
|
||||
COMMAND git describe --tags --always --abbrev=0
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE SANTA_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND xcode-select -p
|
||||
OUTPUT_VARIABLE DEFAULT_XCODE_ROOT_FOLDER
|
||||
)
|
||||
|
||||
string(STRIP "${DEFAULT_XCODE_ROOT_FOLDER}" DEFAULT_XCODE_ROOT_FOLDER)
|
||||
|
||||
add_custom_target(tests)
|
||||
function(AddTestTarget target_name run_as_root)
|
||||
if(run_as_root)
|
||||
add_custom_target(
|
||||
"${target_name}_runner"
|
||||
COMMAND sudo $<TARGET_FILE:${target_name}>
|
||||
COMMENT "Running ${target_name}_runner as root"
|
||||
)
|
||||
|
||||
else()
|
||||
add_custom_target(
|
||||
"${target_name}_runner"
|
||||
COMMAND $<TARGET_FILE:${target_name}>
|
||||
)
|
||||
endif()
|
||||
|
||||
add_dependencies(tests "${target_name}_runner")
|
||||
endfunction()
|
||||
|
||||
add_custom_target(fuzz)
|
||||
function(AddFuzzTarget target_name max_len max_total_time run_as_root)
|
||||
add_custom_command(TARGET "${target_name}" POST_BUILD
|
||||
COMMAND codesign --force --verify --verbose --sign "${CODESIGN_IDENTITY}" $<TARGET_FILE:${target_name}>
|
||||
COMMENT "Signing ${target_name} with the following identity: ${CODESIGN_IDENTITY}"
|
||||
)
|
||||
|
||||
if(${run_as_root})
|
||||
set(sudo_command "sudo")
|
||||
else()
|
||||
set(sudo_command "")
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
"${target_name}_runner"
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_corpus"
|
||||
COMMAND ${sudo_command} $<TARGET_FILE:${target_name}> -max_len=${max_len} -max_total_time=${max_total_time} "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_corpus" "${CMAKE_CURRENT_SOURCE_DIR}/${target_name}_seed_corpus"
|
||||
COMMENT "Running fuzzer: ${target_name}"
|
||||
)
|
||||
|
||||
add_dependencies("${target_name}_runner" "${target_name}")
|
||||
add_dependencies(fuzz "${target_name}_runner")
|
||||
endfunction()
|
||||
|
||||
function(main)
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CODESIGN_IDENTITY)
|
||||
if ("$ENV{CODESIGN_IDENTITY}" STREQUAL "")
|
||||
message(FATAL_ERROR "Please define CODESIGN_IDENTITY - You can either use an environment variable or pass it to CMake with -DCODESIGN_IDENTITY=identity. If you are using sudo, make sure to keep the variable defined")
|
||||
endif()
|
||||
|
||||
set(CODESIGN_IDENTITY "$ENV{CODESIGN_IDENTITY}")
|
||||
endif()
|
||||
|
||||
if(NOT MACOSX_VERSION_MIN)
|
||||
set(MACOSX_VERSION_MIN 10.9)
|
||||
endif()
|
||||
|
||||
if(NOT MACOSX_SDK_VERSION)
|
||||
set(sdk_version_name "Latest")
|
||||
else()
|
||||
set(sdk_version_name "${MACOSX_SDK_VERSION}")
|
||||
endif()
|
||||
|
||||
if(NOT XCODE_ROOT_FOLDER)
|
||||
set(XCODE_ROOT_FOLDER "${DEFAULT_XCODE_ROOT_FOLDER}")
|
||||
endif()
|
||||
|
||||
message(STATUS "MACOSX_VERSION_MIN: ${MACOSX_VERSION_MIN}")
|
||||
message(STATUS "MACOSX_SDK_VERSION: ${sdk_version_name}")
|
||||
message(STATUS "XCODE_ROOT_FOLDER: ${XCODE_ROOT_FOLDER}")
|
||||
message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
|
||||
message(STATUS "SANTA_VERSION: ${SANTA_VERSION}")
|
||||
|
||||
add_subdirectory(CocoaPods)
|
||||
add_subdirectory(Source/santad)
|
||||
add_subdirectory(Source/santactl)
|
||||
add_subdirectory(Source/santabs)
|
||||
add_subdirectory(Source/SantaGUI)
|
||||
add_subdirectory(Source/santa-driver)
|
||||
|
||||
add_subdirectory(Fuzzing)
|
||||
add_subdirectory(Tests)
|
||||
|
||||
set(redist_folder_path "${CMAKE_BINARY_DIR}/redist")
|
||||
add_custom_command(
|
||||
OUTPUT "${redist_folder_path}/conf/install.sh"
|
||||
|
||||
# Copy the binaries
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_BINARY_DIR}/Source/SantaGUI/Santa.app" "${redist_folder_path}/binaries/Santa.app"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_BINARY_DIR}/Source/santa-driver/santa-driver.kext" "${redist_folder_path}/binaries/santa-driver.kext"
|
||||
|
||||
# Copy the configuration files
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/Conf" "${redist_folder_path}/conf"
|
||||
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${redist_folder_path}/conf/Package"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_SOURCE_DIR}/Docs/deployment/com.google.santa.example.mobileconfig" "${redist_folder_path}/conf"
|
||||
|
||||
COMMENT "Generating redistributable package"
|
||||
)
|
||||
|
||||
add_custom_target(redist DEPENDS "${redist_folder_path}/conf/install.sh")
|
||||
add_dependencies(redist santad santa-driver santactl santabs Santa)
|
||||
|
||||
install(FILES Conf/com.google.santad.plist DESTINATION /Library/LaunchDaemons)
|
||||
install(FILES Conf/com.google.santagui.plist DESTINATION /Library/LaunchAgents)
|
||||
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E echo To finalize the installation, run the following commands)")
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E echo sudo /sbin/kextload /Library/Extensions/santa-driver.kext)")
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E echo sudo /bin/launchctl load /Library/LaunchDaemons/com.google.santad.plist)")
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E echo sudo /bin/launchctl asuser `/usr/bin/stat -f '%u' /dev/console` /bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist)")
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E echo Configuration guide available on ReadTheDocs - https://santa.readthedocs.io/en/latest/deployment/configuration/)")
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
195
CocoaPods/CMakeLists.txt
Normal file
195
CocoaPods/CMakeLists.txt
Normal file
@@ -0,0 +1,195 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(CocoaPods)
|
||||
|
||||
function(main)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_SOURCE_DIR}/Pods/Manifest.lock"
|
||||
COMMAND pod repo update && pod install
|
||||
DEPENDS "${CMAKE_SOURCE_DIR}/Podfile"
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Fetching CocoaPods dependencies"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target("${PROJECT_NAME}"
|
||||
DEPENDS "${CMAKE_SOURCE_DIR}/Pods/Manifest.lock"
|
||||
)
|
||||
|
||||
ImportCocoaPodsLibrary(
|
||||
FMDB
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMDatabase.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMResultSet.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMDatabasePool.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMDatabaseQueue.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMDB.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMDatabaseAdditions.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMDatabaseQueue.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMDatabaseAdditions.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMDatabase.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMDatabasePool.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/FMDB/src/fmdb/FMResultSet.m"
|
||||
)
|
||||
|
||||
target_link_libraries(FMDB PUBLIC sqlite3 "-framework Cocoa")
|
||||
|
||||
ImportCocoaPodsLibrary(
|
||||
MOLCertificate
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLCertificate/Source/MOLCertificate"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLCertificate/Source/MOLCertificate/MOLCertificate.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLCertificate/Source/MOLCertificate/MOLCertificate.h"
|
||||
)
|
||||
|
||||
target_link_libraries(MOLCertificate PUBLIC "-framework Security" "-framework Cocoa")
|
||||
|
||||
ImportCocoaPodsLibrary(
|
||||
MOLCodesignChecker
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLCodesignChecker/Source/MOLCodesignChecker"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLCodesignChecker/Source/MOLCodesignChecker/MOLCodesignChecker.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLCodesignChecker/Source/MOLCodesignChecker/MOLCodesignChecker.m"
|
||||
)
|
||||
|
||||
target_link_libraries(MOLCodesignChecker PUBLIC "-framework Security" "-framework Cocoa")
|
||||
|
||||
ImportCocoaPodsLibrary(
|
||||
MOLAuthenticatingURLSession
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLAuthenticatingURLSession/Source/MOLAuthenticatingURLSession"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLAuthenticatingURLSession/Source/MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLAuthenticatingURLSession/Source/MOLAuthenticatingURLSession/MOLDERDecoder.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLAuthenticatingURLSession/Source/MOLAuthenticatingURLSession/MOLDERDecoder.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLAuthenticatingURLSession/Source/MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.h"
|
||||
)
|
||||
|
||||
target_link_libraries(MOLAuthenticatingURLSession PUBLIC "-framework Security" MOLCertificate "-framework Cocoa")
|
||||
|
||||
ImportCocoaPodsLibrary(
|
||||
MOLFCMClient
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLFCMClient/MOLFCMClient"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLFCMClient/Source/MOLFCMClient/MOLFCMClient.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLFCMClient/Source/MOLFCMClient/MOLFCMClient.h"
|
||||
)
|
||||
|
||||
target_link_libraries(MOLFCMClient PUBLIC MOLCertificate MOLAuthenticatingURLSession "-framework Cocoa")
|
||||
target_compile_options(MOLFCMClient PRIVATE -fobjc-weak)
|
||||
|
||||
ImportCocoaPodsLibrary(
|
||||
MOLXPCConnection
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLXPCConnection/Source/MOLXPCConnection"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLXPCConnection/Source/MOLXPCConnection/MOLXPCConnection.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/MOLXPCConnection/Source/MOLXPCConnection/MOLXPCConnection.m"
|
||||
)
|
||||
|
||||
target_link_libraries(MOLXPCConnection PUBLIC "-framework Cocoa")
|
||||
|
||||
ImportCocoaPodsLibrary(
|
||||
OCMock
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMExceptionReturnValueProvider.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMBlockCaller.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMExpectationRecorder.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMockObject.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMArgAction.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMMacroState.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMRealObjectForwarder.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMInvocationStub.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCPartialMockObject.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMock.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMStubRecorder.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMRecorder.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMInvocationExpectation.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMFunctions.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMIndirectReturnValueProvider.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCProtocolMockObject.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMBlockArgCaller.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMReturnValueProvider.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMBoxedReturnValueProvider.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCClassMockObject.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSObject+OCMAdditions.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMInvocationMatcher.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMPassByRefSetter.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMArg.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSNotificationCenter+OCMAdditions.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMObserverRecorder.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMNotificationPoster.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMVerifier.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMConstraint.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMLocation.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSInvocation+OCMAdditions.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSValue+OCMAdditions.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCObserverMockObject.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSMethodSignature+OCMAdditions.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMRealObjectForwarder.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMMacroState.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMArgAction.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMockObject.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMExpectationRecorder.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMBlockCaller.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMExceptionReturnValueProvider.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMIndirectReturnValueProvider.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMFunctions.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMStubRecorder.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMRecorder.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMInvocationExpectation.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCPartialMockObject.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMInvocationStub.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMPassByRefSetter.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMInvocationMatcher.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSObject+OCMAdditions.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCClassMockObject.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMBoxedReturnValueProvider.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMBlockArgCaller.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMReturnValueProvider.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCProtocolMockObject.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSMethodSignature+OCMAdditions.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMFunctionsPrivate.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSInvocation+OCMAdditions.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCObserverMockObject.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSValue+OCMAdditions.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMLocation.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMConstraint.h"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMVerifier.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMNotificationPoster.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMObserverRecorder.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/NSNotificationCenter+OCMAdditions.m"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/OCMock/Source/OCMock/OCMArg.h"
|
||||
)
|
||||
|
||||
target_link_libraries(OCMock PUBLIC "-framework Cocoa")
|
||||
target_compile_options(OCMock PRIVATE -fno-objc-arc)
|
||||
endfunction()
|
||||
|
||||
function(ImportCocoaPodsLibrary library_name source_directory)
|
||||
set_source_files_properties(${ARGN} PROPERTIES GENERATED True)
|
||||
|
||||
add_library("${library_name}" STATIC ${ARGN})
|
||||
add_dependencies("${library_name}" "${PROJECT_NAME}")
|
||||
|
||||
target_include_directories("${library_name}" PUBLIC
|
||||
"${source_directory}"
|
||||
"${CMAKE_SOURCE_DIR}/Pods/Headers/Public"
|
||||
)
|
||||
|
||||
target_compile_options("${library_name}" PUBLIC
|
||||
-Wno-deprecated -fmodules -fcxx-modules -fobjc-arc
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_options("${library_name}" PRIVATE -g3)
|
||||
endif()
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_options("${library_name}" PRIVATE -O0)
|
||||
else()
|
||||
target_compile_options("${library_name}" PRIVATE -O3)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
@@ -20,7 +20,5 @@
|
||||
<true />
|
||||
<key>ProcessType</key>
|
||||
<string>Interactive</string>
|
||||
<key>ThrottleInterval</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- Minimal Configuration -->
|
||||
<key>ClientMode</key>
|
||||
<integer>1</integer>
|
||||
|
||||
<!-- For documentation of other keys, see the following URL:
|
||||
https://github.com/google/santa/wiki/Configuration-Keys -->
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -39,6 +39,12 @@ GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
mkdir -p /usr/local/bin
|
||||
/bin/ln -s /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin
|
||||
|
||||
if [ ! -d /var/db/santa ] ; then
|
||||
mkdir /var/db/santa
|
||||
fi
|
||||
|
||||
cp ${SOURCE}/conf/com.google.santa.example.mobileconfig /var/db/santa
|
||||
|
||||
/bin/cp ${SOURCE}/conf/com.google.santad.plist /Library/LaunchDaemons
|
||||
/bin/cp ${SOURCE}/conf/com.google.santagui.plist /Library/LaunchAgents
|
||||
/bin/cp ${SOURCE}/conf/com.google.santa.asl.conf /etc/asl/
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
|
||||
[ "$EUID" != 0 ] && printf "%s\n" "This requires running as root/sudo." && exit 1
|
||||
|
||||
if [[ -d "binaries" ]]; then
|
||||
SOURCE="."
|
||||
elif [[ -d "../binaries" ]]; then
|
||||
SOURCE=".."
|
||||
else
|
||||
echo "Can't find binaries, run install.sh from inside the conf directory" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
/bin/launchctl remove com.google.santad
|
||||
sleep 1
|
||||
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
|
||||
@@ -21,6 +30,7 @@ user=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
/bin/rm -f /private/etc/asl/com.google.santa.asl.conf
|
||||
/bin/rm -f /private/etc/newsyslog.d/com.google.santa.newsyslog.conf
|
||||
/bin/rm -f /usr/local/bin/santactl # just a symlink
|
||||
|
||||
#uncomment to remove the config file and all databases, log files
|
||||
#/bin/rm -rf /var/db/santa
|
||||
#/bin/rm -f /var/log/santa*
|
||||
|
||||
@@ -37,6 +37,7 @@ Two configuration methods can be used to control Santa: a local configuration pr
|
||||
| MachineIDKey | String | The key to use on MachineIDPlist. |
|
||||
| EventLogType | String | Defines how event logs are stored. Options are 1) syslog: Sent to ASL or ULS (if built with the 10.12 SDK or later). 2) filelog: Sent to a file on disk. Use EventLogPath to specify a path. Defaults to filelog |
|
||||
| EventLogPath | String | If EventLogType is set to filelog, EventLogPath will provide the path to save logs. Defaults to /var/db/santa/santa.log. If you change this value ensure you also update com.google.santa.newsyslog.conf with the new path. |
|
||||
| EnableMachineIDDecoration | Bool | If YES, this appends the MachineID to the end of each log line. Defaults to NO. |
|
||||
|
||||
*overridable by the sync server: run `santactl status` to check the current running config
|
||||
|
||||
|
||||
25
Fuzzing/CMakeLists.txt
Normal file
25
Fuzzing/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
|
||||
set(LIBFUZZER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libFuzzer/bin/libFuzzer.a")
|
||||
|
||||
function(ImportLibFuzzer)
|
||||
add_custom_command(
|
||||
OUTPUT "${LIBFUZZER_PATH}"
|
||||
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/libFuzzer/build.sh"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/libFuzzer/build.sh"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libFuzzer"
|
||||
COMMENT "Building libFuzzer..."
|
||||
)
|
||||
|
||||
add_custom_target(libfuzzer_builder DEPENDS "${LIBFUZZER_PATH}")
|
||||
endfunction()
|
||||
|
||||
function(main)
|
||||
ImportLibFuzzer()
|
||||
|
||||
add_subdirectory(santacache)
|
||||
add_subdirectory(santad)
|
||||
add_subdirectory(santactl)
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
4
Fuzzing/libFuzzer/.gitignore
vendored
Normal file
4
Fuzzing/libFuzzer/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
bin
|
||||
llvm-*.src
|
||||
llvm-*.src.tar.xz
|
||||
|
||||
109
Fuzzing/libFuzzer/build.sh
Executable file
109
Fuzzing/libFuzzer/build.sh
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
LLVM_VERSION='5.0.1'
|
||||
LLVM_COMPILERRT_TARBALL_NAME="llvm-${LLVM_VERSION}.src.tar.xz"
|
||||
LLVM_COMPILERRT_SRC_FOLDER_NAME=`echo "${LLVM_COMPILERRT_TARBALL_NAME}" | cut -d '.' -f 1-4`
|
||||
LLVM_COMPILERRT_TARBALL_URL="http://releases.llvm.org/${LLVM_VERSION}/${LLVM_COMPILERRT_TARBALL_NAME}"
|
||||
|
||||
LIBFUZZER_FOLDER="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
LOG_FILE=`mktemp`
|
||||
|
||||
main() {
|
||||
echo "libFuzzer build script"
|
||||
|
||||
echo " > Checking dependencies..."
|
||||
checkDependencies || return 1
|
||||
|
||||
echo " > Entering libFuzzer folder..."
|
||||
cd "${LIBFUZZER_FOLDER}" > /dev/null 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "Failed to enter the libFuzzer folder: ${LIBFUZZER_FOLDER}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${LLVM_COMPILERRT_TARBALL_NAME}" ] ; then
|
||||
echo " > Downloading the LLVM tarball..."
|
||||
curl "${LLVM_COMPILERRT_TARBALL_URL}" -o "${LLVM_COMPILERRT_TARBALL_NAME}" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to download the LLVM tarball"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo " > An existing LLVM tarball was found"
|
||||
fi
|
||||
|
||||
if [ -d "${LLVM_COMPILERRT_SRC_FOLDER_NAME}" ] ; then
|
||||
echo " > Deleting existing LLVM folder..."
|
||||
rm -rf "${LLVM_COMPILERRT_SRC_FOLDER_NAME}" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to delete the existing source folder"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo " > Extracting the LLVM tarball..."
|
||||
tar xf "${LLVM_COMPILERRT_TARBALL_NAME}" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
rm "${LLVM_COMPILERRT_TARBALL_NAME}" "${LLVM_COMPILERRT_SRC_FOLDER_NAME}"
|
||||
dumpLogFile "Failed to extract the LLVM tarball"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -d "bin" ] ; then
|
||||
echo " > Deleting existing bin folder..."
|
||||
rm -rf "bin" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to delete the existing bin folder"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir "bin" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to create the bin folder"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo " > Building libFuzzer..."
|
||||
( cd "bin" && "../${LLVM_COMPILERRT_SRC_FOLDER_NAME}/lib/Fuzzer/build.sh" ) > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to build the library"
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf "\nFinished building libFuzzer\n"
|
||||
rm "${LOG_FILE}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
checkDependencies() {
|
||||
executable_list=( "clang++" "curl" "tar" )
|
||||
|
||||
for executable in "${executable_list[@]}" ; do
|
||||
which "${executable}" > /dev/null 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "The following program was not found: ${executable}"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
dumpLogFile() {
|
||||
if [ $# -eq 1 ] ; then
|
||||
local message="$1"
|
||||
else
|
||||
local message="An error has occurred"
|
||||
fi
|
||||
|
||||
printf "${message}\n"
|
||||
printf "Log file follows\n===\n"
|
||||
cat "${LOG_FILE}"
|
||||
printf "\n===\n"
|
||||
rm "${LOG_FILE}"
|
||||
}
|
||||
|
||||
main $@
|
||||
exit $?
|
||||
3
Fuzzing/santacache/.gitignore
vendored
Normal file
3
Fuzzing/santacache/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
santacache.dSYM
|
||||
santacache
|
||||
|
||||
35
Fuzzing/santacache/CMakeLists.txt
Normal file
35
Fuzzing/santacache/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(santacache_fuzzer)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
function(main)
|
||||
set(PROJECT_SOURCEFILES
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
add_executable("${PROJECT_NAME}" EXCLUDE_FROM_ALL ${PROJECT_SOURCEFILES})
|
||||
|
||||
add_dependencies("${PROJECT_NAME}" libfuzzer_builder)
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE "${LIBFUZZER_PATH}")
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santa-driver"
|
||||
)
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-g -O0 -Wno-deprecated-declarations
|
||||
-fsanitize-coverage=trace-pc-guard
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE
|
||||
-fsanitize=address -fno-omit-frame-pointer
|
||||
-mno-omit-leaf-frame-pointer
|
||||
)
|
||||
|
||||
AddFuzzTarget("${PROJECT_NAME}" 16 60 false)
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
BIN
Fuzzing/santacache/santacache_fuzzer_seed_corpus/example01
Executable file
BIN
Fuzzing/santacache/santacache_fuzzer_seed_corpus/example01
Executable file
Binary file not shown.
41
Fuzzing/santacache/src/main.cpp
Normal file
41
Fuzzing/santacache/src/main.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/// Copyright 2018 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include <SantaCache.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
static SantaCache<uint64_t, uint64_t> decision_cache(5000, 2);
|
||||
|
||||
std::uint64_t fields[2] = {};
|
||||
|
||||
if (size > 16) {
|
||||
std::cout << "Invalid size! Start with -max_len=16\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::memcpy(fields, data, size);
|
||||
|
||||
decision_cache.set(fields[0], fields[1]);
|
||||
auto returned_value = decision_cache.get(fields[0]);
|
||||
|
||||
if (returned_value != fields[1]) {
|
||||
std::cout << fields[0] << ", " << fields[1] << " -> " << returned_value << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
61
Fuzzing/santactl/CMakeLists.txt
Normal file
61
Fuzzing/santactl/CMakeLists.txt
Normal file
@@ -0,0 +1,61 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(santactl_fuzzer)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
function(main)
|
||||
set(PROJECT_SOURCEFILES
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncRuleDownload.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncConstants.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncStage.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncState.m"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTRule.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTLogging.m"
|
||||
|
||||
src/main.mm
|
||||
)
|
||||
|
||||
add_executable("${PROJECT_NAME}" EXCLUDE_FROM_ALL ${PROJECT_SOURCEFILES})
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-fobjc-arc -Wno-everything -fmodules -fcxx-modules
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE
|
||||
COCOAPODS=1
|
||||
)
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad"
|
||||
)
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE
|
||||
MOLCertificate MOLCodesignChecker
|
||||
MOLAuthenticatingURLSession FMDB MOLFCMClient
|
||||
)
|
||||
|
||||
#
|
||||
# Add libfuzzer
|
||||
#
|
||||
|
||||
add_dependencies("${PROJECT_NAME}" libfuzzer_builder)
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE "${LIBFUZZER_PATH}")
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-g -O0 -Wno-deprecated-declarations
|
||||
-fsanitize-coverage=trace-pc-guard
|
||||
)
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE
|
||||
-fsanitize=address -fno-omit-frame-pointer
|
||||
-mno-omit-leaf-frame-pointer
|
||||
)
|
||||
|
||||
AddFuzzTarget("${PROJECT_NAME}" 428 60 false)
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
16
Fuzzing/santactl/santactl_fuzzer_seed_corpus/example01
Normal file
16
Fuzzing/santactl/santactl_fuzzer_seed_corpus/example01
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"rule_type": "BINARY",
|
||||
"policy": "BLACKLIST",
|
||||
"sha256": "2dc104631939b4bdf5d6bccab76e166e37fe5e1605340cf68dab919df58b8eda",
|
||||
"custom_msg": "blacklist firefox"
|
||||
},
|
||||
{
|
||||
"rule_type": "CERTIFICATE",
|
||||
"policy": "BLACKLIST",
|
||||
"sha256": "e7726cf87cba9e25139465df5bd1557c8a8feed5c7dd338342d8da0959b63c8d",
|
||||
"custom_msg": "blacklist dash app certificate"
|
||||
}
|
||||
]
|
||||
}
|
||||
62
Fuzzing/santactl/src/main.mm
Normal file
62
Fuzzing/santactl/src/main.mm
Normal file
@@ -0,0 +1,62 @@
|
||||
/// Copyright 2018 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <SNTCommandSyncRuleDownload.h>
|
||||
#include <SNTCommandSyncState.h>
|
||||
#include <SNTCommandSyncConstants.h>
|
||||
#include <SNTRule.h>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
NSData *buffer = [NSData dataWithBytes:static_cast<const void *>(data) length:size];
|
||||
if (!buffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:buffer options:0 error:&error];
|
||||
if (!response) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (![response isKindOfClass:[NSDictionary class]]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (![response objectForKey:kRules]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SNTCommandSyncState *state = [[SNTCommandSyncState alloc] init];
|
||||
if (!state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SNTCommandSyncRuleDownload *obj = [[SNTCommandSyncRuleDownload alloc] initWithState:state];
|
||||
if (!obj) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (NSDictionary *ruleDict in response[kRules]) {
|
||||
SNTRule *rule = [obj ruleFromDictionary:ruleDict];
|
||||
if (rule) {
|
||||
std::cerr << "Rule: " << [[rule description] UTF8String] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
109
Fuzzing/santad/CMakeLists.txt
Normal file
109
Fuzzing/santad/CMakeLists.txt
Normal file
@@ -0,0 +1,109 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(santad_fuzzer)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
function(main)
|
||||
GenerateFuzzTarget(src/databaseRuleAddRules.mm 45 60 true)
|
||||
GenerateFuzzTarget(src/checkCacheForVnodeID.mm 16 60 true)
|
||||
GenerateFuzzTarget(src/databaseRemoveEventsWithIDs.mm 80 60 true)
|
||||
endfunction()
|
||||
|
||||
function(GenerateFuzzTarget source_file data_size timeout run_as_root)
|
||||
# Generate one new project for each source file in the 'src' folder. Copy
|
||||
# the project source files and settings from santactl
|
||||
set(PROJECT_SANTACTLSOURCEFILES
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/SNTCommand.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/SNTCommand.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/SNTCommandController.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/SNTCommandController.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Resources/santactl-Info.plist"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/SNTCommandBundleInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/SNTCommandCheckCache.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/SNTCommandFileInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/SNTCommandFlushCache.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/SNTCommandRule.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/SNTCommandStatus.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/SNTCommandVersion.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/NSData+Zlib.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/NSData+Zlib.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSync.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncConstants.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncConstants.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncEventUpload.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncEventUpload.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncLogUpload.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncLogUpload.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncManager.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncManager.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncPostflight.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncPostflight.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncPreflight.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncPreflight.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncRuleDownload.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncRuleDownload.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncStage.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncStage.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncState.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync/SNTCommandSyncState.m"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTConfigurator.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTFileInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTRule.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTStoredEvent.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTSystemInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCControlInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCSyncdInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTLogging.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTDropRootPrivs.m"
|
||||
)
|
||||
|
||||
get_filename_component(target_name "${source_file}" NAME_WE)
|
||||
set(target_name "santad_${target_name}_fuzzer")
|
||||
|
||||
add_executable("${target_name}" EXCLUDE_FROM_ALL ${source_file} ${PROJECT_SANTACTLSOURCEFILES})
|
||||
|
||||
target_compile_options("${target_name}" PRIVATE
|
||||
-fobjc-arc -Wno-everything -fmodules -fcxx-modules
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
target_compile_definitions("${target_name}" PRIVATE
|
||||
COCOAPODS=1
|
||||
)
|
||||
|
||||
target_include_directories("${target_name}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
)
|
||||
|
||||
target_link_libraries("${target_name}" PRIVATE
|
||||
MOLCertificate MOLCodesignChecker
|
||||
MOLAuthenticatingURLSession FMDB MOLFCMClient
|
||||
MOLXPCConnection
|
||||
)
|
||||
|
||||
#
|
||||
# Add libfuzzer
|
||||
#
|
||||
|
||||
add_dependencies("${target_name}" libfuzzer_builder)
|
||||
target_link_libraries("${target_name}" PRIVATE "${LIBFUZZER_PATH}")
|
||||
|
||||
target_compile_options("${target_name}" PRIVATE
|
||||
-g -O0 -Wno-deprecated-declarations
|
||||
-fsanitize-coverage=trace-pc-guard
|
||||
)
|
||||
|
||||
target_link_libraries("${target_name}" PRIVATE
|
||||
-fsanitize=address -fno-omit-frame-pointer
|
||||
-mno-omit-leaf-frame-pointer
|
||||
)
|
||||
|
||||
AddFuzzTarget("${target_name}" ${data_size} ${timeout} ${run_as_root})
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
BIN
Fuzzing/santad/santad_checkCacheForVnodeID_fuzzer_seed_corpus/example01
Executable file
BIN
Fuzzing/santad/santad_checkCacheForVnodeID_fuzzer_seed_corpus/example01
Executable file
Binary file not shown.
@@ -0,0 +1 @@
|
||||
К'.p▒└G╗М┐║ЙSЮ╝и▌РУерЭxt1iАЫШ9ы*H╩4R"═©$-├Уww╙+Р╝╘[┼иу╧oС┬ОwRpЗя≤х°е
|
||||
BIN
Fuzzing/santad/santad_databaseRuleAddRules_fuzzer_seed_corpus/example01
Executable file
BIN
Fuzzing/santad/santad_databaseRuleAddRules_fuzzer_seed_corpus/example01
Executable file
Binary file not shown.
55
Fuzzing/santad/src/checkCacheForVnodeID.mm
Normal file
55
Fuzzing/santad/src/checkCacheForVnodeID.mm
Normal file
@@ -0,0 +1,55 @@
|
||||
/// Copyright 2018 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
if (size > 16) {
|
||||
std::cerr << "Invalid buffer size of " << size
|
||||
<< " (should be <= 16)" << std::endl;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
santa_vnode_id_t vnodeID = {};
|
||||
std::memcpy(&vnodeID, data, size);
|
||||
|
||||
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
|
||||
daemonConn.invalidationHandler = ^{
|
||||
printf("An error occurred communicating with the daemon, is it running?\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
[daemonConn resume];
|
||||
|
||||
[[daemonConn remoteObjectProxy] checkCacheForVnodeID:vnodeID
|
||||
withReply:^(santa_action_t action) {
|
||||
if (action == ACTION_RESPOND_ALLOW) {
|
||||
std::cerr << "File exists in [whitelist] kernel cache" << std::endl;;
|
||||
} else if (action == ACTION_RESPOND_DENY) {
|
||||
std::cerr << "File exists in [blacklist] kernel cache" << std::endl;;
|
||||
} else if (action == ACTION_UNSET) {
|
||||
std::cerr << "File does not exist in cache" << std::endl;;
|
||||
}
|
||||
}];
|
||||
|
||||
return 0;
|
||||
}
|
||||
51
Fuzzing/santad/src/databaseRemoveEventsWithIDs.mm
Normal file
51
Fuzzing/santad/src/databaseRemoveEventsWithIDs.mm
Normal file
@@ -0,0 +1,51 @@
|
||||
/// Copyright 2018 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
auto *eventId = reinterpret_cast<const std::uint64_t *>(data);
|
||||
std::size_t eventIdCount = size / sizeof(std::uint64_t);
|
||||
if (eventIdCount == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
|
||||
daemonConn.invalidationHandler = ^{
|
||||
printf("An error occurred communicating with the daemon, is it running?\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
[daemonConn resume];
|
||||
|
||||
NSMutableSet *eventIds = [NSMutableSet setWithCapacity:eventIdCount];
|
||||
for (std::size_t i = 0; i < eventIdCount; i++) {
|
||||
auto id = [NSNumber numberWithInteger:eventId[i]];
|
||||
[eventIds addObject:id];
|
||||
}
|
||||
|
||||
[[daemonConn remoteObjectProxy] databaseRemoveEventsWithIDs:[eventIds allObjects]];
|
||||
return 0;
|
||||
}
|
||||
73
Fuzzing/santad/src/databaseRuleAddRules.mm
Normal file
73
Fuzzing/santad/src/databaseRuleAddRules.mm
Normal file
@@ -0,0 +1,73 @@
|
||||
/// Copyright 2018 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct InputData {
|
||||
std::uint32_t cleanSlate;
|
||||
std::uint32_t state;
|
||||
std::uint32_t type;
|
||||
char hash[33];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
if (size > sizeof(InputData)) {
|
||||
std::cerr << "Invalid buffer size of " << size
|
||||
<< " (should be <= " << sizeof(InputData)
|
||||
<< ")" << std::endl;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
InputData input_data = {};
|
||||
std::memcpy(&input_data, data, size);
|
||||
|
||||
SNTRule *newRule = [[SNTRule alloc] init];
|
||||
newRule.state = (SNTRuleState) input_data.state;
|
||||
newRule.type = (SNTRuleType) input_data.type;
|
||||
newRule.shasum = @(input_data.hash);
|
||||
newRule.customMsg = @"";
|
||||
|
||||
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
|
||||
daemonConn.invalidationHandler = ^{
|
||||
printf("An error occurred communicating with the daemon, is it running?\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
[daemonConn resume];
|
||||
[[daemonConn remoteObjectProxy] databaseRuleAddRules:@[newRule]
|
||||
cleanSlate:NO
|
||||
reply:^(NSError *error) {
|
||||
if (!error) {
|
||||
if (newRule.state == SNTRuleStateRemove) {
|
||||
printf("Removed rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
|
||||
} else {
|
||||
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
return 0;
|
||||
}
|
||||
20
Podfile.lock
20
Podfile.lock
@@ -4,12 +4,12 @@ PODS:
|
||||
- FMDB/standard (2.7.2)
|
||||
- MOLAuthenticatingURLSession (2.4):
|
||||
- MOLCertificate (~> 1.8)
|
||||
- MOLCertificate (1.8)
|
||||
- MOLCertificate (1.9)
|
||||
- MOLCodesignChecker (1.10):
|
||||
- MOLCertificate (~> 1.8)
|
||||
- MOLFCMClient (1.7):
|
||||
- MOLAuthenticatingURLSession (~> 2.4)
|
||||
- MOLXPCConnection (1.1):
|
||||
- MOLXPCConnection (1.2):
|
||||
- MOLCodesignChecker (~> 1.9)
|
||||
- OCMock (3.4.1)
|
||||
|
||||
@@ -22,15 +22,25 @@ DEPENDENCIES:
|
||||
- MOLXPCConnection
|
||||
- OCMock
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- FMDB
|
||||
- MOLAuthenticatingURLSession
|
||||
- MOLCertificate
|
||||
- MOLCodesignChecker
|
||||
- MOLFCMClient
|
||||
- MOLXPCConnection
|
||||
- OCMock
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
FMDB: 6198a90e7b6900cfc046e6bc0ef6ebb7be9236aa
|
||||
MOLAuthenticatingURLSession: c238aa1c9a7b1077eb39a6f40204bfe76a7d204e
|
||||
MOLCertificate: c999513316d511c69f290fbf313dfe8dca4ad592
|
||||
MOLCertificate: e9e88a396c57032cab847f51a46e20c730cd752a
|
||||
MOLCodesignChecker: b0d5db9d2f9bd94e0fd093891a5d40e5ad77cbc0
|
||||
MOLFCMClient: ee45348909351f232e2759c580329072ae7e02d4
|
||||
MOLXPCConnection: de9d5535928f59766a768384e411077b83ec2f9c
|
||||
MOLXPCConnection: c27af5cb1c43b18319698b0e568a8ddc2fc1e306
|
||||
OCMock: 2cd0716969bab32a2283ff3a46fd26a8c8b4c5e3
|
||||
|
||||
PODFILE CHECKSUM: ddca043a7ace9ec600c108621c56d13a50d17236
|
||||
|
||||
COCOAPODS: 1.4.0
|
||||
COCOAPODS: 1.5.3
|
||||
|
||||
25
README.md
25
README.md
@@ -43,7 +43,7 @@ correctly, but a rule for a binary's fingerprint will override a decision for a
|
||||
certificate; i.e. you can whitelist a certificate while blacklisting a binary
|
||||
signed with that certificate, or vice-versa.
|
||||
|
||||
* Path-based rules (via NSRegularExpression/ICU): This allows a similar feature to that found in Managed Client (the precursor to configuration profiles, which used the same implementation mechanism), Application Launch Restrictions via the mcxalr binary. This implementation carries the added benefit of being configurable via regex, and not relying on LaunchServices. As detailed in the wiki, when evaluating rules this holds the lowest precendence.
|
||||
* Path-based rules (via NSRegularExpression/ICU): This allows a similar feature to that found in Managed Client (the precursor to configuration profiles, which used the same implementation mechanism), Application Launch Restrictions via the mcxalr binary. This implementation carries the added benefit of being configurable via regex, and not relying on LaunchServices. As detailed in the wiki, when evaluating rules this holds the lowest precedence.
|
||||
|
||||
* Failsafe cert rules: You cannot put in a deny rule that would block the certificate used to sign launchd, a.k.a. pid 1, and therefore all components used in macOS. The binaries in every OS update (and in some cases entire new versions) are therefore auto-whitelisted. This does not affect binaries from Apple's App Store, which use various certs that change regularly for common apps. Likewise, you cannot blacklist Santa itself, and Santa uses a distinct separate cert than other Google apps.
|
||||
|
||||
@@ -124,7 +124,7 @@ A tool like Santa doesn't really lend itself to screenshots, so here's a video i
|
||||
<img src="https://zippy.gfycat.com/MadFatalAmphiuma.gif" alt="Santa Block Video" />
|
||||
</p>
|
||||
|
||||
Building
|
||||
Building with Xcode
|
||||
========
|
||||
|
||||
```sh
|
||||
@@ -144,6 +144,27 @@ and for security-reasons parts of Santa will not operate properly if not signed.
|
||||
|
||||
For more details on building see the [building.md](https://github.com/google/santa/blob/master/Docs/development/building.md) document.
|
||||
|
||||
Building with CMake
|
||||
========
|
||||
### General steps
|
||||
1. Install Xcode and the command line tools
|
||||
2. Install CMake using homebrew
|
||||
3. Clone the santa source code repository
|
||||
4. Set the signing key
|
||||
5. Create a build folder and configure the project
|
||||
6. Run make
|
||||
|
||||
Example
|
||||
git clone https://github.com/google/santa.git
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
export CODESIGN_IDENTITY=XXX
|
||||
cmake ../santa
|
||||
|
||||
make -j `sysctl -n hw.ncpu`
|
||||
The CODESIGN_IDENTITY parameter can also be passed directly to CMake: cmake -DCODESIGN_IDENTITY=XXX /path/to/source/code
|
||||
|
||||
Kext Signing
|
||||
============
|
||||
Kernel extensions on macOS 10.9 and later must be signed using an Apple-provided
|
||||
|
||||
16
Rakefile
16
Rakefile
@@ -155,20 +155,8 @@ namespace :tests do
|
||||
|
||||
desc "Tests: Kernel"
|
||||
task :kernel do
|
||||
Rake::Task['unload'].invoke()
|
||||
Rake::Task['install:debug'].invoke()
|
||||
Rake::Task['load_kext'].invoke
|
||||
FileUtils.mkdir_p("/tmp/santa_kerneltests_tmp")
|
||||
begin
|
||||
puts "\033[?25l\033[12h" # hide cursor
|
||||
puts "Running kernel tests"
|
||||
system "cd /tmp/santa_kerneltests_tmp && sudo #{xcodebuilddir}/Debug/KernelTests"
|
||||
rescue Exception
|
||||
ensure
|
||||
puts "\033[?25h\033[12l\n\n" # unhide cursor
|
||||
FileUtils.rm_rf("/tmp/santa_kerneltests_tmp")
|
||||
Rake::Task['unload_kext'].execute
|
||||
end
|
||||
Rake::Task['build:debug'].invoke()
|
||||
system "sudo #{xcodebuilddir}/Debug/KernelTests"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
0D10BE861A0AABD600C0C944 /* SNTDropRootPrivs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D10BE851A0AABD600C0C944 /* SNTDropRootPrivs.m */; };
|
||||
0D10BE871A0AABD600C0C944 /* SNTDropRootPrivs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D10BE851A0AABD600C0C944 /* SNTDropRootPrivs.m */; };
|
||||
0D10BE891A0AAF6700C0C944 /* SNTDropRootPrivs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D10BE851A0AABD600C0C944 /* SNTDropRootPrivs.m */; };
|
||||
0D10D18420C19445008251ED /* SNTCommandCacheHistogram.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D10D18320C19445008251ED /* SNTCommandCacheHistogram.m */; };
|
||||
0D1B477019A53419008CADD3 /* SNTAboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1B476E19A53419008CADD3 /* SNTAboutWindowController.m */; };
|
||||
0D1B477119A53419008CADD3 /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0D1B476F19A53419008CADD3 /* AboutWindow.xib */; };
|
||||
0D202D191CDD2EE500A88F16 /* SNTCommandSyncTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D202D181CDD2EE500A88F16 /* SNTCommandSyncTest.m */; };
|
||||
@@ -184,6 +185,11 @@
|
||||
C7FB56F71DBFB480004E14EF /* SNTXPCSyncdInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FB56F51DBFB480004E14EF /* SNTXPCSyncdInterface.m */; };
|
||||
C7FB57001DBFC213004E14EF /* SNTSyncdQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FB56FF1DBFC213004E14EF /* SNTSyncdQueue.m */; };
|
||||
D1761D2DAE8C790F29AFE6C9 /* libPods-LogicTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D949AA996AEAC326A4F6596 /* libPods-LogicTests.a */; };
|
||||
D221001320AB35FB003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = D221001020AB35F3003C65C2 /* SNTXPCUnprivilegedControlInterface.m */; };
|
||||
D221001420AB3609003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = D221001020AB35F3003C65C2 /* SNTXPCUnprivilegedControlInterface.m */; };
|
||||
D221001520AB360A003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = D221001020AB35F3003C65C2 /* SNTXPCUnprivilegedControlInterface.m */; };
|
||||
D221001620AB360A003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = D221001020AB35F3003C65C2 /* SNTXPCUnprivilegedControlInterface.m */; };
|
||||
D221001720AB360B003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = D221001020AB35F3003C65C2 /* SNTXPCUnprivilegedControlInterface.m */; };
|
||||
D6D356C5BC8709C2E355C113 /* libPods-santactl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E3B4B2438711ACD7D560014 /* libPods-santactl.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -286,6 +292,7 @@
|
||||
0D0A1EC5191AB9B000B8450F /* SNTCommandSyncPostflight.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncPostflight.m; sourceTree = "<group>"; };
|
||||
0D10BE851A0AABD600C0C944 /* SNTDropRootPrivs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTDropRootPrivs.m; sourceTree = "<group>"; };
|
||||
0D10BE881A0AAC2100C0C944 /* SNTDropRootPrivs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTDropRootPrivs.h; sourceTree = "<group>"; };
|
||||
0D10D18320C19445008251ED /* SNTCommandCacheHistogram.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTCommandCacheHistogram.m; sourceTree = "<group>"; };
|
||||
0D1B476D19A53419008CADD3 /* SNTAboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTAboutWindowController.h; sourceTree = "<group>"; };
|
||||
0D1B476E19A53419008CADD3 /* SNTAboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTAboutWindowController.m; sourceTree = "<group>"; };
|
||||
0D1B476F19A53419008CADD3 /* AboutWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindow.xib; sourceTree = "<group>"; };
|
||||
@@ -448,6 +455,8 @@
|
||||
C7FB56F51DBFB480004E14EF /* SNTXPCSyncdInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTXPCSyncdInterface.m; sourceTree = "<group>"; };
|
||||
C7FB56FE1DBFC213004E14EF /* SNTSyncdQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTSyncdQueue.h; sourceTree = "<group>"; };
|
||||
C7FB56FF1DBFC213004E14EF /* SNTSyncdQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTSyncdQueue.m; sourceTree = "<group>"; };
|
||||
D221000F20AB35F3003C65C2 /* SNTXPCUnprivilegedControlInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTXPCUnprivilegedControlInterface.h; sourceTree = "<group>"; };
|
||||
D221001020AB35F3003C65C2 /* SNTXPCUnprivilegedControlInterface.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTXPCUnprivilegedControlInterface.m; sourceTree = "<group>"; };
|
||||
D7DEE68F05C49966396A2F10 /* Pods-santad-santabs.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad-santabs.release.xcconfig"; path = "Pods/Target Support Files/Pods-santad-santabs/Pods-santad-santabs.release.xcconfig"; sourceTree = "<group>"; };
|
||||
D9B2D7095028D87E12E63EF7 /* Pods-santad-santabs.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad-santabs.debug.xcconfig"; path = "Pods/Target Support Files/Pods-santad-santabs/Pods-santad-santabs.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
DF5FE88E12AB307864A53CE7 /* Pods-santactl.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santactl.release.xcconfig"; path = "Pods/Target Support Files/Pods-santactl/Pods-santactl.release.xcconfig"; sourceTree = "<group>"; };
|
||||
@@ -743,6 +752,8 @@
|
||||
0DCD604E19115A06006B445C /* SNTXPCNotifierInterface.m */,
|
||||
C7FB56F41DBFB480004E14EF /* SNTXPCSyncdInterface.h */,
|
||||
C7FB56F51DBFB480004E14EF /* SNTXPCSyncdInterface.m */,
|
||||
D221000F20AB35F3003C65C2 /* SNTXPCUnprivilegedControlInterface.h */,
|
||||
D221001020AB35F3003C65C2 /* SNTXPCUnprivilegedControlInterface.m */,
|
||||
);
|
||||
path = common;
|
||||
sourceTree = "<group>";
|
||||
@@ -801,6 +812,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C7DA62F81E241A02009BDF2C /* SNTCommandBundleInfo.m */,
|
||||
0D10D18320C19445008251ED /* SNTCommandCacheHistogram.m */,
|
||||
C76614EB1D142D3C00D150C1 /* SNTCommandCheckCache.m */,
|
||||
0DCD5FBE1909D64A006B445C /* SNTCommandFileInfo.m */,
|
||||
0DE4C8A518FF3B1700466D04 /* SNTCommandFlushCache.m */,
|
||||
@@ -929,8 +941,6 @@
|
||||
0D260DA818B68E12002A0B55 /* Sources */,
|
||||
0D260DA918B68E12002A0B55 /* Frameworks */,
|
||||
0D260DAA18B68E12002A0B55 /* Resources */,
|
||||
D49A3AB950AFD99741E9AF89 /* [CP] Embed Pods Frameworks */,
|
||||
23869BA352E2C86DEFE62819 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -949,7 +959,6 @@
|
||||
0DD98E671A5DD02000A754C6 /* Update Version Info */,
|
||||
0D35BD9A18FD71CE00921A21 /* Sources */,
|
||||
0D35BD9B18FD71CE00921A21 /* Frameworks */,
|
||||
2737FE8516A33567D3449943 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -969,8 +978,6 @@
|
||||
0D385DB2180DE4A900418BC6 /* Sources */,
|
||||
0D385DB3180DE4A900418BC6 /* Frameworks */,
|
||||
0D385DB4180DE4A900418BC6 /* Resources */,
|
||||
BA20035148DDEF5808B2C7EF /* [CP] Embed Pods Frameworks */,
|
||||
DDE76075391100F3BCE3634E /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -1012,7 +1019,6 @@
|
||||
0DD98E661A5DCED300A754C6 /* Update Version Info */,
|
||||
0D9A7F391759330400035EB5 /* Sources */,
|
||||
0D9A7F3A1759330400035EB5 /* Frameworks */,
|
||||
531BBB5BA4BF953D628D29AB /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -1033,7 +1039,6 @@
|
||||
C78227501E1C3C58006EB2D6 /* Sources */,
|
||||
C78227511E1C3C58006EB2D6 /* Frameworks */,
|
||||
C78227521E1C3C58006EB2D6 /* Resources */,
|
||||
FCF4BDAA180BD63C32AC85DE /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -1253,51 +1258,6 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
23869BA352E2C86DEFE62819 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
2737FE8516A33567D3449943 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-santactl/Pods-santactl-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
531BBB5BA4BF953D628D29AB /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-santad/Pods-santad-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9BF8830B1029605A497F13D5 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -1334,21 +1294,6 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
BA20035148DDEF5808B2C7EF /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Santa/Pods-Santa-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
C7F95F971E23F9E7007A6BF5 /* Update bundle version */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -1384,51 +1329,6 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
D49A3AB950AFD99741E9AF89 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DDE76075391100F3BCE3634E /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Santa/Pods-Santa-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
FCF4BDAA180BD63C32AC85DE /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-santad-santabs/Pods-santad-santabs-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -1484,6 +1384,7 @@
|
||||
0DD0D492194F9BEF005F27EB /* SNTLogging.m in Sources */,
|
||||
0DE71A761B95F7F900518526 /* SNTCachedDecision.m in Sources */,
|
||||
0DCD605919115E5A006B445C /* SNTXPCNotifierInterface.m in Sources */,
|
||||
D221001720AB360B003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */,
|
||||
0DE50F691912B0CD007B2B0C /* SNTRule.m in Sources */,
|
||||
0D202D1B1CDD465400A88F16 /* SNTCommandSyncState.m in Sources */,
|
||||
C73A4B9A1DC10753007B6789 /* SNTSyncdQueue.m in Sources */,
|
||||
@@ -1505,12 +1406,14 @@
|
||||
C776A1071DEE160500A56616 /* SNTCommandSyncManager.m in Sources */,
|
||||
0DCD605C19117A90006B445C /* SNTCommandSyncPreflight.m in Sources */,
|
||||
0D41640519197AD7006A356A /* SNTCommandSyncEventUpload.m in Sources */,
|
||||
0D10D18420C19445008251ED /* SNTCommandCacheHistogram.m in Sources */,
|
||||
0D42D2B919D2042900955F08 /* SNTConfigurator.m in Sources */,
|
||||
0DF395641AB76A7900CBC520 /* NSData+Zlib.m in Sources */,
|
||||
C7FB56F71DBFB480004E14EF /* SNTXPCSyncdInterface.m in Sources */,
|
||||
0D10BE871A0AABD600C0C944 /* SNTDropRootPrivs.m in Sources */,
|
||||
0DE4C8A618FF3B1700466D04 /* SNTCommandFlushCache.m in Sources */,
|
||||
4092327A1A51B66400A04527 /* SNTCommandRule.m in Sources */,
|
||||
D221001420AB3609003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */,
|
||||
C76614EC1D142D3C00D150C1 /* SNTCommandCheckCache.m in Sources */,
|
||||
0D416401191974F1006A356A /* SNTCommandSyncState.m in Sources */,
|
||||
0DC5D871192160180078A5C0 /* SNTCommandSyncLogUpload.m in Sources */,
|
||||
@@ -1537,6 +1440,7 @@
|
||||
0D89310F1C931986002E8D74 /* SNTRule.m in Sources */,
|
||||
0DCD605119115A06006B445C /* SNTXPCNotifierInterface.m in Sources */,
|
||||
0DB77FD91CCE824A004DF060 /* SNTBlockMessage.m in Sources */,
|
||||
D221001620AB360A003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */,
|
||||
0D827E6519DF392E006EC811 /* SNTConfigurator.m in Sources */,
|
||||
0D89310E1C931979002E8D74 /* SNTXPCControlInterface.m in Sources */,
|
||||
0D385DF2180DE51600418BC6 /* SNTMessageWindowController.m in Sources */,
|
||||
@@ -1575,6 +1479,7 @@
|
||||
0DB8ACC1185662DC00FEF9C7 /* SNTApplication.m in Sources */,
|
||||
0D9A7F421759330500035EB5 /* main.m in Sources */,
|
||||
0DA73C9F1934F8100056D7C4 /* SNTLogging.m in Sources */,
|
||||
D221001320AB35FB003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */,
|
||||
C7C721B11E23FF300051FAA6 /* SNTXPCBundleServiceInterface.m in Sources */,
|
||||
0DE71A751B95F7F900518526 /* SNTCachedDecision.m in Sources */,
|
||||
C7FB56F61DBFB480004E14EF /* SNTXPCSyncdInterface.m in Sources */,
|
||||
@@ -1605,6 +1510,7 @@
|
||||
C7C721B41E24042B0051FAA6 /* SNTStoredEvent.m in Sources */,
|
||||
C7C721B51E2408BE0051FAA6 /* SNTFileInfo.m in Sources */,
|
||||
C7BBA67E1E54C7E200F1E6A8 /* SNTXPCNotifierInterface.m in Sources */,
|
||||
D221001520AB360A003C65C2 /* SNTXPCUnprivilegedControlInterface.m in Sources */,
|
||||
C7479F071E5374BF0054C1CF /* SNTXPCControlInterface.m in Sources */,
|
||||
C7479F091E5374E50054C1CF /* SNTRule.m in Sources */,
|
||||
C79A23581E23F7E80037AFA8 /* main.m in Sources */,
|
||||
@@ -1692,6 +1598,7 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INSTALL_PATH = "";
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(OTHER_CFLAGS)",
|
||||
"-fcxx-modules",
|
||||
@@ -1726,6 +1633,7 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INSTALL_PATH = "";
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(OTHER_CFLAGS)",
|
||||
"-fcxx-modules",
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
debugAsWhichUser = "root"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
|
||||
139
Source/SantaGUI/CMakeLists.txt
Normal file
139
Source/SantaGUI/CMakeLists.txt
Normal file
@@ -0,0 +1,139 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(Santa)
|
||||
|
||||
function(CompileXib output_file_path_variable input_file_path)
|
||||
get_filename_component(input_file_name "${input_file_path}" NAME_WE)
|
||||
set(output_file_path "${CMAKE_CURRENT_BINARY_DIR}/${input_file_name}.nib")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${output_file_path}"
|
||||
COMMAND ibtool --compile "${output_file_path}" "${input_file_path}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMENT "Compiling: ${input_file_path}"
|
||||
)
|
||||
|
||||
set("${output_file_path_variable}" "${output_file_path}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# It is important to only use absolute file paths with actool
|
||||
function(CompileAssets asset_file_list_variable)
|
||||
set(asset_file_list
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/AppIcon.icns"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/Assets.car"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${asset_file_list}
|
||||
COMMAND xcrun actool --output-format=human-readable-text --notices --warnings --errors --platform macosx --minimum-deployment-target ${MACOSX_VERSION_MIN} --target-device mac --app-icon AppIcon --output-partial-info-plist "${CMAKE_CURRENT_BINARY_DIR}/Assets.plist" --compile "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/Resources/Images.xcassets"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMENT "Packaging resources"
|
||||
)
|
||||
|
||||
set("${asset_file_list_variable}" ${asset_file_list} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(main)
|
||||
# Include all files to make IDEs happy
|
||||
set(PROJECT_SOURCEFILES
|
||||
SNTAboutWindowController.h
|
||||
SNTAboutWindowController.m
|
||||
|
||||
SNTAccessibleTextField.h
|
||||
SNTAccessibleTextField.m
|
||||
|
||||
SNTAppDelegate.h
|
||||
SNTAppDelegate.m
|
||||
|
||||
SNTMessageWindow.h
|
||||
SNTMessageWindow.m
|
||||
|
||||
SNTMessageWindowController.h
|
||||
SNTMessageWindowController.m
|
||||
|
||||
SNTNotificationManager.h
|
||||
SNTNotificationManager.m
|
||||
|
||||
main.m
|
||||
|
||||
# The "common" folder contains some of the files required to build this target
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTConfigurator.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCControlInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCUnprivilegedControlInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTBlockMessage.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTRule.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTStoredEvent.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTSystemInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCBundleServiceInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCNotifierInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTLogging.m"
|
||||
)
|
||||
|
||||
set(PROJECT_XIBFILES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Resources/AboutWindow.xib"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Resources/MessageWindow.xib"
|
||||
)
|
||||
|
||||
foreach(xib_file ${PROJECT_XIBFILES})
|
||||
CompileXib(output_nib_file_path "${xib_file}")
|
||||
|
||||
list(APPEND PROJECT_RESOURCES "${output_nib_file_path}")
|
||||
list(APPEND PROJECT_SOURCEFILES "${output_nib_file_path}")
|
||||
endforeach()
|
||||
|
||||
CompileAssets(asset_file_list)
|
||||
foreach(asset_file ${asset_file_list})
|
||||
list(APPEND PROJECT_RESOURCES "${asset_file}")
|
||||
list(APPEND PROJECT_SOURCEFILES "${asset_file}")
|
||||
endforeach()
|
||||
|
||||
add_executable("${PROJECT_NAME}" MACOSX_BUNDLE ${PROJECT_SOURCEFILES})
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE -ObjC)
|
||||
|
||||
set_target_properties("${PROJECT_NAME}" PROPERTIES
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.google.SantaGUI"
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Resources/SantaGUI-Info.plist"
|
||||
RESOURCE "${PROJECT_RESOURCES}"
|
||||
)
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE
|
||||
MOLCertificate MOLCodesignChecker MOLXPCConnection
|
||||
|
||||
"-framework Cocoa"
|
||||
)
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-fobjc-arc -Wno-everything -fmodules
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE -g3)
|
||||
endif()
|
||||
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE
|
||||
COCOAPODS=1
|
||||
SANTAGUI=1
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE DEBUG=1)
|
||||
else()
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE NDEBUG=1)
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/SantaGUI"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad"
|
||||
)
|
||||
|
||||
add_custom_command(TARGET "${PROJECT_NAME}" POST_BUILD
|
||||
COMMAND codesign --force --verify --verbose --sign "${CODESIGN_IDENTITY}" "${CMAKE_BINARY_DIR}/Source/SantaGUI/Santa.app"
|
||||
COMMENT "Signing ${PROJECT_NAME} with the following identity: ${CODESIGN_IDENTITY}"
|
||||
)
|
||||
|
||||
# Ignore errors about the missing RESOURCE destination missing; this is a cmake bug
|
||||
install(TARGETS "${PROJECT_NAME}" BUNDLE DESTINATION "/Applications")
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
@@ -9,20 +9,24 @@
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Google, Inc.</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.${PRODUCT_NAME:rfc1034identifier}GUI</string>
|
||||
<string>com.google.SantaGUI</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<string>Santa</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<string>Santa</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<string>${MACOSX_VERSION_MIN}</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
// Create listener for return connection from daemon.
|
||||
NSXPCListener *listener = [NSXPCListener anonymousListener];
|
||||
self.daemonListener = [[MOLXPCConnection alloc] initServerWithListener:listener];
|
||||
self.daemonListener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
|
||||
self.daemonListener.privilegedInterface = [SNTXPCNotifierInterface notifierInterface];
|
||||
self.daemonListener.exportedObject = self.notificationManager;
|
||||
self.daemonListener.acceptedHandler = ^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
@@ -116,7 +116,7 @@
|
||||
// Create listener for return connection from the bundle service.
|
||||
NSXPCListener *listener = [NSXPCListener anonymousListener];
|
||||
self.bundleListener = [[MOLXPCConnection alloc] initServerWithListener:listener];
|
||||
self.bundleListener.exportedInterface = [SNTXPCNotifierInterface bundleNotifierInterface];
|
||||
self.bundleListener.privilegedInterface = [SNTXPCNotifierInterface bundleNotifierInterface];
|
||||
self.bundleListener.exportedObject = self.notificationManager;
|
||||
self.bundleListener.acceptedHandler = ^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
|
||||
@@ -98,6 +98,13 @@
|
||||
///
|
||||
@property(readonly, nonatomic) NSString *eventLogPath;
|
||||
|
||||
///
|
||||
/// Enabling this appends the Santa machine ID to the end of each log line. If nothing
|
||||
/// has been overriden, this is the host's UUID.
|
||||
/// Defaults to NO.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableMachineIDDecoration;
|
||||
|
||||
#pragma mark - GUI Settings
|
||||
|
||||
///
|
||||
|
||||
@@ -72,6 +72,8 @@ static NSString *const kFileChangesRegexKey = @"FileChangesRegex";
|
||||
static NSString *const kEventLogType = @"EventLogType";
|
||||
static NSString *const kEventLogPath = @"EventLogPath";
|
||||
|
||||
static NSString *const kEnableMachineIDDecoration = @"EnableMachineIDDecoration";
|
||||
|
||||
// The keys managed by a sync server or mobileconfig.
|
||||
static NSString *const kClientModeKey = @"ClientMode";
|
||||
static NSString *const kWhitelistRegexKey = @"WhitelistRegex";
|
||||
@@ -126,6 +128,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kMachineIDPlistKeyKey : string,
|
||||
kEventLogType : string,
|
||||
kEventLogPath : string,
|
||||
kEnableMachineIDDecoration : number,
|
||||
};
|
||||
_defaults = [NSUserDefaults standardUserDefaults];
|
||||
[_defaults addSuiteNamed:@"com.google.santa"];
|
||||
@@ -280,6 +283,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableMachineIDDecoration {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
#pragma mark Public Interface
|
||||
|
||||
- (SNTClientMode)clientMode {
|
||||
@@ -451,6 +458,11 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return self.configState[kEventLogPath] ?: @"/var/db/santa/santa.log";
|
||||
}
|
||||
|
||||
- (BOOL)enableMachineIDDecoration {
|
||||
NSNumber *number = self.configState[kEnableMachineIDDecoration];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
///
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
#ifndef SANTA__COMMON__KERNELCOMMON_H
|
||||
#define SANTA__COMMON__KERNELCOMMON_H
|
||||
|
||||
// Defines the lengths of paths and Vnode IDs passed around.
|
||||
#define MAX_VNODE_ID_STR 21 // digits in UINT64_MAX + 1 for NULL-terminator
|
||||
|
||||
// Defines the name of the userclient class and the driver bundle ID.
|
||||
#define USERCLIENT_CLASS "com_google_SantaDriver"
|
||||
#define USERCLIENT_ID "com.google.santa-driver"
|
||||
@@ -41,6 +38,7 @@ enum SantaDriverMethods {
|
||||
kSantaUserClientClearCache,
|
||||
kSantaUserClientCacheCount,
|
||||
kSantaUserClientCheckCache,
|
||||
kSantaUserClientCacheBucketCount,
|
||||
|
||||
// Any methods supported by the driver should be added above this line to
|
||||
// ensure this remains the count of methods.
|
||||
@@ -82,10 +80,27 @@ typedef enum {
|
||||
#define RESPONSE_VALID(x) \
|
||||
(x == ACTION_RESPOND_ALLOW || x == ACTION_RESPOND_DENY)
|
||||
|
||||
// Struct to manage vnode IDs
|
||||
typedef struct santa_vnode_id_t {
|
||||
uint64_t fsid;
|
||||
uint64_t fileid;
|
||||
|
||||
#ifdef __cplusplus
|
||||
bool operator==(const santa_vnode_id_t& rhs) const {
|
||||
return fsid == rhs.fsid && fileid == rhs.fileid;
|
||||
}
|
||||
// This _must not_ be used for anything security-sensitive. It exists solely to make
|
||||
// the msleep/wakeup calls easier.
|
||||
uint64_t unsafe_simple_id() const {
|
||||
return (((uint64_t)fsid << 32) | fileid);
|
||||
}
|
||||
#endif
|
||||
} santa_vnode_id_t;
|
||||
|
||||
// Message struct that is sent down the IODataQueue.
|
||||
typedef struct {
|
||||
santa_action_t action;
|
||||
uint64_t vnode_id;
|
||||
santa_vnode_id_t vnode_id;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
@@ -99,4 +114,10 @@ typedef struct {
|
||||
char pname[MAXPATHLEN];
|
||||
} santa_message_t;
|
||||
|
||||
// Used for the kSantaUserClientCacheBucketCount request.
|
||||
typedef struct {
|
||||
uint16_t per_bucket[1024];
|
||||
uint64_t start;
|
||||
} santa_bucket_count_t;
|
||||
|
||||
#endif // SANTA__COMMON__KERNELCOMMON_H
|
||||
|
||||
@@ -12,118 +12,56 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
|
||||
#import "SNTCachedDecision.h"
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTKernelCommon.h"
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
|
||||
@class SNTRule;
|
||||
@class SNTStoredEvent;
|
||||
@class MOLXPCConnection;
|
||||
#import "SNTXPCUnprivilegedControlInterface.h"
|
||||
|
||||
///
|
||||
/// Protocol implemented by santad and utilized by santactl
|
||||
/// Protocol implemented by santad and utilized by santactl (privileged operations)
|
||||
///
|
||||
@protocol SNTDaemonControlXPC
|
||||
@protocol SNTDaemonControlXPC <SNTUnprivilegedDaemonControlXPC>
|
||||
|
||||
///
|
||||
/// Kernel ops
|
||||
///
|
||||
- (void)cacheCounts:(void (^)(uint64_t rootCache, uint64_t nonRootCache))reply;
|
||||
- (void)flushCache:(void (^)(BOOL))reply;
|
||||
- (void)checkCacheForVnodeID:(uint64_t)vnodeID withReply:(void (^)(santa_action_t))reply;
|
||||
|
||||
///
|
||||
/// Database ops
|
||||
///
|
||||
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply;
|
||||
- (void)databaseRuleAddRules:(NSArray *)rules
|
||||
cleanSlate:(BOOL)cleanSlate
|
||||
reply:(void (^)(NSError *error))reply;
|
||||
- (void)databaseEventCount:(void (^)(int64_t count))reply;
|
||||
- (void)databaseEventsPending:(void (^)(NSArray *events))reply;
|
||||
- (void)databaseRemoveEventsWithIDs:(NSArray *)ids;
|
||||
- (void)databaseRuleForBinarySHA256:(NSString *)binarySHA256
|
||||
certificateSHA256:(NSString *)certificateSHA256
|
||||
reply:(void (^)(SNTRule *))reply;
|
||||
///
|
||||
/// Decision ops
|
||||
///
|
||||
|
||||
///
|
||||
/// @param filePath A Path to the file, can be nil.
|
||||
/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will
|
||||
/// be calculated by this method from the filePath.
|
||||
/// @param certificateSHA256 A SHA256 hash of the signing certificate, can be nil.
|
||||
/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be
|
||||
/// returned. Binary rules take precedence over cert rules.
|
||||
///
|
||||
- (void)decisionForFilePath:(NSString *)filePath
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
certificateSHA256:(NSString *)certificateSHA256
|
||||
reply:(void (^)(SNTEventState))reply;
|
||||
|
||||
///
|
||||
/// Config ops
|
||||
///
|
||||
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
|
||||
- (void)clientMode:(void (^)(SNTClientMode))reply;
|
||||
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)(void))reply;
|
||||
- (void)xsrfToken:(void (^)(NSString *))reply;
|
||||
- (void)setXsrfToken:(NSString *)token reply:(void (^)(void))reply;
|
||||
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
|
||||
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
|
||||
- (void)syncCleanRequired:(void (^)(BOOL))reply;
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)(void))reply;
|
||||
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)bundlesEnabled:(void (^)(BOOL))reply;
|
||||
- (void)setBundlesEnabled:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
|
||||
|
||||
///
|
||||
/// GUI Ops
|
||||
///
|
||||
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
|
||||
///
|
||||
/// Syncd Ops
|
||||
///
|
||||
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)pushNotifications:(void (^)(BOOL))reply;
|
||||
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message reply:(void (^)(void))reply;
|
||||
|
||||
///
|
||||
/// Bundle Ops
|
||||
///
|
||||
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event reply:(SNTBundleHashBlock)reply;
|
||||
- (void)syncBundleEvent:(SNTStoredEvent *)event relatedEvents:(NSArray<SNTStoredEvent *> *)events;
|
||||
|
||||
@end
|
||||
|
||||
@interface SNTXPCControlInterface : NSObject
|
||||
@interface SNTXPCControlInterface : SNTXPCUnprivilegedControlInterface
|
||||
|
||||
///
|
||||
/// Returns the MachService ID for this service.
|
||||
/// Internal method used to initialize the control interface
|
||||
///
|
||||
+ (NSString *)serviceId;
|
||||
|
||||
///
|
||||
/// Returns an initialized NSXPCInterface for the SNTDaemonControlXPC protocol.
|
||||
/// Ensures any methods that accept custom classes as arguments are set-up before returning
|
||||
///
|
||||
+ (NSXPCInterface *)controlInterface;
|
||||
|
||||
///
|
||||
/// Retrieve a pre-configured MOLXPCConnection for communicating with santad.
|
||||
/// Connections just needs any handlers set and then can be resumed and used.
|
||||
///
|
||||
+ (MOLXPCConnection *)configuredConnection;
|
||||
+ (void)initializeControlInterface:(NSXPCInterface *)r;
|
||||
|
||||
@end
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
return @"SantaXPCControl";
|
||||
}
|
||||
|
||||
+ (NSXPCInterface *)controlInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTDaemonControlXPC)];
|
||||
+ (void)initializeControlInterface:(NSXPCInterface *)r {
|
||||
[super initializeControlInterface:r];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(databaseEventsPending:)
|
||||
@@ -37,16 +37,11 @@
|
||||
forSelector:@selector(databaseRuleAddRules:cleanSlate:reply:)
|
||||
argumentIndex:0
|
||||
ofReply:NO];
|
||||
}
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(hashBundleBinariesForEvent:reply:)
|
||||
argumentIndex:1
|
||||
ofReply:YES];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(syncBundleEvent:relatedEvents:)
|
||||
argumentIndex:1
|
||||
ofReply:NO];
|
||||
+ (NSXPCInterface *)controlInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTDaemonControlXPC)];
|
||||
[self initializeControlInterface:r];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
119
Source/common/SNTXPCUnprivilegedControlInterface.h
Normal file
119
Source/common/SNTXPCUnprivilegedControlInterface.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/// Copyright 2015 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
|
||||
#import "SNTCachedDecision.h"
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTKernelCommon.h"
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
|
||||
@class SNTRule;
|
||||
@class SNTStoredEvent;
|
||||
@class MOLXPCConnection;
|
||||
|
||||
///
|
||||
/// Protocol implemented by santad and utilized by santactl (unprivileged operations)
|
||||
///
|
||||
@protocol SNTUnprivilegedDaemonControlXPC
|
||||
|
||||
///
|
||||
/// Kernel ops
|
||||
///
|
||||
- (void)cacheCounts:(void (^)(uint64_t count))reply;
|
||||
- (void)cacheBucketCount:(void (^)(NSArray *))reply;
|
||||
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply;
|
||||
- (void)driverConnectionEstablished:(void (^)(BOOL))reply;
|
||||
|
||||
///
|
||||
/// Database ops
|
||||
///
|
||||
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply;
|
||||
- (void)databaseEventCount:(void (^)(int64_t count))reply;
|
||||
|
||||
///
|
||||
/// Decision ops
|
||||
///
|
||||
|
||||
///
|
||||
/// @param filePath A Path to the file, can be nil.
|
||||
/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will
|
||||
/// be calculated by this method from the filePath.
|
||||
/// @param certificateSHA256 A SHA256 hash of the signing certificate, can be nil.
|
||||
/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be
|
||||
/// returned. Binary rules take precedence over cert rules.
|
||||
///
|
||||
- (void)decisionForFilePath:(NSString *)filePath
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
certificateSHA256:(NSString *)certificateSHA256
|
||||
reply:(void (^)(SNTEventState))reply;
|
||||
|
||||
///
|
||||
/// Config ops
|
||||
///
|
||||
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
|
||||
- (void)xsrfToken:(void (^)(NSString *))reply;
|
||||
- (void)clientMode:(void (^)(SNTClientMode))reply;
|
||||
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)syncCleanRequired:(void (^)(BOOL))reply;
|
||||
- (void)bundlesEnabled:(void (^)(BOOL))reply;
|
||||
|
||||
///
|
||||
/// GUI Ops
|
||||
///
|
||||
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
|
||||
///
|
||||
/// Syncd Ops
|
||||
///
|
||||
- (void)pushNotifications:(void (^)(BOOL))reply;
|
||||
|
||||
///
|
||||
/// Bundle Ops
|
||||
///
|
||||
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event reply:(SNTBundleHashBlock)reply;
|
||||
- (void)syncBundleEvent:(SNTStoredEvent *)event relatedEvents:(NSArray<SNTStoredEvent *> *)events;
|
||||
|
||||
@end
|
||||
|
||||
@interface SNTXPCUnprivilegedControlInterface : NSObject
|
||||
|
||||
///
|
||||
/// Returns the MachService ID for this service.
|
||||
///
|
||||
+ (NSString *)serviceId;
|
||||
|
||||
///
|
||||
/// Returns an initialized NSXPCInterface for the SNTUnprivilegedDaemonControlXPC protocol.
|
||||
/// Ensures any methods that accept custom classes as arguments are set-up before returning
|
||||
///
|
||||
+ (NSXPCInterface *)controlInterface;
|
||||
|
||||
///
|
||||
/// Retrieve a pre-configured MOLXPCConnection for communicating with santad.
|
||||
/// Connections just needs any handlers set and then can be resumed and used.
|
||||
///
|
||||
+ (MOLXPCConnection *)configuredConnection;
|
||||
|
||||
///
|
||||
/// Internal method used to initialize the control interface
|
||||
///
|
||||
|
||||
+ (void)initializeControlInterface:(NSXPCInterface *)r;
|
||||
|
||||
@end
|
||||
54
Source/common/SNTXPCUnprivilegedControlInterface.m
Normal file
54
Source/common/SNTXPCUnprivilegedControlInterface.m
Normal file
@@ -0,0 +1,54 @@
|
||||
/// Copyright 2015 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTXPCUnprivilegedControlInterface.h"
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTRule.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTXPCUnprivilegedControlInterface
|
||||
|
||||
+ (NSString *)serviceId {
|
||||
return @"SantaUnprivilegedXPCControl";
|
||||
}
|
||||
|
||||
+ (void)initializeControlInterface:(NSXPCInterface *)r {
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(hashBundleBinariesForEvent:reply:)
|
||||
argumentIndex:1
|
||||
ofReply:YES];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(syncBundleEvent:relatedEvents:)
|
||||
argumentIndex:1
|
||||
ofReply:NO];
|
||||
}
|
||||
|
||||
+ (NSXPCInterface *)controlInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTUnprivilegedDaemonControlXPC)];
|
||||
[self initializeControlInterface:r];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
+ (MOLXPCConnection *)configuredConnection {
|
||||
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceId]
|
||||
privileged:YES];
|
||||
c.remoteInterface = [self controlInterface];
|
||||
return c;
|
||||
}
|
||||
|
||||
@end
|
||||
112
Source/santa-driver/CMakeLists.txt
Normal file
112
Source/santa-driver/CMakeLists.txt
Normal file
@@ -0,0 +1,112 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(santa-driver)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
function(main)
|
||||
# Include all files to make IDEs happy
|
||||
set(PROJECT_INFOPLIST "${CMAKE_CURRENT_SOURCE_DIR}/Resources/santa-driver-Info.plist")
|
||||
|
||||
set(PROJECT_SOURCEFILES
|
||||
SantaCache.h
|
||||
|
||||
SantaDecisionManager.cc
|
||||
SantaDecisionManager.h
|
||||
|
||||
SantaDriver.cc
|
||||
SantaDriver.h
|
||||
|
||||
SantaDriverClient.cc
|
||||
SantaDriverClient.h
|
||||
|
||||
main.c
|
||||
|
||||
"${PROJECT_INFOPLIST}"
|
||||
)
|
||||
|
||||
add_library("${PROJECT_NAME}" MODULE ${PROJECT_SOURCEFILES})
|
||||
set_target_properties("${PROJECT_NAME}" PROPERTIES
|
||||
BUNDLE TRUE
|
||||
BUNDLE_EXTENSION "kext"
|
||||
MACOSX_BUNDLE_INFO_PLIST "${PROJECT_INFOPLIST}"
|
||||
)
|
||||
|
||||
# As we have to include the executables in the same bundle, make sure
|
||||
# we add them as dependencies
|
||||
add_dependencies("${PROJECT_NAME}" santad santactl santabs)
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0
|
||||
-nostdinc -fmodules -gmodules -fno-builtin -Wno-trigraphs -msoft-float -O0 -fno-common
|
||||
-fapple-kext -fasm-blocks -fstrict-aliasing -MMD -MT dependencies
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE -g3)
|
||||
endif()
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module
|
||||
-Wno-missing-field-initializers -Wno-missing-prototypes -Werror=return-type -Wdocumentation
|
||||
-Wunreachable-code -Werror=deprecated-objc-isa-usage -Werror=objc-root-class -Wno-missing-braces
|
||||
-Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter
|
||||
-Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized
|
||||
-Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion
|
||||
-Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion
|
||||
-Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wpointer-sign
|
||||
-Wno-newline-eof -Wdeprecated-declarations -Wno-sign-conversion -Winfinite-recursion -Wcomma
|
||||
-Wblock-capture-autoreleasing -Wstrict-prototypes -Wunguarded-availability
|
||||
)
|
||||
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE
|
||||
KERNEL
|
||||
KERNEL_PRIVATE
|
||||
DRIVER_PRIVATE
|
||||
APPLE
|
||||
NeXT
|
||||
CMAKE
|
||||
-DSANTA_VERSION="${SANTA_VERSION}"
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE DEBUG)
|
||||
else()
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE NDEBUG)
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" SYSTEM PRIVATE
|
||||
"${XCODE_ROOT_FOLDER}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${MACOSX_SDK_VERSION}.sdk/System/Library/Frameworks/Kernel.framework/PrivateHeaders"
|
||||
"${XCODE_ROOT_FOLDER}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${MACOSX_SDK_VERSION}.sdk/System/Library/Frameworks/Kernel.framework/Headers"
|
||||
)
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/santa-driver"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
)
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE
|
||||
-Xlinker -export_dynamic
|
||||
-Xlinker -no_deduplicate
|
||||
-Xlinker -kext
|
||||
-nostdlib -lkmodc++ -lkmod -lcc_kext
|
||||
)
|
||||
|
||||
add_custom_command(TARGET "${PROJECT_NAME}" POST_BUILD
|
||||
# Copy santad and santactl
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:santad> "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.kext/Contents/MacOS"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:santactl> "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.kext/Contents/MacOS"
|
||||
|
||||
# Copy the the santabs service
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_BINARY_DIR}/Source/santabs/santabs.xpc" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.kext/Contents/XPCServices/santabs.xpc"
|
||||
|
||||
# Sign everything
|
||||
COMMAND codesign --force --verify --verbose --sign "${CODESIGN_IDENTITY}" "${CMAKE_BINARY_DIR}/Source/santa-driver/santa-driver.kext"
|
||||
COMMENT "Building and signing santa-driver.kext"
|
||||
)
|
||||
|
||||
install(TARGETS "${PROJECT_NAME}" LIBRARY DESTINATION "/Library/Extensions")
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin/santactl)")
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
@@ -9,15 +9,15 @@
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Google, Inc.</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<string>com.google.santa-driver</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<string>santa-driver</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<string>santa-driver</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>IOKitPersonalities</key>
|
||||
<dict>
|
||||
<key>SantaDriver</key>
|
||||
|
||||
@@ -40,9 +40,21 @@
|
||||
#define OSDecrementAtomic(addr) OSAtomicDecrement64((volatile int64_t *)addr)
|
||||
#endif // KERNEL
|
||||
|
||||
/**
|
||||
A type to specialize to help SantaCache with its hashing.
|
||||
|
||||
The default works for numeric types with a multiplicative hash
|
||||
using a prime near to the golden ratio, per Knuth.
|
||||
*/
|
||||
template<typename T> uint64_t SantaCacheHasher(T const& t) {
|
||||
return t * 11400714819323198549UL;
|
||||
};
|
||||
|
||||
/**
|
||||
A somewhat simple, concurrent linked-list hash table intended for use in IOKit kernel extensions.
|
||||
Maps 64-bit unsigned integer keys to values.
|
||||
|
||||
The type used for keys must overload the == operator and a specialization of
|
||||
SantaCacheHasher must exist for it.
|
||||
|
||||
Enforces a maximum size by clearing all entries if a new value
|
||||
is added that would go over the maximum size declared at creation.
|
||||
@@ -50,7 +62,7 @@
|
||||
The number of buckets is calculated as `maximum_size` / `per_bucket`
|
||||
rounded up to the next power of 2. Locking is done per-bucket.
|
||||
*/
|
||||
template<class T> class SantaCache {
|
||||
template<typename KeyT, typename ValueT> class SantaCache {
|
||||
public:
|
||||
/**
|
||||
Initialize a newly created cache.
|
||||
@@ -65,8 +77,7 @@ template<class T> class SantaCache {
|
||||
if (unlikely(per_bucket < 1)) per_bucket = 1;
|
||||
if (unlikely(per_bucket > 64)) per_bucket = 64;
|
||||
max_size_ = maximum_size;
|
||||
bucket_count_ = 1 << (32 - __builtin_clz(
|
||||
((uint32_t)max_size_ / per_bucket) - 1));
|
||||
bucket_count_ = 1 << (32 - __builtin_clz(((uint32_t)max_size_ / per_bucket) - 1));
|
||||
buckets_ = (struct bucket *)IOMalloc(bucket_count_ * sizeof(struct bucket));
|
||||
bzero(buckets_, bucket_count_ * sizeof(struct bucket));
|
||||
}
|
||||
@@ -82,13 +93,13 @@ template<class T> class SantaCache {
|
||||
/**
|
||||
Get an element from the cache. Returns zero_ if item doesn't exist.
|
||||
*/
|
||||
T get(uint64_t key) {
|
||||
ValueT get(KeyT key) {
|
||||
struct bucket *bucket = &buckets_[hash(key)];
|
||||
lock(bucket);
|
||||
struct entry *entry = (struct entry *)((uintptr_t)bucket->head - 1);
|
||||
while (entry != nullptr) {
|
||||
if (entry->key == key) {
|
||||
T val = entry->value;
|
||||
ValueT val = entry->value;
|
||||
unlock(bucket);
|
||||
return val;
|
||||
}
|
||||
@@ -102,103 +113,39 @@ template<class T> class SantaCache {
|
||||
Set an element in the cache.
|
||||
|
||||
@note If the cache is full when this is called, this will
|
||||
empty the cache before inserting the new value.
|
||||
empty the cache before inserting the new value.
|
||||
|
||||
@param key, The key
|
||||
@param value, The value with parameterized type
|
||||
@param previous_value, If the has_prev_value parameter is true the new
|
||||
value will only be set if this parameter is equal to the provided value.
|
||||
This allows set to become a CAS operation.
|
||||
@param has_prev_value, Pass true if previous_value should be used.
|
||||
@param key The key.
|
||||
@param value The value with parameterized type.
|
||||
|
||||
@return the previous value (which may be zero_)
|
||||
@return true if the value was set.
|
||||
*/
|
||||
T set(uint64_t key, T value, T previous_value, bool has_prev_value) {
|
||||
struct bucket *bucket = &buckets_[hash(key)];
|
||||
lock(bucket);
|
||||
struct entry *entry = (struct entry *)((uintptr_t)bucket->head - 1);
|
||||
struct entry *previous_entry = nullptr;
|
||||
while (entry != nullptr) {
|
||||
if (entry->key == key) {
|
||||
T existing_value = entry->value;
|
||||
|
||||
if (has_prev_value && previous_value != existing_value) {
|
||||
unlock(bucket);
|
||||
return existing_value;
|
||||
}
|
||||
|
||||
entry->value = value;
|
||||
|
||||
if (value == zero_) {
|
||||
if (previous_entry != nullptr) {
|
||||
previous_entry->next = entry->next;
|
||||
} else {
|
||||
bucket->head = (struct entry *)((uintptr_t)entry->next + 1);
|
||||
}
|
||||
IOFreeAligned(entry, sizeof(struct entry));
|
||||
OSDecrementAtomic(&count_);
|
||||
}
|
||||
|
||||
unlock(bucket);
|
||||
return existing_value;
|
||||
}
|
||||
previous_entry = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
// If value is zero_, we're clearing but there's nothing to clear
|
||||
// so we don't need to do anything else. Alternatively, if has_prev_value
|
||||
// is true and is not zero_ we don't want to set a value.
|
||||
if (value == zero_ || (has_prev_value && previous_value != zero_)) {
|
||||
unlock(bucket);
|
||||
return zero_;
|
||||
}
|
||||
|
||||
// Check that adding this new item won't take the cache
|
||||
// over its maximum size.
|
||||
if (count_ + 1 > max_size_) {
|
||||
unlock(bucket);
|
||||
lock(&clear_bucket_);
|
||||
// Check again in case clear has already run while waiting for lock
|
||||
if (count_ + 1 > max_size_) {
|
||||
clear();
|
||||
}
|
||||
lock(bucket);
|
||||
unlock(&clear_bucket_);
|
||||
}
|
||||
|
||||
// Allocate a new entry, set the key and value, then put this new entry at
|
||||
// the head of this bucket's linked list.
|
||||
struct entry *new_entry = (struct entry *)IOMallocAligned(
|
||||
sizeof(struct entry), 2);
|
||||
new_entry->key = key;
|
||||
new_entry->value = value;
|
||||
new_entry->next = (struct entry *)((uintptr_t)bucket->head - 1);
|
||||
bucket->head = (struct entry *)((uintptr_t)new_entry + 1);
|
||||
OSIncrementAtomic(&count_);
|
||||
|
||||
unlock(bucket);
|
||||
return zero_;
|
||||
}
|
||||
|
||||
/**
|
||||
Overload to allow setting without providing a previous value
|
||||
*/
|
||||
T set(uint64_t key, T value) {
|
||||
bool set(KeyT key, ValueT value) {
|
||||
return set(key, value, {}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
Overload to allow setting while providing a previous value
|
||||
Set an element in the cache.
|
||||
|
||||
@note If the cache is full when this is called, this will
|
||||
empty the cache before inserting the new value.
|
||||
|
||||
@param key The key.
|
||||
@param value The value with parameterized type.
|
||||
@param previous_value the new value will only be set if this
|
||||
parameter is equal to the existing value in the cache.
|
||||
This allows set to become a CAS operation.
|
||||
|
||||
@return true if the value was set
|
||||
*/
|
||||
T set(uint64_t key, T value, T previous_value) {
|
||||
bool set(KeyT key, ValueT value, ValueT previous_value) {
|
||||
return set(key, value, previous_value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
An alias for `set(key, zero_)`
|
||||
*/
|
||||
inline void remove(uint64_t key) {
|
||||
inline void remove(KeyT key) {
|
||||
set(key, zero_);
|
||||
}
|
||||
|
||||
@@ -238,10 +185,42 @@ template<class T> class SantaCache {
|
||||
return count_;
|
||||
}
|
||||
|
||||
/**
|
||||
Fill in the per_bucket_counts array with the number of entries in each bucket.
|
||||
|
||||
The per_buckets_count array will contain the per-bucket counts, up to the number
|
||||
in array_size. The start_bucket parameter will determine which bucket to start off
|
||||
with and upon return will contain either 0 if no buckets are remaining or the next
|
||||
bucket to begin with when called again.
|
||||
*/
|
||||
void bucket_counts(uint16_t *per_bucket_counts, uint16_t *array_size, uint64_t *start_bucket) {
|
||||
if (per_bucket_counts == nullptr || array_size == nullptr || start_bucket == nullptr) return;
|
||||
|
||||
uint64_t start = *start_bucket;
|
||||
uint16_t size = *array_size;
|
||||
if (start + size > bucket_count_) size = bucket_count_ - start;
|
||||
|
||||
for (uint16_t i = 0; i < size; ++i) {
|
||||
uint16_t count = 0;
|
||||
struct bucket *bucket = &buckets_[start++];
|
||||
lock(bucket);
|
||||
struct entry *entry = (struct entry *)((uintptr_t)bucket->head - 1);
|
||||
while (entry != nullptr) {
|
||||
if (entry->value != zero_) ++count;
|
||||
entry = entry->next;
|
||||
}
|
||||
unlock(bucket);
|
||||
per_bucket_counts[i] = count;
|
||||
}
|
||||
|
||||
*array_size = size;
|
||||
*start_bucket = (start >= bucket_count_) ? 0 : start;
|
||||
}
|
||||
|
||||
private:
|
||||
struct entry {
|
||||
uint64_t key;
|
||||
T value;
|
||||
KeyT key;
|
||||
ValueT value;
|
||||
struct entry *next;
|
||||
};
|
||||
|
||||
@@ -251,6 +230,88 @@ template<class T> class SantaCache {
|
||||
struct entry *head;
|
||||
};
|
||||
|
||||
/**
|
||||
Set an element in the cache.
|
||||
|
||||
@note If the cache is full when this is called, this will
|
||||
empty the cache before inserting the new value.
|
||||
|
||||
@param key The key
|
||||
@param value The value with parameterized type
|
||||
@param previous_value If has_prev_value is true, the new value will only
|
||||
be set if this parameter is equal to the existing value in the cache.
|
||||
This allows set to become a CAS operation.
|
||||
@param has_prev_value Pass true if previous_value should be used.
|
||||
|
||||
@return true if the entry was set, false if it was not
|
||||
*/
|
||||
bool set(KeyT key, ValueT value, ValueT previous_value, bool has_prev_value) {
|
||||
struct bucket *bucket = &buckets_[hash(key)];
|
||||
lock(bucket);
|
||||
struct entry *entry = (struct entry *)((uintptr_t)bucket->head - 1);
|
||||
struct entry *previous_entry = nullptr;
|
||||
while (entry != nullptr) {
|
||||
if (entry->key == key) {
|
||||
ValueT existing_value = entry->value;
|
||||
|
||||
if (has_prev_value && previous_value != existing_value) {
|
||||
unlock(bucket);
|
||||
return false;
|
||||
}
|
||||
|
||||
entry->value = value;
|
||||
|
||||
if (value == zero_) {
|
||||
if (previous_entry != nullptr) {
|
||||
previous_entry->next = entry->next;
|
||||
} else {
|
||||
bucket->head = (struct entry *)((uintptr_t)entry->next + 1);
|
||||
}
|
||||
IOFreeAligned(entry, sizeof(struct entry));
|
||||
OSDecrementAtomic(&count_);
|
||||
}
|
||||
|
||||
unlock(bucket);
|
||||
return true;
|
||||
}
|
||||
previous_entry = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
// If value is zero_, we're clearing but there's nothing to clear
|
||||
// so we don't need to do anything else. Alternatively, if has_prev_value
|
||||
// is true and is not zero_ we don't want to set a value.
|
||||
if (value == zero_ || (has_prev_value && previous_value != zero_)) {
|
||||
unlock(bucket);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that adding this new item won't take the cache
|
||||
// over its maximum size.
|
||||
if (count_ + 1 > max_size_) {
|
||||
unlock(bucket);
|
||||
lock(&clear_bucket_);
|
||||
// Check again in case clear has already run while waiting for lock
|
||||
if (count_ + 1 > max_size_) {
|
||||
clear();
|
||||
}
|
||||
lock(bucket);
|
||||
unlock(&clear_bucket_);
|
||||
}
|
||||
|
||||
// Allocate a new entry, set the key and value, then put this new entry at
|
||||
// the head of this bucket's linked list.
|
||||
struct entry *new_entry = (struct entry *)IOMallocAligned(sizeof(struct entry), 2);
|
||||
new_entry->key = key;
|
||||
new_entry->value = value;
|
||||
new_entry->next = (struct entry *)((uintptr_t)bucket->head - 1);
|
||||
bucket->head = (struct entry *)((uintptr_t)new_entry + 1);
|
||||
OSIncrementAtomic(&count_);
|
||||
|
||||
unlock(bucket);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Lock a bucket. Spins until the lock is acquired.
|
||||
*/
|
||||
@@ -277,7 +338,7 @@ template<class T> class SantaCache {
|
||||
/**
|
||||
Holder for a 'zero' entry for the current type
|
||||
*/
|
||||
const T zero_ = {};
|
||||
const ValueT zero_ = {};
|
||||
|
||||
/**
|
||||
Special bucket used when automatically clearing due to size
|
||||
@@ -288,13 +349,9 @@ template<class T> class SantaCache {
|
||||
|
||||
/**
|
||||
Hash a key to determine which bucket it belongs in.
|
||||
|
||||
Multiplicative hash using a prime near to the golden ratio, per Knuth.
|
||||
This seems to have good bucket distribution generally and for the range of
|
||||
values we expect to see.
|
||||
*/
|
||||
inline uint64_t hash(uint64_t input) const {
|
||||
return (input * 11400714819323198549ul) % bucket_count_;
|
||||
inline uint64_t hash(KeyT input) const {
|
||||
return SantaCacheHasher<KeyT>(input) % bucket_count_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ OSDefineMetaClassAndStructors(SantaDecisionManager, OSObject);
|
||||
|
||||
#pragma mark Object Lifecycle
|
||||
|
||||
template<> uint64_t SantaCacheHasher<santa_vnode_id_t>(santa_vnode_id_t const& s) {
|
||||
return (SantaCacheHasher<uint64_t>(s.fsid) << 1) ^ SantaCacheHasher<uint64_t>(s.fileid);
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::init() {
|
||||
if (!super::init()) return false;
|
||||
|
||||
@@ -29,9 +33,8 @@ bool SantaDecisionManager::init() {
|
||||
decision_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
|
||||
log_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
|
||||
|
||||
root_decision_cache_ = new SantaCache<uint64_t>(5000, 2);
|
||||
non_root_decision_cache_ = new SantaCache<uint64_t>(500, 2);
|
||||
vnode_pid_map_ = new SantaCache<uint64_t>(2000, 5);
|
||||
decision_cache_ = new SantaCache<santa_vnode_id_t, uint64_t>(10000, 2);
|
||||
vnode_pid_map_ = new SantaCache<santa_vnode_id_t, uint64_t>(2000, 5);
|
||||
|
||||
decision_dataqueue_ = IOSharedDataQueue::withEntries(
|
||||
kMaxDecisionQueueEvents, sizeof(santa_message_t));
|
||||
@@ -42,17 +45,12 @@ bool SantaDecisionManager::init() {
|
||||
if (!log_dataqueue_) return kIOReturnNoMemory;
|
||||
|
||||
client_pid_ = 0;
|
||||
root_fsid_ = 0;
|
||||
|
||||
ts_ = { .tv_sec = kRequestLoopSleepMilliseconds / 1000,
|
||||
.tv_nsec = kRequestLoopSleepMilliseconds % 1000 * 1000000 };
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::free() {
|
||||
delete root_decision_cache_;
|
||||
delete non_root_decision_cache_;
|
||||
delete decision_cache_;
|
||||
delete vnode_pid_map_;
|
||||
|
||||
if (decision_dataqueue_lock_) {
|
||||
@@ -93,17 +91,6 @@ void SantaDecisionManager::ConnectClient(pid_t pid) {
|
||||
|
||||
client_pid_ = pid;
|
||||
|
||||
// Determine root fsid
|
||||
vfs_context_t ctx = vfs_context_create(NULL);
|
||||
if (ctx) {
|
||||
vnode_t root = vfs_rootvnode();
|
||||
if (root) {
|
||||
root_fsid_ = GetVnodeIDForVnode(ctx, root) >> 32;
|
||||
vnode_put(root);
|
||||
}
|
||||
vfs_context_rele(ctx);
|
||||
}
|
||||
|
||||
// Any decisions made while the daemon wasn't
|
||||
// connected should be cleared
|
||||
ClearCache();
|
||||
@@ -112,8 +99,8 @@ void SantaDecisionManager::ConnectClient(pid_t pid) {
|
||||
failed_log_queue_requests_ = 0;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::DisconnectClient(bool itDied) {
|
||||
if (client_pid_ < 1) return;
|
||||
void SantaDecisionManager::DisconnectClient(bool itDied, pid_t pid) {
|
||||
if (client_pid_ == 0 || (pid > 0 && pid != client_pid_)) return;
|
||||
client_pid_ = 0;
|
||||
|
||||
// Ask santad to shutdown, in case it's running.
|
||||
@@ -210,74 +197,58 @@ kern_return_t SantaDecisionManager::StopListener() {
|
||||
|
||||
#pragma mark Cache Management
|
||||
|
||||
/**
|
||||
Return the correct cache for a given identifier.
|
||||
|
||||
@param identifier The identifier
|
||||
@return SantaCache* The cache to use
|
||||
*/
|
||||
SantaCache<uint64_t>* SantaDecisionManager::CacheForIdentifier(
|
||||
const uint64_t identifier) {
|
||||
return (identifier >> 32 == root_fsid_) ?
|
||||
root_decision_cache_ : non_root_decision_cache_;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::AddToCache(
|
||||
uint64_t identifier, santa_action_t decision, uint64_t microsecs) {
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
|
||||
|
||||
auto decision_cache = CacheForIdentifier(identifier);
|
||||
|
||||
santa_vnode_id_t identifier, santa_action_t decision, uint64_t microsecs) {
|
||||
switch (decision) {
|
||||
case ACTION_REQUEST_BINARY:
|
||||
decision_cache->set(identifier, val, 0);
|
||||
decision_cache_->set(identifier, (uint64_t)ACTION_REQUEST_BINARY << 56, 0);
|
||||
break;
|
||||
case ACTION_RESPOND_ACK:
|
||||
decision_cache->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56));
|
||||
decision_cache_->set(identifier, (uint64_t)ACTION_RESPOND_ACK << 56,
|
||||
((uint64_t)ACTION_REQUEST_BINARY << 56));
|
||||
break;
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
case ACTION_RESPOND_DENY:
|
||||
// TODO(bur): Avoid calling set() twice, finding and locking buckets is fast, but not free.
|
||||
if (decision_cache->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56))) {
|
||||
decision_cache->set(identifier, val, ((uint64_t)ACTION_RESPOND_ACK << 56));
|
||||
case ACTION_RESPOND_DENY: {
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
|
||||
if (!decision_cache_->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56))) {
|
||||
decision_cache_->set(identifier, val, ((uint64_t)ACTION_RESPOND_ACK << 56));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
wakeup((void *)identifier);
|
||||
wakeup((void *)identifier.unsafe_simple_id());
|
||||
}
|
||||
|
||||
void SantaDecisionManager::RemoveFromCache(uint64_t identifier) {
|
||||
CacheForIdentifier(identifier)->remove(identifier);
|
||||
if (unlikely(!identifier)) return;
|
||||
wakeup((void *)identifier);
|
||||
void SantaDecisionManager::RemoveFromCache(santa_vnode_id_t identifier) {
|
||||
if (unlikely(identifier.fsid == 0 && identifier.fileid == 0)) return;
|
||||
decision_cache_->remove(identifier);
|
||||
wakeup((void *)identifier.unsafe_simple_id());
|
||||
}
|
||||
|
||||
uint64_t SantaDecisionManager::RootCacheCount() const {
|
||||
return root_decision_cache_->count();
|
||||
uint64_t SantaDecisionManager::CacheCount() const {
|
||||
return decision_cache_->count();
|
||||
}
|
||||
|
||||
uint64_t SantaDecisionManager::NonRootCacheCount() const {
|
||||
return non_root_decision_cache_->count();
|
||||
void SantaDecisionManager::ClearCache() {
|
||||
decision_cache_->clear();
|
||||
}
|
||||
|
||||
void SantaDecisionManager::ClearCache(bool non_root_only) {
|
||||
if (!non_root_only) root_decision_cache_->clear();
|
||||
non_root_decision_cache_->clear();
|
||||
void SantaDecisionManager::CacheBucketCount(
|
||||
uint16_t *per_bucket_counts, uint16_t *array_size, uint64_t *start_bucket) {
|
||||
decision_cache_->bucket_counts(per_bucket_counts, array_size, start_bucket);
|
||||
}
|
||||
|
||||
#pragma mark Decision Fetching
|
||||
|
||||
santa_action_t SantaDecisionManager::GetFromCache(uint64_t identifier) {
|
||||
santa_action_t SantaDecisionManager::GetFromCache(santa_vnode_id_t identifier) {
|
||||
auto result = ACTION_UNSET;
|
||||
uint64_t decision_time = 0;
|
||||
|
||||
auto decision_cache = CacheForIdentifier(identifier);
|
||||
|
||||
uint64_t cache_val = decision_cache->get(identifier);
|
||||
uint64_t cache_val = decision_cache_->get(identifier);
|
||||
if (cache_val == 0) return result;
|
||||
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
@@ -288,7 +259,7 @@ santa_action_t SantaDecisionManager::GetFromCache(uint64_t identifier) {
|
||||
if (result == ACTION_RESPOND_DENY) {
|
||||
auto expiry_time = decision_time + (kMaxDenyCacheTimeMilliseconds * 1000);
|
||||
if (expiry_time < GetCurrentUptime()) {
|
||||
decision_cache->remove(identifier);
|
||||
decision_cache_->remove(identifier);
|
||||
return ACTION_UNSET;
|
||||
}
|
||||
}
|
||||
@@ -298,7 +269,7 @@ santa_action_t SantaDecisionManager::GetFromCache(uint64_t identifier) {
|
||||
}
|
||||
|
||||
santa_action_t SantaDecisionManager::GetFromDaemon(
|
||||
santa_message_t *message, uint64_t identifier) {
|
||||
santa_message_t *message, santa_vnode_id_t identifier) {
|
||||
auto return_action = ACTION_UNSET;
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -326,7 +297,7 @@ santa_action_t SantaDecisionManager::GetFromDaemon(
|
||||
// request, indicated with ACTION_RESPOND_ACK.
|
||||
auto cache_check_count = 0;
|
||||
do {
|
||||
msleep((void *)message->vnode_id, NULL, 0, "", &ts_);
|
||||
msleep((void *)message->vnode_id.unsafe_simple_id(), NULL, 0, "", &ts_);
|
||||
return_action = GetFromCache(identifier);
|
||||
} while (ClientConnected() &&
|
||||
((return_action == ACTION_REQUEST_BINARY && ++cache_check_count < kRequestCacheChecks)
|
||||
@@ -353,7 +324,7 @@ santa_action_t SantaDecisionManager::GetFromDaemon(
|
||||
santa_action_t SantaDecisionManager::FetchDecision(
|
||||
const kauth_cred_t cred,
|
||||
const vnode_t vp,
|
||||
const uint64_t vnode_id) {
|
||||
const santa_vnode_id_t vnode_id) {
|
||||
while (true) {
|
||||
if (!ClientConnected()) return ACTION_RESPOND_ALLOW;
|
||||
|
||||
@@ -368,7 +339,7 @@ santa_action_t SantaDecisionManager::FetchDecision(
|
||||
} else if (return_action == ACTION_REQUEST_BINARY || return_action == ACTION_RESPOND_ACK) {
|
||||
// This thread will now sleep for kRequestLoopSleepMilliseconds (1s) or
|
||||
// until AddToCache is called, indicating a response has arrived.
|
||||
msleep((void *)vnode_id, NULL, 0, "", &ts_);
|
||||
msleep((void *)vnode_id.unsafe_simple_id(), NULL, 0, "", &ts_);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -403,7 +374,6 @@ bool SantaDecisionManager::PostToDecisionQueue(santa_message_t *message) {
|
||||
LOGE("Failed to queue more than %d decision requests, killing daemon",
|
||||
kMaxDecisionQueueFailures);
|
||||
proc_signal(client_pid_, SIGKILL);
|
||||
client_pid_ = 0;
|
||||
}
|
||||
}
|
||||
lck_mtx_unlock(decision_dataqueue_lock_);
|
||||
@@ -417,10 +387,6 @@ bool SantaDecisionManager::PostToLogQueue(santa_message_t *message) {
|
||||
if (failed_log_queue_requests_++ == 0) {
|
||||
LOGW("Dropping log queue messages");
|
||||
}
|
||||
// If enqueue failed, pop an item off the queue and try again.
|
||||
uint32_t dataSize = 0;
|
||||
log_dataqueue_->dequeue(0, &dataSize);
|
||||
kr = log_dataqueue_->enqueue(message, sizeof(santa_message_t));
|
||||
} else {
|
||||
if (failed_log_queue_requests_ > 0) {
|
||||
failed_log_queue_requests_--;
|
||||
@@ -448,24 +414,11 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
|
||||
int *errno) {
|
||||
// Get ID for the vnode
|
||||
auto vnode_id = GetVnodeIDForVnode(ctx, vp);
|
||||
if (!vnode_id) return KAUTH_RESULT_DEFER;
|
||||
if (vnode_id.fsid == 0 && vnode_id.fileid == 0) return KAUTH_RESULT_DEFER;
|
||||
|
||||
// Fetch decision
|
||||
auto returnedAction = FetchDecision(cred, vp, vnode_id);
|
||||
|
||||
// If file has dirty blocks, remove from cache and deny. This would usually
|
||||
// be the case if a file has been written to and flushed but not yet
|
||||
// closed.
|
||||
if (vnode_hasdirtyblks(vp)) {
|
||||
RemoveFromCache(vnode_id);
|
||||
returnedAction = ACTION_RESPOND_DENY;
|
||||
|
||||
char path[MAXPATHLEN];
|
||||
int len = MAXPATHLEN;
|
||||
path[MAXPATHLEN - 1] = 0;
|
||||
LOGW("file has dirty blocks: %s", vn_getpath(vp, path, &len) ? "unknown" : path);
|
||||
}
|
||||
|
||||
switch (returnedAction) {
|
||||
case ACTION_RESPOND_ALLOW: {
|
||||
auto proc = vfs_context_proc(ctx);
|
||||
|
||||
@@ -64,7 +64,7 @@ class SantaDecisionManager : public OSObject {
|
||||
void ConnectClient(pid_t pid);
|
||||
|
||||
/// Called by SantaDriverClient when a client disconnects
|
||||
void DisconnectClient(bool itDied = false);
|
||||
void DisconnectClient(bool itDied = false, pid_t pid = proc_selfpid());
|
||||
|
||||
/// Returns whether a client is currently connected or not.
|
||||
bool ClientConnected() const;
|
||||
@@ -86,7 +86,7 @@ class SantaDecisionManager : public OSObject {
|
||||
kern_return_t StopListener();
|
||||
|
||||
/// Adds a decision to the cache, with a timestamp.
|
||||
void AddToCache(uint64_t identifier,
|
||||
void AddToCache(santa_vnode_id_t identifier,
|
||||
const santa_action_t decision,
|
||||
const uint64_t microsecs = GetCurrentUptime());
|
||||
|
||||
@@ -94,20 +94,30 @@ class SantaDecisionManager : public OSObject {
|
||||
Fetches a response from the cache, first checking to see if the entry
|
||||
has expired.
|
||||
*/
|
||||
santa_action_t GetFromCache(uint64_t identifier);
|
||||
santa_action_t GetFromCache(santa_vnode_id_t identifier);
|
||||
|
||||
/// Checks to see if a given identifier is in the cache and removes it.
|
||||
void RemoveFromCache(uint64_t identifier);
|
||||
void RemoveFromCache(santa_vnode_id_t identifier);
|
||||
|
||||
/// Returns the number of entries in the cache.
|
||||
uint64_t RootCacheCount() const;
|
||||
uint64_t NonRootCacheCount() const;
|
||||
uint64_t CacheCount() const;
|
||||
|
||||
/// Clears the cache.
|
||||
void ClearCache();
|
||||
|
||||
|
||||
/**
|
||||
Clears the cache(s). If non_root_only is true, only the non-root cache
|
||||
is cleared.
|
||||
Fills out the per_bucket_counts array with the number of items in each bucket in the
|
||||
non-root decision cache.
|
||||
|
||||
@param per_bucket_counts An array of uint16_t's to fill in with the number of items in each
|
||||
bucket. The size of this array is expected to equal array_size.
|
||||
@param array_size The size of the per_bucket_counts array on input. Upon return this will be
|
||||
updated to the number of slots that were actually used.
|
||||
@param start_bucket If non-zero this is the bucket in the cache to start from. Upon return this
|
||||
will be the next numbered bucket to start from for subsequent requests.
|
||||
*/
|
||||
void ClearCache(bool non_root_only = false);
|
||||
void CacheBucketCount(uint16_t *per_bucket_counts, uint16_t *array_size, uint64_t *start_bucket);
|
||||
|
||||
/// Increments the count of active callbacks pending.
|
||||
void IncrementListenerInvocations();
|
||||
@@ -120,15 +130,18 @@ class SantaDecisionManager : public OSObject {
|
||||
|
||||
@param ctx The VFS context to use.
|
||||
@param vp The Vnode to get the ID for
|
||||
@return uint64_t The Vnode ID as a 64-bit unsigned int.
|
||||
@return santa_vnode_id_t The Vnode ID.
|
||||
*/
|
||||
static inline uint64_t GetVnodeIDForVnode(const vfs_context_t ctx, const vnode_t vp) {
|
||||
static inline santa_vnode_id_t GetVnodeIDForVnode(const vfs_context_t ctx, const vnode_t vp) {
|
||||
struct vnode_attr vap;
|
||||
VATTR_INIT(&vap);
|
||||
VATTR_WANTED(&vap, va_fsid);
|
||||
VATTR_WANTED(&vap, va_fileid);
|
||||
vnode_getattr(vp, &vap, ctx);
|
||||
return (((uint64_t)vap.va_fsid << 32) | vap.va_fileid);
|
||||
return {
|
||||
.fsid = vap.va_fsid,
|
||||
.fileid = vap.va_fileid
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,7 +211,7 @@ class SantaDecisionManager : public OSObject {
|
||||
@param identifier The vnode ID string for this request
|
||||
@return santa_action_t The response for this request
|
||||
*/
|
||||
santa_action_t GetFromDaemon(santa_message_t *message, uint64_t identifier);
|
||||
santa_action_t GetFromDaemon(santa_message_t *message, santa_vnode_id_t identifier);
|
||||
|
||||
/**
|
||||
Fetches an execution decision for a file, first using the cache and then
|
||||
@@ -212,7 +225,7 @@ class SantaDecisionManager : public OSObject {
|
||||
@return santa_action_t The response for this request
|
||||
*/
|
||||
santa_action_t FetchDecision(
|
||||
const kauth_cred_t cred, const vnode_t vp, const uint64_t vnode_id);
|
||||
const kauth_cred_t cred, const vnode_t vp, const santa_vnode_id_t vnode_id);
|
||||
|
||||
/**
|
||||
Posts the requested message to the decision data queue.
|
||||
@@ -266,21 +279,8 @@ class SantaDecisionManager : public OSObject {
|
||||
return (uint64_t)((sec * 1000000) + usec);
|
||||
}
|
||||
|
||||
SantaCache<uint64_t> *root_decision_cache_;
|
||||
SantaCache<uint64_t> *non_root_decision_cache_;
|
||||
SantaCache<uint64_t> *vnode_pid_map_;
|
||||
|
||||
/**
|
||||
Return the correct cache for a given identifier.
|
||||
|
||||
@param identifier The identifier
|
||||
@return SantaCache* The cache to use
|
||||
*/
|
||||
SantaCache<uint64_t>* CacheForIdentifier(const uint64_t identifier);
|
||||
|
||||
// This is the file system ID of the root filesystem,
|
||||
// used to determine which cache to use for requests
|
||||
uint32_t root_fsid_;
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *decision_cache_;
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *vnode_pid_map_;
|
||||
|
||||
lck_grp_t *sdm_lock_grp_;
|
||||
lck_grp_attr_t *sdm_lock_grp_attr_;
|
||||
@@ -301,19 +301,20 @@ class SantaDecisionManager : public OSObject {
|
||||
kauth_listener_t vnode_listener_;
|
||||
kauth_listener_t fileop_listener_;
|
||||
|
||||
struct timespec ts_;
|
||||
struct timespec ts_= { .tv_sec = kRequestLoopSleepMilliseconds / 1000,
|
||||
.tv_nsec = kRequestLoopSleepMilliseconds % 1000 * 1000000 };
|
||||
};
|
||||
|
||||
/**
|
||||
The kauth callback function for the Vnode scope
|
||||
|
||||
@param actor's credentials
|
||||
@param data that was passed when the listener was registered
|
||||
@param action that was requested
|
||||
@param VFS context
|
||||
@param Vnode being operated on
|
||||
@param Parent Vnode. May be nullptr.
|
||||
@param Pointer to an errno-style error.
|
||||
@param credential actor's credentials
|
||||
@param idata data that was passed when the listener was registered
|
||||
@param action action that was requested
|
||||
@param arg0 VFS context
|
||||
@param arg1 Vnode being operated on
|
||||
@param arg2 Parent Vnode. May be nullptr.
|
||||
@param arg3 Pointer to an errno-style error.
|
||||
*/
|
||||
extern "C" int vnode_scope_callback(
|
||||
kauth_cred_t credential,
|
||||
@@ -327,13 +328,13 @@ extern "C" int vnode_scope_callback(
|
||||
/**
|
||||
The kauth callback function for the FileOp scope
|
||||
|
||||
@param actor's credentials
|
||||
@param data that was passed when the listener was registered
|
||||
@param action that was requested
|
||||
@param depends on action, usually the vnode ref.
|
||||
@param depends on action.
|
||||
@param depends on action, usually 0.
|
||||
@param depends on action, usually 0.
|
||||
@param credential actor's credentials
|
||||
@param idata data that was passed when the listener was registered
|
||||
@param action action that was requested
|
||||
@param arg0 depends on action, usually the vnode ref.
|
||||
@param arg1 depends on action.
|
||||
@param arg2 depends on action, usually 0.
|
||||
@param arg3 depends on action, usually 0.
|
||||
*/
|
||||
extern "C" int fileop_scope_callback(
|
||||
kauth_cred_t credential,
|
||||
|
||||
@@ -37,34 +37,38 @@ bool SantaDriverClient::initWithTask(
|
||||
|
||||
bool SantaDriverClient::start(IOService *provider) {
|
||||
myProvider = OSDynamicCast(com_google_SantaDriver, provider);
|
||||
|
||||
if (!myProvider) return false;
|
||||
if (!super::start(provider)) return false;
|
||||
|
||||
decisionManager = myProvider->GetDecisionManager();
|
||||
if (!decisionManager) return false;
|
||||
decisionManager->retain();
|
||||
|
||||
return true;
|
||||
return super::start(provider);
|
||||
}
|
||||
|
||||
void SantaDriverClient::stop(IOService *provider) {
|
||||
super::stop(provider);
|
||||
myProvider = nullptr;
|
||||
decisionManager->release();
|
||||
decisionManager = nullptr;
|
||||
super::stop(provider);
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::clientDied() {
|
||||
LOGI("Client died.");
|
||||
decisionManager->DisconnectClient(true);
|
||||
return terminate(0) ? kIOReturnSuccess : kIOReturnError;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::clientClose() {
|
||||
decisionManager->DisconnectClient(true);
|
||||
return terminate(kIOServiceSynchronous) ? kIOReturnSuccess : kIOReturnError;
|
||||
LOGI("Client disconnected.");
|
||||
decisionManager->DisconnectClient();
|
||||
return terminate(0) ? kIOReturnSuccess : kIOReturnError;
|
||||
}
|
||||
|
||||
bool SantaDriverClient::terminate(IOOptionBits options) {
|
||||
decisionManager->DisconnectClient();
|
||||
LOGI("Client disconnected.");
|
||||
|
||||
bool SantaDriverClient::didTerminate(IOService *provider, IOOptionBits options, bool *defer) {
|
||||
decisionManager->DisconnectClient(false, 0);
|
||||
if (myProvider && myProvider->isOpen(this)) myProvider->close(this);
|
||||
|
||||
return super::terminate(options);
|
||||
return super::didTerminate(provider, options, defer);
|
||||
}
|
||||
|
||||
#pragma mark Fetching memory and data queue notifications
|
||||
@@ -133,9 +137,10 @@ IOReturn SantaDriverClient::allow_binary(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
const uint64_t vnode_id = static_cast<const uint64_t>(arguments->scalarInput[0]);
|
||||
if (!vnode_id) return kIOReturnInvalid;
|
||||
me->decisionManager->AddToCache(vnode_id, ACTION_RESPOND_ALLOW);
|
||||
if (arguments->structureInputSize != sizeof(santa_vnode_id_t)) return kIOReturnInvalid;
|
||||
santa_vnode_id_t *vnode_id = (santa_vnode_id_t *)arguments->structureInput;
|
||||
if (vnode_id->fsid == 0 || vnode_id->fileid == 0) return kIOReturnInvalid;
|
||||
me->decisionManager->AddToCache(*vnode_id, ACTION_RESPOND_ALLOW);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
@@ -145,9 +150,10 @@ IOReturn SantaDriverClient::deny_binary(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
const uint64_t vnode_id = static_cast<const uint64_t>(arguments->scalarInput[0]);
|
||||
if (!vnode_id) return kIOReturnInvalid;
|
||||
me->decisionManager->AddToCache(vnode_id, ACTION_RESPOND_DENY);
|
||||
if (arguments->structureInputSize != sizeof(santa_vnode_id_t)) return kIOReturnInvalid;
|
||||
santa_vnode_id_t *vnode_id = (santa_vnode_id_t *)arguments->structureInput;
|
||||
if (vnode_id->fsid == 0 || vnode_id->fileid == 0) return kIOReturnInvalid;
|
||||
me->decisionManager->AddToCache(*vnode_id, ACTION_RESPOND_DENY);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
@@ -157,9 +163,10 @@ IOReturn SantaDriverClient::acknowledge_binary(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
const uint64_t vnode_id = static_cast<const uint64_t>(arguments->scalarInput[0]);
|
||||
if (!vnode_id) return kIOReturnInvalid;
|
||||
me->decisionManager->AddToCache(vnode_id, ACTION_RESPOND_ACK, 0);
|
||||
if (arguments->structureInputSize != sizeof(santa_vnode_id_t)) return kIOReturnInvalid;
|
||||
santa_vnode_id_t *vnode_id = (santa_vnode_id_t *)arguments->structureInput;
|
||||
if (vnode_id->fsid == 0 || vnode_id->fileid == 0) return kIOReturnInvalid;
|
||||
me->decisionManager->AddToCache(*vnode_id, ACTION_RESPOND_ACK);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
@@ -169,8 +176,7 @@ IOReturn SantaDriverClient::clear_cache(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
const bool non_root_only = static_cast<const bool>(arguments->scalarInput[0]);
|
||||
me->decisionManager->ClearCache(non_root_only);
|
||||
me->decisionManager->ClearCache();
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
@@ -180,8 +186,7 @@ IOReturn SantaDriverClient::cache_count(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
arguments->scalarOutput[0] = me->decisionManager->RootCacheCount();
|
||||
arguments->scalarOutput[1] = me->decisionManager->NonRootCacheCount();
|
||||
arguments->scalarOutput[0] = me->decisionManager->CacheCount();
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
@@ -190,8 +195,27 @@ IOReturn SantaDriverClient::check_cache(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
const uint64_t input = static_cast<const uint64_t>(arguments->scalarInput[0]);
|
||||
arguments->scalarOutput[0] = me->decisionManager->GetFromCache(input);
|
||||
if (arguments->structureInputSize != sizeof(santa_vnode_id_t)) return kIOReturnInvalid;
|
||||
santa_vnode_id_t *vnode_id = (santa_vnode_id_t *)arguments->structureInput;
|
||||
if (vnode_id->fsid == 0 || vnode_id->fileid == 0) return kIOReturnInvalid;
|
||||
arguments->scalarOutput[0] = me->decisionManager->GetFromCache(*vnode_id);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::cache_bucket_count(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments) {
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
santa_bucket_count_t *counts = reinterpret_cast<santa_bucket_count_t *>(
|
||||
arguments->structureOutput);
|
||||
const santa_bucket_count_t *input = reinterpret_cast<const santa_bucket_count_t *>(
|
||||
arguments->structureInput);
|
||||
|
||||
uint16_t s = sizeof(counts->per_bucket) / sizeof(uint16_t);
|
||||
counts->start = input->start;
|
||||
me->decisionManager->CacheBucketCount(counts->per_bucket, &s, &(counts->start));
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
@@ -209,12 +233,14 @@ IOReturn SantaDriverClient::externalMethod(
|
||||
static IOExternalMethodDispatch sMethods[kSantaUserClientNMethods] = {
|
||||
// Function ptr, input scalar count, input struct size, output scalar count, output struct size
|
||||
{ &SantaDriverClient::open, 0, 0, 0, 0 },
|
||||
{ &SantaDriverClient::allow_binary, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::deny_binary, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::acknowledge_binary, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::clear_cache, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::cache_count, 0, 0, 2, 0 },
|
||||
{ &SantaDriverClient::check_cache, 1, 0, 1, 0 }
|
||||
{ &SantaDriverClient::allow_binary, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::deny_binary, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::acknowledge_binary, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::clear_cache, 0, 0, 0, 0 },
|
||||
{ &SantaDriverClient::cache_count, 0, 0, 1, 0 },
|
||||
{ &SantaDriverClient::check_cache, 0, sizeof(santa_vnode_id_t), 1, 0 },
|
||||
{ &SantaDriverClient::cache_bucket_count, 0, sizeof(santa_bucket_count_t),
|
||||
0, sizeof(santa_bucket_count_t) },
|
||||
};
|
||||
|
||||
if (selector > static_cast<UInt32>(kSantaUserClientNMethods)) {
|
||||
|
||||
@@ -47,11 +47,14 @@ class com_google_SantaDriverClient : public IOUserClient {
|
||||
/// Called when this class is stopping
|
||||
void stop(IOService *provider) override;
|
||||
|
||||
/// Called when a client disconnects
|
||||
/// Called when a client manually disconnects (via IOServiceClose)
|
||||
IOReturn clientClose() override;
|
||||
|
||||
/// Called when the driver is shutting down
|
||||
bool terminate(IOOptionBits options) override;
|
||||
/// Called when a client dies
|
||||
IOReturn clientDied() override;
|
||||
|
||||
/// Called during termination
|
||||
bool didTerminate(IOService* provider, IOOptionBits options, bool* defer) override;
|
||||
|
||||
/// Called in clients with IOConnectSetNotificationPort
|
||||
IOReturn registerNotificationPort(
|
||||
@@ -105,6 +108,11 @@ class com_google_SantaDriverClient : public IOUserClient {
|
||||
static IOReturn check_cache(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to find out how many items are in each cache bucket.
|
||||
/// Input and output are both an instance of santa_bucket_count_t.
|
||||
static IOReturn cache_bucket_count(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
private:
|
||||
com_google_SantaDriver *myProvider;
|
||||
SantaDecisionManager *decisionManager;
|
||||
|
||||
11
Source/santa-driver/main.c
Normal file
11
Source/santa-driver/main.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <mach/mach_types.h>
|
||||
|
||||
extern kern_return_t _start(kmod_info_t *ki, void *data);
|
||||
extern kern_return_t _stop(kmod_info_t *ki, void *data);
|
||||
|
||||
__attribute__((visibility("default"))) KMOD_EXPLICIT_DECL(com.google.santa-driver, SANTA_VERSION, _start, _stop)
|
||||
|
||||
__private_extern__ kmod_start_func_t *_realmain = 0;
|
||||
__private_extern__ kmod_stop_func_t *_antimain = 0;
|
||||
|
||||
__private_extern__ int _kext_apple_cc = __APPLE_CC__ ;
|
||||
68
Source/santabs/CMakeLists.txt
Normal file
68
Source/santabs/CMakeLists.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(santabs)
|
||||
|
||||
function(main)
|
||||
# Include all files to make IDEs happy
|
||||
set(PROJECT_SOURCEFILES
|
||||
SNTBundleService.h
|
||||
SNTBundleService.m
|
||||
|
||||
main.m
|
||||
|
||||
Resources/santabs-Info.plist
|
||||
|
||||
# The "common" folder contains some of the files required to build this target
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTFileInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTStoredEvent.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCBundleServiceInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCNotifierInterface.m"
|
||||
)
|
||||
|
||||
add_executable("${PROJECT_NAME}" MACOSX_BUNDLE ${PROJECT_SOURCEFILES})
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE -ObjC)
|
||||
|
||||
set_target_properties("${PROJECT_NAME}" PROPERTIES
|
||||
BUNDLE_EXTENSION "xpc"
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Resources/santabs-Info.plist"
|
||||
)
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE
|
||||
MOLCertificate MOLCodesignChecker
|
||||
FMDB MOLXPCConnection
|
||||
|
||||
"-framework Cocoa"
|
||||
)
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-fobjc-arc -Wno-everything -fmodules
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE -g3)
|
||||
endif()
|
||||
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE
|
||||
COCOAPODS=1
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE DEBUG=1)
|
||||
else()
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE NDEBUG=1)
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/santabs"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
)
|
||||
|
||||
add_custom_command(TARGET "${PROJECT_NAME}" POST_BUILD
|
||||
COMMAND codesign --force --verify --verbose --sign "${CODESIGN_IDENTITY}" "${CMAKE_BINARY_DIR}/Source/santabs/santabs.xpc/Contents/MacOS/santabs"
|
||||
COMMAND codesign --force --verify --verbose --sign "${CODESIGN_IDENTITY}" "${CMAKE_BINARY_DIR}/Source/santabs/santabs.xpc"
|
||||
COMMENT "Signing ${PROJECT_NAME} with the following identity: ${CODESIGN_IDENTITY}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
@@ -7,21 +7,21 @@
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>santabs</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<string>santabs</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<string>com.google.santabs</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<string>santabs</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>XPCService</key>
|
||||
<dict>
|
||||
<key>ServiceType</key>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
// Create listener for return connection from SantaGUI.
|
||||
NSXPCListener *listener = [NSXPCListener anonymousListener];
|
||||
self.listener = [[MOLXPCConnection alloc] initServerWithListener:listener];
|
||||
self.listener.exportedInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
self.listener.unprivilegedInterface = self.listener.privilegedInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
self.listener.exportedObject = self;
|
||||
self.listener.acceptedHandler = ^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
int main(int argc, const char *argv[]) {
|
||||
MOLXPCConnection *c =
|
||||
[[MOLXPCConnection alloc] initServerWithListener:[NSXPCListener serviceListener]];
|
||||
c.exportedInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
c.privilegedInterface = c.unprivilegedInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
c.exportedObject = [[SNTBundleService alloc] init];
|
||||
[c resume];
|
||||
}
|
||||
|
||||
117
Source/santactl/CMakeLists.txt
Normal file
117
Source/santactl/CMakeLists.txt
Normal file
@@ -0,0 +1,117 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(santactl)
|
||||
|
||||
function(main)
|
||||
# Include all files to make IDEs happy
|
||||
set(PROJECT_SOURCEFILES
|
||||
SNTCommand.h
|
||||
SNTCommand.m
|
||||
|
||||
SNTCommandController.h
|
||||
SNTCommandController.m
|
||||
|
||||
main.m
|
||||
|
||||
Resources/santactl-Info.plist
|
||||
|
||||
# Generic commands
|
||||
Commands/SNTCommandBundleInfo.m
|
||||
Commands/SNTCommandCheckCache.m
|
||||
Commands/SNTCommandFileInfo.m
|
||||
Commands/SNTCommandFlushCache.m
|
||||
Commands/SNTCommandRule.m
|
||||
Commands/SNTCommandStatus.m
|
||||
Commands/SNTCommandVersion.m
|
||||
Commands/SNTCommandCacheHistogram.m
|
||||
|
||||
# Sync server
|
||||
Commands/sync/NSData+Zlib.h
|
||||
Commands/sync/NSData+Zlib.m
|
||||
|
||||
Commands/sync/SNTCommandSync.m
|
||||
|
||||
Commands/sync/SNTCommandSyncConstants.h
|
||||
Commands/sync/SNTCommandSyncConstants.m
|
||||
|
||||
Commands/sync/SNTCommandSyncEventUpload.h
|
||||
Commands/sync/SNTCommandSyncEventUpload.m
|
||||
|
||||
Commands/sync/SNTCommandSyncLogUpload.h
|
||||
Commands/sync/SNTCommandSyncLogUpload.m
|
||||
|
||||
Commands/sync/SNTCommandSyncManager.h
|
||||
Commands/sync/SNTCommandSyncManager.m
|
||||
|
||||
Commands/sync/SNTCommandSyncPostflight.h
|
||||
Commands/sync/SNTCommandSyncPostflight.m
|
||||
|
||||
Commands/sync/SNTCommandSyncPreflight.h
|
||||
Commands/sync/SNTCommandSyncPreflight.m
|
||||
|
||||
Commands/sync/SNTCommandSyncRuleDownload.h
|
||||
Commands/sync/SNTCommandSyncRuleDownload.m
|
||||
|
||||
Commands/sync/SNTCommandSyncStage.h
|
||||
Commands/sync/SNTCommandSyncStage.m
|
||||
|
||||
Commands/sync/SNTCommandSyncState.h
|
||||
Commands/sync/SNTCommandSyncState.m
|
||||
|
||||
# The "common" folder contains some of the files required to build this target
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTConfigurator.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTFileInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTRule.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTStoredEvent.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTSystemInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCControlInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCUnprivilegedControlInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCSyncdInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTLogging.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTDropRootPrivs.m"
|
||||
)
|
||||
|
||||
add_executable("${PROJECT_NAME}" ${PROJECT_SOURCEFILES})
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE -ObjC)
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-fobjc-arc -Wno-everything -fmodules
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE -g3)
|
||||
endif()
|
||||
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE
|
||||
COCOAPODS=1
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE DEBUG=1)
|
||||
else()
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE NDEBUG=1)
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santactl/Commands/sync"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
)
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE
|
||||
MOLCertificate MOLCodesignChecker
|
||||
MOLAuthenticatingURLSession FMDB MOLFCMClient
|
||||
MOLXPCConnection
|
||||
|
||||
"-framework Cocoa"
|
||||
)
|
||||
|
||||
add_custom_command(TARGET "${PROJECT_NAME}" POST_BUILD
|
||||
COMMAND codesign --force --verify --verbose --sign "${CODESIGN_IDENTITY}" $<TARGET_FILE:${PROJECT_NAME}>
|
||||
COMMENT "Signing ${PROJECT_NAME} with the following identity: ${CODESIGN_IDENTITY}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
@@ -22,14 +22,14 @@
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
@interface SNTCommandBundleInfo : SNTCommand<SNTCommandProtocol>
|
||||
@end
|
||||
|
||||
@implementation SNTCommandBundleInfo
|
||||
|
||||
#ifdef DEBUG
|
||||
REGISTER_COMMAND_NAME(@"bundleinfo")
|
||||
#endif
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return NO;
|
||||
@@ -78,3 +78,5 @@ REGISTER_COMMAND_NAME(@"bundleinfo")
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
81
Source/santactl/Commands/SNTCommandCacheHistogram.m
Normal file
81
Source/santactl/Commands/SNTCommandCacheHistogram.m
Normal file
@@ -0,0 +1,81 @@
|
||||
/// Copyright 2018 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommand.h"
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@interface SNTCommandCacheHistogram : SNTCommand<SNTCommandProtocol>
|
||||
@end
|
||||
|
||||
@implementation SNTCommandCacheHistogram
|
||||
|
||||
REGISTER_COMMAND_NAME(@"cachehistogram")
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)requiresDaemonConn {
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (NSString *)shortHelpText {
|
||||
return @"Print a cache distribution histogram.";
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
return (@"Prints a histogram of each bucket of the in-kernel cache\n"
|
||||
@" Use -g to get 'graphical' output\n"
|
||||
@"Only available in DEBUG builds.");
|
||||
}
|
||||
|
||||
- (void)runWithArguments:(NSArray *)arguments {
|
||||
[[self.daemonConn remoteObjectProxy] cacheBucketCount:^(NSArray *counts) {
|
||||
NSMutableDictionary<NSNumber *, NSNumber *> *d = [NSMutableDictionary dictionary];
|
||||
[counts enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
d[obj] = @([d[obj] intValue] + 1);
|
||||
}];
|
||||
printf("There are %llu empty buckets\n", [d[@0] unsignedLongLongValue]);
|
||||
|
||||
for (NSNumber *key in [d.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
if ([key isEqual:@0]) continue;
|
||||
uint64_t k = [key unsignedLongLongValue];
|
||||
uint64_t v = [d[key] unsignedLongLongValue];
|
||||
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"-g"]) {
|
||||
printf("%4llu: ", k);
|
||||
for (uint64_t y = 0; y < v; ++y) {
|
||||
printf("#");
|
||||
}
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("%4llu: %llu\n", k, v);
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -24,14 +24,14 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
@interface SNTCommandCheckCache : SNTCommand<SNTCommandProtocol>
|
||||
@end
|
||||
|
||||
@implementation SNTCommandCheckCache
|
||||
|
||||
#ifdef DEBUG
|
||||
REGISTER_COMMAND_NAME(@"checkcache")
|
||||
#endif
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return NO;
|
||||
@@ -51,7 +51,7 @@ REGISTER_COMMAND_NAME(@"checkcache")
|
||||
}
|
||||
|
||||
- (void)runWithArguments:(NSArray *)arguments {
|
||||
uint64_t vnodeID = [self vnodeIDForFile:arguments.firstObject];
|
||||
santa_vnode_id_t vnodeID = [self vnodeIDForFile:arguments.firstObject];
|
||||
[[self.daemonConn remoteObjectProxy] checkCacheForVnodeID:vnodeID
|
||||
withReply:^(santa_action_t action) {
|
||||
if (action == ACTION_RESPOND_ALLOW) {
|
||||
@@ -67,10 +67,13 @@ REGISTER_COMMAND_NAME(@"checkcache")
|
||||
}];
|
||||
}
|
||||
|
||||
- (uint64_t)vnodeIDForFile:(NSString *)path {
|
||||
- (santa_vnode_id_t)vnodeIDForFile:(NSString *)path {
|
||||
struct stat fstat = {};
|
||||
stat(path.fileSystemRepresentation, &fstat);
|
||||
return (((uint64_t)fstat.st_dev << 32) | fstat.st_ino);
|
||||
santa_vnode_id_t ret = {.fsid = fstat.st_dev, .fileid = fstat.st_ino};
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommand.h"
|
||||
@@ -27,9 +29,7 @@
|
||||
|
||||
@implementation SNTCommandFlushCache
|
||||
|
||||
#ifdef DEBUG
|
||||
REGISTER_COMMAND_NAME(@"flushcache")
|
||||
#endif
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return YES;
|
||||
@@ -61,3 +61,5 @@ REGISTER_COMMAND_NAME(@"flushcache")
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@@ -50,10 +50,16 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
// Daemon status
|
||||
__block BOOL driverConnected;
|
||||
__block NSString *clientMode;
|
||||
__block uint64_t cpuEvents, ramEvents;
|
||||
__block double cpuPeak, ramPeak;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] driverConnectionEstablished:^(BOOL connected) {
|
||||
driverConnected = connected;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] clientMode:^(SNTClientMode cm) {
|
||||
switch (cm) {
|
||||
case SNTClientModeMonitor:
|
||||
@@ -81,11 +87,10 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
BOOL fileLogging = ([[SNTConfigurator configurator] fileChangesRegex] != nil);
|
||||
|
||||
// Kext status
|
||||
__block uint64_t rootCacheCount = -1, nonRootCacheCount = -1;
|
||||
__block uint64_t cacheCount = -1;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t rootCache, uint64_t nonRootCache) {
|
||||
rootCacheCount = rootCache;
|
||||
nonRootCacheCount = nonRootCache;
|
||||
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t count) {
|
||||
cacheCount = count;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
@@ -160,6 +165,7 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
if ([arguments containsObject:@"--json"]) {
|
||||
NSDictionary *stats = @{
|
||||
@"daemon" : @{
|
||||
@"driver_connected" : @(driverConnected),
|
||||
@"mode" : clientMode ?: @"null",
|
||||
@"file_logging" : @(fileLogging),
|
||||
@"watchdog_cpu_events" : @(cpuEvents),
|
||||
@@ -168,8 +174,7 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
@"watchdog_ram_peak" : @(ramPeak),
|
||||
},
|
||||
@"kernel" : @{
|
||||
@"root_cache_count" : @(rootCacheCount),
|
||||
@"non_root_cache_count": @(nonRootCacheCount),
|
||||
@"cache_count" : @(cacheCount),
|
||||
},
|
||||
@"database" : @{
|
||||
@"binary_rules" : @(binaryRuleCount),
|
||||
@@ -192,13 +197,13 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
printf("%s\n", [statsStr UTF8String]);
|
||||
} else {
|
||||
printf(">>> Daemon Info\n");
|
||||
printf(" %-25s | %s\n", "Driver Connected", driverConnected ? "Yes" : "No");
|
||||
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
|
||||
printf(" %-25s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
|
||||
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
|
||||
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
|
||||
printf(">>> Kernel Info\n");
|
||||
printf(" %-25s | %lld\n", "Root cache count", rootCacheCount);
|
||||
printf(" %-25s | %lld\n", "Non-root cache count", nonRootCacheCount);
|
||||
printf(" %-25s | %lld\n", "Cache count", cacheCount);
|
||||
printf(">>> Database Info\n");
|
||||
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
|
||||
printf(" %-25s | %lld\n", "Certificate Rules", certRuleCount);
|
||||
|
||||
@@ -37,7 +37,7 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
#pragma mark SNTCommand protocol methods
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return NO;
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)requiresDaemonConn {
|
||||
@@ -57,6 +57,9 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
}
|
||||
|
||||
- (void)runWithArguments:(NSArray *)arguments {
|
||||
// Connect to santad while we are root, so that we pass the XPC authentication
|
||||
[self.daemonConn resume];
|
||||
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
LOGE(@"Failed to drop root privileges. Exiting.");
|
||||
@@ -68,7 +71,6 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
exit(1);
|
||||
}
|
||||
|
||||
[self.daemonConn resume];
|
||||
BOOL daemon = [arguments containsObject:@"--daemon"];
|
||||
self.syncManager = [[SNTCommandSyncManager alloc] initWithDaemonConnection:self.daemonConn
|
||||
isDaemon:daemon];
|
||||
@@ -91,7 +93,7 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
// Create listener for return connection from daemon.
|
||||
NSXPCListener *listener = [NSXPCListener anonymousListener];
|
||||
self.listener = [[MOLXPCConnection alloc] initServerWithListener:listener];
|
||||
self.listener.exportedInterface = [SNTXPCSyncdInterface syncdInterface];
|
||||
self.listener.privilegedInterface = [SNTXPCSyncdInterface syncdInterface];
|
||||
self.listener.exportedObject = self.syncManager;
|
||||
self.listener.acceptedHandler = ^{
|
||||
LOGD(@"santad <--> santactl connections established");
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommandSyncStage.h"
|
||||
#import "SNTRule.h"
|
||||
|
||||
@interface SNTCommandSyncRuleDownload : SNTCommandSyncStage
|
||||
- (SNTRule *)ruleFromDictionary:(NSDictionary *)dict;
|
||||
@end
|
||||
|
||||
@@ -82,7 +82,11 @@
|
||||
do {
|
||||
NSDictionary *requestDict = cursor ? @{kCursor : cursor} : @{};
|
||||
NSDictionary *response = [self performRequest:[self requestWithDictionary:requestDict]];
|
||||
if (!response) return nil;
|
||||
|
||||
if (![response isKindOfClass:[NSDictionary class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (NSDictionary *ruleDict in response[kRules]) {
|
||||
SNTRule *rule = [self ruleFromDictionary:ruleDict];
|
||||
if (rule) [newRules addObject:rule];
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.santactl</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<string>santactl</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CSFlags</key>
|
||||
<string>kill</string>
|
||||
</dict>
|
||||
|
||||
114
Source/santad/CMakeLists.txt
Normal file
114
Source/santad/CMakeLists.txt
Normal file
@@ -0,0 +1,114 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(santad)
|
||||
|
||||
function(main)
|
||||
# Include all files to make IDEs happy
|
||||
set(PROJECT_SOURCEFILES
|
||||
SNTApplication.h
|
||||
SNTApplication.m
|
||||
|
||||
SNTCachedDecision.h
|
||||
SNTCachedDecision.m
|
||||
|
||||
SNTDaemonControlController.h
|
||||
SNTDaemonControlController.m
|
||||
|
||||
SNTDatabaseController.h
|
||||
SNTDatabaseController.m
|
||||
|
||||
SNTDriverManager.h
|
||||
SNTDriverManager.m
|
||||
|
||||
SNTExecutionController.h
|
||||
SNTExecutionController.m
|
||||
|
||||
SNTNotificationQueue.h
|
||||
SNTNotificationQueue.m
|
||||
|
||||
SNTPolicyProcessor.h
|
||||
SNTPolicyProcessor.m
|
||||
|
||||
SNTSyncdQueue.h
|
||||
SNTSyncdQueue.m
|
||||
|
||||
DataLayer/SNTDatabaseTable.h
|
||||
DataLayer/SNTDatabaseTable.m
|
||||
|
||||
DataLayer/SNTEventTable.h
|
||||
DataLayer/SNTEventTable.m
|
||||
|
||||
DataLayer/SNTRuleTable.h
|
||||
DataLayer/SNTRuleTable.m
|
||||
|
||||
Logs/SNTEventLog.h
|
||||
Logs/SNTEventLog.m
|
||||
|
||||
Logs/SNTFileEventLog.h
|
||||
Logs/SNTFileEventLog.m
|
||||
|
||||
Logs/SNTSyslogEventLog.h
|
||||
Logs/SNTSyslogEventLog.m
|
||||
|
||||
main.m
|
||||
|
||||
Resources/santad-info.plist
|
||||
|
||||
# The "common" folder contains some of the files required to build this target
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTBlockMessage.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTFileInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTConfigurator.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTDropRootPrivs.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTRule.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTStoredEvent.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTSystemInfo.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCBundleServiceInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCControlInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCUnprivilegedControlInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCNotifierInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTXPCSyncdInterface.m"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTLogging.m"
|
||||
)
|
||||
|
||||
add_executable("${PROJECT_NAME}" ${PROJECT_SOURCEFILES})
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE -ObjC)
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE
|
||||
MOLCertificate MOLCodesignChecker
|
||||
FMDB MOLXPCConnection
|
||||
|
||||
"-framework Cocoa"
|
||||
)
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-fobjc-arc -Wno-everything -fmodules
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE -g3)
|
||||
endif()
|
||||
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE
|
||||
COCOAPODS=1
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE DEBUG=1)
|
||||
else()
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE NDEBUG=1)
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad/DataLayer"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad/Logs"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
)
|
||||
|
||||
add_custom_command(TARGET "${PROJECT_NAME}" POST_BUILD
|
||||
COMMAND codesign --force --verify --verbose --sign "${CODESIGN_IDENTITY}" $<TARGET_FILE:${PROJECT_NAME}>
|
||||
COMMENT "Signing ${PROJECT_NAME} with the following identity: ${CODESIGN_IDENTITY}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
@@ -50,4 +50,5 @@
|
||||
|
||||
// A UTC Date formatter.
|
||||
@property(readonly, nonatomic) NSDateFormatter *dateFormatter;
|
||||
@property(readonly, nonatomic) NSString *machineID;
|
||||
@end
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#import "SNTCachedDecision.h"
|
||||
#import "SNTConfigurator.h"
|
||||
|
||||
@interface SNTEventLog ()
|
||||
@property NSMutableDictionary<NSNumber *, SNTCachedDecision *> *detailStore;
|
||||
@@ -43,6 +44,9 @@
|
||||
_dateFormatter = [[NSDateFormatter alloc] init];
|
||||
_dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
|
||||
_dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
|
||||
|
||||
// Grab the system UUID on init
|
||||
_machineID = [[SNTConfigurator configurator] machineID];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -77,14 +81,14 @@
|
||||
|
||||
- (void)cacheDecision:(SNTCachedDecision *)cd {
|
||||
dispatch_sync(self.detailStoreQueue, ^{
|
||||
self.detailStore[@(cd.vnodeId)] = cd;
|
||||
self.detailStore[@(cd.vnodeId.fileid)] = cd;
|
||||
});
|
||||
}
|
||||
|
||||
- (SNTCachedDecision *)cachedDecisionForMessage:(santa_message_t)message {
|
||||
__block SNTCachedDecision *cd;
|
||||
dispatch_sync(self.detailStoreQueue, ^{
|
||||
cd = self.detailStore[@(message.vnode_id)];
|
||||
cd = self.detailStore[@(message.vnode_id.fileid)];
|
||||
});
|
||||
return cd;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
message.uid, [self nameForUID:message.uid],
|
||||
message.gid, [self nameForGID:message.gid]];
|
||||
|
||||
if ([[SNTConfigurator configurator] enableMachineIDDecoration]) {
|
||||
[outStr appendFormat:@"|machineid=%@", self.machineID];
|
||||
}
|
||||
|
||||
[self writeLog:outStr];
|
||||
}
|
||||
|
||||
@@ -165,6 +169,10 @@
|
||||
[self addArgsForPid:message.pid toString:outLog];
|
||||
}
|
||||
|
||||
if ([[SNTConfigurator configurator] enableMachineIDDecoration]) {
|
||||
[outLog appendFormat:@"|machineid=%@", self.machineID];
|
||||
}
|
||||
|
||||
[self writeLog:outLog];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.santad</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<string>santad</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#import "SNTSyncdQueue.h"
|
||||
#import "SNTSyslogEventLog.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
#import "SNTXPCUnprivilegedControlInterface.h"
|
||||
#import "SNTXPCNotifierInterface.h"
|
||||
|
||||
@interface SNTApplication ()
|
||||
@@ -117,7 +118,8 @@
|
||||
|
||||
_controlConnection =
|
||||
[[MOLXPCConnection alloc] initServerWithName:[SNTXPCControlInterface serviceId]];
|
||||
_controlConnection.exportedInterface = [SNTXPCControlInterface controlInterface];
|
||||
_controlConnection.privilegedInterface = [SNTXPCControlInterface controlInterface];
|
||||
_controlConnection.unprivilegedInterface = [SNTXPCUnprivilegedControlInterface controlInterface];
|
||||
_controlConnection.exportedObject = dc;
|
||||
[_controlConnection resume];
|
||||
|
||||
@@ -250,7 +252,7 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
if (![props[@"DAVolumeMountable"] boolValue]) return;
|
||||
|
||||
[app.eventLog logDiskDisappeared:props];
|
||||
[app.driverManager flushCacheNonRootOnly:YES];
|
||||
[app.driverManager flushCache];
|
||||
}
|
||||
|
||||
- (void)startSyncd {
|
||||
@@ -261,10 +263,9 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
LOGI(@"Failed to fork");
|
||||
self.syncdPID = 0;
|
||||
} else if (self.syncdPID == 0) {
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
_exit(EPERM);
|
||||
}
|
||||
// The santactl executable will drop privileges just after the XPC
|
||||
// connection has been estabilished; this is done this way so that
|
||||
// the XPC authentication can occur
|
||||
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--daemon", "--syslog", NULL));
|
||||
}
|
||||
LOGI(@"santactl started with pid: %i", self.syncdPID);
|
||||
@@ -303,7 +304,7 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
if (!new && !old) return;
|
||||
if (![new.pattern isEqualToString:old.pattern]) {
|
||||
LOGI(@"Changed [white|black]list regex, flushing cache");
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
[self.driverManager flushCache];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,7 +312,7 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
- (void)clientModeDidChange:(SNTClientMode)clientMode {
|
||||
if (clientMode == SNTClientModeLockdown) {
|
||||
LOGI(@"Changed client mode, flushing cache.");
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
[self.driverManager flushCache];
|
||||
}
|
||||
[[self.notQueue.notifierConnection remoteObjectProxy] postClientModeNotification:clientMode];
|
||||
}
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTKernelCommon.h"
|
||||
|
||||
///
|
||||
/// Store information about executions from decision making for later logging.
|
||||
///
|
||||
@interface SNTCachedDecision : NSObject
|
||||
|
||||
@property uint64_t vnodeId;
|
||||
@property santa_vnode_id_t vnodeId;
|
||||
@property SNTEventState decision;
|
||||
@property NSString *decisionExtra;
|
||||
@property NSString *sha256;
|
||||
|
||||
@@ -70,19 +70,27 @@ double watchdogRAMPeak = 0;
|
||||
|
||||
#pragma mark Kernel ops
|
||||
|
||||
- (void)cacheCounts:(void (^)(uint64_t, uint64_t))reply {
|
||||
NSArray<NSNumber *> *counts = [self.driverManager cacheCounts];
|
||||
reply([counts[0] unsignedLongLongValue], [counts[1] unsignedLongLongValue]);
|
||||
- (void)cacheCounts:(void (^)(uint64_t))reply {
|
||||
uint64_t count = [self.driverManager cacheCount];
|
||||
reply(count);
|
||||
}
|
||||
|
||||
- (void)cacheBucketCount:(void (^)(NSArray *))reply {
|
||||
reply([self.driverManager cacheBucketCount]);
|
||||
}
|
||||
|
||||
- (void)flushCache:(void (^)(BOOL))reply {
|
||||
reply([self.driverManager flushCacheNonRootOnly:NO]);
|
||||
reply([self.driverManager flushCache]);
|
||||
}
|
||||
|
||||
- (void)checkCacheForVnodeID:(uint64_t)vnodeID withReply:(void (^)(santa_action_t))reply {
|
||||
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply {
|
||||
reply([self.driverManager checkCache:vnodeID]);
|
||||
}
|
||||
|
||||
- (void)driverConnectionEstablished:(void (^)(BOOL))reply {
|
||||
reply(self.driverManager.connectionEstablished);
|
||||
}
|
||||
|
||||
#pragma mark Database ops
|
||||
|
||||
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply {
|
||||
@@ -100,7 +108,7 @@ double watchdogRAMPeak = 0;
|
||||
NSPredicate *p = [NSPredicate predicateWithFormat:@"SELF.state != %d", SNTRuleStateWhitelist];
|
||||
if ([rules filteredArrayUsingPredicate:p].count || cleanSlate) {
|
||||
LOGI(@"Received non-whitelist rule, flushing cache");
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
[self.driverManager flushCache];
|
||||
}
|
||||
|
||||
reply(error);
|
||||
|
||||
@@ -45,21 +45,30 @@
|
||||
///
|
||||
/// Sends a response to a query back to the kernel.
|
||||
///
|
||||
- (kern_return_t)postToKernelAction:(santa_action_t)action forVnodeID:(uint64_t)vnodeId;
|
||||
- (kern_return_t)postToKernelAction:(santa_action_t)action forVnodeID:(santa_vnode_id_t)vnodeId;
|
||||
|
||||
///
|
||||
/// Get the number of binaries in the kernel's caches.
|
||||
///
|
||||
- (NSArray<NSNumber *> *)cacheCounts;
|
||||
- (uint64_t)cacheCount;
|
||||
|
||||
///
|
||||
/// Return an array representing all buckets in the kernel's decision cache where each number
|
||||
/// is the number of items in that bucket.
|
||||
///
|
||||
- (NSArray<NSNumber *> *)cacheBucketCount;
|
||||
|
||||
///
|
||||
/// Flush the kernel's binary cache.
|
||||
///
|
||||
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly;
|
||||
- (BOOL)flushCache;
|
||||
|
||||
///
|
||||
/// Check the kernel cache for a VnodeID
|
||||
///
|
||||
- (santa_action_t)checkCache:(uint64_t)vnodeID;
|
||||
- (santa_action_t)checkCache:(santa_vnode_id_t)vnodeID;
|
||||
|
||||
/// Returns whether the connection to the driver has been established.
|
||||
@property(readonly) BOOL connectionEstablished;
|
||||
|
||||
@end
|
||||
|
||||
@@ -23,61 +23,27 @@
|
||||
|
||||
@interface SNTDriverManager ()
|
||||
@property io_connect_t connection;
|
||||
@property(readwrite) BOOL connectionEstablished;
|
||||
@end
|
||||
|
||||
@implementation SNTDriverManager
|
||||
|
||||
static const int MAX_DELAY = 15;
|
||||
|
||||
#pragma mark init/dealloc
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
kern_return_t kr;
|
||||
io_service_t serviceObject;
|
||||
CFDictionaryRef classToMatch;
|
||||
|
||||
if (!(classToMatch = IOServiceMatching(USERCLIENT_CLASS))) {
|
||||
LOGD(@"Failed to create matching dictionary");
|
||||
CFDictionaryRef classToMatch = IOServiceMatching(USERCLIENT_CLASS);
|
||||
if (!classToMatch) {
|
||||
LOGE(@"Failed to create matching dictionary");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Attempt to load driver. It may already be running, so ignore any return value.
|
||||
KextManagerLoadKextWithIdentifier(CFSTR(USERCLIENT_ID), NULL);
|
||||
|
||||
// Locate driver. Wait for it if necessary.
|
||||
int delay = 1;
|
||||
do {
|
||||
CFRetain(classToMatch); // this ref is released by IOServiceGetMatchingService
|
||||
serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, classToMatch);
|
||||
|
||||
if (!serviceObject) {
|
||||
LOGD(@"Waiting for Santa driver to become available");
|
||||
sleep(delay);
|
||||
if (delay < MAX_DELAY) delay *= 2;
|
||||
}
|
||||
} while (!serviceObject);
|
||||
CFRelease(classToMatch);
|
||||
|
||||
// This calls `initWithTask`, `attach` and `start` in `SantaDriverClient`
|
||||
kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &_connection);
|
||||
IOObjectRelease(serviceObject);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOGD(@"Failed to open Santa driver service");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Call `open` in `SantaDriverClient`
|
||||
kr = IOConnectCallMethod(_connection, kSantaUserClientOpen, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
if (kr == kIOReturnExclusiveAccess) {
|
||||
LOGD(@"A client is already connected");
|
||||
return nil;
|
||||
} else if (kr != kIOReturnSuccess) {
|
||||
LOGD(@"An error occurred while opening the connection");
|
||||
return nil;
|
||||
}
|
||||
// Wait for the driver to appear
|
||||
[self waitForDriver:classToMatch];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -86,6 +52,71 @@ static const int MAX_DELAY = 15;
|
||||
IOServiceClose(_connection);
|
||||
}
|
||||
|
||||
#pragma mark Driver Waiting
|
||||
|
||||
// Helper function used with IOServiceAddMatchingNotification which expects
|
||||
// a DriverAppearedBlock to be passed as the 'reference' argument. The block
|
||||
// will be called for each object iterated over and if the block wants to keep
|
||||
// the object, it should call IOObjectRetain().
|
||||
typedef void (^DriverAppearedBlock)(io_object_t object);
|
||||
static void driverAppearedHandler(void *info, io_iterator_t iterator) {
|
||||
DriverAppearedBlock block = (__bridge DriverAppearedBlock)info;
|
||||
if (!block) return;
|
||||
io_object_t object = 0;
|
||||
while ((object = IOIteratorNext(iterator))) {
|
||||
block(object);
|
||||
IOObjectRelease(object);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the driver to appear, then attach to and open it.
|
||||
- (void)waitForDriver:(CFDictionaryRef CF_RELEASES_ARGUMENT)matchingDict {
|
||||
IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
CFRunLoopAddSource([[NSRunLoop currentRunLoop] getCFRunLoop],
|
||||
IONotificationPortGetRunLoopSource(notificationPort),
|
||||
kCFRunLoopDefaultMode);
|
||||
|
||||
io_iterator_t iterator = 0;
|
||||
|
||||
DriverAppearedBlock block = ^(io_object_t object) {
|
||||
// This calls `initWithTask`, `attach` and `start` in `SantaDriverClient`
|
||||
kern_return_t kr = IOServiceOpen(object, mach_task_self(), 0, &_connection);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOGE(@"Failed to open santa-driver service: 0x%X", kr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Call `open` in `SantaDriverClient`
|
||||
kr = IOConnectCallMethod(_connection, kSantaUserClientOpen, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
if (kr == kIOReturnExclusiveAccess) {
|
||||
LOGE(@"A client is already connected");
|
||||
exit(2);
|
||||
} else if (kr != kIOReturnSuccess) {
|
||||
LOGE(@"An error occurred while opening the connection: 0x%X", kr);
|
||||
exit(3);
|
||||
}
|
||||
|
||||
// Release the iterator to disarm the notifications.
|
||||
IOObjectRelease(iterator);
|
||||
IONotificationPortDestroy(notificationPort);
|
||||
|
||||
_connectionEstablished = YES;
|
||||
};
|
||||
|
||||
LOGI(@"Waiting for Santa driver to become available");
|
||||
|
||||
IOServiceAddMatchingNotification(notificationPort,
|
||||
kIOMatchedNotification,
|
||||
matchingDict,
|
||||
driverAppearedHandler,
|
||||
(__bridge_retained void *)block,
|
||||
&iterator);
|
||||
|
||||
// Call the handler once to 'empty' the iterator, arming it. If the driver is already loaded
|
||||
// this will immediately cause the connection to be fully established.
|
||||
driverAppearedHandler((__bridge_retained void*)block, iterator);
|
||||
}
|
||||
|
||||
#pragma mark Incoming messages
|
||||
|
||||
- (void)listenForDecisionRequests:(void (^)(santa_message_t))callback {
|
||||
@@ -98,7 +129,7 @@ static const int MAX_DELAY = 15;
|
||||
|
||||
- (void)listenForRequestsOfType:(santa_queuetype_t)type
|
||||
withCallback:(void (^)(santa_message_t))callback {
|
||||
kern_return_t kr;
|
||||
while (!self.connectionEstablished) usleep(100000); // 100ms
|
||||
|
||||
// Allocate a mach port to receive notifactions from the IODataQueue
|
||||
mach_port_t receivePort = IODataQueueAllocateNotificationPort();
|
||||
@@ -108,9 +139,9 @@ static const int MAX_DELAY = 15;
|
||||
}
|
||||
|
||||
// This will call registerNotificationPort() inside our user client class
|
||||
kr = IOConnectSetNotificationPort(self.connection, type, receivePort, 0);
|
||||
kern_return_t kr = IOConnectSetNotificationPort(self.connection, type, receivePort, 0);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOGD(@"Failed to register notification port for type %d: %d", type, kr);
|
||||
LOGD(@"Failed to register notification port for type %d: 0x%X", type, kr);
|
||||
mach_port_destroy(mach_task_self(), receivePort);
|
||||
return;
|
||||
}
|
||||
@@ -120,7 +151,7 @@ static const int MAX_DELAY = 15;
|
||||
mach_vm_size_t size = 0;
|
||||
kr = IOConnectMapMemory(self.connection, type, mach_task_self(), &address, &size, kIOMapAnywhere);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOGD(@"Failed to map memory for type %d: %d", type, kr);
|
||||
LOGD(@"Failed to map memory for type %d: 0x%X", type, kr);
|
||||
mach_port_destroy(mach_task_self(), receivePort);
|
||||
return;
|
||||
}
|
||||
@@ -135,7 +166,7 @@ static const int MAX_DELAY = 15;
|
||||
if (kr == kIOReturnSuccess) {
|
||||
callback(vdata);
|
||||
} else {
|
||||
LOGE(@"Error dequeuing data for type %d: %d", type, kr);
|
||||
LOGE(@"Error dequeuing data for type %d: 0x%X", type, kr);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
@@ -147,27 +178,27 @@ static const int MAX_DELAY = 15;
|
||||
|
||||
#pragma mark Outgoing messages
|
||||
|
||||
- (kern_return_t)postToKernelAction:(santa_action_t)action forVnodeID:(uint64_t)vnodeId {
|
||||
- (kern_return_t)postToKernelAction:(santa_action_t)action forVnodeID:(santa_vnode_id_t)vnodeId {
|
||||
switch (action) {
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
return IOConnectCallScalarMethod(_connection,
|
||||
return IOConnectCallStructMethod(_connection,
|
||||
kSantaUserClientAllowBinary,
|
||||
&vnodeId,
|
||||
1,
|
||||
sizeof(vnodeId),
|
||||
0,
|
||||
0);
|
||||
case ACTION_RESPOND_DENY:
|
||||
return IOConnectCallScalarMethod(_connection,
|
||||
return IOConnectCallStructMethod(_connection,
|
||||
kSantaUserClientDenyBinary,
|
||||
&vnodeId,
|
||||
1,
|
||||
sizeof(vnodeId),
|
||||
0,
|
||||
0);
|
||||
case ACTION_RESPOND_ACK:
|
||||
return IOConnectCallScalarMethod(_connection,
|
||||
return IOConnectCallStructMethod(_connection,
|
||||
kSantaUserClientAcknowledgeBinary,
|
||||
&vnodeId,
|
||||
1,
|
||||
sizeof(vnodeId),
|
||||
0,
|
||||
0);
|
||||
default:
|
||||
@@ -175,9 +206,9 @@ static const int MAX_DELAY = 15;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)cacheCounts {
|
||||
uint32_t input_count = 2;
|
||||
uint64_t cache_counts[2] = {0, 0};
|
||||
- (uint64_t)cacheCount {
|
||||
uint32_t input_count = 1;
|
||||
uint64_t cache_counts[1] = {0};
|
||||
|
||||
IOConnectCallScalarMethod(_connection,
|
||||
kSantaUserClientCacheCount,
|
||||
@@ -186,30 +217,47 @@ static const int MAX_DELAY = 15;
|
||||
cache_counts,
|
||||
&input_count);
|
||||
|
||||
return @[ @(cache_counts[0]), @(cache_counts[1]) ];
|
||||
return cache_counts[0];
|
||||
}
|
||||
|
||||
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly {
|
||||
const uint64_t nonRoot = nonRootOnly;
|
||||
- (BOOL)flushCache {
|
||||
return IOConnectCallScalarMethod(_connection,
|
||||
kSantaUserClientClearCache,
|
||||
&nonRoot,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0) == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
- (santa_action_t)checkCache:(uint64_t)vnodeID {
|
||||
uint32_t input_count = 1;
|
||||
uint64_t vnode_action = 0;
|
||||
- (santa_action_t)checkCache:(santa_vnode_id_t)vnodeID {
|
||||
uint64_t output;
|
||||
uint32_t outputCnt = 1;
|
||||
|
||||
IOConnectCallScalarMethod(_connection,
|
||||
kSantaUserClientCheckCache,
|
||||
&vnodeID,
|
||||
1,
|
||||
&vnode_action,
|
||||
&input_count);
|
||||
return (santa_action_t)vnode_action;
|
||||
IOConnectCallMethod(self.connection, kSantaUserClientCheckCache,
|
||||
NULL, 0, &vnodeID, sizeof(santa_vnode_id_t),
|
||||
&output, &outputCnt, NULL, 0);
|
||||
return (santa_action_t)output;
|
||||
}
|
||||
|
||||
- (NSArray *)cacheBucketCount {
|
||||
santa_bucket_count_t counts = {};
|
||||
size_t size = sizeof(counts);
|
||||
|
||||
NSMutableArray *a = [NSMutableArray array];
|
||||
|
||||
do {
|
||||
IOConnectCallStructMethod(self.connection,
|
||||
kSantaUserClientCacheBucketCount,
|
||||
&counts,
|
||||
size,
|
||||
&counts,
|
||||
&size);
|
||||
for (uint64_t i = 0; i < sizeof(counts.per_bucket) / sizeof(uint16_t); ++i) {
|
||||
[a addObject:@(counts.per_bucket[i])];
|
||||
}
|
||||
} while (counts.start > 0);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -90,7 +90,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
|
||||
- (void)validateBinaryWithMessage:(santa_message_t)message {
|
||||
// Get info about the file. If we can't get this info, allow execution and log an error.
|
||||
if (unlikely(message.path == NULL)) {
|
||||
LOGE(@"Path for vnode_id is NULL: %llu", message.vnode_id);
|
||||
LOGE(@"Path for vnode_id is NULL: %llu/%llu", message.vnode_id.fsid, message.vnode_id.fileid);
|
||||
[_driverManager postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:message.vnode_id];
|
||||
return;
|
||||
}
|
||||
@@ -115,14 +115,16 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
|
||||
[_driverManager postToKernelAction:ACTION_RESPOND_ACK forVnodeID:message.vnode_id];
|
||||
}
|
||||
|
||||
// Get codesigning info about the file.
|
||||
NSError *csError;
|
||||
MOLCodesignChecker *csInfo =
|
||||
[[MOLCodesignChecker alloc] initWithBinaryPath:binInfo.path
|
||||
fileDescriptor:binInfo.fileHandle.fileDescriptor
|
||||
error:&csError];
|
||||
// Ignore codesigning if there are any errors with the signature.
|
||||
if (csError) csInfo = nil;
|
||||
// Get codesigning info about the file but only if it's a Mach-O.
|
||||
MOLCodesignChecker *csInfo;
|
||||
if (binInfo.isMachO) {
|
||||
NSError *csError;
|
||||
csInfo = [[MOLCodesignChecker alloc] initWithBinaryPath:binInfo.path
|
||||
fileDescriptor:binInfo.fileHandle.fileDescriptor
|
||||
error:&csError];
|
||||
// Ignore codesigning if there are any errors with the signature.
|
||||
if (csError) csInfo = nil;
|
||||
}
|
||||
|
||||
// Actually make the decision.
|
||||
SNTCachedDecision *cd = [self.policyProcessor decisionForFileInfo:binInfo
|
||||
@@ -139,7 +141,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
|
||||
if (action == ACTION_RESPOND_ALLOW) [_eventLog cacheDecision:cd];
|
||||
|
||||
// Send the decision to the kernel.
|
||||
[_driverManager postToKernelAction:action forVnodeID:cd.vnodeId];
|
||||
[_driverManager postToKernelAction:action forVnodeID:message.vnode_id];
|
||||
|
||||
// Log to database if necessary.
|
||||
if (cd.decision != SNTEventStateAllowBinary &&
|
||||
|
||||
21
Tests/CMakeLists.txt
Normal file
21
Tests/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
|
||||
function(main)
|
||||
file(GLOB test_list LIST_DIRECTORIES true "${CMAKE_CURRENT_SOURCE_DIR}/*")
|
||||
|
||||
foreach(test_path ${test_list})
|
||||
if(NOT EXISTS "${test_path}/CMakeLists.txt")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
if("${test_path}" STREQUAL "${CMAKE_SOURCE_DIR}/Tests/LogicTests")
|
||||
message("Not adding LogicTests (not yet supported)")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
message(STATUS "Adding test folder: ${test_path}")
|
||||
add_subdirectory("${test_path}")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
31
Tests/KernelTests/CMakeLists.txt
Normal file
31
Tests/KernelTests/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(KernelTests)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
function(main)
|
||||
# Include all files to make IDEs happy
|
||||
set(PROJECT_SOURCEFILES
|
||||
main.mm
|
||||
)
|
||||
|
||||
add_executable("${PROJECT_NAME}" EXCLUDE_FROM_ALL ${PROJECT_SOURCEFILES})
|
||||
AddTestTarget("${PROJECT_NAME}" true)
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-fobjc-arc -Wno-everything -fmodules
|
||||
-fcxx-modules -mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE DEBUG=1)
|
||||
else()
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE NDEBUG=1)
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
@@ -14,12 +14,14 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <IOKit/IOKitLib.h>
|
||||
#import <IOKit/kext/KextManager.h>
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <libkern/OSKextLib.h>
|
||||
#include <mach/mach.h>
|
||||
#include <numeric>
|
||||
#include <sys/ptrace.h>
|
||||
@@ -31,34 +33,41 @@
|
||||
///
|
||||
/// Kernel Extension Tests
|
||||
///
|
||||
/// Build and launch as root while the kernel extension is loaded and nothing is already connected.
|
||||
/// Build and launch as root. This target is dependent on the santa-driver target and these
|
||||
/// tests will load santa-driver from the same location this binary is executed from, unloading
|
||||
/// any existing driver (and daemon) if necessary.
|
||||
///
|
||||
|
||||
#define TSTART(testName) \
|
||||
do { printf(" %-50s ", testName); } while (0)
|
||||
#define TPASS() \
|
||||
do { printf("\x1b[32mPASS\x1b[0m\n"); } while (0)
|
||||
do { printf("PASS\n"); } while (0)
|
||||
#define TPASSINFO(fmt, ...) \
|
||||
do { printf("\x1b[32mPASS\x1b[0m\n " fmt "\n", ##__VA_ARGS__); } while (0)
|
||||
do { printf("PASS\n " fmt "\n", ##__VA_ARGS__); } while (0)
|
||||
#define TFAIL() \
|
||||
do { \
|
||||
printf("\x1b[31mFAIL\x1b[0m\n"); \
|
||||
printf("FAIL\n"); \
|
||||
[self unloadExtension]; \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
#define TFAILINFO(fmt, ...) \
|
||||
do { \
|
||||
printf("\x1b[31mFAIL\x1b[0m\n -> " fmt "\n\nTest failed.\n\n", ##__VA_ARGS__); \
|
||||
printf("FAIL\n -> " fmt "\n\nTest failed.\n\n", ##__VA_ARGS__); \
|
||||
[self unloadExtension]; \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
@interface SantaKernelTests : NSObject
|
||||
@property io_connect_t connection;
|
||||
@property int timesSeenLs;
|
||||
@property int timesSeenCat;
|
||||
@property int timesSeenCp;
|
||||
|
||||
@property int testExeIteration;
|
||||
@property int timesSeenTestExeIteration;
|
||||
// A block that tests can set to handle specific files/binaries.
|
||||
// The block should return an action to respond to the kernel with.
|
||||
// If no block is specified or no action is returned, the exec will be allowed.
|
||||
@property(atomic, copy) santa_action_t (^handlerBlock)(santa_message_t msg);
|
||||
|
||||
- (void)unloadDaemon;
|
||||
- (void)unloadExtension;
|
||||
- (void)loadExtension;
|
||||
- (void)runTests;
|
||||
@end
|
||||
|
||||
@@ -94,21 +103,22 @@
|
||||
|
||||
/// Call in-kernel function: |kSantaUserClientAllowBinary| or |kSantaUserClientDenyBinary|
|
||||
/// passing the |vnodeID|.
|
||||
- (void)postToKernelAction:(santa_action_t)action forVnodeID:(uint64_t)vnodeid {
|
||||
if (action == ACTION_RESPOND_ALLOW) {
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientAllowBinary, &vnodeid, 1, 0, 0);
|
||||
} else if (action == ACTION_RESPOND_DENY) {
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientDenyBinary, &vnodeid, 1, 0, 0);
|
||||
- (void)postToKernelAction:(santa_action_t)action forVnodeID:(santa_vnode_id_t)vnodeid {
|
||||
if (action == ACTION_RESPOND_DENY) {
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientDenyBinary,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
} else if (action == ACTION_RESPOND_ACK) {
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientAcknowledgeBinary,
|
||||
&vnodeid, 1, 0, 0);
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientAcknowledgeBinary,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
} else {
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientAllowBinary,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Call in-kernel function: |kSantaUserClientClearCache|
|
||||
- (void)flushCache {
|
||||
uint64_t nonRootOnly = 0;
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientClearCache, &nonRootOnly, 1, 0, 0);
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientClearCache, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
#pragma mark - Connection Tests
|
||||
@@ -177,23 +187,16 @@
|
||||
/// From then on, monitors the IODataQueue and responds for files specifically used in other tests.
|
||||
/// For everything else, allows execution normally to avoid deadlocking the system.
|
||||
- (void)beginListening {
|
||||
kern_return_t kr;
|
||||
santa_message_t vdata;
|
||||
UInt32 dataSize;
|
||||
IODataQueueMemory *queueMemory;
|
||||
mach_port_t receivePort;
|
||||
|
||||
mach_vm_address_t address = 0;
|
||||
mach_vm_size_t size = 0;
|
||||
|
||||
TSTART("Allocates a notification port");
|
||||
mach_port_t receivePort;
|
||||
if (!(receivePort = IODataQueueAllocateNotificationPort())) {
|
||||
TFAIL();
|
||||
}
|
||||
TPASS();
|
||||
|
||||
TSTART("Registers the notification port");
|
||||
kr = IOConnectSetNotificationPort(self.connection, QUEUETYPE_DECISION, receivePort, 0);
|
||||
kern_return_t kr = IOConnectSetNotificationPort(
|
||||
self.connection, QUEUETYPE_DECISION, receivePort, 0);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
mach_port_destroy(mach_task_self(), receivePort);
|
||||
TFAILINFO("KR: %d", kr);
|
||||
@@ -202,6 +205,8 @@
|
||||
TPASS();
|
||||
|
||||
TSTART("Maps shared memory");
|
||||
mach_vm_address_t address = 0;
|
||||
mach_vm_size_t size = 0;
|
||||
kr = IOConnectMapMemory(self.connection, QUEUETYPE_DECISION, mach_task_self(),
|
||||
&address, &size, kIOMapAnywhere);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
@@ -210,85 +215,26 @@
|
||||
}
|
||||
TPASS();
|
||||
|
||||
// Fetch the SHA-256 of /bin/ed, as we'll be using that for the cache invalidation test.
|
||||
NSString *edSHA = [self sha256ForPath:@"/bin/ed"];
|
||||
|
||||
// Create the RE used for matching testexe's
|
||||
NSString *cwd = [[NSFileManager defaultManager] currentDirectoryPath];
|
||||
NSString *pattern = [cwd stringByAppendingPathComponent:@"testexe\\.(\\d+)"];
|
||||
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
||||
options:0
|
||||
error:NULL];
|
||||
|
||||
/// Begin listening for events
|
||||
queueMemory = (IODataQueueMemory *)address;
|
||||
IODataQueueMemory *queueMemory = (IODataQueueMemory *)address;
|
||||
do {
|
||||
while (IODataQueueDataAvailable(queueMemory)) {
|
||||
dataSize = sizeof(vdata);
|
||||
santa_message_t vdata;
|
||||
UInt32 dataSize = sizeof(vdata);
|
||||
kr = IODataQueueDequeue(queueMemory, &vdata, &dataSize);
|
||||
if (kr == kIOReturnSuccess) {
|
||||
if (vdata.action != ACTION_REQUEST_BINARY) continue;
|
||||
|
||||
if ([[self sha256ForPath:@(vdata.path)] isEqual:edSHA]) {
|
||||
[self postToKernelAction:ACTION_RESPOND_DENY forVnodeID:vdata.vnode_id];
|
||||
} else if (strncmp("/bin/mv", vdata.path, strlen("/bin/mv")) == 0) {
|
||||
[self postToKernelAction:ACTION_RESPOND_DENY forVnodeID:vdata.vnode_id];
|
||||
} else if (strncmp("/bin/ls", vdata.path, strlen("/bin/ls")) == 0) {
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
|
||||
self.timesSeenLs++;
|
||||
} else if (strncmp("/bin/cp", vdata.path, strlen("/bin/cp")) == 0) {
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
|
||||
self.timesSeenCp++;
|
||||
} else if (strncmp("/bin/cat", vdata.path, strlen("/bin/cat")) == 0) {
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
|
||||
self.timesSeenCat++;
|
||||
} else if (strncmp("/usr/bin/cal", vdata.path, strlen("/usr/bin/cal")) == 0) {
|
||||
static int count = 0;
|
||||
if (count++) TFAILINFO("Large binary should not re-request");
|
||||
[self postToKernelAction:ACTION_RESPOND_ACK forVnodeID:vdata.vnode_id];
|
||||
for (int i = 0; i < 15; ++i) {
|
||||
printf("\033[s"); // save cursor position
|
||||
printf("%i/15", i);
|
||||
sleep(1);
|
||||
printf("\033[u"); // restore cursor position
|
||||
}
|
||||
printf("\033[K\033[u"); // clear line, restore cursor position
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
|
||||
} else if (strncmp("/bin/ln", vdata.path, strlen("/bin/ln")) == 0) {
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
|
||||
|
||||
TSTART("Sends valid pid/ppid");
|
||||
if (vdata.pid < 1 || vdata.ppid < 1) {
|
||||
TFAIL();
|
||||
}
|
||||
TPASSINFO("Received pid, ppid: %d, %d", vdata.pid, vdata.ppid);
|
||||
} else {
|
||||
NSString *path = @(vdata.path);
|
||||
|
||||
// If current executable is one of our test exe's from handlesLotsOfBinaries,
|
||||
// check that the number has increased.
|
||||
NSArray *matches = [re matchesInString:path
|
||||
options:0
|
||||
range:NSMakeRange(0, path.length)];
|
||||
if (matches.count == 1 && [matches[0] numberOfRanges] == 2) {
|
||||
NSUInteger count = [[path substringWithRange:[matches[0] rangeAtIndex:1]] intValue];
|
||||
if (count <= self.testExeIteration && count > 0) {
|
||||
self.timesSeenTestExeIteration++;
|
||||
if (self.timesSeenTestExeIteration > 2) {
|
||||
TFAILINFO("Saw same binary several times");
|
||||
}
|
||||
} else {
|
||||
self.timesSeenTestExeIteration = 0;
|
||||
self.testExeIteration = (int)count;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow everything not related to our testing.
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
|
||||
}
|
||||
} else {
|
||||
if (kr != kIOReturnSuccess) {
|
||||
TFAILINFO("Error receiving data: %d", kr);
|
||||
continue;
|
||||
}
|
||||
if (vdata.action != ACTION_REQUEST_BINARY) continue;
|
||||
|
||||
santa_action_t action = ACTION_RESPOND_ALLOW;
|
||||
|
||||
@synchronized(self) {
|
||||
if (self.handlerBlock) action = self.handlerBlock(vdata);
|
||||
}
|
||||
|
||||
[self postToKernelAction:action forVnodeID:vdata.vnode_id];
|
||||
}
|
||||
} while (IODataQueueWaitForAvailableData(queueMemory, receivePort) == kIOReturnSuccess);
|
||||
|
||||
@@ -298,12 +244,15 @@
|
||||
|
||||
#pragma mark - Functional Tests
|
||||
|
||||
/// Tests that blocking works correctly
|
||||
- (void)receiveAndBlockTests {
|
||||
TSTART("Blocks denied binaries");
|
||||
|
||||
NSTask *ed = [self taskWithPath:@"/bin/ed"];
|
||||
self.handlerBlock = ^santa_action_t(santa_message_t msg) {
|
||||
if (strncmp("/bin/ed", msg.path, 7) == 0) return ACTION_RESPOND_DENY;
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
};
|
||||
|
||||
NSTask *ed = [self taskWithPath:@"/bin/ed"];
|
||||
@try {
|
||||
[ed launch];
|
||||
[ed waitUntilExit];
|
||||
@@ -314,17 +263,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that an allowed binary is cached
|
||||
- (void)receiveAndCacheTests {
|
||||
TSTART("Permits & caches allowed binaries");
|
||||
|
||||
self.timesSeenLs = 0;
|
||||
__block int timesSeenLs = 0;
|
||||
self.handlerBlock = ^santa_action_t(santa_message_t msg) {
|
||||
if (strncmp("/bin/ls", msg.path, 7) == 0) ++timesSeenLs;
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
};
|
||||
|
||||
NSTask *ls = [self taskWithPath:@"/bin/ls"];
|
||||
[ls launch];
|
||||
[ls waitUntilExit];
|
||||
|
||||
if (self.timesSeenLs != 1) {
|
||||
if (timesSeenLs != 1) {
|
||||
TFAILINFO("Didn't record first run of ls");
|
||||
}
|
||||
|
||||
@@ -332,25 +284,37 @@
|
||||
[ls launch];
|
||||
[ls waitUntilExit];
|
||||
|
||||
if (self.timesSeenLs > 1) {
|
||||
if (timesSeenLs > 1) {
|
||||
TFAILINFO("Received request for ls a second time");
|
||||
}
|
||||
|
||||
TPASS();
|
||||
}
|
||||
|
||||
/// Tests that a write to a cached vnode will invalidate the cached response for that file
|
||||
- (void)invalidatesCacheTests {
|
||||
TSTART("Invalidates cache for manually closed FDs");
|
||||
|
||||
// Copy the ls binary to a new file
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
if (![fm copyItemAtPath:@"/bin/pwd" toPath:@"invalidacachetest_tmp" error:nil]) {
|
||||
NSString *target =
|
||||
[[fm currentDirectoryPath] stringByAppendingPathComponent:@"invalidatecachetest"];
|
||||
NSString *edSHA = [self sha256ForPath:@"/bin/ed"];
|
||||
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
self.handlerBlock = ^santa_action_t(santa_message_t msg) {
|
||||
__strong __typeof(weakSelf) self = weakSelf;
|
||||
if ([[self sha256ForPath:@(msg.path)] isEqual:edSHA]) {
|
||||
return ACTION_RESPOND_DENY;
|
||||
}
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
};
|
||||
|
||||
// Copy the pwd binary to a new file
|
||||
if (![fm copyItemAtPath:@"/bin/pwd" toPath:target error:nil]) {
|
||||
TFAILINFO("Failed to create temp file");
|
||||
}
|
||||
|
||||
// Launch the new file to put it in the cache
|
||||
NSTask *pwd = [self taskWithPath:@"invalidacachetest_tmp"];
|
||||
NSTask *pwd = [self taskWithPath:target];
|
||||
[pwd launch];
|
||||
[pwd waitUntilExit];
|
||||
|
||||
@@ -362,7 +326,7 @@
|
||||
// Now replace the contents of the test file (which is cached) with the contents of /bin/ed,
|
||||
// which is 'blacklisted' by SHA-256 during the tests.
|
||||
FILE *infile = fopen("/bin/ed", "r");
|
||||
FILE *outfile = fopen("invalidacachetest_tmp", "w");
|
||||
FILE *outfile = fopen(target.UTF8String, "w");
|
||||
int ch;
|
||||
while ((ch = fgetc(infile)) != EOF) {
|
||||
fputc(ch, outfile);
|
||||
@@ -370,13 +334,13 @@
|
||||
fclose(infile);
|
||||
|
||||
// Now try running the temp file again. If it succeeds, the test failed.
|
||||
NSTask *ed = [self taskWithPath:@"invalidacachetest_tmp"];
|
||||
NSTask *ed = [self taskWithPath:target];
|
||||
|
||||
@try {
|
||||
[ed launch];
|
||||
[ed waitUntilExit];
|
||||
TFAILINFO("Launched after write while file open");
|
||||
[fm removeItemAtPath:@"invalidacachetest_tmp" error:nil];
|
||||
[fm removeItemAtPath:target error:nil];
|
||||
} @catch (NSException *exception) {
|
||||
// This is a pass, but we have more to do.
|
||||
}
|
||||
@@ -385,7 +349,7 @@
|
||||
fclose(outfile);
|
||||
|
||||
// And try running the temp file again. If it succeeds, the test failed.
|
||||
ed = [self taskWithPath:@"invalidacachetest_tmp"];
|
||||
ed = [self taskWithPath:target];
|
||||
|
||||
@try {
|
||||
[ed launch];
|
||||
@@ -394,14 +358,25 @@
|
||||
} @catch (NSException *exception) {
|
||||
TPASS();
|
||||
} @finally {
|
||||
[fm removeItemAtPath:@"invalidacachetest_tmp" error:nil];
|
||||
[fm removeItemAtPath:target error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)invalidatesCacheAutoCloseTest {
|
||||
TSTART("Invalidates cache for auto-closed FDs");
|
||||
TSTART("Invalidates cache for auto closed FDs");
|
||||
|
||||
// Check invalidations when kernel auto-closes descriptors
|
||||
NSString *edSHA = [self sha256ForPath:@"/bin/ed"];
|
||||
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
self.handlerBlock = ^santa_action_t(santa_message_t msg) {
|
||||
__strong __typeof(weakSelf) self = weakSelf;
|
||||
if ([[self sha256ForPath:@(msg.path)] isEqual:edSHA]) {
|
||||
return ACTION_RESPOND_DENY;
|
||||
}
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
};
|
||||
|
||||
// Create temporary file
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
if (![fm copyItemAtPath:@"/bin/pwd" toPath:@"invalidacachetest_tmp" error:nil]) {
|
||||
TFAILINFO("Failed to create temp file");
|
||||
@@ -411,13 +386,11 @@
|
||||
NSTask *pwd = [self taskWithPath:@"invalidacachetest_tmp"];
|
||||
[pwd launch];
|
||||
[pwd waitUntilExit];
|
||||
|
||||
// Exit if this fails with a useful message.
|
||||
if ([pwd terminationStatus] != 0) {
|
||||
TFAILINFO("Second launch of test binary failed");
|
||||
}
|
||||
|
||||
// Replace file contents
|
||||
// Replace file contents using dd, which doesn't close FDs
|
||||
NSDictionary *attrs = [fm attributesOfItemAtPath:@"/bin/ed" error:NULL];
|
||||
NSTask *dd = [self taskWithPath:@"/bin/dd"];
|
||||
dd.arguments = @[ @"if=/bin/ed",
|
||||
@@ -441,17 +414,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests the clear cache function works correctly
|
||||
- (void)clearCacheTests {
|
||||
TSTART("Can clear cache");
|
||||
|
||||
self.timesSeenCat = 0;
|
||||
__block int timesSeenCat = 0;
|
||||
self.handlerBlock = ^santa_action_t(santa_message_t msg) {
|
||||
if (strncmp("/bin/cat", msg.path, 8) == 0) ++timesSeenCat;
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
};
|
||||
|
||||
NSTask *cat = [self taskWithPath:@"/bin/cat"];
|
||||
[cat launch];
|
||||
[cat waitUntilExit];
|
||||
|
||||
if (self.timesSeenCat != 1) {
|
||||
if (timesSeenCat != 1) {
|
||||
TFAILINFO("Didn't record first run of cat");
|
||||
}
|
||||
|
||||
@@ -461,23 +437,27 @@
|
||||
[cat launch];
|
||||
[cat waitUntilExit];
|
||||
|
||||
if (self.timesSeenCat != 2) {
|
||||
if (timesSeenCat != 2) {
|
||||
TFAIL();
|
||||
}
|
||||
|
||||
TPASS();
|
||||
}
|
||||
|
||||
/// Tests that the kernel still denies blocked binaries even if launched while traced
|
||||
- (void)blocksDeniedTracedBinaries {
|
||||
TSTART("Denies blocked processes running while traced");
|
||||
|
||||
self.handlerBlock = ^santa_action_t(santa_message_t msg) {
|
||||
if (strncmp("/bin/mv", msg.path, 7) == 0) return ACTION_RESPOND_DENY;
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
};
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
TFAILINFO("Failed to fork");
|
||||
} else if (pid > 0) {
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
while (waitpid(pid, &status, 0) != pid); // handle EINTR
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == EPERM) {
|
||||
TPASS();
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
@@ -494,44 +474,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that the kernel can handle _lots_ of executions.
|
||||
- (void)handlesLotsOfBinaries {
|
||||
TSTART("Handles lots of binaries");
|
||||
|
||||
const int LIMIT = 12000;
|
||||
|
||||
for (int i = 0; i < LIMIT; ++i) {
|
||||
printf("\033[s"); // save cursor position
|
||||
|
||||
printf("%d/%i", i + 1, LIMIT);
|
||||
|
||||
NSString *fname = [@"testexe" stringByAppendingFormat:@".%i", i];
|
||||
[[NSFileManager defaultManager] copyItemAtPath:@"/bin/hostname" toPath:fname error:NULL];
|
||||
|
||||
@try {
|
||||
NSTask *testexec = [self taskWithPath:fname];
|
||||
[testexec launch];
|
||||
[testexec waitUntilExit];
|
||||
} @catch (NSException *e) {
|
||||
TFAILINFO("Failed to launch");
|
||||
}
|
||||
|
||||
unlink([fname UTF8String]);
|
||||
printf("\033[u"); // restore cursor position
|
||||
}
|
||||
printf("\033[K\033[u"); // clear line, restore cursor position
|
||||
|
||||
TPASS();
|
||||
}
|
||||
|
||||
- (void)testCachePerformance {
|
||||
TSTART("Test cache performance");
|
||||
TSTART("Test cache performance...");
|
||||
|
||||
// Execute echo 100 times, saving the time taken for each run
|
||||
std::vector<double> times;
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
printf("\033[s"); // save cursor position
|
||||
printf("%d/%d", i + 1, 100);
|
||||
NSTask *t = [[NSTask alloc] init];
|
||||
t.launchPath = @"/bin/echo";
|
||||
t.standardOutput = [NSPipe pipe];
|
||||
@@ -542,11 +490,8 @@
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
|
||||
if (i > 5) times.push_back(duration);
|
||||
printf("\033[u"); // restore cursor position
|
||||
}
|
||||
|
||||
printf("\033[K\033[u"); // clear line, restore cursor position
|
||||
|
||||
// Sort and remove first 10 and last 10 entries.
|
||||
std::sort(times.begin(), times.end());
|
||||
times.erase(times.begin(), times.begin()+10);
|
||||
@@ -570,12 +515,31 @@
|
||||
}
|
||||
|
||||
- (void)testLargeBinary {
|
||||
TSTART("Handles large binary");
|
||||
TSTART("Handles large binary...");
|
||||
|
||||
__block int calCount = 0;
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
self.handlerBlock = ^santa_action_t(santa_message_t msg) {
|
||||
__strong __typeof(weakSelf) self = weakSelf;
|
||||
if (strncmp("/usr/bin/cal", msg.path, 12) == 0) {
|
||||
if (calCount++) TFAILINFO("Large binary should not re-request");
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC),
|
||||
dispatch_get_global_queue(0, 0), ^{
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:msg.vnode_id];
|
||||
});
|
||||
return ACTION_RESPOND_ACK;
|
||||
}
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
};
|
||||
|
||||
@try {
|
||||
NSTask *testexec = [self taskWithPath:@"/usr/bin/cal"];
|
||||
[testexec launch];
|
||||
[testexec waitUntilExit];
|
||||
int sleepCount = 0;
|
||||
while ([testexec isRunning]) {
|
||||
sleep(1);
|
||||
if (++sleepCount > 5) TFAILINFO("Took longer than expected to start/stop");
|
||||
}
|
||||
} @catch (NSException *e) {
|
||||
TFAILINFO("Failed to launch");
|
||||
}
|
||||
@@ -585,8 +549,57 @@
|
||||
|
||||
#pragma mark - Main
|
||||
|
||||
- (void)unloadDaemon {
|
||||
NSTask *t = [[NSTask alloc] init];
|
||||
t.launchPath = @"/bin/launchctl";
|
||||
t.arguments = @[ @"remove", @"com.google.santad" ];
|
||||
t.standardOutput = t.standardError = [NSPipe pipe];
|
||||
[t launch];
|
||||
[t waitUntilExit];
|
||||
}
|
||||
|
||||
- (void)unloadExtension {
|
||||
// Don't check the status of this, the kext may not be loaded..
|
||||
OSStatus ret = KextManagerUnloadKextWithIdentifier(CFSTR("com.google.santa-driver"));
|
||||
if (ret != kOSReturnSuccess && ret != kOSKextReturnNotFound) {
|
||||
NSLog(@"Failed to unload extension: 0x%X", ret);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadExtension {
|
||||
TSTART("Loads extension");
|
||||
|
||||
NSError *error;
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
NSString *src = [[fm currentDirectoryPath] stringByAppendingPathComponent:@"santa-driver.kext"];
|
||||
NSString *dest = [NSTemporaryDirectory() stringByAppendingPathComponent:@"santa-driver.kext"];
|
||||
[fm removeItemAtPath:dest error:NULL]; // ensure dest is free
|
||||
if (![fm copyItemAtPath:src toPath:dest error:&error] || error) {
|
||||
TFAILINFO("Failed to copy kext: %s", error.description.UTF8String);
|
||||
}
|
||||
|
||||
NSDictionary *attrs = @{
|
||||
NSFileOwnerAccountName : @"root",
|
||||
NSFileGroupOwnerAccountName : @"wheel",
|
||||
NSFilePosixPermissions : @0755
|
||||
};
|
||||
|
||||
[fm setAttributes:attrs ofItemAtPath:dest error:NULL];
|
||||
for (NSString *path in [fm enumeratorAtPath:dest]) {
|
||||
[fm setAttributes:attrs ofItemAtPath:[dest stringByAppendingPathComponent:path] error:NULL];
|
||||
}
|
||||
|
||||
NSURL *destURL = [NSURL fileURLWithPath:dest];
|
||||
OSStatus ret = KextManagerLoadKextWithURL((__bridge CFURLRef)destURL, NULL);
|
||||
if (ret != kOSReturnSuccess) {
|
||||
TFAILINFO("Failed to load kext: 0x%X", ret);
|
||||
}
|
||||
usleep(50000);
|
||||
TPASS();
|
||||
}
|
||||
|
||||
- (void)runTests {
|
||||
printf("\nSanta Kernel Tests\n==================\n");
|
||||
printf("-> Connection tests:\n");
|
||||
|
||||
// Test that connection can be established
|
||||
@@ -598,19 +611,18 @@
|
||||
|
||||
// Wait for driver to finish getting ready
|
||||
sleep(1);
|
||||
printf("\n-> Functional tests:\033[m\n");
|
||||
|
||||
printf("\n-> Functional tests:\n");
|
||||
[self receiveAndBlockTests];
|
||||
[self receiveAndCacheTests];
|
||||
[self invalidatesCacheTests];
|
||||
[self invalidatesCacheAutoCloseTest];
|
||||
[self clearCacheTests];
|
||||
[self blocksDeniedTracedBinaries];
|
||||
|
||||
printf("\n-> Performance tests:\033[m\n");
|
||||
[self testCachePerformance];
|
||||
[self testLargeBinary];
|
||||
[self handlesLotsOfBinaries];
|
||||
|
||||
printf("\n-> Performance tests:\n");
|
||||
[self testCachePerformance];
|
||||
|
||||
printf("\nAll tests passed.\n\n");
|
||||
}
|
||||
@@ -626,8 +638,18 @@ int main(int argc, const char *argv[]) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
chdir([[[NSBundle mainBundle] bundlePath] UTF8String]);
|
||||
|
||||
SantaKernelTests *skt = [[SantaKernelTests alloc] init];
|
||||
printf("\nSanta Kernel Tests\n==================\n\n");
|
||||
printf("-> Loading tests:\n");
|
||||
[skt unloadDaemon];
|
||||
[skt unloadExtension];
|
||||
[skt loadExtension];
|
||||
printf("\n");
|
||||
|
||||
[skt runTests];
|
||||
[skt unloadExtension];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -80,11 +80,15 @@
|
||||
santa_message_t message = {0};
|
||||
message.pid = 12;
|
||||
message.ppid = 1;
|
||||
message.vnode_id = 1234;
|
||||
message.vnode_id = [self getVnodeId];
|
||||
strncpy(message.path, "/a/file", 7);
|
||||
return message;
|
||||
}
|
||||
|
||||
- (santa_vnode_id_t)getVnodeId {
|
||||
return (santa_vnode_id_t){.fsid = 1234, .fileid = 5678};
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
[self.mockFileInfo stopMocking];
|
||||
[self.mockCodesignChecker stopMocking];
|
||||
@@ -107,7 +111,7 @@
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
|
||||
forVnodeID:1234]);
|
||||
forVnodeID:[self getVnodeId]]);
|
||||
}
|
||||
|
||||
- (void)testBinaryBlacklistRule {
|
||||
@@ -122,7 +126,7 @@
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
|
||||
forVnodeID:1234]);
|
||||
forVnodeID:[self getVnodeId]]);
|
||||
}
|
||||
|
||||
- (void)testCertificateWhitelistRule {
|
||||
@@ -140,7 +144,7 @@
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
|
||||
forVnodeID:1234]);
|
||||
forVnodeID:[self getVnodeId]]);
|
||||
}
|
||||
|
||||
- (void)testCertificateBlacklistRule {
|
||||
@@ -158,7 +162,7 @@
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
|
||||
forVnodeID:1234]);
|
||||
forVnodeID:[self getVnodeId]]);
|
||||
}
|
||||
|
||||
- (void)testDefaultDecision {
|
||||
@@ -168,12 +172,12 @@
|
||||
OCMExpect([self.mockConfigurator clientMode]).andReturn(SNTClientModeMonitor);
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
|
||||
forVnodeID:1234]);
|
||||
forVnodeID:[self getVnodeId]]);
|
||||
|
||||
OCMExpect([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
|
||||
forVnodeID:1234]);
|
||||
forVnodeID:[self getVnodeId]]);
|
||||
}
|
||||
|
||||
- (void)testOutOfScope {
|
||||
@@ -182,13 +186,13 @@
|
||||
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
|
||||
forVnodeID:1234]);
|
||||
forVnodeID:[self getVnodeId]]);
|
||||
}
|
||||
|
||||
- (void)testMissingShasum {
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
|
||||
forVnodeID:1234]);
|
||||
forVnodeID:[self getVnodeId]]);
|
||||
}
|
||||
|
||||
- (void)testPageZero {
|
||||
@@ -197,7 +201,7 @@
|
||||
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
|
||||
forVnodeID:1234]);
|
||||
forVnodeID:[self getVnodeId]]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "SantaCache.h"
|
||||
|
||||
@@ -28,71 +30,113 @@
|
||||
}
|
||||
|
||||
- (void)testSetAndGet {
|
||||
auto sut = new SantaCache<uint64_t>();
|
||||
auto sut = SantaCache<uint64_t, uint64_t>();
|
||||
|
||||
sut->set(72057611258548992llu, 10000192);
|
||||
XCTAssertEqual(sut->get(72057611258548992llu), 10000192);
|
||||
|
||||
delete sut;
|
||||
sut.set(72057611258548992llu, 10000192);
|
||||
XCTAssertEqual(sut.get(72057611258548992llu), 10000192);
|
||||
}
|
||||
|
||||
- (void)testCacheRemove {
|
||||
auto sut = new SantaCache<uint64_t>();
|
||||
auto sut = SantaCache<uint64_t, uint64_t>();
|
||||
|
||||
sut->set(0xDEADBEEF, 42);
|
||||
sut->remove(0xDEADBEEF);
|
||||
sut.set(0xDEADBEEF, 42);
|
||||
sut.remove(0xDEADBEEF);
|
||||
|
||||
XCTAssertEqual(sut->get(0xDEADBEEF), 0);
|
||||
|
||||
delete sut;
|
||||
XCTAssertEqual(sut.get(0xDEADBEEF), 0);
|
||||
}
|
||||
|
||||
- (void)testBucketGrowCopy {
|
||||
auto sut = new SantaCache<uint64_t>();
|
||||
auto sut = SantaCache<uint64_t, uint64_t>();
|
||||
|
||||
sut->set(386, 42);
|
||||
sut->set(2434, 42);
|
||||
sut.set(386, 42);
|
||||
sut.set(2434, 42);
|
||||
|
||||
XCTAssertEqual(sut->get(386), 42);
|
||||
XCTAssertEqual(sut->get(2434), 42);
|
||||
|
||||
delete sut;
|
||||
XCTAssertEqual(sut.get(386), 42);
|
||||
XCTAssertEqual(sut.get(2434), 42);
|
||||
}
|
||||
|
||||
- (void)testBucketShrinkCopy {
|
||||
auto sut = new SantaCache<uint64_t>(100, 1);
|
||||
auto sut = SantaCache<uint64_t, uint64_t>(100, 1);
|
||||
|
||||
sut->set(386, 42);
|
||||
sut->set(2434, 42);
|
||||
sut->set(4482, 42);
|
||||
sut.set(386, 42);
|
||||
sut.set(2434, 42);
|
||||
sut.set(4482, 42);
|
||||
|
||||
sut->remove(2434);
|
||||
sut.remove(2434);
|
||||
|
||||
XCTAssertEqual(sut->get(386), 42);
|
||||
XCTAssertEqual(sut->get(2434), 0);
|
||||
XCTAssertEqual(sut->get(4482), 42);
|
||||
|
||||
delete sut;
|
||||
XCTAssertEqual(sut.get(386), 42);
|
||||
XCTAssertEqual(sut.get(2434), 0);
|
||||
XCTAssertEqual(sut.get(4482), 42);
|
||||
}
|
||||
|
||||
- (void)testCacheResetAtLimit {
|
||||
auto sut = new SantaCache<uint64_t>(5);
|
||||
auto sut = SantaCache<uint64_t, uint64_t>(5);
|
||||
|
||||
sut->set(1, 42);
|
||||
sut->set(2, 42);
|
||||
sut->set(3, 42);
|
||||
sut->set(4, 42);
|
||||
sut->set(5, 42);
|
||||
XCTAssertEqual(sut->get(3), 42);
|
||||
sut->set(6, 42);
|
||||
XCTAssertEqual(sut->get(3), 0);
|
||||
XCTAssertEqual(sut->get(6), 42);
|
||||
sut.set(1, 42);
|
||||
sut.set(2, 42);
|
||||
sut.set(3, 42);
|
||||
sut.set(4, 42);
|
||||
sut.set(5, 42);
|
||||
XCTAssertEqual(sut.get(3), 42);
|
||||
sut.set(6, 42);
|
||||
XCTAssertEqual(sut.get(3), 0);
|
||||
XCTAssertEqual(sut.get(6), 42);
|
||||
}
|
||||
|
||||
delete sut;
|
||||
// Helper to test bucket distributions for uint64_t/uint64_t combinations.
|
||||
- (void)distributionTestHelper:(SantaCache<uint64_t, uint64_t> *)sut bucketRatio:(int)br {
|
||||
uint16_t count[512];
|
||||
uint16_t array_size = 512;
|
||||
uint64_t start_bucket = 0;
|
||||
std::vector<uint16_t> per_bucket;
|
||||
do {
|
||||
sut->bucket_counts(count, &array_size, &start_bucket);
|
||||
for (int i = 0; i < array_size; ++i) {
|
||||
per_bucket.push_back(count[i]);
|
||||
}
|
||||
} while (start_bucket > 0);
|
||||
|
||||
// Calculate mean
|
||||
double mean = std::accumulate(per_bucket.begin(), per_bucket.end(), 0.0) / per_bucket.size();
|
||||
XCTAssertLessThanOrEqual(mean, br, @"Mean per-bucket count is greater than %d", br);
|
||||
|
||||
// Calculate stdev
|
||||
double accum = 0.0;
|
||||
std::for_each(per_bucket.begin(), per_bucket.end(), [&](const double d) {
|
||||
accum += (d - mean) * (d - mean);
|
||||
});
|
||||
double stddev = sqrt(accum / (per_bucket.size() - 1));
|
||||
double maxStdDev = (double)br / 2;
|
||||
XCTAssertLessThanOrEqual(stddev, maxStdDev,
|
||||
@"Standard deviation between buckets is greater than %f", maxStdDev);
|
||||
}
|
||||
|
||||
- (void)testDistributionRandomKeys {
|
||||
const int bucket_ratio = 5;
|
||||
auto sut = new SantaCache<uint64_t, uint64_t>(5000, bucket_ratio);
|
||||
|
||||
// Fill the cache with random keys, all set to 1.
|
||||
for (int i = 0; i < 4000; ++i) {
|
||||
sut->set((uint64_t)arc4random() << 32 | arc4random(), 1);
|
||||
}
|
||||
|
||||
[self distributionTestHelper:sut bucketRatio:bucket_ratio];
|
||||
}
|
||||
|
||||
- (void)testDistributionMontonicKeys {
|
||||
const int bucket_ratio = 5;
|
||||
auto sut = new SantaCache<uint64_t, uint64_t>(5000, bucket_ratio);
|
||||
|
||||
// Fill the cache with monotonic keys, all set to 1.
|
||||
for (int i = 0; i < 4000; ++i) {
|
||||
sut->set(i, 1);
|
||||
}
|
||||
|
||||
[self distributionTestHelper:sut bucketRatio:bucket_ratio];
|
||||
}
|
||||
|
||||
- (void)testThreading {
|
||||
auto sut = new SantaCache<uint64_t>();
|
||||
auto sut = new SantaCache<uint64_t, uint64_t>();
|
||||
|
||||
for (int x = 0; x < 200; ++x) {
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
@@ -120,57 +164,105 @@
|
||||
}
|
||||
|
||||
- (void)testCount {
|
||||
auto sut = new SantaCache<uint64_t>();
|
||||
auto sut = SantaCache<uint64_t, int>();
|
||||
|
||||
XCTAssertEqual(sut->count(), 0);
|
||||
XCTAssertEqual(sut.count(), 0);
|
||||
|
||||
sut->set(4012, 42);
|
||||
sut->set(42, 0);
|
||||
sut->set(0x8BADF00D, 40010);
|
||||
sut.set(4012, 42);
|
||||
sut.set(42, 0);
|
||||
sut.set(0x8BADF00D, 40010);
|
||||
|
||||
XCTAssertEqual(sut->count(), 2);
|
||||
XCTAssertEqual(sut.count(), 2);
|
||||
}
|
||||
|
||||
delete sut;
|
||||
- (void)testDoubles {
|
||||
auto sut = SantaCache<double, double>();
|
||||
|
||||
sut.set(3.14, 2.718281);
|
||||
sut.set(1.41429, 2.5029);
|
||||
sut.set(4.6692, 1.2020569);
|
||||
sut.set(1.61803398, 0.57721);
|
||||
|
||||
XCTAssertEqual(sut.count(), 4);
|
||||
XCTAssertEqual(sut.get(3.14), 2.718281);
|
||||
XCTAssertEqual(sut.get(1.41429), 2.5029);
|
||||
XCTAssertEqual(sut.get(4.6692), 1.2020569);
|
||||
XCTAssertEqual(sut.get(1.61803398), 0.57721);
|
||||
|
||||
XCTAssertEqual(sut.get(5.5555), 0);
|
||||
XCTAssertEqual(sut.get(3.1459124), 0);
|
||||
}
|
||||
|
||||
template<> uint64_t SantaCacheHasher<std::string>(std::string const& s) {
|
||||
return std::hash<std::string>{}(s);
|
||||
}
|
||||
|
||||
- (void)testStrings {
|
||||
auto sut = new SantaCache<std::string>();
|
||||
auto sut = SantaCache<std::string, std::string>();
|
||||
|
||||
sut->set(1, "deadbeef");
|
||||
sut->set(2, "feedface");
|
||||
std::string s1 = "foo";
|
||||
std::string s2 = "bar";
|
||||
|
||||
XCTAssertEqual(sut->count(), 2);
|
||||
XCTAssertEqual(sut->get(1), "deadbeef");
|
||||
XCTAssertEqual(sut->get(2), "feedface");
|
||||
sut.set(s1, "deadbeef");
|
||||
sut.set(s2, "feedface");
|
||||
|
||||
sut->remove(2);
|
||||
XCTAssertEqual(sut.count(), 2);
|
||||
XCTAssertEqual(sut.get(s1), "deadbeef");
|
||||
XCTAssertEqual(sut.get(s2), "feedface");
|
||||
|
||||
XCTAssertTrue(sut->get(2).empty());
|
||||
sut.remove(s2);
|
||||
|
||||
delete sut;
|
||||
XCTAssertTrue(sut.get(s2).empty());
|
||||
}
|
||||
|
||||
- (void)testCompareAndSwap {
|
||||
auto sut = new SantaCache<uint64_t>(100, 2);
|
||||
auto sut = SantaCache<uint64_t, uint64_t>(100, 2);
|
||||
|
||||
sut->set(1, 42);
|
||||
sut->set(1, 666, 1);
|
||||
sut->set(1, 666, 0);
|
||||
XCTAssertEqual(sut->get(1), 42);
|
||||
sut.set(1, 42);
|
||||
sut.set(1, 666, 1);
|
||||
sut.set(1, 666, 0);
|
||||
XCTAssertEqual(sut.get(1), 42);
|
||||
|
||||
sut->set(1, 0);
|
||||
XCTAssertEqual(sut->get(1), 0);
|
||||
sut.set(1, 0);
|
||||
XCTAssertEqual(sut.get(1), 0);
|
||||
|
||||
sut->set(1, 42, 1);
|
||||
XCTAssertEqual(sut->get(1), 0);
|
||||
sut.set(1, 42, 1);
|
||||
XCTAssertEqual(sut.get(1), 0);
|
||||
|
||||
sut->set(1, 42, 0);
|
||||
XCTAssertEqual(sut->get(1), 42);
|
||||
sut.set(1, 42, 0);
|
||||
XCTAssertEqual(sut.get(1), 42);
|
||||
|
||||
sut->set(1, 0, 666);
|
||||
XCTAssertEqual(sut->get(1), 42);
|
||||
sut->set(1, 0, 42);
|
||||
XCTAssertEqual(sut->get(1), 0);
|
||||
sut.set(1, 0, 666);
|
||||
XCTAssertEqual(sut.get(1), 42);
|
||||
sut.set(1, 0, 42);
|
||||
XCTAssertEqual(sut.get(1), 0);
|
||||
}
|
||||
|
||||
struct S {
|
||||
uint64_t first_val;
|
||||
uint64_t second_val;
|
||||
|
||||
bool operator==(const S& rhs) {
|
||||
return first_val == rhs.first_val && second_val == rhs.second_val;
|
||||
}
|
||||
};
|
||||
template<> uint64_t SantaCacheHasher<S>(S const& s) {
|
||||
return SantaCacheHasher<uint64_t>(s.first_val) ^ (SantaCacheHasher<uint64_t>(s.second_val) << 1);
|
||||
}
|
||||
|
||||
- (void)testStructKeys {
|
||||
auto sut = SantaCache<S, uint64_t>(10, 2);
|
||||
|
||||
S s1 = {1024, 2048};
|
||||
S s2 = {4096, 8192};
|
||||
S s3 = {16384, 32768};
|
||||
sut.set(s1, 10);
|
||||
sut.set(s2, 20);
|
||||
sut.set(s3, 30);
|
||||
|
||||
XCTAssertEqual(sut.get(s1), 10);
|
||||
XCTAssertEqual(sut.get(s2), 20);
|
||||
XCTAssertEqual(sut.get(s3), 30);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
55
Tests/santa-driver/CMakeLists.txt
Normal file
55
Tests/santa-driver/CMakeLists.txt
Normal file
@@ -0,0 +1,55 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(santa-driver-test)
|
||||
|
||||
function(main)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_GNU_EXTENSIONS OFF)
|
||||
|
||||
# Include all files to make IDEs happy
|
||||
set(PROJECT_SOURCEFILES
|
||||
main.mm
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad/SNTDriverManager.h"
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad/SNTDriverManager.m"
|
||||
|
||||
"${CMAKE_SOURCE_DIR}/Source/common/SNTLogging.m"
|
||||
)
|
||||
|
||||
add_executable("${PROJECT_NAME}" EXCLUDE_FROM_ALL ${PROJECT_SOURCEFILES})
|
||||
AddTestTarget("${PROJECT_NAME}" true)
|
||||
|
||||
target_link_libraries("${PROJECT_NAME}" PRIVATE
|
||||
-ObjC "-framework Cocoa"
|
||||
)
|
||||
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE
|
||||
-fobjc-arc -Wno-everything -fmodules -fcxx-modules
|
||||
-mmacosx-version-min=${MACOSX_VERSION_MIN}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
target_compile_options("${PROJECT_NAME}" PRIVATE -g3)
|
||||
endif()
|
||||
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE
|
||||
COCOAPODS=1
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE DEBUG=1)
|
||||
else()
|
||||
target_compile_definitions("${PROJECT_NAME}" PRIVATE NDEBUG=1)
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/Source/santad"
|
||||
"${CMAKE_SOURCE_DIR}/Source/common"
|
||||
)
|
||||
|
||||
add_custom_command(TARGET "${PROJECT_NAME}" POST_BUILD
|
||||
COMMAND codesign --force --verify --verbose --sign "${CODESIGN_IDENTITY}" $<TARGET_FILE:${PROJECT_NAME}>
|
||||
COMMENT "Signing ${PROJECT_NAME} with the following identity: ${CODESIGN_IDENTITY}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
main()
|
||||
70
Tests/santa-driver/main.mm
Normal file
70
Tests/santa-driver/main.mm
Normal file
@@ -0,0 +1,70 @@
|
||||
/// Copyright 2018 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
/*
|
||||
This test will attempt to connect to the driver multiple times to make
|
||||
sure that it doesn't cause crashes
|
||||
*/
|
||||
|
||||
#import "SNTDriverManager.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable condVariable;
|
||||
std::atomic_bool terminate(false);
|
||||
|
||||
void SignalHandler(int signalId) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
terminate = true;
|
||||
|
||||
condVariable.notify_one();
|
||||
}
|
||||
|
||||
void Thread() {
|
||||
while (!terminate) {
|
||||
SNTDriverManager *driverManager = [[SNTDriverManager alloc] init];
|
||||
if (driverManager != nullptr) {
|
||||
std::cout << ".";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
signal(SIGINT, &SignalHandler);
|
||||
|
||||
std::vector<std::thread> thread_list;
|
||||
for (auto i = 0U; i < 1U; i++) {
|
||||
thread_list.emplace_back(Thread);
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
condVariable.wait(
|
||||
lock,
|
||||
[]{
|
||||
return terminate.load();
|
||||
}
|
||||
);
|
||||
|
||||
for (auto &t : thread_list) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user