Under Construction

Aixpert Script for SSHD Settings

In the example shown so far, only the PermitRootLogin attribute can be set to “yes,” and back to “no” for an undo. Besides PermitRootLogin, there are a number of other security-relevant attributes, such as Ciphers, DisableForwarding, or PermitEmptyPasswords. Instead of writing a separate script for each of these attributes, based on the script developed above, it is advisable to adapt the existing script and support arbitrary attributes and values. To do this, the attribute and desired value must be passed as arguments, as in the standard script chusrattr. The only argument so far, the rule name, should be optional, as with chusrattr. If the argument is present, an undo entry should be created; if the argument is not present, no undo entry is created.

First, we change the description of the local_chsshdconf script as follows:

#   Command Line Arguments  : This script expects one or two command line arguments.
#                           The first argument should be of the form
#                           'Attr=value', where 'Attr' is a valid keyword for sshd_config
#                           and 'value' is the value to set or to check for.
#                           The second argument is optional and if provided,
#                           indicates that the undo xml rule has to be
#                           generated.
#                           Syntac - local_chsshdconf Attr=value [rulename]
#
#   OUTPUT                  : None
#
#   Description             : This script changes the attribute 'Attr' to
#                           the value 'value' in /etc/ssh/sshd_config.
#                           It dynamically generates an undo XML rule with
#                           shortname as "Undo$2_<timestamp>" if the second
#                           argument is provided.
#                           This script should be run with superuser privileges.

As a path prefix for temporary files we now use /etc/security/aixpert/tmp/local_chsshdconf (variable TMP):

# Initialize variables AIXPERT_FIFO, LOG, REPORT, SCPTDIR and UNDOXML
. /etc/security/aixpert/bin/initialize_variables

TMP=/etc/security/aixpert/tmp/local_chsshdconf
PID=$$

The check for the correct number of arguments must also be modified. The script must be called with one or two arguments:

if [ $# -lt 1 ] || [ $# -gt 2 ]
then
    echo "Usage : local_chsshdconf Attr=value [rulename]\n"
    exit 1
fi

The attribute to be set or checked is passed along with its value as an argument of the form “Attr=value“. The argument must be split into the attribute name and the value:

# Determine the user attribute that has to be changed.
sshdattr=`echo $1 | awk -F "=" '{print $1; if(NF != 2) exit 1}'`

if [ $? -ne 0 ] || [ "$sshdattr" = "" ]
then
    dspmsg -s 4 aixpert.cat 1 "Usage : local_chsshdconf Attr=value [Rulename]\n"
exit 1
fi

# Determine the value that is being assigned to the $sshdattr
value=`echo $1 | awk -F "=" '{print $2}'`

The attribute name is stored in the variable sshdattr and the value in the variable value.

To check attributes, we write a small function that we place at the beginning of the script. This function prints the effective configuration using “sshd -T“, searches for the line containing the desired attribute, and returns the corresponding value.

function getEffectiveSshdValue
{
    typeset -l keyword="$1" # translate to lower case
    typeset key
    typeset value="invalid"

    sshd -T | grep -w "$keyword" | read key value
    print "$value"
}

Using this function, you can then rewrite the check for an attribute. The previous check:

    defvalue=`awk '$1 ~ /^PermitRootLogin$/ { print $2; }' /etc/ssh/sshd_config 2>/dev/null | tail -n 1`
   case "$defvalue" in
    [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]) ret=0 ;;
    esac

we then rewrite as follows:

   defvalue=`getEffectiveSshdValue "$sshdattr"`
  [ "$defvalue" = "$value" ] && ret=0

The messages if the check is not successful (set value deviates from the target value) must also be adjusted slightly:

   if [ $ret = 1 ]
    then
        if [ "$BrType" = "1" ]
        then
            echo "Base Compliance Report\n";
            printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" $sshdattr
$value $defvalue >>$TMPREPT
        elif [ "$DrType" = "1" ]
        then
            echo "Description Report\n";
            printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" $sshdattr
