summaryrefslogtreecommitdiff
path: root/src/system/standalone_root_pw_reset
blob: 4cd4b98d4192beb1a1f449a1b03c9649f1058f44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/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 <gilligan@vyatta.com>
# 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