Home | History | Annotate | Download | only in SystemService
      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