// _____ _ // |_ _| |_ _ _ ___ ___ _ __ __ _ // | | | ' \| '_/ -_) -_) ' \/ _` |_ // |_| |_||_|_| \___\___|_|_|_\__,_(_) // // Threema iOS Client // Copyright (c) 2016-2020 Threema GmbH // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License, version 3, // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #import "MDMSetup.h" #import "LicenseStore.h" #import "UserSettings.h" #import "MyIdentityStore.h" #import "ServerAPIConnector.h" #import "ValidationLogger.h" #import #ifdef DEBUG static const DDLogLevel ddLogLevel = DDLogLevelAll; #else static const DDLogLevel ddLogLevel = DDLogLevelNotice; #endif NSString * const MDM_CONFIGURATION_KEY = @"com.apple.configuration.managed"; // Company MDM persists in user defaults NSString * const MDM_FEEDBACK_KEY = @"com.apple.feedback.managed"; NSString * const MDM_THREEMA_CONFIGURATION_KEY = @"threema_mdm_configuration"; // Threema MDM persists in user defaults NSString * const MDM_KEY_LICENSE_USERNAME = @"th_license_username"; // String NSString * const MDM_KEY_LICENSE_PASSWORD = @"th_license_password"; // String NSString * const MDM_KEY_NICKNAME = @"th_nickname"; // String max 32 NSString * const MDM_KEY_LINKED_EMAIL = @"th_linked_email"; // String NSString * const MDM_KEY_LINKED_PHONE = @"th_linked_phone"; // String NSString * const MDM_KEY_FIRST_NAME = @"th_firstname"; // String NSString * const MDM_KEY_LAST_NAME = @"th_lastname"; // String NSString * const MDM_KEY_CSI = @"th_csi"; // String NSString * const MDM_KEY_CATEGORY = @"th_category"; // String NSString * const MDM_KEY_CONTACT_SYNC = @"th_contact_sync"; // Bool NSString * const MDM_KEY_READONLY_PROFILE = @"th_readonly_profile"; // Bool NSString * const MDM_KEY_ID_BACKUP = @"th_id_backup"; // String NSString * const MDM_KEY_ID_BACKUP_PASSWORD = @"th_id_backup_password"; // String NSString * const MDM_KEY_BLOCK_UNKNOWN = @"th_block_unknown"; // Bool NSString * const MDM_KEY_HIDE_INACTIVE_IDS = @"th_hide_inactive_ids"; // Bool NSString * const MDM_KEY_DISABLE_SAVE_TO_GALLERY = @"th_disable_save_to_gallery"; // Bool NSString * const MDM_KEY_DISABLE_ADD_CONTACT = @"th_disable_add_contact"; // Bool NSString * const MDM_KEY_DISABLE_EXPORT = @"th_disable_export"; // Bool NSString * const MDM_KEY_DISABLE_BACKUPS = @"th_disable_backups"; // Bool NSString * const MDM_KEY_DISABLE_ID_EXPORT = @"th_disable_id_export"; // Bool NSString * const MDM_KEY_DISABLE_SYSTEM_BACKUPS = @"th_disable_system_backups"; // Bool NSString * const MDM_KEY_DISABLE_MESSAGE_PREVIEW = @"th_disable_message_preview"; // Bool NSString * const MDM_KEY_DISABLE_SEND_PROFILE_PICTURE = @"th_disable_send_profile_picture"; // Bool NSString * const MDM_KEY_DISABLE_CALLS = @"th_disable_calls"; // Bool NSString * const MDM_KEY_DISABLE_VIDEO_CALLS = @"th_disable_video_calls"; // String NSString * const MDM_KEY_DISABLE_CREATE_GROUP = @"th_disable_create_group"; // Bool NSString * const MDM_KEY_SKIP_WIZARD = @"th_skip_wizard"; // Bool NSString * const MDM_KEY_DISABLE_WEB = @"th_disable_web"; // Bool NSString * const MDM_KEY_WEB_HOSTS = @"th_web_hosts"; // String NSString * const MDM_KEY_DISABLE_SHARE_MEDIA = @"th_disable_share_media"; // Bool NSString * const MDM_KEY_SAFE_ENABLE = @"th_safe_enable"; // Bool NSString * const MDM_KEY_SAFE_PASSWORD = @"th_safe_password"; // String min. 8, max. 4096 NSString * const MDM_KEY_SAFE_SERVER_URL = @"th_safe_server_url"; // String NSString * const MDM_KEY_SAFE_SERVER_USERNAME = @"th_safe_server_username"; // String NSString * const MDM_KEY_SAFE_SERVER_PASSWORD = @"th_safe_server_password"; // String NSString * const MDM_KEY_SAFE_RESTORE_ENABLE = @"th_safe_restore_enable"; // Bool NSString * const MDM_KEY_SAFE_RESTORE_ID = @"th_safe_restore_id"; // String 8 NSString * const MDM_KEY_SAFE_PASSWORD_PATTERN = @"th_safe_password_pattern"; // String NSString * const MDM_KEY_SAFE_PASSWORD_MESSAGE = @"th_safe_password_message"; // String NSString * const MDM_KEY_THREEMA_CONFIGURATION = @"mdm"; NSString * const MDM_KEY_THREEMA_OVERRIDE = @"override"; NSString * const MDM_KEY_THREEMA_PARAMS = @"params"; static NSDictionary *_mdmCache; static NSDictionary *_mdmCacheSetup; @implementation MDMSetup - (MDMSetup*)initWithSetup:(BOOL)setup { self = [super init]; if (self) { isSetup = setup; isLicenseRequired = [[LicenseStore sharedLicenseStore] getRequiresLicenseKey] == YES; queue = dispatch_queue_create("ch.threema.MdmConfiguration", NULL); } return self; } + (void)clearMdmCache { _mdmCache = nil; _mdmCacheSetup = nil; } // MARK: MDM values - (BOOL)disableBackups { NSNumber *disableBackups = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_BACKUPS]; return [disableBackups isKindOfClass:[NSNumber class]] ? disableBackups.boolValue : NO; } - (BOOL)disableIdExport { NSNumber *disableIdExport = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_ID_EXPORT]; return [disableIdExport isKindOfClass:[NSNumber class]] ? disableIdExport.boolValue : NO; } - (BOOL)disableSystemBackups { NSNumber *disableSystemBackups = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_SYSTEM_BACKUPS]; return [disableSystemBackups isKindOfClass:[NSNumber class]] ? disableSystemBackups.boolValue : NO; } - (BOOL)readonlyProfile { NSNumber *readonlyProfile = [self getMdmConfigurationBoolForKey:MDM_KEY_READONLY_PROFILE]; return [readonlyProfile isKindOfClass:[NSNumber class]] ? readonlyProfile.boolValue : NO; } - (BOOL)disableAddContact { NSNumber *disableAddContact = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_ADD_CONTACT]; return [disableAddContact isKindOfClass:[NSNumber class]] ? disableAddContact.boolValue : NO; } - (BOOL)disableSaveToGallery { NSNumber *disableSaveToGallery = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_SAVE_TO_GALLERY]; return [disableSaveToGallery isKindOfClass:[NSNumber class]] ? disableSaveToGallery.boolValue : NO; } - (BOOL)disableExport { NSNumber *disableExport = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_EXPORT]; return [disableExport isKindOfClass:[NSNumber class]] ? disableExport.boolValue : NO; } - (BOOL)disableMessagePreview { NSNumber *disableMessagePreview = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_MESSAGE_PREVIEW]; return [disableMessagePreview isKindOfClass:[NSNumber class]] ? disableMessagePreview.boolValue : NO; } - (BOOL)disableCalls { NSNumber *disableCalls = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_CALLS]; return [disableCalls isKindOfClass:[NSNumber class]] ? disableCalls.boolValue : NO; } - (BOOL)disableVideoCalls { NSNumber *disableVideoCalls = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_VIDEO_CALLS]; return [disableVideoCalls isKindOfClass:[NSNumber class]] ? disableVideoCalls.boolValue : NO; } - (BOOL)disableWeb { NSNumber *disableWeb = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_WEB]; return [disableWeb isKindOfClass:[NSNumber class]] ? disableWeb.boolValue : NO; } - (NSString *)webHosts { NSString *webHosts = [self getMdmConfigurationValueForKey:MDM_KEY_WEB_HOSTS]; return [webHosts isKindOfClass:[NSString class]] ? webHosts : nil; } - (BOOL)disableCreateGroup { NSNumber *disableCreateGroup = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_CREATE_GROUP]; return [disableCreateGroup isKindOfClass:[NSNumber class]] ? disableCreateGroup.boolValue : NO; } - (BOOL)disableSendProfilePicture { NSNumber *disableSendProfilePicture = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_SEND_PROFILE_PICTURE]; return [disableSendProfilePicture isKindOfClass:[NSNumber class]] ? disableSendProfilePicture.boolValue : NO; } - (BOOL)skipWizard { NSNumber *skipWizard = [self getMdmConfigurationBoolForKey:MDM_KEY_SKIP_WIZARD]; return [skipWizard isKindOfClass:[NSNumber class]] ? skipWizard.boolValue : NO; } - (BOOL)disableShareMedia { NSNumber *disableShareMedia = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_SHARE_MEDIA]; return [disableShareMedia isKindOfClass:[NSNumber class]] ? disableShareMedia.boolValue : NO; } - (BOOL)disableHideStaleContacts { NSNumber *disableHideInactiveIds = [self getMdmConfigurationBoolForKey:MDM_KEY_HIDE_INACTIVE_IDS]; return [disableHideInactiveIds isKindOfClass:[NSNumber class]] ? YES : NO; } - (NSNumber*)safeEnable { NSNumber *safeEnable = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_ENABLE]; return [safeEnable isKindOfClass:[NSNumber class]] ? safeEnable : nil; } - (NSString*)safePassword { NSString *safePassword = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_PASSWORD]; return [safePassword isKindOfClass:[NSString class]] ? safePassword : nil; } - (NSString*)safeServerUrl { NSString *safeServerUrl = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_SERVER_URL]; return [safeServerUrl isKindOfClass:[NSString class]] ? safeServerUrl : nil; } - (NSString*)safeServerUsername { NSString *safeServerUsername = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_SERVER_USERNAME]; return [safeServerUsername isKindOfClass:[NSString class]] ? safeServerUsername : nil; } - (NSString*)safeServerPassword { NSString *safeServerPassword = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_SERVER_PASSWORD]; return [safeServerPassword isKindOfClass:[NSString class]] ? safeServerPassword : nil; } - (BOOL)safeRestoreEnable { NSNumber *safeRestoreEnable = [self getMdmConfigurationBoolForKey:MDM_KEY_SAFE_RESTORE_ENABLE]; return [safeRestoreEnable isKindOfClass:[NSNumber class]] ? safeRestoreEnable.boolValue : YES; } - (NSString*)safeRestoreId { NSString *safeRestoreId = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_RESTORE_ID]; return [safeRestoreId isKindOfClass:[NSString class]] ? safeRestoreId : nil; } - (NSString *)safePasswordPattern { NSString *safePasswordPattern = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_PASSWORD_PATTERN]; return [safePasswordPattern isKindOfClass:[NSString class]] ? safePasswordPattern : nil; } - (NSString *)safePasswordMessage { NSString *safePasswordMessage = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_PASSWORD_MESSAGE]; return [safePasswordMessage isKindOfClass:[NSString class]] ? safePasswordMessage : nil; } // MARK: Threema Safe status - (BOOL)isSafeBackupDisable { return [[self safeSetupWork] isSafeBackupStatusSetWithSafeState:SafeSetupWork.backupDisable]; } - (BOOL)isSafeBackupForce { return [[self safeSetupWork] isSafeBackupStatusSetWithSafeState:SafeSetupWork.backupForce]; } - (BOOL)isSafeBackupPasswordPreset { return [[self safeSetupWork] isSafeBackupStatusSetWithSafeState:SafeSetupWork.passwordPreset]; } - (BOOL)isSafeBackupServerPreset { return [[self safeSetupWork] isSafeBackupStatusSetWithSafeState:SafeSetupWork.serverPreset]; } - (BOOL)isSafeRestoreDisable { return [[self safeSetupWork] isSafeRestoreStatusSetWithSafeState:SafeSetupWork.restoreDisable]; } - (BOOL)isSafeRestoreForce { return [[self safeSetupWork] isSafeRestoreStatusSetWithSafeState:SafeSetupWork.restoreForce]; } - (BOOL)isSafeRestorePasswordPreset { return [[self safeSetupWork] isSafeRestoreStatusSetWithSafeState:SafeSetupWork.passwordPreset]; } - (BOOL)isSafeRestoreServerPreset { return [[self safeSetupWork] isSafeRestoreStatusSetWithSafeState:SafeSetupWork.serverPreset]; } - (SafeSetupWork*)safeSetupWork { return [[SafeSetupWork alloc] initWithMdmSetup:self]; } // MARK: apply MDM to user settings, identity - (void)loadRenewableValues { if (![self isManaged]) { return; } [self loadLicenseInfo]; UserSettings *userSettings = [UserSettings sharedUserSettings]; NSNumber *blockUnknown = [self getMdmConfigurationBoolForKey:MDM_KEY_BLOCK_UNKNOWN]; if ([blockUnknown isKindOfClass:[NSNumber class]]) { userSettings.blockUnknown = blockUnknown.boolValue; } NSNumber *hideInactiveIds = [self getMdmConfigurationBoolForKey:MDM_KEY_HIDE_INACTIVE_IDS]; if ([hideInactiveIds isKindOfClass:[NSNumber class]]) { userSettings.hideStaleContacts = hideInactiveIds.boolValue; } NSNumber *contactSync = [self getMdmConfigurationBoolForKey:MDM_KEY_CONTACT_SYNC]; if ([contactSync isKindOfClass:[NSNumber class]]) { userSettings.syncContacts = contactSync.boolValue; } if ([self existsMdmKey:MDM_KEY_DISABLE_SAVE_TO_GALLERY]) { userSettings.autoSaveMedia = ![self disableSaveToGallery]; } if ([self existsMdmKey:MDM_KEY_DISABLE_MESSAGE_PREVIEW]) { userSettings.pushDecrypt = ![self disableMessagePreview]; } if ([self existsMdmKey:MDM_KEY_DISABLE_CALLS]) { userSettings.enableThreemaCall = ![self disableCalls]; } if ([self existsMdmKey:MDM_KEY_DISABLE_VIDEO_CALLS]) { userSettings.enableVideoCall= ![self disableVideoCalls]; } if ([self existsMdmKey:MDM_KEY_DISABLE_SEND_PROFILE_PICTURE]) { if ([self disableSendProfilePicture]) { [userSettings setSendProfilePicture:SendProfilePictureNone]; } } } - (void)loadLicenseInfo { if (![self isManaged]) { return; } NSString *licenseUsername = [self getMdmConfigurationValueForKey:MDM_KEY_LICENSE_USERNAME]; if ([licenseUsername isKindOfClass:[NSString class]] && licenseUsername.length > 0) { [LicenseStore sharedLicenseStore].licenseUsername = licenseUsername; } NSString *licensePassword = [self getMdmConfigurationValueForKey:MDM_KEY_LICENSE_PASSWORD]; if ([licensePassword isKindOfClass:[NSString class]] && licensePassword.length > 0) { [LicenseStore sharedLicenseStore].licensePassword = licensePassword; } } - (void)loadIDCreationValues { if (![self isManaged]) { return; } MyIdentityStore *identityStore = [MyIdentityStore sharedMyIdentityStore]; NSString *nickname = [self getMdmConfigurationValueForKey:MDM_KEY_NICKNAME]; if ([nickname isKindOfClass:[NSString class]] && nickname.length > 0 && nickname.length < 32) { identityStore.pushFromName = nickname; } NSNumber *contactSync = [self getMdmConfigurationBoolForKey:MDM_KEY_CONTACT_SYNC]; if ([contactSync isKindOfClass:[NSNumber class]]) { [UserSettings sharedUserSettings].syncContacts = contactSync.boolValue; } NSString *email = [self getMdmConfigurationValueForKey:MDM_KEY_LINKED_EMAIL]; if ([email isKindOfClass:[NSString class]] && email.length > 0) { identityStore.createIDEmail = email; } NSString *phone = [self getMdmConfigurationValueForKey:MDM_KEY_LINKED_PHONE]; if ([phone isKindOfClass:[NSString class]] && phone.length > 0) { identityStore.createIDPhone = phone; } NSString *firstName = [self getMdmConfigurationValueForKey:MDM_KEY_FIRST_NAME]; if ([firstName isKindOfClass:[NSString class]]) { identityStore.firstName = firstName; } NSString *lastName = [self getMdmConfigurationValueForKey:MDM_KEY_LAST_NAME]; if ([lastName isKindOfClass:[NSString class]]) { identityStore.lastName = lastName; } NSString *csi = [self getMdmConfigurationValueForKey:MDM_KEY_CSI]; if ([csi isKindOfClass:[NSString class]]) { identityStore.csi = csi; } NSString *category = [self getMdmConfigurationValueForKey:MDM_KEY_CATEGORY]; if ([category isKindOfClass:[NSString class]]) { identityStore.category = category; } [[LicenseStore sharedLicenseStore] performUpdateWorkInfo]; } - (BOOL)hasIDBackup { if (![self isManaged]) { return NO; } _idBackup = [self getMdmConfigurationValueForKey:MDM_KEY_ID_BACKUP]; if ([_idBackup isKindOfClass:[NSString class]] == NO || _idBackup.length < 1) { return NO; } _idBackupPassword = [self getMdmConfigurationValueForKey:MDM_KEY_ID_BACKUP_PASSWORD]; return YES; } - (void)restoreIDBackupOnCompletion:(void(^)(void))onCompletion onError:(void(^)(NSError *error))onError { if ([self hasIDBackup] == NO) { return; } MyIdentityStore *identityStore = [MyIdentityStore sharedMyIdentityStore]; identityStore.pendingCreateID = YES; [identityStore restoreFromBackup:_idBackup withPassword:_idBackupPassword onCompletion:^{ ServerAPIConnector *apiConnector = [[ServerAPIConnector alloc] init]; /* Obtain server group from server */ [apiConnector updateMyIdentityStore:identityStore onCompletion:^{ [identityStore storeInKeychain]; onCompletion(); } onError:^(NSError *error) { onError(error); }]; } onError:^(NSError *error) { onError(error); }]; } - (BOOL)isManaged { NSDictionary *mdm = [self getMdmConfiguration]; return isLicenseRequired && mdm != nil && [mdm count] > 0; } - (BOOL)existsMdmKey:(NSString*)mdmKey { NSDictionary *mdm = [self getMdmConfiguration]; return [[mdm allKeys] containsObject:mdmKey]; } /// Apply Threema MDM parameters (workData) to company MDM - (void)applyThreemaMdm:(NSDictionary *)workData { if (!isLicenseRequired) { return; } dispatch_sync(queue, ^{ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; if (workData != nil && [[workData allKeys] containsObject:MDM_KEY_THREEMA_CONFIGURATION]) { NSDictionary *threemaMdm = [defaults dictionaryForKey:MDM_THREEMA_CONFIGURATION_KEY]; NSDictionary *newThreemaMdm = workData[MDM_KEY_THREEMA_CONFIGURATION]; NSMutableDictionary *currentThreemaMdm = [[NSMutableDictionary alloc] initWithDictionary:threemaMdm]; if (currentThreemaMdm == nil || [currentThreemaMdm count] == 0) { // use new Threema MDM threemaMdm = newThreemaMdm; } else if([currentThreemaMdm isEqualToDictionary:newThreemaMdm] == NO) { // remove missing Threema MDM parameters NSMutableArray *missingMdmKeys = [[NSMutableArray alloc] init]; NSMutableDictionary *currentThreemaMdmParameters = [[NSMutableDictionary alloc] initWithDictionary:currentThreemaMdm[MDM_KEY_THREEMA_PARAMS]]; [currentThreemaMdmParameters enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { if ([[newThreemaMdm[MDM_KEY_THREEMA_PARAMS] allKeys] containsObject:key] == NO) { [missingMdmKeys addObject:key]; } }]; [currentThreemaMdmParameters removeObjectsForKeys:missingMdmKeys]; [currentThreemaMdm setObject:currentThreemaMdmParameters forKey:MDM_KEY_THREEMA_PARAMS]; // apply new Threema MDM parameters BOOL override = ((NSNumber *)newThreemaMdm[MDM_KEY_THREEMA_OVERRIDE]).boolValue; NSDictionary *newThreemaMdmParameters = [self applyMdmParameters:currentThreemaMdm[MDM_KEY_THREEMA_PARAMS] source:newThreemaMdm[MDM_KEY_THREEMA_PARAMS] override:override]; NSMutableDictionary *newThreemaMdmConfiguration = [[NSMutableDictionary alloc] initWithDictionary:threemaMdm]; [newThreemaMdmConfiguration setObject:newThreemaMdmParameters forKey:MDM_KEY_THREEMA_PARAMS]; [newThreemaMdmConfiguration setObject:[NSNumber numberWithBool:override] forKey:MDM_KEY_THREEMA_OVERRIDE]; threemaMdm = newThreemaMdmConfiguration; } // store Threema MDM [defaults setObject:threemaMdm forKey:MDM_THREEMA_CONFIGURATION_KEY]; [defaults synchronize]; } else { [defaults removeObjectForKey:MDM_THREEMA_CONFIGURATION_KEY]; [defaults synchronize]; } [MDMSetup clearMdmCache]; }); [self loadIDCreationValues]; [self loadRenewableValues]; } - (NSDictionary*)getMdmCompany { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; return [defaults dictionaryForKey:MDM_CONFIGURATION_KEY]; /// fake company MDM parameters here // NSDictionary *companyMdm = [[NSDictionary alloc] initWithObjectsAndKeys:@1, MDM_KEY_READONLY_PROFILE, @"123456", MDM_KEY_LICENSE_PASSWORD, @"po", MDM_KEY_LICENSE_USERNAME, @"test@test.ch", MDM_KEY_LINKED_EMAIL, @"+41791112233", MDM_KEY_LINKED_PHONE, @"id_pass", MDM_KEY_ID_BACKUP_PASSWORD, @"safe_pass", MDM_KEY_SAFE_PASSWORD, @"server_pass", MDM_KEY_SAFE_SERVER_PASSWORD, @0, MDM_KEY_DISABLE_VIDEO_CALLS, nil]; // return companyMdm; } - (NSDictionary*)getMdmConfiguration { __block NSDictionary *mdm; dispatch_sync(queue, ^{ if (isLicenseRequired) { if (!isSetup) { if (_mdmCache == nil) { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; _mdmCache = [self getMdmParameters:[self getMdmCompany] threemMdm:[defaults dictionaryForKey:MDM_THREEMA_CONFIGURATION_KEY]]; } mdm = _mdmCache; } else { if (_mdmCacheSetup == nil) { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; _mdmCacheSetup = [self getMdmParameters:[self getMdmCompany] threemMdm:[defaults dictionaryForKey:MDM_THREEMA_CONFIGURATION_KEY]]; } mdm = _mdmCacheSetup; } } else { mdm = [[NSDictionary alloc] init]; } }); return mdm; } - (NSDictionary*)getMdmParameters:(NSDictionary*)companyMdm threemMdm:(NSDictionary*)threemaMdm { if (companyMdm != nil) { if (threemaMdm != nil) { // merge company and Threema MDM BOOL override = ((NSNumber *)threemaMdm[MDM_KEY_THREEMA_OVERRIDE]).boolValue; NSDictionary *newMdm = [self applyMdmParameters:companyMdm source:threemaMdm[MDM_KEY_THREEMA_PARAMS] override:override]; DDLogNotice(@"\nCompany MDM"); [self printMDMIntoDebugLog:companyMdm]; DDLogNotice(@"\nThreema MDM"); [self printMDMIntoDebugLog:threemaMdm]; DDLogNotice(@"\nMerged Company and Threema MDM"); [self printMDMIntoDebugLog:newMdm]; return newMdm; } else { // use Company MDM DDLogNotice(@"\nCompany MDM"); [self printMDMIntoDebugLog:companyMdm]; return companyMdm; } } else if (threemaMdm != nil && [[threemaMdm allKeys] containsObject:MDM_KEY_THREEMA_PARAMS]) { // use Threema MDM NSDictionary *destinationMdm = [[NSDictionary alloc] init]; BOOL override = ((NSNumber *)threemaMdm[MDM_KEY_THREEMA_OVERRIDE]).boolValue; NSDictionary *newMdm = [self applyMdmParameters:destinationMdm source:threemaMdm[MDM_KEY_THREEMA_PARAMS] override:override]; DDLogNotice(@"\nThreema MDM"); [self printMDMIntoDebugLog:newMdm]; return newMdm; } // print empty mdm DDLogNotice(@"\nCompany and Threema MDM is empty"); return [[NSDictionary alloc] init]; } - (NSDictionary*)applyMdmParameters:(NSDictionary*)destination source:(NSDictionary*)source override:(BOOL)override { NSMutableDictionary *mdmParameters = [[NSMutableDictionary alloc] initWithDictionary:destination]; // apply parameter if is override and renweable or missing [[source allKeys] enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL * _Nonnull stop) { if (override == YES && [self isRenewable:key]) { if ([[mdmParameters allKeys] containsObject:key]) { [mdmParameters setValue:source[key] forKey:key]; } else { [mdmParameters addEntriesFromDictionary:@{key: source[key]}]; } } if (isSetup == YES && [[mdmParameters allKeys] containsObject:key] == NO) { [mdmParameters addEntriesFromDictionary:@{key: source[key]}]; } else if (isSetup == YES && override == YES && [[mdmParameters allKeys] containsObject:key] == YES) { [mdmParameters setValue:source[key] forKey:key]; } }]; return mdmParameters; } - (void)deleteThreemaMdm { dispatch_sync(queue, ^{ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults removeObjectForKey:MDM_THREEMA_CONFIGURATION_KEY]; [defaults synchronize]; [MDMSetup clearMdmCache]; }); } - (BOOL)isRenewable:(NSString *)mdmKey { NSArray *renewableKeys = @[MDM_KEY_LICENSE_USERNAME, MDM_KEY_LICENSE_PASSWORD, MDM_KEY_NICKNAME, MDM_KEY_FIRST_NAME, MDM_KEY_LAST_NAME, MDM_KEY_CSI, MDM_KEY_CATEGORY, MDM_KEY_READONLY_PROFILE, MDM_KEY_BLOCK_UNKNOWN, MDM_KEY_HIDE_INACTIVE_IDS, MDM_KEY_DISABLE_SAVE_TO_GALLERY, MDM_KEY_DISABLE_ADD_CONTACT, MDM_KEY_DISABLE_EXPORT, MDM_KEY_DISABLE_BACKUPS, MDM_KEY_DISABLE_ID_EXPORT, MDM_KEY_DISABLE_SYSTEM_BACKUPS, MDM_KEY_DISABLE_MESSAGE_PREVIEW, MDM_KEY_DISABLE_SEND_PROFILE_PICTURE, MDM_KEY_DISABLE_CALLS, MDM_KEY_DISABLE_CREATE_GROUP, MDM_KEY_DISABLE_WEB, MDM_KEY_WEB_HOSTS, MDM_KEY_SAFE_ENABLE, MDM_KEY_SAFE_PASSWORD, MDM_KEY_SAFE_SERVER_URL, MDM_KEY_SAFE_SERVER_USERNAME, MDM_KEY_SAFE_SERVER_PASSWORD, MDM_KEY_SAFE_PASSWORD_PATTERN, MDM_KEY_SAFE_PASSWORD_MESSAGE, MDM_KEY_DISABLE_SHARE_MEDIA, MDM_KEY_CONTACT_SYNC, MDM_KEY_DISABLE_VIDEO_CALLS]; return [renewableKeys containsObject:mdmKey]; } - (NSNumber*)getMdmConfigurationBoolForKey:(NSString*)key { // Some MDMs cannot send real booleans, so we support "true"/"1" and "false"/"0" as strings also id mdmVal = [self getMdmConfigurationValueForKey:key]; if ([mdmVal isKindOfClass:[NSNumber class]]) { return mdmVal; } else if ([mdmVal isKindOfClass:[NSString class]]) { if ([mdmVal caseInsensitiveCompare:@"true"] == NSOrderedSame || [mdmVal isEqualToString:@"1"]) { return [NSNumber numberWithBool:YES]; } else if ([mdmVal caseInsensitiveCompare:@"false"] == NSOrderedSame || [mdmVal isEqualToString:@"0"]) { return [NSNumber numberWithBool:NO]; } } return nil; } - (id)getMdmConfigurationValueForKey:(NSString*)key { NSDictionary *mdm = [self getMdmConfiguration]; return mdm[key]; } - (void)printMDMIntoDebugLog:(NSDictionary *)mdm { if ([[mdm allKeys] containsObject:MDM_KEY_THREEMA_PARAMS]) { BOOL override = ((NSNumber *)mdm[MDM_KEY_THREEMA_OVERRIDE]).boolValue; DDLogNotice(@"%@: %@", MDM_KEY_THREEMA_OVERRIDE, override ? @"true" : @"false"); for(NSString *key in mdm[MDM_KEY_THREEMA_PARAMS]) { [self checkIsPasswordAndLog:key value:[mdm[MDM_KEY_THREEMA_PARAMS] objectForKey:key]]; } } else { for(NSString *key in mdm) { [self checkIsPasswordAndLog:key value:[mdm objectForKey:key]]; } } } - (void)checkIsPasswordAndLog:(NSString *)key value:(NSString *)value { if (![key containsString:@"pass"] || [key isEqualToString:MDM_KEY_SAFE_PASSWORD_PATTERN] || [key isEqualToString:MDM_KEY_SAFE_PASSWORD_MESSAGE] || value == nil) { DDLogNotice(@"%@: %@", key, value); } else { DDLogNotice(@"%@: ***", key); } } @end