#!/bin/bash


if [ ! -r "./test_privs.c" ]; then
	cat > "./test_privs.c" << EOF
#include <stdio.h>
#include <stdlib.h>
#include "X11/Intrinsic.h"

Bool xf86PrivsElevated(void)
{
  static Bool privsTested = FALSE;
  static Bool privsElevated = TRUE;

  if (!privsTested) {
#if defined(WIN32)
    privsElevated = FALSE;
#else
    if ((getuid() != geteuid()) || (getgid() != getegid())) {
      privsElevated = TRUE;
    } else {
#if defined(HAVE_ISSETUGID)
      privsElevated = issetugid();
#elif defined(HAVE_GETRESUID)
      uid_t ruid, euid, suid;
      gid_t rgid, egid, sgid;

      if ((getresuid(&ruid, &euid, &suid) == 0) &&
          (getresgid(&rgid, &egid, &sgid) == 0)) {
        privsElevated = (euid != suid) || (egid != sgid);
      }
      else {
        printf("Failed getresuid or getresgid");
        /* Something went wrong, make defensive assumption */
        privsElevated = TRUE;
      }
#else
      if (getuid()==0) {
        /* running as root: uid==euid==0 */
        privsElevated = FALSE;
      }
      else {
        /*
         * If there are saved ID's the process might still be privileged
         * even though the above test succeeded. If issetugid() and
         * getresgid() aren't available, test this by trying to set
         * euid to 0.
         */
        unsigned int oldeuid;
        oldeuid = geteuid();

        if (seteuid(0) != 0) {
          privsElevated = FALSE;
        } else {
          if (seteuid(oldeuid) != 0) {
            printf("FatalError: Failed to drop privileges.  Exiting\n");
            return -5;
            //FatalError("Failed to drop privileges.  Exiting\n");
          }
          privsElevated = TRUE;
        }
      }
#endif
    }
#endif
    privsTested = TRUE;
  }
  return privsElevated;
}

int main() {
  int e = xf86PrivsElevated();
  printf("xf86PrivsElevated()=%d\n", e);
  return e;
}
EOF
fi


declare -a variants
function build_test() {
	defines=$1
	name=$2
	gcc ${defines} -o test_privs_${name}.bin test_privs.c
	if [ "$?" -eq "0" ]; then
		echo "built ${name} variant"
		variants[${#variants[@]}]=${name}
	else
		echo "failed to build ${name}"
	fi
}

echo "**************************************************"
echo "building all variants (some may fail - that's OK):"
echo "**************************************************"
build_test "-DHAVE_ISSETUGID" "issetugid"
build_test "-DHAVE_GETRESUID" "getresuid"
build_test "" "fallbackseteuidtest"
echo
if [ "${#variants[*]}" -eq "0" ]; then
	echo "ERROR: all variants failed to build!"
	echo "please review the gcc error messages above"
	exit 1
fi

function test_all_variants() {
	erc=$1
	preexec=$2
	for i in ${!variants[*]}; do
		variant="./test_privs_${variants[$i]}.bin"
		ls -ln ${variant}
		bin="${preexec} ${variant}"
		#echo "$bin"
		${bin}
		rc=$?
		if [ "${rc}" -ne "${erc}" ]; then
			echo "* ERROR: expected ${erc} but returned ${rc} for ${bin}"
		fi
	done
	echo
}


echo "Testing as current user: `id -u`:"
test_all_variants "0" ""

echo "Testing via sudo:"
test_all_variants "0" "sudo"

echo "Testing suid non root:"
chmod +s ./test_privs_*.bin
test_all_variants "0" ""

echo "Testing suid non root via sudo:"
test_all_variants "1" "sudo"

echo "Testing suid binary as root:"
sudo chown root:root ./test_privs_*.bin
sudo chmod +s ./test_privs_*.bin
test_all_variants "0" "sudo"

echo "Testing suid binary as current user:"
test_all_variants "1" ""

echo "Cleaning up"
rm -f ./test_privs_*.bin
sudo rm -f ./test_privs_*.bin

