Differences between revisions 19 and 20
Revision 19 as of 2022-01-20 05:29:14
Size: 5217
Editor: larryv
Comment: "it's" -> "its"; minor formatting tweaks
Revision 20 as of 2022-01-20 15:21:58
Size: 5114
Editor: GreyCat
Comment: More formatting changes, a few wording changes, minor example code change.
Deletions are marked like this. Additions are marked like this.
Line 9: Line 9:
Nothing you can do in bash can ''possibly'' work. {{{passwd(1)}}} does not read from standard input. This is ''intentional''. It is for your protection. Passwords were never intended to be put into programs, or generated '''by''' programs. They were intended to be entered only by the fingers of an actual human being, with a functional brain, and never, ever written down anywhere. So before you continue, consider the possibility that the authors of {{{passwd(1)}}} were on to something, and you probably '''shouldn't''' be trying to script {{{passwd(1)}}} input. Nothing you can do in bash can ''possibly'' work. The traditional `passwd(1)` does not read from standard input. This is ''intentional''. It is for your protection. Passwords were never intended to be put into programs, or generated '''by''' programs. They were intended to be entered only by the fingers of an actual human being, with a functional brain, and never, ever written down anywhere. So before you continue, consider the possibility that the authors of `passwd(1)` were on to something, and you probably '''shouldn't''' be trying to script `passwd(1)` input.
Line 15: Line 15:
The first approach involves constructing your own hashed password (DES, MD5, Blowfish, or whatever your OS uses) using nonstandard tools such as http://wooledge.org/~greg/crypt/ or Debian/Ubuntu's `mkpasswd` package. You would then write that hashed password, along with additional fields, in a line in your system's local password-hash file (which may be {{{/etc/passwd}}}, or {{{/etc/shadow}}}, or {{{/etc/master.passwd}}}, or {{{/etc/security/passwd}}}, or ...). This requires that you read the relevant man pages on your system, find out where the password hash goes, what formatting the file requires, and then construct code that writes it out in that format. The first approach involves constructing your own hashed password (DES, MD5, Blowfish, or whatever your OS uses) using nonstandard tools such as http://wooledge.org/~greg/crypt/ or Debian/Ubuntu's `mkpasswd` package. You would then write that hashed password, along with additional fields, in a line in your system's local password-hash file (which may be `/etc/passwd`, or `/etc/shadow`, or `/etc/master.passwd`, or `/etc/security/passwd`, or ...). This requires that you read the relevant man pages on your system, find out where the password hash goes, what formatting the file requires, and then construct code that writes it out in that format.
Line 17: Line 17:
A minor variant of this involves using a system-specific tool to write the line for you, given the hashed password that you constructed. For example, on Debian/Ubuntu, we've been told that {{{useradd -m joe -s /bin/bash -p "$(mkpasswd "$password")"}}} might work. A minor variant of this involves using a system-specific tool to write the line for you, given the hashed password that you constructed. For example, on Debian/Ubuntu, we've been told that `useradd -m joe -s /bin/bash -p "$(mkpasswd "$password")"` might work.
Line 21: Line 21:
The second approach is to use [[http://expect.nist.gov/|expect]] or its [[http://pexpect.sourceforge.net/pexpect.html|python equivalent]]. I think expect even has this ''exact'' problem as one of its canonical examples. The second approach is to use [[http://expect.nist.gov/|Expect]] or its [[http://pexpect.sourceforge.net/pexpect.html|python equivalent]]. I think Expect even has this ''exact'' problem as one of its canonical examples.
Line 25: Line 25:
Finally, system-specific tools designed to do this may already exist on your platform. For example, some GNU/Linux systems have a {{{newusers(8)}}} command specifically designed for this; or a {{{chpasswd(8)}}} tool which can be coerced into doing these sorts of things. Or they may have a `--stdin` flag on their `passwd` command. Also try commands such as `apropos users` or `man -k account` to see what else might exist. Be creative. Finally, system-specific tools designed to do this may already exist on your platform. We've already mentioned `useradd`. Some GNU/Linux systems also have a `newusers(8)` command specifically designed for this, or a `chpasswd(8)` tool which can be coerced into doing these sorts of things. Or they may have a `--stdin` flag on their `passwd` command. Also try commands such as `apropos users` or `man -k account` to see what else might exist. Be creative.
Line 32: Line 32:
As an aside, the reverse of this FAQ is also a problem. It's trivial, at least under Linux, to wrap any program in a way that forces the controlling terminal to be an abstraction that's connected to any kind of I/O you like. This means it's very difficult to securely guarantee that a user with local access is actually giving your program input directly from a keyboard. Often people do this by reading from {{{/dev/tty}}}. This, just like the way the {{{passwd}}} program works, is only a small step to discourage bad security practices like storing passwords in plain text files. The following runs Bash, which reads a program on FD 3, which unwittingly gets its input through a pipe (which could just as easily be a file), using just one function from the Python standard library. As an aside, the reverse of this FAQ is also a problem. It's trivial, at least under Linux, to wrap any program in a way that forces the controlling terminal to be an abstraction that's connected to any kind of I/O you like. This means it's very difficult to securely guarantee that a user with local access is actually giving your program input directly from a keyboard. Often people do this by reading from `/dev/tty`. This, just like the way the `passwd` program works, is only a small step to discourage bad security practices like storing passwords in plain text files. The following runs Bash, which reads a program on FD 3, which unwittingly gets its input through a pipe (which could just as easily be a file), using just one function from the Python standard library.
Line 39: Line 39:
    printf '\n%s\n' "password is: $passw"     printf '\npassword is: %s\n' "$passw"
Line 56: Line 56:
    printf '\n%s\n' "password is: $passw"     printf '\npassword is: %s\n' "$passw"
Line 62: Line 62:
Additionally, reading from {{{/dev/tty}}} is just plain annoying because it breaks the way users expect their redirections to work. Just don't do it. Better is to use {{{[[ -t 0 ]]}}} to test for a tty and handle the condition accordingly. Even this can be annoying when a sysadmin is expecting certain behavior that changes depending on I/O. If you must use either of these tricks, document it, and provide an option to disable any I/O conditional behavior.

Also you can use the {{{newusers}}} utility to add new users with predefined passwords.
Additionally, reading from `/dev/tty` is just plain annoying because it breaks the way users expect their redirections to work. Just don't do it. Better is to use `[[ -t 0 ]]` to test for a tty and handle the condition accordingly. Even this can be annoying when a sysadmin is expecting certain behavior that changes depending on I/O. If you must use either of these tricks, document it, and provide an option to disable any I/O conditional behavior.

I want to set a user's password using the Unix passwd command, but how do I script that? It doesn't read standard input!

OK, first of all, I know there are going to be some people reading this, right now, who don't even understand the question. Here, this does not work:

{ echo oldpass; echo newpass; echo newpass; } | passwd
# This DOES NOT WORK!

Nothing you can do in bash can possibly work. The traditional passwd(1) does not read from standard input. This is intentional. It is for your protection. Passwords were never intended to be put into programs, or generated by programs. They were intended to be entered only by the fingers of an actual human being, with a functional brain, and never, ever written down anywhere. So before you continue, consider the possibility that the authors of passwd(1) were on to something, and you probably shouldn't be trying to script passwd(1) input.

Nonetheless, we get hordes of users asking how they can circumvent 35 years of Unix security. And we get people contributing their favorite security-removing "solutions" to this page. If you still think this is what you want, read on.

Construct your own hashed password and write it to some file

The first approach involves constructing your own hashed password (DES, MD5, Blowfish, or whatever your OS uses) using nonstandard tools such as http://wooledge.org/~greg/crypt/ or Debian/Ubuntu's mkpasswd package. You would then write that hashed password, along with additional fields, in a line in your system's local password-hash file (which may be /etc/passwd, or /etc/shadow, or /etc/master.passwd, or /etc/security/passwd, or ...). This requires that you read the relevant man pages on your system, find out where the password hash goes, what formatting the file requires, and then construct code that writes it out in that format.

A minor variant of this involves using a system-specific tool to write the line for you, given the hashed password that you constructed. For example, on Debian/Ubuntu, we've been told that useradd -m joe -s /bin/bash -p "$(mkpasswd "$password")" might work.

Fool the computer into thinking you are a human

The second approach is to use Expect or its python equivalent. I think Expect even has this exact problem as one of its canonical examples.

Find some magic system-specific tool

Finally, system-specific tools designed to do this may already exist on your platform. We've already mentioned useradd. Some GNU/Linux systems also have a newusers(8) command specifically designed for this, or a chpasswd(8) tool which can be coerced into doing these sorts of things. Or they may have a --stdin flag on their passwd command. Also try commands such as apropos users or man -k account to see what else might exist. Be creative.

See also FAQ #69 -- I want to automate an ssh (or scp, or sftp) connection.


Don't rely on /dev/tty for security

As an aside, the reverse of this FAQ is also a problem. It's trivial, at least under Linux, to wrap any program in a way that forces the controlling terminal to be an abstraction that's connected to any kind of I/O you like. This means it's very difficult to securely guarantee that a user with local access is actually giving your program input directly from a keyboard. Often people do this by reading from /dev/tty. This, just like the way the passwd program works, is only a small step to discourage bad security practices like storing passwords in plain text files. The following runs Bash, which reads a program on FD 3, which unwittingly gets its input through a pipe (which could just as easily be a file), using just one function from the Python standard library.

 ~ $ { echo 'o hi there' | python -c 'import pty; pty.spawn(["bash", "/dev/fd/3"])'; } <<"EOF" 3<&0- <&2 # <&2 prevents disconnecting echo's stdin. No real effect.
{
    stty -echo
    read -p 'Password: ' passw
    printf '\npassword is: %s\n' "$passw"
    stty echo
} </dev/tty
EOF

o hi there
Password:
password is: o hi there


# version without using Python

#{ echo 'o hi there' | script -c "bash /dev/fd/3" /dev/null; } <<"EOF" 3<&- 3<&0- <&2  # Linux
{ echo 'o hi there' | script -q /dev/null bash /dev/fd/3; } <<"EOF" 3<&- 3<&0- <&2     # FreeBSD; Mac OS X
{
    stty -echo
    read -p 'Password: ' passw
    printf '\npassword is: %s\n' "$passw"
    stty echo
} </dev/tty
EOF

Additionally, reading from /dev/tty is just plain annoying because it breaks the way users expect their redirections to work. Just don't do it. Better is to use [[ -t 0 ]] to test for a tty and handle the condition accordingly. Even this can be annoying when a sysadmin is expecting certain behavior that changes depending on I/O. If you must use either of these tricks, document it, and provide an option to disable any I/O conditional behavior.

BashFAQ/078 (last edited 2022-01-20 15:21:58 by GreyCat)