From a3112854fa1691940fe0b5a2b623b5badc8dd5a4 Mon Sep 17 00:00:00 2001 From: Alexey Tikhonov Date: Sat, 2 Sep 2023 14:51:52 +0200 Subject: [PATCH] UTILS: add capabilities management helpers --- Makefile.am | 4 + configure.ac | 7 ++ contrib/ci/deps.sh | 2 + contrib/sssd.spec.in | 1 + src/util/capabilities.c | 208 ++++++++++++++++++++++++++++++++++++++++ src/util/server.c | 13 +++ src/util/util.h | 3 + 7 files changed, 238 insertions(+) create mode 100644 src/util/capabilities.c diff --git a/Makefile.am b/Makefile.am index 46ef95a8865..ae8893542b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1289,6 +1289,7 @@ libsss_util_la_SOURCES = \ src/util/well_known_sids.c \ src/util/string_utils.c \ src/util/become_user.c \ + src/util/capabilities.c \ src/util/util_watchdog.c \ src/util/sss_ptr_hash.c \ src/util/files.c \ @@ -1305,6 +1306,7 @@ libsss_util_la_CFLAGS = \ libsss_util_la_LIBADD = \ $(LIBADD_TIMER) \ $(SSSD_LIBS) \ + $(CAP_LIBS) \ $(SYSTEMD_LOGIN_LIBS) \ $(UNICODE_LIBS) \ $(PCRE_LIBS) \ @@ -4717,6 +4719,7 @@ krb5_child_LDADD = \ $(CLIENT_LIBS) \ $(SYSTEMD_LOGIN_LIBS) \ $(JANSSON_LIBS) \ + $(CAP_LIBS) \ $(NULL) ldap_child_SOURCES = \ @@ -4742,6 +4745,7 @@ ldap_child_LDADD = \ libsss_debug.la \ $(TALLOC_LIBS) \ $(POPT_LIBS) \ + $(CAP_LIBS) \ $(DHASH_LIBS) \ $(KRB5_LIBS) diff --git a/configure.ac b/configure.ac index 36302fbfb35..b41b468dcd3 100644 --- a/configure.ac +++ b/configure.ac @@ -513,6 +513,13 @@ AS_IF([test x$have_check = x], [ AC_CHECK_HEADERS([check.h],,AC_MSG_ERROR([Could not find CHECK headers])) ]) +PKG_CHECK_MODULES([CAP], [libcap], [have_libcap=1], [have_libcap=]) +AS_IF([test x$have_libcap = x], [ + AC_MSG_ERROR([libcap is missing]) +], [ + AC_CHECK_HEADERS([sys/capability.h],,AC_MSG_ERROR([Could not find sys/capability.h headers])) +]) + AC_PATH_PROG([DOXYGEN], [doxygen], [false]) AM_CONDITIONAL([HAVE_DOXYGEN], [test x$DOXYGEN != xfalse ]) diff --git a/contrib/ci/deps.sh b/contrib/ci/deps.sh index f6f50185866..426a743c823 100644 --- a/contrib/ci/deps.sh +++ b/contrib/ci/deps.sh @@ -46,6 +46,7 @@ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then krb5-server krb5-workstation libunistring-devel + libcap-devel ) if [[ "$DISTRO_BRANCH" == -redhat-redhatenterprise*-8.*- || @@ -180,6 +181,7 @@ if [[ "$DISTRO_BRANCH" == -debian-* ]]; then libp11-kit-dev bc libunistring-dev + libcap-dev ) DEPS_INTGCHECK_SATISFIED=true diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in index aebb1a9637d..70c4a92c1a8 100644 --- a/contrib/sssd.spec.in +++ b/contrib/sssd.spec.in @@ -112,6 +112,7 @@ BuildRequires: gettext-devel # required for p11_child smartcard tests BuildRequires: gnutls-utils BuildRequires: jansson-devel +BuildRequires: libcap-devel BuildRequires: libcurl-devel BuildRequires: libjose-devel BuildRequires: keyutils-libs-devel diff --git a/src/util/capabilities.c b/src/util/capabilities.c new file mode 100644 index 00000000000..ca5f09bee29 --- /dev/null +++ b/src/util/capabilities.c @@ -0,0 +1,208 @@ +/* + SSSD + + Capabilities management helpers + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "config.h" +#include +#include +#include + +#include "util/util.h" + + +typedef struct _cap_description +{ + cap_value_t val; + const char *name; +} cap_description; + +#define _CAP_DESCR(cap) {cap, #cap} + +static cap_description _all_caps[] = +{ + _CAP_DESCR(CAP_AUDIT_CONTROL), + _CAP_DESCR(CAP_AUDIT_READ), + _CAP_DESCR(CAP_AUDIT_WRITE), + _CAP_DESCR(CAP_BLOCK_SUSPEND), + _CAP_DESCR(CAP_BPF), + _CAP_DESCR(CAP_CHECKPOINT_RESTORE), + _CAP_DESCR(CAP_CHOWN), + _CAP_DESCR(CAP_DAC_OVERRIDE), + _CAP_DESCR(CAP_DAC_READ_SEARCH), + _CAP_DESCR(CAP_FOWNER), + _CAP_DESCR(CAP_FSETID), + _CAP_DESCR(CAP_IPC_LOCK), + _CAP_DESCR(CAP_IPC_OWNER), + _CAP_DESCR(CAP_KILL), + _CAP_DESCR(CAP_LEASE), + _CAP_DESCR(CAP_LINUX_IMMUTABLE), + _CAP_DESCR(CAP_MAC_ADMIN), + _CAP_DESCR(CAP_MAC_OVERRIDE), + _CAP_DESCR(CAP_MKNOD), + _CAP_DESCR(CAP_NET_ADMIN), + _CAP_DESCR(CAP_NET_BIND_SERVICE), + _CAP_DESCR(CAP_NET_BROADCAST), + _CAP_DESCR(CAP_NET_RAW), + _CAP_DESCR(CAP_PERFMON), + _CAP_DESCR(CAP_SETGID), + _CAP_DESCR(CAP_SETFCAP), + _CAP_DESCR(CAP_SETPCAP), + _CAP_DESCR(CAP_SETUID), + _CAP_DESCR(CAP_SYS_ADMIN), + _CAP_DESCR(CAP_SYS_BOOT), + _CAP_DESCR(CAP_SYS_CHROOT), + _CAP_DESCR(CAP_SYS_MODULE), + _CAP_DESCR(CAP_SYS_NICE), + _CAP_DESCR(CAP_SYS_PACCT), + _CAP_DESCR(CAP_SYS_PTRACE), + _CAP_DESCR(CAP_SYS_RAWIO), + _CAP_DESCR(CAP_SYS_RESOURCE), + _CAP_DESCR(CAP_SYS_TIME), + _CAP_DESCR(CAP_SYS_TTY_CONFIG), + _CAP_DESCR(CAP_SYSLOG), + _CAP_DESCR(CAP_WAKE_ALARM) +}; + +static inline const char *cap_flag_to_str(cap_flag_value_t flag) +{ + if (flag == CAP_SET) { + return "*1*"; + } + return " 0 "; +} + +errno_t sss_log_caps_to_str(bool only_non_zero, char **_str) +{ + int ret; + char *str = NULL; + size_t i; + cap_t caps; + cap_flag_value_t effective, permitted, bounding; + + caps = cap_get_proc(); + if (caps == NULL) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, "cap_get_proc() failed: %d ('%s')\n", + ret, strerror(ret)); + return ret; + } + + for (i = 0; i < sizeof(_all_caps)/sizeof(cap_description); ++i) { + if (!CAP_IS_SUPPORTED(_all_caps[i].val)) { + continue; + } + ret = cap_get_flag(caps, _all_caps[i].val, CAP_EFFECTIVE, &effective); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, + "cap_get_flag(CAP_EFFECTIVE) failed: %d ('%s')\n", + ret, strerror(ret)); + goto done; + } + ret = cap_get_flag(caps, _all_caps[i].val, CAP_PERMITTED, &permitted); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, + "cap_get_flag(CAP_PERMITTED) failed: %d ('%s')\n", + ret, strerror(ret)); + goto done; + } + ret = cap_get_bound(_all_caps[i].val); + if (ret == 1) { + bounding = CAP_SET; + } else if (ret == 0) { + bounding = CAP_CLEAR; + } else { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, "cap_get_bound failed: %d ('%s')\n", + ret, strerror(ret)); + goto done; + } + + if (only_non_zero && (effective == CAP_CLEAR) && + (permitted == CAP_CLEAR) && (bounding == CAP_CLEAR)) { + continue; + } + + str = talloc_asprintf_append(str, + " %25s: effective = %s, permitted = %s, bounding = %s\n", + _all_caps[i].name, cap_flag_to_str(effective), + cap_flag_to_str(permitted), cap_flag_to_str(bounding)); + if (str == NULL) { + ret = ENOMEM; + goto done; + } + } + + ret = 0; + +done: + if (ret == 0) { + *_str = str; + } else { + talloc_free(str); + } + + if (cap_free(caps) == -1) { + DEBUG(SSSDBG_TRACE_FUNC, "cap_free() failed\n"); + } + + return ret; +} + +errno_t sss_drop_cap(cap_value_t cap) +{ + int ret; + + cap_t caps = cap_get_proc(); + if (caps == NULL) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, "cap_get_proc() failed: %d ('%s')\n", + ret, strerror(ret)); + return ret; + } + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_CLEAR) == -1) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, + "cap_set_flag(CAP_EFFECTIVE) failed: %d ('%s')\n", + ret, strerror(ret)); + goto done; + } + if (cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_CLEAR) == -1) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, + "cap_set_flag(CAP_PERMITTED) failed: %d ('%s')\n", + ret, strerror(ret)); + goto done; + } + if (cap_set_proc(caps) == -1) { + ret = errno; + DEBUG(SSSDBG_TRACE_FUNC, "cap_set_proc() failed: %d ('%s')\n", + ret, strerror(ret)); + goto done; + } + + ret = 0; + +done: + if (cap_free(caps) == -1) { + DEBUG(SSSDBG_TRACE_FUNC, "cap_free() failed\n"); + } + + return ret; +} diff --git a/src/util/server.c b/src/util/server.c index d2c4a5ab42b..c2d6944db88 100644 --- a/src/util/server.c +++ b/src/util/server.c @@ -770,6 +770,19 @@ int server_setup(const char *name, bool is_responder, void server_loop(struct main_context *main_ctx) { + char *caps; + int ret; + + ret = sss_log_caps_to_str(true, &caps); + if (ret != 0) { + DEBUG(SSSDBG_IMPORTANT_INFO, "Failed to log current capabilities\n"); + } else { + DEBUG(SSSDBG_IMPORTANT_INFO, + "Entering main loop with following capabilities:\n%s", + caps ? caps : " (nothing)\n"); + talloc_free(caps); + } + /* wait for events - this is where the server sits for most of its life */ tevent_loop_wait(main_ctx->event_ctx); diff --git a/src/util/util.h b/src/util/util.h index 1c2a42b09eb..bfef4bb8aba 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -751,6 +752,8 @@ errno_t switch_creds(TALLOC_CTX *mem_ctx, int num_gids, gid_t *gids, struct sss_creds **saved_creds); errno_t restore_creds(struct sss_creds *saved_creds); +errno_t sss_log_caps_to_str(bool only_non_zero, char **_str); +errno_t sss_drop_cap(cap_value_t cap); /* from sss_semanage.c */ /* Please note that libsemange relies on files and directories created with