macOS shell script check. A better way to execute the if statement and check for a condition

I need to write a very simple shell script that will check if the system is configured to enforce multi-factor authentication.

To verify that the system is configured to enforce multi-factor authentication, run the following commands:

/usr/sbin/system_profiler SPConfigurationProfileDataType | /usr/bin/grep enforceSmartCard

If the results do not show "enforceSmartCard=1", this is a finding.

I created this very simple script, but I am pretty sure there is a more effective, elegant and efficient way to achieve the same result.

Basically if it was you, what kind of modifications would you apply to the script below to achieve the same results?

Thank you so much in advance for your help.

#!/bin/zsh

myVAR=`/usr/sbin/system_profiler SPConfigurationProfileDataType | /usr/bin/grep enforceSmartCard`

if [ $myVAR = "enforceSmartCard=1" ]
then
    echo "The system is configured to enforce multi-factor authentication"
else
    echo "The system is not configured to enforce multi-factor authentication"
fi

Solution 1:

This might be a little more elegant, not necessarily simpler: I'm wrapping the parts of the pipeline in their own functions

profile () { /usr/sbin/system_profiler SPConfigurationProfileDataType; }
enforces2fa () { /usr/bin/grep -F -q 'enforceSmartCard=1'; }

if profile | enforces2fa; then
    echo "The system is configured to enforce multi-factor authentication"
else
    echo "The system is not configured to enforce multi-factor authentication"
fi

Some notes

  • prefer $(...) over `...` for command substitution: it's easier to read.
  • if takes a command and branches based on the command's exit status. At a bash prompt, enter help if for more details.
    • [ is a bash builtin command, not just syntax.
  • if "enforceSmartCard=1" is the only text on the line, add -x to the grep options.

Additionally, you can modify the PATH in your script to streamline the functions

PATH=$(getconf PATH)      # /usr/bin:/bin:/usr/sbin:/sbin

profile () { system_profiler SPConfigurationProfileDataType; }
enforces2fa () { grep -F -q 'enforceSmartCard=1'; }

This is only in effect for the duration of the running script, and will not affect your interactive shell.

Solution 2:

I'd be inclined to read the value of the enforceSmartCard key directly from the com.apple.security.smartcard.plist file located in the /Library/Preferences folder.

This can be done using the defaults command-line tool, which will be significantly faster than the system_profiler, and you won't need to grep its output:

defaults read /Library/Preferences/com.apple.security.smartcard enforceSmartCard

Here are the possible outputs for the system_profiler | grep and defaults commands, together with their meaning:

system_profiler | grep defaults
enforceSmartCard=0 0 The enforceSmartCard key is set,
and its value is false
enforceSmartCard=1 1 The enforceSmartCard key is set,
and its value is true
non-zero exit status Error message and
non-zero exit status
The enforceSmartCard key is not
set, or (defaults only) the property
list does not exist

The default value for enforceSmartCard is false, therefore a non-zero exit status is essentially equivalent in meaning—for our purposes—to enforceSmartCard=0. In the case of the defaults command, an error message is also printed that looks something like this:

The domain/default pair of (/Library/Preferences/com.apple.security.smartcard, enforceSmartCard) does not exist

so, provided there isn't a typographical error when you issue the command, then either the file /Library/Preferences/com.apple.security.smartcard.plist does not exist, or it does and the enforceSmartCard key is not set. Either way, the error message is of little value, so this can be suppressed by redirecting stderr into the void:

defaults read /Library/Preferences/com.apple.security.smartcard enforceSmartCard 2>/dev/null

Thus, the predicate we discriminate upon will be whether or not this command outputs "1" (without quotes): if it does, then a user must authenticate using their smart card (in addition to whatever other form of authentication would be required); any other result (either an output of "0", or a non-zero exit status with no output) infers that a user may authenticate without using a smart card.

Here's the final script that should be equivalent to yours:

#!/usr/bin/env zsh

plist=/Library/Preferences/com.apple.security.smartcard

(( $( defaults read "$plist" \
enforceSmartCard 2>/dev/null )
)) && i=1 _not= || 
      i=2 _not=not

printf '%s ' The system is ${_not} configured to \
             enforce multi-factor authentication \
             >/dev/fd/$i