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