From 8ac58a757c98ef604b174efd0deab859b93a1bf7 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Wed, 11 Jan 2017 15:27:28 +0000 Subject: [PATCH] ALT: Minimize linux capabilities When PR_SET_KEEPCAPS is enables before the user change, disable it back after the change. If HAVE_LINUX_CAPABILITY_H is enabled, minimize linux capabilities early like in HAVE_LINUXTHREADS mode: lower capabilities to CAP_SYS_RESOURCE|CAP_NET_BIND_SERVICE at the end of the first user change in ns_os_changeuser, and clear remaining capabilities later when ns_os_dropprivs is called right after the second ns_os_changeuser call in load_configuration. If HAVE_LINUXTHREADS is enabled, keep the saved set-user-ID during the first user change in ns_os_changeuser, and finalize the switch later when ns_os_dropprivs is called right after the second ns_os_changeuser call in load_configuration. This trick activates NPTL feature of forcing process credential changes over all threads. --- bind/bin/named/server.c | 1 + bind/bin/named/unix/include/named/os.h | 3 ++ bind/bin/named/unix/os.c | 59 +++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/bind/bin/named/server.c b/bind/bin/named/server.c index dcd4f0e696d..0c0fdc409bd 100644 --- a/bind/bin/named/server.c +++ b/bind/bin/named/server.c @@ -9194,6 +9194,7 @@ load_configuration(const char *filename, named_server_t *server, */ if (first_time) { named_os_changeuser(); + named_os_dropprivs(); } /* diff --git a/bind/bin/named/unix/include/named/os.h b/bind/bin/named/unix/include/named/os.h index 7f167b1c2d1..ea17fecd086 100644 --- a/bind/bin/named/unix/include/named/os.h +++ b/bind/bin/named/unix/include/named/os.h @@ -54,6 +54,9 @@ named_os_minprivs(void); FILE * named_os_openfile(const char *filename, mode_t mode, bool switch_user); +void +named_os_dropprivs(void); + void named_os_writepidfile(const char *filename, bool first_time); diff --git a/bind/bin/named/unix/os.c b/bind/bin/named/unix/os.c index 1ab428baee3..44faacde85e 100644 --- a/bind/bin/named/unix/os.c +++ b/bind/bin/named/unix/os.c @@ -41,7 +41,9 @@ #include #include #include +#ifndef WANT_SETPERMS #include +#endif /* WANT_SETPERMS */ #include #include @@ -75,7 +77,7 @@ static void linux_setcaps(cap_t caps) { char strbuf[ISC_STRERRORSIZE]; - if ((getuid() != 0 && !non_root_caps) || non_root) { + if ((caps && getuid() != 0 && !non_root_caps) || non_root) { return; } if (cap_set_proc(caps) < 0) { @@ -249,6 +251,16 @@ linux_keepcaps(void) { } } +static void +linux_losecaps(void) { + if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0 && errno != EINVAL) { + char strbuf[ISC_STRERRORSIZE]; + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("prctl(PR_SET_KEEPCAPS, 0) failed: %s", + strbuf); + } +} + #endif /* HAVE_SYS_CAPABILITY_H */ static void @@ -455,7 +467,7 @@ named_os_changeuser(void) { named_main_earlyfatal("setgid(): %s", strbuf); } - if (setuid(runas_pw->pw_uid) < 0) { + if (setresuid(runas_pw->pw_uid, runas_pw->pw_uid, -1) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); named_main_earlyfatal("setuid(): %s", strbuf); } @@ -475,6 +487,31 @@ named_os_changeuser(void) { #endif /* if defined(HAVE_SYS_CAPABILITY_H) */ } +void +named_os_dropprivs(void) { + char strbuf[ISC_STRERRORSIZE]; + if (runas_pw == NULL) { + return; + } + if (setresuid(runas_pw->pw_uid, runas_pw->pw_uid, runas_pw->pw_uid) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("setresuid(): %s", strbuf); + } + +#if defined(HAVE_SYS_CAPABILITY_H) + /* + * Restore the ability of named to drop core after the setuid() + * call has disabled it. + */ + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s", + strbuf); + } + +#endif /* if defined(HAVE_SYS_CAPABILITY_H) */ +} + uid_t ns_os_uid(void) { if (runas_pw == NULL) { @@ -507,7 +544,7 @@ named_os_minprivs(void) { #if defined(HAVE_SYS_CAPABILITY_H) linux_keepcaps(); named_os_changeuser(); - linux_minprivs(); + linux_losecaps(); #endif /* if defined(HAVE_SYS_CAPABILITY_H) */ } @@ -576,18 +613,21 @@ static int mkdirpath(char *filename, void (*report)(const char *, ...)) { char *slash = strrchr(filename, '/'); char strbuf[ISC_STRERRORSIZE]; - unsigned int mode; if (slash != NULL && slash != filename) { struct stat sb; *slash = '\0'; if (stat(filename, &sb) == -1) { +#ifdef WANT_SETPERMS + unsigned int mode; if (errno != ENOENT) { +#endif /* WANT_SETPERMS */ strerror_r(errno, strbuf, sizeof(strbuf)); (*report)("couldn't stat '%s': %s", filename, strbuf); goto error; +#ifdef WANT_SETPERMS } if (mkdirpath(filename, report) == -1) { goto error; @@ -618,6 +658,7 @@ mkdirpath(char *filename, void (*report)(const char *, ...)) { (*report)("couldn't chown '%s': %s", filename, strbuf); } +#endif /* WANT_SETPERMS */ } *slash = '/'; } @@ -628,6 +669,7 @@ error: return (-1); } +#ifdef WANT_SETPERMS #if !HAVE_SYS_CAPABILITY_H static void setperms(uid_t uid, gid_t gid) { @@ -677,6 +719,7 @@ setperms(uid_t uid, gid_t gid) { #endif /* if defined(HAVE_SETEUID) */ } #endif /* HAVE_SYS_CAPABILITY_H */ +#endif /* WANT_SETPERMS */ FILE * named_os_openfile(const char *filename, mode_t mode, bool switch_user) { @@ -684,6 +727,9 @@ named_os_openfile(const char *filename, mode_t mode, bool switch_user) { FILE *fp; int fd; +#ifndef WANT_SETPERMS + UNUSED(switch_user); +#endif /* WANT_SETPERMS */ /* * Make the containing directory if it doesn't exist. */ @@ -700,6 +746,7 @@ named_os_openfile(const char *filename, mode_t mode, bool switch_user) { } free(f); +#ifdef WANT_SETPERMS if (switch_user && runas_pw != NULL) { uid_t olduid = getuid(); gid_t oldgid = getgid(); @@ -733,7 +780,9 @@ named_os_openfile(const char *filename, mode_t mode, bool switch_user) { "directory permissions " "or reconfigure the filename."); } - } else { + } else +#endif /* WANT_SETPERMS */ + { fd = safe_open(filename, mode, false); } -- 2.33.4