tpm2: add hwdb quirk db, to deal with borked TPMs (#40931)
This adds infrastructure so that we can maintain quirks for TPM devices in hwdb, to deal with issues such as #40485. I expected that we need this sooner or later anyway, so here we go. Fixes: #40485
This commit is contained in:
@@ -231,6 +231,11 @@ CHANGES WITH 260 in spe:
|
||||
ID_INTEGRATION= because it was never used and the new variable covers
|
||||
the idea that variable was intended for better.
|
||||
|
||||
* A new udev builtin "tpm2_id" is now available which will extract
|
||||
vendor/model identification from connected TPM2 devices as they are
|
||||
probed. This is then used to import data from the udev database,
|
||||
possibly containing quirk and other information about specific TPMs.
|
||||
|
||||
Changes in systemd-networkd:
|
||||
|
||||
* MultiPathRoute= option now supports interface-bound ECMP routes.
|
||||
@@ -469,6 +474,9 @@ CHANGES WITH 260 in spe:
|
||||
system components to synchronously hook into the OOM killing logic,
|
||||
by registering a Varlink socket in a special directory.
|
||||
|
||||
* systemd-analyze learnt a new verb "identify-tpm2" which shows
|
||||
vendor/model information extracted from the system's TPM.
|
||||
|
||||
Changes in units:
|
||||
|
||||
* runlevel[0-6].target units that were removed in v258 have been
|
||||
|
||||
@@ -1015,3 +1015,12 @@ non-volatile indexes (NV Indexes), could not be initialized.
|
||||
This typically means the persistent NV index memory available on the TPM is
|
||||
taken up by other resources, or is extremely limited. A TPM reset might help
|
||||
recovering space (but will invalidate all TPM bound keys and resources).
|
||||
|
||||
-- 8f07a5b814ca4762b89fcc3082e48aed
|
||||
Subject: TPM NV index backed PCRs not supported on the local TPM
|
||||
Defined-By: systemd
|
||||
Support: %SUPPORT_URL%
|
||||
|
||||
The Trusted Platform Module's (TPM) NV index support is too limited to properly
|
||||
implement NV index backed additional PCRs. NvPCRs will not be allocated or
|
||||
initialized, and will not be available on the system.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# Use "systemd-analyze identify-tpm2" to generate the modalias string for your
|
||||
# hardware. Don't forget to prefix it with "tpm:" for inclusion in a match here.
|
||||
#
|
||||
# Currently, the only relevant property to set here is TPM2_BROKEN_NVPCR=1,
|
||||
# which should be set on TPMs where NvPCRs don't work. Specifically, because
|
||||
# on some hardware the combination of TPMA_NV_ORDERLY + TPM2_NT_EXTEND cause
|
||||
# NV_Extend() operations to time out. For details, see:
|
||||
# https://github.com/systemd/systemd/issues/40485
|
||||
|
||||
# ST33TPHF2ESPI Firmware 73.4
|
||||
tpm2:*:mfSTM:*:fw73.4.*:
|
||||
TPM2_BROKEN_NVPCR=1
|
||||
@@ -26,6 +26,7 @@ hwdb_files_test = files(
|
||||
'60-keyboard.hwdb',
|
||||
'60-seat.hwdb',
|
||||
'60-sensor.hwdb',
|
||||
'60-tpm2.hwdb',
|
||||
'70-analyzers.hwdb',
|
||||
'70-av-production.hwdb',
|
||||
'70-cameras.hwdb',
|
||||
|
||||
+30
-27
@@ -100,13 +100,14 @@ TYPES = {'mouse': ('usb', 'bluetooth', 'ps2', '*'),
|
||||
# Patterns that are used to set general properties on a device
|
||||
GENERAL_MATCHES = {'acpi',
|
||||
'bluetooth',
|
||||
'usb',
|
||||
'dmi',
|
||||
'ieee1394',
|
||||
'OUI',
|
||||
'pci',
|
||||
'sdio',
|
||||
'tpm2',
|
||||
'usb',
|
||||
'vmbus',
|
||||
'OUI',
|
||||
'ieee1394',
|
||||
'dmi',
|
||||
}
|
||||
|
||||
def upperhex_word(length):
|
||||
@@ -147,6 +148,7 @@ def property_grammar():
|
||||
mount_matrix = Group(mount_matrix_row + ';' + mount_matrix_row + ';' + mount_matrix_row)('MOUNT_MATRIX')
|
||||
xkb_setting = Optional(Word(alphanums + '+-/@._'))
|
||||
id_input_setting = Optional(Or((Literal('0'), Literal('1'))))
|
||||
zero_one = Or((Literal('0'), Literal('1')))
|
||||
|
||||
# Although this set doesn't cover all of characters in database entries, it's enough for test targets.
|
||||
name_literal = Word(printables + ' ')
|
||||
@@ -156,13 +158,13 @@ def property_grammar():
|
||||
('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', INTEGER),
|
||||
('MOUSE_WHEEL_CLICK_COUNT', INTEGER),
|
||||
('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER),
|
||||
('ID_INPUT_3D_MOUSE', Or((Literal('0'), Literal('1')))),
|
||||
('ID_AUTOSUSPEND', Or((Literal('0'), Literal('1')))),
|
||||
('ID_INPUT_3D_MOUSE', zero_one),
|
||||
('ID_AUTOSUSPEND', zero_one),
|
||||
('ID_AUTOSUSPEND_DELAY_MS', INTEGER),
|
||||
('ID_AV_PRODUCTION_CONTROLLER', Or((Literal('0'), Literal('1')))),
|
||||
('ID_AV_LIGHTS', Or((Literal('0'), Literal('1')))),
|
||||
('ID_PERSIST', Or((Literal('0'), Literal('1')))),
|
||||
('ID_PDA', Or((Literal('0'), Literal('1')))),
|
||||
('ID_AV_PRODUCTION_CONTROLLER', zero_one),
|
||||
('ID_AV_LIGHTS', zero_one),
|
||||
('ID_PERSIST', zero_one),
|
||||
('ID_PDA', zero_one),
|
||||
('ID_INPUT', id_input_setting),
|
||||
('ID_INPUT_ACCELEROMETER', id_input_setting),
|
||||
('ID_INPUT_JOYSTICK', id_input_setting),
|
||||
@@ -176,12 +178,12 @@ def property_grammar():
|
||||
('ID_INPUT_TOUCHPAD', id_input_setting),
|
||||
('ID_INPUT_TOUCHSCREEN', id_input_setting),
|
||||
('ID_INPUT_TRACKBALL', id_input_setting),
|
||||
('ID_SIGNAL_ANALYZER', Or((Literal('0'), Literal('1')))),
|
||||
('ID_MAKER_TOOL', Or((Literal('0'), Literal('1')))),
|
||||
('ID_HARDWARE_WALLET', Or((Literal('0'), Literal('1')))),
|
||||
('ID_SOFTWARE_RADIO', Or((Literal('0'), Literal('1')))),
|
||||
('ID_MM_DEVICE_IGNORE', Or((Literal('0'), Literal('1')))),
|
||||
('ID_NET_AUTO_LINK_LOCAL_ONLY', Or((Literal('0'), Literal('1')))),
|
||||
('ID_SIGNAL_ANALYZER', zero_one),
|
||||
('ID_MAKER_TOOL', zero_one),
|
||||
('ID_HARDWARE_WALLET', zero_one),
|
||||
('ID_SOFTWARE_RADIO', zero_one),
|
||||
('ID_MM_DEVICE_IGNORE', zero_one),
|
||||
('ID_NET_AUTO_LINK_LOCAL_ONLY', zero_one),
|
||||
('POINTINGSTICK_SENSITIVITY', INTEGER),
|
||||
('ID_INTEGRATION', Or(('internal', 'external'))),
|
||||
('ID_INPUT_TOUCHPAD_INTEGRATION', Or(('internal', 'external'))),
|
||||
@@ -193,25 +195,26 @@ def property_grammar():
|
||||
('ACCEL_MOUNT_MATRIX', mount_matrix),
|
||||
('ACCEL_LOCATION', Or(('display', 'base'))),
|
||||
('PROXIMITY_NEAR_LEVEL', INTEGER),
|
||||
('IEEE1394_UNIT_FUNCTION_MIDI', Or((Literal('0'), Literal('1')))),
|
||||
('IEEE1394_UNIT_FUNCTION_AUDIO', Or((Literal('0'), Literal('1')))),
|
||||
('IEEE1394_UNIT_FUNCTION_VIDEO', Or((Literal('0'), Literal('1')))),
|
||||
('IEEE1394_UNIT_FUNCTION_MIDI', zero_one),
|
||||
('IEEE1394_UNIT_FUNCTION_AUDIO', zero_one),
|
||||
('IEEE1394_UNIT_FUNCTION_VIDEO', zero_one),
|
||||
('ID_VENDOR_FROM_DATABASE', name_literal),
|
||||
('ID_MODEL_FROM_DATABASE', name_literal),
|
||||
('ID_TAG_MASTER_OF_SEAT', Literal('1')),
|
||||
('ID_INFRARED_CAMERA', Or((Literal('0'), Literal('1')))),
|
||||
('ID_INFRARED_CAMERA', zero_one),
|
||||
('ID_CAMERA_DIRECTION', Or(('front', 'rear'))),
|
||||
('SOUND_FORM_FACTOR', Or(('internal', 'webcam', 'speaker', 'headphone', 'headset', 'handset', 'microphone'))),
|
||||
('ID_SYS_VENDOR_IS_RUBBISH', Or((Literal('0'), Literal('1')))),
|
||||
('ID_PRODUCT_NAME_IS_RUBBISH', Or((Literal('0'), Literal('1')))),
|
||||
('ID_PRODUCT_VERSION_IS_RUBBISH', Or((Literal('0'), Literal('1')))),
|
||||
('ID_BOARD_VERSION_IS_RUBBISH', Or((Literal('0'), Literal('1')))),
|
||||
('ID_PRODUCT_SKU_IS_RUBBISH', Or((Literal('0'), Literal('1')))),
|
||||
('ID_CHASSIS_ASSET_TAG_IS_RUBBISH', Or((Literal('0'), Literal('1')))),
|
||||
('ID_SYS_VENDOR_IS_RUBBISH', zero_one),
|
||||
('ID_PRODUCT_NAME_IS_RUBBISH', zero_one),
|
||||
('ID_PRODUCT_VERSION_IS_RUBBISH', zero_one),
|
||||
('ID_BOARD_VERSION_IS_RUBBISH', zero_one),
|
||||
('ID_PRODUCT_SKU_IS_RUBBISH', zero_one),
|
||||
('ID_CHASSIS_ASSET_TAG_IS_RUBBISH', zero_one),
|
||||
('ID_CHASSIS', name_literal),
|
||||
('ID_SYSFS_ATTRIBUTE_MODEL', name_literal),
|
||||
('ID_NET_NAME_FROM_DATABASE', name_literal),
|
||||
('ID_NET_NAME_INCLUDE_DOMAIN', Or((Literal('0'), Literal('1')))),
|
||||
('ID_NET_NAME_INCLUDE_DOMAIN', zero_one),
|
||||
('TPM2_BROKEN_NVPCR', zero_one),
|
||||
)
|
||||
fixed_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE')
|
||||
for name, val in props]
|
||||
|
||||
@@ -195,6 +195,11 @@
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">has-tpm2</arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-analyze</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">identify-tpm2</arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-analyze</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
@@ -1028,6 +1033,27 @@ default ignore - -</programlisting>
|
||||
<xi:include href="version-info.xml" xpointer="v257"/>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title><command>systemd-analyze identify-tpm2</command></title>
|
||||
|
||||
<para>Shows vendor information about the TPM 2.0 device discovered.</para>
|
||||
|
||||
<example>
|
||||
<title>Example Output</title>
|
||||
|
||||
<programlisting> Family Indicator: 2.0
|
||||
Level: 0
|
||||
Revision: 1.59
|
||||
Specification Date: Mon 2023-01-09
|
||||
Manufacturer: STM
|
||||
Vendor String: ST33KTPM2XSPI
|
||||
Firmware Version: 9.258
|
||||
Modalias String: fi2.0:lv0:rv1.59:sy2023:sd9:mfSTM:vsST33KTPM2XSPI:ty0:fw9.258.0:</programlisting>
|
||||
</example>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v260"/>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title><command>systemd-analyze pcrs <optional><replaceable>PCR</replaceable>…</optional></command></title>
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# do not edit this file, it will be overwritten on update
|
||||
|
||||
ACTION=="remove", GOTO="tpm2_id_end"
|
||||
SUBSYSTEM!="tpmrm", GOTO="tpm2_id_end"
|
||||
KERNEL!="tpmrm[0-9]*", GOTO="tpm2_id_end"
|
||||
|
||||
IMPORT{program}="tpm2_id identify"
|
||||
ENV{ID_TPM2_MODALIAS}!="", IMPORT{builtin}="hwdb 'tpm2:$env{ID_TPM2_MODALIAS}'"
|
||||
|
||||
LABEL="tpm2_id_end"
|
||||
@@ -23,6 +23,7 @@ rules = [
|
||||
'60-persistent-v4l.rules',
|
||||
'60-sensor.rules',
|
||||
'60-serial.rules',
|
||||
'60-tpm2-id.rules',
|
||||
'65-integration.rules',
|
||||
'70-camera.rules',
|
||||
'70-joystick.rules',
|
||||
|
||||
@@ -77,7 +77,7 @@ _systemd_analyze() {
|
||||
)
|
||||
|
||||
local -A VERBS=(
|
||||
[STANDALONE]='time blame unit-files unit-paths exit-status compare-versions timestamp timespan pcrs nvpcrs srk has-tpm2 smbios11 chid image-policy'
|
||||
[STANDALONE]='time blame unit-files unit-paths exit-status compare-versions timestamp timespan pcrs nvpcrs srk has-tpm2 identify-tpm2 smbios11 chid image-policy'
|
||||
[CRITICAL_CHAIN]='critical-chain'
|
||||
[DOT]='dot'
|
||||
[DUMP]='dump'
|
||||
|
||||
@@ -2,8 +2,141 @@
|
||||
|
||||
#include "analyze.h"
|
||||
#include "analyze-has-tpm2.h"
|
||||
#include "format-table.h"
|
||||
#include "log.h"
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
#include "tpm2-util.h"
|
||||
|
||||
int verb_has_tpm2(int argc, char **argv, void *userdata) {
|
||||
return verb_has_tpm2_generic(arg_quiet);
|
||||
}
|
||||
|
||||
int verb_identify_tpm2(int argc, char **argv, void *userdata) {
|
||||
#if HAVE_TPM2
|
||||
int r;
|
||||
|
||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
||||
r = tpm2_context_new_or_warn(/* device= */ NULL, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
Tpm2VendorInfo info;
|
||||
r = tpm2_get_vendor_info(c, &info);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire TPM2 vendor information: %m");
|
||||
|
||||
_cleanup_(table_unrefp) Table *table = table_new_vertical();
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
if (!isempty(info.family_indicator)) {
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_FIELD, "Family Indicator",
|
||||
TABLE_STRING, info.family_indicator);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
_cleanup_free_ char *rv = NULL;
|
||||
if (asprintf(&rv, "%" PRIu32 ".%" PRIu32,
|
||||
info.revision_major,
|
||||
info.revision_minor) < 0)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_FIELD, "Level",
|
||||
TABLE_UINT32, info.level,
|
||||
TABLE_FIELD, "Revision",
|
||||
TABLE_STRING, rv);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
if (info.year >= 1900) {
|
||||
struct tm tm = {
|
||||
.tm_year = info.year - 1900,
|
||||
.tm_mon = 0, /* january */
|
||||
.tm_mday = info.day_of_year, /* timegm() will normalize this */
|
||||
};
|
||||
|
||||
usec_t ts;
|
||||
r = mktime_or_timegm_usec(&tm, /* utc= */ true, &ts);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to convert the specification date, ignoring.");
|
||||
else {
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_FIELD, "Specification Date",
|
||||
TABLE_TIMESTAMP_DATE, ts);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isempty(info.manufacturer)) {
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_FIELD, "Manufacturer",
|
||||
TABLE_STRING, info.manufacturer);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
if (!isempty(info.vendor_string)) {
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_FIELD, "Vendor String",
|
||||
TABLE_STRING, info.vendor_string);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
if (info.vendor_tpm_type != 0) {
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_FIELD, "Vendor TPM Type",
|
||||
TABLE_UINT32_HEX_0x, info.vendor_tpm_type);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
/* Show the first two 16bit words of the firmware version as major/minor */
|
||||
_cleanup_free_ char *fw = NULL;
|
||||
if (asprintf(&fw, "%" PRIu16 ".%" PRIu16,
|
||||
info.firmware_version_major,
|
||||
info.firmware_version_minor) < 0)
|
||||
return log_oom();
|
||||
|
||||
/* Show the second 32bit as a single value, if non-zero */
|
||||
if (info.firmware_version2 != 0 && strextendf(&fw, ".%" PRIu32, info.firmware_version2) < 0)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_FIELD, "Firmware Version",
|
||||
TABLE_STRING, fw);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
_cleanup_free_ char *m = NULL;
|
||||
if (tpm2_vendor_info_to_modalias(&info, &m) < 0)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_FIELD, "Modalias String",
|
||||
TABLE_STRING, m);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
#else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support not enabled at build time.");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
#pragma once
|
||||
|
||||
int verb_has_tpm2(int argc, char *argv[], void *userdata);
|
||||
int verb_identify_tpm2(int argc, char *argv[], void *userdata);
|
||||
|
||||
@@ -258,6 +258,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" dlopen-metadata FILE Parse and print ELF dlopen metadata\n"
|
||||
"\n%3$sTPM Operations:%4$s\n"
|
||||
" has-tpm2 Report whether TPM2 support is available\n"
|
||||
" identify-tpm2 Show TPM2 vendor information\n"
|
||||
" pcrs [PCR...] Show TPM2 PCRs and their names\n"
|
||||
" nvpcrs [NVPCR...] Show additional TPM2 PCRs stored in NV indexes\n"
|
||||
" srk [>FILE] Write TPM2 SRK (to FILE)\n"
|
||||
@@ -810,6 +811,7 @@ static int run(int argc, char *argv[]) {
|
||||
{ "fdstore", 2, VERB_ANY, 0, verb_fdstore },
|
||||
{ "image-policy", 2, 2, 0, verb_image_policy },
|
||||
{ "has-tpm2", VERB_ANY, 1, 0, verb_has_tpm2 },
|
||||
{ "identify-tpm2", VERB_ANY, 1, 0, verb_identify_tpm2 },
|
||||
{ "pcrs", VERB_ANY, VERB_ANY, 0, verb_pcrs },
|
||||
{ "nvpcrs", VERB_ANY, VERB_ANY, 0, verb_nvpcrs },
|
||||
{ "srk", VERB_ANY, 1, 0, verb_srk },
|
||||
|
||||
+19
-1
@@ -47,9 +47,11 @@ int dlsym_many_or_warn_sentinel(void *dl, int log_level, ...) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_level, ...) {
|
||||
int dlopen_verbose(void **dlp, const char *filename) {
|
||||
int r;
|
||||
|
||||
assert(dlp);
|
||||
|
||||
if (*dlp)
|
||||
return 0; /* Already loaded */
|
||||
|
||||
@@ -62,6 +64,22 @@ int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_l
|
||||
}
|
||||
|
||||
log_debug("Loaded shared library '%s' via dlopen().", filename);
|
||||
*dlp = TAKE_PTR(dl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_level, ...) {
|
||||
int r;
|
||||
|
||||
assert(dlp);
|
||||
|
||||
if (*dlp)
|
||||
return 0; /* Already loaded */
|
||||
|
||||
_cleanup_(dlclosep) void *dl = NULL;
|
||||
r = dlopen_verbose(&dl, filename);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, log_level);
|
||||
|
||||
@@ -11,6 +11,7 @@ static inline void dlclosep(void **dlp) {
|
||||
safe_dlclose(*dlp);
|
||||
}
|
||||
|
||||
int dlopen_verbose(void **dlp, const char *filename);
|
||||
int dlsym_many_or_warn_sentinel(void *dl, int log_level, ...) _sentinel_;
|
||||
int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_level, ...) _sentinel_;
|
||||
|
||||
|
||||
+236
-5
@@ -3,6 +3,8 @@
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ansi-color.h"
|
||||
#include "bitfield.h"
|
||||
@@ -11,6 +13,8 @@
|
||||
#include "constants.h"
|
||||
#include "creds-util.h"
|
||||
#include "cryptsetup-util.h"
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "dlfcn-util.h"
|
||||
#include "efi-api.h"
|
||||
@@ -43,6 +47,7 @@
|
||||
#include "time-util.h"
|
||||
#include "tpm2-pcr.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "unaligned.h"
|
||||
#include "virt.h"
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
@@ -53,6 +58,7 @@
|
||||
static void *libtss2_esys_dl = NULL;
|
||||
static void *libtss2_rc_dl = NULL;
|
||||
static void *libtss2_mu_dl = NULL;
|
||||
static void *libtss2_tcti_device_dl = NULL;
|
||||
|
||||
static DLSYM_PROTOTYPE(Esys_Create) = NULL;
|
||||
static DLSYM_PROTOTYPE(Esys_CreateLoaded) = NULL;
|
||||
@@ -226,6 +232,20 @@ static int dlopen_tpm2_mu(void) {
|
||||
DLSYM_ARG(Tss2_MU_UINT32_Marshal));
|
||||
}
|
||||
|
||||
static int dlopen_tpm2_tcti_device(void) {
|
||||
/* The "device" TCTI is the most relevant one, let's also load it explicitly on dlopen_tpm2(), even
|
||||
* if we don't resolve any symbols here. */
|
||||
|
||||
ELF_NOTE_DLOPEN("tpm",
|
||||
"Support for TPM",
|
||||
ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
|
||||
"libtss2-tcti-device.so.0");
|
||||
|
||||
return dlopen_verbose(
|
||||
&libtss2_tcti_device_dl,
|
||||
"libtss2-tcti-device.so.0");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int dlopen_tpm2(void) {
|
||||
@@ -244,6 +264,10 @@ int dlopen_tpm2(void) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dlopen_tpm2_tcti_device();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
@@ -329,6 +353,151 @@ static int tpm2_get_capability(
|
||||
return more == TPM2_YES;
|
||||
}
|
||||
|
||||
int tpm2_vendor_info_to_modalias(const Tpm2VendorInfo *info, char **ret) {
|
||||
_cleanup_free_ char *m = NULL;
|
||||
|
||||
assert(info);
|
||||
assert(ret);
|
||||
|
||||
/* Closely inspired by kernel modalias strings, this distills information from the TPM vendor data
|
||||
* into a string suitable for matching hwdb */
|
||||
|
||||
if (asprintf(&m,
|
||||
"fi%s:"
|
||||
"lv%" PRIu32 ":"
|
||||
"rv%" PRIu32 ".%" PRIu32 ":"
|
||||
"sy%" PRIu32 ":"
|
||||
"sd%" PRIu32 ":"
|
||||
"mf%s:"
|
||||
"vs%s:"
|
||||
"ty%" PRIx32 ":"
|
||||
"fw%" PRIu16 ".%" PRIu16 ".%" PRIu32 ":",
|
||||
info->family_indicator,
|
||||
info->level,
|
||||
info->revision_major,
|
||||
info->revision_minor,
|
||||
info->year,
|
||||
info->day_of_year,
|
||||
info->manufacturer,
|
||||
info->vendor_string,
|
||||
info->vendor_tpm_type,
|
||||
info->firmware_version_major,
|
||||
info->firmware_version_minor,
|
||||
info->firmware_version2) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *mangle_vendor_chars(char *c, size_t n) {
|
||||
char *end = c;
|
||||
assert(c || n == 0);
|
||||
|
||||
/* Suppress all control characters, whitespace and non-ASCII bytes */
|
||||
for (char *x = c; x < c + n; x++) {
|
||||
if (!IN_SET(*x, ' ', 0))
|
||||
end = x + 1;
|
||||
|
||||
if ((unsigned char) *x <= (unsigned char) ' ' ||
|
||||
(unsigned char) *x >= 127)
|
||||
*x = '_';
|
||||
}
|
||||
|
||||
/* Drop trailing spaces and NUL bytes */
|
||||
*end = 0;
|
||||
return c;
|
||||
}
|
||||
|
||||
int tpm2_get_vendor_info(
|
||||
Tpm2Context *c,
|
||||
Tpm2VendorInfo *ret) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(ret);
|
||||
|
||||
TPMU_CAPABILITIES capabilities = {};
|
||||
r = tpm2_get_capability(
|
||||
c,
|
||||
TPM2_CAP_TPM_PROPERTIES,
|
||||
TPM2_PT_FAMILY_INDICATOR,
|
||||
TPM2_PT_FIRMWARE_VERSION_2 - TPM2_PT_FAMILY_INDICATOR + 1, /* get all relevant fields at once */
|
||||
&capabilities);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
Tpm2VendorInfo info = {};
|
||||
|
||||
for (uint32_t i = 0; i < capabilities.tpmProperties.count; i++) {
|
||||
TPMS_TAGGED_PROPERTY *p = capabilities.tpmProperties.tpmProperty + i;
|
||||
|
||||
switch (p->property) {
|
||||
|
||||
case TPM2_PT_FAMILY_INDICATOR:
|
||||
unaligned_write_be32(info.family_indicator, p->value);
|
||||
mangle_vendor_chars(info.family_indicator, sizeof(info.family_indicator));
|
||||
break;
|
||||
|
||||
case TPM2_PT_LEVEL:
|
||||
info.level = p->value;
|
||||
break;
|
||||
|
||||
case TPM2_PT_REVISION:
|
||||
info.revision_major = p->value / 100;
|
||||
info.revision_minor = p->value % 100;
|
||||
break;
|
||||
|
||||
case TPM2_PT_DAY_OF_YEAR:
|
||||
info.day_of_year = p->value;
|
||||
break;
|
||||
|
||||
case TPM2_PT_YEAR:
|
||||
info.year = p->value;
|
||||
break;
|
||||
|
||||
case TPM2_PT_MANUFACTURER:
|
||||
unaligned_write_be32(info.manufacturer, p->value);
|
||||
mangle_vendor_chars(info.manufacturer, sizeof(info.manufacturer));
|
||||
break;
|
||||
|
||||
case TPM2_PT_VENDOR_STRING_1:
|
||||
unaligned_write_be32(info.vendor_string+0, p->value);
|
||||
break;
|
||||
|
||||
case TPM2_PT_VENDOR_STRING_2:
|
||||
unaligned_write_be32(info.vendor_string+4, p->value);
|
||||
break;
|
||||
|
||||
case TPM2_PT_VENDOR_STRING_3:
|
||||
unaligned_write_be32(info.vendor_string+8, p->value);
|
||||
break;
|
||||
|
||||
case TPM2_PT_VENDOR_STRING_4:
|
||||
unaligned_write_be32(info.vendor_string+12, p->value);
|
||||
break;
|
||||
|
||||
case TPM2_PT_VENDOR_TPM_TYPE:
|
||||
info.vendor_tpm_type = p->value;
|
||||
break;
|
||||
|
||||
case TPM2_PT_FIRMWARE_VERSION_1:
|
||||
info.firmware_version_major = p->value >> 16;
|
||||
info.firmware_version_minor = p->value & 0xFFFFU;
|
||||
break;
|
||||
|
||||
case TPM2_PT_FIRMWARE_VERSION_2:
|
||||
info.firmware_version2 = p->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mangle_vendor_chars(info.vendor_string, sizeof(info.vendor_string));
|
||||
*ret = TAKE_STRUCT(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TPMA_CC_TO_TPM2_CC(cca) (((cca) & TPMA_CC_COMMANDINDEX_MASK) >> TPMA_CC_COMMANDINDEX_SHIFT)
|
||||
|
||||
static int tpm2_cache_capabilities(Tpm2Context *c) {
|
||||
@@ -671,6 +840,9 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
|
||||
c->capability_commands = mfree(c->capability_commands);
|
||||
c->capability_ecc_curves = mfree(c->capability_ecc_curves);
|
||||
|
||||
c->tcti_driver = mfree(c->tcti_driver);
|
||||
c->tcti_param = mfree(c->tcti_param);
|
||||
|
||||
return mfree(c);
|
||||
}
|
||||
|
||||
@@ -716,7 +888,8 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
|
||||
}
|
||||
|
||||
if (device) {
|
||||
const char *param, *driver, *fn;
|
||||
_cleanup_free_ char *_driver = NULL;
|
||||
const char *param, *driver;
|
||||
const TSS2_TCTI_INFO* info;
|
||||
TSS2_TCTI_INFO_FUNC func;
|
||||
size_t sz = 0;
|
||||
@@ -724,7 +897,9 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
|
||||
param = strchr(device, ':');
|
||||
if (param) {
|
||||
/* Syntax #1: Pair of driver string and arbitrary parameter */
|
||||
driver = strndupa_safe(device, param - device);
|
||||
driver = _driver = strndup(device, param - device);
|
||||
if (!driver)
|
||||
return log_oom_debug();
|
||||
if (isempty(driver))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 driver name is empty, refusing.");
|
||||
|
||||
@@ -738,7 +913,9 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
|
||||
|
||||
log_debug("Using TPM2 TCTI driver '%s' with device '%s'.", driver, param);
|
||||
|
||||
fn = strjoina("libtss2-tcti-", driver, ".so.0");
|
||||
_cleanup_free_ char *fn = strjoin("libtss2-tcti-", driver, ".so.0");
|
||||
if (!fn)
|
||||
return log_oom_debug();
|
||||
|
||||
/* Better safe than sorry, let's refuse strings that cannot possibly be valid driver early, before going to disk. */
|
||||
if (!filename_is_valid(fn))
|
||||
@@ -778,6 +955,14 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
|
||||
if (rc != TPM2_RC_SUCCESS)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
"Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
|
||||
|
||||
context->tcti_driver = strdup(driver);
|
||||
if (!context->tcti_driver)
|
||||
return log_oom_debug();
|
||||
|
||||
context->tcti_param = strdup(param);
|
||||
if (!context->tcti_param)
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
rc = sym_Esys_Initialize(&context->esys_context, context->tcti_context, NULL);
|
||||
@@ -5976,7 +6161,8 @@ int tpm2_undefine_nv_index(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_define_nvpcr_nv_index(
|
||||
#if HAVE_OPENSSL
|
||||
static int tpm2_define_nvpcr_nv_index(
|
||||
Tpm2Context *c,
|
||||
const Tpm2Handle *session,
|
||||
TPM2_HANDLE nv_index,
|
||||
@@ -6095,7 +6281,7 @@ int tpm2_define_nvpcr_nv_index(
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tpm2_extend_nvpcr_nv_index(
|
||||
static int tpm2_extend_nvpcr_nv_index(
|
||||
Tpm2Context *c,
|
||||
TPM2_HANDLE nv_index,
|
||||
const Tpm2Handle *nv_handle,
|
||||
@@ -6136,6 +6322,7 @@ int tpm2_extend_nvpcr_nv_index(
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int tpm2_read_nv_index(
|
||||
Tpm2Context *c,
|
||||
@@ -7307,6 +7494,44 @@ int tpm2_nvpcr_acquire_anchor_secret(struct iovec *ret, bool sync_secondary) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
static int tpm2_context_can_nvindex(Tpm2Context *c) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
if (!streq_ptr(c->tcti_driver, "device")) {
|
||||
log_debug("Not checking udev database, because not using 'device' TCTI.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!c->tcti_param) {
|
||||
log_debug("No device specified, not checking udev database.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
|
||||
r = sd_device_new_from_devname(&d, c->tcti_param);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to acquire udev entry for device '%s': %m", c->tcti_param);
|
||||
|
||||
r = device_get_property_bool(d, "TPM2_BROKEN_NVPCR");
|
||||
if (r == -ENOENT) {
|
||||
log_device_debug_errno(d, r, "No TPM2_BROKEN_NVPCR property for '%s', assuming NvPCRs work.", c->tcti_param);
|
||||
return 1;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(d, r, "Failed to query TPM2_BROKEN_NVPCR for '%s': %m", c->tcti_param);
|
||||
if (r > 0) {
|
||||
log_device_debug(d, "TPM2_BROKEN_NVPCR property for '%s' explicitly set, NvPCRs do not work.", c->tcti_param);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_device_debug(d, "TPM2_BROKEN_NVPCR property for '%s' explicitly set to false, hence NvPCRs work.", c->tcti_param);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int tpm2_nvpcr_initialize(
|
||||
Tpm2Context *c,
|
||||
const Tpm2Handle *session,
|
||||
@@ -7320,6 +7545,12 @@ int tpm2_nvpcr_initialize(
|
||||
assert(c);
|
||||
assert(name);
|
||||
|
||||
r = tpm2_context_can_nvindex(c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 device does not support NvPCRs, not initializing.");
|
||||
|
||||
_cleanup_(nvpcr_data_done) NvPCRData p = {};
|
||||
r = nvpcr_data_load(name, &p);
|
||||
if (r < 0)
|
||||
|
||||
+20
-2
@@ -53,6 +53,8 @@ typedef struct Tpm2Context {
|
||||
void *tcti_dl;
|
||||
TSS2_TCTI_CONTEXT *tcti_context;
|
||||
ESYS_CONTEXT *esys_context;
|
||||
char *tcti_driver;
|
||||
char *tcti_param;
|
||||
|
||||
/* Some selected cached capabilities of the TPM */
|
||||
TPMS_ALG_PROPERTY *capability_algorithms;
|
||||
@@ -195,6 +197,24 @@ static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
|
||||
return tpm2_digest_many(alg, digest, NULL, 0, false);
|
||||
}
|
||||
|
||||
typedef struct Tpm2VendorInfo {
|
||||
uint32_t level;
|
||||
uint32_t revision_major;
|
||||
uint32_t revision_minor;
|
||||
uint32_t day_of_year;
|
||||
uint32_t year;
|
||||
uint32_t vendor_tpm_type;
|
||||
uint16_t firmware_version_major;
|
||||
uint16_t firmware_version_minor;
|
||||
uint32_t firmware_version2;
|
||||
char family_indicator[4+1];
|
||||
char manufacturer[4+1];
|
||||
char vendor_string[4*4+1];
|
||||
} Tpm2VendorInfo;
|
||||
|
||||
int tpm2_vendor_info_to_modalias(const Tpm2VendorInfo *info, char **ret);
|
||||
int tpm2_get_vendor_info(Tpm2Context *c, Tpm2VendorInfo *ret);
|
||||
|
||||
void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg);
|
||||
void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg);
|
||||
void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg);
|
||||
@@ -304,8 +324,6 @@ int tpm2_tpm2b_public_to_fingerprint(const TPM2B_PUBLIC *public, void **ret_fing
|
||||
|
||||
int tpm2_define_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE requested_nv_index, const TPM2B_DIGEST *write_policy, TPM2_HANDLE *ret_nv_index, Tpm2Handle **ret_nv_handle, TPM2B_NV_PUBLIC *ret_nv_public);
|
||||
int tpm2_write_policy_nv_index(Tpm2Context *c, const Tpm2Handle *policy_session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, const TPM2B_DIGEST *policy_digest);
|
||||
int tpm2_define_nvpcr_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, TPMI_ALG_HASH algorithm, Tpm2Handle **ret_nv_handle);
|
||||
int tpm2_extend_nvpcr_nv_index(Tpm2Context *c, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, const struct iovec *digest);
|
||||
int tpm2_undefine_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle);
|
||||
int tpm2_read_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, struct iovec *ret_value);
|
||||
|
||||
|
||||
@@ -309,6 +309,9 @@ _SD_BEGIN_DECLARATIONS;
|
||||
#define SD_MESSAGE_TPM_NVINDEX_EXHAUSTED SD_ID128_MAKE(ab,98,4e,a0,08,96,4f,b8,8d,6e,38,9f,b5,13,fb,94)
|
||||
#define SD_MESSAGE_TPM_NVINDEX_EXHAUSTED_STR SD_ID128_MAKE_STR(ab,98,4e,a0,08,96,4f,b8,8d,6e,38,9f,b5,13,fb,94)
|
||||
|
||||
#define SD_MESSAGE_TPM_NVPCR_UNSUPPORTED SD_ID128_MAKE(8f,07,a5,b8,14,ca,47,62,b8,9f,cc,30,82,e4,8a,ed)
|
||||
#define SD_MESSAGE_TPM_NVPCR_UNSUPPORTED_STR SD_ID128_MAKE_STR(8f,07,a5,b8,14,ca,47,62,b8,9f,cc,30,82,e4,8a,ed)
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <getopt.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include "sd-messages.h"
|
||||
|
||||
@@ -291,8 +292,8 @@ static int setup_srk(void) {
|
||||
log_struct_errno(LOG_INFO, r,
|
||||
LOG_MESSAGE("Insufficient permissions to access TPM, not generating SRK."),
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_SRK_ENROLLMENT_NEEDS_AUTHORIZATION_STR));
|
||||
return 76; /* Special return value which means "Insufficient permissions to access TPM,
|
||||
* cannot generate SRK". This isn't really an error when called at boot. */;
|
||||
return EX_PROTOCOL; /* Special return value which means "Insufficient permissions to access TPM,
|
||||
* cannot generate SRK". This isn't really an error when called at boot. */;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -422,6 +423,12 @@ static int setup_nvpcr_one(
|
||||
|
||||
r = tpm2_nvpcr_initialize(c->tpm2_context, /* session= */ NULL, name, &c->anchor_secret);
|
||||
}
|
||||
if (r == -EOPNOTSUPP) {
|
||||
c->n_failed++;
|
||||
return log_struct_errno(LOG_ERR, r,
|
||||
LOG_MESSAGE("The TPM does not correctly support NV indexes in NT_EXTEND mode, unable to allocate NvPCR '%s': %m", name),
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_TPM_NVPCR_UNSUPPORTED_STR));
|
||||
}
|
||||
if (r == -ENOSPC) {
|
||||
c->n_failed++;
|
||||
return log_struct_errno(LOG_ERR, r,
|
||||
@@ -446,7 +453,7 @@ static int setup_nvpcr_one(
|
||||
|
||||
static int setup_nvpcr(void) {
|
||||
_cleanup_(setup_nvpcr_context_done) SetupNvPCRContext c = {};
|
||||
int r = 0;
|
||||
int r;
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
r = conf_files_list_nulstr(
|
||||
@@ -471,13 +478,11 @@ static int setup_nvpcr(void) {
|
||||
RET_GATHER(ret, setup_nvpcr_one(&c, *i));
|
||||
}
|
||||
|
||||
if (c.n_already > 0 && c.n_anchored == 0 && !arg_early) {
|
||||
if (c.n_already > 0 && c.n_anchored == 0 && !arg_early)
|
||||
/* If we didn't anchor anything right now, but we anchored something earlier, then it might
|
||||
* have happened in the initrd, and thus the anchor ID was not committed to /var/ or the ESP
|
||||
* yet. Hence, let's explicitly do so now, to catch up. */
|
||||
|
||||
RET_GATHER(ret, tpm2_nvpcr_acquire_anchor_secret(/* ret= */ NULL, /* sync_secondary= */ true));
|
||||
}
|
||||
|
||||
if (c.n_failed > 0)
|
||||
log_warning("%zu NvPCRs failed to initialize, proceeding anyway.", c.n_failed);
|
||||
@@ -492,6 +497,13 @@ static int setup_nvpcr(void) {
|
||||
else if (c.n_failed == 0)
|
||||
log_debug("No NvPCRs defined, nothing initialized.");
|
||||
|
||||
/* Turn some errors into recognizable ones, which we can catch with
|
||||
* SuccessExitStatus= in the service unit file. */
|
||||
if (ret == -EOPNOTSUPP)
|
||||
return EX_UNAVAILABLE; /* e.g. no NvPCR support in TPM */
|
||||
if (ret == -ENOSPC)
|
||||
return EX_CANTCREAT; /* NV index space on TPM exhausted */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -511,10 +523,15 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
umask(0022);
|
||||
|
||||
/* Execute both jobs, and then return unlisted errors preferably, and listed errors
|
||||
* (i.e. EX_UNAVAILABLE, EX_CANTCREAT, EX_PROTOCOL) otherwise. */
|
||||
r = setup_srk();
|
||||
RET_GATHER(r, setup_nvpcr());
|
||||
|
||||
return r;
|
||||
int k = setup_nvpcr();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (k < 0)
|
||||
return k;
|
||||
return r != EXIT_SUCCESS ? r : k;
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
|
||||
|
||||
@@ -59,6 +59,10 @@ if conf.get('HAVE_ACL') == 1
|
||||
udevadm_extract_sources += files('udev-builtin-uaccess.c')
|
||||
endif
|
||||
|
||||
if conf.get('HAVE_TPM2') == 1
|
||||
udevadm_extract_sources += files('udev-builtin-tpm2_id.c')
|
||||
endif
|
||||
|
||||
############################################################
|
||||
|
||||
generate_keyboard_keys_list = files('generate-keyboard-keys-list.sh')
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "device-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "udev-builtin.h"
|
||||
|
||||
static int builtin_tpm2_id(UdevEvent *event, int argc, char *argv[]) {
|
||||
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
|
||||
int r;
|
||||
|
||||
if (argc != 2 || !streq(argv[1], "identify"))
|
||||
return log_device_error_errno(
|
||||
dev, SYNTHETIC_ERRNO(EINVAL), "%s: expected: identify", argv[0]);
|
||||
|
||||
const char *dn;
|
||||
r = sd_device_get_devname(dev, &dn);
|
||||
if (r < 0)
|
||||
return log_device_error_errno(dev, r, "Failed to get device node for device: %m");
|
||||
|
||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
||||
r = tpm2_context_new(dn, &c);
|
||||
if (r < 0)
|
||||
return log_device_error_errno(dev, r, "Failed to open device node '%s': %m", dn);
|
||||
|
||||
Tpm2VendorInfo info;
|
||||
r = tpm2_get_vendor_info(c, &info);
|
||||
if (r < 0)
|
||||
return log_device_error_errno(dev, r, "Failed to acquire TPM2 vendor information: %m");
|
||||
|
||||
if (!isempty(info.manufacturer)) {
|
||||
r = udev_builtin_add_property(event, "ID_TPM2_MANUFACTURER", info.manufacturer);
|
||||
if (r < 0)
|
||||
return log_device_error_errno(dev, r, "Failed to set field: %m");
|
||||
}
|
||||
|
||||
if (!isempty(info.vendor_string)) {
|
||||
r = udev_builtin_add_property(event, "ID_TPM2_VENDOR_STRING", info.vendor_string);
|
||||
if (r < 0)
|
||||
return log_device_error_errno(dev, r, "Failed to set field: %m");
|
||||
}
|
||||
|
||||
_cleanup_free_ char *m = NULL;
|
||||
r = tpm2_vendor_info_to_modalias(&info, &m);
|
||||
if (r < 0)
|
||||
return log_device_error_errno(dev, r, "Failed to get modalias string for TPM2 device: %m");
|
||||
|
||||
r = udev_builtin_add_property(event, "ID_TPM2_MODALIAS", m);
|
||||
if (r < 0)
|
||||
return log_device_error_errno(dev, r, "Failed to set field: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const UdevBuiltin udev_builtin_tpm2_id = {
|
||||
.name = "tpm2_id",
|
||||
.cmd = builtin_tpm2_id,
|
||||
.help = "Identify TPM2 chips",
|
||||
.run_once = true,
|
||||
};
|
||||
@@ -29,6 +29,9 @@ static const UdevBuiltin *const builtins[_UDEV_BUILTIN_MAX] = {
|
||||
[UDEV_BUILTIN_NET_ID] = &udev_builtin_net_id,
|
||||
[UDEV_BUILTIN_NET_LINK] = &udev_builtin_net_setup_link,
|
||||
[UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id,
|
||||
#if HAVE_TPM2
|
||||
[UDEV_BUILTIN_TPM2_ID] = &udev_builtin_tpm2_id,
|
||||
#endif
|
||||
#if HAVE_ACL
|
||||
[UDEV_BUILTIN_UACCESS] = &udev_builtin_uaccess,
|
||||
#endif
|
||||
|
||||
@@ -43,6 +43,9 @@ extern const UdevBuiltin udev_builtin_net_driver;
|
||||
extern const UdevBuiltin udev_builtin_net_id;
|
||||
extern const UdevBuiltin udev_builtin_net_setup_link;
|
||||
extern const UdevBuiltin udev_builtin_path_id;
|
||||
#if HAVE_TPM2
|
||||
extern const UdevBuiltin udev_builtin_tpm2_id;
|
||||
#endif
|
||||
#if HAVE_ACL
|
||||
extern const UdevBuiltin udev_builtin_uaccess;
|
||||
#endif
|
||||
|
||||
@@ -51,6 +51,9 @@ typedef enum UdevBuiltinCommand {
|
||||
UDEV_BUILTIN_NET_ID,
|
||||
UDEV_BUILTIN_NET_LINK,
|
||||
UDEV_BUILTIN_PATH_ID,
|
||||
#if HAVE_TPM2
|
||||
UDEV_BUILTIN_TPM2_ID,
|
||||
#endif
|
||||
#if HAVE_ACL
|
||||
UDEV_BUILTIN_UACCESS,
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "process-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "udev-config.h"
|
||||
#include "udev-manager.h"
|
||||
#include "udevd.h"
|
||||
@@ -61,6 +62,7 @@ int run_udevd(int argc, char *argv[]) {
|
||||
(void) dlopen_libblkid();
|
||||
(void) dlopen_libkmod();
|
||||
(void) dlopen_libmount();
|
||||
(void) dlopen_tpm2();
|
||||
|
||||
if (arg_daemonize) {
|
||||
pid_t pid;
|
||||
|
||||
@@ -103,3 +103,6 @@ systemd-dissect --image-policy='root=signed:=absent+unused' --mtree /var/tmp/nvp
|
||||
|
||||
set +o pipefail
|
||||
diff /tmp/nvpcr/log-before /run/log/systemd/tpm2-measure.log | grep -F '"content":{"nvIndexName":"verity","string":"verity:'
|
||||
|
||||
systemd-analyze identify-tpm2
|
||||
udevadm test-builtin 'tpm2_id identify' /dev/tpmrm0
|
||||
|
||||
@@ -22,5 +22,9 @@ Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart={{LIBEXECDIR}}/systemd-tpm2-setup --early=yes --graceful
|
||||
|
||||
# The tool returns 76 if the TPM cannot be accessed due to an authorization failure and we can't generate an SRK.
|
||||
SuccessExitStatus=76
|
||||
# The tool returns EX_PROTOCOL if the TPM cannot be accessed due to an authorization failure and we can't generate an SRK.
|
||||
SuccessExitStatus=PROTOCOL
|
||||
# The tool returns EX_UNAVAILABLE if some key functionality (e.g. NvPCR) support is not available in the TPM device.
|
||||
SuccessExitStatus=UNAVAILABLE
|
||||
# The tool returns EX_CANTCREAT if the NV index space is exhausted.
|
||||
SuccessExitStatus=CANTCREAT
|
||||
|
||||
@@ -23,5 +23,9 @@ Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart={{LIBEXECDIR}}/systemd-tpm2-setup --graceful
|
||||
|
||||
# The tool returns 76 if the TPM cannot be accessed due to an authorization failure and we can't generate an SRK.
|
||||
SuccessExitStatus=76
|
||||
# The tool returns EX_PROTOCOL if the TPM cannot be accessed due to an authorization failure and we can't generate an SRK.
|
||||
SuccessExitStatus=PROTOCOL
|
||||
# The tool returns EX_UNAVAILABLE if some key functionality (e.g. NvPCR) support is not available in the TPM device.
|
||||
SuccessExitStatus=UNAVAILABLE
|
||||
# The tool returns EX_CANTCREAT if the NV index space is exhausted.
|
||||
SuccessExitStatus=CANTCREAT
|
||||
|
||||
Reference in New Issue
Block a user