#!/bin/sh
#
# OpenVAS
# $Id$
# Description: Synchronize with NVT feed.
# This shell script synchronizes the local set of
# OpenVAS Network Vulerability Tests (NVTs) and
# associated includefiles with a given upstream
# feed of updated or new files.
#
# Authors:
# Vlatko Kosturjak <kost@linux.hr>
# Michael Wiegand <michael.wiegand@greenbone.net>
# Timo Pollmeier <timo.pollmeier@greenbone.net>
#
# Script is complete rewrite of original sync script by
# Lukas Grunwald <l.grunwald@dn-systems.de>
# Jan-Oliver Wagner <jan-oliver.wagner@intevation.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2,
# as published by the Free Software Foundation
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.

# if you need to debug script
# set -x

# old IFS
IFS0="$IFS"
# newline workaround because $'\n' doesnt work sometimes
NEWLINE='
'
#

# configure NVT_DIR where we will sync NVTs
if [ -z "$NVT_DIR" ]; then
  OPENVASSD=`which openvassd`
  if [ -z "$OPENVASSD" ] ; then
    echo "[e] Error: openvassd is not in the path, could not determine NVT directory."
    exit 1
  else
    NVT_DIR=`openvassd -s | awk -F" = " '/^plugins_folder/ { print $2 }'`
  fi
fi
# private subdirectory
if [ -z "$PRIVATE_SUBDIR" ]
then
  PRIVATE_SUBDIR="private"
fi
# OpenVAS Integrity Test key ID, used by do_migrate_to_private, check_signature
OPENVAS_KEY_ID=48479FF648DB4530
# delete option for rsync
RSYNC_DELETE="--delete --exclude \"$PRIVATE_SUBDIR/\""

# Script and feed information which will be made available to user through
# command line options and automated tools.
SCRIPT_NAME="openvas-nvt-sync"
VERSION=3.4.0
RESTRICTED=0

INFOFILE="$NVT_DIR/plugin_feed_info.inc"
if [ -r $INFOFILE ] ; then
  FEED_VERSION=`grep PLUGIN_SET $INFOFILE | sed -e 's/[^0-9]//g'`
  FEED_NAME=`grep PLUGIN_FEED $INFOFILE | sed 's/PLUGIN_FEED\s*\=\s*\"\([^"]\+\)\";/\1/'`
  FEED_VENDOR=`grep FEED_VENDOR $INFOFILE | sed 's/FEED_VENDOR\s*\=\s*\"\([^"]\+\)\";/\1/'`
  FEED_HOME=`grep FEED_HOME $INFOFILE | sed 's/FEED_HOME\s*\=\s*\"\([^"]\+\)\";/\1/'`
  FEED_PRESENT=1
else
  FEED_PRESENT=0
fi

if [ -z "$FEED_NAME" ] ; then
  FEED_NAME="OpenVAS NVT Feed"
fi

if [ -z "$FEED_VENDOR" ] ; then
  FEED_VENDOR="The OpenVAS Project"
fi

if [ -z "$FEED_HOME" ] ; then
  FEED_HOME="http://www.openvas.org/openvas-nvt-feed.html"
fi

# The URL of the plugin feed
if [ -z "$OV_RSYNC_FEED" ]; then
  OV_RSYNC_FEED=rsync://feed.openvas.org:/nvt-feed
  # An alternative syntax which might work if the above doesn't:
  # OV_RSYNC_FEED=rsync@feed.openvas.org::nvt-feed
fi

if [ -z "$OV_HTTP_FEED" ]; then
  OV_HTTP_FEED=http://www.openvas.org/openvas-nvt-feed-current.tar.bz2
fi

if [ -z "$TMPDIR" ]; then
  SYNC_TMP_DIR=/tmp
  # If we have mktemp, create a temporary dir (safer)
  if [ -n "`which mktemp`" ]; then
    SYNC_TMP_DIR=`mktemp -t -d openvas-nvt-sync.XXXXXXXXXX` || { echo "ERROR: Cannot create temporary directory for file download" >&2; exit 1 ; }
    trap "rm -rf $SYNC_TMP_DIR" EXIT HUP INT TRAP TERM
  fi
else
  SYNC_TMP_DIR="$TMPDIR"
fi

