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