Reputation: 9484
I'm writing a Python script that changes the username and password of a Linux account user - it's part of a larger internal web-gui system that queues up password change requests from apache2
(which can't run as root), and then changes the passwords itself. The python script itself obviously must run as root in order to change passwords.
The password change function is pretty straightforward:
def chpasswd(user, passwd):
if os.getuid() != 0:
syslog.syslog("Error: chpasswd.py must be run as root")
return
proc = Popen(
['/usr/sbin/chpasswd'],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
print "Changing: " + user + ':' + passwd
out, err = proc.communicate(user + ':' + passwd)
proc.wait()
print out
if proc.returncode != 0:
print "Error: Return code", proc.returncode, ", stderr: ", out, err
if out:
syslog.syslog("stdout: " + out)
if err:
syslog.syslog("stderr: " + err)
The print
statements are just there for temporary debugging. This runs fine and doesn't report any errors - there's nothing on out
or err
; but for some reason the actual UNIX password simply isn't changed.
The script which invokes this function is listening on a locally bound TCP socket. When it receives a change password request (in the form of user:password
- later to be encrypted but for now plaintext) it adds it to a queue and then invokes the chpasswd
function.
So, typical usage would be:
# telnet localhost 7001
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
jsmith:mynewpassword
When the server is running in a bash window (not as a daemon) it prints out:
# python chpasswd.py
Starting Password Server
New connection from: 127.0.0.1
Changing: jsmith:mynewpassword
The last statement, you can see, is the print
statement in my chpasswd
function.
But after doing the above, when I actually try to login as a user using the new password, I get:
$ su jsmith
Password:
su: Authentication failure
Is there some obvious thing I'm doing wrong here? My suspicion was that somehow the connection with Popen
is not actually closing, or perhaps the single line user:password
text is not being transmitted. So I tried doing something like:
out, err = proc.communicate(user + ':' + passwd + '\x04')
Notice the extra \x04
character at the end, indicating End Of Transmission. Adding this in still didn't get it to work however - the password remained unchanged.
I'm running this on Debian Wheezy, in case it makes any difference.
Update:
Investigating further, I can see that my chpasswd
function actually is changing the password - if I cat
the /etc/shadow
file before and after connecting to my password server, I see there is a different hash.
It's just that when I try to authenticate using the plaintext password, it doesn't work. Therefore, my suspicion is that somehow, the communication with Popen
is either adding additional characters, or losing characters somehow. Of course, since /etc/shadow
is a hash, I can't figure out exactly what's going on here.
Upvotes: 1
Views: 4716
Reputation: 40693
The problem in this particular instance was that telnet adds "\r\n"
after you press return on entering text. Since your server was not stripping the data of whitespace this was preserved when changing the password.
It is possible to get telnet to not send the carriage return and newline characters by ending a line with the end-of-transmission character (EOT). You can do this by pressing Ctrl-D.
eg
$ telnet localhost 7001
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
jsmith:mynewpassword^DChanging: jsmith:mynewpassword
Alternatively you can pipe the line into telnet
echo -n jsmith:mynewpassword | telnet localhost 7001
Obviously, you'll only want to do this for testing or the new password will end up in your shell history. (The -n
argument suppresses the printing of newline characters by echo
)
Or you might want to do away with telnet
altogether and use netcat
instead.
echo -n jsmith:mynewpassword | netcat localhost 7001
Upvotes: 1