Differences between revisions 11 and 15 (spanning 4 versions)
Revision 11 as of 2010-09-30 17:40:35
Size: 2517
Editor: 41
Comment:
Revision 15 as of 2012-04-06 14:33:05
Size: 4750
Editor: GreyCat
Comment: move "don't read from /dev/tty" down to the bottom, and tell what FAQ 69 is
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. 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.
Line 11: Line 11:
Nonetheless, we get hordes of users asking how they can circumvent 35 years of Unix security. 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.
Line 13: Line 13:
You have a few choices. The first is to manually generate your own hashed password strings (for example, using http://wooledge.org/~greg/crypt/ or a similar tool) and then write them to 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. === Construct your own hashed password and write it to some file ===
Line 15: Line 15:
The second is to use [[http://expect.nist.gov/|expect]]. I think it even has this ''exact'' problem as one of its canonical examples. 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:
The third is to use some system-specific tools which may or may not 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. Also try commands such as `apropos users` or `man -k account` to see what else might exist. Be creative. 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 19: Line 19:
A fourth option that works at least on linux (if not other systems) is {{{ echo "password" | passwd --stdin username }}}. Check your `passwd(1)` man page before using.
 ''GNU strikes again.'' -- GreyCat
=== Fool the computer into thinking you are a human ===
Line 22: Line 21:
A fifth option (for debian and ubuntu) is to use mkpasswd (make sure to first {{{ apt-get install mkpasswd }}} or you will be missing this binary). You then use the output from this binary together with '''useradd''''s -p option. For example: {{{ useradd -m joe -s /bin/bash -p `mkpasswd "123456"` }}}. 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.
=== Find some magic system-specific tool ===
Line 24: Line 24:
See also [[BashFAQ/069|FAQ #69]]. 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.

See also [[BashFAQ/069|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 it's 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 '\n%s\n' "password is: $passw"
    stty echo
} </dev/tty
EOF

o hi there
Password:
password is: o hi there
}}}

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. 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. 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.

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 it's 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 '\n%s\n' "password is: $passw"
    stty echo
} </dev/tty
EOF

o hi there
Password:
password is: o hi there

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 2023-06-07 16:48:20 by larryv)