#!/bin/sh

po_domain="alterator-osec"
conf_dir="/etc/osec"
dirs_conf="$conf_dir/dirs.conf"
pipe_conf="$conf_dir/alterator-pipe.conf"
logrotate="$conf_dir/logrotate"

start_page=1
last_page=1
max_lines=20
new_request=1

# Import functions.
. alterator-sh-functions

# Add additional functions.
write_string_param()
{
  printf '%s "%s"\n' "$1" "$(write_string "$2")"
}

write_bool_param()
{
  printf '%s %s\n' "$1" "$(write_bool "$2")"
}

# Reads the report configuration file.
read_pipe_conf()
{
  while read ln; do
    eval "$ln"
  done < "$pipe_conf"

SEND_PIPE="${SEND_PIPE:-/bin/mail -s "[osec] Integrity test" root}"
OSEC_LOG_NAME="${OSEC_LOG_NAME:-/var/log/osec.log}"
OSEC_LOGROTATE="${OSEC_LOGROTATE:-/etc/osec/logrotate}"
LOGROTATE_CMD="${LOGROTATE_CMD:-/usr/sbin/logrotate -f \"$OSEC_LOGROTATE\"}"
LOG_PIPE="${LOG_PIPE:-/usr/bin/tee \"$OSEC_LOG_NAME\"}"
REPORT_PIPE="${REPORT_PIPE:-/usr/bin/osec_reporter}"
}

# Tests the 'send mail' option.
test_send_mail()
{
  [ -z "${SEND_PIPE##/bin/mail*}" ]
}

# Tests the 'write log' option.
test_write_log()
{
  [ -z "${LOG_PIPE##/usr/bin/tee*}" ]
}

# Writes the report configuration file.
write_pipe_conf()
{
    # Process 'send mail' flag.
    if test_bool "$1"; then
      cat <<EOF > "$pipe_conf"
SEND_PIPE='/bin/mail -s "[osec] Integrity test" root'
EOF
    else
      cat <<EOF > "$pipe_conf"
SEND_PIPE='/bin/cat > /dev/null'
EOF
    fi

    # Process 'write log' flag.
    if test_bool "$2"; then
  cat <<EOF >> "$pipe_conf"
OSEC_LOG_NAME='/var/log/osec.log'
OSEC_LOGROTATE='/etc/osec/logrotate'
LOGROTATE_CMD="/usr/sbin/logrotate -f \"$OSEC_LOGROTATE\""
LOG_PIPE="/usr/bin/tee \"$OSEC_LOG_NAME\""
EOF
    else
  cat <<EOF >> "$pipe_conf"
OSEC_LOG_NAME=
OSEC_LOGROTATE=
LOGROTATE_CMD=
LOG_PIPE='/bin/cat'
EOF
    fi

    # Write constant part.
    cat <<EOF >> "$pipe_conf"
REPORT_PIPE='/usr/bin/osec_reporter'
EOF
}

# Reads the number of rotations of the log file.
read_rotate()
{
  awk '/rotate/ { print $2 }' < "$logrotate"
}

# Update logrotate configuration file
update_logrotate()
{
  sed -i "s/rotate .*\b/rotate $1/g" "$logrotate"
}

# Finds cron job.
find_periodic()
{
  [ -f /etc/cron.daily/alterator-osec ] && echo 'daily' && return;
  [ -f /etc/cron.weekly/alterator-osec ] && echo 'weekly' && return;
  [ -f /etc/cron.monthly/alterator-osec ] && echo 'monthly' && return;
  echo 'off'
}

# Sets the periodic job.
set_periodic()
{
  rm -f /etc/cron.*/alterator-osec
  case "$1" in
    daily)
      ln -s /etc/osec/run-osec /etc/cron.daily/alterator-osec
      ;;
    weekly)
      ln -s /etc/osec/run-osec /etc/cron.weekly/alterator-osec
      ;;
    monthly)
      ln -s /etc/osec/run-osec /etc/cron.monthly/alterator-osec
      ;;
  esac
}

# Tests if the 'osec' process is running.
test_running()
{
  [ -f /var/run/osec ]
}

# Starts osec process.
run_osec()
{
  /etc/osec/run-osec &
}

