Skip to content

Commit c10759f

Browse files
committed
feat: added certificate verification to the helper app. It will verify the incoming connection certificate if it's Stats. If not the connection will not be opened. It prevents abusing the helper app by 3d party applications to access the helper interface. Thanks @senzee1984 for finding and helping solve that problem. This commit will force the update of the helper app and will require a password for users who have installed the helper.
1 parent 1093b27 commit c10759f

File tree

7 files changed

+105
-16
lines changed

7 files changed

+105
-16
lines changed

SMC/Helper/Info.plist

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
<key>CFBundleName</key>
88
<string>eu.exelban.Stats.SMC.Helper</string>
99
<key>CFBundleShortVersionString</key>
10-
<string>1.0.0</string>
10+
<string>1.0.1</string>
1111
<key>CFBundleVersion</key>
12-
<string>1</string>
13-
<key>CFBundleInfoDictionaryVersion</key>
14-
<string>6.0</string>
12+
<string>2</string>
13+
<key>CFBundleInfoDictionaryVersion</key>
14+
<string>6.0</string>
1515
<key>SMAuthorizedClients</key>
1616
<array>
1717
<string>anchor apple generic and identifier &quot;eu.exelban.Stats&quot; and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = RP2S87B72W)</string>

SMC/Helper/main.swift

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,31 @@ class Helper: NSObject, NSXPCListenerDelegate, HelperProtocol {
4848
}
4949
}
5050

