Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init: optionally load the system SELinux policy #400

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ DEFAULT_STOP_TIMEOUT=XXX
this, its process group is sent a SIGKILL signal which should cause it to terminate immediately.
The default if unspecified is 10 seconds. (The value can be overridden for individual services
via the service description).
SUPPORT_SELINUX=1|0
Whether to build support for loading the system SELinux policy at boot (Linux only).
See doc/linux/SELINUX.md for more information.
SUPPORT_CGROUPS=1|0
Whether to include support for cgroups (Linux only).
SUPPORT_CAPABILITIES=1|0
Expand Down
5 changes: 5 additions & 0 deletions BUILD_MESON
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ Custom options:
Available values : enabled, disabled, auto
Default value : auto

support-selinux : Whether to build support for loading the system SELinux policy at boot
(Linux only). See doc/linux/SELinux.md for more information.
Available values : enabled, disabled, auto
Default value : auto


Running the test suite
=-=-=-=-=-=-=-=-=-=-=-
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ The following people (in alphabetical order) have contributed:
* Oliver Amann - Code, testing, documentation
* Locria Cyber - Code, documentation
* q66 - Code, testing, documentation.
* Rahul Sandhu - Code
3 changes: 2 additions & 1 deletion build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ includes/mconfig.h: ../mconfig tools/mconfig-gen.cc version.conf
$(if $(SUPPORT_IOPRIO),SUPPORT_IOPRIO=$(SUPPORT_IOPRIO),) \
$(if $(SUPPORT_OOM_ADJ),SUPPORT_OOM_ADJ=$(SUPPORT_OOM_ADJ),) \
$(if $(USE_UTMPX),USE_UTMPX=$(USE_UTMPX),) \
$(if $(USE_INITGROUPS),USE_INITGROUPS=$(USE_INITGROUPS),) > includes/mconfig.h
$(if $(USE_INITGROUPS),USE_INITGROUPS=$(USE_INITGROUPS),) \
$(if $(SUPPORT_SELINUX),SUPPORT_SELINUX=$(SUPPORT_SELINUX),) > includes/mconfig.h

clean:
rm -f 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_SELINUX
#mesondefine SUPPORT_CAPABILITIES
#mesondefine SUPPORT_IOPRIO
#mesondefine SUPPORT_OOM_ADJ
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 @@ -89,6 +89,9 @@ int main(int argc, char **argv)
if (vars.find("DEFAULT_AUTO_RESTART") != vars.end()) {
cout << "#define DEFAULT_AUTO_RESTART " << vars["DEFAULT_AUTO_RESTART"] << "\n";
}
if (vars.find("SUPPORT_SELINUX") != vars.end()) {
cout << "#define SUPPORT_SELINUX " << vars["SUPPORT_SELINUX"] << "\n";
}

cout << "\n// Constants\n";
cout << "\nconstexpr static char DINIT_VERSION[] = " << stringify(vars["VERSION"]) << ";\n";
Expand Down
10 changes: 10 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ Optional options:
--enable-initgroups Enable initialization of supplementary groups for run-as
[Enabled]
--disable-initgroups Disable initialization of supplementary groups for run-as
--enable-selinux Enable SELinux support [Disabled]
--disable-selinux Disable SELinux support
--enable-auto-restart Enable auto-restart for services by default (Deprecated;
use --default-auto-restart=...)
--disable-auto-restart Disable auto-restart for services by default (Deprecated;
Expand Down Expand Up @@ -283,6 +285,7 @@ for var in PREFIX \
SUPPORT_OOM_ADJ \
USE_UTMPX \
USE_INITGROUPS \
SUPPORT_SELINUX \
SYSCONTROLSOCKET \
STRIPOPTS
do
Expand Down Expand Up @@ -324,6 +327,8 @@ for arg in "$@"; do
--disable-initgroups|--enable-initgroups=no) USE_INITGROUPS=0 ;;
--enable-auto-restart|--enable-auto-restart=yes) DEFAULT_AUTO_RESTART=ALWAYS ;; # Deprecated
--disable-auto-restart|--enable-auto-restart=no) DEFAULT_AUTO_RESTART=NEVER ;; # Deprecated
--enable-selinux|--enable-selinux=yes) SUPPORT_SELINUX=1 ;;
--disable-selinux|--enable-selinux=no) SUPPORT_SELINUX=0 ;;
--enable-strip|--enable-strip=yes) STRIPOPTS="-s" ;;
--disable-strip|--enable-strip=no) STRIPOPTS="" ;;
--default-auto-restart=never) DEFAULT_AUTO_RESTART=NEVER ;;
Expand Down Expand Up @@ -355,6 +360,7 @@ done
: "${DEFAULT_START_TIMEOUT:="60"}"
: "${DEFAULT_STOP_TIMEOUT:="10"}"
: "${USE_INITGROUPS:="1"}"
: "${SUPPORT_SELINUX:="0"}"
if [ "$PLATFORM" = "Linux" ]; then
: "${BUILD_SHUTDOWN:="yes"}"
: "${SUPPORT_CGROUPS:="1"}"
Expand Down Expand Up @@ -479,6 +485,9 @@ fi
if [ "$AUTO_LDFLAGS_BASE" = true ] && [ "$PLATFORM" = FreeBSD ]; then
try_ld_argument LDFLAGS_BASE -lrt
fi
if [ "$AUTO_LDFLAGS_BASE" = true ] && [ "$SUPPORT_SELINUX" = "1" ]; then
try_ld_argument LDFLAGS_BASE -lselinux
fi
if [ "$SUPPORT_CAPABILITIES" != 0 ]; then
if [ "$AUTO_LDFLAGS_LIBCAP" = true ]; then
try_ld_argument LDFLAGS_LIBCAP -lcap
Expand Down Expand Up @@ -589,6 +598,7 @@ LDFLAGS_LIBCAP=$LDFLAGS_LIBCAP
# Feature settings
SUPPORT_CGROUPS=$SUPPORT_CGROUPS
USE_INITGROUPS=$USE_INITGROUPS
SUPPORT_SELINUX=$SUPPORT_SELINUX
SUPPORT_CAPABILITIES=$SUPPORT_CAPABILITIES
SUPPORT_IOPRIO=$SUPPORT_IOPRIO
SUPPORT_OOM_ADJ=$SUPPORT_OOM_ADJ
Expand Down
43 changes: 43 additions & 0 deletions doc/linux/SELINUX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Dinit SELinux Awareness

