diff --git a/TODO b/TODO
index b375327fe0..7bbf334ef1 100644
--- a/TODO
+++ b/TODO
@@ -179,8 +179,6 @@ Features:
* insert the new pidfs inode number as a third field into PidRef, so that
PidRef are reasonably serializable without having to pass around fds.
-* systemd-analyze smbios11 to dump smbios type 11 vendor strings
-
* move documentation about our common env vars (SYSTEMD_LOG_LEVEL,
SYSTEMD_PAGER, …) into a man page of its own, and just link it from our
various man pages that so far embed the whole list again and again, in an
diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml
index 9a4b0cada4..67c7e07778 100644
--- a/man/systemd-analyze.xml
+++ b/man/systemd-analyze.xml
@@ -186,6 +186,11 @@
architectures
NAME
+
+ systemd-analyze
+ OPTIONS
+ smbios11
+
@@ -979,6 +984,26 @@ x86-64 native
+
+ systemd-analyze smbios11
+
+ Shows a list of SMBIOS Type #11 strings passed to the system. Also see
+ smbios-type-117.
+
+
+ Example output
+ $ systemd-analyze smbios11
+io.systemd.stub.kernel-cmdline-extra=console=ttyS0
+io.systemd.credential.binary:ssh.ephemeral-authorized_keys-all=c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSURGd20xbFp4WlRGclJteG9ZQlozOTYzcE1uYlJCaDMwM1MxVXhLSUM2NmYgbGVubmFydEB6ZXRhCg==
+io.systemd.credential:vmm.notify_socket=vsock-stream:2:254570042
+
+3 SMBIOS Type #11 strings passed.
+
+
+
+
+
+
diff --git a/src/analyze/analyze-smbios11.c b/src/analyze/analyze-smbios11.c
new file mode 100644
index 0000000000..cee5a3ad0b
--- /dev/null
+++ b/src/analyze/analyze-smbios11.c
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "analyze.h"
+#include "analyze-smbios11.h"
+#include "escape.h"
+#include "smbios11.h"
+#include "virt.h"
+
+int verb_smbios11(int argc, char *argv[], void *userdata) {
+ unsigned n = 0;
+ int r;
+
+ for (unsigned i = 0;; i++) {
+ _cleanup_free_ char *data = NULL;
+ bool written = false;
+ size_t size;
+
+ r = read_smbios11_field(i, SIZE_MAX, &data, &size);
+ if (r == -ENOENT) /* Reached the end */
+ break;
+ if (r < 0)
+ return log_error_errno(r, "Failed to read SMBIOS Type #11 string %u: %m", i);
+ bool incomplete = r == 0;
+
+ size_t left, skip;
+ const char *p;
+ for (p = data, left = size; left > 0; p += skip, left -= skip) {
+ const char *nul;
+
+ nul = memchr(p, 0, left);
+ if (nul)
+ skip = (nul - p) + 1;
+ else {
+ nul = p + left;
+ skip = left;
+ }
+
+ if (nul - p == 0) /* Skip empty strings */
+ continue;
+
+ _cleanup_free_ char *escaped = NULL;
+ escaped = cescape_length(p, nul - p);
+ if (!escaped)
+ return log_oom();
+
+ if (written)
+ fputc('\n', stdout);
+
+ fputs(escaped, stdout);
+ written = true;
+ n++;
+ }
+
+ if (written) {
+ if (incomplete)
+ fputs(special_glyph(SPECIAL_GLYPH_ELLIPSIS), stdout);
+
+ fputc('\n', stdout);
+ }
+
+ if (i == UINT_MAX) /* Prevent overflow */
+ break;
+ }
+
+ if (!arg_quiet) {
+ if (n == 0)
+ log_info("No SMBIOS Type #11 strings passed.");
+ else
+ log_info("\n%u SMBIOS Type #11 strings passed.", n);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/analyze/analyze-smbios11.h b/src/analyze/analyze-smbios11.h
new file mode 100644
index 0000000000..4b1f334dc8
--- /dev/null
+++ b/src/analyze/analyze-smbios11.h
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_smbios11(int argc, char *argv[], void *userdata);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index cf4894a9d3..db3996faea 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -34,6 +34,7 @@
#include "analyze-plot.h"
#include "analyze-security.h"
#include "analyze-service-watchdogs.h"
+#include "analyze-smbios11.h"
#include "analyze-srk.h"
#include "analyze-syscall-filter.h"
#include "analyze-time.h"
@@ -241,6 +242,7 @@ static int help(int argc, char *argv[], void *userdata) {
" image-policy POLICY... Analyze image policy string\n"
" pcrs [PCR...] Show TPM2 PCRs and their names\n"
" srk [>FILE] Write TPM2 SRK (to FILE)\n"
+ " smbios11 List strings passed via SMBIOS Type #11\n"
"\nOptions:\n"
" --recursive-errors=MODE Control which units are verified\n"
" --offline=BOOL Perform a security review on unit file(s)\n"
@@ -657,6 +659,7 @@ static int run(int argc, char *argv[]) {
{ "pcrs", VERB_ANY, VERB_ANY, 0, verb_pcrs },
{ "srk", VERB_ANY, 1, 0, verb_srk },
{ "architectures", VERB_ANY, VERB_ANY, 0, verb_architectures },
+ { "smbios11", VERB_ANY, 1, 0, verb_smbios11 },
{}
};
diff --git a/src/analyze/meson.build b/src/analyze/meson.build
index f150ed7613..a307923c22 100644
--- a/src/analyze/meson.build
+++ b/src/analyze/meson.build
@@ -22,6 +22,7 @@ systemd_analyze_sources = files(
'analyze-plot.c',
'analyze-security.c',
'analyze-service-watchdogs.c',
+ 'analyze-smbios11.c',
'analyze-srk.c',
'analyze-syscall-filter.c',
'analyze-time.c',
diff --git a/src/core/import-creds.c b/src/core/import-creds.c
index f27ffed4a0..4008ea43e9 100644
--- a/src/core/import-creds.c
+++ b/src/core/import-creds.c
@@ -20,6 +20,7 @@
#include "path-util.h"
#include "proc-cmdline.h"
#include "recurse-dir.h"
+#include "smbios11.h"
#include "strv.h"
#include "virt.h"
@@ -578,38 +579,17 @@ static int import_credentials_smbios(ImportCredentialContext *c) {
return 0;
for (unsigned i = 0;; i++) {
- struct dmi_field_header {
- uint8_t type;
- uint8_t length;
- uint16_t handle;
- uint8_t count;
- char contents[];
- } _packed_ *dmi_field_header;
- _cleanup_free_ char *p = NULL;
- _cleanup_free_ void *data = NULL;
+ _cleanup_free_ char *data = NULL;
size_t size;
- assert_cc(offsetof(struct dmi_field_header, contents) == 5);
-
- if (asprintf(&p, "/sys/firmware/dmi/entries/11-%u/raw", i) < 0)
- return log_oom();
-
- r = read_virtual_file(p, sizeof(dmi_field_header) + CREDENTIALS_TOTAL_SIZE_MAX, (char**) &data, &size);
+ r = read_smbios11_field(i, CREDENTIALS_TOTAL_SIZE_MAX, &data, &size);
if (r < 0) {
/* Once we reach ENOENT there are no more DMI Type 11 fields around. */
- log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, "Failed to open '%s', ignoring: %m", p);
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, "Failed to read SMBIOS type #11 object %u, ignoring: %m", i);
break;
}
- if (size < offsetof(struct dmi_field_header, contents))
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DMI field header of '%s' too short.", p);
-
- dmi_field_header = data;
- if (dmi_field_header->type != 11 ||
- dmi_field_header->length != offsetof(struct dmi_field_header, contents))
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid DMI field header.");
-
- r = parse_smbios_strings(c, dmi_field_header->contents, size - offsetof(struct dmi_field_header, contents));
+ r = parse_smbios_strings(c, data, size);
if (r < 0)
return r;
diff --git a/src/shared/meson.build b/src/shared/meson.build
index c5106d87d5..a1c3b421cc 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -157,6 +157,7 @@ shared_sources = files(
'service-util.c',
'sleep-config.c',
'smack-util.c',
+ 'smbios11.c',
'socket-label.c',
'socket-netlink.c',
'spawn-ask-password-agent.c',
diff --git a/src/shared/smbios11.c b/src/shared/smbios11.c
new file mode 100644
index 0000000000..5009427810
--- /dev/null
+++ b/src/shared/smbios11.c
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "fileio.h"
+#include "macro.h"
+#include "smbios11.h"
+#include "virt.h"
+
+int read_smbios11_field(unsigned i, size_t max_size, char **ret_data, size_t *ret_size) {
+ _cleanup_free_ char *p = NULL, *contents = NULL;
+ _cleanup_free_ void *data = NULL;
+ size_t size, contents_size;
+ int r;
+
+ assert(ret_data);
+ assert(ret_size);
+
+ /* Parses DMI OEM strings fields (SMBIOS type 11), as settable with qemu's -smbios type=11,value=… switch. */
+
+ if (detect_container() > 0) /* don't access /sys/ in a container */
+ return -ENOENT;
+
+ if (asprintf(&p, "/sys/firmware/dmi/entries/11-%u/raw", i) < 0)
+ return -ENOMEM;
+
+ struct dmi_field_header {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+ uint8_t count;
+ char contents[];
+ } _packed_ *dmi_field_header;
+
+ assert_cc(offsetof(struct dmi_field_header, contents) == 5);
+
+ r = read_virtual_file(
+ p,
+ max_size >= SIZE_MAX - offsetof(struct dmi_field_header, contents) ? SIZE_MAX :
+ sizeof(dmi_field_header) + max_size,
+ (char**) &data, &size);
+ if (r < 0)
+ return r;
+
+ if (size < offsetof(struct dmi_field_header, contents))
+ return -EBADMSG;
+
+ dmi_field_header = data;
+ if (dmi_field_header->type != 11 ||
+ dmi_field_header->length != offsetof(struct dmi_field_header, contents))
+ return -EBADMSG;
+
+ contents_size = size - offsetof(struct dmi_field_header, contents);
+ contents = memdup_suffix0(dmi_field_header->contents, contents_size);
+ if (!contents)
+ return -ENOMEM;
+
+ *ret_data = TAKE_PTR(contents);
+ *ret_size = contents_size;
+
+ return r; /* NB! read_virtual_file() returns 0 on incomplete reads, and 1 in complete reads */
+}
diff --git a/src/shared/smbios11.h b/src/shared/smbios11.h
new file mode 100644
index 0000000000..ca8fe45260
--- /dev/null
+++ b/src/shared/smbios11.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include
+
+int read_smbios11_field(unsigned i, size_t max_size, char **ret_data, size_t *ret_size);
diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh
index 18f5c4d804..fde15192f7 100755
--- a/test/units/TEST-65-ANALYZE.sh
+++ b/test/units/TEST-65-ANALYZE.sh
@@ -947,6 +947,9 @@ systemd-analyze architectures x86-64
systemd-analyze architectures native
systemd-analyze architectures uname
+systemd-analyze smbios11
+systemd-analyze smbios11 -q
+
systemd-analyze log-level info
touch /testok