I'm writing iOS code to authenticate with an HTTPS proxy server, and need to use the Core Foundation APIs in order to construct the "Proxy-Authorization" header. I'm blocked trying to build a valid CFHTTPAuthenticationRef object, though.
Upon receiving the '407 Proxy authentication required' response with a 'Proxy-Authenticate: Basic realm="example.com"' header, I'm calling CFHTTPAuthenticationCreateFromResponse
. This returns a CFHTTPAuthenticationRef object that is invalid; the error is -1000, or kCFStreamErrorHTTPAuthenticationTypeUnsupported.
From looking at the very outdated source code available at http://www.opensource.apple.com/source/CFNetwork/CFNetwork-129.20/HTTP/CFHTTPAuthentication.c, this should all be working fine. I've verified in the debugger that some of the internal structures, like _schemes, are in fact being parsed properly, so I know my header value is correct. Clearly the code inside CFHTTPAuthenticationCreateFromResponse
has changed, but I'm at a loss as to what I need to do to make this work.
From stepping through the assembly code, it seems like it may be related to a lack of URL. There is no public API to add a URL to a response, and the fact it's not needed for Basic and Digest auth is odd.
I've observed this on OSX Mavericks, iOS 7 simulator, and iOS 8 simulator. I've boiled this down to a simple repro function, pasted below. I've also tried using a test proxy server and CFReadStream, creating the response from CFReadStreamCopyProperty(requestStream, kCFStreamPropertyHTTPResponseHeader)
, to no avail (which, presumably, would associate a URL with the response object).
void testCFAuthentication()
{
Boolean result;
CFStringRef str;
const char *rawResponse = "HTTP/1.1 407 Proxy authentication required\r\nProxy-Authenticate: Basic realm=\"example.com\"\r\n\r\n";
CFHTTPMessageRef responseMessage = NULL;
responseMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false);
// Optional hack, doesn't help
/*
CFURLRef url = NULL;
url = CFURLCreateWithString(NULL, CFSTR("https://example.com/"), NULL);
_CFHTTPMessageSetResponseURL(responseMessage, url);
NSLog(@"_CFHTTPMessageSetResponseURL called\n");
*/
result = CFHTTPMessageAppendBytes(responseMessage, (uint8_t *)rawResponse, strlen(rawResponse));
NSLog(@"CFHTTPMessageAppendBytes result: %d\n", (int)result);
NSLog(@"CFHTTPMessageIsHeaderComplete: %d\n", (int)CFHTTPMessageIsHeaderComplete(responseMessage));
NSLog(@"CFHTTPMessageGetResponseStatusCode: %d\n", (int)CFHTTPMessageGetResponseStatusCode(responseMessage));
NSLog(@"CFHTTPMessageCopyResponseStatusLine: %s\n", CFStringGetCStringPtr(CFHTTPMessageCopyResponseStatusLine(responseMessage), kCFStringEncodingUTF8));
NSLog(@"Proxy-Authenticate header value: %s\n", CFStringGetCStringPtr(CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Proxy-Authenticate")), kCFStringEncodingUTF8));
CFHTTPAuthenticationRef auth = NULL;
auth = CFHTTPAuthenticationCreateFromResponse(NULL, responseMessage);
NSLog(@"CFHTTPAuthenticationCreateFromResponse:\n"); CFShow(auth);
if (auth) {
CFStreamError error = { 0 };
result = CFHTTPAuthenticationIsValid(auth, &error);
if (!result) {
NSLog(@"CFHTTPAuthenticationIsValid: false, error %d (0x%x), domain %ld (0x%lx)\n", error.error, error.error, error.domain, error.domain);
} else {
NSLog(@"CFHTTPAuthenticationCopyMethod=%s\n", CFStringGetCStringPtr(CFHTTPAuthenticationCopyMethod(auth), kCFStringEncodingUTF8));
NSLog(@"authRealm=%s\n", CFStringGetCStringPtr(CFHTTPAuthenticationCopyRealm(auth), kCFStringEncodingUTF8));
}
};
}
Output:
2015-02-13 11:01:05.654 CFHTTPMessageAppendBytes result: 1
2015-02-13 11:01:05.655 CFHTTPMessageIsHeaderComplete: 1
2015-02-13 11:01:05.656 CFHTTPMessageGetResponseStatusCode: 407
2015-02-13 11:01:05.657 CFHTTPMessageCopyResponseStatusLine: HTTP/1.1 407 Proxy authentication required
2015-02-13 11:01:05.657 Proxy-Authenticate header value: Basic realm="example.com"
2015-02-13 11:01:05.658 CFHTTPAuthenticationCreateFromResponse:
<CFHTTPAuthentication 0x7fe0d36017f0>{state = Failed; scheme = <undecided>, forProxy = true}
2015-02-13 11:01:05.658 CFHTTPAuthenticationIsValid: false, error -1000 (0xfffffc18), domain 4 (0x4)