-
Notifications
You must be signed in to change notification settings - Fork 0
/
PrivilegedTaskRunnerHelper.swift
135 lines (99 loc) · 5.4 KB
/
PrivilegedTaskRunnerHelper.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//
// PrivilegedTaskRunnerHelper.swift
// PrivilegedTaskRunnerHelper
//
// Created by Chanwoo Noh on 2018. 7. 12..
// Copyright © 2018년 Chanwoo Noh. All rights reserved.
//
import Foundation
class PrivilegedTaskRunnerHelper: NSObject, RemoteProcessProtocol, NSXPCListenerDelegate {
var listener:NSXPCListener
let commandPath:String = "/usr/sbin/lsof"
let commandArguments:[String] = ["-iTCP", "-sTCP:LISTEN", "-n", "-P"]
override init() {
self.listener = NSXPCListener(machServiceName:HelperConstants.machServiceName)
super.init()
self.listener.delegate = self
}
/// Starts the helper daemon
func run() {
self.listener.resume()
RunLoop.current.run()
}
/// Called when the client connects to the helper daemon
func listener(_ listener:NSXPCListener, shouldAcceptNewConnection connection: NSXPCConnection) -> Bool {
connection.exportedInterface = NSXPCInterface(with: RemoteProcessProtocol.self)
connection.exportedObject = self;
connection.resume()
return true
}
/// Functions to run from the main app
func runCommand(path: String, authData: NSData?, reply: @escaping (String) -> Void) {
var authRef:AuthorizationRef?
// Verify the passed authData looks reasonable
if authData?.length == 0 || authData?.length != kAuthorizationExternalFormLength {
NSLog("PrivilegedTaskRunnerHelper: Authorization data is malformed")
}
// Convert NSData passed through XPC to AuthorizationExternalForm
let authExt: UnsafeMutablePointer<AuthorizationExternalForm> = UnsafeMutablePointer.allocate(capacity: kAuthorizationExternalFormLength * MemoryLayout<AuthorizationExternalForm>.size)
memcpy(authExt, authData?.bytes, (authData?.length)!)
_ = AuthorizationCreateFromExternalForm(authExt, &authRef)
// Extract the AuthorizationRef from it's external form
var status = AuthorizationCreateFromExternalForm(authExt, &authRef)
if (status == errAuthorizationSuccess) {
NSLog("PrivilegedTaskRunnerHelper: AuthorizationCreateFromExternalForm was successful")
// Get the authorization right definition name of the function calling this
let authName = "so.yoko.portscanner.runCommand"
// Create an AuthorizationItem using that definition's name
var authItem = AuthorizationItem(name: (authName as NSString).utf8String!, valueLength: 0, value:UnsafeMutableRawPointer(bitPattern: 0), flags: 0)
// Create the AuthorizationRights for using the AuthorizationItem
var authRight:AuthorizationRights = AuthorizationRights(count: 1, items:&authItem)
// Check if the user is authorized for the AuthorizationRights. If not it might ask the user for their or an admins credentials
status = AuthorizationCopyRights(authRef!, &authRight, nil, [ .extendRights, .interactionAllowed ], nil);
if (status == errAuthorizationSuccess) {
NSLog("PrivilegedTaskRunnerHelper: AuthorizationCopyRights was successful")
// Create cli commands that needs to be run chained / piped
// let needsSudoCommand = CliCommand(launchPath: "/bin/ls", arguments: ["/var/db/sudo"])
let needsSudoCommand = CliCommand(launchPath: commandPath, arguments: commandArguments)
// Prepare cli command runner
let command = ProcessHelper(commands: [needsSudoCommand])
// Prepare result tuple
var commandResult: String?
// Execute cli commands and prepare for exceptions
do {
commandResult = try command.execute()
}
catch {
NSLog("PrivilegedTaskRunnerHelper: Failed to run command")
}
reply(commandResult!)
}
}
else {
NSLog("PrivilegedTaskRunnerHelper: Authorization failed")
}
}
/// Functions to run from the main app
func runCommand(path: String, reply: @escaping (String) -> Void) {
// Create cli commands that needs to be run chained / piped
// let needsSudoCommand = CliCommand(launchPath: "/bin/ls", arguments: ["/var/db/sudo"])
let needsSudoCommand = CliCommand(launchPath: commandPath, arguments: commandArguments)
// Prepare cli command runner
let command = ProcessHelper(commands: [needsSudoCommand])
// Prepare result tuple
var commandResult: String?
// Execute cli commands and prepare for exceptions
do {
commandResult = try command.execute()
}
catch {
NSLog("PrivilegedTaskRunnerHelper: Failed to run command")
}
reply(commandResult!)
}
/// Return daemon's bundle version
/// Because communication over XPC is asynchronous, all methods in the protocol must have a return type of void
func getVersion(reply: (String) -> Void) {
reply(Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as! String)
}
}