Review of Manual OCSP Requests for SecTrustEvaluate (iOS makes OCSP requests outside of NSURLProtocol)

Originator:m.kuratczyk
Number:rdar://716337334 Date Originated:July 9, 2019
Status:Open Resolved:
Product:iOS Product Version:iOS 8+
Classification:Bug Reproducible:Always
 
DESCRIPTION OF PROBLEM

OCSP requests do not follow NSURLProtocol subclassing. Therefore OCSP requests are not proxied when using an NSURLProtocol subclass to proxy all traffic.

Issue in more detail: https://github.com/OnionBrowser/OnionBrowser/issues/178.

Our current workaround is to inspect the X.509v3 certificate being authenticated for OCSP URLs and then make the requests ourselves; subsequently attaching the response with `SecTrustSetOCSPResponse()`. Reference: https://github.com/Psiphon-Labs/OCSPCache.

Our Solution
=========

In our implementation we do the following:
1. Check if there is a pinned OCSP response
- Call SecTrustEvaluate with revocation policy `SecPolicyCreateRevocation(kSecRevocationOCSPMethod|kSecRevocationRequirePositiveResponse|kSecRevocationNetworkAccessDisabled)`
- If `SecTrustEvaluate` succeeds, assume there was a pinned OCSP response

2. Make OCSP request manually through local proxy
- Inspect leaf X.509v3 certificate of serverTrust in `URLSession:task:didReceiveChallenge:completionHandler:` for OCSP URLs
- Make OCSP requests and attach OCSP response to trust
- Call `SecTrustEvaluate` with revocation policy `SecPolicyCreateRevocation(kSecRevocationOCSPMethod|kSecRevocationRequirePositiveResponse|kSecRevocationNetworkAccessDisabled)`

Note: Since we are caching OCSP responses, but do not know when iOS considers responses valid/invalid we only evict responses when `SecTrustEvaluate` fails with an OCSP response fetched out of band. If the response we attach was cached then we make another remote request for a new response. This can be seen here: https://github.com/Psiphon-Labs/OCSPCache/blob/b945a5784cd88ed5693a62a931617bd371f3c9a8/OCSPCache/Classes/OCSPAuthURLSessionDelegate.m#L196-L216.

3. Fallback on CRLs

Questions
=======

- Is the assumption above regarding pinned OCSP responses correct?

- Are the revocation checks in https://github.com/Psiphon-Labs/OCSPCache/blob/b945a5784cd88ed5693a62a931617bd371f3c9a8/OCSPCache/Classes/OCSPAuthURLSessionDelegate.m#L138 performed correctly?

- We are only attaching the OCSP response for the leaf certificate being authenticated. Will plaintext OCSP requests be made by the system for intermediate certificates?


STEPS TO REPRODUCE 

1. Create NSURLProtocol subclass which proxies all traffic through a SOCKS or HTTP proxy by setting connection proxy dictionary (https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1411499-connectionproxydictionary?language=objc)

2.
```
SecPolicyRef policy = SecPolicyCreateRevocation(kSecRevocationOCSPMethod);
SecTrustSetPolicies(trust, policy);

SecTrustResultType trustResultType;
SecTrustEvaluate(trust, &trustResultType); // triggers plaintext OCSP request outside of proxy
```

---

Note: There are tests in https://github.com/Psiphon-Labs/OCSPCache which shed some light on implementation details and expected behaviour.

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!