Update to upstream commit 0186093

Related: rhbz#2084552

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
This commit is contained in:
Anderson Toshiyuki Sasaki 2022-08-25 12:00:46 +02:00
parent e8f918028a
commit 02a44a5bc5
11 changed files with 112 additions and 1980 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
/rust-keylime-0.1.0~20220603gitaed51c7-vendor.tar.xz
/rust-keylime-0.1.0~20220603gitaed51c7.tar.gz
/rust-keylime-0.1.0~20220805git0186093.tar.gz
/rust-keylime-0.1.0~20220805git0186093-vendor.tar.xz

View File

@ -6,16 +6,16 @@
%global crate keylime_agent
%global crate_version 0.1.0
%global commit aed51c7c8c526953e945357594352c3df2ca4ace
%global commit 01860934f7308bc5ea1e68c8d858aea056620ce8
%global shortcommit %(c=%{commit}; echo ${c:0:7})
%global commitdate 20220603
%global commitdate 20220805
# RHEL: Use bundled deps as it doesn't ship Rust libraries
%global bundled_rust_deps 1
Name: keylime-agent-rust
Version: %{crate_version}~%{commitdate}git%{shortcommit}
Release: 4%{?dist}
Release: 1%{?dist}
Summary: Rust agent for Keylime
# Upstream license specification: Apache-2.0
@ -40,28 +40,20 @@ URL: https://github.com/keylime/rust-keylime/
# The source tarball is downloaded using the following commands:
# spectool -g keylime-agent-rust.spec
Source0: %{url}/archive/%{commit}/rust-keylime-%{version}.tar.gz
# The vendor tarball is created using cargo vendor:
# The vendor tarball is created using cargo-vendor-filterer to remove Windows
# related files (https://github.com/cgwalters/cargo-vendor-filterer)
# tar xf rust-keylime-%%{version}.tar.gz
# cd rust-keylime-%%{version}
# cargo vendor
# cargo vendor-filterer --platform x86_64-unknown-linux-gnu \
# --platform powerpc64le-unknown-linux-gnu \
# --platform aarch64-unknown-linux-gnu \
# --platform i686-unknown-linux-gnu \
# --platform s390x-unknown-linux-gnu \
# --exclude-crate-path "libloading#tests"
# tar jcf rust-keylime-%%{version}-vendor.tar.xz vendor
Source1: rust-keylime-%{version}-vendor.tar.xz
# Drop dependencies and adjust the features
Patch0: rust-keylime-drop-dependencies.patch
# Add serialization functions to fix issue on big-endian arches
Patch1: rust-keylime-add-quote-serialization.patch
# Show path on missing mTLS certificate
Patch2: rust-keylime-show-path-missing-cert.patch
# Use more descriptive error messages for missing files errors
Patch3: rust-keylime-descriptive-error-messages.patch
# Set supplementary groups when dropping privileges
Patch4: rust-keylime-set-supplementary-groups.patch
# Adjust the supported and required features
Patch5: rust-keylime-adjust-features.patch
# Load configuration file only once during startup
Patch6: rust-keylime-load-config-once.patch
# Add support for PCR hash algorithms other than SHA-1 to the IMA emulator
Patch7: rust-keylime-ima-emulator-hash-algorithms.patch
Patch0: rust-keylime-adjust-features.patch
ExclusiveArch: %{rust_arches}
@ -87,8 +79,8 @@ Provides: bundled(crate(actix-codec)) = 0.5.0
Provides: bundled(crate(actix-http)) = 3.0.4
Provides: bundled(crate(actix-macros)) = 0.2.3
Provides: bundled(crate(actix-router)) = 0.5.0
Provides: bundled(crate(actix-rt)) = 2.6.0
Provides: bundled(crate(actix-server)) = 2.0.0
Provides: bundled(crate(actix-rt)) = 2.7.0
Provides: bundled(crate(actix-server)) = 2.1.1
Provides: bundled(crate(actix-service)) = 2.0.2
Provides: bundled(crate(actix-tls)) = 3.0.3
Provides: bundled(crate(actix-utils)) = 3.0.0
@ -108,36 +100,36 @@ Provides: bundled(crate(bindgen)) = 0.59.2
Provides: bundled(crate(bitfield)) = 0.13.2
Provides: bundled(crate(bitflags)) = 1.3.2
Provides: bundled(crate(block-buffer)) = 0.10.2
Provides: bundled(crate(brotli)) = 3.3.3
Provides: bundled(crate(brotli)) = 3.3.4
Provides: bundled(crate(brotli-decompressor)) = 2.3.2
Provides: bundled(crate(bytes)) = 1.1.0
Provides: bundled(crate(bytestring)) = 1.0.0
Provides: bundled(crate(cc)) = 1.0.72
Provides: bundled(crate(cc)) = 1.0.73
Provides: bundled(crate(cexpr)) = 0.6.0
Provides: bundled(crate(cfg-if)) = 1.0.0
Provides: bundled(crate(clang-sys)) = 1.3.3
Provides: bundled(crate(clap)) = 2.34.0
Provides: bundled(crate(clap)) = 3.1.18
Provides: bundled(crate(clap_derive)) = 3.1.18
Provides: bundled(crate(clap_lex)) = 0.2.4
Provides: bundled(crate(clap_lex)) = 0.2.0
Provides: bundled(crate(compress-tools)) = 0.12.2
Provides: bundled(crate(convert_case)) = 0.4.0
Provides: bundled(crate(cookie)) = 0.16.0
Provides: bundled(crate(cpufeatures)) = 0.2.1
Provides: bundled(crate(cpufeatures)) = 0.2.2
Provides: bundled(crate(crc32fast)) = 1.3.2
Provides: bundled(crate(crypto-common)) = 0.1.3
Provides: bundled(crate(derive_more)) = 0.99.17
Provides: bundled(crate(digest)) = 0.10.3
Provides: bundled(crate(dlv-list)) = 0.2.3
Provides: bundled(crate(either)) = 1.7.0
Provides: bundled(crate(encoding_rs)) = 0.8.30
Provides: bundled(crate(enumflags2)) = 0.7.3
Provides: bundled(crate(enumflags2_derive)) = 0.7.3
Provides: bundled(crate(encoding_rs)) = 0.8.31
Provides: bundled(crate(enumflags2)) = 0.7.5
Provides: bundled(crate(enumflags2_derive)) = 0.7.4
Provides: bundled(crate(env_logger)) = 0.7.1
Provides: bundled(crate(env_logger)) = 0.9.0
Provides: bundled(crate(fastrand)) = 1.7.0
Provides: bundled(crate(firestorm)) = 0.5.0
Provides: bundled(crate(flate2)) = 1.0.22
Provides: bundled(crate(firestorm)) = 0.5.1
Provides: bundled(crate(flate2)) = 1.0.24
Provides: bundled(crate(fnv)) = 1.0.7
Provides: bundled(crate(foreign-types)) = 0.3.2
Provides: bundled(crate(foreign-types-shared)) = 0.1.1
@ -152,28 +144,26 @@ Provides: bundled(crate(futures-sink)) = 0.3.21
Provides: bundled(crate(futures-task)) = 0.3.21
Provides: bundled(crate(futures-util)) = 0.3.21
Provides: bundled(crate(generic-array)) = 0.14.5
Provides: bundled(crate(getrandom)) = 0.2.4
Provides: bundled(crate(getrandom)) = 0.2.6
Provides: bundled(crate(glob)) = 0.3.0
Provides: bundled(crate(h2)) = 0.3.11
Provides: bundled(crate(hamming)) = 0.1.3
Provides: bundled(crate(h2)) = 0.3.13
Provides: bundled(crate(hashbrown)) = 0.9.1
Provides: bundled(crate(hashbrown)) = 0.11.2
Provides: bundled(crate(heck)) = 0.4.0
Provides: bundled(crate(hex)) = 0.4.3
Provides: bundled(crate(hostname-validator)) = 1.1.0
Provides: bundled(crate(http)) = 0.2.6
Provides: bundled(crate(http-body)) = 0.4.4
Provides: bundled(crate(httparse)) = 1.6.0
Provides: bundled(crate(hostname-validator)) = 1.1.1
Provides: bundled(crate(http)) = 0.2.8
Provides: bundled(crate(http-body)) = 0.4.5
Provides: bundled(crate(httparse)) = 1.7.1
Provides: bundled(crate(httpdate)) = 1.0.2
Provides: bundled(crate(humantime)) = 1.3.0
Provides: bundled(crate(humantime)) = 2.1.0
Provides: bundled(crate(hyper)) = 0.14.17
Provides: bundled(crate(hyper)) = 0.14.19
Provides: bundled(crate(hyper-tls)) = 0.5.0
Provides: bundled(crate(idna)) = 0.2.3
Provides: bundled(crate(indexmap)) = 1.8.0
Provides: bundled(crate(instant)) = 0.1.12
Provides: bundled(crate(ipnet)) = 2.3.1
Provides: bundled(crate(itoa)) = 1.0.1
Provides: bundled(crate(indexmap)) = 1.8.2
Provides: bundled(crate(ipnet)) = 2.5.0
Provides: bundled(crate(itoa)) = 1.0.2
Provides: bundled(crate(jobserver)) = 0.1.24
Provides: bundled(crate(keylime_agent)) = 0.1.0
Provides: bundled(crate(language-tags)) = 0.3.2
@ -181,124 +171,124 @@ Provides: bundled(crate(lazy_static)) = 1.4.0
Provides: bundled(crate(lazycell)) = 1.3.0
Provides: bundled(crate(libc)) = 0.2.126
Provides: bundled(crate(libloading)) = 0.7.3
Provides: bundled(crate(local-channel)) = 0.1.2
Provides: bundled(crate(local-waker)) = 0.1.2
Provides: bundled(crate(lock_api)) = 0.4.6
Provides: bundled(crate(log)) = 0.4.14
Provides: bundled(crate(local-channel)) = 0.1.3
Provides: bundled(crate(local-waker)) = 0.1.3
Provides: bundled(crate(lock_api)) = 0.4.7
Provides: bundled(crate(log)) = 0.4.17
Provides: bundled(crate(matches)) = 0.1.9
Provides: bundled(crate(mbox)) = 0.6.0
Provides: bundled(crate(memchr)) = 2.4.1
Provides: bundled(crate(memchr)) = 2.5.0
Provides: bundled(crate(mime)) = 0.3.16
Provides: bundled(crate(minimal-lexical)) = 0.2.1
Provides: bundled(crate(miniz_oxide)) = 0.4.4
Provides: bundled(crate(mio)) = 0.7.14
Provides: bundled(crate(mio)) = 0.8.2
Provides: bundled(crate(native-tls)) = 0.2.8
Provides: bundled(crate(miniz_oxide)) = 0.5.3
Provides: bundled(crate(mio)) = 0.8.3
Provides: bundled(crate(native-tls)) = 0.2.10
Provides: bundled(crate(nom)) = 7.1.1
Provides: bundled(crate(num-derive)) = 0.3.3
Provides: bundled(crate(num-integer)) = 0.1.44
Provides: bundled(crate(num-traits)) = 0.2.14
Provides: bundled(crate(num-traits)) = 0.2.15
Provides: bundled(crate(num_cpus)) = 1.13.1
Provides: bundled(crate(num_threads)) = 0.1.5
Provides: bundled(crate(once_cell)) = 1.9.0
Provides: bundled(crate(openssl)) = 0.10.38
Provides: bundled(crate(num_threads)) = 0.1.6
Provides: bundled(crate(oid)) = 0.2.1
Provides: bundled(crate(once_cell)) = 1.12.0
Provides: bundled(crate(openssl)) = 0.10.40
Provides: bundled(crate(openssl-macros)) = 0.1.0
Provides: bundled(crate(openssl-probe)) = 0.1.5
Provides: bundled(crate(openssl-sys)) = 0.9.72
Provides: bundled(crate(openssl-sys)) = 0.9.74
Provides: bundled(crate(ordered-multimap)) = 0.3.1
Provides: bundled(crate(os_str_bytes)) = 6.0.0
Provides: bundled(crate(parking_lot)) = 0.11.2
Provides: bundled(crate(parking_lot_core)) = 0.8.5
Provides: bundled(crate(os_str_bytes)) = 6.1.0
Provides: bundled(crate(parking_lot)) = 0.12.1
Provides: bundled(crate(parking_lot_core)) = 0.9.3
Provides: bundled(crate(paste)) = 1.0.7
Provides: bundled(crate(peeking_take_while)) = 0.1.2
Provides: bundled(crate(percent-encoding)) = 2.1.0
Provides: bundled(crate(pest)) = 2.1.3
Provides: bundled(crate(pin-project-lite)) = 0.2.8
Provides: bundled(crate(picky-asn1)) = 0.3.3
Provides: bundled(crate(picky-asn1)) = 0.5.0
Provides: bundled(crate(picky-asn1-der)) = 0.2.5
Provides: bundled(crate(picky-asn1-der)) = 0.3.1
Provides: bundled(crate(picky-asn1-x509)) = 0.6.1
Provides: bundled(crate(pin-project-lite)) = 0.2.9
Provides: bundled(crate(pin-utils)) = 0.1.0
Provides: bundled(crate(pkg-config)) = 0.3.24
Provides: bundled(crate(pkg-config)) = 0.3.25
Provides: bundled(crate(ppv-lite86)) = 0.2.16
Provides: bundled(crate(pretty_env_logger)) = 0.4.0
Provides: bundled(crate(primal)) = 0.3.0
Provides: bundled(crate(primal-bit)) = 0.3.0
Provides: bundled(crate(primal-check)) = 0.3.1
Provides: bundled(crate(primal-estimate)) = 0.3.1
Provides: bundled(crate(primal-sieve)) = 0.3.1
Provides: bundled(crate(proc-macro-error)) = 1.0.4
Provides: bundled(crate(proc-macro-error-attr)) = 1.0.4
Provides: bundled(crate(proc-macro2)) = 1.0.36
Provides: bundled(crate(proc-macro2)) = 1.0.39
Provides: bundled(crate(quick-error)) = 1.2.3
Provides: bundled(crate(quote)) = 1.0.15
Provides: bundled(crate(quote)) = 1.0.18
Provides: bundled(crate(rand)) = 0.8.5
Provides: bundled(crate(rand_chacha)) = 0.3.1
Provides: bundled(crate(rand_core)) = 0.6.3
Provides: bundled(crate(regex)) = 1.5.4
Provides: bundled(crate(regex-syntax)) = 0.6.25
Provides: bundled(crate(regex)) = 1.5.6
Provides: bundled(crate(regex-syntax)) = 0.6.26
Provides: bundled(crate(remove_dir_all)) = 0.5.3
Provides: bundled(crate(reqwest)) = 0.11.10
Provides: bundled(crate(rust-ini)) = 0.17.0
Provides: bundled(crate(rustc-hash)) = 1.1.0
Provides: bundled(crate(rustc_version)) = 0.3.3
Provides: bundled(crate(rustc_version)) = 0.4.0
Provides: bundled(crate(ryu)) = 1.0.9
Provides: bundled(crate(ryu)) = 1.0.10
Provides: bundled(crate(scopeguard)) = 1.1.0
Provides: bundled(crate(semver)) = 0.11.0
Provides: bundled(crate(semver)) = 1.0.5
Provides: bundled(crate(semver)) = 1.0.9
Provides: bundled(crate(semver-parser)) = 0.10.2
Provides: bundled(crate(serde)) = 1.0.136
Provides: bundled(crate(serde_derive)) = 1.0.136
Provides: bundled(crate(serde_json)) = 1.0.79
Provides: bundled(crate(serde)) = 1.0.137
Provides: bundled(crate(serde_bytes)) = 0.11.6
Provides: bundled(crate(serde_derive)) = 1.0.137
Provides: bundled(crate(serde_json)) = 1.0.81
Provides: bundled(crate(serde_urlencoded)) = 0.7.1
Provides: bundled(crate(sha-1)) = 0.10.0
Provides: bundled(crate(shlex)) = 1.1.0
Provides: bundled(crate(signal-hook-registry)) = 1.4.0
Provides: bundled(crate(slab)) = 0.4.5
Provides: bundled(crate(slab)) = 0.4.6
Provides: bundled(crate(smallvec)) = 1.8.0
Provides: bundled(crate(socket2)) = 0.4.4
Provides: bundled(crate(stable_deref_trait)) = 1.2.0
Provides: bundled(crate(static_assertions)) = 1.1.0
Provides: bundled(crate(strsim)) = 0.8.0
Provides: bundled(crate(strsim)) = 0.10.0
Provides: bundled(crate(syn)) = 1.0.86
Provides: bundled(crate(syn)) = 1.0.96
Provides: bundled(crate(synstructure)) = 0.12.6
Provides: bundled(crate(target-lexicon)) = 0.12.3
Provides: bundled(crate(target-lexicon)) = 0.12.4
Provides: bundled(crate(tempfile)) = 3.3.0
Provides: bundled(crate(termcolor)) = 1.1.2
Provides: bundled(crate(termcolor)) = 1.1.3
Provides: bundled(crate(textwrap)) = 0.11.0
Provides: bundled(crate(textwrap)) = 0.15.0
Provides: bundled(crate(thiserror)) = 1.0.30
Provides: bundled(crate(thiserror-impl)) = 1.0.30
Provides: bundled(crate(thiserror)) = 1.0.31
Provides: bundled(crate(thiserror-impl)) = 1.0.31
Provides: bundled(crate(time)) = 0.3.9
Provides: bundled(crate(time-macros)) = 0.2.4
Provides: bundled(crate(tinyvec)) = 1.5.1
Provides: bundled(crate(tinyvec)) = 1.6.0
Provides: bundled(crate(tinyvec_macros)) = 0.1.0
Provides: bundled(crate(tokio)) = 1.16.1
Provides: bundled(crate(tokio-macros)) = 1.7.0
Provides: bundled(crate(tokio)) = 1.19.2
Provides: bundled(crate(tokio-macros)) = 1.8.0
Provides: bundled(crate(tokio-native-tls)) = 0.3.0
Provides: bundled(crate(tokio-openssl)) = 0.6.3
Provides: bundled(crate(tokio-util)) = 0.6.9
Provides: bundled(crate(tokio-util)) = 0.7.1
Provides: bundled(crate(tokio-util)) = 0.7.3
Provides: bundled(crate(tower-service)) = 0.3.1
Provides: bundled(crate(tracing)) = 0.1.30
Provides: bundled(crate(tracing-attributes)) = 0.1.20
Provides: bundled(crate(tracing-core)) = 0.1.22
Provides: bundled(crate(tracing)) = 0.1.35
Provides: bundled(crate(tracing-core)) = 0.1.27
Provides: bundled(crate(try-lock)) = 0.2.3
Provides: bundled(crate(tss-esapi)) = 7.0.0
Provides: bundled(crate(tss-esapi)) = 7.1.0
Provides: bundled(crate(tss-esapi-sys)) = 0.3.0
Provides: bundled(crate(typenum)) = 1.15.0
Provides: bundled(crate(ucd-trie)) = 0.1.3
Provides: bundled(crate(unicode-bidi)) = 0.3.7
Provides: bundled(crate(unicode-bidi)) = 0.3.8
Provides: bundled(crate(unicode-ident)) = 1.0.0
Provides: bundled(crate(unicode-normalization)) = 0.1.19
Provides: bundled(crate(unicode-width)) = 0.1.9
Provides: bundled(crate(unicode-xid)) = 0.2.2
Provides: bundled(crate(unicode-xid)) = 0.2.3
Provides: bundled(crate(url)) = 2.2.2
Provides: bundled(crate(uuid)) = 0.8.2
Provides: bundled(crate(vec_map)) = 0.8.2
Provides: bundled(crate(version_check)) = 0.9.4
Provides: bundled(crate(want)) = 0.3.0
Provides: bundled(crate(which)) = 4.2.5
Provides: bundled(crate(zeroize)) = 1.5.2
Provides: bundled(crate(zeroize_derive)) = 1.3.1
Provides: bundled(crate(zstd)) = 0.10.0
Provides: bundled(crate(zstd-safe)) = 4.1.4
Provides: bundled(crate(zeroize)) = 1.5.5
Provides: bundled(crate(zeroize_derive)) = 1.3.2
Provides: bundled(crate(zstd)) = 0.10.2
Provides: bundled(crate(zstd-safe)) = 4.1.6
Provides: bundled(crate(zstd-sys)) = 1.6.3
%description
@ -329,6 +319,15 @@ install -Dpm 644 ./dist/systemd/system/keylime_agent.service \
install -Dpm 644 ./dist/systemd/system/var-lib-keylime-secure.mount \
%{buildroot}%{_unitdir}/var-lib-keylime-secure.mount
# Setting up the agent to use keylime:keylime user/group after dropping privileges.
sed -e 's/^run_as[[:space:]]*=.*/run_as = keylime:keylime/g' -i keylime-agent.conf
# Using sha256 for tpm_hash_alg.
sed -e 's/^tpm_hash_alg[[:space:]]*=.*/tpm_hash_alg = sha256/g' -i keylime-agent.conf
install -Dpm 600 keylime-agent.conf \
%{buildroot}%{_sysconfdir}/keylime-agent.conf
%preun
%systemd_preun keylime_agent.service
%systemd_preun var-lib-keylime-secure.mount
@ -340,6 +339,7 @@ install -Dpm 644 ./dist/systemd/system/var-lib-keylime-secure.mount \
%files
%license LICENSE
%doc README.md
%config(noreplace) %attr(600,keylime,keylime) %{_sysconfdir}/keylime-agent.conf
%{_unitdir}/keylime_agent.service
%{_unitdir}/var-lib-keylime-secure.mount
%attr(700,keylime,keylime) %dir %{_rundir}/keylime
@ -355,6 +355,9 @@ install -Dpm 644 ./dist/systemd/system/var-lib-keylime-secure.mount \
%endif
%changelog
* Thu Aug 25 2022 Anderson Toshiyuki Sasaki <ansasaki@redhat.com> - 0.1.0~20220805git0185093-1
- Update to upstream commit 0186093
* Mon Jul 18 2022 Anderson Toshiyuki Sasaki <ansasaki@redhat.com> - 0.1.0~20220603gitaed51c7-4
- Remove vendored source files for non-Linux or unsupported platforms

