2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 14:07:59 +00:00

Refactor the privilege dropping

On Linux, the libcap is now mandatory.  It makes things simpler for us.

System without {set,get}res{uid,gid} now have compatibility shim using
setreuid/setregid or seteuid/setegid to setup effective UID/GID, so the
same code can be called all the time (including on Linux).
This commit is contained in:
Ondřej Surý
2022-10-06 14:22:20 +02:00
parent 64a26f54b0
commit 576345a447
2 changed files with 151 additions and 95 deletions

View File

@@ -63,7 +63,7 @@ static struct passwd *runas_pw = NULL;
static bool done_setuid = false;
static int dfd[2] = { -1, -1 };
#ifdef HAVE_SYS_CAPABILITY_H
#if HAVE_LIBCAP
static bool non_root = false;
static bool non_root_caps = false;
@@ -249,7 +249,137 @@ linux_keepcaps(void) {
}
}
#endif /* HAVE_SYS_CAPABILITY_H */
#endif /* HAVE_LIBCAP */
/*
* First define compatibility shims if {set,get}res{uid,gid} are not available
*/
#if !HAVE_GETRESGID
static int
getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) {
*rgid = -1;
*egid = getegid();
*sgid = -1;
return (0);
}
#endif /* !HAVE_GETRESGID */
#if !HAVE_SETRESGID
static int
setresgid(gid_t rgid, gid_t egid, gid_t sgid) {
REQUIRE(rgid == -1);
REQUIRE(sgid == -1);
#if HAVE_SETREGID
return (setregid(rgid, egid));
#else /* HAVE_SETREGID */
return (setegid(egid));
#endif /* HAVE_SETREGID */
}
#endif /* !HAVE_SETRESGID */
#if !HAVE_GETRESUID
static int
getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) {
*rgid = -1;
*egid = geteuid();
*sgid = -1;
return (0);
}
#endif /* !HAVE_GETRESUID */
#if !HAVE_SETRESUID
static int
setresuid(uid_t ruid, uid_t euid, uid_t suid) {
REQUIRE(rgid == -1);
REQUIRE(sgid == -1);
#if HAVE_SETREGID
return (setregid(rgid, egid));
#else /* HAVE_SETREGID */
return (setegid(egid));
#endif /* HAVE_SETREGID */
}
#endif /* !HAVE_SETRESUID */
static int
set_effective_gid(gid_t gid) {
gid_t oldgid;
if (getresgid(&(gid_t){ 0 }, &oldgid, &(gid_t){ 0 }) == -1) {
return (-1);
}
if (oldgid == gid) {
return (0);
}
if (setresgid(-1, gid, -1) == -1) {
return (-1);
}
if (getresgid(&(gid_t){ 0 }, &oldgid, &(gid_t){ 0 }) == -1) {
return (-1);
}
if (oldgid != gid) {
return (-1);
}
return (0);
}
static int
set_effective_uid(uid_t uid) {
uid_t olduid;
if (getresuid(&(uid_t){ 0 }, &olduid, &(uid_t){ 0 }) == -1) {
return (-1);
}
if (olduid == uid) {
return (0);
}
if (setresuid(-1, uid, -1) == -1) {
return (-1);
}
if (getresuid(&(uid_t){ 0 }, &olduid, &(uid_t){ 0 }) == -1) {
return (-1);
}
if (olduid != uid) {
return (-1);
}
/* Success */
return (0);
}
static void
setperms(uid_t uid, gid_t gid) {
char strbuf[ISC_STRERRORSIZE];
/*
* Drop the gid privilege first, because in some cases the gid privilege
* cannot be dropped after the uid privilege has been dropped.
*/
if (set_effective_gid(gid) == -1) {
strerror_r(errno, strbuf, sizeof(strbuf));
named_main_earlywarning("unable to set effective gid to %d: %s",
gid, strbuf);
}
if (set_effective_uid(uid) == -1) {
strerror_r(errno, strbuf, sizeof(strbuf));
named_main_earlywarning("unable to set effective uid to %d: %s",
uid, strbuf);
}
}
static void
setup_syslog(const char *progname) {
@@ -265,9 +395,9 @@ setup_syslog(const char *progname) {
void
named_os_init(const char *progname) {
setup_syslog(progname);
#ifdef HAVE_SYS_CAPABILITY_H
#if HAVE_LIBCAP
linux_initialprivs();
#endif /* ifdef HAVE_SYS_CAPABILITY_H */
#endif /* HAVE_LIBCAP */
#ifdef SIGXFSZ
signal(SIGXFSZ, SIG_IGN);
#endif /* ifdef SIGXFSZ */
@@ -460,7 +590,7 @@ named_os_changeuser(void) {
named_main_earlyfatal("setuid(): %s", strbuf);
}
#if defined(HAVE_SYS_CAPABILITY_H)
#if HAVE_LIBCAP
/*
* Restore the ability of named to drop core after the setuid()
* call has disabled it.
@@ -472,7 +602,7 @@ named_os_changeuser(void) {
}
linux_minprivs();
#endif /* if defined(HAVE_SYS_CAPABILITY_H) */
#endif /* HAVE_LIBCAP */
}
uid_t
@@ -506,11 +636,11 @@ named_os_adjustnofile(void) {
void
named_os_minprivs(void) {
#if defined(HAVE_SYS_CAPABILITY_H)
#if HAVE_LIBCAP
linux_keepcaps();
named_os_changeuser();
linux_minprivs();
#endif /* if defined(HAVE_SYS_CAPABILITY_H) */
#endif /* HAVE_LIBCAP */
}
static int
@@ -630,56 +760,6 @@ error:
return (-1);
}
#if !HAVE_SYS_CAPABILITY_H
static void
setperms(uid_t uid, gid_t gid) {
#if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID)
char strbuf[ISC_STRERRORSIZE];
#endif /* if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID) */
#if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID)
gid_t oldgid, tmpg;
#endif /* if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) */
#if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID)
uid_t olduid, tmpu;
#endif /* if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID) */
#if defined(HAVE_SETEGID)
if (getegid() != gid && setegid(gid) == -1) {
strerror_r(errno, strbuf, sizeof(strbuf));
named_main_earlywarning("unable to set effective "
"gid to %ld: %s",
(long)gid, strbuf);
}
#elif defined(HAVE_SETRESGID)
if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) {
if (setresgid(-1, gid, -1) == -1) {
strerror_r(errno, strbuf, sizeof(strbuf));
named_main_earlywarning("unable to set effective "
"gid to %d: %s",
gid, strbuf);
}
}
#endif /* if defined(HAVE_SETEGID) */
#if defined(HAVE_SETEUID)
if (geteuid() != uid && seteuid(uid) == -1) {
strerror_r(errno, strbuf, sizeof(strbuf));
named_main_earlywarning("unable to set effective "
"uid to %ld: %s",
(long)uid, strbuf);
}
#elif defined(HAVE_SETRESUID)
if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) {
if (setresuid(-1, uid, -1) == -1) {
strerror_r(errno, strbuf, sizeof(strbuf));
named_main_earlywarning("unable to set effective "
"uid to %d: %s",
uid, strbuf);
}
}
#endif /* if defined(HAVE_SETEUID) */
}
#endif /* !HAVE_SYS_CAPABILITY_H */
FILE *
named_os_openfile(const char *filename, mode_t mode, bool switch_user) {
char strbuf[ISC_STRERRORSIZE], *f;
@@ -705,19 +785,17 @@ named_os_openfile(const char *filename, mode_t mode, bool switch_user) {
if (switch_user && runas_pw != NULL) {
uid_t olduid = getuid();
gid_t oldgid = getgid();
#if HAVE_SYS_CAPABILITY_H
REQUIRE(olduid == runas_pw->pw_uid);
REQUIRE(oldgid == runas_pw->pw_gid);
#else /* HAVE_SYS_CAPABILITY_H */
/* Set UID/GID to the one we'll be running with eventually */
/*
* Set UID/GID to the one we'll be running with
* eventually.
*/
setperms(runas_pw->pw_uid, runas_pw->pw_gid);
#endif
fd = safe_open(filename, mode, false);
#if !HAVE_SYS_CAPABILITY_H
/* Restore UID/GID to previous uid/gid */
setperms(olduid, oldgid);
#endif
if (fd == -1) {
fd = safe_open(filename, mode, false);

View File

@@ -351,10 +351,10 @@ AS_CASE([$host],
AC_CHECK_FUNCS([sysctlbyname])
#
# Older versions of HP/UX don't define seteuid() and setegid()
# Check for uid/gid setting variants
#
AC_CHECK_FUNCS([seteuid setresuid])
AC_CHECK_FUNCS([setegid setresgid])
AC_CHECK_FUNCS([setresuid setreuid getresuid])
AC_CHECK_FUNCS([setresgid setregid getresgid])
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
@@ -1050,32 +1050,10 @@ case "$enable_chroot" in
;;
esac
LIBCAP_LIBS=""
AC_MSG_CHECKING([whether to enable Linux capabilities])
# [pairwise: --enable-linux-caps, --disable-linux-caps]
AC_ARG_ENABLE([linux-caps],
[AS_HELP_STRING([--disable-linux-caps],
[disable Linux capabilities])],
[],
[AS_CASE([$host],
[*-linux*],[enable_linux_caps=yes],
[enable_linux_caps=no])])
AS_IF([test "$enable_linux_caps" = "yes"],
[AC_MSG_RESULT([yes])
AC_CHECK_HEADERS([sys/capability.h],
[],
[AC_MSG_ERROR(m4_normalize([sys/capability.h header is required for Linux capabilities support.
Either install libcap or use --disable-linux-caps.]))])
AX_SAVE_FLAGS([cap])
AC_SEARCH_LIBS([cap_set_proc], [cap],
[LIBCAP_LIBS="$ac_cv_search_cap_set_proc"],
[AC_MSG_ERROR(m4_normalize([libcap is required for Linux capabilities support.
Either install libcap or use --disable-linux-caps.]))])
AX_RESTORE_FLAGS([cap])],
[AC_MSG_RESULT([no])])
AC_SUBST([LIBCAP_LIBS])
AS_CASE([$host],
[*-linux*],
[PKG_CHECK_MODULES([LIBCAP], [libcap],
[AC_DEFINE([HAVE_LIBCAP], [1], [Define to 1 if libcap was found])])])
case "$host" in
*-solaris*)