]> git.saurik.com Git - apple/security.git/blobdiff - keychain/TrustedPeersHelper/Container_MachineIDs.swift
Security-59754.80.3.tar.gz
[apple/security.git] / keychain / TrustedPeersHelper / Container_MachineIDs.swift
index 7c015f98c323906647d72b844ac8a047d6ad79cd..b684c77ea4df5b989075a19b15456928de1f4593 100644 (file)
@@ -1,4 +1,3 @@
-
 import CoreData
 import Foundation
 
@@ -8,7 +7,7 @@ extension MachineMO {
             return false
         }
 
-        let dateLimit = Date(timeIntervalSinceNow: -60*60*TimeInterval(hours))
+        let dateLimit = Date(timeIntervalSinceNow: -60 * 60 * TimeInterval(hours))
         return modifiedDate.compare(dateLimit) == ComparisonResult.orderedDescending
     }
 
@@ -58,7 +57,7 @@ extension Container {
 
         // Once we run this upgrade, we will set the allowed bool to false, since it's unused.
         // Therefore, if we have a single record with "allowed" set, we haven't run the upgrade.
-        let runUpgrade = knownMachineMOs.filter { $0.allowed }.count > 0
+        let runUpgrade = knownMachineMOs.contains { $0.allowed }
         if runUpgrade {
             knownMachineMOs.forEach { mo in
                 if mo.allowed {
@@ -71,15 +70,29 @@ extension Container {
         }
     }
 
-    func setAllowedMachineIDs(_ allowedMachineIDs: Set<String>, reply: @escaping (Bool, Error?) -> Void) {
+    func enforceIDMSListChanges(knownMachines: Set<MachineMO>) -> Bool {
+        if self.containerMO.honorIDMSListChanges == "YES"{
+            return true
+        } else if self.containerMO.honorIDMSListChanges == "NO" {
+            return false
+        } else if self.containerMO.honorIDMSListChanges == "UNKNOWN" && knownMachines.isEmpty {
+            return false
+        } else if self.containerMO.honorIDMSListChanges == "UNKNOWN" && !knownMachines.isEmpty {
+            return true
+        } else {
+            return true
+        }
+    }
+
+    func setAllowedMachineIDs(_ allowedMachineIDs: Set<String>, honorIDMSListChanges: Bool, reply: @escaping (Bool, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Bool, Error?) -> Void = {
-            os_log("setAllowedMachineIDs complete: %@", log: tplogTrace, type: .info, traceError($1))
+            os_log("setAllowedMachineIDs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             reply($0, $1)
         }
 
-        os_log("Setting allowed machine IDs: %@", log: tplogDebug, type: .default, allowedMachineIDs)
+        os_log("Setting allowed machine IDs: %{public}@", log: tplogDebug, type: .default, allowedMachineIDs)
 
         // Note: we currently ignore any machineIDs that are set in the model, but never appeared on the
         // Trusted Devices list. We should give them a grace period (1wk?) then kick them out.
@@ -87,21 +100,21 @@ extension Container {
         self.moc.performAndWait {
             do {
                 var differences = false
+                self.containerMO.honorIDMSListChanges = honorIDMSListChanges ? "YES" : "NO"
 
                 var knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
-                let knownMachineIDs = Set(knownMachines.compactMap { $0.machineID } )
-
+                let knownMachineIDs = Set(knownMachines.compactMap { $0.machineID })
 
                 knownMachines.forEach { machine in
                     guard let mid = machine.machineID else {
-                        os_log("Machine has no ID: %@", log: tplogDebug, type: .default, machine)
+                        os_log("Machine has no ID: %{public}@", log: tplogDebug, type: .default, machine)
                         return
                     }
                     if allowedMachineIDs.contains(mid) {
                         if machine.status == TPMachineIDStatus.allowed.rawValue {
-                            os_log("Machine ID still trusted: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                            os_log("Machine ID still trusted: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                         } else {
-                            os_log("Machine ID newly retrusted: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                            os_log("Machine ID newly retrusted: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                             differences = true
                         }
                         machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
@@ -117,25 +130,23 @@ extension Container {
                             if machine.seenOnFullList {
                                 machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
                                 machine.modified = Date()
-                                os_log("Newly distrusted machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                                os_log("Newly distrusted machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                                 differences = true
-
                             } else {
                                 if machine.modifiedInPast(hours: cutoffHours) {
-                                    os_log("Allowed-but-unseen machine ID isn't on full list, last modified %@, ignoring: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+                                    os_log("Allowed-but-unseen machine ID isn't on full list, last modified %{public}@, ignoring: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
                                 } else {
-                                    os_log("Allowed-but-unseen machine ID isn't on full list, last modified %@, distrusting: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+                                    os_log("Allowed-but-unseen machine ID isn't on full list, last modified %{public}@, distrusting: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
                                     machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
                                     machine.modified = Date()
                                     differences = true
                                 }
                             }
-
                         } else if machine.status == TPMachineIDStatus.unknown.rawValue {
                             if machine.modifiedInPast(hours: cutoffHours) {
-                                os_log("Unknown machine ID last modified %@; leaving unknown: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+                                os_log("Unknown machine ID last modified %{public}@; leaving unknown: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
                             } else {
-                                os_log("Unknown machine ID last modified %@; distrusting: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+                                os_log("Unknown machine ID last modified %{public}@; distrusting: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
                                 machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
                                 machine.modified = Date()
                                 differences = true
@@ -146,7 +157,7 @@ extension Container {
 
                 // Do we need to create any further objects?
                 allowedMachineIDs.forEach { machineID in
-                    if(!knownMachineIDs.contains(machineID)) {
+                    if !knownMachineIDs.contains(machineID) {
                         // We didn't know about this machine before; it's newly trusted!
                         let machine = MachineMO(context: self.moc)
                         machine.machineID = machineID
@@ -154,7 +165,7 @@ extension Container {
                         machine.seenOnFullList = true
                         machine.modified = Date()
                         machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
-                        os_log("Newly trusted machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                        os_log("Newly trusted machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                         differences = true
 
                         self.containerMO.addToMachines(machine)
@@ -162,21 +173,25 @@ extension Container {
                     }
                 }
 
-                // Now, are there any machine IDs in the model that aren't in the list? If so, add them as "unknown"
-                let modelMachineIDs = self.model.allMachineIDs()
-                modelMachineIDs.forEach { peerMachineID in
-                    if !knownMachineIDs.contains(peerMachineID) && !allowedMachineIDs.contains(peerMachineID) {
-                        os_log("Peer machineID is unknown, beginning grace period: %@", log: tplogDebug, type: .default, peerMachineID)
-                        let machine = MachineMO(context: self.moc)
-                        machine.machineID = peerMachineID
-                        machine.container = containerMO
-                        machine.seenOnFullList = false
-                        machine.modified = Date()
-                        machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
-                        differences = true
+                if self.enforceIDMSListChanges(knownMachines: knownMachines) {
+                    // Are there any machine IDs in the model that aren't in the list? If so, add them as "unknown"
+                    let modelMachineIDs = self.model.allMachineIDs()
+                    modelMachineIDs.forEach { peerMachineID in
+                        if !knownMachineIDs.contains(peerMachineID) && !allowedMachineIDs.contains(peerMachineID) {
+                            os_log("Peer machineID is unknown, beginning grace period: %{public}@", log: tplogDebug, type: .default, peerMachineID)
+                            let machine = MachineMO(context: self.moc)
+                            machine.machineID = peerMachineID
+                            machine.container = containerMO
+                            machine.seenOnFullList = false
+                            machine.modified = Date()
+                            machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
+                            differences = true
 
-                        self.containerMO.addToMachines(machine)
+                            self.containerMO.addToMachines(machine)
+                        }
                     }
+                } else {
+                    os_log("Believe we're in a demo account, not enforcing IDMS list", log: tplogDebug, type: .default)
                 }
 
                 // We no longer use allowed machine IDs.
@@ -186,7 +201,7 @@ extension Container {
 
                 reply(differences, nil)
             } catch {
-                os_log("Error setting machine ID list: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                os_log("Error setting machine ID list: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                 reply(false, error)
             }
         }
@@ -195,17 +210,17 @@ extension Container {
     func addAllow(_ machineIDs: [String], reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("addAllow complete: %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("addAllow complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
 
-        os_log("Adding allowed machine IDs: %@", log: tplogDebug, type: .default, machineIDs)
+        os_log("Adding allowed machine IDs: %{public}@", log: tplogDebug, type: .default, machineIDs)
 
         self.moc.performAndWait {
             do {
                 var knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
-                let knownMachineIDs = Set(knownMachines.compactMap { $0.machineID } )
+                let knownMachineIDs = Set(knownMachines.compactMap { $0.machineID })
 
                 // We treat an add push as authoritative (even though we should really confirm it with a full list fetch).
                 // We can get away with this as we're using this list as a deny-list, and if we accidentally don't deny someone fast enough, that's okay.
@@ -215,10 +230,9 @@ extension Container {
                             if machine.machineID == machineID {
                                 machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
                                 machine.modified = Date()
-                                os_log("Continue to trust machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                                os_log("Continue to trust machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                             }
                         }
-
                     } else {
                         let machine = MachineMO(context: self.moc)
                         machine.machineID = machineID
@@ -226,7 +240,7 @@ extension Container {
                         machine.seenOnFullList = false
                         machine.modified = Date()
                         machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
-                        os_log("Newly trusted machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                        os_log("Newly trusted machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                         self.containerMO.addToMachines(machine)
 
                         knownMachines.insert(machine)
@@ -244,17 +258,17 @@ extension Container {
     func removeAllow(_ machineIDs: [String], reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("removeAllow complete: %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("removeAllow complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
 
-        os_log("Removing allowed machine IDs: %@", log: tplogDebug, type: .default, machineIDs)
+        os_log("Removing allowed machine IDs: %{public}@", log: tplogDebug, type: .default, machineIDs)
 
         self.moc.performAndWait {
             do {
                 var knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
-                let knownMachineIDs = Set(knownMachines.compactMap { $0.machineID } )
+                let knownMachineIDs = Set(knownMachines.compactMap { $0.machineID })
 
                 // This is an odd approach: we'd like to confirm that this MID was actually removed (and not just a delayed push).
                 // So, let's set the status to "unknown", and its modification date to the distant past.
@@ -265,17 +279,16 @@ extension Container {
                             if machine.machineID == machineID {
                                 machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
                                 machine.modified = Date.distantPast
-                                os_log("Now suspicious of machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                                os_log("Now suspicious of machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                             }
                         }
-
                     } else {
                         let machine = MachineMO(context: self.moc)
                         machine.machineID = machineID
                         machine.container = containerMO
                         machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
                         machine.modified = Date.distantPast
-                        os_log("Suspicious of new machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                        os_log("Suspicious of new machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                         self.containerMO.addToMachines(machine)
 
                         knownMachines.insert(machine)
@@ -290,28 +303,46 @@ extension Container {
         }
     }
 
+    func fetchAllowedMachineIDs(reply: @escaping (Set<String>?, Error?) -> Void) {
+        self.semaphore.wait()
+        let reply: (Set<String>?, Error?) -> Void = {
+            os_log("fetchAllowedMachineIDs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
+            self.semaphore.signal()
+            reply($0, $1)
+        }
+
+        os_log("Fetching allowed machine IDs", log: tplogDebug, type: .default)
+
+        self.moc.performAndWait {
+            let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
+            let allowedMachineIDs = knownMachines.filter { $0.status == Int64(TPMachineIDStatus.allowed.rawValue) }.compactMap { $0.machineID }
+
+            reply(Set(allowedMachineIDs), nil)
+        }
+    }
+
     func onqueueMachineIDAllowedByIDMS(machineID: String) -> Bool {
+
         // For Demo accounts, if the list is entirely empty, then everything is allowed
-        let machines = containerMO.machines as? Set<MachineMO> ?? Set()
-        if machines.count == 0 {
-            os_log("machineID list is empty; allowing %@", log: tplogDebug, type: .debug, machineID)
+        let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
+
+        if !self.enforceIDMSListChanges(knownMachines: knownMachines) {
+            os_log("not enforcing idms list changes; allowing %{public}@", log: tplogDebug, type: .debug, machineID)
             return true
         }
 
         // Note: this function rejects grey devices: machineIDs that are neither allowed nor disallowed
-        for mo in machines {
-            if mo.machineID == machineID {
-                if mo.status == TPMachineIDStatus.allowed.rawValue {
-                    return true
-                } else {
-                    os_log("machineID %@ not explicitly allowed: %@", log: tplogDebug, type: .debug, machineID, mo)
-                    return false
-                }
+        for mo in knownMachines where mo.machineID == machineID {
+            if mo.status == TPMachineIDStatus.allowed.rawValue {
+                return true
+            } else {
+                os_log("machineID %{public}@ not explicitly allowed: %{public}@", log: tplogDebug, type: .debug, machineID, mo)
+                return false
             }
         }
 
         // Didn't find it? reject.
-        os_log("machineID %@ not found on list", log: tplogDebug, type: .debug, machineID)
+        os_log("machineID %{public}@ not found on list", log: tplogDebug, type: .debug, machineID)
         return false
     }
 
@@ -331,15 +362,15 @@ extension Container {
         // We don't want to automatically kick out new peers if they rejoin with the same MID.
 
         let machines = containerMO.machines as? Set<MachineMO> ?? Set()
-        let knownMachineIDs = Set(machines.compactMap { $0.machineID } )
+        let knownMachineIDs = Set(machines.compactMap { $0.machineID })
 
         // Peers trust themselves. So if the ego peer is in Octagon, its machineID will be in this set
         let trustedMachineIDs = Set(dynamicInfo.includedPeerIDs.compactMap { self.model.peer(withID: $0)?.permanentInfo.machineID })
 
         // if this account is not a demo account...
-        if machines.count > 0 {
+        if self.enforceIDMSListChanges(knownMachines: machines) {
             for peerMachineID in trustedMachineIDs.subtracting(knownMachineIDs) {
-                os_log("Peer machineID is unknown, beginning grace period: %@", log: tplogDebug, type: .default, peerMachineID)
+                os_log("Peer machineID is unknown, beginning grace period: %{public}@", log: tplogDebug, type: .default, peerMachineID)
                 let machine = MachineMO(context: self.moc)
                 machine.machineID = peerMachineID
                 machine.container = self.containerMO
@@ -350,14 +381,12 @@ extension Container {
                 self.containerMO.addToMachines(machine)
             }
         } else {
-            os_log("Believe we're in a demo account; not starting an unknown machine ID timer", log: tplogDebug, type: .default)
+            os_log("Not enforcing IDMS list changes", log: tplogDebug, type: .default)
         }
 
-        for mo in (machines) {
-            if mo.status == TPMachineIDStatus.disallowed.rawValue {
-                os_log("Dropping knowledge of machineID %@", log: tplogDebug, type: .debug, String(describing: mo.machineID))
-                self.containerMO.removeFromMachines(mo)
-            }
+        for mo in (machines) where mo.status == TPMachineIDStatus.disallowed.rawValue {
+            os_log("Dropping knowledge of machineID %{public}@", log: tplogDebug, type: .debug, String(describing: mo.machineID))
+            self.containerMO.removeFromMachines(mo)
         }
     }
 
@@ -365,9 +394,14 @@ extension Container {
     // Useful means that there's an unknown MID whose modification date is before the cutoff
     // A full list fetch would either confirm it as 'untrusted' or make it trusted again
     func onqueueFullIDMSListWouldBeHelpful() -> Bool {
+
+        if self.containerMO.honorIDMSListChanges == "UNKNOWN" {
+            return true
+        }
+
         let unknownMOs = (containerMO.machines as? Set<MachineMO> ?? Set()).filter { $0.status == TPMachineIDStatus.unknown.rawValue }
         let outdatedMOs = unknownMOs.filter { !$0.modifiedInPast(hours: cutoffHours) }
 
-        return outdatedMOs.count > 0
+        return !outdatedMOs.isEmpty
     }
 }