#!/bin/sh
#
# OpenVAS
# $Id: $
# Description: Synchronize with CERT data feed.
#
# Authors:
# Timo Pollmeier <timo.pollmeier@greenbone.net>
#
# Copyright:
# Copyright (C) 2013 Greenbone Networks GmbH
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2,
# or, at your option, any later version 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.

# configure CERT_DIR where we will sync CERT data
if [ -z "$CERT_DIR" ]; then
  OPENVASSD=`which openvassd`
  if [ -z "$OPENVASSD" ] ; then
    echo "[e] Error: openvassd is not in the path, could not determine CERT directory."
    exit 1
  else
    # get the parent directory of the plugins
    CERT_DIR=`openvassd -s | awk -F" = " '/^plugins_folder/ { print $2 }' | sed -s 's/\(^.*\)\/plugins/\1/'`
    # suffix it with "cert-data" which is our target (destination) directory
    CERT_DIR="$CERT_DIR/cert-data"
  fi
fi
CERT_DB="$CERT_DIR/cert.db"

# private directory
if [ -z "$PRIVATE_SUBDIR" ]
then
  PRIVATE_SUBDIR="private"
fi

# delete options for rsync
RSYNC_DELETE="--delete --exclude \"/cert.db\" --exclude \"$PRIVATE_SUBDIR/\""

# Script and feed information which will be made available to user through
# command line options and automated tools.
SCRIPT_NAME="openvas-certdata-sync"
VERSION=4.0.2
CERT_RES_DIR=/usr/share/openvas/cert
RESTRICTED=0

TIMESTAMP="$CERT_DIR/timestamp"

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

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

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

if [ -z "$OV_CERT_RSYNC_FEED" ]; then 
  OV_CERT_RSYNC_FEED=rsync://feed.openvas.org:/cert-data
  # An alternative syntax which might work if the above doesn't:
  # OV_RSYNC_FEED=rsync@feed.openvas.org::cert-data
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-cert-data-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 CERT advisory data"
  echo "OpenVAS administrator functions:"
  echo " --refresh      refresh database without downloading feed data"
  echo " --selftest     perform self-test"
  echo " --identify     display information"
  echo " --version      display version"
  echo " --describe     display current CERT feed info"
  echo " --feedversion   display current CERT feed version"
  echo ""
  echo "Environment variables:"
  echo "CERT_DIR             where to place CERT advisories"
  echo "OV_CERT_RSYNC_FEED   URL of rsync feed"
  echo "TMPDIR               temporary directory used to download the files"
  echo "PRIVATE_SUBDIR       subdirectory to exclude from deletion by rsync"
  echo ""
  exit 0
}



CMD_RSYNC=`which rsync`
CMD_SQLITE=`which sqlite3`
SQLITE="sqlite3 -noheader"
TMP_CERT="$SYNC_TMP_DIR/openvas-feed-`date +%F`-$$.tar.bz2"

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

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

  if [ -z "$CMD_SQLITE" ]; then
    echo "[e] Error: sqlite3 not found (required)";
    exit 1
  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"
}

do_rsync () {
  if [ -z "$CMD_RSYNC" ]; then
    echo "[w] rsync not found!"
  else
    echo "[i] Using rsync: $CMD_RSYNC"
    echo "[i] Configured CERT data rsync feed: $OV_CERT_RSYNC_FEED"
    mkdir -p "$CERT_DIR"
    eval "$CMD_RSYNC -ltvrP $RSYNC_DELETE \"$OV_CERT_RSYNC_FEED\" \"$CERT_DIR\""
    if [ $? -ne 0 ] ; then
      echo "Error: rsync failed. Your CERT data might be broken now."
      exit 1
    fi
  fi
}

do_self_test () {
  chk_system_tools
}

do_describe () {
  echo "This script synchronizes a CERT advisory 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 [ -r $TIMESTAMP ] ; then
      echo `cat $TIMESTAMP`
  fi
}

