Skip to content

Commit

Permalink
Add capabilities support on Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
q66 committed Oct 20, 2024
1 parent 927bec0 commit 80ea7ff
Show file tree
Hide file tree
Showing 24 changed files with 265 additions and 22 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/meson_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ jobs:
if: ${{ matrix.arch == 'amd64' }}
run: |
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install g++ meson m4 -y
DEBIAN_FRONTEND=noninteractive apt-get install g++ meson m4 libcap-dev file -y
- name: Getting depends (i386)
if: ${{ matrix.arch == 'i386' }}
run: |
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install meson m4:i386 g++:i386 -y
DEBIAN_FRONTEND=noninteractive apt-get install meson m4:i386 g++:i386 libcap-dev:i386 -y
- name: Setup
run: meson setup -Dunit-tests=true -Digr-tests=true dirbuild
- name: Build
Expand Down Expand Up @@ -130,7 +130,7 @@ jobs:
- name: Getting depends
run: |
apk update
apk add meson g++ m4
apk add meson g++ m4 libcap-dev
- name: Setup
run: meson setup -Dunit-tests=true -Digr-tests=true dirbuild
- name: Build
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/regular_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ jobs:
if: ${{ matrix.arch == 'amd64' }}
run: |
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install g++ make m4 file -y
DEBIAN_FRONTEND=noninteractive apt-get install g++ make m4 libcap-dev file -y
- name: Getting depends (i386)
if: ${{ matrix.arch == 'i386' }}
run: |
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install gcc:i386 make:i386 m4:i386 g++:i386 file -y
DEBIAN_FRONTEND=noninteractive apt-get install gcc:i386 make:i386 m4:i386 g++:i386 libcap-dev:i386 file -y
- name: Print g++ architecture
run: g++ -dumpmachine
- name: Build
Expand Down Expand Up @@ -123,7 +123,7 @@ jobs:
- name: Getting depends
run: |
apk update
apk add make g++ m4 file
apk add make g++ m4 file libcap-dev
- name: Print g++ architecture
run: g++ -dumpmachine
- name: Build
Expand Down
1 change: 1 addition & 0 deletions build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ includes/mconfig.h: ../mconfig tools/mconfig-gen.cc version.conf
DEFAULT_START_TIMEOUT=$(DEFAULT_START_TIMEOUT) \
DEFAULT_STOP_TIMEOUT=$(DEFAULT_STOP_TIMEOUT) \
$(if $(SUPPORT_CGROUPS),SUPPORT_CGROUPS=$(SUPPORT_CGROUPS),) \
$(if $(SUPPORT_CAPABILITIES),SUPPORT_CAPABILITIES=$(SUPPORT_CAPABILITIES),) \
$(if $(USE_UTMPX),USE_UTMPX=$(USE_UTMPX),) \
$(if $(USE_INITGROUPS),USE_INITGROUPS=$(USE_INITGROUPS),) > includes/mconfig.h

Expand Down
1 change: 1 addition & 0 deletions build/mconfig.mesontemplate
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#mesondefine USE_UTMPX
#mesondefine USE_INITGROUPS
#mesondefine SUPPORT_CGROUPS
#mesondefine SUPPORT_CAPABILITIES
#mesondefine DEFAULT_AUTO_RESTART
#mesondefine DEFAULT_START_TIMEOUT
#mesondefine DEFAULT_STOP_TIMEOUT
Expand Down
3 changes: 3 additions & 0 deletions build/tools/mconfig-gen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ int main(int argc, char **argv)
if (vars.find("SUPPORT_CGROUPS") != vars.end()) {
cout << "#define SUPPORT_CGROUPS " << vars["SUPPORT_CGROUPS"] << "\n";
}
if (vars.find("SUPPORT_CAPABILITIES") != vars.end()) {
cout << "#define SUPPORT_CAPABILITIES " << vars["SUPPORT_CAPABILITIES"] << "\n";
}
if (vars.find("DEFAULT_AUTO_RESTART") != vars.end()) {
cout << "#define DEFAULT_AUTO_RESTART " << vars["DEFAULT_AUTO_RESTART"] << "\n";
}
Expand Down
1 change: 1 addition & 0 deletions configs/mconfig.Linux
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ TEST_LDFLAGS=$(TEST_LDFLAGS_BASE) $(TEST_CXXFLAGS)
# Features.

SUPPORT_CGROUPS=1
SUPPORT_CAPABILITIES=1


# Service defaults.
Expand Down
1 change: 1 addition & 0 deletions configs/mconfig.Linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ FEATURE_SETTINGS=$(
echo "# Feature settings"
echo ""
echo "SUPPORT_CGROUPS=1"
echo "SUPPORT_CAPABILITIES=1"
)

