Add option to drop Linux capabilities

Conflicts with setuid support, but that is commonly not enabled.
This commit is contained in:
Christian Göttsche 2020-09-02 14:39:25 +02:00 committed by BenBE
parent d72b0a682e
commit f4404effa4
4 changed files with 138 additions and 8 deletions

View File

@ -59,15 +59,15 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install Dependencies - name: Install Dependencies
run: sudo apt-get install libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev run: sudo apt-get install libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors --with-capabilities
- name: Build - name: Build
run: make -k run: make -k
- name: Distcheck - name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors' run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors --with-capabilities'
build-ubuntu-latest-full-featured-clang: build-ubuntu-latest-full-featured-clang:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -81,15 +81,15 @@ jobs:
sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
sudo apt-get update -q sudo apt-get update -q
- name: Install Dependencies - name: Install Dependencies
run: sudo apt-get install clang-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev run: sudo apt-get install clang-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors --with-capabilities
- name: Build - name: Build
run: make -k run: make -k
- name: Distcheck - name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors' run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors --with-capabilities'
build-ubuntu-latest-clang-analyzer: build-ubuntu-latest-clang-analyzer:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -103,11 +103,11 @@ jobs:
sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
sudo apt-get update -q sudo apt-get update -q
- name: Install Dependencies - name: Install Dependencies
run: sudo apt-get install clang-11 clang-tools-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev run: sudo apt-get install clang-11 clang-tools-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev
- name: Bootstrap - name: Bootstrap
run: ./autogen.sh run: ./autogen.sh
- name: Configure - name: Configure
run: scan-build-11 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors run: scan-build-11 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors --with-capabilities
- name: Build - name: Build
run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)" run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)"

View File