show_intro () {
  echo "[i] This script synchronizes a CERT advisory directory with the OpenVAS one."
  echo "[i] CERT dir: $CERT_DIR"
}

do_sync () {
  if [ -z "$CMD_RSYNC" ] ; then
    echo "[w] rsync not found!"
    echo -n "[e] no utility available in PATH environment variable to download CERT data"
    exit 1
  else
    echo "[i] Will use rsync"
    do_rsync
  fi
}

reinit () {
  echo "[i] Major change in internal CERT data structures."
  echo "[i] Reinitialization of database necessary."
  echo "[i] This update might take a while.."
  $SQLITE $CERT_DB < $CERT_RES_DIR/cert_db_init.sql
}

check_db_version () {
  DB_VERSION=`$SQLITE $CERT_DB "select value from meta where name = 'database_version';" 2>/dev/null || echo 0`
  case "$DB_VERSION" in
    0) reinit;;
    1) reinit;;
    2) reinit;;
  esac
}

update_sec_db () {
  if [ ! -f $CERT_DB ]
  then
    echo "[i] Initializing CERT advisory database"
    $SQLITE $CERT_DB < $CERT_RES_DIR/cert_db_init.sql
    DB_LASTUPDATE=0
  else
    check_db_version
    DFN_REFDATE=`$SQLITE $CERT_DB "SELECT date(max(modification_time),'unixepoch') from dfn_cert_advs;" | tr -d "-"`
  fi

  if [ -z "$DFN_REFDATE" ]
  then
    DFN_REFDATE=0
  fi

  DB_LASTUPDATE=`$SQLITE $CERT_DB "SELECT value FROM meta WHERE name = 'last_update';"`

  if [ -z "$DB_LASTUPDATE" ]
  then
    # Happens when initial sync was aborted
    echo "Error: Inconsistent data. Resetting CERT database."
    rm -f $CERT_DB
    $SQLITE $CERT_DB < $CERT_RES_DIR/cert_db_init.sql
    DFN_REFDATE=0
    DB_LASTUPDATE=0
  fi

  xmlcount=$(ls $CERT_DIR/dfn-cert-*.xml 2> /dev/null | wc -l)
  if [ $xmlcount -ne 0 ]
  then
    for certfile in `ls $CERT_DIR/dfn-cert-*.xml`
    do
      if [ `stat -c "%Y" $certfile | cut -d " " -f 1 | tr -d "-"` -ge $DB_LASTUPDATE ]
      then
        echo "[i] Updating $certfile"
        xsltproc --stringparam refdate "$DFN_REFDATE" $CERT_RES_DIR/dfn_cert_update.xsl $certfile | $SQLITE $CERT_DB
      else
        echo "[i] Skipping $certfile, file is older than last revision"
      fi
    done
  else
    echo "[w] No DFN-CERT advisories found in $CERT_DIR"
  fi

  $SQLITE $CERT_DB "UPDATE meta SET value ='`date +%s`' WHERE name = 'last_update';"

}

do_refresh () {
  echo "[i] Refreshing database without feed sync."
  update_sec_db
}

if [ -n "$1" ]; then
  while test $# -gt 0; do
    case "$1" in
      --help)
        do_help
        exit 0
        ;;
      --refresh)
        do_refresh
        exit 0
        ;;
      --check)
        exit 0
        ;;
      --version)
        echo $VERSION
        exit 0
        ;;
      --identify)
        echo "CERTSYNC|$SCRIPT_NAME|$VERSION|$FEED_NAME|$RESTRICTED|CERTSYNC"
        exit 0
        ;;
      --selftest)
        SELFTEST_FAIL=0
        do_self_test
        exit $SELFTEST_FAIL
        ;;
      --describe)
        do_describe
        exit 0
        ;;
      --feedversion)
        do_feedversion
        exit 0
        ;;
    esac
    shift
  done
fi

show_intro
do_sync
update_sec_db

exit 0