SERVICE_DEFAULTS=$(
Expand Down
6 changes: 6 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ for var in PREFIX \
SHUTDOWN_PREFIX \
BUILD_SHUTDOWN \
SUPPORT_CGROUPS \
SUPPORT_CAPABILITIES \
USE_UTMPX \
USE_INITGROUPS \
SYSCONTROLSOCKET \
Expand Down Expand Up @@ -239,6 +240,8 @@ for arg in "$@"; do
--disable-shutdown|--enable-shutdown=no) BUILD_SHUTDOWN=no ;;
--enable-cgroups|--enable-cgroups=yes) SUPPORT_CGROUPS=1 ;;
--disable-cgroups|--enable-cgroups=no) SUPPORT_CGROUPS=0 ;;
--enable-capabilities|--enable-capabilities=yes) SUPPORT_CAPABILITIES=1 ;;
--disable-capabilities|--enable-capabilities=no) SUPPORT_CAPABILITIES=0 ;;
--enable-utmpx|--enable-utmpx=yes) USE_UTMPX=1 ;;
--disable-utmpx|--enable-utmpx=no) USE_UTMPX=0 ;;
--enable-initgroups|--enable-initgroups=yes) USE_INITGROUPS=1 ;;
Expand Down Expand Up @@ -278,10 +281,12 @@ done
if [ "$PLATFORM" = "Linux" ]; then
: "${BUILD_SHUTDOWN:="yes"}"
: "${SUPPORT_CGROUPS:="1"}"
: "${SUPPORT_CAPABILITIES:="1"}"
: "${SYSCONTROLSOCKET:="/run/dinitctl"}"
else
: "${BUILD_SHUTDOWN:="no"}"
: "${SUPPORT_CGROUPS:="0"}"
: "${SUPPORT_CAPABILITIES:="0"}"
: "${SYSCONTROLSOCKET:="/var/run/dinitctl"}"
fi

Expand Down Expand Up @@ -467,6 +472,7 @@ STRIPOPTS=$STRIPOPTS
# Feature settings
SUPPORT_CGROUPS=$SUPPORT_CGROUPS
USE_INITGROUPS=$USE_INITGROUPS
SUPPORT_CAPABILITIES=$SUPPORT_CAPABILITIES
# Optional settings
SHUTDOWN_PREFIX=${SHUTDOWN_PREFIX:-}
Expand Down
6 changes: 6 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ igr_tests = get_option('igr-tests')
fuzzer = get_option('fuzzer')
man_pages = get_option('man-pages')
support_cgroups = get_option('support-cgroups')
support_capabilities = get_option('support-capabilities')
use_utmpx = get_option('use-utmpx')
use_initgroups = get_option('use-initgroups')
default_auto_restart = get_option('default-auto-restart')
Expand All @@ -56,6 +57,8 @@ if platform == 'freebsd' and compiler.has_link_argument('-lrt')
add_project_link_arguments('-lrt', language : 'cpp')
endif

libcap_dep = dependency('libcap', required: support_capabilities)

