How to change a Linux user password from python

后端 未结 6 845
心在旅途
心在旅途 2021-01-03 08:23

I\'m having problems with changing a Linux user\'s password from python. I\'ve tried so many things, but I couldn\'t manage to solve the issue, here is the sample of things

相关标签:
6条回答
  • 2021-01-03 08:31

    I ran accross the same problem today and I wrote a simple wrapper around subprocess to call the passwd command and feed stdin with the new password. This code is not fool proof and only works when running as root which does not prompt for the old password.

    import subprocess
    from time import sleep
    
    PASSWD_CMD='/usr/bin/passwd'
    
    def set_password(user, password):
        cmd = [PASSWD_CMD, user]
        p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
        p.stdin.write(u'%(p)s\n%(p)s\n' % { 'p': password })
        p.stdin.flush()
        # Give `passwd` cmd 1 second to finish and kill it otherwise.
        for x in range(0, 10):
            if p.poll() is not None:
                break
            sleep(0.1)
        else:
            p.terminate()
            sleep(1)
            p.kill()
            raise RuntimeError('Setting password failed. '
                    '`passwd` process did not terminate.')
        if p.returncode != 0:
            raise RuntimeError('`passwd` failed: %d' % p.returncode)
    

    If you need the output of passwd you can also pass stdout=subprocess.PIPE to the Popen call and read from it. In my case I was only interested if the operation succeeded or not so I simply skipped that part.

    Security consideration: Do not use something like echo -n 'password\npassword\n | passwd username' as this will make the password visible in the process list.

    SUDO

    Since you seam to want to be using sudo passwd <username> I would recommend adding a new line to your /etc/sudoers (use visudo for that!)

    some_user ALL = NOPASSWD: /usr/bin/passwd
    

    Sudo will not ask for the password for some_user and the script will run as expected.

    Alternatively simply add an extra p.stdin.write(u'%s\n' % SUDO_PASSWORD) line. That way sudo will receive the user password first and then passwd receives the new user password.

    0 讨论(0)
  • 2021-01-03 08:31

    As mentioned before passing passwords on the command line is not very secure unfortunately. Additionally something like "--stdin" for passwd is not available on every passwd implementation. Therefor here is a more secure version using chpasswd:

    def setPassword(userName:str, password:str):
        p = subprocess.Popen([ "/usr/sbin/chpasswd" ], universal_newlines=True, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (stdout, stderr) = p.communicate(userName + ":" + password + "\n")
        assert p.wait() == 0
        if stdout or stderr:
            raise Exception("Error encountered changing the password!")
    

    Explanation:

    With subprocess.Popen we launch an instance of chpasswd. We pipe the user name and password to an instance of chpasswd. chpasswd will then change the password using the settings defined for the current operating system. chpasswd will remain completely silent if no error occurred. Therefor the code above will raise an exception if any kind of error occurred (without having a closer look to the actual error).

    0 讨论(0)
  • usermod-based version:

    #!/usr/bin/env python
    from crypt      import crypt
    from getpass    import getpass
    from subprocess import Popen, PIPE
    
    sudo_password_callback = lambda: sudo_password # getpass("[sudo] password: ")
    username, username_newpassword = 'testaccount', '$2&J|5ty)*X?9+KqODA)7'
    
    # passwd has no `--stdin` on my system, so `usermod` is used instead
    # hash password for `usermod`
    try:
        hashed = crypt(username_newpassword) # use the strongest available method
    except TypeError: # Python < 3.3
        p = Popen(["mkpasswd", "-m", "sha-512", "-s"], stdin=PIPE, stdout=PIPE,
                  universal_newlines=True)
        hashed = p.communicate(username_newpassword)[0][:-1] # chop '\n'
        assert p.wait() == 0
    assert hashed == crypt(username_newpassword, hashed)
    
    # change password
    p = Popen(['sudo', '-S',  # read sudo password from the pipe
               # XXX: hashed is visible to other users
               'usermod',  '-p', hashed, username],
              stdin=PIPE, universal_newlines=True)
    p.communicate(sudo_password_callback() + '\n')
    assert p.wait() == 0
    
    0 讨论(0)
  • 2021-01-03 08:44

    For those that --stdin isn't an option:

    import subprocess
    cmd = "bash -c \"echo -e 'NewPassword\\nNewPassword' | passwd root\""
    subprocess.check_call(cmd, shell=True)
    
    0 讨论(0)
  • 2021-01-03 08:47

    The user you are running this as must have sudo permission to run the passwd command without a password.

    >>> from subprocess import Popen
    >>> proc = Popen(['/usr/bin/sudo', '/usr/bin/passwd', 'test', '--stdin'])
    >>> proc.communicate('newpassword')
    
    0 讨论(0)
  • 2021-01-03 08:48

    Try using the '--stdin' option to the passwd command in your pipes. To quote from the man page:

        --stdin
          This option is used to indicate that passwd should read the new
          password from standard input, which can be a pipe.
    

    Another option, if your Linux has the usermod command, as root (or via sudo) you can explicitly set the (encrypted) password using the '-p' option.

    0 讨论(0)
提交回复
热议问题