# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4 package require tcltest 2 namespace import tcltest::* package require Pextlib 1.0 source "testlib.tcl" testConstraint notarm64 [expr {[exec -ignorestderr -- /usr/bin/arch] ne "arm64"}] test execve_selfpreserving_env "Test that you cannot get out of the sandbox by unsetting environment variables" \ -setup [setup [list allow $cwd]] \ -cleanup [expect] \ -body { set lines [split [exec -ignorestderr -- ./env DYLD_INSERT_LIBRARIES=foo DARWINTRACE_LOG=bar ./env] "\n"] set result {} foreach line $lines { if {[string match "DYLD_INSERT_LIBRARIES=*" $line] || [string match "DARWINTRACE_LOG=*" $line]} { lappend result $line } } return [lsort $result] } \ -match glob \ -result [list "DARWINTRACE_LOG=/tmp/macports-test-*" "DYLD_INSERT_LIBRARIES=$darwintrace_lib"] test execve_preserves_environment "Test that execve(2) will restore DYLD_INSERT_LIBRARIES and DARWINTRACE_LOG when deleted" \ -setup [setup [list allow $cwd]] \ -cleanup [expect] \ -body { set lines [split [exec -ignorestderr -- ./env -u DYLD_INSERT_LIBRARIES -u DARWINTRACE_LOG ./env] "\n"] set result {} foreach line $lines { if {[string match "DYLD_INSERT_LIBRARIES=*" $line] || [string match "DARWINTRACE_LOG=*" $line]} { lappend result $line } } return [lsort $result] } \ -match glob \ -result [list "DARWINTRACE_LOG=/tmp/macports-test-*" "DYLD_INSERT_LIBRARIES=$darwintrace_lib"] test execve_outside_sandbox "Test that you cannot run tools outside of the sandbox" \ -setup [setup {}] \ -cleanup [expect "$cwd/stat"] \ -body {exec -ignorestderr -- ./execve ./stat ./stat.c 2>@1} \ -result "execve: No such file or directory" test spawn_outside_sandbox "Test that you cannot run tools outside of the sandbox with posix_spawn(2)" \ -setup [setup {}] \ -cleanup [expect "$cwd/stat"] \ -body { set lines [list] lappend lines [exec -ignorestderr -- ./posix_spawn ./stat ./stat.c 2>@1] set ::env(DARWINTRACE_SPAWN_SETEXEC) 1 lappend lines [exec -ignorestderr -- ./posix_spawn ./stat ./stat.c 2>@1] unset ::env(DARWINTRACE_SPAWN_SETEXEC) return $lines } \ -result [lrepeat 2 "posix_spawn: No such file or directory"] test execve_uninitialized "Test that execve(2) outside the sandbox succeeds if darwintrace is uninitialized" \ -setup [setup {}] \ -cleanup [expect {}] \ -body { set ::env(DARWINTRACE_UNINITIALIZE) 1 set output [exec -ignorestderr -- ./execve ./stat ./stat.c 2>@1] unset ::env(DARWINTRACE_UNINITIALIZE) return $output } \ -result "" test spawn_uninitialized "Test that posix_spawn(2) outside the sandbox succeeds if darwintrace is uninitialized" \ -setup [setup {}] \ -cleanup [expect {}] \ -body { set ::env(DARWINTRACE_UNINITIALIZE) 1 set lines [list] lappend lines [exec -ignorestderr -- ./posix_spawn ./stat ./stat.c 2>@1] set ::env(DARWINTRACE_SPAWN_SETEXEC) 1 lappend lines [exec -ignorestderr -- ./posix_spawn ./stat ./stat.c 2>@1] unset ::env(DARWINTRACE_SPAWN_SETEXEC) unset ::env(DARWINTRACE_UNINITIALIZE) return $lines } \ -result [lrepeat 2 ""] test execve_inside_sandbox "Test that execve(2) inside the sandbox succeeds" \ -setup [setup [list deny "$cwd/stat.c" allow $cwd]] \ -cleanup [expect "$cwd/stat.c"] \ -body {exec -ignorestderr -- ./execve ./stat ./stat.c 2>@1} \ -result "stat: No such file or directory" test spawn_inside_sandbox "Test that posix_spawn(2) inside the sandbox succeeds" \ -setup [setup [list deny "$cwd/stat.c" allow $cwd]] \ -cleanup [expect "$cwd/stat.c"] \ -body { set lines [list] lappend lines [exec -ignorestderr -- ./posix_spawn ./stat ./stat.c 2>@1] set ::env(DARWINTRACE_SPAWN_SETEXEC) 1 lappend lines [exec -ignorestderr -- ./posix_spawn ./stat ./stat.c 2>@1] unset ::env(DARWINTRACE_SPAWN_SETEXEC) return $lines } \ -result [lrepeat 2 "stat: No such file or directory"] test execve_interpreter_outside_sandbox "Test that execve(2) on a script with an interpreter outside of the sandbox fails" \ -setup { set fd [open "execve_script" w 0700] puts $fd "#! \t ${cwd}/stat stat.c" close $fd [setup [list deny "$cwd/stat" allow $cwd]] } \ -cleanup { file delete -force execve_script [expect [list "$cwd/stat"]] } \ -body {exec -ignorestderr -- ./execve ./execve_script 2>@1} \ -result "execve: No such file or directory" test spawn_interpreter_outside_sandbox "Test that posix_spawn(2) on a script with an interpreter outside of the sandbox fails" \ -setup { set fd [open "execve_script" w 0700] puts $fd "#! \t ${cwd}/stat stat.c" close $fd [setup [list deny "$cwd/stat" allow $cwd]] } \ -cleanup { file delete -force execve_script [expect [list "$cwd/stat"]] } \ -body { set lines [list] lappend lines [exec -ignorestderr -- ./posix_spawn ./execve_script 2>@1] set ::env(DARWINTRACE_SPAWN_SETEXEC) 1 lappend lines [exec -ignorestderr -- ./posix_spawn ./execve_script 2>@1] unset ::env(DARWINTRACE_SPAWN_SETEXEC) return $lines } \ -result [lrepeat 2 "posix_spawn: No such file or directory"] test execve_non_interpreter "Test that execve(2) on a script with broken shebang does not produce violations" \ -setup { set fd [open "execve_script" w 0700] puts $fd "#" close $fd [setup [list allow /]] } \ -cleanup { file delete -force execve_script [expect {}] } \ -body {exec -ignorestderr -- ./execve ./execve_script 2>@1} \ -result "execve: Exec format error" test spawn_non_interpreter "Test that posix_spawn(2) on a script with broken shebang does not produce violations" \ -setup { set fd [open "execve_script" w 0700] puts $fd "#" close $fd [setup [list allow /]] } \ -cleanup { file delete -force execve_script [expect {}] } \ -body { set lines [list] lappend lines [exec -ignorestderr -- ./posix_spawn ./execve_script 2>@1] set ::env(DARWINTRACE_SPAWN_SETEXEC) 1 lappend lines [exec -ignorestderr -- ./posix_spawn ./execve_script 2>@1] unset ::env(DARWINTRACE_SPAWN_SETEXEC) return $lines } \ -result [lrepeat 2 "posix_spawn: Exec format error"] test execve_non_existing_inside_sandbox "Test that execve(2) on a non-existing file inside the sandbox does not produce violations" \ -setup [setup [list allow /]] \ -cleanup [expect {}] \ -body {exec -ignorestderr -- ./execve ./execve_non_existing_binary 2>@1} \ -result "execve: No such file or directory" test spawn_non_existing_inside_sandbox "Test that posix_spawn(2) on a non-existing file inside the sandbox does not produce violations" \ -setup [setup [list allow /]] \ -cleanup [expect {}] \ -body { set lines [list] lappend lines [exec -ignorestderr -- ./posix_spawn ./execve_non_existing_binary 2>@1] set ::env(DARWINTRACE_SPAWN_SETEXEC) 1 lappend lines [exec -ignorestderr -- ./posix_spawn ./execve_non_existing_binary 2>@1] unset ::env(DARWINTRACE_SPAWN_SETEXEC) return $lines } \ -result [lrepeat 2 "posix_spawn: No such file or directory"] # This test is currently broken on arm64, because Apple compiles its SIP-protected binaries with # pointer authenticaation using the arm64e architecture, but marks it as a preview and only allows # Apple-signed binaries, or arbitrary binaries on systems that are booted with # boot-args=-arm64e_preview_abi (which cannot be enabled without disabling SIP). test spawn_sip_binary "Test that posix_spawn(2) works on a SIP-protected binary (which will make a copy)" \ -constraints {notarm64} \ -setup { file delete -force [file join $::env(DARWINTRACE_SIP_WORKAROUND_PATH) [getuid] usr/bin/env] [setup [list allow /]] } \ -cleanup [expect {}] \ -body { set lines [split [exec -ignorestderr -- ./posix_spawn /usr/bin/env] "\n"] set result {} foreach line $lines { if {[string match "DYLD_INSERT_LIBRARIES=*" $line] || [string match "DARWINTRACE_LOG=*" $line]} { lappend result $line } } return [concat [lsort $result] [file exists [file join $::env(DARWINTRACE_SIP_WORKAROUND_PATH) [getuid] usr/bin/env]]] } \ -match glob \ -result [list "DARWINTRACE_LOG=/tmp/macports-test-*" "DYLD_INSERT_LIBRARIES=$darwintrace_lib" 1] # This test is currently broken on arm64, because Apple compiles its SIP-protected binaries with # pointer authenticaation using the arm64e architecture, but marks it as a preview and only allows # Apple-signed binaries, or arbitrary binaries on systems that are booted with # boot-args=-arm64e_preview_abi (which cannot be enabled without disabling SIP). test spawn_sip_script "Test that posix_spawn(2) works on a SIP-protected shell script (which will copy the interpreter)" \ -constraints {notarm64} \ -setup [setup [list allow /]] \ -cleanup [expect {}] \ -body {exec -ignorestderr -- ./posix_spawn /usr/bin/umask} \ -match regexp \ -result "0\[0-7]{3}" test spawn_sip_suid_binary "Test that posix_spawn(2) works on a SIP-protected SUID binary (which will not be copied)" \ -setup [setup [list allow /]] \ -cleanup [expect {}] \ -body { set status 0 if {[catch {exec -ignorestderr -- ./posix_spawn /usr/bin/crontab -l 2>@1} results options]} { if {[string match "crontab: no crontab for *" $results]} { # The current user has no crontab; that will cause crontab -l # to fail, but we don't care; hide the error. set status 0 } else { puts $::errorInfo set details [dict get $options -errorcode] if {[lindex $details 0] eq "CHILDSTATUS"} { set status [lindex $details 2] } else { return -options $options -level 0 $results } } } return $status } \ -result 0 cleanupTests