pass special characters from input to bash script - bash

I've a bash script that simple has to add new user and sign a password that is passed when script is called:
./adduser_script username password
and the password is then used as a parameter in the script like this:
/usr/sbin/useradd ... -p `openssl passwd -1 "$2"` ...
the problem occurs of course when password contains special characters like $#, $* itd. So when i call the script:
/adduser_script username aa$#bbb
and after script ends password looks like: aabbb (so the special charakters are removed from original password). The question is how can I correctly pass the original password with special charakters to the script?
Thanks in advance,
Regards

have you tried strong qoutes ??
use 'aa$#bb' instead of weak qoutes i.e. "aa$#bb"
for example: check with echo command
echo "aa$#bb" will print aabb
while
echo 'aa$#bb' will print aa$#bb
In your script use
/usr/sbin/useradd ... -p `openssl passwd -1 '$2'` ...
now you need not to worry about qoutes while passing password as argument.

The problem is probably not in your script at all, but rather on how you call it. At least from the snippets you provide, it doesn't seem like the password field is being evaluated.
So, when you call the script, if an argument contains something like $a, bash will replace it with the value of the variable a, or an empty string if it is unset.
If $ needs to be in the password, then it needs to be in single quotes.
./adduser_script username 'password$#aaa'

You can also use double quotes with escape .
For example: set password "MyComplexP\#\$\$word"

I have been facing similar issue and here is my in take on it.
One thing we need to consider is the user of single and double quotes in our script file.
if we put a value, variable in single quote the value will be as it is and it will not be replaced with actual value we reference.
example,
name='java'
echo '$name' ---> prints $name to console , but
echo "$name" ---> prints java to console.
So, play around on which quote is best to used based on your circumstance.

/usr/sbin/useradd ... -p "$(openssl passwd -1 '$2')"

Related

Travis CI: How do I escape my password for `travis encrypt` to work?

