3

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"?

10
  • 2
    Either parse the file or use something like gconf or any external program to handle the config file Mar 16, 2013 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, 2013 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, 2013 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, 2013 at 11:04
  • 1
    @Joe of course there is, you can just start using read or cat and iterate afterwards. Mar 17, 2013 at 11:09

1 Answer 1

4

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"')"'
  fi
done

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

9
  • 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, 2013 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, 2013 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. Mar 17, 2013 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, 2013 at 20:08
  • @RanyAlbegWein Quite obviously shell variable names cannot have white space in it. May 4, 2013 at 12:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .