analyze: add inspect-elf verb to parse package metadata

Parses and prints package metadata from executables, libraries and core files

$ systemd-analyze inspect-elf /tmp/core ../fsverity-utils/fsverityb /bin/bash --json=off --no-pager
__________________________
           path: /tmp/core
        elfType: coredump
elfArchitecture: AMD x86-64

    module name: /tmp/crash
           type: deb
           name: hello
        version: 1.0
   architecture: amd64
             os: debian
      osVersion: 11
        buildId: b33541096a09c29a0ba4ec5c69364a2711b7c269

    module name: /usr/lib/x86_64-linux-gnu/libc-2.31.so
           type: deb
           name: hello
        version: 1.0
   architecture: amd64
             os: debian
      osVersion: 11
        buildId: 54eef5ce96cf37cb175b0d93186836ca1caf470c

    module name: /usr/lib/x86_64-linux-gnu/ld-2.31.so
           type: deb
           name: hello
        version: 1.0
   architecture: amd64
             os: debian
      osVersion: 11
        buildId: 32438eb3b034da54caf58c7a65446639f7cfe274
__________________________________________________________________
           path: /home/luca/git/systemd/../fsverity-utils/fsverity
        elfType: executable
elfArchitecture: AMD x86-64

           type: deb
           name: fsverity-utils
        version: 1.3-1
   architecture: amd64
             os: debian
   debugInfoUrl: https://debuginfod.debian.net
        buildId: 05b899e6ee0d3653e20458719b202ed3ca8d566f
_________________________
           path: /bin/bash
        elfType: executable
elfArchitecture: AMD x86-64

        buildId: 4fef260f60e257d2dbd4126bf8add83837aea190
$
$ systemd-analyze inspect-elf /tmp/core ../fsverity-utils/fsverity /bin/bash /tmp/core.test-condition.1000.f9b9a84a9fd1482c9702d6afa6f6934b.37640.1637083078000000 --json=pretty --no-pager
{
	"elfType" : "coredump",
	"elfArchitecture" : "AMD x86-64",
	"/home/bluca/git/fsverity-utils/fsverity" : {
		"type" : "deb",
		"name" : "fsverity-utils",
		"version" : "1.3-1",
		"buildId" : "7c895ecd2a271f93e96268f479fdc3c64a2ec4ee"
	},
	"/home/bluca/git/fsverity-utils/libfsverity.so.0" : {
		"type" : "deb",
		"name" : "fsverity-utils",
		"version" : "1.3-1",
		"buildId" : "b5e428254abf14237b0ae70ed85fffbb98a78f88"
	}
}
{
	"elfType" : "executable",
	"elfArchitecture" : "AMD x86-64",
	"/home/bluca/git/systemd/../fsverity-utils/fsverity" : {
		"type" : "deb",
		"name" : "fsverity-utils",
		"version" : "1.3-1",
		"buildId" : "7c895ecd2a271f93e96268f479fdc3c64a2ec4ee"
	}
}
{
	"elfType" : "executable",
	"elfArchitecture" : "AMD x86-64",
	"/bin/bash" : {
		"buildId" : "3313b4cb119dcce16927a9b6cc61dcd97dfc4d59"
	}
}
{
	"elfType" : "coredump",
	"elfArchitecture" : "AMD x86-64"
}
This commit is contained in:
Luca Boccassi 2021-11-17 01:45:07 +00:00
parent d48c2721b6
commit 917e655457
8 changed files with 193 additions and 1 deletions

View file

@ -681,6 +681,39 @@ $ systemd-analyze verify /tmp/source:alias.service
</programlisting>
</example>
</refsect2>
<refsect2>
<title><command>systemd-analyze inspect-elf <replaceable>FILE</replaceable>...</command></title>
<para>This command will load the specified file(s), and if they are ELF objects (executables,
libraries, core files, etc.) it will parse the embedded packaging metadata, if any, and print
it in a table or json format. See the <ulink url="https://systemd.io/COREDUMP_PACKAGE_METADATA/">
Packaging Metadata</ulink> documentation for more information.</para>
<example>
<title>Table output</title>
<programlisting>$ systemd-analyze inspect-elf --json=pretty /tmp/core.fsverity.1000.f77dac5dc161402aa44e15b7dd9dcf97.58561.1637106137000000
{
"elfType" : "coredump",
"elfArchitecture" : "AMD x86-64",
"/home/bluca/git/fsverity-utils/fsverity" : {
"type" : "deb",
"name" : "fsverity-utils",
"version" : "1.3-1",
"buildId" : "7c895ecd2a271f93e96268f479fdc3c64a2ec4ee"
},
"/home/bluca/git/fsverity-utils/libfsverity.so.0" : {
"type" : "deb",
"name" : "fsverity-utils",
"version" : "1.3-1",
"buildId" : "b5e428254abf14237b0ae70ed85fffbb98a78f88"
}
}
</programlisting>
</example>
</refsect2>
</refsect1>
<refsect1>

View file

@ -63,6 +63,7 @@ _systemd_analyze() {
[CAT_CONFIG]='cat-config'
[SECURITY]='security'
[CONDITION]='condition'
[INSPECT_ELF]='inspect-elf'
)
local CONFIGS='systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
@ -169,6 +170,14 @@ _systemd_analyze() {
fi
comps=$( __get_services $mode )
fi
elif __contains_word "$verb" ${VERBS[INSPECT_ELF]}; then
if [[ $cur = -* ]]; then
comps='--help --version --json=off --json=pretty --json=short'
else
comps=$( compgen -A file -- "$cur" )
compopt -o filenames
fi
fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )

View file

@ -54,6 +54,7 @@
'timestamp:Parse a systemd syntax timestamp'
'timespan:Parse a systemd syntax timespan'
'security:Analyze security settings of a service'
'inspect-elf:Parse and print ELF package metadata'
# log-level, log-target, service-watchdogs have been deprecated
)

128
src/analyze/analyze-elf.c Normal file
View file

@ -0,0 +1,128 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "analyze-elf.h"
#include "elf-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "format-table.h"
#include "format-util.h"
#include "json.h"
#include "path-util.h"
#include "strv.h"
int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
char **filename;
int r;
STRV_FOREACH(filename, filenames) {
_cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
_cleanup_(table_unrefp) Table *t = NULL;
_cleanup_free_ char *abspath = NULL;
_cleanup_close_ int fd = -1;
r = path_make_absolute_cwd(*filename, &abspath);
if (r < 0)
return log_error_errno(r, "Could not make an absolute path out of \"%s\": %m", *filename);
path_simplify(abspath);
fd = RET_NERRNO(open(abspath, O_RDONLY|O_CLOEXEC));
if (fd < 0)
return log_error_errno(fd, "Could not open \"%s\": %m", abspath);
r = parse_elf_object(fd, abspath, /* fork_disable_dump= */false, NULL, &package_metadata);
if (r < 0)
return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath);
t = table_new("", "");
if (!t)
return log_oom();
r = table_set_align_percent(t, TABLE_HEADER_CELL(0), 100);
if (r < 0)
return table_log_add_error(r);
r = table_add_many(
t,
TABLE_STRING, "path:",
TABLE_STRING, abspath);
if (r < 0)
return table_log_add_error(r);
if (package_metadata) {
JsonVariant *module_json;
const char *module_name;
JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, package_metadata) {
const char *field_name;
JsonVariant *field;
/* The ELF type and architecture are added as top-level objects,
* since they are only parsed for the file itself, but the packaging
* metadata is parsed recursively in core files, so there might be
* multiple modules. */
if (STR_IN_SET(module_name, "elfType", "elfArchitecture")) {
_cleanup_free_ char *suffixed = NULL;
suffixed = strjoin(module_name, ":");
if (!suffixed)
return log_oom();
r = table_add_many(
t,
TABLE_STRING, suffixed,
TABLE_STRING, json_variant_string(module_json));
if (r < 0)
return table_log_add_error(r);
continue;
}
/* path/elfType/elfArchitecture come first just once per file,
* then we might have multiple modules, so add a separator between
* them to make the output more readable. */
r = table_add_many(t, TABLE_EMPTY, TABLE_EMPTY);
if (r < 0)
return table_log_add_error(r);
/* In case of core files the module name will be the executable,
* but for binaries/libraries it's just the path, so don't print it
* twice. */
if (!streq(abspath, module_name)) {
r = table_add_many(
t,
TABLE_STRING, "module name:",
TABLE_STRING, module_name);
if (r < 0)
return table_log_add_error(r);
}
JSON_VARIANT_OBJECT_FOREACH(field_name, field, module_json)
if (json_variant_is_string(field)) {
_cleanup_free_ char *suffixed = NULL;
suffixed = strjoin(field_name, ":");
if (!suffixed)
return log_oom();
r = table_add_many(
t,
TABLE_STRING, suffixed,
TABLE_STRING, json_variant_string(field));
if (r < 0)
return table_log_add_error(r);
}
}
}
if (json_flags & JSON_FORMAT_OFF) {
(void) table_set_header(t, true);
r = table_print(t, NULL);
if (r < 0)
return table_log_print_error(r);
} else
json_variant_dump(package_metadata, json_flags, stdout, NULL);
}
return 0;
}

