--- /dev/null
+name: Scorecards supply-chain security
+on:
+ # Only the default branch is supported.
+ branch_protection_rule:
+ schedule:
+ - cron: '15 21 * * 6'
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ analysis:
+ name: Scorecards analysis
+ if: github.repository == 'systemd/systemd'
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to upload the results to code-scanning dashboard.
+ security-events: write
+ # Used to receive a badge.
+ id-token: write
+
+ steps:
+ - name: "Checkout code"
+ uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # tag=v3.0.0
+ with:
+ persist-credentials: false
+
+ - name: "Run analysis"
+ uses: ossf/scorecard-action@e363bfca00e752f91de7b7d2a77340e2e523cb18 # tag=v2.0.4
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if:
+ # - you want to enable the Branch-Protection check on a *public* repository, or
+ # - you are installing Scorecards on a *private* repository
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
+ # repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
+
+ # Publish the results for public repositories to enable scorecard badges. For more details, see
+ # https://github.com/ossf/scorecard-action#publishing-results.
+ # For private repositories, `publish_results` will automatically be set to `false`, regardless
+ # of the value entered here.
+ publish_results: ${{ github.event_name != 'pull_request' }}
+
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
+ # format to the repository Actions tab.
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # tag=v3.0.0
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # Upload the results to GitHub's code scanning dashboard.
+ - name: "Upload to code-scanning"
+ if: ${{ github.event_name != 'pull_request' }}
+ uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # tag=v1.0.26
+ with:
+ sarif_file: results.sarif
[![CentOS CI - Rawhide (SELinux)](https://jenkins-systemd.apps.ocp.ci.centos.org/buildStatus/icon?subject=CentOS%20CI%20-%20Rawhide%20(SELinux)&job=upstream-vagrant-rawhide-selinux)](https://jenkins-systemd.apps.ocp.ci.centos.org/view/Upstream/job/upstream-vagrant-rawhide-selinux/)<br/>
[![Fossies codespell report](https://fossies.org/linux/test/systemd-main.tar.gz/codespell.svg)](https://fossies.org/linux/test/systemd-main.tar.gz/codespell.html)</br>
[![Coverage Status](https://coveralls.io/repos/github/systemd/systemd/badge.svg?branch=main)](https://coveralls.io/github/systemd/systemd?branch=main)</br>
-[![Packaging status](https://repology.org/badge/tiny-repos/systemd.svg)](https://repology.org/project/systemd/versions)
+[![Packaging status](https://repology.org/badge/tiny-repos/systemd.svg)](https://repology.org/project/systemd/versions)</br>
+[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/systemd/systemd/badge)](https://api.securityscorecards.dev/projects/github.com/systemd/systemd)
## Details
* add ability to set hostname with suffix derived from machine id at boot
-* ask dracut to generate usr= on the kernel cmdline so that we don't need to
- read /etc/fstab from the root fs from the initrd and do daemon-reload
-
* add PR_SET_DUMPABLE service setting
* homed/userdb: maybe define a "companion" dir for home directories where apps
must either be unset, empty, disabled, or all policies configured there must fail. Also see the
example below with <literal>Name=dmz0</literal>.</para>
- <para>Note that specifying a name that the kernel might use for another
- interface (for example <literal>eth0</literal>) is dangerous because the
- name assignment done by udev will race with the assignment done by the
- kernel, and only one interface may use the name. Depending on the order of
- operations, either udev or the kernel will win, making the naming
- unpredictable. It is best to use some different prefix, for example
- <literal>internal0</literal>/<literal>external0</literal> or
- <literal>lan0</literal>/<literal>lan1</literal>/<literal>lan3</literal>.
- </para>
+ <para>Note that specifying a name that the kernel might use for another interface (for example
+ <literal>eth0</literal>) is dangerous because the name assignment done by udev will race with the
+ assignment done by the kernel, and only one interface may use the name. Depending on the order of
+ operations, either udev or the kernel will win, making the naming unpredictable. It is best to use
+ some different prefix, for example <literal>internal0</literal>/<literal>external0</literal> or
+ <literal>lan0</literal>/<literal>lan1</literal>/<literal>lan3</literal>.</para>
+
+ <para>Interface names must have a minimum length of 1 character and a maximum length of 15
+ characters, and may contain any 7bit ASCII character, with the exception of control characters,
+ <literal>:</literal>, <literal>/</literal> and <literal>%</literal>. While <literal>.</literal> is
+ an allowed character, it's recommended to avoid it when naming interfaces as various tools (such as
+ <citerefentry><refentrytitle>resolvconf</refentrytitle><manvolnum>1</manvolnum></citerefentry>) use
+ it as separator character. Also, fully numeric interface names are not allowed (in order to avoid
+ ambiguity with interface specification by numeric indexes), as are the special strings
+ <literal>.</literal>, <literal>..</literal>, <literal>all</literal> and
+ <literal>default</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
If the empty string is assigned to this option, the list is reset, and all prior assignments
have no effect. If the kernel does not support the alternative names, then this setting will
be ignored.</para>
+
+ <para>Alternative interface names may be used to identify interfaces in various tools. In contrast
+ to the primary name (as configured with <varname>Name=</varname> above) there may be multiple
+ alternative names referring to the same interface. Alternative names may have a maximum length of
+ 127 characters, in contrast to the 15 allowed for the primary interface name, but otherwise are
+ subject to the same naming constraints.</para>
</listitem>
</varlistentry>
<varlistentry>
if (r < 0)
return log_error_errno(r, "Failed to allocate device monitor: %m");
+ (void) sd_device_monitor_set_description(monitor, "security-device");
+
r = sd_device_monitor_filter_add_match_tag(monitor, "security-device");
if (r < 0)
return log_error_errno(r, "Failed to configure device monitor: %m");
if (r < 0)
return log_error_errno(r, "Failed to allocate device monitor: %m");
+ (void) sd_device_monitor_set_description(monitor, "tpmrm");
+
r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, "tpmrm", NULL);
if (r < 0)
return log_error_errno(r, "Failed to configure device monitor: %m");
if (r < 0)
return r;
- (void) sd_event_source_set_description(sd_device_monitor_get_event_source(m->device_seat_monitor), "logind-seat-monitor");
+ (void) sd_device_monitor_set_description(m->device_seat_monitor, "seat");
r = sd_device_monitor_new(&m->device_monitor);
if (r < 0)
if (r < 0)
return r;
- (void) sd_event_source_set_description(sd_device_monitor_get_event_source(m->device_monitor), "logind-device-monitor");
+ (void) sd_device_monitor_set_description(m->device_monitor, "input,graphics,drm");
/* Don't watch keys if nobody cares */
if (!manager_all_buttons_ignored(m)) {
if (r < 0)
return r;
- (void) sd_event_source_set_description(sd_device_monitor_get_event_source(m->device_button_monitor), "logind-button-monitor");
+ (void) sd_device_monitor_set_description(m->device_button_monitor, "button");
}
/* Don't bother watching VCSA devices, if nobody cares */
if (r < 0)
return r;
- (void) sd_event_source_set_description(sd_device_monitor_get_event_source(m->device_vcsa_monitor), "logind-vcsa-monitor");
+ (void) sd_device_monitor_set_description(m->device_vcsa_monitor, "vcsa");
}
return 0;
#define THREADS_MAX 64
#define ELF_PACKAGE_METADATA_ID 0xcafe1a7e
+/* The amount of data we're willing to write to each of the output pipes. */
+#define COREDUMP_PIPE_MAX (1024*1024U)
+
static void *dw_dl = NULL;
static void *elf_dl = NULL;
return r;
if (ret) {
- r = RET_NERRNO(pipe2(return_pipe, O_CLOEXEC));
+ r = RET_NERRNO(pipe2(return_pipe, O_CLOEXEC|O_NONBLOCK));
if (r < 0)
return r;
}
if (ret_package_metadata) {
- r = RET_NERRNO(pipe2(json_pipe, O_CLOEXEC));
+ r = RET_NERRNO(pipe2(json_pipe, O_CLOEXEC|O_NONBLOCK));
if (r < 0)
return r;
}
goto child_fail;
if (buf) {
- r = loop_write(return_pipe[1], buf, strlen(buf), false);
- if (r < 0)
+ size_t len = strlen(buf);
+
+ if (len > COREDUMP_PIPE_MAX) {
+ /* This is iffy. A backtrace can be a few hundred kilobytes, but too much is
+ * too much. Let's log a warning and ignore the rest. */
+ log_warning("Generated backtrace is %zu bytes (more than the limit of %u bytes), backtrace will be truncated.",
+ len, COREDUMP_PIPE_MAX);
+ len = COREDUMP_PIPE_MAX;
+ }
+
+ /* Bump the space for the returned string.
+ * Failure is ignored, because partial output is still useful. */
+ (void) fcntl(return_pipe[1], F_SETPIPE_SZ, len);
+
+ r = loop_write(return_pipe[1], buf, len, false);
+ if (r == -EAGAIN)
+ log_warning("Write failed, backtrace will be truncated.");
+ else if (r < 0)
goto child_fail;
return_pipe[1] = safe_close(return_pipe[1]);
if (package_metadata) {
_cleanup_fclose_ FILE *json_out = NULL;
+ /* Bump the space for the returned string. We don't know how much space we'll need in
+ * advance, so we'll just try to write as much as possible and maybe fail later. */
+ (void) fcntl(json_pipe[1], F_SETPIPE_SZ, COREDUMP_PIPE_MAX);
+
json_out = take_fdopen(&json_pipe[1], "w");
if (!json_out) {
r = -errno;
goto child_fail;
}
- json_variant_dump(package_metadata, JSON_FORMAT_FLUSH, json_out, NULL);
+ r = json_variant_dump(package_metadata, JSON_FORMAT_FLUSH, json_out, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write JSON package metadata, ignoring: %m");
}
_exit(EXIT_SUCCESS);
return -errno;
r = json_parse_file(json_in, NULL, 0, &package_metadata, NULL, NULL);
- if (r < 0 && r != -EINVAL) /* EINVAL: json was empty, so we got nothing, but that's ok */
- return r;
+ if (r < 0 && r != -ENODATA) /* ENODATA: json was empty, so we got nothing, but that's ok */
+ log_warning_errno(r, "Failed to read or parse json metadata, ignoring: %m");
}
if (ret)
return (int) sz - 1;
}
-void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix) {
+int json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix) {
if (!v)
- return;
+ return 0;
if (!f)
f = stdout;
fputc('\n', f); /* In case of SSE add a second newline */
if (flags & JSON_FORMAT_FLUSH)
- fflush(f);
+ return fflush_and_check(f);
+ return 0;
}
int json_variant_filter(JsonVariant **v, char **to_remove) {
int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
_cleanup_(json_source_unrefp) JsonSource *source = NULL;
_cleanup_free_ char *text = NULL;
- const char *p;
int r;
if (f)
if (r < 0)
return r;
+ if (isempty(text))
+ return -ENODATA;
+
if (path) {
source = json_source_new(path);
if (!source)
return -ENOMEM;
}
- p = text;
+ const char *p = text;
return json_parse_internal(&p, source, flags, ret, ret_line, ret_column, false);
}
} JsonFormatFlags;
int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
-void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix);
+int json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix);
int json_variant_filter(JsonVariant **v, char **to_remove);
return log_error_errno(r, "Failed to add %s subsystem match to monitor: %m", subsystem);
}
+ _cleanup_free_ char *desc = NULL;
+ const char *sysname = NULL;
+ if (device)
+ (void) sd_device_get_sysname(device, &sysname);
+
+ desc = strjoin(sysname ?: subsystem, devlink ? ":" : ":initialization", devlink);
+ if (desc)
+ (void) sd_device_monitor_set_description(monitor, desc);
+
r = sd_device_monitor_attach_event(monitor, event);
if (r < 0)
return log_error_errno(r, "Failed to attach event to device monitor: %m");
assert_se(json_variant_equal(a, b));
}
+TEST(json_parse_file_empty) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ assert_se(fopen_unlocked("/dev/null", "re", &f) >= 0);
+ assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) == -ENODATA);
+ assert_se(v == NULL);
+}
+
+TEST(json_parse_file_invalid) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ assert_se(f = fmemopen_unlocked((void*) "kookoo", 6, "r"));
+ assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) == -EINVAL);
+ assert_se(v == NULL);
+}
+
TEST(source) {
static const char data[] =
"\n"
assert_se( iszero_safe(DBL_MIN / -INFINITY));
assert_se( iszero_safe(DBL_MAX / INFINITY / 2));
assert_se( iszero_safe(DBL_MAX / -INFINITY * DBL_MAX));
+#if defined(__i386__) && !defined(__SSE2_MATH__)
+ /* On 32bit x86, -mfpmath=387 is the default and SSE2 is not used. Then, floating point values are
+ * calculated in 80bit, and truncated to the length of the used type (double in this case). Hence,
+ * DBL_MAX * 2 is temporary calculated as a normal value, and goes to zero when divided with
+ * INFINITY. See issue #25044. */
+ log_debug("i386 architecture without SSE2 is detected. "
+ "Skipping \"assert_se(!iszero_safe(DBL_MAX * 2 / INFINITY))\".");
+#else
assert_se(!iszero_safe(DBL_MAX * 2 / INFINITY));
+#endif
/* infinity / infinity is NaN */
assert_se(!iszero_safe(INFINITY / INFINITY));
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
- (void) sd_event_source_set_description(sd_device_monitor_get_event_source(monitor),
- sender == MONITOR_GROUP_UDEV ? "device-monitor-udev" : "device-monitor-kernel");
+ (void) sd_device_monitor_set_description(monitor, sender == MONITOR_GROUP_UDEV ? "udev" : "kernel");
*ret = TAKE_PTR(monitor);
return 0;
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
- (void) sd_event_source_set_description(sd_device_monitor_get_event_source(monitor), "worker-device-monitor");
-
/* Process first device */
(void) worker_device_monitor_handler(monitor, dev, manager);
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
- (void) sd_event_source_set_description(sd_device_monitor_get_event_source(manager->monitor), "device-monitor");
-
r = sd_event_add_io(manager->event, NULL, fd_worker, EPOLLIN, on_worker, manager);
if (r < 0)
return log_error_errno(r, "Failed to create worker event source: %m");
/etc/systemd/system/a-b-.service.d/drop3.conf
}
+test_transient_slice_dropins () {
+ echo "Testing dropins for a transient slice..."
+ echo "*** test transient slice drop-ins"
+
+ # FIXME: implement reloading of individual units.
+ #
+ # The settings here are loaded twice. For most settings it doesn't matter,
+ # but Documentation is not deduplicated, so we current get repeated entried
+ # which is a bug.
+
+ mkdir -p /etc/systemd/system/slice.d
+ mkdir -p /etc/systemd/system/a-.slice.d
+ mkdir -p /etc/systemd/system/a-b-.slice.d
+ mkdir -p /etc/systemd/system/a-b-c.slice.d
+
+ echo -e '[Unit]\nDocumentation=man:drop1' >/etc/systemd/system/slice.d/drop1.conf
+ echo -e '[Unit]\nDocumentation=man:drop2' >/etc/systemd/system/a-.slice.d/drop2.conf
+ echo -e '[Unit]\nDocumentation=man:drop3' >/etc/systemd/system/a-b-.slice.d/drop3.conf
+ echo -e '[Unit]\nDocumentation=man:drop4' >/etc/systemd/system/a-b-c.slice.d/drop4.conf
+
+ # Invoke daemon-reload to make sure that the call below doesn't fail
+ systemctl daemon-reload
+
+ # No fragment is required, so this works
+ systemctl cat a-b-c.slice
+
+ busctl call \
+ org.freedesktop.systemd1 \
+ /org/freedesktop/systemd1 \
+ org.freedesktop.systemd1.Manager \
+ StartTransientUnit 'ssa(sv)a(sa(sv))' \
+ 'a-b-c.slice' 'replace' \
+ 1 \
+ 'Documentation' as 1 'man:drop5' \
+ 0
+
+ data=$(systemctl show -P Documentation a-b-c.slice)
+ test "$data" = "man:drop1 man:drop2 man:drop3 man:drop4 man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
+
+ # Do a reload and check again
+ systemctl daemon-reload
+ data=$(systemctl show -P Documentation a-b-c.slice)
+ test "$data" = "man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
+
+ clear_units a-b-c.slice
+ rm /etc/systemd/system/slice.d/drop1.conf \
+ /etc/systemd/system/a-.slice.d/drop2.conf \
+ /etc/systemd/system/a-b-.slice.d/drop3.conf
+}
+
test_template_dropins () {
echo "Testing template dropins..."
test_hierarchical_service_dropins
test_hierarchical_slice_dropins
test_transient_service_dropins
+test_transient_slice_dropins
test_template_dropins
test_alias_dropins
test_masked_dropins