$value $defvalue >>$TMPREPT
        else
            printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" $sshdattr
$value $defvalue >>$REPORT
            printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" $sshdattr
$value $defvalue >>$AIXPERT_FIFO
        fi
    fi

To start a test run for the modified verification, the XML file custom/sshd.xml must be adjusted:

/etc/security/aixpert # cat custom/sshd.xml
<?xml version="1.0" encoding="UTF-8"?>
<AIXPertSecurityHardening>
  <AIXPertEntry name="test_permitrootlogin" function="permitrootlogin">
    <AIXPertRuleType type="TEST"/>
    <AIXPertDescription>Ensure that PermitRootLogin is False.</AIXPertDescription>
    <AIXPertPrereqList>openssh.base.server</AIXPertPrereqList>
    <AIXPertCommand>/etc/security/aixpert/bin/local_chsshdconf</AIXPertCommand>
    <AIXPertArgs>PermitRootLogin=no test_permitrootlogin</AIXPertArgs>
   <AIXPertGroup>SSHD policy rules</AIXPertGroup>
  </AIXPertEntry>

</AIXPertSecurityHardening>
/etc/security/aixpert #

The current value of PermitRootLogin is:

# grep ^PermitRootLogin /etc/ssh/sshd_config
PermitRootLogin yes
#

A review should therefore not be successful:

# aixpert -c -P custom/sshd.xml
Processedrules=1        Passedrules=0   Failedrules=1   Level=TEST
        Input file=custom/sshd.xml
#

As expected, the verification fails (Failedrules=1). We change the value of PermitRootLogin to “no” and run another verification:

# aixpert -c -P custom/sshd.xml
Processedrules=1        Passedrules=1   Failedrules=0   Level=TEST
        Input file=custom/sshd.xml
#

The verification is now successful. This allows the modified script to check any attributes of the sshd.

Next, we implement the setting of arbitrary attributes in /etc/ssh/sshd_config. Here, too, we use a function that should be placed at the beginning of the script:

function setSshdValue
{
    typeset -l keyToChange="$1"
    typeset newValue="$2"
    typeset -l lowerCaseKey

    changed=false
    while read -r line
    do
       print "$line" | read -r key value rest
       lowerCaseKey="$key"
       case "$lowerCaseKey" in
       $keyToChange)
           print "$key $newValue"
            changed=true
            ;;
       *) print "$line" ;;
        esac
    done </etc/ssh/sshd_config >/etc/ssh/sshd_config.new
   ${change} || print "$key $newValue" >>/etc/ssh/sshd_config.new
    cp /etc/ssh/sshd_config.new /etc/ssh/sshd_config
    rm -f /etc/ssh/sshd_config.new
}

A while loop searches line by line for the attribute name to be changed. If it is found, the name and new value are output. Otherwise, the lines are output unchanged. If the attribute is not found, the name and new value are output at the end. All output is redirected to an intermediate file (/etc/ssh/sshd_config.new).

The previous lines for setting PermitRootLogin (in the script after the verification) can then be removed and replaced by the following call to the above function:

setSshdValue "$sshdattr" "$value"

Before we test the new script version, we set PermitRootLogin to “yes” and remove all active rules:

/etc/security/aixpert # aixpert -u
/etc/security/aixpert # aixpert -u
There are no security rules to undo in file /etc/security/aixpert/core/appliedaixpert.xml
/etc/security/aixpert # rm check_report.txt core/appliedaixpert.* core/undo.xml log/* tmp/* undo/*
rm: check_report.txt: No such file or directory
rm: cannot remove directory undo/data
/etc/security/aixpert # grep ^PermitRootLogin /etc/ssh/sshd_config
PermitRootLogin yes
/etc/security/aixpert #

We now apply the rule for setting PermitRootLogin again:

/etc/security/aixpert # aixpert -f custom/sshd.xml
Processedrules=1        Passedrules=1   PrereqFailedrules=0     Failedrules=0   Level=TEST
        Input file=custom/sshd.xml
