I've been writing a number of bash scripts lately which get variables from a parameter or from a file. Some of the scripts run as root (using sudo)

This is on my notebook, so there aren't any other users to cause security problems in this situation, but ...

I'd like to be able to write a script I can release into the wild.

Probably, what this question does is make a strong case for not writing anything in bash where root/sudo is involved and input/parameters can't be trusted.

Other than locking everything down with sudoers, is there a way to read an arbitrary string into a bash program and parse it without allowing bash to expand it, potentially executing something along the way?

I don't see any solution for defending against a rogue parameter.

For a file, there might be a way, but it's not simple.

The string could be safely input to the script using read, but once it's in one or more variables, I can't see any method of accessing it that wouldn't involve potentially dangerous variable expansion.

I think it would be possible to read from a file using read -n 1 to get one character at a time in a loop into an array. That ought to defang just about anything, but it's a bit cumbersome having to reassemble everything character by character later.

I'm thinking of input like:

'$(rm -rf ./*)' or 'eval shutdown -h now'

or similar given as input which would run when a variable containing it was referenced. If the script was running as root, this could be a real problem!

Any ideas on how to address this other than, "Get a real programming language"?

  • 2
    Either parse the file or use something like gconf or any external program to handle the config file – Ulrich Dangel Mar 16 '13 at 0:03
  • Rewrite the script into a safer language... Python is probably a good bet (it is used for lots of system-y stuff in Fedora, for example). Also take a look at the Secure Programming for Unix and Linux HOWTO, consider carefully what is said about inputs and outputs. – vonbrand Mar 16 '13 at 1:17
  • @UlrichDangel - aside from reading the file character by character as I suggested in my question, how do I parse something potentially dangerous in bash? I guess I'd have to pipe the file right into sed or awk or a similar program - other than bash. In bash, there doesn't seem to be a way to treat a string in a variable as just a string, not subject to further expansion. – Joe Mar 17 '13 at 10:54
  • @UlrichDangel gconf is interesting. It's sort of what I was looking for, but way too powerful. It also looks like it may have too many things in it which, if broken, would take the rest of the system with them. – Joe Mar 17 '13 at 11:04
  • 1
    @Joe of course there is, you can just start using read or cat and iterate afterwards. – Ulrich Dangel Mar 17 '13 at 11:09

It's difficult to answer that without knowing what kind of information has to be provided in that file. That the script is stored on a system with several users is not automatically a security problem. Why should they (be able to) provide input for other users?

However, you can safely read a line from a file by read -r line. And if you just need data of the kind foo=bar then you can easily check the line for invalid characters (or structures) by [[ =~ ]].

If you are not limited to using a single file then an easy solution can be taken from DJB's software: one file per variable. The variables can then be defined by something like:

for varname in ${vars[@]}; do
  if [ -f "${path_to_var_files}"/$varname ]; then
    eval $varname='"$(cat '"${path_to_var_files}"/$varname"')"'

...if trailing newlines are not important.

A lot of problems (for the system, not for the respective user though) can be solved by minimizing the code running under sudo. Don't let a sudo script source the file. Let a user level script source the file and make the sudo calls. sudo scripts should have a clear interface with easy to be checked input.

| improve this answer | |
  • I must be missing something. If I put my sample input into your code, it looks like it would be executed inside the $() before it even gets into var. Your idea of doing the parameter reading code before the sudo code would limit any damage to one user which is a lot better. I'll have to look at my scripts and see if that approach will fit. – Joe Mar 17 '13 at 11:27
  • The application I'm currently working on is a personal backup system based on rsync. So far, it needs a small number of global parameters - like where things the scripts need are kept and a number of parameters which define a specific backup task like source, destination, whether the source should be unmounted before proceeding ... all parameter=value pairs, except a few which start out as small arrays. – Joe Mar 17 '13 at 11:31
  • @Joe The input is not executed within $(), it is the output within $(). $() is replaced by the output (which is safely created by cat) and becomes the parameter value. – Hauke Laging Mar 17 '13 at 13:45
  • I think you're right. I need to go back and do some more testing to see exactly what works and what doesn't. – Joe Mar 18 '13 at 20:08
  • @RanyAlbegWein Quite obviously shell variable names cannot have white space in it. – Hauke Laging May 4 '13 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.