]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/helper.c
mDNSResponder-164.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 Change History (most recent first):
18
19 $Log: helper.c,v $
20 Revision 1.20 2007/09/12 18:07:44 cheshire
21 Fix compile errors ("passing argument from incompatible pointer type")
22
23 Revision 1.19 2007/09/12 00:42:47 mcguire
24 <rdar://problem/5468236> BTMM: Need to clean up security associations
25
26 Revision 1.18 2007/09/12 00:40:16 mcguire
27 <rdar://problem/5469660> 9A547: Computer Name had incorrectly encoded unicode
28
29 Revision 1.17 2007/09/09 02:21:17 mcguire
30 <rdar://problem/5469345> Leopard Server9A547(Insatll):mDNSResponderHelper crashing
31
32 Revision 1.16 2007/09/07 22:44:03 mcguire
33 <rdar://problem/5448420> Move CFUserNotification code to mDNSResponderHelper
34
35 Revision 1.15 2007/09/07 22:24:36 vazquez
36 <rdar://problem/5466301> Need to stop spewing mDNSResponderHelper logs
37
38 Revision 1.14 2007/09/06 20:39:05 cheshire
39 Added comment explaining why we allow both "ddns" and "sndd" as valid item types
40 The Keychain APIs on Intel appear to store the four-character item type backwards (at least some of the time)
41
42 Revision 1.13 2007/09/04 22:32:58 mcguire
43 <rdar://problem/5453633> BTMM: BTMM overwrites /etc/racoon/remote/anonymous.conf
44
45 Revision 1.12 2007/08/29 21:42:12 mcguire
46 <rdar://problem/5431192> BTMM: Duplicate Private DNS names are being added to DynamicStore
47
48 Revision 1.11 2007/08/28 00:33:04 jgraessley
49 <rdar://problem/5423932> Selective compilation options
50
51 Revision 1.10 2007/08/27 22:16:38 mcguire
52 <rdar://problem/5437362> BTMM: MTU should be set to 1280
53
54 Revision 1.9 2007/08/27 22:13:59 mcguire
55 <rdar://problem/5437373> BTMM: IPSec security associations should have a shorter timeout
56
57 Revision 1.8 2007/08/23 21:49:51 cheshire
58 Made code layout style consistent with existing project style; added $Log header
59
60 Revision 1.7 2007/08/23 00:29:05 mcguire
61 <rdar://problem/5425800> BTMM: IPSec policy not installed in some situations - connections fail
62
63 Revision 1.6 2007/08/18 01:02:03 mcguire
64 <rdar://problem/5415593> No Bonjour services are getting registered at boot
65
66 Revision 1.5 2007/08/18 00:59:55 mcguire
67 <rdar://problem/5392568> Blocked: BTMM: Start racoon with '-e' parameter
68
69 Revision 1.4 2007/08/16 01:00:06 mcguire
70 <rdar://problem/5392548> BTMM: Install generate IPsec policies to block non-BTMM traffic
71
72 Revision 1.3 2007/08/15 23:20:28 mcguire
73 <rdar://problem/5408105> BTMM: racoon files can get corrupted if autotunnel is listening on port > 32767
74
75 Revision 1.2 2007/08/10 22:30:39 mcguire
76 <rdar://problem/5400259> BTMM: racoon config files are not always the correct mode
77
78 Revision 1.1 2007/08/08 22:34:58 mcguire
79 <rdar://problem/5197869> Security: Run mDNSResponder as user id mdnsresponder instead of root
80 */
81
82 #include <sys/cdefs.h>
83 #include <arpa/inet.h>
84 #include <bsm/libbsm.h>
85 #include <net/if.h>
86 #include <net/route.h>
87 #include <netinet/in.h>
88 #include <netinet6/in6_var.h>
89 #include <netinet6/nd6.h>
90 #include <netinet6/ipsec.h>
91 #include <sys/ioctl.h>
92 #include <sys/socket.h>
93 #include <asl.h>
94 #include <ctype.h>
95 #include <dirent.h>
96 #include <errno.h>
97 #include <fcntl.h>
98 #include <stdarg.h>
99 #include <stdbool.h>
100 #include <string.h>
101 #include <unistd.h>
102 #include <Security/Security.h>
103 #include <SystemConfiguration/SCDynamicStore.h>
104 #include <SystemConfiguration/SCPreferencesSetSpecific.h>
105 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
106 #include "mDNSEmbeddedAPI.h"
107 #include "dns_sd.h"
108 #include "dnssd_ipc.h"
109 #include "libpfkey.h"
110 #include "helper.h"
111 #include "helpermsgServer.h"
112 #include "helper-server.h"
113
114 #if TARGET_OS_EMBEDDED
115 #define NO_SECURITYFRAMEWORK 1
116 #endif
117
118 typedef struct sadb_x_policy *ipsec_policy_t;
119
120 uid_t mDNSResponderUID;
121 gid_t mDNSResponderGID;
122 static const char kTunnelAddressInterface[] = "lo0";
123
124 void
125 debug_(const char *func, const char *fmt, ...)
126 {
127 char buf[2048];
128 va_list ap;
129 ssize_t n = snprintf(buf, sizeof(buf), "%s: ", func);
130
131 if (n >= (int)sizeof(buf))
132 return;
133 va_start(ap, fmt);
134 vsnprintf(&buf[n], sizeof(buf)-n, fmt, ap);
135 va_end(ap);
136 helplog(ASL_LEVEL_DEBUG, buf);
137 }
138
139 static int
140 authorized(audit_token_t *token)
141 {
142 int ok = 0;
143 pid_t pid = (pid_t)-1;
144 uid_t euid = (uid_t)-1;
145
146 audit_token_to_au32(*token, NULL, &euid, NULL, NULL, NULL, &pid, NULL,
147 NULL);
148 ok = (euid == mDNSResponderUID || euid == 0);
149 if (!ok)
150 helplog(ASL_LEVEL_NOTICE,
151 "Unauthorized access by euid=%lu pid=%lu",
152 (unsigned long)euid, (unsigned long)pid);
153 return ok;
154 }
155
156 static void
157 closefds(int from)
158 {
159 int fd = 0;
160 struct dirent entry, *entryp = NULL;
161 DIR *dirp = opendir("/dev/fd");
162
163 if (dirp == NULL)
164 {
165 /* fall back to the erroneous getdtablesize method */
166 for (fd = from; fd < getdtablesize(); ++fd)
167 close(fd);
168 return;
169 }
170 while (0 == readdir_r(dirp, &entry, &entryp) && NULL != entryp)
171 {
172 fd = atoi(entryp->d_name);
173 if (fd >= from && fd != dirfd(dirp))
174 close(fd);
175 }
176 closedir(dirp);
177 }
178
179 kern_return_t
180 do_mDNSIdleExit(__unused mach_port_t port, audit_token_t token)
181 {
182 debug("entry");
183 if (!authorized(&token))
184 goto fin;
185 helplog(ASL_LEVEL_INFO, "Idle exit");
186 exit(0);
187
188 fin:
189 debug("fin");
190 return KERN_SUCCESS;
191 }
192
193 kern_return_t
194 do_mDNSDynamicStoreSetConfig(__unused mach_port_t port, int key,
195 vm_offset_t value, mach_msg_type_number_t valueCnt, int *err,
196 audit_token_t token)
197 {
198 CFStringRef sckey = NULL;
199 CFDataRef bytes = NULL;
200 CFPropertyListRef plist = NULL;
201 SCDynamicStoreRef store = NULL;
202
203 debug("entry");
204 *err = 0;
205 if (!authorized(&token))
206 {
207 *err = kmDNSHelperNotAuthorized;
208 goto fin;
209 }
210 switch ((enum mDNSDynamicStoreSetConfigKey)key)
211 {
212 case kmDNSMulticastConfig:
213 sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS);
214 break;
215 case kmDNSDynamicConfig:
216 sckey = CFSTR("State:/Network/DynamicDNS");
217 break;
218 case kmDNSPrivateConfig:
219 sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS);
220 break;
221 case kmDNSBackToMyMacConfig:
222 sckey = CFSTR("State:/Network/BackToMyMac");
223 break;
224 default:
225 debug("unrecognized key %d", key);
226 *err = kmDNSHelperInvalidConfigKey;
227 goto fin;
228 }
229 if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value,
230 valueCnt, kCFAllocatorNull)))
231 {
232 debug("CFDataCreateWithBytesNoCopy of value failed");
233 *err = kmDNSHelperCreationFailed;
234 goto fin;
235 }
236 if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes,
237 kCFPropertyListImmutable, NULL)))
238 {
239 debug("CFPropertyListCreateFromXMLData of bytes failed");
240 *err = kmDNSHelperInvalidPList;
241 goto fin;
242 }
243 CFRelease(bytes);
244 bytes = NULL;
245 if (NULL == (store = SCDynamicStoreCreate(NULL,
246 CFSTR(kmDNSHelperServiceName), NULL, NULL)))
247 {
248 debug("SCDynamicStoreCreate failed");
249 *err = kmDNSHelperDynamicStoreFailed;
250 goto fin;
251 }
252 SCDynamicStoreSetValue(store, sckey, plist);
253 *err = 0;
254 debug("succeeded");
255
256 fin:
257 if (0 != *err)
258 debug("failed err=%d", *err);
259 if (NULL != bytes)
260 CFRelease(bytes);
261 if (NULL != plist)
262 CFRelease(plist);
263 if (NULL != store)
264 CFRelease(store);
265 vm_deallocate(mach_task_self(), value, valueCnt);
266 update_idle_timer();
267 return KERN_SUCCESS;
268 }
269
270 char usercompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name the user saw
271 char userhostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name the user saw
272 char lastcompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name saved to preferences
273 char lasthostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name saved to preferences
274
275 static CFStringRef CFS_OQ = NULL;
276 static CFStringRef CFS_CQ = NULL;
277 static CFStringRef CFS_Format = NULL;
278 static CFStringRef CFS_ComputerName = NULL;
279 static CFStringRef CFS_ComputerNameMsg = NULL;
280 static CFStringRef CFS_LocalHostName = NULL;
281 static CFStringRef CFS_LocalHostNameMsg = NULL;
282 static CFStringRef CFS_Problem = NULL;
283
284 static CFUserNotificationRef gNotification = NULL;
285 static CFRunLoopSourceRef gNotificationRLS = NULL;
286
287 static void NotificationCallBackDismissed(CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
288 {
289 debug("entry");
290 (void)responseFlags; // Unused
291 if (userNotification != gNotification) helplog(ASL_LEVEL_ERR, "NotificationCallBackDismissed: Wrong CFUserNotificationRef");
292 if (gNotificationRLS)
293 {
294 // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread.
295 // We need to explicitly specify the desired CFRunLoop from which we want to remove this event source.
296 CFRunLoopRemoveSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode);
297 CFRelease(gNotificationRLS);
298 gNotificationRLS = NULL;
299 CFRelease(gNotification);
300 gNotification = NULL;
301 }
302 // By dismissing the alert, the user has conceptually acknowleged the rename.
303 // (e.g. the machine's name is now officially "computer-2.local", not "computer.local".)
304 // If we get *another* conflict, the new alert should refer to the 'old' name
305 // as now being "computer-2.local", not "computer.local"
306 usercompname[0] = 0;
307 userhostname[0] = 0;
308 lastcompname[0] = 0;
309 lasthostname[0] = 0;
310 update_idle_timer();
311 unpause_idle_timer();
312 }
313
314 static void ShowNameConflictNotification(CFMutableArrayRef header, CFStringRef subtext)
315 {
316 CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
317 if (!dictionary) return;
318
319 debug("entry");
320
321 CFDictionarySetValue(dictionary, kCFUserNotificationAlertHeaderKey, header);
322 CFDictionarySetValue(dictionary, kCFUserNotificationAlertMessageKey, subtext);
323
324 CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, CFSTR("/System/Library/CoreServices/mDNSResponder.bundle"), kCFURLPOSIXPathStyle, true);
325 if (urlRef) { CFDictionarySetValue(dictionary, kCFUserNotificationLocalizationURLKey, urlRef); CFRelease(urlRef); }
326
327 if (gNotification) // If notification already on-screen, update it in place
328 CFUserNotificationUpdate(gNotification, 0, kCFUserNotificationCautionAlertLevel, dictionary);
329 else // else, we need to create it
330 {
331 SInt32 error;
332 gNotification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationCautionAlertLevel, &error, dictionary);
333 if (!gNotification || error) { helplog(ASL_LEVEL_ERR, "ShowNameConflictNotification: CFUserNotificationRef: Error %d", error); return; }
334 gNotificationRLS = CFUserNotificationCreateRunLoopSource(NULL, gNotification, NotificationCallBackDismissed, 0);
335 if (!gNotificationRLS) { helplog(ASL_LEVEL_ERR,"ShowNameConflictNotification: RLS"); CFRelease(gNotification); gNotification = NULL; return; }
336 // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread.
337 // We need to explicitly specify the desired CFRunLoop to which we want to add this event source.
338 CFRunLoopAddSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode);
339 debug("gRunLoop=%p gNotification=%p gNotificationRLS=%p", gRunLoop, gNotification, gNotificationRLS);
340 pause_idle_timer();
341 }
342
343 CFRelease(dictionary);
344 }
345
346 static CFMutableArrayRef GetHeader(const char* oldname, const char* newname, const CFStringRef msg, const char* suffix)
347 {
348 CFMutableArrayRef alertHeader = NULL;
349
350 const CFStringRef cfoldname = CFStringCreateWithCString(NULL, oldname, kCFStringEncodingUTF8);
351 // NULL newname means we've given up trying to construct a name that doesn't conflict
352 const CFStringRef cfnewname = newname ? CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8) : NULL;
353 // We tag a zero-width non-breaking space at the end of the literal text to guarantee that, no matter what
354 // arbitrary computer name the user may choose, this exact text (with zero-width non-breaking space added)
355 // can never be one that occurs in the Localizable.strings translation file.
356 if (!cfoldname)
357 helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for old=%s", newname);
358 else if (newname && !cfnewname)
359 helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for new=%s", newname);
360 else
361 {
362 const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfoldname, suffix);
363 const CFStringRef s2 = cfnewname ? CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfnewname, suffix) : NULL;
364
365 alertHeader = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
366
367 if (!s1)
368 helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for old=%s", oldname);
369 else if (cfnewname && !s2)
370 helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for new=%s", newname);
371 else if (!alertHeader)
372 helplog(ASL_LEVEL_ERR, "Could not construct CFArray for notification");
373 else
374 {
375 // Make sure someone is logged in. We don't want this popping up over the login window
376 uid_t uid;
377 gid_t gid;
378 CFStringRef userName = SCDynamicStoreCopyConsoleUser(NULL, &uid, &gid);
379 if (userName)
380 {
381 CFRelease(userName);
382 CFArrayAppendValue(alertHeader, msg); // Opening phrase of message, provided by caller
383 CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s1); CFArrayAppendValue(alertHeader, CFS_CQ);
384 CFArrayAppendValue(alertHeader, CFSTR(" is already in use on this network. "));
385 if (s2)
386 {
387 CFArrayAppendValue(alertHeader, CFSTR("The name has been changed to "));
388 CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s2); CFArrayAppendValue(alertHeader, CFS_CQ);
389 CFArrayAppendValue(alertHeader, CFSTR("."));
390 }
391 else
392 CFArrayAppendValue(alertHeader, CFSTR("All attempts to find an available name by adding a number to the name were also unsuccessful."));
393 }
394 }
395 if (s1) CFRelease(s1);
396 if (s2) CFRelease(s2);
397 }
398 if (cfoldname) CFRelease(cfoldname);
399 if (cfnewname) CFRelease(cfnewname);
400
401 return alertHeader;
402 }
403
404 static void update_notification(void)
405 {
406 debug("entry ucn=%s, uhn=%s, lcn=%s, lhn=%s", usercompname, userhostname, lastcompname, lasthostname);
407 if (!CFS_OQ)
408 {
409 // 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.
410 // By appending this invisible character on the end of literal names, we ensure the these strings cannot inadvertently match any string
411 // in the localization file -- since we know for sure that none of our strings in the localization file contain the ZWNBS character.
412 CFS_OQ = CFStringCreateWithCString(NULL, "“", kCFStringEncodingUTF8);
413 CFS_CQ = CFStringCreateWithCString(NULL, "”", kCFStringEncodingUTF8);
414 CFS_Format = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF", kCFStringEncodingUTF8);
415 CFS_ComputerName = CFStringCreateWithCString(NULL, "The name of your computer ", kCFStringEncodingUTF8);
416 CFS_ComputerNameMsg = CFStringCreateWithCString(NULL, "To change the name of your computer, "
417 "open System Preferences and click Sharing, then type the name in the Computer Name field.", kCFStringEncodingUTF8);
418 CFS_LocalHostName = CFStringCreateWithCString(NULL, "This computer’s local hostname ", kCFStringEncodingUTF8);
419 CFS_LocalHostNameMsg = CFStringCreateWithCString(NULL, "To change the local hostname, "
420 "open System Preferences and click Sharing, then click “Edit” and type the name in the Local Hostname field.", kCFStringEncodingUTF8);
421 CFS_Problem = CFStringCreateWithCString(NULL, "This may indicate a problem with the local network. "
422 "Please inform your network administrator.", kCFStringEncodingUTF8);
423 }
424
425 if (!usercompname[0] && !userhostname[0])
426 {
427 if (gNotificationRLS)
428 {
429 debug("canceling notification %p", gNotification);
430 CFUserNotificationCancel(gNotification);
431 unpause_idle_timer();
432 }
433 }
434 else
435 {
436 CFMutableArrayRef header = NULL;
437 CFStringRef* subtext = NULL;
438 if (userhostname[0] && !lasthostname[0]) // we've given up trying to construct a name that doesn't conflict
439 {
440 header = GetHeader(userhostname, NULL, CFS_LocalHostName, ".local");
441 subtext = &CFS_Problem;
442 }
443 else if (usercompname[0])
444 {
445 header = GetHeader(usercompname, lastcompname, CFS_ComputerName, "");
446 subtext = &CFS_ComputerNameMsg;
447 }
448 else
449 {
450 header = GetHeader(userhostname, lasthostname, CFS_LocalHostName, ".local");
451 subtext = &CFS_LocalHostNameMsg;
452 }
453 ShowNameConflictNotification(header, *subtext);
454 CFRelease(header);
455 }
456 }
457
458 kern_return_t
459 do_mDNSPreferencesSetName(__unused mach_port_t port, int key, const char* old, const char* new, int *err, audit_token_t token)
460 {
461 SCPreferencesRef session = NULL;
462 Boolean ok = FALSE;
463 Boolean locked = FALSE;
464 CFStringRef cfstr = NULL;
465 char* user = NULL;
466 char* last = NULL;
467 Boolean needUpdate = FALSE;
468
469 debug("entry %s old=%s new=%s", key==kmDNSComputerName ? "ComputerName" : (key==kmDNSLocalHostName ? "LocalHostName" : "UNKNOWN"), old, new);
470 *err = 0;
471 if (!authorized(&token))
472 {
473 *err = kmDNSHelperNotAuthorized;
474 goto fin;
475 }
476 switch ((enum mDNSPreferencesSetNameKey)key)
477 {
478 case kmDNSComputerName:
479 user = usercompname;
480 last = lastcompname;
481 break;
482 case kmDNSLocalHostName:
483 user = userhostname;
484 last = lasthostname;
485 break;
486 default:
487 debug("unrecognized key: %d", key);
488 *err = kmDNSHelperInvalidNameKey;
489 goto fin;
490 }
491
492 if (!last)
493 {
494 helplog(ASL_LEVEL_ERR, "%s: no last ptr", __func__);
495 goto fin;
496 }
497
498 if (!user)
499 {
500 helplog(ASL_LEVEL_ERR, "%s: no user ptr", __func__);
501 goto fin;
502 }
503
504 if (0 == strncmp(old, new, MAX_DOMAIN_LABEL+1))
505 {
506 // if we've changed the name, but now someone else has set it to something different, we no longer need the notification
507 if (last[0] && 0 != strncmp(last, new, MAX_DOMAIN_LABEL+1))
508 {
509 last[0] = 0;
510 user[0] = 0;
511 needUpdate = TRUE;
512 }
513 goto fin;
514 }
515 else
516 {
517 if (strncmp(last, new, MAX_DOMAIN_LABEL+1))
518 {
519 strncpy(last, new, MAX_DOMAIN_LABEL);
520 needUpdate = TRUE;
521 }
522 }
523
524 if (!user[0])
525 {
526 strncpy(user, old, MAX_DOMAIN_LABEL);
527 needUpdate = TRUE;
528 }
529
530 if (!new[0]) // we've given up trying to construct a name that doesn't conflict
531 goto fin;
532
533 cfstr = CFStringCreateWithCString(NULL, new, kCFStringEncodingUTF8);
534
535 session = SCPreferencesCreate(NULL, CFSTR(kmDNSHelperServiceName), NULL);
536
537 if (cfstr == NULL || session == NULL)
538 {
539 debug("SCPreferencesCreate failed");
540 *err = kmDNSHelperPreferencesFailed;
541 goto fin;
542 }
543 if (!SCPreferencesLock(session, 0))
544 {
545 debug("lock failed");
546 *err = kmDNSHelperPreferencesLockFailed;
547 goto fin;
548 }
549 locked = TRUE;
550
551 switch ((enum mDNSPreferencesSetNameKey)key)
552 {
553 case kmDNSComputerName:
554 {
555 // We want to write the new Computer Name to System Preferences, without disturbing the user-selected
556 // system-wide default character set used for things like AppleTalk NBP and NETBIOS service advertising.
557 // Note that this encoding is not used for the computer name, but since both are set by the same call,
558 // we need to take care to set the name without changing the character set.
559 CFStringEncoding encoding = kCFStringEncodingUTF8;
560 CFStringRef unused = SCDynamicStoreCopyComputerName(NULL, &encoding);
561 if (unused) { CFRelease(unused); unused = NULL; }
562 else encoding = kCFStringEncodingUTF8;
563
564 ok = SCPreferencesSetComputerName(session, cfstr, encoding);
565 }
566 break;
567 case kmDNSLocalHostName:
568 ok = SCPreferencesSetLocalHostName(session, cfstr);
569 break;
570 default:
571 break;
572 }
573
574 if (!ok || !SCPreferencesCommitChanges(session) ||
575 !SCPreferencesApplyChanges(session))
576 {
577 debug("SCPreferences update failed");
578 *err = kmDNSHelperPreferencesSetFailed;
579 goto fin;
580 }
581 *err = 0;
582 debug("succeeded");
583
584 fin:
585 if (0 != *err)
586 debug("failed err=%d", *err);
587 if (NULL != cfstr)
588 CFRelease(cfstr);
589 if (NULL != session)
590 {
591 if (locked)
592 SCPreferencesUnlock(session);
593 CFRelease(session);
594 }
595 update_idle_timer();
596 if (needUpdate) update_notification();
597 return KERN_SUCCESS;
598 }
599
600 enum DNSKeyFormat
601 {
602 formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem
603 };
604
605 // On Mac OS X on Intel, the four-character string seems to be stored backwards, at least sometimes.
606 // I suspect some overenthusiastic inexperienced engineer said, "On Intel everything's backwards,
607 // therefore I need to add some byte swapping in this API to make this four-character string backwards too."
608 // To cope with this we allow *both* "ddns" and "sndd" as valid item types.
609
610 static const char dnsprefix[] = "dns:";
611 static const char ddns[] = "ddns";
612 static const char ddnsrev[] = "sndd";
613
614 #ifndef NO_SECURITYFRAMEWORK
615 static enum DNSKeyFormat
616 getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp)
617 {
618 static UInt32 tags[3] =
619 {
620 kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr
621 };
622 static SecKeychainAttributeInfo attributeInfo =
623 {
624 sizeof(tags)/sizeof(tags[0]), tags, NULL
625 };
626 SecKeychainAttributeList *attributes = NULL;
627 enum DNSKeyFormat format;
628 Boolean malformed = FALSE;
629 OSStatus status = noErr;
630 int i = 0;
631
632 *attributesp = NULL;
633 if (noErr != (status = SecKeychainItemCopyAttributesAndData(item,
634 &attributeInfo, NULL, &attributes, NULL, NULL)))
635 {
636 debug("SecKeychainItemCopyAttributesAndData %d - skipping",
637 status);
638 goto skip;
639 }
640 if (attributeInfo.count != attributes->count)
641 malformed = TRUE;
642 for (i = 0; !malformed && i < (int)attributeInfo.count; ++i)
643 if (attributeInfo.tag[i] != attributes->attr[i].tag)
644 malformed = TRUE;
645 if (malformed)
646 {
647 debug(
648 "malformed result from SecKeychainItemCopyAttributesAndData - skipping");
649 goto skip;
650 }
651 debug("entry (\"%.*s\", \"%.*s\", \"%.*s\")",
652 (int)attributes->attr[0].length, attributes->attr[0].data,
653 (int)attributes->attr[1].length, attributes->attr[1].data,
654 (int)attributes->attr[2].length, attributes->attr[2].data);
655 if (attributes->attr[1].length >= MAX_ESCAPED_DOMAIN_NAME +
656 sizeof(dnsprefix)-1)
657 {
658 debug("kSecServiceItemAttr too long (%u) - skipping",
659 (unsigned int)attributes->attr[1].length);
660 goto skip;
661 }
662 if (attributes->attr[2].length >= MAX_ESCAPED_DOMAIN_NAME)
663 {
664 debug("kSecAccountItemAttr too long (%u) - skipping",
665 (unsigned int)attributes->attr[2].length);
666 goto skip;
667 }
668 if (attributes->attr[1].length >= sizeof(dnsprefix)-1 &&
669 0 == strncasecmp(attributes->attr[1].data, dnsprefix,
670 sizeof(dnsprefix)-1))
671 format = formatDnsPrefixedServiceItem;
672 else if (attributes->attr[0].length == sizeof(ddns)-1 &&
673 0 == strncasecmp(attributes->attr[0].data, ddns, sizeof(ddns)-1))
674 format = formatDdnsTypeItem;
675 else if (attributes->attr[0].length == sizeof(ddnsrev)-1 &&
676 0 == strncasecmp(attributes->attr[0].data, ddnsrev, sizeof(ddnsrev)-1))
677 format = formatDdnsTypeItem;
678 else
679 {
680 debug("uninterested in this entry");
681 goto skip;
682 }
683 *attributesp = attributes;
684 debug("accepting this entry");
685 return format;
686
687 skip:
688 SecKeychainItemFreeAttributesAndData(attributes, NULL);
689 return formatNotDNSKey;
690 }
691
692 static CFPropertyListRef
693 getKeychainItemInfo(SecKeychainItemRef item,
694 SecKeychainAttributeList *attributes, enum DNSKeyFormat format)
695 {
696 CFMutableArrayRef entry = NULL;
697 CFDataRef data = NULL;
698 OSStatus status = noErr;
699 UInt32 keylen = 0;
700 void *keyp = 0;
701
702 if (NULL == (entry = CFArrayCreateMutable(NULL, 0,
703 &kCFTypeArrayCallBacks)))
704 {
705 debug("CFArrayCreateMutable failed");
706 goto error;
707 }
708 switch ((enum DNSKeyFormat)format)
709 {
710 case formatDdnsTypeItem:
711 data = CFDataCreate(kCFAllocatorDefault,
712 attributes->attr[1].data, attributes->attr[1].length);
713 break;
714 case formatDnsPrefixedServiceItem:
715 data = CFDataCreate(kCFAllocatorDefault,
716 attributes->attr[1].data + sizeof(dnsprefix)-1,
717 attributes->attr[1].length - (sizeof(dnsprefix)-1));
718 default:
719 assert("unknown DNSKeyFormat value");
720 break;
721 }
722 if (NULL == data)
723 {
724 debug("CFDataCreate for attr[1] failed");
725 goto error;
726 }
727 CFArrayAppendValue(entry, data);
728 CFRelease(data);
729 if (NULL == (data = CFDataCreate(kCFAllocatorDefault,
730 attributes->attr[2].data, attributes->attr[2].length)))
731 {
732 debug("CFDataCreate for attr[2] failed");
733 goto error;
734 }
735 CFArrayAppendValue(entry, data);
736 CFRelease(data);
737 if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, NULL,
738 NULL, NULL, &keylen, &keyp)))
739 {
740 debug("could not retrieve key for \"%.*s\": %d",
741 (int)attributes->attr[1].length, attributes->attr[1].data,
742 status);
743 goto error;
744 }
745 data = CFDataCreate(kCFAllocatorDefault, keyp, keylen);
746 SecKeychainItemFreeAttributesAndData(NULL, keyp);
747 if (NULL == data)
748 {
749 debug("CFDataCreate for keyp failed");
750 goto error;
751 }
752 CFArrayAppendValue(entry, data);
753 CFRelease(data);
754 return entry;
755
756 error:
757 if (NULL != entry)
758 CFRelease(entry);
759 return NULL;
760 }
761 #endif
762
763 kern_return_t
764 do_mDNSKeychainGetSecrets(__unused mach_port_t port, __unused unsigned int *numsecrets,
765 __unused vm_offset_t *secrets, __unused mach_msg_type_number_t *secretsCnt, __unused int *err,
766 __unused audit_token_t token)
767 {
768 #ifndef NO_SECURITYFRAMEWORK
769 CFWriteStreamRef stream = NULL;
770 CFDataRef result = NULL;
771 CFPropertyListRef entry = NULL;
772 CFMutableArrayRef keys = NULL;
773 SecKeychainRef skc = NULL;
774 SecKeychainItemRef item = NULL;
775 SecKeychainSearchRef search = NULL;
776 SecKeychainAttributeList *attributes = NULL;
777 enum DNSKeyFormat format;
778 OSStatus status = 0;
779
780 debug("entry");
781 *err = 0;
782 *numsecrets = 0;
783 *secrets = (vm_offset_t)NULL;
784 if (!authorized(&token))
785 {
786 *err = kmDNSHelperNotAuthorized;
787 goto fin;
788 }
789 if (NULL == (keys = CFArrayCreateMutable(NULL, 0,
790 &kCFTypeArrayCallBacks)))
791 {
792 debug("CFArrayCreateMutable failed");
793 *err = kmDNSHelperCreationFailed;
794 goto fin;
795 }
796 if (noErr != (status = SecKeychainCopyDefault(&skc)))
797 {
798 *err = kmDNSHelperKeychainCopyDefaultFailed;
799 goto fin;
800 }
801 if (noErr != (status = SecKeychainSearchCreateFromAttributes(skc, kSecGenericPasswordItemClass, NULL, &search)))
802 {
803 *err = kmDNSHelperKeychainSearchCreationFailed;
804 goto fin;
805 }
806 for (status = SecKeychainSearchCopyNext(search, &item);
807 noErr == status;
808 status = SecKeychainSearchCopyNext(search, &item))
809 {
810 if (formatNotDNSKey != (format = getDNSKeyFormat(item,
811 &attributes)) &&
812 NULL != (entry = getKeychainItemInfo(item, attributes,
813 format)))
814 {
815 CFArrayAppendValue(keys, entry);
816 CFRelease(entry);
817 }
818 SecKeychainItemFreeAttributesAndData(attributes, NULL);
819 CFRelease(item);
820 }
821 if (errSecItemNotFound != status)
822 helplog(ASL_LEVEL_ERR, "%s: SecKeychainSearchCopyNext failed: %d",
823 __func__, status);
824 if (NULL == (stream =
825 CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault,
826 kCFAllocatorDefault)))
827 {
828 *err = kmDNSHelperCreationFailed;
829 debug("CFWriteStreamCreateWithAllocatedBuffers failed");
830 goto fin;
831 }
832 CFWriteStreamOpen(stream);
833 if (0 == CFPropertyListWriteToStream(keys, stream,
834 kCFPropertyListBinaryFormat_v1_0, NULL))
835 {
836 *err = kmDNSHelperPListWriteFailed;
837 debug("CFPropertyListWriteToStream failed");
838 goto fin;
839 }
840 result = CFWriteStreamCopyProperty(stream,
841 kCFStreamPropertyDataWritten);
842 if (KERN_SUCCESS != vm_allocate(mach_task_self(), secrets,
843 CFDataGetLength(result), VM_FLAGS_ANYWHERE))
844 {
845 *err = kmDNSHelperCreationFailed;
846 debug("vm_allocate failed");
847 goto fin;
848 }
849 CFDataGetBytes(result, CFRangeMake(0, CFDataGetLength(result)),
850 (void *)*secrets);
851 *secretsCnt = CFDataGetLength(result);
852 *numsecrets = CFArrayGetCount(keys);
853 debug("succeeded");
854
855 fin:
856 debug("returning %u secrets", *numsecrets);
857 if (NULL != stream)
858 {
859 CFWriteStreamClose(stream);
860 CFRelease(stream);
861 }
862 if (NULL != result)
863 CFRelease(result);
864 if (NULL != keys)
865 CFRelease(keys);
866 if (NULL != search)
867 CFRelease(search);
868 if (NULL != skc)
869 CFRelease(skc);
870 update_idle_timer();
871 return KERN_SUCCESS;
872 #else
873 return KERN_FAILURE;
874 #endif
875 }
876
877 typedef enum _mDNSTunnelPolicyWhich
878 {
879 kmDNSTunnelPolicySetup,
880 kmDNSTunnelPolicyTeardown,
881 kmDNSTunnelPolicyGenerate
882 } mDNSTunnelPolicyWhich;
883
884 static const uint8_t kWholeV6Mask = 128;
885 static const uint8_t kZeroV6Mask = 0;
886
887 static int
888 doTunnelPolicy(mDNSTunnelPolicyWhich which,
889 v6addr_t loc_inner, uint8_t loc_bits,
890 v4addr_t loc_outer, uint16_t loc_port,
891 v6addr_t rmt_inner, uint8_t rmt_bits,
892 v4addr_t rmt_outer, uint16_t rmt_port);
893
894 static int
895 aliasTunnelAddress(v6addr_t address)
896 {
897 struct in6_aliasreq ifra_in6;
898 int err = 0;
899 int s = -1;
900
901 if (0 > (s = socket(AF_INET6, SOCK_DGRAM, 0)))
902 {
903 helplog(ASL_LEVEL_ERR, "socket(AF_INET6, ...) failed: %s",
904 strerror(errno));
905 err = kmDNSHelperDatagramSocketCreationFailed;
906 goto fin;
907 }
908 bzero(&ifra_in6, sizeof(ifra_in6));
909 strlcpy(ifra_in6.ifra_name, kTunnelAddressInterface,
910 sizeof(ifra_in6.ifra_name));
911 ifra_in6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
912 ifra_in6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
913
914 ifra_in6.ifra_addr.sin6_family = AF_INET6;
915 ifra_in6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
916 memcpy(&(ifra_in6.ifra_addr.sin6_addr), address,
917 sizeof(ifra_in6.ifra_addr.sin6_addr));
918
919 ifra_in6.ifra_prefixmask.sin6_family = AF_INET6;
920 ifra_in6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
921 memset(&(ifra_in6.ifra_prefixmask.sin6_addr), 0xFF,
922 sizeof(ifra_in6.ifra_prefixmask.sin6_addr));
923
924 if (0 > ioctl(s, SIOCAIFADDR_IN6, &ifra_in6))
925 {
926 helplog(ASL_LEVEL_ERR,
927 "ioctl(..., SIOCAIFADDR_IN6, ...) failed: %s",
928 strerror(errno));
929 err = kmDNSHelperInterfaceCreationFailed;
930 goto fin;
931 }
932
933 v6addr_t zero = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
934 err = doTunnelPolicy(kmDNSTunnelPolicyGenerate,
935 address, kWholeV6Mask, NULL, 0,
936 zero, kZeroV6Mask, NULL, 0);
937
938 fin:
939 if (0 <= s)
940 close(s);
941 return err;
942 }
943
944 static int
945 unaliasTunnelAddress(v6addr_t address)
946 {
947 struct in6_ifreq ifr;
948 int err = 0;
949 int s = -1;
950
951 if (0 > (s = socket(AF_INET6, SOCK_DGRAM, 0)))
952 {
953 helplog(ASL_LEVEL_ERR, "socket(AF_INET6, ...) failed: %s",
954 strerror(errno));
955 err = kmDNSHelperDatagramSocketCreationFailed;
956 goto fin;
957 }
958 bzero(&ifr, sizeof(ifr));
959 strlcpy(ifr.ifr_name, kTunnelAddressInterface, sizeof(ifr.ifr_name));
960 ifr.ifr_ifru.ifru_addr.sin6_family = AF_INET6;
961 ifr.ifr_ifru.ifru_addr.sin6_len = sizeof(struct sockaddr_in6);
962 memcpy(&(ifr.ifr_ifru.ifru_addr.sin6_addr), address,
963 sizeof(ifr.ifr_ifru.ifru_addr.sin6_addr));
964
965 if (0 > ioctl(s, SIOCDIFADDR_IN6, &ifr))
966 {
967 helplog(ASL_LEVEL_ERR,
968 "ioctl(..., SIOCDIFADDR_IN6, ...) failed: %s",
969 strerror(errno));
970 err = kmDNSHelperInterfaceDeletionFailed;
971 goto fin;
972 }
973
974 v6addr_t zero = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
975 err = doTunnelPolicy(kmDNSTunnelPolicyTeardown,
976 address, kWholeV6Mask, NULL, 0,
977 zero, kZeroV6Mask, NULL, 0);
978
979 fin:
980 if (0 <= s)
981 close(s);
982 return err;
983 }
984
985 int
986 do_mDNSAutoTunnelInterfaceUpDown(__unused mach_port_t port, int updown,
987 v6addr_t address, int *err, audit_token_t token)
988 {
989 debug("entry");
990 *err = 0;
991 if (!authorized(&token))
992 {
993 *err = kmDNSHelperNotAuthorized;
994 goto fin;
995 }
996 switch ((enum mDNSUpDown)updown)
997 {
998 case kmDNSUp:
999 *err = aliasTunnelAddress(address);
1000 break;
1001 case kmDNSDown:
1002 *err = unaliasTunnelAddress(address);
1003 break;
1004 default:
1005 *err = kmDNSHelperInvalidInterfaceState;
1006 goto fin;
1007 }
1008 debug("succeeded");
1009
1010 fin:
1011 update_idle_timer();
1012 return KERN_SUCCESS;
1013 }
1014
1015 static const char racoon_config_path[] = "/etc/racoon/remote/anonymous.conf";
1016 static const char racoon_config_path_orig[] = "/etc/racoon/remote/anonymous.conf.orig";
1017
1018 static const char configHeader[] = "# BackToMyMac\n";
1019
1020 static int IsFamiliarRacoonConfiguration()
1021 {
1022 int fd = open(racoon_config_path, O_RDONLY);
1023 debug("entry");
1024 if (0 > fd)
1025 {
1026 helplog(ASL_LEVEL_NOTICE, "open \"%s\" failed: %s", racoon_config_path, strerror(errno));
1027 return 0;
1028 }
1029 else
1030 {
1031 char header[sizeof(configHeader)] = {0};
1032 ssize_t bytesRead = read(fd, header, sizeof(header)-1);
1033 close(fd);
1034 if (bytesRead != sizeof(header)-1) return 0;
1035 return (0 == memcmp(header, configHeader, sizeof(header)-1));
1036 }
1037 }
1038
1039 static void
1040 revertAnonymousRacoonConfiguration()
1041 {
1042 debug("entry");
1043 if (!IsFamiliarRacoonConfiguration())
1044 {
1045 helplog(ASL_LEVEL_NOTICE, "\"%s\" does not look familiar, leaving in place", racoon_config_path);
1046 return;
1047 }
1048
1049 if (0 > rename(racoon_config_path_orig, racoon_config_path))
1050 {
1051 helplog(ASL_LEVEL_NOTICE, "rename \"%s\" \"%s\" failed: %s", racoon_config_path_orig, racoon_config_path, strerror(errno));
1052 helplog(ASL_LEVEL_NOTICE, "\"%s\" looks familiar, unlinking", racoon_config_path);
1053 unlink(racoon_config_path);
1054 }
1055 }
1056
1057 static int
1058 createAnonymousRacoonConfiguration(const char *keydata)
1059 {
1060 static const char config1[] =
1061 "remote anonymous {\n"
1062 " exchange_mode aggressive;\n"
1063 " doi ipsec_doi;\n"
1064 " situation identity_only;\n"
1065 " verify_identifier off;\n"
1066 " generate_policy on;\n"
1067 " shared_secret use \"";
1068 static const char config2[] =
1069 "\";\n"
1070 " nonce_size 16;\n"
1071 " lifetime time 5 min;\n"
1072 " initial_contact on;\n"
1073 " support_proxy on;\n"
1074 " nat_traversal force;\n"
1075 " proposal_check claim;\n"
1076 " proposal {\n"
1077 " encryption_algorithm aes;\n"
1078 " hash_algorithm sha1;\n"
1079 " authentication_method pre_shared_key;\n"
1080 " dh_group 2;\n"
1081 " lifetime time 5 min;\n"
1082 " }\n"
1083 "}\n\n"
1084 "sainfo anonymous { \n"
1085 " pfs_group 2;\n"
1086 " lifetime time 10 min;\n"
1087 " encryption_algorithm aes;\n"
1088 " authentication_algorithm hmac_sha1;\n"
1089 " compression_algorithm deflate;\n"
1090 "}\n";
1091 char tmp_config_path[] =
1092 "/etc/racoon/remote/tmp.XXXXXX";
1093 int fd = mkstemp(tmp_config_path);
1094
1095 debug("entry");
1096
1097 if (0 > fd)
1098 {
1099 helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s",
1100 tmp_config_path, strerror(errno));
1101 return -1;
1102 }
1103 write(fd, configHeader, sizeof(configHeader)-1);
1104 write(fd, config1, sizeof(config1)-1);
1105 write(fd, keydata, strlen(keydata));
1106 write(fd, config2, sizeof(config2)-1);
1107 close(fd);
1108
1109 if (IsFamiliarRacoonConfiguration())
1110 helplog(ASL_LEVEL_NOTICE, "\"%s\" looks familiar, will overwrite", racoon_config_path);
1111 else 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
1112 helplog(ASL_LEVEL_NOTICE, "rename \"%s\" \"%s\" failed: %s", racoon_config_path, racoon_config_path_orig, strerror(errno));
1113 else
1114 debug("successfully renamed \"%s\" \"%s\"", racoon_config_path, racoon_config_path_orig);
1115
1116 if (0 > rename(tmp_config_path, racoon_config_path))
1117 {
1118 unlink(tmp_config_path);
1119 helplog(ASL_LEVEL_ERR, "rename \"%s\" \"%s\" failed: %s",
1120 tmp_config_path, racoon_config_path, strerror(errno));
1121 revertAnonymousRacoonConfiguration();
1122 return -1;
1123 }
1124
1125 debug("successfully renamed \"%s\" \"%s\"", tmp_config_path, racoon_config_path);
1126 return 0;
1127 }
1128
1129 static int
1130 notifyRacoon(void)
1131 {
1132 debug("entry");
1133 static const char racoon_pid_path[] = "/var/run/racoon.pid";
1134 char buf[] = "18446744073709551615"; /* largest 64-bit integer */
1135 char *p = NULL;
1136 ssize_t n = 0;
1137 unsigned long m = 0;
1138 int fd = open(racoon_pid_path, O_RDONLY);
1139
1140 if (0 > fd)
1141 {
1142 debug("open \"%s\" failed, and that's OK: %s", racoon_pid_path,
1143 strerror(errno));
1144 return kmDNSHelperRacoonNotificationFailed;
1145 }
1146 n = read(fd, buf, sizeof(buf)-1);
1147 close(fd);
1148 if (1 > n)
1149 {
1150 debug("read of \"%s\" failed: %s", racoon_pid_path,
1151 n == 0 ? "empty file" : strerror(errno));
1152 return kmDNSHelperRacoonNotificationFailed;
1153 }
1154 buf[n] = '\0';
1155 m = strtoul(buf, &p, 10);
1156 if (*p != '\0' && !isspace(*p))
1157 {
1158 debug("invalid PID \"%s\" (around '%c')", buf, *p);
1159 return kmDNSHelperRacoonNotificationFailed;
1160 }
1161 if (2 > m)
1162 {
1163 debug("refusing to kill PID %lu", m);
1164 return kmDNSHelperRacoonNotificationFailed;
1165 }
1166 if (0 != kill(m, SIGHUP))
1167 {
1168 debug("Could not signal racoon (%lu): %s", m, strerror(errno));
1169 return kmDNSHelperRacoonNotificationFailed;
1170 }
1171 debug("Sent SIGHUP to racoon (%lu)", m);
1172 return 0;
1173 }
1174
1175 static int
1176 startRacoon(void)
1177 {
1178 debug("entry");
1179 char * const racoon_args[] = { "/usr/sbin/racoon", "-e", NULL };
1180 ssize_t n = 0;
1181 pid_t pid = 0;
1182 int status = 0;
1183
1184 if (0 == (pid = fork()))
1185 {
1186 closefds(0);
1187 execve(racoon_args[0], racoon_args, NULL);
1188 helplog(ASL_LEVEL_ERR, "execve of \"%s\" failed: %s",
1189 racoon_args[0], strerror(errno));
1190 exit(2);
1191 }
1192 helplog(ASL_LEVEL_NOTICE, "racoon (pid=%lu) started",
1193 (unsigned long)pid);
1194 n = waitpid(pid, &status, 0);
1195 if (-1 == n)
1196 {
1197 helplog(ASL_LEVEL_ERR, "Unexpected waitpid failure: %s",
1198 strerror(errno));
1199 return kmDNSHelperRacoonStartFailed;
1200 }
1201 else if (pid != n)
1202 {
1203 helplog(ASL_LEVEL_ERR, "Unexpected waitpid return value %d",
1204 (int)n);
1205 return kmDNSHelperRacoonStartFailed;
1206 }
1207 else if (WIFSIGNALED(status))
1208 {
1209 helplog(ASL_LEVEL_ERR,
1210 "racoon (pid=%lu) terminated due to signal %d",
1211 (unsigned long)pid, WTERMSIG(status));
1212 return kmDNSHelperRacoonStartFailed;
1213 }
1214 else if (WIFSTOPPED(status))
1215 {
1216 helplog(ASL_LEVEL_ERR,
1217 "racoon (pid=%lu) has stopped due to signal %d",
1218 (unsigned long)pid, WSTOPSIG(status));
1219 return kmDNSHelperRacoonStartFailed;
1220 }
1221 else if (0 != WEXITSTATUS(status))
1222 {
1223 helplog(ASL_LEVEL_ERR,
1224 "racoon (pid=%lu) exited with status %d",
1225 (unsigned long)pid, WEXITSTATUS(status));
1226 return kmDNSHelperRacoonStartFailed;
1227 }
1228 debug("racoon (pid=%lu) daemonized normally", (unsigned long)pid);
1229 return 0;
1230 }
1231
1232 static int
1233 kickRacoon(void)
1234 {
1235 if ( 0 == notifyRacoon() )
1236 return 0;
1237 return startRacoon();
1238 }
1239
1240 int
1241 do_mDNSConfigureServer(__unused mach_port_t port, int updown, const char *keydata, int *err, audit_token_t token)
1242 {
1243 debug("entry");
1244 *err = 0;
1245
1246 if (!authorized(&token))
1247 {
1248 *err = kmDNSHelperNotAuthorized;
1249 goto fin;
1250 }
1251
1252 switch ((enum mDNSUpDown)updown)
1253 {
1254 case kmDNSUp:
1255 if (0 != createAnonymousRacoonConfiguration(keydata))
1256 {
1257 *err = kmDNSHelperRacoonConfigCreationFailed;
1258 goto fin;
1259 }
1260 break;
1261 case kmDNSDown:
1262 revertAnonymousRacoonConfiguration();
1263 break;
1264 default:
1265 *err = kmDNSHelperInvalidServerState;
1266 goto fin;
1267 }
1268
1269 if (0 != (*err = kickRacoon()))
1270 goto fin;
1271 debug("succeeded");
1272
1273 fin:
1274 update_idle_timer();
1275 return KERN_SUCCESS;
1276 }
1277
1278 static unsigned int routeSeq = 1;
1279
1280 static int
1281 setupTunnelRoute(v6addr_t local, v6addr_t remote)
1282 {
1283 struct
1284 {
1285 struct rt_msghdr hdr;
1286 struct sockaddr_in6 dst;
1287 struct sockaddr_in6 gtwy;
1288 } msg;
1289 int err = 0;
1290 int s = -1;
1291
1292 if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET)))
1293 {
1294 helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s",
1295 strerror(errno));
1296 err = kmDNSHelperRoutingSocketCreationFailed;
1297 goto fin;
1298 }
1299 memset(&msg, 0, sizeof(msg));
1300 msg.hdr.rtm_msglen = sizeof(msg);
1301 msg.hdr.rtm_type = RTM_ADD;
1302 /* The following flags are set by `route add -inet6 -host ...` */
1303 msg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC;
1304 msg.hdr.rtm_version = RTM_VERSION;
1305 msg.hdr.rtm_seq = routeSeq++;
1306 msg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
1307 msg.hdr.rtm_inits = RTV_MTU;
1308 msg.hdr.rtm_rmx.rmx_mtu = 1280;
1309
1310 msg.dst.sin6_len = sizeof(msg.dst);
1311 msg.dst.sin6_family = AF_INET6;
1312 memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr));
1313
1314 msg.gtwy.sin6_len = sizeof(msg.gtwy);
1315 msg.gtwy.sin6_family = AF_INET6;
1316 memcpy(&msg.gtwy.sin6_addr, local, sizeof(msg.gtwy.sin6_addr));
1317
1318 /* send message, ignore error when route already exists */
1319 if (0 > write(s, &msg, msg.hdr.rtm_msglen))
1320 {
1321 int errno_ = errno;
1322
1323 debug("write to routing socket failed: %s", strerror(errno_));
1324 if (EEXIST != errno_)
1325 {
1326 err = kmDNSHelperRouteAdditionFailed;
1327 goto fin;
1328 }
1329 }
1330
1331 fin:
1332 if (0 <= s)
1333 close(s);
1334 return err;
1335 }
1336
1337 static int
1338 teardownTunnelRoute(v6addr_t remote)
1339 {
1340 struct
1341 {
1342 struct rt_msghdr hdr;
1343 struct sockaddr_in6 dst;
1344 } msg;
1345 int err = 0;
1346 int s = -1;
1347
1348 if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET)))
1349 {
1350 helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s",
1351 strerror(errno));
1352 err = kmDNSHelperRoutingSocketCreationFailed;
1353 goto fin;
1354 }
1355 memset(&msg, 0, sizeof(msg));
1356
1357 msg.hdr.rtm_msglen = sizeof(msg);
1358 msg.hdr.rtm_type = RTM_DELETE;
1359 msg.hdr.rtm_version = RTM_VERSION;
1360 msg.hdr.rtm_seq = routeSeq++;
1361 msg.hdr.rtm_addrs = RTA_DST;
1362
1363 msg.dst.sin6_len = sizeof(msg.dst);
1364 msg.dst.sin6_family = AF_INET6;
1365 memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr));
1366 if (0 > write(s, &msg, msg.hdr.rtm_msglen))
1367 {
1368 int errno_ = errno;
1369
1370 debug("write to routing socket failed: %s", strerror(errno_));
1371 if (ESRCH != errno_)
1372 {
1373 err = kmDNSHelperRouteDeletionFailed;
1374 goto fin;
1375 }
1376 }
1377
1378 fin:
1379 if (0 <= s)
1380 close(s);
1381 return err;
1382 }
1383
1384 static int
1385 v4addr_to_string(v4addr_t addr, char *buf, size_t buflen)
1386 {
1387 if (NULL == inet_ntop(AF_INET, addr, buf, buflen))
1388 {
1389 helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s",
1390 strerror(errno));
1391 return kmDNSHelperInvalidNetworkAddress;
1392 }
1393 else
1394 return 0;
1395 }
1396
1397 static int
1398 v6addr_to_string(v6addr_t addr, char *buf, size_t buflen)
1399 {
1400 if (NULL == inet_ntop(AF_INET6, addr, buf, buflen))
1401 {
1402 helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s",
1403 strerror(errno));
1404 return kmDNSHelperInvalidNetworkAddress;
1405 }
1406 else
1407 return 0;
1408 }
1409
1410 /* Caller owns object returned in `policy' */
1411 static int
1412 generateTunnelPolicy(mDNSTunnelPolicyWhich which, int in,
1413 v4addr_t src, uint16_t src_port,
1414 v4addr_t dst, uint16_t dst_port,
1415 ipsec_policy_t *policy, size_t *len)
1416 {
1417 char srcs[INET_ADDRSTRLEN], dsts[INET_ADDRSTRLEN];
1418 char buf[128];
1419 char *inOut = in ? "in" : "out";
1420 ssize_t n = 0;
1421 int err = 0;
1422
1423 *policy = NULL;
1424 *len = 0;
1425
1426 switch (which)
1427 {
1428 case kmDNSTunnelPolicySetup:
1429 if (0 != (err = v4addr_to_string(src, srcs, sizeof(srcs))))
1430 goto fin;
1431 if (0 != (err = v4addr_to_string(dst, dsts, sizeof(dsts))))
1432 goto fin;
1433 n = snprintf(buf, sizeof(buf),
1434 "%s ipsec esp/tunnel/%s[%u]-%s[%u]/require",
1435 inOut, srcs, src_port, dsts, dst_port);
1436 break;
1437 case kmDNSTunnelPolicyTeardown:
1438 n = strlcpy(buf, inOut, sizeof(buf));
1439 break;
1440 case kmDNSTunnelPolicyGenerate:
1441 n = snprintf(buf, sizeof(buf), "%s generate", inOut);
1442 break;
1443 default:
1444 err = kmDNSHelperIPsecPolicyCreationFailed;
1445 goto fin;
1446 }
1447
1448 if (n >= (int)sizeof(buf))
1449 {
1450 err = kmDNSHelperResultTooLarge;
1451 goto fin;
1452 }
1453
1454 debug("policy=\"%s\"", buf);
1455 if (NULL == (*policy = (ipsec_policy_t)ipsec_set_policy(buf, n)))
1456 {
1457 helplog(ASL_LEVEL_ERR,
1458 "Could not create IPsec policy from \"%s\"", buf);
1459 err = kmDNSHelperIPsecPolicyCreationFailed;
1460 goto fin;
1461 }
1462 *len = ((ipsec_policy_t)(*policy))->sadb_x_policy_len * 8;
1463
1464 fin:
1465 return err;
1466 }
1467
1468 static int
1469 sendPolicy(int s, int setup,
1470 struct sockaddr *src, uint8_t src_bits,
1471 struct sockaddr *dst, uint8_t dst_bits,
1472 ipsec_policy_t policy, size_t len)
1473 {
1474 static unsigned int policySeq = 0;
1475 int err = 0;
1476
1477 debug("entry, setup=%d", setup);
1478 if (setup)
1479 err = pfkey_send_spdadd(s, src, src_bits, dst, dst_bits, -1,
1480 (char *)policy, len, policySeq++);
1481 else
1482 err = pfkey_send_spddelete(s, src, src_bits, dst, dst_bits, -1,
1483 (char *)policy, len, policySeq++);
1484 if (0 > err)
1485 {
1486 helplog(ASL_LEVEL_ERR, "Could not set IPsec policy: %s",
1487 ipsec_strerror());
1488 err = kmDNSHelperIPsecPolicySetFailed;
1489 goto fin;
1490 }
1491 else
1492 err = 0;
1493 debug("succeeded");
1494
1495 fin:
1496 return err;
1497 }
1498
1499 static int
1500 removeSA(int s, struct sockaddr *src, struct sockaddr *dst)
1501 {
1502 int err = 0;
1503
1504 debug("entry");
1505 err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, src, dst);
1506 if (0 > err)
1507 {
1508 helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror());
1509 err = kmDNSHelperIPsecRemoveSAFailed;
1510 goto fin;
1511 }
1512 err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, dst, src);
1513 if (0 > err)
1514 {
1515 helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror());
1516 err = kmDNSHelperIPsecRemoveSAFailed;
1517 goto fin;
1518 }
1519 else
1520 err = 0;
1521
1522 debug("succeeded");
1523
1524 fin:
1525 return err;
1526 }
1527
1528 static int
1529 doTunnelPolicy(mDNSTunnelPolicyWhich which,
1530 v6addr_t loc_inner, uint8_t loc_bits,
1531 v4addr_t loc_outer, uint16_t loc_port,
1532 v6addr_t rmt_inner, uint8_t rmt_bits,
1533 v4addr_t rmt_outer, uint16_t rmt_port)
1534 {
1535 struct sockaddr_in6 sin_loc;
1536 struct sockaddr_in6 sin_rmt;
1537 ipsec_policy_t policy = NULL;
1538 size_t len = 0;
1539 int s = -1;
1540 int err = 0;
1541
1542 debug("entry");
1543 if (0 > (s = pfkey_open()))
1544 {
1545 helplog(ASL_LEVEL_ERR,
1546 "Could not create IPsec policy socket: %s",
1547 ipsec_strerror());
1548 err = kmDNSHelperIPsecPolicySocketCreationFailed;
1549 goto fin;
1550 }
1551
1552 memset(&sin_loc, 0, sizeof(sin_loc));
1553 sin_loc.sin6_len = sizeof(sin_loc);
1554 sin_loc.sin6_family = AF_INET6;
1555 sin_loc.sin6_port = htons(0);
1556 memcpy(&sin_loc.sin6_addr, loc_inner, sizeof(sin_loc.sin6_addr));
1557
1558 memset(&sin_rmt, 0, sizeof(sin_rmt));
1559 sin_rmt.sin6_len = sizeof(sin_rmt);
1560 sin_rmt.sin6_family = AF_INET6;
1561 sin_rmt.sin6_port = htons(0);
1562 memcpy(&sin_rmt.sin6_addr, rmt_inner, sizeof(sin_rmt.sin6_addr));
1563
1564 int setup = which != kmDNSTunnelPolicyTeardown;
1565
1566 if (0 != (err = generateTunnelPolicy(which, 1,
1567 rmt_outer, rmt_port,
1568 loc_outer, loc_port,
1569 &policy, &len)))
1570 goto fin;
1571 if (0 != (err = sendPolicy(s, setup,
1572 (struct sockaddr *)&sin_rmt, rmt_bits,
1573 (struct sockaddr *)&sin_loc, loc_bits,
1574 policy, len)))
1575 goto fin;
1576 if (NULL != policy)
1577 {
1578 free(policy);
1579 policy = NULL;
1580 }
1581 if (0 != (err = generateTunnelPolicy(which, 0,
1582 loc_outer, loc_port,
1583 rmt_outer, rmt_port,
1584 &policy, &len)))
1585 goto fin;
1586 if (0 != (err = sendPolicy(s, setup,
1587 (struct sockaddr *)&sin_loc, loc_bits,
1588 (struct sockaddr *)&sin_rmt, rmt_bits,
1589 policy, len)))
1590 goto fin;
1591
1592 if (which == kmDNSTunnelPolicyTeardown && loc_outer && rmt_outer)
1593 {
1594 struct sockaddr_in sin_loc;
1595 struct sockaddr_in sin_rmt;
1596
1597 memset(&sin_loc, 0, sizeof(sin_loc));
1598 sin_loc.sin_len = sizeof(sin_loc);
1599 sin_loc.sin_family = AF_INET;
1600 sin_loc.sin_port = htons(0);
1601 memcpy(&sin_loc.sin_addr, loc_outer, sizeof(sin_loc.sin_addr));
1602
1603 memset(&sin_rmt, 0, sizeof(sin_rmt));
1604 sin_rmt.sin_len = sizeof(sin_rmt);
1605 sin_rmt.sin_family = AF_INET;
1606 sin_rmt.sin_port = htons(0);
1607 memcpy(&sin_rmt.sin_addr, rmt_outer, sizeof(sin_rmt.sin_addr));
1608
1609 if (0 != (err = removeSA(s, (struct sockaddr *)&sin_loc, (struct sockaddr *)&sin_rmt)))
1610 goto fin;
1611 }
1612
1613 debug("succeeded");
1614
1615 fin:
1616 if (0 >= s)
1617 close(s);
1618 if (NULL != policy)
1619 free(policy);
1620 return err;
1621 }
1622
1623 int
1624 do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete,
1625 v6addr_t loc_inner, v4addr_t loc_outer, uint16_t loc_port,
1626 v6addr_t rmt_inner, v4addr_t rmt_outer, uint16_t rmt_port,
1627 const char *keydata, int *err, audit_token_t token)
1628 {
1629 static const char config[] =
1630 "%s"
1631 "remote %s [%u] {\n"
1632 " exchange_mode aggressive;\n"
1633 " doi ipsec_doi;\n"
1634 " situation identity_only;\n"
1635 " verify_identifier off;\n"
1636 " generate_policy on;\n"
1637 " shared_secret use \"%s\";\n"
1638 " nonce_size 16;\n"
1639 " lifetime time 5 min;\n"
1640 " initial_contact on;\n"
1641 " support_proxy on;\n"
1642 " nat_traversal force;\n"
1643 " proposal_check claim;\n"
1644 " proposal {\n"
1645 " encryption_algorithm aes;\n"
1646 " hash_algorithm sha1;\n"
1647 " authentication_method pre_shared_key;\n"
1648 " dh_group 2;\n"
1649 " lifetime time 5 min;\n"
1650 " }\n"
1651 "}\n\n"
1652 "sainfo address %s any address %s any {\n"
1653 " pfs_group 2;\n"
1654 " lifetime time 10 min;\n"
1655 " encryption_algorithm aes;\n"
1656 " authentication_algorithm hmac_sha1;\n"
1657 " compression_algorithm deflate;\n"
1658 "}\n\n"
1659 "sainfo address %s any address %s any {\n"
1660 " pfs_group 2;\n"
1661 " lifetime time 10 min;\n"
1662 " encryption_algorithm aes;\n"
1663 " authentication_algorithm hmac_sha1;\n"
1664 " compression_algorithm deflate;\n"
1665 "}\n";
1666 char path[PATH_MAX] = "";
1667 char li[INET6_ADDRSTRLEN], lo[INET_ADDRSTRLEN],
1668 ri[INET6_ADDRSTRLEN], ro[INET_ADDRSTRLEN];
1669 FILE *fp = NULL;
1670 int fd = -1;
1671 char tmp_path[PATH_MAX] = "";
1672
1673 debug("entry");
1674 *err = 0;
1675 if (!authorized(&token))
1676 {
1677 *err = kmDNSHelperNotAuthorized;
1678 goto fin;
1679 }
1680 switch ((enum mDNSAutoTunnelSetKeysReplaceDelete)replacedelete)
1681 {
1682 case kmDNSAutoTunnelSetKeysReplace:
1683 case kmDNSAutoTunnelSetKeysDelete:
1684 break;
1685 default:
1686 *err = kmDNSHelperInvalidTunnelSetKeysOperation;
1687 goto fin;
1688 }
1689 if (0 != (*err = v6addr_to_string(loc_inner, li, sizeof(li))))
1690 goto fin;
1691 if (0 != (*err = v6addr_to_string(rmt_inner, ri, sizeof(ri))))
1692 goto fin;
1693 if (0 != (*err = v4addr_to_string(loc_outer, lo, sizeof(lo))))
1694 goto fin;
1695 if (0 != (*err = v4addr_to_string(rmt_outer, ro, sizeof(ro))))
1696 goto fin;
1697 debug("loc_inner=%s rmt_inner=%s", li, ri);
1698 debug("loc_outer=%s loc_port=%u rmt_outer=%s rmt_port=%u",
1699 lo, loc_port, ro, rmt_port);
1700
1701 if ((int)sizeof(path) <= snprintf(path, sizeof(path),
1702 "/etc/racoon/remote/%s.%u.conf", ro,
1703 rmt_port))
1704 {
1705 *err = kmDNSHelperResultTooLarge;
1706 goto fin;
1707 }
1708 if (kmDNSAutoTunnelSetKeysReplace == replacedelete)
1709 {
1710 if ((int)sizeof(tmp_path) <=
1711 snprintf(tmp_path, sizeof(tmp_path), "%s.XXXXXX", path))
1712 {
1713 *err = kmDNSHelperResultTooLarge;
1714 goto fin;
1715 }
1716 if (0 > (fd = mkstemp(tmp_path)))
1717 {
1718 helplog(ASL_LEVEL_ERR, "mktemp \"%s\" failed: %s",
1719 tmp_path, strerror(errno));
1720 *err = kmDNSHelperRacoonConfigCreationFailed;
1721 goto fin;
1722 }
1723 if (NULL == (fp = fdopen(fd, "w")))
1724 {
1725 helplog(ASL_LEVEL_ERR, "fdopen: %s",
1726 strerror(errno));
1727 *err = kmDNSHelperRacoonConfigCreationFailed;
1728 goto fin;
1729 }
1730 fd = -1;
1731 fprintf(fp, config, configHeader, ro, rmt_port, keydata, ri, li, li, ri);
1732 fclose(fp);
1733 fp = NULL;
1734 if (0 > rename(tmp_path, path))
1735 {
1736 helplog(ASL_LEVEL_ERR,
1737 "rename \"%s\" \"%s\" failed: %s",
1738 tmp_path, path, strerror(errno));
1739 *err = kmDNSHelperRacoonConfigCreationFailed;
1740 goto fin;
1741 }
1742 if (0 != (*err = kickRacoon()))
1743 goto fin;
1744 }
1745 else
1746 {
1747 if (0 != unlink(path))
1748 debug("unlink \"%s\" failed: %s", path,
1749 strerror(errno));
1750 }
1751
1752 if (0 != (*err = doTunnelPolicy(kmDNSTunnelPolicyTeardown,
1753 loc_inner, kWholeV6Mask, loc_outer, loc_port,
1754 rmt_inner, kWholeV6Mask, rmt_outer, rmt_port)))
1755 goto fin;
1756 if (kmDNSAutoTunnelSetKeysReplace == replacedelete &&
1757 0 != (*err = doTunnelPolicy(kmDNSTunnelPolicySetup,
1758 loc_inner, kWholeV6Mask, loc_outer, loc_port,
1759 rmt_inner, kWholeV6Mask, rmt_outer, rmt_port)))
1760 goto fin;
1761
1762 if (0 != (*err = teardownTunnelRoute(rmt_inner)))
1763 goto fin;
1764 if (kmDNSAutoTunnelSetKeysReplace == replacedelete &&
1765 0 != (*err = setupTunnelRoute(loc_inner, rmt_inner)))
1766 goto fin;
1767
1768 debug("succeeded");
1769
1770 fin:
1771 if (NULL != fp)
1772 fclose(fp);
1773 if (0 <= fd)
1774 close(fd);
1775 unlink(tmp_path);
1776 update_idle_timer();
1777 return KERN_SUCCESS;
1778 }