From ea1313d80d5ee1623b00c8cdf6e7ff8a7e14c2ae Mon Sep 17 00:00:00 2001
From: Mykyta Holubakha <hilobakho@gmail.com>
Date: Thu, 12 Jan 2017 04:25:03 +0200
Subject: Keep CAP_SYS_PTRACE with suid binary

---
 sway/main.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/sway/main.c b/sway/main.c
index e8a02e7a..6c74aab2 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -10,6 +10,9 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <sys/capability.h>
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
 #include "sway/extensions.h"
 #include "sway/layout.h"
 #include "sway/config.h"
@@ -289,6 +292,18 @@ int main(int argc, char **argv) {
 		return 0;
 	}
 
+#ifdef __linux__
+	bool suid = false;
+	if (getuid() != geteuid() || getgid() != getegid()) {
+		// Retain capabilities after setuid()
+		if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
+			sway_log(L_ERROR, "Cannot keep caps after setuid()");
+			exit(EXIT_FAILURE);
+		}
+		suid = true;
+	}
+#endif
+
 	// we need to setup logging before wlc_init in case it fails.
 	if (debug) {
 		init_log(L_DEBUG);
@@ -311,6 +326,19 @@ int main(int argc, char **argv) {
 	}
 	register_extensions();
 
+#ifdef __linux__
+	if (suid) {
+		// Drop every cap except CAP_SYS_PTRACE
+		cap_t caps = cap_init();
+		cap_value_t keep = CAP_SYS_PTRACE;
+		if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
+			cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
+			cap_set_proc(caps)) {
+			sway_log(L_ERROR, "Failed to drop extra capabilities");
+			exit(EXIT_FAILURE);
+		}
+	}
+#endif
 	// handle SIGTERM signals
 	signal(SIGTERM, sig_handler);
 
-- 
cgit v1.2.3