1/* 2 * Copyright (c) 2010 - 2013 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 <SystemConfiguration/SystemConfiguration.h> 26#include <netsmb/smb_lib.h> 27#include <smbclient/smbclient.h> 28#include <smbclient/smbclient_internal.h> 29#include "rcfile.h" 30#include "preference.h" 31#include "smb_preferences.h" 32 33#define defaultCodePage 437 34 35 36/* 37 * level values: 38 * 0 - default/global 39 * 1 - server 40 * 2 - server:share 41 */ 42static void readPreferenceSection(struct rcfile *rcfile, struct smb_prefs *prefs, 43 const char *sname, int level) 44{ 45 char *p; 46 int32_t altflags; 47 48 /* global only preferences */ 49 if (level == 0) { 50 /* 51 * Neither of these are defined in the man pages. We should add 52 * kernel log level, then in future remove debug level. 53 */ 54 rc_getint(rcfile, sname, "debug_level", &prefs->KernelLogLevel); 55 rc_getint(rcfile, sname, "kloglevel", &prefs->KernelLogLevel); 56 57 /* 58 * Check for SMB 1, SMB 2/3 Negotiation. Default is to start with SMB 1 59 * and try to negotiate to SMB 2/3 60 * 0 = try both SMB 1/2/3 61 * 1 = SMB 1 only 62 * 2 = SMB 2 only 63 * 3 = SMB 3 only 64 */ 65 rc_getstringptr(rcfile, sname, "smb_neg", &p); 66 if (p) { 67 if (strcmp(p, "normal") == 0) { 68 /* start with SMB 1 and try for SMB 2/3 */ 69 prefs->smb_negotiate = 0; 70 71 /* if SMB 1 or SMB 2/3, then also turn off netbios */ 72 if (prefs->tryBothPorts) { 73 prefs->tryBothPorts = FALSE; 74 prefs->tcp_port = SMB_TCP_PORT_445; 75 } 76 } 77 else { 78 if (strcmp(p, "smb1_only") == 0) { 79 prefs->smb_negotiate = 1; 80 } 81 else if ((strcmp(p, "smb2_only") == 0) || 82 (strcmp(p, "smb3_only") == 0)) { 83 if (strcmp(p, "smb2_only") == 0) { 84 prefs->smb_negotiate = 2; 85 } 86 else { 87 prefs->smb_negotiate = 3; 88 } 89 90 /* if SMB 2/3 only, then also turn off netbios */ 91 if (prefs->tryBothPorts) { 92 prefs->tryBothPorts = FALSE; 93 prefs->tcp_port = SMB_TCP_PORT_445; 94 } 95 } 96 } 97 } 98 99 /* Check for Require Signing */ 100 /* Only get the value if it exist, ignore any error we don't care */ 101 (void)rc_getbool(rcfile, sname, "signing_required", (int *) &prefs->signing_required); 102 103 /* Only get the value if it exists */ 104 if (rc_getbool(rcfile, sname, "validate_neg_off", &altflags) == 0) { 105 if (altflags) 106 prefs->altflags |= SMBFS_MNT_VALIDATE_NEG_OFF; 107 else 108 prefs->altflags &= ~SMBFS_MNT_VALIDATE_NEG_OFF; 109 } 110 } 111 112 /* server only preferences */ 113 if (level == 1) { 114 rc_getstringptr(rcfile, sname, "addr", &p); 115 if (p) { 116 if (prefs->NetBIOSDNSName) { 117 CFRelease(prefs->NetBIOSDNSName); 118 } 119 prefs->NetBIOSDNSName = CFStringCreateWithCString(kCFAllocatorSystemDefault, p, kCFStringEncodingUTF8); 120 } 121 } 122 123 /* global or server preferences */ 124 if ((level == 0) || (level == 1)) { 125 rc_getint(rcfile, sname, "nbtimeout", &prefs->NetBIOSResolverTimeout); 126 /* Make sure they set it to something */ 127 if (prefs->NetBIOSResolverTimeout == 0) { 128 prefs->NetBIOSResolverTimeout = DefaultNetBIOSResolverTimeout; 129 } 130 131 /* 132 * We default to trying both ports, if this is not set then the URL 133 * is overriding the preference, ignore the preference file setting. 134 */ 135 if (prefs->tryBothPorts) { 136 rc_getstringptr(rcfile, sname, "port445", &p); 137 /* See if the configuration file wants us to use a specific port. */ 138 if (p) { 139 if (strcmp(p, "netbios_only") == 0) { 140 prefs->tryBothPorts = FALSE; 141 prefs->tcp_port = NBSS_TCP_PORT_139; 142 } 143 else if (strcmp(p, "no_netbios") == 0) { 144 prefs->tryBothPorts = FALSE; 145 prefs->tcp_port = SMB_TCP_PORT_445; 146 } 147 } 148 } 149 150 /* Really should be getting this from System Configuration */ 151 rc_getstringptr(rcfile, sname, "minauth", &p); 152 if (p) { 153 /* 154 * "minauth" was set in this section; override 155 * the current minimum authentication setting. 156 */ 157 if (strcmp(p, "kerberos") == 0) { 158 /* 159 * Don't fall back to NTLMv2, NTLMv1, or 160 * a clear text password. 161 */ 162 prefs->minAuthAllowed = SMB_MINAUTH_KERBEROS; 163 } else if (strcmp(p, "ntlmv2") == 0) { 164 /* 165 * Don't fall back to NTLMv1 or a clear 166 * text password. 167 */ 168 prefs->minAuthAllowed = SMB_MINAUTH_NTLMV2; 169 } else if (strcmp(p, "ntlm") == 0) { 170 /* 171 * Don't send the LM response over the wire. 172 */ 173 prefs->minAuthAllowed = SMB_MINAUTH_NTLM; 174 } else if (strcmp(p, "lm") == 0) { 175 /* 176 * Fail if the server doesn't do encrypted 177 * passwords. 178 */ 179 prefs->minAuthAllowed = SMB_MINAUTH_LM; 180 } else if (strcmp(p, "none") == 0) { 181 /* 182 * Anything goes. 183 * (The following statement should be 184 * optimized away.) 185 */ 186 prefs->minAuthAllowed = SMB_MINAUTH; 187 } 188 } 189 190 rc_getint(rcfile, sname, "max_resp_timeout", &prefs->max_resp_timeout); 191 /* Make sure they set it to something reasonable */ 192 if (prefs->max_resp_timeout > 600) { 193 prefs->max_resp_timeout = 600; /* 10 mins is a long, long time */ 194 } 195 } 196 197 /* global, server, user, or share preferences */ 198 199 /* Only get the value if it exist */ 200 if (rc_getbool(rcfile, sname, "compound_on", &altflags) == 0) { 201 if (altflags) 202 prefs->altflags |= SMBFS_MNT_COMPOUND_ON; 203 else 204 prefs->altflags &= ~SMBFS_MNT_COMPOUND_ON; 205 } 206 207 /* Only get the value if it exist */ 208 if (rc_getbool(rcfile, sname, "notify_off", &altflags) == 0) { 209 if (altflags) 210 prefs->altflags |= SMBFS_MNT_NOTIFY_OFF; 211 else 212 prefs->altflags &= ~SMBFS_MNT_NOTIFY_OFF; 213 } 214 215 /* Only get the value if it exist */ 216 if (rc_getbool(rcfile, sname, "streams", &altflags) == 0) { 217 if (altflags) 218 prefs->altflags |= SMBFS_MNT_STREAMS_ON; 219 else 220 prefs->altflags &= ~SMBFS_MNT_STREAMS_ON; 221 } 222 223 /* Only get the value if it exist */ 224 if ( rc_getbool(rcfile, sname, "soft", &altflags) == 0) { 225 if (altflags) 226 prefs->altflags |= SMBFS_MNT_SOFT; 227 else 228 prefs->altflags &= ~SMBFS_MNT_SOFT; 229 } 230 231 /* 232 * Start of the HIDDEN options of nsmb. 233 */ 234 235 /* 236 * We are not adding this in the man pages, because we do not want to keep 237 * this as a configuration option. This is for debug purposes only and 238 * should be removed once <rdar://problem/7236779> is complete. Force 239 * ACLs on if we have a network sid. 240 */ 241 if (rc_getbool(rcfile, sname, "debug_acl_on", &altflags) == 0) { 242 if (altflags) 243 prefs->altflags |= SMBFS_MNT_DEBUG_ACL_ON; 244 else 245 prefs->altflags &= ~SMBFS_MNT_DEBUG_ACL_ON; 246 } 247 248 /* 249 * Another hidden config option. Force readdirattr off 250 */ 251 if (rc_getbool(rcfile, sname, "readdirattr_off", &altflags) == 0) { 252 if (altflags) 253 prefs->altflags |= SMBFS_MNT_READDIRATTR_OFF; 254 else 255 prefs->altflags &= ~SMBFS_MNT_READDIRATTR_OFF; 256 } 257 258 /* 259 * Another hidden config option, to force LANMAN on 260 */ 261 if (rc_getbool(rcfile, sname, "lanman_on", &altflags) == 0) { 262 if (altflags) { 263 prefs->lanman_on = 1; 264 smb_log_info("%s: LANMAN support enabled", ASL_LEVEL_DEBUG, __FUNCTION__); 265 } else { 266 prefs->lanman_on = 0; 267 } 268 } 269 270 /* 271 * Another hidden config option, to force Kerberos off 272 * When <12991970> is fixed, remove this code 273 */ 274 if (rc_getbool(rcfile, sname, "kerberos_off", &altflags) == 0) { 275 if (altflags) { 276 prefs->altflags |= SMBFS_MNT_KERBEROS_OFF; 277 } else { 278 prefs->altflags &= ~SMBFS_MNT_KERBEROS_OFF; 279 } 280 } 281 282 /* 283 * Another hidden config option, to force File IDs off 284 */ 285 if (rc_getbool(rcfile, sname, "file_ids_off", &altflags) == 0) { 286 if (altflags) { 287 prefs->altflags |= SMBFS_MNT_FILE_IDS_OFF; 288 } else { 289 prefs->altflags &= ~SMBFS_MNT_FILE_IDS_OFF; 290 } 291 } 292 293 /* 294 * Another hidden config option, to force AAPL off 295 */ 296 if (rc_getbool(rcfile, sname, "aapl_off", &altflags) == 0) { 297 if (altflags) { 298 prefs->altflags |= SMBFS_MNT_AAPL_OFF; 299 } else { 300 prefs->altflags &= ~SMBFS_MNT_AAPL_OFF; 301 } 302 } 303} 304 305static CFStringRef getLocalNetBIOSNameUsingHostName() 306{ 307 CFMutableStringRef NetBIOSName = NULL; 308 char buf[_POSIX_HOST_NAME_MAX+1], *cp; 309 310 if (gethostname(buf, sizeof(buf)) != 0) { 311 smb_log_info("%s: Couldn't obtain the Local NetBIOS Name using gethostname", 312 ASL_LEVEL_DEBUG, __FUNCTION__); 313 return NULL; 314 } 315 cp = strchr(buf, '.'); 316 if (cp) 317 *cp = 0; 318 buf[MIN(SMB_MAXNetBIOSNAMELEN, _POSIX_HOST_NAME_MAX)] = 0; 319 NetBIOSName = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); 320 if (NetBIOSName) { 321 CFStringAppendCString(NetBIOSName, buf, kCFStringEncodingUTF8); 322 CFStringUppercase(NetBIOSName, CFLocaleGetSystem()); 323 } 324 return NetBIOSName; 325} 326 327 328/* 329 * Retrieve any SMB System Configuration Preference. This routine always succeeds, 330 * on any failure we fill in the default values. 331 */ 332static void getSCPreferences(struct smb_prefs *prefs) 333{ 334 SCPreferencesRef scPrefs; 335 CFMutableStringRef NetBIOSName = NULL; 336 CFStringRef DOSCodePage; 337 338 scPrefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("SMB Client"), CFSTR(kSMBPreferencesAppID)); 339 if (!scPrefs) { 340 smb_log_info("%s: Couldn't obtain system config preferences: %s", 341 ASL_LEVEL_DEBUG, __FUNCTION__, strerror(errno)); 342 prefs->WINSAddresses = NULL; 343 prefs->WinCodePage = CFStringConvertWindowsCodepageToEncoding(defaultCodePage); 344 goto done; 345 } 346 NetBIOSName = (CFMutableStringRef)SCPreferencesGetValue(scPrefs, CFSTR(kSMBPrefNetBIOSName)); 347 if (NetBIOSName) { 348 NetBIOSName = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, NetBIOSName); 349 } 350 if (NetBIOSName) { 351 CFStringUppercase(NetBIOSName, CFLocaleGetSystem()); 352 } 353 prefs->LocalNetBIOSName = NetBIOSName; 354 355 prefs->WINSAddresses = SCPreferencesGetValue(scPrefs, CFSTR(kSMBPrefWINSServerAddressList)); 356 if (prefs->WINSAddresses) { 357 CFRetain(prefs->WINSAddresses); 358#ifdef SMB_DEBUG 359 char wins[SMB_MAX_DNS_SRVNAMELEN+1]; 360 CFIndex ii, count = CFArrayGetCount(prefs->WINSAddresses); 361 362 for (ii=0; ii < count; ii++) { 363 CFStringRef winString = CFArrayGetValueAtIndex(prefs->WINSAddresses, ii); 364 wins[0] = 0; 365 CFStringGetCString(winString, wins, sizeof(wins), kCFStringEncodingUTF8); 366 smb_log_info("WINS[%d] \"%s\" ", ASL_LEVEL_ERR, (int)ii, wins); 367 } 368#endif // SMB_DEBUG 369 } 370 DOSCodePage = SCPreferencesGetValue(scPrefs, CFSTR(kSMBPrefDOSCodePage)); 371 if (DOSCodePage) { 372 if ((CFStringHasPrefix(DOSCodePage, CFSTR("CP")) == FALSE) && (CFStringHasPrefix(DOSCodePage, CFSTR("cp")) == FALSE)) { 373 CFMutableStringRef WinCodePageStr = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, CFSTR("cp")); 374 if (WinCodePageStr) { 375 CFStringAppend(WinCodePageStr, DOSCodePage); 376 prefs->WinCodePage = CFStringConvertIANACharSetNameToEncoding(WinCodePageStr); 377 CFRelease(WinCodePageStr); 378 goto done; 379 } 380 } else { 381 prefs->WinCodePage = CFStringConvertIANACharSetNameToEncoding(DOSCodePage); 382 goto done; 383 } 384 } 385 /* We have no other choice use the default code page */ 386 prefs->WinCodePage = CFStringConvertWindowsCodepageToEncoding(defaultCodePage); 387done: 388 if (scPrefs) { 389 CFRelease(scPrefs); 390 } 391 if (prefs->LocalNetBIOSName == NULL) { 392 /* If all else fail try using the host name */ 393 prefs->LocalNetBIOSName = getLocalNetBIOSNameUsingHostName(); 394 } 395 /* Test to make sure we have a code page we can use. */ 396 if (CFStringIsEncodingAvailable(prefs->WinCodePage) == FALSE) { 397 prefs->WinCodePage = CFStringConvertWindowsCodepageToEncoding(defaultCodePage); 398 } 399 400} 401 402void getDefaultPreferences(struct smb_prefs *prefs) 403{ 404 /* <11860141> Disable LANMAN (RAP) for getting share lists */ 405 memset(prefs, 0, sizeof(*prefs)); 406 prefs->tryBothPorts = TRUE; 407 prefs->tcp_port = SMB_TCP_PORT_445; 408 prefs->altflags = SMBFS_MNT_STREAMS_ON | SMBFS_MNT_COMPOUND_ON; 409 prefs->minAuthAllowed = SMB_MINAUTH_NTLMV2; 410 prefs->NetBIOSResolverTimeout = DefaultNetBIOSResolverTimeout; 411 412 /* Now get any values stored in the System Configuration */ 413 getSCPreferences(prefs); 414} 415 416void releasePreferenceInfo(struct smb_prefs *prefs) 417{ 418 if (prefs->LocalNetBIOSName) { 419 CFRelease(prefs->LocalNetBIOSName); 420 prefs->LocalNetBIOSName = NULL; 421 } 422 if (prefs->WINSAddresses) { 423 CFRelease(prefs->WINSAddresses); 424 prefs->WINSAddresses = NULL; 425 } 426 if (prefs->NetBIOSDNSName) { 427 CFRelease(prefs->NetBIOSDNSName); 428 prefs->NetBIOSDNSName = NULL; 429 } 430} 431 432void setWINSAddress(struct smb_prefs *prefs, const char *winsAddress, int count) 433{ 434 int ii; 435 CFMutableArrayRef winsArray = CFArrayCreateMutable( kCFAllocatorSystemDefault, 0, 436 &kCFTypeArrayCallBacks ); 437 if (winsArray == NULL) { 438 return; 439 } 440 for (ii=0; ii < count; ii++) { 441 CFStringRef winsName = CFStringCreateWithCString(kCFAllocatorSystemDefault, winsAddress, kCFStringEncodingUTF8); 442 if (winsName) { 443 CFArrayAppendValue(winsArray, winsName); 444 CFRelease(winsName); 445 } 446 winsAddress += strlen(winsAddress) + 1; 447 } 448 if (CFArrayGetCount(winsArray) == 0) { 449 CFRelease(winsArray); 450 } 451 if (prefs->WINSAddresses) { 452 CFRelease(prefs->WINSAddresses); 453 } 454 prefs->WINSAddresses = winsArray; 455} 456 457void readPreferences(struct smb_prefs *prefs, char *serverName, char *shareName, 458 int noUserPrefs, int resetPrefs) 459{ 460 struct rcfile *rcfile; 461 char sname[SMB_MAX_DNS_SRVNAMELEN + SMB_MAXSHARENAMELEN + 4]; 462 463 /* Set the default values */ 464 if (resetPrefs) { 465 getDefaultPreferences(prefs); 466 } 467 468 /* Now read the nsmb.conf file preference */ 469 rcfile = smb_open_rcfile(noUserPrefs); 470 if (rcfile == NULL) { 471 return; 472 } 473 /* Read the global preference section */ 474 readPreferenceSection(rcfile, prefs, "default", 0); 475 476 /* Need a server name, before we can read any of the [server] sections. */ 477 if (serverName) { 478 readPreferenceSection(rcfile, prefs, serverName, 1); 479 /* Need a server and share name, before we can read any of the [server:share] sections. */ 480 if (shareName) { 481 snprintf(sname, sizeof(sname), "%s:%s", serverName, shareName); 482 readPreferenceSection(rcfile, prefs, sname, 2); 483 } 484 } 485 /* Done with it close the preference file */ 486 rc_close(rcfile); 487} 488 489 490CFStringEncoding getPrefsCodePage( void ) 491{ 492 struct smb_prefs prefs; 493 494 getDefaultPreferences(&prefs); 495 releasePreferenceInfo(&prefs); 496 return prefs.WinCodePage; 497} 498