#! /bin/bash
# Copyright (C) 2007 Apple Inc. All rights reserved.

# Basic script to test access checks that depend on the supplementary
# group list.

SCRIPTBASE=${SCRIPTBASE:-$(cd $(dirname $0)/.. && pwd )}

.  $SCRIPTBASE/common.sh || exit 2
.  $SCRIPTBASE/directory-services.sh || exit 2

#QUIET=">/dev/null 2>&1"

TESTUSER=${TESTUSER:-smbtest}
TESTPASSWD=${TESTPASSWD:-smbtest}
CIFSDD=${CIFSDD:-/usr/local/bin/cifsdd}
DEBUGLEVEL=${DEBUGLEVEL:-4}

ASROOT=sudo

SHAREPATH=/tmp/$(basename $0)

SERVER=localhost
SHARE=GroupAccess

testfile=$(basename $0)-$$.data
tmpfile=/tmp/$(basename $0)-$$.verify

failed=0

failtest()
{
    echo "*** TEST FAILED ***"
    failed=`expr $failed + 1`
}

# Set up a Samba share.
rm -rf $SHAREPATH
mkdir -p $SHAREPATH

defaults write /Library/Preferences/SystemConfiguration/com.apple.smb.server \
    LoggingLevel -int $DEBUGLEVEL
/usr/libexec/samba/synchronize-shares

create_smb_share $SHARE $SHAREPATH

save_config_file /etc/memberd.conf $(basename $0)
cat > /etc/memberd.conf <<EOF
#1.1
MaxItemsInCache 0
DefaultExpirationInSecs 0
DefaultFailureExpirationInSecs 0
DefaultLoginExpirationInSecs 0
# Generated by $0
EOF

$ASROOT killall -TERM DirectoryService

cleanup()
{
    remove_smb_share $SHARE
    restore_config_file /etc/memberd.conf $(basename $0)
    rm -f $tmpfile
    rm -rf $SHAREPATH

    ds_delete_user $TESTUSER > /dev/null 2>&1
}

# Allow users to set NOCLEANUP to leave the temporary test state around.
if [ -z "$NOCLEANUP" ]; then
    register_cleanup_handler cleanup
fi

# Alter the file permissions so that only root and the group owner can read it.
set_group_ownership()
{
    file=$1
    gid=$2

    $ASROOT chown 0:$gid $file
    $ASROOT chmod u=r--,g=r--,o=-- $file
}

clear_group_ownership()
{
    file=$1
    $ASROOT chmod 644 $file
}

user_is_in_group()
{
    user="$1"
    gid="$2"

    id -G $user | grep $gid > /dev/null 2>&1
}

copyfile()
{
    $CIFSDD --debuglevel=$DEBUGLEVEL \
            -U "$TESTUSER"%"$TESTPASSWD" \
            "$@"
}

ds_delete_user $TESTUSER > /dev/null 2>&1
ds_create_user $TESTUSER $TESTPASSWD || \
		   testerr $0 "failed to add user $TESTUSER"

dd if=/dev/random of=/$SHAREPATH/$testfile bs=4096 count=1

for gid in $(ds_list_group_ids) ; do
    rm -f $tmpfile

    if [ $[ $gid < 0 ] == "1" ]; then
	continue;
    fi

    echo Testing group $gid

    # Count the current group list. id(1) resolves nested groups, so it's
    # better than anything we can do by hand.
    ngroups=$(ds_count_user_groups $TESTUSER)

    set_group_ownership /$SHAREPATH/$testfile $gid

    echo "Checking that local access is denied" | indent
    $ASROOT su - $TESTUSER -c \
	"dd if=/$SHAREPATH/$testfile of=/dev/null" $QUIET && failtest

    echo "Checking that access is denied" | indent
    copyfile if=//$SERVER/$SHARE/$testfile of=$tmpfile > /dev/null 2>&1

    if [ "$?" == "0" ]; then
	# We can end up in a group we don't really expect to be in if the
	# group is nested. In this case, access will be granted, so we
	# should not fail.
	if user_is_in_group $TESTUSER $gid ; then
	    echo "Access granted due to nested groups" | indent
	else
	    echo This should have failed:
	    echo "    " copyfile if=//$SERVER/$SHARE/$testfile of=$tmpfile
	    testerr $0 "$ngroups supplementary groups"
	fi
    fi

    echo "Added $TESTUSER to group $gid ($ngroups supplementary groups)" | \
	indent

    ds_add_user_to_group $TESTUSER $gid || \
	testerr $0 "failed to add $TESTUSER to group $gid"
    $ASROOT killall -TERM DirectoryServices
    ngroups=$(ds_count_user_groups $TESTUSER)

    echo "Checking that local access is allowed" | indent
    $ASROOT su - $TESTUSER -c \
	"dd if=/$SHAREPATH/$testfile of=/dev/null" $QUIET || failtest

    echo "Checking that access is allowed" | indent
    copyfile if=//$SERVER/$SHARE/$testfile of=$tmpfile > /dev/null

    if [ "$?" != "0" ]; then
	echo "This should have succeeded (first chance):"
	echo "    " copyfile if=//$SERVER/$SHARE/$testfile of=$tmpfile

	# DirectoryService floors the TTL to 30sec, so wait before
	# retrying.
	echo "Re-checking that access is allowed (in 30 sec)"
	sleep 31
	copyfile if=//$SERVER/$SHARE/$testfile of=$tmpfile > /dev/null
	if [ "$?" != "0" ]; then
	    testerr $0 "$ngroups supplementary groups"
	fi
    fi

    clear_group_ownership /$SHAREPATH/$testfile
    files_are_the_same /$SHAREPATH/$testfile $tmpfile || failtest
done

testok $0 $failed