View file

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "json.h"
int analyze_elf(char **filenames, JsonFormatFlags json_flags);

View file

@ -13,6 +13,7 @@
#include "alloc-util.h"
#include "analyze-condition.h"
#include "analyze-elf.h"
#include "analyze-security.h"
#include "analyze-verify.h"
#include "bus-error.h"
@ -2431,6 +2432,12 @@ static int do_security(int argc, char *argv[], void *userdata) {
/*flags=*/ 0);
}
static int do_elf_inspection(int argc, char *argv[], void *userdata) {
pager_open(arg_pager_flags);
return analyze_elf(strv_skip(argv, 1), arg_json_format_flags);
}
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL, *dot_link = NULL;
int r;
@ -2473,6 +2480,7 @@ static int help(int argc, char *argv[], void *userdata) {
" timestamp TIMESTAMP... Validate a timestamp\n"
" timespan SPAN... Validate a time span\n"
" security [UNIT...] Analyze security of unit\n"
" inspect-elf FILE... Parse and print ELF package metadata\n"
"\nOptions:\n"
" --recursive-errors=MODE Control which units are verified\n"
" --offline=BOOL Perform a security review on unit file(s)\n"
@ -2759,7 +2767,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --offline= is only supported for security right now.");
if (arg_json_format_flags != JSON_FORMAT_OFF && !streq_ptr(argv[optind], "security"))
if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --json= is only supported for security right now.");
@ -2835,6 +2843,7 @@ static int run(int argc, char *argv[]) {
{ "timestamp", 2, VERB_ANY, 0, test_timestamp },
{ "timespan", 2, VERB_ANY, 0, dump_timespan },
{ "security", VERB_ANY, VERB_ANY, 0, do_security },
{ "inspect-elf", 2, VERB_ANY, 0, do_elf_inspection },
{}
};

View file

@ -4,6 +4,8 @@ systemd_analyze_sources = files('''
analyze.c
analyze-condition.c
analyze-condition.h
analyze-elf.c
analyze-elf.h
analyze-verify.c
analyze-verify.h
analyze-security.c

View file

@ -595,6 +595,10 @@ set -e
rm /tmp/img/usr/lib/systemd/system/testfile.service
if systemd-analyze --version | grep -q -F "+ELFUTILS"; then
systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"'
fi
systemd-analyze log-level info
echo OK >/testok