summaryrefslogtreecommitdiff
path: root/plugins/password/helpers/passwd-expect
blob: 7db21ad1fcef6630cb9478c768200a8841cf9001 (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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#
# This scripts changes a password on the local system or a remote host.
# Connections to the remote (this can also be localhost) are made by ssh, rsh,
# telnet or rlogin.

# @author  Gaudenz Steinlin <gaudenz@soziologie.ch>

# For sudo support alter sudoers (using visudo) so that it contains the
# following information (replace 'apache' if your webserver runs under another
# user):
# -----
# # Needed for Horde's passwd module
# Runas_Alias     REGULARUSERS = ALL, !root
# apache ALL=(REGULARUSERS) NOPASSWD:/usr/bin/passwd
# -----

# @stdin              The username, oldpassword, newpassword (in this order)
#                     will be taken from stdin
# @param -prompt      regexp for the shell prompt
# @param -password    regexp password prompt
# @param -oldpassword regexp for the old password
# @param -newpassword regexp for the new password
# @param -verify      regexp for verifying the password
# @param -success     regexp for success changing the password
# @param -login       regexp for the telnet prompt for the loginname
# @param -host        hostname to be connected
# @param -timeout     timeout for each step
# @param -log         file for writing error messages
# @param -output      file for loging the output
# @param -telnet      use telnet
# @param -ssh         use ssh (default)
# @param -rlogin      use rlogin
# @param -slogin      use slogin
# @param -sudo        use sudo
# @param -program     command for changing passwords
#
# @return             0 on success, 1 on failure
#


# default values
set host               "localhost"
set login              "ssh"
set program            "passwd"
set prompt_string      "(%|\\\$|>)"
set fingerprint_string "The authenticity of host.* can't be established.*\nRSA key fingerprint is.*\nAre you sure you want to continue connecting.*"
set password_string    "(P|p)assword.*"
set oldpassword_string "((O|o)ld|login|\\\(current\\\) UNIX) (P|p)assword.*"
set newpassword_string "(N|n)ew.* (P|p)assword.*"
set badoldpassword_string "(Authentication token manipulation error).*"
set badpassword_string "((passwd|BAD PASSWORD).*|(passwd|Bad:).*\r)"
set verify_string      "((R|r)e-*enter.*(P|p)assword|Retype new( UNIX)? password|(V|v)erification|(V|v)erify|(A|a)gain).*"
set success_string     "((P|p)assword.* changed|successfully)"
set login_string       "(((L|l)ogin|(U|u)sername).*)"
set timeout            20
set log                "/tmp/passwd.out"
set output             false
set output_file        "/tmp/passwd.log"

# read input from stdin
fconfigure stdin -blocking 1

gets stdin user
gets stdin password(old)
gets stdin password(new)

# alternative: read input from command line
#if {$argc < 3} {
#    send_user "Too few arguments: Usage $argv0 username oldpass newpass"
#    exit 1
#}
#set user [lindex $argv 0]
#set password(old) [lindex $argv 1]
#set password(new) [lindex $argv 2]

# no output to the user
log_user 0

# read in other options
for {set i 0} {$i<$argc} {incr i} {
    set arg [lindex $argv $i]
    switch -- $arg "-prompt" {
        incr i
        set prompt_string [lindex $argv $i]
        continue
    } "-password" {
        incr i
        set password_string [lindex $argv $i]
        continue
    } "-oldpassword" {
        incr i
        set oldpassword_string [lindex $argv $i]
        continue
    } "-newpassword" {
        incr i
        set newpassword_string [lindex $argv $i]
        continue
    } "-verify" {
        incr i
        set verify_string [lindex $argv $i]
        continue
    } "-success" {
        incr i
        set success_string [lindex $argv $i]
        continue
    } "-login" {
        incr i
        set login_string [lindex $argv $i]
        continue
    } "-host" {
        incr i
        set host [lindex $argv $i]
        continue
    } "-timeout" {
        incr i
        set timeout [lindex $argv $i]
        continue
    } "-log" {
        incr i
        set log [lindex $argv $i]
        continue
    } "-output" {
        incr i
        set output_file [lindex $argv $i]
        set output true
        continue
    } "-telnet" {
        set login "telnet"
        continue
    } "-ssh" {
        set login "ssh"
        continue
    } "-ssh-exec" {
        set login "ssh-exec"
        continue
    } "-rlogin" {
        set login "rlogin"
        continue
    } "-slogin" {
        set login "slogin"
        continue
    } "-sudo" {
        set login "sudo"
        continue
    } "-program" {
        incr i
        set program [lindex $argv $i]
        continue
    }
}

# log session
if {$output} {
   log_file $output_file
}

set err [open $log "w" "0600"]

# start remote session
if {[string match $login "rlogin"]} {
   set pid [spawn rlogin $host -l $user]
} elseif {[string match $login "slogin"]} {
   set pid [spawn slogin $host -l $user]
} elseif {[string match $login "ssh"]} {
   set pid [spawn ssh $host -l $user]
} elseif {[string match $login "ssh-exec"]} {
   set pid [spawn ssh $host -l $user $program]
} elseif {[string match $login "sudo"]} {
   set pid [spawn sudo -u $user $program]
} elseif {[string match $login "telnet"]} {
   set pid [spawn telnet $host]
   expect -re $login_string {
     sleep .5
     send "$user\r"
   }
} else {
   puts $err "Invalid login mode. Valid modes: rlogin, slogin, ssh, telnet, sudo\n"
   close $err
   exit 1
}

set old_password_notentered true

if {![string match $login "sudo"]} {
  # log in
  expect {
    -re $fingerprint_string {sleep .5
                             send yes\r
                             exp_continue}
    -re $password_string    {sleep .5
                             send $password(old)\r}
    timeout                 {puts $err "Could not login to system (no password prompt)\n"
                             close $err
                             exit 1}
  }

  # start password changing program
  expect {
    -re $prompt_string      {sleep .5
                             send $program\r}
    # The following is for when passwd is the login shell or ssh-exec is used
    -re $oldpassword_string {sleep .5
                             send $password(old)\r
                             set old_password_notentered false}
    timeout                 {puts $err  "Could not login to system (bad old password?)\n"
                             close $err
                             exit 1}
  }
}

# send old password
if {$old_password_notentered} {
  expect {
    -re $oldpassword_string {sleep .5
                             send $password(old)\r}
    timeout                 {puts $err "Could not start passwd program (no old password prompt)\n"
                             close $err
                             exit 1}
  }
}

# send new password
expect {
  -re $newpassword_string {sleep .5
                           send $password(new)\r}
  -re $badoldpassword_string {puts $err "Old password is incorrect\n"
                           close $err
                           exit 1}
  timeout                 {puts "Could not change password (bad old password?)\n"
                           close $err
                           exit 1}
}

# send new password again
expect {
  -re $badpassword_string {puts $err "$expect_out(0,string)"
                           close $err
                           send \003
                           sleep .5
                           exit 1}
  -re $verify_string      {sleep .5
                           send $password(new)\r}
  timeout                 {puts $err "New password not valid (too short, bad password, too similar, ...)\n"
                           close $err
                           send \003
                           sleep .5
                           exit 1}
}

# check response
expect {
  -re $success_string {sleep .5
                       send exit\r}
  -re $badpassword_string {puts $err "$expect_out(0,string)"
                           close $err
                           exit 1}
  timeout             {puts $err "Could not change password.\n"
                       close $err
                       exit 1}
}

# exit succsessfully
expect {
  eof {close $err
       exit 0}
}
close $err