]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/helper.c
8b2a74200b40f96d36440afea5a7fd33fb228953
[apple/mdnsresponder.git] / mDNSMacOSX / helper.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2007 Apple 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: helper.c,v $
20 Revision 1.66 2009/04/20 20:40:14 cheshire
21 <rdar://problem/6786150> uDNS: Running location cycling caused configd and mDNSResponder to deadlock
22 Changed mDNSPreferencesSetName (and similar) routines from MIG "routine" to MIG "simpleroutine"
23 so we don't deadlock waiting for a result that we're just going to ignore anyway
24
25 Revision 1.65 2009/03/20 22:12:28 mcguire
26 <rdar://problem/6703952> Support CFUserNotificationDisplayNotice in mDNSResponderHelper
27 Make the call to the helper a simpleroutine: don't wait for an unused return value
28
29 Revision 1.64 2009/03/20 21:52:39 cheshire
30 <rdar://problem/6703952> Support CFUserNotificationDisplayNotice in mDNSResponderHelper
31 Need to CFRelease strings in do_mDNSNotify
32
33 Revision 1.63 2009/03/20 21:21:15 cheshire
34 <rdar://problem/6703952> Support CFUserNotificationDisplayNotice in mDNSResponderHelper
35 Need to set error code correctly in do_mDNSNotify
36
37 Revision 1.62 2009/03/20 20:52:22 cheshire
38 <rdar://problem/6703952> Support CFUserNotificationDisplayNotice in mDNSResponderHelper
39
40 Revision 1.61 2009/03/14 01:42:56 mcguire
41 <rdar://problem/5457116> BTMM: Fix issues with multiple .Mac accounts on the same machine
42
43 Revision 1.60 2009/02/18 02:09:10 cheshire
44 <rdar://problem/6514947> Sleep Proxy: PF_ROUTE command to set ARP entry returns errno 17 (EEXIST)
45 Also need to set rtmsg.hdr.rtm_index
46
47 Revision 1.59 2009/02/17 23:33:45 cheshire
48 <rdar://problem/6514947> Sleep Proxy: PF_ROUTE command to set ARP entry returns errno 17 (EEXIST)
49
50 Revision 1.58 2009/02/04 22:23:04 cheshire
51 Simplified do_mDNSPowerRequest --
52 code was checking for CFAbsoluteTimeGetCurrent() returning NULL, which makes no sense
53
54 Revision 1.57 2009/01/22 02:14:27 cheshire
55 <rdar://problem/6515626> Sleep Proxy: Set correct target MAC address, instead of all zeroes
56
57 Revision 1.56 2009/01/20 21:03:22 cheshire
58 Improved debugging messages
59
60 Revision 1.55 2009/01/20 02:37:26 mcguire
61 revert previous erroneous commit
62
63 Revision 1.54 2009/01/20 02:35:15 mcguire
64 mDNSMacOSX/mDNSMacOSX.c
65
66 Revision 1.53 2009/01/14 01:38:42 mcguire
67 <rdar://problem/6492710> Write out DynamicStore per-interface SleepProxyServer info
68
69 Revision 1.52 2009/01/13 05:31:34 mkrochma
70 <rdar://problem/6491367> Replace bzero, bcopy with mDNSPlatformMemZero, mDNSPlatformMemCopy, memset, memcpy
71
72 Revision 1.51 2009/01/12 22:26:12 mkrochma
73 Change DynamicStore location from BonjourSleepProxy/DiscoveredServers to SleepProxyServers
74
75 Revision 1.50 2008/12/15 18:40:41 mcguire
76 <rdar://problem/6444440> Socket leak in helper's doTunnelPolicy
77
78 Revision 1.49 2008/12/12 00:37:42 mcguire
79 <rdar://problem/6417648> BTMM outbound fails if /var/run/racoon doesn't exist
80
81 Revision 1.48 2008/12/05 02:35:24 mcguire
82 <rdar://problem/6107390> Write to the DynamicStore when a Sleep Proxy server is available on the network
83
84 Revision 1.47 2008/11/21 02:28:55 mcguire
85 <rdar://problem/6354979> send racoon a SIGUSR1 instead of SIGHUP
86
87 Revision 1.46 2008/11/11 02:09:42 cheshire
88 Removed some unnecessary log messages
89
90 Revision 1.45 2008/11/06 23:35:38 cheshire
91 Refinements to the do_mDNSSetARP() routine
92
93 Revision 1.44 2008/11/05 18:41:14 cheshire
94 Log errors from read() call in do_mDNSSetARP()
95
96 Revision 1.43 2008/11/04 23:54:09 cheshire
97 Added routine mDNSSetARP(), used to replace an SPS client's entry in our ARP cache with
98 a dummy one, so that IP traffic to the SPS client initiated by the SPS machine can be
99 captured by our BPF filters, and used as a trigger to wake the sleeping machine.
100
101 Revision 1.42 2008/10/31 23:35:31 cheshire
102 When scheduling new power event make sure all old events are deleted;
103 mDNSPowerRequest(-1,-1); just clears old events without scheduling a new one
104
105 Revision 1.41 2008/10/31 18:41:55 cheshire
106 Do update_idle_timer() before returning from do_mDNSRequestBPF()
107
108 Revision 1.40 2008/10/30 01:05:27 cheshire
109 mDNSPowerRequest(0, 0) means "sleep now"
110
111 Revision 1.39 2008/10/29 21:26:50 cheshire
112 Only log IOPMSchedulePowerEvent calls when there's an error
113
114 Revision 1.38 2008/10/24 01:42:36 cheshire
115 Added mDNSPowerRequest helper routine to request a scheduled wakeup some time in the future
116
117 Revision 1.37 2008/10/24 00:17:22 mcguire
118 Add compatibility for older racoon behavior
119
120 Revision 1.36 2008/10/22 17:22:31 cheshire
121 Remove SO_NOSIGPIPE bug workaround
122
123 Revision 1.35 2008/10/20 22:01:28 cheshire
124 Made new Mach simpleroutine "mDNSRequestBPF"
125
126 Revision 1.34 2008/10/02 23:50:07 mcguire
127 <rdar://problem/6136442> shutdown time issues
128 improve log messages when SCDynamicStoreCreate() fails
129
130 Revision 1.33 2008/09/30 01:00:45 cheshire
131 Added workaround to avoid SO_NOSIGPIPE bug
132
133 Revision 1.32 2008/09/27 01:11:46 cheshire
134 Added handler to respond to kmDNSSendBPF message
135
136 Revision 1.31 2008/09/08 17:42:40 mcguire
137 <rdar://problem/5536811> change location of racoon files
138 cleanup, handle stat failure cases, reduce log messages
139
140 Revision 1.30 2008/09/05 21:51:26 mcguire
141 <rdar://problem/6077707> BTMM: Need to launch racoon by opening VPN control socket
142
143 Revision 1.29 2008/09/05 18:26:53 mcguire
144 <rdar://problem/6077707> BTMM: Need to launch racoon by opening VPN control socket
145
146 Revision 1.28 2008/09/04 22:49:28 mcguire
147 <rdar://problem/5536811> change location of racoon files
148
149 Revision 1.27 2008/08/28 23:11:12 mcguire
150 <rdar://problem/5858535> handle SIGTERM in mDNSResponderHelper
151
152 Revision 1.26 2008/08/19 00:35:02 mcguire
153 <rdar://problem/5858535> handle SIGTERM in mDNSResponderHelper
154
155 Revision 1.25 2008/08/13 23:04:06 mcguire
156 <rdar://problem/5858535> handle SIGTERM in mDNSResponderHelper
157 Preparation: rename message function, as it will no longer be called only on idle exit
158
159 Revision 1.24 2008/01/30 19:01:51 mcguire
160 <rdar://problem/5703989> Crash in mDNSResponderHelper
161
162 Revision 1.23 2007/11/30 23:21:51 cheshire
163 Rename variables to eliminate "declaration of 'sin_loc' shadows a previous local" warning
164
165 Revision 1.22 2007/11/27 00:08:49 jgraessley
166 <rdar://problem/5613538> Interface specific resolvers not setup correctly
167
168 Revision 1.21 2007/11/07 00:22:30 jgraessley
169 Bug #: <rdar://problem/5573573> mDNSResponder doesn't build without IPSec
170 Reviewed by: Stuart Cheshire
171
172 Revision 1.20 2007/09/12 18:07:44 cheshire
173 Fix compile errors ("passing argument from incompatible pointer type")
174
175 Revision 1.19 2007/09/12 00:42:47 mcguire
176 <rdar://problem/5468236> BTMM: Need to clean up security associations
177
178 Revision 1.18 2007/09/12 00:40:16 mcguire
179 <rdar://problem/5469660> 9A547: Computer Name had incorrectly encoded unicode
180
181 Revision 1.17 2007/09/09 02:21:17 mcguire
182 <rdar://problem/5469345> Leopard Server9A547(Insatll):mDNSResponderHelper crashing
183
184 Revision 1.16 2007/09/07 22:44:03 mcguire
185 <rdar://problem/5448420> Move CFUserNotification code to mDNSResponderHelper
186
187 Revision 1.15 2007/09/07 22:24:36 vazquez
188 <rdar://problem/5466301> Need to stop spewing mDNSResponderHelper logs
189
190 Revision 1.14 2007/09/06 20:39:05 cheshire
191 Added comment explaining why we allow both "ddns" and "sndd" as valid item types
192 The Keychain APIs on Intel appear to store the four-character item type backwards (at least some of the time)
193
194 Revision 1.13 2007/09/04 22:32:58 mcguire
195 <rdar://problem/5453633> BTMM: BTMM overwrites /etc/racoon/remote/anonymous.conf
196
197 Revision 1.12 2007/08/29 21:42:12 mcguire
198 <rdar://problem/5431192> BTMM: Duplicate Private DNS names are being added to DynamicStore
199
200 Revision 1.11 2007/08/28 00:33:04 jgraessley
201 <rdar://problem/5423932> Selective compilation options
202
203 Revision 1.10 2007/08/27 22:16:38 mcguire
204 <rdar://problem/5437362> BTMM: MTU should be set to 1280
205
206 Revision 1.9 2007/08/27 22:13:59 mcguire
207 <rdar://problem/5437373> BTMM: IPSec security associations should have a shorter timeout
208
209 Revision 1.8 2007/08/23 21:49:51 cheshire
210 Made code layout style consistent with existing project style; added $Log header
211
212 Revision 1.7 2007/08/23 00:29:05 mcguire
213 <rdar://problem/5425800> BTMM: IPSec policy not installed in some situations - connections fail
214
215 Revision 1.6 2007/08/18 01:02:03 mcguire
216 <rdar://problem/5415593> No Bonjour services are getting registered at boot
217
218 Revision 1.5 2007/08/18 00:59:55 mcguire
219 <rdar://problem/5392568> Blocked: BTMM: Start racoon with '-e' parameter
220
221 Revision 1.4 2007/08/16 01:00:06 mcguire
222 <rdar://problem/5392548> BTMM: Install generate IPsec policies to block non-BTMM traffic
223
224 Revision 1.3 2007/08/15 23:20:28 mcguire
225 <rdar://problem/5408105> BTMM: racoon files can get corrupted if autotunnel is listening on port > 32767
226
227 Revision 1.2 2007/08/10 22:30:39 mcguire
228 <rdar://problem/5400259> BTMM: racoon config files are not always the correct mode
229
230 Revision 1.1 2007/08/08 22:34:58 mcguire
231 <rdar://problem/5197869> Security: Run mDNSResponder as user id mdnsresponder instead of root
232 */
233
234 #include <sys/cdefs.h>
235 #include <arpa/inet.h>
236 #include <bsm/libbsm.h>
237 #include <net/if.h>
238 #include <net/route.h>
239 #include <net/if_dl.h>
240 #include <net/if_types.h>
241 #include <netinet/in.h>
242 #include <netinet/if_ether.h>
243 #include <netinet6/in6_var.h>
244 #include <netinet6/nd6.h>
245 #include <netinet6/ipsec.h>
246 #include <sys/ioctl.h>
247 #include <sys/socket.h>
248 #include <asl.h>
249 #include <ctype.h>
250 #include <dirent.h>
251 #include <errno.h>
252 #include <fcntl.h>
253 #include <stdarg.h>
254 #include <stdbool.h>
255 #include <string.h>
256 #include <unistd.h>
257 #include <Security/Security.h>
258 #include <SystemConfiguration/SystemConfiguration.h>
259 #include <SystemConfiguration/SCDynamicStore.h>
260 #include <SystemConfiguration/SCPreferencesSetSpecific.h>
261 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
262 #include <TargetConditionals.h>
263 #include <IOKit/pwr_mgt/IOPMLib.h>
264
265 #include "mDNSEmbeddedAPI.h"
266 #include "dns_sd.h"
267 #include "dnssd_ipc.h"
268 #include "libpfkey.h"
269 #include "helper.h"
270 #include "helpermsgServer.h"
271 #include "helper-server.h"
272 #include "ipsec_options.h"
273
274 #ifndef RTF_IFSCOPE
275 #define RTF_IFSCOPE 0x1000000
276 #endif
277
278 #if TARGET_OS_EMBEDDED
279 #define NO_CFUSERNOTIFICATION 1
280 #define NO_SECURITYFRAMEWORK 1
281 #endif
282
283 // Embed the client stub code here, so we can access private functions like ConnectToServer, create_hdr, deliver_request
284 #include "../mDNSShared/dnssd_ipc.c"
285 #include "../mDNSShared/dnssd_clientstub.c"
286
287 typedef struct sadb_x_policy *ipsec_policy_t;
288
289 uid_t mDNSResponderUID;
290 gid_t mDNSResponderGID;
291 static const char kTunnelAddressInterface[] = "lo0";
292
293 void
294 debug_(const char *func, const char *fmt, ...)
295 {
296 char buf[2048];
297 va_list ap;
298
299 va_start(ap, fmt);
300 vsnprintf(buf, sizeof(buf), fmt, ap);
301 va_end(ap);
302 helplog(ASL_LEVEL_DEBUG, "%s: %s", func, buf);
303 }
304
305 static int
306 authorized(audit_token_t *token)
307 {
308 int ok = 0;
309 pid_t pid = (pid_t)-1;
310 uid_t euid = (uid_t)-1;
311
312 audit_token_to_au32(*token, NULL, &euid, NULL, NULL, NULL, &pid, NULL,
313 NULL);
314 ok = (euid == mDNSResponderUID || euid == 0);
315 if (!ok)
316 helplog(ASL_LEVEL_NOTICE,
317 "Unauthorized access by euid=%lu pid=%lu",
318 (unsigned long)euid, (unsigned long)pid);
319 return ok;
320 }
321
322 kern_return_t
323 do_mDNSExit(__unused mach_port_t port, audit_token_t token)
324 {
325 debug("entry");
326 if (!authorized(&token))
327 goto fin;
328 helplog(ASL_LEVEL_INFO, "exit");
329 exit(0);
330
331 fin:
332 debug("fin");
333 return KERN_SUCCESS;
334 }
335
336 kern_return_t do_mDNSRequestBPF(__unused mach_port_t port, audit_token_t token)
337 {
338 if (!authorized(&token)) return KERN_SUCCESS;
339 DNSServiceRef ref;
340 DNSServiceErrorType err = ConnectToServer(&ref, 0, send_bpf, NULL, NULL, NULL);
341 if (err) { helplog(ASL_LEVEL_ERR, "do_mDNSRequestBPF: ConnectToServer %d", err); return err; }
342
343 char *ptr;
344 size_t len = sizeof(DNSServiceFlags);
345 ipc_msg_hdr *hdr = create_hdr(send_bpf, &len, &ptr, 0, ref);
346 if (!hdr) { DNSServiceRefDeallocate(ref); return kDNSServiceErr_NoMemory; }
347 put_flags(0, &ptr);
348 deliver_request(hdr, ref); // Will free hdr for us
349 DNSServiceRefDeallocate(ref);
350 update_idle_timer();
351 return KERN_SUCCESS;
352 }
353
354 kern_return_t do_mDNSPowerRequest(__unused mach_port_t port, int key, int interval, int *err, audit_token_t token)
355 {
356 *err = -1;
357 if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; }
358
359 CFArrayRef events = IOPMCopyScheduledPowerEvents();
360 if (events)
361 {
362 int i;
363 CFIndex count = CFArrayGetCount(events);
364 for (i=0; i<count; i++)
365 {
366 CFDictionaryRef dict = CFArrayGetValueAtIndex(events, i);
367 CFStringRef id = CFDictionaryGetValue(dict, CFSTR(kIOPMPowerEventAppNameKey));
368 if (CFEqual(id, CFSTR("mDNSResponderHelper")))
369 {
370 CFDateRef EventTime = CFDictionaryGetValue(dict, CFSTR(kIOPMPowerEventTimeKey));
371 CFStringRef EventType = CFDictionaryGetValue(dict, CFSTR(kIOPMPowerEventTypeKey));
372 IOReturn result = IOPMCancelScheduledPowerEvent(EventTime, id, EventType);
373 //helplog(ASL_LEVEL_ERR, "Deleting old event %s");
374 if (result) helplog(ASL_LEVEL_ERR, "IOPMCancelScheduledPowerEvent %d failed %d", i, result);
375 }
376 }
377 CFRelease(events);
378 }
379
380 if (key < 0) // mDNSPowerRequest(-1,-1) means "clear any stale schedules" (see above)
381 *err = 0;
382 else if (key == 0) // mDNSPowerRequest(0, 0) means "sleep now"
383 {
384 IOReturn r = IOPMSleepSystem(IOPMFindPowerManagement(MACH_PORT_NULL));
385 if (r) { usleep(100000); helplog(ASL_LEVEL_ERR, "IOPMSleepSystem %d", r); }
386 *err = r;
387 }
388 else if (key > 0)
389 {
390 CFDateRef w = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval);
391 if (w)
392 {
393 IOReturn r = IOPMSchedulePowerEvent(w, CFSTR("mDNSResponderHelper"), key ? CFSTR(kIOPMAutoWake) : CFSTR(kIOPMAutoSleep));
394 if (r) { usleep(100000); helplog(ASL_LEVEL_ERR, "IOPMSchedulePowerEvent(%d) %d %x", interval, r, r); }
395 *err = r;
396 CFRelease(w);
397 }
398 }
399 fin:
400 update_idle_timer();
401 return KERN_SUCCESS;
402 }
403
404 kern_return_t do_mDNSSetARP(__unused mach_port_t port, int ifindex, v4addr_t v4, ethaddr_t eth, int *err, audit_token_t token)
405 {
406 //helplog(ASL_LEVEL_ERR, "do_mDNSSetARP %d %d.%d.%d.%d %02X:%02X:%02X:%02X:%02X:%02X",
407 // ifindex, v4[0], v4[1], v4[2], v4[3], eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]);
408
409 *err = -1;
410 if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; }
411
412 static int s = -1, seq = 0;
413 if (s < 0)
414 {
415 s = socket(PF_ROUTE, SOCK_RAW, 0);
416 if (s < 0) helplog(ASL_LEVEL_ERR, "do_mDNSSetARP: socket(PF_ROUTE, SOCK_RAW, 0) failed %d (%s)", errno, strerror(errno));
417 }
418
419 if (s >= 0)
420 {
421 struct timeval tv;
422 gettimeofday(&tv, 0);
423
424 struct { struct rt_msghdr hdr; struct sockaddr_inarp dst; struct sockaddr_dl sdl; } rtmsg;
425 memset(&rtmsg, 0, sizeof(rtmsg));
426
427 rtmsg.hdr.rtm_msglen = sizeof(rtmsg);
428 rtmsg.hdr.rtm_version = RTM_VERSION;
429 rtmsg.hdr.rtm_type = RTM_ADD;
430 rtmsg.hdr.rtm_index = ifindex;
431 rtmsg.hdr.rtm_flags = RTF_HOST | RTF_STATIC | RTF_IFSCOPE;
432 rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
433 rtmsg.hdr.rtm_pid = 0;
434 rtmsg.hdr.rtm_seq = seq++;
435 rtmsg.hdr.rtm_errno = 0;
436 rtmsg.hdr.rtm_use = 0;
437 rtmsg.hdr.rtm_inits = RTV_EXPIRE;
438 rtmsg.hdr.rtm_rmx.rmx_expire = tv.tv_sec + 30;
439
440 rtmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
441 rtmsg.dst.sin_family = AF_INET;
442 rtmsg.dst.sin_port = 0;
443 rtmsg.dst.sin_addr.s_addr = *(in_addr_t*)v4;
444 rtmsg.dst.sin_srcaddr.s_addr = 0;
445 rtmsg.dst.sin_tos = 0;
446 rtmsg.dst.sin_other = 0;
447
448 rtmsg.sdl.sdl_len = sizeof(struct sockaddr_dl);
449 rtmsg.sdl.sdl_family = AF_LINK;
450 rtmsg.sdl.sdl_index = ifindex;
451 rtmsg.sdl.sdl_type = IFT_ETHER;
452 rtmsg.sdl.sdl_nlen = 0;
453 rtmsg.sdl.sdl_alen = ETHER_ADDR_LEN;
454 rtmsg.sdl.sdl_slen = 0;
455
456 // Target MAC address goes in rtmsg.sdl.sdl_data[0..5]; (See LLADDR() in /usr/include/net/if_dl.h)
457 memcpy(rtmsg.sdl.sdl_data, eth, sizeof(ethaddr_t));
458
459 int len = write(s, (char *)&rtmsg, sizeof(rtmsg));
460 if (len < 0)
461 helplog(ASL_LEVEL_ERR, "do_mDNSSetARP: write(%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s)",
462 sizeof(rtmsg), ifindex, v4[0], v4[1], v4[2], v4[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno));
463 len = read(s, (char *)&rtmsg, sizeof(rtmsg));
464 if (len < 0)
465 helplog(ASL_LEVEL_ERR, "do_mDNSSetARP: read (%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s)",
466 sizeof(rtmsg), ifindex, v4[0], v4[1], v4[2], v4[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno));
467
468 *err = 0;
469 }
470
471 fin:
472 update_idle_timer();
473 return KERN_SUCCESS;
474 }
475
476 kern_return_t do_mDNSNotify(__unused mach_port_t port, const char *title, const char *msg, audit_token_t token)
477 {
478 if (!authorized(&token)) return KERN_SUCCESS;
479
480 #ifndef NO_CFUSERNOTIFICATION
481 static const char footer[] = "(Note: This message only appears on machines with 17.x.x.x IP addresses — i.e. at Apple — not on customer machines.)";
482 CFStringRef alertHeader = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
483 CFStringRef alertBody = CFStringCreateWithCString(NULL, msg, kCFStringEncodingUTF8);
484 CFStringRef alertFooter = CFStringCreateWithCString(NULL, footer, kCFStringEncodingUTF8);
485 CFStringRef alertMessage = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@\r\r%@"), alertBody, alertFooter);
486 CFRelease(alertBody);
487 CFRelease(alertFooter);
488 int err = CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, NULL);
489 if (err) helplog(ASL_LEVEL_ERR, "CFUserNotificationDisplayNotice returned %d", err);
490 CFRelease(alertHeader);
491 CFRelease(alertMessage);
492 #endif /* NO_CFUSERNOTIFICATION */
493
494 update_idle_timer();
495 return KERN_SUCCESS;
496 }
497
498 kern_return_t
499 do_mDNSDynamicStoreSetConfig(__unused mach_port_t port, int key,
500 const char* subkey, vm_offset_t value, mach_msg_type_number_t valueCnt,
501 audit_token_t token)
502 {
503 CFStringRef sckey = NULL;
504 Boolean release_sckey = FALSE;
505 CFDataRef bytes = NULL;
506 CFPropertyListRef plist = NULL;
507 SCDynamicStoreRef store = NULL;
508
509 debug("entry");
510 if (!authorized(&token)) goto fin;
511
512 switch ((enum mDNSDynamicStoreSetConfigKey)key)
513 {
514 case kmDNSMulticastConfig:
515 sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS);
516 break;
517 case kmDNSDynamicConfig:
518 sckey = CFSTR("State:/Network/DynamicDNS");
519 break;
520 case kmDNSPrivateConfig:
521 sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS);
522 break;
523 case kmDNSBackToMyMacConfig:
524 sckey = CFSTR("State:/Network/BackToMyMac");
525 break;
526 case kmDNSSleepProxyServersState:
527 {
528 CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0);
529 CFStringAppend(tmp, CFSTR("State:/Network/Interface/"));
530 CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8);
531 CFStringAppend(tmp, CFSTR("/SleepProxyServers"));
532 sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp);
533 release_sckey = TRUE;
534 CFRelease(tmp);
535 break;
536 }
537 default:
538 debug("unrecognized key %d", key);
539 goto fin;
540 }
541 if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value,
542 valueCnt, kCFAllocatorNull)))
543 {
544 debug("CFDataCreateWithBytesNoCopy of value failed");
545 goto fin;
546 }
547 if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes,
548 kCFPropertyListImmutable, NULL)))
549 {
550 debug("CFPropertyListCreateFromXMLData of bytes failed");
551 goto fin;
552 }
553 CFRelease(bytes);
554 bytes = NULL;
555 if (NULL == (store = SCDynamicStoreCreate(NULL,
556 CFSTR(kmDNSHelperServiceName), NULL, NULL)))
557 {
558 debug("SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
559 goto fin;
560 }
561 SCDynamicStoreSetValue(store, sckey, plist);
562 debug("succeeded");
563
564 fin:
565 if (NULL != bytes)
566 CFRelease(bytes);
567 if (NULL != plist)
568 CFRelease(plist);
569 if (NULL != store)
570 CFRelease(store);
571 if (release_sckey && sckey)
572 CFRelease(sckey);
573 vm_deallocate(mach_task_self(), value, valueCnt);
574 update_idle_timer();
575 return KERN_SUCCESS;
576 }
577
578 char usercompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name the user saw
579 char userhostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name the user saw
580 char lastcompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name saved to preferences
581 char lasthostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name saved to preferences
582
583 #ifndef NO_CFUSERNOTIFICATION
584 static CFStringRef CFS_OQ = NULL;
585 static CFStringRef CFS_CQ = NULL;
586 static CFStringRef CFS_Format = NULL;
587 static CFStringRef CFS_ComputerName = NULL;
588 static CFStringRef CFS_ComputerNameMsg = NULL;
589 static CFStringRef CFS_LocalHostName = NULL;
590 static CFStringRef CFS_LocalHostNameMsg = NULL;
591 static CFStringRef CFS_Problem = NULL;
592
593 static CFUserNotificationRef gNotification = NULL;
594 static CFRunLoopSourceRef gNotificationRLS = NULL;
595
596 static void NotificationCallBackDismissed(CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
597 {
598 debug("entry");
599 (void)responseFlags; // Unused
600 if (userNotification != gNotification) helplog(ASL_LEVEL_ERR, "NotificationCallBackDismissed: Wrong CFUserNotificationRef");
601 if (gNotificationRLS)
602 {
603 // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread.
604 // We need to explicitly specify the desired CFRunLoop from which we want to remove this event source.
605 CFRunLoopRemoveSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode);
606 CFRelease(gNotificationRLS);
607 gNotificationRLS = NULL;
608 CFRelease(gNotification);
609 gNotification = NULL;
610 }
611 // By dismissing the alert, the user has conceptually acknowleged the rename.
612 // (e.g. the machine's name is now officially "computer-2.local", not "computer.local".)
613 // If we get *another* conflict, the new alert should refer to the 'old' name
614 // as now being "computer-2.local", not "computer.local"
615 usercompname[0] = 0;
616 userhostname[0] = 0;
617 lastcompname[0] = 0;
618 lasthostname[0] = 0;
619 update_idle_timer();
620 unpause_idle_timer();
621 }
622
623 static void ShowNameConflictNotification(CFMutableArrayRef header, CFStringRef subtext)
624 {
625 CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
626 if (!dictionary) return;
627
628 debug("entry");
629
630 CFDictionarySetValue(dictionary, kCFUserNotificationAlertHeaderKey, header);
631 CFDictionarySetValue(dictionary, kCFUserNotificationAlertMessageKey, subtext);
632
633 CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, CFSTR("/System/Library/CoreServices/mDNSResponder.bundle"), kCFURLPOSIXPathStyle, true);
634 if (urlRef) { CFDictionarySetValue(dictionary, kCFUserNotificationLocalizationURLKey, urlRef); CFRelease(urlRef); }
635
636 if (gNotification) // If notification already on-screen, update it in place
637 CFUserNotificationUpdate(gNotification, 0, kCFUserNotificationCautionAlertLevel, dictionary);
638 else // else, we need to create it
639 {
640 SInt32 error;
641 gNotification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationCautionAlertLevel, &error, dictionary);
642 if (!gNotification || error) { helplog(ASL_LEVEL_ERR, "ShowNameConflictNotification: CFUserNotificationRef: Error %d", error); return; }
643 gNotificationRLS = CFUserNotificationCreateRunLoopSource(NULL, gNotification, NotificationCallBackDismissed, 0);
644 if (!gNotificationRLS) { helplog(ASL_LEVEL_ERR,"ShowNameConflictNotification: RLS"); CFRelease(gNotification); gNotification = NULL; return; }
645 // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread.
646 // We need to explicitly specify the desired CFRunLoop to which we want to add this event source.
647 CFRunLoopAddSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode);
648 debug("gRunLoop=%p gNotification=%p gNotificationRLS=%p", gRunLoop, gNotification, gNotificationRLS);
649 pause_idle_timer();
650 }
651
652 CFRelease(dictionary);
653 }
654
655 static CFMutableArrayRef GetHeader(const char* oldname, const char* newname, const CFStringRef msg, const char* suffix)
656 {
657 CFMutableArrayRef alertHeader = NULL;
658
659 const CFStringRef cfoldname = CFStringCreateWithCString(NULL, oldname, kCFStringEncodingUTF8);
660 // NULL newname means we've given up trying to construct a name that doesn't conflict
661 const CFStringRef cfnewname = newname ? CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8) : NULL;
662 // We tag a zero-width non-breaking space at the end of the literal text to guarantee that, no matter what
663 // arbitrary computer name the user may choose, this exact text (with zero-width non-breaking space added)
664 // can never be one that occurs in the Localizable.strings translation file.
665 if (!cfoldname)
666 helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for old=%s", newname);
667 else if (newname && !cfnewname)
668 helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for new=%s", newname);
669 else
670 {
671 const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfoldname, suffix);
672 const CFStringRef s2 = cfnewname ? CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfnewname, suffix) : NULL;
673
674 alertHeader = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
675
676 if (!s1)
677 helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for old=%s", oldname);
678 else if (cfnewname && !s2)
679 helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for new=%s", newname);
680 else if (!alertHeader)
681 helplog(ASL_LEVEL_ERR, "Could not construct CFArray for notification");
682 else
683 {
684 // Make sure someone is logged in. We don't want this popping up over the login window
685 uid_t uid;
686 gid_t gid;
687 CFStringRef userName = SCDynamicStoreCopyConsoleUser(NULL, &uid, &gid);
688 if (userName)
689 {
690 CFRelease(userName);
691 CFArrayAppendValue(alertHeader, msg); // Opening phrase of message, provided by caller
692 CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s1); CFArrayAppendValue(alertHeader, CFS_CQ);
693 CFArrayAppendValue(alertHeader, CFSTR(" is already in use on this network. "));
694 if (s2)
695 {
696 CFArrayAppendValue(alertHeader, CFSTR("The name has been changed to "));
697 CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s2); CFArrayAppendValue(alertHeader, CFS_CQ);
698 CFArrayAppendValue(alertHeader, CFSTR("."));
699 }
700 else
701 CFArrayAppendValue(alertHeader, CFSTR("All attempts to find an available name by adding a number to the name were also unsuccessful."));
702 }
703 }
704 if (s1) CFRelease(s1);
705 if (s2) CFRelease(s2);
706 }
707 if (cfoldname) CFRelease(cfoldname);
708 if (cfnewname) CFRelease(cfnewname);
709
710 return alertHeader;
711 }
712 #endif /* ndef NO_CFUSERNOTIFICATION */
713
714 static void update_notification(void)
715 {
716 #ifndef NO_CFUSERNOTIFICATION
717 debug("entry ucn=%s, uhn=%s, lcn=%s, lhn=%s", usercompname, userhostname, lastcompname, lasthostname);
718 if (!CFS_OQ)
719 {
720 // Note: the "\xEF\xBB\xBF" byte sequence in the CFS_Format string is the UTF-8 encoding of the zero-width non-breaking space character.
721 // By appending this invisible character on the end of literal names, we ensure the these strings cannot inadvertently match any string
722 // in the localization file -- since we know for sure that none of our strings in the localization file contain the ZWNBS character.
723 CFS_OQ = CFStringCreateWithCString(NULL, "“", kCFStringEncodingUTF8);
724 CFS_CQ = CFStringCreateWithCString(NULL, "”", kCFStringEncodingUTF8);
725 CFS_Format = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF", kCFStringEncodingUTF8);
726 CFS_ComputerName = CFStringCreateWithCString(NULL, "The name of your computer ", kCFStringEncodingUTF8);
727 CFS_ComputerNameMsg = CFStringCreateWithCString(NULL, "To change the name of your computer, "
728 "open System Preferences and click Sharing, then type the name in the Computer Name field.", kCFStringEncodingUTF8);
729 CFS_LocalHostName = CFStringCreateWithCString(NULL, "This computer’s local hostname ", kCFStringEncodingUTF8);
730 CFS_LocalHostNameMsg = CFStringCreateWithCString(NULL, "To change the local hostname, "
731 "open System Preferences and click Sharing, then click “Edit” and type the name in the Local Hostname field.", kCFStringEncodingUTF8);
732 CFS_Problem = CFStringCreateWithCString(NULL, "This may indicate a problem with the local network. "
733 "Please inform your network administrator.", kCFStringEncodingUTF8);
734 }
735
736 if (!usercompname[0] && !userhostname[0])
737 {
738 if (gNotificationRLS)
739 {
740 debug("canceling notification %p", gNotification);
741 CFUserNotificationCancel(gNotification);
742 unpause_idle_timer();
743 }
744 }
745 else
746 {
747 CFMutableArrayRef header = NULL;
748 CFStringRef* subtext = NULL;
749 if (userhostname[0] && !lasthostname[0]) // we've given up trying to construct a name that doesn't conflict
750 {
751 header = GetHeader(userhostname, NULL, CFS_LocalHostName, ".local");
752 subtext = &CFS_Problem;
753 }
754 else if (usercompname[0])
755 {
756 header = GetHeader(usercompname, lastcompname, CFS_ComputerName, "");
757 subtext = &CFS_ComputerNameMsg;
758 }
759 else
760 {
761 header = GetHeader(userhostname, lasthostname, CFS_LocalHostName, ".local");
762 subtext = &CFS_LocalHostNameMsg;
763 }
764 ShowNameConflictNotification(header, *subtext);
765 CFRelease(header);
766 }
767 #endif
768 }
769
770 kern_return_t
771 do_mDNSPreferencesSetName(__unused mach_port_t port, int key, const char* old, const char* new, audit_token_t token)
772 {
773 SCPreferencesRef session = NULL;
774 Boolean ok = FALSE;
775 Boolean locked = FALSE;
776 CFStringRef cfstr = NULL;
777 char* user = NULL;
778 char* last = NULL;
779 Boolean needUpdate = FALSE;
780
781 debug("entry %s old=%s new=%s", key==kmDNSComputerName ? "ComputerName" : (key==kmDNSLocalHostName ? "LocalHostName" : "UNKNOWN"), old, new);
782 if (!authorized(&token)) goto fin;
783
784 switch ((enum mDNSPreferencesSetNameKey)key)
785 {
786 case kmDNSComputerName:
787 user = usercompname;
788 last = lastcompname;
789 break;
790 case kmDNSLocalHostName:
791 user = userhostname;
792 last = lasthostname;
793 break;
794 default:
795 debug("unrecognized key: %d", key);
796 goto fin;
797 }
798
799 if (!last)
800 {
801 helplog(ASL_LEVEL_ERR, "%s: no last ptr", __func__);
802 goto fin;
803 }
804
805 if (!user)
806 {
807 helplog(ASL_LEVEL_ERR, "%s: no user ptr", __func__);
808 goto fin;
809 }
810
811 if (0 == strncmp(old, new, MAX_DOMAIN_LABEL+1))
812 {
813 // if we've changed the name, but now someone else has set it to something different, we no longer need the notification
814 if (last[0] && 0 != strncmp(last, new, MAX_DOMAIN_LABEL+1))
815 {
816 last[0] = 0;
817 user[0] = 0;
818 needUpdate = TRUE;
819 }
820 goto fin;
821 }
822 else
823 {
824 if (strncmp(last, new, MAX_DOMAIN_LABEL+1))
825 {
826 strncpy(last, new, MAX_DOMAIN_LABEL);
827 needUpdate = TRUE;
828 }
829 }
830
831 if (!user[0])
832 {
833 strncpy(user, old, MAX_DOMAIN_LABEL);
834 needUpdate = TRUE;
835 }
836
837 if (!new[0]) // we've given up trying to construct a name that doesn't conflict
838 goto fin;
839
840 cfstr = CFStringCreateWithCString(NULL, new, kCFStringEncodingUTF8);
841
842 session = SCPreferencesCreate(NULL, CFSTR(kmDNSHelperServiceName), NULL);
843
844 if (cfstr == NULL || session == NULL)
845 {
846 debug("SCPreferencesCreate failed");
847 goto fin;
848 }
849 if (!SCPreferencesLock(session, 0))
850 {
851 debug("lock failed");
852 goto fin;
853 }
854 locked = TRUE;
855
856 switch ((enum mDNSPreferencesSetNameKey)key)
857 {
858 case kmDNSComputerName:
859 {
860 // We want to write the new Computer Name to System Preferences, without disturbing the user-selected
861 // system-wide default character set used for things like AppleTalk NBP and NETBIOS service advertising.
862 // Note that this encoding is not used for the computer name, but since both are set by the same call,
863 // we need to take care to set the name without changing the character set.
864 CFStringEncoding encoding = kCFStringEncodingUTF8;
865 CFStringRef unused = SCDynamicStoreCopyComputerName(NULL, &encoding);
866 if (unused) { CFRelease(unused); unused = NULL; }
867 else encoding = kCFStringEncodingUTF8;
868
869 ok = SCPreferencesSetComputerName(session, cfstr, encoding);
870 }
871 break;
872 case kmDNSLocalHostName:
873 ok = SCPreferencesSetLocalHostName(session, cfstr);
874 break;
875 default:
876 break;
877 }
878
879 if (!ok || !SCPreferencesCommitChanges(session) ||
880 !SCPreferencesApplyChanges(session))
881 {
882 debug("SCPreferences update failed");
883 goto fin;
884 }
885 debug("succeeded");
886
887 fin:
888 if (NULL != cfstr)
889 CFRelease(cfstr);
890 if (NULL != session)
891 {
892 if (locked)
893 SCPreferencesUnlock(session);
894 CFRelease(session);
895 }
896 update_idle_timer();
897 if (needUpdate) update_notification();
898 return KERN_SUCCESS;
899 }
900
901 enum DNSKeyFormat
902 {
903 formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem
904 };
905
906 // On Mac OS X on Intel, the four-character string seems to be stored backwards, at least sometimes.
907 // I suspect some overenthusiastic inexperienced engineer said, "On Intel everything's backwards,
908 // therefore I need to add some byte swapping in this API to make this four-character string backwards too."
909 // To cope with this we allow *both* "ddns" and "sndd" as valid item types.
910
911 static const char dnsprefix[] = "dns:";
912 static const char ddns[] = "ddns";
913 static const char ddnsrev[] = "sndd";
914
915 #ifndef NO_SECURITYFRAMEWORK
916 static enum DNSKeyFormat
917 getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp)
918 {
919 static UInt32 tags[3] =
920 {
921 kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr
922 };
923 static SecKeychainAttributeInfo attributeInfo =
924 {
925 sizeof(tags)/sizeof(tags[0]), tags, NULL
926 };
927 SecKeychainAttributeList *attributes = NULL;
928 enum DNSKeyFormat format;
929 Boolean malformed = FALSE;
930 OSStatus status = noErr;
931 int i = 0;
932
933 *attributesp = NULL;
934 if (noErr != (status = SecKeychainItemCopyAttributesAndData(item,
935 &attributeInfo, NULL, &attributes, NULL, NULL)))
936 {
937 debug("SecKeychainItemCopyAttributesAndData %d - skipping",
938 status);
939 goto skip;
940 }
941 if (attributeInfo.count != attributes->count)
942 malformed = TRUE;
943 for (i = 0; !malformed && i < (int)attributeInfo.count; ++i)
944 if (attributeInfo.tag[i] != attributes->attr[i].tag)
945 malformed = TRUE;
946 if (malformed)
947 {
948 debug(
949 "malformed result from SecKeychainItemCopyAttributesAndData - skipping");
950 goto skip;
951 }
952 debug("entry (\"%.*s\", \"%.*s\", \"%.*s\")",
953 (int)attributes->attr[0].length, attributes->attr[0].data,
954 (int)attributes->attr[1].length, attributes->attr[1].data,
955 (int)attributes->attr[2].length, attributes->attr[2].data);
956 if (attributes->attr[1].length >= MAX_ESCAPED_DOMAIN_NAME +
957 sizeof(dnsprefix)-1)
958 {
959 debug("kSecServiceItemAttr too long (%u) - skipping",
960 (unsigned int)attributes->attr[1].length);
961 goto skip;
962 }
963 if (attributes->attr[2].length >= MAX_ESCAPED_DOMAIN_NAME)
964 {
965 debug("kSecAccountItemAttr too long (%u) - skipping",
966 (unsigned int)attributes->attr[2].length);
967 goto skip;
968 }
969 if (attributes->attr[1].length >= sizeof(dnsprefix)-1 &&
970 0 == strncasecmp(attributes->attr[1].data, dnsprefix,
971 sizeof(dnsprefix)-1))
972 format = formatDnsPrefixedServiceItem;
973 else if (attributes->attr[0].length == sizeof(ddns)-1 &&
974 0 == strncasecmp(attributes->attr[0].data, ddns, sizeof(ddns)-1))
975 format = formatDdnsTypeItem;
976 else if (attributes->attr[0].length == sizeof(ddnsrev)-1 &&
977 0 == strncasecmp(attributes->attr[0].data, ddnsrev, sizeof(ddnsrev)-1))
978 format = formatDdnsTypeItem;
979 else
980 {
981 debug("uninterested in this entry");
982 goto skip;
983 }
984 *attributesp = attributes;
985 debug("accepting this entry");
986 return format;
987
988 skip:
989 SecKeychainItemFreeAttributesAndData(attributes, NULL);
990 return formatNotDNSKey;
991 }
992
993 static CFPropertyListRef
994 getKeychainItemInfo(SecKeychainItemRef item,
995 SecKeychainAttributeList *attributes, enum DNSKeyFormat format)
996 {
997 CFMutableArrayRef entry = NULL;
998 CFDataRef data = NULL;
999 OSStatus status = noErr;
1000 UInt32 keylen = 0;
1001 void *keyp = 0;
1002
1003 if (NULL == (entry = CFArrayCreateMutable(NULL, 0,
1004 &kCFTypeArrayCallBacks)))
1005 {
1006 debug("CFArrayCreateMutable failed");
1007 goto error;
1008 }
1009 switch ((enum DNSKeyFormat)format)
1010 {
1011 case formatDdnsTypeItem:
1012 data = CFDataCreate(kCFAllocatorDefault,
1013 attributes->attr[1].data, attributes->attr[1].length);
1014 break;
1015 case formatDnsPrefixedServiceItem:
1016 data = CFDataCreate(kCFAllocatorDefault,
1017 attributes->attr[1].data + sizeof(dnsprefix)-1,
1018 attributes->attr[1].length - (sizeof(dnsprefix)-1));
1019 default:
1020 assert("unknown DNSKeyFormat value");
1021 break;
1022 }
1023 if (NULL == data)
1024 {
1025 debug("CFDataCreate for attr[1] failed");
1026 goto error;
1027 }
1028 CFArrayAppendValue(entry, data);
1029 CFRelease(data);
1030 if (NULL == (data = CFDataCreate(kCFAllocatorDefault,
1031 attributes->attr[2].data, attributes->attr[2].length)))
1032 {
1033 debug("CFDataCreate for attr[2] failed");
1034 goto error;
1035 }
1036 CFArrayAppendValue(entry, data);
1037 CFRelease(data);
1038 if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, NULL,
1039 NULL, NULL, &keylen, &keyp)))
1040 {
1041 debug("could not retrieve key for \"%.*s\": %d",
1042 (int)attributes->attr[1].length, attributes->attr[1].data,
1043 status);
1044 goto error;
1045 }
1046 data = CFDataCreate(kCFAllocatorDefault, keyp, keylen);
1047 SecKeychainItemFreeAttributesAndData(NULL, keyp);
1048 if (NULL == data)
1049 {
1050 debug("CFDataCreate for keyp failed");
1051 goto error;
1052 }
1053 CFArrayAppendValue(entry, data);
1054 CFRelease(data);
1055 return entry;
1056
1057 error:
1058 if (NULL != entry)
1059 CFRelease(entry);
1060 return NULL;
1061 }
1062 #endif
1063
1064 kern_return_t
1065 do_mDNSKeychainGetSecrets(__unused mach_port_t port, __unused unsigned int *numsecrets,
1066 __unused vm_offset_t *secrets, __unused mach_msg_type_number_t *secretsCnt, __unused int *err,
1067 __unused audit_token_t token)
1068 {
1069 #ifndef NO_SECURITYFRAMEWORK
1070 CFWriteStreamRef stream = NULL;
1071 CFDataRef result = NULL;
1072 CFPropertyListRef entry = NULL;
1073 CFMutableArrayRef keys = NULL;
1074 SecKeychainRef skc = NULL;
1075 SecKeychainItemRef item = NULL;
1076 SecKeychainSearchRef search = NULL;
1077 SecKeychainAttributeList *attributes = NULL;
1078 enum DNSKeyFormat format;
1079 OSStatus status = 0;
1080
1081 debug("entry");
1082 *err = 0;
1083 *numsecrets = 0;
1084 *secrets = (vm_offset_t)NULL;
1085 if (!authorized(&token))
1086 {
1087 *err = kmDNSHelperNotAuthorized;
1088 goto fin;
1089 }
1090 if (NULL == (keys = CFArrayCreateMutable(NULL, 0,
1091 &kCFTypeArrayCallBacks)))
1092 {
1093 debug("CFArrayCreateMutable failed");
1094 *err = kmDNSHelperCreationFailed;
1095 goto fin;
1096 }
1097 if (noErr != (status = SecKeychainCopyDefault(&skc)))
1098 {
1099 *err = kmDNSHelperKeychainCopyDefaultFailed;
1100 goto fin;
1101 }
1102 if (noErr != (status = SecKeychainSearchCreateFromAttributes(skc, kSecGenericPasswordItemClass, NULL, &search)))
1103 {
1104 *err = kmDNSHelperKeychainSearchCreationFailed;
1105 goto fin;
1106 }
1107 for (status = SecKeychainSearchCopyNext(search, &item);
1108 noErr == status;
1109 status = SecKeychainSearchCopyNext(search, &item))
1110 {
1111 if (formatNotDNSKey != (format = getDNSKeyFormat(item,
1112 &attributes)) &&
1113 NULL != (entry = getKeychainItemInfo(item, attributes,
1114 format)))
1115 {
1116 CFArrayAppendValue(keys, entry);
1117 CFRelease(entry);
1118 }
1119 SecKeychainItemFreeAttributesAndData(attributes, NULL);
1120 CFRelease(item);
1121 }
1122 if (errSecItemNotFound != status)
1123 helplog(ASL_LEVEL_ERR, "%s: SecKeychainSearchCopyNext failed: %d",
1124 __func__, status);
1125 if (NULL == (stream =
1126 CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault,
1127 kCFAllocatorDefault)))
1128 {
1129 *err = kmDNSHelperCreationFailed;
1130 debug("CFWriteStreamCreateWithAllocatedBuffers failed");
1131 goto fin;
1132 }
1133 CFWriteStreamOpen(stream);
1134 if (0 == CFPropertyListWriteToStream(keys, stream,
1135 kCFPropertyListBinaryFormat_v1_0, NULL))
1136 {
1137 *err = kmDNSHelperPListWriteFailed;
1138 debug("CFPropertyListWriteToStream failed");
1139 goto fin;
1140 }
1141 result = CFWriteStreamCopyProperty(stream,
1142 kCFStreamPropertyDataWritten);
1143 if (KERN_SUCCESS != vm_allocate(mach_task_self(), secrets,
1144 CFDataGetLength(result), VM_FLAGS_ANYWHERE))
1145 {
1146 *err = kmDNSHelperCreationFailed;
1147 debug("vm_allocate failed");
1148 goto fin;
1149 }
1150 CFDataGetBytes(result, CFRangeMake(0, CFDataGetLength(result)),
1151 (void *)*secrets);
1152 *secretsCnt = CFDataGetLength(result);
1153 *numsecrets = CFArrayGetCount(keys);
1154 debug("succeeded");
1155
1156 fin:
1157 debug("returning %u secrets", *numsecrets);
1158 if (NULL != stream)
1159 {
1160 CFWriteStreamClose(stream);
1161 CFRelease(stream);
1162 }
1163 if (NULL != result)
1164 CFRelease(result);
1165 if (NULL != keys)
1166 CFRelease(keys);
1167 if (NULL != search)
1168 CFRelease(search);
1169 if (NULL != skc)
1170 CFRelease(skc);
1171 update_idle_timer();
1172 return KERN_SUCCESS;
1173 #else
1174 return KERN_FAILURE;
1175 #endif
1176 }
1177
1178 #ifndef MDNS_NO_IPSEC
1179 typedef enum _mDNSTunnelPolicyWhich
1180 {
1181 kmDNSTunnelPolicySetup,
1182 kmDNSTunnelPolicyTeardown,
1183 kmDNSTunnelPolicyGenerate
1184 } mDNSTunnelPolicyWhich;
1185
1186 static const uint8_t kWholeV6Mask = 128;
1187 static const uint8_t kZeroV6Mask = 0;
1188
1189 static int
1190 doTunnelPolicy(mDNSTunnelPolicyWhich which,
1191 v6addr_t loc_inner, uint8_t loc_bits,
1192 v4addr_t loc_outer, uint16_t loc_port,
1193 v6addr_t rmt_inner, uint8_t rmt_bits,
1194 v4addr_t rmt_outer, uint16_t rmt_port);
1195
1196 static int
1197 aliasTunnelAddress(v6addr_t address)
1198 {
1199 struct in6_aliasreq ifra_in6;
1200 int err = 0;
1201 int s = -1;
1202
1203 if (0 > (s = socket(AF_INET6, SOCK_DGRAM, 0)))
1204 {
1205 helplog(ASL_LEVEL_ERR, "socket(AF_INET6, ...) failed: %s",
1206 strerror(errno));
1207 err = kmDNSHelperDatagramSocketCreationFailed;
1208 goto fin;
1209 }
1210 memset(&ifra_in6, 0, sizeof(ifra_in6));
1211 strlcpy(ifra_in6.ifra_name, kTunnelAddressInterface,
1212 sizeof(ifra_in6.ifra_name));
1213 ifra_in6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
1214 ifra_in6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
1215
1216 ifra_in6.ifra_addr.sin6_family = AF_INET6;
1217 ifra_in6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
1218 memcpy(&(ifra_in6.ifra_addr.sin6_addr), address,
1219 sizeof(ifra_in6.ifra_addr.sin6_addr));
1220
1221 ifra_in6.ifra_prefixmask.sin6_family = AF_INET6;
1222 ifra_in6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
1223 memset(&(ifra_in6.ifra_prefixmask.sin6_addr), 0xFF,
1224 sizeof(ifra_in6.ifra_prefixmask.sin6_addr));
1225
1226 if (0 > ioctl(s, SIOCAIFADDR_IN6, &ifra_in6))
1227 {
1228 helplog(ASL_LEVEL_ERR,
1229 "ioctl(..., SIOCAIFADDR_IN6, ...) failed: %s",
1230 strerror(errno));
1231 err = kmDNSHelperInterfaceCreationFailed;
1232 goto fin;
1233 }
1234
1235 v6addr_t zero = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
1236 err = doTunnelPolicy(kmDNSTunnelPolicyGenerate,
1237 address, kWholeV6Mask, NULL, 0,
1238 zero, kZeroV6Mask, NULL, 0);
1239
1240 fin:
1241 if (0 <= s)
1242 close(s);
1243 return err;
1244 }
1245
1246 static int
1247 unaliasTunnelAddress(v6addr_t address)
1248 {
1249 struct in6_ifreq ifr;
1250 int err = 0;
1251 int s = -1;
1252
1253 if (0 > (s = socket(AF_INET6, SOCK_DGRAM, 0)))
1254 {
1255 helplog(ASL_LEVEL_ERR, "socket(AF_INET6, ...) failed: %s",
1256 strerror(errno));
1257 err = kmDNSHelperDatagramSocketCreationFailed;
1258 goto fin;
1259 }
1260 memset(&ifr, 0, sizeof(ifr));
1261 strlcpy(ifr.ifr_name, kTunnelAddressInterface, sizeof(ifr.ifr_name));
1262 ifr.ifr_ifru.ifru_addr.sin6_family = AF_INET6;
1263 ifr.ifr_ifru.ifru_addr.sin6_len = sizeof(struct sockaddr_in6);
1264 memcpy(&(ifr.ifr_ifru.ifru_addr.sin6_addr), address,
1265 sizeof(ifr.ifr_ifru.ifru_addr.sin6_addr));
1266
1267 if (0 > ioctl(s, SIOCDIFADDR_IN6, &ifr))
1268 {
1269 helplog(ASL_LEVEL_ERR,
1270 "ioctl(..., SIOCDIFADDR_IN6, ...) failed: %s",
1271 strerror(errno));
1272 err = kmDNSHelperInterfaceDeletionFailed;
1273 goto fin;
1274 }
1275
1276 v6addr_t zero = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
1277 err = doTunnelPolicy(kmDNSTunnelPolicyTeardown,
1278 address, kWholeV6Mask, NULL, 0,
1279 zero, kZeroV6Mask, NULL, 0);
1280
1281 fin:
1282 if (0 <= s)
1283 close(s);
1284 return err;
1285 }
1286 #endif /* ifndef MDNS_NO_IPSEC */
1287
1288 int
1289 do_mDNSAutoTunnelInterfaceUpDown(__unused mach_port_t port, int updown,
1290 v6addr_t address, audit_token_t token)
1291 {
1292 #ifndef MDNS_NO_IPSEC
1293 debug("entry");
1294 if (!authorized(&token)) goto fin;
1295
1296 switch ((enum mDNSUpDown)updown)
1297 {
1298 case kmDNSUp:
1299 aliasTunnelAddress(address);
1300 break;
1301 case kmDNSDown:
1302 unaliasTunnelAddress(address);
1303 break;
1304 default:
1305 goto fin;
1306 }
1307 debug("succeeded");
1308
1309 fin:
1310 #else
1311 (void)port; (void)updown; (void)address; (void)token;
1312 *err = kmDNSHelperIPsecDisabled;
1313 #endif
1314 update_idle_timer();
1315 return KERN_SUCCESS;
1316 }
1317
1318 #ifndef MDNS_NO_IPSEC
1319
1320 static const char g_racoon_config_dir[] = "/var/run/racoon/";
1321 static const char g_racoon_config_dir_old[] = "/etc/racoon/remote/";
1322
1323 CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void);
1324 CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey;
1325
1326 // Major version 6 is 10.2.x (Jaguar)
1327 // Major version 7 is 10.3.x (Panther)
1328 // Major version 8 is 10.4.x (Tiger)
1329 // Major version 9 is 10.5.x (Leopard)
1330 // Major version 10 is 10.6.x (SnowLeopard)
1331 static int MacOSXSystemBuildNumber(char* letter_out, int* minor_out)
1332 {
1333 int major = 0, minor = 0;
1334 char letter = 0, buildver[256]="<Unknown>";
1335 CFDictionaryRef vers = _CFCopySystemVersionDictionary();
1336 if (vers)
1337 {
1338 CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey);
1339 if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8);
1340 sscanf(buildver, "%d%c%d", &major, &letter, &minor);
1341 CFRelease(vers);
1342 }
1343 else
1344 helplog(ASL_LEVEL_NOTICE, "_CFCopySystemVersionDictionary failed");
1345
1346 if (!major) { major=10; letter = 'A'; minor = 190; helplog(ASL_LEVEL_NOTICE, "Note: No Major Build Version number found; assuming 10A190"); }
1347 if (letter_out) *letter_out = letter;
1348 if (minor_out) *minor_out = minor;
1349 return(major);
1350 }
1351
1352 static int g_oldRacoon = -1;
1353 static int g_racoonSignal = SIGUSR1;
1354
1355 static void DetermineRacoonVersion()
1356 {
1357 if (g_oldRacoon == -1)
1358 {
1359 char letter = 0;
1360 int minor = 0;
1361 g_oldRacoon = (MacOSXSystemBuildNumber(&letter, &minor) < 10);
1362 if (g_oldRacoon || (letter == 'A' && minor < 218)) g_racoonSignal = SIGHUP;
1363 debug("%s, signal=%d", g_oldRacoon?"old":"new", g_racoonSignal);
1364 }
1365 }
1366
1367 static int UseOldRacoon()
1368 {
1369 DetermineRacoonVersion();
1370 return g_oldRacoon;
1371 }
1372
1373 static int RacoonSignal()
1374 {
1375 DetermineRacoonVersion();
1376 return g_racoonSignal;
1377 }
1378
1379 static const char* GetRacoonConfigDir()
1380 {
1381 return UseOldRacoon() ? g_racoon_config_dir_old : g_racoon_config_dir;
1382 }
1383
1384 static const char* GetOldRacoonConfigDir()
1385 {
1386 return UseOldRacoon() ? NULL : g_racoon_config_dir_old;
1387 }
1388
1389 static const char racoon_config_file[] = "anonymous.conf";
1390 static const char racoon_config_file_orig[] = "anonymous.conf.orig";
1391
1392 static const char configHeader[] = "# BackToMyMac\n";
1393
1394 static int IsFamiliarRacoonConfiguration(const char* racoon_config_path)
1395 {
1396 int fd = open(racoon_config_path, O_RDONLY);
1397 debug("entry %s", racoon_config_path);
1398 if (0 > fd)
1399 {
1400 helplog(ASL_LEVEL_NOTICE, "open \"%s\" failed: %s", racoon_config_path, strerror(errno));
1401 return 0;
1402 }
1403 else
1404 {
1405 char header[sizeof(configHeader)] = {0};
1406 ssize_t bytesRead = read(fd, header, sizeof(header)-1);
1407 close(fd);
1408 if (bytesRead != sizeof(header)-1) return 0;
1409 return (0 == memcmp(header, configHeader, sizeof(header)-1));
1410 }
1411 }
1412
1413 static void
1414 revertAnonymousRacoonConfiguration(const char* dir)
1415 {
1416 if (!dir) return;
1417
1418 debug("entry %s", dir);
1419
1420 char racoon_config_path[64];
1421 strlcpy(racoon_config_path, dir, sizeof(racoon_config_path));
1422 strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path));
1423
1424 struct stat s;
1425 int ret = stat(racoon_config_path, &s);
1426 debug("stat(%s): %d errno=%d", racoon_config_path, ret, errno);
1427 if (ret == 0)
1428 {
1429 if (IsFamiliarRacoonConfiguration(racoon_config_path))
1430 {
1431 helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path);
1432 unlink(racoon_config_path);
1433 }
1434 else
1435 {
1436 helplog(ASL_LEVEL_NOTICE, "\"%s\" does not look familiar, leaving in place", racoon_config_path);
1437 return;
1438 }
1439 }
1440 else if (errno != ENOENT)
1441 {
1442 helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno));
1443 return;
1444 }
1445
1446 char racoon_config_path_orig[64];
1447 strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig));
1448 strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig));
1449
1450 ret = stat(racoon_config_path_orig, &s);
1451 debug("stat(%s): %d errno=%d", racoon_config_path_orig, ret, errno);
1452 if (ret == 0)
1453 {
1454 if (0 > rename(racoon_config_path_orig, racoon_config_path))
1455 helplog(ASL_LEVEL_NOTICE, "rename \"%s\" \"%s\" failed: %s", racoon_config_path_orig, racoon_config_path, strerror(errno));
1456 else
1457 debug("reverted \"%s\" to \"%s\"", racoon_config_path_orig, racoon_config_path);
1458 }
1459 else if (errno != ENOENT)
1460 {
1461 helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path_orig, strerror(errno));
1462 return;
1463 }
1464 }
1465
1466 static void
1467 moveAsideAnonymousRacoonConfiguration(const char* dir)
1468 {
1469 if (!dir) return;
1470
1471 debug("entry %s", dir);
1472
1473 char racoon_config_path[64];
1474 strlcpy(racoon_config_path, dir, sizeof(racoon_config_path));
1475 strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path));
1476
1477 struct stat s;
1478 int ret = stat(racoon_config_path, &s);
1479 if (ret == 0)
1480 {
1481 if (IsFamiliarRacoonConfiguration(racoon_config_path))
1482 {
1483 helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path);
1484 unlink(racoon_config_path);
1485 }
1486 else
1487 {
1488 char racoon_config_path_orig[64];
1489 strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig));
1490 strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig));
1491 if (0 > rename(racoon_config_path, racoon_config_path_orig)) // If we didn't write it, move it to the side so it can be reverted later
1492 helplog(ASL_LEVEL_NOTICE, "rename \"%s\" to \"%s\" failed: %s", racoon_config_path, racoon_config_path_orig, strerror(errno));
1493 else
1494 debug("successfully renamed \"%s\" to \"%s\"", racoon_config_path, racoon_config_path_orig);
1495 }
1496 }
1497 else if (errno != ENOENT)
1498 {
1499 helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno));
1500 return;
1501 }
1502 }
1503
1504 static int
1505 ensureExistenceOfRacoonConfigDir(const char* const racoon_config_dir)
1506 {
1507 struct stat s;
1508 int ret = stat(racoon_config_dir, &s);
1509 if (ret != 0)
1510 {
1511 if (errno != ENOENT)
1512 {
1513 helplog(ASL_LEVEL_ERR, "stat of \"%s\" failed (%d): %s",
1514 racoon_config_dir, ret, strerror(errno));
1515 return -1;
1516 }
1517 else
1518 {
1519 ret = mkdir(racoon_config_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
1520 if (ret != 0)
1521 {
1522 helplog(ASL_LEVEL_ERR, "mkdir \"%s\" failed: %s",
1523 racoon_config_dir, strerror(errno));
1524 return -1;
1525 }
1526 else
1527 helplog(ASL_LEVEL_INFO, "created directory \"%s\"", racoon_config_dir);
1528 }
1529 }
1530 else if (!(s.st_mode & S_IFDIR))
1531 {
1532 helplog(ASL_LEVEL_ERR, "\"%s\" is not a directory!",
1533 racoon_config_dir);
1534 return -1;
1535 }
1536
1537 return 0;
1538 }
1539
1540 static int
1541 createAnonymousRacoonConfiguration(const char *fqdn)
1542 {
1543 static const char config1[] =
1544 "remote anonymous {\n"
1545 " exchange_mode aggressive;\n"
1546 " doi ipsec_doi;\n"
1547 " situation identity_only;\n"
1548 " verify_identifier off;\n"
1549 " generate_policy on;\n"
1550 " shared_secret keychain_by_id \"dns:";
1551 static const char config2[] =
1552 "\";\n"
1553 " nonce_size 16;\n"
1554 " lifetime time 15 min;\n"
1555 " initial_contact on;\n"
1556 " support_proxy on;\n"
1557 " nat_traversal force;\n"
1558 " proposal_check claim;\n"
1559 " proposal {\n"
1560 " encryption_algorithm aes;\n"
1561 " hash_algorithm sha1;\n"
1562 " authentication_method pre_shared_key;\n"
1563 " dh_group 2;\n"
1564 " lifetime time 15 min;\n"
1565 " }\n"
1566 "}\n\n"
1567 "sainfo anonymous { \n"
1568 " pfs_group 2;\n"
1569 " lifetime time 10 min;\n"
1570 " encryption_algorithm aes;\n"
1571 " authentication_algorithm hmac_sha1;\n"
1572 " compression_algorithm deflate;\n"
1573 "}\n";
1574 char tmp_config_path[64];
1575 char racoon_config_path[64];
1576 const char* const racoon_config_dir = GetRacoonConfigDir();
1577 const char* const racoon_config_dir_old = GetOldRacoonConfigDir();
1578 int fd = -1;
1579
1580 debug("entry");
1581
1582 if (0 > ensureExistenceOfRacoonConfigDir(racoon_config_dir))
1583 return -1;
1584
1585 strlcpy(tmp_config_path, racoon_config_dir, sizeof(tmp_config_path));
1586 strlcat(tmp_config_path, "tmp.XXXXXX", sizeof(tmp_config_path));
1587
1588 fd = mkstemp(tmp_config_path);
1589
1590 if (0 > fd)
1591 {
1592 helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s",
1593 tmp_config_path, strerror(errno));
1594 return -1;
1595 }
1596 write(fd, configHeader, sizeof(configHeader)-1);
1597 write(fd, config1, sizeof(config1)-1);
1598 write(fd, fqdn, strlen(fqdn));
1599 write(fd, config2, sizeof(config2)-1);
1600 close(fd);
1601
1602 strlcpy(racoon_config_path, racoon_config_dir, sizeof(racoon_config_path));
1603 strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path));
1604
1605 moveAsideAnonymousRacoonConfiguration(racoon_config_dir_old);
1606 moveAsideAnonymousRacoonConfiguration(racoon_config_dir);
1607
1608 if (0 > rename(tmp_config_path, racoon_config_path))
1609 {
1610 unlink(tmp_config_path);
1611 helplog(ASL_LEVEL_ERR, "rename \"%s\" \"%s\" failed: %s",
1612 tmp_config_path, racoon_config_path, strerror(errno));
1613 revertAnonymousRacoonConfiguration(racoon_config_dir_old);
1614 revertAnonymousRacoonConfiguration(racoon_config_dir);
1615 return -1;
1616 }
1617
1618 debug("successfully renamed \"%s\" \"%s\"", tmp_config_path, racoon_config_path);
1619 return 0;
1620 }
1621
1622 static int
1623 notifyRacoon(void)
1624 {
1625 debug("entry");
1626 static const char racoon_pid_path[] = "/var/run/racoon.pid";
1627 char buf[] = "18446744073709551615"; /* largest 64-bit integer */
1628 char *p = NULL;
1629 ssize_t n = 0;
1630 unsigned long m = 0;
1631 int fd = open(racoon_pid_path, O_RDONLY);
1632
1633 if (0 > fd)
1634 {
1635 debug("open \"%s\" failed, and that's OK: %s", racoon_pid_path,
1636 strerror(errno));
1637 return kmDNSHelperRacoonNotificationFailed;
1638 }
1639 n = read(fd, buf, sizeof(buf)-1);
1640 close(fd);
1641 if (1 > n)
1642 {
1643 debug("read of \"%s\" failed: %s", racoon_pid_path,
1644 n == 0 ? "empty file" : strerror(errno));
1645 return kmDNSHelperRacoonNotificationFailed;
1646 }
1647 buf[n] = '\0';
1648 m = strtoul(buf, &p, 10);
1649 if (*p != '\0' && !isspace(*p))
1650 {
1651 debug("invalid PID \"%s\" (around '%c')", buf, *p);
1652 return kmDNSHelperRacoonNotificationFailed;
1653 }
1654 if (2 > m)
1655 {
1656 debug("refusing to kill PID %lu", m);
1657 return kmDNSHelperRacoonNotificationFailed;
1658 }
1659 if (0 != kill(m, RacoonSignal()))
1660 {
1661 debug("Could not signal racoon (%lu): %s", m, strerror(errno));
1662 return kmDNSHelperRacoonNotificationFailed;
1663 }
1664 debug("Sent racoon (%lu) signal %d", m, RacoonSignal());
1665 return 0;
1666 }
1667
1668 static void
1669 closefds(int from)
1670 {
1671 int fd = 0;
1672 struct dirent entry, *entryp = NULL;
1673 DIR *dirp = opendir("/dev/fd");
1674
1675 if (dirp == NULL)
1676 {
1677 /* fall back to the erroneous getdtablesize method */
1678 for (fd = from; fd < getdtablesize(); ++fd)
1679 close(fd);
1680 return;
1681 }
1682 while (0 == readdir_r(dirp, &entry, &entryp) && NULL != entryp)
1683 {
1684 fd = atoi(entryp->d_name);
1685 if (fd >= from && fd != dirfd(dirp))
1686 close(fd);
1687 }
1688 closedir(dirp);
1689 }
1690
1691 static int
1692 startRacoonOld(void)
1693 {
1694 debug("entry");
1695 char * const racoon_args[] = { "/usr/sbin/racoon", "-e", NULL };
1696 ssize_t n = 0;
1697 pid_t pid = 0;
1698 int status = 0;
1699
1700 if (0 == (pid = fork()))
1701 {
1702 closefds(0);
1703 execve(racoon_args[0], racoon_args, NULL);
1704 helplog(ASL_LEVEL_ERR, "execve of \"%s\" failed: %s",
1705 racoon_args[0], strerror(errno));
1706 exit(2);
1707 }
1708 helplog(ASL_LEVEL_NOTICE, "racoon (pid=%lu) started",
1709 (unsigned long)pid);
1710 n = waitpid(pid, &status, 0);
1711 if (-1 == n)
1712 {
1713 helplog(ASL_LEVEL_ERR, "Unexpected waitpid failure: %s",
1714 strerror(errno));
1715 return kmDNSHelperRacoonStartFailed;
1716 }
1717 else if (pid != n)
1718 {
1719 helplog(ASL_LEVEL_ERR, "Unexpected waitpid return value %d",
1720 (int)n);
1721 return kmDNSHelperRacoonStartFailed;
1722 }
1723 else if (WIFSIGNALED(status))
1724 {
1725 helplog(ASL_LEVEL_ERR,
1726 "racoon (pid=%lu) terminated due to signal %d",
1727 (unsigned long)pid, WTERMSIG(status));
1728 return kmDNSHelperRacoonStartFailed;
1729 }
1730 else if (WIFSTOPPED(status))
1731 {
1732 helplog(ASL_LEVEL_ERR,
1733 "racoon (pid=%lu) has stopped due to signal %d",
1734 (unsigned long)pid, WSTOPSIG(status));
1735 return kmDNSHelperRacoonStartFailed;
1736 }
1737 else if (0 != WEXITSTATUS(status))
1738 {
1739 helplog(ASL_LEVEL_ERR,
1740 "racoon (pid=%lu) exited with status %d",
1741 (unsigned long)pid, WEXITSTATUS(status));
1742 return kmDNSHelperRacoonStartFailed;
1743 }
1744 debug("racoon (pid=%lu) daemonized normally", (unsigned long)pid);
1745 return 0;
1746 }
1747
1748 // constant and structure for the racoon control socket
1749 #define VPNCTL_CMD_PING 0x0004
1750 typedef struct vpnctl_hdr_struct
1751 {
1752 u_int16_t msg_type;
1753 u_int16_t flags;
1754 u_int32_t cookie;
1755 u_int32_t reserved;
1756 u_int16_t result;
1757 u_int16_t len;
1758 } vpnctl_hdr;
1759
1760 static int
1761 startRacoon(void)
1762 {
1763 debug("entry");
1764 int fd = socket(PF_UNIX, SOCK_STREAM, 0);
1765 if (0 > fd)
1766 {
1767 helplog(ASL_LEVEL_ERR, "Could not create endpoint for racoon control socket: %d %s",
1768 errno, strerror(errno));
1769 return kmDNSHelperRacoonStartFailed;
1770 }
1771
1772 struct sockaddr_un saddr;
1773 memset(&saddr, 0, sizeof(saddr));
1774 saddr.sun_family = AF_UNIX;
1775 saddr.sun_len = sizeof(saddr);
1776 static const char racoon_control_sock_path[] = "/var/run/vpncontrol.sock";
1777 strcpy(saddr.sun_path, racoon_control_sock_path);
1778 int result = connect(fd, (struct sockaddr*) &saddr, saddr.sun_len);
1779 if (0 > result)
1780 {
1781 helplog(ASL_LEVEL_ERR, "Could not connect racoon control socket %s: %d %s",
1782 racoon_control_sock_path, errno, strerror(errno));
1783 return kmDNSHelperRacoonStartFailed;
1784 }
1785
1786 u_int32_t btmm_cookie = 0x4d4d5442;
1787 vpnctl_hdr h = { VPNCTL_CMD_PING, 0, btmm_cookie, 0, 0, 0 };
1788 size_t bytes = 0;
1789 ssize_t ret = 0;
1790
1791 while (bytes < sizeof(vpnctl_hdr))
1792 {
1793 ret = write(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes);
1794 if (ret == -1)
1795 {
1796 helplog(ASL_LEVEL_ERR, "Could not write to racoon control socket: %d %s",
1797 errno, strerror(errno));
1798 return kmDNSHelperRacoonStartFailed;
1799 }
1800 bytes += ret;
1801 }
1802
1803 int nfds = fd + 1;
1804 fd_set fds;
1805 int counter = 0;
1806 struct timeval tv;
1807 bytes = 0;
1808 h.cookie = 0;
1809
1810 while (counter < 100)
1811 {
1812 FD_ZERO(&fds);
1813 FD_SET(fd, &fds);
1814 tv = (struct timeval){ 0, 10000 }; // 10 milliseconds * 100 iterations = 1 second max wait time
1815
1816 result = select(nfds, &fds, (fd_set*)NULL, (fd_set*)NULL, &tv);
1817 if (result > 0)
1818 {
1819 if (FD_ISSET(fd, &fds))
1820 {
1821 ret = read(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes);
1822
1823 if (ret == -1)
1824 {
1825 helplog(ASL_LEVEL_ERR, "Could not read from racoon control socket: %d %s",
1826 strerror(errno));
1827 break;
1828 }
1829 bytes += ret;
1830 if (bytes >= sizeof(vpnctl_hdr)) break;
1831 }
1832 else
1833 {
1834 debug("select returned but fd_isset not on expected fd\n");
1835 }
1836 }
1837 else if (result < 0)
1838 {
1839 debug("select returned %d errno %d %s\n", result, errno, strerror(errno));
1840 if (errno != EINTR) break;
1841 }
1842 }
1843
1844 close(fd);
1845
1846 if (bytes < sizeof(vpnctl_hdr) || h.cookie != btmm_cookie) return kmDNSHelperRacoonStartFailed;
1847
1848 debug("racoon started");
1849 return 0;
1850 }
1851
1852 static int
1853 kickRacoon(void)
1854 {
1855 if ( 0 == notifyRacoon() )
1856 return 0;
1857 return UseOldRacoon() ? startRacoonOld() : startRacoon();
1858 }
1859
1860 #endif /* ndef MDNS_NO_IPSEC */
1861
1862 int
1863 do_mDNSConfigureServer(__unused mach_port_t port, int updown, const char *fqdn, audit_token_t token)
1864 {
1865 #ifndef MDNS_NO_IPSEC
1866 debug("entry");
1867 if (!authorized(&token)) goto fin;
1868
1869 switch ((enum mDNSUpDown)updown)
1870 {
1871 case kmDNSUp:
1872 if (0 != createAnonymousRacoonConfiguration(fqdn)) goto fin;
1873 break;
1874 case kmDNSDown:
1875 revertAnonymousRacoonConfiguration(GetOldRacoonConfigDir());
1876 revertAnonymousRacoonConfiguration(GetRacoonConfigDir());
1877 break;
1878 default:
1879 goto fin;
1880 }
1881
1882 if (0 != kickRacoon())
1883 goto fin;
1884 debug("succeeded");
1885
1886 fin:
1887 #else
1888 (void)port; (void)updown; (void)fqdn; (void)token;
1889 *err = kmDNSHelperIPsecDisabled;
1890 #endif
1891 update_idle_timer();
1892 return KERN_SUCCESS;
1893 }
1894
1895 #ifndef MDNS_NO_IPSEC
1896
1897 static unsigned int routeSeq = 1;
1898
1899 static int
1900 setupTunnelRoute(v6addr_t local, v6addr_t remote)
1901 {
1902 struct
1903 {
1904 struct rt_msghdr hdr;
1905 struct sockaddr_in6 dst;
1906 struct sockaddr_in6 gtwy;
1907 } msg;
1908 int err = 0;
1909 int s = -1;
1910
1911 if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET)))
1912 {
1913 helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s",
1914 strerror(errno));
1915 err = kmDNSHelperRoutingSocketCreationFailed;
1916 goto fin;
1917 }
1918 memset(&msg, 0, sizeof(msg));
1919 msg.hdr.rtm_msglen = sizeof(msg);
1920 msg.hdr.rtm_type = RTM_ADD;
1921 /* The following flags are set by `route add -inet6 -host ...` */
1922 msg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC;
1923 msg.hdr.rtm_version = RTM_VERSION;
1924 msg.hdr.rtm_seq = routeSeq++;
1925 msg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
1926 msg.hdr.rtm_inits = RTV_MTU;
1927 msg.hdr.rtm_rmx.rmx_mtu = 1280;
1928
1929 msg.dst.sin6_len = sizeof(msg.dst);
1930 msg.dst.sin6_family = AF_INET6;
1931 memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr));
1932
1933 msg.gtwy.sin6_len = sizeof(msg.gtwy);
1934 msg.gtwy.sin6_family = AF_INET6;
1935 memcpy(&msg.gtwy.sin6_addr, local, sizeof(msg.gtwy.sin6_addr));
1936
1937 /* send message, ignore error when route already exists */
1938 if (0 > write(s, &msg, msg.hdr.rtm_msglen))
1939 {
1940 int errno_ = errno;
1941
1942 debug("write to routing socket failed: %s", strerror(errno_));
1943 if (EEXIST != errno_)
1944 {
1945 err = kmDNSHelperRouteAdditionFailed;
1946 goto fin;
1947 }
1948 }
1949
1950 fin:
1951 if (0 <= s)
1952 close(s);
1953 return err;
1954 }
1955
1956 static int
1957 teardownTunnelRoute(v6addr_t remote)
1958 {
1959 struct
1960 {
1961 struct rt_msghdr hdr;
1962 struct sockaddr_in6 dst;
1963 } msg;
1964 int err = 0;
1965 int s = -1;
1966
1967 if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET)))
1968 {
1969 helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s",
1970 strerror(errno));
1971 err = kmDNSHelperRoutingSocketCreationFailed;
1972 goto fin;
1973 }
1974 memset(&msg, 0, sizeof(msg));
1975
1976 msg.hdr.rtm_msglen = sizeof(msg);
1977 msg.hdr.rtm_type = RTM_DELETE;
1978 msg.hdr.rtm_version = RTM_VERSION;
1979 msg.hdr.rtm_seq = routeSeq++;
1980 msg.hdr.rtm_addrs = RTA_DST;
1981
1982 msg.dst.sin6_len = sizeof(msg.dst);
1983 msg.dst.sin6_family = AF_INET6;
1984 memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr));
1985 if (0 > write(s, &msg, msg.hdr.rtm_msglen))
1986 {
1987 int errno_ = errno;
1988
1989 debug("write to routing socket failed: %s", strerror(errno_));
1990 if (ESRCH != errno_)
1991 {
1992 err = kmDNSHelperRouteDeletionFailed;
1993 goto fin;
1994 }
1995 }
1996
1997 fin:
1998 if (0 <= s)
1999 close(s);
2000 return err;
2001 }
2002
2003 static int
2004 v4addr_to_string(v4addr_t addr, char *buf, size_t buflen)
2005 {
2006 if (NULL == inet_ntop(AF_INET, addr, buf, buflen))
2007 {
2008 helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s",
2009 strerror(errno));
2010 return kmDNSHelperInvalidNetworkAddress;
2011 }
2012 else
2013 return 0;
2014 }
2015
2016 static int
2017 v6addr_to_string(v6addr_t addr, char *buf, size_t buflen)
2018 {
2019 if (NULL == inet_ntop(AF_INET6, addr, buf, buflen))
2020 {
2021 helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s",
2022 strerror(errno));
2023 return kmDNSHelperInvalidNetworkAddress;
2024 }
2025 else
2026 return 0;
2027 }
2028
2029 /* Caller owns object returned in `policy' */
2030 static int
2031 generateTunnelPolicy(mDNSTunnelPolicyWhich which, int in,
2032 v4addr_t src, uint16_t src_port,
2033 v4addr_t dst, uint16_t dst_port,
2034 ipsec_policy_t *policy, size_t *len)
2035 {
2036 char srcs[INET_ADDRSTRLEN], dsts[INET_ADDRSTRLEN];
2037 char buf[128];
2038 char *inOut = in ? "in" : "out";
2039 ssize_t n = 0;
2040 int err = 0;
2041
2042 *policy = NULL;
2043 *len = 0;
2044
2045 switch (which)
2046 {
2047 case kmDNSTunnelPolicySetup:
2048 if (0 != (err = v4addr_to_string(src, srcs, sizeof(srcs))))
2049 goto fin;
2050 if (0 != (err = v4addr_to_string(dst, dsts, sizeof(dsts))))
2051 goto fin;
2052 n = snprintf(buf, sizeof(buf),
2053 "%s ipsec esp/tunnel/%s[%u]-%s[%u]/require",
2054 inOut, srcs, src_port, dsts, dst_port);
2055 break;
2056 case kmDNSTunnelPolicyTeardown:
2057 n = strlcpy(buf, inOut, sizeof(buf));
2058 break;
2059 case kmDNSTunnelPolicyGenerate:
2060 n = snprintf(buf, sizeof(buf), "%s generate", inOut);
2061 break;
2062 default:
2063 err = kmDNSHelperIPsecPolicyCreationFailed;
2064 goto fin;
2065 }
2066
2067 if (n >= (int)sizeof(buf))
2068 {
2069 err = kmDNSHelperResultTooLarge;
2070 goto fin;
2071 }
2072
2073 debug("policy=\"%s\"", buf);
2074 if (NULL == (*policy = (ipsec_policy_t)ipsec_set_policy(buf, n)))
2075 {
2076 helplog(ASL_LEVEL_ERR,
2077 "Could not create IPsec policy from \"%s\"", buf);
2078 err = kmDNSHelperIPsecPolicyCreationFailed;
2079 goto fin;
2080 }
2081 *len = ((ipsec_policy_t)(*policy))->sadb_x_policy_len * 8;
2082
2083 fin:
2084 return err;
2085 }
2086
2087 static int
2088 sendPolicy(int s, int setup,
2089 struct sockaddr *src, uint8_t src_bits,
2090 struct sockaddr *dst, uint8_t dst_bits,
2091 ipsec_policy_t policy, size_t len)
2092 {
2093 static unsigned int policySeq = 0;
2094 int err = 0;
2095
2096 debug("entry, setup=%d", setup);
2097 if (setup)
2098 err = pfkey_send_spdadd(s, src, src_bits, dst, dst_bits, -1,
2099 (char *)policy, len, policySeq++);
2100 else
2101 err = pfkey_send_spddelete(s, src, src_bits, dst, dst_bits, -1,
2102 (char *)policy, len, policySeq++);
2103 if (0 > err)
2104 {
2105 helplog(ASL_LEVEL_ERR, "Could not set IPsec policy: %s",
2106 ipsec_strerror());
2107 err = kmDNSHelperIPsecPolicySetFailed;
2108 goto fin;
2109 }
2110 else
2111 err = 0;
2112 debug("succeeded");
2113
2114 fin:
2115 return err;
2116 }
2117
2118 static int
2119 removeSA(int s, struct sockaddr *src, struct sockaddr *dst)
2120 {
2121 int err = 0;
2122
2123 debug("entry");
2124 err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, src, dst);
2125 if (0 > err)
2126 {
2127 helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror());
2128 err = kmDNSHelperIPsecRemoveSAFailed;
2129 goto fin;
2130 }
2131 err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, dst, src);
2132 if (0 > err)
2133 {
2134 helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror());
2135 err = kmDNSHelperIPsecRemoveSAFailed;
2136 goto fin;
2137 }
2138 else
2139 err = 0;
2140
2141 debug("succeeded");
2142
2143 fin:
2144 return err;
2145 }
2146
2147 static int
2148 doTunnelPolicy(mDNSTunnelPolicyWhich which,
2149 v6addr_t loc_inner, uint8_t loc_bits,
2150 v4addr_t loc_outer, uint16_t loc_port,
2151 v6addr_t rmt_inner, uint8_t rmt_bits,
2152 v4addr_t rmt_outer, uint16_t rmt_port)
2153 {
2154 struct sockaddr_in6 sin6_loc;
2155 struct sockaddr_in6 sin6_rmt;
2156 ipsec_policy_t policy = NULL;
2157 size_t len = 0;
2158 int s = -1;
2159 int err = 0;
2160
2161 debug("entry");
2162 if (0 > (s = pfkey_open()))
2163 {
2164 helplog(ASL_LEVEL_ERR,
2165 "Could not create IPsec policy socket: %s",
2166 ipsec_strerror());
2167 err = kmDNSHelperIPsecPolicySocketCreationFailed;
2168 goto fin;
2169 }
2170
2171 memset(&sin6_loc, 0, sizeof(sin6_loc));
2172 sin6_loc.sin6_len = sizeof(sin6_loc);
2173 sin6_loc.sin6_family = AF_INET6;
2174 sin6_loc.sin6_port = htons(0);
2175 memcpy(&sin6_loc.sin6_addr, loc_inner, sizeof(sin6_loc.sin6_addr));
2176
2177 memset(&sin6_rmt, 0, sizeof(sin6_rmt));
2178 sin6_rmt.sin6_len = sizeof(sin6_rmt);
2179 sin6_rmt.sin6_family = AF_INET6;
2180 sin6_rmt.sin6_port = htons(0);
2181 memcpy(&sin6_rmt.sin6_addr, rmt_inner, sizeof(sin6_rmt.sin6_addr));
2182
2183 int setup = which != kmDNSTunnelPolicyTeardown;
2184
2185 if (0 != (err = generateTunnelPolicy(which, 1,
2186 rmt_outer, rmt_port,
2187 loc_outer, loc_port,
2188 &policy, &len)))
2189 goto fin;
2190 if (0 != (err = sendPolicy(s, setup,
2191 (struct sockaddr *)&sin6_rmt, rmt_bits,
2192 (struct sockaddr *)&sin6_loc, loc_bits,
2193 policy, len)))
2194 goto fin;
2195 if (NULL != policy)
2196 {
2197 free(policy);
2198 policy = NULL;
2199 }
2200 if (0 != (err = generateTunnelPolicy(which, 0,
2201 loc_outer, loc_port,
2202 rmt_outer, rmt_port,
2203 &policy, &len)))
2204 goto fin;
2205 if (0 != (err = sendPolicy(s, setup,
2206 (struct sockaddr *)&sin6_loc, loc_bits,
2207 (struct sockaddr *)&sin6_rmt, rmt_bits,
2208 policy, len)))
2209 goto fin;
2210
2211 if (which == kmDNSTunnelPolicyTeardown && loc_outer && rmt_outer)
2212 {
2213 struct sockaddr_in sin_loc;
2214 struct sockaddr_in sin_rmt;
2215
2216 memset(&sin_loc, 0, sizeof(sin_loc));
2217 sin_loc.sin_len = sizeof(sin_loc);
2218 sin_loc.sin_family = AF_INET;
2219 sin_loc.sin_port = htons(0);
2220 memcpy(&sin_loc.sin_addr, loc_outer, sizeof(sin_loc.sin_addr));
2221
2222 memset(&sin_rmt, 0, sizeof(sin_rmt));
2223 sin_rmt.sin_len = sizeof(sin_rmt);
2224 sin_rmt.sin_family = AF_INET;
2225 sin_rmt.sin_port = htons(0);
2226 memcpy(&sin_rmt.sin_addr, rmt_outer, sizeof(sin_rmt.sin_addr));
2227
2228 if (0 != (err = removeSA(s, (struct sockaddr *)&sin_loc, (struct sockaddr *)&sin_rmt)))
2229 goto fin;
2230 }
2231
2232 debug("succeeded");
2233
2234 fin:
2235 if (s >= 0)
2236 pfkey_close(s);
2237 if (NULL != policy)
2238 free(policy);
2239 return err;
2240 }
2241
2242 #endif /* ndef MDNS_NO_IPSEC */
2243
2244 int
2245 do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete,
2246 v6addr_t loc_inner, v4addr_t loc_outer, uint16_t loc_port,
2247 v6addr_t rmt_inner, v4addr_t rmt_outer, uint16_t rmt_port,
2248 const char *fqdn, int *err, audit_token_t token)
2249 {
2250 #ifndef MDNS_NO_IPSEC
2251 static const char config[] =
2252 "%s"
2253 "remote %s [%u] {\n"
2254 " disconnect_on_idle idle_timeout 600 idle_direction idle_outbound;\n"
2255 " exchange_mode aggressive;\n"
2256 " doi ipsec_doi;\n"
2257 " situation identity_only;\n"
2258 " verify_identifier off;\n"
2259 " generate_policy on;\n"
2260 " my_identifier user_fqdn \"dns:%s\";\n"
2261 " shared_secret keychain \"dns:%s\";\n"
2262 " nonce_size 16;\n"
2263 " lifetime time 15 min;\n"
2264 " initial_contact on;\n"
2265 " support_proxy on;\n"
2266 " nat_traversal force;\n"
2267 " proposal_check claim;\n"
2268 " proposal {\n"
2269 " encryption_algorithm aes;\n"
2270 " hash_algorithm sha1;\n"
2271 " authentication_method pre_shared_key;\n"
2272 " dh_group 2;\n"
2273 " lifetime time 15 min;\n"
2274 " }\n"
2275 "}\n\n"
2276 "sainfo address %s any address %s any {\n"
2277 " pfs_group 2;\n"
2278 " lifetime time 10 min;\n"
2279 " encryption_algorithm aes;\n"
2280 " authentication_algorithm hmac_sha1;\n"
2281 " compression_algorithm deflate;\n"
2282 "}\n\n"
2283 "sainfo address %s any address %s any {\n"
2284 " pfs_group 2;\n"
2285 " lifetime time 10 min;\n"
2286 " encryption_algorithm aes;\n"
2287 " authentication_algorithm hmac_sha1;\n"
2288 " compression_algorithm deflate;\n"
2289 "}\n";
2290 char path[PATH_MAX] = "";
2291 char li[INET6_ADDRSTRLEN], lo[INET_ADDRSTRLEN],
2292 ri[INET6_ADDRSTRLEN], ro[INET_ADDRSTRLEN];
2293 FILE *fp = NULL;
2294 int fd = -1;
2295 char tmp_path[PATH_MAX] = "";
2296
2297 debug("entry");
2298 *err = 0;
2299 if (!authorized(&token))
2300 {
2301 *err = kmDNSHelperNotAuthorized;
2302 goto fin;
2303 }
2304 switch ((enum mDNSAutoTunnelSetKeysReplaceDelete)replacedelete)
2305 {
2306 case kmDNSAutoTunnelSetKeysReplace:
2307 case kmDNSAutoTunnelSetKeysDelete:
2308 break;
2309 default:
2310 *err = kmDNSHelperInvalidTunnelSetKeysOperation;
2311 goto fin;
2312 }
2313 if (0 != (*err = v6addr_to_string(loc_inner, li, sizeof(li))))
2314 goto fin;
2315 if (0 != (*err = v6addr_to_string(rmt_inner, ri, sizeof(ri))))
2316 goto fin;
2317 if (0 != (*err = v4addr_to_string(loc_outer, lo, sizeof(lo))))
2318 goto fin;
2319 if (0 != (*err = v4addr_to_string(rmt_outer, ro, sizeof(ro))))
2320 goto fin;
2321 debug("loc_inner=%s rmt_inner=%s", li, ri);
2322 debug("loc_outer=%s loc_port=%u rmt_outer=%s rmt_port=%u",
2323 lo, loc_port, ro, rmt_port);
2324
2325 if ((int)sizeof(path) <= snprintf(path, sizeof(path),
2326 "%s%s.%u.conf", GetRacoonConfigDir(), ro,
2327 rmt_port))
2328 {
2329 *err = kmDNSHelperResultTooLarge;
2330 goto fin;
2331 }
2332 if (kmDNSAutoTunnelSetKeysReplace == replacedelete)
2333 {
2334 if (0 > ensureExistenceOfRacoonConfigDir(GetRacoonConfigDir()))
2335 {
2336 *err = kmDNSHelperRacoonConfigCreationFailed;
2337 goto fin;
2338 }
2339 if ((int)sizeof(tmp_path) <=
2340 snprintf(tmp_path, sizeof(tmp_path), "%s.XXXXXX", path))
2341 {
2342 *err = kmDNSHelperResultTooLarge;
2343 goto fin;
2344 }
2345 if (0 > (fd = mkstemp(tmp_path)))
2346 {
2347 helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s",
2348 tmp_path, strerror(errno));
2349 *err = kmDNSHelperRacoonConfigCreationFailed;
2350 goto fin;
2351 }
2352 if (NULL == (fp = fdopen(fd, "w")))
2353 {
2354 helplog(ASL_LEVEL_ERR, "fdopen: %s",
2355 strerror(errno));
2356 *err = kmDNSHelperRacoonConfigCreationFailed;
2357 goto fin;
2358 }
2359 fd = -1;
2360 fprintf(fp, config, configHeader, ro, rmt_port, fqdn, fqdn, ri, li, li, ri);
2361 fclose(fp);
2362 fp = NULL;
2363 if (0 > rename(tmp_path, path))
2364 {
2365 helplog(ASL_LEVEL_ERR,
2366 "rename \"%s\" \"%s\" failed: %s",
2367 tmp_path, path, strerror(errno));
2368 *err = kmDNSHelperRacoonConfigCreationFailed;
2369 goto fin;
2370 }
2371 if (0 != (*err = kickRacoon()))
2372 goto fin;
2373 }
2374 else
2375 {
2376 if (0 != unlink(path))
2377 debug("unlink \"%s\" failed: %s", path,
2378 strerror(errno));
2379 }
2380
2381 if (0 != (*err = doTunnelPolicy(kmDNSTunnelPolicyTeardown,
2382 loc_inner, kWholeV6Mask, loc_outer, loc_port,
2383 rmt_inner, kWholeV6Mask, rmt_outer, rmt_port)))
2384 goto fin;
2385 if (kmDNSAutoTunnelSetKeysReplace == replacedelete &&
2386 0 != (*err = doTunnelPolicy(kmDNSTunnelPolicySetup,
2387 loc_inner, kWholeV6Mask, loc_outer, loc_port,
2388 rmt_inner, kWholeV6Mask, rmt_outer, rmt_port)))
2389 goto fin;
2390
2391 if (0 != (*err = teardownTunnelRoute(rmt_inner)))
2392 goto fin;
2393 if (kmDNSAutoTunnelSetKeysReplace == replacedelete &&
2394 0 != (*err = setupTunnelRoute(loc_inner, rmt_inner)))
2395 goto fin;
2396
2397 debug("succeeded");
2398
2399 fin:
2400 if (NULL != fp)
2401 fclose(fp);
2402 if (0 <= fd)
2403 close(fd);
2404 unlink(tmp_path);
2405 #else
2406 (void)replacedelete; (void)loc_inner; (void)loc_outer; (void)loc_port; (void)rmt_inner;
2407 (void)rmt_outer; (void)rmt_port; (void)keydata; (void)token;
2408
2409 *err = kmDNSHelperIPsecDisabled;
2410 #endif /* MDNS_NO_IPSEC */
2411 update_idle_timer();
2412 return KERN_SUCCESS;
2413 }