From 7e55a251d6ef8e9676572fb55df47f19ae4b2d09 Mon Sep 17 00:00:00 2001 From: Ed Wildgoose <ed+git@wildgooses.com> Date: Fri, 26 Jul 2024 17:12:41 +0100 Subject: [PATCH 1/5] Propogate capabilities to child process --- c_src/exec_impl.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/c_src/exec_impl.cpp b/c_src/exec_impl.cpp index 2f1b199..7ee8e84 100644 --- a/c_src/exec_impl.cpp +++ b/c_src/exec_impl.cpp @@ -364,6 +364,55 @@ static int getpty(int& fdmp, ei::StringBuffer<128>& err) { return 0; } +//------------------------------------------------------------------------------ +void propagate_caps(ei::StringBuffer<128>& err) +{ +#ifdef HAVE_CAP + cap_t caps; + cap_value_t cap_list[CAP_LAST_CAP + 1]; + int num_caps=0; + + // Get the current process capabilities + caps = cap_get_proc(); + if (caps == NULL) { + err.write("error %d on cap_get_proc: %s\n", errno, strerror(errno)); + } + + // Get the bounding set of capabilities + cap_flag_value_t cap_value; + for (cap_value_t i = 0; i <= CAP_LAST_CAP; ++i) { + if (cap_get_flag(caps, i, CAP_PERMITTED, &cap_value) == -1) { + err.write("error %d on cap_get_flag: %s\n", errno, strerror(errno)); + } + if (cap_value == CAP_SET) { + cap_list[num_caps++] = i; + } + } + + // Set inheritable capabilities to all permitted capabilities + if (cap_set_flag(caps, CAP_INHERITABLE, num_caps, cap_list, CAP_SET) == -1) { + err.write("error %d on cap_set_flag: %s\n", errno, strerror(errno)); + } + + // Apply inheritable capabilities + if (cap_set_proc(caps) == -1) { + err.write("error %d on cap_set_proc: %s\n", errno, strerror(errno)); + } + + // Set ambient capabilities + for (size_t i = 0; i < num_caps; i++) { + if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap_list[i], 0, 0) == -1) { + err.write("error %d on PR_CAP_AMBIENT_RAISE: %s\n", errno, strerror(errno)); + } + } + + // Free the capability state + if (cap_free(caps) == -1) { + err.write("error %d on cap_free: %s\n", errno, strerror(errno)); + } +#endif +} + //------------------------------------------------------------------------------ pid_t start_child(CmdOptions& op, std::string& error) { @@ -489,7 +538,6 @@ pid_t start_child(CmdOptions& op, std::string& error) } else if (pid == 0) { // I am the child int r; - if (op.pty()) { int fds; char pts_name[256]; @@ -647,6 +695,8 @@ pid_t start_child(CmdOptions& op, std::string& error) } */ + propagate_caps(err); + const char* executable = op.executable().empty() ? argv[0] : op.executable().c_str(); From 29ed3bd8acccc7e4a2282fe963f84af2c4ea12d5 Mon Sep 17 00:00:00 2001 From: Serge Aleynikov <saleyn@gmail.com> Date: Sun, 1 Sep 2024 15:12:37 -0400 Subject: [PATCH 2/5] Add error handling --- c_src/exec_impl.cpp | 56 ++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/c_src/exec_impl.cpp b/c_src/exec_impl.cpp index 7ee8e84..f8a4410 100644 --- a/c_src/exec_impl.cpp +++ b/c_src/exec_impl.cpp @@ -364,53 +364,64 @@ static int getpty(int& fdmp, ei::StringBuffer<128>& err) { return 0; } +struct Caps { + Caps() : m_caps(cap_get_proc()) {} + ~Caps() { if (m_caps) cap_free(m_caps); } + + bool valid() const { return !!m_caps; } + cap_t value() { return m_caps; } + void add(cap_value_t cap) { m_cap_list.push_back(cap); } + cap_value_t* list() { return m_cap_list.data(); } + size_t size() const { return m_cap_list.size(); } +private: + cap_t m_caps; + std::vector<cap_value_t> m_cap_list; +}; + //------------------------------------------------------------------------------ -void propagate_caps(ei::StringBuffer<128>& err) +bool propagate_caps(ei::StringBuffer<128>& err) { #ifdef HAVE_CAP - cap_t caps; - cap_value_t cap_list[CAP_LAST_CAP + 1]; - int num_caps=0; + Caps caps; // Get the current process capabilities - caps = cap_get_proc(); - if (caps == NULL) { + if (!caps.valid()) { err.write("error %d on cap_get_proc: %s\n", errno, strerror(errno)); + return false; } // Get the bounding set of capabilities cap_flag_value_t cap_value; for (cap_value_t i = 0; i <= CAP_LAST_CAP; ++i) { - if (cap_get_flag(caps, i, CAP_PERMITTED, &cap_value) == -1) { + if (cap_get_flag(caps.value(), i, CAP_PERMITTED, &cap_value) < 0) { err.write("error %d on cap_get_flag: %s\n", errno, strerror(errno)); + return false; } - if (cap_value == CAP_SET) { - cap_list[num_caps++] = i; - } + if (cap_value == CAP_SET) + cap_list.add(i); } // Set inheritable capabilities to all permitted capabilities - if (cap_set_flag(caps, CAP_INHERITABLE, num_caps, cap_list, CAP_SET) == -1) { + if (cap_set_flag(caps.value(), CAP_INHERITABLE, caps.size(), caps.list(), CAP_SET) < 0) { err.write("error %d on cap_set_flag: %s\n", errno, strerror(errno)); + return false; } // Apply inheritable capabilities - if (cap_set_proc(caps) == -1) { + if (cap_set_proc(caps.value()) < 0) { err.write("error %d on cap_set_proc: %s\n", errno, strerror(errno)); + return false; } // Set ambient capabilities - for (size_t i = 0; i < num_caps; i++) { - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap_list[i], 0, 0) == -1) { - err.write("error %d on PR_CAP_AMBIENT_RAISE: %s\n", errno, strerror(errno)); + for (size_t i = 0; i < caps.size(); i++) { + if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, caps.list()[i], 0, 0) < 0) { + err.write("error %d on PR_CAP_AMBIENT_RAISE[%d]: %s\n", errno, i, strerror(errno)); + return false; } } - - // Free the capability state - if (cap_free(caps) == -1) { - err.write("error %d on cap_free: %s\n", errno, strerror(errno)); - } #endif + return true; } //------------------------------------------------------------------------------ @@ -695,7 +706,10 @@ pid_t start_child(CmdOptions& op, std::string& error) } */ - propagate_caps(err); + if (!propagate_caps(err)) { + perror(err.c_str()); + exit(EXIT_FAILURE); + } const char* executable = op.executable().empty() ? argv[0] : op.executable().c_str(); From 3a46e2313a5e359b645783b6afbfc110c52cfef7 Mon Sep 17 00:00:00 2001 From: Serge Aleynikov <saleyn@gmail.com> Date: Sun, 1 Sep 2024 15:19:29 -0400 Subject: [PATCH 3/5] Fix compilation --- c_src/exec_impl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/c_src/exec_impl.cpp b/c_src/exec_impl.cpp index f8a4410..b8687c9 100644 --- a/c_src/exec_impl.cpp +++ b/c_src/exec_impl.cpp @@ -364,6 +364,7 @@ static int getpty(int& fdmp, ei::StringBuffer<128>& err) { return 0; } +#ifdef HAVE_CAP struct Caps { Caps() : m_caps(cap_get_proc()) {} ~Caps() { if (m_caps) cap_free(m_caps); } @@ -377,6 +378,7 @@ struct Caps { cap_t m_caps; std::vector<cap_value_t> m_cap_list; }; +#endif //------------------------------------------------------------------------------ bool propagate_caps(ei::StringBuffer<128>& err) From 4bf68405361b2b36b30e7af66de7c0b95a19bed5 Mon Sep 17 00:00:00 2001 From: Serge Aleynikov <saleyn@gmail.com> Date: Sun, 1 Sep 2024 15:56:14 -0400 Subject: [PATCH 4/5] Add names of capabilities --- c_src/exec_impl.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/c_src/exec_impl.cpp b/c_src/exec_impl.cpp index b8687c9..96e062c 100644 --- a/c_src/exec_impl.cpp +++ b/c_src/exec_impl.cpp @@ -365,6 +365,47 @@ static int getpty(int& fdmp, ei::StringBuffer<128>& err) { } #ifdef HAVE_CAP +// Placeholder for atom declaration +// sed -n 's/^#define \(CAP_.*\) .*/\1/p' /usr/include/linux/capability.h | tr A-Z a-z | sed 's/cap_//' +// +const char *cap_name[CAP_LAST_CAP+1] = { + "chown", + "dac_override", + "dac_read_search", + "fowner", + "fsetid", + "kill", + "setgid", + "setuid", + "setpcap", + "linux_immutable", + "net_bind_service", + "net_broadcast", + "net_admin", + "net_raw", + "ipc_lock", + "ipc_owner", + "sys_module", + "sys_rawio", + "sys_chroot", + "sys_ptrace", + "sys_pacct", + "sys_admin", + "sys_boot", + "sys_nice", + "sys_resource", + "sys_time", + "sys_tty_config", + "mknod", + "lease", + "audit_write", + "audit_control", + "setfcap", + "mac_override", + "mac_admin", + "syslog" +}; + struct Caps { Caps() : m_caps(cap_get_proc()) {} ~Caps() { if (m_caps) cap_free(m_caps); } From 7188da8d1b7685c35275f44f365289b9378dcbb9 Mon Sep 17 00:00:00 2001 From: Serge Aleynikov <saleyn@gmail.com> Date: Sun, 1 Sep 2024 21:52:00 -0400 Subject: [PATCH 5/5] Update capability names --- c_src/exec_impl.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/c_src/exec_impl.cpp b/c_src/exec_impl.cpp index 96e062c..0f2977b 100644 --- a/c_src/exec_impl.cpp +++ b/c_src/exec_impl.cpp @@ -365,7 +365,7 @@ static int getpty(int& fdmp, ei::StringBuffer<128>& err) { } #ifdef HAVE_CAP -// Placeholder for atom declaration +// Placeholder for decoding capability names passed in the "run child" command // sed -n 's/^#define \(CAP_.*\) .*/\1/p' /usr/include/linux/capability.h | tr A-Z a-z | sed 's/cap_//' // const char *cap_name[CAP_LAST_CAP+1] = { @@ -403,7 +403,9 @@ const char *cap_name[CAP_LAST_CAP+1] = { "setfcap", "mac_override", "mac_admin", - "syslog" + "syslog", + "wake_alarm", + "block_suspend", }; struct Caps {