do_help () {
  echo "$0: Sync NVTs using different protocols"
  echo " --rsync	sync with rsync (default)"
  echo " --wget		sync with wget"
  echo " --curl		sync with curl"
  echo " --check	just checksum check"
  echo "OpenVAS administrator functions:"
  echo " --selftest	 perform self-test"
  echo " --identify	 display information"
  echo " --version	 display version"
  echo " --describe	 display current feed info"
  echo " --feedversion	 display current feed version info"
  echo " --nvt-dir <dir> set directory of the NVT collection for this run"
  echo " --migrate-to-private	migrate unsigned files to private directory"
  echo ""
  echo "Environment variables:"
  echo "NVT_DIR		where to extract plugins (absolute path)"
  echo "PRIVATE_SUBDIR	subdirectory of \$NVT_DIR to migrate unsigned files to"
  echo "OV_RSYNC_FEED	URL of rsync feed"
  echo "OV_HTTP_FEED	URL of http feed"
  echo "TMPDIR		temporary directory used to download the files"
  echo "Note that you can use standard ones as well (e.g. http_proxy) for wget/curl"
  echo ""
  exit 0
}

CMD_RSYNC=`which rsync`
CMD_MD5SUM=`which md5sum`
CMD_WGET=`which wget`
CMD_CURL=`which curl`
CMD_GPG=`which gpg` # May become obsolete when migrate-to-private is removed
TMP_NVT="$SYNC_TMP_DIR/openvas-feed-`date +%F`-$$.tar.bz2"

chk_system_tools () {
  echo "[i] Searching for required system tools (look for warnings)..."

  if [ -z "$CMD_MD5SUM" ]; then
    SELFTEST_FAIL=1
    echo "[w] Warning: MD5SUM not found";
  fi

  if [ -z "$CMD_RSYNC" ]; then
    echo "[w] Warning: RSYNC not found";
  fi

  if [ -z "$CMD_WGET" ]; then
    echo "[w] Warning: wget not found";
  fi

  if [ -z "$CMD_CURL" ]; then
    echo "[w] Warning: curl not found";
  fi

  # May become obsolete if migrate-to-private is removed
  if [ -z "$CMD_GPG" ]; then
    echo "[w] Warning: gpg not found";
  fi

  if [ -z "$CMD_RSYNC" -a -z "$CMD_WGET" -a -z "$CMD_CURL" ]; then
    SELFTEST_FAIL=1
  fi

  echo "[i] If you did not get any warnings, that means you have all tools required"

  echo "[i] Note that it is recommended to have md5sum and one of the following: rsync, wget or curl."
}

do_rsync () {
  if [ -z "$CMD_RSYNC" ]; then
    echo "[w] rsync not found!"
  else
    echo "[i] Using rsync: $CMD_RSYNC"
    echo "[i] Configured NVT rsync feed: $OV_RSYNC_FEED"
    mkdir -p "$NVT_DIR"

    # migration will become obsolete when OpenVAS 5 is retired
    if [ ! -d "$NVT_DIR/$PRIVATE_SUBDIR" ]
    then
      tty -s
      if [ 0 -eq "$?" ]
      then
        # interactive
        echo "[w] Private directory '$NVT_DIR/$PRIVATE_SUBDIR' not found."
        echo "[w] Non-feed NVTs not migrated there will be deleted by rsync."
        read -p "Run migration now ([y/n], any other input aborts)? " REPLY
        echo ""
        if [ "Y" = "$REPLY" ] || [ "y" = "$REPLY" ]
        then
          do_migrate_to_private
        elif [ "N" = "$REPLY" ] || [ "n" = "$REPLY" ]
        then
          echo "[i] Skipping migration."
        else
          echo "[i] NVT sync aborted by user."
          exit 0
        fi
      else
        # non-interactive
        echo "[e] Private directory '$PRIVATE_SUBDIR' not found. Aborting to prevent loss of user NVTs."
        echo "[i] Please run this script on an interactive console or migrate NVTs by running this script with option --migrate-to-private."
        exit 1
      fi
    fi
    # end migration prompt

    eval "$CMD_RSYNC -ltvrP $RSYNC_DELETE \"$OV_RSYNC_FEED\" \"$NVT_DIR\""
    if [ $? -ne 0 ] ; then
      echo "Error: rsync failed. Your NVT collection might be broken now."
      exit 1
    fi
  fi
}

