简体   繁体   中英

How can I use Python to automate setting a password using the Unix pass command line program

I'm trying to automate setting new passwords using the Unix pass program. I understand that there is a Python library, pexpect , that might help, but I would like to avoid using third-party libraries.

When using a terminal, the flow looks like this:

$ pass insert --force gmail
>> Enter password for gmail: <type in password using masked prompt>
>> Retype password for gmail: <reenter password>

What I would like my function to do:

  1. Run the command pass insert --force {entry_name}
  2. Capture the output (and echo it for testing)
  3. Check output for the presence of 'password for gmail', and if True
    • write '{password}\\n' to stdin
    • write '{password}\\n' to stdin again
  4. Echo any errors or messages for testing

Issues:

I'm stuck on step 2. The subprocess either hangs indefinitely, times out with an error, or produces no output.

Attempts:

  • I've tried configurations of Popen(), using both stdin.write() and communicate().
  • I've set wait() calls at various points.
  • I've tried both the shell=True and shell=False options (prefer False for security reasons)

Code :

def set_pass_password(entry_name, password):
    from subprocess import Popen, PIPE

    command = ['pass', 'insert', '--force', entry_name]

    sub = Popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)

    # At this point I assume that the command has run, and that there is an "Enter password..." message
    message = sub.stdout.read()  # also tried readline() and readlines()
    print(message) # never happens, because process hangs on stdout.read()

    if 'password for {}'.format(entry_name) in message:
        err, msg = sub.communicate(input='{p}\n{p}\n'.format(p=password))
        print('errors: {}\nmessage: {}'.format(err, msg))

Edit: the original answer was about passwd , which is what's used to set passwords. I noticed late that you use pass , which is a keystore (doesn't actually change the Unix password). The pass program works differently and will not print a prompt if stdin is not a tty. Therefore the following very simple program works:

def set_pass_password(entry_name, password):
    from subprocess import Popen, PIPE

    command = ['pass', 'insert', '--force', entry_name]

    sub = Popen(command, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE)

    err, msg = sub.communicate(input='{p}\n{p}\n'.format(p=password))
    print('errors: {}\nmessage: {}'.format(err, msg))

if __name__ == "__main__":
    set_pass_password("ttt", "ttt123asdqwe")

(you will see that both stderr and stdout are empty, if the command succeeded).

For the passwd command:

FYI: the passwd command outputs the prompt to stderr , not stdout .

NOTE: rather than sending the password twice in the same 'write', you might need to wait for the second prompt before sending the password again.

For this simple case, code similar to yours should work, but in general you should use select on all the pipes and send/receive data when the other side is ready, so you don't get deadlocks.

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