OTDeviceInformationAdapter.m [plain text]
#import "keychain/ot/OTDeviceInformationAdapter.h"
#import "keychain/ot/OTConstants.h"
#import "keychain/ckks/CKKSListenerCollection.h"
#import "keychain/ckks/CKKS.h"
#include <SystemConfiguration/SystemConfiguration.h>
#import <Security/SecInternalReleasePriv.h>
#if TARGET_OS_OSX
#include <sys/sysctl.h>
#include <IOKit/IOKitLib.h>
#else
#import <sys/utsname.h>
#include <MobileGestalt.h>
#endif
static void updateDeviceNameChanges(SCDynamicStoreRef store, CFArrayRef keys, void *context);
@interface OTDeviceInformationActualAdapter ()
@property CKKSListenerCollection<id<OTDeviceInformationNameUpdateListener>>* deviceNameUpdateListeners;
@property (assign) SCDynamicStoreRef store;
@end
@implementation OTDeviceInformationActualAdapter
- (void)dealloc {
if (self.store) {
CFRelease(self.store);
self.store = NULL;
}
}
- (NSString*)modelID
{
static NSString *hwModel = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if TARGET_OS_SIMULATOR
// Asking for a real value in the simulator gives the results for the underlying mac. Not particularly useful.
hwModel = [NSString stringWithFormat:@"#elif TARGET_OS_OSX
size_t size;
sysctlbyname("hw.model", NULL, &size, NULL, 0);
char *sysctlString = malloc(size);
sysctlbyname("hw.model", sysctlString, &size, NULL, 0);
hwModel = [[NSString alloc] initWithUTF8String:sysctlString];
free(sysctlString);
// macOS running virtualized sometimes has new and unknown model IDs.
// So, if we don't recognize the model ID, return something more useful.
if(!([hwModel hasPrefix:@"iMac"] ||
[hwModel hasPrefix:@"Mac"])) {
hwModel = [NSString stringWithFormat:@"MacUnknown- }
#else
struct utsname systemInfo;
uname(&systemInfo);
hwModel = [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
#endif
});
return hwModel;
}
- (NSString* _Nullable)deviceName
{
if (SecIsInternalRelease()) {
NSString *deviceName = CFBridgingRelease(SCDynamicStoreCopyComputerName(NULL, NULL));
return deviceName;
} else {
return nil;
}
}
- (void)registerForDeviceNameUpdates:(id<OTDeviceInformationNameUpdateListener>)listener
{
// Octagon only uses the device name on internal releases.
// Therefore, if this is not an internal release, don't bother registering clients--they don't need the update.
if (SecIsInternalRelease()) {
@synchronized (self) {
[self setupDeviceNameListener];
[self.deviceNameUpdateListeners registerListener:listener];
}
}
}
- (NSString*)osVersion
{
return SecCKKSHostOSVersion();
}
#if TARGET_OS_IPHONE
#include <MobileGestalt.h>
- (NSString*)serialNumber
{
int mgError = kMGNoError;
NSString *serialNumber = CFBridgingRelease(MGCopyAnswerWithError(kMGQSerialNumber, NULL, &mgError));
if (![serialNumber isKindOfClass:[NSString class]]) {
serialNumber = nil;
secnotice("octagon", "failed getting serial number: }
return serialNumber;
}
#else
- (NSString* _Nullable)serialNumber
{
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpert == MACH_PORT_NULL) {
secnotice("octagon", "failed getting serial number (platform IOPlatformExpertDevice)");
return nil;
}
NSString *serialNumber = CFBridgingRelease(IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0));
if (![serialNumber isKindOfClass:[NSString class]]) {
serialNumber = nil;
secnotice("octagon", "failed getting serial number (IORegistryEntry)");
}
IOObjectRelease(platformExpert);
return serialNumber;
}
#endif
- (void)setupDeviceNameListener {
if (self.deviceNameUpdateListeners == nil) {
self.deviceNameUpdateListeners = [[CKKSListenerCollection alloc] initWithName:@"OTDeviceInformationActualAdapter"];
CFStringRef computerKey = SCDynamicStoreKeyCreateComputerName(NULL);
if (computerKey == NULL) {
return;
}
NSArray *keys = @[ (__bridge NSString *)computerKey];
CFRelease(computerKey);
SCDynamicStoreContext context = { .info = (void *)(__bridge CFTypeRef)self };
self.store = SCDynamicStoreCreate(NULL, CFSTR("OTDeviceInformationActualAdapter"), updateDeviceNameChanges, &context);
if (self.store == NULL) {
return;
}
SCDynamicStoreSetNotificationKeys(self.store, (__bridge CFArrayRef)keys, NULL);
SCDynamicStoreSetDispatchQueue(self.store, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
}
}
@end
static void updateDeviceNameChanges(SCDynamicStoreRef store, CFArrayRef keys, void *info)
{
secnotice("octagon", "Notified that the device name has changed");
OTDeviceInformationActualAdapter *adapter = (__bridge id)info;
[adapter.deviceNameUpdateListeners iterateListeners:^void(id<OTDeviceInformationNameUpdateListener> object){
[object deviceNameUpdated];
}];
}