#!/bin/sh -efu

# This file is a part of ALT Linux SeLinux policy.
# Copyright (C) 2013 ALT Linux company

# Turn on libshell verbose mode
verbose=1

# Require libshell
. shell-error
. shell-quote
. shell-config
. shell-var
. shell-args

show_help()
{
    cat <<EOF
Usage: $PROG -l <LEVEL>

SeLinux helper. Prepare environment according to security coaccording to security context and run program.

Options:

  -c, --cmd=<COMMAND> Command to run;
  -l, --level=<LEVEL> Multi-Level Security context;
  -h, --help          show this text and exit;

Report bugs to http://bugzilla.altlinux.org/

EOF
    exit
}

# Expect: "c1,c3.c5,c9,c12,c14.c16,c234,c56.c59"
# Only native notation expected. Do not use mctransd.
# Human readable format does not work.
# Use: make_var_name <MLS_SECURITY_CONTEXT>
make_var_name () {
  in="$1" && shift

  # Check for correct invocation. No spaces allowed in secure context.
  if [ -z "${in##* *}" ]; then
    return
  fi

  local ifs_save="$IFS"
  IFS=','
  result_var_name=
  for i in $in; do
    sequence="$(echo "$i" | sed -n -e 's/^c\([[:digit:]]\+\)\.c\([[:digit:]]\+\)$/\1 \2/p')"
    seq_start="${sequence%% *}"
    seq_end="${sequence##* }"
    if [ -n "$seq_start" -a -n "$seq_end" ]; then
      chunk="$(seq -s ',' $seq_start $seq_end | sed -n -e "s/\([[:digit:]]\+\)/c\1/gp")"
      result_var_name="$result_var_name,$chunk"
    else
      result_var_name="$result_var_name,$i"
    fi
  done
  IFS="$ifs_save"
  result_var_name="${result_var_name##,}"
  result_var_name="$(echo "$result_var_name" | tr '[:lower:]' '[:upper:]')"
  result_var_name="$(echo "$result_var_name" | sed -e 's/,/_/'g)"
  echo "$result_var_name"
}

# Check for X presence
check_xorg() {

  local xorg="yes"

  if shell_var_is_yes "$xorg" && [ -z "${DISPLAY+1}" ]; then
    verbose "DISPLAY variable isn't defined."
    xorg="no"
  fi

  if shell_var_is_yes "$xorg" && ! which xauth >/dev/null 2>&1; then
    verbose "Can't find xauth"
    xorg="no"
  fi

  if shell_var_is_yes "$xorg" && ! which xprop >/dev/null 2>&1; then
    verbose "Can't find xprop"
    xorg="no"
  fi

  if shell_var_is_yes "$xorg" && ! xauth -i list "$DISPLAY" >/dev/null 2>&1; then
    verbose "Can't find authorization entries for $DISPLAY"
    xorg="no"
  fi

  verbose "Xorg is present: $xorg"
  echo "$xorg"
}

# Make sure that proccess has D-BUS at same secyrity context.
dbus_xorg () {

  # suffix for variable names
  suffix="$(make_var_name "$MLS")"

  if [ -z "$suffix" ]; then
    verbose "Can't construct D-BUS variable name for \`$MLS' security context."
    return
  fi

  readonly addr_var_name="SELINUX_ALTLINUX_DBUS_ADDR_$suffix"
  readonly pid_var_name="SELINUX_ALTLINUX_DBUS_PID_$suffix"

  verbose "D-BUS address variable name: $addr_var_name"
  verbose "D-BUS PID variable name: $pid_var_name"

  # check for previous D-BUS setup
  readonly addr="$(xprop -root | sed -n -e "/$addr_var_name(STRING) = / s/.\+\"\(.*\)\"/\1/p")"
  readonly pid="$(xprop -root | sed -n -e "/$pid_var_name(STRING) = / s/.\+\"\(.\+\)\"/\1/p")"
  startup_dbus="no"

  if [ -z "$addr" ]; then
    verbose "Try startup new D-BUS. No previous D-BUS address found."
    startup_dbus="yes"
  elif [ -z "$pid" ]; then
    verbose "Try startup new D-BUS. No previous D-BUS PID found."
    startup_dbus="yes"
  else
    # Do additional check
    pid_cmd="$(ps -p $pid -o comm=)"
    if [ "$pid_cmd" != "dbus-daemon" ]; then
      verbose "PID = $pid, but it is not a D-BUS daemon."
      startup_dbus="yes"
    fi
  fi

  if ! shell_var_is_yes "$startup_dbus"; then
    verbose "It seems already exists copy for D-BUS at current level $MLS."
    verbose "$addr_var_name is $addr"
    verbose "$pid_var_name is $pid"
    DBUS_SESSION_BUS_ADDRESS="$addr"
    DBUS_SESSION_BUS_PID="$pid"
    verbose "Export DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"
    export DBUS_SESSION_BUS_ADDRESS
    verbose "Export DBUS_SESSION_BUS_PID=$DBUS_SESSION_BUS_PID"
    export DBUS_SESSION_BUS_PID

    return
  fi

  verbose "It is necessary to start up new D-BUS copy."
  eval "$(dbus-launch --sh-syntax --exit-with-session)"

  if [ -z "$DBUS_SESSION_BUS_ADDRESS" -o -z "$DBUS_SESSION_BUS_PID" ]; then
    verbose "Can't lunch new D-BUS copy"
    return
  fi

  # Leave information for other proccesses at same level
  verbose "Save $addr_var_name as an root-window property"
  xprop -root -format $addr_var_name 8s -set $addr_var_name "$DBUS_SESSION_BUS_ADDRESS"

  verbose "Save $pid_var_name as an root-window property"
  xprop -root -format $pid_var_name 8s -set $pid_var_name "$DBUS_SESSION_BUS_PID"
}