## Prepare mconfig.h
mconfig_data.set_quoted('DINIT_VERSION', version)
mconfig_data.set_quoted('SYSCONTROLSOCKET', dinit_control_socket_path)
Expand All @@ -68,6 +71,9 @@ mconfig_data.set10('USE_INITGROUPS', use_initgroups)
if support_cgroups.auto() and platform == 'linux' or support_cgroups.enabled()
mconfig_data.set('SUPPORT_CGROUPS', '1')
endif
if libcap_dep.found()
mconfig_data.set('SUPPORT_CAPABILITIES', '1')
endif
if use_utmpx.enabled() or (use_utmpx.auto() and compiler.has_header_symbol('utmpx.h', '_PATH_UTMPX') and
compiler.has_header_symbol('utmpx.h', '_PATH_WTMPX'))
mconfig_data.set('USE_UTMPX', '1')
Expand Down
8 changes: 7 additions & 1 deletion meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ option(
'support-cgroups',
type : 'feature',
value : 'auto',
description : 'Enable Cgroups supprot.'
description : 'Enable Cgroups support.'
)
option(
'support-capabilities',
type : 'feature',
value : 'auto',
description : 'Enable capabilities support.'
)
option(
'build-shutdown',
Expand Down
4 changes: 4 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ ifeq ($(BUILD_SHUTDOWN),yes)
SHUTDOWN=$(SHUTDOWN_PREFIX)shutdown
endif

ifeq ($(SUPPORT_CAPABILITIES),1)
ALL_LDFLAGS+=-lcap
endif

dinit_objects = dinit.o load-service.o service.o proc-service.o baseproc-service.o control.o dinit-log.o \
dinit-main.o run-child-proc.o options-processing.o dinit-env.o settings.o

Expand Down
5 changes: 5 additions & 0 deletions src/baseproc-service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ bool base_process_service::start_ps_process(const std::vector<const char *> &cmd
#if SUPPORT_CGROUPS
run_params.run_in_cgroup = run_in_cgroup.c_str();
#endif
#if SUPPORT_CAPABILITIES
run_params.cap_iab = cap_iab;
run_params.secbits = secbits;
run_params.no_new_privs = onstart_flags.no_new_privs;
#endif
run_child_proc(run_params);
}
else {
Expand Down
117 changes: 109 additions & 8 deletions src/includes/load-service.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
#include <service-constants.h>
#include <mconfig.h>

#if SUPPORT_CAPABILITIES
#include <sys/capability.h>
#endif

struct service_flags_t
{
// on-start flags:
Expand All @@ -42,14 +46,45 @@ struct service_flags_t
bool signal_process_only : 1; // signal the session process, not the whole group
bool always_chain : 1; // always start chain-to service on exit
bool kill_all_on_stop : 1; // kill all other processes before stopping this service
bool no_new_privs : 1; // set PR_SET_NO_NEW_PRIVS

service_flags_t() noexcept : rw_ready(false), log_ready(false),
runs_on_console(false), starts_on_console(false), shares_console(false), unmask_intr(false),
pass_cs_fd(false), start_interruptible(false), skippable(false), signal_process_only(false),
always_chain(false), kill_all_on_stop(false)
always_chain(false), kill_all_on_stop(false), no_new_privs(false)
{
}
};

#if SUPPORT_CAPABILITIES
struct secure_bits_t
{
bool keep_caps : 1;
bool keep_caps_locked : 1;
bool no_setuid_fixup : 1;
bool no_setuid_fixup_locked : 1;
bool noroot : 1;
bool noroot_locked : 1;

secure_bits_t() noexcept : keep_caps(false), keep_caps_locked(false),
no_setuid_fixup(false), no_setuid_fixup_locked(false),
noroot(false), noroot_locked(false)
{
}

unsigned int get() const noexcept {
unsigned int r = 0;
// as referenced in uapi
if (noroot) r |= 1 << 0;
if (noroot_locked) r |= 1 << 1;
if (no_setuid_fixup) r |= 1 << 2;
if (no_setuid_fixup_locked) r |= 1 << 3;
if (keep_caps) r |= 1 << 4;
if (keep_caps_locked) r |= 1 << 5;
return r;
}
};
#endif

// Resource limits for a particular service & particular resource
struct service_rlimits
Expand Down Expand Up @@ -230,7 +265,11 @@ enum class setting_id_t {
SETTING_RLIMIT_NOFILE, SETTING_RLIMIT_CORE, SETTING_RLIMIT_DATA, SETTING_RLIMIT_ADDRSPACE,
// Possibly unsupported depending on platform/build options:
#if SUPPORT_CGROUPS
RUN_IN_CGROUP
RUN_IN_CGROUP,
#endif
#if SUPPORT_CAPABILITIES
CAPABILITIES,
SECURE_BITS,
#endif
};

Expand Down Expand Up @@ -445,7 +484,8 @@ inline string read_config_name(string_iterator & i, string_iterator end, bool en
// part will be added as [start,end). May be null.
inline void read_setting_value(std::string &setting_val, setting_op_t operation,
file_pos_ref input_pos, string_iterator &i, string_iterator end,
std::list<std::pair<unsigned,unsigned>> *part_positions = nullptr)
std::list<std::pair<unsigned,unsigned>> *part_positions = nullptr,
char delimiter = ' ')
{
using std::locale;
using std::isspace;
Expand All @@ -455,10 +495,11 @@ inline void read_setting_value(std::string &setting_val, setting_op_t operation,
i = skipwsln(i, end, line_num);

if (operation == setting_op_t::PLUSASSIGN) {
// Ensure whitespace at end of current value. This is really only for debugging niceness
// since the offsets (part_positions) are what really define the seperated components.
// Ensure that values are correctly delimited. This is usually only for debugging
// niceness as for commands where this is mostly used the offsets actually delimit
// the components, but e.g. for capabilities (comma-separated) it matters more.
if (!setting_val.empty()) {
setting_val += ' ';
setting_val += delimiter;
}
}
else {
Expand Down Expand Up @@ -542,9 +583,9 @@ inline void read_setting_value(std::string &setting_val, setting_op_t operation,
// See read_setting_value(std::string &, ...)
inline void read_setting_value(ha_string &setting_val, setting_op_t operation,
file_pos_ref input_pos, string_iterator &i, string_iterator end,
std::list<std::pair<unsigned,unsigned>> *part_positions = nullptr) {
std::list<std::pair<unsigned,unsigned>> *part_positions = nullptr, char delimiter = ' ') {
std::string sval = std::string(setting_val.c_str(), setting_val.length());
read_setting_value(sval, operation, input_pos, i, end, part_positions);
read_setting_value(sval, operation, input_pos, i, end, part_positions, delimiter);
setting_val = sval;
}

Expand Down Expand Up @@ -1333,6 +1374,11 @@ class service_settings_wrapper
string run_in_cgroup;
#endif

#if SUPPORT_CAPABILITIES
string capabilities;
secure_bits_t secbits;
#endif

#if USE_UTMPX
char inittab_id[sizeof(utmpx().ut_id)] = {0};
char inittab_line[sizeof(utmpx().ut_line)] = {0};
Expand Down Expand Up @@ -1379,6 +1425,14 @@ class service_settings_wrapper
report_lint("'run-in-cgroup' specified, but ignored for the specified (or default) service type.");
}
#endif
#if SUPPORT_CAPABILITIES
if (!capabilities.empty()) {
report_lint("'capabilities' specified, but ignored for the specified (or default) service type.");
}
if (secbits.get()) {
report_lint("'secure-bits' specified, but ignored for the specified (or default) service type.");
}
#endif
if (run_as_uid != (uid_t)-1) {
report_lint("'run-as' specified, but ignored for the specified (or default) service type.");
}
Expand All @@ -1399,6 +1453,11 @@ class service_settings_wrapper
if (onstart_flags.skippable) {
report_lint("option 'skippable' was specified, but ignored for the specified (or default) service type.");
}
#if SUPPORT_CAPABILITIES
if (onstart_flags.no_new_privs) {
report_lint("option 'no_new_privs' was specified, but ignored for the specified (or default) service type.");
}
#endif
if (log_type != log_type_id::NONE) {
report_lint("option 'log_type' was specified, but ignored for the specified (or default) service type.");
}
Expand Down Expand Up @@ -1544,6 +1603,43 @@ void process_service_line(settings_wrapper &settings, const char *name, const ch
settings.run_in_cgroup = read_setting_value(input_pos, i, end, nullptr);
break;
#endif
#if SUPPORT_CAPABILITIES
case setting_id_t::CAPABILITIES:
read_setting_value(settings.capabilities, setting_op, input_pos, i, end, nullptr, ',');
break;
case setting_id_t::SECURE_BITS:
{
std::list<std::pair<unsigned,unsigned>> indices;
string onstart_cmds = read_setting_value(input_pos, i, end, &indices);
for (auto indexpair : indices) {
string secbit_txt = onstart_cmds.substr(indexpair.first,
indexpair.second - indexpair.first);
if (secbit_txt == "keep-caps") {
settings.secbits.keep_caps = true;
}
else if (secbit_txt == "keep-caps-locked") {
settings.secbits.keep_caps_locked = true;
}
else if (secbit_txt == "no-setuid-fixup") {
settings.secbits.no_setuid_fixup = true;
}
else if (secbit_txt == "no-setuid-fixup-locked") {
settings.secbits.no_setuid_fixup_locked = true;
}
else if (secbit_txt == "noroot") {
settings.secbits.noroot = true;
}
else if (secbit_txt == "noroot-locked") {
settings.secbits.noroot_locked = true;
}
else {
throw service_description_exc(name, "unknown secure bit: " + secbit_txt,
"secure-bits", input_pos);
}
}
break;
}
#endif
case setting_id_t::SOCKET_LISTEN:
settings.socket_path = read_setting_value(input_pos, i, end, nullptr);
break;
Expand Down Expand Up @@ -1797,6 +1893,11 @@ void process_service_line(settings_wrapper &settings, const char *name, const ch
else if (option_txt == "kill-all-on-stop") {
settings.onstart_flags.kill_all_on_stop = true;
}
#if SUPPORT_CAPABILITIES
else if (option_txt == "no-new-privs") {
settings.onstart_flags.no_new_privs = true;
}
#endif
else {
throw service_description_exc(name, "unknown option: " + option_txt,
"options", input_pos);
Expand Down
Loading

0 comments on commit 80ea7ff

Please sign in to comment.