#!/bin/bash
# Based on http://www.fuschlberger.net/programs/ssh-scp-sftp-chroot-jail/
# by Wolfgang Fuschlberger <wf-hp@gmx.net>, covered under GPL-2+
#
# Features:
# - enable scp and sftp in the chroot-jail
# - use one directory (default /home/jail/) as chroot for all users
# - create new accounts
# - move existing accounts to chroot
#####################################################################

# path to sshd's config file: needed for automatic detection of the locaten of
# the sftp-server binary
SSHD_CONFIG="/etc/ssh/sshd_config"

# Check if we are called with username or update
if [ -z "$1" ] ; then
  echo
  echo "ERROR: Parameter missing. Did you forget the username?"
  echo "-------------------------------------------------------------"
  echo
  echo "USAGE:"
  echo "Create new chrooted account or"
  echo "add existing User to chroot-jail:"
  echo "-> $0 username [userpassword]"
  echo
  echo
  echo "Updating files in the chroot-jail:"
  echo "-> $0 update [/path/to/chroot-shell [/path/to/jail]]"
  echo "-------------------------------------------------------------"
  echo
  echo "To uninstall:"
  echo " # userdel \$USER"
  echo " # rm -rf /home/jail"
  echo " (this deletes all Users' files!)"
  echo " manually delete the User's line from /etc/sudoers"
  exit
fi

if [ -z "$PATH" ] ; then
  PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
fi

echo
echo "Release: $RELEASE"
echo

echo "Am I root?  "
if [ "$(whoami 2>/dev/null)" != "root" ] && [ "$(id -un 2>/dev/null)" != "root" ] ; then
  echo "  NO!

Error: You must be root to run this script." >&2
  exit 1
fi
echo "  OK";

APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /sbin/unix_chkpwd"

# Check existence of necessary files
echo "Checking for which... "
if [ -f /usr/bin/which ] || [ -f /bin/which ] || [ -f /sbin/which ] || [ -f /usr/sbin/which ];
  then echo "  OK";
  else echo "  failed

Please install which-binary!
" >&2
exit 1
fi

echo "Checking for chroot..."
if which chroot ; then
  echo "  OK"
else
  echo "  failed, 'chroot' not found!" >&2
  echo "  Please install chroot-package/binary!" >&2
  exit 1
fi

echo "Checking for sudo..."
if which sudo ; then
  echo "  OK"
else
  echo "  failed, 'sudo' not found!" >&2
  echo "  Please install sudo-package/binary!" >&2
  exit 1
fi

echo "Checking for dirname..."
if which dirname ; then
  echo "  OK"
else
  echo "  failed, 'dirname' not found!" >&2
  echo "  Please install dirname-binary (to be found eg in the package coreutils)!" >&2
  exit 1
fi

echo "Checking for awk..."
if which awk ; then
  echo "  OK"
else
  echo "  failed, 'awk' not found!" >&2
  echo "  Please install (g)awk-package/binary!" >&2
  exit 1
fi

# get location of sftp-server binary from /etc/ssh/sshd_config
# check for existence of /etc/ssh/sshd_config and for
# (uncommented) line with sftp-server filename. If neither exists, just skip
# this step and continue without sftp-server
if [ ! -f ${SSHD_CONFIG} ] ; then
   echo "File ${SSHD_CONFIG} not found."
   echo "Not checking for path to sftp-server."
   echo "Please adjust the global \$SSHD_CONFIG variable"
else
  if ! (grep -v "^#" ${SSHD_CONFIG} | grep -i sftp-server &> /dev/null); then
    echo "Obviously no sftp-server is running on this system."
  else
    SFTP_SERVER=$(grep -v "^#" ${SSHD_CONFIG} | grep -i sftp-server | awk  '{ print $3}')
  fi
fi

#if !(grep -v "^#" /etc/ssh/sshd_config | grep -i sftp-server /etc/ssh/sshd_config | awk  '{ print $3}' &> /dev/null); then
APPS="$APPS $SFTP_SERVER"

# Get accountname to create / move
CHROOT_USERNAME=$1
JAILSHELL=/usr/bin/ngcp-chroot-shell
JAILPATH=/home/jail
[ -n "$2" ] && USERPW=$2

# Create common jail and it's subdirectories if it does not exist.
JAILDIRS="dev etc etc/pam.d bin home sbin usr usr/bin usr/lib"
TIMEOUT=3
SLEEP_INTERVAL=1

