Post

Replies

Boosts

Views

Activity

Reply to Detecting DarkWake and Maintainance Sleep transitions
Thanks for all the help, Kevin. I was able to look at the power plane service IO object using the KE_PrintIOObject(io_service_t myObj) function you had mentioned above. The data returned has a IOPowerManagement dictionary which has values for CurrentPowerState and DesiredPowerState. I looked for these every time a kIOGeneralInterest event was generated by the power plane service.
Topic: App & System Services SubTopic: Core OS Tags:
Jan ’25
Reply to Detecting DarkWake and Maintainance Sleep transitions
I have tried the XPC activity system by writing a small test program which I load up using launchd, making it a LaunchDaemon. What I observe is that I get the activity callback to run only once when I first put the device to sleep after loading the daemon. I do not see the callback running for any DarkWakes or even any subsequent wakeup -> sleep cycle (I open and close the macbook lid to do this i.e. clamshell sleep). Here is my test code: #import <Foundation/Foundation.h> #import <xpc/xpc.h> #include <signal.h> // Define a unique identifier for the activity const char *activityIdentifier = "com.example.myapp.myactivity"; void CleanupAndExit(void) { NSLog(@"Unregistering activity..."); xpc_activity_unregister(activityIdentifier); NSLog(@"Exiting program."); exit(0); } void HandleSignal(int signal) { CleanupAndExit(); } void PerformTask(void) { // Get the current date NSDate *currentDate = [NSDate date]; // Create a date formatter NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; // Set the desired date and time format [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; // Convert the NSDate to a human-readable string NSString *dateString = [dateFormatter stringFromDate:currentDate]; NSLog(@"DarkWake timestamp: %@", dateString); } int main(int argc, const char * argv[]) { xpc_transaction_begin(); @autoreleasepool { // Register signal handlers for cleanup signal(SIGINT, HandleSignal); // Handle Ctrl+C signal(SIGTERM, HandleSignal); // Handle termination // Set up criteria for the activity xpc_object_t criteria = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_REQUIRE_SCREEN_SLEEP, true); // Requires screen sleep xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_ALLOW_BATTERY, true); // Allow on battery xpc_dictionary_set_string(criteria, XPC_ACTIVITY_PRIORITY, XPC_ACTIVITY_PRIORITY_MAINTENANCE); // Priority: Maintainance // Register the activity xpc_activity_register(activityIdentifier, criteria, ^(xpc_activity_t activity) { xpc_activity_state_t state = xpc_activity_get_state(activity); if (state == XPC_ACTIVITY_STATE_RUN) { NSLog(@"Activity triggered: %s", activityIdentifier); PerformTask(); } else if (state == XPC_ACTIVITY_STATE_WAIT) { NSLog(@"Activity is waiting to be scheduled."); } else { NSLog(@"Activity state: %ld", (long)state); } }); // Run the main run loop to keep the program alive NSLog(@"Waiting for activities to trigger..."); [[NSRunLoop currentRunLoop] run]; } xpc_transaction_end(); return 0; } Let me know if I am not interacting with the XPC system as intended. Regarding the IOPMFindPowerManagement approach, can you please clarify it a bit more. From the IOPMFindPowerManagement method I get a io_connect_t object. How does this one allow me to get the IOrootDomain IOServiceObject like you say. I am new to this API so could not find a way to do so. Also, I think I need the io_service_t object to pass to the IOServiceAddInterestNotification from the io_connect_t. Can you please point me to the right direction
Topic: App & System Services SubTopic: Core OS Tags:
Dec ’24
Reply to Network Framework Revocation Checks
I finally found a solution to the OCSP requests not being sent. The problem was caused by the /etc/hosts file. I have my server and my OCSP responder hosted on the same machine. That is why I had a line in the hosts file like: 192.168.x.x server1.local.com server2.local.com ocsp.responder.com These are just placeholder names. With this line I could not see any OCSP requests on my WireShark trace. As soon as I decoupled those lines in the /etc/hosts file like: 192.168.x.x server1.local.com server2.local.com ocsp.responder.com 192.168.x.x ocsp.responder.com I could see OCSP requests from the macOS machine to my OCSP responder. Everything now works pretty well. This macOS machines was running Monterey(12.4) , but I do compile my project with the target OS being 10.15.
Jun ’22
Reply to Network Framework Revocation Checks
The server is not responding with an OCSP check in the handshake negotiation. OCSP stapling is not enabled on the server. I tried two leaf certificate with my new root CA and still no OCSP messages on the WireShark traces. Weirdly when I try to visit websites hosted by digicert.com which server revoked certificates from their CAs, I can see the OCSP messages going to http://ocsp.digicert.com. The application correctly recognizes the revoked certificate and fails the TLS trust evaluation. These test websites can be found here: DigiCert Root Certificates - Download & Test | DigiCert.com
Jun ’22
Reply to Network Framework Revocation Checks
I have submitted another feedback report (FB10033659) for your reference. I can download the CRL and access OCSP endpoint from my macOS machine. I also verified the server certificate revocation status using an OpenSSL command: openssl ocsp -issuer caCert.cer -cert switchCert.cer -text -url http://<address>/ocsp I am able to get the response back like this for a revoked certificate: Response Verify Failure 4571629228:error:27FFF065:OCSP routines:CRYPTO_internal:certificate verify error:/AppleInternal/Library/BuildRoots/b6051351-c030-11ec-96e9-3e7866fcf3a1/Library/Caches/com.apple.xbs/Sources/libressl/libressl-2.8/crypto/ocsp/ocsp_vfy.c:141:Verify error:unable to get local issuer certificate switchCert.cer: revoked This Update: May 31 16:14:42 2022 GMT Next Update: Jun 2 04:34:42 2022 GMT Reason: certificateHold Revocation Time: May 31 16:24:00 2022 GMT This is all expected, but my macOS Command Line test app as you suggested I make still makes no requests to the OCSP endpoint where I am running WireShark traces to monitor incoming traffic. This is both with a valid or invalid chain. To expand that a bit more, the chain the server sends to the macOS machine is the leaf certificate and the root CA. This root CA I have added to the keychain and marked as "Always Trust" for everything in Keychain Access. Weirdly both Chrome and Safari mark this revoked server certificate as valid, when I try to make a HTTPS request to the server. Also not making any requests to the CRL or OCSP endpoints. On my Windows machines, this certificate is marked correctly as revoked in chrome when I try to make HTTPS requests to the same server. Is this something related?
Jun ’22
Reply to Network Framework Revocation Checks
Thanks meaton. I did not come across any documentation saying CRL checks had been removed from 10.15. I set up an OCSP responder setup using a Windows server and I can see OCSP requests coming in for my Windows clients but the macOS client does not send any such request. Still getting the error code -67635 corresponding to errSecIncompleteCertRevocationCheck. I prefer to make this whole operation synchronously, so was continuing to use SecTrustEvaluateWithError but when that continued to give me the same errors, I switched over to SecTrustEvaluateAsyncWithError. I was blocking the main thread using a std::promise and std::future setup like this: std::future<void> barrier_future = m_barrier.get_future(); std::thread workThread(TrustEval); barrier_future.wait(); workThread.join(); if(trustEvalResult) { fprintf(stderr,"COMPLETE = TRUE \n"); complete(true); } else { fprintf(stderr,"COMPLETE = FALSE \n"); complete(false); } where m_barrier and trustEvalResult are global variables. This code is still inside the sec_protocol_options_set_verify_block. The TrustEval function running in a separate thread namely workThread is: void TrustEval() { fprintf(stderr,"Starting TRUST EVAL \n"); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ SecTrustEvaluateAsyncWithError(peerTrust, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(SecTrustRef evaluatedTrust, bool trustResult, CFErrorRef error) { if (trustResult) { fprintf(stderr,"TRUST EVAL SUCCESSFUL \n"); // Evaluation succeeded! trustEvalResult = true; } else { fprintf(stderr,"TRUST EVAL FAILED \n"); // Evaluation failed: Check the error trustEvalResult = false; } // Finally, release the trust and error. if (evaluatedTrust) { CFRelease(evaluatedTrust); } if (error) { CFRelease(error); } fprintf(stderr,"Setting m_barrier \n"); m_barrier.set_value(); } ); }); } But still see the revocation check fail from the SecTrustWithErrorCallback block. And no OCSP requests from the macOS machine. I should mention going to the browser I can access the crl and ocsp endpoint urls.
May ’22
Reply to Network Framework Revocation Checks
Posting the code snippet above here as well as I reached word limit above: sec_protocol_options_set_verify_block(sec_options, ^(sec_protocol_metadata_t metadata, sec_trust_t trust_ref, sec_protocol_verify_complete_t complete){ if (ignoreCertValidation) { logger->Info("TLS certificate validation errors will be ignored."); complete(true); } else { SecTrustRef peerTrust = NULL; if (!(peerTrust = sec_trust_copy_ref(trust_ref))) { logger->Error("Unable to copy SecTrustRef for SSL verification."); } CFArrayRef trustPolicy = NULL; SecPolicyRef revocationPolicy = NULL; OSStatus copyPolicyStatus = SecTrustCopyPolicies(peerTrust, &trustPolicy); if (copyPolicyStatus != errSecSuccess) { string errMessage = StringUtils::CFStringRefToString(SecCopyErrorMessageString(copyPolicyStatus, NULL)); logger->Error("Unable to create additional policies for TLS trust evaluation. Reason: %s", errMessage.c_str()); } else { CFMutableArrayRef mPolicies = CFArrayCreateMutableCopy(kCFAllocatorDefault, (CFArrayGetCount(trustPolicy) + 1), trustPolicy); CFRelease(trustPolicy); SecPolicyRef sslPolicy = (SecPolicyRef)CFArrayGetValueAtIndex(mPolicies, 0); CFDictionaryRef sslPolicyProperties = SecPolicyCopyProperties(sslPolicy); CFStringRef policyType = (CFStringRef)CFDictionaryGetValue(sslPolicyProperties, kSecPolicyOid); if (policyType && CFEqual(kSecPolicyAppleSSL, policyType)) // Only apply revocation to SSL validation { if (crlChecks == CRLChecks::Hard) { revocationPolicy = SecPolicyCreateRevocation(kSecRevocationCRLMethod | kSecRevocationRequirePositiveResponse); } else { revocationPolicy = SecPolicyCreateRevocation(kSecRevocationCRLMethod); } if (revocationPolicy) { CFArrayAppendValue(mPolicies, revocationPolicy); OSStatus setPolicyStatus = SecTrustSetPolicies(peerTrust, mPolicies); if (setPolicyStatus != errSecSuccess) { string errMessage = StringUtils::CFStringRefToString(SecCopyErrorMessageString(setPolicyStatus, NULL)); logger->Error("Unable to add additional policies for TLS evaluation. Reason: %s", errMessage.c_str()); } } } if (revocationPolicy) { CFRelease(revocationPolicy); } if (sslPolicyProperties) { CFRelease(sslPolicyProperties); } if (mPolicies) { CFRelease(mPolicies); } } CFErrorRef trustError = NULL; if (SecTrustEvaluateWithError(peerTrust, &trustError)) { logger->Debug("TLS trust evaluation successful"); complete(true); } else { SecTrustResultType trustResult; SecTrustGetTrustResult(peerTrust, &trustResult); switch(trustResult) { case kSecTrustResultUnspecified: logger->Error("Trust evaluation result - kSecTrustResultUnspecified"); break; case kSecTrustResultProceed: logger->Error("Trust evaluation result - kSecTrustResultProceed"); break; case kSecTrustResultDeny: logger->Error("Trust evaluation result - kSecTrustResultDeny"); break; case kSecTrustResultRecoverableTrustFailure: logger->Error("Trust evaluation result - kSecTrustResultRecoverableTrustFailure"); break; case kSecTrustResultFatalTrustFailure: logger->Error("Trust evaluation result - kSecTrustResultFatalTrustFailure"); break; case kSecTrustResultOtherError: logger->Error("Trust evaluation result - kSecTrustResultOtherError"); break; case kSecTrustResultInvalid: logger->Error("Trust evaluation result - kSecTrustResultInvalid"); break; } CFDictionaryRef trust_results = NULL; trust_results = SecTrustCopyResult(peerTrust); CFBooleanRef revoChecked = (CFBooleanRef)CFDictionaryGetValue(trust_results, kSecTrustRevocationChecked); if(revoChecked == kCFBooleanTrue) { logger->Error("Revocation check returned TRUE"); } else { logger->Error("Revocation check returned FALSE"); } if(trust_results) { CFRelease(trust_results); } // Call to CFErrorCopyDescription can sometimes return the wrong error message strings. // Check the Security.framework error codes at Security.framework/Headers/SecBase.h CFIndex errCode = CFErrorGetCode(trustError); const string errMessage = StringUtils::CFStringRefToString(CFErrorCopyDescription(trustError)); logger->Error("TLS trust evaluation failed. Error: %ld '%s'", errCode, errMessage.c_str()); complete(false); } if (trustError) { CFRelease(trustError); } if (peerTrust) { CFRelease(peerTrust); } } }, m_connectionQueue);
May ’22