#
# STAR FROM HERE
#

GETOPT_ALLOW_UNKNOWN=1
TEMP=`getopt -n $PROG -o 'l:c:,h' -l 'level:,cmd:,help' -- "$@"` || show_usage
eval set -- "$TEMP"

CMD=
MLS=
while :; do
    case "$1" in
        -c|--cmd) shift
            CMD="$1"
            ;;
        -l|--level) shift
            MLS="$1"
            ;;
        -h|--help) show_help
            ;;
        --) shift; break
            ;;
    esac
    shift
done

[ -n "$MLS" ] || show_usage
[ -n "$CMD" ] || show_usage

CURRENT_CONTEXT="$(runcon | cut -d ':' -f 4-5 | cut -d '-' -f 1)"

if [ -z "$CURRENT_CONTEXT" ]; then
  verbose "Can't get current SeLinux context."
  exit 1
fi

if [ "$CURRENT_CONTEXT" != "$MLS" ]; then
  verbose "Required SeLinux context: $MLS. But have: $CURRENT_CONTEXT"
  exit 1
fi

#
# COMMON PART
#

readonly ME="$(id -u -n)"

readonly MYHOME="$(getent passwd "$ME" | cut -d ':' -f 6)"
[ -n "$MYHOME" ] || fatal "Can't get my home"

# pam_mktemp.so set ENV variables:
# TMPDIR=/tmp/.private/<USERNAME>
# TMP=/tmp/.private/<USERNAME>

# When user uses `newrole' command special helper creates
# /tmp/.private/<USERNAME>/<MLS> directory with necessary MLS rights.

TMPBASE="/tmp/.private/$ME"

TMP="$TMPBASE/$MLS"
export TMP

TMPDIR="$TMP"
export TMPDIR

KEEPOUT="${KEEPOUT:-no}"
readonly XORG_ACTIVE="$(check_xorg)"

if shell_var_is_no "$KEEPOUT" && shell_var_is_yes "$XORG_ACTIVE"; then
  # This file runs in new security context.
  # At certain circumstances don't have right to write to STDOUT/STDERR.
  # For example STDERR/STDOUT is redirected to .xsession-errors file.

  # Code is taken from /etc/X11/Xsession as an example.
  f="$TMP/.xsession-errors"
  [ -z "${DISPLAY##:*}" ] && f="$f$DISPLAY" || f="$f-$DISPLAY"
  verbose "Use $f for xsession errors."

  # Redirect STDOUT/STDERR to Xsession like file.
  if [ ! -e "$f" ]; then
    verbose "Create $f"
    install -m600 /dev/null "$f" 2>/dev/null
  fi

  verbose "Close current STDOUT/STDERR"
  exec 1>&-
  exec 2>&-
  exec 1>>"$f"
  exec 2>>"$f"

  unset f

  echo "Use this file as STDOUT for $$"
  echo "Use this file as STDERR for $$" >&2
fi

verbose "HOME = $MYHOME"
verbose "TMP PREFIX = $TMPBASE"
verbose "TMP = $TMP"
verbose "TMPDIR = $TMPDIR"

# Permanent storage for given security context
RWSTORE="$HOME/docs/$MLS"
verbose "User's permanent storage is located at: $RWSTORE"

# BASH stuff
export HISTFILE="$TMP/.bash_history"
export BASH_ENV="$RWSTORE/.bashrc"
export ENV="$RWSTORE/.bashrc"
# To clear uncomment next lines:
# export HISTFILESIZE=0
# export HISTSIZE=0
# unset HISTSIZE

# Each secure level should have its own copy D-BUS deamon
if [ -n "${DBUS_SESSION_BUS_ADDRESS+1}" ]; then
  verbose "Clear variable DBUS_SESSION_BUS_ADDRESS = $DBUS_SESSION_BUS_ADDRESS"
  unset DBUS_SESSION_BUS_ADDRESS