for directory in $JAILDIRS; do
    TARGET="$JAILPATH/$directory"

    if [ ! -d "$TARGET" ]; then
        mkdir -p "$TARGET"
        echo "Attempting to create $TARGET"

        waited=0
        while [ ! -d "$TARGET" ]; do
            if [ "$waited" -ge "$TIMEOUT" ]; then
                echo "ERROR: $TARGET was not created after $TIMEOUT seconds"
                exit 1
            fi
            echo "Waiting for $TARGET to be created..."
            sleep $SLEEP_INTERVAL
            waited=$((waited + SLEEP_INTERVAL))
        done

        echo "$TARGET successfully created"
    fi
done
echo

# Check if we can cd into the jail directory
if ! cd ${JAILPATH}; then
  echo "ERROR: Cannot change directory to ${JAILPATH}"
  exit 1
fi

# Creating necessary devices
[ -r $JAILPATH/dev/urandom ] || mknod $JAILPATH/dev/urandom c 1 9
[ -r $JAILPATH/dev/null ]    || mknod -m 666 $JAILPATH/dev/null    c 1 3
[ -r $JAILPATH/dev/zero ]    || mknod -m 666 $JAILPATH/dev/zero    c 1 5
[ -r $JAILPATH/dev/tty ]     || mknod -m 666 $JAILPATH/dev/tty     c 5 0

# if we only want to update the files in the jail
# skip the creation of the new account
if [ "$1" != "update" ]; then

# Modify sudo config to enable chroot-ing for users,
# must be removed by hand if account is deleted
SUDOERS="$CHROOT_USERNAME       ALL=NOPASSWD: $(which chroot), /bin/su - $CHROOT_USERNAME"
if [ -d /etc/sudoers.d ]; then
  echo "Installing sudoers configuration file /etc/sudoers.d/jail-$CHROOT_USERNAME"
  echo "$SUDOERS" > "/etc/sudoers.d/jail-$CHROOT_USERNAME"
  chmod 0440 "/etc/sudoers.d/jail-$CHROOT_USERNAME"
else
  echo "Modifying /etc/sudoers"
  echo "$SUDOERS" >> /etc/sudoers
fi

# Define HomeDir for simple referencing
HOMEDIR="$JAILPATH/home/$CHROOT_USERNAME"

# Create new account, setting $JAILSHELL to the above created script
# and $HOME to $JAILPATH/home/*.
if ( id "${CHROOT_USERNAME}" > /dev/null 2>&1 ) ; then
  echo "User exists already, modifying user \"${CHROOT_USERNAME}\""
  usermod -d "$HOMEDIR" -m -s "$JAILSHELL" "$CHROOT_USERNAME" && chmod 700 "$HOMEDIR"
else
  echo "Adding user \"$CHROOT_USERNAME\" to system"
  useradd -m -d "$HOMEDIR" -s "$JAILSHELL" "$CHROOT_USERNAME" && chmod 700 "$HOMEDIR"
  if [ -n "$USERPW" ] ; then
    echo "${CHROOT_USERNAME}:${USERPW}" | chpasswd
  fi
fi

# Create /usr/bin/groups in the jail
echo "#!/bin/bash" > usr/bin/groups
echo "id -Gn" >> usr/bin/groups
chmod 755 usr/bin/groups

# Add users to etc/passwd
#
# check if file exists (ie we are not called for the first time)
# if yes skip root's entry and do not overwrite the file
if [ ! -f etc/passwd ] ; then
 grep /etc/passwd -e "^root" > ${JAILPATH}/etc/passwd
fi
if [ ! -f etc/group ] ; then
 grep /etc/group -e "^root" > ${JAILPATH}/etc/group
# add the group for all users to etc/group (otherwise there is a nasty error
# message and probably because of that changing directories doesn't work with
# winSCP)
 grep /etc/group -e "^users" >> ${JAILPATH}/etc/group
fi

# grep the username which was given to us from /etc/passwd and add it
# to ./etc/passwd replacing the $HOME with the directory as it will then
# appear in the jail
echo "Adding User $CHROOT_USERNAME to jail"
grep -e "^$CHROOT_USERNAME:" /etc/passwd | \
 sed -e "s#$JAILPATH##"      \
     -e "s#$JAILSHELL#/bin/bash#"  >> ${JAILPATH}/etc/passwd

# if the system uses one account/one group we write the
# account's group to etc/group
grep -e "^$CHROOT_USERNAME:" /etc/group >> ${JAILPATH}/etc/group

# write the user's line from /etc/shadow to /home/jail/etc/shadow
grep -e "^$CHROOT_USERNAME:" /etc/shadow >> ${JAILPATH}/etc/shadow
chmod 600 ${JAILPATH}/etc/shadow

# endif for =! update
fi

# Copy the apps and the related libs
echo "Copying necessary library-files to jail (may take some time)"

