Update, add services, and drop dependencies

- Update to aed51c7 commit
- Require keylime-base on Fedora >= 36
- Update clap dependency
- Drop rustc-serialize and flate2 dependencies
- Make wiremock an optional dependency and re-enable tests
- Fix serialization of structures in quotes to fix issue on big-endian
- Add systemd services for the agent and secure mount
- BuildRequire systemd for the services
- Use more descriptive error messages on missing files errors
- Set supplementary groups when dropping privileges
- Create /usr/libexec/keylime directory

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
This commit is contained in:
Anderson Toshiyuki Sasaki 2022-06-24 18:49:08 +02:00
parent 5a1aadc7b6
commit 5a0b848dcf
8 changed files with 695 additions and 25 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
/rust-keylime-0.1.0~20211110gitd5a3191.tar.gz
/rust-keylime-0.1.0~20220602gitc98e381-vendor.tar.xz
/rust-keylime-0.1.0~20220602gitc98e381.tar.gz
/rust-keylime-0.1.0~20220603gitaed51c7.tar.gz
/rust-keylime-0.1.0~20220603gitaed51c7-vendor.tar.xz

View File

@ -1,18 +1,14 @@
# keylime-agent-rust.spec
# Generated by rust2rpm 20
# missing dev-dependencies: wiremock
%bcond_with check
%bcond_without check
%global crate keylime_agent
%global crate_version 0.1.0
# As the latest git version uses an unreleased tss-esapi crate, we
# stick to an older version. FIXME: once the new tss-esapi crate is
# available on crates.io, update the revision to the latest
%global commit c98e381363752fd38e4ac45d0e654c76f11b02c5
%global commit aed51c7c8c526953e945357594352c3df2ca4ace
%global shortcommit %(c=%{commit}; echo ${c:0:7})
%global commitdate 20220602
%global commitdate 20220603
%if 0%{?rhel}
# RHEL: Use bundled deps as it doesn't ship Rust libraries
@ -22,11 +18,6 @@
%global bundled_rust_deps 0
%endif
%ifarch armv7hl
# drop debuginfo generation for armv7hl OOM problems
%global rustflags_debuginfo 0
%endif
Name: keylime-agent-rust
Version: %{crate_version}~%{commitdate}git%{shortcommit}
Release: %{?autorelease}%{!?autorelease:1%{?dist}}
@ -60,14 +51,34 @@ Source0: %{url}/archive/%{commit}/rust-keylime-%{version}.tar.gz
# cargo vendor
# tar jcf rust-keylime-%%{version}-vendor.tar.xz vendor
Source1: rust-keylime-%{version}-vendor.tar.xz
# Drop rustc-serialize and flate2, update clap, and make wiremock optional
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
ExclusiveArch: %{rust_arches}
%if 0%{?bundled_rust_deps}
BuildRequires: rust-toolset
Requires: tpm2-tss
# The keylime-base package provides the configuration file from the python
# implementation which can be used for the rust implementation. It is available
# from Fedora 36
%if 0%{?fedora} >= 36
Requires: keylime-base
%endif
BuildRequires: systemd
BuildRequires: openssl-devel
BuildRequires: libarchive-devel
BuildRequires: tpm2-tss-devel
%if 0%{?bundled_rust_deps}
BuildRequires: rust-toolset
%else
BuildRequires: rust-packaging >= 21-2
%endif
@ -79,12 +90,6 @@ Conflicts: keylime-agent
%description
Rust agent for Keylime
%files
%license LICENSE
%doc README.md
%{_bindir}/keylime_agent
%{_bindir}/keylime_ima_emulator
%prep
%autosetup -n rust-keylime-%{commit} -p1
%if 0%{?bundled_rust_deps}
@ -92,9 +97,6 @@ Rust agent for Keylime
%cargo_prep -V 1
%else
%cargo_prep
%endif
%if !0%{?bundled_rust_deps}
%generate_buildrequires
%cargo_generate_buildrequires
%endif
@ -105,6 +107,37 @@ Rust agent for Keylime
%install
%cargo_install
mkdir -p %{buildroot}/%{_sharedstatedir}/keylime
mkdir -p --mode=0700 %{buildroot}/%{_rundir}/keylime
mkdir -p --mode=0700 %{buildroot}/%{_localstatedir}/log/keylime
mkdir -p --mode=0700 %{buildroot}/%{_libexecdir}/keylime
install -Dpm 644 ./dist/systemd/system/keylime_agent.service \
%{buildroot}%{_unitdir}/keylime_agent.service
install -Dpm 644 ./dist/systemd/system/var-lib-keylime-secure.mount \
%{buildroot}%{_unitdir}/var-lib-keylime-secure.mount
%preun
%systemd_preun keylime_agent.service
%systemd_preun var-lib-keylime-secure.mount
%postun
%systemd_postun_with_restart keylime_agent.service
%systemd_postun_with_restart var-lib-keylime-secure.mount
%files
%license LICENSE
%doc README.md
%{_unitdir}/keylime_agent.service
%{_unitdir}/var-lib-keylime-secure.mount
%attr(700,keylime,keylime) %dir %{_rundir}/keylime
%attr(700,keylime,keylime) %dir %{_localstatedir}/log/keylime
%attr(700,keylime,keylime) %{_sharedstatedir}/keylime
%attr(700,keylime,keylime) %{_libexecdir}/keylime
%{_bindir}/keylime_agent
%{_bindir}/keylime_ima_emulator
%if %{with check}
%check
%cargo_test

View File

@ -0,0 +1,217 @@
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

@ -0,0 +1,43 @@
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

@ -0,0 +1,193 @@
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

@ -0,0 +1,162 @@
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

@ -0,0 +1,20 @@
--- 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~20220602gitc98e381-vendor.tar.xz) = 9860a61b03d9658fc9f7a5eb515761ac581358cd6e85b2f2fd6d41bd55f33779c8c109a8081a953f513c6da6355c3106d9c7e25d35b77742c9954982b3e7dc7b
SHA512 (rust-keylime-0.1.0~20220602gitc98e381.tar.gz) = 9802a00e6993f5059a5b9720ce6da6516c200a9960864cf5b8e64d42d0c3ef545fc15527f9782011633062867859e020f51ac1e64a97a65ab0fdf370a9bf1bec
SHA512 (rust-keylime-0.1.0~20220603gitaed51c7.tar.gz) = 0a045b0caa13a582a1270428edb49a7e20cc7df15b749458a9ddb2b84c05f240225d9e876a0cc082978dc5b52f7e0175cbbc3b937edd1ffed68e252be3ea17f8
SHA512 (rust-keylime-0.1.0~20220603gitaed51c7-vendor.tar.xz) = 071cec539b70dc25ca8a03fbc17a185d0d8bc3eb90d120da72abfa056712a41e773cc5f372d2d4dd1e6f13b8aba448c200492c830d2f2241c7cb27e5a2833352