# Kill osec process.
kill_osec()
{
  kill `cat /var/run/osec`
  sleep 2
  if test_running; then
    kill -9 `cat /var/run/osec`
    rm -f /var/run/osec
    rm -f /var/lock/osec
  fi
}

# Prepare configuration.
[ -d "$conf_dir" ] || mkdir "$conf_dir"
[ -f "$dirs_conf" ] || touch "$dirs_conf"
[ -f "$pipe_conf" ] || write_pipe_conf "#t"

# Prints the path description.
# (Descriptions is taken from the Wikipedia article on FHS)
# TODO: external list based dictionary.
describe_dir()
{
  case "$1" in
    /)
      echo "`_ 'Primary hierarchy root (entire file system)'`"
      ;;
    /bin)
      echo "`_ 'Essential command binaries'`"
      ;;
    /boot)
      echo "`_ 'Boot loader files'`"
      ;;
    /dev)
      echo "`_ 'Essential devices'`"
      ;;
    /etc)
      echo "`_ 'Host-specific system-wide configuration files'`"
      ;;
    /home)
      echo `_ "Non-root users' home directories"`
      ;;
    /lib)
      echo "`_ 'Essential libraries'`"
      ;;
    /lib64)
      echo "`_ 'Essential 64-bit libraries'`"
      ;;
    /lost+found)
      echo "`_ 'Recovered and broken files'`"
      ;;
    /media)
      echo "`_ 'Mount points for removable media'`"
      ;;
    /mnt)
      echo "`_ 'Temporarily mounted filesystems'`"
      ;;
    /opt)
      echo "`_ 'Optional application software packages'`"
      ;;
    /proc)
      echo "`_ 'Virtual filesystem documenting kernel and processes'`"
      ;;
    /root)
      echo "`_ 'Home directory for the root user'`"
      ;;
    /sbin)
      echo "`_ 'Essential system binaries'`"
      ;;
    /srv)
      echo "`_ 'Site-specific data which is served by the system'`"
      ;;
    /sys)
      echo "`_ 'Virtual filesystem documenting kernel and devices'`"
      ;;
    /tmp)
      echo "`_ 'Temporary files'`"
      ;;
    /usr)
      echo "`_ 'Secondary hierarchy (user data)'`"
      ;;
    /usr/bin)
      echo "`_ 'Non-essential command binaries'`"
      ;;
    /usr/include)
      echo "`_ 'Standard include files'`"
      ;;
    /usr/lib)
      echo "`_ 'Non-essential libraries'`"
      ;;
    /usr/lib64)
      echo "`_ 'Non-essential 64-bit libraries'`"
      ;;
    /usr/sbin)
      echo "`_ 'Non-essential system binaries'`"
      ;;
    /usr/share)
      echo "`_ 'Architecture-independent (shared) data'`"
      ;;
    /usr/src)
      echo "`_ 'Source code'`"
      ;;
    /usr/X11R6/bin)
      echo "`_ 'X Window System binaries'`"
      ;;
    /usr/X11R6/lib)
      echo "`_ 'X Window System libraties'`"
      ;;
    /usr/games)
      echo "`_ 'Gaming binaries'`"
      ;;
    /usr/libexec)
      echo "`_ 'Private executable services'`"
      ;;
    /usr/local)
      echo "`_ 'Tertiary hierarchy (local data)'`"
      ;;
    /var)
      echo "`_ 'Variable files (logs, spool and e-mail files)'`"
      ;;
    /var/lib)
      echo "`_ 'Persistent memory files (databases)'`"
      ;;
    /var/lock)
      echo "`_ 'Lock files (resource tracking)'`"
      ;;
    /var/log)
      echo "`_ 'Log files'`"
      ;;
    /var/mail)
      echo `_ "Users' mailboxes"`
      ;;
    /var/run)
      echo "`_ 'Information about the running system'`"
      ;;
    /var/spool)
      echo "`_ 'Spool for tasks waiting to be processed'`"
      ;;
    /var/tmp)
      echo "`_ 'Temporary files to be preserved between reboots'`"
      ;;
    *)
      echo "`_ 'User specified path'`"
      ;;
  esac
}

