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