View File

@ -1,217 +0,0 @@
From 423f7337d991ec5085914a361e68260bdd513ac6 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Mon, 20 Jun 2022 11:51:46 +0200
Subject: [PATCH] tpm: Add serialization functions for structures in quotes
Add serialization and deserialization functions for the data in quotes
to avoid endianness issues when the arch is big-endian.
The added serialization and deserialization functions will convert the
data endianness as necessary.
Note: the official marshalling and unmarshalling functions cannot be
used directly because the tpm2-tools uses a custom format.
Fixes: #407
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
src/tpm.rs | 139 +++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 107 insertions(+), 32 deletions(-)
diff --git a/src/tpm.rs b/src/tpm.rs
index 9002c29..5521892 100644
--- a/src/tpm.rs
+++ b/src/tpm.rs
@@ -52,15 +52,20 @@ use tss_esapi::{
tss2_esys::{
Tss2_MU_TPM2B_PUBLIC_Marshal, Tss2_MU_TPMS_ATTEST_Marshal,
Tss2_MU_TPMS_ATTEST_Unmarshal, Tss2_MU_TPMT_SIGNATURE_Marshal,
- TPM2B_ATTEST, TPM2B_PUBLIC, TPML_DIGEST, TPML_PCR_SELECTION,
- TPMS_ATTEST, TPMS_SCHEME_HASH, TPMT_SIGNATURE, TPMT_SIG_SCHEME,
- TPMU_SIG_SCHEME,
+ TPM2B_ATTEST, TPM2B_DIGEST, TPM2B_PUBLIC, TPML_DIGEST,
+ TPML_PCR_SELECTION, TPMS_ATTEST, TPMS_PCR_SELECTION,
+ TPMS_SCHEME_HASH, TPMT_SIGNATURE, TPMT_SIG_SCHEME, TPMU_SIG_SCHEME,
},
utils::TpmsContext,
Context,
};
pub const MAX_NONCE_SIZE: usize = 64;
+pub const TPML_DIGEST_SIZE: usize = std::mem::size_of::<TPML_DIGEST>();
+pub const TPML_PCR_SELECTION_SIZE: usize =
+ std::mem::size_of::<TPML_PCR_SELECTION>();
+pub const TPMS_PCR_SELECTION_SIZE: usize =
+ std::mem::size_of::<TPMS_PCR_SELECTION>();
/*
* Input: None
@@ -123,6 +128,95 @@ pub(crate) fn create_ek(
assert_eq_size!(TPML_PCR_SELECTION, [u8; 132]);
assert_eq_size!(TPML_DIGEST, [u8; 532]);
+// Serialize a TPML_PCR_SELECTION into a Vec<u8>
+// The serialization will adjust the data endianness as necessary and add paddings to keep the
+// memory aligment.
+pub(crate) fn serialize_pcrsel(
+ pcr_selection: &TPML_PCR_SELECTION,
+) -> Vec<u8> {
+ let mut output = Vec::with_capacity(TPML_PCR_SELECTION_SIZE);
+ output.extend(u32::to_le_bytes(pcr_selection.count));
+ for selection in pcr_selection.pcrSelections.iter() {
+ output.extend(selection.hash.to_le_bytes());
+ output.extend(selection.sizeofSelect.to_le_bytes());
+ output.extend(selection.pcrSelect);
+ output.extend([0u8; 1]); // padding to keep the memory alignment
+ }
+ output
+}
+
+// Deserialize a TPML_PCR_SELECTION from a &[u8] slice.
+// The deserialization will adjust the data endianness as necessary.
+pub(crate) fn deserialize_pcrsel(
+ pcrsel_vec: &[u8],
+) -> Result<TPML_PCR_SELECTION> {
+ if pcrsel_vec.len() != TPML_PCR_SELECTION_SIZE {
+ return Err(KeylimeError::InvalidRequest);
+ }
+
+ let mut reader = std::io::Cursor::new(pcrsel_vec);
+ let mut count_vec = [0u8; 4];
+ reader.read_exact(&mut count_vec)?;
+ let count = u32::from_le_bytes(count_vec);
+
+ let mut pcr_selections: [TPMS_PCR_SELECTION; 16] =
+ [TPMS_PCR_SELECTION::default(); 16];
+
+ for selection in &mut pcr_selections {
+ let mut hash_vec = [0u8; 2];
+ reader.read_exact(&mut hash_vec)?;
+ selection.hash = u16::from_le_bytes(hash_vec);
+
+ let mut size_vec = [0u8; 1];
+ reader.read_exact(&mut size_vec)?;
+ selection.sizeofSelect = u8::from_le_bytes(size_vec);
+
+ reader.read_exact(&mut selection.pcrSelect)?;
+ }
+
+ Ok(TPML_PCR_SELECTION {
+ count,
+ pcrSelections: pcr_selections,
+ })
+}
+
+// Serialize a TPML_DIGEST into a Vec<u8>
+// The serialization will adjust the data endianness as necessary.
+pub(crate) fn serialize_digest(digest_list: &TPML_DIGEST) -> Vec<u8> {
+ let mut output = Vec::with_capacity(TPML_DIGEST_SIZE);
+ output.extend(u32::to_le_bytes(digest_list.count));
+ for digest in digest_list.digests.iter() {
+ output.extend(digest.size.to_le_bytes());
+ output.extend(digest.buffer);
+ }
+ output
+}
+
+// Deserialize a TPML_DIGEST from a &[u8] slice.
+// The deserialization will adjust the data endianness as necessary.
+pub(crate) fn deserialize_digest(digest_vec: &[u8]) -> Result<TPML_DIGEST> {
+ if digest_vec.len() != TPML_DIGEST_SIZE {
+ return Err(KeylimeError::InvalidRequest);
+ }
+
+ let mut reader = std::io::Cursor::new(digest_vec);
+ let mut count_vec = [0u8; 4];
+
+ reader.read_exact(&mut count_vec)?;
+ let count = u32::from_le_bytes(count_vec);
+
+ let mut digests: [TPM2B_DIGEST; 8] = [TPM2B_DIGEST::default(); 8];
+
+ for digest in &mut digests {
+ let mut size_vec = [0u8; 2];
+ reader.read_exact(&mut size_vec)?;
+ digest.size = u16::from_le_bytes(size_vec);
+ reader.read_exact(&mut digest.buffer)?;
+ }
+
+ Ok(TPML_DIGEST { count, digests })
+}
+
// Recreate how tpm2-tools creates the PCR out file. Roughly, this is a
// TPML_PCR_SELECTION + number of TPML_DIGESTS + TPML_DIGESTs.
// Reference:
@@ -140,16 +234,14 @@ pub(crate) fn pcrdata_to_vec(
const DIGEST_SIZE: usize = std::mem::size_of::<TPML_DIGEST>();
let mut pcrsel: TPML_PCR_SELECTION = selection_list.into();
- pcrsel.count = pcrsel.count.to_le();
- let pcrsel_vec: [u8; PCRSEL_SIZE] =
- unsafe { std::mem::transmute(pcrsel) };
+ let pcrsel_vec = serialize_pcrsel(&pcrsel);
let digest: Vec<TPML_DIGEST> = pcrdata.into();
let num_tpml_digests = digest.len() as u32;
let mut digest_vec = Vec::with_capacity(digest.len() * DIGEST_SIZE);
for d in digest {
- let vec: [u8; DIGEST_SIZE] = unsafe { std::mem::transmute(d) };
+ let vec = serialize_digest(&d);
digest_vec.extend(vec);
}
@@ -665,41 +757,24 @@ pub mod testing {
);
fn vec_to_pcrdata(val: &[u8]) -> Result<(PcrSelectionList, PcrData)> {
- const PCRSEL_SIZE: usize = std::mem::size_of::<TPML_PCR_SELECTION>();
- const DIGEST_SIZE: usize = std::mem::size_of::<TPML_DIGEST>();
-
let mut reader = std::io::Cursor::new(val);
- let mut pcrsel_vec = [0u8; PCRSEL_SIZE];
- let len = reader.read(&mut pcrsel_vec)?;
- if len != pcrsel_vec.len() {
- return Err(KeylimeError::InvalidRequest);
- }
- let mut pcrsel = unsafe {
- std::mem::transmute::<[u8; PCRSEL_SIZE], TPML_PCR_SELECTION>(
- pcrsel_vec,
- )
- };
+ let mut pcrsel_vec = [0u8; TPML_PCR_SELECTION_SIZE];
+ reader.read_exact(&mut pcrsel_vec)?;
+
+ let pcrsel = deserialize_pcrsel(&pcrsel_vec)?;
let pcrlist: PcrSelectionList = pcrsel.try_into()?;
let mut count_vec = [0u8; 4];
- let len = reader.read(&mut count_vec)?;
- if len < count_vec.len() {
- return Err(KeylimeError::InvalidRequest);
- }
+ reader.read_exact(&mut count_vec)?;
let count = u32::from_le_bytes(count_vec);
// Always 1 PCR digest should follow
if count != 1 {
return Err(KeylimeError::InvalidRequest);
}
- let mut digest_vec = [0u8; DIGEST_SIZE];
- let len = reader.read(&mut digest_vec)?;
- if len != digest_vec.len() {
- return Err(KeylimeError::InvalidRequest);
- }
- let mut digest = unsafe {
- std::mem::transmute::<[u8; DIGEST_SIZE], TPML_DIGEST>(digest_vec)
- };
+ let mut digest_vec = [0u8; TPML_DIGEST_SIZE];
+ reader.read_exact(&mut digest_vec)?;
+ let digest = deserialize_digest(&digest_vec)?;
let mut digest_list = DigestList::new();
for i in 0..digest.count {
digest_list.add(digest.digests[i as usize].try_into()?);
--
2.35.3

View File

@ -1,11 +1,11 @@
--- a/Cargo.toml 2022-07-04 14:30:04.244003909 +0200
+++ b/Cargo.toml 2022-07-04 14:31:46.421291210 +0200
@@ -37,28 +37,14 @@
--- a/Cargo.toml 2022-08-25 12:16:51.789234342 +0200
+++ b/Cargo.toml 2022-08-25 12:21:45.451435220 +0200
@@ -39,28 +39,13 @@
static_assertions = "1"
tempfile = "3.0.4"
tokio = {version = "1", features = ["full"]}
-tss-esapi = "7.0.0"
+tss-esapi = {version = "7.0.0", features = ["generate-bindings"]}
tokio = {version = "1.13.1", features = ["full"]}
-tss-esapi = "7.1.0"
+tss-esapi = {version="7.1.0", features = ["generate-bindings"]}
thiserror = "1.0"
uuid = {version = "0.8", features = ["v4"]}
-zmq = {version = "0.9.2", optional = true}
@ -18,7 +18,7 @@
actix-rt = "2"
[features]
# The features enabled by default
-# The features enabled by default
-default = ["with-zmq", "legacy-python-actions"]
-# this should change to dev-dependencies when we have integration testing
-testing = ["wiremock"]

View File

@ -1,43 +0,0 @@
From e20b936fc9d92ae05406cb86471d3e6fba823b7f Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Tue, 28 Jun 2022 13:47:38 +0200
Subject: [PATCH] main: Use more descriptive message for missing files error
Use more descriptive message when required paths are missing instead of
the generic message.
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
src/main.rs | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/main.rs b/src/main.rs
index 9404284..ef29eb2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -608,9 +608,22 @@ async fn main() -> Result<()> {
let payload = Arc::clone(&encr_payload_arc);
let revocation_cert = revocation::get_revocation_cert_path(&config)?;
- let actions_dir =
- Path::new(&config.revocation_actions_dir).canonicalize()?;
- let work_dir = Path::new(&config.work_dir).canonicalize()?;
+ let actions_dir = Path::new(&config.revocation_actions_dir)
+ .canonicalize()
+ .map_err(|e| {
+ Error::Configuration(format!(
+ "Path {} set in revocation_actions_dir not found: {}",
+ &config.revocation_actions_dir, e
+ ))
+ })?;
+
+ let work_dir =
+ Path::new(&config.work_dir).canonicalize().map_err(|e| {
+ Error::Configuration(format!(
+ "Path {} set in keylime_dir not found: {}",
+ &config.work_dir, e
+ ))
+ })?;
let quotedata = web::Data::new(QuoteData {
tpmcontext: Mutex::new(ctx),

View File

@ -1,193 +0,0 @@
From 9229cb4673aad2cae7605e66bdf2160716e1f694 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Thu, 9 Jun 2022 18:34:34 +0200
Subject: [PATCH 1/4] Update clap dependency to 3.1.18
This change replaces clap::App with clap::Command in main.rs file.
On commit f3fa925 the clap dependency in Cargo.lock file was updated
from version 3.0.14 to version 3.1.18.
In clap version 3.1.0, clap::App was deprecated in favor of
clap::Command. This causes warnings during the Keylime agent build due
to the usage of the deprecated structure.
To avoid similar issues in the future, use the tilde dependency version
requirement to allow versions >= 3.1.18 but < 3.2.0.
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
Cargo.lock | 76 ++++++++++++++++++++++-------------------------------
Cargo.toml | 2 +-
src/main.rs | 2 +-
3 files changed, 33 insertions(+), 47 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 92f3974..f0c7d39 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,7 +21,7 @@ doc = false
actix-web = { version = "4", features = ["openssl"] }
base64 = "0.13"
cfg-if = "1"
-clap = { version = "3.0.14", features = ["derive"] }
+clap = { version = "~3.1.18", features = ["derive"] }
compress-tools = "0.12"
flate2 = "1.0.4"
futures = "0.3.6"
diff --git a/src/main.rs b/src/main.rs
index 1896104..8540597 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -51,7 +51,7 @@ mod tpm;
mod version_handler;
use actix_web::{dev::Service, http, middleware, rt, web, App, HttpServer};
-use clap::{App as ClapApp, Arg};
+use clap::{Arg, Command as ClapApp};
use common::*;
use compress_tools::*;
use error::{Error, Result};
--
2.35.3
From c61e3f389e4bde4d0a318a571c9b3dcf8a62749f Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Tue, 17 May 2022 18:31:01 +0200
Subject: [PATCH 2/4] Drop unused dependency rustc-serialize
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
Cargo.lock | 7 -------
Cargo.toml | 1 -
2 files changed, 8 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index f0c7d39..0f4e4ab 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -34,7 +34,6 @@ picky-asn1-x509 = "0.6.1"
pretty_env_logger = "0.4"
reqwest = {version = "0.11", features = ["json"]}
rust-ini = "0.17"
-rustc-serialize = "0.3.24"
serde = "1.0.80"
serde_derive = "1.0.80"
serde_json = { version = "1.0", features = ["raw_value"] }
--
2.35.3
From f7b59a6a7739d275ffe15a915eb48a36ceafccdd Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Tue, 17 May 2022 18:56:50 +0200
Subject: [PATCH 3/4] Drop unused dependency flate2
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
Cargo.lock | 1 -
Cargo.toml | 1 -
src/tpm.rs | 3 ---
3 files changed, 5 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 0f4e4ab..69d2eec 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,7 +23,6 @@ base64 = "0.13"
cfg-if = "1"
clap = { version = "~3.1.18", features = ["derive"] }
compress-tools = "0.12"
-flate2 = "1.0.4"
futures = "0.3.6"
hex = "0.4"
libc = "0.2.43"
diff --git a/src/tpm.rs b/src/tpm.rs
index 355dd0d..43129f1 100644
--- a/src/tpm.rs
+++ b/src/tpm.rs
@@ -20,8 +20,6 @@ use openssl::{
pkey::{Id, PKeyRef, Public},
};
-use flate2::{write::ZlibEncoder, Compression};
-
use tss_esapi::{
abstraction::{
ak,
@@ -635,7 +633,6 @@ pub(crate) fn quote(
#[cfg(test)]
pub mod testing {
use super::*;
- use flate2::read::ZlibDecoder;
use tss_esapi::constants::structure_tags::StructureTag;
use tss_esapi::structures::{Attest, AttestBuffer, DigestList, Ticket};
use tss_esapi::tss2_esys::Tss2_MU_TPMT_SIGNATURE_Unmarshal;
--
2.35.3
From c3f0b4f603089d38715535f8e16b7a40f413e4de Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Thu, 9 Jun 2022 16:21:06 +0200
Subject: [PATCH 4/4] Make wiremock an optional dependency
Make a wiremock optional, enabled when the 'testing' feature is enabled.
The goal is to reduce the number of dependencies, with the downside that
the test code coverage will drop.
Wiremock is currently used only on 'registrar_agent' tests to mock
responses from the registrar service.
Note: wiremock was moved to be an optional regular dependency because
optional dev-dependencies are not supported.
see: https://github.com/rust-lang/cargo/issues/1596
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
Cargo.toml | 7 +++++--
src/registrar_agent.rs | 1 +
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 69d2eec..5052512 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -43,16 +43,19 @@ tss-esapi = "7.1.0"
thiserror = "1.0"
uuid = {version = "0.8", features = ["v4"]}
zmq = {version = "0.9.2", optional = true}
+# wiremock was moved to be a regular dependency because optional
+# dev-dependencies are not supported
+# see: https://github.com/rust-lang/cargo/issues/1596
+wiremock = {version = "0.5", optional = true}
[dev-dependencies]
actix-rt = "2"
-wiremock = "0.5"
[features]
# The features enabled by default
default = ["with-zmq", "legacy-python-actions"]
# this should change to dev-dependencies when we have integration testing
-testing = []
+testing = ["wiremock"]
# Whether the agent should be compiled with support to listen for notification
# messages on ZeroMQ
with-zmq = ["zmq"]
diff --git a/src/registrar_agent.rs b/src/registrar_agent.rs
index 85b5931..b02ba48 100644
--- a/src/registrar_agent.rs
+++ b/src/registrar_agent.rs
@@ -149,6 +149,7 @@ pub(crate) async fn do_register_agent(
}
}
+#[cfg(feature = "testing")]
#[cfg(test)]
mod tests {
use super::*;
--
2.35.3

View File

@ -1,899 +0,0 @@
From d30000a1c3ae6cb8f6dc60c295e037614605b36e Mon Sep 17 00:00:00 2001
From: Daiki Ueno <dueno@redhat.com>
Date: Tue, 5 Jul 2022 16:24:32 +0900
Subject: [PATCH 1/4] algorithms: Add conversion between our hash algorithms
and OpenSSL's
Signed-off-by: Daiki Ueno <dueno@redhat.com>
---
src/algorithms.rs | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/algorithms.rs b/src/algorithms.rs
index 414ef100..019f9360 100644
--- a/src/algorithms.rs
+++ b/src/algorithms.rs
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2021 Keylime Authors
+use openssl::hash::MessageDigest;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::fmt;
@@ -74,6 +75,18 @@ impl From<HashAlgorithm> for HashingAlgorithm {
}
}
+impl From<HashAlgorithm> for MessageDigest {
+ fn from(hash_algorithm: HashAlgorithm) -> Self {
+ match hash_algorithm {
+ HashAlgorithm::Sha1 => MessageDigest::sha1(),
+ HashAlgorithm::Sha256 => MessageDigest::sha256(),
+ HashAlgorithm::Sha384 => MessageDigest::sha384(),
+ HashAlgorithm::Sha512 => MessageDigest::sha512(),
+ HashAlgorithm::Sm3_256 => MessageDigest::sm3(),
+ }
+ }
+}
+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EncryptionAlgorithm {
Rsa,
From a90aa5b0852d587114aca7a8e79306987c562624 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <dueno@redhat.com>
Date: Tue, 5 Jul 2022 15:43:42 +0900
Subject: [PATCH 2/4] ima_entry: add IMA entry parser ported from Python
Keylime
Signed-off-by: Daiki Ueno <dueno@redhat.com>
---
src/ima_entry.rs | 516 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 516 insertions(+)
create mode 100644 src/ima_entry.rs
diff --git a/src/ima_entry.rs b/src/ima_entry.rs
new file mode 100644
index 00000000..7bb4a970
--- /dev/null
+++ b/src/ima_entry.rs
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright 2022 Keylime Authors
+
+// Parser for IMA ASCII entries.
+//
+// Implements the templates (modes) and types as defined in:
+// https://elixir.bootlin.com/linux/latest/source/security/integrity/ima/ima_template.c
+// https://www.kernel.org/doc/html/v5.12/security/IMA-templates.html
+
+use crate::algorithms::HashAlgorithm;
+use openssl::hash::MessageDigest;
+use std::convert::{TryFrom, TryInto};
+use std::io::{Error, ErrorKind, Result, Write};
+
+pub trait Encode {
+ fn encode(&self, writer: &mut dyn Write) -> Result<()>;
+}
+
+pub trait EncodeLegacy {
+ fn encode_legacy(&self, writer: &mut dyn Write) -> Result<()>;
+}
+
+#[derive(PartialEq)]
+pub struct Digest {
+ pub algorithm: HashAlgorithm,
+ value: Vec<u8>,
+}
+
+impl Digest {
+ pub fn new(algorithm: HashAlgorithm, value: &[u8]) -> Result<Self> {
+ let digest: MessageDigest = algorithm.into();
+ if value.len() != digest.size() {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "invalid digest value",
+ ));
+ }
+ let mut v = Vec::with_capacity(digest.size());
+ v.extend_from_slice(value);
+ Ok(Self {
+ algorithm,
+ value: v,
+ })
+ }
+
+ pub fn value(&self) -> &[u8] {
+ &self.value
+ }
+
+ pub fn start(algorithm: HashAlgorithm) -> Self {
+ let digest: MessageDigest = algorithm.into();
+ Self {
+ algorithm,
+ value: vec![0x00u8; digest.size()],
+ }
+ }
+
+ pub fn ff(algorithm: HashAlgorithm) -> Self {
+ let digest: MessageDigest = algorithm.into();
+ Self {
+ algorithm,
+ value: vec![0xffu8; digest.size()],
+ }
+ }
+}
+
+impl TryFrom<&str> for Digest {
+ type Error = std::io::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ let tokens: Vec<&str> = value.splitn(2, ':').collect();
+ if tokens.len() == 1 {
+ Ok(Digest {
+ algorithm: HashAlgorithm::Sha1,
+ value: hex::decode(&tokens[0]).map_err(|_| {
+ Error::new(
+ ErrorKind::InvalidInput,
+ "invalid hex encoding",
+ )
+ })?,
+ })
+ } else {
+ Ok(Digest {
+ algorithm: tokens[0].try_into().map_err(|_| {
+ Error::new(ErrorKind::InvalidInput, "invalid algorithm")
+ })?,
+ value: hex::decode(&tokens[1]).map_err(|_| {
+ Error::new(
+ ErrorKind::InvalidInput,
+ "invalid hex encoding",
+ )
+ })?,
+ })
+ }
+ }
+}
+
+impl Encode for Digest {
+ fn encode(&self, writer: &mut dyn Write) -> Result<()> {
+ if self.algorithm == HashAlgorithm::Sha1 {
+ writer.write_all(&(self.value.len() as u32).to_le_bytes())?;
+ writer.write_all(&self.value)?;
+ } else {
+ let algorithm = format!("{}", self.algorithm);
+ let total_len = algorithm.len() + 2 + self.value.len();
+ writer.write_all(&(total_len as u32).to_le_bytes())?;
+ writer.write_all(algorithm.as_bytes())?;
+ writer.write_all(&[58u8, 0u8])?;
+ writer.write_all(&self.value)?;
+ }
+ Ok(())
+ }
+}
+
+impl EncodeLegacy for Digest {
+ fn encode_legacy(&self, writer: &mut dyn Write) -> Result<()> {
+ writer.write_all(&self.value)?;
+ Ok(())
+ }
+}
+
+struct Name {
+ name: String,
+}
+
+impl TryFrom<&str> for Name {
+ type Error = std::io::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ Ok(Self {
+ name: value.to_string(),
+ })
+ }
+}
+
+const TCG_EVENT_NAME_LEN_MAX: usize = 255;
+
+impl Encode for Name {
+ fn encode(&self, writer: &mut dyn Write) -> Result<()> {
+ let bytes = self.name.as_bytes();
+ writer.write_all(&((bytes.len() + 1) as u32).to_le_bytes())?;
+ writer.write_all(bytes)?;
+ writer.write_all(&[0u8])?; // NUL
+ Ok(())
+ }
+}
+
+impl EncodeLegacy for Name {
+ fn encode_legacy(&self, writer: &mut dyn Write) -> Result<()> {
+ let bytes = self.name.as_bytes();
+ writer.write_all(bytes)?;
+ writer.write_all(&vec![0u8; TCG_EVENT_NAME_LEN_MAX - bytes.len()])?;
+ Ok(())
+ }
+}
+
+struct Signature {
+ value: Vec<u8>,
+}
+
+impl TryFrom<&str> for Signature {
+ type Error = std::io::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ let value = hex::decode(value).map_err(|_| {
+ Error::new(ErrorKind::InvalidInput, "invalid hex encoding")
+ })?;
+ // basic checks on signature
+ if value.len() < 9 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "invalid signature",
+ ));
+ }
+ let sig_size = u16::from_be_bytes(value[7..9].try_into().unwrap()); //#[allow_ci]
+ if (sig_size as usize) + 9 != value.len() {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "invalid signature",
+ ));
+ }
+ Ok(Self { value })
+ }
+}
+
+impl Encode for Signature {
+ fn encode(&self, writer: &mut dyn Write) -> Result<()> {
+ writer.write_all(&(self.value.len() as u32).to_le_bytes())?;
+ writer.write_all(&self.value)?;
+ Ok(())
+ }
+}
+
+struct Buffer {
+ value: Vec<u8>,
+}
+
+impl TryFrom<&str> for Buffer {
+ type Error = std::io::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ Ok(Self {
+ value: hex::decode(value).map_err(|_| {
+ Error::new(ErrorKind::InvalidInput, "invalid hex encoding")
+ })?,
+ })
+ }
+}
+
+impl Encode for Buffer {
+ fn encode(&self, writer: &mut dyn Write) -> Result<()> {
+ writer.write_all(&(self.value.len() as u32).to_le_bytes())?;
+ writer.write_all(&self.value)?;
+ Ok(())
+ }
+}
+
+pub trait EventData: Encode {
+ fn path(&self) -> &str;
+}
+
+struct Ima {
+ digest: Digest,
+ path: Name,
+}
+
+impl TryFrom<&str> for Ima {
+ type Error = std::io::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ let tokens: Vec<&str> = value.splitn(2, ' ').collect();
+ if tokens.len() != 2 {
+ return Err(Error::new(ErrorKind::InvalidInput, value));
+ }
+ Ok(Self {
+ digest: Digest::try_from(tokens[0])?,
+ path: Name::try_from(tokens[1])?,
+ })
+ }
+}
+
+impl EventData for Ima {
+ fn path(&self) -> &str {
+ &self.path.name
+ }
+}
+
+impl Encode for Ima {
+ fn encode(&self, writer: &mut dyn Write) -> Result<()> {
+ self.digest.encode_legacy(writer)?;
+ self.path.encode_legacy(writer)?;
+ Ok(())
+ }
+}
+
+struct ImaNg {
+ digest: Digest,
+ path: Name,
+}
+
+impl TryFrom<&str> for ImaNg {
+ type Error = std::io::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ let tokens: Vec<&str> = value.splitn(2, ' ').collect();
+ if tokens.len() != 2 {
+ return Err(Error::new(ErrorKind::InvalidInput, value));
+ }
+
+ Ok(Self {
+ digest: Digest::try_from(tokens[0])?,
+ path: Name::try_from(tokens[1])?,
+ })
+ }
+}
+
+impl EventData for ImaNg {
+ fn path(&self) -> &str {
+ &self.path.name
+ }
+}
+
+impl Encode for ImaNg {
+ fn encode(&self, writer: &mut dyn Write) -> Result<()> {
+ self.digest.encode(writer)?;
+ self.path.encode(writer)?;
+ Ok(())
+ }
+}
+
+struct ImaSig {
+ digest: Digest,
+ path: Name,
+ signature: Option<Signature>,
+}
+
+impl EventData for ImaSig {
+ fn path(&self) -> &str {
+ &self.path.name
+ }
+}
+
+impl TryFrom<&str> for ImaSig {
+ type Error = std::io::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ // extract signature first
+ let (value, signature) = value
+ .rsplit_once(' ')
+ .ok_or_else(|| Error::new(ErrorKind::InvalidInput, value))?;
+
+ // parse d-ng|n-ng as in ima-ng
+ let tokens: Vec<&str> = value.splitn(2, ' ').collect();
+ if tokens.len() != 2 {
+ return Err(Error::new(ErrorKind::InvalidInput, value));
+ }
+
+ let digest = Digest::try_from(tokens[0])?;
+ let path = Name::try_from(tokens[1])?;
+ let signature = if !signature.is_empty() {
+ Some(Signature::try_from(signature)?)
+ } else {
+ None
+ };
+
+ Ok(Self {
+ digest,
+ path,
+ signature,
+ })
+ }
+}
+
+impl Encode for ImaSig {
+ fn encode(&self, writer: &mut dyn Write) -> Result<()> {
+ self.digest.encode(writer)?;
+ self.path.encode(writer)?;
+ if let Some(signature) = &self.signature {
+ signature.encode(writer)?;
+ } else {
+ writer.write_all(&0u32.to_le_bytes())?;
+ }
+ Ok(())
+ }
+}
+
+struct ImaBuf {
+ digest: Digest,
+ name: Name,
+ data: Buffer,
+}
+
+impl TryFrom<&str> for ImaBuf {
+ type Error = std::io::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ let tokens: Vec<&str> = value.splitn(3, ' ').collect();
+ if tokens.len() != 3 {
+ return Err(Error::new(ErrorKind::InvalidInput, value));
+ }
+ Ok(Self {
+ digest: Digest::try_from(tokens[0])?,
+ name: Name::try_from(tokens[1])?,
+ data: Buffer::try_from(tokens[2])?,
+ })
+ }
+}
+
+impl EventData for ImaBuf {
+ fn path(&self) -> &str {
+ &self.name.name
+ }
+}
+
+impl Encode for ImaBuf {
+ fn encode(&self, writer: &mut dyn Write) -> Result<()> {
+ self.digest.encode(writer)?;
+ self.name.encode(writer)?;
+ self.data.encode(writer)?;
+ Ok(())
+ }
+}
+
+pub struct Entry {
+ pub template_hash: Digest,
+ pub event_data: Box<dyn EventData>,
+}
+
+impl TryFrom<&str> for Entry {
+ type Error = std::io::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ let tokens: Vec<&str> = value.splitn(4, ' ').collect();
+ if tokens.len() != 4 {
+ return Err(Error::new(ErrorKind::InvalidInput, value));
+ }
+
+ let template_hash = Digest {
+ algorithm: HashAlgorithm::Sha1,
+ value: hex::decode(&tokens[1]).map_err(|_| {
+ Error::new(ErrorKind::InvalidInput, "invalid hex encoding")
+ })?,
+ };
+ let mode = tokens[2];
+ let event = tokens[3];
+
+ match mode {
+ "ima" => Ok(Self {
+ template_hash,
+ event_data: Box::new(Ima::try_from(event)?),
+ }),
+ "ima-ng" => Ok(Self {
+ template_hash,
+ event_data: Box::new(ImaNg::try_from(event)?),
+ }),
+ "ima-sig" => Ok(Self {
+ template_hash,
+ event_data: Box::new(ImaSig::try_from(event)?),
+ }),
+ "ima-buf" => Ok(Self {
+ template_hash,
+ event_data: Box::new(ImaBuf::try_from(event)?),
+ }),
+ template => Err(Error::new(
+ ErrorKind::Other,
+ format!("unrecognized template \"{}\"", template,),
+ )),
+ }
+ }
+}
+
+// Unit Testing
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_parse_ima() {
+ let entry: Entry = "10 d7026dc672344d3ee372217bdbc7395947788671 ima 6f66d1d8e2fffcc12dfcb78c04b81fe5b8bbae4e /usr/bin/kmod"
+ .try_into().expect("unable to parse ima template");
+ assert_eq!(entry.event_data.path(), "/usr/bin/kmod");
+ let mut buf = vec![];
+ entry
+ .event_data
+ .encode(&mut buf)
+ .expect("unable to encode event data");
+ assert_eq!(
+ &buf,
+ &hex::decode("6f66d1d8e2fffcc12dfcb78c04b81fe5b8bbae4e2f7573722f62696e2f6b6d6f640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), //#[allow_ci]
+ );
+ }
+
+ #[test]
+ fn test_parse_ima_ng() {
+ let entry: Entry = "10 7936eb315fb4e74b99e7d461bc5c96049e1ee092 ima-ng sha1:bc026ae66d81713e4e852465e980784dc96651f8 /usr/lib/systemd/systemd"
+ .try_into().expect("unable to parse ima-ng template");
+ assert_eq!(entry.event_data.path(), "/usr/lib/systemd/systemd");
+ let mut buf = vec![];
+ entry
+ .event_data
+ .encode(&mut buf)
+ .expect("unable to encode event data");
+ assert_eq!(
+ &buf,
+ &hex::decode("14000000bc026ae66d81713e4e852465e980784dc96651f8190000002f7573722f6c69622f73797374656d642f73797374656d6400").unwrap(), //#[allow_ci]
+ );
+ }
+
+ #[test]
+ fn test_parse_ima_sig() {
+ let entry: Entry = "10 06e804489a77ddab51b9ef27e17053c0e5d503bd ima-sig sha1:1cb84b12db45d7da8de58ba6744187db84082f0e /usr/bin/zmore 030202531f402500483046022100bff9c02dc7b270c83cc94bfec10eecd42831de2cdcb04f024369a14623bc3a91022100cc4d015ae932fb98d6846645ed7d1bb1afd4621ec9089bc087126f191886dd31"
+ .try_into().expect("unable to parse ima-sig template");
+ assert_eq!(entry.event_data.path(), "/usr/bin/zmore");
+ let mut buf = vec![];
+ entry
+ .event_data
+ .encode(&mut buf)
+ .expect("unable to encode event data");
+ assert_eq!(
+ &buf,
+ &hex::decode("140000001cb84b12db45d7da8de58ba6744187db84082f0e0f0000002f7573722f62696e2f7a6d6f72650051000000030202531f402500483046022100bff9c02dc7b270c83cc94bfec10eecd42831de2cdcb04f024369a14623bc3a91022100cc4d015ae932fb98d6846645ed7d1bb1afd4621ec9089bc087126f191886dd31").unwrap(), //#[allow_ci]
+ );
+ }
+
+ #[test]
+ fn test_parse_ima_sig_missing() {
+ let entry: Entry = "10 5426cf3031a43f5bfca183d79950698a95a728f6 ima-sig sha256:f1125b940480d20ad841d26d5ea253edc0704b5ec1548c891edf212cb1a9365e /lib/modules/5.4.48-openpower1/kernel/drivers/usb/common/usb-common.ko "
+ .try_into().expect("unable to parse ima-sig template without signature");
+ assert_eq!(entry.event_data.path(), "/lib/modules/5.4.48-openpower1/kernel/drivers/usb/common/usb-common.ko");
+ let mut buf = vec![];
+ entry
+ .event_data
+ .encode(&mut buf)
+ .expect("unable to encode event data");
+ assert_eq!(
+ &buf,
+ &hex::decode("280000007368613235363a00f1125b940480d20ad841d26d5ea253edc0704b5ec1548c891edf212cb1a9365e470000002f6c69622f6d6f64756c65732f352e342e34382d6f70656e706f776572312f6b65726e656c2f647269766572732f7573622f636f6d6d6f6e2f7573622d636f6d6d6f6e2e6b6f0000000000").unwrap(), //#[allow_ci]
+ );
+ }
+
+ #[test]
+ fn test_parse_ima_buf() {
+ let entry: Entry = "10 b7862dbbf1383ac6c7cca7f02d981a081aacb1f1 ima-buf sha1:6e0e6fc8a188ef4f059638949adca4d221946906 device_resume 6e616d653d544553543b757569643d43525950542d5645524954592d39656633326535623635623034343234613561386562343436636630653731332d544553543b63617061636974793d303b6d616a6f723d3235333b6d696e6f723d303b6d696e6f725f636f756e743d313b6e756d5f746172676574733d313b6163746976655f7461626c655f686173683d346565383065333365353635643336333430356634303238393436653837623365396563306335383661666639656630656436663561653762656237326431333b"
+ .try_into().expect("unable to parse ima-buf template");
+ assert_eq!(entry.event_data.path(), "device_resume");
+ let mut buf = vec![];
+ entry
+ .event_data
+ .encode(&mut buf)
+ .expect("unable to encode event data");
+ assert_eq!(
+ &buf,
+ &hex::decode("140000006e0e6fc8a188ef4f059638949adca4d2219469060e0000006465766963655f726573756d6500ce0000006e616d653d544553543b757569643d43525950542d5645524954592d39656633326535623635623034343234613561386562343436636630653731332d544553543b63617061636974793d303b6d616a6f723d3235333b6d696e6f723d303b6d696e6f725f636f756e743d313b6e756d5f746172676574733d313b6163746976655f7461626c655f686173683d346565383065333365353635643336333430356634303238393436653837623365396563306335383661666639656630656436663561653762656237326431333b").unwrap(), //#[allow_ci]
+ );
+ }
+}
From 7decf3b0985235e28f36c1b445def535d615c8df Mon Sep 17 00:00:00 2001
From: Daiki Ueno <dueno@redhat.com>
Date: Sun, 15 May 2022 17:46:36 +0200
Subject: [PATCH 3/4] ima_emulator: Support PCR hash algorithms other than
SHA-1
This ports the recent changes to keylime_ima_emulator in the Python
stack, which allows PCR hash algorithms other than SHA-1 through the
command-line options.
Signed-off-by: Daiki Ueno <dueno@redhat.com>
---
src/algorithms.rs | 1 -
src/ima_emulator.rs | 176 ++++++++++++++++++++++++++++++--------------
2 files changed, 122 insertions(+), 55 deletions(-)
diff --git a/src/algorithms.rs b/src/algorithms.rs
index 019f9360..3386abd7 100644
--- a/src/algorithms.rs
+++ b/src/algorithms.rs
@@ -10,7 +10,6 @@ use tss_esapi::{
AsymmetricAlgorithm, HashingAlgorithm, SignatureSchemeAlgorithm,
},
structures::{HashScheme, SignatureScheme},
- tss2_esys::TPMT_SIG_SCHEME,
};
// This error needs to be public because we implement TryFrom for public types
diff --git a/src/ima_emulator.rs b/src/ima_emulator.rs
index ba590bbc..c09e41b6 100644
--- a/src/ima_emulator.rs
+++ b/src/ima_emulator.rs
@@ -1,28 +1,32 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2021 Keylime Authors
+mod algorithms;
+use crate::algorithms::HashAlgorithm;
+mod ima_entry;
+use openssl::hash::{hash, MessageDigest};
+
use log::*;
-use std::convert::TryFrom;
+use clap::Parser;
+use std::collections::HashMap;
+use std::convert::{TryFrom, TryInto};
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
+use std::path::{Path, PathBuf};
use thiserror::Error;
use tss_esapi::{
abstraction::pcr,
handles::PcrHandle,
- interface_types::algorithm::HashingAlgorithm,
structures::{Digest, DigestValues, PcrSelectionListBuilder, PcrSlot},
Context, Tcti,
};
const IMA_ML: &str = "/sys/kernel/security/ima/ascii_runtime_measurements";
-const START_HASH: &[u8; 20] = &[0u8; 20];
-const FF_HASH: &[u8; 20] = &[0xffu8; 20];
-
#[derive(Error, Debug)]
enum ImaEmulatorError {
#[error("Invalid envvar")]
@@ -31,8 +35,14 @@ enum ImaEmulatorError {
TssEsapiError(#[from] tss_esapi::Error),
#[error("Decoding error")]
FromHexError(#[from] hex::FromHexError),
+ #[error("Algorithm error")]
+ AlgorithmError(#[from] algorithms::AlgorithmError),
+ #[error("OpenSSL error")]
+ OpenSSLError(#[from] openssl::error::ErrorStack),
#[error("I/O error")]
IoError(#[from] std::io::Error),
+ #[error("Integer parsing error")]
+ ParseInt(#[from] std::num::ParseIntError),
#[error("{0}")]
Other(String),
}
@@ -41,57 +51,75 @@ type Result<T> = std::result::Result<T, ImaEmulatorError>;
fn ml_extend(
context: &mut Context,
- ml: &str,
+ ml: &Path,
mut position: usize,
+ ima_hash_alg: HashAlgorithm,
+ pcr_hash_alg: HashAlgorithm,
search_hash: Option<&Digest>,
) -> Result<usize> {
let f = File::open(ml)?;
let mut reader = BufReader::new(f);
- let mut running_hash: Vec<u8> = Vec::from(&START_HASH[..]);
+ let ima_digest: MessageDigest = ima_hash_alg.into();
+ let ima_start_hash = ima_entry::Digest::start(ima_hash_alg);
+ let pcr_digest: MessageDigest = pcr_hash_alg.into();
+ let mut running_hash = ima_entry::Digest::start(pcr_hash_alg);
+ let ff_hash = ima_entry::Digest::ff(pcr_hash_alg);
for line in reader.by_ref().lines().skip(position) {
let line = line?;
if line.is_empty() {
continue;
}
- let tokens: Vec<&str> = line.splitn(5, ' ').collect();
- if tokens.len() < 5 {
- error!("invalid measurement list file line: -{}-", line);
- }
+
+ let entry: ima_entry::Entry = line.as_str().try_into()?;
+
position += 1;
- let path = tokens[4];
- let template_hash = hex::decode(tokens[1])?;
- let template_hash = if template_hash == START_HASH {
- Digest::try_from(&FF_HASH[..])
+ // Set correct hash for time of measure, time of use (ToMToU) errors
+ // and if a file is already opened for write.
+ // https://elixir.bootlin.com/linux/v5.12.12/source/security/integrity/ima/ima_main.c#L101
+ let pcr_template_hash = if entry.template_hash == ima_start_hash {
+ Digest::try_from(ff_hash.value())
} else {
- Digest::try_from(template_hash)
+ let mut event_data = vec![];
+ entry.event_data.encode(&mut event_data)?;
+ let pcr_event_hash = hash(pcr_digest, &event_data)?;
+ let ima_event_hash = hash(ima_digest, &event_data)?;
+ if ima_event_hash.as_ref() != entry.template_hash.value() {
+ return Err(ImaEmulatorError::Other(
+ "IMA template hash doesn't match".to_string(),
+ ));
+ }
+ Digest::try_from(pcr_event_hash.as_ref())
}?;
match search_hash {
None => {
println!(
"extending hash {} for {}",
- hex::encode(template_hash.value()),
- &path
+ hex::encode(pcr_template_hash.value()),
+ entry.event_data.path(),
);
let mut vals = DigestValues::new();
- vals.set(HashingAlgorithm::Sha1, template_hash);
- // TODO: Add support for other hash algorithms
+ vals.set(pcr_hash_alg.into(), pcr_template_hash);
context.execute_with_nullauth_session(|ctx| {
ctx.pcr_extend(PcrHandle::Pcr10, vals)
})?;
}
Some(search_hash) => {
- let mut hasher = openssl::sha::Sha1::new();
- hasher.update(&running_hash);
- hasher.update(&template_hash);
- running_hash = hasher.finish().into();
- let digest = Digest::try_from(running_hash.as_slice())?;
+ let mut hasher = openssl::hash::Hasher::new(pcr_digest)?;
+ hasher.update(running_hash.value())?;
+ hasher.update(&pcr_template_hash)?;
+ running_hash =
+ ima_entry::Digest::new(pcr_hash_alg, &hasher.finish()?)?;
+ let digest = Digest::try_from(running_hash.value())?;
let mut vals = DigestValues::new();
- vals.set(HashingAlgorithm::Sha1, digest.clone());
+ vals.set(pcr_hash_alg.into(), digest.clone());
if digest == *search_hash {
- println!("Located last IMA file updated: {}", path);
+ println!(
+ "Located last IMA file updated: {}",
+ entry.event_data.path()
+ );
return Ok(position);
}
}
@@ -106,7 +134,20 @@ fn ml_extend(
Ok(position)
}
+#[derive(Parser)]
+#[clap(about)]
+struct Args {
+ #[clap(long = "hash_algs", short = 'a', default_value = "sha1")]
+ hash_algs: Vec<String>,
+ #[clap(long, short = 'i', default_value = "sha1")]
+ ima_hash_alg: String,
+ #[clap(long, short = 'f', default_value = IMA_ML)]
+ ima_log: PathBuf,
+}
+
fn main() -> std::result::Result<(), ImaEmulatorError> {
+ let args = Args::parse();
+
let tcti =
match Tcti::from_environment_variable() {
Ok(tcti) => tcti,
@@ -124,37 +165,64 @@ fn main() -> std::result::Result<(), ImaEmulatorError> {
));
}
- // check if pcr is clean
- let pcr_list = PcrSelectionListBuilder::new()
- .with_selection(HashingAlgorithm::Sha1, &[PcrSlot::Slot10])
- .build()?;
- let pcr_data = context
- .execute_without_session(|ctx| pcr::read_all(ctx, pcr_list))?;
- let digest = pcr_data
- .pcr_bank(HashingAlgorithm::Sha1)
- .ok_or_else(|| {
- ImaEmulatorError::Other(
- "IMA slot does not have SHA-1 bank".to_string(),
- )
- })?
- .get_digest(PcrSlot::Slot10)
- .ok_or_else(|| {
- ImaEmulatorError::Other(
- "could not read value from IMA PCR".to_string(),
- )
- })?;
-
- let mut pos = 0;
-
- if digest.value() != START_HASH {
- log::warn!("IMA PCR is not empty, trying to find the last updated file in the measurement list...");
- pos = ml_extend(&mut context, IMA_ML, 0, Some(digest))?;
+ let ima_hash_alg: HashAlgorithm =
+ args.ima_hash_alg.as_str().try_into()?;
+ let mut positions = HashMap::new();
+ for pcr_hash_alg in args.hash_algs {
+ let pcr_hash_alg: HashAlgorithm = pcr_hash_alg.as_str().try_into()?;
+ positions.insert(pcr_hash_alg, 0usize);
+ }
+
+ for (pcr_hash_alg, position) in positions.iter_mut() {
+ // check if pcr is clean
+ let pcr_list = PcrSelectionListBuilder::new()
+ .with_selection((*pcr_hash_alg).into(), &[PcrSlot::Slot10])
+ .build()?;
+ let pcr_data = context
+ .execute_without_session(|ctx| pcr::read_all(ctx, pcr_list))?;
+ let digest = pcr_data
+ .pcr_bank((*pcr_hash_alg).into())
+ .ok_or_else(|| {
+ ImaEmulatorError::Other(format!(
+ "IMA slot does not have {} bank",
+ *pcr_hash_alg,
+ ))
+ })?
+ .get_digest(PcrSlot::Slot10)
+ .ok_or_else(|| {
+ ImaEmulatorError::Other(
+ "could not read value from IMA PCR".to_string(),
+ )
+ })?;
+
+ let pcr_digest: MessageDigest = (*pcr_hash_alg).into();
+ let pcr_start_hash = vec![0x00u8; pcr_digest.size()];
+ if digest.value() != pcr_start_hash {
+ log::warn!("IMA PCR is not empty, trying to find the last updated file in the measurement list...");
+ *position = ml_extend(
+ &mut context,
+ &args.ima_log,
+ *position,
+ ima_hash_alg,
+ *pcr_hash_alg,
+ Some(digest),
+ )?;
+ }
}
- println!("Monitoring {}", IMA_ML);
+ println!("Monitoring {}", args.ima_log.display());
loop {
- pos = ml_extend(&mut context, IMA_ML, pos, None)?;
+ for (pcr_hash_alg, position) in positions.iter_mut() {
+ *position = ml_extend(
+ &mut context,
+ &args.ima_log,
+ *position,
+ ima_hash_alg,
+ *pcr_hash_alg,
+ None,
+ )?;
+ }
// FIXME: We could poll IMA_ML as in the python implementation, though
// the file is not pollable:
From d0e2dd7dc9f24df1eb5ff3f8cde95e5e561992d6 Mon Sep 17 00:00:00 2001
From: Karel Srot <ksrot@redhat.com>
Date: Wed, 6 Jul 2022 22:02:53 +0200
Subject: [PATCH 4/4] Enable usage of Rust IMA emulator in E2E tests.
Instalation of Rust IMA emulator has been implemented in
https://github.com/RedHat-SP-Security/keylime-tests/pull/141
Signed-off-by: Karel Srot <ksrot@redhat.com>
---
packit-ci.fmf | 1 +
1 file changed, 1 insertion(+)
diff --git a/packit-ci.fmf b/packit-ci.fmf
index 9a9f9581..064bc0ae 100644
--- a/packit-ci.fmf
+++ b/packit-ci.fmf
@@ -4,6 +4,7 @@
environment:
TPM_BINARY_MEASUREMENTS: /var/tmp/binary_bios_measurements
+ RUST_IMA_EMULATOR: 1
prepare:
how: shell

View File

@ -1,339 +0,0 @@
--- a/src/common.rs 2022-06-03 17:28:38.000000000 +0200
+++ b/src/common.rs 2022-07-04 13:48:28.257066939 +0200
@@ -231,59 +231,122 @@
impl KeylimeConfig {
pub fn build() -> Result<Self> {
- let agent_ip =
- config_get_env("cloud_agent", "cloudagent_ip", "CLOUDAGENT_IP")?;
+ let conf_name = config_file_get();
+ let conf = Ini::load_from_file(&conf_name)?;
+
+ let agent_ip = config_get_env(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "cloudagent_ip",
+ "CLOUDAGENT_IP",
+ )?;
let agent_port = config_get_env(
+ &conf_name,
+ &conf,
"cloud_agent",
"cloudagent_port",
"CLOUDAGENT_PORT",
)?;
- let registrar_ip =
- config_get_env("cloud_agent", "registrar_ip", "REGISTRAR_IP")?;
+ let registrar_ip = config_get_env(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "registrar_ip",
+ "REGISTRAR_IP",
+ )?;
let registrar_port = config_get_env(
+ &conf_name,
+ &conf,
"cloud_agent",
"registrar_port",
"REGISTRAR_PORT",
)?;
- let agent_uuid_config = config_get("cloud_agent", "agent_uuid")?;
+ let agent_uuid_config =
+ config_get(&conf_name, &conf, "cloud_agent", "agent_uuid")?;
let agent_uuid = get_uuid(&agent_uuid_config);
- let agent_contact_ip = cloudagent_contact_ip_get();
- let agent_contact_port = cloudagent_contact_port_get()?;
+ let agent_contact_ip = cloudagent_contact_ip_get(&conf_name, &conf);
+ let agent_contact_port =
+ cloudagent_contact_port_get(&conf_name, &conf)?;
let hash_alg = HashAlgorithm::try_from(
- config_get("cloud_agent", "tpm_hash_alg")?.as_str(),
+ config_get(&conf_name, &conf, "cloud_agent", "tpm_hash_alg")?
+ .as_str(),
)?;
let enc_alg = EncryptionAlgorithm::try_from(
- config_get("cloud_agent", "tpm_encryption_alg")?.as_str(),
+ config_get(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "tpm_encryption_alg",
+ )?
+ .as_str(),
)?;
let sign_alg = SignAlgorithm::try_from(
- config_get("cloud_agent", "tpm_signing_alg")?.as_str(),
+ config_get(&conf_name, &conf, "cloud_agent", "tpm_signing_alg")?
+ .as_str(),
)?;
// There was a typo in Python Keylime and this accounts for having a version
// of Keylime installed that still has this typo. TODO: Remove later
let run_revocation = bool::from_str(
- &config_get("cloud_agent", "listen_notifications")
- .or_else(|_| {
- config_get("cloud_agent", "listen_notfications")
- })?
- .to_lowercase(),
- )?;
- let revocation_cert = config_get("cloud_agent", "revocation_cert")?;
- let revocation_ip = config_get("general", "receive_revocation_ip")?;
- let revocation_port =
- config_get("general", "receive_revocation_port")?;
+ &config_get(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "listen_notifications",
+ )
+ .or_else(|_| {
+ config_get(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "listen_notfications",
+ )
+ })?
+ .to_lowercase(),
+ )?;
+ let revocation_cert =
+ config_get(&conf_name, &conf, "cloud_agent", "revocation_cert")?;
+ let revocation_ip = config_get(
+ &conf_name,
+ &conf,
+ "general",
+ "receive_revocation_ip",
+ )?;
+ let revocation_port = config_get(
+ &conf_name,
+ &conf,
+ "general",
+ "receive_revocation_port",
+ )?;
- let secure_size = config_get("cloud_agent", "secure_size")?;
- let payload_script = config_get("cloud_agent", "payload_script")?;
+ let secure_size =
+ config_get(&conf_name, &conf, "cloud_agent", "secure_size")?;
+ let payload_script =
+ config_get(&conf_name, &conf, "cloud_agent", "payload_script")?;
let dec_payload_filename =
- config_get("cloud_agent", "dec_payload_file")?;
- let key_filename = config_get("cloud_agent", "enc_keyname")?;
+ config_get(&conf_name, &conf, "cloud_agent", "dec_payload_file")?;
+
+ let key_filename =
+ config_get(&conf_name, &conf, "cloud_agent", "enc_keyname")?;
let extract_payload_zip = bool::from_str(
- &config_get("cloud_agent", "extract_payload_zip")?.to_lowercase(),
+ &config_get(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "extract_payload_zip",
+ )?
+ .to_lowercase(),
)?;
- let work_dir =
- config_get_env("cloud_agent", "keylime_dir", "KEYLIME_DIR")
- .or_else::<Error, _>(|_| Ok(String::from(WORK_DIR)))?;
+
+ let work_dir = config_get_env(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "keylime_dir",
+ "KEYLIME_DIR",
+ )
+ .or_else::<Error, _>(|_| Ok(String::from(WORK_DIR)))?;
let tpm_data_path = PathBuf::from(&work_dir).join(TPM_DATA);
let tpm_data = if tpm_data_path.exists() {
@@ -302,20 +365,31 @@
None
};
- let mut keylime_ca_path = config_get("cloud_agent", "keylime_ca")?;
+ let mut keylime_ca_path =
+ config_get(&conf_name, &conf, "cloud_agent", "keylime_ca")?;
if keylime_ca_path == "default" {
keylime_ca_path = Path::new(&work_dir)
.join(DEFAULT_CA_PATH)
.display()
.to_string();
}
- let revocation_actions =
- config_get("cloud_agent", "revocation_actions")
- .or_else::<Error, _>(|_| Ok(String::from(REV_ACTIONS)))?;
- let revocation_actions_dir =
- config_get("cloud_agent", "revocation_actions_dir")
- .or_else::<Error, _>(|_| Ok(String::from(REV_ACTIONS_DIR)))?;
+ let revocation_actions = config_get(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "revocation_actions",
+ )
+ .or_else::<Error, _>(|_| Ok(String::from(REV_ACTIONS)))?;
+ let revocation_actions_dir = config_get(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "revocation_actions_dir",
+ )
+ .or_else::<Error, _>(|_| Ok(String::from(REV_ACTIONS_DIR)))?;
let allow_payload_revocation_actions = match config_get(
+ &conf_name,
+ &conf,
"cloud_agent",
"allow_payload_revocation_actions",
) {
@@ -323,7 +397,7 @@
Err(_) => ALLOW_PAYLOAD_REV_ACTIONS,
};
let run_as = if permissions::get_euid() == 0 {
- match config_get("cloud_agent", "run_as") {
+ match config_get(&conf_name, &conf, "cloud_agent", "run_as") {
Ok(user_group) => Some(user_group),
Err(_) => {
warn!("Cannot drop privileges since 'run_as' is empty or missing in 'cloud_agent' section of keylime.conf.");
@@ -334,19 +408,27 @@
None
};
- let mtls_enabled =
- match config_get("cloud_agent", "mtls_cert_enabled") {
- Ok(enabled) => bool::from_str(&enabled.to_lowercase())
- .or::<Error>(Ok(MTLS_ENABLED))?,
- Err(_) => true,
- };
-
- let enable_insecure_payload =
- match config_get("cloud_agent", "enable_insecure_payload") {
- Ok(allowed) => bool::from_str(&allowed.to_lowercase())
- .or::<Error>(Ok(ALLOW_INSECURE_PAYLOAD))?,
- Err(_) => false,
- };
+ let mtls_enabled = match config_get(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "mtls_cert_enabled",
+ ) {
+ Ok(enabled) => bool::from_str(&enabled.to_lowercase())
+ .or::<Error>(Ok(MTLS_ENABLED))?,
+ Err(_) => true,
+ };
+
+ let enable_insecure_payload = match config_get(
+ &conf_name,
+ &conf,
+ "cloud_agent",
+ "enable_insecure_payload",
+ ) {
+ Ok(allowed) => bool::from_str(&allowed.to_lowercase())
+ .or::<Error>(Ok(ALLOW_INSECURE_PAYLOAD))?,
+ Err(_) => false,
+ };
Ok(KeylimeConfig {
agent_ip,
@@ -477,19 +559,11 @@
}
}
-/// Returns revocation ip from keylime.conf if env var not present
-fn revocation_ip_get() -> Result<String> {
- config_get_env("general", "receive_revocation_ip", "REVOCATION_IP")
-}
-
-/// Returns revocation port from keylime.conf if env var not present
-fn revocation_port_get() -> Result<String> {
- config_get_env("general", "receive_revocation_port", "REVOCATION_PORT")
-}
-
/// Returns the contact ip for the agent if set
-fn cloudagent_contact_ip_get() -> Option<String> {
+fn cloudagent_contact_ip_get(conf_name: &str, conf: &Ini) -> Option<String> {
match config_get_env(
+ conf_name,
+ conf,
"cloud_agent",
"agent_contact_ip",
"KEYLIME_AGENT_CONTACT_IP",
@@ -500,8 +574,13 @@
}
/// Returns the contact ip for the agent if set
-fn cloudagent_contact_port_get() -> Result<Option<u32>> {
+fn cloudagent_contact_port_get(
+ conf_name: &str,
+ conf: &Ini,
+) -> Result<Option<u32>> {
match config_get_env(
+ conf_name,
+ conf,
"cloud_agent",
"agent_contact_port",
"KEYLIME_AGENT_CONTACT_PORT",
@@ -518,15 +597,18 @@
}
/*
- * Input: [section] and key
+ * Input: conf_name, conf, [section] and key
* Return: Returns the matched key
*
* Example call:
- * let port = common::config_get("general","cloudagent_port");
+ * let port = common::config_get(conf_file_name, file_Ini,"general","cloudagent_port");
*/
-fn config_get(section: &str, key: &str) -> Result<String> {
- let conf_name = config_file_get();
- let conf = Ini::load_from_file(&conf_name)?;
+fn config_get(
+ conf_name: &str,
+ conf: &Ini,
+ section: &str,
+ key: &str,
+) -> Result<String> {
let section = match conf.section(Some(section.to_owned())) {
Some(section) => section,
None =>
@@ -554,23 +636,29 @@
}
/*
- * Input: [section] and key and environment variable
+ * Input: conf_name, conf,[section] and key and environment variable
* Return: Returns the matched key
*
* Example call:
- * let port = common::config_get_env("general","cloudagent_port", "CLOUDAGENT_PORT");
+ * let port = common::config_get_env(conf_file_name, file_Ini, "general","cloudagent_port", "CLOUDAGENT_PORT");
*/
-fn config_get_env(section: &str, key: &str, env: &str) -> Result<String> {
+fn config_get_env(
+ conf_name: &str,
+ conf: &Ini,
+ section: &str,
+ key: &str,
+ env: &str,
+) -> Result<String> {
match env::var(env) {
Ok(ip) => {
// The variable length must be larger than 0 to accept
if !ip.is_empty() {
Ok(ip)
} else {
- config_get(section, key)
+ config_get(conf_name, conf, section, key)
}
}
- _ => config_get(section, key),
+ _ => config_get(conf_name, conf, section, key),
}
}

View File

@ -1,162 +0,0 @@
From 4ec807b1bbcfaa67f85d0c2659b1f439c2f540b4 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Fri, 24 Jun 2022 16:25:54 +0200
Subject: [PATCH] permissions: Set supplementary groups when dropping
privileges
This allows the agent to run with the user primary group and have the
access given by the supplementary groups.
Fixes: #414
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
src/permissions.rs | 70 ++++++++++++++++++++++++++++++++++------------
1 file changed, 52 insertions(+), 18 deletions(-)
diff --git a/src/permissions.rs b/src/permissions.rs
index 279a6dd..86e3fae 100644
--- a/src/permissions.rs
+++ b/src/permissions.rs
@@ -2,7 +2,7 @@
// Copyright 2021 Keylime Authors
use crate::error::{Error, Result};
-use libc::{gid_t, uid_t};
+use libc::{c_char, c_int, gid_t, uid_t};
use log::*;
use std::os::unix::ffi::OsStrExt;
use std::{
@@ -13,10 +13,9 @@ use std::{
ptr,
};
-#[derive(Debug)]
pub(crate) struct UserIds {
- uid: uid_t,
- gid: gid_t,
+ passwd: libc::passwd,
+ group: libc::group,
}
pub(crate) fn get_gid() -> gid_t {
@@ -47,14 +46,14 @@ impl TryFrom<&str> for UserIds {
let group = parts[1];
// Get gid from group name
- let gid = if let Ok(g_cstr) = CString::new(group.as_bytes()) {
+ let grnam = if let Ok(g_cstr) = CString::new(group.as_bytes()) {
let p = unsafe { libc::getgrnam(g_cstr.as_ptr()) };
if p.is_null() {
let e = io::Error::last_os_error();
error!("Could not get group {}: {}", group, e);
return Err(Error::Conversion(e.to_string()));
}
- unsafe { (*p).gr_gid }
+ unsafe { (*p) }
} else {
return Err(Error::Conversion(format!(
"Failed to convert {} to CString",
@@ -63,14 +62,14 @@ impl TryFrom<&str> for UserIds {
};
// Get uid from user name
- let uid = if let Ok(u_cstr) = CString::new(user.as_bytes()) {
+ let passwd = if let Ok(u_cstr) = CString::new(user.as_bytes()) {
let p = unsafe { libc::getpwnam(u_cstr.as_ptr()) };
if p.is_null() {
let e = io::Error::last_os_error();
error!("Could not get user {}: {}", user, e);
return Err(Error::Conversion(e.to_string()));
}
- unsafe { (*p).pw_uid }
+ unsafe { (*p) }
} else {
return Err(Error::Conversion(format!(
"Failed to convert {} to CString",
@@ -78,7 +77,10 @@ impl TryFrom<&str> for UserIds {
)));
};
- Ok(UserIds { uid, gid })
+ Ok(UserIds {
+ passwd,
+ group: grnam,
+ })
}
}
@@ -88,22 +90,51 @@ impl TryFrom<&str> for UserIds {
pub(crate) fn run_as(user_group: &str) -> Result<()> {
let ids: UserIds = user_group.try_into()?;
- // Drop supplementary groups
- if unsafe { libc::setgroups(0, ptr::null()) } != 0 {
+ // Set gid
+ if unsafe { libc::setgid(ids.group.gr_gid) } != 0 {
let e = io::Error::last_os_error();
- error!("Could not drop supplementary groups: {}", e);
+ error!("Could not set group id: {}", e);
return Err(Error::Permission);
}
- // Set gid
- if unsafe { libc::setgid(ids.gid) } != 0 {
+ // Get list of supplementary groups
+ let mut sup_groups: [gid_t; 32] = [0u32; 32];
+ let mut ngroups: c_int = 32;
+ if unsafe {
+ libc::getgrouplist(
+ ids.passwd.pw_name,
+ ids.group.gr_gid,
+ sup_groups.as_mut_ptr(),
+ &mut ngroups,
+ )
+ } < 0
+ {
+ // Allocate a Vec and try again
+ let mut sup_groups: Vec<gid_t> = Vec::with_capacity(ngroups as usize);
+ if unsafe {
+ libc::getgrouplist(
+ ids.passwd.pw_name,
+ ids.group.gr_gid,
+ sup_groups.as_mut_ptr(),
+ &mut ngroups,
+ )
+ } < 0
+ {
+ error!("Could not get list of supplementary groups");
+ return Err(Error::Permission);
+ }
+ }
+
+ // Set supplementary groups
+ if unsafe { libc::setgroups(ngroups as usize, sup_groups.as_ptr()) } != 0
+ {
let e = io::Error::last_os_error();
- error!("Could not set group id: {}", e);
+ error!("Could not set supplementary groups: {}", e);
return Err(Error::Permission);
}
// Set uid
- if unsafe { libc::setuid(ids.uid) } != 0 {
+ if unsafe { libc::setuid(ids.passwd.pw_uid) } != 0 {
let e = io::Error::last_os_error();
error!("Could not set user id: {}", e);
return Err(Error::Permission);
@@ -126,9 +157,12 @@ pub(crate) fn chown(user_group: &str, path: &Path) -> Result<()> {
return Err(Error::Permission);
}
- // change directory owner to root
+ // change directory owner
let c_path = CString::new(path.as_os_str().as_bytes())?;
- if unsafe { libc::chown(c_path.as_ptr(), ids.uid, ids.gid) } != 0 {
+ if unsafe {
+ libc::chown(c_path.as_ptr(), ids.passwd.pw_uid, ids.group.gr_gid)
+ } != 0
+ {
error!("Failed to change file {} owner.", path.display());
return Err(Error::Permission);
}

View File

@ -1,20 +0,0 @@
--- a/src/main.rs 2022-06-28 16:54:37.559307146 +0200
+++ b/src/main.rs 2022-06-28 17:37:39.512350575 +0200
@@ -512,7 +512,16 @@
let ssl_context;
if config.mtls_enabled {
let keylime_ca_cert =
- crypto::load_x509(Path::new(&config.keylime_ca_path))?;
+ match crypto::load_x509(Path::new(&config.keylime_ca_path)) {
+ Ok(t) => Ok(t),
+ Err(e) => {
+ error!(
+ "Certificate not installed: {}",
+ config.keylime_ca_path
+ );
+ Err(e)
+ }
+ }?;
cert = crypto::generate_x509(&nk_priv, &config.agent_uuid)?;
mtls_cert = Some(&cert);

View File

@ -1,2 +1,2 @@
SHA512 (rust-keylime-0.1.0~20220603gitaed51c7.tar.gz) = 0a045b0caa13a582a1270428edb49a7e20cc7df15b749458a9ddb2b84c05f240225d9e876a0cc082978dc5b52f7e0175cbbc3b937edd1ffed68e252be3ea17f8
SHA512 (rust-keylime-0.1.0~20220603gitaed51c7-vendor.tar.xz) = 11bf0e27e5c94acddd04b9b17b2642f5a17a2ad13126075b93f964d07afec46fce2bf376f9bf74840a2c447b11b0e26a1ffe07c74a899685a67461c69254652e
SHA512 (rust-keylime-0.1.0~20220805git0186093.tar.gz) = bd5fc911b022938896bb26f2500338c4cf0fc623ab471c3a1e8f846448d5d7b19c9ceb953c63bd306b88d0c52430737ff04650829a8a8a28697647c5dd856445
SHA512 (rust-keylime-0.1.0~20220805git0186093-vendor.tar.xz) = e1b810b08ce14aae664efcace9a984f7c7999c878704d7e0c93ca500e6bdf638e00ecec5bc6708ff6d69296c42a0bb29c6511ce7fa3f60d41d05a14e73ed8332