fi

if [ -n "${DBUS_SESSION_BUS_PID+1}" ]; then
  verbose "Clear variable DBUS_SESSION_BUS_PID = $DBUS_SESSION_BUS_PID"
  unset DBUS_SESSION_BUS_PID
fi

if [ -n "${DBUS_SESSION_WINDOWID+1}" ]; then
  verbose "Clear variable DBUS_SESSION_WINDOWID = $DBUS_SESSION_WINDOWID"
  unset DBUS_SESSION_WINDOWID
fi

#
# XORG PART
#

setup_kde() {
  # KDETMP
  KDETMP="$TMP"
  export KDETMP
  verbose "KDETMP = $KDETMP"

  # KDEHOME
  if [ -n "${KDEHOME+1}" ]; then
    KDEHOME="$KDEHOME/$MLS"
  else
    KDEHOME="$MYHOME/.kde4/$MLS" 
  fi
  export KDEHOME
  verbose "KDEHOME = $KDEHOME"

  # KDEVARTMP
  if [ -n "${KDEVARTMP+1}" ]; then
    KDEVARTMP="$KDEVARTMP/$MLS"
  else
    KDEVARTMP="/var/tmp/kdecache-$ME/$MLS" 
  fi
  export KDEVARTMP
  verbose "KDEVARTMP = $KDEVARTMP"

  # Set this variable to spawn KIO-slaves directly from the application process
  # itself. By default KIO-slaves are spawned using klauncher / kdeinit . This
  # option is useful if the KIO-slave should run in the same environment as the
  # application. This can be the case with Clearcase.
  KDE_FORK_SLAVES=YES
  verbose "Set KDE_FORK_SLAVES to $KDE_FORK_SLAVES"
  export KDE_FORK_SLAVES

  # There are clients dependent on kbuildsycoca4 binary database.
  if which kbuildsycoca4 &>/dev/null; then
    verbose "Build kbuildsycoca4 binary database."
    kbuildsycoca4 --nosignal
  fi
}

setup_gnome() {
  # GNOME22_USER_DIR
  if [ -n "${GNOME22_USER_DIR+1}" ]; then
    GNOME22_USER_DIR="$GNOME22_USER_DIR/$MLS"
  else
    GNOME22_USER_DIR="$MYHOME/.gnome2/$MLS"
  fi
  export GNOME22_USER_DIR
  verbose "GNOME22_USER_DIR = $GNOME22_USER_DIR"
}

setup_xdg_user_dirs() {
  # Creating $XDG_CONFIG_HOME/user-dirs.dirs file
  #   caja need XDG_DESKTOP_DIR
  [ -z "$XDG_CONFIG_HOME" ] && return
  [ -x "/usr/bin/xdg-user-dirs-update" ] || return

  /usr/bin/xdg-user-dirs-update ||:
}

if shell_var_is_yes "$XORG_ACTIVE"; then

  # XDG_CONFIG_HOME
  if [ -n "${XDG_CONFIG_HOME+1}" ]; then
    XDG_CONFIG_HOME="$XDG_CONFIG_HOME/$MLS"
  else
    XDG_CONFIG_HOME="$MYHOME/.config/$MLS"
  fi
  export XDG_CONFIG_HOME
  verbose "XDG_CONFIG_HOME = $XDG_CONFIG_HOME"

  # XDG_RUNTIME_DIR
  #   This env used in glib function: g_get_user_runtime_dir().
  #   For example, dconf using g_get_user_runtime_dir().
  if [ -n "${XDG_RUNTIME_DIR+1}" ]; then
	  XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR/$MLS"
  else
	  XDG_RUNTIME_DIR="/var/run/user/$UID/$MLS"
  fi
  export XDG_RUNTIME_DIR
  verbose "XDG_RUNTIME_DIR = $XDG_RUNTIME_DIR"

  # XRE_PROFILE_PATH
  #   Firefox's default profile
  XRE_PROFILE_PATH="$XDG_CONFIG_HOME/mozilla/firefox/default"
  export XRE_PROFILE_PATH
  verbose "XRE_PROFILE_PATH = $XRE_PROFILE_PATH"

  # Client doesn't need to know how to find session manager
  verbose "Unset SESSION_MANAGER variable"
  unset SESSION_MANAGER

  setup_xdg_user_dirs

  setup_kde

  setup_gnome

  # Take D-BUS care
  dbus_xorg "$MLS"
fi

exec "$CMD" "$@"

# vim: ai ts=2 sw=2 et sts=2 ft=sh
# vim: autoindent tabstop=2 shiftwidth=2 expandtab softtabstop=2 filetype=sh
