#!/bin/bash # **** License **** # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # This code was originally developed by Vyatta, Inc. # Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. # All Rights Reserved. # # Author: Bob Gilligan # Description: Standalone script to set the admin passwd to new value # value. Note: This script can ONLY be run as a standalone # init program by grub. # # **** End License **** # The Vyatta config file: CF=/opt/vyatta/etc/config/config.boot # Admin user name ADMIN=vyos set_encrypted_password() { local user_raw=$1 epwd=$2 file=$3 local user # Escape the username before using it in awk/sed regexes user=$(printf '%s' "$user_raw" | sed -e 's/\./\\&/g') # Check if encrypted-password exists within this specific user's block if awk "/ user $user \{/{f=1} f && /encrypted-password/{found=1; exit} f && /^ }/{exit} END{exit !found}" "$file"; then # Replace existing encrypted-password line sed -i \ -e "/ user $user {/,/encrypted-password/s/encrypted-password .*\$/encrypted-password \"$epwd\"/" "$file" else # Key-only account: no encrypted-password or plaintext-password line exists # in this user's block (e.g. authentication via SSH key only). if awk "/ user $user \{/{f=1} f && /authentication \{/{found=1; exit} f && /^ \}/{exit} END{exit !found}" "$file"; then # 'authentication' block exists - insert encrypted-password inside it sed -i \ -e "/ user $user {/,/authentication {/s/\(authentication {\)\$/\1\n encrypted-password \"$epwd\"/" "$file" else # No authentication block at all. Without this branch the sed range # above would overrun the block boundary and corrupt a later account's # authentication block. Build the section from scratch instead. sed -i \ -e "/ user $user {/a\\ authentication {\\ encrypted-password \"$epwd\"\\ plaintext-password \"\"\\ }" "$file" fi fi } clear_plaintext_password() { local user_raw=$1 file=$2 local user # Escape the username before using it in awk/sed regexes user=$(printf '%s' "$user_raw" | sed -e 's/\./\\&/g') # Only clear if plaintext-password exists within this specific user's block. # Without this guard, if the user has no plaintext-password line the sed # range never closes inside their block and spills into the next account # that does have one, wiping that account's plaintext-password instead. if awk "/ user $user \{/{f=1} f && /plaintext-password/{found=1; exit} f && /^ \}/{exit} END{exit !found}" "$file"; then sed -i \ -e "/ user $user {/,/plaintext-password/s/plaintext-password .*\$/plaintext-password \"\"/" "$file" fi } # How long to wait for user to respond, in seconds TIME_TO_WAIT=30 change_password() { local user=$1 local pwd1="1" local pwd2="2" until [ "$pwd1" == "$pwd2" ] do read -p "Enter $user password: " -r -s pwd1 echo read -p "Retype $user password: " -r -s pwd2 echo if [ "$pwd1" != "$pwd2" ] then echo "Passwords do not match" fi done # set the password for the user then store it in the config # so the user is recreated on the next full system boot. local epwd=$(mkpasswd --method=yescrypt "$pwd1") # escape any slashes in resulting password local eepwd=$(sed 's:/:\\/:g' <<< $epwd) set_encrypted_password $user $eepwd $CF # There is issue that 'set_encrypted_password' only handles the 'encrypted-password' line, # but leaves any existing 'plaintext-password' untouched (T8346). # We need to clear 'plaintext-password' for the user during the password change flow. clear_plaintext_password $user $CF } # System is so messed up that doing anything would be a mistake dead() { echo $* echo echo "This tool can only recover missing administrator password." echo "It is not a full system restore" echo echo -n "Hit return to reboot system: " read /sbin/reboot -f } echo "Standalone root password recovery tool." echo # # Check to see if we are running in standalone mode. We'll # know that we are if our pid is 1. # if [ "$$" != "1" ]; then echo "This tool can only be run in standalone mode." exit 1 fi # # OK, now we know we are running in standalone mode. Talk to the # user. # echo -n "Do you wish to reset the admin password? (y or n) " read -t $TIME_TO_WAIT response if [ "$?" != "0" ]; then echo echo "Response not received in time." echo "The admin password will not be reset." echo "Rebooting in 5 seconds..." sleep 5 echo /sbin/reboot -f fi response=${response:0:1} if [ "$response" != "y" -a "$response" != "Y" ]; then echo "OK, the admin password will not be reset." echo -n "Rebooting in 5 seconds..." sleep 5 echo /sbin/reboot -f fi echo -en "Which admin account do you want to reset? [$ADMIN] " read admin_user ADMIN=${admin_user:-$ADMIN} echo "Starting process to reset the admin password..." echo "Re-mounting root filesystem read/write..." mount -o remount,rw / if [ ! -f /etc/passwd ] then dead "Missing password file" fi if [ ! -d /opt/vyatta/etc/config ] then dead "Missing VyOS config directory /opt/vyatta/etc/config" fi # Leftover from V3.0 if grep -q /opt/vyatta/etc/config /etc/fstab then echo "Mounting the config filesystem..." mount /opt/vyatta/etc/config/ fi if [ ! -f $CF ] then dead "$CF file not found" fi if ! grep -q 'system {' $CF then dead "$CF file does not contain system settings" fi if ! grep -q ' login {' $CF then # Recreate login section of system sed -i -e '/system {/a\ login {\ }' $CF fi if ! grep -q " user $ADMIN " $CF then echo "Recreating administrator $ADMIN in $CF..." sed -i -e "/ login {/a\\ user $ADMIN {\\ authentication {\\ encrypted-password \$6$IhbXHdwgYkLnt/$VRIsIN5c2f2v4L2l4F9WPDrRDEtWXzH75yBswmWGERAdX7oBxmq6m.sWON6pO6mi6mrVgYBxdVrFcCP5bI.nt.\\ plaintext-password \"\"\\ }\\ level admin\\ }" $CF fi echo "Saving backup copy of config.boot..." cp $CF ${CF}.before_pwrecovery sync echo "Setting the administrator ($ADMIN) password..." change_password $ADMIN echo $(date "+%b%e %T") $(hostname) "Admin password changed" \ | tee -a /var/log/auth.log >>/var/log/messages sync echo "System will reboot in 10 seconds..." sleep 10 /sbin/reboot -f