1 #import "keychain/ot/OTDeviceInformationAdapter.h"
2 #import "keychain/ot/OTConstants.h"
3 #import "keychain/ckks/CKKSListenerCollection.h"
4 #import "keychain/ckks/CKKS.h"
6 #include <SystemConfiguration/SystemConfiguration.h>
7 #import <Security/SecInternalReleasePriv.h>
10 #include <sys/sysctl.h>
11 #include <IOKit/IOKitLib.h>
13 #import <sys/utsname.h>
14 #include <MobileGestalt.h>
17 static void updateDeviceNameChanges(SCDynamicStoreRef store, CFArrayRef keys, void *context);
19 @interface OTDeviceInformationActualAdapter ()
20 @property CKKSListenerCollection<id<OTDeviceInformationNameUpdateListener>>* deviceNameUpdateListeners;
21 @property (assign) SCDynamicStoreRef store;
24 @implementation OTDeviceInformationActualAdapter
28 CFRelease(self.store);
35 static NSString *hwModel = nil;
36 static dispatch_once_t onceToken;
37 dispatch_once(&onceToken, ^{
38 #if TARGET_OS_SIMULATOR
39 // Asking for a real value in the simulator gives the results for the underlying mac. Not particularly useful.
40 hwModel = [NSString stringWithFormat:@"%s", getenv("SIMULATOR_MODEL_IDENTIFIER")];
43 sysctlbyname("hw.model", NULL, &size, NULL, 0);
44 char *sysctlString = malloc(size);
45 sysctlbyname("hw.model", sysctlString, &size, NULL, 0);
46 hwModel = [[NSString alloc] initWithUTF8String:sysctlString];
49 // macOS running virtualized sometimes has new and unknown model IDs.
50 // So, if we don't recognize the model ID, return something more useful.
51 if(!([hwModel hasPrefix:@"iMac"] ||
52 [hwModel hasPrefix:@"Mac"])) {
53 hwModel = [NSString stringWithFormat:@"MacUnknown-%@", hwModel];
56 struct utsname systemInfo;
59 hwModel = [NSString stringWithCString:systemInfo.machine
60 encoding:NSUTF8StringEncoding];
66 - (NSString* _Nullable)deviceName
68 if (SecIsInternalRelease()) {
69 NSString *deviceName = CFBridgingRelease(SCDynamicStoreCopyComputerName(NULL, NULL));
76 - (void)registerForDeviceNameUpdates:(id<OTDeviceInformationNameUpdateListener>)listener
78 // Octagon only uses the device name on internal releases.
79 // Therefore, if this is not an internal release, don't bother registering clients--they don't need the update.
80 if (SecIsInternalRelease()) {
81 @synchronized (self) {
82 [self setupDeviceNameListener];
83 [self.deviceNameUpdateListeners registerListener:listener];
88 - (NSString*)osVersion
90 return SecCKKSHostOSVersion();
95 #include <MobileGestalt.h>
97 - (NSString*)serialNumber
99 int mgError = kMGNoError;
100 NSString *serialNumber = CFBridgingRelease(MGCopyAnswerWithError(kMGQSerialNumber, NULL, &mgError));
101 if (![serialNumber isKindOfClass:[NSString class]]) {
103 secnotice("octagon", "failed getting serial number: %d", mgError);
110 - (NSString*)serialNumber
112 io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
113 if (platformExpert == MACH_PORT_NULL) {
114 secnotice("octagon", "failed getting serial number (platform IOPlatformExpertDevice)");
117 NSString *serialNumber = CFBridgingRelease(IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0));
118 if (![serialNumber isKindOfClass:[NSString class]]) {
120 secnotice("octagon", "failed getting serial number (IORegistryEntry)");
122 IOObjectRelease(platformExpert);
129 - (void)setupDeviceNameListener {
130 if (self.deviceNameUpdateListeners == nil) {
131 self.deviceNameUpdateListeners = [[CKKSListenerCollection alloc] initWithName:@"OTDeviceInformationActualAdapter"];
133 CFStringRef computerKey = SCDynamicStoreKeyCreateComputerName(NULL);
134 if (computerKey == NULL) {
137 NSArray *keys = @[ (__bridge NSString *)computerKey];
138 CFRelease(computerKey);
140 SCDynamicStoreContext context = { .info = (void *)(__bridge CFTypeRef)self };
141 self.store = SCDynamicStoreCreate(NULL, CFSTR("OTDeviceInformationActualAdapter"), updateDeviceNameChanges, &context);
142 if (self.store == NULL) {
146 SCDynamicStoreSetNotificationKeys(self.store, (__bridge CFArrayRef)keys, NULL);
147 SCDynamicStoreSetDispatchQueue(self.store, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
153 static void updateDeviceNameChanges(SCDynamicStoreRef store, CFArrayRef keys, void *info)
155 secnotice("octagon", "Notified that the device name has changed");
156 OTDeviceInformationActualAdapter *adapter = (__bridge id)info;
158 [adapter.deviceNameUpdateListeners iterateListeners:^void(id<OTDeviceInformationNameUpdateListener> object){
159 [object deviceNameUpdated];