The travis encryption docs mention that I have to bash-escape my password before encrypting it:
Note on escaping certain symbols
When you use travis encrypt to encrypt sensitive data, it is important to note that it will be processed as a bash statement. This means that secret you are encrypting should not cause errors when bash parses it. Having incomplete data will cause bash to dump the error statement to the log, which contains portions of your sensitive data.
Thus, you need to escape special characters such as braces, parentheses, backslashes, and pipe symbols. For example, when you want to assign the string 6&a(5!1Ab\ to FOO, you need to execute:
travis encrypt "FOO=6\\&a\\(5\\!1Ab\\\\"
The answer for bash seems to revolve around printf "%q", but it's still too complicated to figure out how to wire printf with the travis cli.
What's the bash one-liner for having travis encrypt do what it's supposed to do?
I mean, I want to paste my variable name and its value and be sure that it will be encrypted properly, without having to worry about bash escaping. We can stay with the example above, assuming I want to assign the string 6&a(5!1Ab\ to the variable FOO.
And while we're at it, what would be the corresponding one-liner for travis env set? That would help those fighting with the data too large error.
Based on Armali's answer:
read -r && travis encrypt "$(printf %q "$REPLY")"
then paste your variable and its value:
FOO=6&a(5!1Ab\
What's the bash one-liner for having travis encrypt do what it's supposed to do?
I mean, I want to paste my variable name and its value and be sure that it will be encrypted properly, without having to worry about bash escaping.
This is generally impossible, because if the value is to be inserted into the command line, some kind of quoting is indispensable, hence at least one quoting character (if allowed to occur in the value) must be handled specially and cannot simply be pasted in; that's the reason why enclosing the value in ' ' doesn't work if ' occurs in the value (and surely ' shall be allowed in a password). Thus, the requirement of being able to paste the value can only be met if the requirement of a one-liner is dropped and the value is supplied as input.
Then, since the travis encrypt command needs extra quoting of the argument (perhaps because, as chepner thinks, eval is invoked), we can provide this quoting with printf %q, e. g.
read -r
6&a(5!1Ab\
travis encrypt "$(printf %q "FOO=$REPLY")"
(the bold line to be pasted).
How about putting VAR=VALUE in the read statement to put everything together? read -r, paste FOO=6&a(5!1Ab\ and finally travis encrypt "$(printf %q "$REPLY")".
Of course that would also work.
Furthermore, is there really no way to pipe the result of read -r to travis encrypt?
If you mean literally to pipe, then no, there's no way to pipe something to a command that only expects arguments. But if you just wonder if we can concatenate the commands, then yes, we can as well write e. g.
read -r; travis encrypt "$(printf %q "$REPLY")"
FOO=6&a(5!1Ab\
it is so much easier to just base64 the string and decode it in your build. saves all the faffing about with the escaping in travis.
Travis, and any other CI/CD service, allows for secret environment variables to be stored secretly to be used for every build. In setting for your projects build, add environment variable option. To echo the the password in cli to use for base64 youd still need to escape the string as it needs to be evaluated by bash. create a temp file with your password. Then:
cat /tmp/secret | base64 -w 0
copy the string and paste it in the value field for travis ci env var setting
in .travis.yaml file to retrieve the secret simply:
echo $SECRET | base64 -d
There may be edge cases, though, but this should solve most use cases.

Bash- passing input without shell interpreting parameter expansion chars

So I have a script where I type the script.sh followed by input for a set of if-else statements. Like this:
script.sh fnSw38h$?2
The output echoes out the input in the end.
But I noticed that $? is interpreted as 0/1 so the output would echo:
fnSw38h12
How can I stop the shell from expanding the characters and take it face value?
I looked at something like opt noglob or something similar but they didn't work.
When I put it like this:
script.sh 'fnSw38h$?2'
it works. But how do I capture that within single quotes ('') when I can't state variables inside it like Var='$1'
Please help!
How to pass a password to a script
I gather from the comments that the true purpose of this script is to validate a password. If this is an important or sensitive application, you really should be using professional security tools. If this application is not sensitive or this is just a learning exercise, then read on for a first introduction to the issues.
First, do not do this:
script.sh fnSw38h$?2
This password will appear in ps and be visible to any user on the system in plain text.
Instead, have the user type the password as input to the script, such as:
#!/bin/sh
IFS= read -r var
Here, read will gather input from the keyboard free from shell interference and it will not appear in ps output.
var will have the password for you to verify but you really shouldn't have plain text passwords saved anywhere for you to verify against. It is much better to put the password through a one-way hash and then compare the hash with something that you have saved in a file. For example:
var=$(head -n1 | md5sum)
Here, head will read one line (the password) and pass it to md5sum which will convert it to a hash. This hash can be compared with the known correct hash for this user's password. The text returned by head will be exactly what the user typed, unmangled by the shell.
Actually, for a known hash algorithm, it is possible to make a reverse look-up table for common passwords. So, the solution is to create a variable, called salt, that has some user dependent information:
var=$( { head -n1; echo "$salt"; } | md5sum)
The salt does not have to be kept secret. It is just there to make look-up tables more difficult to compute.
The md5sum algorithm, however, has been found to have some weaknesses. So, it should be replaced with more recent hash algorithms. As I write, that would probably be a sha-2 variant.
Again, if this is a sensitive application, do not use home-made tools
Answer to original question
how do I capture that within single quotes ('') when I can't state variables inside it like Var='$1'
The answer is that you don't need to. Consider, for example, this script:
#!/bin/sh
var=$1
echo $var
First, note that $$ and $? are both shell variables:
$ echo $$ $?
28712 0
Now, let's try our script:
$ bash ./script.sh '$$ $?'
$$ $?
These variables were not expanded because (1) when they appeared on the command line, they were in single-quotes, and (2) in the script, they were assigned to variables and bash does not expand variables recursively. In other words, on the line echo $var, bash will expand $var to get $$ $? but there it stops. It does not expand what was in var.
You can escape any dollar signs in a double-quoted string that are not meant to introduce a parameter expansion.
var=foo
# Pass the literal string fnSw38h$?2foo to script.sh
script.sh "fnSw38h\$?2$var"
You cannot do what you are trying to do. What is entered on the command line (such as the arguments to your script) must be in shell syntax, and will be interpreted by the shell (according to the shell's rules) before being handed to your script.
When someone runs the command script.sh fnSw38h$?2, the shell parses the argument as the text "fnSw38h", followed by $? which means "substitute the exit status of the last command here", followed by "2". So the shell does as it's been told, it substitutes the exit status of the last command, then hands the result of that to your script.
Your script never receives "fnSw38h$?2", and cannot recover the argument in that form. It receives something like "fnSw38h02" or "fnSw38h12", because that's what the user asked the shell to pass it. That might not be what the user wanted to pass it, but as I said, the command must be in shell syntax, and in shell syntax an unescaped and unnquoted $? means "substitute the last exit status here".
If the user wants to pass "$?" as part of the argument, they must escape or single-quote it on the command line. Period.

how to handle filename having double dollar

I am just unable to search this in question list. Please help me here to find if someone already asked.
I am doing ssh on various m/c with filename like "/a/b/file$$$$".
ssh $host "/a/b/file$$$$"
is now been replaced with
ssh $host "/a/b/file54645464"
above is the proc id of the bash scrpt which i am running.
so the issue is that later queries couldn't find this
any pointer will be of great help.
Try
ssh $host '/a/b/file$$$$'
Quoting with ' instead of " prevents variable substitution.
From the manpage (section QUOTING):
Enclosing characters in single quotes preserves the literal value of each character within the quotes.
The variable substituted in your case is $:
$
Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the current shell, not the subshell.
Since a $ sign is (also) used to access the value of the $ variable every pair ($$) is substituted with the process ID and that is why you end up with twice the process id.
use single quotes to have $ not expanded:
$> echo '$ok'
$ok

Simple solution for handling special characters in shell script input

I have a script which can overwrite values in a configuration file using options, for example, option --password can overwrite the setting in the configuration file (please note, this is not a discussion about security). However a password can contain contain characters, that are by bash, recognized as special characters, and those characters needs to be escaped or placed within " ".
Now I understand this. Although I can't say whom will be using this script in the future, so I would like to save he or she the trouble of having an incorrect password simply because, he or she forgot to place the password within " " or escape the special character.
What is the best way of dealing with such an input?
Thanks in advance.
Hm.. Double quotes are not enough. Must use single quotes, because the rare situation, for example
mycommand --password "AAA$PWD" #is worng for any exported environment varname
mycommand --password 'AAA$PWD' #ok
Here is no way avoid this, because your users using a sort of shell, what have variable expansions and metachar-globbing. Your command getting already expanded args, so here is no way catch this in your script.
The only way - as #Bohemian told above - reading the password from a tty. You can write a simple wrapper around your current script, what will read the password from a tty and after will execute your script with properly escaped --pasword argument.
There is a simple, but not very intuitive solution.
Encapsulate the character that is causing problem with '"CARACTER"'. And put all the password string between single quotes.
In my case the character that was causing the problem was the ' (single quote).
So if I have a command like that:
mycommand --password 'AAA'PWD'
Replace by that:
mycommand --password 'AAA'"'"'PWD'
Now my password is a concatenation of three strings. Explanation:
'AAA' + "'" + 'PWD' # plus sign is just to make clear the contatenation.
That's works.

Perl substitution using string that contains a dollar sign on windows

I'm using perl on windows and am trying to do a one liner using perl to substitute a placeholder in a file using a windows variable that contains a dollar sign. Does anyone know what the correct usage is to make it work with the dollar sign. I've tried various ways and can't seem to get it to work.
For example, I have a properties file that has a token in it (!MYPASSWORD!) that I'm trying to replace like:
somevalue="!MYPASSWORD!"
I have a batch file that looks up a variable say called NEWPASSWORD that contains the password $abc12345$ and I want to use perl substitution to replace the value like the following. Note I may not always know where the $ signs are so I cant escape them. For example another password may be abc$124$563:
echo %NEWPASSWORD% <-- this would contain $abc12345$
perl -p -i.bak -e "s/!MYPASSWORD!/%NEWPASSWORD%/g" a.properties
When its done I want a.properties to be :
somevalue="$abc12345$"
Thanks in advance
Use ' as regexp delimeter symbol. It will disable all variable substitution:
perl -p -i.bak -e "s'!MYPASSWORD!'%NEWPASSWORD%'g" a.properties
I presume you are getting password from user input. why not just do that in Perl without having to go through batch since you are already using Perl? Its easier. you can then use modules like Term::Inkey to mask password and stuff.
simply use escape before dollar, like that :
\$

Resources