passwd in one command isn't working - linux

I developed a tool to send one-line commands to different linux machines in one shot using JSch (A java library used to communicating with other machines over ssh)
So our client needs to change the password on ALL machines. Google helped me reach this point:
echo -e "123\n123" | passwd username
Where '123' is the new password.
The command executes but this is ALWAYS the output:
[root#QNA-XR1 ~]# echo -e "123\n123" | passwd
Changing password for root
New password:
Retype password:
passwd: password for root is unchanged
Which indicates that the command didn't succeed.
Please note that this is an small device with linux running on it. It's a privately compiled version to be as compact as possible. I don't know much about linux actually !
This is the machines info:
[root#QNA-XR1 ~]# uname -a
Linux QNA-XR1 2.6.22-XR100-v1.1.7 #1 Tue Aug 19 22:55:50 EDT 2008 ppc unknown
passwd help:
[root#QNA-XR1 ~]# passwd --help
BusyBox v1.7.3 (2008-01-09 00:06:30 EST) multi-call binary
Usage: passwd [OPTION] [name]
Change a user password. If no name is specified,
changes the password for the current user.
Options:
-a Define which algorithm shall be used for the password
(choices: des, md5)
-d Delete the password for the specified user account
-l Locks (disables) the specified user account
-u Unlocks (re-enables) the specified user account
echo help
[root#QNA-XR1 ~]# help echo
echo: echo [-neE] [arg ...]
Output the ARGs. If -n is specified, the trailing newline is
suppressed. If the -e option is given, interpretation of the
following backslash-escaped characters is turned on:
\a alert (bell)
\b backspace
\c suppress trailing newline
\E escape character
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash
\num the character whose ASCII code is NUM (octal).
You can explicitly turn off the interpretation of the above characters
with the -E option.
Thanks a lot in advance for you help.

You can use chpasswd (as root but it's not secure):
# echo "user:passwd" | chpasswd

/bin/passwd may be opening /dev/tty to force reading from a terminal instead of a pipe.
You may be better off encrypting (hashing, really) your new password using crypt(), and then replacing the password hash in /etc/shadow (for systems that have it) or /etc/passwd (for systems that don't). This has the disadvantage of being somewhat OS-dependent, but it doesn't get into weird tty games.
You might also be able to force allocation of a tty in ssh - ssh can operate both with or without one. Then you would add a couple of delays before sending the password in plaintext twice - this method is less OS-dependent, but tty games can be less than fun sometimes.

You can use the single command to change the user password.
echo -e "password\npassword" | sudo -S passwd testuser

Related

How can I script a remote connection that is elevated via su?

I have scripts and programs which run commands on a remote computer, and I need some of the commands to be elevated, I.e. su. Since this is scripted, I cannot rely on a user to enter the password; it needs to be passed to su by the script.
I have tried a bunch of things, including echoing the password to su like so:
ssh user#host "echo password | su -c myCommand"
and
echo password | ssh user#host su -c myCommand
and in programming languages like C# creating a new process and reading from its stdout and writing the password to its stdin.
I've tried some alternate things others have mentioned online, but they generally involve commands that I do not have on some of the machines. Some of them have no expect or sudo or other alternatives others have mentioned.
How can I do this without installing any other tools?
The su tag says general su support is off topic, but this is not about su support or usage, it's specifically about a programming problem which requires elevated su use, so hopefully it's not taken as off topic. "Linux & Unix" and "Super User" users may be interested in this question too, but it really applies more to programming.
TL;DR: Use echo pass | ssh -t -t user#host 'su -c "command"'
This one gave me headaches and took a while to figure out. There are hints about the answer elsewhere, but I have not seen the answer stated explicitly anywhere, so it took a bit of playing around with to get right.
Using ssh
echo <password> | ssh -t -t <user>#<host> 'su -c "<command>"'
The quotes seem to be necessary. If I omitted one or the other pair of quotes I got incorrect results for some commands. For example, <…> ssh -t -t me#mypc su -c "ls -l /root/something_secure" would give me the ls contents of me's home directory, ignoring the -l and the /root/something_secure. So I had to have the quotes set up like that.
The -t is where the magic is at for our need, and yes you should put 2 of them. Doing echo pass | su -c command can fail because su doesn't take a password from standard input. Doing echo pass | ssh <…> su -c command seems like a good idea at first, because su needs to get input from ssh, and ssh gets it from standard input, but really su gets it from a "tty", not from standard input. The -t tells ssh to fake it by using a pseudo-tty which sends standard input to this fake tty. Sometimes ssh will complain and not want to allocate the pseudo-tty for you, so doing the -t twice tells ssh to shut up and do it anyway.
-t' Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful,
e.g. when implementing menu services. Multiple -t options force tty
allocation, even if ssh has no local tty.
From man ssh
Example:
echo MySecretPass | ssh -t -t me#mydevice 'su -c "chown me /some/file"'
Ssh does not have a password parameter. If you have not set up a password-less secure login, you should do so. Read up here. Otherwise, this answer will supply the password to su for you, but you would still have to supply the password to ssh for the initial ssh login.
Using plink (Putty command line ssh-like tool for Windows)
echo <password> | plink -t -t -pw <password> <user>#<host> "su -c '<command>'"
Again, the quotes seemed to be necessary. If I omitted them, the results were incorrect, similar as with using ssh.
Example:
echo RootPass | plink -t -t -pw MePass me#mydevice "su -c 'echo Rooty!> /root/rootiness'"
Reasoning is similar to the ssh section above.
Your favorite programming language
If your favorite programming language supports the ability to execute a shell command and control the standard input/output for it, then instead of piping an echo you can instead run the ssh or plink command above (minus the piped echo), then put the password into standard input.
So for C#
Process process = new Process();
// If I remember correctly, I think UseShellExecute needs to be false to redirect std in/out
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
// You can use either ssh or plink; I'll use plink here
process.StartInfo.FileName = "<path to plink>\\plink.exe";
process.StartInfo.Arguments = "-t -t -l " + username + " -pw " + password + " " + hostname + " 'su -c \"MyCommand\"'";
process.StartInfo.CreateNoWindow = true;
process.OutputDataReceived += new DataReceivedEventHandler(FunctionCallbackYouMake);
process.ErrorDataReceived += new DataReceivedEventHandler(AnotherFunctionYouMake);
// I'm not actually sure if this next line is necessary, but I've been using it because of an example I used
process.SynchronizingObject = null;
process.Start();
And then read the data coming in through the ssh (plink) connection by using the function callback you make and provide to OutputDataReceived. It has the form void f(object sending_process, DataReceivedEventArgs event)
You can write data to process.StandardInput, including the su password, such as
process.StandardInput.Write(password);
process.StandardInput.Flush();
I've done something similar in Java as well, so I know you can do pretty much the same thing there, but it's been so long I don't recall all the details, but it also involves a process object and setting up std in/out handlers. If I have time later I might include some of those details.
No matter what language or method you use, some programs may block if they get too much stuff in their output streams or error streams, so make sure that you register for both standard output and standard error even if you don't care about them, even if all you do is read their data and discard it.
But don't hard-code the password!
Since this is for programming, there is generally no need to hard-code passwords into scripts. You could if you absolutely had to, but preferably use variables place of the username, password, and host name so that you are more secure and your tool is more flexible.
For example, I have a line in Windows cmd which does the following
echo %password% | plink -t -t -pw %password% %username%#%ip% "su -c '<what I want to do goes here>'"
And the username, password, and ip are supplied as arguments to the cmd file. Also note the C# example above, they are variables.

'su' by using 'script' in Docker returns different results compared to the standard environment

I need to request certain commands via su including password in one line.
I found a solution and it is working in a standard environment (Ubuntu) (more about solution here):
{ sleep 1; echo password; } | script -qc 'su -l user -c id' /dev/null | tail -n +2
But I am faced with the problem that this solution is not suitable in a Docker container environment
Script terminates the command without waiting for echo and as a result i get:
su: Authentication failure
Any help is much appreciated.
Passing the password for su via stdin is problematic for various reasons: the biggest one is probably that your password will end up in the history.
You could instead:
Call the entire script as the specific user and thus enter the password manually
Use sudo with the appropriate NOPASSWD sudoers configuration
In your case you are using docker, so you could just set the USER in your Dockerfile

Access SSH client IP address, within a screen session

Accessing the IP address of a connecting SSH client is possible via environment variables (such as SSH_CONNECTION), as described in
Find the IP address of the client in an SSH session
In a GNU screen session though, those environment variables are defined by whoever started the screen to begin with. Is there any way to also get hold of the SSH connection information, for someone who enters an already-existing screen session later, like from another host?
I can't think of a way to determine this, but this can be useful in cases where screen sessions are shared between different people, for example.
If the screen session is launched as root, you can but it won't be perfectly reliable
If two users type in the same screen window, they will both interact within the same shell. One can write a command. The other can press the <enter> key.
You have to get access to the environment variable SSH_CONNECTION (or better SSH_CLIENT) which is only possible if you are root, or if you use the same user inside the screen session.
Supposing you are root inside the screen session, you can know the last user active in a screen session by using the ps command and finding the last active session.
ps h -C screen katime -o pid,user
By using the pid, and accessing the /proc/<pid>/environ file, you can get the SSH_CLIENT variable.
sed -z '/SSH_CLIENT/p;d' /proc/`ps h -C screen katime -o pid |head -1`/environ
--> SSH_CLIENT=257.31.120.12
All of this suppose that your screen is executed as root
You can also chose to log all the active connections.
For such need, I would suggest you to store both the full list of connections and their last activity.
ps eh -C screen kstime -o pid,atime | while read pid stime; do echo -n "$stime: ";\
gawk -v 'RS=\0' -F= '$1=="SSH_CLIENT" {print $2}' /proc/$pid/environ; done
Result:
00:00:00: 257.31.120.12 61608 22
00:07:11: 258.1.2.3.4 49947 22
Note that you can also parse the result of the ps eh -C screen kstime -o args command if you find it easier.
EDIT:
This is a working Debian command to get all users currently connected to the same screen session:
find /var/run/screen/
-name $(pstree -sp $$ |sed 's/.*screen(\([0-9]*\)).*/\1/;q').*
-printf "%h\n"
| cut -f2 -d-
You can check the output of the last command that would list of all IP addresses or hostnames of all connection made if sshd is the only way to connect to server.
ec2-user]# last
ec2-user pts/0 115.250.185.183 Sun May 29 13:49 still logged in
ec2-user pts/0 115.250.140.241 Sat May 28 07:26 - 10:15 (02:48)
root pts/4 113.21.68.105 Tue May 3 10:15 - 10:15 (00:00)
Alternatively (on Linux), you can check /var/log/secure where sshd will usually log all details of all the connections made even if they don't result in successful logins.
If you're trying to support the multi-display mode ('screen -x'), then as someone said above you are likely out of luck.
One the other hand, if you could assume single-user mode, then you could create a wrapper/alias for the screen command that carries along an environment variable into screen (see 'screen -X stuff ...'); in this case you are just passing along SSH_CLIENT that will have the appropriate value.
If you can assume a given username comes from a single location (or, if more than one location, then simply choose most recent), then you can do some grep/sed on output of 'last' command.
client_ip=`last -ai | grep "still logged in" | grep "$USER " | grep -v '0.0.0.0' | tail -n 1 | sed 's/.* //g'`
echo "Hello $client_ip"
If your screen is starting usually in detached mode, then in your .screenrc, add the the following:
shell -$SHELL
Then your screen will have all the the variables.
For currently running screens that you are stuck with, simply run.
source ~/.bash_profile
Replace the path and the file name to match your environment.

Most reliable way to identify the current user through a sudo

I have an application that may or may not be run while users are sudo'ed to a shared user account. I would like to reliably identify who the real user is for a sort of "honor-system" ACL. I think there's some way by tracing parent/group/session process ids the way that the pstree command does, but I'm not sure how to do that best or if there are better alternatives.
I tried getlogin() originally. That works if ./myapp is used, but it fails with 'cat input | ./myapp` (because the "controlling terminal" is a pipe owned by the shared account).
I'd rather not trust environment variables, as I don't want my "honor system" to be completely thwarted by a simply unset, when the information is still available elsewhere.
I'd also like to avoid forcing a lookup in the password database, as that is a remote RPC (NIS or LDAP) and I'm pretty sure wtmp already contains the information I need.
For a shell script, you might use this to get the sudo'ing user:
WHO=$(who am i | sed -e 's/ .*//'`)
and extract the id from the login using:
ID_WHO=$(id -u $WHO)
I'll ferret out the C library equivalent later.
sudo sets the environment variables SUDO_USER, SUDO_UID, and SUDO_GID.
You can test this with:
$ sudo env
[sudo] password for shteef:
TERM=xterm
# [...snip...]
SHELL=/bin/bash
LOGNAME=root
USER=root
USERNAME=root
SUDO_COMMAND=/usr/bin/env
SUDO_USER=shteef
SUDO_UID=1000
SUDO_GID=1000
But if your users have shell access on the shared account, then I suppose you cannot blindly trust this either.
How about:
#!/usr/bin/ksh
username=`id | cut -d"=" -f2 | cut -d" " -f1`
if [ $username == "0(root)" ]
then
print "Yes, the user is root"
else
print "Sorry! the user $username, is not a root"
fi

Using the passwd command from within a shell script

I'm writing a shell script to automatically add a new user and update their password. I don't know how to get passwd to read from the shell script instead of interactively prompting me for the new password. My code is below.
adduser $1
passwd $1
$2
$2
from "man 1 passwd":
--stdin
This option is used to indicate that passwd should read the new
password from standard input, which can be a pipe.
So in your case
adduser "$1"
echo "$2" | passwd "$1" --stdin
[Update] a few issues were brought up in the comments:
Your passwd command may not have a --stdin option: use the chpasswd
utility instead, as suggested by ashawley.
If you use a shell other than bash, "echo" might not be a builtin command,
and the shell will call /bin/echo. This is insecure because the password
will show up in the process table and can be seen with tools like ps.
In this case, you should use another scripting language. Here is an example in Perl:
#!/usr/bin/perl -w
open my $pipe, '|chpasswd' or die "can't open pipe: $!";
print {$pipe} "$username:$password";
close $pipe
The only solution works on Ubuntu 12.04:
echo -e "new_password\nnew_password" | (passwd user)
But the second option only works when I change from:
echo "password:name" | chpasswd
To:
echo "user:password" | chpasswd
See explanations in original post: Changing password via a script
Nowadays, you can use this command:
echo "user:pass" | chpasswd
Read the wise words from:
http://mywiki.wooledge.org/BashFAQ/078
I quote:
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.
Nonetheless, we get hordes of users asking how they can circumvent 35 years of Unix security.
It goes on to explain how you can set your shadow(5) password properly, and shows you the GNU-I-only-care-about-security-if-it-doesn't-make-me-think-too-much-way of abusing passwd(1).
Lastly, if you ARE going to use the silly GNU passwd(1) extension --stdin, do not pass the password putting it on the command line.
echo $mypassword | passwd --stdin # Eternal Sin.
echo "$mypassword" | passwd --stdin # Eternal Sin, but at least you remembered to quote your PE.
passwd --stdin <<< "$mypassword" # A little less insecure, still pretty insecure, though.
passwd --stdin < "passwordfile" # With a password file that was created with a secure `umask(1)`, a little bit secure.
The last is the best you can do with GNU passwd. Though I still wouldn't recommend it.
Putting the password on the command line means anyone with even the remotest hint of access to the box can be monitoring ps or such and steal the password. Even if you think your box is safe; it's something you should really get in the habit of avoiding at all cost (yes, even the cost of doing a bit more trouble getting the job done).
Here-document works if your passwd doesn't support --stdin and you don't want to (or can't) use chpasswd for some reason.
Example:
#!/usr/bin/env bash
username="user"
password="pass"
passwd ${username} << EOD
${password}
${password}
EOD
Tested under Arch Linux. This passwd is an element of shadow-utils and installed from the core/filesystem package, which you usually have by default since the package is required by core/base.
You could use chpasswd
echo $1:$2 | chpasswd
For those who need to 'run as root' remotely through a script logging into a user account in the sudoers file, I found an evil horrible hack, that is no doubt very insecure:
sshpass -p 'userpass' ssh -T -p port user#server << EOSSH
sudo -S su - << RROOT
userpass
echo ""
echo "*** Got Root ***"
echo ""
#[root commands go here]
useradd -m newuser
echo "newuser:newpass" | chpasswd
RROOT
EOSSH
I stumbled upon the same problem and for some reason the --stdin option was not available on the version of passwd I was using (shipped in Ubuntu 14.04).
If any of you happen to experience the same issue, you can work it around as I did, by using the chpasswd command like this:
echo "<user>:<password>" | chpasswd
Tested this on a CentOS VMWare image that I keep around for this sort of thing. Note that you probably want to avoid putting passwords as command-line arguments, because anybody on the entire machine can read them out of 'ps -ef'.
That said, this will work:
user="$1"
password="$2"
adduser $user
echo $password | passwd --stdin $user
This is the definitive answer for a teradata node admin.
Go to your /etc/hosts file and create a list of IP's or node names in a text file.
SMP007-1
SMP007-2
SMP007-3
Put the following script in a file.
#set a password across all nodes
printf "User ID: "
read MYUSERID
printf "New Password: "
read MYPASS
while read -r i; do
echo changing password on "$i"
ssh root#"$i" sudo echo "$MYUSERID":"$MYPASS" | chpasswd
echo password changed on "$i"
done< /usr/bin/setpwd.srvrs
Okay I know I've broken a cardinal security rule with ssh and root
but I'll let you security folks deal with it.
Now put this in your /usr/bin subdir along with your setpwd.srvrs config file.
When you run the command it prompts you one time for the User ID
then one time for the password. Then the script traverses all nodes
in the setpwd.srvrs file and does a passwordless ssh to each node,
then sets the password without any user interaction or secondary
password validation.
For me on Raspbian it works only this way (old password added):
#!/usr/bin/env bash
username="pi"
password="Szevasz123"
new_ps="Szevasz1234"
passwd ${username} << EOD
${password}
${new_ps}
${new_ps}
EOD
Have you looked at the -p option of adduser (which AFAIK is just another name for useradd)? You may also want to look at the -P option of luseradd which takes a plaintext password, but I don't know if luseradd is a standard command (it may be part of SE Linux or perhaps just an oddity of Fedora).
Sometimes it is useful to set a password which nobody knows. This seems to work:
tr -dc A-Za-z0-9 < /dev/urandom | head -c44 | passwd --stdin $user
echo 'yourPassword' | sudo -S yourCommand
if -S doesnt work try with -kS
You can use the expect utility to drive all programs that read from a tty (as opposed to stdin, which is what passwd does). Expect comes with ready to run examples for all sorts of interactive problems, like passwd entry.

Resources