# File metric calculations.
examine_file()
{
  local fn="$1"
  if [ -f "$fn" ]; then
    local lc=`wc -l "$fn" | awk '{ print $1 }'`
    last_page="$(( lc / max_lines ))"
    [ "$(( last_page * max_lines ))" -eq "$lc" ] || \
      last_page="$(( last_page + 1 ))"
    return 0
  else
    return 1
  fi
}

# Remove watched path
path_rm() {
  local IFS=';'
  local del_path=
  for del_path in $in_list_path; do
    # Delete path item.
    echo "[osec-delete] Delete path item: $del_path" 1>&2
    local del_pat='$1 != "'"$del_path"'"'
    local TMPFILE=`mktemp`
    awk -F "#" "$del_pat" < "$dirs_conf" > "$TMPFILE"
    mv "$TMPFILE" "$dirs_conf"
    rm -f "$TMPFILE"
  done
}

# Print log
show_log() {
  local log="$in_log_name"
  local num=

  if ! [ -f "$log" ] > /dev/null 2>&1; then
    printf "(num \"1\" log_line \"Can't find $log\")\n" >&3
    return
  fi

  num=1
  cat "$log" | while read line; do
    printf "(num \"%d\" log_line \"%s\")\n" $num "$line" >&3
    num=$((num + 1))
  done
}

