Odd behaviour with Expect password update script

Hi there. I've been forced by circumstance to write an expect script to handle password updates on a number of servers. There's a mix of Solaris 8, 9, 10, RedHat and Ubuntu. There's no chance the client will allow us to hook them up to a directory, so we have to make do.

This script is mostly intended for myself and my colleagues to update our own passwords, but I've built in the ability for it to update a user's passwords. I've also elected to use ssh $server sudo passwd to get around the not-standardised password policies. It has a wrapper script written in bash, so in the near future I may enforce a password policy there.

I've found some unexpected behaviour though. As part of error-handling, I'm trying to cater for instances where the script logs in to a server as the sysadmin, but finds that their password is expired. Here's the relevant bit of the script, and hopefully the comments will help you follow the logic:

# 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 ssh key 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 echo'd back, which messes the following expect

        # 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
                                }
                        }
                }
        }

Here's the behaviour (sanitised and using QWERTYuiop12345 as an example password)

$ expect expect.chpwd examplehost me
spawn ssh -q -o StrictHostKeyChecking=no -t examplehost sudo passwd me
me@examplehost's password:
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user me.
Changing password for me
(current) UNIX password:
QWERTYuiop12345

passwd: Authentication token manipulation error

ERROR: It appears we can't authenticate to examplehost.  You'll need to resolve that manually.

And here's the debug:

expect: does "" (spawn_id exp7) match regular expression "me@examplehost's [pP]assword:"? Gate "me@examplehost's ?assword:"? gate=no
"\(current\) UNIX [pP]assword:|[oO]ld [pP]assword:"? (No Gate, RE only) gate=yes re=no
"\[sudo\] password for|[pP]assword:"? (No Gate, RE only) gate=yes re=no
me@examplehost's password:
expect: does "me@examplehost's password: " (spawn_id exp7) match regular expression "me@examplehost's [pP]assword:"? Gate "me@examplehost's ?assword:"? gate=yes re=yes
expect: set expect_out(0,string) "me@examplehost's password:"
expect: set expect_out(spawn_id) "exp7"
expect: set expect_out(buffer) "me@examplehost's password:"
send: sending "QWERTYuiop12345\n\r" to { exp7 }
expect: continuing expect

expect: does " " (spawn_id exp7) match regular expression "me@examplehost's [pP]assword:"? Gate "me@examplehost's ?assword:"? gate=no
"\(current\) UNIX [pP]assword:|[oO]ld [pP]assword:"? (No Gate, RE only) gate=yes re=no
"\[sudo\] password for|[pP]assword:"? (No Gate, RE only) gate=yes re=no


expect: does " \r\n" (spawn_id exp7) match regular expression "me@examplehost's [pP]assword:"? Gate "me@examplehost's ?assword:"? gate=no
"\(current\) UNIX [pP]assword:|[oO]ld [pP]assword:"? (No Gate, RE only) gate=yes re=no
"\[sudo\] password for|[pP]assword:"? (No Gate, RE only) gate=yes re=no
WARNING: Your password has expired.
You must change your password now and login again!

expect: does " \r\nWARNING: Your password has expired.\r\nYou must change your password now and login again!\r\n" (spawn_id exp7) 
match regular expression "me@examplehost's [pP]assword:"? Gate "me@examplehost's ?assword:"? gate=no
"\(current\) UNIX [pP]assword:|[oO]ld [pP]assword:"? (No Gate, RE only) gate=yes re=no
"\[sudo\] password for|[pP]assword:"? (No Gate, RE only) gate=yes re=no
Changing password for user me.
Changing password for me
(current) UNIX password:

expect: does " \r\nWARNING: Your password has expired.\r\nYou must change your password now and login again!\r\nChanging password for user me.\r\nChanging password for me\r\n(current) UNIX password: \r\n" (spawn_id exp7) 
match regular expression "me@examplehost's [pP]assword:"? Gate "me@examplehost's ?assword:"? gate=no
"\(current\) UNIX [pP]assword:|[oO]ld [pP]assword:"? (No Gate, RE only) gate=yes re=yes
expect: set expect_out(0,string) "(current) UNIX password:"
expect: set expect_out(spawn_id) "exp7"
expect: set expect_out(buffer) " \r\nWARNING: Your password has expired.\r\nYou must change your password now and login again!\r\nChanging password for user me.\r\nChanging password for me\r\n(current) UNIX password:"
send: sending "QWERTYuiop12345\n\r" to { exp7 }
Gate keeper glob pattern for '[nN]ew UNIX [pP]assword:|[nN]ew [pP]assword:|[aA]gain:|[rR]etype|[rR]e-enter|[rR]eenter' is ''. Not usable, disabling the performance booster.
Gate keeper glob pattern for 'updated|successfully' is ''. Not usable, disabling the performance booster.

expect: does " \r\n" (spawn_id exp7) match regular expression "[nN]ew UNIX [pP]assword:|[nN]ew [pP]assword:|[aA]gain:|[rR]etype|[rR]e-enter|[rR]eenter"? (No Gate, RE only) gate=yes re=no
"BAD PASSWORD"? no
"updated|successfully"? (No Gate, RE only) gate=yes re=no
QWERTYuiop12345


expect: does " \r\nQWERTYuiop12345\r\n\r\n" (spawn_id exp7) match regular expression "[nN]ew UNIX [pP]assword:|[nN]ew [pP]assword:|[aA]gain:|[rR]etype|[rR]e-enter|[rR]eenter"? (No Gate, RE only) gate=yes re=no
"BAD PASSWORD"? no
"updated|successfully"? (No Gate, RE only) gate=yes re=no
passwd: Authentication token manipulation error

expect: does " \r\nQWERTYuiop12345\r\n\r\npasswd: Authentication token manipulation error\r\r\n" (spawn_id exp7) 
match regular expression "[nN]ew UNIX [pP]assword:|[nN]ew [pP]assword:|[aA]gain:|[rR]etype|[rR]e-enter|[rR]eenter"? (No Gate, RE only) gate=yes re=no
"BAD PASSWORD"? no
"updated|successfully"? (No Gate, RE only) gate=yes re=no
expect: read eof
expect: set expect_out(spawn_id) "exp7"
expect: set expect_out(buffer) " \r\nQWERTYuiop12345\r\n\r\npasswd: Authentication token manipulation error\r\r\n"

ERROR: It appears we can't authenticate to examplehost.  You'll need to resolve that manually.

So you can see that -re "$whoami@$server's \[pP\]assword:" is found, the password is sent, and the script moves on. It then finds (current) UNIX password: and dutifully sends the password, however that password is printed out to the console (echoing off the remote ssh process?) and we get an Authentication token manipulation error.

The if/else works fine. I've also commented it entirely out and just had

        -re "\\\(current\\\) UNIX \[pP\]assword:|\[oO\]ld \[pP\]assword:" {
                send "$adminpass\r"
                exp_continue
        }

with the same behaviour. Could it be the buffer?

Apart from this one niggle, the rest of the script works exactly as desired.

Any thoughts appreciated.