/* $Id: unixres.c,v 1.3 2003/10/25 19:09:28 j_ali Exp $ */
/* Copyright (c) Slash'EM development team, 2001. */
/* NetHack may be freely redistributed.  See license for details. */

/* [ALI] This module defines nh_xxx functions to replace getuid etc which
 * will hide privileges from the caller if so desired.
 *
 * Currently supported UNIX variants:
 *	Linux version 2.1.44 and above
 *	FreeBSD (versions unknown)
 *
 * Note: SunOS and Solaris have no mechanism for retrieving the saved id,
 * so temporarily dropping privileges on these systems is sufficient to
 * hide them.
 */
#ifdef __GNUC__
#define _GNU_SOURCE
#endif

#include "config.h"

#ifdef GETRES_SUPPORT

# if defined(LINUX)

/* requires dynamic linking with libc */
#include <dlfcn.h>

static int
real_getresuid(ruid, euid, suid)
uid_t *ruid, *euid, *suid;
{
    int (*f)(uid_t *, uid_t *, uid_t *); /* getresuid signature */

    f = dlsym(RTLD_NEXT, "getresuid");
    if (!f) return -1;

    return f(ruid, euid, suid);
}

static int
real_getresgid(rgid, egid, sgid)
gid_t *rgid, *egid, *sgid;
{
    int (*f)(gid_t *, gid_t *, gid_t *); /* getresgid signature */

    f = dlsym(RTLD_NEXT, "getresgid");
    if (!f) return -1;

    return f(rgid, egid, sgid);
}

# else
#  if defined(BSD) || defined(SVR4)

#   ifdef SYS_getresuid

static int
real_getresuid(ruid, euid, suid)
uid_t *ruid, *euid, *suid;
{
    return syscall(SYS_getresuid, ruid, euid, suid);
}

#   else	/* SYS_getresuid */

#ifdef SVR4
#include <sys/stat.h>
#endif /* SVR4 */

static int
real_getresuid(ruid, euid, suid)
uid_t *ruid, *euid, *suid;
{
    int retval;
    int pfd[2];
    struct stat st;
    if (pipe(pfd))
	return -1;
    retval = fstat(pfd[0], &st);
    close(pfd[0]);
    close(pfd[1]);
    if (!retval) {
	*euid = st.st_uid;
	*ruid = syscall(SYS_getuid);
	*suid = *ruid;			/* Not supported under SVR4 */
    }
    return retval;
}

#   endif	/* SYS_getresuid */

#   ifdef SYS_getresgid

static int
real_getresgid(rgid, egid, sgid)
gid_t *rgid, *egid, *sgid;
{
    return syscall(SYS_getresgid, rgid, egid, sgid);
}

#   else	/* SYS_getresgid */

static int
real_getresgid(rgid, egid, sgid)
gid_t *rgid, *egid, *sgid;
{
    int retval;
    int pfd[2];
    struct stat st;
    if (pipe(pfd))
	return -1;
    retval = fstat(pfd[0], &st);
    close(pfd[0]);
    close(pfd[1]);
    if (!retval) {
	*egid = st.st_gid;
	*rgid = syscall(SYS_getgid);
	*sgid = *rgid;			/* Not supported under SVR4 */
    }
    return retval;
}

#   endif	/* SYS_getresgid */
#  endif	/* BSD || SVR4 */
# endif		/* LINUX */

static unsigned int hiding_privileges = 0;

/*
 * Note: returns the value _after_ action.
 */

int
hide_privileges(flag)
boolean flag;
{
    if (flag)
	hiding_privileges++;
    else if (hiding_privileges)
	hiding_privileges--;
    return hiding_privileges;
}

int
nh_getresuid(ruid, euid, suid)
uid_t *ruid, *euid, *suid;
{
    int retval = real_getresuid(ruid, euid, suid);
    if (!retval && hiding_privileges)
	*euid = *suid = *ruid;
    return retval;
}

uid_t
nh_getuid()
{
    uid_t ruid, euid, suid;
    (void) real_getresuid(&ruid, &euid, &suid);
    return ruid;
}

uid_t
nh_geteuid()
{
    uid_t ruid, euid, suid;
    (void) real_getresuid(&ruid, &euid, &suid);
    if (hiding_privileges)
	euid = ruid;
    return euid;
}

int
nh_getresgid(rgid, egid, sgid)
gid_t *rgid, *egid, *sgid;
{
    int retval = real_getresgid(rgid, egid, sgid);
    if (!retval && hiding_privileges)
	*egid = *sgid = *rgid;
    return retval;
}

gid_t
nh_getgid()
{
    gid_t rgid, egid, sgid;
    (void) real_getresgid(&rgid, &egid, &sgid);
    return rgid;
}

gid_t
nh_getegid()
{
    gid_t rgid, egid, sgid;
    (void) real_getresgid(&rgid, &egid, &sgid);
    if (hiding_privileges)
	egid = rgid;
    return egid;
}

#else	/* GETRES_SUPPORT */

# ifdef GNOME_GRAPHICS 
int
hide_privileges(flag)
boolean flag;
{
    return 0;
}
# endif

#endif	/* GETRES_SUPPORT */
