go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/macos.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package resources
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"encoding/json"
    10  	"errors"
    11  	"io"
    12  	"strings"
    13  
    14  	"go.mondoo.com/cnquery/providers-sdk/v1/util/convert"
    15  	"go.mondoo.com/cnquery/providers/os/connection/shared"
    16  	"go.mondoo.com/cnquery/providers/os/resources/macos"
    17  	"howett.net/plist"
    18  )
    19  
    20  func (m *mqlMacos) userPreferences() (map[string]interface{}, error) {
    21  	conn := m.MqlRuntime.Connection.(shared.Connection)
    22  	preferences, err := macos.NewPreferences(conn).UserPreferences()
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	return convert.JsonToDict(preferences)
    28  }
    29  
    30  func (m *mqlMacos) userHostPreferences() (map[string]interface{}, error) {
    31  	conn := m.MqlRuntime.Connection.(shared.Connection)
    32  
    33  	preferences, err := macos.NewPreferences(conn).UserHostPreferences()
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	return convert.JsonToDict(preferences)
    39  }
    40  
    41  func (m *mqlMacos) globalAccountPolicies() (map[string]interface{}, error) {
    42  	conn := m.MqlRuntime.Connection.(shared.Connection)
    43  
    44  	cmd, err := conn.RunCommand("pwpolicy -getaccountpolicies")
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	data, err := io.ReadAll(cmd.Stdout)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	return Decode(bytes.NewReader(data))
    55  }
    56  
    57  // GetPreferences returns the time machine preferences
    58  //
    59  // NOTE: this cannot be implemented via:
    60  // parse.plist('/Library/Preferences/com.apple.TimeMachine.plist').params['AutoBackup'] == 1
    61  // since the binary is missing the Full Disk Access (FDA), therefore even applications with
    62  // sudo permissions cannot access the file. Instead we need to call
    63  // defaults read /Library/Preferences/com.apple.TimeMachine.plist which has FDA
    64  // see https://developer.apple.com/forums/thread/108348
    65  func (m *mqlMacosTimemachine) preferences() (map[string]interface{}, error) {
    66  	conn := m.MqlRuntime.Connection.(shared.Connection)
    67  
    68  	cmd, err := conn.RunCommand("defaults read /Library/Preferences/com.apple.TimeMachine.plist")
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	var buf bytes.Buffer
    74  	scanner := bufio.NewScanner(cmd.Stdout)
    75  	for scanner.Scan() {
    76  		line := scanner.Text()
    77  		// skip the BackupAlias since they are not parsable when returned by the `defaults` command
    78  		if strings.HasPrefix(strings.TrimSpace(line), "BackupAlias") {
    79  			continue
    80  		}
    81  		buf.WriteString(line)
    82  	}
    83  
    84  	if err := scanner.Err(); err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	return Decode(bytes.NewReader(buf.Bytes()))
    89  }
    90  
    91  func (m *mqlMacosSystemsetup) runCmd(command string) (string, error) {
    92  	conn := m.MqlRuntime.Connection.(shared.Connection)
    93  
    94  	cmd, err := conn.RunCommand(command)
    95  	if err != nil {
    96  		return "", err
    97  	}
    98  
    99  	data, err := io.ReadAll(cmd.Stdout)
   100  	if err != nil {
   101  		return "", err
   102  	}
   103  
   104  	// NOTE: systemsetup returns exit 0 even if it does not have enough permissions
   105  	// Therefore we need to handle this case here
   106  	if strings.TrimSpace(string(data)) == "You need administrator access to run this tool... exiting!" {
   107  		return "", errors.New("macos.systemsetup needs elevated permissions")
   108  	}
   109  
   110  	return string(data), nil
   111  }
   112  
   113  func (m *mqlMacosSystemsetup) date() (string, error) {
   114  	data, err := m.runCmd("systemsetup -getdate")
   115  	return macos.SystemSetupCmdOutput{}.ParseDate(data), err
   116  }
   117  
   118  func (m *mqlMacosSystemsetup) time() (string, error) {
   119  	data, err := m.runCmd("systemsetup -gettime")
   120  	return macos.SystemSetupCmdOutput{}.ParseTime(data), err
   121  }
   122  
   123  func (m *mqlMacosSystemsetup) timeZone() (string, error) {
   124  	data, err := m.runCmd("systemsetup -gettimezone")
   125  	return macos.SystemSetupCmdOutput{}.ParseTimeZone(data), err
   126  }
   127  
   128  func (m *mqlMacosSystemsetup) usingNetworkTime() (string, error) {
   129  	data, err := m.runCmd("systemsetup -getusingnetworktime")
   130  	return macos.SystemSetupCmdOutput{}.ParseUsingNetworktTime(data), err
   131  }
   132  
   133  func (m *mqlMacosSystemsetup) networkTimeServer() (string, error) {
   134  	data, err := m.runCmd("systemsetup -getnetworktimeserver")
   135  	return macos.SystemSetupCmdOutput{}.ParseNetworkTimeServer(data), err
   136  }
   137  
   138  func (m *mqlMacosSystemsetup) sleep() ([]interface{}, error) {
   139  	data, err := m.runCmd("systemsetup -getsleep")
   140  	return convert.SliceAnyToInterface(macos.SystemSetupCmdOutput{}.ParseSleep(data)), err
   141  }
   142  
   143  func (m *mqlMacosSystemsetup) displaySleep() (string, error) {
   144  	data, err := m.runCmd("systemsetup -getdisplaysleep")
   145  	return macos.SystemSetupCmdOutput{}.ParseDisplaySleep(data), err
   146  }
   147  
   148  func (m *mqlMacosSystemsetup) harddiskSleep() (string, error) {
   149  	data, err := m.runCmd("systemsetup -getdisplaysleep")
   150  	return macos.SystemSetupCmdOutput{}.ParseHardDiskSleep(data), err
   151  }
   152  
   153  func (m *mqlMacosSystemsetup) wakeOnModem() (string, error) {
   154  	data, err := m.runCmd("systemsetup -getwakeonmodem")
   155  	return macos.SystemSetupCmdOutput{}.ParseWakeOnModem(data), err
   156  }
   157  
   158  func (m *mqlMacosSystemsetup) wakeOnNetworkAccess() (string, error) {
   159  	data, err := m.runCmd("systemsetup -getwakeonnetworkaccess")
   160  	return macos.SystemSetupCmdOutput{}.ParseWakeOnNetwork(data), err
   161  }
   162  
   163  func (m *mqlMacosSystemsetup) restartPowerFailure() (string, error) {
   164  	data, err := m.runCmd("systemsetup -getrestartpowerfailure")
   165  	return macos.SystemSetupCmdOutput{}.ParseRestartPowerFailure(data), err
   166  }
   167  
   168  func (m *mqlMacosSystemsetup) restartFreeze() (string, error) {
   169  	data, err := m.runCmd("systemsetup -getrestartfreeze")
   170  	return macos.SystemSetupCmdOutput{}.ParseRestartFreeze(data), err
   171  }
   172  
   173  func (m *mqlMacosSystemsetup) allowPowerButtonToSleepComputer() (string, error) {
   174  	data, err := m.runCmd("systemsetup -getallowpowerbuttontosleepcomputer")
   175  	return macos.SystemSetupCmdOutput{}.ParseAllowPowerButtonToSleep(data), err
   176  }
   177  
   178  func (m *mqlMacosSystemsetup) remoteLogin() (string, error) {
   179  	data, err := m.runCmd("systemsetup -getremotelogin")
   180  	return macos.SystemSetupCmdOutput{}.ParseRemoteLogin(data), err
   181  }
   182  
   183  func (m *mqlMacosSystemsetup) remoteAppleEvents() (string, error) {
   184  	data, err := m.runCmd("systemsetup -getremoteappleevents")
   185  	return macos.SystemSetupCmdOutput{}.ParseRemoteAppleEvents(data), err
   186  }
   187  
   188  func (m *mqlMacosSystemsetup) computerName() (string, error) {
   189  	data, err := m.runCmd("systemsetup -getcomputername")
   190  	return macos.SystemSetupCmdOutput{}.ParseComputerName(data), err
   191  }
   192  
   193  func (m *mqlMacosSystemsetup) localSubnetName() (string, error) {
   194  	data, err := m.runCmd("systemsetup -getlocalsubnetname")
   195  	return macos.SystemSetupCmdOutput{}.ParseLocalSubnetname(data), err
   196  }
   197  
   198  func (m *mqlMacosSystemsetup) startupDisk() (string, error) {
   199  	data, err := m.runCmd("systemsetup -getstartupdisk")
   200  	return data, err
   201  }
   202  
   203  func (m *mqlMacosSystemsetup) waitForStartupAfterPowerFailure() (string, error) {
   204  	data, err := m.runCmd("systemsetup -getwaitforstartupafterpowerfailure")
   205  	return macos.SystemSetupCmdOutput{}.ParseWaitForStartupAfterPowerFailure(data), err
   206  }
   207  
   208  func (m *mqlMacosSystemsetup) disableKeyboardWhenEnclosureLockIsEngaged() (string, error) {
   209  	data, err := m.runCmd("systemsetup -getdisablekeyboardwhenenclosurelockisengaged")
   210  	return macos.SystemSetupCmdOutput{}.ParseDisableKeyboardWhenEnclosureLockIsEngaged(data), err
   211  }
   212  
   213  func Decode(r io.ReadSeeker) (map[string]interface{}, error) {
   214  	var data map[string]interface{}
   215  	decoder := plist.NewDecoder(r)
   216  	err := decoder.Decode(&data)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	// NOTE: we need to do the extra conversion here to make sure we use supported
   222  	// values by our dict structure: string, float64, int64
   223  	// plist also uses uint64 heavily which we do not support
   224  	// TODO: we really do not want to use the poor-man's json conversion version
   225  	jsondata, err := json.Marshal(data)
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  
   230  	var dataJson map[string]interface{}
   231  	err = json.Unmarshal(jsondata, &dataJson)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	return dataJson, nil
   237  }