51-
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection connection: NSXPCConnection) -> Bool {
52-
connection.exportedInterface = NSXPCInterface(with: HelperProtocol.self)
53-
connection.exportedObject = self
54-
connection.invalidationHandler = {
55-
if let connectionIndex = self.connections.firstIndex(of: connection) {
51+
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
52+
do {
53+
let isValid = try CodesignCheck.codeSigningMatches(pid: newConnection.processIdentifier)
54+
if !isValid {
55+
NSLog("invalid connection, dropping")
56+
return false
57+
}
58+
} catch {
59+
NSLog("error checking code signing: \(error)")
60+
return false
61+
}
62+
63+
newConnection.exportedInterface = NSXPCInterface(with: HelperProtocol.self)
64+
newConnection.exportedObject = self
65+
newConnection.invalidationHandler = {
66+
if let connectionIndex = self.connections.firstIndex(of: newConnection) {
5667
self.connections.remove(at: connectionIndex)
5768
}
5869
if self.connections.isEmpty {
5970
self.shouldQuit = true
6071
}
6172
}
6273

63-
self.connections.append(connection)
64-
connection.resume()
74+
self.connections.append(newConnection)
75+
newConnection.resume()
6576

6677
return true
6778
}
@@ -185,3 +196,81 @@ extension Helper {
185196
exit(0)
186197
}
187198
}
199+
200+
// https://github.com/duanefields/VirtualKVM/blob/master/VirtualKVM/CodesignCheck.swift
201+
let kSecCSDefaultFlags = 0
202+
203+
enum CodesignCheckError: Error {
204+
case message(String)
205+
}
206+
207+
struct CodesignCheck {
208+
public static func codeSigningMatches(pid: pid_t) throws -> Bool {
209+
return try self.codeSigningCertificatesForSelf() == self.codeSigningCertificates(forPID: pid)
210+
}
211+
212+
private static func codeSigningCertificatesForSelf() throws -> [SecCertificate] {
213+
guard let secStaticCode = try secStaticCodeSelf() else { return [] }
214+
return try codeSigningCertificates(forStaticCode: secStaticCode)
215+
}
216+
217+
private static func codeSigningCertificates(forPID pid: pid_t) throws -> [SecCertificate] {
218+
guard let secStaticCode = try secStaticCode(forPID: pid) else { return [] }
219+
return try codeSigningCertificates(forStaticCode: secStaticCode)
220+
}
221+
222+
private static func executeSecFunction(_ secFunction: () -> (OSStatus) ) throws {
223+
let osStatus = secFunction()
224+
guard osStatus == errSecSuccess else {
225+
throw CodesignCheckError.message(String(describing: SecCopyErrorMessageString(osStatus, nil)))
226+
}
227+
}
228+
229+
private static func secStaticCodeSelf() throws -> SecStaticCode? {
230+
var secCodeSelf: SecCode?
231+
try executeSecFunction { SecCodeCopySelf(SecCSFlags(rawValue: 0), &secCodeSelf) }
232+
guard let secCode = secCodeSelf else {
233+
throw CodesignCheckError.message("SecCode returned empty from SecCodeCopySelf")
234+
}
235+
return try secStaticCode(forSecCode: secCode)
236+
}
237+
238+
private static func secStaticCode(forPID pid: pid_t) throws -> SecStaticCode? {
239+
var secCodePID: SecCode?
240+
try executeSecFunction { SecCodeCopyGuestWithAttributes(nil, [kSecGuestAttributePid: pid] as CFDictionary, [], &secCodePID) }
241+
guard let secCode = secCodePID else {
242+
throw CodesignCheckError.message("SecCode returned empty from SecCodeCopyGuestWithAttributes")
243+
}
244+
return try secStaticCode(forSecCode: secCode)
245+
}
246+
247+
private static func secStaticCode(forSecCode secCode: SecCode) throws -> SecStaticCode? {
248+
var secStaticCodeCopy: SecStaticCode?
249+
try executeSecFunction { SecCodeCopyStaticCode(secCode, [], &secStaticCodeCopy) }
250+
guard let secStaticCode = secStaticCodeCopy else {
251+
throw CodesignCheckError.message("SecStaticCode returned empty from SecCodeCopyStaticCode")
252+
}
253+
return secStaticCode
254+
}
255+
256+
private static func isValid(secStaticCode: SecStaticCode) throws {
257+
try executeSecFunction { SecStaticCodeCheckValidity(secStaticCode, SecCSFlags(rawValue: kSecCSDoNotValidateResources | kSecCSCheckNestedCode), nil) }
258+
}
259+
260+
private static func secCodeInfo(forStaticCode secStaticCode: SecStaticCode) throws -> [String: Any]? {
261+
try isValid(secStaticCode: secStaticCode)
262+
var secCodeInfoCFDict: CFDictionary?
263+
try executeSecFunction { SecCodeCopySigningInformation(secStaticCode, SecCSFlags(rawValue: kSecCSSigningInformation), &secCodeInfoCFDict) }
264+
guard let secCodeInfo = secCodeInfoCFDict as? [String: Any] else {
265+
throw CodesignCheckError.message("CFDictionary returned empty from SecCodeCopySigningInformation")
266+
}
267+
return secCodeInfo
268+
}
269+
270+
private static func codeSigningCertificates(forStaticCode secStaticCode: SecStaticCode) throws -> [SecCertificate] {
271+
guard
272+
let secCodeInfo = try secCodeInfo(forStaticCode: secStaticCode),
273+
let secCertificates = secCodeInfo[kSecCodeInfoCertificates as String] as? [SecCertificate] else { return [] }
274+
return secCertificates
275+
}
276+
}

Stats.xcodeproj/xcshareddata/xcschemes/SMC.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1600"
3+
LastUpgradeVersion = "1610"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

Stats.xcodeproj/xcshareddata/xcschemes/Stats.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1600"
3+
LastUpgradeVersion = "1610"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

Stats.xcodeproj/xcshareddata/xcschemes/WidgetsExtension.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1600"
3+
LastUpgradeVersion = "1610"
44
wasCreatedForAppExtension = "YES"
55
version = "2.0">
66
<BuildAction

Stats/Supporting Files/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<key>CFBundleShortVersionString</key>
1818
<string>$(MARKETING_VERSION)</string>
1919
<key>CFBundleVersion</key>
20-
<string>639</string>
20+
<string>640</string>
2121
<key>Description</key>
2222
<string>Simple macOS system monitor in your menu bar</string>
2323
<key>LSApplicationCategoryType</key>

Widgets/Supporting Files/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<key>CFBundleShortVersionString</key>
1414
<string>2.11.20</string>
1515
<key>CFBundleVersion</key>
16-
<string>639</string>
16+
<string>640</string>
1717
<key>NSExtension</key>
1818
<dict>
1919
<key>NSExtensionPointIdentifier</key>

0 commit comments

Comments
 (0)