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(a)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(a)altlinux.org>
[rharwood(a)redhat.com: rewrite commit message, comments, and error strings]
Reviewed-by: Robbie Harwood <rharwood(a)redhat.com>
Reviewed-by: Simo Sorce <simo(a)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);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.