do_wget () {
  if [ -z "$CMD_WGET" ]; then
    echo "[w] GNU wget not found!"
  else
    echo "[i] Using GNU wget: $CMD_WGET"
    echo "[i] Configured NVT http feed: $OV_HTTP_FEED"
    echo "[i] Downloading to: $TMP_NVT"
    mkdir -p "$NVT_DIR" \
    && wget "$OV_HTTP_FEED" -O $TMP_NVT \
    && cd "$NVT_DIR" \
    && tar xvjf $TMP_NVT \
    && rm -f $TMP_NVT \
    && echo "[i] Download complete"
  fi
}

do_curl () {
  if [ -z "$CMD_CURL" ]; then
    echo "[w] curl not found!"
  else
    echo "[i] Using curl: $CMD_CURL"
    echo "[i] Configured NVT http feed: $OV_HTTP_FEED"
    echo "[i] Downloading to: $TMP_NVT"
    mkdir -p "$NVT_DIR" \
    && curl "$OV_HTTP_FEED" -o $TMP_NVT \
    && cd "$NVT_DIR" \
    && tar xvjf $TMP_NVT \
    && rm -f $TMP_NVT \
    && echo "[i] Download complete"
  fi
}

do_check_md5 () {
  if [ -z "CMD_MD5SUM" ]; then
    echo "[w] md5sum utility not found, cannot check NVT checksums! You've been warned!"
  else
    echo -n "[i] Checking dir: "
    eval "cd \"$NVT_DIR\""
    if [ $? -ne 0 ] ; then
      echo "not ok"
      echo "Check your NVT dir for existence and permissions!"
      exit 1
    else
      echo "ok"
    fi
    echo -n "[i] Checking MD5 checksum: "
    eval "cd \"$NVT_DIR\" ; $CMD_MD5SUM -c --status \"$NVT_DIR/md5sums\""
    if [ $? -ne 0 ] ; then
      echo "not ok"
      echo "Error: md5sums not correct. Your NVT collection might be broken now."
      echo "Please try this for details: cd \"$NVT_DIR\" ; $CMD_MD5SUM -c \"$NVT_DIR/md5sums\" | less"
      exit 1
    fi
    echo "ok"
  fi
}

do_self_test () {
  chk_system_tools
}

do_describe () {
  echo "This script synchronizes an NVT collection with the '$FEED_NAME'."
  echo "The '$FEED_NAME' is provided by '$FEED_VENDOR'."
  echo "Online information about this feed: '$FEED_HOME'."

}

do_feedversion () {
  if [ $FEED_PRESENT -eq 1 ] ; then
    echo $FEED_VERSION
  fi
}

show_intro () {
  echo "[i] This script synchronizes an NVT collection with the '$FEED_NAME'."
  echo "[i] The '$FEED_NAME' is provided by '$FEED_VENDOR'."
  echo "[i] Online information about this feed: '$FEED_HOME'."
  echo "[i] NVT dir: $NVT_DIR"

}

do_sync () {
  if [ -z "$CMD_RSYNC" ] || [ $FEED_PRESENT -eq 0 ] ; then
    if [ $FEED_PRESENT -eq 0 ] ; then
      echo "[i] rsync is not recommended for the initial sync. Falling back on http."
    else
      echo "[w] rsync not found!"
    fi
    if [ -z "$CMD_WGET" ]; then
      echo "[w] GNU wget not found!"
      if [ -z "$CMD_CURL" ]; then
        echo "[w] curl not found!"
        echo -n "[e] no utility available in PATH environment variable to download plugins"
        exit 1
      else
        echo "[i] Will use curl"
        do_curl
      fi
    else
      echo "[i] Will use wget"
      do_wget
    fi
  else
    echo "[i] Will use rsync"
    do_rsync
  fi
}