# The original code worked fine on RedHat 7.3, but did not on FC3.
# On FC3, when the 'ldd' is done, there is a 'linux-gate.so.1' that
# points to nothing (or a 90xb.....), and it also does not pick up
# some files that start with a '/'. To fix this, I am doing the ldd
# to a file called ldlist, then going back into the file and pulling
# out the libs that start with '/'
#
# Randy K.
#
# The original code worked fine on 2.4 kernel systems. Kernel 2.6
# introduced an internal library called 'linux-gate.so.1'. This
# 'phantom' library caused non-critical errors to display during the
# copy since the file does not actually exist on the file system.
# To fix re-direct output of ldd to a file, parse the file and get
# library files that start with /
#

# create temporary files with mktemp, if that doesn't work for some reason use
# the old method with $HOME/ldlist[2] (so I don't have to check the existence
# of the mktemp package / binary at the beginning
#
TMPFILE1=$(mktemp -t ngcp-create-jail-file1-XXXXXXXXXX 2>/dev/null)
if ! [ -e "${TMPFILE1}" ]; then TMPFILE1="${HOME}/ldlist" ; touch "$TMPFILE1" ; fi
TMPFILE2=$(mktemp -t ngcp-create-jail-file2-XXXXXXXXXX 2>/dev/null)
if ! [ -e "${TMPFILE2}" ] ; then TMPFILE2="${HOME}/ldlist2" ; touch "$TMPFILE2" ; fi

for app in $APPS;  do
    # First of all, check that this application exists
    if [ -x "$app" ]; then
        # Check that the directory exists; create it if not.
        # app_path=`echo $app | sed -e 's#\(.\+\)/[^/]\+#\1#'`
        app_path=$(dirname "$app")
        if ! [ -d ".$app_path" ]; then
            mkdir -p ".$app_path"
        fi

        # If the files in the chroot are on the same file system as the
        # original files you should be able to use hard links instead of
        # copying the files, too. Symbolic links cannot be used, because the
        # original files are outside the chroot.
        cp -p "$app" ".$app"

        # get list of necessary libraries
        ldd "$app" >> "${TMPFILE1}"
    fi
done

# Clear out any old temporary file before we start
while read -r line ; do
  for word in ${line} ; do
    if [[ "${word}" =~ ^/.* ]] ; then
       echo "${word}" >> "${TMPFILE2}"
    fi
  done
done < "${TMPFILE1}"

while read -r lib ; do
    mkdir -p ".$(dirname "$lib")" > /dev/null 2>&1

  # If the files in the chroot are on the same file system as the original
  # files you should be able to use hard links instead of copying the files,
  # too. Symbolic links cannot be used, because the original files are
  # outside the chroot.
    cp "$lib" ".$lib"
done < "${TMPFILE2}"

#
# Now, cleanup the 2 files we created for the library list
#
#/bin/rm -f ${HOME}/ldlist
#/bin/rm -f ${HOME}/ldlist2
/bin/rm -f "${TMPFILE1}"
/bin/rm -f "${TMPFILE2}"

# Copy necessary files that are not listed by ldd.
if [ -d /lib/x86_64-linux-gnu ] ; then
  LIB_DIR='/lib/x86_64-linux-gnu'
else
  LIB_DIR='/lib'
fi
TARGET_LIB_DIR="${JAILPATH}${LIB_DIR}"

echo "Copying libnss, libnsl and related libraries to: ${TARGET_LIB_DIR}"
cp \
   "${LIB_DIR}"/libcap.* \
   "${LIB_DIR}"/libnsl* \
   "${LIB_DIR}"/libnss_compat* \
   "${LIB_DIR}"/libnss_files* \
   "${TARGET_LIB_DIR}"

# if you are using PAM you need stuff from /etc/pam.d/ in the jail,
echo "Copying files from /etc/pam.d/ to jail"
cp /etc/pam.d/* ${JAILPATH}/etc/pam.d/

# ...and of course the PAM-modules...

if [ -d "${LIB_DIR}"/security ] ; then
  echo "Copying PAM-Modules (/${LIB_DIR}/security) to jail"
  cp -r "${LIB_DIR}"/security "${TARGET_LIB_DIR}"
fi

# ...and something else useful for PAM
cp -r /etc/security ${JAILPATH}/etc/
cp /etc/login.defs ${JAILPATH}/etc/

if [ -f /etc/DIR_COLORS ] ; then
  cp /etc/DIR_COLORS ${JAILPATH}/etc/
fi

# Don't give more permissions than necessary
chown root:root ${JAILPATH}/bin/su
chmod 700 ${JAILPATH}/bin/su

exit

