This is an automated email from the git hooks/post-receive script.
rharwood pushed a commit to branch master in repository gssproxy.
commit 2ca452537b29cc04ea5248db6328b2ddf948db15 Author: Stanislav Levin slev@altlinux.org AuthorDate: Fri Dec 7 10:29:20 2018 +0300
Retain CAP_SYS_PTRACE when running as unpriviliged
Without CAP_SYS_PTRACE, gssproxy will be unable to read /proc/pid/exe, which breaks program name matching.
Fixes: https://pagure.io/gssproxy/issue/239 Signed-off-by: Stanislav Levin slev@altlinux.org [rharwood@redhat.com: rewrite commit message, comments, and error strings] Reviewed-by: Robbie Harwood rharwood@redhat.com Reviewed-by: Simo Sorce simo@redhat.com Merges: #240 --- Makefile.am | 2 +- configure.ac | 7 ++ contrib/gssproxy.spec.in | 1 + src/gp_init.c | 164 ++++++++++++++++++++++++++++++++++++++++++++--- src/gp_proxy.h | 2 + 5 files changed, 167 insertions(+), 9 deletions(-)
diff --git a/Makefile.am b/Makefile.am index 5f3aeb0..3595963 100644 --- a/Makefile.am +++ b/Makefile.am @@ -88,7 +88,7 @@ AM_CPPFLAGS += \ -DSYSCONFDIR="$(sysconfdir)" \ -DLOCALEDIR="$(localedir)"
-GSS_PROXY_LIBS = $(POPT_LIBS) $(KRB5_LIBS) $(VERTO_LIBS) $(INI_LIBS) $(GSSAPI_LIBS) $(GSSRPC_LIBS) +GSS_PROXY_LIBS = $(POPT_LIBS) $(KRB5_LIBS) $(VERTO_LIBS) $(INI_LIBS) $(GSSAPI_LIBS) $(GSSRPC_LIBS) $(CAP_LIBS)
if BUILD_SELINUX GSS_PROXY_LIBS += $(SELINUX_LIBS) diff --git a/configure.ac b/configure.ac index 0af44ab..a7f6aaf 100644 --- a/configure.ac +++ b/configure.ac @@ -280,6 +280,13 @@ AC_CHECK_LIB(gssrpc, gssrpc_xdrmem_create,, [$GSSAPI_LIBS $GSSRPC_LIBS]) AC_SUBST([GSSRPC_LIBS])
+AC_CHECK_FUNC([prctl],,[AC_MSG_ERROR([Failed to find prctl])]) +AC_CHECK_LIB([cap], [cap_set_proc],[CAP_LIBS=-lcap], + [AC_MSG_ERROR(["Failed to find libcap symbols"])]) +AC_SUBST([CAP_LIBS]) +AC_CHECK_HEADERS([sys/capability.h],, + [AC_MSG_ERROR([Could not find libcap headers])]) + AC_CHECK_FUNCS([__secure_getenv secure_getenv])
WITH_INITSCRIPT diff --git a/contrib/gssproxy.spec.in b/contrib/gssproxy.spec.in index ccd3e50..3ce7113 100644 --- a/contrib/gssproxy.spec.in +++ b/contrib/gssproxy.spec.in @@ -40,6 +40,7 @@ BuildRequires: libselinux-devel BuildRequires: keyutils-libs-devel BuildRequires: libini_config-devel >= 1.2.0 BuildRequires: libverto-devel +BuildRequires: libcap-devel BuildRequires: popt-devel BuildRequires: findutils BuildRequires: systemd-units diff --git a/src/gp_init.c b/src/gp_init.c index f64e22c..47a461c 100644 --- a/src/gp_init.c +++ b/src/gp_init.c @@ -1,17 +1,21 @@ /* Copyright (C) 2011,2015 the GSS-PROXY contributors, see COPYING for license */
-#include <stdlib.h> -#include <sys/types.h> -#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <linux/capability.h> #include <locale.h> +#include <pwd.h> #include <signal.h> -#include <fcntl.h> -#include <errno.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <sys/capability.h> +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> -#include <stdio.h> -#include <pwd.h> -#include <grp.h> + #include "gp_proxy.h"
void init_server(bool daemonize, int *wait_fd) @@ -223,6 +227,16 @@ int drop_privs(struct gp_config *cfg) return 0; }
+ /* Retain capabilities when changing UID to non-zero. We drop the ones we + * don't need after the switch. */ + ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + if (ret) { + ret = errno; + GPDEBUG("Failed to set keep capabilities: [%d:%s]\n", + ret, gp_strerror(ret)); + return ret; + } + ret = getpwnam_r(cfg->proxy_user, &pws, buf, 2048, &pw); if (ret) { GPDEBUG("Failed to look up proxy user: '%s'! [%d:%s]\n", @@ -253,5 +267,139 @@ int drop_privs(struct gp_config *cfg) return ret; }
+ /* Now drop the capabilities we don't need, and turn PR_SET_KEEPCAPS back + * off. */ + ret = drop_caps(); + if (ret) { + return ret; + } + + if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0)) { + ret = errno; + GPDEBUG("Failed to reset keep capabilities: [%d:%s]\n", + ret, gp_strerror(ret)); + return ret; + } + return 0; } + +/* Remove all capabilties from the process. (In order to manipulate our + * capability set, we need to have CAP_SETPCAP.) */ +int clear_bound_caps() +{ + cap_t caps = NULL; + cap_value_t cap = 0; + const cap_value_t setpcap_list[] = { CAP_SETPCAP }; + int ret; + + caps = cap_get_proc(); + if (caps == NULL) { + ret = errno; + GPDEBUG("Failed to get current capabilities: [%d:%s]\n", + ret, gp_strerror(ret)); + goto done; + } + + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, setpcap_list, CAP_SET) == -1) { + ret = errno; + GPDEBUG("Failed to set CAP_SETPCAP in effective set: [%d:%s]\n", ret, + gp_strerror(ret)); + goto done; + } + + if (cap_set_proc(caps) == -1) { + ret = errno; + GPDEBUG("Failed to apply CAP_SETPCAP: [%d:%s]\n", ret, + gp_strerror(ret)); + goto done; + } + + /* Now that we have CAP_SETPCAP in the effective set, remove all other + * capabilities. */ + while (CAP_IS_SUPPORTED(cap)) { + if (cap_drop_bound(cap) != 0) { + ret = errno; + GPDEBUG("Failed to drop bounding set capability: [%d:%s]\n", + ret, gp_strerror(ret)); + goto done; + } + cap++; + } + ret = 0; + +done: + if (caps && cap_free(caps) == -1) { + ret = errno; + GPDEBUG("Failed to free capability state: [%d:%s]\n", + ret, gp_strerror(ret)); + } + return ret; +} + +/* For program name matching, we need to have CAP_SYS_PTRACE in order to read + * /proc/pid/exe. Because we've set PR_SET_KEEPCAPS, every thread inherits + * the process set of its parent, so we drop everything but CAP_SYS_PTRACE. */ +int drop_caps() +{ + cap_t caps = NULL; + int ret; + const cap_value_t ptrace_list[] = { CAP_SYS_PTRACE }; + + /* Completely drop the bounding set. */ + ret = clear_bound_caps(); + if (ret) { + goto done; + } + + ret = CAP_IS_SUPPORTED(CAP_SYS_PTRACE); + if (ret == -1) { + ret = errno; + GPDEBUG("Failed to check if CAP_SYS_PTRACE is supported: [%d:%s]\n", + ret, gp_strerror(ret)); + goto done; + } else if (!ret) { + GPDEBUG("Capability CAPS_SYS_PTRACE is not supported\n"); + ret = EINVAL; + goto done; + } + + /* Now, make an empty capabilitiy set and put CAP_SYS_PTRACE in it. */ + caps = cap_init(); + if (caps == NULL) { + ret = errno; + GPDEBUG("Failed to init capabilities: [%d:%s]\n", + ret, gp_strerror(ret)); + goto done; + } + + if (cap_set_flag(caps, CAP_PERMITTED, 1, ptrace_list, CAP_SET) == -1) { + ret = errno; + GPDEBUG("Failed to set permitted capabilities: [%d:%s]\n", + ret, gp_strerror(ret)); + goto done; + } + + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, ptrace_list, CAP_SET) == -1) { + ret = errno; + GPDEBUG("Failed to set effective capabilities: [%d:%s]\n", + ret, gp_strerror(ret)); + goto done; + } + + if (cap_set_proc(caps) == -1) { + ret = errno; + GPDEBUG("Failed to apply capability set: [%d:%s]\n", + ret, gp_strerror(ret)); + goto done; + } + ret = 0; + +done: + if (caps && cap_free(caps) == -1) { + ret = errno; + GPDEBUG("Failed to free capability state: [%d:%s]\n", + ret, gp_strerror(ret)); + } + return ret; +} diff --git a/src/gp_proxy.h b/src/gp_proxy.h index 3e944ab..8763bcf 100644 --- a/src/gp_proxy.h +++ b/src/gp_proxy.h @@ -102,6 +102,8 @@ verto_ctx *init_event_loop(void); void init_proc_nfsd(struct gp_config *cfg); void write_pid(void); int drop_privs(struct gp_config *cfg); +int drop_caps(void); +int clear_bound_caps(void);
/* from gp_socket.c */ void free_unix_socket(verto_ctx *ctx, verto_ev *ev);