Get executable path from audit token provided by NEFilterDataProvider

I'm using this code to get the path of an executable from the audit token provided in NEFilterDataProvider.handleNewFlow(_:):

private func securePathFromAuditToken(_ auditToken: Data) throws -> String? {
    let secFlags = SecCSFlags()
    var secCode: SecCode?
    var status = SecCodeCopyGuestWithAttributes(nil, [kSecGuestAttributeAudit: auditToken] as CFDictionary, secFlags, &secCode)
    guard let secCode = secCode else {
        throw SecError(status)
    }
    var secStaticCode: SecStaticCode?
    status = SecCodeCopyStaticCode(secCode, secFlags, &secStaticCode)
    guard let secStaticCode = secStaticCode else {
        throw SecError(status)
    }
    var dict: CFDictionary?
    status = SecCodeCopySigningInformation(secStaticCode, secFlags, &dict)
    guard let dict = dict as NSDictionary? else {
        throw SecError(status)
    }
    if let identifier = dict[kSecCodeInfoIdentifier as String] as? String, let path = NSWorkspace.shared.urlForApplication(withBundleIdentifier: identifier)?.path {
        return path
    } else if let path = dict[kSecCodeInfoMainExecutable as String] as? String {
        return path
    }
    return nil
}

But it seems that only applications inside the /Applications folder have a non-nil path. For all other executables I have to resort to this code, which I have read is not as secure:

private func insecurePathFromAuditToken(_ auditToken: Data) throws -> String? {
    if auditToken.count == MemoryLayout<audit_token_t>.size {
        let pid = auditToken.withUnsafeBytes { buffer in
            audit_token_to_pid(buffer.baseAddress!.assumingMemoryBound(to: audit_token_t.self).pointee)
        }
        let pathbuf = UnsafeMutablePointer<Int8>.allocate(capacity: Int(PROC_PIDPATHINFO_SIZE))
        defer {
            pathbuf.deallocate()
        }
        let ret = proc_pidpath(pid, pathbuf, UInt32(PROC_PIDPATHINFO_SIZE))
        if ret <= 0 {
            throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
        }
        return String(cString: pathbuf)
    }
    return nil
}

This seems to happen with both NEFilterFlow.sourceAppAuditToken and sourceProcessAuditToken. Is this expected? Can it really be that all executables that are not apps shipped with macOS are not signed?

Can it really be that all executables that are not apps shipped with macOS are not signed?

No. The issue here is that SecCode is trying to access the app on disk and the App Sandbox applied to your NE provider is preventing that access. Have a look for sandbox violation reports, per Discovering and diagnosing App Sandbox violations.


Also, your first code snippet has some serious problems:

  • It uses NSWorkspace, which isn’t really appropriate for an NE provider. That’s particularly true if your NE provider is packaged as a system extension. System extensions are effectively daemons and NSWorkspace is from AppKit, which is not a daemon-safe framework.

  • There can be multiple apps with the same bundle ID and NSWorkspace will return one of them, which may not be the same as the one associated with that audit token.

  • It assumes that the code signing identifier and the bundle ID are connected, which they are not. For bundled code the former defaults to the latter but there’s no hard requirement.

The droid you’re looking for here is SecCodeCopyPath. If you search DevForums for that, you’ll find a number of other threads where I’ve gone into this issue in the past.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Get executable path from audit token provided by NEFilterDataProvider
 
 
Q