Dinit has support for basic SELinux awareness. This document is intended to
outline the extent and inner workings of Dinit's SELinux awareness. The reader
is assumed to be knowledgeable about the basics of [SELinux](https://github.com/SELinuxProject/selinux-notebook)
and Dinit.

Dinit needs to be built with SELinux support (see [BUILD](/BUILD)) to enable the features that are
mentioned in this document.

## Loading the system SELinux policy
When booted as the system init system, dinit by default will attempt to load the
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"system init system" sounds weird. Say "system init" just as we do elsewhere.

system's SELinux policy and transition itself to a context specified by that policy
if not already done so in earlier boot (e.g. by an initramfs). This behaviour may be
disabled by passing dinit the `--disable-selinux-policy` flag.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"flag" -> command-line argument.


If not already mounted in earlier boot (e.g. by an initramfs), dinit will mount `/sys`,
and selinuxfs (typically `/sys/fs/selinux`). This occurs before any services are started,
as loading the SELinux policy is the first thing dinit does.
Comment on lines +17 to +19
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments elsewhere about assigning responsibility for these actions.

It seems odd to mention this here but not also mention the temporary mounting of /proc, especially as it appears in the chart below.


The following flowchart provides an overview of the process of loading the policy:
```mermaid
flowchart TD
A[Start] --> B{"Is dinit running as the system manager?"}
B -->|Yes| C{Have we been requested to not load the SELinux policy?}
C -->|No| D[Continue rest of dinit initialization]
C -->|Yes| E[Is the SELinux policy already loaded?]
E -->|Yes| D
E --> |No| G[Attempt to mount /proc]
G --> J[Attempt to load the SELinux policy]
J --> K{Did the SELinux policy load succeed?}
K -->|Yes| L[Attempt to calculate our new context and transition]
K -->|No| M{Was enforcing mode requested?}
M -->|Yes| I[Error exit early]
M -->|No| D
L --> N{Did we successfully transition?}
N -->|Yes| P{Did we mount /proc?}
N -->|No| O[Log an error]
O --> P
P -->|Yes| Q[Unmount /proc]
P -->|No| D
Q --> D
```
14 changes: 14 additions & 0 deletions doc/manpages/dinit.8.m4
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ If service description settings contain relative cgroup paths, they will be reso
this path.
This option is only available if \fBdinit\fR is built with cgroups support.
.TP
\fB\-\-disable\-selinux\-policy\fR
Disable loading of the system SELinux policy.
This option is only available if \fBdinit\fR is built with SELinux support.
.TP
\fB\-\-help\fR
Display brief help text and then exit.
.TP
Expand Down Expand Up @@ -298,6 +302,16 @@ There are several ways to work around this.
Service names following the \fB\-\-container\fR (\fB\-o\fR) or \fB\-\-system\-mgr\fR (\fB\-m\fR) options are not ignored.
Also, the \fB\-\-service\fR (\fB\-t\fR) option can be used to force a service name to be recognised regardless of operating mode.
.\"
.SH SELINUX SUPPORT
.LP
When running as PID 1 on a SELinux enabled machine, \fBdinit\fR will by default load the system's SELinux policy.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I look at the code, it looks to me like the SELinux policy will be loaded if dinit is running as system manager and system init, but this says "when running as PID 1"? (I already pointed out a similar issue in the previous review; you should address all cases).

Isn't it the case that this happens only if Dinit has been built with SELinux support enabled?

What happens in case of various failures? Eg failure to load the policy.

This behaviour can be disabled by passing the \fB\-\-disable\-selinux\-policy\fR option to dinit.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't really need to mention this, that option is already documented (also "dinit" lacks formatting).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Referring to line 308)

.LP
When loading the SELinux policy, dinit will automatically mount a few special filesystems needed to successfully load the policy.
\fBsysfs\fR will be mounted at \fB/sys\fR, and \fBselinuxfs\fR will be mounted at \fB/sys/fs/selinux\fR.
\fBdinit\fR will not unmount either.
\fBprocfs\fR will also be mounted at \fB/proc\fR, but \fBdinit\fR will unmount it after loading the SELinux policy.
Comment on lines +310 to +313
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this whole section problematic. First, other than /proc, it's not really dinit that mounts the filesystems; that happens inside the SELinux library and is prone to change if the library does, so I would prefer that the documentation doesn't claim that dinit does this itself but instead makes it clear that the SELinux framework may mount filesystems (and specify /sys/ etc as examples).

If you're going to mention /proc being temporarily mounted (and I guess that it's probably a good idea to mention it) then at least be clear on the significance of this. Rather than talk about when it's mounted and unmounted just say that it is temporarily mounted in order to load the policy, that's much more concise and less confusing. But also, point out that the /proc directory must exist for this to be successful, and that the temporary mount will overmount any previously-mounted /proc until the temporary mount is removed (I guess you perhaps didn't realise this), and what the likely outcome of being unable to mount /proc will be.

.\"
.SH FILES
.\"
.TP
Expand Down
3 changes: 3 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ support_ioprio = get_option('support-ioprio')
support_oom_adj = get_option('support-oom-adj')
use_utmpx = get_option('use-utmpx')
use_initgroups = get_option('use-initgroups')
support_selinux = get_option('support-selinux')
default_auto_restart = get_option('default-auto-restart')
default_start_timeout = get_option('default-start-timeout').to_string()
default_stop_timeout = get_option('default-stop-timeout').to_string()
Expand All @@ -61,6 +62,7 @@ endif

## Dependencies
libcap_dep = dependency('libcap', required: support_capabilities)
libselinux_dep = dependency('libselinux', version : '>= 2.1.9', required : support_selinux)

## Prepare mconfig.h
mconfig_data.set_quoted('DINIT_VERSION', version)
Expand All @@ -71,6 +73,7 @@ mconfig_data.set('DEFAULT_AUTO_RESTART', default_auto_restart)
mconfig_data.set('DEFAULT_START_TIMEOUT', default_start_timeout)
mconfig_data.set('DEFAULT_STOP_TIMEOUT', default_stop_timeout)
mconfig_data.set10('USE_INITGROUPS', use_initgroups)
mconfig_data.set10('SUPPORT_SELINUX', libselinux_dep.found() or support_selinux.enabled())
mconfig_data.set10('SUPPORT_CGROUPS', support_cgroups.auto() and platform == 'linux' or support_cgroups.enabled())
mconfig_data.set10('SUPPORT_CAPABILITIES', libcap_dep.found() and not support_capabilities.disabled())
mconfig_data.set10('SUPPORT_IOPRIO', support_ioprio.auto() and platform == 'linux' or support_ioprio.enabled())
Expand Down
6 changes: 6 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,9 @@ option(
value : 'auto',
description : 'Building shutdown/reboot/soft-reboot/halt or not.'
)
option(
'support-selinux',
type : 'feature',
value : 'auto',
description : 'SELinux support'
)
13 changes: 7 additions & 6 deletions src/dinit-log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -282,18 +282,19 @@ void buffered_log_stream::watch_removed() noexcept

} // end namespace

// Initialise the logging subsystem
// Potentially throws std::bad_alloc or std::system_error
void init_log(bool syslog_format)
void init_log(bool syslog_format) noexcept
{
log_stream[DLOG_CONS].add_watch(event_loop, STDOUT_FILENO, dasynq::OUT_EVENTS, false);
enable_console_log(true);

// The main (non-console) log won't be active yet, but we set the format here so that we
// buffer messages in the correct format:
log_format_syslog[DLOG_MAIN] = syslog_format;
}

void init_console_log()
{
log_stream[DLOG_CONS].add_watch(event_loop, STDOUT_FILENO, dasynq::OUT_EVENTS, false);
enable_console_log(true);
}

void setup_log_console_handoff(service_set *sset)
{
services = sset;
Expand Down
Loading