# Will become obsolete when OpenVAS 5 is retired
do_migrate_to_private () {
  echo "[i] Migrating non-OpenVAS files to private sub-directory '$PRIVATE_SUBDIR' of NVT directory '$NVT_DIR'. This can take a few minutes."

  if [ -z "$CMD_GPG" ]
  then
    echo "[e] Error: gpg not found"
    exit 1
  fi

  if [ ! -d "$NVT_DIR/$PRIVATE_SUBDIR" ]
  then
    mkdir -p "$NVT_DIR/$PRIVATE_SUBDIR"
    if [ $? -ne 0 ]
    then
      echo "[e] Error: Could not create private directory."
      exit 1
    fi
  fi

  IFS="$NEWLINE"
  for fname in `find "$NVT_DIR/" -path "$NVT_DIR/$PRIVATE_SUBDIR/*" -prune \
                                 -o \( \
                                      -type f \
                                      -not \( \
                                              -name "*.asc" \
                                              -o -path "$NVT_DIR/md5sums" \
                                              -o -path "$NVT_DIR/COPYING*" \
                                          \) \
                                      -print \
                                    \)`
  do
    check_signature "$fname"
  done

  for fname in `find "$NVT_DIR/" -path "$NVT_DIR/$PRIVATE_SUBDIR/*" -prune \
                                 -o \( \
                                      -type f \
                                      -name "*.asc" \
                                      -print \
                                    \)`
  do
    if [ -f "$fname" -a ! -f "${fname%.asc}" ]
    then
      check_signature "$fname"
    fi
  done
  IFS="$IFS0"

  echo "[i] Migration done."
}

# Will become obsolete when OpenVAS 5 is retired
check_signature () {
  openvas_sig_okay=0
  fname="$1"
  if [ -f "${fname}.asc" ]
  then
    for key_id in `gpg --homedir /dev/null --batch --status-fd 1 < ${fname}.asc 2>/dev/null | awk '$1 == "[GNUPG:]" && $2 == "NO_PUBKEY" { print $3 }'`
    do
      if [ "$key_id" = "$OPENVAS_KEY_ID" ]
      then
        openvas_sig_okay=1
      fi
    done
  fi
  if [ $openvas_sig_okay -ne 1 -a -f "$fname" ]
  then
    move_to="$NVT_DIR/$PRIVATE_SUBDIR${fname#$NVT_DIR}"
    move_file "$fname" "$move_to"
    if [ -f "${fname}.asc" ]
    then
      move_file "${fname}.asc" "${move_to}.asc"
    fi
  fi
}

# Will become obsolete when OpenVAS 5 is retired
move_file () {
  src="$1"
  dst="$2"
  if [ -e "$dst" ]
  then
    echo "[w] Warning: '$dst' already exists."
    index=1;
    while [ -e "${2}~$index" ]
    do
      index=`expr $index + 1`
    done
    dst="$2~$index~"
    echo "[i] renaming and moving '${src##$NVT_DIR}' to '${dst##$NVT_DIR}'"
  else
    echo "[i] moving '${src##$NVT_DIR}' to '${dst##$NVT_DIR}'"
  fi

  dstdir="${dst%/*}"
  if [ ! -d "$dstdir" ]
  then
    mkdir -p "$dstdir"
    if [ $? -ne 0 ]
    then
      echo "[e] Error: Could not create directory '$dstdir'"
      exit 1
    fi
  fi

  mv "$src" "$dst"
  if [ $? -ne 0 ]
  then
    echo "[e] Error: Could not move '$src' file to '$dst'"
    exit 1
  fi
}

if [ -n "$1" ]; then
  while test $# -gt 0; do
    case "$1" in
      # migrate-to-private will become obsolete when OpenVAS 5 is retired
      --migrate-to-private)
        do_migrate_to_private
        exit 0
        ;;
      --help)
        do_help
        exit 0
        ;;
      --rsync)
        do_rsync
        do_check_md5
        exit 0
        ;;
      --wget)
        do_wget
        do_check_md5
        exit 0
        ;;
      --curl)
        do_curl
        do_check_md5
        exit 0
        ;;
      --check)
        do_check_md5
        exit 0
        ;;
      --version)
        echo $VERSION
        exit 0
        ;;
      --identify)
        echo "NVTSYNC|$SCRIPT_NAME|$VERSION|$FEED_NAME|$RESTRICTED|NVTSYNC"
        exit 0
        ;;
      --selftest)
        SELFTEST_FAIL=0
        do_self_test
        exit $SELFTEST_FAIL
        ;;
      --describe)
        do_describe
        exit 0
        ;;
      --feedversion)
        do_feedversion
        exit 0
        ;;
      --nvt-dir)
        NVT_DIR="$2"
        shift
        ;;
    esac
    shift
  done
fi

show_intro
do_sync
do_check_md5

exit 0
