Skip to content

Commit

Permalink
feat: added certificate verification to the helper app. It will verif…
Browse files Browse the repository at this point in the history
…y 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.
  • Loading branch information
exelban committed Dec 14, 2024
1 parent 1093b27 commit c10759f
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 16 deletions.
8 changes: 4 additions & 4 deletions SMC/Helper/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
<key>CFBundleName</key>
<string>eu.exelban.Stats.SMC.Helper</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<string>1.0.1</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<string>2</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>SMAuthorizedClients</key>
<array>
<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>
Expand Down
103 changes: 96 additions & 7 deletions SMC/Helper/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,31 @@ class Helper: NSObject, NSXPCListenerDelegate, HelperProtocol {
}
}

func listener(_ listener: NSXPCListener, shouldAcceptNewConnection connection: NSXPCConnection) -> Bool {
connection.exportedInterface = NSXPCInterface(with: HelperProtocol.self)
connection.exportedObject = self
connection.invalidationHandler = {
if let connectionIndex = self.connections.firstIndex(of: connection) {
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
do {
let isValid = try CodesignCheck.codeSigningMatches(pid: newConnection.processIdentifier)
if !isValid {
NSLog("invalid connection, dropping")
return false
}
} catch {
NSLog("error checking code signing: \(error)")
return false
}

newConnection.exportedInterface = NSXPCInterface(with: HelperProtocol.self)
newConnection.exportedObject = self
newConnection.invalidationHandler = {
if let connectionIndex = self.connections.firstIndex(of: newConnection) {
self.connections.remove(at: connectionIndex)
}
if self.connections.isEmpty {
self.shouldQuit = true
}
}

self.connections.append(connection)
connection.resume()
self.connections.append(newConnection)
newConnection.resume()

return true
}
Expand Down Expand Up @@ -185,3 +196,81 @@ extension Helper {
exit(0)
}
}

// https://github.com/duanefields/VirtualKVM/blob/master/VirtualKVM/CodesignCheck.swift
let kSecCSDefaultFlags = 0

enum CodesignCheckError: Error {
case message(String)
}

struct CodesignCheck {
public static func codeSigningMatches(pid: pid_t) throws -> Bool {
return try self.codeSigningCertificatesForSelf() == self.codeSigningCertificates(forPID: pid)
}

private static func codeSigningCertificatesForSelf() throws -> [SecCertificate] {
guard let secStaticCode = try secStaticCodeSelf() else { return [] }
return try codeSigningCertificates(forStaticCode: secStaticCode)
}

private static func codeSigningCertificates(forPID pid: pid_t) throws -> [SecCertificate] {
guard let secStaticCode = try secStaticCode(forPID: pid) else { return [] }
return try codeSigningCertificates(forStaticCode: secStaticCode)
}

private static func executeSecFunction(_ secFunction: () -> (OSStatus) ) throws {
let osStatus = secFunction()
guard osStatus == errSecSuccess else {
throw CodesignCheckError.message(String(describing: SecCopyErrorMessageString(osStatus, nil)))
}
}

private static func secStaticCodeSelf() throws -> SecStaticCode? {
var secCodeSelf: SecCode?
try executeSecFunction { SecCodeCopySelf(SecCSFlags(rawValue: 0), &secCodeSelf) }
guard let secCode = secCodeSelf else {
throw CodesignCheckError.message("SecCode returned empty from SecCodeCopySelf")
}
return try secStaticCode(forSecCode: secCode)
}

private static func secStaticCode(forPID pid: pid_t) throws -> SecStaticCode? {
var secCodePID: SecCode?
try executeSecFunction { SecCodeCopyGuestWithAttributes(nil, [kSecGuestAttributePid: pid] as CFDictionary, [], &secCodePID) }
guard let secCode = secCodePID else {
throw CodesignCheckError.message("SecCode returned empty from SecCodeCopyGuestWithAttributes")
}
return try secStaticCode(forSecCode: secCode)
}

private static func secStaticCode(forSecCode secCode: SecCode) throws -> SecStaticCode? {
var secStaticCodeCopy: SecStaticCode?
try executeSecFunction { SecCodeCopyStaticCode(secCode, [], &secStaticCodeCopy) }
guard let secStaticCode = secStaticCodeCopy else {
throw CodesignCheckError.message("SecStaticCode returned empty from SecCodeCopyStaticCode")
}
return secStaticCode
}

private static func isValid(secStaticCode: SecStaticCode) throws {
try executeSecFunction { SecStaticCodeCheckValidity(secStaticCode, SecCSFlags(rawValue: kSecCSDoNotValidateResources | kSecCSCheckNestedCode), nil) }
}

private static func secCodeInfo(forStaticCode secStaticCode: SecStaticCode) throws -> [String: Any]? {
try isValid(secStaticCode: secStaticCode)
var secCodeInfoCFDict: CFDictionary?
try executeSecFunction { SecCodeCopySigningInformation(secStaticCode, SecCSFlags(rawValue: kSecCSSigningInformation), &secCodeInfoCFDict) }
guard let secCodeInfo = secCodeInfoCFDict as? [String: Any] else {
throw CodesignCheckError.message("CFDictionary returned empty from SecCodeCopySigningInformation")
}
return secCodeInfo
}

private static func codeSigningCertificates(forStaticCode secStaticCode: SecStaticCode) throws -> [SecCertificate] {
guard
let secCodeInfo = try secCodeInfo(forStaticCode: secStaticCode),
let secCertificates = secCodeInfo[kSecCodeInfoCertificates as String] as? [SecCertificate] else { return [] }
return secCertificates
}
}
2 changes: 1 addition & 1 deletion Stats.xcodeproj/xcshareddata/xcschemes/SMC.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
LastUpgradeVersion = "1610"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
2 changes: 1 addition & 1 deletion Stats.xcodeproj/xcshareddata/xcschemes/Stats.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
LastUpgradeVersion = "1610"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
LastUpgradeVersion = "1610"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
Expand Down
2 changes: 1 addition & 1 deletion Stats/Supporting Files/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>639</string>
<string>640</string>
<key>Description</key>
<string>Simple macOS system monitor in your menu bar</string>
<key>LSApplicationCategoryType</key>
Expand Down
2 changes: 1 addition & 1 deletion Widgets/Supporting Files/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<key>CFBundleShortVersionString</key>
<string>2.11.20</string>
<key>CFBundleVersion</key>
<string>639</string>
<string>640</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
Expand Down

0 comments on commit c10759f

Please sign in to comment.