Repo // Sandbox entitlements and hardened runtime.

This commit is contained in:
ShikiSuen 2022-08-31 16:16:38 +08:00
parent f300305f85
commit bc0c26891a
6 changed files with 130 additions and 28 deletions

View File

@ -13,12 +13,14 @@ import Cocoa
private let kTargetBin = "vChewing"
private let kTargetType = "app"
private let kTargetBundle = "vChewing.app"
private let kTargetBundleWithComponents = "Library/Input%20Methods/vChewing.app"
private let urlDestinationPartial = FileManager.default.urls(
for: .inputMethodsDirectory, in: .userDomainMask
)[0]
private let urlTargetPartial = urlDestinationPartial.appendingPathComponent(kTargetBundle)
private let urlTargetFullBinPartial = urlTargetPartial.appendingPathComponent("Contents/MacOS/")
private let realHomeDir = URL(
fileURLWithFileSystemRepresentation: getpwuid(getuid()).pointee.pw_dir, isDirectory: true, relativeTo: nil
)
private let urlDestinationPartial = realHomeDir.appendingPathComponent("Library/Input Methods")
private let urlTargetPartial = realHomeDir.appendingPathComponent(kTargetBundleWithComponents)
private let urlTargetFullBinPartial = urlTargetPartial.appendingPathComponent("Contents/MacOS")
.appendingPathComponent(kTargetBin)
private let kDestinationPartial = urlDestinationPartial.path
@ -93,9 +95,9 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
window?.standardWindowButton(.zoomButton)?.isHidden = true
if FileManager.default.fileExists(
atPath: (kTargetPartialPath as NSString).expandingTildeInPath)
atPath: kTargetPartialPath)
{
let currentBundle = Bundle(path: (kTargetPartialPath as NSString).expandingTildeInPath)
let currentBundle = Bundle(path: kTargetPartialPath)
let shortVersion =
currentBundle?.infoDictionary?["CFBundleShortVersionString"] as? String
let currentVersion =
@ -136,15 +138,12 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
}
func removeThenInstallInputMethod() {
if FileManager.default.fileExists(
atPath: (kTargetPartialPath as NSString).expandingTildeInPath)
== false
{
installInputMethod(
previousExists: false, previousVersionNotFullyDeactivatedWarning: false
)
return
}
// if !FileManager.default.fileExists(atPath: kTargetPartialPath) {
// installInputMethod(
// previousExists: false, previousVersionNotFullyDeactivatedWarning: false
// )
// return
// }
let shouldWaitForTranslocationRemoval =
isAppBundleTranslocated(atPath: kTargetPartialPath)
@ -152,7 +151,7 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
//
do {
let sourceDir = (kDestinationPartial as NSString).expandingTildeInPath
let sourceDir = kDestinationPartial
let fileManager = FileManager.default
let fileURLString = String(format: "%@/%@", sourceDir, kTargetBundle)
let fileURL = URL(fileURLWithPath: fileURLString)
@ -216,8 +215,9 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
}
let cpTask = Process()
cpTask.launchPath = "/bin/cp"
print(kDestinationPartial)
cpTask.arguments = [
"-R", targetBundle, (kDestinationPartial as NSString).expandingTildeInPath,
"-R", targetBundle, kDestinationPartial,
]
cpTask.launch()
cpTask.waitUntilExit()
@ -231,7 +231,11 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
endAppWithDelay()
}
guard let imeBundle = Bundle(path: (kTargetPartialPath as NSString).expandingTildeInPath),
let imeURLInstalled = realHomeDir.appendingPathComponent("Library/Input Methods/vChewing.app")
_ = try? shell("/usr/bin/xattr -drs com.apple.quarantine \(kTargetPartialPath)")
guard let imeBundle = Bundle(url: imeURLInstalled),
let imeIdentifier = imeBundle.bundleIdentifier
else {
endAppWithDelay()
@ -343,6 +347,30 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
NSApp.terminate(self)
}
private func shell(_ command: String) throws -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
if #available(macOS 10.13, *) {
task.executableURL = URL(fileURLWithPath: "/bin/zsh")
} else {
task.launchPath = "/bin/zsh"
}
task.standardInput = nil
if #available(macOS 10.13, *) {
try task.run()
}
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
// Determines if an app is translocated by Gatekeeper to a randomized path.
// See https://weblog.rogueamoeba.com/2016/06/29/sierra-and-gatekeeper-path-randomization/
// Originally written by Zonble Yang in Objective-C (MIT License).
@ -350,7 +378,7 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
func isAppBundleTranslocated(atPath bundlePath: String) -> Bool {
var entryCount = getfsstat(nil, 0, 0)
var entries: [statfs] = .init(repeating: .init(), count: Int(entryCount))
let absPath = (bundlePath as NSString).expandingTildeInPath.cString(using: .utf8)
let absPath = bundlePath.cString(using: .utf8)
entryCount = getfsstat(&entries, entryCount * Int32(MemoryLayout<statfs>.stride), MNT_NOWAIT)
for entry in entries.prefix(Int(entryCount)) {
let isMatch = withUnsafeBytes(of: entry.f_mntfromname) { mntFromName in

View File

@ -22,6 +22,9 @@ public enum InputMode: String {
public enum IME {
static let arrSupportedLocales = ["en", "zh-Hant", "zh-Hans", "ja"]
static let dlgOpenPath = NSOpenPanel()
public static let realHomeDir = URL(
fileURLWithFileSystemRepresentation: getpwuid(getuid()).pointee.pw_dir, isDirectory: true, relativeTo: nil
)
// MARK: - Bundle Identifier
@ -173,16 +176,23 @@ public enum IME {
return -1
}
let kTargetBin = "vChewing"
// v2.3.0 sudo
if isSudo {
print(
"vChewing binary does not support sudo-uninstall since v2.3.0. Please use the bundled uninstall.sh instead.\n\nIf you want to fix the installation (i.e. removing all incorrectly installed files outside of the current user folder), please use the bundled fixinstall.sh instead.\n\nIf you don't know how to proceed, please bring either the uninstall.sh / install.sh or the instruction article https://vchewing.github.io/UNINSTALL.html to Apple Support (support.apple.com) for help. Their senior advisors can understand these uninstall instructions."
)
return -1
}
let kTargetBundle = "/vChewing.app"
let pathLibrary =
isSudo
? "/Library"
: FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0].path
: IME.realHomeDir.appendingPathComponent("Library/").path
let pathIMELibrary =
isSudo
? "/Library/Input Methods"
: FileManager.default.urls(for: .inputMethodsDirectory, in: .userDomainMask)[0].path
: IME.realHomeDir.appendingPathComponent("Library/Input Methods/").path
let pathUnitKeyboardLayouts = "/Keyboard Layouts"
let arrKeyLayoutFiles = [
"/vChewing ETen.keylayout", "/vChewingKeyLayout.bundle", "/vChewing MiTAC.keylayout",
@ -203,15 +213,13 @@ public enum IME {
// symbol link
IME.trashTargetIfExists(mgrLangModel.dataFolderPath(isDefaultFolder: true))
IME.trashTargetIfExists(pathLibrary + "/Preferences/" + bundleID + ".plist") // App
IME.trashTargetIfExists(pathLibrary + "/Receipts/org.atelierInmu.vChewing.bom") // pkg
IME.trashTargetIfExists(pathLibrary + "/Receipts/org.atelierInmu.vChewing.plist") // pkg
}
if !IME.trashTargetIfExists(pathIMELibrary + kTargetBundle) { return -1 } // App
//
if selfKill {
let killTask = Process()
killTask.launchPath = "/usr/bin/killall"
killTask.arguments = ["-9", kTargetBin]
killTask.launch()
killTask.waitUntilExit()
NSApplication.shared.terminate(nil)
}
return 0
}

22
vChewing.entitlements Normal file
View File

@ -0,0 +1,22 @@
<?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>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key>
<array>
<string>/</string>
</array>
<key>com.apple.security.temporary-exception.mach-register.global-name</key>
<string>org.atelierInmu.inputmethod.vChewing_Connection</string>
<key>com.apple.security.temporary-exception.shared-preference.read-only</key>
<string>org.atelierInmu.inputmethod.vChewing</string>
</dict>
</plist>

View File

@ -219,6 +219,9 @@
5B18BA7327C7BD8C0056EB19 /* LICENSE-JPN.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-JPN.txt"; sourceTree = "<group>"; };
5B18BA7427C7BD8C0056EB19 /* LICENSE-CHT.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHT.txt"; sourceTree = "<group>"; };
5B20430628BEE30900BFC6FD /* BookmarkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkManager.swift; sourceTree = "<group>"; };
5B20430B28BEFC0C00BFC6FD /* vChewing.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = vChewing.entitlements; sourceTree = "<group>"; };
5B20430C28BEFC1200BFC6FD /* vChewingPhraseEditor.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = vChewingPhraseEditor.entitlements; sourceTree = "<group>"; };
5B20430D28BF279900BFC6FD /* vChewingInstaller.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = vChewingInstaller.entitlements; sourceTree = "<group>"; };
5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleStates.swift; sourceTree = "<group>"; };
5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleDisplay.swift; sourceTree = "<group>"; };
5B21176F28753B9D000443A9 /* ctlInputMethod_Delegates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_Delegates.swift; sourceTree = "<group>"; };
@ -789,6 +792,9 @@
6A0D4E9215FC0CFA00ABF4B3 = {
isa = PBXGroup;
children = (
5B20430D28BF279900BFC6FD /* vChewingInstaller.entitlements */,
5B20430C28BEFC1200BFC6FD /* vChewingPhraseEditor.entitlements */,
5B20430B28BEFC0C00BFC6FD /* vChewing.entitlements */,
5BBD627827B6C4D900271480 /* Update-Info.plist */,
5B18BA7027C7BD8B0056EB19 /* Makefile */,
5B0C5EDE27C7D94C0078037C /* DataCompiler */,
@ -1534,11 +1540,13 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = vChewingPhraseEditor.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2201;
DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@ -1584,11 +1592,13 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = vChewingPhraseEditor.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2201;
DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@ -1716,6 +1726,7 @@
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = vChewing.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
@ -1723,6 +1734,7 @@
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
@ -1773,6 +1785,7 @@
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = vChewing.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
@ -1780,6 +1793,7 @@
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
@ -1818,12 +1832,14 @@
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = vChewingInstaller.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2201;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
@ -1860,12 +1876,14 @@
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = vChewingInstaller.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2201;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;

View File

@ -0,0 +1,14 @@
<?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>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key>
<array>
<string>/Library/Input Methods/</string>
</array>
<key>com.apple.security.temporary-exception.shared-preference.read-write</key>
<string>org.atelierInmu.vChewing.vChewingInstaller</string>
</dict>
</plist>

View File

@ -0,0 +1,12 @@
<?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>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>