/etc/security/aixpert #

According to the output, the rule was successfully processed (Passedrules=1). A quick check with “aixpert -c” and grep shows that PermitRootLogin was set to “no“:

# aixpert -c
Processedrules=1        Passedrules=1   Failedrules=0   Level=TEST
        Input file=/etc/security/aixpert/core/appliedaixpert.xml
# grep ^PermitRo /etc/ssh/sshd_config
PermitRootLogin no
#

Finally, the undo functionality needs to be adjusted. To perform an undo, the script simply needs to be called with the attribute name and the original value of the attribute. A rule name cannot be specified in this case, since no new undo entry should be created in /etc/security/aixpert/core/undo.xml during the undo.

We check whether the script was called with two arguments, i.e., with the rule name. If this is the case, the current value of the attribute is determined and stored in the variable defvalue. The variable undo is also set.

# This script generates Undo rule if the number of arguments is 2,
# otherwise it works as undo script.
if [ $# -eq 2 ]
then
    # Determine the current value of $sshdattr from the effective configuration
    defvalue=`getEffectiveSshdValue "$sshdattr"`

    # Generate a dynamic undo rule
    undo=1
fi

The script no longer needs to create an undo script, since the script itself can be called for undo. The corresponding lines in the script for creating the undo script are thus eliminated.

However, the undo entry in the XML file /etc/security/aixpert/core/undo.xml still needs to be created. However, the script itself is now stored as the AIXPertCommand, and the attribute name is specified as the argument (AIXPertArgs) along with the original value ($defvalue):

   # Add an undo XML rule to the file $UNDOXML
    awk -v rulepart1="\t<AIXPertEntry name=\"$name\">\n\
\t\t<AIXPertRuleType type=\"Undo\"/>\n"\
-v rulepart2="\t\t<AIXPertDescription ${desc_catmsginfo}>$undomsg \"$desc\"</AIXPertDes
cription>\n\
\t\t<AIXPertPrereqList/>\n"\
-v rulepart3="\t\t<AIXPertCommand>/etc/security/aixpert/bin/local_chsshdconf</AIXPertCo
mmand>\n\
\t\t<AIXPertArgs>$sshdattr=$defvalue</AIXPertArgs>\n\
\t\t<AIXPertGroup>SSHD policy rules</AIXPertGroup>\n\
\t</AIXPertEntry>" '{if(match($0,"^[\t ]*</AIXPertUndo>")==0) print $0;
else print rulepart1 rulepart2 rulepart3 "\n" $0}' $UNDOXML >$TMP$PID

Overall, this results in the following version of the script:

/etc/security/aixpert # cat bin/local_chsshdconf
#! /bin/ksh
#
# Copyright (c) 2022 by PowerCampus 01 GmbH
#
# Anyone is free to copy, publish, use, or distribute this software,
# for any purpose, commercial or non-commercial, and by any means,
# as long as this copyright notice is kept.
# Modification of this software is allowed, as long as this copyright
# notice is kept, and a notice is placed in the software that indicates
# that the software has been modified. It is not allowed to sell this
# software without the written permission of PowerCampus 01.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
#
# Command Line Arguments  : This script expects one or two command line arguments.
#                           The first argument should be of the form
#                           'Attr=value', where 'Attr' is a valid keyword for sshd_config
#                           and 'value' is the value to set or to check for.
#                           The second argument is optional and if provided,
#                           indicates that the undo xml rule has to be
#                           generated.
#                           Syntax - local_chsshdconf Attr=value [rulename]
#
# OUTPUT                  : None
#
# Description             : This script changes the attribute 'Attr' to
#                           the value 'value' in /etc/ssh/sshd_config.
#                           It dynamically generates an undo XML rule with
#                           shortname as "Undo$2_<timestamp>" if the second
#                           argument is provided.
#                           This script should be run with superuser privileges.
#

function getEffectiveSshdValue
{
        typeset -l keyword="$1"
        typeset key
        typeset value="invalid"

        sshd -T | grep -w "$keyword" | read key value
        print "$value"
}

function setSshdValue
{
        typeset -l keyToChange="$1"
        typeset newValue="$2"
        typeset -l lowerCaseKey

        changed=false
        while read -r line
        do
               print "$line" | read -r key value rest
               lowerCaseKey="$key"
               case "$lowerCaseKey" in
               $keyToChange)
                       print "$key $newValue"
                        changed=true
                        ;;
               *) print "$line" ;;
                esac
        done </etc/ssh/sshd_config >/etc/ssh/sshd_config.new
       ${change} || print "$key $newValue" >>/etc/ssh/sshd_config.new
        cp /etc/ssh/sshd_config.new /etc/ssh/sshd_config
        rm -f /etc/ssh/sshd_config.new
}

export PATH=/usr/bin:/usr/sbin:$PATH

# Initialize variables AIXPERT_FIFO, LOG, REPORT, SCPTDIR and UNDOXML
. /etc/security/aixpert/bin/initialize_variables

TMP=/etc/security/aixpert/tmp/local_chsshdconf
PID=$$

# Log output and errors to /etc/security/aixpert/log/aixpert.log
exec 1>>$LOG
exec 2>&1

# echo all the commands and the current time and date to the AIXpert log
set -x
date
echo $0 $@ AIXPERT_CHECK_REPORT=$AIXPERT_CHECK_REPORT BASE_REPT=$BASE_REPT DETAILED_REPT=$DETAILED_REPT

