Assertion failed

Apr 30, 2015

Getopt from bad to worse

Note: This is about getopt in shells e.g. bash, not languages like C

Getopt used to be the way to parse arguments in shellscripts, but it has some issues. Most notably it’s very hard to handle whitespaces in arguments correctly. Let’s say you have the flag -f which takes some value e.g. -f myval. If this value contains a whitespace, e.g. -f 'my val', we have a problem. Getting this parsed correctly in the shell is really hard and I can’t remember ever having seen it done in a way without notable issues. Because of this modern shells contain a function called getopts, which solves most of the problems getopt has.

If all this has already been solved why are we here? Well, after searching online for how to parse long options with getopts which it usualy doesn’t support natively, I came across alot of posts where they used getopt instead since at least the GNU version has support for long options. In an effort I belive is to get around the whitespace issue they will send the output from getopt through eval.

Let’s look at some examples. Usually you will see something similar to the following when getopt is used.

opts=$(getopt $optstring "$@")
set -- $opts

But in a lot of the code out there it’s done like this instead.

opts=$(getopt $optstring "$@")
eval set -- "$opts"

With the way the shell works this doesn’t solve any problems with whitespaces, instead it introduces new challenges, which will often let users execute commands in the context of the script.

As an example let’s use the following script as an example.

#!/bin/sh

opts=$(getopt 'f:' "$@")
eval set -- "$opts"

f_arg=

while true ; do
  case "$1" in
    -f)
      f_arg="$2"
      shift 2
      ;;
    --)
      shift
      break
      ;;
    *)
      echo "Something unexpected: $1" >&1
      shift
      ;;
  esac
done

echo "$f_arg"

Now let’s see how it behaves.

% ./getopt.sh

% ./getopt.sh -f
getopt: option requires an argument -- 'f'

% ./getopt.sh -f test
test
% ./getopt.sh -f 'test1 test2'
Something unexpected: test2
test1

At first glance this doesn’t look right, but this is expected behavior, even when quoting $opts and using eval. This is not the most interesting part and you hopefully knew this already, so let’s continue and try some more creative arguments.

% ./getopt.sh -f 'test1 -- ;echo test2 #'
test2
test1

What’s happening here we are able to specify commands to be executed in the context of the script as commandline arguments. In most cases it would be harmless, but I would argue it’s still not a good thing. The bad big issue arises when the script is used by unprivieleged users through for example sudo to interact with some piece of software. E.g. you might have some servers on which some users need to interact with a piece of software. They might need to read out information from the software, and to do this they need to be a special user. To get around this you allow them to run these commands/script through sudo, in which case this issue could have a noteworthy security impact by allowing the user to run arbitrary commands instead of just a few pre-defined ones.

Let’s see this example in action where we have to run the script as a specific user. /usr/local/bin/getopt.sh is the same script as before, but modified to abort as early as possible if running as the wrong user.

% whoami
marius
% which getopt.sh
/usr/local/bin/getopt.sh
% getopt.sh -f test
Not the correct user!
% sudo -l
User marius may run the followig commands on testhost:
    (svcuser) /usr/local/bin/getopt.sh
% sudo -u svcuser getopt.sh -f test
test
% sudo -u svcuser getopt.sh -f 'test -- ;whoami #'
svcuser
test
% sudo -u svcuser getopt.sh -f 'test -- ;sh ;exit #'
$ whoami
svcuser
$ exit

With the last example where we started a shell the original program will stop executing after the shell we spawn exits thanks to the exit in the arguments.

In the end I would recomend using getopts instead of getopt, as it solves the whitespace problem and it’s built into most shells today, and if you for some reason have to use getopt you should not send its output through eval.

posted at 16:00  ·   ·  sh  bash  getopt  linux  bsd

Apr 30, 2015

XMPP-federation with google

I have my own XMPP-server running ejabberd with federation enabled. But when trying to add a contact using gmail to the roster, it failed and I got the following message in the log.

2015-04-30 13:34:35.086 [info] <0.2063.0>@ejabberd_s2s_out:wait_for_validation:450 Closing s2s connection: lden.org -> gmail.com (invalid dialback key)

After some searching on the web it turns out google does not support TLS when fedrating. Since I had configured ejabberd to require TLS, even with a self-signed certificate this did not work.

To get around this I had to change s2s_use_starttls from ‘required’ to ‘optional’. The caveat with this is you now allow servers to speak unencrypted between each other. I would prefer if this was not allowed, so I have yet to decide if I leave this as optional or change it back to required and survive without being able to federate with google.

Mastodon