Use getauxval() for issuid() on Linux
This commit is contained in:
@@ -71,6 +71,7 @@ AC_CHECK_HEADERS([\
|
||||
search.h \
|
||||
shadow.h \
|
||||
stdint.h \
|
||||
sys/auxv.h \
|
||||
sys/bswap.h \
|
||||
sys/errno.h \
|
||||
sys/ioctl.h \
|
||||
@@ -184,6 +185,7 @@ AC_CHECK_FUNCS([ \
|
||||
asprintf \
|
||||
atexit \
|
||||
cgetent \
|
||||
getauxval \
|
||||
getconfattr \
|
||||
getprogname \
|
||||
getrlimit \
|
||||
|
@@ -33,24 +33,183 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifdef HAVE_SYS_AUXV_H
|
||||
#include <sys/auxv.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "roken.h"
|
||||
|
||||
/* NetBSD calls AT_UID AT_RUID. Everyone else calls it AT_UID. */
|
||||
#if defined(AT_EUID) && defined(AT_RUID) && !defined(AT_UID)
|
||||
#define AT_UID AT_RUID
|
||||
#endif
|
||||
#if defined(AT_EGID) && defined(AT_RGID) && !defined(AT_GID)
|
||||
#define AT_GID AT_RGID
|
||||
#endif
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#ifdef __GLIBC_PREREQ
|
||||
#define HAVE_GLIBC_API_VERSION_SUPPORT(maj, min) __GLIBC_PREREQ(maj, min)
|
||||
#else
|
||||
#define HAVE_GLIBC_API_VERSION_SUPPORT(maj, min) \
|
||||
((__GLIBC << 16) + GLIBC_MINOR >= ((maj) << 16) + (min))
|
||||
#endif
|
||||
|
||||
#if HAVE_GLIBC_API_VERSION_SUPPORT(2, 19)
|
||||
#define GETAUXVAL_SETS_ERRNO
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETAUXVAL
|
||||
static unsigned long
|
||||
rk_getauxval(unsigned long type)
|
||||
{
|
||||
errno = 0;
|
||||
#ifdef GETAUXVAL_SETS_ERRNO
|
||||
return getauxval(type);
|
||||
#else
|
||||
unsigned long ret = getauxval(type);
|
||||
|
||||
if (ret == 0)
|
||||
errno = ENOENT;
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
#define USE_RK_GETAUXVAL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns non-zero if the caller's process started as set-uid or
|
||||
* set-gid (and therefore the environment cannot be trusted).
|
||||
*
|
||||
* @return Non-zero if the environment is not trusted.
|
||||
*/
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
issuid(void)
|
||||
{
|
||||
/*
|
||||
* We want to use issetugid(), but issetugid() is not the same on
|
||||
* all OSes.
|
||||
*
|
||||
* On Illumos derivatives, OpenBSD, and Solaris issetugid() returns
|
||||
* true IFF the program exec()ed was set-uid or set-gid.
|
||||
*
|
||||
* On NetBSD and FreeBSD issetugid() returns true if the program
|
||||
* exec()ed was set-uid or set-gid, or if the process has switched
|
||||
* UIDs/GIDs or otherwise changed privileges or is a descendant of
|
||||
* such a process and has not exec()ed since.
|
||||
*
|
||||
* What we want here is to know only if the program exec()ed was
|
||||
* set-uid or set-gid, so we can decide whether to trust the
|
||||
* enviroment variables. We don't care if this was a process that
|
||||
* started as root and later changed UIDs/privs whatever: since it
|
||||
* started out as privileged, it inherited an environment from a
|
||||
* privileged pre-exec self, and so on, so the environment is
|
||||
* trusted.
|
||||
*
|
||||
* Therefore the FreeBSD/NetBSD issetugid() does us no good.
|
||||
*
|
||||
* Linux, meanwhile, has no issetugid() (at least glibc doesn't
|
||||
* anyways).
|
||||
*
|
||||
* Systems that support ELF put an "auxilliary vector" on the stack
|
||||
* prior to starting the RTLD, and this vector includes (optionally)
|
||||
* information about the process' EUID, RUID, EGID, RGID, and so on
|
||||
* at the time of exec(), which we can use to construct proper
|
||||
* issetugid() functionality.
|
||||
*
|
||||
* Where available, we use the ELF auxilliary vector as a fallback
|
||||
* if issetugid() is not available.
|
||||
*
|
||||
* All of this is as of late March 2015, and might become stale in
|
||||
* the future.
|
||||
*/
|
||||
|
||||
#ifdef USE_RK_GETAUXVAL
|
||||
/* If we have getauxval(), use that */
|
||||
|
||||
#if (defined(AT_EUID) && defined(AT_UID) || (defined(AT_EGID) && defined(AT_GID)))
|
||||
int seen = 0;
|
||||
#endif
|
||||
|
||||
#if defined(AT_EUID) && defined(AT_UID)
|
||||
{
|
||||
unsigned long euid;
|
||||
unsigned long uid;
|
||||
|
||||
euid = rk_getauxval(AT_EUID);
|
||||
if (errno == 0)
|
||||
seen |= 1;
|
||||
uid = rk_getauxval(AT_UID);
|
||||
if (errno == 0)
|
||||
seen |= 2;
|
||||
if (euid != uid)
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#if defined(AT_EGID) && defined(AT_GID)
|
||||
{
|
||||
unsigned long egid;
|
||||
unsigned long gid;
|
||||
|
||||
egid = rk_getauxval(AT_EGID);
|
||||
if (errno == 0)
|
||||
seen |= 4;
|
||||
gid = rk_getauxval(AT_GID);
|
||||
if (errno == 0)
|
||||
seen |= 8;
|
||||
if (egid != gid)
|
||||
return 2;
|
||||
}
|
||||
#endif
|
||||
#ifdef AT_SECURE
|
||||
/* AT_SECURE is set if the program was set-id. */
|
||||
if (rk_getauxval(AT_SECURE) != 0)
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
#if (defined(AT_EUID) && defined(AT_UID) || (defined(AT_EGID) && defined(AT_GID)))
|
||||
if (seen == 15)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/* rk_getauxval() does set errno */
|
||||
if (errno == 0)
|
||||
return 0;
|
||||
/*
|
||||
* Fall through if we have getauxval() but we didn't have (or don't
|
||||
* know if we don't have) the aux entries that we needed.
|
||||
*/
|
||||
#endif /* USE_RK_GETAUXVAL */
|
||||
|
||||
#if defined(HAVE_ISSETUGID)
|
||||
/*
|
||||
* If we have issetugid(), use it.
|
||||
*
|
||||
* We may lose on some BSDs. This manifests as, for example,
|
||||
* gss_store_cred() not honoring KRB5CCNAME.
|
||||
*/
|
||||
return issetugid();
|
||||
#else /* !HAVE_ISSETUGID */
|
||||
#endif /* USE_RK_GETAUXVAL */
|
||||
|
||||
/*
|
||||
* Paranoia: for extra safety we ought to default to returning 1.
|
||||
* But who knows what that might break where users link statically
|
||||
* and use a.out, say. Also, on Windows we should always return 0.
|
||||
*
|
||||
* For now we stick to returning zero by default.
|
||||
*/
|
||||
|
||||
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
|
||||
if(getuid() != geteuid())
|
||||
if (getuid() != geteuid())
|
||||
return 1;
|
||||
#endif
|
||||
#if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
|
||||
if(getgid() != getegid())
|
||||
if (getgid() != getegid())
|
||||
return 2;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
#endif /* HAVE_ISSETUGID */
|
||||
}
|
||||
|
Reference in New Issue
Block a user