summaryrefslogtreecommitdiffstats
path: root/htop.c
diff options
context:
space:
mode:
authorChristian Göttsche <cgzones@googlemail.com>2020-09-02 14:39:25 +0200
committerBenBE <BenBE@geshi.org>2021-01-11 20:19:51 +0100
commitf4404effa45ce378a8a72f2fff9641c7d7a0cc6f (patch)
tree742f042db6c21e5c2203bceb4a88c5b50bf744a8 /htop.c
parentd72b0a682ecad0a8d5793022733447e855a61798 (diff)
Add option to drop Linux capabilities
Conflicts with setuid support, but that is commonly not enabled.
Diffstat (limited to 'htop.c')
-rw-r--r--htop.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/htop.c b/htop.c
index e59c0282..ff5dc875 100644
--- a/htop.c
+++ b/htop.c
@@ -8,6 +8,7 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep
#include <assert.h>
+#include <errno.h>
#include <getopt.h>
#include <locale.h>
#include <stdbool.h>
@@ -34,6 +35,19 @@ in the source distribution for its full text.
#include "UsersTable.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) {
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"
"-F --filter=FILTER Show only the commands matching the given filter\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"
"-M --no-mouse Disable the mouse\n"
"-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
@@ -75,6 +95,9 @@ typedef struct CommandLineSettings_ {
bool allowUnicode;
bool highlightChanges;
int highlightDelaySecs;
+#ifdef HAVE_LIBCAP
+ enum CapMode capabilitiesMode;
+#endif
} CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) {
@@ -91,6 +114,9 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.allowUnicode = true,
.highlightChanges = false,
.highlightDelaySecs = -1,
+#ifdef HAVE_LIBCAP
+ .capabilitiesMode = (geteuid() == 0) ? CAP_MODE_BASIC : CAP_MODE_NONE,
+#endif
};
const struct option long_opts[] =
@@ -108,6 +134,9 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"pid", required_argument, 0, 'p'},
{"filter", required_argument, 0, 'F'},
{"highlight-changes", optional_argument, 0, 'H'},
+#ifdef HAVE_LIBCAP
+ {"drop-capabilities", optional_argument, 0, 128},
+#endif
{0,0,0,0}
};
@@ -225,6 +254,27 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
flags.highlightChanges = true;
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:
exit(1);
}
@@ -254,6 +304,65 @@ static void setCommFilter(State* state, char** commFilter) {
*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) {
/* initialize locale */
@@ -265,6 +374,11 @@ int main(int argc, char** argv) {
CommandLineSettings flags = parseArguments(argc, argv);
+#ifdef HAVE_LIBCAP
+ if (dropCapabilities(flags.capabilitiesMode) < 0)
+ exit(1);
+#endif
+
Platform_init();
Process_setupColumnWidths();

© 2014-2024 Faster IT GmbH | imprint | privacy policy