1 /* -*- Mode: C; tab-width: 4 -*- 2 * 3 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 // <rdar://problem/4278931> Doesn't compile correctly with latest Platform SDK 19 20 #if !defined(_WIN32_DCOM) 21 # define _WIN32_DCOM 22 #endif 23 24 25 #include "Firewall.h" 26 #include <windows.h> 27 #include <crtdbg.h> 28 #include <netfw.h> 29 #include <objbase.h> 30 #include <oleauto.h> 31 32 33 static const int kMaxTries = 30; 34 static const int kRetrySleepPeriod = 1 * 1000; // 1 second 35 36 37 static OSStatus 38 mDNSFirewallInitialize(OUT INetFwProfile ** fwProfile) 39 { 40 INetFwMgr * fwMgr = NULL; 41 INetFwPolicy * fwPolicy = NULL; 42 int numRetries = 0; 43 HRESULT err = kNoErr; 44 45 _ASSERT(fwProfile != NULL); 46 47 *fwProfile = NULL; 48 49 // Use COM to get a reference to the firewall settings manager. This 50 // call will fail on anything other than XP SP2 51 52 err = CoCreateInstance( __uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&fwMgr ); 53 require(SUCCEEDED(err) && ( fwMgr != NULL ), exit); 54 55 // Use the reference to get the local firewall policy 56 57 err = fwMgr->get_LocalPolicy(&fwPolicy); 58 require(SUCCEEDED(err) && ( fwPolicy != NULL ), exit); 59 60 // Use the reference to get the extant profile. Empirical evidence 61 // suggests that there is the potential for a race condition when a system 62 // service whose startup type is automatic calls this method. 63 // This is true even when the service declares itself to be dependent 64 // on the firewall service. Re-trying the method will succeed within 65 // a few seconds. 66 67 do 68 { 69 err = fwPolicy->get_CurrentProfile(fwProfile); 70 71 if (err) 72 { 73 Sleep(kRetrySleepPeriod); 74 } 75 } 76 while (err && (numRetries++ < kMaxTries)); 77 78 require(SUCCEEDED(err), exit); 79 80 err = kNoErr; 81 82 exit: 83 84 // Release temporary COM objects 85 86 if (fwPolicy != NULL) 87 { 88 fwPolicy->Release(); 89 } 90 91 if (fwMgr != NULL) 92 { 93 fwMgr->Release(); 94 } 95 96 return err; 97 } 98 99 100 static void 101 mDNSFirewallCleanup 102 ( 103 IN INetFwProfile * fwProfile 104 ) 105 { 106 // Call Release on the COM reference. 107 108 if (fwProfile != NULL) 109 { 110 fwProfile->Release(); 111 } 112 } 113 114 115 static OSStatus 116 mDNSFirewallAppIsEnabled 117 ( 118 IN INetFwProfile * fwProfile, 119 IN const wchar_t * fwProcessImageFileName, 120 OUT BOOL * fwAppEnabled 121 ) 122 { 123 BSTR fwBstrProcessImageFileName = NULL; 124 VARIANT_BOOL fwEnabled; 125 INetFwAuthorizedApplication * fwApp = NULL; 126 INetFwAuthorizedApplications* fwApps = NULL; 127 OSStatus err = kNoErr; 128 129 _ASSERT(fwProfile != NULL); 130 _ASSERT(fwProcessImageFileName != NULL); 131 _ASSERT(fwAppEnabled != NULL); 132 133 *fwAppEnabled = FALSE; 134 135 // Get the list of authorized applications 136 137 err = fwProfile->get_AuthorizedApplications(&fwApps); 138 require(SUCCEEDED(err) && ( fwApps != NULL ), exit); 139 140 fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName); 141 require_action( ( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr); 142 143 // Look for us 144 145 err = fwApps->Item(fwBstrProcessImageFileName, &fwApp); 146 147 if (SUCCEEDED(err) && ( fwApp != NULL ) ) 148 { 149 // It's listed, but is it enabled? 150 151 err = fwApp->get_Enabled(&fwEnabled); 152 require(SUCCEEDED(err), exit); 153 154 if (fwEnabled != VARIANT_FALSE) 155 { 156 // Yes, it's enabled 157 158 *fwAppEnabled = TRUE; 159 } 160 } 161 162 err = kNoErr; 163 164 exit: 165 166 // Deallocate the BSTR 167 168 if ( fwBstrProcessImageFileName != NULL ) 169 { 170 SysFreeString(fwBstrProcessImageFileName); 171 } 172 173 // Release the COM objects 174 175 if (fwApp != NULL) 176 { 177 fwApp->Release(); 178 } 179 180 if (fwApps != NULL) 181 { 182 fwApps->Release(); 183 } 184 185 return err; 186 } 187 188 189 static OSStatus 190 mDNSFirewallAddApp 191 ( 192 IN INetFwProfile * fwProfile, 193 IN const wchar_t * fwProcessImageFileName, 194 IN const wchar_t * fwName 195 ) 196 { 197 BOOL fwAppEnabled; 198 BSTR fwBstrName = NULL; 199 BSTR fwBstrProcessImageFileName = NULL; 200 INetFwAuthorizedApplication * fwApp = NULL; 201 INetFwAuthorizedApplications* fwApps = NULL; 202 OSStatus err = S_OK; 203 204 _ASSERT(fwProfile != NULL); 205 _ASSERT(fwProcessImageFileName != NULL); 206 _ASSERT(fwName != NULL); 207 208 // First check to see if the application is already authorized. 209 err = mDNSFirewallAppIsEnabled( fwProfile, fwProcessImageFileName, &fwAppEnabled ); 210 require_noerr(err, exit); 211 212 // Only add the application if it isn't enabled 213 214 if (!fwAppEnabled) 215 { 216 // Get the list of authorized applications 217 218 err = fwProfile->get_AuthorizedApplications(&fwApps); 219 require(SUCCEEDED(err) && ( fwApps != NULL ), exit); 220 221 // Create an instance of an authorized application. 222 223 err = CoCreateInstance( __uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&fwApp ); 224 require(SUCCEEDED(err) && ( fwApp != NULL ), exit); 225 226 fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName); 227 require_action(( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr); 228 229 // Set the executable file name 230 231 err = fwApp->put_ProcessImageFileName(fwBstrProcessImageFileName); 232 require(SUCCEEDED(err), exit); 233 234 fwBstrName = SysAllocString(fwName); 235 require_action( ( fwBstrName != NULL ) && ( SysStringLen(fwBstrName) > 0 ), exit, err = kNoMemoryErr); 236 237 // Set the friendly name 238 239 err = fwApp->put_Name(fwBstrName); 240 require(SUCCEEDED(err), exit); 241 242 // Now add the application 243 244 err = fwApps->Add(fwApp); 245 require(SUCCEEDED(err), exit); 246 } 247 248 err = kNoErr; 249 250 exit: 251 252 // Deallocate the BSTR objects 253 254 if ( fwBstrName != NULL ) 255 { 256 SysFreeString(fwBstrName); 257 } 258 259 if ( fwBstrProcessImageFileName != NULL ) 260 { 261 SysFreeString(fwBstrProcessImageFileName); 262 } 263 264 // Release the COM objects 265 266 if (fwApp != NULL) 267 { 268 fwApp->Release(); 269 } 270 271 if (fwApps != NULL) 272 { 273 fwApps->Release(); 274 } 275 276 return err; 277 } 278 279 280 281 282 283 static OSStatus 284 285 mDNSFirewallIsFileAndPrintSharingEnabled 286 287 ( 288 289 IN INetFwProfile * fwProfile, 290 291 OUT BOOL * fwServiceEnabled 292 293 ) 294 295 { 296 297 VARIANT_BOOL fwEnabled; 298 299 INetFwService* fwService = NULL; 300 301 INetFwServices* fwServices = NULL; 302 303 OSStatus err = S_OK; 304 305 306 307 _ASSERT(fwProfile != NULL); 308 309 _ASSERT(fwServiceEnabled != NULL); 310 311 312 313 *fwServiceEnabled = FALSE; 314 315 316 317 // Retrieve the globally open ports collection. 318 319 err = fwProfile->get_Services(&fwServices); 320 321 require( SUCCEEDED( err ), exit ); 322 323 324 325 // Attempt to retrieve the globally open port. 326 327 err = fwServices->Item(NET_FW_SERVICE_FILE_AND_PRINT, &fwService); 328 329 require( SUCCEEDED( err ), exit ); 330 331 332 333 // Find out if the globally open port is enabled. 334 335 err = fwService->get_Enabled(&fwEnabled); 336 337 require( SUCCEEDED( err ), exit ); 338 339 if (fwEnabled != VARIANT_FALSE) 340 341 { 342 343 *fwServiceEnabled = TRUE; 344 345 } 346 347 348 349 exit: 350 351 352 353 // Release the globally open port. 354 355 if (fwService != NULL) 356 357 { 358 359 fwService->Release(); 360 361 } 362 363 364 365 // Release the globally open ports collection. 366 367 if (fwServices != NULL) 368 369 { 370 371 fwServices->Release(); 372 373 } 374 375 376 377 return err; 378 379 } 380 381 382 OSStatus 383 mDNSAddToFirewall 384 ( 385 LPWSTR executable, 386 LPWSTR name 387 ) 388 { 389 INetFwProfile * fwProfile = NULL; 390 HRESULT comInit = E_FAIL; 391 OSStatus err = kNoErr; 392 393 // Initialize COM. 394 395 comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE ); 396 397 // Ignore this case. RPC_E_CHANGED_MODE means that COM has already been 398 // initialized with a different mode. 399 400 if (comInit != RPC_E_CHANGED_MODE) 401 { 402 err = comInit; 403 require(SUCCEEDED(err), exit); 404 } 405 406 // Connect to the firewall 407 408 err = mDNSFirewallInitialize(&fwProfile); 409 require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit); 410 411 // Add us to the list of exempt programs 412 413 err = mDNSFirewallAddApp( fwProfile, executable, name ); 414 require_noerr(err, exit); 415 416 exit: 417 418 // Disconnect from the firewall 419 420 if ( fwProfile != NULL ) 421 { 422 mDNSFirewallCleanup(fwProfile); 423 } 424 425 // De-initialize COM 426 427 if (SUCCEEDED(comInit)) 428 { 429 CoUninitialize(); 430 } 431 432 return err; 433 } 434 435 436 BOOL 437 mDNSIsFileAndPrintSharingEnabled( BOOL * retry ) 438 { 439 INetFwProfile * fwProfile = NULL; 440 HRESULT comInit = E_FAIL; 441 BOOL enabled = FALSE; 442 OSStatus err = kNoErr; 443 444 // Initialize COM. 445 446 *retry = FALSE; 447 comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE ); 448 449 // Ignore this case. RPC_E_CHANGED_MODE means that COM has already been 450 // initialized with a different mode. 451 452 if (comInit != RPC_E_CHANGED_MODE) 453 { 454 *retry = TRUE; 455 err = comInit; 456 require(SUCCEEDED(err), exit); 457 } 458 459 // Connect to the firewall 460 461 err = mDNSFirewallInitialize(&fwProfile); 462 require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit); 463 464 err = mDNSFirewallIsFileAndPrintSharingEnabled( fwProfile, &enabled ); 465 require_noerr( err, exit ); 466 467 exit: 468 469 // Disconnect from the firewall 470 471 if ( fwProfile != NULL ) 472 { 473 mDNSFirewallCleanup(fwProfile); 474 } 475 476 // De-initialize COM 477 478 if (SUCCEEDED(comInit)) 479 { 480 CoUninitialize(); 481 } 482 483 return enabled; 484 } 485