diff --git a/src/btop.cpp b/src/btop.cpp index 208da4a..c6a7b9c 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -64,6 +64,7 @@ namespace Global { string fg_green = "\x1b[1;92m"; string fg_red = "\x1b[0;91m"; + uid_t real_uid, set_uid; fs::path self_path; @@ -298,6 +299,18 @@ namespace Runner { ~thread_lock() { if (status == 0) pthread_mutex_unlock(&pt_mutex); } }; + //* Wrapper for raising priviliges when using SUID bit + class gain_priv { + int status = -1; + public: + gain_priv() { + if (Global::real_uid != Global::set_uid) this->status = seteuid(Global::set_uid); + } + ~gain_priv() { + if (status == 0) status = seteuid(Global::real_uid); + } + }; + string output; string empty_bg; bool pause_output = false; @@ -385,6 +398,9 @@ namespace Runner { //? Atomic lock used for blocking non thread-safe actions in main thread atomic_lock lck(active); + //? Set effective user if SUID bit is set + gain_priv powers{}; + auto& conf = current_conf; //! DEBUG stats @@ -616,6 +632,17 @@ int main(int argc, char **argv) { Global::start_time = time_s(); + //? Save real and effective userid's and drop priviliges until needed if running with SUID bit set + Global::real_uid = getuid(); + Global::set_uid = geteuid(); + if (Global::real_uid != Global::set_uid) { + if (seteuid(Global::real_uid) != 0) { + Global::real_uid = Global::set_uid; + Global::exit_error_msg = "Failed to change effective user ID. Unset btop SUID bit to ensure security on this system. Quitting!"; + clean_quit(1); + } + } + //? Call argument parser if launched with arguments if (argc > 1) argumentParser(argc, argv); diff --git a/src/btop_config.cpp b/src/btop_config.cpp index ca3e66a..b0edcde 100644 --- a/src/btop_config.cpp +++ b/src/btop_config.cpp @@ -589,6 +589,7 @@ namespace Config { void write() { if (conf_file.empty() or not write_new) return; Logger::debug("Writing new config file"); + if (geteuid() != Global::real_uid and seteuid(Global::real_uid) != 0) return; std::ofstream cwrite(conf_file, std::ios::trunc); if (cwrite.good()) { cwrite << "#? Config file for btop v. " << Global::Version; diff --git a/src/btop_shared.hpp b/src/btop_shared.hpp index 832208c..b5eaf6b 100644 --- a/src/btop_shared.hpp +++ b/src/btop_shared.hpp @@ -27,6 +27,7 @@ tab-size = 4 #include #include #include +#include using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic, std::array, std::tuple; @@ -43,6 +44,7 @@ namespace Global { extern atomic resized; extern string overlay; extern string clock; + extern uid_t real_uid, set_uid; } namespace Runner { diff --git a/src/btop_tools.cpp b/src/btop_tools.cpp index 8778dbe..061e43f 100644 --- a/src/btop_tools.cpp +++ b/src/btop_tools.cpp @@ -406,6 +406,18 @@ namespace Logger { size_t loglevel; fs::path logfile; + //* Wrapper for lowering priviliges if using SUID bit and currently isn't using real userid + class lose_priv { + int status = -1; + public: + lose_priv() { + if (geteuid() != Global::real_uid) this->status = seteuid(Global::real_uid); + } + ~lose_priv() { + if (status == 0) status = seteuid(Global::set_uid); + } + }; + void set(const string& level) { loglevel = v_index(log_levels, level); } @@ -413,6 +425,7 @@ namespace Logger { void log_write(const size_t level, const string& msg) { if (loglevel < level or logfile.empty()) return; atomic_lock lck(busy, true); + lose_priv neutered{}; std::error_code ec; try { if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {