Skip to main content
awkBash ScriptingBlog

Authenticate Users with Bash Script and AWK

By October 14, 2021September 12th, 2022No Comments

bash script using awk authenticate users

Authenticate Users with BASH Script and AWK

We know that on Linux systems the user’s password is stored in the /etc/shadow file usually. At least for local accounts, we can always query the shadow database and lookup users in a directory backend like AD or openLDAP. We will start the process to authenticate users with bash script and AWK by checking the shadow database with the getent command

$ sudo getent shadow pi
pi:$6$UWNaqcIEjHBxgVYW$D0mDfslpAKqbR8aGpJbOE6pg2x6.z4M0jHgArV/mAm0f6QKWImDXMoOJulTsA3FsaqkwVDyRThX5fRD7e.xz01:18738:0:99999:7:::

Here we look to all shadow sources for the user called pi, this is taken from my Raspberry Pi. The second field is the password field and it is this field that we can use to help authenticate users from our BASH scripts and using AWK. Fields are delimited using the colon.

To extract just the second field we can start to look at the use of AWK. When authenticating users, AWK starts simple and becomes just a little more complex. So we can work through some examples. When using a static user name the process is simple.

$ sudo awk -F: '/^pi/{ print $2 }' /etc/shadow
$6$UWNaqcIEjHBxgVYW$D0mDfslpAKqbR8aGpJbOE6pg2x6.z4M0jHgArV/mAm0f6QKWImDXMoOJulTsA3FsaqkwVDyRThX5fRD7e.xz01

his all looks very easy so far but we would not normally want to hard code the name of the user to authenticate, it will be passed by a variable and it is here where the problems start. For the moment I will use the $USER variable as I am logged in as the user pi.

$ sudo awk -F: '/^$USER/{ print $2 }' /etc/shadow

This does not return anything as the variable is is the single quoted string needed by AWK. To overcome this we need to use a slightly different process. Rather than use the /re/ notation to match on the regular expressions; we can make use of the tilde match operator and use an AWK variable so we do not need the $ symbol

$ sudo awk -F: -v pattern="^$USER"  '$0 ~ pattern { print $2 }' /etc/shadow
$6$UWNaqcIEjHBxgVYW$D0mDfslpAKqbR8aGpJbOE6pg2x6.z4M0jHgArV/mAm0f6QKWImDXMoOJulTsA3FsaqkwVDyRThX5fRD7e.xz01

Using the -v option to AWK we can set an AWK variable that does not the $ syntax, we can set this for our regular expression where we need to look starting with the user name to authenticate, eventually in our bash script.  The tilde ~ is the match operator and we now search the complete line, $0, for our pattern.

Starting the Script

To start the script we need to develop we can add the code so far to a bash script, but prompting for the user with the read command.

#!/bin/bash
FILE='/etc/shadow'
read -p 'Enter user name: ' USER_NAME
ORIGINAL_PASSWORD=$(sudo awk -F: -v pattern="^$USER_NAME" '$0 ~ pattern {print $2}' "$FILE" )
echo "$ORIGINAL_PASSWORD"

To execute the script, here we have called it f1.sh:

$ bash f1.sh
Enter user name: pi
$6$UWNaqcIEjHBxgVYW$D0mDfslpAKqbR8aGpJbOE6pg2x6.z4M0jHgArV/mAm0f6QKWImDXMoOJulTsA3FsaqkwVDyRThX5fRD7e.xz01

The prompt is working well allowing us to dynamically ad the name of the user we want to authenticate with the awk gathered line within the bash script. We are well on our way to authenticate users with bash script and AWK.

Split Elements of the Password Field

The password field from the shadow database is broken into three further fields. The algorithm used, the SALT and the password hash. The $ is used to delimit the fields, but it starts with a $ so the first filed will always be empty. This is more convenient for us as the first field is index 0, so the first field we want is index 1. To split the password field into the three elements we read it into an array

#!/bin/bash
FILE='/etc/shadow'
read -p 'Enter user name: ' USER_NAME
ORIGINAL_PASSWORD=$(sudo awk -F: -v pattern="^$USER_NAME" '$0 ~ pattern {print $2}' "$FILE" )
IFS='$' read -a PWD_ARRAY <<< "$ORIGINAL_PASSWORD"
echo "ALG: ${PWD_ARRAY[1]}"
echo "SALT: ${PWD_ARRAY[2]}"
echo "PWHASH: ${PWD_ARRAY[3]}"

For the read command to be able to split in the correct place we set the IFS, input file separator, to be the dollar and not the space which is the default. The option -a reads into an array rather than the default scalar variable type.

The output should be similar to this:

$ bash f1.sh
Enter user name: pi
ALG: 6
SALT: UWNaqcIEjHBxgVYW
PWHASH: D0mDfslpAKqbR8aGpJbOE6pg2x6.z4M0jHgArV/mAm0f6QKWImDXMoOJulTsA3FsaqkwVDyRThX5fRD7e.xz01

What is the SALT

The SALT is text that is added to the password to help create unique password hashes. If the SALT is random, even if users have the same password the hash produced will be different as the hash is a combination of the Password and SALT encrypted via the hashing algorithm. In our case this is 6 or SHA512. If the same SALT is used then the hash will be the same if the password is the same. This is how users are authenticated. The password hash cannot be decrypted meaning that not even root can read your password. The resulting password hash created by combing the user input with the original SALT is compared with the original hash. If they match the user is authenticated.

The Final Script to Authenticate Users BASH AWK Script

#!/bin/bash
FILE='/etc/shadow'
read -p 'Enter user name: ' USER_NAME
read -sp 'Enter password: ' USER_PASSWORD
echo ""
ORIGINAL_PASSWORD=$(sudo awk -F: -v pattern="^$USER_NAME" '$0 ~ pattern {print $2}' "$FILE" )
IFS='$' read -a PWD_ARRAY <<< "$ORIGINAL_PASSWORD"

ENTERED_PASSWORD=$(openssl passwd -${PWD_ARRAY[1]} -salt ${PWD_ARRAY[2]} $USER_PASSWORD)

if [ $ENTERED_PASSWORD == $ORIGINAL_PASSWORD ] ; then
  echo "Authenticated"
else
  echo "Not Authenticated"
fi

We read the user password they input via read again but with the addition of the option -s to suppress the text display of the text input.