-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathexpect.chpwd
192 lines (175 loc) · 8.57 KB
/
expect.chpwd
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
#!/usr/bin/expect -f
set timeout 10
##########################################################
# Purpose: This expect script connects to a server and resets a user's password
# Author: Rawiri Blundell, Datacom
# Date: October-November 2013
# Prereq's: You have sudo access with an up-to-date password and sshkeys on all target servers
#
# WARNING: This lacks some failsafe features. Ensure you understand how this script works
#
# This is intended to be invoked by the wrapper script chpwd.sh
# but in theory this could be invoked directly. Ensure that .pwdfiler and .newpwd are correct
# and run ./expect.chpwd servername username newuserpwd
# Set variables
# Log file picks up send_log and send_user, use puts to echo to user without logging
log_file log.chpwd
set server [lindex $argv 0]
set user [lindex $argv 1]
set newpwd [lindex $argv 2]
set whoami [exec whoami]
set ostype [exec uname]
# Check the newpwd variable. If it's blank we look for the .newpwd file
if {[llength $newpwd] == 0} {
if {[file exists .newpwd] == 1} {
# Reads new password from file '.newpwd' which is set to chmod 600
# Slightly more secure this way
set newpwdfiler [open "/home/$whoami/bin/.newpwd" r]
set newpass [read $newpwdfiler]
} elseif {[file exists .newpwd] == 0} {
send_user -- "I could not find the required file .newpwd\n"
exit 1
}
} else {
set newpass $newpwd
}
# Check for the .pwdfiler file
if { [file exists .pwdfiler] == 1 } {
# Reads admin password from file '.pwdfiler' which is set to chmod 600
# Slightly more secure this way
set filer [open "/home/$whoami/bin/.pwdfiler" r]
set adminpass [read $filer]
} else {
send_user -- "I could not find the required file .pwdfiler\n"
exit 1
}
# Figure out if $server is Linux or Solaris.
# If $ostype matches Linux, then we assume Linux and set sudo appropriately
if { [regexp -nocase {Linux} $ostype] } {
set sudo sudo
set type linux
# Otherwise we test for Solaris, some of which have annoying path problems
# So we're forced to give the full sudo path
} elseif { [regexp -nocase {SunOS} $ostype] } {
set sudo /usr/local/bin/sudo
set type solaris
# Otherwise, fail out
} else {
send_user "\nERROR: I currently work on Linux or Solaris, this host appears to be neither.\n"
exit 1
}
# Tell the logfile which server we're working on
send_log -- "\n==========================================\nStarting task on $server\n"
# Open the connection and invoke sudo passwd
spawn ssh -q -o StrictHostKeyChecking=no -t $server $sudo passwd $user
expect {
# Set the default action to catch any unexpected responses
default {
send_user "\nERROR: I was unable to connect to $server for some reason. Please try manually.\n"
exit 1
}
# Pre-check for a server that we might not have an sshkey on yet
-re "$whoami@$server's \[pP\]assword:" {
send_log -- "\nWARN: $whoami doesn't appear to have an sshkey setup for $server. You might want to resolve that.\n"
send -- "$adminpass\r"
exp_continue
}
###### Note: the below block doesn't really work that well.
###### The if statement is fine, but the sending of the adminpass is sometimes echo'd back, which messes the following expect
###### This block should be catering for a very rare condition though, so I haven't put too much effort into figuring it out
# Firstly, cater for password expiry for the admin account
-re "\\\(current\\\) UNIX \[pP\]assword:|\[oO\]ld \[pP\]assword:" {
# We need to test if the $user variable is the same as $whoami
# This is to cater for instances where the admin is updating his/her own password
# If true, it's sensible to feed the adminpass as their oldpwd, and newpass as their newpwd
# Otherwise if they're updating a user, we don't want to set the admin's newpwd to the user's newpwd!
if { $whoami != $user } {
send_user -- "\nWARN: It looks like you're updating a user's password\nI don't want to set your password to the user's password.\n"
send_log -- "\nWARN: $whoami's account is expired on $server and needs to be manually sorted out.\n"
# Try to end the remote session with Ctrl-D
send \004
exit 1
} else {
# If the above test passes, we assume the Admin is updating his/her own password and continue
send -- "$adminpass\r"
expect {
default {
send_user -- "\nERROR: It appears we can't authenticate to $server. You'll need to resolve that manually.\n"
exit 1
}
-re "\[nN\]ew UNIX \[pP\]assword:|\[nN\]ew \[pP\]assword:|\[aA\]gain:|\[rR\]etype|\[rR\]e-enter|\[rR\]eenter" {
# Imitate inferior, slow, meatbag humans. Mitigates mismatches.
sleep 2
send -- "$newpass\r"
# exp_continue loops us back to the expect, capturing the second New Password prompt
exp_continue
}
"BAD PASSWORD" {
send_user -- "\nWARN: Password change failed for $whoami on $server due to the password not being acceptable. Try a more complex password.\n"
exit 1
}
# Upon completion of the password change, passwd should tell us it was successful. We use this to exit
# This prevents erroneous triggering of the default condition
-re "updated|successfully" {
send_log -- "\nINFO: Password updated on $server for $whoami.\n"
exit 0
}
}
exp_continue
}
# First expected prompt back will be your password to sudo up
-re "\\\[sudo\\\] password for|\[pP\]assword:" {
send -- "$adminpass\r"
# Now we expect the interaction with passwd
expect {
# If we come across NT Password, we're talking to an AD'd machine which isn't our problem
-re "NT \[pP\]assword" {
# Send Ctrl-D, as Ctrl-C won't escape this
send \004
send_user -- "WARN: $server authenticates against AD, which I don't touch. Moving on...\n"
exit 1
}
-re "\[nN\]ew UNIX \[pP\]assword:|\[nN\]ew \[pP\]assword:|\[aA\]gain:|\[rR\]etype|\[rR\]e-enter|\[rR\]eenter" {
# Imitate inferior, slow, meatbag humans. Mitigates mismatches.
sleep 2
send -- "$newpass\r"
# exp_continue loops us back to the expect, capturing the second New Password prompt
exp_continue
}
# Upon completion of the password change, passwd should tell us it was successful. We use this to exit
# This prevents erroneous triggering of the default condition
-re "updated|successfully" {
send_log -- "\nINFO: Password updated on $server for $user.\n"
exit 0
}
# Sorry, try again message indicates a failed sudo auth
-re "Sorry, try again." {
# Send Ctrl-D, as Ctrl-C won't escape this
send \004
send_user -- "\nERROR: I was unable to sudo up on $server. Something might be wrong. I'll leave the investigation to you...\n"
exit 1
}
# Finally, set a default action for unexpected responses
default {
send_user -- "\nERROR: There was an unexpected problem performing this operation on $server. I'll leave the investigation to you...\n"
exit 1
}
}
exp_continue
}
}
# Now we reconnect and expire the password while we still have a sudo session cached
# First we check that we're not doing this to the admin. We DON'T want to do this to the admin
if { $whoami != $user } {
# Now we check if we're dealing with a Solaris server
if {"$type" == "solaris"} {
spawn ssh -q -o StrictHostKeyChecking=no -t $server $sudo passwd -f $user
# If it's not Solaris, it's likely Linux
} else {
spawn ssh -q -o StrictHostKeyChecking=no -t $server $sudo chage -d 0 $user
}
}
# Potentially there could be another connection to clear the sudo session on the remote server
# I leave it be, as it'll timeout by design, usually after 15 minutes. To do it though, uncomment:
#spawn ssh -q -o StrictHostKeyChecking=no -t $server $sudo -K
exi 0