How do you write a bash script for example, a user logins to the server’s shell, then I want a bash script that will prompt him a password to verify he is a legit user not an intruder. The answer of the password will be located in a file (for example: /etc/verify). If the user not able to type the correct password 3 times the server will kill that connection and bans his IP address from the server.
First off, I have to say that while I am a big fan of shell scripts as the universal solution to almost any problem, I am a bit leery about using it as a security screen rather than coding something in C or similar.
But what you ask about can certainly be done. The key is to know that you can turn off input echo with the stty command, leading to a simple script snippet to prompt for a password:
echo -n "Password: "
stty -echo
read password
stty echo
echo "" # force a carriage return to be output
echo You entered $password
I’ve left blank lines so you can see the three line sequence that lets the password not be shown as the user types it in.
With this script in your toolkit, you then need to grab the correct password from the /etc/verify file:
correct="$(cat /etc/verify)"
and then compare the two:
if [ $password = $correct ] ; then ...
If it fails, increment a counter:
failed=$(( $failed + 1 ))
Put those pieces together and you’ll have everything except the action that should happen when they fail three times in a row. To log someone out, you can simply kill their login shell, which can be quickly identified by finding the parent process ID of the script itself, which is typically the third field in a ps -l output.
To block their IP, I assume you’d need to automatically append the IP address to some sort of firewall, but since there are a number of different firewalls, you’re on your own with that last one.
Hope that’s helpful. I’ll leave putting all these building blocks together as an “exercise for the reader”. 🙂
OK, Dave, I’ve taken your bait! My curiosity got the best of me. Here’s what I know so far about this:
—————
Problem #1
In the line, “word=$word$char”, you have an assignment command in which you’re creating and assigning a value to a variable called “word.” Trouble is, this variable is never referenced anywhere in your script. My guess is that it WAS referenced at one time, but that, after a few iterations of trial-and-error, the reference(s) to it got edited out, but the assignment command never got removed.
Of course, in the current form of the script, this doesn’t hurt anything, so it’s really a non-issue but it certainly did confuse me for awhile!
I’m thinking you probably wanted to output the value of the “word” variable with an echo command somewhere later in the script, probably inside the “while” loop, but that this code got axed during your trial and error.
Fix: Either leave the line (with the assignment command) alone (acceptable), comment out the line (better), or delete the line completely (best).
Problem #2
Try as I might, I just couldn’t seem to get the “test” command (which is a synonym for the [] square brackets), in your command line, “if [ “$char” = “\n” -o “$char” = “\r” ]” to work correctly. I believe that I’ve finally narrowed it down: The cause seems not to be in the “test” command itself, but rather, in the way the “read” command handles Return (and Newline) characters.
For debugging purposes, I simplified the “test” command by shortening the command to one test (instead of two tests connected by “-o”). Then, after much subsequent trial and error, I finally concluded that the “read” command will read and input a Return character without any problems, BUT, FOR SOME REASON, THE RETURN CHARACTER NEVER GETS STORED IN THE VARIABLE (which is named “char” in this instance). I’m not quite sure whether this is happening in Unix, in the I/O input driver for the keyboard, in the “read” command, in the variable assignment within the “read” command, or somewhere else along the way.
In other words, with the “read” command set to input only one character, when the user then types the Return key, the “read” command completes properly and control properly passes to the next command, as it should do. But, after control passes to the next command, the value of the “char” variable is left set to the null string (no data in the variable, in other words), instead of being set to the Return code as we’d like, and the value of the “word” variable in the assignment command therefore is left unchanged (because we’re concatenating the null string onto the end of the existing contents of the word variable). Of course, since a Return character (also a Newline character; I tested both) never gets stored in the “char” variable, it means that the two tests in your “test” command will always fail, and the “while” loop therefore never terminates. (I may be at risk here, of wearing out my Control and C keys 😉
Fix: Change the line with the “test” command (square brackets) such that it tests for the null string in “char” instead of trying to match “char” with a constant value like a Return (\r) or Newline (\n) character. My version looks like this after the change:
if [ -z “$char” ]
The line above says, “If the variable named “char” is null (empty), then execute the “then” part of the “if” command. Otherwise, skip the “then” part.
The modified “if” command above seems to work for me. But, ymmv, depending on which shell you’re using, which version of that shell you’re using, and what underlying operating system you’re using.
Problem #3
This isn’t really a problem at this stage of the trial-and-error game. But, the “read” command in your code is set to echo the input characters as each one is entered. I found that, at least on my system, the “-s” option does indeed work–it keeps “read” from echoing the characters the user types.
For some of my tests, I found it helpful to have the “read” command echo what was typed, to give me a better idea of what was happening. For other tests I did, I found that it was more helpful to have it NOT echo.
Fix: Add the “-s” option to the “read” command.
Problem #4
This isn’t a problem in your code as given above. But, it was for me during my testing and trial-and-error, because during my testing, I used extra “echo” commands extensively to try and see what was happening.
This has to do with how to suppress the final Return code that the “echo” command normally adds to the end of its output. This is mostly a difference in the “echo” command between various shells. The “echo” command version in some shells uses the “-n” option to suppress the final Return. Other shells use “\c” output as part of the string to suppress the Return. Possibly, there might be some shells out there that would work correctly with either construction, but I don’t know.
During my testing, I solved this Return-suppression problem by using two different code lines, one for each of the two versions of the “echo” command, then commenting out one of them or the other. I also added the customary “#!/bin/someshellname” line at the beginning of the script file to force the issue–that is, to ensure that the shell I wanted to use to run my script was the shell that actually gets run.
So, I have this line as the first line:
#!/bin/bash
and these two lines later in the script:
#echo $char”\c”
echo -n $char
That made it easy to switch back and forth between “bash” and “sh.”
I’ve only tested with the sh and bash shells. On my system, the “\c” construction works properly with sh but “-n” doesn’t work properly. With bash, it’s the other way around: “-n” definitely works; “\c” definitely does not.
Fix: If you’re using the “echo” command, when you need to suppress the final Return code, use either the “-n” or the “\c” construction, as appropriate for your shell, to suppress the output of the extra Return code. Optionally, also add the line #!/bin/someshellname to the beginning of your script for good measure.
—————
I called my script “onechar” and saved it directly to my OSX Desktop as an OSX TextEdit plain text (not “rich text”) file without the “.txt” extension, then cd’d to Desktop and made it executable using the Unix “chmod” command.
—————
Complete Test Script
Here’s my complete test script (so far!), with all the above changes incorporated into it:
—————
#!/bin/bash
# Change the line above from …bash to …sh (or to whatever shell you want to use)
# onechar
# Unix shell script to test the inputting of one-character-at-a-time in shell scripts.
#21Jan2012
#B.B.
word=””
while read -s -n 1 char
do
word=$word$char
# Use only one of the 2 lines below; comment out the unused line.
#Use the \c line below for the Bourne shell (sh); use the -n line below for the bash shell (bash):
#echo $char”\c”
echo -n $char
if [ -z “$char” ]
then
echo “” ; echo “carriage return” ;echo $word
exit 0
fi
done
—————
Sample Output:
Entering “abc” (without the quotes) followed by Return (4 characters total) produces this output:
$ ./onechar
abc
carriage return
abc
$
Entering a Return by itself (1 character) produces this result:
$ ./onechar
carriage return
$
—————
So, this is a start, at least!
BTW, I have nothing against solving this with a C program, the possibility of which was mentioned earlier in this discussion. But, often, it’s just nice to be able to use the same programming language (shell script here) throughout a given project or problem solution.
-B.
I think you’re right, Bruce. I tried something like this:
while read -n 1 char
do
word=$word$char
if [ “$char” = “\n” -o “$char” = “\r” ]
then
echo “” ; echo “carriage return” ; echo “”
exit 0
fi
done
But it’s not quite right and didn’t work. Still, it could be a starting point for someone who wanted to fiddle more and make it work. hint hint. 🙂
Dave,
In regard to the poster above who wanted to provide feedback to the user by echoing an asterisk for each password character typed in–
Couldn’t you just read in one character at a time instead of an entire line? Then, the script could check for the return (newline) character for each character entered, and if it’s not a return character, output an asterisk character (instead of echoing the typed-in character).
The man page for my version of bash (bash version 3.2 running on Mac OS X version 10.6.8) shows, in the section on the “read” command, an option argument to the “read” command called “-n” that controls the number of characters that the read command reads before returning.
From the man page:
-n nchars
read returns after reading nchars characters rather than waiting for a complete line of input.
So, let’s say that we set it to read only one character at a time, with “-n 1”. Then, it’s up to us to check each incoming character first to see if it’s a return character, and if not, accumulate it into a shell variable until a return character comes in. After seeing a return character, the script would then proceed as before, comparing the contents of the variable to the passwords to see whether there’s a match or not.
Setting up a simple loop with the command:
read -s -n 1 [possibly other arguments]
at the heart of the (innermost) loop should do what’s needed.
The “-s” option puts the read command into “Silent” mode, meaning that it doesn’t echo each character that the user types in. (Thanks to another poster above, who tipped us off to this “-s” option.)
I’m going to take the standard cop-out here and leave the coding details as an exercise for the reader, but the rest of the coding should be very straightforward shell programming, which most folks should be able to do after only a little shell scripting experience.
No C programming needed (at least for the *nixes for which the above options are valid)–do it all in shell!
However, I’m unsure of how portable this would be, for shells other than bash and for operating systems other than OSX, though–because of the “-n” and “-s” options. And, I don’t have any way to easily check.
Dave, please keep up the great work with this site!
-B.
is it possible to keep the shell prompt before login prompt at the time of connecting to telnet? i.e Shell prompt have to check all the commands before the login prompt.
I’ve seen this done, Robert, but I’m not sure how to do it. I know that it’s all about getting the right “stty” settings, if you want to start by digging into that man page…
This is great, but is it possible to allow it to provide feedback to the user entering the password by echoing ‘*’ characters as they type? I have looked and do not see a mechanism in a script. I just thought I would ask before I write a ‘c’ program to get the password echoing a dummy character as they type.
Any suggestions would be appreciated.
Hi,
I want to develope a (java) GUI application.But it requires user to be either root or in sudoers list. Problem is : how to check and verify username / password from gui.
Thanks.
I have a short script and I’d like to run on the cron job.
Either skill password or enter password in the script.
scp -r /opt/IBM/WebSphere/html/DataSource username@10.10.01.102/opt/IBM/WebSphere/html/
You can turn the echoing off by giving -s option to the read command
# read -s password
Hi Dave,
Thanks for that tip, very handy.
Get a question and also a possibly useful addition…
Q: You mention using /etc/verify, is this a standard *NIX/Linux thing or is it just by way of example here?
The reason I ask is that I work almost entirely on OS X, where it doesn’t seem to exist (I assume because of NetInfo/OpenLDAP or because it just don’t), is there some kind of equivalent on OS X if it is not just an example?
Addittion:
I noticed that somebody else using a similar technique to you pointed out that if stty was not configured to echo before the script it would be after using this method. They suggested using the following construct: –
oldmodes=`stty -g`
stty -echo
read password
stty $oldmodes
Cheers
Dan
hello
I want to say the password in bash
for example if I know a pc’s root password I want to write a bash script to ssh that pc and say the password and enter that pc
would you please email it to me
thanks a lot