1/* 2 * Copyright (c) 2009 - 2010 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 "ntstatus.h" 25#include "smbclient.h" 26#include "netbios.h" 27#include "smbclient_internal.h" 28#include "smbclient_private.h" 29 30#include <netsmb/netbios.h> 31#include <netsmb/smb_lib.h> 32#include <netsmb/nb_lib.h> 33#include <netsmb/smb_conn.h> 34#include "charsets.h" 35#include "parse_url.h" 36#include "remount.h" 37#include <netsmb/upi_mbuf.h> 38#include <sys/mchain.h> 39#include "msdfs.h" 40#include <smbfs/smbfs.h> 41 42void SMBLogInfo(const char *fmt, int log_level,...) 43{ 44 int save_errno = errno; 45 va_list ap; 46 aslmsg m = asl_new(ASL_TYPE_MSG); 47 48 49 va_start(ap, log_level); 50 asl_vlog(NULL, m, log_level, fmt, ap); 51 va_end(ap); 52 asl_free(m); 53 errno = save_errno; /* Never let this routine change errno */ 54} 55 56/* Find all address associated with the NetBIOS name */ 57struct sockaddr_storage * 58SMBResolveNetBIOSNameEx(const char *hostName, uint8_t nodeType, 59 const char *winServer, uint32_t timeout, 60 struct sockaddr_storage *respAddr, int32_t *outCount) 61{ 62 struct sockaddr_storage *outAddr = NULL, *listAddr; 63 char *netbios_name = NULL; 64 struct nb_ctx ctx; 65 CFMutableArrayRef addressArray = NULL; 66 CFMutableDataRef addressData; 67 struct connectAddress *conn; 68 CFIndex ii; 69 int error = 0; 70 struct smb_prefs prefs; 71 72 bzero(&ctx, sizeof(struct nb_ctx)); 73 /* Read the preference files */ 74 readPreferences(&prefs, NULL, NULL, FALSE, TRUE); 75 76 /* They gave us a wins server use it */ 77 if (winServer) { 78 setWINSAddress(&prefs, winServer, 1); 79 } 80 /* They gave us a timeout value use it */ 81 if ((int32_t)timeout > 0) { 82 prefs.NetBIOSResolverTimeout = timeout; 83 } 84 85 /* 86 * We uppercase and convert the server name given in the URL to Windows Code 87 * Page. 88 */ 89 netbios_name = convert_utf8_to_wincs(hostName, prefs.WinCodePage, TRUE); 90 if (netbios_name == NULL) { 91 error = ENOMEM; 92 goto done; 93 } 94 /* Only returns IPv4 address */ 95 error = nbns_resolvename(&ctx, &prefs, netbios_name, nodeType, &addressArray, 96 SMB_TCP_PORT_445, TRUE, FALSE, NULL); 97 if (error) { 98 goto done; 99 } 100 101 if (respAddr) { 102 memcpy(respAddr, &ctx.nb_sender, sizeof(ctx.nb_sender)); 103 } 104 105 listAddr = outAddr = malloc(CFArrayGetCount(addressArray) * sizeof(struct sockaddr_storage)); 106 if (outAddr == NULL) { 107 error = ENOMEM; 108 goto done; 109 } 110 111 for (ii=0; ii < CFArrayGetCount(addressArray); ii++) { 112 addressData = (CFMutableDataRef)CFArrayGetValueAtIndex(addressArray, ii); 113 if (addressData) { 114 conn = (struct connectAddress *)(void *)CFDataGetMutableBytePtr(addressData); 115 if (conn) { 116 *outCount += 1; 117 *listAddr++ = conn->storage; 118 } 119 } 120 } 121 if (*outCount == 0) { 122 free(outAddr); 123 outAddr = NULL; /* Didn't really find any */ 124 error = EHOSTUNREACH; 125 } 126done: 127 if (addressArray) 128 CFRelease(addressArray); 129 130 if (netbios_name) { 131 free(netbios_name); 132 } 133 releasePreferenceInfo(&prefs); 134 errno = error; 135 return outAddr; 136} 137 138 139/* Find all address associated with the NetBIOS name */ 140ssize_t SMBResolveNetBIOSName(const char * hostName, uint8_t nodeType, 141 uint32_t timeout, struct sockaddr_storage ** results) 142{ 143 int32_t outCount = 0; 144 145 *results = SMBResolveNetBIOSNameEx(hostName, nodeType, NULL, timeout, NULL, &outCount); 146 /* We didn't time out, report to the calling process that we had an error */ 147 if (errno && (errno != EHOSTUNREACH)) { 148 return -1; 149 } 150 151 return outCount; 152} 153 154int 155SMBFrameworkVersion(void) { 156 return SMBFS_VERSION; 157} 158 159/* Convert from UTF8 using system code page */ 160char * 161SMBConvertFromUTF8ToCodePage(const char *utf8Str, int uppercase) 162{ 163 return convert_utf8_to_wincs(utf8Str, getPrefsCodePage(), uppercase); 164 165} 166 167/* Convert to UTF8 using system code page */ 168char * 169SMBConvertFromCodePageToUTF8(const char *cpStr) 170{ 171 return convert_wincs_to_utf8(cpStr, getPrefsCodePage()); 172 173} 174 175/* Convert UTF16 to UTF8 string */ 176char * 177SMBConvertFromUTF16ToUTF8(const uint16_t *utf16str, size_t maxLen, uint64_t options) 178{ 179#pragma unused(options) 180 /* XXX - TBD Currently we ignore the options */ 181 return convert_unicode_to_utf8(utf16str, maxLen); 182 183} 184 185/* Convert UTF16 to UTF8 string */ 186uint16_t * 187SMBConvertFromUTF8ToUTF16(const char *utf8str, size_t maxLen, uint64_t options) 188{ 189#pragma unused(maxLen, options) 190 /* XXX - TBD Currently we ignore the length field and options */ 191 return convert_utf8_to_leunicode(utf8str); 192 193} 194 195/* Find all the NetBIOS names associated using supplied host name */ 196struct NodeStatusInfo * 197SMBGetNodeStatus(const char *hostName, uint32_t *outCount) 198{ 199 struct NodeStatusInfo *outAddr = NULL, *listAddr; 200 CFMutableArrayRef addressArray = NULL; 201 CFMutableDataRef theData; 202 CFMutableArrayRef nbrrArray; 203 CFIndex ii; 204 uint32_t jj; 205 struct connectAddress *conn; 206 struct nb_ctx ctx; 207 struct smb_prefs prefs; 208 int error; 209 210 bzero(&ctx, sizeof(struct nb_ctx)); 211 /* Read the preference files */ 212 readPreferences(&prefs, NULL, NULL, FALSE, TRUE); 213 /* Resolve the host name into a list of address */ 214 error = resolvehost(hostName, &addressArray, NULL, NBNS_UDP_PORT_137, TRUE, FALSE); 215 if (error) { 216 goto done; 217 } 218 /* Allocate the memory needed to hold the list of address and info */ 219 listAddr = outAddr = malloc(CFArrayGetCount(addressArray) * sizeof(struct NodeStatusInfo)); 220 if (outAddr == NULL) { 221 error = ENOMEM; 222 goto done; 223 } 224 225 for (ii=0; ii < CFArrayGetCount(addressArray); ii++) { 226 theData = (CFMutableDataRef)CFArrayGetValueAtIndex(addressArray, ii); 227 if (! theData) { 228 continue; 229 } 230 conn = (struct connectAddress *)(void *)CFDataGetMutableBytePtr(theData); 231 if (!conn) { 232 continue; 233 } 234 memcpy(&listAddr->node_storage, &conn->storage, sizeof(conn->storage)); 235 listAddr->node_servername[0] = (char)0; 236 listAddr->node_workgroupname[0] = (char)0; 237 listAddr->node_nbrrArray = NULL; 238 /* Creating the array causes nbns_getnodestatus to return a full list */ 239 nbrrArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, 240 &kCFTypeArrayCallBacks ); 241 listAddr->node_errno = nbns_getnodestatus(&conn->addr, &ctx, &prefs, NULL, 242 listAddr->node_servername, 243 listAddr->node_workgroupname, 244 nbrrArray); 245 246 if (nbrrArray && (listAddr->node_errno == 0)) { 247 struct NBResourceRecord *nbrrDest; 248 249 listAddr->node_nbrrArrayCnt = (uint32_t)CFArrayGetCount(nbrrArray); 250 if (listAddr->node_nbrrArrayCnt) { 251 listAddr->node_nbrrArray = malloc(listAddr->node_nbrrArrayCnt * sizeof(struct NBResourceRecord)); 252 } 253 nbrrDest = listAddr->node_nbrrArray; 254 if (listAddr->node_nbrrArray) 255 for (jj=0; jj < listAddr->node_nbrrArrayCnt; jj++) { 256 struct NBResourceRecord *nbrrSrc = NULL; 257 258 theData = (CFMutableDataRef)CFArrayGetValueAtIndex(nbrrArray, jj); 259 if (theData) { 260 nbrrSrc = (struct NBResourceRecord *)(void *)CFDataGetMutableBytePtr(theData); 261 } 262 if (nbrrSrc == NULL) { 263 listAddr->node_errno = ENOMEM; 264 break; 265 } 266 memcpy(nbrrDest, nbrrSrc, sizeof(*nbrrSrc)); 267 nbrrDest++; 268 } 269 } 270 if ((listAddr->node_errno) && (listAddr->node_nbrrArray)) { 271 free(listAddr->node_nbrrArray); 272 listAddr->node_nbrrArray = NULL; 273 listAddr->node_nbrrArrayCnt = 0; 274 } 275 /* Done release it */ 276 if (nbrrArray) { 277 CFRelease(nbrrArray); 278 } 279 listAddr++; 280 *outCount += 1; 281 } 282 283 if (*outCount == 0) { 284 free(outAddr); 285 outAddr = NULL; /* Didn't really find any */ 286 error = EHOSTUNREACH; 287 } 288 289done: 290 if (addressArray) { 291 CFRelease(addressArray); 292 } 293 releasePreferenceInfo(&prefs); 294 errno = error; 295 return outAddr; 296} 297 298int SMBCheckForAlreadyMountedShare(SMBHANDLE inConnection, 299 CFStringRef shareRef, CFMutableDictionaryRef mdictRef, 300 struct statfs *fs, int fs_cnt) 301{ 302 void * hContext; 303 NTSTATUS status; 304 CFMutableStringRef upperStringRef; 305 char ShareName[SMB_MAXSHARENAMELEN + 1]; 306 307 memset(ShareName, 0, sizeof(ShareName)); 308 status = SMBServerContext(inConnection, &hContext); 309 if (!NT_SUCCESS(status)) { 310 return EBADF; 311 } 312 upperStringRef = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, shareRef); 313 if (upperStringRef == NULL) { 314 return ENOMEM; 315 } 316 CFStringUppercase(upperStringRef, NULL); 317 CFStringGetCString(upperStringRef, ShareName, SMB_MAXSHARENAMELEN + 1, kCFStringEncodingUTF8); 318 CFRelease(upperStringRef); 319 return already_mounted(hContext, ShareName, fs, fs_cnt, mdictRef, 0); 320} 321 322int SMBSetNetworkIdentity(SMBHANDLE inConnection, void *network_sid, char *account, char *domain) 323{ 324 NTSTATUS status; 325 int error; 326 void *hContext = NULL; 327 ntsid_t *ntsid = (ntsid_t *)network_sid; 328 struct smbioc_ntwrk_identity ntwrkID; 329 330 status = SMBServerContext(inConnection, &hContext); 331 if (!NT_SUCCESS(status)) { 332 /* Couldn't get the context? */ 333 return EINVAL; 334 } 335 memset(&ntwrkID, 0, sizeof(ntwrkID)); 336 ntwrkID.ioc_version = SMB_IOC_STRUCT_VERSION; 337 ntwrkID.ioc_ntsid_len = sizeof(*ntsid); 338 ntwrkID.ioc_ntsid = *ntsid; 339 if (account) { 340 strlcpy(ntwrkID.ioc_ntwrk_account, account, sizeof(ntwrkID.ioc_ntwrk_account)); 341 } 342 if (domain) { 343 strlcpy(ntwrkID.ioc_ntwrk_domain, domain, sizeof(ntwrkID.ioc_ntwrk_domain)); 344 } 345 if (smb_ioctl_call(((struct smb_ctx *)hContext)->ct_fd, SMBIOC_NTWRK_IDENTITY, &ntwrkID) == -1) { 346 error = errno; 347 smb_log_info("The SMBIOC_NTWRK_IDENTITY call failed, syserr = %s", 348 ASL_LEVEL_DEBUG, strerror(error)); 349 return error; 350 } 351 return 0; 352} 353 354char *SMBCreateURLString(const char *domain, const char * user, const char * passwd, 355 const char *server, const char *path, int32_t port) 356{ 357 CFStringRef DomainName = NULL; 358 CFStringRef Username = NULL; 359 CFStringRef Password = NULL; 360 CFStringRef ServerName = NULL; 361 CFStringRef FullPath = NULL; 362 CFStringRef PortNumber = NULL; 363 CFStringRef URLString = NULL; 364 char *urlStr = NULL; 365 366 if (domain) { 367 DomainName = CFStringCreateWithCString(kCFAllocatorDefault, domain, kCFStringEncodingUTF8); 368 } 369 370 if (user) { 371 Username = CFStringCreateWithCString(kCFAllocatorDefault, user, kCFStringEncodingUTF8); 372 } 373 374 if (passwd) { 375 Password = CFStringCreateWithCString(kCFAllocatorDefault, passwd, kCFStringEncodingUTF8); 376 } 377 378 if (server) { 379 ServerName = CFStringCreateWithCString(kCFAllocatorDefault, server, kCFStringEncodingUTF8); 380 } 381 382 if (path) { 383 FullPath = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8); 384 } 385 386 if (port != -1) { 387 PortNumber = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR("%d"), port); 388 } 389 URLString = CreateURLCFString(DomainName, Username, Password, ServerName, FullPath, PortNumber); 390 if (URLString) { 391 CFIndex maxLen = (CFStringGetLength(URLString) * 3) + 1; 392 urlStr = calloc(1, maxLen); 393 if (urlStr) { 394 CFStringGetCString(URLString, urlStr, maxLen, kCFStringEncodingUTF8); 395 } 396 CFRelease(URLString); 397 } 398 if (DomainName) { 399 CFRelease(DomainName); 400 } 401 if (Username) { 402 CFRelease(Username); 403 } 404 if (Password) { 405 CFRelease(Password); 406 } 407 if (ServerName) { 408 CFRelease(ServerName); 409 } 410 if (FullPath) { 411 CFRelease(FullPath); 412 } 413 if (PortNumber) { 414 CFRelease(PortNumber); 415 } 416 return urlStr; 417} 418 419CFStringRef 420SMBCreateNetBIOSName(CFStringRef proposedName) 421{ 422 CFMutableStringRef composedName; 423 CFStringEncoding codepage; 424 CFIndex nconverted = 0; 425 CFIndex nused = 0; 426 427 uint8_t name_buffer[NetBIOS_NAME_LEN]; 428 429 if (proposedName == NULL) { 430 return NULL; 431 } 432 433 codepage = getPrefsCodePage(); 434 435 composedName = CFStringCreateMutableCopy(kCFAllocatorDefault, 436 0, proposedName); 437 if (!composedName) { 438 return NULL; 439 } 440 CFStringTrimWhitespace(composedName); 441 CFStringUppercase(composedName, CFLocaleGetSystem()); 442 443 nconverted = CFStringGetBytes(composedName, 444 CFRangeMake(0, 445 MIN((CFIndex)sizeof(name_buffer), CFStringGetLength(composedName))), 446 codepage, 0 /* loss byte */, false /* no BOM */, 447 name_buffer, sizeof(name_buffer), &nused); 448 449 /* We expect the conversion above to always succeed, given that we 450 * tried to remove anything that might not convert to a code page. 451 */ 452 if (nconverted == 0) { 453 char buf[256]; 454 455 buf[0] = '\0'; 456 CFStringGetCString(composedName, buf, sizeof(buf), kCFStringEncodingUTF8); 457 SMBLogInfo("failed to compose a NetBIOS name string from '%s'", 458 ASL_LEVEL_DEBUG, buf); 459 460 CFRelease(composedName); 461 return NULL; 462 } 463 464 CFRelease(composedName); 465 466 /* Null terminate for the benefit of CFStringCreate. Be careful to be 467 * no more that 15 bytes, since the last byte is reserved for the name 468 * type. 469 */ 470 name_buffer[MIN(nused, (CFIndex)sizeof(name_buffer) - 1)] = '\0'; 471 472 composedName = CFStringCreateMutable(kCFAllocatorDefault, 473 NetBIOS_NAME_LEN); 474 if (composedName == NULL) { 475 return NULL; 476 } 477 478 CFStringAppendCString(composedName, (const char *)name_buffer, codepage); 479 return composedName; 480} 481 482 483void 484SMBRemountServer(const void *inputBuffer, size_t inputBufferLen) 485{ 486 fsid_t fsid = *(fsid_t *)inputBuffer; 487 488 if (inputBufferLen == sizeof(fsid)) { 489 int error = smb_remount_with_fsid(fsid); 490 if (error) { 491 SMBLogInfo("%s: error = %d inputBuffer = %p or inputBufferLen = %zu", 492 ASL_LEVEL_DEBUG, __FUNCTION__, error, inputBuffer, inputBufferLen); 493 } 494 } else { 495 SMBLogInfo("%s: Bad inputBuffer = %p or inputBufferLen = %zu", 496 ASL_LEVEL_DEBUG, __FUNCTION__, inputBuffer, inputBufferLen); 497 } 498} 499 500int 501SMBGetDfsReferral(const char * url, CFMutableDictionaryRef dfsReferralDict) 502{ 503 int error; 504 NTSTATUS status; 505 SMBHANDLE serverConnection; 506 void * hContext = NULL; 507 508 if (!dfsReferralDict) { 509 errno = EINVAL; 510 return -1; 511 } 512 513 status = SMBOpenServerEx(url, &serverConnection, kSMBOptionSessionOnly); 514 /* SMBOpenServerEx sets errno, */ 515 if (!NT_SUCCESS(status)) { 516 return -1; 517 } 518 519 status = SMBServerContext(serverConnection, &hContext); 520 if (!NT_SUCCESS(status)) { 521 /* Couldn't get the context? */ 522 SMBReleaseServer(serverConnection); 523 return -1; 524 } 525 526 error = getDfsReferralList(hContext, dfsReferralDict); 527 if (error) { 528 SMBReleaseServer(serverConnection); 529 errno = error; 530 return -1; 531 } 532 533 SMBReleaseServer(serverConnection); 534 return 0; 535} 536