on_message()
{
  echo "[osec-msg] Objects: $in__objects" 1>&2

  case "$in_action" in

    constraints)

      # CONSTRAINTS

      echo '('
      printf 'period (label "%s")' "`_ 'Periodic execution:'`"
      printf 'write_log (label "%s")' "`_ 'Write log'`"
      printf 'rotate (label "%s" match ("^[0123456789]*$" "%s"))' \
        "`_ 'Number of log rotations:'`" "`_ 'Integer value required'`"
      printf 'send_mail (label "%s")' "`_ 'Mail report'`"
      printf 'fstate (label "%s")' "`_ 'State'`"
      printf 'fname (label "%s")' "`_ 'File name'`"
      printf 'fdate (label "%s")' "`_ 'Date'`"
      printf 'ftime (label "%s")' "`_ 'Time'`"
      printf 'fsize (label "%s")' "`_ 'Size'`"
      printf 'path (label "%s")' "`_ 'Path:'`"
      printf 'path_h (label "%s")' "`_ 'Path'`"
      printf 'desc (label "%s")' "`_ 'Description'`"
      echo ')'
      ;;

    list)

      # LIST

      echo '('
      case "$in__objects" in
        list_periods)
          # Period option list.
          printf "(\"%s\" label \"%s\")\n" 'daily' "`_ 'Daily'`"
          printf "(\"%s\" label \"%s\")\n" 'weekly' "`_ 'Weekly'`"
          printf "(\"%s\" label \"%s\")\n" 'monthly' "`_ 'Monthly'`"
          printf "(\"%s\" label \"%s\")\n" 'off' "`_ 'Off'`"
          ;;
        list_path)
          # Directory list.
          cat "$dirs_conf" |
          awk -F "#" '{ if ($1 != "") { print $1 " " $2 } }' |
          (a=1; while read dir desc; do
          [ -n "$desc" ] || desc=`describe_dir $dir`
          printf "(\"%s\" desc \"%s\")\n" "$dir" "$desc"
          a=0
          done; exit $a) # || write_nop
          ;;
        list_logs)
          # Log file list
          read_pipe_conf
          /bin/ls -lt --time-style='+%d-%m-%Y %H:%M:%S' ${OSEC_LOG_NAME}* |
          awk '
          { print "(\"" $8 "\" mdate \"" $6 "\" mtime \"" $7 "\" fsize \"" \
            $5 "\" fstate \"updated\")" }'
          ;;

        log)
          # Log file contents
          [ -n "$in_log_name" ] && show_log
          ;;
        *)
          #write_nop
          ;;
      esac
      echo ')'
      ;;

    read)

      # READ

      read_pipe_conf
      echo '('
      case "$in__objects" in
        *)
          # Running flag
          if test_running; then
            write_bool_param "bool_running" true
            write_string_param "label_status" "`_ 'Running'`"
            echo "[osec-read] OSEC running" 1>&2
          else
            write_bool_param "bool_running" false
            write_string_param "label_status" "`_ 'Not running'`"
            echo "[osec-read] OSEC not running" 1>&2
          fi

          # Write logs
          if test_write_log; then
            write_bool_param "bool_write_log" true
            echo "[osec-read] Write log: true" 1>&2
          else
            write_bool_param "bool_write_log" false
            echo "[osec-read] Write log: false" 1>&2
          fi

          # Mail report
          if test_send_mail; then
            write_bool_param "bool_send_mail" true
            echo "[osec-read] Send mail: true" 1>&2
          else
            write_bool_param "bool_send_mail" false
            echo "[osec-read] Send mail: false" 1>&2
          fi

          # Periodic execution
          write_string_param "list_periods" `find_periodic`
          echo "[osec-read] Periodic execution: $(find_periodic)" 1>&2

          # Number of log rotations
          write_string_param "input_rotate" `read_rotate`
          echo "[osec-read] Number of log rotations: $(read_rotate)" 1>&2
          ;;
      esac
      echo ')'
      ;;

    delete)

      # DELETE

      case "$in__objects" in
        path)
          # Delete path
          [ -n "$in_list_path" ] && path_rm
          write_nop
          ;;
        *)
          ;;
      esac
      ;;

    write)

      # WRITE

      case "$in__objects" in
        logs/*)
          local fn="/${in__objects##logs/}"
          max_lines="${in_max_lines:-66}"
          examine_file "$fn"
          if [ -n "$in_next_page" ]; then
            start_page="$(( start_page + 1 ))"
          fi
          if [ -n "$in_prev_page" ]; then
            start_page="$(( start_page - 1 ))"
          fi
          if [ -n "$in_last_page" ]; then
            start_page=last_page
          fi
          if [ -n "$in_jump_page" ]; then
            start_page="$in_start_page"
          fi
          if [ -n "$in_first_page" ]; then
            start_page=1
          fi        
          [ "$start_page" -le "$last_page" ] || \
            start_page="$last_page"
          [ "$start_page" -ge 1 ] || \
            start_page=1
          write_nop
          new_request=0
          ;;
        *)
          local error=1
          if [ -n "$in_new" ]; then
            # Add new directory item.
            if [ -n "$in_new_path" ]; then
              echo "[osec-write] Add new directory item: $in_new_path" 1>&2
              local TMPFILE=`mktemp`
              ls -1d $in_new_path |
              sort -u "$dirs_conf" - | cat > "$TMPFILE"
              mv "$TMPFILE" "$dirs_conf"
              rm -f "$TMPFILE"
            else
              write_error "`_ 'Parameter "Path" should not be empty.'`" -1
              error=0
            fi
          fi

          # Save configuration options
          if [ -n "$in_set" ]; then
            # Adjust the periodic execution.
            [ -n "$in_list_periods" ] && set_periodic "$in_list_periods"
            # Adjust report configuration.
            read_pipe_conf
            write_pipe_conf "$in_bool_send_mail" "$in_bool_write_log"
            # Update logrotate configuration.
            update_logrotate "$in_input_rotate"
            echo "[osec-write] Set periodic execution to: $in_list_periods" 1>&2
            echo "[osec-write] Set write log to: $in_bool_write_log" 1>&2
            echo "[osec-write] Set mail report to: $in_bool_send_mail" 1>&2
            echo "[osec-write] Set number of log rotations: $in_input_rotate" 1>&2
          fi

          # Kill the 'osec' process.
          if [ -n "$in_kill" ]; then
            echo "[osec-write] Ask to kill the osec process" 1>&2
            kill_osec
          fi

          # Run the 'osec' process.
          if [ -n "$in_run" ]; then
            echo "[osec-write] Ask to run the osec process" 1>&2
            run_osec
          fi

          # Check for error
          [ $error -ne 0 ] && write_nop
          ;;
      esac
      ;;
    *)

      # ALL OTHER MESSAGES

      # XXX echo "[osec] Unsupported action: $in_action" 1>&2
      echo '#f'
      ;;
  esac
}

message_loop

# vim: autoindent tabstop=2 shiftwidth=2 expandtab softtabstop=2 filetype=sh
