2424// https://github.com/grahamgilbert/crypt/
2525
2626import Foundation
27+ import Network
2728import Security
2829import os. log
2930
@@ -105,11 +106,104 @@ class BBMechanism: NSObject {
105106 return s. replacingOccurrences ( of: " \0 " , with: " " ) as NSString
106107 }
107108
108- // profiles Errors
109- private enum ProfilesError : Error {
110- case profilesFailed( retCode: Int32 )
111- case outputPlistNull
112- case outputPlistMalformed
109+ // Get MDM Server FQDN and port:
110+ func getMDMServerDetails( ) -> ( fqdn: String , port: UInt16 ) ? {
111+ os_log ( " Getting MDM server details… " , log: BBMechanism . log, type: . default)
112+ let task = Process ( )
113+ task. launchPath = " /usr/libexec/mdmclient "
114+ task. arguments = [ " DumpManagementStatus " ]
115+ let pipe = Pipe ( )
116+ task. standardOutput = pipe
117+ task. standardError = Pipe ( )
118+ do {
119+ try task. run ( )
120+ task. waitUntilExit ( )
121+
122+ let exitStatus = task. terminationStatus
123+ if exitStatus != 0 {
124+ os_log ( " ERROR: mdmclient failed with non-zero exit status: %d " , log: BBMechanism . log, type: . error, exitStatus)
125+ return nil
126+ }
127+
128+ let data = pipe. fileHandleForReading. readDataToEndOfFile ( )
129+
130+ guard let output: String = String ( data: data, encoding: String . Encoding. utf8) else {
131+ os_log ( " Unable to decode output. " , log: BBMechanism . log, type: . error)
132+ return nil
133+ }
134+
135+ let regex = try NSRegularExpression ( pattern: #"ServerURL = "(https?://[^"]+)""# , options: [ ] )
136+ guard let match = regex. firstMatch ( in: output, options: [ ] , range: NSRange ( output. startIndex... , in: output) ) else {
137+ os_log ( " ServerURL not found. " , log: BBMechanism . log, type: . error)
138+ return nil
139+ }
140+
141+ guard let matchRange = Range ( match. range ( at: 1 ) , in: output) else {
142+ os_log ( " Invalid regex match, crashing prevented. " , log: BBMechanism . log, type: . error)
143+ return nil
144+ }
145+
146+ let urlString = String ( output [ matchRange] )
147+ os_log ( " Extracted ServerURL: %{public}@ " , log: BBMechanism . log, type: . debug, urlString)
148+
149+ if let urlComponents = URLComponents ( string: urlString) , let host = urlComponents. host {
150+ let port = urlComponents. port ?? 443
151+ os_log ( " Parsed FQDN: %{public}@, Port: %d " , log: BBMechanism . log, type: . debug, host, port)
152+ return ( fqdn: host, port: UInt16 ( port) )
153+ } else {
154+ os_log ( " Failed to parse URL components. " , log: BBMechanism . log, type: . error)
155+ }
156+ } catch {
157+ os_log ( " ERROR: %{public}@ " , log: BBMechanism . log, type: . error, error. localizedDescription)
158+ }
159+ return nil
160+ }
161+
162+ // Function to check if the FQDN is reachable on the given port with detailed logging
163+ func checkMDMReachability( fqdn: String , port: UInt16 ) -> ( Bool ) {
164+ os_log ( " Checking reachability of %{public}@ on port %{public}@… " , log: BBMechanism . log,
165+ type: . default, String ( fqdn) , String ( port) )
166+ guard let nwPort = NWEndpoint . Port ( rawValue: port) else {
167+ os_log ( " Invalid port number: %{public}@ " , log: BBMechanism . log, type: . error, String ( port) )
168+ return false
169+ }
170+ let connection = NWConnection ( host: NWEndpoint . Host ( fqdn) , port: nwPort, using: . tcp)
171+ let semaphore = DispatchSemaphore ( value: 0 )
172+ let queue = DispatchQueue ( label: " com.inetum.Bootstrap-Buddy.checkMDM " )
173+ var isReachable = false
174+ connection. stateUpdateHandler = { state in
175+ queue. sync {
176+ switch state {
177+ case . setup:
178+ os_log ( " Connection setup initiated… " , log: BBMechanism . log, type: . debug)
179+ case . waiting( let reason) :
180+ os_log ( " Connection waiting: %{public}@ " , log: BBMechanism . log, type: . debug, reason. localizedDescription)
181+ case . preparing:
182+ os_log ( " Preparing connection… " , log: BBMechanism . log, type: . debug)
183+ case . ready:
184+ os_log ( " Connection successful! " , log: BBMechanism . log, type: . default)
185+ isReachable = true
186+ semaphore. signal ( )
187+ case . failed( let error) :
188+ os_log ( " Connection failed: %{public}@ " , log: BBMechanism . log, type: . error, error. localizedDescription)
189+ semaphore. signal ( )
190+ case . cancelled:
191+ os_log ( " Connection was cancelled. " , log: BBMechanism . log, type: . debug)
192+ default :
193+ os_log ( " Unexpected connection state: %{public}@ " , log: BBMechanism . log, type: . error, String ( describing: state) )
194+ }
195+ }
196+ }
197+ connection. start ( queue: . global( ) )
198+ // Wait up to 5 seconds for a response:
199+ let timeout = DispatchTime . now ( ) + 5
200+ if semaphore. wait ( timeout: timeout) == . timedOut {
201+ os_log ( " Timeout: No response received in 5 seconds. " , log: BBMechanism . log, type: . error)
202+ connection. cancel ( )
203+ return false
204+ }
205+ connection. cancel ( )
206+ return isReachable
113207 }
114208
115209 // Check Bootstrap Token status, whether it's supported and escrowed:
@@ -121,6 +215,7 @@ class BBMechanism: NSObject {
121215 let pipe = Pipe ( )
122216 task. standardOutput = pipe
123217 task. launch ( )
218+ task. waitUntilExit ( )
124219 let data = pipe. fileHandleForReading. readDataToEndOfFile ( )
125220 guard let output: String = String ( data: data, encoding: String . Encoding. utf8)
126221 else { return ( false , false ) }
@@ -147,6 +242,7 @@ class BBMechanism: NSObject {
147242 let pipe = Pipe ( )
148243 task. standardOutput = pipe
149244 task. launch ( )
245+ task. waitUntilExit ( )
150246 let data = pipe. fileHandleForReading. readDataToEndOfFile ( )
151247 guard let output: String = String ( data: data, encoding: String . Encoding. utf8)
152248 else { return false }
@@ -158,4 +254,57 @@ class BBMechanism: NSObject {
158254 return false
159255 }
160256 }
257+
258+ // Check if user at login window is and admin:
259+ func checkAdminStatus( username: String ) -> ( Bool ) {
260+ os_log ( " Checking if user \" %{public}@ \" is an admin… " , log: BBMechanism . log, type: . default, username)
261+ let task = Process ( )
262+ task. launchPath = " /usr/bin/dscl "
263+ task. arguments = [ " . " , " read " , " /Groups/admin " , " GroupMembership " ]
264+ let pipe = Pipe ( )
265+ task. standardOutput = pipe
266+ task. launch ( )
267+ task. waitUntilExit ( )
268+ let data = pipe. fileHandleForReading. readDataToEndOfFile ( )
269+ guard let output: String = String ( data: data, encoding: String . Encoding. utf8)
270+ else { return false }
271+ // Check if the username is in the output:
272+ os_log ( " dscl output: %{public}@ " , log: BBMechanism . log, type: . debug, output)
273+ os_log ( " Admin Status: %{public}@ " , log: BBMechanism . log, type: . debug, output. contains ( username) . description)
274+ return output. contains ( username)
275+ }
276+
277+ // Elevate user to admin
278+ func elevateUser( username: String ) throws {
279+ os_log ( " Temporarily adding user \" %{public}@ \" to admin group… " , log: BBMechanism . log, type: . default, username)
280+ let task = Process ( )
281+ task. launchPath = " /usr/bin/dscl "
282+ task. arguments = [ " . " , " append " , " /Groups/admin " , " GroupMembership " , username]
283+ task. launch ( )
284+ task. waitUntilExit ( )
285+ if task. terminationStatus != 0 {
286+ let termstatus = String ( describing: task. terminationStatus)
287+ os_log (
288+ " User elevation failed with a non-zero exit status: %{public}@ " ,
289+ log: BBMechanism . log, type: . error, termstatus)
290+ }
291+ os_log ( " User \" %{public}@ \" elevated to admin. " , log: BBMechanism . log, type: . default, username)
292+ }
293+
294+ // Demote user to standard
295+ func demoteUser( username: String ) throws {
296+ os_log ( " Removing user \" %{public}@ \" from admin group… " , log: BBMechanism . log, type: . default, username)
297+ let task = Process ( )
298+ task. launchPath = " /usr/bin/dscl "
299+ task. arguments = [ " . " , " delete " , " /Groups/admin " , " GroupMembership " , username]
300+ task. launch ( )
301+ task. waitUntilExit ( )
302+ if task. terminationStatus != 0 {
303+ let termstatus = String ( describing: task. terminationStatus)
304+ os_log (
305+ " User demotion failed with a non-zero exit status: %{public}@ " ,
306+ log: BBMechanism . log, type: . error, termstatus)
307+ }
308+ os_log ( " User \" %{public}@ \" demoted to standard. " , log: BBMechanism . log, type: . default, username)
309+ }
161310}
0 commit comments