简体   繁体   中英

Changing Linux username or password with Python script

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM