Post

Replies

Boosts

Views

Activity

Correct Way to Check If SecKey Requires User Interaction?
What's the best/correct/current way of checking if a SecKey (or any keychain item) is allowed to be accessed (decrypted) by the current process? This would apply to signing or decrypting for a private key, or the data/contents of a password item (or key). Given the number of APIs deprecated, and that some of the listed interaction mechanisms don't seem to work (FB11153260), I was wondering if there was some SecItemCopyMatching magic, or something related to LAContext. In this use-case the SecKey is provided to this code, or it may fall back to enumeration by hostname (common name), so a persistent reference doesn't apply (though I realize that's a best practice). It looks like kSecUseAuthenticationUI doesn't work, and neither does kSecUseAuthenticationContext with interactionNotAllowed set to false. The deprecated SecKeychainSetUserInteractionAllowed still works though. In the past it was possible to enumerate the ACLs of an item using SecKeychainItemCopyAccess, but there doesn't seem to be an alternative. I was hoping it was possible to use kSecUseItemList with one of the kSecUseAuthentication* options in SecItemCopyMatching to return an error or an empty list, but that doesn't seem to work. Same with kSecMatchItemList. LAContext itself has a way to evaluate operations, but not with reference to a particular item. Either examination of the item (e.g. ACLs), evaluation of a policy (can sign?), or failure from something like SecItemCopyMatching would be fine, but it must work with interaction suppressed.
5
0
1k
Aug ’22
NWProtocolFramer willMarkReady Can't Read After First Write?
When creating a framer which will mark ready, we can't seem to get a reply to the first outgoing message sent by the framer, e.g. of the style: < 220 Service ready for new user.\r\n > AUTH TLS\r\n < 234 Command AUTH okay; starting TLS connection.\r\n The incoming message starting 234 is never received by the framer unless it marks ready right after sending the AUTH line. It looks like this is intentional but we can't understand why this is prevented given the framer should probably check the reply before attempting to prepend e.g. a TLS protocol on the stack (may depend on return code). The dependency seems to be that marking ready will allow the next read, but prevent prepending, and prepending will immediately trigger the TLS handshake, and it will consume the 234 line. It looks like "pass through" can be called any time, before or after, but that requires another protocol on the stack to do the work. Do we need to have one framer read the first message and send one message then mark ready and pass through, prepend another identical framer to read the next message then make the decision to prepend TLS, then prepend again for the "decrypted" framer to read the actual application protocol? We're doing this in C/ObjC, but I figured the Swift terminology might help in the title. Apologies if this was already asked somewhere, I couldn't find a good match.
3
0
946
Aug ’22
How to Debug psort_r(3)/dispatch_group Stall on MacStudio?
We've recently noticed an issue on our new MacStudios where calls to psort_r(3) stall forever. We haven't changed our HPC (particle simulation) code at all, and sampling the app shows psort_r is stuck in dispatch_group_wait(3). Taking the code from Libc 1439.141.1, we've assembled our own implementation which allows us to pass a dispatch_queue_t, dispatch_group_t, and specify a wait time. After 10 seconds (a very long time in our case) the call returns with a non-zero exit code and shows the group and queue are in agreement: four additional blocks are waiting to dispatch, but haven't. This still takes more than two hours of simulation to achieve, where calls to psort_r must be succeeding to make forward progress. Prior to this code change we've seen dispatch_group_wait stuck for hours. What else could we do to diagnose/debug this? We only see it on our M1 Ultra MacStudios, and the comparator passed to psort_r is simple C code (constant time). FB10893202
4
0
1.2k
Jul ’22
Building dnsextd(8) for macOS in 2022
Looks like dnsextd(8) was removed from macOS in 10.15 Catalina, and building from source isn't easy. I used the attached patch to build mDNSResponder 1310.140.1, which removes unused functions except for setTrafficClass which is unused by the target (it passes mDNSFalse for useBackgroundTraffic). We're planning to use this as a frontend to BIND 9.18.3 with an external daemon to verify TSIG for dynamic updates. With MDM we should be able to easily provision new TSIG keys remotely on servers, then communicate them out-of-band to the daemon and (hopefully) have the process work almost automatically. I have heard only RC4 is supported in released versions of macOS, and the implementation is broken in macOS Monterey. dnsextd.txt Comments welcome
3
0
1.2k
Jun ’22
OSLog Tools for Examining Difficult Issues?
I've found Console.app much harder to use in recent years, and one of the issues is the firehose of data is much more difficult to examine when you don't know what you're looking for, especially if you have to turn on debug and info. As an example of a tool, I'm posting a script I wrote to gather likely subsystem/category names from other binaries. It's crude, but does a decent job when you're not sure where to start #!/usr/bin/swift import Foundation extension Process { &#9;&#9;class func launchedForLines(url: URL, args: [String], block: (String) -> Void) { &#9;&#9;&#9;&#9;let proc = Process(), pipe = Pipe(), eol = Data([0x0A]) &#9;&#9;&#9;&#9;proc.executableURL = url &#9;&#9;&#9;&#9;proc.arguments = args &#9;&#9;&#9;&#9;proc.standardOutput = pipe &#9;&#9;&#9;&#9;proc.launch() &#9;&#9;&#9;&#9;let output = pipe.fileHandleForReading &#9;&#9;&#9;&#9;var buffer = Data(capacity: Int(LINE_MAX)), chunk = output.availableData &#9;&#9;&#9;&#9;while !chunk.isEmpty { &#9;&#9;&#9;&#9;&#9;&#9;buffer.append(chunk) &#9;&#9;&#9;&#9;&#9;&#9;while let range = buffer.range(of: eol) { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;let slice = buffer[0..<range.lowerBound] &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;buffer.replaceSubrange(0..<range.upperBound, with: Data()) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if let line = String(data: slice, encoding: .utf8) { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;block(line) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;chunk = output.availableData &#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;proc.terminate() &#9;&#9;} } struct Log : Hashable, Equatable { &#9;&#9;let subsystem:String, category:String } func find_logs(url: URL) -> [Log]? { &#9;&#9;let literal = "literal pool for: \"", hold = "HOLD" &#9;&#9;var ring = [hold, hold], idx = false, logs = Set&lt;Log&gt;() &#9;&#9;Process.launchedForLines(url: URL(fileURLWithPath: "/usr/bin/otool", isDirectory: false), args: ["-tV", url.path], block: { (line) in &#9;&#9;&#9;&#9;if let range = line.range(of: literal) { &#9;&#9;&#9;&#9;&#9;&#9;let frag = line[range.upperBound..<line.index(before: line.endIndex)] &#9;&#9;&#9;&#9;&#9;&#9;ring[idx ? 1 : 0] = String(frag) &#9;&#9;&#9;&#9;&#9;&#9;idx = !idx &#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;else if line.range(of: "_os_log_create", options: .literal) != nil { &#9;&#9;&#9;&#9;&#9;&#9;let sub = ring[idx ? 1 : 0], cat = ring[idx ? 0 : 1] &#9;&#9;&#9;&#9;&#9;&#9;if sub != hold &amp;&amp; cat != hold &amp;&amp; sub.range(of: ".", options: .literal) != nil { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;logs.insert(Log(subsystem: sub, category: cat)) &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;ring = [hold, hold] &#9;&#9;&#9;&#9;} &#9;&#9;}) &#9;&#9;return logs.isEmpty ? nil : Array(logs) } func check_mach(url: URL) -> Bool { &#9;&#9;guard let handle = try? FileHandle(forReadingFrom: url) else { &#9;&#9;&#9;&#9;return false &#9;&#9;} &#9;&#9;let size = MemoryLayout.size(ofValue: MH_MAGIC), magic = handle.readData(ofLength: size) &#9;&#9;try? handle.close() &#9;&#9;return magic.count == size &amp;&amp; [MH_MAGIC, MH_MAGIC_64, FAT_CIGAM, FAT_CIGAM_64].contains(magic.withUnsafeBytes({ $0.load(as: UInt32.self) })) } func enumerate(url: URL, block: (URL, URLFileResourceType) -> Void) { &#9;&#9;guard let type = try? url.resourceValues(forKeys: [.fileResourceTypeKey]).fileResourceType else { &#9;&#9;&#9;&#9;return &#9;&#9;} &#9;&#9;block(url, type) &#9;&#9;if (type == .directory) { &#9;&#9;&#9;&#9;_ = (try? FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: [.fileResourceTypeKey], options: []))?.map({ enumerate(url: $0, block: block) }) &#9;&#9;} } let args = ProcessInfo().arguments guard let base = args.count > 1 ? URL(fileURLWithPath: args[1]) : nil else { &#9;&#9;print("Specify exactly one argument, the path to search") &#9;&#9;exit(1) } enumerate(url: base) { (url, type) in &#9;&#9;autoreleasepool { &#9;&#9;&#9;&#9;if type == .regular &amp;&amp; check_mach(url: url), let logs = find_logs(url: url) { &#9;&#9;&#9;&#9;&#9;&#9;print("Found binary \(url.path)") &#9;&#9;&#9;&#9;&#9;&#9;_ = logs.map({ print("Subsystem: \($0.subsystem) Category: \($0.category)") }) &#9;&#9;&#9;&#9;} &#9;&#9;} }
0
0
807
Jul ’20
Which API Key Role to Use for Notarization?
I've tried to find explicit documentation for the role to select when creating an API key for use withxcrun altool --notarize-app --apiKeyBut only found a discussion like Using an API Key with iTMSTransporter. Should it always be App Manager, or is there a less-priviledged one for this task? Notarization doesn't seem like it would require significant access.
7
0
4.1k
May ’20