1/*
2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <SystemConfiguration/SystemConfiguration.h>
25#include <SystemConfiguration/SCSchemaDefinitions.h>
26#include <CoreServices/CoreServices.h>
27#include <CoreServices/CoreServicesPriv.h>
28#include <Security/Security.h>
29#include <netdb.h>
30#include <syslog.h>
31#include "webdavlib.h"
32
33#define STREAM_EVENT_BUFSIZE 4096
34
35// This limits how many times a request will be retried
36// due to receiving EAGAIN from the handleXXXError() routines.
37#define WEBDAVLIB_MAX_AGAIN_COUNT 10
38
39// This is what we send as the User Agent header
40#define WEBAVLIB_USER_AGENT_STRING "WebDAVLib/1.3"
41
42/* Macro to simplify common CFRelease usage */
43#define CFReleaseNull(obj) do { if(obj != NULL) { CFRelease(obj); obj = NULL; } } while (0)
44
45#define kSSLClientPropTLSServerCertificateChain CFSTR("TLSServerCertificateChain") /* array[data] */
46#define kSSLClientPropTLSTrustClientStatus	CFSTR("TLSTrustClientStatus") /* CFNumberRef of kCFNumberSInt32Type (errSSLxxxx) */
47#define kSSLClientPropTLSServerHostName	CFSTR("TLSServerHostName") /* CFString */
48#define CFENVFORMATSTRING "__CF_USER_TEXT_ENCODING=0x%X:0:0"
49
50#define PRIVATE_CERT_UI_COMMAND "/System/Library/Filesystems/webdav.fs/Support/webdav_cert_ui.app/Contents/MacOS/webdav_cert_ui"
51
52
53// Context for the callbacks
54enum CheckAuthCallbackStatus {CheckAuthInprogress = 0, CheckAuthCallbackDone = 1, CheckAuthCallbackStreamError = 2,
55								CheckAuthRedirection = 3};
56struct callback_ctx {
57	CFMutableDataRef		theData;		// buffer for the reply message
58	CFHTTPMessageRef		response;		// holds the response message
59	CFMutableDictionaryRef	sslPropDict;	// holds ssl properties for the stream
60	CFHTTPAuthenticationRef serverAuth;		// holds the authentication object for the server
61
62	uint32_t				againCount;		// Counts how many retries due to EAGAIN
63
64	boolean_t	triedServerCredentials;			// TRUE if we have tried server credentials
65	boolean_t	triedProxyServerCredentials;	// TRUE if we have tried proxy server credentials
66
67	// Dealing with sending credentials securely
68	boolean_t	requireSecureLogin;			// TRUE if credentials must be sent securely (i.e. forbids BASIC Auth without SSL)
69	boolean_t	secureConnection;			// TRUE if SSL connection
70
71	// Proxy
72	CFStringRef				proxyRealm;
73	boolean_t				httpProxyEnabled;	// true if an http proxy is configured (according to SCDynamicStore)
74	CFStringRef				httpProxyServer;	// name or address of the http proxy server
75	int						httpProxyPort;
76	boolean_t				httpsProxyEnabled;	// true if an secure proxy (https) is configured (according to SCDynamicStore)
77	CFStringRef				httpsProxyServer;	// name or address of the https proxy server
78	int						httpsProxyPort;
79	CFDictionaryRef			proxyDict;		// hold the proxy dictionary
80	SCDynamicStoreRef		proxyStore;		// dynamic store for proxy info
81	CFHTTPAuthenticationRef proxyAuth;		// holds the authentication object for the proxy server
82	CFIndex					statusCode;		//  only valid when status is CheckAuthCallbackDone
83	CFStreamError			streamError;	// only valid when status is CheckAuthCallbackStreamError
84	enum CheckAuthCallbackStatus status;
85};
86
87// function prototypes
88// enum WEBDAVLIBAuthStatus checkServerAuth(CFURLRef a_url);
89static void SecAddTrustedCerts(CFArrayRef certs, CFMutableDictionaryRef sslPropDict);
90static CFArrayRef SecCertificateArrayCreateCFDataArray(CFArrayRef certs);
91static CFDataRef SecCertificateCreateCFData(SecCertificateRef cert);
92static int ConfirmCertificate(CFReadStreamRef readStreamRef, SInt32 error, CFURLRef a_url, CFMutableDictionaryRef sslPropDict);
93static enum WEBDAVLIBAuthStatus finalStatusFromStatusCode(struct callback_ctx *ctx, int *error);
94static int handleStreamError(struct callback_ctx *ctx, boolean_t *tryAgain, CFReadStreamRef rdStream, CFURLRef a_url);
95static int handleSSLErrors(struct callback_ctx *ctx, boolean_t *tryAgain, CFReadStreamRef rdStream, CFURLRef a_url);
96static enum WEBDAVLIBAuthStatus sendOptionsRequest(CFURLRef a_url, struct callback_ctx *ctx, int *result);
97static enum WEBDAVLIBAuthStatus sendOptionsRequestAuthenticated(CFURLRef a_url, struct callback_ctx *ctx, CFDictionaryRef creds, int *result);
98static void applyCredentialsToRequest(struct callback_ctx *ctx, CFDictionaryRef creds, CFHTTPMessageRef request);
99static void checkServerAuth_handleStreamEvent(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);
100static int updateNetworkProxies(struct callback_ctx *ctx);
101static void releaseContextItems(struct callback_ctx *ctx);
102static void initContext(struct callback_ctx *ctx);
103
104// Globals
105static SCDynamicStoreRef gProxyStore;
106
107enum
108{
109	kHttpDefaultPort = 80,	// default port for HTTP
110	kHttpsDefaultPort = 443	// default port for HTTPS
111};
112
113/******************************************************************************/
114/*
115 * SecAddTrustedCerts
116 *
117 * Adds trusted SecCertificateRefs to our global SSL properties dictionary
118 */
119static void SecAddTrustedCerts(CFArrayRef certs, CFMutableDictionaryRef	sslPropDict)
120{
121	SecCertificateRef certRef;
122	const void *certPtr;
123	CFMutableArrayRef newCertArr, incomingCerts;
124	CFArrayRef existingCertArr;
125	CFIndex i, count;
126
127	require(certs != NULL, out);
128	require(sslPropDict != NULL, out);
129
130	incomingCerts = NULL;
131
132	// Make a mutable copy of incoming certs
133	incomingCerts = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certs);
134	require(incomingCerts != NULL, out);
135
136	// Any existing trusted certificates?
137	existingCertArr = CFDictionaryGetValue(sslPropDict, _kCFStreamSSLTrustedLeafCertificates);
138
139
140	if (existingCertArr == NULL) {
141		// Add our copy of incoming certs to the dictionary
142		CFDictionarySetValue(sslPropDict, _kCFStreamSSLTrustedLeafCertificates, incomingCerts);
143	}
144	else {
145		// Copy old certificates
146		newCertArr = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, existingCertArr);
147		require(newCertArr != NULL, MallocNewCerts);
148
149		// Remove old certificates
150		CFDictionaryRemoveValue(sslPropDict, _kCFStreamSSLTrustedLeafCertificates);
151
152		// Add any new certs
153		count = CFArrayGetCount(incomingCerts);
154
155		for (i = 0; i < count; ++i)
156		{
157			certPtr = CFArrayGetValueAtIndex(incomingCerts, i);
158			if (certPtr == NULL)
159				continue;
160
161			certRef = *((SecCertificateRef*)((void*)&certPtr));  /*ugly but it works*/
162
163			if (CFArrayContainsValue(newCertArr, CFRangeMake(0, CFArrayGetCount(newCertArr)), certRef) == false) {
164
165				// Don't have this cert yet, so add it
166				CFArrayAppendValue(newCertArr, certRef);
167			}
168		}
169
170		// Now set the new array
171		CFDictionarySetValue(sslPropDict, _kCFStreamSSLTrustedLeafCertificates, newCertArr);
172	}
173
174	if (incomingCerts != NULL) {
175		// Release our reference from the Copy
176		CFRelease(incomingCerts);
177	}
178out:
179MallocNewCerts:
180	return;
181}
182
183
184/******************************************************************************/
185
186/*
187 * SecCertificateCreateCFData
188 *
189 * Creates a CFDataRef from a SecCertificateRef.
190 */
191static CFDataRef SecCertificateCreateCFData(SecCertificateRef cert)
192{
193	CFDataRef data;
194
195	data = SecCertificateCopyData(cert);
196
197	if (data == NULL)
198		syslog(LOG_ERR, "%s : SecCertificateCopyData returned NULL\n", __FUNCTION__);
199
200	return (data);
201}
202
203/******************************************************************************/
204
205/*
206 * SecCertificateArrayCreateCFDataArray
207 *
208 * Convert a CFArray[SecCertificate] to CFArray[CFData].
209 */
210static CFArrayRef SecCertificateArrayCreateCFDataArray(CFArrayRef certs)
211{
212	CFMutableArrayRef array;
213	CFIndex count;
214	int i;
215	const void *certRef;
216
217	count = CFArrayGetCount(certs);
218	array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
219	require(array != NULL, CFArrayCreateMutable);
220
221	for (i = 0; i < count; ++i)
222	{
223		SecCertificateRef cert;
224		CFDataRef data;
225
226		certRef = CFArrayGetValueAtIndex(certs, i);
227		cert = *((SecCertificateRef*)((void*)&certRef)); /* ugly but it works */
228		require(cert != NULL, CFArrayGetValueAtIndex);
229
230		data = SecCertificateCreateCFData(cert);
231		require(data != NULL, SecCertificateCreateCFData);
232
233		CFArrayAppendValue(array, data);
234		CFRelease(data);
235	}
236
237	return (array);
238
239	/************/
240
241SecCertificateCreateCFData:
242CFArrayGetValueAtIndex:
243	CFRelease(array);
244CFArrayCreateMutable:
245
246	return (NULL);
247}
248
249/******************************************************************************/
250/* returns TRUE if user asked to continue with this certificate problem; FALSE if not */
251static int ConfirmCertificate(CFReadStreamRef readStreamRef, SInt32 error, CFURLRef a_url, CFMutableDictionaryRef sslPropDict)
252{
253	int result;
254	CFMutableDictionaryRef dict;
255	CFArrayRef certs;
256	CFArrayRef certs_data;
257	CFNumberRef error_number;
258	CFStringRef host_name;
259	CFDataRef theData;
260	int fd[2];
261	int pid, terminated_pid;
262	union wait status;
263	char CFUserTextEncodingEnvSetting[sizeof(CFENVFORMATSTRING) + 20];
264	char *env[] = {CFUserTextEncodingEnvSetting, "", (char *) 0 };
265
266	/*
267	 * Create a new environment with a definition of __CF_USER_TEXT_ENCODING to work
268	 * around CF's interest in the user's home directory (which could be networked,
269	 * causing recursive references through automount). Make sure we include the uid
270	 * since CF will check for this when deciding if to look in the home directory.
271	 */
272	snprintf(CFUserTextEncodingEnvSetting, sizeof(CFUserTextEncodingEnvSetting), CFENVFORMATSTRING, getuid());
273
274	certs = NULL;
275	result = FALSE;
276	fd[0] = fd[1] = -1;
277
278	/* create a dictionary to stuff things all in */
279	dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
280	require(dict != NULL, CFDictionaryCreateMutable);
281
282	/* get the certificates from the stream and add it with the kSSLClientPropTLSServerCertificateChain key */
283	certs = (CFArrayRef)CFReadStreamCopyProperty(readStreamRef, kCFStreamPropertySSLPeerCertificates);
284	require(certs != NULL, CFReadStreamCopyProperty);
285
286	certs_data = SecCertificateArrayCreateCFDataArray(certs);
287	require(certs_data != NULL, CFReadStreamCopyProperty);
288
289	CFDictionaryAddValue(dict, kSSLClientPropTLSServerCertificateChain, certs_data);
290	CFRelease(certs_data);
291
292	/* convert error to a CFNumberRef and add it with the kSSLClientPropTLSTrustClientStatus key */
293	error_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &error);
294	require(error_number != NULL, CFNumberCreate);
295
296	CFDictionaryAddValue(dict, kSSLClientPropTLSTrustClientStatus, error_number);
297	CFRelease(error_number);
298
299	/* get the host name from the base URL and add it with the kSSLClientPropTLSServerHostName key */
300	host_name = CFURLCopyHostName(a_url);
301	require(host_name != NULL, CFURLCopyHostName);
302
303	CFDictionaryAddValue(dict, kSSLClientPropTLSServerHostName, host_name);
304	CFRelease(host_name);
305
306	/* flatten it */
307	theData = CFPropertyListCreateXMLData(kCFAllocatorDefault, dict);
308	require(theData != NULL, CFPropertyListCreateXMLData);
309
310	CFRelease(dict);
311	dict = NULL;
312
313	/* open a pipe */
314	require(pipe(fd) >= 0, pipe);
315
316	pid = fork();
317	require (pid >= 0, fork);
318	if ( pid > 0 )
319	{
320		/* parent */
321		size_t length;
322		ssize_t bytes_written;
323
324		close(fd[0]); /* close read end */
325		fd[0] = -1;
326		length = CFDataGetLength(theData);
327		bytes_written = write(fd[1], CFDataGetBytePtr(theData), length);
328		require(bytes_written == (ssize_t)length, write);
329
330		close(fd[1]); /* close write end */
331		fd[1] = -1;
332
333		/* Parent waits for child's completion here */
334		while ( (terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0 )
335		{
336			/* retry if EINTR, else break out with error */
337			if ( errno != EINTR )
338			{
339				break;
340			}
341		}
342
343		/* we'll get here when the child completes */
344		if ( (terminated_pid == pid) && (WIFEXITED(status)) )
345		{
346			result = WEXITSTATUS(status) == 0;
347		}
348		else
349		{
350			result = FALSE;
351		}
352
353		// Did the user confirm the certificate?
354		if (result == TRUE) {
355			// Yes, add them to the global SSL properties dictionary
356			SecAddTrustedCerts(certs, sslPropDict);
357		}
358	}
359	else
360	{
361		/* child */
362		close(fd[1]); /* close write end */
363		fd[1] = -1;
364
365		if ( fd[0] != STDIN_FILENO )
366		{
367			require(dup2(fd[0], STDIN_FILENO) == STDIN_FILENO, dup2);
368			close(fd[0]); /* not needed after dup2 */
369			fd[0] = -1;
370		}
371
372		require(execle(PRIVATE_CERT_UI_COMMAND, PRIVATE_CERT_UI_COMMAND, (char *) 0, env) >= 0, execl);
373	}
374
375	return ( result );
376
377	/************/
378
379execl:
380dup2:
381write:
382fork:
383	if (fd[0] != -1)
384	{
385		close(fd[0]);
386	}
387	if (fd[1] != -1)
388	{
389		close(fd[1]);
390	}
391pipe:
392CFPropertyListCreateXMLData:
393CFURLCopyHostName:
394CFNumberCreate:
395	if ( certs != NULL )
396	{
397		CFRelease(certs);
398	}
399CFReadStreamCopyProperty:
400	if ( dict != NULL )
401	{
402		CFRelease(dict);
403	}
404CFDictionaryCreateMutable:
405
406	return ( FALSE );
407}
408/******************************************************************************/
409enum WEBDAVLIBAuthStatus
410queryForProxy(CFURLRef a_url, CFMutableDictionaryRef proxyInfo, int *error)
411{
412	enum WEBDAVLIBAuthStatus finalStatus;
413	int result;
414	struct callback_ctx ctx;
415	CFStringRef cf_port;
416
417	initContext(&ctx);
418
419	finalStatus = sendOptionsRequest(a_url, &ctx, &result);
420	*error = result;
421
422	switch (finalStatus) {
423		case WEBDAVLIB_Success:
424			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_Success", __FUNCTION__);
425			break;
426		case WEBDAVLIB_ProxyAuth:
427			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_ProxyAuth", __FUNCTION__);
428			if(ctx.httpProxyEnabled == TRUE) {
429				// Return http proxy server info in proxyInfo dictionary
430				CFDictionarySetValue(proxyInfo, kWebDAVLibProxySchemeKey, CFSTR("http"));
431				CFDictionarySetValue(proxyInfo, kWebDAVLibProxyServerNameKey, ctx.httpProxyServer);
432
433				if (ctx.proxyRealm != NULL)
434					CFDictionarySetValue(proxyInfo, kWebDAVLibProxyRealmKey, ctx.proxyRealm);
435
436				cf_port = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), ctx.httpProxyPort);
437
438				if (cf_port != NULL) {
439					CFDictionarySetValue(proxyInfo, kWebDAVLibProxyPortKey, cf_port);
440					CFRelease(cf_port);
441				}
442			}
443			else {
444				// Return https proxy server info in proxyInfo dictionary
445				CFDictionarySetValue(proxyInfo, kWebDAVLibProxySchemeKey, CFSTR("https"));
446				CFDictionarySetValue(proxyInfo, kWebDAVLibProxyServerNameKey, ctx.httpsProxyServer);
447
448				if (ctx.proxyRealm != NULL)
449					CFDictionarySetValue(proxyInfo, kWebDAVLibProxyRealmKey, ctx.proxyRealm);
450
451				cf_port = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), ctx.httpsProxyPort);
452				if (cf_port != NULL) {
453					CFDictionarySetValue(proxyInfo, kWebDAVLibProxyPortKey, cf_port);
454					CFRelease(cf_port);
455				}
456			}
457			break;
458		case WEBDAVLIB_ServerAuth:
459			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_ServerAuth", __FUNCTION__);
460			break;
461		case WEBDAVLIB_UnexpectedStatus:
462			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_UnexpectedStatus, errno: %d", __FUNCTION__, result);
463			break;
464		case WEBDAVLIB_IOError:
465			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_IOError, errno: %d", __FUNCTION__, result);
466			break;
467	}
468
469	// Release context items
470	releaseContextItems(&ctx);
471
472	return (finalStatus);
473}
474
475enum WEBDAVLIBAuthStatus
476connectToServer(CFURLRef a_url, CFDictionaryRef creds, boolean_t requireSecureLogin, int *error)
477{
478	enum WEBDAVLIBAuthStatus finalStatus;
479	int result;
480	struct callback_ctx ctx;
481	CFStringRef cf_port;
482
483	initContext(&ctx);
484
485	// remember if caller wants credentials to be sent securely
486	ctx.requireSecureLogin = requireSecureLogin;
487
488	finalStatus = sendOptionsRequestAuthenticated(a_url, &ctx, creds, &result);
489	*error = result;
490
491	switch (finalStatus) {
492		case WEBDAVLIB_Success:
493			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_Success", __FUNCTION__);
494			break;
495		case WEBDAVLIB_ProxyAuth:
496			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_ProxyAuth", __FUNCTION__);
497			break;
498		case WEBDAVLIB_ServerAuth:
499			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_ServerAuth", __FUNCTION__);
500			break;
501		case WEBDAVLIB_UnexpectedStatus:
502			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_UnexpectedStatus, errno %d", __FUNCTION__, result);
503			break;
504		case WEBDAVLIB_IOError:
505			syslog(LOG_DEBUG, "%s: Returning WEBDAVLIB_IOError, errno %d", __FUNCTION__, result);
506			break;
507	}
508
509	// Release context items
510	releaseContextItems(&ctx);
511
512	return (finalStatus);
513}
514
515
516static enum WEBDAVLIBAuthStatus
517sendOptionsRequest(CFURLRef a_url, struct callback_ctx *ctx, int *err)
518{
519	CFHTTPMessageRef message;
520	CFReadStreamRef rdStream;
521	CFURLRef myURL;
522	CFStringRef urlStr;
523	boolean_t done, tryAgain;
524	enum WEBDAVLIBAuthStatus finalStatus;
525
526	*err = 0;
527
528	// initialize the context struct
529	ctx->status = CheckAuthInprogress;
530	ctx->theData = CFDataCreateMutable(NULL, 0);
531	ctx->sslPropDict = NULL;
532	urlStr = NULL;
533	myURL = CFRetain(a_url);
534
535	CFStreamClientContext context = {0, ctx, NULL, NULL, NULL};
536
537	// update proxy information
538	updateNetworkProxies(ctx);
539
540	done = FALSE;
541	while (done == FALSE) {
542		// create a CFHTTP message object
543		message = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("OPTIONS"), myURL, kCFHTTPVersion1_1);
544		CFHTTPMessageSetHeaderFieldValue(message, CFSTR("User-Agent"), CFSTR(WEBAVLIB_USER_AGENT_STRING));
545		CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Accept"), CFSTR("*/*"));
546
547		rdStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, message);
548		CFRelease(message);
549		message = NULL;
550
551		ctx->status = CheckAuthInprogress;
552
553		// apply http/https proxy properties
554		if (ctx->sslPropDict != NULL)
555			CFReadStreamSetProperty(rdStream, kCFStreamPropertyHTTPProxy, ctx->sslPropDict);
556
557		// apply SSL properties
558		if (ctx->sslPropDict != NULL)
559			CFReadStreamSetProperty(rdStream, kCFStreamPropertySSLSettings, ctx->sslPropDict);
560
561		// Set up the callback and schedule
562		CFReadStreamSetClient(rdStream,
563							  kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred,
564							  checkServerAuth_handleStreamEvent,
565							  &context);
566		CFReadStreamScheduleWithRunLoop(rdStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
567
568		// Open the stream and run the runloop
569		CFReadStreamOpen(rdStream);
570
571		while (ctx->status == CheckAuthInprogress) {
572			CFRunLoopRunInMode(kCFRunLoopDefaultMode, 20, TRUE);
573		}
574
575		if (ctx->status == CheckAuthCallbackDone) {
576			// We received an http status code
577			finalStatus = finalStatusFromStatusCode(ctx, err);
578
579			if (finalStatus == WEBDAVLIB_ProxyAuth) {
580				// create an authentication object so we can fetch the realm
581				ctx->proxyAuth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, ctx->response);
582				ctx->proxyRealm = CFHTTPAuthenticationCopyRealm(ctx->proxyAuth);
583			}
584
585			done = TRUE;
586		}
587		else if (ctx->status == CheckAuthRedirection) {
588			// Handle 3xx redirection
589			if(++ctx->againCount > WEBDAVLIB_MAX_AGAIN_COUNT)
590			{
591				// too many redirects
592				*err = EIO;
593				finalStatus = WEBDAVLIB_IOError;
594				done = TRUE;
595			}
596			else {
597				urlStr = CFHTTPMessageCopyHeaderFieldValue(ctx->response, CFSTR("Location"));
598				if (urlStr == NULL) {
599					*err = EIO;
600					finalStatus = WEBDAVLIB_IOError;
601					done = TRUE;
602				}
603				else {
604					myURL = CFURLCreateWithString(kCFAllocatorDefault, urlStr, NULL);
605					CFRelease(urlStr);
606					urlStr = NULL;
607
608					if (myURL == NULL) {
609						*err = EIO;
610						finalStatus = WEBDAVLIB_IOError;
611						done = TRUE;
612					}
613
614					syslog(LOG_DEBUG, "%s: Handling a redirection", __FUNCTION__);
615				}
616			}
617		}
618		else if (ctx->status == CheckAuthCallbackStreamError) {
619			*err = handleStreamError(ctx, &tryAgain, rdStream, a_url);
620
621			if ((tryAgain == FALSE) || (++ctx->againCount > WEBDAVLIB_MAX_AGAIN_COUNT)) {
622					finalStatus = WEBDAVLIB_IOError;
623				done = TRUE;
624			}
625		}
626
627		// Unschedule the callback and close the read stream
628		CFReadStreamSetClient(rdStream, kCFStreamEventNone, NULL, NULL);
629		CFReadStreamUnscheduleFromRunLoop(rdStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
630		CFReadStreamClose(rdStream);
631		CFRelease(rdStream);
632		rdStream = NULL;
633
634		CFRelease(ctx->theData);
635		ctx->theData = CFDataCreateMutable(NULL, 0);
636	}
637
638	if (myURL != NULL)
639		CFRelease(myURL);
640
641	return (finalStatus);
642}
643
644static enum WEBDAVLIBAuthStatus
645sendOptionsRequestAuthenticated(CFURLRef a_url, struct callback_ctx *ctx, CFDictionaryRef creds, int *err)
646{
647	CFHTTPMessageRef message;
648	CFReadStreamRef rdStream;
649	CFURLRef myURL;
650	CFStringRef urlStr, method;
651	boolean_t done, tryAgain;
652
653	enum WEBDAVLIBAuthStatus finalStatus;
654
655	*err = 0;
656	urlStr = NULL;
657	myURL = CFRetain(a_url);
658
659	// initialize the context struct
660	ctx->status = CheckAuthInprogress;
661	ctx->theData = CFDataCreateMutable(NULL, 0);
662	ctx->sslPropDict = NULL;
663
664	CFStreamClientContext context = {0, ctx, NULL, NULL, NULL};
665
666	// update proxy information
667	updateNetworkProxies(ctx);
668
669	done = FALSE;
670	while (done == FALSE) {
671		// create a CFHTTP message object
672		message = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("OPTIONS"), myURL, kCFHTTPVersion1_1);
673		CFHTTPMessageSetHeaderFieldValue(message, CFSTR("User-Agent"), CFSTR(WEBAVLIB_USER_AGENT_STRING));
674		CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Accept"), CFSTR("*/*"));
675
676		// Apply authentication objects to the request
677		applyCredentialsToRequest(ctx, creds, message);
678
679		rdStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, message);
680		CFRelease(message);
681		message = NULL;
682
683		ctx->status = CheckAuthInprogress;
684
685		// apply http/https proxy properties
686		if (ctx->sslPropDict != NULL)
687			CFReadStreamSetProperty(rdStream, kCFStreamPropertyHTTPProxy, ctx->sslPropDict);
688
689		// apply SSL properties
690		if (ctx->sslPropDict != NULL)
691			CFReadStreamSetProperty(rdStream, kCFStreamPropertySSLSettings, ctx->sslPropDict);
692
693		// Set up the callback and schedule
694		CFReadStreamSetClient(rdStream,
695								kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred,
696								checkServerAuth_handleStreamEvent,
697								&context);
698		CFReadStreamScheduleWithRunLoop(rdStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
699
700		// Open the stream and run the runloop
701		CFReadStreamOpen(rdStream);
702
703		while (ctx->status == CheckAuthInprogress) {
704			CFRunLoopRunInMode(kCFRunLoopDefaultMode, 20, TRUE);
705		}
706
707		if (ctx->status == CheckAuthCallbackDone) {
708			// We received an http status code
709			finalStatus = finalStatusFromStatusCode(ctx, err);
710
711			if (finalStatus == WEBDAVLIB_ServerAuth) {
712
713				// do we have an authentication object?
714				if (ctx->serverAuth == NULL) {
715					// create a authentication object for server credentials on the next loop
716					ctx->serverAuth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, ctx->response);
717
718					if (ctx->serverAuth != NULL) {
719						if (CFHTTPAuthenticationIsValid(ctx->serverAuth, NULL) == FALSE) {
720							// game over
721							syslog(LOG_DEBUG, "%s: Server CFHTTPAuthenticationIsValid is FALSE", __FUNCTION__);
722							*err = EIO;
723							done = TRUE;
724						}
725
726						// Do we need credentials at this point?
727						if ( (done == FALSE) && (CFHTTPAuthenticationRequiresUserNameAndPassword(ctx->serverAuth) == TRUE) ) {
728							// Were we given server credentials?
729							if (CFDictionaryContainsKey(creds, kWebDAVLibUserNameKey) == FALSE) {
730								// No server credentials, so just return WEBDAVLIB_ServerAuth
731								syslog(LOG_DEBUG, "%s: No server credentials in dictionary", __FUNCTION__);
732								done = TRUE;
733							}
734
735							// Check if credentials must be sent securely
736							if ( (done == FALSE) && (ctx->requireSecureLogin == TRUE) && (ctx->secureConnection == FALSE) ) {
737								method = CFHTTPAuthenticationCopyMethod(ctx->serverAuth);
738								if ( method != NULL ) {
739									if (CFEqual(method, CFSTR("Basic")) == TRUE) {
740										// game over
741										syslog(LOG_ERR, "%s: Authentication (Basic) too weak", __FUNCTION__);
742										done = TRUE;
743										*err = EAUTH;
744									}
745									CFRelease(method);
746								}
747							}
748
749						}
750					}
751					else {
752						// game over
753						syslog(LOG_DEBUG, "%s: Initial Server CFHTTPAuthenticationCreateFromResponse returned NULL", __FUNCTION__);
754						done = TRUE;
755						*err = EIO;
756					}
757				}
758				else {
759					// We have an auth abject for the server, we need to update it and use it
760					if (CFHTTPAuthenticationIsValid(ctx->serverAuth, NULL) == FALSE) {
761						CFRelease(ctx->serverAuth);
762						ctx->serverAuth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, ctx->response);
763
764						if (ctx->serverAuth != NULL) {
765							if (CFHTTPAuthenticationIsValid(ctx->serverAuth, NULL) == FALSE) {
766								// game over
767								syslog(LOG_DEBUG, "%s: Server CFHTTPAuthenticationIsValid is FALSE", __FUNCTION__);
768								*err = EIO;
769								done = TRUE;
770							}
771
772							// Do we need server credentials at this point?
773							if ( (done == FALSE) && (CFHTTPAuthenticationRequiresUserNameAndPassword(ctx->serverAuth) == TRUE) ) {
774								// Were we given server credentials?
775								if (CFDictionaryContainsKey(creds, kWebDAVLibUserNameKey) == FALSE) {
776									// No server credentials, so just return WEBDAVLIB_ServerAuth
777									syslog(LOG_DEBUG, "%s: No server credentials in dictionary", __FUNCTION__);
778									done = TRUE;
779								}
780
781								// Have we already tried server credentials?
782								if ( (done == FALSE) && (ctx->triedServerCredentials == TRUE) ) {
783									// The server credentials were rejected, just return WEBDAVLIB_ServerAuth
784									syslog(LOG_DEBUG, "%s: Server credentials were not accepted", __FUNCTION__);
785									done = TRUE;
786								}
787
788								// Check if credentials must be sent securely
789								if ( (done == FALSE) && (ctx->requireSecureLogin == TRUE) && (ctx->secureConnection == FALSE)) {
790									method = CFHTTPAuthenticationCopyMethod(ctx->serverAuth);
791									if ( method != NULL ) {
792										if (CFEqual(method, CFSTR("Basic")) == TRUE) {
793											// game over
794											syslog(LOG_ERR, "%s: Authentication (Basic) too weak", __FUNCTION__);
795											done = TRUE;
796											*err = EAUTH;
797										}
798										CFRelease(method);
799									}
800								}
801							}
802						}
803						else {
804							// game over
805							syslog(LOG_DEBUG, "%s: Server CFHTTPAuthenticationCreateFromResponse returned NULL", __FUNCTION__);
806							*err = EIO;
807							done = TRUE;
808						}
809					}
810				}
811			}
812			else if (finalStatus == WEBDAVLIB_ProxyAuth) {
813				// do we have an authentication object?
814				if (ctx->proxyAuth == NULL) {
815					if (CFDictionaryContainsKey(creds, kWebDAVLibProxyUserNameKey) == FALSE) {
816						// No proxy server creds, so just return WEBAVLIB_ProxyAuth
817						syslog(LOG_DEBUG, "%s: No proxy server credentials in dictionary", __FUNCTION__);
818						done = TRUE;
819						continue;
820					}
821
822					// create an authentication object for proxy server credentials on the next loop
823					ctx->proxyAuth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, ctx->response);
824
825					if (ctx->proxyAuth != NULL) {
826						if (CFHTTPAuthenticationIsValid(ctx->proxyAuth, NULL) == FALSE) {
827							// game over
828							syslog(LOG_DEBUG, "%s: Proxy CFHTTPAuthenticationIsValid is FALSE", __FUNCTION__);
829							*err = EIO;
830							done = TRUE;
831						}
832
833						// Do we need proxy server credentials at this point?
834						if ( (done == FALSE) && (CFHTTPAuthenticationRequiresUserNameAndPassword(ctx->proxyAuth) == TRUE) ) {
835							// Were we given proxy server credentials?
836							if (CFDictionaryContainsKey(creds, kWebDAVLibProxyUserNameKey) == FALSE) {
837								// No proxyserver credentials, so just return WEBDAVLIB_ProxyAuth
838								syslog(LOG_DEBUG, "%s: No proxy server credentials in dictionary", __FUNCTION__);
839								done = TRUE;
840							}
841
842							// Check if credentials must be sent securely
843							if ( (done == FALSE) && (ctx->requireSecureLogin == TRUE) && (ctx->secureConnection == FALSE)) {
844								method = CFHTTPAuthenticationCopyMethod(ctx->proxyAuth);
845								if ( method != NULL ) {
846									if (CFEqual(method, CFSTR("Basic")) == TRUE) {
847										// game over
848										syslog(LOG_ERR, "%s: Proxy Server authentication (Basic) too weak", __FUNCTION__);
849										done = TRUE;
850										*err = EAUTH;
851									}
852									CFRelease(method);
853								}
854							}
855						}
856					}
857					else {
858						// game over
859						syslog(LOG_DEBUG, "%s: Server CFHTTPAuthenticationCreateFromResponse returned NULL", __FUNCTION__);
860						*err = EIO;
861						done = TRUE;
862					}
863				}
864				else {
865					// We have an auth abject for the proxy server, we need to update it and use it
866					if (CFHTTPAuthenticationIsValid(ctx->proxyAuth, NULL) == FALSE) {
867						CFRelease(ctx->proxyAuth);
868						ctx->proxyAuth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, ctx->response);
869
870						if (ctx->proxyAuth != NULL) {
871							if (CFHTTPAuthenticationIsValid(ctx->proxyAuth, NULL) == FALSE) {
872								// game over
873								syslog(LOG_DEBUG, "%s: Proxy server CFHTTPAuthenticationIsValid is FALSE", __FUNCTION__);
874								*err = EIO;
875								done = TRUE;
876							}
877
878							// Do we need proxy server credentials at this point?
879							if ((done == FALSE) && (CFHTTPAuthenticationRequiresUserNameAndPassword(ctx->proxyAuth) == TRUE) ) {
880								// Were we given proxy server credentials?
881								if (CFDictionaryContainsKey(creds, kWebDAVLibProxyUserNameKey) == FALSE) {
882									// No proxyserver credentials, so just return WEBDAVLIB_ProxyAuth
883									syslog(LOG_DEBUG, "%s: No proxy server creds in dictionary", __FUNCTION__);
884									done = TRUE;
885								}
886
887								// Have we already tried proxy server credentials?
888								if ((done == FALSE) && (ctx->triedProxyServerCredentials == TRUE) ) {
889									// The proxy server credentials were rejected, just return WEBDAVLIB_ProxyAuth
890									syslog(LOG_DEBUG, "%s: Proxy server credentials were not accepted", __FUNCTION__);
891									done = TRUE;
892								}
893								// Check if credentials must be sent securely
894								if ( (done == FALSE) && (ctx->requireSecureLogin == TRUE) && (ctx->secureConnection == FALSE)) {
895									method = CFHTTPAuthenticationCopyMethod(ctx->proxyAuth);
896									if ( method != NULL ) {
897										if (CFEqual(method, CFSTR("Basic")) == TRUE) {
898											// game over
899											syslog(LOG_ERR, "%s: Proxy Server authentication (Basic) too weak", __FUNCTION__);
900											done = TRUE;
901											*err = EAUTH;
902										}
903										CFRelease(method);
904									}
905								}
906							}
907						}
908						else {
909							// game over
910							syslog(LOG_DEBUG, "%s: Proxy server CFHTTPAuthenticationCreateFromResponse returned NULL", __FUNCTION__);
911							*err = EIO;
912							done = TRUE;
913						}
914					}
915
916				}
917			}
918			else {
919				done = TRUE;
920			}
921		}
922		else if (ctx->status == CheckAuthRedirection) {
923			// Handle 3xx redirection
924			if(++ctx->againCount > WEBDAVLIB_MAX_AGAIN_COUNT)
925			{
926				// too many redirects
927				*err = EIO;
928				finalStatus = WEBDAVLIB_IOError;
929				done = TRUE;
930			}
931			else {
932				urlStr = CFHTTPMessageCopyHeaderFieldValue(ctx->response, CFSTR("Location"));
933				if (urlStr == NULL) {
934					*err = EIO;
935					finalStatus = WEBDAVLIB_IOError;
936					done = TRUE;
937				}
938				else {
939					myURL = CFURLCreateWithString(kCFAllocatorDefault, urlStr, NULL);
940					CFRelease(urlStr);
941					urlStr = NULL;
942
943					if (myURL == NULL) {
944						*err = EIO;
945						finalStatus = WEBDAVLIB_IOError;
946						done = TRUE;
947					}
948
949					syslog(LOG_DEBUG, "%s: Handling a redirection", __FUNCTION__);
950				}
951			}
952		}
953		else if (ctx->status == CheckAuthCallbackStreamError) {
954			*err = handleStreamError(ctx, &tryAgain, rdStream, a_url);
955
956			if ((tryAgain == FALSE) || (++ctx->againCount > WEBDAVLIB_MAX_AGAIN_COUNT)) {
957				finalStatus = WEBDAVLIB_IOError;
958				done = TRUE;
959			}
960		}
961
962		// Unschedule the callback and close the read stream
963		CFReadStreamSetClient(rdStream, kCFStreamEventNone, NULL, NULL);
964		CFReadStreamUnscheduleFromRunLoop(rdStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
965		CFReadStreamClose(rdStream);
966		CFRelease(rdStream);
967		rdStream = NULL;
968
969		CFRelease(ctx->theData);
970		ctx->theData = CFDataCreateMutable(NULL, 0);
971	}
972
973	if (myURL != NULL)
974		CFRelease(myURL);
975
976	return (finalStatus);
977}
978
979static void applyCredentialsToRequest(struct callback_ctx *ctx, CFDictionaryRef creds, CFHTTPMessageRef request)
980{
981	CFStringRef user, password;
982
983	// Check for server credentials
984	if (ctx->serverAuth != NULL) {
985		user = CFDictionaryGetValue(creds, kWebDAVLibUserNameKey);
986		password = CFDictionaryGetValue(creds, kWebDAVLibPasswordKey);
987
988		// Note: To support NTLM, we need to obtain the NT Domain from the user.
989		CFHTTPMessageApplyCredentials(request, ctx->serverAuth, user, password,  NULL);
990		ctx->triedServerCredentials = TRUE;
991	}
992
993	// Check for proxy server credentials
994	if (ctx->proxyAuth != NULL) {
995		user = CFDictionaryGetValue(creds, kWebDAVLibProxyUserNameKey);
996		password = CFDictionaryGetValue(creds, kWebDAVLibProxyPasswordKey);
997
998		// Note: To support NTLM, we need to obtain the NT Domain from the user.
999		CFHTTPMessageApplyCredentials(request, ctx->proxyAuth, user, password,  NULL);
1000		ctx->triedProxyServerCredentials = TRUE;
1001	}
1002}
1003
1004static enum WEBDAVLIBAuthStatus
1005finalStatusFromStatusCode(struct callback_ctx *ctx, int *error)
1006{
1007	enum WEBDAVLIBAuthStatus finalStatus;
1008	*error = 0;
1009
1010	// Remember our main goal is to determine whether or not
1011	// there exists an http/https proxy server to deal with.
1012	// Since we only send an OPTIONS request, many http status codes
1013	// may not make sense (such as 423 Lock Failed).
1014	switch (ctx->statusCode / 100) {
1015		case 2:		// 2xx Successfull
1016			finalStatus = WEBDAVLIB_Success;
1017			break;
1018		case 4:
1019			if (ctx->statusCode == 401) {
1020				finalStatus = WEBDAVLIB_ServerAuth;
1021				*error = EAUTH;
1022			}
1023			else if (ctx->statusCode == 407) {
1024				finalStatus = WEBDAVLIB_ProxyAuth;
1025				*error = EAUTH;
1026			}
1027			else {
1028				syslog(LOG_ERR, "%s: unexpected http status code %ld\n", __FUNCTION__, ctx->statusCode);
1029				*error = EIO;
1030				finalStatus = WEBDAVLIB_UnexpectedStatus;
1031			}
1032			break;
1033		case 1:		// Informational 1xx
1034		case 3:		// Redirection   3xx
1035		case 5:		// Server error  5xx
1036		default:
1037			syslog(LOG_ERR, "%s: unexpected http status code %ld\n", __FUNCTION__, ctx->statusCode);
1038			finalStatus = WEBDAVLIB_UnexpectedStatus;
1039			*error = EIO;
1040			break;
1041	}
1042	return (finalStatus);
1043}
1044
1045static int
1046handleStreamError(struct callback_ctx *ctx, boolean_t *tryAgain, CFReadStreamRef rdStream,CFURLRef a_url)
1047{
1048	int result = EIO;
1049
1050	// we only retry under certain error conditions
1051	*tryAgain = FALSE;
1052
1053	if (ctx->streamError.domain == kCFStreamErrorDomainSSL) {
1054
1055		result = handleSSLErrors(ctx, tryAgain, rdStream, a_url);
1056		if(result == ECANCELED) {
1057			*tryAgain = FALSE;
1058		}
1059	}
1060	else if (ctx->streamError.domain == kCFStreamErrorDomainPOSIX) {
1061		result = ctx->streamError.error;
1062
1063		if (result == EPIPE) {
1064				// busy server, just try again
1065			syslog(LOG_DEBUG, "%s: retrying stream error domain: posix error: EPIPE\n", __FUNCTION__);
1066				*tryAgain = TRUE;
1067		}
1068		else
1069			syslog(LOG_ERR, "%s: stream error domain: posix error: %d\n", __FUNCTION__, (int)ctx->streamError.error);
1070	}
1071	else if (ctx->streamError.domain == kCFStreamErrorDomainHTTP) {
1072		if (ctx->streamError.error == kCFStreamErrorHTTPConnectionLost) {
1073			// connection was dropped, we can try again
1074			syslog(LOG_DEBUG, "%s: retrying, stream error domain: http error: kCFStreamErrorHTTPConnectionLost", __FUNCTION__);
1075			*tryAgain = TRUE;
1076			result = ECONNRESET;
1077		}
1078		else {
1079			syslog(LOG_ERR, "%s: stream error domain: http error: %d", __FUNCTION__, (int)ctx->streamError.error);
1080			result = EIO;
1081		}
1082	}
1083	else if (ctx->streamError.domain == kCFStreamErrorDomainNetDB) {
1084		switch (ctx->streamError.error) {
1085		case EAI_NODATA:
1086			// no address associated with host name
1087			// the network interface was changed
1088			// okay to try again
1089			syslog(LOG_DEBUG, "%s: retrying, stream error domain: netdb error: EAI_NODATA", __FUNCTION__);
1090			*tryAgain = TRUE;
1091			result = EADDRNOTAVAIL;
1092			break;
1093		default:
1094				syslog(LOG_ERR, "%s: stream error domain: netdb error: %d\n", __FUNCTION__, (int)ctx->streamError.error);
1095			result = EIO;
1096			break;
1097		}
1098	}
1099	else {
1100		syslog(LOG_ERR, "%s: stream error domain: %ld error: %d\n", __FUNCTION__, ctx->streamError.domain, (int)ctx->streamError.error);
1101		result = EIO;
1102	}
1103	return (result);
1104}
1105
1106static int
1107handleSSLErrors(struct callback_ctx *ctx, boolean_t *tryAgain, CFReadStreamRef readStreamRef, CFURLRef a_url)
1108{
1109	SInt32 error;
1110	int result;
1111
1112	// no good errno to indicate ssl errors, so we just use EIO
1113	result = EIO;
1114
1115	// in most cases we try again
1116	*tryAgain = TRUE;
1117
1118	// indicate SSL connection
1119	ctx->secureConnection = TRUE;
1120
1121	error = ctx->streamError.error;
1122
1123	syslog(LOG_DEBUG, "%s: stream error domain: ssl error: %d", __FUNCTION__, (int)ctx->streamError.error);
1124
1125	if (ctx->sslPropDict == NULL)
1126		ctx->sslPropDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
1127													 0,
1128													 &kCFTypeDictionaryKeyCallBacks,
1129													 &kCFTypeDictionaryValueCallBacks);
1130	if (ctx->sslPropDict == NULL) {
1131		syslog(LOG_ERR, "%s: no memory for sslPropDictionary", __FUNCTION__);
1132		return ENOMEM;
1133	}
1134
1135	if ( (CFDictionaryGetValue(ctx->sslPropDict, kCFStreamSSLLevel) == NULL) &&
1136		(((error <= errSSLProtocol) && (error > errSSLXCertChainInvalid)) ||
1137		 ((error <= errSSLCrypto) && (error > errSSLUnknownRootCert)) ||
1138		 ((error <= errSSLClosedNoNotify) && (error > errSSLPeerBadCert)) ||
1139		 (error == errSSLIllegalParam) ||
1140		 ((error <= errSSLPeerAccessDenied) && (error > errSSLLast))) )
1141	{
1142		// retry with fall back from TLS to SSL */
1143		CFDictionarySetValue(ctx->sslPropDict, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelSSLv3);
1144		return (result);
1145	}
1146
1147	switch ( ctx->streamError.error )
1148	{
1149		case errSSLCertExpired:
1150		case errSSLCertNotYetValid:
1151			/* The certificate for this server has expired or is not yet valid */
1152			if ( ConfirmCertificate(readStreamRef, error, a_url, ctx->sslPropDict) )
1153			{
1154				result = EAGAIN;
1155			}
1156			else
1157			{
1158				result = ECANCELED;
1159			}
1160			break;
1161
1162		case errSSLBadCert:
1163		case errSSLXCertChainInvalid:
1164		case errSSLHostNameMismatch:
1165			if ( ConfirmCertificate(readStreamRef, error, a_url, ctx->sslPropDict) )
1166			{
1167				result = EAGAIN;
1168			}
1169			else
1170			{
1171				result = ECANCELED;
1172			}
1173			break;
1174
1175		case errSSLUnknownRootCert:
1176		case errSSLNoRootCert:
1177			if ( ConfirmCertificate(readStreamRef, error, a_url, ctx->sslPropDict) )
1178			{
1179				result = EAGAIN;
1180			}
1181			else
1182			{
1183				result = ECANCELED;
1184			}
1185			break;
1186
1187		default:
1188			syslog(LOG_ERR, "%s: stream error domain: ssl error: %d", __FUNCTION__, (int)ctx->streamError.error);
1189			// no sense in retrying
1190			*tryAgain = TRUE;
1191			break;
1192	}
1193
1194	return (result);
1195}
1196
1197static void checkServerAuth_handleStreamEvent(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo)
1198{
1199	struct callback_ctx *ctx;
1200	CFTypeRef theResponsePropertyRef;
1201	CFIndex bytesRead;
1202	CFStreamError streamError;
1203	UInt8 buffer[STREAM_EVENT_BUFSIZE];
1204
1205	ctx = (struct callback_ctx *)clientCallBackInfo;
1206
1207	switch (type) {
1208		case kCFStreamEventHasBytesAvailable:
1209            bytesRead = CFReadStreamRead(stream, buffer, STREAM_EVENT_BUFSIZE);
1210            if (bytesRead > 0) {
1211                CFDataAppendBytes(ctx->theData, buffer, bytesRead);
1212            }
1213            // Don't worry about bytesRead <= 0, because those will generate other events
1214			break;
1215
1216        case kCFStreamEventEndEncountered:
1217			theResponsePropertyRef = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
1218			ctx->response = *((CFHTTPMessageRef*)((void*)&theResponsePropertyRef));
1219			ctx->statusCode = CFHTTPMessageGetResponseStatusCode(ctx->response);
1220			if ((ctx->statusCode / 100) == 3) {
1221				// redirection
1222				ctx->status = CheckAuthRedirection;
1223			}
1224			else
1225				ctx->status = CheckAuthCallbackDone;
1226			syslog(LOG_DEBUG, "%s: StreamEventEndEncountered, status code %ld\n", __FUNCTION__, ctx->statusCode);
1227			break;
1228
1229		case kCFStreamEventErrorOccurred:
1230			streamError = CFReadStreamGetError(stream);
1231			syslog(LOG_DEBUG,"%s: EventHasErrorOccurred: domain %ld, error %d",
1232				   __FUNCTION__, streamError.domain, (int)streamError.error);
1233			ctx->streamError = streamError;
1234			ctx->status = CheckAuthCallbackStreamError;
1235			break;
1236
1237		default:
1238			syslog(LOG_DEBUG, "%s: Received unexpected stream event %lu\n", __FUNCTION__, type);
1239			break;
1240	}
1241}
1242
1243static int updateNetworkProxies(struct callback_ctx *ctx)
1244{
1245	CFNumberRef cf_enabled;
1246	CFNumberRef cf_port;
1247	int enabled;
1248	int err;
1249
1250	if (ctx->proxyStore == NULL) {
1251		ctx->proxyStore = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("WebDAVFSPlugin"), NULL, NULL);
1252		if (ctx->proxyStore == NULL)
1253			return ENOMEM;
1254	}
1255
1256	// Reset all proxy info
1257	if (ctx->proxyRealm != NULL) {
1258		CFRelease(ctx->proxyRealm);
1259		ctx->proxyRealm = NULL;
1260	}
1261
1262	ctx->httpProxyEnabled = FALSE;
1263	ctx->httpsProxyEnabled = FALSE;
1264
1265	if (ctx->httpProxyServer != NULL)
1266	{
1267		CFRelease(ctx->httpProxyServer);
1268		ctx->httpProxyServer = NULL;
1269	}
1270
1271	if (ctx->httpsProxyServer != NULL)
1272	{
1273		CFRelease(ctx->httpsProxyServer);
1274		ctx->httpsProxyServer = NULL;
1275	}
1276
1277	if (ctx->proxyDict != NULL)
1278	{
1279		CFRelease(ctx->proxyDict);
1280		ctx->proxyDict = NULL;
1281	}
1282
1283	ctx->httpProxyEnabled = FALSE;
1284	ctx->httpsProxyEnabled = FALSE;
1285
1286	// fetch the current internet proxy dictionary
1287	ctx->proxyDict = SCDynamicStoreCopyProxies(gProxyStore);
1288
1289	if (ctx->proxyDict != NULL) {
1290		// *********************
1291		// handle HTTP proxies
1292		// *********************
1293
1294		// are HTTP proxies enabled?
1295		cf_enabled = CFDictionaryGetValue(ctx->proxyDict, kSCPropNetProxiesHTTPEnable);
1296		if ( (cf_enabled != NULL) && CFNumberGetValue(cf_enabled, kCFNumberIntType, &enabled) && enabled )
1297		{
1298			// fetch the HTTP proxy host
1299			ctx->httpProxyServer = CFDictionaryGetValue(ctx->proxyDict, kSCPropNetProxiesHTTPProxy);
1300			if ( ctx->httpProxyServer != NULL )
1301			{
1302				CFRetain(ctx->httpProxyServer);
1303
1304				// fetch the HTTP proxy port
1305				cf_port = CFDictionaryGetValue(ctx->proxyDict, kSCPropNetProxiesHTTPPort);
1306				if ( (cf_port != NULL) && CFNumberGetValue(cf_port, kCFNumberIntType, &ctx->httpProxyPort) )
1307				{
1308					if ( ctx->httpProxyPort == 0 )
1309					{
1310						//no port specified so use the default HTTP port
1311						ctx->httpProxyPort = kHttpDefaultPort;
1312					}
1313					ctx->httpProxyEnabled = TRUE;
1314				}
1315			}
1316		}
1317
1318		// *********************
1319		// handle HTTPS proxies
1320		// *********************
1321
1322		// are HTTPS proxies enabled?
1323		cf_enabled = CFDictionaryGetValue(ctx->proxyDict, kSCPropNetProxiesHTTPSEnable);
1324		if ( (cf_enabled != NULL) && CFNumberGetValue(cf_enabled, kCFNumberIntType, &enabled) && enabled )
1325		{
1326			// fetch the HTTPS proxy host
1327			ctx->httpsProxyServer = CFDictionaryGetValue(ctx->proxyDict, kSCPropNetProxiesHTTPSProxy);
1328			if ( ctx->httpsProxyServer != NULL )
1329			{
1330				CFRetain(ctx->httpsProxyServer);
1331
1332				// fetch the HTTPS proxy port
1333				cf_port = CFDictionaryGetValue(ctx->proxyDict, kSCPropNetProxiesHTTPSPort);
1334				if ( (cf_port != NULL) && CFNumberGetValue(cf_port, kCFNumberIntType, &ctx->httpsProxyPort) )
1335				{
1336					if ( ctx->httpsProxyPort == 0 )
1337					{
1338						// no port specified so use the default HTTPS port
1339						ctx->httpsProxyPort = kHttpsDefaultPort;
1340					}
1341					ctx->httpsProxyEnabled = TRUE;
1342				}
1343			}
1344		}
1345	}
1346}
1347
1348static void initContext(struct callback_ctx *ctx)
1349{
1350	ctx->theData = NULL;
1351	ctx->response = NULL;
1352	ctx->sslPropDict = NULL;
1353	ctx->serverAuth = NULL;
1354	ctx->againCount = 0;
1355	ctx->triedServerCredentials = FALSE;
1356	ctx->triedProxyServerCredentials = FALSE;
1357	ctx->requireSecureLogin = FALSE;
1358	ctx->secureConnection = FALSE;
1359	ctx->proxyRealm = NULL;
1360	ctx->httpProxyEnabled = FALSE;
1361	ctx->httpProxyServer = NULL;
1362	ctx->httpsProxyEnabled = FALSE;
1363	ctx->httpsProxyServer = NULL;
1364	ctx->proxyDict = NULL;
1365	ctx->proxyStore = NULL;
1366	ctx->proxyAuth = NULL;
1367}
1368
1369static void releaseContextItems(struct callback_ctx *ctx)
1370{
1371	CFReleaseNull(ctx->theData);
1372	CFReleaseNull(ctx->response);
1373	CFReleaseNull(ctx->sslPropDict);
1374	CFReleaseNull(ctx->serverAuth);
1375	CFReleaseNull(ctx->proxyRealm);
1376	CFReleaseNull(ctx->httpProxyServer);
1377	CFReleaseNull(ctx->httpsProxyServer);
1378	CFReleaseNull(ctx->proxyDict);
1379	CFReleaseNull (ctx->proxyStore);
1380	CFReleaseNull(ctx->proxyAuth);
1381}
1382