@ -307,6 +307,13 @@ if test "x$enable_setuid" = xyes; then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.]) AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi fi
AC_ARG_WITH(capabilities, [AS_HELP_STRING([--with-capabilities], [Enable option to drop Linux capabilities via libcap])],, with_capabilities="no")
if test "x$with_capabilities" = xyes
then
AC_CHECK_LIB([cap], [cap_init], [], [missing_libraries="$missing_libraries libcap"])
AC_CHECK_HEADERS([sys/capability.h], [:], [missing_headers="$missing_headers $ac_header"])
fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no") AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes; then if test "x$enable_delayacct" = xyes; then
m4_ifdef([PKG_PROG_PKG_CONFIG], [ m4_ifdef([PKG_PROG_PKG_CONFIG], [
@ -408,6 +415,7 @@ AC_MSG_RESULT([
(Linux) affinity: $enable_linux_affinity (Linux) affinity: $enable_linux_affinity
(Linux) delay accounting: $enable_delayacct (Linux) delay accounting: $enable_delayacct
(Linux) sensors: $with_sensors (Linux) sensors: $with_sensors
(Linux) capabilities: $with_capabilities
unicode: $enable_unicode unicode: $enable_unicode
hwloc: $enable_hwloc hwloc: $enable_hwloc
setuid: $enable_setuid setuid: $enable_setuid

View File

@ -63,6 +63,14 @@ requesting a sort order with -s.
.TP .TP
\fB\-H \-\-highlight-changes=DELAY\fR \fB\-H \-\-highlight-changes=DELAY\fR
Highlight new and old processes Highlight new and old processes
.TP
\fB \-\-drop-capabilities[=none|basic|strict]\fR
Linux only; requires libcap support.
.br
Drop unneeded Linux capabilities.
In strict mode features like killing, changing process priorities, and reading
process delay accounting information will not work, due to less capabilities
held.
.SH "INTERACTIVE COMMANDS" .SH "INTERACTIVE COMMANDS"
The following commands are supported while in The following commands are supported while in
.BR htop : .BR htop :

114
htop.c
View File

@ -8,6 +8,7 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep #include "config.h" // IWYU pragma: keep
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <getopt.h> #include <getopt.h>
#include <locale.h> #include <locale.h>
#include <stdbool.h> #include <stdbool.h>
@ -34,6 +35,19 @@ in the source distribution for its full text.
#include "UsersTable.h" #include "UsersTable.h"
#include "XUtils.h" #include "XUtils.h"
#ifdef HAVE_LIBCAP
#include <sys/capability.h>
#endif
#ifdef HAVE_LIBCAP
enum CapMode {
CAP_MODE_NONE,
CAP_MODE_BASIC,
CAP_MODE_STRICT
};
#endif
static void printVersionFlag(void) { static void printVersionFlag(void) {
fputs(PACKAGE " " VERSION "\n", stdout); fputs(PACKAGE " " VERSION "\n", stdout);
} }
@ -46,6 +60,12 @@ static void printHelpFlag(void) {
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n" "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-F --filter=FILTER Show only the commands matching the given filter\n" "-F --filter=FILTER Show only the commands matching the given filter\n"
"-h --help Print this help screen\n" "-h --help Print this help screen\n"
#ifdef HAVE_LIBCAP
" --drop-capabilities[=none|basic|strict] Drop Linux capabilities when running as root\n"
" none - do not drop any capabilities\n"
" basic (default) - drop all capabilities not needed by htop\n"
" strict - drop all capabilities except those needed for core functionality\n"
#endif
"-H --highlight-changes[=DELAY] Highlight new and old processes\n" "-H --highlight-changes[=DELAY] Highlight new and old processes\n"
"-M --no-mouse Disable the mouse\n" "-M --no-mouse Disable the mouse\n"
"-p --pid=PID[,PID,PID...] Show only the given PIDs\n" "-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
@ -75,6 +95,9 @@ typedef struct CommandLineSettings_ {
bool allowUnicode; bool allowUnicode;
bool highlightChanges; bool highlightChanges;
int highlightDelaySecs; int highlightDelaySecs;
#ifdef HAVE_LIBCAP
enum CapMode capabilitiesMode;
#endif
} CommandLineSettings; } CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) { static CommandLineSettings parseArguments(int argc, char** argv) {
@ -91,6 +114,9 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.allowUnicode = true, .allowUnicode = true,
.highlightChanges = false, .highlightChanges = false,
.highlightDelaySecs = -1, .highlightDelaySecs = -1,
#ifdef HAVE_LIBCAP
.capabilitiesMode = (geteuid() == 0) ? CAP_MODE_BASIC : CAP_MODE_NONE,
#endif
}; };
const struct option long_opts[] = const struct option long_opts[] =
@ -108,6 +134,9 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"pid", required_argument, 0, 'p'}, {"pid", required_argument, 0, 'p'},
{"filter", required_argument, 0, 'F'}, {"filter", required_argument, 0, 'F'},
{"highlight-changes", optional_argument, 0, 'H'}, {"highlight-changes", optional_argument, 0, 'H'},
#ifdef HAVE_LIBCAP
{"drop-capabilities", optional_argument, 0, 128},
#endif
{0,0,0,0} {0,0,0,0}
}; };
@ -225,6 +254,27 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
flags.highlightChanges = true; flags.highlightChanges = true;
break; break;
} }
#ifdef HAVE_LIBCAP
case 128: {
const char* mode = optarg;
if (!mode && optind < argc && argv[optind] != NULL &&
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
mode = argv[optind++];
}
if (!mode || String_eq(mode, "basic")) {
flags.capabilitiesMode = CAP_MODE_BASIC;
} else if (String_eq(mode, "none")) {
flags.capabilitiesMode = CAP_MODE_NONE;
} else if (String_eq(mode, "strict")) {
flags.capabilitiesMode = CAP_MODE_STRICT;
} else {
fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
exit(1);
}
break;
}
#endif
default: default:
exit(1); exit(1);
} }
@ -254,6 +304,65 @@ static void setCommFilter(State* state, char** commFilter) {
*commFilter = NULL; *commFilter = NULL;
} }
#ifdef HAVE_LIBCAP
static int dropCapabilities(enum CapMode mode) {
if (mode == CAP_MODE_NONE)
return 0;
/* capabilities we keep to operate */
const cap_value_t keepcapsStrict[] = {
CAP_DAC_READ_SEARCH,
CAP_SYS_PTRACE,
};
const cap_value_t keepcapsBasic[] = {
CAP_DAC_READ_SEARCH, /* read non world-readable process files of other users, like /proc/[pid]/io */
CAP_KILL, /* send signals to processes of other users */
CAP_SYS_NICE, /* lower process nice value / change nice value for arbitrary processes */
CAP_SYS_PTRACE, /* read /proc/[pid]/exe */
#ifdef HAVE_DELAYACCT
CAP_NET_ADMIN, /* communicate over netlink socket for delay accounting */
#endif
};
const cap_value_t* const keepcaps = (mode == CAP_MODE_BASIC) ? keepcapsBasic : keepcapsStrict;
const int ncap = (mode == CAP_MODE_BASIC) ? ARRAYSIZE(keepcapsBasic) : ARRAYSIZE(keepcapsStrict);
cap_t caps = cap_init();
if (caps == NULL) {
fprintf(stderr, "Error: can not initialize capabilities: %s\n", strerror(errno));
return -1;
}
if (cap_clear(caps) < 0) {
fprintf(stderr, "Error: can not clear capabilities: %s\n", strerror(errno));
cap_free(caps);
return -1;
}
if (cap_set_flag(caps, CAP_PERMITTED, ncap, keepcaps, CAP_SET) < 0) {
fprintf(stderr, "Error: can not set permitted capabilities: %s\n", strerror(errno));
cap_free(caps);
return -1;
}
if (cap_set_flag(caps, CAP_EFFECTIVE, ncap, keepcaps, CAP_SET) < 0) {
fprintf(stderr, "Error: can not set effective capabilities: %s\n", strerror(errno));
cap_free(caps);
return -1;
}
if (cap_set_proc(caps) < 0) {
fprintf(stderr, "Error: can not set process capabilities: %s\n", strerror(errno));
cap_free(caps);
return -1;
}
cap_free(caps);
return 0;
}
#endif
int main(int argc, char** argv) { int main(int argc, char** argv) {
/* initialize locale */ /* initialize locale */
@ -265,6 +374,11 @@ int main(int argc, char** argv) {
CommandLineSettings flags = parseArguments(argc, argv); CommandLineSettings flags = parseArguments(argc, argv);
#ifdef HAVE_LIBCAP
if (dropCapabilities(flags.capabilitiesMode) < 0)
exit(1);
#endif
Platform_init(); Platform_init();
Process_setupColumnWidths(); Process_setupColumnWidths();