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
mDNSFirewallInitialize(OUT INetFwProfile ** fwProfile)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
mDNSFirewallCleanup(IN INetFwProfile * fwProfile)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
mDNSFirewallAppIsEnabled(IN INetFwProfile * fwProfile,IN const wchar_t * fwProcessImageFileName,OUT BOOL * fwAppEnabled)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
mDNSFirewallAddApp(IN INetFwProfile * fwProfile,IN const wchar_t * fwProcessImageFileName,IN const wchar_t * fwName)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 
mDNSFirewallIsFileAndPrintSharingEnabled(IN INetFwProfile * fwProfile,OUT BOOL * fwServiceEnabled)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
mDNSAddToFirewall(LPWSTR executable,LPWSTR name)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
mDNSIsFileAndPrintSharingEnabled(BOOL * retry)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