if [ $# -lt 1 ] || [ $# -gt 2 ]
then
    echo "Usage : local_chsshdconf Attr=value [rulename]\n"
    exit 1
fi

# A value of 0 indicates that Undo rule need not be created.
# This variable will be set later in the script if there is anything to be undone
undo=0

# Check whether AIXPERT_CHECK_REPORT environment variable is set or not.
report=`echo $AIXPERT_CHECK_REPORT`
BrType=`echo $BASE_REPT`
DrType=`echo $DETAILED_REPT`

# Determine the user attribute that has to be changed.
sshdattr=`echo $1 | awk -F "=" '{print $1; if(NF != 2) exit 1}'`

if [ $? -ne 0 ] || [ "$sshdattr" = "" ]
then
        dspmsg -s 4 aixpert.cat 1 "Usage : local_chsshdconf Attr=value [Rulename]\n"
exit 1
fi

# Determine the value that is being assigned to the $sshdattr
value=`echo $1 | awk -F "=" '{print $2}'`

# This script generates Undo rule if the number of arguments is 2,
# otherwise it works as undo script.
if [ $# -eq 2 ]
then
        # Determine the current value of $sshdattr from the effective configuration
        defvalue=`getEffectiveSshdValue "$sshdattr"`
        if [ "$report" = "1" ]
        then
                ret=1

                [ "$defvalue" = "$value" ] && ret=0

                if [ $ret = 1 ]
                then
                        if [ "$BrType" = "1" ]
                        then
                                echo "Base Compliance Report\n";
                                printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" $sshdattr $value $defvalue >>$TMPREPT
                        elif [ "$DrType" = "1" ]
                        then
                                echo "Description Report\n";
                                printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" $sshdattr $value $defvalue >>$TMPREPT
                        else
                                printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" $sshdattr $value $defvalue >>$REPORT
                                printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" $sshdattr $value $defvalue >>$AIXPERT_FIFO
                        fi
                fi

                exit $ret
        fi
        # Generate a dynamic undo rule
        undo=1
fi

setSshdValue "$sshdattr" "$value"

if [ $undo -eq 1 ]
then
        # Check if UNDOXML file has <AIXPertUndo> tag or not
        empty=`grep "^<AIXPertUndo>" $UNDOXML`
        if [ "$empty" = "" ]
        then
                echo "\n<AIXPertUndo>\n</AIXPertUndo>" >> $UNDOXML
        fi

        # Get the rulename and put it in undo rule
        name=`echo $AIXPERT_NAME`
        # Get the rule description & pass it to undo rule
        desc=`echo $AIXPERT_DESC`
        desc_catmsginfo=`echo $AIXPERT_DESC_CATMSGINFO`
        undomsg=`/usr/bin/dspmsg -s 49 aixpert.cat 1 "Undo action for: "`

        # Add an undo XML rule to the file $UNDOXML
        awk -v rulepart1="\t<AIXPertEntry name=\"$name\">\n\
\t\t<AIXPertRuleType type=\"Undo\"/>\n"\
-v rulepart2="\t\t<AIXPertDescription ${desc_catmsginfo}>$undomsg \"$desc\"</AIXPertDescription>\n\
\t\t<AIXPertPrereqList/>\n"\
-v rulepart3="\t\t<AIXPertCommand>/etc/security/aixpert/bin/local_chsshdconf</AIXPertCommand>\n\
\t\t<AIXPertArgs>$sshdattr=$defvalue</AIXPertArgs>\n\
\t\t<AIXPertGroup>SSHD policy rules</AIXPertGroup>\n\
\t</AIXPertEntry>" '{if(match($0,"^[\t ]*</AIXPertUndo>")==0) print $0;
else print rulepart1 rulepart2 rulepart3 "\n" $0}' $UNDOXML >$TMP$PID
        if [ $? -eq 0 ] ; then
                mv $TMP$PID $UNDOXML
        fi
fi

exit 0

/etc/security/aixpert #

Before we run the final script version again, we set PermitRootLogin back to “yes” and remove all active rules:

/etc/security/aixpert # aixpert -u
/etc/security/aixpert # aixpert -u
There are no security rules to undo in file /etc/security/aixpert/core/appliedaixpert.xml
/etc/security/aixpert # rm check_report.txt core/appliedaixpert.* core/undo.xml log/* tmp/* undo/*
rm: check_report.txt: No such file or directory
rm: cannot remove directory undo/data
/etc/security/aixpert # grep ^PermitRootLogin /etc/ssh/sshd_config
PermitRootLogin yes
/etc/security/aixpert #

We apply the rule to our system again:

/etc/security/aixpert # aixpert -f custom/sshd.xml
Processedrules=1        Passedrules=1   PrereqFailedrules=0     Failedrules=0   Level=TEST
        Input file=custom/sshd.xml
/etc/security/aixpert #

Let’s look at the entry in the undo.xml:

/etc/security/aixpert # cat /etc/security/aixpert/core/undo.xml
<AIXPertUndo>
        <AIXPertEntry name="test_permitrootlogin_DD8096ED">
                <AIXPertRuleType type="Undo"/>
                <AIXPertDescription catalog="" setNum="" msgNum="">Undo action for:  "Ensure that PermitRootLogin is False."</AIXPertDescription>
                <AIXPertPrereqList/>
                <AIXPertCommand>/etc/security/aixpert/bin/local_chsshdconf</AIXPertCommand>
                <AIXPertArgs>PermitRootLogin=yes</AIXPertArgs>
                <AIXPertGroup>SSHD policy rules</AIXPertGroup>
        </AIXPertEntry>
</AIXPertUndo>

/etc/security/aixpert #

The undo entry calls the above script and uses the argument “PermitRootLogin=yes”, which is the original value of PermitRootLogin.

Finally, we check if the undo works correctly:

# aixpert -u
#

A quick check shows that the entries from appliedaixpert.xml and undo.xml have been removed and the value of PermitRootLogin is again “yes”:

/etc/security/aixpert # cat core/appliedaixpert.xml
<?xml version="1.0"?>
<AIXPertSecurityHardening>
</AIXPertSecurityHardening>
/etc/security/aixpert # cat core/undo.xml
<AIXPertUndo>
</AIXPertUndo>
/etc/security/aixpert # grep ^PermitRootLogin /etc/ssh/sshd_config
PermitRootLogin yes
/etc/security/aixpert #