mirror of
https://git.rtems.org/rtems-libbsd/
synced 2025-06-12 18:23:46 +08:00

The sources can be obtained via: http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-544.tar.gz
2868 lines
96 KiB
C
2868 lines
96 KiB
C
/* -*- Mode: C; tab-width: 4 -*-
|
||
*
|
||
* Copyright (c) 2007-2012 Apple Inc. All rights reserved.
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
|
||
#include <sys/cdefs.h>
|
||
#include <arpa/inet.h>
|
||
#include <bsm/libbsm.h>
|
||
#include <net/if.h>
|
||
#include <net/route.h>
|
||
#include <net/if_dl.h>
|
||
#include <net/if_types.h>
|
||
#include <netinet/in.h>
|
||
#include <netinet/if_ether.h>
|
||
#include <netinet6/in6_var.h>
|
||
#include <netinet6/nd6.h>
|
||
#include <netinet6/ipsec.h>
|
||
#include <sys/ioctl.h>
|
||
#include <sys/param.h>
|
||
#include <sys/socket.h>
|
||
#include <asl.h>
|
||
#include <ctype.h>
|
||
#include <dirent.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
#include <signal.h>
|
||
#include <stdarg.h>
|
||
#include <stdbool.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <Security/Security.h>
|
||
#include <SystemConfiguration/SystemConfiguration.h>
|
||
#include <SystemConfiguration/SCPreferencesSetSpecific.h>
|
||
#include <TargetConditionals.h>
|
||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||
#include <net/bpf.h>
|
||
#include <sys/sysctl.h>
|
||
|
||
#include "mDNSEmbeddedAPI.h"
|
||
#include "dns_sd.h"
|
||
#include "dnssd_ipc.h"
|
||
#include "libpfkey.h"
|
||
#include "helper.h"
|
||
#include "helpermsgServer.h"
|
||
#include "helper-server.h"
|
||
#include "ipsec_options.h"
|
||
#include "P2PPacketFilter.h"
|
||
|
||
#include <netinet/ip.h>
|
||
#include <netinet/tcp.h>
|
||
|
||
#ifndef RTF_IFSCOPE
|
||
#define RTF_IFSCOPE 0x1000000
|
||
#endif
|
||
|
||
#if TARGET_OS_EMBEDDED
|
||
#ifndef MDNS_NO_IPSEC
|
||
#define MDNS_NO_IPSEC 1
|
||
#endif
|
||
#define NO_CFUSERNOTIFICATION 1
|
||
#define NO_SECURITYFRAMEWORK 1
|
||
#endif
|
||
|
||
// Embed the client stub code here, so we can access private functions like ConnectToServer, create_hdr, deliver_request
|
||
#include "../mDNSShared/dnssd_ipc.c"
|
||
#include "../mDNSShared/dnssd_clientstub.c"
|
||
|
||
typedef struct sadb_x_policy *ipsec_policy_t;
|
||
|
||
unsigned short InetChecksum(unsigned short *ptr,int nbytes);
|
||
unsigned long in_cksum(unsigned short *ptr,int nbytes);
|
||
void TCPCheckSum(int af, struct tcphdr *t, int tcplen, v6addr_t sadd6, v6addr_t dadd6);
|
||
|
||
uid_t mDNSResponderUID;
|
||
gid_t mDNSResponderGID;
|
||
|
||
void
|
||
debug_(const char *func, const char *fmt, ...)
|
||
{
|
||
char buf[2048];
|
||
va_list ap;
|
||
|
||
va_start(ap, fmt);
|
||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||
va_end(ap);
|
||
helplog(ASL_LEVEL_DEBUG, "%s: %s", func, buf);
|
||
}
|
||
|
||
static int
|
||
authorized(audit_token_t *token)
|
||
{
|
||
int ok = 0;
|
||
pid_t pid = (pid_t)-1;
|
||
uid_t euid = (uid_t)-1;
|
||
|
||
audit_token_to_au32(*token, NULL, &euid, NULL, NULL, NULL, &pid, NULL,
|
||
NULL);
|
||
ok = (euid == mDNSResponderUID || euid == 0);
|
||
if (!ok)
|
||
helplog(ASL_LEVEL_NOTICE,
|
||
"Unauthorized access by euid=%lu pid=%lu",
|
||
(unsigned long)euid, (unsigned long)pid);
|
||
return ok;
|
||
}
|
||
|
||
kern_return_t
|
||
do_mDNSExit(__unused mach_port_t port, audit_token_t token)
|
||
{
|
||
debug("entry");
|
||
if (!authorized(&token))
|
||
goto fin;
|
||
helplog(ASL_LEVEL_INFO, "exit");
|
||
exit(0);
|
||
|
||
fin:
|
||
debug("fin");
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
kern_return_t do_mDNSRequestBPF(__unused mach_port_t port, audit_token_t token)
|
||
{
|
||
if (!authorized(&token)) return KERN_SUCCESS;
|
||
DNSServiceRef ref;
|
||
DNSServiceErrorType err = ConnectToServer(&ref, 0, send_bpf, NULL, NULL, NULL);
|
||
if (err) { helplog(ASL_LEVEL_ERR, "do_mDNSRequestBPF: ConnectToServer %d", err); return err; }
|
||
|
||
char *ptr;
|
||
size_t len = sizeof(DNSServiceFlags);
|
||
ipc_msg_hdr *hdr = create_hdr(send_bpf, &len, &ptr, 0, ref);
|
||
if (!hdr) { DNSServiceRefDeallocate(ref); return kDNSServiceErr_NoMemory; }
|
||
put_flags(0, &ptr);
|
||
deliver_request(hdr, ref); // Will free hdr for us
|
||
DNSServiceRefDeallocate(ref);
|
||
update_idle_timer();
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
kern_return_t do_mDNSPowerRequest(__unused mach_port_t port, int key, int interval, int *err, audit_token_t token)
|
||
{
|
||
*err = -1;
|
||
if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; }
|
||
|
||
CFArrayRef events = IOPMCopyScheduledPowerEvents();
|
||
if (events)
|
||
{
|
||
int i;
|
||
CFIndex count = CFArrayGetCount(events);
|
||
for (i=0; i<count; i++)
|
||
{
|
||
CFDictionaryRef dict = CFArrayGetValueAtIndex(events, i);
|
||
CFStringRef id = CFDictionaryGetValue(dict, CFSTR(kIOPMPowerEventAppNameKey));
|
||
if (CFEqual(id, CFSTR("mDNSResponderHelper")))
|
||
{
|
||
CFDateRef EventTime = CFDictionaryGetValue(dict, CFSTR(kIOPMPowerEventTimeKey));
|
||
CFStringRef EventType = CFDictionaryGetValue(dict, CFSTR(kIOPMPowerEventTypeKey));
|
||
IOReturn result = IOPMCancelScheduledPowerEvent(EventTime, id, EventType);
|
||
//helplog(ASL_LEVEL_ERR, "Deleting old event %s");
|
||
if (result) helplog(ASL_LEVEL_ERR, "IOPMCancelScheduledPowerEvent %d failed %d", i, result);
|
||
}
|
||
}
|
||
CFRelease(events);
|
||
}
|
||
|
||
if (key < 0) // mDNSPowerRequest(-1,-1) means "clear any stale schedules" (see above)
|
||
*err = 0;
|
||
else if (key == 0) // mDNSPowerRequest(0, 0) means "sleep now"
|
||
{
|
||
IOReturn r = IOPMSleepSystem(IOPMFindPowerManagement(MACH_PORT_NULL));
|
||
if (r) { usleep(100000); helplog(ASL_LEVEL_ERR, "IOPMSleepSystem %d", r); }
|
||
*err = r;
|
||
}
|
||
else if (key > 0)
|
||
{
|
||
CFDateRef w = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval);
|
||
if (w)
|
||
{
|
||
IOReturn r = IOPMSchedulePowerEvent(w, CFSTR("mDNSResponderHelper"), key ? CFSTR(kIOPMAutoWake) : CFSTR(kIOPMAutoSleep));
|
||
if (r) { usleep(100000); helplog(ASL_LEVEL_ERR, "IOPMSchedulePowerEvent(%d) %d %x", interval, r, r); }
|
||
*err = r;
|
||
CFRelease(w);
|
||
}
|
||
}
|
||
fin:
|
||
update_idle_timer();
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
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)
|
||
{
|
||
#define IPv6FMTSTRING "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X"
|
||
#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]
|
||
#if 0
|
||
if (family == 4)
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry %d IPv%d %d.%d.%d.%d %02X:%02X:%02X:%02X:%02X:%02X",
|
||
ifindex, family, ip[0], ip[1], ip[2], ip[3], eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]);
|
||
else
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry %d IPv%d " IPv6FMTSTRING " %02X:%02X:%02X:%02X:%02X:%02X",
|
||
ifindex, family, IPv6FMTARGS, eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]);
|
||
#endif
|
||
|
||
*err = -1;
|
||
if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; }
|
||
|
||
static int s = -1, seq = 0;
|
||
if (s < 0)
|
||
{
|
||
s = socket(PF_ROUTE, SOCK_RAW, 0);
|
||
if (s < 0) helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: socket(PF_ROUTE, SOCK_RAW, 0) failed %d (%s)", errno, strerror(errno));
|
||
}
|
||
|
||
if (s >= 0)
|
||
{
|
||
struct timeval tv;
|
||
gettimeofday(&tv, 0);
|
||
if (family == 4)
|
||
{
|
||
struct { struct rt_msghdr hdr; struct sockaddr_inarp dst; struct sockaddr_dl sdl; } rtmsg;
|
||
memset(&rtmsg, 0, sizeof(rtmsg));
|
||
|
||
rtmsg.hdr.rtm_msglen = sizeof(rtmsg);
|
||
rtmsg.hdr.rtm_version = RTM_VERSION;
|
||
rtmsg.hdr.rtm_type = RTM_ADD;
|
||
rtmsg.hdr.rtm_index = ifindex;
|
||
rtmsg.hdr.rtm_flags = RTF_HOST | RTF_STATIC | RTF_IFSCOPE;
|
||
rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
|
||
rtmsg.hdr.rtm_pid = 0;
|
||
rtmsg.hdr.rtm_seq = seq++;
|
||
rtmsg.hdr.rtm_errno = 0;
|
||
rtmsg.hdr.rtm_use = 0;
|
||
rtmsg.hdr.rtm_inits = RTV_EXPIRE;
|
||
rtmsg.hdr.rtm_rmx.rmx_expire = tv.tv_sec + 30;
|
||
|
||
rtmsg.dst.sin_len = sizeof(rtmsg.dst);
|
||
rtmsg.dst.sin_family = AF_INET;
|
||
rtmsg.dst.sin_port = 0;
|
||
rtmsg.dst.sin_addr.s_addr = *(in_addr_t*)ip;
|
||
rtmsg.dst.sin_srcaddr.s_addr = 0;
|
||
rtmsg.dst.sin_tos = 0;
|
||
rtmsg.dst.sin_other = 0;
|
||
|
||
rtmsg.sdl.sdl_len = sizeof(rtmsg.sdl);
|
||
rtmsg.sdl.sdl_family = AF_LINK;
|
||
rtmsg.sdl.sdl_index = ifindex;
|
||
rtmsg.sdl.sdl_type = IFT_ETHER;
|
||
rtmsg.sdl.sdl_nlen = 0;
|
||
rtmsg.sdl.sdl_alen = ETHER_ADDR_LEN;
|
||
rtmsg.sdl.sdl_slen = 0;
|
||
|
||
// Target MAC address goes in rtmsg.sdl.sdl_data[0..5]; (See LLADDR() in /usr/include/net/if_dl.h)
|
||
memcpy(rtmsg.sdl.sdl_data, eth, sizeof(ethaddr_t));
|
||
|
||
int len = write(s, (char *)&rtmsg, sizeof(rtmsg));
|
||
if (len < 0)
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: write(%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s)",
|
||
sizeof(rtmsg), ifindex, ip[0], ip[1], ip[2], ip[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno));
|
||
len = read(s, (char *)&rtmsg, sizeof(rtmsg));
|
||
if (len < 0 || rtmsg.hdr.rtm_errno)
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: read (%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s) %d",
|
||
sizeof(rtmsg), ifindex, ip[0], ip[1], ip[2], ip[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno), rtmsg.hdr.rtm_errno);
|
||
|
||
*err = 0;
|
||
}
|
||
else
|
||
{
|
||
struct { struct rt_msghdr hdr; struct sockaddr_in6 dst; struct sockaddr_dl sdl; } rtmsg;
|
||
memset(&rtmsg, 0, sizeof(rtmsg));
|
||
|
||
rtmsg.hdr.rtm_msglen = sizeof(rtmsg);
|
||
rtmsg.hdr.rtm_version = RTM_VERSION;
|
||
rtmsg.hdr.rtm_type = RTM_ADD;
|
||
rtmsg.hdr.rtm_index = ifindex;
|
||
rtmsg.hdr.rtm_flags = RTF_HOST | RTF_STATIC | RTF_IFSCOPE;
|
||
rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
|
||
rtmsg.hdr.rtm_pid = 0;
|
||
rtmsg.hdr.rtm_seq = seq++;
|
||
rtmsg.hdr.rtm_errno = 0;
|
||
rtmsg.hdr.rtm_use = 0;
|
||
rtmsg.hdr.rtm_inits = RTV_EXPIRE;
|
||
rtmsg.hdr.rtm_rmx.rmx_expire = tv.tv_sec + 30;
|
||
|
||
rtmsg.dst.sin6_len = sizeof(rtmsg.dst);
|
||
rtmsg.dst.sin6_family = AF_INET6;
|
||
rtmsg.dst.sin6_port = 0;
|
||
rtmsg.dst.sin6_flowinfo = 0;
|
||
rtmsg.dst.sin6_addr = *(struct in6_addr*)ip;
|
||
rtmsg.dst.sin6_scope_id = ifindex;
|
||
|
||
rtmsg.sdl.sdl_len = sizeof(rtmsg.sdl);
|
||
rtmsg.sdl.sdl_family = AF_LINK;
|
||
rtmsg.sdl.sdl_index = ifindex;
|
||
rtmsg.sdl.sdl_type = IFT_ETHER;
|
||
rtmsg.sdl.sdl_nlen = 0;
|
||
rtmsg.sdl.sdl_alen = ETHER_ADDR_LEN;
|
||
rtmsg.sdl.sdl_slen = 0;
|
||
|
||
// Target MAC address goes in rtmsg.sdl.sdl_data[0..5]; (See LLADDR() in /usr/include/net/if_dl.h)
|
||
memcpy(rtmsg.sdl.sdl_data, eth, sizeof(ethaddr_t));
|
||
|
||
int len = write(s, (char *)&rtmsg, sizeof(rtmsg));
|
||
if (len < 0)
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: write(%d) interface %d address " IPv6FMTSTRING " seq %d result %d errno %d (%s)",
|
||
sizeof(rtmsg), ifindex, IPv6FMTARGS, rtmsg.hdr.rtm_seq, len, errno, strerror(errno));
|
||
len = read(s, (char *)&rtmsg, sizeof(rtmsg));
|
||
if (len < 0 || rtmsg.hdr.rtm_errno)
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: read (%d) interface %d address " IPv6FMTSTRING " seq %d result %d errno %d (%s) %d",
|
||
sizeof(rtmsg), ifindex, IPv6FMTARGS, rtmsg.hdr.rtm_seq, len, errno, strerror(errno), rtmsg.hdr.rtm_errno);
|
||
|
||
*err = 0;
|
||
}
|
||
|
||
}
|
||
|
||
fin:
|
||
update_idle_timer();
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
kern_return_t do_mDNSNotify(__unused mach_port_t port, const char *title, const char *msg, audit_token_t token)
|
||
{
|
||
if (!authorized(&token)) return KERN_SUCCESS;
|
||
|
||
#ifndef NO_CFUSERNOTIFICATION
|
||
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.)";
|
||
CFStringRef alertHeader = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
|
||
CFStringRef alertBody = CFStringCreateWithCString(NULL, msg, kCFStringEncodingUTF8);
|
||
CFStringRef alertFooter = CFStringCreateWithCString(NULL, footer, kCFStringEncodingUTF8);
|
||
CFStringRef alertMessage = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@\r\r%@"), alertBody, alertFooter);
|
||
CFRelease(alertBody);
|
||
CFRelease(alertFooter);
|
||
int err = CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, NULL);
|
||
if (err) helplog(ASL_LEVEL_ERR, "CFUserNotificationDisplayNotice returned %d", err);
|
||
CFRelease(alertHeader);
|
||
CFRelease(alertMessage);
|
||
#else
|
||
(void)title;
|
||
(void)msg;
|
||
#endif /* NO_CFUSERNOTIFICATION */
|
||
|
||
update_idle_timer();
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
char usercompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name the user saw
|
||
char userhostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name the user saw
|
||
char lastcompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name saved to preferences
|
||
char lasthostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name saved to preferences
|
||
|
||
#ifndef NO_CFUSERNOTIFICATION
|
||
static CFStringRef CFS_OQ = NULL;
|
||
static CFStringRef CFS_CQ = NULL;
|
||
static CFStringRef CFS_Format = NULL;
|
||
static CFStringRef CFS_ComputerName = NULL;
|
||
static CFStringRef CFS_ComputerNameMsg = NULL;
|
||
static CFStringRef CFS_LocalHostName = NULL;
|
||
static CFStringRef CFS_LocalHostNameMsg = NULL;
|
||
static CFStringRef CFS_Problem = NULL;
|
||
|
||
static CFUserNotificationRef gNotification = NULL;
|
||
static CFRunLoopSourceRef gNotificationRLS = NULL;
|
||
|
||
static void NotificationCallBackDismissed(CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
|
||
{
|
||
debug("entry");
|
||
(void)responseFlags; // Unused
|
||
if (userNotification != gNotification) helplog(ASL_LEVEL_ERR, "NotificationCallBackDismissed: Wrong CFUserNotificationRef");
|
||
if (gNotificationRLS)
|
||
{
|
||
// Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread.
|
||
// We need to explicitly specify the desired CFRunLoop from which we want to remove this event source.
|
||
CFRunLoopRemoveSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode);
|
||
CFRelease(gNotificationRLS);
|
||
gNotificationRLS = NULL;
|
||
CFRelease(gNotification);
|
||
gNotification = NULL;
|
||
}
|
||
// By dismissing the alert, the user has conceptually acknowleged the rename.
|
||
// (e.g. the machine's name is now officially "computer-2.local", not "computer.local".)
|
||
// If we get *another* conflict, the new alert should refer to the 'old' name
|
||
// as now being "computer-2.local", not "computer.local"
|
||
usercompname[0] = 0;
|
||
userhostname[0] = 0;
|
||
lastcompname[0] = 0;
|
||
lasthostname[0] = 0;
|
||
update_idle_timer();
|
||
unpause_idle_timer();
|
||
}
|
||
|
||
static void ShowNameConflictNotification(CFMutableArrayRef header, CFStringRef subtext)
|
||
{
|
||
CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||
if (!dictionary) return;
|
||
|
||
debug("entry");
|
||
|
||
CFDictionarySetValue(dictionary, kCFUserNotificationAlertHeaderKey, header);
|
||
CFDictionarySetValue(dictionary, kCFUserNotificationAlertMessageKey, subtext);
|
||
|
||
CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, CFSTR("/System/Library/CoreServices/mDNSResponder.bundle"), kCFURLPOSIXPathStyle, true);
|
||
if (urlRef) { CFDictionarySetValue(dictionary, kCFUserNotificationLocalizationURLKey, urlRef); CFRelease(urlRef); }
|
||
|
||
if (gNotification) // If notification already on-screen, update it in place
|
||
CFUserNotificationUpdate(gNotification, 0, kCFUserNotificationCautionAlertLevel, dictionary);
|
||
else // else, we need to create it
|
||
{
|
||
SInt32 error;
|
||
gNotification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationCautionAlertLevel, &error, dictionary);
|
||
if (!gNotification || error) { helplog(ASL_LEVEL_ERR, "ShowNameConflictNotification: CFUserNotificationRef: Error %d", error); return; }
|
||
gNotificationRLS = CFUserNotificationCreateRunLoopSource(NULL, gNotification, NotificationCallBackDismissed, 0);
|
||
if (!gNotificationRLS) { helplog(ASL_LEVEL_ERR,"ShowNameConflictNotification: RLS"); CFRelease(gNotification); gNotification = NULL; return; }
|
||
// Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread.
|
||
// We need to explicitly specify the desired CFRunLoop to which we want to add this event source.
|
||
CFRunLoopAddSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode);
|
||
debug("gRunLoop=%p gNotification=%p gNotificationRLS=%p", gRunLoop, gNotification, gNotificationRLS);
|
||
pause_idle_timer();
|
||
}
|
||
|
||
CFRelease(dictionary);
|
||
}
|
||
|
||
static CFMutableArrayRef GetHeader(const char* oldname, const char* newname, const CFStringRef msg, const char* suffix)
|
||
{
|
||
CFMutableArrayRef alertHeader = NULL;
|
||
|
||
const CFStringRef cfoldname = CFStringCreateWithCString(NULL, oldname, kCFStringEncodingUTF8);
|
||
// NULL newname means we've given up trying to construct a name that doesn't conflict
|
||
const CFStringRef cfnewname = newname ? CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8) : NULL;
|
||
// We tag a zero-width non-breaking space at the end of the literal text to guarantee that, no matter what
|
||
// arbitrary computer name the user may choose, this exact text (with zero-width non-breaking space added)
|
||
// can never be one that occurs in the Localizable.strings translation file.
|
||
if (!cfoldname)
|
||
helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for old=%s", newname);
|
||
else if (newname && !cfnewname)
|
||
helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for new=%s", newname);
|
||
else
|
||
{
|
||
const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfoldname, suffix);
|
||
const CFStringRef s2 = cfnewname ? CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfnewname, suffix) : NULL;
|
||
|
||
alertHeader = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
||
|
||
if (!s1)
|
||
helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for old=%s", oldname);
|
||
else if (cfnewname && !s2)
|
||
helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for new=%s", newname);
|
||
else if (!alertHeader)
|
||
helplog(ASL_LEVEL_ERR, "Could not construct CFArray for notification");
|
||
else
|
||
{
|
||
// Make sure someone is logged in. We don't want this popping up over the login window
|
||
uid_t uid;
|
||
gid_t gid;
|
||
CFStringRef userName = SCDynamicStoreCopyConsoleUser(NULL, &uid, &gid);
|
||
if (userName)
|
||
{
|
||
CFRelease(userName);
|
||
CFArrayAppendValue(alertHeader, msg); // Opening phrase of message, provided by caller
|
||
CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s1); CFArrayAppendValue(alertHeader, CFS_CQ);
|
||
CFArrayAppendValue(alertHeader, CFSTR(" is already in use on this network. "));
|
||
if (s2)
|
||
{
|
||
CFArrayAppendValue(alertHeader, CFSTR("The name has been changed to "));
|
||
CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s2); CFArrayAppendValue(alertHeader, CFS_CQ);
|
||
CFArrayAppendValue(alertHeader, CFSTR("."));
|
||
}
|
||
else
|
||
CFArrayAppendValue(alertHeader, CFSTR("All attempts to find an available name by adding a number to the name were also unsuccessful."));
|
||
}
|
||
}
|
||
if (s1) CFRelease(s1);
|
||
if (s2) CFRelease(s2);
|
||
}
|
||
if (cfoldname) CFRelease(cfoldname);
|
||
if (cfnewname) CFRelease(cfnewname);
|
||
|
||
return alertHeader;
|
||
}
|
||
#endif /* ndef NO_CFUSERNOTIFICATION */
|
||
|
||
static void update_notification(void)
|
||
{
|
||
#ifndef NO_CFUSERNOTIFICATION
|
||
debug("entry ucn=%s, uhn=%s, lcn=%s, lhn=%s", usercompname, userhostname, lastcompname, lasthostname);
|
||
if (!CFS_OQ)
|
||
{
|
||
// 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.
|
||
// By appending this invisible character on the end of literal names, we ensure the these strings cannot inadvertently match any string
|
||
// in the localization file -- since we know for sure that none of our strings in the localization file contain the ZWNBS character.
|
||
//
|
||
// For languages that are written right to left, when we mix English (host names could be in english with brackets etc. and the
|
||
// rest in Arabic) we need unicode markups for proper formatting. The Unicode sequence 202C (UTF8 E2 80 AC), 200E (UTF8 E2 80 8E) and
|
||
// 202B (UTF8 E2 80 AB) helps with the formatting. See <rdar://problem/8629082> for more details.
|
||
CFS_OQ = CFStringCreateWithCString(NULL, "“\xE2\x80\xAB", kCFStringEncodingUTF8);
|
||
CFS_CQ = CFStringCreateWithCString(NULL, "\xE2\x80\xAC”", kCFStringEncodingUTF8);
|
||
CFS_Format = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF\xE2\x80\x8E", kCFStringEncodingUTF8);
|
||
CFS_ComputerName = CFStringCreateWithCString(NULL, "The name of your computer ", kCFStringEncodingUTF8);
|
||
CFS_ComputerNameMsg = CFStringCreateWithCString(NULL, "To change the name of your computer, "
|
||
"open System Preferences and click Sharing, then type the name in the Computer Name field.", kCFStringEncodingUTF8);
|
||
CFS_LocalHostName = CFStringCreateWithCString(NULL, "This computer’s local hostname ", kCFStringEncodingUTF8);
|
||
CFS_LocalHostNameMsg = CFStringCreateWithCString(NULL, "To change the local hostname, "
|
||
"open System Preferences and click Sharing, then click “Edit” and type the name in the Local Hostname field.", kCFStringEncodingUTF8);
|
||
CFS_Problem = CFStringCreateWithCString(NULL, "This may indicate a problem with the local network. "
|
||
"Please inform your network administrator.", kCFStringEncodingUTF8);
|
||
}
|
||
|
||
if (!usercompname[0] && !userhostname[0])
|
||
{
|
||
if (gNotificationRLS)
|
||
{
|
||
debug("canceling notification %p", gNotification);
|
||
CFUserNotificationCancel(gNotification);
|
||
unpause_idle_timer();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
CFMutableArrayRef header = NULL;
|
||
CFStringRef* subtext = NULL;
|
||
if (userhostname[0] && !lasthostname[0]) // we've given up trying to construct a name that doesn't conflict
|
||
{
|
||
header = GetHeader(userhostname, NULL, CFS_LocalHostName, ".local");
|
||
subtext = &CFS_Problem;
|
||
}
|
||
else if (usercompname[0])
|
||
{
|
||
header = GetHeader(usercompname, lastcompname, CFS_ComputerName, "");
|
||
subtext = &CFS_ComputerNameMsg;
|
||
}
|
||
else
|
||
{
|
||
header = GetHeader(userhostname, lasthostname, CFS_LocalHostName, ".local");
|
||
subtext = &CFS_LocalHostNameMsg;
|
||
}
|
||
ShowNameConflictNotification(header, *subtext);
|
||
CFRelease(header);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
kern_return_t
|
||
do_mDNSPreferencesSetName(__unused mach_port_t port, int key, const char* old, const char* new, audit_token_t token)
|
||
{
|
||
SCPreferencesRef session = NULL;
|
||
Boolean ok = FALSE;
|
||
Boolean locked = FALSE;
|
||
CFStringRef cfstr = NULL;
|
||
char* user = NULL;
|
||
char* last = NULL;
|
||
Boolean needUpdate = FALSE;
|
||
|
||
debug("entry %s old=%s new=%s", key==kmDNSComputerName ? "ComputerName" : (key==kmDNSLocalHostName ? "LocalHostName" : "UNKNOWN"), old, new);
|
||
if (!authorized(&token)) goto fin;
|
||
|
||
switch ((enum mDNSPreferencesSetNameKey)key)
|
||
{
|
||
case kmDNSComputerName:
|
||
user = usercompname;
|
||
last = lastcompname;
|
||
break;
|
||
case kmDNSLocalHostName:
|
||
user = userhostname;
|
||
last = lasthostname;
|
||
break;
|
||
default:
|
||
debug("unrecognized key: %d", key);
|
||
goto fin;
|
||
}
|
||
|
||
if (!last)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "%s: no last ptr", __func__);
|
||
goto fin;
|
||
}
|
||
|
||
if (!user)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "%s: no user ptr", __func__);
|
||
goto fin;
|
||
}
|
||
|
||
if (0 == strncmp(old, new, MAX_DOMAIN_LABEL+1))
|
||
{
|
||
// old and new are same means the config changed i.e, the user has set something in the preferences pane.
|
||
// This means the conflict has been resolved. We need to dismiss the dialogue.
|
||
if (last[0] && 0 != strncmp(last, new, MAX_DOMAIN_LABEL+1))
|
||
{
|
||
last[0] = 0;
|
||
user[0] = 0;
|
||
needUpdate = TRUE;
|
||
}
|
||
goto fin;
|
||
}
|
||
else
|
||
{
|
||
// old and new are not same, this means there is a conflict. For the first conflict, we show
|
||
// the old value and the new value. For all subsequent conflicts, while the dialogue is still
|
||
// up, we do a real time update of the "new" value in the dialogue. That's why we update just
|
||
// "last" here and not "user".
|
||
if (strncmp(last, new, MAX_DOMAIN_LABEL+1))
|
||
{
|
||
strncpy(last, new, MAX_DOMAIN_LABEL);
|
||
needUpdate = TRUE;
|
||
}
|
||
}
|
||
|
||
// If we are not showing the dialogue, we need to remember the first "old" value so that
|
||
// we maintain the same through the lifetime of the dialogue. Subsequence conflicts don't
|
||
// update the "old" value.
|
||
if (!user[0])
|
||
{
|
||
strncpy(user, old, MAX_DOMAIN_LABEL);
|
||
needUpdate = TRUE;
|
||
}
|
||
|
||
if (!new[0]) // we've given up trying to construct a name that doesn't conflict
|
||
goto fin;
|
||
|
||
cfstr = CFStringCreateWithCString(NULL, new, kCFStringEncodingUTF8);
|
||
|
||
session = SCPreferencesCreate(NULL, CFSTR(kmDNSHelperServiceName), NULL);
|
||
|
||
if (cfstr == NULL || session == NULL)
|
||
{
|
||
debug("SCPreferencesCreate failed");
|
||
goto fin;
|
||
}
|
||
if (!SCPreferencesLock(session, 0))
|
||
{
|
||
debug("lock failed");
|
||
goto fin;
|
||
}
|
||
locked = TRUE;
|
||
|
||
switch ((enum mDNSPreferencesSetNameKey)key)
|
||
{
|
||
case kmDNSComputerName:
|
||
{
|
||
// We want to write the new Computer Name to System Preferences, without disturbing the user-selected
|
||
// system-wide default character set used for things like AppleTalk NBP and NETBIOS service advertising.
|
||
// Note that this encoding is not used for the computer name, but since both are set by the same call,
|
||
// we need to take care to set the name without changing the character set.
|
||
CFStringEncoding encoding = kCFStringEncodingUTF8;
|
||
CFStringRef unused = SCDynamicStoreCopyComputerName(NULL, &encoding);
|
||
if (unused) { CFRelease(unused); unused = NULL; }
|
||
else encoding = kCFStringEncodingUTF8;
|
||
|
||
ok = SCPreferencesSetComputerName(session, cfstr, encoding);
|
||
}
|
||
break;
|
||
case kmDNSLocalHostName:
|
||
ok = SCPreferencesSetLocalHostName(session, cfstr);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (!ok || !SCPreferencesCommitChanges(session) ||
|
||
!SCPreferencesApplyChanges(session))
|
||
{
|
||
debug("SCPreferences update failed");
|
||
goto fin;
|
||
}
|
||
debug("succeeded");
|
||
|
||
fin:
|
||
if (NULL != cfstr)
|
||
CFRelease(cfstr);
|
||
if (NULL != session)
|
||
{
|
||
if (locked)
|
||
SCPreferencesUnlock(session);
|
||
CFRelease(session);
|
||
}
|
||
update_idle_timer();
|
||
if (needUpdate) update_notification();
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
enum DNSKeyFormat
|
||
{
|
||
formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem, formatBtmmPrefixedServiceItem
|
||
};
|
||
|
||
// On Mac OS X on Intel, the four-character string seems to be stored backwards, at least sometimes.
|
||
// I suspect some overenthusiastic inexperienced engineer said, "On Intel everything's backwards,
|
||
// therefore I need to add some byte swapping in this API to make this four-character string backwards too."
|
||
// To cope with this we allow *both* "ddns" and "sndd" as valid item types.
|
||
|
||
|
||
#ifndef NO_SECURITYFRAMEWORK
|
||
static const char btmmprefix[] = "btmmdns:";
|
||
static const char dnsprefix[] = "dns:";
|
||
static const char ddns[] = "ddns";
|
||
static const char ddnsrev[] = "sndd";
|
||
|
||
static enum DNSKeyFormat
|
||
getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp)
|
||
{
|
||
static UInt32 tags[4] =
|
||
{
|
||
kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr, kSecLabelItemAttr
|
||
};
|
||
static SecKeychainAttributeInfo attributeInfo =
|
||
{
|
||
sizeof(tags)/sizeof(tags[0]), tags, NULL
|
||
};
|
||
SecKeychainAttributeList *attributes = NULL;
|
||
enum DNSKeyFormat format;
|
||
Boolean malformed = FALSE;
|
||
OSStatus status = noErr;
|
||
int i = 0;
|
||
|
||
*attributesp = NULL;
|
||
if (noErr != (status = SecKeychainItemCopyAttributesAndData(item,
|
||
&attributeInfo, NULL, &attributes, NULL, NULL)))
|
||
{
|
||
debug("SecKeychainItemCopyAttributesAndData %d - skipping",
|
||
status);
|
||
goto skip;
|
||
}
|
||
if (attributeInfo.count != attributes->count)
|
||
malformed = TRUE;
|
||
for (i = 0; !malformed && i < (int)attributeInfo.count; ++i)
|
||
if (attributeInfo.tag[i] != attributes->attr[i].tag)
|
||
malformed = TRUE;
|
||
if (malformed)
|
||
{
|
||
debug(
|
||
"malformed result from SecKeychainItemCopyAttributesAndData - skipping");
|
||
goto skip;
|
||
}
|
||
|
||
debug("entry (\"%.*s\", \"%.*s\", \"%.*s\")",
|
||
(int)attributes->attr[0].length, attributes->attr[0].data,
|
||
(int)attributes->attr[1].length, attributes->attr[1].data,
|
||
(int)attributes->attr[2].length, attributes->attr[2].data);
|
||
if (attributes->attr[1].length >= MAX_ESCAPED_DOMAIN_NAME +
|
||
sizeof(dnsprefix)-1)
|
||
{
|
||
debug("kSecServiceItemAttr too long (%u) - skipping",
|
||
(unsigned int)attributes->attr[1].length);
|
||
goto skip;
|
||
}
|
||
if (attributes->attr[2].length >= MAX_ESCAPED_DOMAIN_NAME)
|
||
{
|
||
debug("kSecAccountItemAttr too long (%u) - skipping",
|
||
(unsigned int)attributes->attr[2].length);
|
||
goto skip;
|
||
}
|
||
if (attributes->attr[1].length >= sizeof(dnsprefix)-1 &&
|
||
0 == strncasecmp(attributes->attr[1].data, dnsprefix,
|
||
sizeof(dnsprefix)-1))
|
||
format = formatDnsPrefixedServiceItem;
|
||
else if (attributes->attr[1].length >= sizeof(btmmprefix)-1 &&
|
||
0 == strncasecmp(attributes->attr[1].data, btmmprefix, sizeof(btmmprefix)-1))
|
||
format = formatBtmmPrefixedServiceItem;
|
||
else if (attributes->attr[0].length == sizeof(ddns)-1 &&
|
||
0 == strncasecmp(attributes->attr[0].data, ddns, sizeof(ddns)-1))
|
||
format = formatDdnsTypeItem;
|
||
else if (attributes->attr[0].length == sizeof(ddnsrev)-1 &&
|
||
0 == strncasecmp(attributes->attr[0].data, ddnsrev, sizeof(ddnsrev)-1))
|
||
format = formatDdnsTypeItem;
|
||
else
|
||
{
|
||
debug("uninterested in this entry");
|
||
goto skip;
|
||
}
|
||
*attributesp = attributes;
|
||
debug("accepting this entry");
|
||
return format;
|
||
|
||
skip:
|
||
SecKeychainItemFreeAttributesAndData(attributes, NULL);
|
||
return formatNotDNSKey;
|
||
}
|
||
|
||
// Insert the attributes as defined by mDNSKeyChainAttributes
|
||
static CFPropertyListRef
|
||
getKeychainItemInfo(SecKeychainItemRef item,
|
||
SecKeychainAttributeList *attributes, enum DNSKeyFormat format)
|
||
{
|
||
CFMutableArrayRef entry = NULL;
|
||
CFDataRef data = NULL;
|
||
OSStatus status = noErr;
|
||
UInt32 keylen = 0;
|
||
void *keyp = 0;
|
||
|
||
if (NULL == (entry = CFArrayCreateMutable(NULL, 0,
|
||
&kCFTypeArrayCallBacks)))
|
||
{
|
||
debug("CFArrayCreateMutable failed");
|
||
goto error;
|
||
}
|
||
|
||
// Insert the Account attribute (kmDNSKcWhere)
|
||
switch ((enum DNSKeyFormat)format)
|
||
{
|
||
case formatDdnsTypeItem:
|
||
data = CFDataCreate(kCFAllocatorDefault,
|
||
attributes->attr[1].data, attributes->attr[1].length);
|
||
break;
|
||
case formatDnsPrefixedServiceItem:
|
||
case formatBtmmPrefixedServiceItem:
|
||
data = CFDataCreate(kCFAllocatorDefault,
|
||
attributes->attr[1].data, attributes->attr[1].length);
|
||
break;
|
||
default:
|
||
assert("unknown DNSKeyFormat value");
|
||
break;
|
||
}
|
||
if (NULL == data)
|
||
{
|
||
debug("CFDataCreate for attr[1] failed");
|
||
goto error;
|
||
}
|
||
CFArrayAppendValue(entry, data);
|
||
CFRelease(data);
|
||
|
||
// Insert the Where attribute (kmDNSKcAccount)
|
||
if (NULL == (data = CFDataCreate(kCFAllocatorDefault,
|
||
attributes->attr[2].data, attributes->attr[2].length)))
|
||
{
|
||
debug("CFDataCreate for attr[2] failed");
|
||
goto error;
|
||
}
|
||
CFArrayAppendValue(entry, data);
|
||
CFRelease(data);
|
||
|
||
// Insert the Key attribute (kmDNSKcKey)
|
||
if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, NULL,
|
||
NULL, NULL, &keylen, &keyp)))
|
||
{
|
||
debug("could not retrieve key for \"%.*s\": %d",
|
||
(int)attributes->attr[1].length, attributes->attr[1].data,
|
||
status);
|
||
goto error;
|
||
}
|
||
data = CFDataCreate(kCFAllocatorDefault, keyp, keylen);
|
||
SecKeychainItemFreeAttributesAndData(NULL, keyp);
|
||
if (NULL == data)
|
||
{
|
||
debug("CFDataCreate for keyp failed");
|
||
goto error;
|
||
}
|
||
CFArrayAppendValue(entry, data);
|
||
CFRelease(data);
|
||
|
||
// Insert the Name attribute (kmDNSKcName)
|
||
if (NULL == (data = CFDataCreate(kCFAllocatorDefault,
|
||
attributes->attr[3].data, attributes->attr[3].length)))
|
||
{
|
||
debug("CFDataCreate for attr[3] failed");
|
||
goto error;
|
||
}
|
||
CFArrayAppendValue(entry, data);
|
||
CFRelease(data);
|
||
return entry;
|
||
|
||
error:
|
||
if (NULL != entry)
|
||
CFRelease(entry);
|
||
return NULL;
|
||
}
|
||
#endif
|
||
|
||
kern_return_t
|
||
do_mDNSKeychainGetSecrets(__unused mach_port_t port, __unused unsigned int *numsecrets,
|
||
__unused vm_offset_t *secrets, __unused mach_msg_type_number_t *secretsCnt, __unused int *err,
|
||
__unused audit_token_t token)
|
||
{
|
||
#ifndef NO_SECURITYFRAMEWORK
|
||
CFWriteStreamRef stream = NULL;
|
||
CFDataRef result = NULL;
|
||
CFPropertyListRef entry = NULL;
|
||
CFMutableArrayRef keys = NULL;
|
||
SecKeychainRef skc = NULL;
|
||
SecKeychainItemRef item = NULL;
|
||
SecKeychainSearchRef search = NULL;
|
||
SecKeychainAttributeList *attributes = NULL;
|
||
enum DNSKeyFormat format;
|
||
OSStatus status = 0;
|
||
|
||
debug("entry");
|
||
*err = 0;
|
||
*numsecrets = 0;
|
||
*secrets = (vm_offset_t)NULL;
|
||
if (!authorized(&token))
|
||
{
|
||
*err = kmDNSHelperNotAuthorized;
|
||
goto fin;
|
||
}
|
||
if (NULL == (keys = CFArrayCreateMutable(NULL, 0,
|
||
&kCFTypeArrayCallBacks)))
|
||
{
|
||
debug("CFArrayCreateMutable failed");
|
||
*err = kmDNSHelperCreationFailed;
|
||
goto fin;
|
||
}
|
||
if (noErr != (status = SecKeychainCopyDefault(&skc)))
|
||
{
|
||
*err = kmDNSHelperKeychainCopyDefaultFailed;
|
||
goto fin;
|
||
}
|
||
if (noErr != (status = SecKeychainSearchCreateFromAttributes(skc, kSecGenericPasswordItemClass, NULL, &search)))
|
||
{
|
||
*err = kmDNSHelperKeychainSearchCreationFailed;
|
||
goto fin;
|
||
}
|
||
for (status = SecKeychainSearchCopyNext(search, &item);
|
||
noErr == status;
|
||
status = SecKeychainSearchCopyNext(search, &item))
|
||
{
|
||
if (formatNotDNSKey != (format = getDNSKeyFormat(item,
|
||
&attributes)) &&
|
||
NULL != (entry = getKeychainItemInfo(item, attributes,
|
||
format)))
|
||
{
|
||
CFArrayAppendValue(keys, entry);
|
||
CFRelease(entry);
|
||
}
|
||
SecKeychainItemFreeAttributesAndData(attributes, NULL);
|
||
CFRelease(item);
|
||
}
|
||
if (errSecItemNotFound != status)
|
||
helplog(ASL_LEVEL_ERR, "%s: SecKeychainSearchCopyNext failed: %d",
|
||
__func__, status);
|
||
if (NULL == (stream =
|
||
CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault,
|
||
kCFAllocatorDefault)))
|
||
{
|
||
*err = kmDNSHelperCreationFailed;
|
||
debug("CFWriteStreamCreateWithAllocatedBuffers failed");
|
||
goto fin;
|
||
}
|
||
CFWriteStreamOpen(stream);
|
||
if (0 == CFPropertyListWriteToStream(keys, stream,
|
||
kCFPropertyListBinaryFormat_v1_0, NULL))
|
||
{
|
||
*err = kmDNSHelperPListWriteFailed;
|
||
debug("CFPropertyListWriteToStream failed");
|
||
goto fin;
|
||
}
|
||
result = CFWriteStreamCopyProperty(stream,
|
||
kCFStreamPropertyDataWritten);
|
||
if (KERN_SUCCESS != vm_allocate(mach_task_self(), secrets,
|
||
CFDataGetLength(result), VM_FLAGS_ANYWHERE))
|
||
{
|
||
*err = kmDNSHelperCreationFailed;
|
||
debug("vm_allocate failed");
|
||
goto fin;
|
||
}
|
||
CFDataGetBytes(result, CFRangeMake(0, CFDataGetLength(result)),
|
||
(void *)*secrets);
|
||
*secretsCnt = CFDataGetLength(result);
|
||
*numsecrets = CFArrayGetCount(keys);
|
||
debug("succeeded");
|
||
|
||
fin:
|
||
debug("returning %u secrets", *numsecrets);
|
||
if (NULL != stream)
|
||
{
|
||
CFWriteStreamClose(stream);
|
||
CFRelease(stream);
|
||
}
|
||
if (NULL != result)
|
||
CFRelease(result);
|
||
if (NULL != keys)
|
||
CFRelease(keys);
|
||
if (NULL != search)
|
||
CFRelease(search);
|
||
if (NULL != skc)
|
||
CFRelease(skc);
|
||
update_idle_timer();
|
||
return KERN_SUCCESS;
|
||
#else
|
||
return KERN_FAILURE;
|
||
#endif
|
||
}
|
||
|
||
#ifndef MDNS_NO_IPSEC
|
||
typedef enum _mDNSTunnelPolicyWhich
|
||
{
|
||
kmDNSTunnelPolicySetup,
|
||
kmDNSTunnelPolicyTeardown,
|
||
kmDNSTunnelPolicyGenerate
|
||
} mDNSTunnelPolicyWhich;
|
||
|
||
// For kmDNSTunnelPolicySetup, you can setup IPv6-in-IPv6 tunnel or IPv6-in-IPv4 tunnel
|
||
// kmDNSNoTunnel is used for other Policy types
|
||
typedef enum _mDNSTunnelType
|
||
{
|
||
kmDNSNoTunnel,
|
||
kmDNSIPv6IPv4Tunnel,
|
||
kmDNSIPv6IPv6Tunnel
|
||
} mDNSTunnelType;
|
||
|
||
static const uint8_t kWholeV6Mask = 128;
|
||
|
||
#endif /* ifndef MDNS_NO_IPSEC */
|
||
|
||
#ifndef MDNS_NO_IPSEC
|
||
|
||
static const char g_racoon_config_dir[] = "/var/run/racoon/";
|
||
static const char g_racoon_config_dir_old[] = "/etc/racoon/remote/";
|
||
|
||
CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void);
|
||
CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey;
|
||
|
||
// Major version 6 is 10.2.x (Jaguar)
|
||
// Major version 7 is 10.3.x (Panther)
|
||
// Major version 8 is 10.4.x (Tiger)
|
||
// Major version 9 is 10.5.x (Leopard)
|
||
// Major version 10 is 10.6.x (SnowLeopard)
|
||
static int MacOSXSystemBuildNumber(char* letter_out, int* minor_out)
|
||
{
|
||
int major = 0, minor = 0;
|
||
char letter = 0, buildver[256]="<Unknown>";
|
||
CFDictionaryRef vers = _CFCopySystemVersionDictionary();
|
||
if (vers)
|
||
{
|
||
CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey);
|
||
if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8);
|
||
sscanf(buildver, "%d%c%d", &major, &letter, &minor);
|
||
CFRelease(vers);
|
||
}
|
||
else
|
||
helplog(ASL_LEVEL_NOTICE, "_CFCopySystemVersionDictionary failed");
|
||
|
||
if (!major) { major=10; letter = 'A'; minor = 190; helplog(ASL_LEVEL_NOTICE, "Note: No Major Build Version number found; assuming 10A190"); }
|
||
if (letter_out) *letter_out = letter;
|
||
if (minor_out) *minor_out = minor;
|
||
return(major);
|
||
}
|
||
|
||
static int UseOldRacoon()
|
||
{
|
||
static int g_oldRacoon = -1;
|
||
|
||
if (g_oldRacoon == -1)
|
||
{
|
||
char letter = 0;
|
||
int minor = 0;
|
||
g_oldRacoon = (MacOSXSystemBuildNumber(&letter, &minor) < 10);
|
||
debug("%s", g_oldRacoon ? "old" : "new");
|
||
}
|
||
|
||
return g_oldRacoon;
|
||
}
|
||
|
||
static int RacoonSignal()
|
||
{
|
||
return UseOldRacoon() ? SIGHUP : SIGUSR1;
|
||
}
|
||
|
||
static const char* GetRacoonConfigDir()
|
||
{
|
||
return UseOldRacoon() ? g_racoon_config_dir_old : g_racoon_config_dir;
|
||
}
|
||
|
||
static const char* GetOldRacoonConfigDir()
|
||
{
|
||
return UseOldRacoon() ? NULL : g_racoon_config_dir_old;
|
||
}
|
||
|
||
static const char racoon_config_file[] = "anonymous.conf";
|
||
static const char racoon_config_file_orig[] = "anonymous.conf.orig";
|
||
|
||
static const char configHeader[] = "# BackToMyMac\n";
|
||
|
||
static int IsFamiliarRacoonConfiguration(const char* racoon_config_path)
|
||
{
|
||
int fd = open(racoon_config_path, O_RDONLY);
|
||
debug("entry %s", racoon_config_path);
|
||
if (0 > fd)
|
||
{
|
||
helplog(ASL_LEVEL_NOTICE, "open \"%s\" failed: %s", racoon_config_path, strerror(errno));
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
char header[sizeof(configHeader)] = {0};
|
||
ssize_t bytesRead = read(fd, header, sizeof(header)-1);
|
||
close(fd);
|
||
if (bytesRead != sizeof(header)-1) return 0;
|
||
return (0 == memcmp(header, configHeader, sizeof(header)-1));
|
||
}
|
||
}
|
||
|
||
static void
|
||
revertAnonymousRacoonConfiguration(const char* dir)
|
||
{
|
||
if (!dir) return;
|
||
|
||
debug("entry %s", dir);
|
||
|
||
char racoon_config_path[64];
|
||
strlcpy(racoon_config_path, dir, sizeof(racoon_config_path));
|
||
strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path));
|
||
|
||
struct stat s;
|
||
int ret = stat(racoon_config_path, &s);
|
||
debug("stat(%s): %d errno=%d", racoon_config_path, ret, errno);
|
||
if (ret == 0)
|
||
{
|
||
if (IsFamiliarRacoonConfiguration(racoon_config_path))
|
||
{
|
||
helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path);
|
||
unlink(racoon_config_path);
|
||
}
|
||
else
|
||
{
|
||
helplog(ASL_LEVEL_NOTICE, "\"%s\" does not look familiar, leaving in place", racoon_config_path);
|
||
return;
|
||
}
|
||
}
|
||
else if (errno != ENOENT)
|
||
{
|
||
helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno));
|
||
return;
|
||
}
|
||
|
||
char racoon_config_path_orig[64];
|
||
strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig));
|
||
strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig));
|
||
|
||
ret = stat(racoon_config_path_orig, &s);
|
||
debug("stat(%s): %d errno=%d", racoon_config_path_orig, ret, errno);
|
||
if (ret == 0)
|
||
{
|
||
if (0 > rename(racoon_config_path_orig, racoon_config_path))
|
||
helplog(ASL_LEVEL_NOTICE, "rename \"%s\" \"%s\" failed: %s", racoon_config_path_orig, racoon_config_path, strerror(errno));
|
||
else
|
||
debug("reverted \"%s\" to \"%s\"", racoon_config_path_orig, racoon_config_path);
|
||
}
|
||
else if (errno != ENOENT)
|
||
{
|
||
helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path_orig, strerror(errno));
|
||
return;
|
||
}
|
||
}
|
||
|
||
static void
|
||
moveAsideAnonymousRacoonConfiguration(const char* dir)
|
||
{
|
||
if (!dir) return;
|
||
|
||
debug("entry %s", dir);
|
||
|
||
char racoon_config_path[64];
|
||
strlcpy(racoon_config_path, dir, sizeof(racoon_config_path));
|
||
strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path));
|
||
|
||
struct stat s;
|
||
int ret = stat(racoon_config_path, &s);
|
||
if (ret == 0)
|
||
{
|
||
if (IsFamiliarRacoonConfiguration(racoon_config_path))
|
||
{
|
||
helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path);
|
||
unlink(racoon_config_path);
|
||
}
|
||
else
|
||
{
|
||
char racoon_config_path_orig[64];
|
||
strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig));
|
||
strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig));
|
||
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
|
||
helplog(ASL_LEVEL_NOTICE, "rename \"%s\" to \"%s\" failed: %s", racoon_config_path, racoon_config_path_orig, strerror(errno));
|
||
else
|
||
debug("successfully renamed \"%s\" to \"%s\"", racoon_config_path, racoon_config_path_orig);
|
||
}
|
||
}
|
||
else if (errno != ENOENT)
|
||
{
|
||
helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno));
|
||
return;
|
||
}
|
||
}
|
||
|
||
static int
|
||
ensureExistenceOfRacoonConfigDir(const char* const racoon_config_dir)
|
||
{
|
||
struct stat s;
|
||
int ret = stat(racoon_config_dir, &s);
|
||
if (ret != 0)
|
||
{
|
||
if (errno != ENOENT)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "stat of \"%s\" failed (%d): %s",
|
||
racoon_config_dir, ret, strerror(errno));
|
||
return -1;
|
||
}
|
||
else
|
||
{
|
||
ret = mkdir(racoon_config_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||
if (ret != 0)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mkdir \"%s\" failed: %s",
|
||
racoon_config_dir, strerror(errno));
|
||
return -1;
|
||
}
|
||
else
|
||
helplog(ASL_LEVEL_INFO, "created directory \"%s\"", racoon_config_dir);
|
||
}
|
||
}
|
||
else if (!(s.st_mode & S_IFDIR))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "\"%s\" is not a directory!",
|
||
racoon_config_dir);
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
createAnonymousRacoonConfiguration(const char *fqdn)
|
||
{
|
||
static const char config1[] =
|
||
"remote anonymous {\n"
|
||
" exchange_mode aggressive;\n"
|
||
" doi ipsec_doi;\n"
|
||
" situation identity_only;\n"
|
||
" verify_identifier off;\n"
|
||
" generate_policy on;\n"
|
||
" shared_secret keychain_by_id \"";
|
||
static const char config2[] =
|
||
"\";\n"
|
||
" nonce_size 16;\n"
|
||
" lifetime time 15 min;\n"
|
||
" initial_contact on;\n"
|
||
" support_proxy on;\n"
|
||
" nat_traversal force;\n"
|
||
" proposal_check claim;\n"
|
||
" proposal {\n"
|
||
" encryption_algorithm aes;\n"
|
||
" hash_algorithm sha256;\n"
|
||
" authentication_method pre_shared_key;\n"
|
||
" dh_group 2;\n"
|
||
" lifetime time 15 min;\n"
|
||
" }\n"
|
||
" proposal {\n"
|
||
" encryption_algorithm aes;\n"
|
||
" hash_algorithm sha1;\n"
|
||
" authentication_method pre_shared_key;\n"
|
||
" dh_group 2;\n"
|
||
" lifetime time 15 min;\n"
|
||
" }\n"
|
||
"}\n\n"
|
||
"sainfo anonymous { \n"
|
||
" pfs_group 2;\n"
|
||
" lifetime time 10 min;\n"
|
||
" encryption_algorithm aes;\n"
|
||
" authentication_algorithm hmac_sha256,hmac_sha1;\n"
|
||
" compression_algorithm deflate;\n"
|
||
"}\n";
|
||
char tmp_config_path[64];
|
||
char racoon_config_path[64];
|
||
const char* const racoon_config_dir = GetRacoonConfigDir();
|
||
const char* const racoon_config_dir_old = GetOldRacoonConfigDir();
|
||
int fd = -1;
|
||
|
||
debug("entry");
|
||
|
||
if (0 > ensureExistenceOfRacoonConfigDir(racoon_config_dir))
|
||
return -1;
|
||
|
||
strlcpy(tmp_config_path, racoon_config_dir, sizeof(tmp_config_path));
|
||
strlcat(tmp_config_path, "tmp.XXXXXX", sizeof(tmp_config_path));
|
||
|
||
fd = mkstemp(tmp_config_path);
|
||
|
||
if (0 > fd)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s",
|
||
tmp_config_path, strerror(errno));
|
||
return -1;
|
||
}
|
||
write(fd, configHeader, sizeof(configHeader)-1);
|
||
write(fd, config1, sizeof(config1)-1);
|
||
write(fd, fqdn, strlen(fqdn));
|
||
write(fd, config2, sizeof(config2)-1);
|
||
close(fd);
|
||
|
||
strlcpy(racoon_config_path, racoon_config_dir, sizeof(racoon_config_path));
|
||
strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path));
|
||
|
||
moveAsideAnonymousRacoonConfiguration(racoon_config_dir_old);
|
||
moveAsideAnonymousRacoonConfiguration(racoon_config_dir);
|
||
|
||
if (0 > rename(tmp_config_path, racoon_config_path))
|
||
{
|
||
unlink(tmp_config_path);
|
||
helplog(ASL_LEVEL_ERR, "rename \"%s\" \"%s\" failed: %s",
|
||
tmp_config_path, racoon_config_path, strerror(errno));
|
||
revertAnonymousRacoonConfiguration(racoon_config_dir_old);
|
||
revertAnonymousRacoonConfiguration(racoon_config_dir);
|
||
return -1;
|
||
}
|
||
|
||
debug("successfully renamed \"%s\" \"%s\"", tmp_config_path, racoon_config_path);
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
notifyRacoon(void)
|
||
{
|
||
debug("entry");
|
||
static const char racoon_pid_path[] = "/var/run/racoon.pid";
|
||
char buf[] = "18446744073709551615"; /* largest 64-bit integer */
|
||
char *p = NULL;
|
||
ssize_t n = 0;
|
||
unsigned long m = 0;
|
||
int fd = open(racoon_pid_path, O_RDONLY);
|
||
|
||
if (0 > fd)
|
||
{
|
||
debug("open \"%s\" failed, and that's OK: %s", racoon_pid_path,
|
||
strerror(errno));
|
||
return kmDNSHelperRacoonNotificationFailed;
|
||
}
|
||
n = read(fd, buf, sizeof(buf)-1);
|
||
close(fd);
|
||
if (1 > n)
|
||
{
|
||
debug("read of \"%s\" failed: %s", racoon_pid_path,
|
||
n == 0 ? "empty file" : strerror(errno));
|
||
return kmDNSHelperRacoonNotificationFailed;
|
||
}
|
||
buf[n] = '\0';
|
||
m = strtoul(buf, &p, 10);
|
||
if (*p != '\0' && !isspace(*p))
|
||
{
|
||
debug("invalid PID \"%s\" (around '%c')", buf, *p);
|
||
return kmDNSHelperRacoonNotificationFailed;
|
||
}
|
||
if (2 > m)
|
||
{
|
||
debug("refusing to kill PID %lu", m);
|
||
return kmDNSHelperRacoonNotificationFailed;
|
||
}
|
||
if (0 != kill(m, RacoonSignal()))
|
||
{
|
||
debug("Could not signal racoon (%lu): %s", m, strerror(errno));
|
||
return kmDNSHelperRacoonNotificationFailed;
|
||
}
|
||
debug("Sent racoon (%lu) signal %d", m, RacoonSignal());
|
||
return 0;
|
||
}
|
||
|
||
static void
|
||
closefds(int from)
|
||
{
|
||
int fd = 0;
|
||
struct dirent entry, *entryp = NULL;
|
||
DIR *dirp = opendir("/dev/fd");
|
||
|
||
if (dirp == NULL)
|
||
{
|
||
/* fall back to the erroneous getdtablesize method */
|
||
for (fd = from; fd < getdtablesize(); ++fd)
|
||
close(fd);
|
||
return;
|
||
}
|
||
while (0 == readdir_r(dirp, &entry, &entryp) && NULL != entryp)
|
||
{
|
||
fd = atoi(entryp->d_name);
|
||
if (fd >= from && fd != dirfd(dirp))
|
||
close(fd);
|
||
}
|
||
closedir(dirp);
|
||
}
|
||
|
||
static int
|
||
startRacoonOld(void)
|
||
{
|
||
debug("entry");
|
||
char * const racoon_args[] = { "/usr/sbin/racoon", "-e", NULL };
|
||
ssize_t n = 0;
|
||
pid_t pid = 0;
|
||
int status = 0;
|
||
|
||
if (0 == (pid = fork()))
|
||
{
|
||
closefds(0);
|
||
execve(racoon_args[0], racoon_args, NULL);
|
||
helplog(ASL_LEVEL_ERR, "execve of \"%s\" failed: %s",
|
||
racoon_args[0], strerror(errno));
|
||
exit(2);
|
||
}
|
||
helplog(ASL_LEVEL_NOTICE, "racoon (pid=%lu) started",
|
||
(unsigned long)pid);
|
||
n = waitpid(pid, &status, 0);
|
||
if (-1 == n)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Unexpected waitpid failure: %s",
|
||
strerror(errno));
|
||
return kmDNSHelperRacoonStartFailed;
|
||
}
|
||
else if (pid != n)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Unexpected waitpid return value %d",
|
||
(int)n);
|
||
return kmDNSHelperRacoonStartFailed;
|
||
}
|
||
else if (WIFSIGNALED(status))
|
||
{
|
||
helplog(ASL_LEVEL_ERR,
|
||
"racoon (pid=%lu) terminated due to signal %d",
|
||
(unsigned long)pid, WTERMSIG(status));
|
||
return kmDNSHelperRacoonStartFailed;
|
||
}
|
||
else if (WIFSTOPPED(status))
|
||
{
|
||
helplog(ASL_LEVEL_ERR,
|
||
"racoon (pid=%lu) has stopped due to signal %d",
|
||
(unsigned long)pid, WSTOPSIG(status));
|
||
return kmDNSHelperRacoonStartFailed;
|
||
}
|
||
else if (0 != WEXITSTATUS(status))
|
||
{
|
||
helplog(ASL_LEVEL_ERR,
|
||
"racoon (pid=%lu) exited with status %d",
|
||
(unsigned long)pid, WEXITSTATUS(status));
|
||
return kmDNSHelperRacoonStartFailed;
|
||
}
|
||
debug("racoon (pid=%lu) daemonized normally", (unsigned long)pid);
|
||
return 0;
|
||
}
|
||
|
||
// constant and structure for the racoon control socket
|
||
#define VPNCTL_CMD_PING 0x0004
|
||
typedef struct vpnctl_hdr_struct
|
||
{
|
||
u_int16_t msg_type;
|
||
u_int16_t flags;
|
||
u_int32_t cookie;
|
||
u_int32_t reserved;
|
||
u_int16_t result;
|
||
u_int16_t len;
|
||
} vpnctl_hdr;
|
||
|
||
static int
|
||
startRacoon(void)
|
||
{
|
||
debug("entry");
|
||
int fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
||
if (0 > fd)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Could not create endpoint for racoon control socket: %d %s",
|
||
errno, strerror(errno));
|
||
return kmDNSHelperRacoonStartFailed;
|
||
}
|
||
|
||
struct sockaddr_un saddr;
|
||
memset(&saddr, 0, sizeof(saddr));
|
||
saddr.sun_family = AF_UNIX;
|
||
saddr.sun_len = sizeof(saddr);
|
||
static const char racoon_control_sock_path[] = "/var/run/vpncontrol.sock";
|
||
strcpy(saddr.sun_path, racoon_control_sock_path);
|
||
int result = connect(fd, (struct sockaddr*) &saddr, saddr.sun_len);
|
||
if (0 > result)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Could not connect racoon control socket %s: %d %s",
|
||
racoon_control_sock_path, errno, strerror(errno));
|
||
return kmDNSHelperRacoonStartFailed;
|
||
}
|
||
|
||
u_int32_t btmm_cookie = 0x4d4d5442;
|
||
vpnctl_hdr h = { htons(VPNCTL_CMD_PING), 0, btmm_cookie, 0, 0, 0 };
|
||
size_t bytes = 0;
|
||
ssize_t ret = 0;
|
||
|
||
while (bytes < sizeof(vpnctl_hdr))
|
||
{
|
||
ret = write(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes);
|
||
if (ret == -1)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Could not write to racoon control socket: %d %s",
|
||
errno, strerror(errno));
|
||
return kmDNSHelperRacoonStartFailed;
|
||
}
|
||
bytes += ret;
|
||
}
|
||
|
||
int nfds = fd + 1;
|
||
fd_set fds;
|
||
int counter = 0;
|
||
struct timeval tv;
|
||
bytes = 0;
|
||
h.cookie = 0;
|
||
|
||
for (counter = 0; counter < 100; counter++)
|
||
{
|
||
FD_ZERO(&fds);
|
||
FD_SET(fd, &fds);
|
||
tv = (struct timeval){ 0, 10000 }; // 10 milliseconds * 100 iterations = 1 second max wait time
|
||
|
||
result = select(nfds, &fds, (fd_set*)NULL, (fd_set*)NULL, &tv);
|
||
if (result > 0)
|
||
{
|
||
if (FD_ISSET(fd, &fds))
|
||
{
|
||
ret = read(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes);
|
||
|
||
if (ret == -1)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Could not read from racoon control socket: %d %s",
|
||
strerror(errno));
|
||
break;
|
||
}
|
||
bytes += ret;
|
||
if (bytes >= sizeof(vpnctl_hdr)) break;
|
||
}
|
||
else
|
||
{
|
||
debug("select returned but fd_isset not on expected fd\n");
|
||
}
|
||
}
|
||
else if (result < 0)
|
||
{
|
||
debug("select returned %d errno %d %s\n", result, errno, strerror(errno));
|
||
if (errno != EINTR) break;
|
||
}
|
||
}
|
||
|
||
close(fd);
|
||
|
||
if (bytes < sizeof(vpnctl_hdr) || h.cookie != btmm_cookie) return kmDNSHelperRacoonStartFailed;
|
||
|
||
debug("racoon started");
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
kickRacoon(void)
|
||
{
|
||
if ( 0 == notifyRacoon() )
|
||
return 0;
|
||
return UseOldRacoon() ? startRacoonOld() : startRacoon();
|
||
}
|
||
|
||
#endif /* ndef MDNS_NO_IPSEC */
|
||
|
||
int
|
||
do_mDNSConfigureServer(__unused mach_port_t port, int updown, const char *fqdn, audit_token_t token)
|
||
{
|
||
#ifndef MDNS_NO_IPSEC
|
||
debug("entry");
|
||
if (!authorized(&token)) goto fin;
|
||
|
||
switch ((enum mDNSUpDown)updown)
|
||
{
|
||
case kmDNSUp:
|
||
if (0 != createAnonymousRacoonConfiguration(fqdn)) goto fin;
|
||
break;
|
||
case kmDNSDown:
|
||
revertAnonymousRacoonConfiguration(GetOldRacoonConfigDir());
|
||
revertAnonymousRacoonConfiguration(GetRacoonConfigDir());
|
||
break;
|
||
default:
|
||
goto fin;
|
||
}
|
||
|
||
if (0 != kickRacoon())
|
||
goto fin;
|
||
debug("succeeded");
|
||
|
||
fin:
|
||
#else
|
||
(void)port; (void)updown; (void)fqdn; (void)token;
|
||
#endif
|
||
update_idle_timer();
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
#ifndef MDNS_NO_IPSEC
|
||
|
||
static unsigned int routeSeq = 1;
|
||
|
||
static int
|
||
setupTunnelRoute(v6addr_t local, v6addr_t remote)
|
||
{
|
||
struct
|
||
{
|
||
struct rt_msghdr hdr;
|
||
struct sockaddr_in6 dst;
|
||
struct sockaddr_in6 gtwy;
|
||
} msg;
|
||
int err = 0;
|
||
int s = -1;
|
||
|
||
if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET)))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s",
|
||
strerror(errno));
|
||
err = kmDNSHelperRoutingSocketCreationFailed;
|
||
goto fin;
|
||
}
|
||
memset(&msg, 0, sizeof(msg));
|
||
msg.hdr.rtm_msglen = sizeof(msg);
|
||
msg.hdr.rtm_type = RTM_ADD;
|
||
/* The following flags are set by `route add -inet6 -host ...` */
|
||
msg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC;
|
||
msg.hdr.rtm_version = RTM_VERSION;
|
||
msg.hdr.rtm_seq = routeSeq++;
|
||
msg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
|
||
msg.hdr.rtm_inits = RTV_MTU;
|
||
msg.hdr.rtm_rmx.rmx_mtu = 1280;
|
||
|
||
msg.dst.sin6_len = sizeof(msg.dst);
|
||
msg.dst.sin6_family = AF_INET6;
|
||
memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr));
|
||
|
||
msg.gtwy.sin6_len = sizeof(msg.gtwy);
|
||
msg.gtwy.sin6_family = AF_INET6;
|
||
memcpy(&msg.gtwy.sin6_addr, local, sizeof(msg.gtwy.sin6_addr));
|
||
|
||
/* send message, ignore error when route already exists */
|
||
if (0 > write(s, &msg, msg.hdr.rtm_msglen))
|
||
{
|
||
int errno_ = errno;
|
||
|
||
debug("write to routing socket failed: %s", strerror(errno_));
|
||
if (EEXIST != errno_)
|
||
{
|
||
err = kmDNSHelperRouteAdditionFailed;
|
||
goto fin;
|
||
}
|
||
}
|
||
|
||
fin:
|
||
if (0 <= s)
|
||
close(s);
|
||
return err;
|
||
}
|
||
|
||
static int
|
||
teardownTunnelRoute(v6addr_t remote)
|
||
{
|
||
struct
|
||
{
|
||
struct rt_msghdr hdr;
|
||
struct sockaddr_in6 dst;
|
||
} msg;
|
||
int err = 0;
|
||
int s = -1;
|
||
|
||
if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET)))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s",
|
||
strerror(errno));
|
||
err = kmDNSHelperRoutingSocketCreationFailed;
|
||
goto fin;
|
||
}
|
||
memset(&msg, 0, sizeof(msg));
|
||
|
||
msg.hdr.rtm_msglen = sizeof(msg);
|
||
msg.hdr.rtm_type = RTM_DELETE;
|
||
msg.hdr.rtm_version = RTM_VERSION;
|
||
msg.hdr.rtm_seq = routeSeq++;
|
||
msg.hdr.rtm_addrs = RTA_DST;
|
||
|
||
msg.dst.sin6_len = sizeof(msg.dst);
|
||
msg.dst.sin6_family = AF_INET6;
|
||
memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr));
|
||
if (0 > write(s, &msg, msg.hdr.rtm_msglen))
|
||
{
|
||
int errno_ = errno;
|
||
|
||
debug("write to routing socket failed: %s", strerror(errno_));
|
||
if (ESRCH != errno_)
|
||
{
|
||
err = kmDNSHelperRouteDeletionFailed;
|
||
goto fin;
|
||
}
|
||
}
|
||
|
||
fin:
|
||
if (0 <= s)
|
||
close(s);
|
||
return err;
|
||
}
|
||
|
||
static int
|
||
v4addr_to_string(v4addr_t addr, char *buf, size_t buflen)
|
||
{
|
||
if (NULL == inet_ntop(AF_INET, addr, buf, buflen))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s",
|
||
strerror(errno));
|
||
return kmDNSHelperInvalidNetworkAddress;
|
||
}
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
v6addr_to_string(v6addr_t addr, char *buf, size_t buflen)
|
||
{
|
||
if (NULL == inet_ntop(AF_INET6, addr, buf, buflen))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s",
|
||
strerror(errno));
|
||
return kmDNSHelperInvalidNetworkAddress;
|
||
}
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
/* Caller owns object returned in `policy' */
|
||
static int
|
||
generateTunnelPolicy(mDNSTunnelPolicyWhich which, mDNSTunnelType type, int in,
|
||
v4addr_t src, uint16_t src_port,
|
||
v4addr_t dst, uint16_t dst_port,
|
||
v6addr_t src6, v6addr_t dst6,
|
||
ipsec_policy_t *policy, size_t *len)
|
||
{
|
||
char srcs[INET_ADDRSTRLEN], dsts[INET_ADDRSTRLEN];
|
||
char srcs6[INET6_ADDRSTRLEN], dsts6[INET6_ADDRSTRLEN];
|
||
char buf[512];
|
||
char *inOut = in ? "in" : "out";
|
||
ssize_t n = 0;
|
||
int err = 0;
|
||
|
||
*policy = NULL;
|
||
*len = 0;
|
||
|
||
switch (which)
|
||
{
|
||
case kmDNSTunnelPolicySetup:
|
||
if (type == kmDNSIPv6IPv4Tunnel)
|
||
{
|
||
if (0 != (err = v4addr_to_string(src, srcs, sizeof(srcs))))
|
||
goto fin;
|
||
if (0 != (err = v4addr_to_string(dst, dsts, sizeof(dsts))))
|
||
goto fin;
|
||
n = snprintf(buf, sizeof(buf),
|
||
"%s ipsec esp/tunnel/%s[%u]-%s[%u]/require",
|
||
inOut, srcs, src_port, dsts, dst_port);
|
||
}
|
||
else if (type == kmDNSIPv6IPv6Tunnel)
|
||
{
|
||
if (0 != (err = v6addr_to_string(src6, srcs6, sizeof(srcs6))))
|
||
goto fin;
|
||
if (0 != (err = v6addr_to_string(dst6, dsts6, sizeof(dsts6))))
|
||
goto fin;
|
||
n = snprintf(buf, sizeof(buf),
|
||
"%s ipsec esp/tunnel/%s-%s/require",
|
||
inOut, srcs6, dsts6);
|
||
}
|
||
break;
|
||
case kmDNSTunnelPolicyTeardown:
|
||
n = strlcpy(buf, inOut, sizeof(buf));
|
||
break;
|
||
case kmDNSTunnelPolicyGenerate:
|
||
n = snprintf(buf, sizeof(buf), "%s generate", inOut);
|
||
break;
|
||
default:
|
||
err = kmDNSHelperIPsecPolicyCreationFailed;
|
||
goto fin;
|
||
}
|
||
|
||
if (n >= (int)sizeof(buf))
|
||
{
|
||
err = kmDNSHelperResultTooLarge;
|
||
goto fin;
|
||
}
|
||
|
||
debug("policy=\"%s\"", buf);
|
||
if (NULL == (*policy = (ipsec_policy_t)ipsec_set_policy(buf, n)))
|
||
{
|
||
helplog(ASL_LEVEL_ERR,
|
||
"Could not create IPsec policy from \"%s\"", buf);
|
||
err = kmDNSHelperIPsecPolicyCreationFailed;
|
||
goto fin;
|
||
}
|
||
*len = ((ipsec_policy_t)(*policy))->sadb_x_policy_len * 8;
|
||
|
||
fin:
|
||
return err;
|
||
}
|
||
|
||
static int
|
||
sendPolicy(int s, int setup,
|
||
struct sockaddr *src, uint8_t src_bits,
|
||
struct sockaddr *dst, uint8_t dst_bits,
|
||
ipsec_policy_t policy, size_t len)
|
||
{
|
||
static unsigned int policySeq = 0;
|
||
int err = 0;
|
||
|
||
debug("entry, setup=%d", setup);
|
||
if (setup)
|
||
err = pfkey_send_spdadd(s, src, src_bits, dst, dst_bits, -1,
|
||
(char *)policy, len, policySeq++);
|
||
else
|
||
err = pfkey_send_spddelete(s, src, src_bits, dst, dst_bits, -1,
|
||
(char *)policy, len, policySeq++);
|
||
if (0 > err)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Could not set IPsec policy: %s",
|
||
ipsec_strerror());
|
||
err = kmDNSHelperIPsecPolicySetFailed;
|
||
goto fin;
|
||
}
|
||
else
|
||
err = 0;
|
||
debug("succeeded");
|
||
|
||
fin:
|
||
return err;
|
||
}
|
||
|
||
static int
|
||
removeSA(int s, struct sockaddr *src, struct sockaddr *dst)
|
||
{
|
||
int err = 0;
|
||
|
||
debug("entry");
|
||
err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, src, dst);
|
||
if (0 > err)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror());
|
||
err = kmDNSHelperIPsecRemoveSAFailed;
|
||
goto fin;
|
||
}
|
||
err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, dst, src);
|
||
if (0 > err)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror());
|
||
err = kmDNSHelperIPsecRemoveSAFailed;
|
||
goto fin;
|
||
}
|
||
else
|
||
err = 0;
|
||
|
||
debug("succeeded");
|
||
|
||
fin:
|
||
return err;
|
||
}
|
||
|
||
static int
|
||
doTunnelPolicy(mDNSTunnelPolicyWhich which, mDNSTunnelType type,
|
||
v6addr_t loc_inner, uint8_t loc_bits,
|
||
v4addr_t loc_outer, uint16_t loc_port,
|
||
v6addr_t rmt_inner, uint8_t rmt_bits,
|
||
v4addr_t rmt_outer, uint16_t rmt_port,
|
||
v6addr_t loc_outer6, v6addr_t rmt_outer6)
|
||
{
|
||
struct sockaddr_in6 sin6_loc;
|
||
struct sockaddr_in6 sin6_rmt;
|
||
ipsec_policy_t policy = NULL;
|
||
size_t len = 0;
|
||
int s = -1;
|
||
int err = 0;
|
||
|
||
debug("entry");
|
||
if (0 > (s = pfkey_open()))
|
||
{
|
||
helplog(ASL_LEVEL_ERR,
|
||
"Could not create IPsec policy socket: %s",
|
||
ipsec_strerror());
|
||
err = kmDNSHelperIPsecPolicySocketCreationFailed;
|
||
goto fin;
|
||
}
|
||
|
||
memset(&sin6_loc, 0, sizeof(sin6_loc));
|
||
sin6_loc.sin6_len = sizeof(sin6_loc);
|
||
sin6_loc.sin6_family = AF_INET6;
|
||
sin6_loc.sin6_port = htons(0);
|
||
memcpy(&sin6_loc.sin6_addr, loc_inner, sizeof(sin6_loc.sin6_addr));
|
||
|
||
memset(&sin6_rmt, 0, sizeof(sin6_rmt));
|
||
sin6_rmt.sin6_len = sizeof(sin6_rmt);
|
||
sin6_rmt.sin6_family = AF_INET6;
|
||
sin6_rmt.sin6_port = htons(0);
|
||
memcpy(&sin6_rmt.sin6_addr, rmt_inner, sizeof(sin6_rmt.sin6_addr));
|
||
|
||
int setup = which != kmDNSTunnelPolicyTeardown;
|
||
|
||
if (0 != (err = generateTunnelPolicy(which, type, 1,
|
||
rmt_outer, rmt_port,
|
||
loc_outer, loc_port,
|
||
rmt_outer6, loc_outer6,
|
||
&policy, &len)))
|
||
goto fin;
|
||
if (0 != (err = sendPolicy(s, setup,
|
||
(struct sockaddr *)&sin6_rmt, rmt_bits,
|
||
(struct sockaddr *)&sin6_loc, loc_bits,
|
||
policy, len)))
|
||
goto fin;
|
||
if (NULL != policy)
|
||
{
|
||
free(policy);
|
||
policy = NULL;
|
||
}
|
||
if (0 != (err = generateTunnelPolicy(which, type, 0,
|
||
loc_outer, loc_port,
|
||
rmt_outer, rmt_port,
|
||
loc_outer6, rmt_outer6,
|
||
&policy, &len)))
|
||
goto fin;
|
||
if (0 != (err = sendPolicy(s, setup,
|
||
(struct sockaddr *)&sin6_loc, loc_bits,
|
||
(struct sockaddr *)&sin6_rmt, rmt_bits,
|
||
policy, len)))
|
||
goto fin;
|
||
|
||
if (which == kmDNSTunnelPolicyTeardown)
|
||
{
|
||
if (rmt_port) // Outer tunnel is IPv4
|
||
{
|
||
if (loc_outer && rmt_outer)
|
||
{
|
||
struct sockaddr_in sin_loc;
|
||
struct sockaddr_in sin_rmt;
|
||
memset(&sin_loc, 0, sizeof(sin_loc));
|
||
sin_loc.sin_len = sizeof(sin_loc);
|
||
sin_loc.sin_family = AF_INET;
|
||
memcpy(&sin_loc.sin_addr, loc_outer, sizeof(sin_loc.sin_addr));
|
||
|
||
memset(&sin_rmt, 0, sizeof(sin_rmt));
|
||
sin_rmt.sin_len = sizeof(sin_rmt);
|
||
sin_rmt.sin_family = AF_INET;
|
||
memcpy(&sin_rmt.sin_addr, rmt_outer, sizeof(sin_rmt.sin_addr));
|
||
if (0 != (err = removeSA(s, (struct sockaddr *)&sin_loc, (struct sockaddr *)&sin_rmt)))
|
||
goto fin;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (loc_outer6 && rmt_outer6)
|
||
{
|
||
struct sockaddr_in6 sin6_lo;
|
||
struct sockaddr_in6 sin6_rm;
|
||
|
||
memset(&sin6_lo, 0, sizeof(sin6_lo));
|
||
sin6_lo.sin6_len = sizeof(sin6_lo);
|
||
sin6_lo.sin6_family = AF_INET6;
|
||
memcpy(&sin6_lo.sin6_addr, loc_outer6, sizeof(sin6_lo.sin6_addr));
|
||
|
||
memset(&sin6_rm, 0, sizeof(sin6_rm));
|
||
sin6_rm.sin6_len = sizeof(sin6_rm);
|
||
sin6_rm.sin6_family = AF_INET6;
|
||
memcpy(&sin6_rm.sin6_addr, rmt_outer6, sizeof(sin6_rm.sin6_addr));
|
||
if (0 != (err = removeSA(s, (struct sockaddr *)&sin6_lo, (struct sockaddr *)&sin6_rm)))
|
||
goto fin;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
debug("succeeded");
|
||
|
||
fin:
|
||
if (s >= 0)
|
||
pfkey_close(s);
|
||
if (NULL != policy)
|
||
free(policy);
|
||
return err;
|
||
}
|
||
|
||
#endif /* ndef MDNS_NO_IPSEC */
|
||
|
||
int
|
||
do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete,
|
||
v6addr_t loc_inner, v6addr_t loc_outer6, uint16_t loc_port,
|
||
v6addr_t rmt_inner, v6addr_t rmt_outer6, uint16_t rmt_port,
|
||
const char *id, int *err, audit_token_t token)
|
||
{
|
||
#ifndef MDNS_NO_IPSEC
|
||
static const char config[] =
|
||
"%s"
|
||
"remote %s [%u] {\n"
|
||
" disconnect_on_idle idle_timeout 600 idle_direction idle_outbound;\n"
|
||
" exchange_mode aggressive;\n"
|
||
" doi ipsec_doi;\n"
|
||
" situation identity_only;\n"
|
||
" verify_identifier off;\n"
|
||
" generate_policy on;\n"
|
||
" my_identifier user_fqdn \"%s\";\n"
|
||
" shared_secret keychain \"%s\";\n"
|
||
" nonce_size 16;\n"
|
||
" lifetime time 15 min;\n"
|
||
" initial_contact on;\n"
|
||
" support_proxy on;\n"
|
||
" nat_traversal force;\n"
|
||
" proposal_check claim;\n"
|
||
" proposal {\n"
|
||
" encryption_algorithm aes;\n"
|
||
" hash_algorithm sha256;\n"
|
||
" authentication_method pre_shared_key;\n"
|
||
" dh_group 2;\n"
|
||
" lifetime time 15 min;\n"
|
||
" }\n"
|
||
" proposal {\n"
|
||
" encryption_algorithm aes;\n"
|
||
" hash_algorithm sha1;\n"
|
||
" authentication_method pre_shared_key;\n"
|
||
" dh_group 2;\n"
|
||
" lifetime time 15 min;\n"
|
||
" }\n"
|
||
"}\n\n"
|
||
"sainfo address %s any address %s any {\n"
|
||
" pfs_group 2;\n"
|
||
" lifetime time 10 min;\n"
|
||
" encryption_algorithm aes;\n"
|
||
" authentication_algorithm hmac_sha256,hmac_sha1;\n"
|
||
" compression_algorithm deflate;\n"
|
||
"}\n\n"
|
||
"sainfo address %s any address %s any {\n"
|
||
" pfs_group 2;\n"
|
||
" lifetime time 10 min;\n"
|
||
" encryption_algorithm aes;\n"
|
||
" authentication_algorithm hmac_sha256,hmac_sha1;\n"
|
||
" compression_algorithm deflate;\n"
|
||
"}\n";
|
||
char path[PATH_MAX] = "";
|
||
char li[INET6_ADDRSTRLEN], lo[INET_ADDRSTRLEN], lo6[INET6_ADDRSTRLEN],
|
||
ri[INET6_ADDRSTRLEN], ro[INET_ADDRSTRLEN], ro6[INET6_ADDRSTRLEN];
|
||
FILE *fp = NULL;
|
||
int fd = -1;
|
||
char tmp_path[PATH_MAX] = "";
|
||
v4addr_t loc_outer, rmt_outer;
|
||
|
||
debug("entry");
|
||
*err = 0;
|
||
if (!authorized(&token))
|
||
{
|
||
*err = kmDNSHelperNotAuthorized;
|
||
goto fin;
|
||
}
|
||
switch ((enum mDNSAutoTunnelSetKeysReplaceDelete)replacedelete)
|
||
{
|
||
case kmDNSAutoTunnelSetKeysReplace:
|
||
case kmDNSAutoTunnelSetKeysDelete:
|
||
break;
|
||
default:
|
||
*err = kmDNSHelperInvalidTunnelSetKeysOperation;
|
||
goto fin;
|
||
}
|
||
|
||
if (0 != (*err = v6addr_to_string(loc_inner, li, sizeof(li))))
|
||
goto fin;
|
||
if (0 != (*err = v6addr_to_string(rmt_inner, ri, sizeof(ri))))
|
||
goto fin;
|
||
|
||
debug("loc_inner=%s rmt_inner=%s", li, ri);
|
||
if (!rmt_port)
|
||
{
|
||
loc_outer[0] = loc_outer[1] = loc_outer[2] = loc_outer[3] = 0;
|
||
rmt_outer[0] = rmt_outer[1] = rmt_outer[2] = rmt_outer[3] = 0;
|
||
|
||
if (0 != (*err = v6addr_to_string(loc_outer6, lo6, sizeof(lo6))))
|
||
goto fin;
|
||
if (0 != (*err = v6addr_to_string(rmt_outer6, ro6, sizeof(ro6))))
|
||
goto fin;
|
||
debug("IPv6 outer tunnel: loc_outer6=%s rmt_outer6=%s", lo6, ro6);
|
||
if ((int)sizeof(path) <= snprintf(path, sizeof(path),
|
||
"%s%s.conf", GetRacoonConfigDir(), ro6))
|
||
{
|
||
*err = kmDNSHelperResultTooLarge;
|
||
goto fin;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
loc_outer[0] = loc_outer6[0];
|
||
loc_outer[1] = loc_outer6[1];
|
||
loc_outer[2] = loc_outer6[2];
|
||
loc_outer[3] = loc_outer6[3];
|
||
|
||
rmt_outer[0] = rmt_outer6[0];
|
||
rmt_outer[1] = rmt_outer6[1];
|
||
rmt_outer[2] = rmt_outer6[2];
|
||
rmt_outer[3] = rmt_outer6[3];
|
||
|
||
if (0 != (*err = v4addr_to_string(loc_outer, lo, sizeof(lo))))
|
||
goto fin;
|
||
if (0 != (*err = v4addr_to_string(rmt_outer, ro, sizeof(ro))))
|
||
goto fin;
|
||
debug("IPv4 outer tunnel: loc_outer=%s loc_port=%u rmt_outer=%s rmt_port=%u",
|
||
lo, loc_port, ro, rmt_port);
|
||
|
||
if ((int)sizeof(path) <= snprintf(path, sizeof(path),
|
||
"%s%s.%u.conf", GetRacoonConfigDir(), ro,
|
||
rmt_port))
|
||
{
|
||
*err = kmDNSHelperResultTooLarge;
|
||
goto fin;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (kmDNSAutoTunnelSetKeysReplace == replacedelete)
|
||
{
|
||
if (0 > ensureExistenceOfRacoonConfigDir(GetRacoonConfigDir()))
|
||
{
|
||
*err = kmDNSHelperRacoonConfigCreationFailed;
|
||
goto fin;
|
||
}
|
||
if ((int)sizeof(tmp_path) <=
|
||
snprintf(tmp_path, sizeof(tmp_path), "%s.XXXXXX", path))
|
||
{
|
||
*err = kmDNSHelperResultTooLarge;
|
||
goto fin;
|
||
}
|
||
if (0 > (fd = mkstemp(tmp_path)))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s",
|
||
tmp_path, strerror(errno));
|
||
*err = kmDNSHelperRacoonConfigCreationFailed;
|
||
goto fin;
|
||
}
|
||
if (NULL == (fp = fdopen(fd, "w")))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "fdopen: %s",
|
||
strerror(errno));
|
||
*err = kmDNSHelperRacoonConfigCreationFailed;
|
||
goto fin;
|
||
}
|
||
fd = -1;
|
||
fprintf(fp, config, configHeader, (!rmt_port ? ro6 : ro), rmt_port, id, id, ri, li, li, ri);
|
||
fclose(fp);
|
||
fp = NULL;
|
||
if (0 > rename(tmp_path, path))
|
||
{
|
||
helplog(ASL_LEVEL_ERR,
|
||
"rename \"%s\" \"%s\" failed: %s",
|
||
tmp_path, path, strerror(errno));
|
||
*err = kmDNSHelperRacoonConfigCreationFailed;
|
||
goto fin;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (0 != unlink(path))
|
||
debug("unlink \"%s\" failed: %s", path,
|
||
strerror(errno));
|
||
}
|
||
|
||
if (0 != (*err = doTunnelPolicy(kmDNSTunnelPolicyTeardown, kmDNSNoTunnel,
|
||
loc_inner, kWholeV6Mask, loc_outer, loc_port,
|
||
rmt_inner, kWholeV6Mask, rmt_outer, rmt_port, loc_outer6, rmt_outer6)))
|
||
goto fin;
|
||
if (kmDNSAutoTunnelSetKeysReplace == replacedelete &&
|
||
0 != (*err = doTunnelPolicy(kmDNSTunnelPolicySetup, (!rmt_port ? kmDNSIPv6IPv6Tunnel : kmDNSIPv6IPv4Tunnel),
|
||
loc_inner, kWholeV6Mask, loc_outer, loc_port,
|
||
rmt_inner, kWholeV6Mask, rmt_outer, rmt_port, loc_outer6, rmt_outer6)))
|
||
goto fin;
|
||
|
||
if (0 != (*err = teardownTunnelRoute(rmt_inner)))
|
||
goto fin;
|
||
if (kmDNSAutoTunnelSetKeysReplace == replacedelete &&
|
||
0 != (*err = setupTunnelRoute(loc_inner, rmt_inner)))
|
||
goto fin;
|
||
|
||
if (kmDNSAutoTunnelSetKeysReplace == replacedelete &&
|
||
0 != (*err = kickRacoon()))
|
||
goto fin;
|
||
|
||
debug("succeeded");
|
||
|
||
fin:
|
||
if (NULL != fp)
|
||
fclose(fp);
|
||
if (0 <= fd)
|
||
close(fd);
|
||
unlink(tmp_path);
|
||
#else
|
||
(void)replacedelete; (void)loc_inner; (void)loc_outer6; (void)loc_port; (void)rmt_inner;
|
||
(void)rmt_outer6; (void)rmt_port; (void)id; (void)token;
|
||
|
||
*err = kmDNSHelperIPsecDisabled;
|
||
#endif /* MDNS_NO_IPSEC */
|
||
update_idle_timer();
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
kern_return_t
|
||
do_mDNSSendWakeupPacket(__unused mach_port_t port, unsigned ifid, const char *eth_addr, const char *ip_addr, int iteration, audit_token_t token)
|
||
{
|
||
int bpf_fd, i, j;
|
||
struct ifreq ifr;
|
||
char ifname[IFNAMSIZ];
|
||
char packet[512];
|
||
char *ptr = packet;
|
||
char bpf_device[12];
|
||
struct ether_addr *ea;
|
||
(void) ip_addr; // unused
|
||
(void) iteration; // unused
|
||
(void) token; // unused
|
||
|
||
if (if_indextoname(ifid, ifname) == NULL)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid interface index %u", ifid);
|
||
return errno;
|
||
}
|
||
|
||
ea = ether_aton(eth_addr);
|
||
if (ea == NULL)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid ethernet address %s", eth_addr);
|
||
return errno;
|
||
}
|
||
|
||
for (i = 0; i < 100; i++)
|
||
{
|
||
snprintf(bpf_device, sizeof(bpf_device), "/dev/bpf%d", i);
|
||
bpf_fd = open(bpf_device, O_RDWR, 0);
|
||
if (bpf_fd == -1)
|
||
continue;
|
||
else break;
|
||
}
|
||
|
||
if (bpf_fd == -1)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket cannot find a bpf device");
|
||
return ENXIO;
|
||
}
|
||
|
||
memset(&ifr, 0, sizeof(ifr));
|
||
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
||
|
||
if (ioctl(bpf_fd, BIOCSETIF, (char *)&ifr) < 0)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket BIOCSETIF failed %s", strerror(errno));
|
||
return errno;
|
||
}
|
||
|
||
// 0x00 Destination address
|
||
for (i=0; i<6; i++) *ptr++ = ea->octet[i];
|
||
|
||
// 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us)
|
||
for (i=0; i<6; i++) *ptr++ = 0;
|
||
|
||
// 0x0C Ethertype (0x0842)
|
||
*ptr++ = 0x08;
|
||
*ptr++ = 0x42;
|
||
|
||
// 0x0E Wakeup sync sequence
|
||
for (i=0; i<6; i++) *ptr++ = 0xFF;
|
||
|
||
// 0x14 Wakeup data
|
||
for (j=0; j<16; j++) for (i=0; i<6; i++) *ptr++ = ea->octet[i];
|
||
|
||
// 0x74 Password
|
||
for (i=0; i<6; i++) *ptr++ = 0;
|
||
|
||
if (write(bpf_fd, packet, ptr - packet) < 0)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno));
|
||
return errno;
|
||
}
|
||
helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent unicast eth_addr %s, ip_addr %s", eth_addr, ip_addr);
|
||
// Send a broadcast one to handle ethernet switches that don't flood forward packets with
|
||
// unknown mac addresses.
|
||
for (i=0; i<6; i++) packet[i] = 0xFF;
|
||
if (write(bpf_fd, packet, ptr - packet) < 0)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno));
|
||
return errno;
|
||
}
|
||
helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent broadcast eth_addr %s, ip_addr %s", eth_addr, ip_addr);
|
||
close(bpf_fd);
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
// Open the specified port for protocol in the P2P firewall.
|
||
kern_return_t
|
||
do_mDNSPacketFilterControl(__unused mach_port_t port, uint32_t command, const char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray, audit_token_t token)
|
||
{
|
||
(void) token; // unused
|
||
int error;
|
||
kern_return_t result = KERN_SUCCESS;
|
||
|
||
helplog(ASL_LEVEL_INFO, "do_mDNSPacketFilterControl: command %d ifname %s, count %d",
|
||
command, ifname, count);
|
||
|
||
switch (command)
|
||
{
|
||
case PF_SET_RULES:
|
||
error = P2PPacketFilterAddBonjourRuleSet(ifname, count, portArray, protocolArray);
|
||
if (error)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "P2PPacketFilterAddBonjourRuleSet failed %s", strerror(error));
|
||
result = KERN_FAILURE;
|
||
}
|
||
break;
|
||
|
||
case PF_CLEAR_RULES:
|
||
error = P2PPacketFilterClearBonjourRules();
|
||
if (error)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "P2PPacketFilterClearBonjourRules failed %s", strerror(error));
|
||
result = KERN_FAILURE;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSPacketFilterControl: invalid command %d", command);
|
||
result = KERN_INVALID_ARGUMENT;
|
||
break;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
unsigned long
|
||
in_cksum(unsigned short *ptr,int nbytes)
|
||
{
|
||
unsigned long sum;
|
||
u_short oddbyte;
|
||
|
||
/*
|
||
* Our algorithm is simple, using a 32-bit accumulator (sum),
|
||
* we add sequential 16-bit words to it, and at the end, fold back
|
||
* all the carry bits from the top 16 bits into the lower 16 bits.
|
||
*/
|
||
sum = 0;
|
||
while (nbytes > 1) {
|
||
sum += *ptr++;
|
||
nbytes -= 2;
|
||
}
|
||
|
||
/* mop up an odd byte, if necessary */
|
||
if (nbytes == 1) {
|
||
/* make sure top half is zero */
|
||
oddbyte = 0;
|
||
|
||
/* one byte only */
|
||
*((u_char *)&oddbyte) = *(u_char *)ptr;
|
||
sum += oddbyte;
|
||
}
|
||
/* Add back carry outs from top 16 bits to low 16 bits. */
|
||
sum = (sum >> 16) + (sum & 0xffff);
|
||
|
||
/* add carry */
|
||
sum += (sum >> 16);
|
||
|
||
return sum;
|
||
}
|
||
|
||
unsigned short
|
||
InetChecksum(unsigned short *ptr,int nbytes)
|
||
{
|
||
unsigned long sum;
|
||
|
||
sum = in_cksum(ptr, nbytes);
|
||
return (unsigned short)~sum;
|
||
}
|
||
|
||
void TCPCheckSum(int af, struct tcphdr *t, int tcplen, v6addr_t sadd6, v6addr_t dadd6)
|
||
{
|
||
unsigned long sum = 0;
|
||
unsigned short *ptr;
|
||
|
||
/* TCP header checksum */
|
||
sum = in_cksum((unsigned short *)t, tcplen);
|
||
|
||
if (af == AF_INET)
|
||
{
|
||
/* Pseudo header */
|
||
ptr = (unsigned short *)sadd6;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
ptr = (unsigned short *)dadd6;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
}
|
||
else if (af == AF_INET6)
|
||
{
|
||
/* Pseudo header */
|
||
ptr = (unsigned short *)sadd6;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
ptr = (unsigned short *)dadd6;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
sum += *ptr++;
|
||
}
|
||
|
||
sum += htons(tcplen);
|
||
sum += htons(IPPROTO_TCP);
|
||
|
||
while (sum >> 16)
|
||
sum = (sum >> 16) + (sum & 0xFFFF);
|
||
|
||
t->th_sum = ~sum;
|
||
|
||
}
|
||
|
||
kern_return_t do_mDNSSendKeepalive(__unused mach_port_t port, v6addr_t sadd6, v6addr_t dadd6, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win, audit_token_t token)
|
||
{
|
||
struct packet4 {
|
||
struct ip ip;
|
||
struct tcphdr tcp;
|
||
} packet4;
|
||
struct packet6 {
|
||
struct tcphdr tcp;
|
||
} packet6;
|
||
int sock, on;
|
||
struct tcphdr *t;
|
||
int af;
|
||
struct sockaddr_storage ss_to;
|
||
struct sockaddr_in *sin_to = (struct sockaddr_in *)&ss_to;
|
||
struct sockaddr_in6 *sin6_to = (struct sockaddr_in6 *)&ss_to;
|
||
void *packet;
|
||
ssize_t packetlen;
|
||
char ctlbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
|
||
struct msghdr msghdr;
|
||
struct iovec iov;
|
||
ssize_t len;
|
||
|
||
if (!authorized(&token))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: Not authorized");
|
||
return kmDNSHelperNotAuthorized;
|
||
}
|
||
|
||
helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: called");
|
||
|
||
// all the incoming arguments are in network order
|
||
if ((*(unsigned *)(sadd6 +4) == 0) && (*(unsigned *)(sadd6 + 8) == 0) && (*(unsigned *)(sadd6 + 12) == 0))
|
||
{
|
||
af = AF_INET;
|
||
memset(&packet4, 0, sizeof (packet4));
|
||
|
||
/* Fill in all the IP header information - should be in host order*/
|
||
packet4.ip.ip_v = 4; /* 4-bit Version */
|
||
packet4.ip.ip_hl = 5; /* 4-bit Header Length */
|
||
packet4.ip.ip_tos = 0; /* 8-bit Type of service */
|
||
packet4.ip.ip_len = 40; /* 16-bit Total length */
|
||
packet4.ip.ip_id = 9864; /* 16-bit ID field */
|
||
packet4.ip.ip_off = 0; /* 13-bit Fragment offset */
|
||
packet4.ip.ip_ttl = 63; /* 8-bit Time To Live */
|
||
packet4.ip.ip_p = IPPROTO_TCP; /* 8-bit Protocol */
|
||
packet4.ip.ip_sum = 0; /* 16-bit Header checksum (below) */
|
||
memcpy(&packet4.ip.ip_src.s_addr, sadd6, 4);
|
||
memcpy(&packet4.ip.ip_dst.s_addr, dadd6, 4);
|
||
|
||
/* IP header checksum */
|
||
packet4.ip.ip_sum = InetChecksum((unsigned short *)&packet4.ip, 20);
|
||
t = &packet4.tcp;
|
||
packet = &packet4;
|
||
packetlen = 40; // sum of IPv4 header len(20) and TCP header len(20)
|
||
}
|
||
else
|
||
{
|
||
af = AF_INET6;
|
||
memset(&packet6, 0, sizeof (packet6));
|
||
t = &packet6.tcp;
|
||
packet = &packet6;
|
||
// We don't send IPv6 header, hence just the TCP header len (20)
|
||
packetlen = 20;
|
||
}
|
||
|
||
/* Fill in all the TCP header information */
|
||
t->th_sport = lport; /* 16-bit Source port number */
|
||
t->th_dport = rport; /* 16-bit Destination port */
|
||
t->th_seq = seq; /* 32-bit Sequence Number */
|
||
t->th_ack = ack; /* 32-bit Acknowledgement Number */
|
||
t->th_off = 5; /* Data offset */
|
||
t->th_flags = TH_ACK;
|
||
t->th_win = win;
|
||
t->th_sum = 0; /* 16-bit checksum (below) */
|
||
t->th_urp = 0; /* 16-bit urgent offset */
|
||
|
||
TCPCheckSum(af, t, 20, sadd6, dadd6);
|
||
|
||
/* Open up a RAW socket */
|
||
if ((sock = socket(af, SOCK_RAW, IPPROTO_TCP)) < 0)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: socket %s", strerror(errno));
|
||
return errno;
|
||
}
|
||
|
||
|
||
if (af == AF_INET)
|
||
{
|
||
on = 1;
|
||
if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on)))
|
||
{
|
||
close(sock);
|
||
helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: setsockopt %s", strerror(errno));
|
||
return errno;
|
||
}
|
||
|
||
memset(sin_to, 0, sizeof(struct sockaddr_in));
|
||
sin_to->sin_len = sizeof(struct sockaddr_in);
|
||
sin_to->sin_family = AF_INET;
|
||
memcpy(&sin_to->sin_addr, sadd6, sizeof(struct in_addr));
|
||
sin_to->sin_port = rport;
|
||
|
||
msghdr.msg_control = NULL;
|
||
msghdr.msg_controllen = 0;
|
||
|
||
}
|
||
else
|
||
{
|
||
struct cmsghdr *ctl;
|
||
|
||
memset(sin6_to, 0, sizeof(struct sockaddr_in6));
|
||
sin6_to->sin6_len = sizeof(struct sockaddr_in6);
|
||
sin6_to->sin6_family = AF_INET6;
|
||
memcpy(&sin6_to->sin6_addr, dadd6, sizeof(struct in6_addr));
|
||
|
||
sin6_to->sin6_port = rport;
|
||
sin6_to->sin6_flowinfo = 0;
|
||
|
||
|
||
msghdr.msg_control = ctlbuf;
|
||
msghdr.msg_controllen = sizeof(ctlbuf);
|
||
ctl = CMSG_FIRSTHDR(&msghdr);
|
||
ctl->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
||
ctl->cmsg_level = IPPROTO_IPV6;
|
||
ctl->cmsg_type = IPV6_PKTINFO;
|
||
struct in6_pktinfo *pktinfo = (struct in6_pktinfo *) CMSG_DATA(ctl);
|
||
memcpy(&pktinfo->ipi6_addr, sadd6, sizeof(struct in6_addr));
|
||
pktinfo->ipi6_ifindex = 0;
|
||
}
|
||
|
||
msghdr.msg_name = (struct sockaddr *)&ss_to;
|
||
msghdr.msg_namelen = ss_to.ss_len;
|
||
iov.iov_base = packet;
|
||
iov.iov_len = packetlen;
|
||
msghdr.msg_iov = &iov;
|
||
msghdr.msg_iovlen = 1;
|
||
msghdr.msg_flags = 0;
|
||
again:
|
||
len = sendmsg(sock, &msghdr, 0);
|
||
if (len == -1)
|
||
{
|
||
if (errno == EINTR)
|
||
goto again;
|
||
}
|
||
|
||
if (len != packetlen)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: sendmsg failed %s", strerror(errno));
|
||
}
|
||
else
|
||
{
|
||
char source[INET6_ADDRSTRLEN], dest[INET6_ADDRSTRLEN];
|
||
|
||
inet_ntop(af, (void *)sadd6, source, sizeof(source));
|
||
inet_ntop(af, (void *)dadd6, dest, sizeof(dest));
|
||
|
||
helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: Success Source %s:%d, Dest %s:%d, %u, %u, %u", source, ntohs(lport), dest, ntohs(rport), ntohl(seq), ntohl(ack), ntohs(win));
|
||
|
||
}
|
||
close(sock);
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
|
||
kern_return_t do_mDNSRetrieveTCPInfo(__unused mach_port_t port, int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t rport,
|
||
uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid, audit_token_t token)
|
||
{
|
||
struct tcp_info ti;
|
||
struct info_tuple itpl;
|
||
int mib[4];
|
||
unsigned int miblen;
|
||
size_t len;
|
||
size_t sz;
|
||
|
||
memset(&itpl, 0, sizeof(struct info_tuple));
|
||
memset(&ti, 0, sizeof(struct tcp_info));
|
||
|
||
if (!authorized(&token))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mDNSRetrieveTCPInfo: Not authorized");
|
||
return kmDNSHelperNotAuthorized;
|
||
}
|
||
|
||
if (family == AF_INET)
|
||
{
|
||
memcpy(&itpl.itpl_local_sin.sin_addr, laddr, sizeof(struct in_addr));
|
||
memcpy(&itpl.itpl_remote_sin.sin_addr, raddr, sizeof(struct in_addr));
|
||
itpl.itpl_local_sin.sin_port = lport;
|
||
itpl.itpl_remote_sin.sin_port = rport;
|
||
itpl.itpl_local_sin.sin_family = AF_INET;
|
||
itpl.itpl_remote_sin.sin_family = AF_INET;
|
||
}
|
||
else
|
||
{
|
||
memcpy(&itpl.itpl_local_sin6.sin6_addr, laddr, sizeof(struct in6_addr));
|
||
memcpy(&itpl.itpl_remote_sin6.sin6_addr, raddr, sizeof(struct in6_addr));
|
||
itpl.itpl_local_sin6.sin6_port = lport;
|
||
itpl.itpl_remote_sin6.sin6_port = rport;
|
||
itpl.itpl_local_sin6.sin6_family = AF_INET6;
|
||
itpl.itpl_remote_sin6.sin6_family = AF_INET6;
|
||
}
|
||
itpl.itpl_proto = IPPROTO_TCP;
|
||
sz = sizeof(mib)/sizeof(mib[0]);
|
||
if (sysctlnametomib("net.inet.tcp.info", mib, &sz) == -1)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctlnametomib failed %d, %s", errno, strerror(errno));
|
||
return errno;
|
||
}
|
||
miblen = (unsigned int)sz;
|
||
len = sizeof(struct tcp_info);
|
||
if (sysctl(mib, miblen, &ti, &len, &itpl, sizeof(struct info_tuple)) == -1)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctl failed %d, %s", errno, strerror(errno));
|
||
return errno;
|
||
}
|
||
|
||
*seq = ti.tcpi_snd_nxt - 1;
|
||
*ack = ti.tcpi_rcv_nxt;
|
||
*win = ti.tcpi_rcv_space >> ti.tcpi_rcv_wscale;
|
||
*intfid = ti.tcpi_last_outif;
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
static int getMACAddress(int family, v6addr_t raddr, v6addr_t gaddr, int *gfamily, ethaddr_t eth)
|
||
{
|
||
struct
|
||
{
|
||
struct rt_msghdr m_rtm;
|
||
char m_space[512];
|
||
} m_rtmsg;
|
||
|
||
struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
|
||
char *cp = m_rtmsg.m_space;
|
||
int seq = 6367, sock, rlen, i;
|
||
struct sockaddr_in *sin = NULL;
|
||
struct sockaddr_in6 *sin6 = NULL;
|
||
struct sockaddr_dl *sdl = NULL;
|
||
struct sockaddr_storage sins;
|
||
struct sockaddr_dl sdl_m;
|
||
|
||
#define NEXTADDR(w, s, len) \
|
||
if (rtm->rtm_addrs & (w)) \
|
||
{ \
|
||
bcopy((char *)s, cp, len); \
|
||
cp += len; \
|
||
}
|
||
|
||
bzero(&sins, sizeof(struct sockaddr_storage));
|
||
bzero(&sdl_m, sizeof(struct sockaddr_dl));
|
||
bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
|
||
|
||
sock = socket(PF_ROUTE, SOCK_RAW, 0);
|
||
if (sock < 0)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Can not open the socket - %s", strerror(errno));
|
||
return errno;
|
||
}
|
||
|
||
rtm->rtm_addrs |= RTA_DST | RTA_GATEWAY;
|
||
rtm->rtm_type = RTM_GET;
|
||
rtm->rtm_flags = 0;
|
||
rtm->rtm_version = RTM_VERSION;
|
||
rtm->rtm_seq = ++seq;
|
||
|
||
sdl_m.sdl_len = sizeof(sdl_m);
|
||
sdl_m.sdl_family = AF_LINK;
|
||
if (family == AF_INET)
|
||
{
|
||
sin = (struct sockaddr_in*)&sins;
|
||
sin->sin_family = AF_INET;
|
||
sin->sin_len = sizeof(struct sockaddr_in);
|
||
memcpy(&sin->sin_addr, raddr, sizeof(struct in_addr));
|
||
NEXTADDR(RTA_DST, sin, sin->sin_len);
|
||
}
|
||
else if (family == AF_INET6)
|
||
{
|
||
sin6 = (struct sockaddr_in6 *)&sins;
|
||
sin6->sin6_len = sizeof(struct sockaddr_in6);
|
||
sin6->sin6_family = AF_INET6;
|
||
memcpy(&sin6->sin6_addr, raddr, sizeof(struct in6_addr));
|
||
NEXTADDR(RTA_DST, sin6, sin6->sin6_len);
|
||
}
|
||
NEXTADDR(RTA_GATEWAY, &sdl_m, sdl_m.sdl_len);
|
||
rtm->rtm_msglen = rlen = cp - (char *)&m_rtmsg;
|
||
|
||
if (write(sock, (char *)&m_rtmsg, rlen) < 0)
|
||
{
|
||
helplog(ASL_LEVEL_INFO, "do_mDNSGetRemoteMAC: writing to routing socket: %s", strerror(errno));
|
||
close(sock);
|
||
return errno;
|
||
}
|
||
|
||
do
|
||
{
|
||
rlen = read(sock, (char *)&m_rtmsg, sizeof(m_rtmsg));
|
||
}
|
||
while (rlen > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != getpid()));
|
||
|
||
if (rlen < 0)
|
||
helplog(ASL_LEVEL_ERR, "do_mDNSGetRemoteMAC: Read from routing socket failed");
|
||
|
||
if (family == AF_INET)
|
||
{
|
||
sin = (struct sockaddr_in *) (rtm + 1);
|
||
sdl = (struct sockaddr_dl *) (sin->sin_len + (char *) sin);
|
||
}
|
||
else if (family == AF_INET6)
|
||
{
|
||
sin6 = (struct sockaddr_in6 *) (rtm +1);
|
||
sdl = (struct sockaddr_dl *) (sin6->sin6_len + (char *) sin6);
|
||
}
|
||
// If the address is not on the local net, we get the IP address of the gateway.
|
||
// We would have to repeat the process to get the MAC address of the gateway
|
||
*gfamily = sdl->sdl_family;
|
||
if (sdl->sdl_family == AF_INET)
|
||
{
|
||
struct sockaddr_in *new_sin = (struct sockaddr_in *)(sin->sin_len +(char*) sin);
|
||
memcpy(gaddr, &new_sin->sin_addr, sizeof(struct in_addr));
|
||
close(sock);
|
||
return -1;
|
||
}
|
||
else if (sdl->sdl_family == AF_INET6)
|
||
{
|
||
struct sockaddr_in6 *new_sin6 = (struct sockaddr_in6 *)(sin6->sin6_len +(char*) sin6);
|
||
memcpy(gaddr, &new_sin6->sin6_addr, sizeof(struct in6_addr));
|
||
close(sock);
|
||
return -1;
|
||
}
|
||
|
||
unsigned char *ptr = (unsigned char *)LLADDR(sdl);
|
||
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
||
(eth)[i] = *(ptr +i);
|
||
|
||
close(sock);
|
||
return KERN_SUCCESS;
|
||
}
|
||
|
||
kern_return_t do_mDNSGetRemoteMAC(__unused mach_port_t port, int family, v6addr_t raddr, ethaddr_t eth, audit_token_t token)
|
||
{
|
||
int ret = 0;
|
||
v6addr_t gateway;
|
||
int gfamily;
|
||
int count = 0;
|
||
|
||
if (!authorized(&token))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Not authorized");
|
||
return kmDNSHelperNotAuthorized;
|
||
}
|
||
|
||
do
|
||
{
|
||
ret = getMACAddress(family, raddr, gateway, &gfamily, eth);
|
||
if (ret == -1)
|
||
{
|
||
memcpy(raddr, gateway, sizeof(family));
|
||
family = gfamily;
|
||
count++;
|
||
}
|
||
}
|
||
while ((ret == -1) && (count < 5));
|
||
return ret;
|
||
}
|
||
|
||
|
||
kern_return_t do_mDNSStoreSPSMACAddress(__unused mach_port_t port, int family, v6addr_t spsaddr, const char *ifname, audit_token_t token)
|
||
{
|
||
ethaddr_t eth;
|
||
char spsip[INET6_ADDRSTRLEN];
|
||
int ret = 0;
|
||
CFStringRef sckey = NULL;
|
||
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:StoreSPSMACAddress"), NULL, NULL);
|
||
SCDynamicStoreRef ipstore = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetIPv6Addresses"), NULL, NULL);
|
||
CFMutableDictionaryRef dict = NULL;
|
||
CFStringRef entityname = NULL;
|
||
CFDictionaryRef ipdict = NULL;
|
||
CFArrayRef addrs = NULL;
|
||
|
||
if (!authorized(&token))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mDNSStoreSPSMAC: Not authorized");
|
||
return kmDNSHelperNotAuthorized;
|
||
}
|
||
|
||
if ((store == NULL) || (ipstore == NULL))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "Unable to access SC Dynamic Store");
|
||
return KERN_FAILURE;
|
||
}
|
||
|
||
// Get the MAC address of the Sleep Proxy Server
|
||
memset(eth, 0, sizeof(eth));
|
||
ret = do_mDNSGetRemoteMAC(port, family, spsaddr, eth, token);
|
||
if (ret != KERN_SUCCESS)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "mDNSStoreSPSMAC: Failed to determine the MAC address");
|
||
goto fin;
|
||
}
|
||
|
||
// Create/Update the dynamic store entry for the specified interface
|
||
sckey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s%s"), "State:/Network/Interface/", ifname, "/BonjourSleepProxyAddress");
|
||
dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||
if (!dict)
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "SPSCreateDict: Could not create CFDictionary dict");
|
||
ret = KERN_FAILURE;
|
||
goto fin;
|
||
}
|
||
|
||
CFStringRef macaddr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%02x:%02x:%02x:%02x:%02x:%02x"), eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]);
|
||
CFDictionarySetValue(dict, CFSTR("MACAddress"), macaddr);
|
||
if (NULL != macaddr) CFRelease(macaddr);
|
||
|
||
if( NULL == inet_ntop(family, (void *)spsaddr, spsip, sizeof(spsip)))
|
||
{
|
||
helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", strerror(errno));
|
||
ret = kmDNSHelperInvalidNetworkAddress;
|
||
goto fin;
|
||
}
|
||
|
||
CFStringRef ipaddr = CFStringCreateWithCString(NULL, spsip, kCFStringEncodingUTF8);
|
||
CFDictionarySetValue(dict, CFSTR("IPAddress"), ipaddr);
|
||
if (NULL != ipaddr) CFRelease(ipaddr);
|
||
|
||
// Get the current IPv6 addresses on this interface and store them so NAs can be sent on wakeup
|
||
if ((entityname = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Interface/%s/IPv6"), ifname)) != NULL)
|
||
{
|
||
if ((ipdict = SCDynamicStoreCopyValue(ipstore, entityname)) != NULL)
|
||
{
|
||
if((addrs = CFDictionaryGetValue(ipdict, CFSTR("Addresses"))) != NULL)
|
||
{
|
||
addrs = CFRetain(addrs);
|
||
CFDictionarySetValue(dict, CFSTR("RegisteredAddresses"), addrs);
|
||
}
|
||
}
|
||
}
|
||
SCDynamicStoreSetValue(store, sckey, dict);
|
||
|
||
fin:
|
||
if (store) CFRelease(store);
|
||
if (ipstore) CFRelease(ipstore);
|
||
if (sckey) CFRelease(sckey);
|
||
if (dict) CFRelease(dict);
|
||
if (ipdict) CFRelease(ipdict);
|
||
if (entityname) CFRelease(entityname);
|
||
if (addrs) CFRelease(addrs);
|
||
|
||
update_idle_timer();
|
||
return ret;
|
||
}
|