#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
#include <sys/codesign.h>
#include <SystemConfiguration/CaptiveNetwork.h>
#include <SystemConfiguration/SCPrivate.h>
#pragma mark -
#pragma mark CaptiveNetwork.framework APIs (exported through the SystemConfiguration.framework)
const CFStringRef kCNNetworkInfoKeySSIDData = CFSTR("SSIDDATA");
const CFStringRef kCNNetworkInfoKeySSID = CFSTR("SSID");
const CFStringRef kCNNetworkInfoKeyBSSID = CFSTR("BSSID");
static void *
__loadCaptiveNetwork(void) {
static void *image = NULL;
static dispatch_once_t once;
dispatch_once(&once, ^{
image = _SC_dlopen("/System/Library/PrivateFrameworks/CaptiveNetwork.framework/CaptiveNetwork");
});
return image;
}
Boolean
CNSetSupportedSSIDs(CFArrayRef ssidArray)
{
static typeof (CNSetSupportedSSIDs) *dyfunc = NULL;
if (!dyfunc) {
void *image = __loadCaptiveNetwork();
if (image) dyfunc = dlsym(image, "__CNSetSupportedSSIDs");
}
return dyfunc ? dyfunc(ssidArray) : FALSE;
}
Boolean
CNMarkPortalOnline(CFStringRef interfaceName)
{
static typeof (CNMarkPortalOnline) *dyfunc = NULL;
if (!dyfunc) {
void *image = __loadCaptiveNetwork();
if (image) dyfunc = dlsym(image, "__CNMarkPortalOnline");
}
return dyfunc ? dyfunc(interfaceName) : FALSE;
}
Boolean
CNMarkPortalOffline(CFStringRef interfaceName)
{
static typeof (CNMarkPortalOffline) *dyfunc = NULL;
if (!dyfunc) {
void *image = __loadCaptiveNetwork();
if (image) dyfunc = dlsym(image, "__CNMarkPortalOffline");
}
return dyfunc ? dyfunc(interfaceName) : FALSE;
}
CFArrayRef
CNCopySupportedInterfaces(void)
{
static typeof (CNCopySupportedInterfaces) *dyfunc = NULL;
if (!dyfunc) {
void *image = __loadCaptiveNetwork();
if (image) dyfunc = dlsym(image, "__CNCopySupportedInterfaces");
}
return dyfunc ? dyfunc() : NULL;
}
#if TARGET_OS_IPHONE
#define CN_COPY_ENTITLEMENT CFSTR("com.apple.developer.networking.wifi-info")
static CFDictionaryRef
__CopyEntitlementsForPID(pid_t pid)
{
uint8_t *buffer = NULL;
size_t bufferlen = 0L;
int64_t datalen = 0L;
CFDataRef cfdata = NULL;
struct csheader {
uint32_t magic;
uint32_t length;
} csheader = { 0, 0 };
int error = -1;
CFPropertyListRef plist = NULL;
error = csops(pid, CS_OPS_ENTITLEMENTS_BLOB, &csheader, sizeof(csheader));
if (error == -1 && errno == ERANGE) {
bufferlen = ntohl(csheader.length);
if (bufferlen > 1024 * 1024 || bufferlen < 8) {
errno = EINVAL;
goto out;
}
buffer = malloc(bufferlen);
if (buffer == NULL) {
goto out;
}
error = csops(pid, CS_OPS_ENTITLEMENTS_BLOB, buffer, bufferlen);
if (error < 0) {
goto out;
}
}
datalen = bufferlen - sizeof(csheader);
if (error == 0 && buffer && datalen > 0) {
cfdata = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer + sizeof(csheader), datalen, kCFAllocatorNull);
if (cfdata == NULL) {
goto out;
}
plist = CFPropertyListCreateWithData(NULL, cfdata, kCFPropertyListImmutable, NULL, NULL);
if (!plist) {
SC_log(LOG_ERR, "Could not decode entitlements for pid %d", pid);
}
}
else {
SC_log(LOG_ERR, "Could not get valid codesigning data for pid %d. Error %d", pid, error);
}
out:
if (error < 0) {
SC_log(LOG_ERR, "Error getting entitlements for pid %d: %s", pid, strerror(errno));
}
if (cfdata != NULL) {
CFRelease(cfdata);
}
if (buffer != NULL) {
free(buffer);
}
if (plist && !isA_CFDictionary(plist)) {
SC_log(LOG_ERR, "Could not decode entitlements for pid %d as a dictionary.", pid);
CFRelease(plist);
plist = NULL;
}
return plist;
}
static Boolean
__isApplicationEntitled(void)
{
if (dyld_get_program_sdk_version() >= DYLD_IOS_VERSION_12_0) {
CFTypeRef entitlement = NULL;
CFDictionaryRef entitlements = __CopyEntitlementsForPID(getpid());
if (entitlements != NULL) {
Boolean entitled = FALSE;
entitlement = CFDictionaryGetValue(entitlements, CN_COPY_ENTITLEMENT);
if(isA_CFBoolean(entitlement)) {
entitled = CFBooleanGetValue(entitlement);
}
CFRelease(entitlements);
return entitled;
}
return FALSE;
}
return TRUE;
}
#endif
CFDictionaryRef
CNCopyCurrentNetworkInfo(CFStringRef interfaceName)
{
#if TARGET_OS_IPHONE && !TARGET_OS_IOSMAC
if (__isApplicationEntitled() == FALSE) {
SC_log(LOG_DEBUG, "Application does not have %@ entitlement", CN_COPY_ENTITLEMENT);
return NULL;
}
static typeof (CNCopyCurrentNetworkInfo) *dyfunc = NULL;
if (!dyfunc) {
void *image = __loadCaptiveNetwork();
if (image) dyfunc = dlsym(image, "__CNCopyCurrentNetworkInfo");
}
return dyfunc ? dyfunc(interfaceName) : NULL;
#else // TARGET_OS_IPHONE && !TARGET_OS_IOSMAC
#pragma unused(interfaceName)
return NULL;
#endif // TARGET_OS_IPHONE && !TARGET_OS_IOSMAC
}