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
33static const int kMaxTries			= 30;
34static const int kRetrySleepPeriod	= 1 * 1000; // 1 second
35
36
37static OSStatus
38mDNSFirewallInitialize(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
82exit:
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
100static void
101mDNSFirewallCleanup
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
115static OSStatus
116mDNSFirewallAppIsEnabled
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
164exit:
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
189static OSStatus
190mDNSFirewallAddApp
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
250exit:
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
283static OSStatus
284
285mDNSFirewallIsFileAndPrintSharingEnabled
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
349exit:
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
382OSStatus
383mDNSAddToFirewall
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
416exit:
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
436BOOL
437mDNSIsFileAndPrintSharingEnabled( 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
467exit:
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