1/* 2 * Copyright (c) 2006 - 2011 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 <CoreFoundation/CoreFoundation.h> 25#include <CoreServices/CoreServicesPriv.h> 26#include <asl.h> 27#include <netsmb/smb_lib.h> 28#include <charsets.h> 29#include <parse_url.h> 30#include <netsmb/smb_conn.h> 31#include <NetFS/NetFS.h> 32#include <NetFS/NetFSUtilPrivate.h> 33#include <netsmb/netbios.h> 34#include <netsmb/nb_lib.h> 35 36#define CIFS_SCHEME_LEN 5 37#define SMB_SCHEME_LEN 4 38 39static void LogCFString(CFStringRef theString, const char *debugstr, const char * func, int lineNum) 40{ 41 char prntstr[1024]; 42 43 if (theString == NULL) 44 return; 45 CFStringGetCString(theString, prntstr, 1024, kCFStringEncodingUTF8); 46 smb_log_info("%s-line:%d %s = %s", ASL_LEVEL_DEBUG, func, lineNum, debugstr, prntstr); 47} 48 49#ifdef SMB_DEBUG 50#define DebugLogCFString LogCFString 51#else // SMB_DEBUG 52#define DebugLogCFString(theString, debugstr, func, lineNum) 53#endif // SMB_DEBUG 54 55/* 56 * Test to see if we have the same url string 57 */ 58int isUrlStringEqual(CFURLRef url1, CFURLRef url2) 59{ 60 if ((url1 == NULL) || (url2 == NULL)) { 61 return FALSE; /* Never match null URLs */ 62 } 63 64 if (CFStringCompare(CFURLGetString(url1), CFURLGetString(url1), 65 kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 66 return TRUE; 67 } 68 return FALSE; 69} 70 71/* 72 * Allocate a buffer and then use CFStringGetCString to copy the c-style string 73 * into the buffer. The calling routine needs to free the buffer when done. 74 * This routine needs a new name. 75 */ 76char *CStringCreateWithCFString(CFStringRef inStr) 77{ 78 CFIndex maxLen; 79 char *str; 80 81 if(inStr == NULL) 82 return NULL; 83 maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(inStr), 84 kCFStringEncodingUTF8) + 1; 85 str = malloc(maxLen); 86 if (!str) { 87 smb_log_info("%s malloc failed, syserr = %s", ASL_LEVEL_ERR, 88 __FUNCTION__,strerror(ENOMEM)); 89 return NULL; 90 } 91 CFStringGetCString(inStr, str, maxLen, kCFStringEncodingUTF8); 92 return str; 93} 94 95/* 96 * See if this is a cifs or smb scheme 97 * 98 * RETURN VALUES: 99 * 0 - No Scheme, could still be our scheme 100 * 4 - SMB scheme, also the length of smb scheme field. 101 * 5 - CIFS scheme, also the length of cifs scheme field. 102 * -1 - Unknown scheme, should be treated as an error. 103 */ 104static int SMBSchemeLength(CFURLRef url) 105{ 106 int len = 0; 107 CFStringRef scheme = CFURLCopyScheme (url); 108 109 if (scheme == NULL) 110 return 0; 111 112 if ( kCFCompareEqualTo == CFStringCompare (scheme, CFSTR("smb"), kCFCompareCaseInsensitive) ) 113 len = SMB_SCHEME_LEN; /* Length of "smb:" */ 114 else if ( kCFCompareEqualTo == CFStringCompare (scheme, CFSTR("cifs"), kCFCompareCaseInsensitive) ) 115 len = CIFS_SCHEME_LEN; /* Length of "cifs:" */ 116 else 117 len = -1; 118 CFRelease(scheme); 119 return len; 120} 121 122/* 123 * This routine will percent escape out the input string by making a copy. The CreateStringByReplacingPercentEscapesUTF8 124 * routine will either return a copy or take a retain on the original string. 125 * 126 * NOTE: We always use kCFStringEncodingUTF8 and kCFAllocatorDefault. 127 */ 128static void CreateStringByReplacingPercentEscapesUTF8(CFStringRef *inOutStr, CFStringRef LeaveEscaped) 129{ 130 CFStringRef inStr = (inOutStr) ? *inOutStr : NULL; 131 132 if (!inStr) /* Just a safety check */ 133 return; 134 135 *inOutStr = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, inStr, LeaveEscaped, kCFStringEncodingUTF8); 136 CFRelease(inStr); /* We always want to release the inStr */ 137} 138 139/* 140 * This routine will percent escape the input string by making a copy. The CreateStringByAddingPercentEscapesUTF8 141 * routine will either return a copy or take a retain on the original string. If doRelease is set then we do 142 * a release on the inStr. 143 * 144 * NOTE: We always use kCFStringEncodingUTF8 and kCFAllocatorDefault. 145 */ 146static void CreateStringByAddingPercentEscapesUTF8(CFStringRef *inOutStr, CFStringRef leaveUnescaped, CFStringRef toBeEscaped, Boolean doRelease) 147{ 148 CFStringRef inStr = (inOutStr) ? *inOutStr : NULL; 149 150 if (!inStr) /* Just a safety check */ 151 return; 152 153 *inOutStr = CFURLCreateStringByAddingPercentEscapes(NULL, inStr, leaveUnescaped, toBeEscaped, kCFStringEncodingUTF8); 154 if (doRelease) 155 CFRelease(inStr); 156} 157 158static CFArrayRef CreateWrkgrpUserArrayFromCFStringRef(CFStringRef inString, CFStringRef separatorString) 159{ 160 CFArrayRef userArray = NULL; 161 162 userArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, inString, separatorString); 163 /* 164 * If there are two array entries then we have a workgoup and username otherwise if we just have one item then its a 165 * username. Any other number could be an error, but since we have no idea what they are trying to do we just treat 166 * it as a username. 167 */ 168 if (userArray && (CFArrayGetCount(userArray) != 2)) { 169 CFRelease(userArray); 170 userArray = NULL; 171 } 172 return userArray; 173 174} 175 176/* 177 * Check to see if there is a workgroup/domain in the URL. If yes then return an array 178 * with the first element as the workgroup and the second element containing the 179 * username and server name. If no workgroup just return NULL. 180 */ 181static CFArrayRef CreateWrkgrpUserArray(CFURLRef url) 182{ 183 CFStringRef netlocation = NULL; 184 CFArrayRef userArray = NULL; 185 186 netlocation = CFURLCopyNetLocation(url); 187 if (!netlocation) 188 return NULL; 189 190 userArray = CreateWrkgrpUserArrayFromCFStringRef(netlocation, CFSTR(";")); 191 CFRelease(netlocation); 192 return userArray; 193} 194 195 196/* 197 * Get the server name out of the URL. CFURLCopyHostName will escape out the 198 * server name for us. So just convert it to the correct code page encoding. 199 * 200 * Note: Currently we put the server name into a c-style string. In the future 201 * it would be nice to keep this as a CFString. 202 */ 203static int SetServerFromURL(struct smb_ctx *ctx, CFURLRef url) 204{ 205 CFIndex maxlen; 206 CFStringRef serverNameRef = CFURLCopyHostName(url); 207 char *ipV6Name = NULL; 208 209 ipV6Name = CStringCreateWithCFString(serverNameRef); 210 if (ipV6Name && isIPv6NumericName(ipV6Name)) { 211 /* CFURLCopyHostName removed the [] so put them back in */ 212 CFMutableStringRef newServer = CFStringCreateMutableCopy(NULL, 1024, CFSTR("[")); 213 if (newServer) { 214 CFStringAppend(newServer, serverNameRef); 215 CFStringAppend(newServer, CFSTR("]")); 216 CFRelease(serverNameRef); 217 serverNameRef = newServer; 218 } 219 } 220 221 /* Free up the buffer we allocated */ 222 if (ipV6Name) { 223 free(ipV6Name); 224 } 225 226 /* 227 * Every time we parse the URL we end up replacing the server name. In the 228 * future we should skip replacing the server name if we already have one and 229 * the one return CFURLCopyHostName matches it. 230 * 231 * Not going to make that big of a change in an update, so lets limit to the 232 * case were we are dealing with using a domain controller. 233 */ 234 if (serverNameRef && ctx->serverNameRef && ctx->serverName && 235 (ctx->serverIsDomainController) && 236 (ctx->ct_flags & SMBCF_CONNECTED) && 237 (CFStringCompare(serverNameRef, ctx->serverNameRef, 0) == kCFCompareEqualTo)) { 238 CFRelease(serverNameRef); 239 return 0; /* Same name nothing to do here */ 240 } 241 if (ctx->serverNameRef) { 242 CFRelease(ctx->serverNameRef); 243 } 244 /* The serverNameRef should always contain the URL host name or the Bonjour Name */ 245 ctx->serverNameRef = serverNameRef; 246 if (ctx->serverNameRef == NULL) 247 return EINVAL; 248 DebugLogCFString(ctx->serverNameRef, "Server", __FUNCTION__, __LINE__); 249 250 maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(ctx->serverNameRef), kCFStringEncodingUTF8) + 1; 251 if (ctx->serverName) 252 free(ctx->serverName); 253 ctx->serverName = malloc(maxlen); 254 if (!ctx->serverName) { 255 CFRelease(ctx->serverNameRef); 256 ctx->serverNameRef = NULL; 257 return ENOMEM; 258 } 259 CFStringGetCString(ctx->serverNameRef, ctx->serverName, maxlen, kCFStringEncodingUTF8); 260 return 0; 261} 262 263/* 264 * Get the user and workgroup names and return them in CFStringRef. 265 * First get the CFURLCopyNetLocation because it will not escape out the string. 266 */ 267static CFStringRef CopyUserAndWorkgroupFromURL(CFStringRef *outWorkGroup, CFURLRef url) 268{ 269 CFURLRef net_url = NULL; 270 CFArrayRef userArray = NULL; 271 CFStringRef userString = NULL; 272 CFMutableStringRef urlString = NULL; 273 CFStringRef wrkgrpString = NULL; 274 275 *outWorkGroup = NULL; /* Always start like we didn't get one. */ 276 /* This will return null if no workgroup in the URL */ 277 userArray = CreateWrkgrpUserArray(url); 278 if (!userArray) /* We just have a username name */ 279 return(CFURLCopyUserName(url)); /* This will escape out the character for us. */ 280 281 /* Now for the hard part; netlocation contains one of the following: 282 * 283 * URL = "//workgroup;username:password@smb-win2003.apple.com" 284 * URL = "//workgroup;username:@smb-win2003.apple.com" 285 * URL = "//workgroup;username@smb-win2003.apple.com" 286 * URL = "//workgroup;@smb-win2003.apple.com" 287 */ 288 /* Get the username first */ 289 urlString = CFStringCreateMutableCopy(NULL, 1024, CFSTR("smb://")); 290 if (!urlString) { 291 CFRelease(userArray); 292 return(CFURLCopyUserName(url)); /* This will escape out the character for us. */ 293 } 294 CFStringAppend(urlString, (CFStringRef)CFArrayGetValueAtIndex(userArray, 1)); 295 net_url = CFURLCreateWithString(NULL, urlString, NULL); 296 CFRelease(urlString); 297 urlString = NULL; 298 /* Not sure what to do if we fail here */ 299 if (!net_url) { 300 CFRelease(userArray); 301 return(CFURLCopyUserName(url)); /* This will escape out the character for us. */ 302 } 303 /* We now have a URL without the workgroup name, just copy out the username. */ 304 userString = CFURLCopyUserName(net_url); 305 CFRelease(net_url); 306 307 /* Now get the workgroup */ 308 wrkgrpString = CFStringCreateCopy(NULL, (CFStringRef)CFArrayGetValueAtIndex(userArray, 0)); 309 CreateStringByReplacingPercentEscapesUTF8(&wrkgrpString, CFSTR("")); 310 if (wrkgrpString) 311 *outWorkGroup = wrkgrpString; /* We have the workgroup return it to the calling routine */ 312 CFRelease(userArray); 313 return(userString); 314} 315 316/* 317 * Get the workgroup and return a username CFStringRef if it exist. 318 * First get the CFURLCopyNetLocation because it will not escape out the string. 319 */ 320static CFStringRef SetWorkgroupFromURL(struct smb_ctx *ctx, CFURLRef url) 321{ 322 CFStringRef userString = NULL; 323 CFStringRef wrkgrpString = NULL; 324 325 userString = CopyUserAndWorkgroupFromURL(&wrkgrpString, url); 326 327 if (wrkgrpString) { 328 LogCFString(wrkgrpString, "Workgroup", __FUNCTION__, __LINE__); 329 if (CFStringGetLength(wrkgrpString) <= SMB_MAXNetBIOSNAMELEN) { 330 str_upper(ctx->ct_setup.ioc_domain, sizeof(ctx->ct_setup.ioc_domain), wrkgrpString); 331 } 332 CFRelease(wrkgrpString); 333 } 334 return(userString); 335} 336 337/* 338 * Need to call SetWorkgroupFromURL just in case we have a workgroup name. CFURL does not handle 339 * a CIFS style URL with a workgroup name. 340 */ 341static int SetUserNameFromURL(struct smb_ctx *ctx, CFURLRef url) 342{ 343 CFStringRef userNameRef = SetWorkgroupFromURL(ctx, url); 344 char username[SMB_MAXUSERNAMELEN+1]; 345 int error; 346 347 /* No user name in the URL */ 348 if (! userNameRef) 349 return 0; 350 LogCFString(userNameRef, "Username",__FUNCTION__, __LINE__); 351 352 /* Conversion failed or the data doesn't fit in the buffer */ 353 if (CFStringGetCString(userNameRef, username, SMB_MAXUSERNAMELEN+1, kCFStringEncodingUTF8) == FALSE) { 354 error = ENAMETOOLONG; /* Not sure what else to return. */ 355 } else { 356 error = smb_ctx_setuser(ctx, username); 357 } 358 CFRelease(userNameRef); 359 return error; 360} 361 362/* 363 * The URL may contain no password, an empty password, or a password. An empty password is a passowrd 364 * and should be treated the same as a password. This is need to make guest access work. 365 * 366 * URL "smb://username:password@server/share" should set the password. 367 * URL "smb://username:@server/" should set the password. 368 * URL "smb://username@server/share" should not set the password. 369 * URL "smb://server/share/path" should not set the password. 370 * 371 */ 372static int SetPasswordFromURL(struct smb_ctx *ctx, CFURLRef url) 373{ 374 CFStringRef passwd = CFURLCopyPassword(url); 375 376 /* URL =" //username@smb-win2003.apple.com" or URL =" //smb-win2003.apple.com" */ 377 if (! passwd) 378 return 0; 379 380 /* Password is too long return an error */ 381 if (CFStringGetLength(passwd) >= SMB_MAXPASSWORDLEN) { 382 CFRelease(passwd); 383 return ENAMETOOLONG; 384 } 385 /* 386 * Works for password and empty password 387 * 388 * URL = "//username:password@smb-win2003.apple.com" 389 * URL = "//username:@smb-win2003.apple.com" 390 */ 391 CFStringGetCString(passwd, ctx->ct_setup.ioc_password, SMB_MAXPASSWORDLEN, kCFStringEncodingUTF8); 392 ctx->ct_flags |= SMBCF_EXPLICITPWD; 393 CFRelease(passwd); 394 return 0; 395} 396 397/* 398 * If URL contains a port then we should get it and set the correct flag. 399 * 400 * URL "smb://username:password@server:445/share" set the port to 445. 401 * 402 */ 403static void SetPortNumberFromURL(struct smb_ctx *ctx, CFURLRef url) 404{ 405 SInt32 port = CFURLGetPortNumber(url); 406 407 /* No port defined in the URL */ 408 if (port == -1) 409 return; 410 /* They supplied a port number use it and only it */ 411 ctx->prefs.tcp_port = port; 412 ctx->prefs.tryBothPorts = FALSE; 413 smb_log_info("Setting port number to %d", ASL_LEVEL_DEBUG, ctx->prefs.tcp_port); 414} 415 416/* 417 * We need to separate the share name and any path component from the URL. 418 * URL "smb://username:password@server" no share name or path. 419 * URL "smb://username:password@server/"no share name or path. 420 * URL "smb://username:password@server/share" just a share name. 421 * URL "smb://username:password@server/share/path" share name and path. 422 * 423 * The Share name and Path name will not begin with a slash. 424 * smb://server/ntfs share = ntfs path = NULL 425 * smb://ntfs/dir1/dir2 share = ntfs path = dir1/dir2 426 * smb://server/OPEN%2fSPACE/dir1 share = OPEN%2fSPACE path = dir1 427 */ 428static int GetShareAndPathFromURL(CFURLRef url, CFStringRef *out_share, CFStringRef *out_path) 429{ 430 Boolean isAbsolute; 431 CFArrayRef userArray = NULL; 432 CFMutableArrayRef userArrayM = NULL; 433 CFStringRef share = CFURLCopyStrictPath(url, &isAbsolute); 434 CFStringRef path = NULL; 435 436 *out_share = NULL; 437 *out_path = NULL; 438 /* We have an empty share treat it like no share */ 439 if (share && (CFStringGetLength(share) == 0)) { 440 CFRelease(share); 441 share = NULL; 442 } 443 /* Since there is no share name we have nothing left to do. */ 444 if (!share) 445 return 0; 446 447 userArray = CFStringCreateArrayBySeparatingStrings(NULL, share, CFSTR("/")); 448 if (userArray && (CFArrayGetCount(userArray) > 1)) 449 userArrayM = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(userArray), userArray); 450 451 if (userArray) 452 CFRelease(userArray); 453 454 if (userArrayM) { 455 CFMutableStringRef newshare; /* Just in case something goes wrong */ 456 457 newshare = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)CFArrayGetValueAtIndex(userArrayM, 0)); 458 if (newshare) { 459 CFStringTrim(newshare, CFSTR("/")); /* Remove any trailing slashes */ 460 CreateStringByReplacingPercentEscapesUTF8((CFStringRef *) &newshare, CFSTR("/")); 461 } 462 CFArrayRemoveValueAtIndex(userArrayM, 0); 463 /* Now remove any trailing slashes */ 464 path = CFStringCreateByCombiningStrings(NULL, userArrayM, CFSTR("/")); 465 if (path && (CFStringGetLength(path) == 0)) { 466 CFRelease(path); /* Empty path remove it */ 467 path = NULL; 468 } 469 if (path) { 470 CFMutableStringRef newpath = CFStringCreateMutableCopy(NULL, 0, path); 471 if (newpath) { 472 CFStringTrim(newpath, CFSTR("/")); /* Remove any trailing slashes */ 473 CFRelease(path); 474 path = newpath; 475 } 476 } 477 if (path) { 478 CreateStringByReplacingPercentEscapesUTF8(&path, CFSTR("/")); 479 LogCFString(path, "Path", __FUNCTION__, __LINE__); 480 } 481 482 CFRelease(userArrayM); 483 /* Something went wrong use the original value */ 484 if (newshare) { 485 CFRelease(share); 486 share = newshare; 487 } 488 } else 489 CreateStringByReplacingPercentEscapesUTF8(&share, CFSTR("/")); 490 491 /* 492 * The above routines will not un-precent escape out slashes. We only allow for the cases 493 * where the share name is a single slash. Slashes are treated as delemiters in the path name. 494 * So if the share name has a single 0x2f then make it a slash. This means you can't have 495 * a share name whos name is 0x2f, not likley to happen. 496 */ 497 if (share && ( kCFCompareEqualTo == CFStringCompare (share, CFSTR("0x2f"), kCFCompareCaseInsensitive) )) { 498 CFRelease(share); 499 share = CFStringCreateCopy(NULL, CFSTR("/")); 500 } 501 502 503 if (share && (CFStringGetLength(share) >= SMB_MAXSHARENAMELEN)) { 504 CFRelease(share); 505 if (path) 506 CFRelease(path); 507 return ENAMETOOLONG; 508 } 509 510 *out_share = share; 511 *out_path = path; 512 return 0; 513} 514 515/* 516 * We need to separate the share name and any path component from the URL. 517 * URL "smb://username:password@server" no share name or path. 518 * URL "smb://username:password@server/"no share name or path. 519 * URL "smb://username:password@server/share" just a share name. 520 * URL "smb://username:password@server/share/path" share name and path. 521 * 522 * The Share name and Path name will not begin with a slash. 523 * smb://server/ntfs share = ntfs path = NULL 524 * smb://ntfs/dir1/dir2 share = ntfs path = dir1/dir2 525 */ 526static int SetShareAndPathFromURL(struct smb_ctx *ctx, CFURLRef url) 527{ 528 CFStringRef share = NULL; 529 CFStringRef path = NULL; 530 CFIndex maxlen; 531 int error; 532 533 error = GetShareAndPathFromURL(url, &share, &path); 534 if (error) { 535 return error; 536 } 537 538 /* Since there is no share name we have nothing left to do. */ 539 if (share == NULL) { 540 if (path != NULL) { 541 CFRelease(path); 542 } 543 544 return 0; 545 } 546 547 DebugLogCFString(share, "Share", __FUNCTION__, __LINE__); 548 549 CreateStringByReplacingPercentEscapesUTF8(&share, CFSTR("")); 550 551 if (ctx->ct_origshare) { 552 free(ctx->ct_origshare); 553 } 554 555 if (ctx->mountPath) { 556 CFRelease(ctx->mountPath); 557 } 558 ctx->mountPath = NULL; 559 560 maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(share), kCFStringEncodingUTF8) + 1; 561 ctx->ct_origshare = malloc(maxlen); 562 if (!ctx->ct_origshare) { 563 CFRelease(share); 564 565 if (path != NULL) { 566 CFRelease(path); 567 } 568 569 return ENOMEM; 570 } 571 572 CFStringGetCString(share, ctx->ct_origshare, maxlen, kCFStringEncodingUTF8); 573 str_upper(ctx->ct_sh.ioc_share, sizeof(ctx->ct_sh.ioc_share), share); 574 CFRelease(share); 575 576 ctx->mountPath = path; 577 578 return 0; 579} 580 581/* 582 * Here we expect something like 583 * "//[workgroup;][user[:password]@]host[/share[/path]]" 584 * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt 585 */ 586int ParseSMBURL(struct smb_ctx *ctx) 587{ 588 int error = EINVAL; 589 590 /* Make sure its a good URL, better be at this point */ 591 if ((!CFURLCanBeDecomposed(ctx->ct_url)) || (SMBSchemeLength(ctx->ct_url) < 0)) { 592 smb_log_info("This is an invalid URL, syserr = %s", ASL_LEVEL_ERR, 593 strerror(error)); 594 return error; 595 } 596 597 error = SetServerFromURL(ctx, ctx->ct_url); 598 if (error) { 599 smb_log_info("The URL has a bad server name, syserr = %s", ASL_LEVEL_ERR, 600 strerror(error)); 601 return error; 602 } 603 error = SetUserNameFromURL(ctx, ctx->ct_url); 604 if (error) { 605 smb_log_info("The URL has a bad user name, syserr = %s", ASL_LEVEL_ERR, 606 strerror(error)); 607 return error; 608 } 609 error = SetPasswordFromURL(ctx, ctx->ct_url); 610 if (error) { 611 smb_log_info("The URL has a bad password, syserr = %s", ASL_LEVEL_ERR, 612 strerror(error)); 613 return error; 614 } 615 SetPortNumberFromURL(ctx, ctx->ct_url); 616 error = SetShareAndPathFromURL(ctx, ctx->ct_url); 617 /* CFURLCopyQueryString to get ?WINS=msfilsys.apple.com;NODETYPE=H info */ 618 return error; 619} 620 621/* 622 * Given a Dfs Referral string create a CFURL. Remember referral have a file 623 * syntax 624 * 625 * Example: "/smb-win2003.apple.com/DfsRoot/DfsLink1" 626 */ 627CFURLRef CreateURLFromReferral(CFStringRef inStr) 628{ 629 CFURLRef ct_url = NULL; 630 CFMutableStringRef urlString = CFStringCreateMutableCopy(NULL, 0, CFSTR("smb:/")); 631 CFStringRef escapeStr = inStr; 632 633 /* 634 * CreateStringByAddingPercentEscapesUTF8() will either create a new string 635 * or return the original with ref count incremented. Either way we have to 636 * call CFRelease on the returned string 637 */ 638 CreateStringByAddingPercentEscapesUTF8(&escapeStr, NULL, NULL, FALSE); 639 640 if (urlString) { 641 CFStringAppend(urlString, escapeStr); 642 ct_url = CFURLCreateWithString(kCFAllocatorDefault, urlString, NULL); 643 CFRelease(urlString); /* We create it now release it */ 644 } 645 646 if (!ct_url) { 647 LogCFString(inStr, "creating url failed", __FUNCTION__, __LINE__); 648 } 649 650 if (escapeStr) { 651 CFRelease(escapeStr); 652 } 653 654 return ct_url; 655} 656 657/* 658 * Given a c-style string create a CFURL. We assume the c-style string is in 659 * URL or UNC format. Anything else will give unexpected behavior. 660 * NOTE: The library code doesn't care if the scheme exist or not in the URL, 661 * but we attempt to create a URL with a scheme, just for correctness sake. 662 * 663 * Note: If its a URL, then do not escape it out again since it should already 664 * be escaped out properly. 665 */ 666CFURLRef CreateSMBURL(const char *url) 667{ 668 CFURLRef ct_url = NULL; 669 CFStringRef urlString = CFStringCreateWithCString(NULL, 670 url, 671 kCFStringEncodingUTF8); 672 CFStringRef escapedUrlString; 673 int UNCformat = 0; 674 675 escapedUrlString = NULL; 676 677 /* 678 * We have a UNC path that we need to convert into a SMB URL. Currently we 679 * just replace the backslashes with slashes 680 */ 681 if (urlString && (*url == '\\') && (*(url + 1) == '\\')) { 682 CFArrayRef urlArray = CFStringCreateArrayBySeparatingStrings(NULL, 683 urlString, 684 CFSTR("\\")); 685 686 CFRelease(urlString); 687 urlString = NULL; 688 if (urlArray) { 689 urlString = CFStringCreateByCombiningStrings(NULL, 690 urlArray, 691 CFSTR("/")); 692 CFRelease(urlArray); 693 } 694 UNCformat = 1; 695 } 696 697 /* Something failed just get out */ 698 if (!urlString) { 699 return NULL; 700 } 701 702 DebugLogCFString(urlString, "urlString ", __FUNCTION__, __LINE__); 703 704 /* 705 * No scheme, add one if we can, but not required by the library code. 706 * NOTE: If no scheme, then expect the string to start with double slashes. 707 */ 708 if ((!CFStringHasPrefix(urlString, CFSTR("smb://"))) && 709 (!CFStringHasPrefix(urlString, CFSTR("cifs://")))) { 710 CFMutableStringRef urlStringM = CFStringCreateMutableCopy(NULL, 711 1024, 712 CFSTR("smb:")); 713 714 if (urlStringM) { 715 CFStringAppend(urlStringM, urlString); 716 CFRelease(urlString); 717 urlString = urlStringM; 718 } 719 } 720 721 /* Something failed just get out */ 722 if (urlString == NULL) { 723 return (NULL); 724 } 725 726 if (UNCformat == 1) { 727 /* For UNC format strings, escape out any non-URL characters */ 728 escapedUrlString = CFURLCreateStringByAddingPercentEscapes(NULL, 729 urlString, 730 NULL, 731 NULL, 732 kCFStringEncodingUTF8); 733 CFRelease(urlString); /* Can release it now */ 734 735 /* Something failed just get out */ 736 if (escapedUrlString == NULL) { 737 return (NULL); 738 } 739 740 /* now create the URL */ 741 ct_url = CFURLCreateWithString(kCFAllocatorDefault, 742 escapedUrlString, 743 NULL); 744 745 CFRelease(escapedUrlString); /* We create it now release it */ 746 } 747 else { 748 /* 749 * For URL format strings, it should already be escaped. 750 * Just create the URL. 751 */ 752 ct_url = CFURLCreateWithString(kCFAllocatorDefault, 753 urlString, 754 NULL); 755 756 CFRelease(urlString); /* Can release it now */ 757 } 758 759 return ct_url; 760} 761 762/* 763 * Given a url parse it and place the component in a dictionary we create. 764 */ 765int smb_url_to_dictionary(CFURLRef url, CFDictionaryRef *dict) 766{ 767 CFMutableDictionaryRef mutableDict = NULL; 768 int error = 0; 769 CFStringRef Server = NULL; 770 CFStringRef Username = NULL; 771 CFStringRef DomainWrkgrp = NULL; 772 CFStringRef Password = NULL; 773 CFStringRef Share = NULL; 774 CFStringRef Path = NULL; 775 CFStringRef Port = NULL; 776 777 /* Make sure its a good URL, better be at this point */ 778 if ((!CFURLCanBeDecomposed(url)) || (SMBSchemeLength(url) < 0)) { 779 smb_log_info("%s: Invalid URL, syserr = %s", ASL_LEVEL_ERR, 780 __FUNCTION__, strerror(EINVAL)); 781 goto ErrorOut; 782 } 783 784 /* create and return the server parameters dictionary */ 785 mutableDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 786 &kCFTypeDictionaryValueCallBacks); 787 if (mutableDict == NULL) { 788 error = errno; 789 smb_log_info("%s: CFDictionaryCreateMutable failed, syserr = %s", 790 ASL_LEVEL_ERR, __FUNCTION__, strerror(error)); 791 goto ErrorOut; 792 } 793 794 /* 795 * SMB can have two different scheme's cifs or smb. When we made SMBSchemeLength call at the 796 * start of this routine it made sure we had one or the other scheme. Always default here to 797 * the SMB scheme. 798 */ 799 CFDictionarySetValue (mutableDict, kNetFSSchemeKey, CFSTR(SMB_SCHEME_STRING)); 800 801 error = NetFSCopyHostAndPort(url, &Server, &Port); 802 if ((Server == NULL) || (error != noErr)) { 803 if (Port != NULL) { 804 CFRelease(Port); 805 } 806 goto ErrorOut; /* Server name is required */ 807 } 808 809 LogCFString(Server, "Server String", __FUNCTION__, __LINE__); 810 CFDictionarySetValue (mutableDict, kNetFSHostKey, Server); 811 CFRelease(Server); 812 Server = NULL; 813 814 if (Port != NULL) { 815 CFDictionarySetValue (mutableDict, kNetFSAlternatePortKey, Port); 816 CFRelease(Port); 817 Port = NULL; 818 } 819 820 Username = CopyUserAndWorkgroupFromURL(&DomainWrkgrp, url); 821 LogCFString(Username, "Username String", __FUNCTION__, __LINE__); 822 LogCFString(DomainWrkgrp, "DomainWrkgrp String", __FUNCTION__, __LINE__); 823 error = 0; 824 if ((Username) && (CFStringGetLength(Username) >= SMB_MAXUSERNAMELEN)) 825 error = ENAMETOOLONG; 826 827 if ((DomainWrkgrp) && (CFStringGetLength(DomainWrkgrp) > SMB_MAXNetBIOSNAMELEN)) 828 error = ENAMETOOLONG; 829 830 if (error) { 831 if (Username) 832 CFRelease(Username); 833 if (DomainWrkgrp) 834 CFRelease(DomainWrkgrp); 835 goto ErrorOut; /* Username or Domain name is too long */ 836 } 837 838 /* 839 * We have a domain name so combined it with the user name so we can it 840 * display to the user. We now test to make sure we have a username. Having 841 * a domain without a username makes no sense, so don't return either. 842 */ 843 if (DomainWrkgrp && Username && CFStringGetLength(Username)) { 844 CFMutableStringRef tempString = CFStringCreateMutableCopy(NULL, 0, DomainWrkgrp); 845 846 if (tempString) { 847 CFStringAppend(tempString, CFSTR("\\")); 848 CFStringAppend(tempString, Username); 849 CFRelease(Username); 850 Username = tempString; 851 } 852 } 853 854 if (Username) 855 { 856 CFDictionarySetValue (mutableDict, kNetFSUserNameKey, Username); 857 CFRelease(Username); 858 } 859 860 if (DomainWrkgrp) { 861 CFRelease(DomainWrkgrp); 862 } 863 864 Password = CFURLCopyPassword(url); 865 if (Password) { 866 if (CFStringGetLength(Password) >= SMB_MAXPASSWORDLEN) { 867 error = ENAMETOOLONG; 868 CFRelease(Password); 869 goto ErrorOut; /* Password is too long */ 870 } 871 CFDictionarySetValue (mutableDict, kNetFSPasswordKey, Password); 872 CFRelease(Password); 873 } 874 875 /* 876 * We used to keep the share and path as two different elements in the dictionary. This was 877 * changed to satisfy NetFS and other plugins. We still need to check and make sure the 878 * share and path are correct. So now split them apart and then put them put them back together. 879 */ 880 error = GetShareAndPathFromURL(url, &Share, &Path); 881 if (error) 882 goto ErrorOut; /* Share name is too long */ 883 884 LogCFString(Share, "Share String", __FUNCTION__, __LINE__); 885 LogCFString(Path, "Path String", __FUNCTION__, __LINE__); 886 887 if (Share && Path) { 888 /* 889 * We have a share and path, but there is nothing in the 890 * share, then return an error 891 */ 892 if (CFStringGetLength(Share) == 0) { 893 CFRelease(Path); 894 CFRelease(Share); 895 Share = Path = NULL; 896 error = EINVAL; 897 smb_log_info("%s: No share name found, syserr = %s", 898 ASL_LEVEL_ERR, __FUNCTION__, strerror(error)); 899 goto ErrorOut; 900 } 901 if (CFStringGetLength(Path)) { 902 CFMutableStringRef tempString = CFStringCreateMutableCopy(NULL, 0, Share); 903 if (tempString) { 904 CFStringAppend(tempString, CFSTR("/")); 905 CFStringAppend(tempString, Path); 906 CFDictionarySetValue (mutableDict, kNetFSPathKey, tempString); 907 CFRelease(tempString); 908 CFRelease(Share); 909 Share = NULL; 910 } 911 } 912 } 913 /* Ignore any empty share at this point */ 914 if (Share && CFStringGetLength(Share)) 915 CFDictionarySetValue (mutableDict, kNetFSPathKey, Share); 916 917 if (Share) 918 CFRelease(Share); 919 920 if (Path) 921 CFRelease(Path); 922 923 *dict = mutableDict; 924 return 0; 925 926ErrorOut: 927 928 *dict = NULL; 929 if (mutableDict) 930 CFRelease(mutableDict); 931 if (!error) /* No error set it to the default error */ 932 error = EINVAL; 933 return error; 934 935} 936 937/* 938 * Given a dictionary create a url string. We assume that the dictionary has any characters that need to 939 * be escaped out escaped out. 940 */ 941static int smb_dictionary_to_urlstring(CFDictionaryRef dict, CFMutableStringRef *urlReturnString) 942{ 943 int error = 0; 944 CFMutableStringRef urlStringM = NULL; 945 CFStringRef DomainWrkgrp = NULL; 946 CFStringRef Username = NULL; 947 CFStringRef Password = NULL; 948 CFStringRef Server = NULL; 949 CFStringRef PortNumber = NULL; 950 CFStringRef Path = NULL; 951 Boolean releaseUsername = FALSE; 952 char *ipV6Name = NULL; 953 954 urlStringM = CFStringCreateMutableCopy(NULL, 1024, CFSTR("smb://")); 955 if (urlStringM == NULL) { 956 error = errno; 957 smb_log_info("%s: couldn't allocate the url string, syserr = %s", 958 ASL_LEVEL_ERR, __FUNCTION__, strerror(error)); 959 goto WeAreDone; 960 } 961 962 /* Get the server name, required value */ 963 Server = CFDictionaryGetValue(dict, kNetFSHostKey); 964 if (Server == NULL) { 965 error = EINVAL; 966 smb_log_info("%s: no server name, syserr = %s", ASL_LEVEL_ERR, 967 __FUNCTION__, strerror(error)); 968 goto WeAreDone; 969 } 970 971 /* 972 * So we have three basic server names to cover here. 973 * 974 * 1. Bonjour Name requires these /@:,?=;&+$ extra characters to be percent 975 * escape out. 976 * 977 * 2. NetBIOS Name requires these ~!@#$%^&;'.(){} extra characters to be 978 * percent escape out. 979 * 980 * 3. DNS Names requires the DOT IPv6 Notification address to be enclosed in 981 * square brackets and that the colon not to be escaped out. 982 * 983 * Note that CFURLCopyHostName will remove the square brackets from the DOT 984 * IPv6 Notification address. smb_url_to_dictionary will put the brackets 985 * back into the IPv6 address in the dictionary. We expect this behavior so 986 * anyone changing the dictionary server field will need to make sure 987 * it does have square brackets. 988 */ 989 ipV6Name = CStringCreateWithCFString(Server); 990 991 /* Is this an IPv6 numeric name, then leave the square brackets around it */ 992 if (ipV6Name && isIPv6NumericName(ipV6Name)) { 993 CreateStringByAddingPercentEscapesUTF8(&Server, CFSTR("[]"), NULL, FALSE); 994 } 995 else { 996 /* Some other name make sure we percent escape all the characters needed */ 997 CreateStringByAddingPercentEscapesUTF8(&Server, NULL, CFSTR("~!'()/@:,?=;&+$"), FALSE); 998 } 999 1000 /* Free up the buffer we allocated */ 1001 if (ipV6Name) { 1002 free(ipV6Name); 1003 } 1004 1005 if (Server == NULL) { 1006 error = EINVAL; 1007 smb_log_info("%s: still no server name, syserr = %s", ASL_LEVEL_ERR, 1008 __FUNCTION__, strerror(error)); 1009 goto WeAreDone; 1010 } 1011 1012 /* Now get all the other parts of the url. */ 1013 Username = CFDictionaryGetValue(dict, kNetFSUserNameKey); 1014 /* We have a user name see if they entered a domain also. */ 1015 if (Username) { 1016 CFArrayRef userArray = NULL; 1017 /* 1018 * Remember that on windows a back slash is illegal, so if someone wants to use one 1019 * in the username they will need to escape percent it out. 1020 */ 1021 userArray = CreateWrkgrpUserArrayFromCFStringRef(Username, CFSTR("\\")); 1022 /* If we have an array then we have a domain\username in the Username String */ 1023 if (userArray) { 1024 DomainWrkgrp = CFStringCreateCopy(NULL, (CFStringRef)CFArrayGetValueAtIndex(userArray, 0)); 1025 Username = CFStringCreateCopy(NULL, (CFStringRef)CFArrayGetValueAtIndex(userArray, 1)); 1026 CFRelease(userArray); 1027 releaseUsername = TRUE; 1028 } 1029 } 1030 1031 Password = CFDictionaryGetValue(dict, kNetFSPasswordKey); 1032 Path = CFDictionaryGetValue(dict, kNetFSPathKey); 1033 PortNumber = CFDictionaryGetValue(dict, kNetFSAlternatePortKey); 1034 1035 /* 1036 * Percent escape out any URL special characters, for the username, password, Domain/Workgroup, 1037 * path, and port. Not sure the port is required, but AFP does it so why not. 1038 * The CreateStringByAddingPercentEscapesUTF8 will return either NULL or a value that must be 1039 * released. 1040 */ 1041 CreateStringByAddingPercentEscapesUTF8(&DomainWrkgrp, NULL, CFSTR("@:;/?"), TRUE); 1042 CreateStringByAddingPercentEscapesUTF8(&Username, NULL, CFSTR("@:;/?"), releaseUsername); 1043 CreateStringByAddingPercentEscapesUTF8(&Password, NULL, CFSTR("@:;/?"), FALSE); 1044 CreateStringByAddingPercentEscapesUTF8(&Path, CFSTR("%%"), CFSTR("?#"), FALSE); 1045 CreateStringByAddingPercentEscapesUTF8(&PortNumber, NULL, NULL, FALSE); 1046 1047 LogCFString(Username, "Username String", __FUNCTION__, __LINE__); 1048 LogCFString(DomainWrkgrp, "Domain String", __FUNCTION__, __LINE__); 1049 LogCFString(Path, "Path String", __FUNCTION__, __LINE__); 1050 LogCFString(PortNumber, "PortNumber String", __FUNCTION__, __LINE__); 1051 1052 /* Add the Domain/Workgroup */ 1053 if (DomainWrkgrp) { 1054 CFStringAppend(urlStringM, DomainWrkgrp); 1055 CFStringAppend(urlStringM, CFSTR(";")); 1056 } 1057 /* Add the username and password */ 1058 if (Username || Password) { 1059 if (Username) 1060 CFStringAppend(urlStringM, Username); 1061 if (Password) { 1062 CFStringAppend(urlStringM, CFSTR(":")); 1063 CFStringAppend(urlStringM, Password); 1064 } 1065 CFStringAppend(urlStringM, CFSTR("@")); 1066 } 1067 1068 /* Add the server */ 1069 CFStringAppend(urlStringM, Server); 1070 1071 /* Add the port number */ 1072 if (PortNumber) { 1073 CFStringAppend(urlStringM, CFSTR(":")); 1074 CFStringAppend(urlStringM, PortNumber); 1075 } 1076 1077 /* Add the share and path */ 1078 if (Path) { 1079 CFStringAppend(urlStringM, CFSTR("/")); 1080 /* If the share name is a slash percent escape it out */ 1081 if ( kCFCompareEqualTo == CFStringCompare (Path, CFSTR("/"), kCFCompareCaseInsensitive) ) 1082 CFStringAppend(urlStringM, CFSTR("0x2f")); 1083 else 1084 CFStringAppend(urlStringM, Path); 1085 } 1086 1087 DebugLogCFString(urlStringM, "URL String", __FUNCTION__, __LINE__); 1088 1089WeAreDone: 1090 if (Username) 1091 CFRelease(Username); 1092 if (Password) 1093 CFRelease(Password); 1094 if (DomainWrkgrp) 1095 CFRelease(DomainWrkgrp); 1096 if (Path) 1097 CFRelease(Path); 1098 if (PortNumber) 1099 CFRelease(PortNumber); 1100 if (Server) 1101 CFRelease(Server); 1102 1103 if (error == 0) 1104 *urlReturnString = urlStringM; 1105 else if (urlStringM) 1106 CFRelease(urlStringM); 1107 1108 return error; 1109} 1110 1111 1112/* 1113 * Given a dictionary create a url. We assume that the dictionary has any characters that need to 1114 * be escaped out escaped out. 1115 */ 1116int smb_dictionary_to_url(CFDictionaryRef dict, CFURLRef *url) 1117{ 1118 int error; 1119 CFMutableStringRef urlStringM = NULL; 1120 1121 error = smb_dictionary_to_urlstring(dict, &urlStringM); 1122 /* Ok we have everything we need for the URL now create it. */ 1123 if ((error == 0) && urlStringM) { 1124 *url = CFURLCreateWithString(NULL, urlStringM, NULL); 1125 if (*url == NULL) 1126 error = errno; 1127 } 1128 1129 if (urlStringM) 1130 CFRelease(urlStringM); 1131 if (error) 1132 smb_log_info("%s: creating the url failed, syserr = %s", ASL_LEVEL_ERR, 1133 __FUNCTION__, strerror(error)); 1134 1135 return error; 1136 1137} 1138 1139CFStringRef CreateURLCFString(CFStringRef Domain, CFStringRef Username, 1140 CFStringRef Password, CFStringRef ServerName, 1141 CFStringRef Path, CFStringRef PortNumber) 1142{ 1143 CFMutableDictionaryRef mutableDict = NULL; 1144 int error; 1145 CFMutableStringRef urlString = NULL; 1146 1147 mutableDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 1148 &kCFTypeDictionaryValueCallBacks); 1149 if (mutableDict == NULL) { 1150 smb_log_info("%s: CFDictionaryCreateMutable failed, syserr = %s", 1151 ASL_LEVEL_ERR, __FUNCTION__, strerror(errno)); 1152 return NULL; 1153 } 1154 1155 if (Domain && Username) { 1156 CFMutableStringRef tempString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, Domain); 1157 1158 if (tempString) { 1159 CFStringAppend(tempString, CFSTR("\\")); 1160 CFStringAppend(tempString, Username); 1161 Username = tempString; 1162 } 1163 else { 1164 CFRetain(Username); 1165 } 1166 } else if (Username) { 1167 CFRetain(Username); 1168 } 1169 1170 if (Username) { 1171 CFDictionarySetValue(mutableDict, kNetFSUserNameKey, Username); 1172 CFRelease(Username); 1173 } 1174 1175 if (Password) { 1176 CFDictionarySetValue(mutableDict, kNetFSPasswordKey, Password); 1177 } 1178 1179 if (ServerName) { 1180 CFDictionarySetValue(mutableDict, kNetFSHostKey, ServerName); 1181 } 1182 1183 if (Path) { 1184 CFDictionarySetValue (mutableDict, kNetFSPathKey, Path); 1185 } 1186 1187 if (PortNumber) { 1188 CFDictionarySetValue (mutableDict, kNetFSAlternatePortKey, PortNumber); 1189 } 1190 1191 error = smb_dictionary_to_urlstring(mutableDict, &urlString); 1192 CFRelease(mutableDict); 1193 1194 if (error) { 1195 errno = error; 1196 } 1197 1198 return urlString; 1199} 1200 1201/* 1202 * Check to make sure we have the correct user name and share. If we already have 1203 * both a username and share name then we are done, otherwise get the latest stuff. 1204 */ 1205static void UpdateDictionaryWithUserAndShare(struct smb_ctx *ctx, CFMutableDictionaryRef mutableDict) 1206{ 1207 CFStringRef DomainWrkgrp = NULL; 1208 CFStringRef Username = NULL; 1209 CFStringRef share = NULL; 1210 CFMutableStringRef path = NULL; 1211 1212 Username = CFDictionaryGetValue(mutableDict, kNetFSUserNameKey); 1213 share = CFDictionaryGetValue(mutableDict, kNetFSPathKey); 1214 1215 /* Everything we need is in the dictionary */ 1216 if (share && Username) 1217 return; 1218 1219 /* Add the user name, if we have one */ 1220 if (ctx->ct_setup.ioc_user[0]) { 1221 Username = CFStringCreateWithCString(NULL, ctx->ct_setup.ioc_user, kCFStringEncodingUTF8); 1222 if (ctx->ct_setup.ioc_domain[0]) /* They gave us a domain add it */ 1223 DomainWrkgrp = CFStringCreateWithCString(NULL, ctx->ct_setup.ioc_domain, kCFStringEncodingUTF8); 1224 1225 /* 1226 * We have a domain name so combined it with the user name. We now test 1227 * to make sure we have a username. Having a domain without a username 1228 * makes no sense, so don't return either. 1229 */ 1230 if (DomainWrkgrp && Username && CFStringGetLength(Username)) { 1231 CFMutableStringRef tempString = CFStringCreateMutableCopy(NULL, 0, DomainWrkgrp); 1232 1233 if (tempString) { 1234 CFStringAppend(tempString, CFSTR("\\")); 1235 CFStringAppend(tempString, Username); 1236 CFRelease(Username); 1237 Username = tempString; 1238 } 1239 } 1240 if (DomainWrkgrp) { 1241 CFRelease(DomainWrkgrp); 1242 } 1243 if (Username) 1244 { 1245 CFDictionarySetValue (mutableDict, kNetFSUserNameKey, Username); 1246 CFRelease(Username); 1247 } 1248 } 1249 /* if we have a share then we are done */ 1250 if (share || !ctx->ct_origshare) 1251 return; 1252 1253 path = CFStringCreateMutable(NULL, 1024); 1254 /* Should never happen, but just to be safe */ 1255 if (!path) 1256 return; 1257 1258 CFStringAppendCString(path, ctx->ct_origshare, kCFStringEncodingUTF8); 1259 /* Add the path if we have one */ 1260 if (ctx->mountPath) { 1261 CFStringAppend(path, CFSTR("/")); 1262 CFStringAppend(path, ctx->mountPath); 1263 } 1264 CFDictionarySetValue (mutableDict, kNetFSPathKey, path); 1265 CFRelease(path); 1266} 1267 1268/* 1269 * We need to create the from name. The from name is just a URL without the scheme. We 1270 * never put the password in the from name, but if they have an empty password then we 1271 * need to make sure that it's included. 1272 * 1273 * Examples: 1274 * URL "smb://username:@server/share" - Empty password, just remove the scheme. 1275 * URL "smb://username:password@server/share" - Need to remove the password and the scheme. 1276 * URL "smb://username@server" - Need to add the share and remove the scheme. 1277 * URL "smb://server" - Need to add the username and share and remove the scheme. 1278 * URL "smb://server/share/path" - Need to add the usernameand remove the scheme. 1279 */ 1280void CreateSMBFromName(struct smb_ctx *ctx, char *fromname, int maxlen) 1281{ 1282 CFMutableStringRef urlStringM = NULL; 1283 CFMutableStringRef newUrlStringM = NULL; 1284 CFMutableDictionaryRef mutableDict = NULL; 1285 CFStringRef Password = NULL; 1286 int SchemeLength = 0; 1287 int error = 0;; 1288 1289 /* Always start with the original url and a cleaned out the from name */ 1290 bzero(fromname, maxlen); 1291 SchemeLength = SMBSchemeLength(ctx->ct_url); 1292 urlStringM = CFStringCreateMutableCopy(NULL, 0, CFURLGetString(ctx->ct_url)); 1293 if (urlStringM == NULL) { 1294 smb_log_info("Failed creating URL string, syserr = %s", ASL_LEVEL_ERR, strerror(errno)); 1295 return; 1296 } 1297 1298 error = smb_url_to_dictionary(ctx->ct_url, (CFDictionaryRef *)&mutableDict); 1299 if (error || (mutableDict == NULL)) { 1300 smb_log_info("Failed parsing URL, syserr = %s", ASL_LEVEL_DEBUG, strerror(error)); 1301 goto WeAreDone; 1302 } 1303 UpdateDictionaryWithUserAndShare(ctx, mutableDict); 1304 1305 Password = CFDictionaryGetValue(mutableDict, kNetFSPasswordKey); 1306 /* 1307 * If there is a password and its not an empty password then remove it. Never 1308 * show the password in the mount from name. 1309 */ 1310 if (Password && (CFStringGetLength(Password) > 0)) { 1311 CFDictionaryRemoveValue(mutableDict, kNetFSPasswordKey); 1312 } 1313 /* Guest access has an empty password. */ 1314 if (ctx->ct_setup.ioc_userflags & SMBV_GUEST_ACCESS) 1315 CFDictionarySetValue (mutableDict, kNetFSPasswordKey, CFSTR("")); 1316 1317 /* 1318 * Recreate the URL from our new dictionary. The old code would not escape 1319 * out the share/path, which can cause us issue. Now the from name can look 1320 * pretty goofy, but we will always work with alias. Now this was originally 1321 * done because carbon would use the last part of the mount from name as the 1322 * volume name. We now return the volume name, so this is no longer an issue. 1323 */ 1324 error = smb_dictionary_to_urlstring(mutableDict, &newUrlStringM); 1325 if (error || (newUrlStringM == NULL)) { 1326 smb_log_info("Failed parsing dictionary, syserr = %s", ASL_LEVEL_DEBUG, 1327 strerror(error)); 1328 goto WeAreDone; 1329 } 1330 if (urlStringM) 1331 CFRelease(urlStringM); 1332 urlStringM = newUrlStringM; 1333 newUrlStringM = NULL; 1334 /* smb_dictionary_to_urlstring always uses the SMB scheme */ 1335 SchemeLength = SMB_SCHEME_LEN; 1336 if (CFStringGetLength(urlStringM) < (maxlen+SchemeLength)) 1337 goto WeAreDone; 1338 1339 /* 1340 * At this point the URL is too big to fit in the mount from name. See if 1341 * removing the username will make it fit. 1342 */ 1343 CFDictionaryRemoveValue(mutableDict, kNetFSUserNameKey); 1344 CFDictionaryRemoveValue(mutableDict, kNetFSPasswordKey); 1345 1346 1347 /* 1348 * Recreate the URL from our new dictionary. The old code would not escape 1349 * out the share/path, which can cause us issue. Now the from name can look 1350 * pretty goofy, but we will always work with alias. Now this was originally 1351 * done because carbon would use the last part of the mount from name as the 1352 * volume name. We now return the volume name, so this is no longer an issue. 1353 */ 1354 error = smb_dictionary_to_urlstring(mutableDict, &newUrlStringM); 1355 if (error || (newUrlStringM == NULL)) { 1356 smb_log_info("Removing username failed parsing dictionary, syserr = %s", 1357 ASL_LEVEL_DEBUG, strerror(error)); 1358 goto WeAreDone; 1359 } 1360 if (urlStringM) 1361 CFRelease(urlStringM); 1362 urlStringM = newUrlStringM; 1363 newUrlStringM = NULL; 1364 1365WeAreDone: 1366 if (urlStringM && (SchemeLength > 0)) { 1367 /* Remove the scheme, start at the begining */ 1368 CFRange range1 = CFRangeMake(0, SchemeLength); 1369 CFStringDelete(urlStringM, range1); 1370 } 1371 if (urlStringM) 1372 CFStringGetCString(urlStringM, fromname, maxlen, kCFStringEncodingUTF8); 1373 if (error) 1374 smb_log_info("Mount from name is %s, syserr = %s", ASL_LEVEL_ERR, 1375 fromname, strerror(error)); 1376 else 1377 smb_log_info("Mount from name is %s", ASL_LEVEL_DEBUG, fromname); 1378 1379 if (urlStringM) 1380 CFRelease(urlStringM); 1381 if (mutableDict) 1382 CFRelease(mutableDict); 1383} 1384 1385/* 1386 * See if this is a BTMM address 1387 */ 1388int isBTMMAddress(CFStringRef serverNameRef) 1389{ 1390 boolean_t foundBTMM; 1391 CFStringRef btmmRef; 1392 CFStringRef serverNameQual; 1393 CFIndex serverNameLen, btmmCount, serverCount, btmmIndex, srvIndex; 1394 CFArrayRef btmmArrRef, serverArrRef; 1395 CFStringRef btmmTmpRef, serverTmpRef; 1396 CFComparisonResult res; 1397 1398 foundBTMM = TRUE; 1399 btmmRef = NULL; 1400 serverNameQual = NULL; 1401 btmmArrRef = NULL; 1402 serverArrRef = NULL; 1403 1404 if (serverNameRef == NULL) { 1405 smb_log_info("%s: serverNameRef is NULL!", ASL_LEVEL_DEBUG, __FUNCTION__); 1406 foundBTMM = FALSE; 1407 goto out; 1408 } 1409 1410 serverNameLen = CFStringGetLength(serverNameRef); 1411 if (serverNameLen == 0) { 1412 smb_log_info("%s: serverNameRef len is 0!", ASL_LEVEL_DEBUG, __FUNCTION__); 1413 foundBTMM = FALSE; 1414 goto out; 1415 } 1416 1417 /* Create a copy of the server name, add a trailing '.' */ 1418 /* if it doesn't already have one. */ 1419 if (CFStringGetCharacterAtIndex(serverNameRef, serverNameLen - 1) == (UniChar)'.') { 1420 serverNameQual = CFStringCreateCopy(kCFAllocatorDefault, serverNameRef); 1421 } else { 1422 serverNameQual = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@."), serverNameRef); 1423 } 1424 1425 if (serverNameQual == NULL) { 1426 foundBTMM = FALSE; 1427 goto out; 1428 } 1429 1430 /* Fetch BTMM domain from DynamicStore */ 1431 btmmRef = _CSBackToMyMacCopyDomain(); 1432 if (btmmRef == NULL) { 1433 foundBTMM = FALSE; 1434 goto out; 1435 } 1436 1437 /* Split them into component string arrays */ 1438 btmmArrRef = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, btmmRef, CFSTR(".")); 1439 btmmCount = CFArrayGetCount(btmmArrRef); 1440 1441 serverArrRef = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, serverNameQual, CFSTR(".")); 1442 serverCount = CFArrayGetCount(serverArrRef); 1443 1444 if (btmmCount == 0 || serverCount == 0) { 1445 foundBTMM = FALSE; 1446 goto out; 1447 } 1448 1449 if (btmmCount > serverCount) { 1450 /* Not a BTMM domain */ 1451 foundBTMM = FALSE; 1452 goto out; 1453 } 1454 1455 for (btmmIndex = btmmCount - 1, srvIndex = serverCount - 1; btmmIndex >= 0; btmmIndex--, srvIndex--) { 1456 btmmTmpRef = CFArrayGetValueAtIndex(btmmArrRef, btmmIndex); 1457 serverTmpRef = CFArrayGetValueAtIndex(serverArrRef, srvIndex); 1458 1459 res = CFStringCompare(btmmTmpRef, serverTmpRef, kCFCompareCaseInsensitive); 1460 if (res != kCFCompareEqualTo) { 1461 /* Not a BTMM domain */ 1462 foundBTMM = FALSE; 1463 break; 1464 } 1465 } 1466 1467 if (foundBTMM == TRUE) { 1468 smb_log_info("%s: found a btmm address", ASL_LEVEL_DEBUG, __FUNCTION__); 1469 } 1470 1471out: 1472 1473 // Clean up mem 1474 if (btmmArrRef != NULL) { 1475 CFRelease(btmmArrRef); 1476 } 1477 if (serverArrRef != NULL) { 1478 CFRelease(serverArrRef); 1479 } 1480 if (btmmRef != NULL) { 1481 CFRelease(btmmRef); 1482 } 1483 if (serverNameQual !=NULL) { 1484 CFRelease(serverNameQual); 1485 } 1486 1487 return (foundBTMM); 1488} 1489