]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSWindows/SystemService/Firewall.cpp
mDNSResponder-212.1.tar.gz
[apple/mdnsresponder.git] / mDNSWindows / SystemService / Firewall.cpp
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 Change History (most recent first):
18
19 $Log: Firewall.cpp,v $
20 Revision 1.6 2009/04/24 04:55:26 herscher
21 <rdar://problem/3496833> Advertise SMB file sharing via Bonjour
22
23 Revision 1.5 2009/03/30 20:39:29 herscher
24 <rdar://problem/5925472> Current Bonjour code does not compile on Windows
25 <rdar://problem/5712486> Put in extra defensive checks to prevent NULL pointer dereferencing crash
26 <rdar://problem/5187308> Move build train to Visual Studio 2005
27
28 Revision 1.4 2006/08/14 23:26:07 cheshire
29 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
30
31 Revision 1.3 2005/09/29 06:33:54 herscher
32 <rdar://problem/4278931> Fix compilation error when using latest Microsoft Platform SDK.
33
34 Revision 1.2 2004/09/15 09:39:53 shersche
35 Retry the method INetFwPolicy::get_CurrentProfile on error
36
37 Revision 1.1 2004/09/13 07:32:31 shersche
38 Wrapper for Windows Firewall API code
39
40
41 */
42
43 // <rdar://problem/4278931> Doesn't compile correctly with latest Platform SDK
44
45 #if !defined(_WIN32_DCOM)
46 # define _WIN32_DCOM
47 #endif
48
49
50 #include "Firewall.h"
51 #include <windows.h>
52 #include <crtdbg.h>
53 #include <netfw.h>
54 #include <objbase.h>
55 #include <oleauto.h>
56
57
58 static const int kMaxTries = 30;
59 static const int kRetrySleepPeriod = 1 * 1000; // 1 second
60
61
62 static OSStatus
63 mDNSFirewallInitialize(OUT INetFwProfile ** fwProfile)
64 {
65 INetFwMgr * fwMgr = NULL;
66 INetFwPolicy * fwPolicy = NULL;
67 int numRetries = 0;
68 HRESULT err = kNoErr;
69
70 _ASSERT(fwProfile != NULL);
71
72 *fwProfile = NULL;
73
74 // Use COM to get a reference to the firewall settings manager. This
75 // call will fail on anything other than XP SP2
76
77 err = CoCreateInstance( __uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&fwMgr );
78 require(SUCCEEDED(err) && ( fwMgr != NULL ), exit);
79
80 // Use the reference to get the local firewall policy
81
82 err = fwMgr->get_LocalPolicy(&fwPolicy);
83 require(SUCCEEDED(err) && ( fwPolicy != NULL ), exit);
84
85 // Use the reference to get the extant profile. Empirical evidence
86 // suggests that there is the potential for a race condition when a system
87 // service whose startup type is automatic calls this method.
88 // This is true even when the service declares itself to be dependent
89 // on the firewall service. Re-trying the method will succeed within
90 // a few seconds.
91
92 do
93 {
94 err = fwPolicy->get_CurrentProfile(fwProfile);
95
96 if (err)
97 {
98 Sleep(kRetrySleepPeriod);
99 }
100 }
101 while (err && (numRetries++ < kMaxTries));
102
103 require(SUCCEEDED(err), exit);
104
105 err = kNoErr;
106
107 exit:
108
109 // Release temporary COM objects
110
111 if (fwPolicy != NULL)
112 {
113 fwPolicy->Release();
114 }
115
116 if (fwMgr != NULL)
117 {
118 fwMgr->Release();
119 }
120
121 return err;
122 }
123
124
125 static void
126 mDNSFirewallCleanup
127 (
128 IN INetFwProfile * fwProfile
129 )
130 {
131 // Call Release on the COM reference.
132
133 if (fwProfile != NULL)
134 {
135 fwProfile->Release();
136 }
137 }
138
139
140 static OSStatus
141 mDNSFirewallAppIsEnabled
142 (
143 IN INetFwProfile * fwProfile,
144 IN const wchar_t * fwProcessImageFileName,
145 OUT BOOL * fwAppEnabled
146 )
147 {
148 BSTR fwBstrProcessImageFileName = NULL;
149 VARIANT_BOOL fwEnabled;
150 INetFwAuthorizedApplication * fwApp = NULL;
151 INetFwAuthorizedApplications* fwApps = NULL;
152 OSStatus err = kNoErr;
153
154 _ASSERT(fwProfile != NULL);
155 _ASSERT(fwProcessImageFileName != NULL);
156 _ASSERT(fwAppEnabled != NULL);
157
158 *fwAppEnabled = FALSE;
159
160 // Get the list of authorized applications
161
162 err = fwProfile->get_AuthorizedApplications(&fwApps);
163 require(SUCCEEDED(err) && ( fwApps != NULL ), exit);
164
165 fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
166 require_action( ( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr);
167
168 // Look for us
169
170 err = fwApps->Item(fwBstrProcessImageFileName, &fwApp);
171
172 if (SUCCEEDED(err) && ( fwApp != NULL ) )
173 {
174 // It's listed, but is it enabled?
175
176 err = fwApp->get_Enabled(&fwEnabled);
177 require(SUCCEEDED(err), exit);
178
179 if (fwEnabled != VARIANT_FALSE)
180 {
181 // Yes, it's enabled
182
183 *fwAppEnabled = TRUE;
184 }
185 }
186
187 err = kNoErr;
188
189 exit:
190
191 // Deallocate the BSTR
192
193 if ( fwBstrProcessImageFileName != NULL )
194 {
195 SysFreeString(fwBstrProcessImageFileName);
196 }
197
198 // Release the COM objects
199
200 if (fwApp != NULL)
201 {
202 fwApp->Release();
203 }
204
205 if (fwApps != NULL)
206 {
207 fwApps->Release();
208 }
209
210 return err;
211 }
212
213
214 static OSStatus
215 mDNSFirewallAddApp
216 (
217 IN INetFwProfile * fwProfile,
218 IN const wchar_t * fwProcessImageFileName,
219 IN const wchar_t * fwName
220 )
221 {
222 BOOL fwAppEnabled;
223 BSTR fwBstrName = NULL;
224 BSTR fwBstrProcessImageFileName = NULL;
225 INetFwAuthorizedApplication * fwApp = NULL;
226 INetFwAuthorizedApplications* fwApps = NULL;
227 OSStatus err = S_OK;
228
229 _ASSERT(fwProfile != NULL);
230 _ASSERT(fwProcessImageFileName != NULL);
231 _ASSERT(fwName != NULL);
232
233 // First check to see if the application is already authorized.
234 err = mDNSFirewallAppIsEnabled( fwProfile, fwProcessImageFileName, &fwAppEnabled );
235 require_noerr(err, exit);
236
237 // Only add the application if it isn't enabled
238
239 if (!fwAppEnabled)
240 {
241 // Get the list of authorized applications
242
243 err = fwProfile->get_AuthorizedApplications(&fwApps);
244 require(SUCCEEDED(err) && ( fwApps != NULL ), exit);
245
246 // Create an instance of an authorized application.
247
248 err = CoCreateInstance( __uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&fwApp );
249 require(SUCCEEDED(err) && ( fwApp != NULL ), exit);
250
251 fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
252 require_action(( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr);
253
254 // Set the executable file name
255
256 err = fwApp->put_ProcessImageFileName(fwBstrProcessImageFileName);
257 require(SUCCEEDED(err), exit);
258
259 fwBstrName = SysAllocString(fwName);
260 require_action( ( fwBstrName != NULL ) && ( SysStringLen(fwBstrName) > 0 ), exit, err = kNoMemoryErr);
261
262 // Set the friendly name
263
264 err = fwApp->put_Name(fwBstrName);
265 require(SUCCEEDED(err), exit);
266
267 // Now add the application
268
269 err = fwApps->Add(fwApp);
270 require(SUCCEEDED(err), exit);
271 }
272
273 err = kNoErr;
274
275 exit:
276
277 // Deallocate the BSTR objects
278
279 if ( fwBstrName != NULL )
280 {
281 SysFreeString(fwBstrName);
282 }
283
284 if ( fwBstrProcessImageFileName != NULL )
285 {
286 SysFreeString(fwBstrProcessImageFileName);
287 }
288
289 // Release the COM objects
290
291 if (fwApp != NULL)
292 {
293 fwApp->Release();
294 }
295
296 if (fwApps != NULL)
297 {
298 fwApps->Release();
299 }
300
301 return err;
302 }
303
304
305 static OSStatus
306 mDNSFirewallIsFileAndPrintSharingEnabled
307 (
308 IN INetFwProfile * fwProfile,
309 OUT BOOL * fwServiceEnabled
310 )
311 {
312 VARIANT_BOOL fwEnabled;
313 INetFwService* fwService = NULL;
314 INetFwServices* fwServices = NULL;
315 OSStatus err = S_OK;
316
317 _ASSERT(fwProfile != NULL);
318 _ASSERT(fwServiceEnabled != NULL);
319
320 *fwServiceEnabled = FALSE;
321
322 // Retrieve the globally open ports collection.
323 err = fwProfile->get_Services(&fwServices);
324 require( SUCCEEDED( err ), exit );
325
326 // Attempt to retrieve the globally open port.
327 err = fwServices->Item(NET_FW_SERVICE_FILE_AND_PRINT, &fwService);
328 require( SUCCEEDED( err ), exit );
329
330 // Find out if the globally open port is enabled.
331 err = fwService->get_Enabled(&fwEnabled);
332 require( SUCCEEDED( err ), exit );
333 if (fwEnabled != VARIANT_FALSE)
334 {
335 *fwServiceEnabled = TRUE;
336 }
337
338 exit:
339
340 // Release the globally open port.
341 if (fwService != NULL)
342 {
343 fwService->Release();
344 }
345
346 // Release the globally open ports collection.
347 if (fwServices != NULL)
348 {
349 fwServices->Release();
350 }
351
352 return err;
353 }
354
355
356 OSStatus
357 mDNSAddToFirewall
358 (
359 LPWSTR executable,
360 LPWSTR name
361 )
362 {
363 INetFwProfile * fwProfile = NULL;
364 HRESULT comInit = E_FAIL;
365 OSStatus err = kNoErr;
366
367 // Initialize COM.
368
369 comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
370
371 // Ignore this case. RPC_E_CHANGED_MODE means that COM has already been
372 // initialized with a different mode.
373
374 if (comInit != RPC_E_CHANGED_MODE)
375 {
376 err = comInit;
377 require(SUCCEEDED(err), exit);
378 }
379
380 // Connect to the firewall
381
382 err = mDNSFirewallInitialize(&fwProfile);
383 require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit);
384
385 // Add us to the list of exempt programs
386
387 err = mDNSFirewallAddApp( fwProfile, executable, name );
388 require_noerr(err, exit);
389
390 exit:
391
392 // Disconnect from the firewall
393
394 if ( fwProfile != NULL )
395 {
396 mDNSFirewallCleanup(fwProfile);
397 }
398
399 // De-initialize COM
400
401 if (SUCCEEDED(comInit))
402 {
403 CoUninitialize();
404 }
405
406 return err;
407 }
408
409
410 BOOL
411 mDNSIsFileAndPrintSharingEnabled()
412 {
413 INetFwProfile * fwProfile = NULL;
414 HRESULT comInit = E_FAIL;
415 BOOL enabled = FALSE;
416 OSStatus err = kNoErr;
417
418 // Initialize COM.
419
420 comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
421
422 // Ignore this case. RPC_E_CHANGED_MODE means that COM has already been
423 // initialized with a different mode.
424
425 if (comInit != RPC_E_CHANGED_MODE)
426 {
427 err = comInit;
428 require(SUCCEEDED(err), exit);
429 }
430
431 // Connect to the firewall
432
433 err = mDNSFirewallInitialize(&fwProfile);
434 require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit);
435
436 err = mDNSFirewallIsFileAndPrintSharingEnabled( fwProfile, &enabled );
437 require_noerr( err, exit );
438
439 exit:
440
441 // Disconnect from the firewall
442
443 if ( fwProfile != NULL )
444 {
445 mDNSFirewallCleanup(fwProfile);
446 }
447
448 // De-initialize COM
449
450 if (SUCCEEDED(comInit))
451 {
452 CoUninitialize();
453 }
454
455 return enabled;
456 }