[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [libvirt] [PATCH 2/4] libvirt-guest.init: quoting variables

On 03/09/2011 01:54 AM, Philipp Hahn wrote:
> At least protect the $uri variable against further expansion by properly
> quoting it. While doing that, also quote all other variables to protect
> against shell meta characters.

Hmm.  Valid URIs can contain '?', which _is_ a glob character, so I
agree that we need to quote "$uri" everywhere that we have already
correctly obtatined it in order to avoid inadvertent globbing.

Meanwhile, $URIS is indeed a user-settable variable, via
/etc/sysconfig/libvirt-guests, so we should want to protect ourselves
from malicious input.  (On the other hand, we already blindly source
/etc/sysconfig/libvirt-guests without validating that it contains _only_
shell assignments, so a malicious user could already put arbitrary shell
code in there.  The real way to be safe would be to parse out _just_ the
assignments we care about, using sed or read, and eval those assignments
rather than sourcing the entire file.  I guess we have to draw a line
somewhere at how much paranoia we want to express.  If we can trust that
/etc/sysconfig/libvirt-guests can't be subverted by a malicious user,
then we can trust that it won't contain a malicious $URIS and instead
chalk up bugs in that file as system administration shortcomings.)

However, that points out a shortcoming in your patch - we already do
constructs like

for uri in $URIS; do

which _already_ corrupted any globbing and performed IFS splitting, at
which point $uri would not contain any further globbing (except in the
weird case of a glob that expanded to an existing file name which
happens to also be a valid glob), so quoting just "$uri" as in your
patch is a lost cause unless we address the problem from the point of
the user input to begin with.

Meanwhile, we already know that a valid URI does not contain whitespace
(a valid URI can represent whitespace via encoding, where needed), so
it's already acceptable to use IFS splitting on $URIS to break it into
individual URIs, it's just that we need to suppress globbing in the
process.  'set -f' can be used for this, if needed.

If we could use bash shell arrays, it would be a matter of creating an
array variable ${URI[]} that contains the individual URIs, doing the
work of safely splitting things only once.  But libvirt-guests has to
run under /bin/sh == dash for Debian, which lacks shell arrays.

I'm still thinking about how to handle this one...

Meanwhile, I'm not a fan of blindly quoting everything; there are
documented cases where you can trigger shell bugs by doing too much
quoting.  For example:

foo=`some_command "with quotes"`

is portable, but

foo="`some_command "with quotes"`"

is not.  So I prefer to quote variables that come from external source,
but not internal variables that are obviously safe (that said, quoting
generally doesn't hurt, so even if something is overkill does not meant
that I am rejecting the patch).

> +++ b/tools/libvirt-guests.init.sh
> @@ -66,12 +66,10 @@ run_virsh() {
>      shift
>      if [ "x$uri" = xdefault ]; then
> -        conn=
> +        virsh "$@" </dev/null
>      else
> -        conn="-c $uri"
> +        virsh -c "$uri" "$@" </dev/null
>      fi
> -
> -    virsh $conn "$@" </dev/null

This part's good.

> @@ -89,7 +87,7 @@ list_guests() {
>      uuids=
>      for id in $(echo "$list" | awk 'NR > 2 {print $1}'); do
> -        uuid=$(run_virsh_c $uri dominfo $id | awk '/^UUID:/{print $2}')
> +        uuid=$(run_virsh_c "$uri" dominfo "$id" | awk '/^UUID:/{print $2}')

Quoting "$id" is overkill; we know it's numeric.  But it probably
doesn't hurt.

> -    name=$(run_virsh_c $uri dominfo $uuid 2>/dev/null | \
> +    name=$(run_virsh_c "$uri" dominfo "$uuid" 2>/dev/null | \

Likewise, if we know $uuid is valid, then it contains neither $IFS nor
globbing characters.  Should we instead be tightening up the list_guests
function to guarantee that we never encounter garbage data?

> @@ -146,25 +144,25 @@ start() {
>      while read uri list; do
>          configured=false
>          for confuri in $URIS; do
> -            if [ $confuri = $uri ]; then
> +            if [ "$confuri" = "$uri" ]; then

To be really robust (for example, $confuri could be '(', which is known
to trip up some buggy test(1) implementations), this should be:

[ "x$confuri" = "x$uri" ]

>                  configured=true
>                  break
>              fi
>          done
> -        if ! $configured; then
> +        if ! "$configured"; then

This one's overkill - we explicitly set $configured to either 'true' or
'false'; no external data possible.

>      virsh_pid=$!
>      while true; do
>          sleep 1
> -        kill -0 $virsh_pid >/dev/null 2>&1 || break
> -        progress=$(run_virsh_c $uri domjobinfo $guest 2>/dev/null | \
> +        kill -0 "$virsh_pid" >/dev/null 2>&1 || break
> +        progress=$(run_virsh_c "$uri" domjobinfo "$guest" 2>/dev/null | \

Overkill - we know $virsh_pid is numeric.

>      timeout=$SHUTDOWN_TIMEOUT
> -    while [ $timeout -gt 0 ]; do
> +    while [ "$timeout" -gt 0 ]; do

Reasonable, since SHUTDOWN_TIMEOUT might come from external sources.

>          sleep 1
>          timeout=$((timeout - 1))
> -        guest_is_on $uri $guest || return
> -        $guest_running || break
> -        printf '\r%s%-12d ' "$label" $timeout
> +        guest_is_on "$uri" "$guest" || return
> +        "$guest_running" || break
> +        printf '\r%s%-12d ' "$label" "$timeout"

Overkill, since once you are inside the while loop, you are guaranteed
that $timeout is now numeric.

>      done
> -    if guest_is_on $uri $guest; then
> -        if $guest_running; then
> +    if guest_is_on "$uri" "$guest"; then
> +        if "$guest_running"; then

Overkill, since $guest_running is under our control.

> -        list=$(list_guests $uri)
> +        list=$(list_guests "$uri")
>          if [ $? -eq 0 ]; then
>              empty=true
>              for uuid in $list; do
> -                $empty || printf ", "
> -                printf %s "$(guest_name $uri $uuid)"
> +                "$empty" || printf ", "

Overkill, since $empty is under our control.

>      while read uri list; do
> -        if $suspending; then
> +        if "$suspending"; then


> @@ -330,7 +328,7 @@ case "$1" in
>          usage 0
>          ;;
>      start|stop|gueststatus)
> -        $1
> +        "$1"

Overkill, since we know that $1 is one of three safe values.

I think I'll apply your patch as-is, in spite of the overkill, then
worry about how to safely split $URIS as a separate patch.

Eric Blake   eblake redhat com    +1-801-349-2682
Libvirt virtualization library http://libvirt.org

Attachment: signature.asc
Description: OpenPGP digital signature

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]