feature: local auth & backup way to install proxy helper
This commit is contained in:
parent
60691df9c3
commit
aeef5261dd
@ -44,6 +44,7 @@
|
||||
49ABB749236B0F9E00535CD7 /* UnsafePointer+bridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49ABB748236B0F9E00535CD7 /* UnsafePointer+bridge.swift */; };
|
||||
49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B10869216A356D0064FFCE /* String+Extension.swift */; };
|
||||
49B4575D244F4A2A00463C39 /* PrivilegedHelperManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */; };
|
||||
49B4575F244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */; };
|
||||
49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BC061B212931F4005A0FE7 /* AboutViewController.swift */; };
|
||||
49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C9EF63223E78F5005D8B6A /* ClashProxy.swift */; };
|
||||
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B2020CD7463001EBF94 /* AppDelegate.swift */; };
|
||||
@ -153,6 +154,7 @@
|
||||
49ABB748236B0F9E00535CD7 /* UnsafePointer+bridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UnsafePointer+bridge.swift"; sourceTree = "<group>"; };
|
||||
49B10869216A356D0064FFCE /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
|
||||
49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivilegedHelperManager.swift; sourceTree = "<group>"; };
|
||||
49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrivilegedHelperManager+Legacy.swift"; sourceTree = "<group>"; };
|
||||
49BC061B212931F4005A0FE7 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
||||
49C9EF63223E78F5005D8B6A /* ClashProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashProxy.swift; sourceTree = "<group>"; };
|
||||
49CF3B1D20CD7463001EBF94 /* ClashX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClashX.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -267,6 +269,7 @@
|
||||
F977FAAD23669D6400C17F1F /* ConnectionManager.swift */,
|
||||
F9E754CF239CC21F00CEE7CC /* WebPortalManager.swift */,
|
||||
49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */,
|
||||
49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */,
|
||||
);
|
||||
path = Managers;
|
||||
sourceTree = "<group>";
|
||||
@ -680,6 +683,7 @@
|
||||
499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */,
|
||||
49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */,
|
||||
49B4575D244F4A2A00463C39 /* PrivilegedHelperManager.swift in Sources */,
|
||||
49B4575F244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift in Sources */,
|
||||
4966E9E32118153A00A391FB /* NSUserNotificationCenter+Extension.swift in Sources */,
|
||||
F9E754D2239CC28D00CEE7CC /* NSAlert+Extension.swift in Sources */,
|
||||
499976C821359F0400E7BF83 /* ClashWebViewContoller.swift in Sources */,
|
||||
@ -948,7 +952,7 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MARKETING_VERSION = 1.3;
|
||||
MARKETING_VERSION = 1.5;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_LDFLAGS = (
|
||||
@ -978,7 +982,7 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MARKETING_VERSION = 1.3;
|
||||
MARKETING_VERSION = 1.5;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_LDFLAGS = (
|
||||
"-sectcreate",
|
||||
|
@ -2,7 +2,7 @@
|
||||
// DateFormatter+.swift
|
||||
// ClashX
|
||||
//
|
||||
// Created by Etan Chen on 2019/12/14.
|
||||
// Created by yicheng on 2019/12/14.
|
||||
// Copyright © 2019 west2online. All rights reserved.
|
||||
//
|
||||
|
||||
|
76
ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift
Normal file
76
ClashX/General/Managers/PrivilegedHelperManager+Legacy.swift
Normal file
@ -0,0 +1,76 @@
|
||||
//
|
||||
// PrivilegedHelperManager+Legacy.swift
|
||||
// ClashX
|
||||
//
|
||||
// Created by yicheng 2020/4/22.
|
||||
// Copyright © 2020 west2online. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
extension PrivilegedHelperManager {
|
||||
func getInstallScript() -> String {
|
||||
let appPath = Bundle.main.bundlePath
|
||||
let bash = """
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
plistPath=/Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist
|
||||
rm -rf /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName)
|
||||
if [ -e ${plistPath} ]; then
|
||||
launchctl unload -w ${plistPath}
|
||||
rm ${plistPath}
|
||||
fi
|
||||
launchctl remove \(PrivilegedHelperManager.machServiceName) || true
|
||||
|
||||
rm -f /Library/PrivilegedHelperTools/com.west2online.ClashX.ProxyConfigHelper
|
||||
cp \(appPath)/Contents/Library/LaunchServices/\(PrivilegedHelperManager.machServiceName) /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName)
|
||||
|
||||
echo '
|
||||
<?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>Label</key>
|
||||
<string>\(PrivilegedHelperManager.machServiceName)</string>
|
||||
<key>MachServices</key>
|
||||
<dict>
|
||||
<key>\(PrivilegedHelperManager.machServiceName)</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>Program</key>
|
||||
<string>/Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName)</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName)</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
' > ${plistPath}
|
||||
|
||||
launchctl load -w ${plistPath}
|
||||
"""
|
||||
return bash
|
||||
}
|
||||
|
||||
func legacyInstallHelper() {
|
||||
defer {
|
||||
resetConnection()
|
||||
}
|
||||
let script = getInstallScript()
|
||||
let tmpPath = FileManager.default.temporaryDirectory.appendingPathComponent(NSUUID().uuidString).appendingPathExtension("sh")
|
||||
do {
|
||||
try script.write(to: tmpPath, atomically: true, encoding: .utf8)
|
||||
let appleScriptStr = "do shell script \"bash \(tmpPath.path) \" with administrator privileges"
|
||||
let appleScript = NSAppleScript(source: appleScriptStr)
|
||||
var dict: NSDictionary?
|
||||
if let _ = appleScript?.executeAndReturnError(&dict) {
|
||||
return
|
||||
} else {
|
||||
Logger.log("apple script fail: \(String(describing: dict))")
|
||||
}
|
||||
} catch let err {
|
||||
Logger.log("legacyInstallHelper create script fail: \(err)")
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ import ServiceManagement
|
||||
|
||||
class PrivilegedHelperManager {
|
||||
private var cancelInstallCheck = false
|
||||
private var useLecgyInstall = false
|
||||
|
||||
private var authRef: AuthorizationRef?
|
||||
private var connection: NSXPCConnection?
|
||||
private var _helper: ProxyConfigRemoteProcessProtocol?
|
||||
@ -38,6 +40,12 @@ class PrivilegedHelperManager {
|
||||
}
|
||||
}
|
||||
|
||||
func resetConnection() {
|
||||
connection?.invalidate()
|
||||
connection = nil
|
||||
_helper = nil
|
||||
}
|
||||
|
||||
private func initAuthorizationRef() {
|
||||
// Create an empty AuthorizationRef
|
||||
let status = AuthorizationCreate(nil, nil, AuthorizationFlags(), &authRef)
|
||||
@ -52,9 +60,7 @@ class PrivilegedHelperManager {
|
||||
Logger.log("installHelperDaemon", level: .info)
|
||||
|
||||
defer {
|
||||
connection?.invalidate()
|
||||
connection = nil
|
||||
_helper = nil
|
||||
resetConnection()
|
||||
}
|
||||
|
||||
// Create authorization reference for the user
|
||||
@ -69,7 +75,7 @@ class PrivilegedHelperManager {
|
||||
|
||||
// Ask user for the admin privileges to install the
|
||||
var authItem = AuthorizationItem(name: (kSMRightBlessPrivilegedHelper as NSString).utf8String!, valueLength: 0, value: nil, flags: 0)
|
||||
var authRights = withUnsafeMutablePointer(to: &authItem) { pointer in
|
||||
var authRights = withUnsafeMutablePointer(to: &authItem) { pointer in
|
||||
AuthorizationRights(count: 1, items: pointer)
|
||||
}
|
||||
let flags: AuthorizationFlags = [[], .interactionAllowed, .extendRights, .preAuthorize]
|
||||
@ -98,37 +104,6 @@ class PrivilegedHelperManager {
|
||||
return .success
|
||||
}
|
||||
|
||||
func authData() -> Data? {
|
||||
guard let authRef = authRef else { return nil }
|
||||
var authRefExtForm = AuthorizationExternalForm()
|
||||
|
||||
// Make an external form of the AuthorizationRef
|
||||
var status = AuthorizationMakeExternalForm(authRef, &authRefExtForm)
|
||||
if status != OSStatus(errAuthorizationSuccess) {
|
||||
Logger.log("AppviewController: AuthorizationMakeExternalForm failed", level: .error)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add all or update required authorization right definition to the authorization database
|
||||
var currentRight: CFDictionary?
|
||||
|
||||
// Try to get the authorization right definition from the database
|
||||
status = AuthorizationRightGet(AppAuthorizationRights.rightName.utf8String!, ¤tRight)
|
||||
|
||||
if status == errAuthorizationDenied {
|
||||
let defaultRules = AppAuthorizationRights.rightDefaultRule
|
||||
status = AuthorizationRightSet(authRef,
|
||||
AppAuthorizationRights.rightName.utf8String!,
|
||||
defaultRules as CFDictionary,
|
||||
AppAuthorizationRights.rightDescription,
|
||||
nil, "Common" as CFString)
|
||||
}
|
||||
|
||||
// We need to put the AuthorizationRef to a form that can be passed through inter process call
|
||||
let authData = NSData(bytes: &authRefExtForm, length: kAuthorizationExternalFormLength)
|
||||
return authData as Data
|
||||
}
|
||||
|
||||
private func helperConnection() -> NSXPCConnection? {
|
||||
// Check that the connection is valid before trying to do an inter process call to helper
|
||||
if connection == nil {
|
||||
@ -193,11 +168,18 @@ extension PrivilegedHelperManager {
|
||||
return
|
||||
}
|
||||
|
||||
if useLecgyInstall {
|
||||
useLecgyInstall = false
|
||||
legacyInstallHelper()
|
||||
return
|
||||
}
|
||||
|
||||
let result = installHelperDaemon()
|
||||
if case .success = result {
|
||||
return
|
||||
}
|
||||
result.alertAction()
|
||||
useLecgyInstall = result.shouldRetryLegacyWay()
|
||||
NSAlert.alert(with: result.alertContent)
|
||||
}
|
||||
|
||||
@ -205,7 +187,11 @@ extension PrivilegedHelperManager {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = NSLocalizedString("ClashX needs to install/update a helper tool with administrator privileges to set system proxy quickly.If not helper tool installed, ClashX won't be able to set your system proxy", comment: "")
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: NSLocalizedString("Install", comment: ""))
|
||||
if useLecgyInstall {
|
||||
alert.addButton(withTitle: NSLocalizedString("Lecgy Install", comment: ""))
|
||||
} else {
|
||||
alert.addButton(withTitle: NSLocalizedString("Install", comment: ""))
|
||||
}
|
||||
alert.addButton(withTitle: NSLocalizedString("Quit", comment: ""))
|
||||
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
|
||||
switch alert.runModal() {
|
||||
@ -260,6 +246,21 @@ fileprivate enum DaemonInstallResult {
|
||||
}
|
||||
}
|
||||
|
||||
func shouldRetryLegacyWay() -> Bool {
|
||||
switch self {
|
||||
case .success: return false
|
||||
case let .blessError(code):
|
||||
switch code {
|
||||
case kSMErrorJobMustBeEnabled:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func alertAction() {
|
||||
switch self {
|
||||
case let .blessError(code):
|
||||
|
@ -34,8 +34,6 @@ class SystemProxyManager: NSObject {
|
||||
PrivilegedHelperManager.shared.helper()
|
||||
}
|
||||
|
||||
private let authData = PrivilegedHelperManager.shared.authData
|
||||
|
||||
func saveProxy() {
|
||||
guard !disableRestoreProxy else { return }
|
||||
Logger.log("saveProxy", level: .debug)
|
||||
@ -59,7 +57,7 @@ class SystemProxyManager: NSObject {
|
||||
return
|
||||
}
|
||||
Logger.log("enableProxy", level: .debug)
|
||||
helper?.enableProxy(withPort: Int32(port), socksPort: Int32(socksPort), authData: authData(), error: { error in
|
||||
helper?.enableProxy(withPort: Int32(port), socksPort: Int32(socksPort), error: { error in
|
||||
if let error = error {
|
||||
Logger.log("enableProxy \(error)", level: .error)
|
||||
}
|
||||
@ -76,15 +74,15 @@ class SystemProxyManager: NSObject {
|
||||
Logger.log("disableProxy", level: .debug)
|
||||
|
||||
if disableRestoreProxy || forceDisable {
|
||||
helper?.disableProxy(withAuthData: authData(), error: { error in
|
||||
helper?.disableProxy { error in
|
||||
if let error = error {
|
||||
Logger.log("disableProxy \(error)", level: .error)
|
||||
}
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
helper?.restoreProxy(withCurrentPort: Int32(port), socksPort: Int32(socksPort), info: savedProxyInfo, authData: authData(), error: { error in
|
||||
helper?.restoreProxy(withCurrentPort: Int32(port), socksPort: Int32(socksPort), info: savedProxyInfo, error: { error in
|
||||
if let error = error {
|
||||
Logger.log("restoreProxy \(error)", level: .error)
|
||||
}
|
||||
|
@ -65,6 +65,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Invalid input" = "Invalid input";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Lecgy Install" = "Lecgy Install";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Need to Restart the ClashX to Take effect, Please start clashX manually" = "Need to Restart the ClashX to Take effect, Please start clashX manually";
|
||||
|
||||
|
@ -65,6 +65,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Invalid input" = "输入不合法";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Lecgy Install" = "传统安装";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Need to Restart the ClashX to Take effect, Please start clashX manually" = "需要重启ClashX生效,请手动启动ClashX";
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>com.west2online.ClashX.ProxyConfigHelper</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.4</string>
|
||||
<string>1.5</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2</string>
|
||||
<key>SMAuthorizedClients</key>
|
||||
|
@ -90,25 +90,14 @@ ProxyConfigRemoteProcessProtocol
|
||||
|
||||
- (void)enableProxyWithPort:(int)port
|
||||
socksPort:(int)socksPort
|
||||
authData:(NSData *)authData
|
||||
error:(stringReplyBlock)reply {
|
||||
ProxySettingTool *tool = [ProxySettingTool new];
|
||||
NSString *err = [tool setupAuth:authData];
|
||||
if (err != nil) {
|
||||
reply(err);
|
||||
return;
|
||||
}
|
||||
[tool enableProxyWithport:port socksPort:socksPort];
|
||||
reply(nil);
|
||||
}
|
||||
|
||||
- (void)disableProxyWithAuthData:(NSData *)authData error:(stringReplyBlock)reply {
|
||||
- (void)disableProxy:(stringReplyBlock)reply {
|
||||
ProxySettingTool *tool = [ProxySettingTool new];
|
||||
NSString *err = [tool setupAuth:authData];
|
||||
if (err != nil) {
|
||||
reply(err);
|
||||
return;
|
||||
}
|
||||
[tool disableProxy];
|
||||
reply(nil);
|
||||
}
|
||||
@ -117,14 +106,8 @@ ProxyConfigRemoteProcessProtocol
|
||||
- (void)restoreProxyWithCurrentPort:(int)port
|
||||
socksPort:(int)socksPort
|
||||
info:(NSDictionary *)dict
|
||||
authData:(NSData *)authData
|
||||
error:(stringReplyBlock)reply {
|
||||
ProxySettingTool *tool = [ProxySettingTool new];
|
||||
NSString *err = [tool setupAuth:authData];
|
||||
if (err != nil) {
|
||||
reply(err);
|
||||
return;
|
||||
}
|
||||
[tool restoreProxySettint:dict currentPort:port currentSocksPort:socksPort];
|
||||
reply(nil);
|
||||
}
|
||||
|
@ -19,15 +19,13 @@ typedef void(^dictReplyBlock)(NSDictionary *);
|
||||
|
||||
- (void)enableProxyWithPort:(int)port
|
||||
socksPort:(int)socksPort
|
||||
authData:(NSData *)authData
|
||||
error:(stringReplyBlock)reply;
|
||||
|
||||
- (void)disableProxyWithAuthData:(NSData *)authData error:(stringReplyBlock)reply;
|
||||
- (void)disableProxy:(stringReplyBlock)reply;
|
||||
|
||||
- (void)restoreProxyWithCurrentPort:(int)port
|
||||
socksPort:(int)socksPort
|
||||
info:(NSDictionary *)dict
|
||||
authData:(NSData *)authData
|
||||
error:(stringReplyBlock)reply;
|
||||
|
||||
- (void)getCurrentProxySetting:(dictReplyBlock)reply;
|
||||
|
@ -12,7 +12,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ProxySettingTool : NSObject
|
||||
|
||||
- (NSString *)setupAuth:(NSData *)authData;
|
||||
- (void)enableProxyWithport:(int)port socksPort:(int)socksPort;
|
||||
- (void)disableProxy;
|
||||
|
||||
|
@ -21,6 +21,13 @@
|
||||
|
||||
@implementation ProxySettingTool
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
[self localAuth];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
- (void)enableProxyWithport:(int)port socksPort:(int)socksPort {
|
||||
@ -242,6 +249,7 @@
|
||||
return authFlags;
|
||||
}
|
||||
|
||||
/*
|
||||
- (NSString *)setupAuth:(NSData *)authData {
|
||||
if (authData.length == 0 || authData.length != kAuthorizationExternalFormLength) {
|
||||
return @"PrivilegedTaskRunnerHelper: Authorization data is malformed";
|
||||
@ -274,6 +282,23 @@
|
||||
self.authRef = authRef;
|
||||
return nil;
|
||||
}
|
||||
*/
|
||||
|
||||
- (void)localAuth {
|
||||
OSStatus myStatus;
|
||||
AuthorizationFlags myFlags = [self authFlags];
|
||||
myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, myFlags, &_authRef);
|
||||
|
||||
if (myStatus != errAuthorizationSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0};
|
||||
AuthorizationRights myRights = {1, &myItems};
|
||||
myStatus = AuthorizationCopyRights (self.authRef, &myRights, NULL, myFlags, NULL );
|
||||
}
|
||||
|
||||
|
||||
- (void)freeAuth {
|
||||
if (self.authRef) {
|
||||
|
Loading…
Reference in New Issue
Block a user