pax_global_header 0000666 0000000 0000000 00000000064 14343407245 0014520 g ustar 00root root 0000000 0000000 52 comment=2928d8e95553395071c60ea235cbdaf1220ced35
tpm2-abrmd-3.0.0/ 0000775 0000000 0000000 00000000000 14343407245 0013465 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/.ci/ 0000775 0000000 0000000 00000000000 14343407245 0014136 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/.ci/coverity.run 0000775 0000000 0000000 00000006237 14343407245 0016543 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
# SPDX-License-Identifier: BSD-2
set -eo pipefail
# Override project to the old project name becuase coverity didn't understand the rename from
# 01org/tpm2-abrmd to tpm2-software/tpm2-abrmd
export PROJECT='01org/tpm2-abrmd'
echo "PROJECT=$PROJECT"
if [ -z "$COVERITY_SCAN_TOKEN" ]; then
echo "coverity.run invoked without COVERITY_SCAN_TOKEN set...exiting!"
exit 1
fi
if [ -z "$COVERITY_SUBMISSION_EMAIL" ]; then
echo "coverity.run invoked without COVERITY_SUBMISSION_EMAIL set...exiting!"
exit 1
fi
# Sanity check, this should only be executing on the coverity_scan branch
if [[ "$REPO_BRANCH" != *coverity_scan ]]; then
echo "coverity.run invoked for non-coverity branch $REPO_BRANCH...exiting!"
exit 1
fi
if [[ "$CC" == clang* ]]; then
echo "Coverity scan branch detected, not running with clang...exiting!"
exit 1
fi
# branch is coverity_scan
echo "Running coverity build"
# ensure coverity_scan tool is available to the container
# We cannot package these in the docker image, as we would be distributing their software
# for folks not coupled to our COVERITY_SCAN_TOKEN.
if [ ! -f "$(pwd)/cov-analysis/bin/cov-build" ]; then
curl --data-urlencode "project=$PROJECT" \
--data-urlencode "token=$COVERITY_SCAN_TOKEN" \
"https://scan.coverity.com/download/linux64" -o coverity_tool.tgz
stat coverity_tool.tgz
curl --data-urlencode "project=$PROJECT" \
--data-urlencode "token=$COVERITY_SCAN_TOKEN" \
--data-urlencode "md5=1" \
"https://scan.coverity.com/download/linux64" -o coverity_tool.md5
stat coverity_tool.md5
cat coverity_tool.md5
md5sum coverity_tool.tgz
echo "$(cat coverity_tool.md5)" coverity_tool.tgz | md5sum -c
echo "unpacking cov-analysis"
tar -xf coverity_tool.tgz
mv cov-analysis-* cov-analysis
fi
export PATH=$PATH:$(pwd)/cov-analysis/bin
echo "Which cov-build: $(which cov-build)"
# get the deps to build with
$DOCKER_BUILD_DIR/.ci/get_deps.sh "$(dirname $DOCKER_BUILD_DIR)"
pushd "$DOCKER_BUILD_DIR"
echo "Performing build with Coverity Scan"
rm -rf cov-int
./bootstrap && ./configure --enable-debug && make clean
cov-build --dir $DOCKER_BUILD_DIR/cov-int make -j $(nproc)
echo "Collecting Coverity data for submission"
rm -fr README
AUTHOR="$(git log -1 $HEAD --pretty="%aN")"
AUTHOR_EMAIL="$(git log -1 $HEAD --pretty="%aE")"
VERSION="$(git rev-parse HEAD)"
echo "Name: $AUTHOR" >> README
echo "Email: $AUTHOR_EMAIL" >> README
echo "Project: $PROJECT" >> README
echo "Build-Version: $VERSION" >> README
echo "Description: $REPO_NAME $REPO_BRANCH" >> README
echo "Submitted-by: $PROJECT CI" >> README
echo "---README---"
cat README
echo "---EOF---"
SCAN_FILE="$(basename $PROJECT)-scan.tgz"
rm -f "$SCAN_FILE"
tar -czf "$SCAN_FILE" README cov-int
rm -rf README cov-int
# upload the results
echo "Testing for scan results..."
stat "$SCAN_FILE"
echo "Submitting data to Coverity"
curl --form token="$COVERITY_SCAN_TOKEN" \
--form email="$COVERITY_SUBMISSION_EMAIL" \
--form project="$PROJECT" \
--form file=@"$SCAN_FILE" \
--form version="$VERSION" \
--form description="$REPO_NAME $REPO_BRANCH" \
"https://scan.coverity.com/builds?project=$PROJECT"
rm -rf $SCAN_FILE
popd
exit 0
tpm2-abrmd-3.0.0/.ci/docker.env 0000664 0000000 0000000 00000000366 14343407245 0016124 0 ustar 00root root 0000000 0000000 #SPDX-License-Identifier: BSD-2
PROJECT
DOCKER_BUILD_DIR
LD_LIBRARY_PATH=/usr/local/lib/
CC
COVERITY_SCAN_TOKEN
COVERITY_SUBMISSION_EMAIL
PROJECT
REPO_BRANCH
REPO_NAME
ENABLE_COVERAGE
ENABLE_FUZZING
DOCKER_IMAGE
TPM2TSS_BRANCH
MAKE_TARGET
tpm2-abrmd-3.0.0/.ci/docker.run 0000775 0000000 0000000 00000001760 14343407245 0016142 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
# SPDX-License-Identifier: BSD-2
set -exo pipefail
$DOCKER_BUILD_DIR/.ci/get_deps.sh "$(dirname $DOCKER_BUILD_DIR)"
pushd $DOCKER_BUILD_DIR
SCANBUILD=""
CONFIG_OPTS="--enable-unit --enable-integration --disable-silent-rules"
if [ -d build ]; then
rm -rf build
fi
./bootstrap
mkdir build
pushd build
if [ -z "$MAKE_TARGET" ]; then
if [[ -z "$CC" || "$CC" == gcc* ]]; then
SCANBUILD=""
MAKE_TARGET="distcheck"
else
SCANBUILD="scan-build --status-bugs"
CFLAGS="-D_REENTRANT"
CONFIG_EXTRA="--disable-defaultflags --enable-debug --enable-asan"
MAKE_TARGET="check"
fi
fi
if [[ "$ENABLE_COVERAGE" == "true" ]]; then
CFLAGS="-D_REENTRANT"
CONFIG_EXTRA="--disable-defaultflags --enable-code-coverage --enable-debug"
fi
export CFLAGS
$SCANBUILD ../configure --enable-debug $CONFIG_OPTS $CONFIG_EXTRA
$SCANBUILD make -j$(nproc) $MAKE_TARGET
if [[ "$ENABLE_COVERAGE" == "true" ]]; then
bash <(curl -s https://codecov.io/bash)
fi
popd
popd
exit 0
tpm2-abrmd-3.0.0/.ci/get_deps.sh 0000775 0000000 0000000 00000001467 14343407245 0016277 0 ustar 00root root 0000000 0000000 # SPDX-License-Identifier: BSD-2
set -exo pipefail
pushd "$1"
TSS_VERSION=`git ls-remote --refs --tags https://github.com/tpm2-software/tpm2-tss.git | cut --delimiter='/' --fields=3 | tail --lines=1`
if [ -z "$TPM2TSS_BRANCH" ]; then
echo "TPM2TSS_BRANCH is unset, please specify TPM2TSS_BRANCH"
exit 1
fi
# Install tpm2-tss
if [ ! -d tpm2-tss ]; then
if [ -z "$GIT_FULL_CLONE" ]; then
echo "Doing shallow clone of tss"
git_extra_flags="--depth=1"
else
echo "Doing deep clone of tss"
fi
git clone $git_extra_flags -b "${TSS_VERSION}" "https://github.com/tpm2-software/tpm2-tss.git"
pushd tpm2-tss
./bootstrap
./configure --enable-debug --disable-esys --disable-esapi --disable-fapi
make -j$(nproc)
make install
popd
else
echo "tpm2-tss already installed, skipping..."
fi
popd
exit 0
tpm2-abrmd-3.0.0/.cirrus.yml 0000664 0000000 0000000 00000002350 14343407245 0015575 0 ustar 00root root 0000000 0000000 task:
env:
CFLAGS: -I/usr/local/include
LDFLAGS: -L/usr/local/lib
LD_LIBRARY_PATH: /usr/local/lib
PKG_CONFIG_PATH: "/usr/local/lib/pkgconfig:/usr/local/libdata/pkgconfig"
ibmtpm_name: ibmtpm1563
freebsd_instance:
matrix:
image_family: freebsd-13-1
install_script:
- pkg upgrade -y
- pkg install -y bash gmake coreutils libtool pkgconf autoconf autoconf-archive openssl
- pkg install -y automake glib expat dbus dbus-glib cmocka wget git gettext-runtime tpm2-tss
- wget --quiet --show-progress --progress=dot:giga "https://downloads.sourceforge.net/project/ibmswtpm2/$ibmtpm_name.tar.gz"
- shasum -a256 $ibmtpm_name.tar.gz | grep ^fc3a17f8315c1f47670764f2384943afc0d3ba1e9a0422dacb08d455733bd1e9
- mkdir -p $ibmtpm_name
- tar xvf $ibmtpm_name.tar.gz -C $ibmtpm_name && cd $ibmtpm_name/src
- sed -i '' -e 's/gcc/clang/g' makefile
- sed -i '' -e 's/-Wall //g' makefile
- sed -i '' -e 's/-Werror //g' makefile
- gmake -j && cp tpm_server /usr/bin/
- cd -
- rm -rf $ibmtpm_name $ibmtpm_name.tar.gz
script:
- ./bootstrap
- ./configure --enable-unit=yes --enable-integration=yes --disable-dependency-tracking
- gmake -j check || { cat test-suite.log; exit 1; }
tpm2-abrmd-3.0.0/.codecov.yml 0000664 0000000 0000000 00000000056 14343407245 0015711 0 ustar 00root root 0000000 0000000 ignore:
- "test"
- "src/tabrmd-generated.*"
tpm2-abrmd-3.0.0/.github/ 0000775 0000000 0000000 00000000000 14343407245 0015025 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/.github/workflows/ 0000775 0000000 0000000 00000000000 14343407245 0017062 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/.github/workflows/main.yml 0000664 0000000 0000000 00000006633 14343407245 0020541 0 ustar 00root root 0000000 0000000 name: Linux Build Status
on:
[push, pull_request]
jobs:
build-test:
runs-on: ubuntu-latest
if: "!contains(github.ref, 'coverity_scan')"
strategy:
matrix:
DOCKER_IMAGE: [ "ubuntu-18.04", "ubuntu-20.04", "fedora-32", "opensuse-leap" ]
TPM2TSS_BRANCH: ["master"]
CC: ["gcc", "clang"]
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Launch Action
uses:
tpm2-software/ci/runCI@main
with:
PROJECT_NAME: ${{ github.event.repository.name }}
DOCKER_IMAGE: "${{ matrix.DOCKER_IMAGE }}"
TPM2TSS_BRANCH: "${{ matrix.TPM2TSS_BRANCH }}"
CC: "${{ matrix.CC }}"
GIT_FULL_CLONE: true
- name: failure
if: ${{ failure() }}
run: find -name test-suite.log | xargs cat || true
multi-arch:
runs-on: ubuntu-latest
if: "!contains(github.ref, 'coverity_scan')"
strategy:
matrix:
ARCH: [
"ubuntu-20.04.arm32v7",
"ubuntu-20.04.arm64v8",
"fedora-32.ppc64le"
]
steps:
- name: Setup QEMU
run: |
sudo apt-get update
sudo apt-get install qemu binfmt-support qemu-user-static
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- name: Check out repository
uses: actions/checkout@v2
- name: Launch Action
uses:
tpm2-software/ci/runCI@main
with:
PROJECT_NAME: ${{ github.event.repository.name }}
DOCKER_IMAGE: ${{ matrix.ARCH }}
TPM2TSS_BRANCH: master
CC: gcc
MAKE_TARGET: check
- name: failure
if: ${{ failure() }}
run: find -name test-suite.log | xargs cat || true
coverage-test:
runs-on: ubuntu-latest
if: "!contains(github.ref, 'coverity_scan')"
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Launch Action
uses:
tpm2-software/ci/runCI@main
with:
PROJECT_NAME: ${{ github.event.repository.name }}
ENABLE_COVERAGE: true
DOCKER_IMAGE: ubuntu-18.04
TPM2TSS_BRANCH: master
MAKE_TARGET: check
CC: gcc
- name: failure
if: ${{ failure() }}
run: find -name test-suite.log | xargs cat || true
coverity-test:
runs-on: ubuntu-latest
if: contains(github.ref, 'coverity_scan') && github.event_name == 'push'
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Launch Action
uses:
tpm2-software/ci/coverityScan@main
with:
PROJECT_NAME: ${{ github.event.repository.name }}
ENABLE_COVERITY: true
TPM2TSS_BRANCH: "3.0.x"
TPM2TOOLS_BRANCH: "4.0"
REPO_BRANCH: ${{ github.ref }}
REPO_NAME: ${{ github.repository }}
DOCKER_IMAGE: ubuntu-18.04
CC: gcc
COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
COVERITY_SUBMISSION_EMAIL: william.c.roberts@intel.com
whitespace-check:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && !contains(github.ref, 'coverity_scan')
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Perform Whitespace Check
env:
BASE_REF: ${{ github.base_ref }}
run: git fetch origin "$BASE_REF" && git diff --check "origin/$BASE_REF"
tpm2-abrmd-3.0.0/.gitignore 0000664 0000000 0000000 00000001503 14343407245 0015454 0 ustar 00root root 0000000 0000000 .deps/
.dirstamp
.libs/
tmp/
*.gcda
*.gcno
*.gcov
*.la
*.lo
*.log
*.o
*.pp
*.pp.bz2
*.swp
*.trs
AUTHORS
Makefile
Makefile.in
aclocal.m4
aminclude_static.am
autom4te.cache/
compile
config.guess
config.log
config.status
config.sub
configure
depcomp
install-sh
libtool
ltmain.sh
dist/tss2-tcti-tabrmd.pc
m4/libtool.m4
m4/lt~obsolete.m4
m4/ltoptions.m4
m4/ltsugar.m4
m4/ltversion.m4
m4/ax_*.m4
m4/extensions.m4
m4/pkg.m4
man/man*/
missing
test-driver
test-suite.log
selinux/all_interfaces.conf
selinux/iferror.m4
selinux/tabrmd.mod
selinux/tabrmd.mod.fc
selinux/tabrmd.tmp
src/tpm2-abrmd
src/tabrmd-generated.c
src/tabrmd-generated.h
src/libutil.a
test/*_unit
test/tcti-tabrmd_read-write
test/integration/*
!test/integration/*.c
!test/integration/*.h
dist/*.preset
dist/*.service
tpm2-abrmd-*-coverage.info
tpm2-abrmd-*-coverage/*
VERSION
tpm2-abrmd-3.0.0/.lgtm.yml 0000664 0000000 0000000 00000001057 14343407245 0015234 0 ustar 00root root 0000000 0000000 extraction:
cpp:
prepare:
packages:
- autoconf-archive
- acl
after_prepare:
- cd "$LGTM_WORKSPACE"
- mkdir installdir
- git clone https://github.com/tpm2-software/tpm2-tss.git
- cd tpm2-tss
- ./bootstrap
- ./configure --prefix="$LGTM_WORKSPACE/installdir/usr" --disable-doxygen-doc --disable-esys --disable-fapi
- make install
- export PKG_CONFIG_PATH="$LGTM_WORKSPACE/installdir/usr/lib/pkgconfig:$PKG_CONFIG_PATH"
- export LD_LIBRARY_PATH="$LGTM_WORKSPACE/installdir/usr/lib:$LD_LIBRARY_PATH"
tpm2-abrmd-3.0.0/CHANGELOG.md 0000664 0000000 0000000 00000027157 14343407245 0015312 0 ustar 00root root 0000000 0000000 # Change Log
All notable changes to this project are documented in this file.
The format is based on [Keep a CHANGELOG](http://keepachangelog.com/)
### 3.0.0 - 2022-12-05
### Fixed
- A bug in special command processing in TPM2_GetCapability when an
audit session is in use cuased tpm2-abrmd to abort.
### Added
- New SELinux interfaces for communication with keylime
### Changed
- DBUS permissions in tpm2-abrmd.conf to match the in-kernel RM, ie
/dev/tpmrm0, permissions. Now users MUST be in the tss group to
send to tpm2-abrmd over DBUS.
### 2.4.1 - 2022-03-04
### Added
- Contributor Covenant Code of Conduct.
### Fixed
- superflous warning messages about tcti status.
- `WARNING **: 11:00:56.205: tcti_conf before: "(null)"`
- `WARNING **: 11:00:56.205: tcti_conf after: "mssim"`
- GCC 11 build error: error: argument 2 of `__atomic_load’ discards ‘volatile’ qualifier`
- Initialize gerror pointer variable to NULL to fix use of unitialized memory and segfault.
- Updated missing defaults in manpage.
- Port CI to composite actions in tpm2-software/ci.
### Removed
- Dependency on 'which' utility in configure.ac.
- ubuntu-16.04 from CI.
### 2.4.0 - 2021-02-08
### Added
- configure option --enable-asan which will cause the build to produce
ASAN enabled binaries.
- CI runs with clang enable asan for tests.
- Service start depends on systemd device unit: dev-tpm0.device.
- Testing support with swtpm.
### Fixed
- Numerous memory leaks.
- udev settle service deprecation warnings.
- StandardOutput=syslog deprecation warnings.
### Changed
- StandardOutput=syslog removed from systemd service file as syslog is
deprecated since systemd version 246.
- Requires tpm2-tss version 2.4.0 or greater for udev TAG=systemd rules.
### Removed
- valgrind configure options.
- usage of udev settle service.
### 2.3.3 - 2020-08-10
### Fixed:
- Fixed handle resource leak exhausting TPM resources.
## 2.3.2 - 2020-05-18
### Added
- Added cirrus CI specific config files to enable FreeBSD builds.
### Changed
- Changed test scripts to be more portable.
- Changed include header paths specific to FreeBSD.
## 2.3.1 - 2020-01-10
### Fixed
- Provide meaningful exit codes on initialization failures.
- Prevent systemd from starting the daemon before udev changes ownership
of the TPM device node.
- Prevent systemd from starting the daemon if there is no TPM device
node.
- Prevent systemd from restarting the daemon if it fails.
- Add SELinux policy to allow daemon to resolve names.
- Add SELinux policy boolean (disabled by default) to allow daemon to
connect to all unreserved ports.
## 2.3.0 - 2019-11-13
### Added
- Add '--enable-debug' flag to configure script to simplify debug builds.
This relies on the AX_CHECK_ENABLE_DEBUG autoconf archive macro.
### Canged
- Replaced custom dynamic TCTI loading code with libtss2-tctildr from
upstream tpm2-tss repo.
### Fixed
- Explicitly set '-O2' optimization when using FORTIFY_SOURCE as required.
## 2.2.0 - 2019-07-17
### Added
- New configuration option `--disable-defaultflags/ added. This is
for use for packaging for targets that do not support the default
compilation / linking flags.
### Changed
- Use private dependencies properly in pkg-config metadata for TCTI.
- Refactor daemon main module to enable better handling of error
conditions and enable more thorough unit testing.
- Updated dependencies to ensure compatibility with pkg-config fixes
in tpm2-tss.
### Fixed
- Bug causing TCTI to block when used by libtss2-sys built with partial
reads enabled.
- Removed unnecessary libs / flags for pthreads in the TCTI pkg-config.
- Output from configure script now accurately describes the state of the
flags that govern the integration tests.
## 2.1.1 - 2019-03-08
### Fixed
- Unit tests accessing dbus have been fixed to use mock functions. Unit
tests no longer depend on dbus.
- Race condition between client connections and dbus proxy object
creation by registering bus name after instantiation of the proxy object.
## 1.3.3 - 2019-03-08
### Fixed
- Race condition between client connections and dbus proxy object
creation by registering bus name after instantiation of the proxy object.
## 2.1.0 - 2019-02-06
### Added
- `-Wstrict-overflow=5` now used in default CFLAGS.
- Handling of `TPM2_RC_CONTEXT_GAP` on behalf of users.
- Convert `TPM2_PT_CONTEXT_GAP_MAX` response from lower layer to
`UINT32_MAX`
### Changed
- travis-ci now uses 'xenial' builder
- Significant refactoring of TCTI handling code.
- `--install` added to ACLOCAL_AMFLAGS to install aclocal required macros
instead of using the default symlinks
- Launch `dbus-run-session` in the automake test environment to
automagically set up a dbus session bus instance when one isn't present.
### Fixed
- Bug caused by unloading of `libtss2-tcti-tabrmd.so` on dlclose. GLib
does not support reloading a second time.
- Bug causing `-fstack-protector-all` to be used on systems with core
libraries (i.e. libc) that do not support it. This caused failures at
link-time.
- Unnecessary symbols from libtest utility library no longer included in
TCTI library.
## 2.0.3 - 2018-10-31
### Fixed
- Update build to account for upstream change to glib '.pc' files
described in: https://gitlab.gnome.org/GNOME/glib/issues/1521
## 2.0.2 - 2018-09-10
### Fixed
- Merge fixes from 1.3.2
- --enable-integration option to configure script now works as documented.
## 1.3.2 - 2018-09-10
### Fixed
- Format specifier with wrong size in util module.
- Initialize TCTI context to 0 before setting values. This will cause all
members that aren't explicitly initialized by be 0.
## 2.0.1 - 2018-07-25
### Fixed
- Default shared object name for dynamic TCTI loading now includes major
version number suffix.
- Race condition in between session mgmt and connection teardown logic.
- Leaked references to Connection objects in SessionList when calculating
the number of sessions belonging to a Connection.
- Replace old '01org' URLs in configure script.
## 2.0.0 - 2018-06-22
### Added
- Integration test script and build support to execute integration tests
against a physical TPM2 device on the build platform.
- Implementation of dynamic TCTI initialization mechanism.
- configure option `--enable-integration` to enable integration tests.
The simulator executable must be on PATH.
- Support for version 2.0 of tpm2-tss libraries.
### Changed
- 'max-transient-objects' command line option renamted to 'max-transients'.
- Added -Wextra for more strict checks at compile time.
- Install location of headers to $(includedir)/tss2.
### Fixed
- Added missing checks for NULL parameters identified by the check-build.
- Bug in session continuation logic.
- Off by one error in HandleMap.
- Memory leak and uninitialized variable issues in unit tests.
### Removed
- Command line option --fail-on-loaded-trans.
- udev rules for TPM device node. This now lives in the tpm2-tss repo.
- Remove legacy TCTI initialization functions.
- configure option `--with-simulatorbin`.
## 1.3.1 - 2018-03-18
### Fixed
- Distribute systemd preset template instead of the generated file.
## 1.3.0 - 2018-03-02
### Added
- New configure option (--test-hwtpm) to run integration tests against a
physical TPM2 device on the build platform.
- Install systemd service file to allow on-demand systemd unit activation.
### Changed
- Converted some inappropriate uses of g_error to critical / warning instead.
- Removed use of gen_require from SELinux policy, use dbus_stub instead.
- udev rules now give tss group read / write access to the TPM device node.
- udev rules now give tss user and group read / write access to kernel RM
node.
### Fixed
- Memory leak on an error path in the AccessBroker.
## 1.2.0 - 2017-12-08
### Added
- Check SAPI library is < 2.0.0 (API change upstream).
- Abstract class for IPC frontend implementation. Port dbus code from main
module to class inheriting from the IpcFrontend.
- SELinux policy module to work around policy in Fedora.
- Limit maximum number of active sessions per connection with '--max-sessions'.
- Flush all transient objects and sessions on daemon start with '--flush-all'.
- Allow passing of sessions across connections with ContextSave / Load.
### Changed
- Set valgrind leak-check flag to 'full'.
- Client / server communication uses PF_LOCAL sockets instead of pipes.
- bootstrap script now creates VERSION file from 'git describe'. Autoconf gets
version string from it, automake distributes it in 'distcheck'.
- Test harness upgraded to simulator version 974.
- Unit tests upgraded to the 1.x cmocka API.
- Replace use of thread in CommandSource with GMainLoop.
- Replace use of file descriptors with GIO streams.
- Separate 'dispose' and 'finalize' functions in each object.
- Move creation of FDs from connection_new to calling context (dependency
inversion).
### Fixed
- Unref the GUnixFDList returned by GIO / dbus in the TCTI init function.
This fixes a memory leak in the TCTI library.
## 1.1.1 - 2017-08-25
### Added
- Systemd 'preset' file and corresponding options to the configure script.
- Option to configure script to allow the addition of a string prefix to
the udev rules file name.
### Changed
- Replace use of sigaction with g_unix_signal_* stuff from glib.
- Rewrite of INSTALL.md including info on custom configure script options.
- Default value for --with-simulatorbin configure option has been removed.
New default behavior is to disable integration tests.
- CommandSource will no longer reject commands without parameters.
- Unit tests updated to use cmocka v1.0.0 API.
- Integration tests now run daemon under valgrind memcheck and fail when
errors are found.
- CommandSource now tracks max FD in set of client FDs to prevent unnecessary
iterations over FD_SETSIZE fds.
### Fixed
- Release tarballs now include essential files missing from 1.1.0 release.
- Robustness fixes in CommandSource.
- Stability fixes in Tpm2Command handling that could result in crashes.
- int-log-compiler.sh now fails if required binaries not found.
- check-valgrind target now depends on check_PROGRAMS to ensure daemon is
built before tests are run.
- NULL deref bug in TCTI.
- Mishandling of short reads in util module.
- Race condition on daemon shutdown that could cause deadlock.
- Several logic errors & data initialization for more strict compiler
versions.
## 1.1.0 - 2017-07-01
### Added
- Integration test harness supporting parallel execution using automake
- Environment variables TABRMD_TEST_BUS_TYPE and TABRMD_TEST_BUS_NAME to
control D-Bus type and name selection respectively in the integration test
harness.
- tss2_tcti_tabrmd_init_full function to libtcti-tabrmd to allow for selection
of D-Bus bus type and name used by tpm2-abrmd instance.
- Command line option --dbus-name to control the name claimed by the daemon
on the D-Bus.
- Command line option --prng-seed-file to allow configuration of seed source.
The default is /dev/urandom. The only use of the PRNG in the daemon is to
differentiate between the connections held by a single client.
infrastructure.
- Lots of new unit and integration tests.
- Automated coverity static analysis scans.
### Changed
- New configuration option to specify location of simulator binary (required
for integration tests).
- Lots of documentation updates (README.md / INSTALL.md)
- Travis-CI now executes all tests under valgrind / memcheck.
### Fixed
- Deconflict command line options: -t for TCTI selection, -a to fail if
transient objects are already loaded in the TPM.
- Clients can hold multiple TCTI connections again (fixed regression).
- Syslog log handler now only shows info & debug messages when
G_MESSAGES_DEBUG is set to 'all'.
- Free memory in error path in integration test harness.
- distcheck make target now works.
## 1.0.0 - 2017-05-21
### Added
- Everything - initial release.
tpm2-abrmd-3.0.0/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000012564 14343407245 0016274 0 ustar 00root root 0000000 0000000
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[MAINTAINERS](MAINTAINERS).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
tpm2-abrmd-3.0.0/CONTRIBUTING.md 0000664 0000000 0000000 00000004470 14343407245 0015723 0 ustar 00root root 0000000 0000000 # Guidelines for submitting bugs:
All non security bugs should be filed on the Issues tracker:
https://github.com/01org/tpm2-abrmd/issues
Security sensitive bugs should be handled per the instructions in
SECURITY.md.
# Guideline for submitting changes:
All changes to the source code must follow the coding standard used in the
surrounding source and documented [here](doc/coding_standard_c.md).
All changes should be introduced via github pull requests. This allows anyone to
comment and provide feedback in lieu of having a mailing list. For pull requests
opened by non-maintainers, any maintainer may review and merge that pull
request. For maintainers, they either must have their pull request reviewed by
another maintainer if possible, or leave the PR open for at least 24 hours, we
consider this the window for comments.
## Patch requirements
* All tests must pass on Travis CI for the merge to occur.
* All changes should adhere to the coding standard.
* All commits must be accurately described by the associated commit message
and be limited to a single "unit" of change as described in:
http://www.ericbmerritt.com/2011/09/21/commit-hygiene-and-git.html
* All commits should adhere to the git commit message guidelines described
here: https://chris.beams.io/posts/git-commit/ with the following exceptions.
* We allow commit subject lines up to 80 characters.
* Commit subject lines should be prefixed with a string identifying the
effected subsystem / object. If the change is spread over a number of
subsystems them the prefix may be omitted.
* All contributions must adhere to the Developers Certificate of Origin. The
full text of the DCO is here: https://developercertificate.org/. Contributors
must add a 'Signed-off-by' line to their commits. This indicates the
submitters acceptance of the DCO.
## Picking a Branch
All bug fixes and changes that are backward compatible should be committed to
the `1.x` release branch. This branch will be merged back into the `master`
branch periodically. If the change you're working on is not backward
compatible (e.g. removes a command line option) then it should not go into
the release branch but instead should go into master directly.
## Guideline for merging changes
Changes must be merged with the "rebase" option on github to avoid merge commits.
This provides for a clear linear history.
tpm2-abrmd-3.0.0/INSTALL.md 0000664 0000000 0000000 00000042352 14343407245 0015123 0 ustar 00root root 0000000 0000000 This is a quick set of instructions to build, install and run the tpm2-abrmd.
# Dependencies
To build and install the tpm2-abrmd software the following dependencies are
required:
* GNU Autoconf
* GNU Autoconf archive
* GNU Automake
* GNU Libtool
* C compiler
* C Library Development Libraries and Header Files (for pthreads headers)
* pkg-config
* glib and gio 2.0 libraries and development files
* libtss2-sys, libtss2-mu and TCTI libraries from https://github.com/tpm2-software/tpm2-tss
* dbus
**NOTE**: Different GNU/Linux distros package glib-2.0 differently and so
additional packages may be required. The tabrmd requires the GObject and
GIO D-Bus support from glib-2.0 so please be sure you have whatever packages
your distro provides are installed for these features.
The following dependencies are required only if the test suite is being built
and executed.
* cmocka unit test framework
* [swtpm](https://github.com/stefanberger/swtpm) or
[tpm_server](https://sourceforge.net/projects/ibmswtpm2/) TPM2 simulator
* Alternately, run the test suite on a real TPM hardware, with a safety
attention described below.
# System User & Group
`tpm2-abrmd` must run as user `tss` or `root`.
As is common security practice we encourage *everyone* to run the `tpm2-abrmd`
as an unprivileged user. This requires creating a user account and group to
use for this purpose. Our current configuration assumes that the name for this
user and group is `tss` per the norm established by the `trousers` TPM 1.2
software stack.
This account and the associated group must be created before running the
daemon. The following command should be sufficient to create the `tss`
account:
```
$ sudo useradd --system --user-group tss
```
You may wish to further restrict this user account based on your needs. This
topic however is beyond the scope of this document.
To run tpm2-abrmd as root, which is not recommended, use the `--allow-root`
option.
# Obtaining the Source Code
As is always the case, you should check for packages available through your
Linux distro before you attempt to download and build the tpm2-abrmd from
source code directly. If you need a newer version than provided by your
Distro of choice then you should download our latest stable release here:
https://github.com/01org/tpm2-abrmd/releases/latest.
The latest unstable development work can be obtained from the Git VCS here:
https://github.com/01org/tpm2-abrmd.git. This method should be used only by
developers and should be assumed to be unstable.
The remainder of this document assumes that you have:
* obtained the tpm2-abmrd source code using a method described above
* extracted the source code if necessary
* set your current working directory to be the root of the tpm2-abrmd source
tree
## Bootstrap the Build (git only)
If you're looking to contribute to the project then you will need to build
from the project's git repository. Building from git requires some additional
work to "bootstrap" the autotools machinery. This is accomplished by
executing the `bootstrap script:
```
$ ./bootstrap
```
If you're building from a release source tarball you should skip this step.
## Configure the Build
The source code for must be configured before the tpm2-abrmd can be built. In
the most simple case you may run the `configure script without any options:
```
$ ./configure
```
If your system is capable of compiling the source code then the `configure`
script will exit with a status code of `0`. Otherwise an error code will be
returned.
### Custom `./configure` Options
In many cases you'll need to provide the `./configure` script with additional
information about your environment. Typically you'll either be telling the
script about some location to install a component, or you'll be instructing
the script to enable some additional feature or function. We'll cover each
in turn.
Invoking the configure script with the `--help` option will display
all supported options.
The default values for GNU installation directories are documented here:
https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
### D-Bus Policy: `--with-dbuspolicydir`
The `tpm2-abrmd` claims a name on the D-Bus system bus. This requires policy
to allow the `tss` user account to claim this name. By default the build
installs this configuration file to `${sysconfdir}/dbus-1/system.d`. We allow
this to be overridden using the `--with-dbuspolicydir` option.
Using Debian (and it's various derivatives) as an example we can instruct the
build to install the dbus policy configuration in the right location with the
following configure option:
```
--with-dbuspolicydir=/etc/dbus-1/system.d
```
### Systemd
In most configurations the `tpm2-abrmd` daemon should be started as part of
the boot process. To enable this we provide a systemd unit as well as a
systemd preset file.
#### Systemd Uint: `--with-systemdsystemunitdir`
By default the build installs this file to `${libdir}/systemd/system. Just
like D-Bus the location of unit files is distro specific and so you may need
to configure the build to install this file in the appropriate location.
Again using Debian as an example we can instruct the build to install the
systemd unit in the right location with the following configure option:
```
--with-systemdsystemunitdir=/lib/systemd/system
```
#### Systemd Preset Dir: `--with-systemdpresetdir=DIR`
By default the build installs the systemd preset file for the tabrmd to
`${libdir}/systemd/system-preset`. If you need to install this file to a
different directory pass the desired path to the `configure` script using this
option. For example:
```
--with-systemdpresetdir=/lib/systemd/system-preset
```
#### Systemd Preset Default: `--with-systemdpresetdisable`
The systemd preset file will enable the tabrmd by default, causing it to be
started by systemd on boot. If you wish for the daemon to be disabled by
default some reason you may use this option to the `configure` script to do
so.
#### `--datarootdir`
To override the system data directory, used for
${datadir}/dbus-1/system-services/com.intel.tss2.Tabrmd.service,
use the `--datarootdir` option.
Using Debian as an example we can instruct the build to install the
DBUS service files in the right location with the following configure option:
```
--datarootdir=/usr/share
```
### Enable Unit Tests: `--enable-unit`
When provided to the `./configure` script this option will attempt to detect
whether or not the cmocka unit testing library is installed. If not then the
configure step will fail. If it is available then the unit tests will be
enabled in the build and they will be built and executed as part of the test
harness:
```
$ ./configure --enable-unit
```
If the `./configure` script finds the cmocka framework then executing `make
check` will cause the unit tests to be built and executed.
### Integration Tests:
In addition to unit tests we provide a collection of integration tests.
Integration tests differ from unit tests in that they require a running
instance of the `tpm2-abrmd` daemon. Integration tests exercise a specific
behavior of the `tpm2-abrmd` and ensure that the daemon behaves as expected.
In order for the `tpm2-abrmd` daemon to run it must have a TPM2 device to
talk to. This can be done in two ways:
1) Using an implementation of the TPM2 in software the TPM2 device can be
simulated. This is the most flexible, fast and safest way to test the
`tpm2-abrmd`.
2) Using the TPM2 device on your computer (if it has one). This method is
*NOT* recommended and may cause damage / wear-out of the TPM and its
resources. This option is provided for developers and integrators who know
what they're doing and who understand the associated risks: You have been
warned.
The next two subsections describe these two configurations:
### Run Integration Tests: `--enable-integration`
If the configure script is passed the `--enable-integration` option then the
test harness will execute the integration tests against the software TPM2
simulator. The configure script will check for the existance of one of the
software TPM2 simulator executables `swtpm` or `tpm_server` on the PATH. An
instance of the simulator will be created for each test executable to allow the
parallel execution of test cases.
This is the recommended integration test configuration. It requires that you
first download and compile the TPM2 software simulator as documented by the
simulators' maintainers. Once you have `swtpm` or `tpm_server` built, you must
ensure that it is discoverable via the PATH environment variable when the
`./configure` script is run.
### Run Integration Tests with hardware TPM2: `--enable-test-hwtpm`
It's possible to run the integration tests against "real" TPM2 hardware, not
just against the simulator. This does however come with some restrictions.
Firstly your TPM2 hardware must be properly initialized by your platform and
the kernel properly configured to load the appropriate driver. How to do all
of this is beyond the scope of this document and so we assume the reader has
already done this.
Once your TPM2 hardware is all set up you may configure tpm2-abrmd build to
run the integration tests against the TPM2 device by passing the
`./configure` script the `--enable-test-hwtpm` option:
```
$ ./configure --enable-test-hwtpm
```
Providing this option will enable the integration tests much like the
`--enable-integration` but the configure script will not check for the
simulator executable.
The test harness can then be run with the typical `make check` though there
are some limitations due to the use of the hardware TPM:
1) The test harness does not support parallel execution of tests in this
configuration. When each test is executed a fresh instance of the daemon is
started and since there's only one instance of the TPM2 device.
2) The tests must be executed as a privileged user. This is required as the
daemon will interact with the TPM2 device through the `/dev/tpm0` device node
and access to it typically requires elevated privileges. The easiest approach,
which is also the most risky, is to run tests in this configuration as the
`root` user.
3) To simplify the test harness in this configuration the tpm2-abrmd is run
on the DBus system bus (it's run on the session bus when tests are executed
against the simulator) and the default DBus name for the daemon
(com.intel.tss2.Tabrmd) is used. This implies that any existing instance of
the daemon that may have been started when your system boots *must* be stopped
before the test harness can run tests against your TPM hardware.
Finally executing tests against real TPM2 hardware is exponentially slower
than running them against the software simulator. People often refer to the
TPM2 as a "crypto decelerator" for a reason :) As such we recommend that tests
be executed individually until you're confident your configuration works as
expected. An example follows:
```
sudo make check TESTS=test/integration/auth-session-start-flush.int
```
This example runs only the `auth-session-start-flush.int` test which isn't
particularly compute-intensive since it doesn't create any keys or carry out
any signing or encryption operations other than those done by the TPM2
device as part of its session management operations.
*WARNING*: If this test suite is executed against a TPM2 it may result in the
TPM2 device being damaged or destroyed. You have been warned ... again.
# Compilation
Compiling the code requires running `make`. You may provide `make` whatever
parameters required for your environment (e.g. to enable parallel builds) but
the defaults should be sufficient:
```
$ make
```
# Installation
Once successfully built the `tpm2-abrmd` daemon it should be installed using
the command:
```
$ sudo make install
```
This will install the executable to locations determined at configure time.
See the output of `./configure --help` for the available options. Typically
you won't need to do much more than provide an alternative `--prefix` option
at configure time, and maybe `DESTDIR` at install time if you're packaging
for a distro.
**NOTE**: It may be necessary to run ldconfig (as root) to update the run-time
bindings before executing a program that links against the tabrmd library:
```
$ sudo ldconfig
```
# Post-install
After installing the compiled software and configuration all components with
new configuration (Systemd and D-Bus) must be prompted to reload their configs.
This can be accomplished by restarting your system but this isn't strictly
necessary and is generally considered bad form.
Instead each component can be instructed to reload its config manually. The
following sections describe this process for each.
## D-Bus
The dbus-daemon will also need to be instructed to read this configuration
file (assuming it's installed in a location consulted by dbus-daemon) before
the policy will be in effect. This is typically accomplished by sending the
`dbus-daemon` the `HUP` signal like so:
```
$ sudo pkill -HUP dbus-daemon
```
If this doesn't work on your distro please consult your distro's documentation
for DBUS-DAEMON(1).
## Systemd
Assuming that the `tpm2-abrmd` unit was installed in the correct location for
your distro Systemd must be instructed to reload it's configuration. This is
accomplished with the following command:
```
$ systemctl daemon-reload
```
Once systemd has loaded the unit file you should be able to use `systemctl`
to perform the start / stop / status operations as expected. Systemd should
also now start the daemon when the system boots.
## SELinux
On systems using SELinux (for example, check with the `getenforce` utility) the
default service policy is too restrictive. The repository contains an SELinux
policy module which, when applied as below, allows the daemon to operate on
systems with SELinux in enforcing mode.
First, ensure SELinux policy development files are installed (i.e on Fedora they
can be installed with `dnf install selinux-policy-devel`).
Then build and install the policy module:
```
$ pushd selinux
$ make -f /usr/share/selinux/devel/Makefile tabrmd.pp
$ sudo semodule -i tabrmd.pp
$ sudo restorecon /path/to/installed/tpm2-abrmd
```
Once the module is installed you can ensure the daemon is correctly labelled by
checking with:
```
$ ls -Z /path/to/installed/tpm2-abrmd
system_u:object_r:tabrmd_exec_t:s0 /usr/local/sbin/tpm2-abrmd
```
## Sanity Tests
Once the `tpm2-abrmd` installed and running it's possible to query the
dbus-daemon to see evidence of its presence. The following command will list
all names on the system bus. We filter for the expected name to keep the
output manageable:
```
dbus-send --system --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames
```
that should return output like:
```
method return time=1524690245.245721 sender=org.freedesktop.DBus -> destination=:1.190 serial=3 reply_serial=2
array [
...
string "com.intel.tss2.Tabrmd"
...
]
```
This lets us know that the daemon has claimed the expected name on the system
bus, but for many this isn't enough. How do we know that the daemon isn't hung
/ unresponsive? The most simple way to do this is to use the 'introspection'
interface to query the daemon for the functions / methods that it supports.
Every dbus daemon supports this interface:
```
dbus-send --system --dest=com.intel.tss2.Tabrmd --type=method_call --print-reply /com/intel/tss2/Tabrmd/Tcti org.freedesktop.DBus.Introspectable.Introspect
```
that should return output like:
```
method return time=1524690897.749245 sender=:1.192 -> destination=:1.193 serial=7 reply_serial=2
string "
"
```
tpm2-abrmd-3.0.0/LICENSE 0000664 0000000 0000000 00000002324 14343407245 0014473 0 ustar 00root root 0000000 0000000 Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
tpm2-abrmd-3.0.0/MAINTAINERS 0000664 0000000 0000000 00000000117 14343407245 0015161 0 ustar 00root root 0000000 0000000 Philip Tricca
William Roberts
tpm2-abrmd-3.0.0/Makefile.am 0000664 0000000 0000000 00000051537 14343407245 0015534 0 ustar 00root root 0000000 0000000 # SPDX-License-Identifier: BSD-2-Clause
# we have BUILT_SOURCES so builddir must be in VPATH
VPATH = $(srcdir) $(builddir)
ACLOCAL_AMFLAGS = -I m4 --install
.PHONY: unit-count
unit-count: check
sh scripts/unit-count.sh
CLEAN_LOCAL_DEPS =
clean-local: $(CLEAN_LOCAL_DEPS)
AM_CFLAGS = $(EXTRA_CFLAGS) \
-I$(srcdir)/src -I$(srcdir)/src/include -I$(builddir)/src \
$(GIO_CFLAGS) $(GLIB_CFLAGS) $(PTHREAD_CFLAGS) \
$(TSS2_SYS_CFLAGS) $(TSS2_MU_CFLAGS) $(TSS2_TCTILDR_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) $(TSS2_RC_CFLAGS)
AM_LDFLAGS = $(EXTRA_LDFLAGS) $(CODE_COVERAGE_LIBS)
TESTS_UNIT = \
test/tpm2_unit \
test/command-attrs_unit \
test/connection_unit \
test/connection-manager_unit \
test/logging_unit \
test/message-queue_unit \
test/resource-manager_unit \
test/response-sink_unit \
test/command-source_unit \
test/handle-map-entry_unit \
test/handle-map_unit \
test/ipc-frontend_unit \
test/ipc-frontend-dbus_unit \
test/random_unit \
test/session-entry_unit \
test/session-list_unit \
test/tabrmd-init_unit \
test/tabrmd-options_unit \
test/test-skeleton_unit \
test/tcti_unit \
test/thread_unit \
test/tpm2-command_unit \
test/tpm2-response_unit \
test/tss2-tcti-tabrmd_unit \
test/tcti-tabrmd-receive_unit \
test/util_unit
TESTS_INTEGRATION = \
test/integration/auth-session-max.int \
test/integration/auth-session-start-flush.int \
test/integration/auth-session-start-save.int \
test/integration/auth-session-start-save-load.int \
test/integration/max-transient-upperbound.int \
test/integration/get-capability-handles-transient.int \
test/integration/manage-transient-keys.int \
test/integration/session-gap.int \
test/integration/session-load-from-closed-connection.int \
test/integration/session-load-from-closed-connections-lru.int \
test/integration/session-load-from-open-connection.int \
test/integration/start-auth-session.int \
test/integration/tcti-cancel.int \
test/integration/tcti-connections-max.int \
test/integration/tcti-double-finalize.int \
test/integration/tcti-set-locality.int \
test/integration/hash-sequence.int \
test/integration/not-enough-handles-for-command.int \
test/integration/password-authorization.int \
test/integration/tpm2-command-flush-no-handle.int \
test/integration/util-buf-max-upper-bound.int \
test/integration/get-capability-with-session.int
TESTS_INTEGRATION_NOHW = test/integration/tcti-connect-multiple.int
if ENABLE_ASAN
ASAN_EXTRA_ENV := \
G_SLICE=always \
ASAN_OPTIONS=fast_unwind_on_malloc=0:malloc_context_size=30
endif
# empty init for these since they're manipulated by conditionals
TESTS =
noinst_LTLIBRARIES =
XFAIL_TESTS = \
test/integration/start-auth-session.int
TEST_EXTENSIONS = .int
AM_TESTS_ENVIRONMENT = \
$(ASAN_EXTRA_ENV) \
TEST_FUNC_LIB=$(srcdir)/scripts/int-test-funcs.sh \
PATH=./src:$(PATH) \
dbus-run-session
INT_LOG_COMPILER = $(srcdir)/scripts/int-test-setup.sh
INT_LOG_FLAGS = --tabrmd-tcti=$(TABRMD_TCTI) $(ASAN_INT_LOG_FLAGS)
if ENABLE_INTEGRATION
noinst_LTLIBRARIES += $(libtest)
TESTS += $(TESTS_INTEGRATION)
if !HWTPM
TESTS += $(TESTS_INTEGRATION_NOHW)
endif
endif
if UNIT
TESTS += $(TESTS_UNIT)
endif
sbin_PROGRAMS = src/tpm2-abrmd
check_PROGRAMS = $(sbin_PROGRAMS) $(TESTS)
# libraries
libtss2_tcti_tabrmd = src/libtss2-tcti-tabrmd.la
libtest = test/integration/libtest.la
libutil = src/libutil.la
lib_LTLIBRARIES = $(libtss2_tcti_tabrmd)
noinst_LTLIBRARIES += \
$(libutil)
man_MANS = \
man/man3/Tss2_Tcti_Tabrmd_Init.3 \
man/man7/tss2-tcti-tabrmd.7 \
man/man8/tpm2-abrmd.8
libtss2_tcti_tabrmddir = $(includedir)/tss2
libtss2_tcti_tabrmd_HEADERS = $(srcdir)/src/include/tss2-tcti-tabrmd.h
EXTRA_DIST = \
src/tabrmd.xml \
test/integration/test.h \
test/integration/tpm2-struct-init.h \
src/tcti-tabrmd.map \
man/colophon.in \
man/Tss2_Tcti_Tabrmd_Init.3.in \
man/tss2-tcti-tabrmd.7.in \
man/tpm2-abrmd.8.in \
dist/tpm2-abrmd.conf \
dist/com.intel.tss2.Tabrmd.service \
scripts/int-test-funcs.sh \
scripts/int-test-setup.sh \
selinux/tabrmd.fc \
selinux/tabrmd.if \
selinux/tabrmd.te \
AUTHORS \
CHANGELOG.md \
CONTRIBUTING.md \
INSTALL.md \
LICENSE \
README.md \
VERSION
CLEANFILES = \
$(man_MANS) \
src/tabrmd-generated.c \
src/tabrmd-generated.h \
test/integration/*.log \
_tabrmd.log
DISTCLEANFILES = \
core \
default.profraw \
$(pkgconfig_DATA) \
$(systemdpreset_DATA) \
$(systemdsystemunit_DATA)
BUILT_SOURCES = src/tabrmd-generated.h src/tabrmd-generated.c
pkgconfig_DATA = dist/tss2-tcti-tabrmd.pc
dbuspolicy_DATA = dist/tpm2-abrmd.conf
if HAVE_SYSTEMD
systemdpreset_DATA = dist/tpm2-abrmd.preset
systemdsystemunit_DATA = dist/tpm2-abrmd.service
dbusservicedir = $(datadir)/dbus-1/system-services
dbusservice_DATA = dist/com.intel.tss2.Tabrmd.service
endif # HAVE_SYSTEMD
# ax_code_coverage
if AUTOCONF_CODE_COVERAGE_2019_01_06
include $(top_srcdir)/aminclude_static.am
CLEAN_LOCAL_DEPS += code-coverage-clean
distclean-local: code-coverage-dist-clean
# workaround for a bug in Autoconf Archive 2019.01.06
# (https://github.com/autoconf-archive/autoconf-archive/pull/182)
if CODE_COVERAGE_ENABLED
AM_DISTCHECK_CONFIGURE_FLAGS := $(AM_DISTCHECK_CONFIGURE_FLAGS) --disable-code-coverage
endif # CODE_COVERAGE_ENABLED
else # AUTOCONF_CODE_COVERAGE_2019_01_06
@CODE_COVERAGE_RULES@
endif # AUTOCONF_CODE_COVERAGE_2019_01_06
# This is a hack required to ensure that BUILT_SOURCES are generated before
# the file that requires them.
src/ipc-frontend-dbus.c : $(BUILT_SOURCES)
# utility library with most of the code that makes up the daemon
# -Wno-unused-parameter for automatically-generated tabrmd-generated.c:
src_libutil_la_CFLAGS = $(AM_CFLAGS) -Wno-unused-parameter
src_libutil_la_LIBADD = $(GIO_LIBS) $(GLIB_LIBS) $(PTHREAD_LIBS) \
$(TSS2_SYS_LIBS) $(TSS2_MU_LIBS) $(TSS2_TCTILDR_LIBS) $(TSS2_RC_LIBS)
src_libutil_la_SOURCES = \
src/tpm2.c \
src/tpm2.h \
src/command-attrs.c \
src/command-attrs.h \
src/command-source.c \
src/command-source.h \
src/connection.c \
src/connection.h \
src/connection-manager.c \
src/connection-manager.h \
src/control-message.c \
src/control-message.h \
src/handle-map-entry.c \
src/handle-map-entry.h \
src/handle-map.c \
src/handle-map.h \
src/ipc-frontend.c \
src/ipc-frontend.h \
src/ipc-frontend-dbus.h \
src/ipc-frontend-dbus.c \
src/logging.c \
src/logging.h \
src/message-queue.c \
src/message-queue.h \
src/random.c \
src/random.h \
src/resource-manager-session.c \
src/resource-manager-session.h \
src/resource-manager.c \
src/resource-manager.h \
src/response-sink.c \
src/response-sink.h \
src/session-entry-state-enum.c \
src/session-entry-state-enum.h \
src/session-entry.c \
src/session-entry.h \
src/session-list.c \
src/session-list.h \
src/sink-interface.c \
src/sink-interface.h \
src/source-interface.c \
src/source-interface.h \
src/tabrmd-defaults.h \
src/tabrmd-error.c \
src/tabrmd-generated.c \
src/tabrmd-generated.h \
src/tabrmd-init.c \
src/tabrmd-init.h \
src/tabrmd-options.c \
src/tabrmd-options.h \
src/tabrmd.h \
src/tcti.c \
src/tcti.h \
src/thread.c \
src/thread.h \
src/tpm2-command.c \
src/tpm2-command.h \
src/tpm2-header.c \
src/tpm2-header.h \
src/tpm2-response.c \
src/tpm2-response.h \
src/util.c \
src/util.h
test_integration_libtest_la_LIBADD = $(TSS2_SYS_LIBS) $(TSS2_TCTILDR_LIBS) \
$(GLIB_LIBS)
test_integration_libtest_la_SOURCES = \
test/integration/common.c \
test/integration/common.h \
test/integration/context-util.c \
test/integration/context-util.h \
test/integration/test-options.c \
test/integration/test-options.h \
test/mock-funcs.c \
test/mock-funcs.h \
test/mock-io-stream.c \
test/mock-io-stream.h \
test/tcti-mock.c \
test/tcti-mock.h
src_libtss2_tcti_tabrmd_la_LIBADD = $(GIO_LIBS) $(GLIB_LIBS) $(TSS2_MU_LIBS) \
$(libutil) $(TSS2_TCTILDR_LIBS)
if !ENABLE_ASAN
UNDEFINED_SYMS := -Wl,--no-undefined
endif
src_libtss2_tcti_tabrmd_la_LDFLAGS = -fPIC $(UNDEFINED_SYMS) -Wl,-z,nodelete -Wl,--version-script=$(srcdir)/src/tcti-tabrmd.map
src_libtss2_tcti_tabrmd_la_SOURCES = src/tcti-tabrmd.c src/tcti-tabrmd-priv.h $(srcdir)/src/tcti-tabrmd.map
src_tpm2_abrmd_LDADD = $(GIO_LIBS) $(GLIB_LIBS) $(PTHREAD_LIBS) \
$(TSS2_SYS_LIBS) $(TSS2_TCTILDR_LIBS) $(libutil)
src_tpm2_abrmd_SOURCES = src/tabrmd.c
AUTHORS :
git log --format='%aN <%aE>' | grep -v 'users.noreply.github.com' | sort | \
uniq -c | sort -nr | sed 's/^\s*//' | cut -d" " -f2- > $@
man/man3/%.3 : man/%.3.in
$(AM_V_GEN)$(call man_tcti_prefix,$@,$^)
man/man7/%.7 : man/%.7.in
$(AM_V_GEN)$(call man_tcti_prefix,$@,$^)
man/man8/%.8 : man/%.8.in
$(AM_V_GEN)$(call man_tcti_prefix,$@,$^)
# this function is used to generate man pages from templates found in
# $(srcdir)/man/*.in
define man_tcti_prefix
$(call make_parent_dir,$@)
-$(AM_V_at)rm -f $@
$(AM_V_GEN)sed -e "s,[@]VERSION[@],$(PACKAGE_VERSION),g;" $2 $(srcdir)/man/colophon.in >> $1
endef # man_tcti_prefix
%-generated.c %-generated.h : src/tabrmd.xml
$(AM_V_GEN)$(call make_parent_dir,$@) && \
$(GDBUS_CODEGEN) --interface-prefix=com.intel.tss2. \
--generate-c-code=src/tabrmd-generated $^
define make_parent_dir
if [ ! -d $(dir $1) ]; then mkdir -p $(dir $1); fi
endef
if UNIT
UNIT_CFLAGS = $(AM_CFLAGS) $(CMOCKA_CFLAGS)
UNIT_LIBS = $(CMOCKA_LIBS) $(libutil) $(libtest)
test_tabrmd_init_unit_CFLAGS = $(UNIT_CFLAGS)
test_tabrmd_init_unit_LDADD = $(UNIT_LIBS)
test_tabrmd_init_unit_LDFLAGS = -Wl,--wrap=g_main_loop_is_running \
-Wl,--wrap=g_main_loop_quit,--wrap=g_unix_signal_add \
-Wl,--wrap=random_seed_from_file,--wrap=random_get_bytes \
-Wl,--wrap=ipc_frontend_connect,--wrap=Tss2_TctiLdr_Initialize \
-Wl,--wrap=tpm2_init_tpm,--wrap=command_attrs_init_tpm \
-Wl,--wrap=thread_start,--wrap=tpm2_flush_all_context
test_tabrmd_init_unit_SOURCES = test/tabrmd-init_unit.c
test_tabrmd_options_unit_CFLAGS = $(UNIT_CFLAGS)
test_tabrmd_options_unit_LDADD = $(UNIT_LIBS)
test_tabrmd_options_unit_LDFLAGS = -Wl,--wrap=g_option_context_new \
-Wl,--wrap=g_option_context_add_main_entries,--wrap=g_option_context_parse \
-Wl,--wrap=set_logger,--wrap=g_option_context_free
test_tabrmd_options_unit_SOURCES = test/tabrmd-options_unit.c
test_test_skeleton_unit_CFLAGS = $(UNIT_CFLAGS)
test_test_skeleton_unit_LDADD = $(UNIT_LIBS)
test_test_skeleton_unit_SOURCES = test/test-skeleton_unit.c
test_response_sink_unit_CFLAGS = $(UNIT_CFLAGS)
test_response_sink_unit_LDADD = $(UNIT_LIBS)
test_response_sink_unit_SOURCES = test/response-sink_unit.c
test_connection_unit_CFLAGS = $(UNIT_CFLAGS)
test_connection_unit_LDADD = $(UNIT_LIBS)
test_connection_unit_SOURCES = test/connection_unit.c
test_connection_manager_unit_CFLAGS = $(UNIT_CFLAGS)
test_connection_manager_unit_LDADD = $(UNIT_LIBS)
test_connection_manager_unit_SOURCES = test/connection-manager_unit.c
test_command_attrs_unit_CFLAGS = $(UNIT_CFLAGS)
test_command_attrs_unit_LDADD = $(UNIT_LIBS)
test_command_attrs_unit_LDFLAGS = -Wl,--wrap=tpm2_get_command_attrs
test_command_attrs_unit_SOURCES = test/command-attrs_unit.c
test_command_source_unit_CFLAGS = $(UNIT_CFLAGS)
test_command_source_unit_LDADD = $(UNIT_LIBS)
test_command_source_unit_LDFLAGS = -Wl,--wrap=g_source_set_callback,--wrap=connection_manager_lookup_istream,--wrap=connection_manager_remove,--wrap=sink_enqueue,--wrap=read_tpm_buffer_alloc,--wrap=command_attrs_from_cc
test_command_source_unit_SOURCES = test/command-source_unit.c
test_handle_map_entry_unit_CFLAGS = $(UNIT_CFLAGS)
test_handle_map_entry_unit_LDADD = $(UNIT_LIBS)
test_handle_map_entry_unit_SOURCES = test/handle-map-entry_unit.c
test_handle_map_unit_CFLAGS = $(UNIT_CFLAGS)
test_handle_map_unit_LDADD = $(UNIT_LIBS)
test_handle_map_unit_SOURCES = test/handle-map_unit.c
test_ipc_frontend_unit_CFLAGS = $(UNIT_CFLAGS)
test_ipc_frontend_unit_LDADD = $(UNIT_LIBS)
test_ipc_frontend_unit_SOURCES = test/ipc-frontend_unit.c
test_ipc_frontend_dbus_unit_CFLAGS = $(UNIT_CFLAGS)
test_ipc_frontend_dbus_unit_LDADD = $(UNIT_LIBS)
test_ipc_frontend_dbus_unit_SOURCES = test/ipc-frontend-dbus_unit.c
test_logging_unit_CFLAGS = $(UNIT_CFLAGS)
test_logging_unit_LDADD = $(UNIT_LIBS)
test_logging_unit_LDFLAGS = -Wl,--wrap=getenv,--wrap=syslog
test_logging_unit_SOURCES = test/logging_unit.c
test_thread_unit_CFLAGS = $(UNIT_CFLAGS)
test_thread_unit_LDADD = $(UNIT_LIBS)
test_thread_unit_SOURCES = test/thread_unit.c
test_tpm2_command_unit_CFLAGS = $(UNIT_CFLAGS)
test_tpm2_command_unit_LDADD = $(UNIT_LIBS)
test_tpm2_command_unit_SOURCES = test/tpm2-command_unit.c
test_tpm2_response_unit_CFLAGS = $(UNIT_CFLAGS)
test_tpm2_response_unit_LDADD = $(UNIT_LIBS)
test_tpm2_response_unit_SOURCES = test/tpm2-response_unit.c
test_util_unit_CFLAGS = $(UNIT_CFLAGS)
test_util_unit_LDADD = $(UNIT_LIBS)
test_util_unit_LDFLAGS = -Wl,--wrap=g_input_stream_read,--wrap=g_output_stream_write
test_util_unit_SOURCES = test/util_unit.c
test_message_queue_unit_CFLAGS = $(UNIT_CFLAGS)
test_message_queue_unit_LDADD = $(UNIT_LIBS)
test_message_queue_unit_SOURCES = test/message-queue_unit.c
test_tpm2_unit_CFLAGS = $(UNIT_CFLAGS)
test_tpm2_unit_LDADD = $(UNIT_LIBS)
test_tpm2_unit_LDFLAGS = -Wl,--wrap=Tss2_Sys_FlushContext \
-Wl,--wrap=Tss2_Sys_ContextLoad,--wrap=Tss2_Sys_ContextSave \
-Wl,--wrap=Tss2_Sys_GetCapability,--wrap=Tss2_Sys_Initialize \
-Wl,--wrap=Tss2_Sys_Startup
test_tpm2_unit_SOURCES = test/tpm2_unit.c
test_random_unit_CFLAGS = $(UNIT_CFLAGS)
test_random_unit_LDADD = $(UNIT_LIBS)
test_random_unit_LDFLAGS = -Wl,--wrap=open,--wrap=read,--wrap=close
test_random_unit_SOURCES = test/random_unit.c
test_session_entry_unit_CFLAGS = $(UNIT_CFLAGS)
test_session_entry_unit_LDADD = $(UNIT_LIBS)
test_session_entry_unit_SOURCES = test/session-entry_unit.c
test_session_list_unit_CFLAGS = $(UNIT_CFLAGS)
test_session_list_unit_LDADD = $(UNIT_LIBS)
test_session_list_unit_SOURCES = test/session-list_unit.c
test_resource_manager_unit_CFLAGS = $(UNIT_CFLAGS)
test_resource_manager_unit_LDADD = $(UNIT_LIBS)
test_resource_manager_unit_LDFLAGS = -Wl,--wrap=tpm2_send_command,--wrap=sink_enqueue,--wrap=tpm2_context_saveflush,--wrap=tpm2_context_load
test_resource_manager_unit_SOURCES = test/resource-manager_unit.c
test_tcti_unit_CFLAGS = $(UNIT_CFLAGS)
test_tcti_unit_LDADD = $(UNIT_LIBS)
test_tcti_unit_SOURCES = test/tcti_unit.c
test_tss2_tcti_tabrmd_unit_CFLAGS = $(UNIT_CFLAGS)
test_tss2_tcti_tabrmd_unit_LDADD = $(UNIT_LIBS)
test_tss2_tcti_tabrmd_unit_LDFLAGS = -Wl,--wrap=g_dbus_proxy_call_with_unix_fd_list_sync,--wrap=tcti_tabrmd_call_cancel_sync,--wrap=tcti_tabrmd_call_set_locality_sync,--wrap=tcti_tabrmd_proxy_new_for_bus_sync
test_tss2_tcti_tabrmd_unit_SOURCES = src/tcti-tabrmd.c test/tss2-tcti-tabrmd_unit.c
test_tcti_tabrmd_receive_unit_CFLAGS = $(UNIT_CFLAGS) -DG_DISABLE_CAST_CHECKS
test_tcti_tabrmd_receive_unit_LDADD = $(UNIT_LIBS)
test_tcti_tabrmd_receive_unit_LDFLAGS = -Wl,--wrap=poll,--wrap=g_socket_connection_get_socket,--wrap=g_socket_get_fd,--wrap=g_input_stream_read,--wrap=g_io_stream_get_input_stream,--wrap=g_input_stream_read
test_tcti_tabrmd_receive_unit_SOURCES = src/tcti-tabrmd.c test/tcti-tabrmd-receive_unit.c
endif
TEST_INT_LIBS = $(libtest) $(libutil) $(libtss2_tcti_tabrmd) $(GLIB_LIBS)
test_integration_auth_session_max_int_LDADD = $(TEST_INT_LIBS)
test_integration_auth_session_max_int_SOURCES = test/integration/main.c \
test/integration/auth-session-max.int.c
test_integration_auth_session_start_flush_int_LDADD = $(TEST_INT_LIBS)
test_integration_auth_session_start_flush_int_SOURCES = test/integration/main.c \
test/integration/auth-session-start-flush.int.c
test_integration_auth_session_start_save_int_LDADD = $(TEST_INT_LIBS)
test_integration_auth_session_start_save_int_SOURCES = test/integration/main.c \
test/integration/auth-session-start-save.int.c
test_integration_auth_session_start_save_load_int_LDADD = $(TEST_INT_LIBS)
test_integration_auth_session_start_save_load_int_SOURCES = test/integration/main.c \
test/integration/auth-session-start-save-load.int.c
test_integration_max_transient_upperbound_int_LDADD = $(TEST_INT_LIBS)
test_integration_max_transient_upperbound_int_SOURCES = test/integration/main.c \
test/integration/max-transient-upperbound.int.c
test_integration_get_capability_handles_transient_int_LDADD = $(TEST_INT_LIBS)
test_integration_get_capability_handles_transient_int_SOURCES = \
test/integration/main.c test/integration/get-capability-handles-transient.int.c
test_integration_session_gap_int_LDADD = $(TEST_INT_LIBS)
test_integration_session_gap_int_SOURCES = test/integration/main.c \
test/integration/session-gap.int.c
test_integration_session_load_from_open_connection_int_LDADD = $(TEST_INT_LIBS)
test_integration_session_load_from_open_connection_int_SOURCES = \
test/integration/session-load-from-open-connection.int.c
test_integration_session_load_from_closed_connection_int_LDADD = $(TEST_INT_LIBS)
test_integration_session_load_from_closed_connection_int_SOURCES = \
test/integration/session-load-from-closed-connection.int.c
test_integration_session_load_from_closed_connections_lru_int_LDADD = $(TEST_INT_LIBS)
test_integration_session_load_from_closed_connections_lru_int_SOURCES = \
test/integration/session-load-from-closed-connections-lru.int.c
test_integration_start_auth_session_int_LDADD = $(TEST_INT_LIBS)
test_integration_start_auth_session_int_SOURCES = test/integration/main.c test/integration/start-auth-session.int.c
test_integration_tcti_cancel_int_LDADD = $(TEST_INT_LIBS)
test_integration_tcti_cancel_int_SOURCES = test/integration/main.c test/integration/tcti-cancel.int.c
test_integration_tcti_double_finalize_int_LDADD = $(TEST_INT_LIBS)
test_integration_tcti_double_finalize_int_SOURCES = test/integration/main.c \
test/integration/tcti-double-finalize.int.c
test_integration_tcti_connect_multiple_int_LDADD = $(TEST_INT_LIBS)
test_integration_tcti_connect_multiple_int_SOURCES = test/integration/tcti-connect-multiple.int.c
test_integration_tcti_connections_max_int_LDADD = $(TEST_INT_LIBS)
test_integration_tcti_connections_max_int_SOURCES = test/integration/tcti-connections-max.int.c
test_integration_tcti_set_locality_int_LDADD = $(TEST_INT_LIBS)
test_integration_tcti_set_locality_int_SOURCES = test/integration/main.c test/integration/tcti-set-locality.int.c
test_integration_hash_sequence_int_LDADD = $(TEST_INT_LIBS)
test_integration_hash_sequence_int_SOURCES = test/integration/main.c test/integration/hash-sequence.int.c
test_integration_not_enough_handles_for_command_int_LDADD = $(TEST_INT_LIBS)
test_integration_not_enough_handles_for_command_int_SOURCES = test/integration/main.c test/integration/not-enough-handles-for-command.int.c
test_integration_password_authorization_int_LDADD = $(TEST_INT_LIBS)
test_integration_password_authorization_int_SOURCES = test/integration/main.c test/integration/password-authorization.int.c
test_integration_manage_transient_keys_int_LDADD = $(TEST_INT_LIBS)
test_integration_manage_transient_keys_int_SOURCES = test/integration/main.c test/integration/manage-transient-keys.int.c
test_integration_tpm2_command_flush_no_handle_int_LDADD = $(TEST_INT_LIBS)
test_integration_tpm2_command_flush_no_handle_int_SOURCES = \
test/integration/main.c test/integration/tpm2-command-flush-no-handle.int.c
test_integration_util_buf_max_upper_bound_int_LDADD = $(TEST_INT_LIBS)
test_integration_util_buf_max_upper_bound_int_SOURCES = \
test/integration/main.c test/integration/util-buf-max-upper-bound.int.c
test_integration_get_capability_with_session_int_LDADD = $(TEST_INT_LIBS)
test_integration_get_capability_with_session_int_SOURCES = \
test/integration/main.c test/integration/get-capability-with-session.int.c
if WITH_SEPOLICY
refpoldir = $(datadir)/selinux/packages
refpol_DATA = selinux/tabrmd.pp.bz2
refpolifdir = $(datadir)/selinux/devel/include/contrib
refpolif_DATA = selinux/tabrmd.if
CLEANFILES += \
$(refpol_DATA) \
selinux/all_interfaces.conf \
selinux/iferror.m4 \
selinux/tabrmd.mod \
selinux/tabrmd.mod.fc \
selinux/tabrmd.pp \
selinux/tabrmd.tmp
selinux/all_interfaces.conf: $(SEPOL_SUPPORT) $(SEPOL_INTERFACES) $(refpolif_DATA)
@test -d $(@D) || mkdir -p $(@D)
@echo "ifdef(\`__if_error',\`m4exit(1)')" > selinux/iferror.m4
@echo "divert(-1)" > $@
$(M4) $^ selinux/iferror.m4 | $(SED) -e s/dollarsstar/\$$\*/g >> $@
@echo "divert" >> $@
%.mod: $(SEPOL_SUPPORT) selinux/all_interfaces.conf %.te
@test -d $(@D) || mkdir -p $(@D)
$(M4) $(SEPOL_M4PARAM) -s $^ > $(@:.mod=.tmp)
$(CHECKMODULE) -M -m $(@:.mod=.tmp) -o $@
%.mod.fc: $(SEPOL_SUPPORT) %.fc
@test -d $(@D) || mkdir -p $(@D)
$(M4) $(SEPOL_M4PARAM) $^ > $@
%.pp: %.mod %.mod.fc
$(SEMODULE_PACKAGE) -o $@ -m $< -f $<.fc
%.pp.bz2: %.pp
$(BZIP2) -k -f -9 $^ && touch $@
endif
tpm2-abrmd-3.0.0/README.md 0000664 0000000 0000000 00000011306 14343407245 0014745 0 ustar 00root root 0000000 0000000 
[](https://cirrus-ci.com/github/tpm2-software/tpm2-abrmd)
[](https://scan.coverity.com/projects/01org-tpm2-abrmd)
[](https://codecov.io/gh/tpm2-software/tpm2-abrmd)
[](https://lgtm.com/projects/g/tpm2-software/tpm2-abrmd/context:cpp)
# TPM2 Access Broker & Resource Manager
This is a system daemon implementing the TPM2 access broker (TAB) & Resource
Manager (RM) spec from the TCG. The daemon (tpm2-abrmd) is implemented using
Glib and the GObject system. In this documentation and in the code we use
`tpm2-abrmd` and `tabrmd` interchangeably.
## Build & Install
Instructions to build and install this software are available in the
[INSTALL.md](INSTALL.md) file.
## tpm2-abrmd
`tpm2-abrmd` is a daemon. It should be started as part of the OS boot process.
Communication between the daemon and clients using the TPM is done with a
combination of DBus and Unix pipes. DBus is used for discovery, session
management and the 'cancel', 'setLocality', and 'getPollHandles' API calls
(mostly these aren't yet implemented). Pipes are used to send and receive
TPM commands and responses (respectively) between client and server.
The daemon owns the com.intel.tss2.Tabrmd name on dbus. It can be configured
to connect to either the system or the session bus. Configuring name
selection would be a handy feature but that's future work.
Check out the man page TPM2-ABRMD(8) for the currently supported options.
## libtcti-tabrmd
This repository also hosts a client library for interacting with this daemon.
It is intended for use with the SAPI library (libtss2-sapi) like any other
TCTI. The initialization function for this library is hard coded to connect to
the tabrmd on the system bus as this is the most common configuration.
Check out the man page TSS2-TCTI-TABRMD(7) and TSS2_TCTI_TABRMD_INIT(3).
## tpm2-abrmd vs in-kernel RM
The current implementations are mostly equivalent with a few differences.
Both provide isolation between objects & sessions created by different
connections which is the core functionality required by applications. The
reason we have both is that the in-kernel RM was only added very recently
(4.12) and we have TPM2 users in environments with kernels going back to the
3.x series. So the user space RM will be around at least till everyone is
using the kernel RM.
For the short term we're recommending that developers stick to using the
tabrmd as the default to get the most stable / widest possible support.
If you structure your code properly you'll be able to switch in / out TCTI
modules with relative ease and migrating to the in-kernel RM should be pretty
painless. Eventually, all of the required features will end up in the kernel
RM and it will become the default.
How we get to the ideal future of a single RM in the kernel: our current plan
is to prototype various features in user space as a way to get them tested /
validated. There's a lot of stuff in the related TCG spec that we haven't yet
implemented and we all agree that it's generally a bad ideal to to put
features into the kernel before we:
1. understand how they work
2. how they're going to be used by applications
3. agree we want the feature at all
A good example of this are the asynchronous portions of the SAPI. Right now
with the kernel RM you can use the async API but it won't really be
asynchronous: Calls to functions that should be async will block since the
kernel doesn't supply user space with an async / polling I/O interface. For
the short term, if you want to use the SAPI in an event driven I/O framework
you will only get async I/O from the user space resource manager. In the long
run though, if this feature is important to our users, we can work to upstream
support to the in-kernel RM. The plan is to treat future features in the same
way.
This was the subject of a talk that was given @ the Linux Plumbers Conference
2017:
http://linuxplumbersconf.com/2017/ocw//system/presentations/4818/original/TPM2-kernel-evnet-app_tricca-sakkinen.pdf
# Related Specifications
* [TPM2 Software Stack Access Broker and Resource Manager](https://trustedcomputinggroup.org/wp-content/uploads/TSS-TAB-and-Resource-Manager-ver1.0-rev16_Public_Review.pdf)
* [TPM2 Software Stack System API and TPM2 Command Transmission Interface](http://www.trustedcomputinggroup.org/wp-content/uploads/TSS-system-API-01.pdf)
tpm2-abrmd-3.0.0/RELEASE.md 0000664 0000000 0000000 00000013100 14343407245 0015062 0 ustar 00root root 0000000 0000000 # Release Process:
This document describes the general process that maintainers must follow when making a release of the `tabrmd`.
# Milestones
All releases should have a milestone used to track the release. If the release version is not known, as covered in [Version Numbers](#Version Numbers),
then an "x" may be used for the unknown number, or the generic term "next" may be used. The description field of the milestone will be used to record
the CHANGELOG for that release. See [CHANGELOG Update](#CHANGELOG Update) for details.
# Version Numbers
Our releases will follow the semantic versioning scheme.
You can find a thorough description of this scheme here: [http://semver.org/](http://semver.org/)
In short, this scheme has 3 parts to the version number: A.B.C
* A is the 'major' version, incremented when an API incompatible change is made
* B is the 'minor' version, incremented when an API compatible change is made
* C is the 'micro' version, incremented for bug fix releases
Please refer to the [Semantic Versioning](http://semver.org/) website for the authoritative description.
## Version String
The version string is set for the rest of the autotools bits by autoconf.
Autoconf gets this string from the `AC_INIT` macro in the configure.ac file.
Once you decide on the next version number (using the scheme above) you must set it manually in configure.ac.
The version string must be in the form `A.B.C` where `A`, `B` and `C` are integers representing the major, minor and micro components of the version number.
## Release Candidates
In the run up to a release the maintainers may create tags to identify progress toward the release.
In these cases we will append a string to the release number to indicate progress using the abbreviation `rc` for 'release candidate'.
This string will take the form of `_rcX`.
We append an incremental digit `X` in case more than one release candidate is necessary to communicate progress as development moves forward.
# Static Analysis
Before a release is made the `coverity_scan` branch must be updated to the point in git history where the release will be made from.
This branch must be pushed to github which will cause the travis-ci infrastructure to run an automated coverity scan.
The results of this scan must be dispositioned by the maintainers before the release is made.
# CHANGELOG Update
Before tagging the repository with the release version, the maintainer MUST update the CHANGELOG file with the contents from the description field
from the corresponding release milestone and update any missing version string details in the CHANGELOG and milestone entry.
# Git Tags
When a release is made a tag is created in the git repo identifying the release by the [version string](#Version String).
The tag should be pushed to upstream git repo as the last step in the release process.
**NOTE** tags for release candidates will be deleted from the git repository after a release with the corresponding version number has been made.
**NOTE** release (not release candidate) tags should be considered immutable.
## Signed tags
Git supports GPG signed tags and for releases after the `1.1.0` release will have tags signed by a maintainer.
For details on how to sign and verify git tags see: https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work.
# Release tarballs
We use the git tag as a way to mark the point of the release in the projects history.
We do not however encourage users to build from git unless they intend to modify the source code and contribute to the project.
For the end user we provide release "tarballs" following the GNU conventions as closely as possible.
To make a release tarball use the `distcheck` make target.
This target includes a number of sanity checks that are extremely helpful.
For more information on `automake` and release tarballs see: https://www.gnu.org/software/automake/manual/html_node/Dist.html#Dist
## Hosting Releases on Github
Github automagically generates a page in their UI that maps git tags to 'releases' (even if the tag isn't for a release).
Additionally they support hosting release tarballs through this same interface.
The release tarball created in the previous step must be posted to github using the release interface.
Additionally this tarball must be accompanied by a detached GPG signature.
The Debian wiki has an excellent description of how to post a signed release to Github here: https://wiki.debian.org/Creating%20signed%20GitHub%20releases
**NOTE** release candidates must be taken down after a release with the corresponding version number is available.
## Signing Release Tarballs
Signatures must be generated using the `--detach-sign` and `--armor` options to the `gpg` command.
## Verifying Signatures
Verifying the signature on a release tarball requires the project maintainers public keys be installed in the GPG keyring of the verifier.
With both the release tarball and signature file in the same directory the following command will verify the signature:
```
$ gpg --verify tpm2-abrmd-X.Y.Z.tar.gz.asc
```
## Signing Keys
The GPG keys used to sign a release tag and the associated tarball must be the same.
Additionally they must:
* belong to a project maintainer
* be discoverable using a public GPG key server
* be associated with the maintainers github account (https://help.github.com/articles/adding-a-new-gpg-key-to-your-github-account/)
# Announcements
Release candidates and proper releases should be announced on the 01.org TPM2 mailing list: https://lists.01.org/mailman/listinfo/tpm2.
This announcement should be accompanied by a link to the release page on Github as well as a link to the CHANGELOG.md accompanying the release.
tpm2-abrmd-3.0.0/SECURITY.md 0000664 0000000 0000000 00000003105 14343407245 0015255 0 ustar 00root root 0000000 0000000 # Security Policy
## Supported Versions
Currently supported versions:
| Version | Supported |
| ------- | ------------------ |
| > 2.4.0 | :white_check_mark: |
| < 2.4.0 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
## Security Reporting Guidelines
### Reporting
Security vulnerabilities *should be emailed* to **all** members of the [MAINTAINERS](MAINTAINERS) file to coordinate the
disclosure of the vulnerability.
### Tracking
When a maintainer is notified of a security vulnerability, they *must* create a GitHub security advisory
per the instructions at:
-
Maintainers *should* use the optional feature through GitHub to request a CVE be issued, alternatively RedHat has provided CVE's
in the past and *may* be used, but preference is on GitHub as the issuing CNA.
### Publishing
Once ready, maintainers should publish the security vulnerability as outlined in:
-
As well as ensuring the publishing of the CVE, maintainers *shal*l have new release versions ready to publish at the same time as
the CVE. Maintainers *should* should strive to adhere to a sub 60 say turn around from report to release.
tpm2-abrmd-3.0.0/bootstrap 0000775 0000000 0000000 00000000643 14343407245 0015433 0 ustar 00root root 0000000 0000000 #!/bin/sh
# SPDX-License-Identifier: BSD-2-Clause
git describe --tags --always --dirty > VERSION
autoreconf --install --sym
if grep "Invalid policy. Valid policies: git-directory, minor-version." configure >/dev/null; then
echo "ERROR: ax_is_release.m4 is outdated. ./configure will fail."
echo "Please download from http://ftpmirror.gnu.org/autoconf-archive/autoconf-archive-2019.01.06.tar.xz"
exit 1
fi
tpm2-abrmd-3.0.0/configure.ac 0000664 0000000 0000000 00000022120 14343407245 0015750 0 ustar 00root root 0000000 0000000 # SPDX-License-Identifier: BSD-2-Clause
AC_INIT([tpm2-abrmd],
[m4_esyscmd_s([cat ./VERSION])],
[https://github.com/tpm2-software/tpm2-abrmd/issues],
[],
[https://github.com/tpm2-software/tpm2-abrmd])
AC_CONFIG_MACRO_DIR([m4])
AX_IS_RELEASE(dash-version)
AX_CHECK_ENABLE_DEBUG([info])
AC_PROG_CC
AC_PROG_LN_S
AC_USE_SYSTEM_EXTENSIONS
LT_INIT()
PKG_INSTALLDIR()
AM_INIT_AUTOMAKE([foreign subdir-objects])
# enable "silent-rules" option by default
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AX_RECURSIVE_EVAL([$sbindir], [SBINDIR])
AC_SUBST([SBINDIR])
AC_CONFIG_FILES([Makefile dist/tss2-tcti-tabrmd.pc dist/tpm2-abrmd.service dist/tpm2-abrmd.preset])
# propagate configure arguments to distcheck
AC_SUBST([DISTCHECK_CONFIGURE_FLAGS],[$ac_configure_args])
AX_PTHREAD([], [AC_MSG_ERROR([requires pthread])])
AC_ARG_ENABLE([unit],
[AS_HELP_STRING([--enable-unit],
[build cmocka unit tests])],,
[enable_unit=no])
AS_IF([test "x$enable_unit" != xno],
[PKG_CHECK_MODULES([CMOCKA],
[cmocka >= 1.0])])
AM_CONDITIONAL([UNIT], [test "x$enable_unit" != xno])
# -dl or -dld
AC_SEARCH_LIBS([dlopen], [dl dld], [], [
AC_MSG_ERROR([unable to find the dlopen() function])
])
PKG_CHECK_MODULES([GIO], [gio-unix-2.0])
PKG_CHECK_MODULES([GLIB], [glib-2.0])
PKG_CHECK_MODULES([GOBJECT], [gobject-2.0])
PKG_CHECK_MODULES([TSS2_SYS],[tss2-sys >= 2.4.0])
PKG_CHECK_MODULES([TSS2_MU],[tss2-mu])
PKG_CHECK_MODULES([TSS2_TCTILDR],[tss2-tctildr])
PKG_CHECK_MODULES([TSS2_RC],[tss2-rc])
AC_ARG_VAR([GDBUS_CODEGEN],[The gdbus-codegen executable.])
AC_CHECK_PROG([GDBUS_CODEGEN], [gdbus-codegen], [gdbus-codegen])
AS_IF([test x"$GDBUS_CODEGEN" != x"gdbus-codegen"],
[AC_MSG_ERROR([*** gdbus-codegen is required to build tpm2-abrmd])])
# Check OS and set library and compile flags accordingly
case "${host_os}" in
*bsd* | *BSD*)
HOSTOS='BSD'
;;
*)
#Assume linux
HOSTOS='Linux'
;;
esac
AC_SUBST([HOSTOS])
AX_CODE_COVERAGE
m4_ifdef([_AX_CODE_COVERAGE_RULES],
[AM_CONDITIONAL(AUTOCONF_CODE_COVERAGE_2019_01_06, [true])],
[AM_CONDITIONAL(AUTOCONF_CODE_COVERAGE_2019_01_06, [false])])
AX_ADD_AM_MACRO_STATIC([])
# allow
AC_ARG_ENABLE([dlclose],
[AS_HELP_STRING([--disable-dlclose],
[Some versions of libc cause a sigsegv on exit, this disables the dlclose and works around that bug])],
[AC_DEFINE([DISABLE_DLCLOSE], [1])]
)
# function from the gnu.org docs
AC_DEFUN([MY_ARG_WITH],
[AC_ARG_WITH(m4_translit([[$1]], [_], [-]),
[AS_HELP_STRING([--with-m4_translit([$1], [_], [-])],
[use $1 (default is $2)])],
[use_[]$1=$withval],
[use_[]$1=$2])
])
#
# systemd
#
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR],
[Directory for systemd service files]),,
[with_systemdsystemunitdir=${libdir}/systemd/system])
AS_IF([test "x$with_systemdsystemunitdir" != xno],
[AC_SUBST([systemdsystemunitdir],
[$with_systemdsystemunitdir])])
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
# systemd preset directory
AC_ARG_WITH([systemdpresetdir],
AS_HELP_STRING([--with-systemdpresetdir=DIR],
[Directory for systemd preset files]),,
[with_systemdpresetdir=${libdir}/systemd/system-preset])
AC_SUBST([systemdpresetdir], [$with_systemdpresetdir])
# systemd preset default (enable / disable)
AC_ARG_WITH([systemdpresetdisable],
AS_HELP_STRING([--with-systemdpresetdisable],
[Configure systemd preset to 'disable', default is 'enable']),
[AC_SUBST([SYSTEMD_PRESET_DEFAULT],[disable])],
[AC_SUBST([SYSTEMD_PRESET_DEFAULT],[enable])])
#
# dbus
#
AC_ARG_WITH([dbuspolicydir],
[AS_HELP_STRING([--with-dbuspolicydir=DIR],[D-Bus policy directory])],,
[with_dbuspolicydir=${sysconfdir}/dbus-1/system.d])
AX_NORMALIZE_PATH([with_dbuspolicydir])
AC_SUBST([dbuspolicydir], [$with_dbuspolicydir])
m4_define([PATH_PROG],
[
AC_PATH_PROG([$1],[$2],[no])
AS_IF([test "x$$1" = "xno"],
[AC_MSG_ERROR([missing required command: $2])])
])
# selinux reference policy
AC_ARG_WITH(
[sepolicy],
AS_HELP_STRING([--with-sepolicy=@<:@/usr/share/selinux/devel/include@:>@],
[build SELinux policy module]),
[
AM_PATH_PYTHON([3])
PATH_PROG([BZIP2],[bzip2])
PATH_PROG([CHECKMODULE],[checkmodule])
PATH_PROG([M4],[m4])
PATH_PROG([SED],[sed])
PATH_PROG([SEMODULE_PACKAGE],[semodule_package])
AS_IF([test "x$with_sepolicy" = xyes],
with_sepolicy=/usr/share/selinux/devel/include)
AS_IF(
[test -d $with_sepolicy],
[
sepol_support=$(find ${with_sepolicy}/support -type f \
-name '*.spt' | sort | tr '\n' ' ')
sepol_interfaces=$(find $with_sepolicy -type f -name '*.if' | \
sort | tr '\n' ' ')
],
[AC_MSG_ERROR([$with_sepolicy isn't a directory])])
AC_SUBST([SEPOL_INTERFACES],[$sepol_interfaces])
AC_SUBST([SEPOL_SUPPORT],[$sepol_support])
AC_SUBST(
[SEPOL_M4PARAM],
["-D enable_mcs -D distro_redhat -D hide_broken_symptoms \
-D mls_num_sens=16 -D mls_num_cats=1024 -D mcs_num_cats=1024"])
],[with_sepolicy=no])
AM_CONDITIONAL([WITH_SEPOLICY], [test "x$with_sepolicy" != xno])
#
# Real TPM hardware
#
AC_ARG_ENABLE([test-hwtpm],
[AS_HELP_STRING([--enable-test-hwtpm],
[enable the integration test on a real tpm hardware])],
[enable_integration=$enableval],
[enable_test_hwtpm=no])
AM_CONDITIONAL([HWTPM], [test "x$enable_test_hwtpm" != xno])
#
# enable integration tests and check for simulator binary
#
AC_ARG_ENABLE([integration],
[AS_HELP_STRING([--enable-integration],
[build and execute integration tests])],,
[enable_integration=no])
AS_IF([test "x$enable_integration" = "xyes"],
[AS_IF([test "x$enable_test_hwtpm" = "xno"],
[PATH_PROG([DBUS_RUN_SESSION], [dbus-run-session])
AC_CHECK_PROG([swtpm], [swtpm], [yes], [no])
AC_CHECK_PROG([tpm_server], [tpm_server], [yes], [no])
AS_IF([test "$swtpm" = yes], [TABRMD_TCTI=swtpm],
[AS_IF([test "$tpm_server" = yes], [TABRMD_TCTI=mssim],
[AC_MSG_ERROR([Integration tests require swtpm or tpm_server to be installed.])])])
AC_SUBST([TABRMD_TCTI])
AS_IF([test "$HOSTOS" = "Linux"],
[AC_CHECK_PROG(ss, [ss], [yes], [no])],
[AC_CHECK_PROG(ss, [sockstat], [yes], [no])])
AS_IF([test "x$ss" != "xyes"],
[AC_MSG_ERROR([Integration tests require the sockstat/ss executable to be installed.])])],
[AC_MSG_NOTICE([Integration tests will be executed against the TPM device.])])])
AM_CONDITIONAL([ENABLE_INTEGRATION],[test "x$enable_integration" = "xyes"])
AC_ARG_ENABLE([defaultflags],
[AS_HELP_STRING([--disable-defaultflags],
[Disable default preprocessor, compiler, and linker flags.])],,
[enable_defaultflags=yes])
AS_IF([test "x$enable_defaultflags" = "xyes"],
[
# preprocessor / compiler / linker flags
# these macros are defined in m4/flags.m4
AS_IF([test "x$enable_debug" = "xno"],
[AX_ADD_FORTIFY_SOURCE
ADD_COMPILER_FLAG([-O2])])
ADD_COMPILER_FLAG([-Wall])
ADD_COMPILER_FLAG([-Wextra])
AS_IF([test "x$ax_is_release" = "xno"],
[ADD_COMPILER_FLAG([-Werror])])
ADD_COMPILER_FLAG([-std=gnu99])
ADD_COMPILER_FLAG([-Wformat])
ADD_COMPILER_FLAG([-Wformat-security])
ADD_COMPILER_FLAG([-Wno-missing-braces])
ADD_COMPILER_FLAG([-fdata-sections])
ADD_COMPILER_FLAG([-ffunction-sections])
ADD_TOOLCHAIN_FLAG([-fstack-protector-all])
ADD_COMPILER_FLAG([-fpic])
ADD_COMPILER_FLAG([-fPIC])
ADD_COMPILER_FLAG([-Wstrict-overflow=5])
ADD_LINK_FLAG([-Wl,--gc-sections])
ADD_LINK_FLAG([-Wl,--no-undefined])
ADD_LINK_FLAG([-Wl,-z,noexecstack])
ADD_LINK_FLAG([-Wl,-z,now])
ADD_LINK_FLAG([-Wl,-z,relro])
])
AC_ARG_ENABLE([asan],
[AS_HELP_STRING([--enable-asan],
[Configure an ASAN enabled build.])],
[enable_asan=$enableval],
[enable_asan=no])
AS_IF([test "x$enable_asan" = "xyes"],
[
# preprocessor / compiler / linker flags
# these macros are defined in m4/flags.m4
AS_IF([test "x$enable_debug" = "xno"],
[ADD_COMPILER_FLAG([-g])])
ADD_COMPILER_FLAG([-O0])
ADD_COMPILER_FLAG([-fsanitize=address])
ADD_COMPILER_FLAG([-fno-omit-frame-pointer])
ADD_LINK_FLAG([-fsanitize=address])
])
AM_CONDITIONAL([ENABLE_ASAN],[test "x$enable_asan" = "xyes"])
AC_SUBST([PATH])
AC_OUTPUT
tpm2-abrmd-3.0.0/coverity/ 0000775 0000000 0000000 00000000000 14343407245 0015331 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/coverity/coverity-model.c 0000664 0000000 0000000 00000001045 14343407245 0020437 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
/*
* The g_error function provided by GLib message logging module:
* https://developer.gnome.org/glib/stable/glib-Message-Logging.html#g-error
* is particularly difficult for Coverity since it doesn't know that this
* function *always* aborts the program. Without this model for Coverity we
* get piles of "Dereference after null check" false positives.
*/
void g_error (const char *format, ...) {
__coverity_panic__ ();
}
tpm2-abrmd-3.0.0/dist/ 0000775 0000000 0000000 00000000000 14343407245 0014430 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/dist/com.intel.tss2.Tabrmd.service 0000664 0000000 0000000 00000000135 14343407245 0022003 0 ustar 00root root 0000000 0000000 [D-BUS Service]
Name=com.intel.tss2.Tabrmd
Exec=/bin/false
SystemdService=tpm2-abrmd.service
tpm2-abrmd-3.0.0/dist/tpm2-abrmd.conf 0000664 0000000 0000000 00000001771 14343407245 0017252 0 ustar 00root root 0000000 0000000
tpm2-abrmd-3.0.0/dist/tpm2-abrmd.preset.in 0000664 0000000 0000000 00000000054 14343407245 0020225 0 ustar 00root root 0000000 0000000 @SYSTEMD_PRESET_DEFAULT@ tpm2-abrmd.service
tpm2-abrmd-3.0.0/dist/tpm2-abrmd.service.in 0000664 0000000 0000000 00000000563 14343407245 0020370 0 ustar 00root root 0000000 0000000 [Unit]
Description=TPM2 Access Broker and Resource Management Daemon
# These settings are needed when using the device TCTI. If the
# TCP mssim is used then the settings should be commented out.
After=dev-tpm0.device
Requires=dev-tpm0.device
[Service]
Type=dbus
BusName=com.intel.tss2.Tabrmd
ExecStart=@SBINDIR@/tpm2-abrmd
User=tss
[Install]
WantedBy=multi-user.target
tpm2-abrmd-3.0.0/dist/tss2-tcti-tabrmd.pc.in 0000664 0000000 0000000 00000000613 14343407245 0020464 0 ustar 00root root 0000000 0000000 prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: tss2-tcti-tabrmd
Description: TCTI library for communicating with the TPM2 access broker / resource manager daemon (tabrmd).
URL: https://github.com/tpm2-software/tpm2-abrmd
Version: @VERSION@
Requires.private: tss2-sys tss2-mu glib-2.0 gio-2.0
Cflags: -I${includedir}
Libs: -L${libdir} -ltss2-tcti-tabrmd
tpm2-abrmd-3.0.0/doc/ 0000775 0000000 0000000 00000000000 14343407245 0014232 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/doc/coding_standard_c.md 0000664 0000000 0000000 00000021631 14343407245 0020204 0 ustar 00root root 0000000 0000000 # Coding Standard
The coding standard followed in this code base is similar in most ways to the
style followed by the Linux kernel. Naturally there are exceptions. Most
notably is the indentation (more on this later). Above all else we ask
that when modifying the code that you follow the most important rule:
check the surrounding code and imitate its style [1].
## C Standard and Compiler Stuff
Code should hold as close to the C99 standard as possible with the exception
that GCC specific extensions are generally accepted. The code must compile
without warnings for the primary target compiler when all warnings are
enabled. If a warning is unavoidable, the offending line must be documented
with an explanation of why said code can not be modified to appease the
compiler [2].
It is worth noting however that this code base has yet to run into a compiler
warning that wasn't the authors fault. It is likely that if you do find
yourself disabling `-Werror` you're probably doing something wrong.
## Comments
There are two acceptable commenting styles: block, line. Block comments
are used to document a largeish block of code, typically a function. Sometimes
block comments are useful with a function to document a loop or a particularly
tricky part of an algorithm. Line comments are typically a single sentence on a
single line. They provide some brief explanation of a line or two of code.
NOTE: Comments are most useful to convey the purpose, parameters and results
from functions and objects in a system. Excessive use of comments within a
function is indicative of two things: over documentation and code that needs
to be refactored. To combat the first case let's say that it's safe to assume
that your code will be read by competent C programmers. The second case can be
tricky and there's no rule of thumb. Follow your instincts and keep in mind
that excessive line comments are a sort of "code smell" that may mean your
code would benefit from some restructuring.
### Examples
```c
/*
* This block comment could apply to some function and describe its inner
* workings. Notice these sentences have traditional capitalization and
* punctuation... that's because it has to be read in a way completely
* unlike the Post-It style content of next-line and line comments.
*/
```
```c
// This is *not* an acceptable block comment.
// Don't do this.
// Please.
```
```c
/* This is a line comment */
```
## Whitespace
All indentation must be spaces, not tabs. Lines are indented in multiples of
4 spaces. Each line of code and documentation will end with a non-whitespace
character.
## Naming Variables, Functions and Other Stuff
Names should clearly convey the purpose of whatever is being named. While the
data type of a variable conveys some information, this alone is insufficient.
Multiple word names are descriptive and most easily read if words are
separated with the underscore character, "_".
Variable and function names must be lowercase. Words in each name must be
separated by an underscore character: "_". Macros and constants (anything
declared with #define) must be in all-caps, again with words separated by
underscores.
Objects created using the GObject system follow the GObject naming convention
with individual words in object names as upper case characters.
### Examples
```c
unsigned int table_index = find_index(jacket_table, “color”, COLOR_RED);
```
```c
int last_item = 0;
while (!last_item) {
/* ... */
}
```
```c
int char_found = is_alpha (c);
```
Single letter variable names should be avoided. Exceptions are:
* "i", "j", and "k" are loop counters or temporary array indexes
* "m" and "n" are row and column indexes for multidimensional arrays
* "c" and "s" are temporary/parameter characters or strings
* "r", "g", "b", and "a" are red, green, blue, and alpha levels, but only when
* they are used together
* "x", "y", and "z" are coordinate values
Abbreviated words in variable names should be avoided. Exceptions are:
* "char" = character
* "col" = column. Typically there is also "row" so it is not confused with color
* "cnt" = count
* "pos" = position
* "rem" = remainder
* "ctx" = context
Function names should follow the naming conventions of variables and clearly
describe not only what the function does, but also the nature of what it
returns (if anything). Functions that return boolean integers should be named
to reflect the true condition even if they are created to detect false
conditions. Functions should never be hidden in conditional statements, with
the exception of loops where it makes the code more simple.
```c
bool is_number_prime = is_prime(i);
```
```c
if (is_number_prime) {
/* ... */
}
```
```c
while (!labeled_correctly(sample[i])) {
/* ... */
}
```
A function that is exported for use in multiple source files should be
prefixed with the source file (or object module) name where it is defined.
For example, the file list.c may contain the implementation of a dynamic
list ADT including an exported method for creating a list instance and an
internal (static) method for overflow checking. The first function might be
named "list_create", and the second, "is_overflowed".
The use of the static keyword when defining functions is a useful way to scope
the visibility of the function to the same translation unit. A negative side
effect of this is preventing the testing of the function through the unit
testing harness. Generally we accept exposing symbols to get better test
coverage.
## Files
Typically every header file has the same base filename as an associated source
file. Exceptions to this rule are generally limited to modules that will
expose a separate set symbols to external consumers. In this case the internal
version of the header should be suffixed with '-priv'.
Files names are formatted in the same way as described above with the
exception of hyphens "-" separating words.
The body of each header file must be surrounded by an include guard (aka
"header guard"). These guards shall be given the same name as the file in
which they reside. Their names shall be all caps, with words separated by
the underscore character "_".
Header files should never define functions or variables.
Header files should only #include what is necessary to allow a file that
includes it to compile. Associated source files will always #include the
header of the same name, but should #include files whose resources are used
within the source even if they are already included in that header. This
provides a complete context for readers of the source file... i.e., they
don't have to search through headers to determine where a resource came from.
### Example
Consider a header and source that use FILE pointers declared in stdio.h. The
header must `#include ` to ensure files that include it will be able
to compile. The corresponding source #includes the associated header and
therefore receives the benefit of stdio.h automatically.
## Types
Types shall be selected for their use case. Variables should only be of a
signed type if something should ever be negative. A common, incorrect use, is
to declare loop counters as int instead of unsigned, or to use an int to hold
the size of some object.
## Formatting
Always use space characters, 4 spaces per level of indentation.
Conditional statements (such as if, else, while, for, switch, etc) must place
the opening brace on the same line after the end of the control flow statement.
The closing brace should be placed at the same column position as the beginning
of the associated control flow statement on a line by itself.
Function definitions specify the return type on a line, followed by the
function name followed by the first parameter. Each additional parameter is
listed on a separate line aligned with the line above. The opening brace
defining the function's scope must be on the following line at column
position 0.
A space must separate a control flow statement or function and the opening
parenthesis.
Line length should not exceed 80 characters and should be split on the nearest
whitespace or delimiter character. When splitting lines with
### Example
```c
if (some_int > 0) {
statement1;
statement2;
}
```
```c
void
some_function (short_t arg_1,
longer_name_t arg_2)
{
statement1;
statement2;
}
```
```c
some_long_variable_name =
some_long_function_name (lots_of_parameters_1, lots_of_parameters_2);
```
```c
some_long_variable_name = some_long_function_name (lots_of_parameters_1,
lots_of_parameters_2,
lots_of_parameters_3);
```
These formatting conditions are contrary to Kernighan and Ritchie's "one true brace style" [3].
## References
1. GNOME C Coding Style : https://developer.gnome.org/programming-guidelines/stable/c-coding-style.html.en
2. Alan Bridger, Mick Brooks, and Jim Pisano, C Coding Standards, 2001, http://www.alma.nrao.edu/development/computing/docs/joint/0009/2001-02-28.pdf
3. Brian Kernighan, Dennis Ritchie, The C Programing Language, 1988
tpm2-abrmd-3.0.0/doc/reference-counting.txt 0000664 0000000 0000000 00000004517 14343407245 0020564 0 ustar 00root root 0000000 0000000 In working on this project I've probably made every possible mistake that
can be made when using a reference counted object system. This document
is meant to serve as a reminder to myself and a statement to anyone else
who comes along and needs to figure out why this code is written the way
it is.
When to increment / decrement the reference count on objects:
I've been unfortunately inconsistent in my application of "rules" for when
to increase, and when to decrease the reference counts on object. I'm in
the process of fixing this so now is the right time to write this all down.
When you instantiate a new object using a *_new function it's returned to
the caller with a reference count of 1. When you're done with the object
you *must* signal to the object system that you no longer need the reference
by calling g_object_unref manually. This is all well and good as the common
case but when we start passing references to objects around in a complex
system it's not always clear. Let's cover some specific cases that have
caused me some pain and suffering in recent months:
Let's call this first example "Passing a reference, passing ownership".
I started doing this with the Tpm2Command / Tpm2Response processing
pipeline. When commands / responses are created they start with a reference
count of 1. I started out with the source *not* unref'ing the command
object and just passing the reference to the next stage in the pipeline.
My logic here was that the next state was effectively inheriting ownership
of the object so having the reference count increased and then immediately
decreased seemed superfluous to me.
However I ran into some weird edge cases early on. Mostly this is because
passing the object and assuming that a reference comes along with it isn't
something that you can formalize in source code. It's an assumption and if
someone doesn't make the same assumption (or you forget that your code
makes this assumption) you're in for some painful debugging.
Rule of thumb: When your code / function takes a reference to an object it
should be explicitly released when you're done with said object. If during
the course of the code you write you end up passing said object to another
object / thread said receiver is responsible for *taking* a reference for
themselves. This is as simple calling g_object_ref in whatever function
receives the reference.
tpm2-abrmd-3.0.0/m4/ 0000775 0000000 0000000 00000000000 14343407245 0014005 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/m4/flags.m4 0000664 0000000 0000000 00000006023 14343407245 0015344 0 ustar 00root root 0000000 0000000 dnl ADD_COMPILER_FLAG:
dnl A macro to add a CFLAG to the EXTRA_CFLAGS variable. This macro will
dnl check to be sure the compiler supports the flag. Flags can be made
dnl mandatory (configure will fail).
dnl $1: C compiler flag to add to EXTRA_CFLAGS.
dnl $2: Set to "required" to cause configure failure if flag not supported..
AC_DEFUN([ADD_COMPILER_FLAG],[
AX_CHECK_COMPILE_FLAG([$1],[
EXTRA_CFLAGS="$EXTRA_CFLAGS $1"
AC_SUBST([EXTRA_CFLAGS])],[
AS_IF([test x$2 != xrequired],[
AC_MSG_WARN([Optional CFLAG "$1" not supported by your compiler, continuing.])],[
AC_MSG_ERROR([Required CFLAG "$1" not supported by your compiler, aborting.])]
)],[
-Wall -Werror]
)]
)
dnl ADD_TOOLCHAIN_FLAG:
dnl A macro to add a CFLAG to the EXTRA_CFLAGS variable. This macro will
dnl check to be sure the toolchain (compiler + linker + libs) supports the
dnl flag. Flags can be made mandatory (configure will fail).
dnl $1: C compiler flag to add to EXTRA_CFLAGS.
dnl $2: Set to "required" to cause configure failure if flag not supported..
AC_DEFUN([ADD_TOOLCHAIN_FLAG],[
AX_CHECK_LINK_FLAG([$1],[
EXTRA_CFLAGS="$EXTRA_CFLAGS $1"
AC_SUBST([EXTRA_CFLAGS])],[
AS_IF([test x$2 != xrequired],[
AC_MSG_WARN([Optional CFLAG "$1" not supported by your toolchain, continuing.])],[
AC_MSG_ERROR([Required CFLAG "$1" not supported by your toolchain, aborting.])]
)],[
-Werror]
)]
)
dnl ADD_PREPROC_FLAG:
dnl Add the provided preprocessor flag to the EXTRA_CFLAGS variable. This
dnl macro will check to be sure the preprocessor supports the flag.
dnl The flag can be made mandatory by providing the string 'required' as
dnl the second parameter.
dnl $1: Preprocessor flag to add to EXTRA_CFLAGS.
dnl $2: Set to "required" t ocause configure failure if preprocessor flag
dnl is not supported.
AC_DEFUN([ADD_PREPROC_FLAG],[
AX_CHECK_PREPROC_FLAG([$1],[
EXTRA_CFLAGS="$EXTRA_CFLAGS $1"
AC_SUBST([EXTRA_CFLAGS])],[
AS_IF([test x$2 != xrequired],[
AC_MSG_WARN([Optional preprocessor flag "$1" not supported by your compiler, continuing.])],[
AC_MSG_ERROR([Required preprocessor flag "$1" not supported by your compiler, aborting.])]
)],[
-Wall -Werror]
)]
)
dnl ADD_LINK_FLAG:
dnl A macro to add a LDLAG to the EXTRA_LDFLAGS variable. This macro will
dnl check to be sure the linker supports the flag. Flags can be made
dnl mandatory (configure will fail).
dnl $1: linker flag to add to EXTRA_LDFLAGS.
dnl $2: Set to "required" to cause configure failure if flag not supported.
AC_DEFUN([ADD_LINK_FLAG],[
AX_CHECK_LINK_FLAG([$1],[
EXTRA_LDFLAGS="$EXTRA_LDFLAGS $1"
AC_SUBST([EXTRA_LDFLAGS])],[
AS_IF([test x$2 != xrequired],[
AC_MSG_WARN([Optional LDFLAG "$1" not supported by your linker, continuing.])],[
AC_MSG_ERROR([Required LDFLAG "$1" not supported by your linker, aborting.])]
)],[
-Werror]
)]
)
tpm2-abrmd-3.0.0/man/ 0000775 0000000 0000000 00000000000 14343407245 0014240 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/man/Tss2_Tcti_Tabrmd_Init.3.in 0000664 0000000 0000000 00000010423 14343407245 0021023 0 ustar 00root root 0000000 0000000 .\" Process this file with
.\" groff -man -Tascii foo.1
.\"
.TH TSS2_TCTI_TABRMD_INIT 3 "APRIL 2018" Intel "TPM2 Software Stack"
.SH NAME
Tss2_Tcti_Tabrmd_Init \- Initialization function for the tpm2-abrmd TCTI
library.
.SH SYNOPSIS
The
.BR Tss2_Tcti_Tabrmd_Init ()
function is used to initialize a TCTI context for communication with the
.BR tpm2-abrmd (8).
.sp
.B #include
.sp
.BI "TSS2_RC Tss2_Tcti_Tabrmd_Init (TSS2_TCTI_CONTEXT " "*tcti_context" ", size_t " "*size" ", const char " "*conf" );
.sp
.SH DESCRIPTION
.BR Tss2_Tcti_Tabrmd_Init ()
attempts to initialize a caller allocated
.I tcti_context
of size
.I size
with configuration specified in the configuration string
.I conf.
\&. Since the
.I tcti_context
must be a caller allocated buffer, the caller needs to know the size required
by the TCTI library. The minimum size of this context can be discovered by
providing
.BR NULL
for the
.I tcti_context
and a non-
.BR NULL
.I size
parameter. The initialization function will then populate the
.I size
parameter with the minimum size of the
.I tcti_context
buffer. The caller my then allocate a buffer of this size (or larger) and
call
.B tss2_Tcti_Tabrmd_Init ()
again providing the newly allocated
.I tcti_context
and the size of this context in the
.I size
parameter. This pattern is common to all TCTI initialization functions. We
provide an example of this pattern using the
.BR Tss2_Tcti_Tabrmd_Init ()
function in the section titled
.B EXAMPLE.
.sp
The
.I conf
parameter is a string of key / value pairs describing the desired connection
properties for the TCTI. If the caller provides a NULL
.I conf
string then defaults that correspond to the defaults for the tpm2-abrmd (8)
will be used. This is the same as providing the
.I conf
string: "bus_name=com.intel.tss2.Tabrmd,bus_type=system". Keys and values
are separated by the '=' character while each key / value pair is separated by
the ',' character. The supported keys and values are:
.IP \[bu] 2
.B bus_name
- the dbus name owned by the daemon. See the tpm2-abrmd (8)
.I --dbus-name
option.
.IP \[bu]
.B bus_type
- the bus type used for the connection with the daemon. The value associated
with this key may be either "system" or "session".
.RE
.sp
Once initialized, the TCTI context returned exposes the Trusted Computing
Group (TCG) defined API for the lowest level communication with the TPM.
Using this API the caller can exchange (send / receive) TPM2 command and
response buffers with the
.B tpm2-abrmd (8).
In nearly all cases however, the caller
will initialize a context using this function before passing the context to
a higher level API like the System API (SAPI), and then never touch it again.
.sp
For a more thorough discussion of the TCTI API see the \*(lqTSS System Level
API and TPM Command Transmission Interface Specification\*(rq specification
as published by the TCG:
\%https://trustedcomputinggroup.org/tss-system-level-api-tpm-command-transmission-interface-specification/
.sp
.SH RETURN VALUE
A successful call to
.BR Tss2_Tcti_Tabrmd_Init ()
will return
.B TSS2_RC_SUCCESS.
An unsuccessful call will produce a response code described in section
.B ERRORS.
.SH ERRORS
.B TSS2_TCTI_RC_BAD_VALUE
is returned if the
.I size
parameter is NULL.
.sp
.B TSS2_TCTI_RC_NO_CONNECTION
is returned when communication with the tpm2-abrmd (8) fails.
.sp
.B TSS2_TCTI_RC_GENERAL_FAILURE
is returned for all other errors.
.SH EXAMPLE
.nf
#include
#include
#include
#include
TSS2_RC rc;
TSS2_TCTI_CONTEXT *tcti_context;
size_t size;
rc = tss2_tcti_tabrmd_init (NULL, &size, NULL,
if (rc != TSS2_RC_SUCCESS) {
fprintf (stderr, "Failed to get allocation size for tabrmd TCTI "
" context: 0x%" PRIx32 "\n", rc);
exit (EXIT_FAILURE);
}
tcti_context = calloc (1, size);
if (tcti_context == NULL) {
fprintf (stderr, "Allocation for TCTI context failed: %s\n",
strerror (errno));
exit (EXIT_FAILURE);
}
rc = tss2_tcti_tabrmd_init (tcti_context, &size, NULL);
if (rc != TSS2_RC_SUCCESS) {
fprintf (stderr, "Failed to initialize tabrmd TCTI context: "
"0x%" PRIx32 "\n", rc);
free (tcti_context);
exit (EXIT_FAILURE);
}
exit (EXIT_SUCCESS);
.fi
.SH AUTHOR
Philip Tricca
.SH "SEE ALSO"
.BR tcti-tabrmd (7),
.BR tpm2-abrmd (8)
tpm2-abrmd-3.0.0/man/colophon.in 0000664 0000000 0000000 00000000421 14343407245 0016406 0 ustar 00root root 0000000 0000000 .SH COLOPHON
This page is part of the @VERSION@ release of Intel's TPM2 Access Broker &
Resource Management Daemon. A description of the project, information about
reporting bugs, and the latest version of this page can be found at
\%https://github.com/01org/tpm2\-abrmd/.
tpm2-abrmd-3.0.0/man/tpm2-abrmd.8.in 0000664 0000000 0000000 00000010200 14343407245 0016674 0 ustar 00root root 0000000 0000000 .\" Process this file with
.\" groff -man -Tascii foo.1
.\"
.TH TPM2-ABRMD 8 "March 2018" Intel "TPM2 Software Stack"
.SH NAME
tpm2-abrmd \- TPM2 access broker and resource management daemon
.SH SYNOPSIS
.B tpm2-abrmd
.RB [\-m][\-e][\-i][\-o][\-l\ logger-name][\-r][\-s][\-g\ /dev/urandom][\-t\ conf]
.SH DESCRIPTION
.B tpm2-abrmd
is a daemon that implements the TPM access broker and resource manager as
described by the Trusted Computing Group (TGC) in the
.B \*(lqTSS System Level API and TPM Command Transmission Interface
.B Specification\*(rq.
This daemon uses the DBus system bus and some pipes to communicate with
clients.
.SH OPTIONS
.TP
\fB\-t,\ \-\-tcti\fR
Provide the daemon with a string that describes the TCTI and how to configure
it for communication with the next component down the TSS2 stack. This string
is formatted as "tcti-name:tcti-conf" where:
.RS
.IP 'tcti-name'
The name of the TCTI library shared object file. Libraries are found using
the same algorithm as dlopen (3). If the TCTI library file name follows the
naming convention: \fBlibtss2-tcti-.so.0\fR where is the name for
the TCTI, the value of \fB\fR may be supplied in place of the full
library file name. See 'EXAMPLES' below.
.IP 'tcti-conf'
The configuration string passed to the TCTI library upon initialization.
.PP
If this option is omitted (or a NULL string provided) then a default TCTI is
used in it's default configuration. If the string does not contain a colon
then it will be interpreted as only the 'tcti-name'. To provide only the
configuration string (using the default TCTI) then the first character in the
string passed to this option must be a colon followed by the configuration
string. See examples below.
.RE
.TP
\fB\-o,\ \-\-allow-root\fR
Allow daemon to run as root. If this option is not provided the daemon will
refused to run as the root user. Use of this option is \fBnot\fR recommended.
.TP
\fB\-m,\ \-\-max-connections\fR
Set an upper bound on the number of concurrent client connections allowed.
Once this number of client connections is reached new connections will be
rejected with an error. If the option is not specified the default is \fB27\fR.
.TP
\fB\-f,\ \-\-flush-all\fR
Flush all objects and sessions when daemon is started.
.TP
\fB\-l,\ \-\-logger\fR
Direct logging output to named logging target. Supported targets are
\fBstdout\fR and \fBsyslog\fR. If the logger option is not specified the
default is \fBstdout\fR.
.TP
\fB\-e,\ \-\-max-sessions\fR
Set and upper bound on the number of sessions that each client connection
is allowed to create (loaded or active) at any one time. If the option is not
specified the default is \fB4\fR.
.TP
\fB\-r,\ \-\-max-transients\fR
Set an upper bound on the number of transient objects that each client
connection allowed to load. Once this number of objects is reached attempts
to load new transient objects will produce an error. If the option is not
specified the default is \fB27\fR.
.TP
\fB\-n,\ \-\-dbus-name\fR
Claim the given name on dbus. This option overrides the default of
com.intel.tss2.Tabrmd.
.TP
\fB\-g,\ \-\-prng-seed-file\fR
Read seed for pseudo-random number generator from the provided file.
.TP
\fB\-s,\ \-\-session\fR
Connect daemon to the session dbus. If the option is not specified the daemon
connects to the system dbus.
.TP
\fB\-v,\ \-\-version\fR
Display version string.
.SH EXAMPLES
.TP 3
Execute daemon with default TCTI and options:
.B tpm2-abrmd
.TP
Execute daemon with default TCTI and provided config string:
.B tpm2-abrmd --tcti=":/dev/tpm0"
.TP
This is equivalent to:
.B tpm2-abrmd --tcti="device:/dev/tpm0"
.br
.B tpm2-abrmd --tcti="libtss2-tcti-device.so.0:/dev/tpm0"
.TP
Have daemon use swtpm TPM2 Simulator tcti library
'libtss2-tcti-swtpm.so.0'.
This connects to a TPM2 simulator via a TCP swtpm.
.br
.B tpm2-abrmd --tcti="swtpm"
.br
.B tpm2-abrmd --tcti="libtss2-tcti-swtpm.so.0"
.TP
Have daemon use tcti library 'libtss2-tcti-swtpm.so.0' and config string
'host=127.0.0.1,port=5555':
.B tpm2-abrmd --tcti=swtpm:host=127.0.0.1,port=5555"
.br
.B tpm2-abrmd --tcti="libtss2-tcti-swtpm.so.0:host=127.0.0.1,port=5555"
.SH AUTHOR
Philip Tricca
.SH "SEE ALSO"
.BR tcsd (8)
tpm2-abrmd-3.0.0/man/tss2-tcti-tabrmd.7.in 0000664 0000000 0000000 00000001134 14343407245 0020037 0 ustar 00root root 0000000 0000000 .\" Process this file with
.\" groff -man -Tascii foo.1
.\"
.TH TCTI-TABMRD 7 "MAY 2017" Intel "TPM2 Software Stack"
.SH NAME
tcti-tabrmd \- tabrmd TCTI library
.SH SYNOPSIS
A TPM Command Transmission Interface (TCTI) module for interaction with the
tpm2-abrmd.
.SH DESCRIPTION
tcti-tabrmd is a library that abstracts the IPC magic necessary to
communicate with the tpm2-abrmd. Consult the \*(lqTSS System Level API and
TPM Command Transmission Interface Specification\*(rq
for a detailed discussion of the TCTI API.
.SH AUTHOR
Philip Tricca
.SH "SEE ALSO"
.BR tpm2-abrmd (8)
tpm2-abrmd-3.0.0/scripts/ 0000775 0000000 0000000 00000000000 14343407245 0015154 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/scripts/int-test-funcs.sh 0000664 0000000 0000000 00000011431 14343407245 0020373 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2017, Intel Corporation
# All rights reserved.
# This file contains a collection of support functions used by the automake
# test harness to execute our integration tests.
# This function takes a PID as a parameter and determines whether or not the
# process is currently running. If the daemon is running 0 is returned. Any
# other value indicates that the daemon isn't running.
daemon_status ()
{
local pid=$1
if [ $(kill -0 "${pid}" 2> /dev/null) ]; then
echo "failed to detect running daemon with PID: ${pid}";
return 1
fi
return 0
}
# This is a generic function to start a daemon, setup the environment
# variables, redirect output to a log file, store the PID of the daemon
# in a file and disconnect the daemon from the parent shell.
daemon_start ()
{
local daemon_bin="$1"
local daemon_opts="$2"
local daemon_log_file="$3"
local daemon_pid_file="$4"
local daemon_env="$5"
printf "starting daemon: %s\n environment: %s\n options: %s\n" "${daemon_bin}" "${daemon_env}" "${daemon_opts}"
env ${daemon_env} ${daemon_bin} ${daemon_opts} > ${daemon_log_file} 2>&1 &
local ret=$?
local pid=$!
if [ ${ret} -ne 0 ]; then
echo "failed to start daemon: \"${daemon_bin}\" with env: \"${daemon_env}\""
exit ${ret}
fi
sleep 1
daemon_status "${pid}"
if [ $? -ne 0 ]; then
echo "daemon died after successfully starting in background, check " \
"log file: ${daemon_log_file}"
return 1
fi
echo ${pid} > ${daemon_pid_file}
echo "successfully started daemon: ${daemon_bin} with PID: ${pid}"
return 0
}
# function to start the simulator
# This also that we have a private place to store the NVChip file. Since we
# can't tell the simulator what to name this file we must generate a random
# directory under /tmp, move to this directory, start the simulator, then
# return to the old pwd.
simulator_start ()
{
local sim_bin="$1"
local sim_port="$2"
local sim_log_file="$3"
local sim_pid_file="$4"
local sim_tmp_dir="$5"
# simulator port is a random port between 1024 and 65535
cd ${sim_tmp_dir}
case "$sim_bin" in
*swtpm) daemon_start "$sim_bin" "socket --tpm2 --server port=$sim_port \
--ctrl type=tcp,port=$((sim_port + 1)) \
--flags not-need-init --tpmstate dir=$PWD \
--seccomp action=none" \
"$sim_log_file" "$sim_pid_file";;
*tpm_server) daemon_start "$sim_bin" "-port $sim_port" \
"$sim_log_file" "$sim_pid_file";;
*) echo "Unknown TPM simulator $sim_bin"; return 1;;
esac
local ret=$?
cd -
return $ret
}
# function to start the tabrmd
# This is little more than a call to the daemon_start function with special
# command line options and an environment string.
tabrmd_start ()
{
local tabrmd_bin=$1
local tabrmd_log_file=$2
local tabrmd_pid_file=$3
local tabrmd_opts="$4"
local tabrmd_env="G_MESSAGES_DEBUG=all"
daemon_start "${tabrmd_bin}" "--flush-all ${tabrmd_opts}" "${tabrmd_log_file}" \
"${tabrmd_pid_file}" "${tabrmd_env}" "${LOG_FLAGS}"
}
# function to stop a running daemon
# This function takes a single parameter: a file containing the PID of the
# process to be killed. The PID is extracted and the daemon killed.
daemon_stop ()
{
local pid_file=$1
local pid=0
local ret=0
if [ ! -f ${pid_file} ]; then
echo "failed to stop daemon, no pid file: ${pid_file}"
return 1
fi
pid=$(cat ${pid_file})
daemon_status "${pid}"
if [ $? -ne 0 ]; then
echo "failed to detect running daemon with PID: ${pid}";
return ${ret}
fi
kill ${pid}
ret=$?
if [ ${ret} -eq 0 ]; then
wait ${pid}
ret=$?
else
echo "failed to kill daemon process with PID: ${pid}"
fi
return ${ret}
}
# function to start the dbus-daemon
# This dbus-daemon creates a session message bus used by the
# communication between tpm2-abrmd and testcase. The dbus info
# is told to testcase through DBUS_SESSION_BUS_ADDRESS and
# DBUS_SESSION_BUS_PID.
dbus_daemon_start ()
{
local dbus_log_file="$1"
local dbus_pid_file="$2"
local dbus_opts="--session --print-address 3 --nofork --nopidfile"
local dbus_addr_file=`mktemp`
local dbus_env="DBUS_VERBOSE=1"
exec 3<>$dbus_addr_file
daemon_start dbus-daemon "${dbus_opts}" "${dbus_log_file}" "${dbus_pid_file}" \
"${dbus_env}"
local ret=$?
if [ $ret -eq 0 ]; then
export DBUS_SESSION_BUS_ADDRESS=`cat "${dbus_addr_file}"`
export DBUS_SESSION_BUS_PID=`cat "${dbus_pid_file}"`
fi
rm -f $dbus_addr_file
return $ret
}
tpm2-abrmd-3.0.0/scripts/int-test-setup.sh 0000775 0000000 0000000 00000014540 14343407245 0020424 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2017, Intel Corporation
# All rights reserved.
set -u
set +o nounset
# default int-test-funcs script, overridden in TEST_FUNCTIONS env variable
TEST_FUNC_LIB=${TEST_FUNC_LIB:-scripts/int-test-funcs.sh}
if [ -e ${TEST_FUNC_LIB} ]; then
. ${TEST_FUNC_LIB}
else
echo "Error: Unable to locate support test function library: " \
"${TEST_FUNC_LIB}"
exit 1
fi
usage_error ()
{
echo "$0: $*" >&2
print_usage >&2
exit 2
}
print_usage ()
{
cat < /dev/null | grep "${PID}" | grep -q "${SIM_PORT_DATA}"
ret_data=$?
${sock_tool} 2> /dev/null | grep "${PID}" | grep -q "${SIM_PORT_CMD}"
ret_cmd=$?
if [ \( $ret_data -eq 0 \) -a \( $ret_cmd -eq 0 \) ]; then
echo "Simulator with PID ${PID} bound to port ${SIM_PORT_DATA} and " \
"${SIM_PORT_CMD} successfully.";
break
fi
echo "Port conflict? Cleaning up PID: ${PID}"
kill "${PID}"
BACKOFF=$((${BACKOFF}*${BACKOFF_FACTOR}))
echo "Failed to start simulator: port ${SIM_PORT_DATA} or " \
"${SIM_PORT_CMD} probably in use. Retrying in ${BACKOFF}."
sleep ${BACKOFF}
if [ $i -eq 10 ]; then
echo "Failed to start simulator after $i tries. Giving up.";
exit 1
fi
done
TABRMD_NAME="com.intel.tss2.Tabrmd${SIM_PORT_DATA}"
TABRMD_OPTS="${TABRMD_OPTS} --dbus-name=${TABRMD_NAME}"
TABRMD_OPTS="${TABRMD_OPTS} --tcti=${TABRMD_TCTI}:port=${SIM_PORT_DATA}"
if [ `whoami` == "root" ]; then
TABRMD_OPTS="--allow-root ${TABRMD_OPTS}"
fi
TABRMD_TEST_TCTI_CONF="${TABRMD_TEST_TCTI_CONF},bus_name=${TABRMD_NAME}"
;;
"device")
TABRMD_OPTS="--allow-root --tcti=device:/dev/tpm0"
SIM_PORT_DATA=$(od -A n -N 2 -t u2 /dev/urandom | \
awk -v min=${PORT_MIN} -v max=${PORT_MAX} \
'{print ($1 % (max - min)) + min}')
;;
*)
echo "whoops"
exit 1
;;
esac
# start tpm2-abrmd daemon
TABRMD_LOG_FILE=${TEST_BIN}_tabrmd.log
TABRMD_PID_FILE=${TEST_BIN}_tabrmd.pid
tabrmd_start ${TABRMD_BIN} ${TABRMD_LOG_FILE} ${TABRMD_PID_FILE} "${TABRMD_OPTS}"
if [ $? -ne 0 ]; then
echo "failed to start tabrmd with name ${TABRMD_NAME}"
fi
sleep 1
# List session bus names registered
dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames
# execute the test script and capture exit code
env G_MESSAGES_DEBUG=all TABRMD_TEST_TCTI_CONF="${TABRMD_TEST_TCTI_CONF}" TABRMD_TEST_TCTI_RETRIES=10 $@
ret_test=$?
# This sleep is sadly necessary: If we kill the tabrmd w/o sleeping for a
# second after the test finishes the simulator will die too. Bug in the
# simulator?
sleep 1
# teardown tabrmd
daemon_stop ${TABRMD_PID_FILE}
ret_tabrmd=$?
rm -rf ${TABRMD_PID_FILE}
# do configuration specific tear-down
case "${TABRMD_TCTI}"
in
# when testing against the simulator we must shut it down
"swtpm"|"mssim")
# ignore exit code (it's always 143 AFAIK)
daemon_stop ${SIM_PID_FILE}
rm -rf ${SIM_TMP_DIR} ${SIM_PID_FILE}
;;
esac
# handle exit codes
if [ $ret_test -ne 0 ]; then
echo "Execution of $@ failed: $ret_test"
exit $ret_test
fi
if [ $ret_tabrmd -ne 0 ]; then
echo "Execution of tabrmd failed: $ret_tabrmd"
exit $ret_tabrmd
fi
exit 0
tpm2-abrmd-3.0.0/scripts/unit-count.sh 0000664 0000000 0000000 00000000742 14343407245 0017620 0 ustar 00root root 0000000 0000000 #!/bin/sh
# This is a quick script that parses the log files from 'make check' to
# extract the number of unit tests in each test binary. It keeps a running
# total of the tests dumping this count to stdout before exiting.
ls -1 test/*.log |
{
VAL=0;
while read FILE; do
TMP=$(sed -e 's&^.*[[:space:]]\+\([0-9]\+\)[[:space:]]\+test(s) run\.$&\1&;tx;d;:x' ${FILE});
VAL=$(expr "${VAL}" + "${TMP}");
done;
echo "Total number of unit tests: ${VAL}"
}
tpm2-abrmd-3.0.0/selinux/ 0000775 0000000 0000000 00000000000 14343407245 0015154 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/selinux/tabrmd.fc 0000664 0000000 0000000 00000000226 14343407245 0016737 0 ustar 00root root 0000000 0000000 /usr/sbin/tpm2-abrmd -- gen_context(system_u:object_r:tabrmd_exec_t,s0)
/usr/local/sbin/tpm2-abrmd -- gen_context(system_u:object_r:tabrmd_exec_t,s0)
tpm2-abrmd-3.0.0/selinux/tabrmd.if 0000664 0000000 0000000 00000001570 14343407245 0016750 0 ustar 00root root 0000000 0000000 ##
########################################
##
## Create and use a unix stream socket
##
##
##
## Domain allowed access.
##
##
#
interface(`tabrmd_create_unix_stream_sockets',`
gen_require(`
type tabrmd_t;
')
allow $1 tabrmd_t:unix_stream_socket create_stream_socket_perms;
')
########################################
##
## Send messages to and from
## tabrmd over DBUS.
##
##
##
## Domain allowed access.
##
##
#
interface(`tabr,d_dbus_chat',`
gen_require(`
type tabrmd_t;
class dbus send_msg;
')
allow $1 tabrmd_t:dbus send_msg;
allow tabrmd_t $1:dbus send_msg;
')
tpm2-abrmd-3.0.0/selinux/tabrmd.te 0000664 0000000 0000000 00000001246 14343407245 0016762 0 ustar 00root root 0000000 0000000 policy_module(tabrmd, 0.0.2)
########################################
#
# Declarations
#
gen_tunable(`tabrmd_connect_all_unreserved', false)
type tabrmd_t;
type tabrmd_exec_t;
init_daemon_domain(tabrmd_t, tabrmd_exec_t)
allow tabrmd_t self:unix_dgram_socket { create_socket_perms };
dev_rw_tpm(tabrmd_t)
logging_send_syslog_msg(tabrmd_t)
sysnet_dns_name_resolve(tabrmd_t)
optional_policy(`
dbus_stub()
dbus_system_domain(tabrmd_t, tabrmd_exec_t)
allow system_dbusd_t tabrmd_t:unix_stream_socket rw_stream_socket_perms;
fwupd_dbus_chat(tabrmd_t)
')
tunable_policy(`tabrmd_connect_all_unreserved',`
corenet_tcp_connect_all_unreserved_ports(tabrmd_t)
')
tpm2-abrmd-3.0.0/src/ 0000775 0000000 0000000 00000000000 14343407245 0014254 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/src/command-attrs.c 0000664 0000000 0000000 00000004060 14343407245 0017171 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include "util.h"
#include "command-attrs.h"
G_DEFINE_TYPE (CommandAttrs, command_attrs, G_TYPE_OBJECT);
/*
* G_DEFINE_TYPE requires an instance init even though we don't use it.
*/
static void
command_attrs_init (CommandAttrs *attrs)
{
UNUSED_PARAM(attrs);
/* noop */
}
static void
command_attrs_finalize (GObject *obj)
{
CommandAttrs *attrs = COMMAND_ATTRS (obj);
g_debug (__func__);
g_clear_pointer (&attrs->command_attrs, g_free);
G_OBJECT_CLASS (command_attrs_parent_class)->finalize (obj);
}
static void
command_attrs_class_init (CommandAttrsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_debug ("command_attrs_class_init");
if (command_attrs_parent_class == NULL)
command_attrs_parent_class = g_type_class_peek_parent (klass);
object_class->finalize = command_attrs_finalize;
}
/*
* Create a new CommandAttrs object. Do no initialization in the
* constructor since initialization requires a round-trip to the TPM
* to query for TPMA_CCs.
*/
CommandAttrs*
command_attrs_new (void)
{
return COMMAND_ATTRS (g_object_new (TYPE_COMMAND_ATTRS, NULL));
}
/*
*/
gint
command_attrs_init_tpm (CommandAttrs *attrs,
Tpm2 *tpm2)
{
TSS2_RC rc;
rc = tpm2_get_command_attrs (tpm2,
&attrs->count,
&attrs->command_attrs);
if (rc != TSS2_RC_SUCCESS) {
return -1;
}
return 0;
}
/* TPM2_CC is in the lower 15 bits of the TPMA_CC */
#define TPM2_CC_FROM_TPMA_CC(attrs) (attrs & 0x7fff)
TPMA_CC
command_attrs_from_cc (CommandAttrs *attrs,
TPM2_CC command_code)
{
unsigned int i;
for (i = 0; i < attrs->count; ++i)
if (TPM2_CC_FROM_TPMA_CC (attrs->command_attrs[i]) == command_code)
return attrs->command_attrs[i];
return (TPMA_CC) { 0 };}
tpm2-abrmd-3.0.0/src/command-attrs.h 0000664 0000000 0000000 00000003116 14343407245 0017177 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef COMMAND_ATTRS_H
#define COMMAND_ATTRS_H
#include
#include
#include
#include
#include
G_BEGIN_DECLS
typedef struct _CommandAttrsClass {
GObjectClass parent;
} CommandAttrsClass;
typedef struct _CommandAttrs {
GObject parent_instance;
TPMA_CC *command_attrs;
UINT32 count;
} CommandAttrs;
#include "tpm2.h"
#define TYPE_COMMAND_ATTRS (command_attrs_get_type ())
#define COMMAND_ATTRS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_COMMAND_ATTRS, CommandAttrs))
#define COMMAND_ATTRS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_COMMAND_ATTRS, CommandAttrsClass))
#define IS_COMMAND_ATTRS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_COMMAND_ATTRS))
#define IS_COMMAND_ATTRS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_COMMAND_ATTRS))
#define COMMAND_ATTRS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_COMMAND_ATTRS, CommandAttrsClass))
GType command_attrs_get_type (void);
CommandAttrs* command_attrs_new (void);
gint command_attrs_init_tpm (CommandAttrs *attrs,
Tpm2 *tpm2);
TPMA_CC command_attrs_from_cc (CommandAttrs *attrs,
TPM2_CC command_code);
G_END_DECLS
#endif /* COMMAND_ATTRS_H */
tpm2-abrmd-3.0.0/src/command-source.c 0000664 0000000 0000000 00000034315 14343407245 0017342 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include
#include "connection.h"
#include "connection-manager.h"
#include "command-source.h"
#include "source-interface.h"
#include "tpm2-command.h"
#include "tpm2-header.h"
#include "util.h"
#ifndef G_SOURCE_FUNC
#define G_SOURCE_FUNC(x) ((GSourceFunc)(void*)x)
#endif
enum {
PROP_0,
PROP_COMMAND_ATTRS,
PROP_CONNECTION_MANAGER,
PROP_SINK,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/**
* Function implementing the Source interface. Adds a sink for the source
* to pass data to.
*/
void
command_source_add_sink (Source *self,
Sink *sink)
{
CommandSource *src = COMMAND_SOURCE (self);
GValue value = G_VALUE_INIT;
g_debug (__func__);
g_value_init (&value, G_TYPE_OBJECT);
g_value_set_object (&value, sink);
g_object_set_property (G_OBJECT (src), "sink", &value);
g_value_unset (&value);
}
/*
* This function initializes the SourceInterface. This is just registering
* a function pointer with the interface.
*/
static void
command_source_source_interface_init (gpointer g_iface)
{
SourceInterface *source = (SourceInterface*)g_iface;
source->add_sink = command_source_add_sink;
}
/*
* This is a callback function used to clean up memory used by the
* source_data_t structure. It's called by the GHashTable when removing
* source_data_t values.
*/
static void
source_data_free (gpointer data)
{
source_data_t *source_data = (source_data_t*)data;
g_object_unref (source_data->cancellable);
g_source_unref (source_data->source);
g_free (source_data);
}
/*
* Initialize a CommandSource instance.
*/
static void
command_source_init (CommandSource *source)
{
source->main_context = g_main_context_new ();
source->main_loop = g_main_loop_new (source->main_context, FALSE);
/*
* GHashTable mapping a GSocket to an instance of the source_data_t
* structure. The socket is the I/O mechanism for communicating with a
* client (from Connection object), and the source_data_t instance is
* a collection of data that we use to manage callbacks for I/O events
* from the main loop.
* The hash table owns a reference to the socket (key) and it owns
* the structure held in the value (it will be freed when removed).
*/
source->istream_to_source_data_map =
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
g_object_unref,
source_data_free);
}
G_DEFINE_TYPE_WITH_CODE (
CommandSource,
command_source,
TYPE_THREAD,
G_IMPLEMENT_INTERFACE (TYPE_SOURCE,
command_source_source_interface_init)
);
static void
command_source_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
CommandSource *self = COMMAND_SOURCE (object);
g_debug (__func__);
switch (property_id) {
case PROP_COMMAND_ATTRS:
self->command_attrs = COMMAND_ATTRS (g_value_dup_object (value));
break;
case PROP_CONNECTION_MANAGER:
self->connection_manager = CONNECTION_MANAGER (g_value_get_object (value));
break;
case PROP_SINK:
/* be rigid initially, add flexiblity later if we need it */
if (self->sink != NULL) {
g_warning (" sink already set");
break;
}
self->sink = SINK (g_value_get_object (value));
g_object_ref (self->sink);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
command_source_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
CommandSource *self = COMMAND_SOURCE (object);
g_debug (__func__);
switch (property_id) {
case PROP_COMMAND_ATTRS:
g_value_set_object (value, self->command_attrs);
break;
case PROP_CONNECTION_MANAGER:
g_value_set_object (value, self->connection_manager);
break;
case PROP_SINK:
g_value_set_object (value, self->sink);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/*
* This function is invoked by the GMainLoop thread when a client GSocket has
* data ready. This is what makes the CommandSource a source (of Tpm2Commands).
* Here we take the GSocket, extract the command body from the it and
* transform it to a Tpm2Command. Most of the details are handled by utility
* functions further down the stack.
*
* If an error occurs while getting the command from the GSocket the connection
* with the client will be closed and removed from the ConnectionManager.
* Additionally the function will return FALSE and the GSource will no longer
* monitor the GSocket for the G_IO_IN condition.
*/
gboolean
command_source_on_input_ready (GInputStream *istream,
gpointer user_data)
{
source_data_t *data = (source_data_t*)user_data;
Connection *connection;
Tpm2Command *command;
TPMA_CC attributes = { 0 };
uint8_t *buf;
size_t buf_size;
g_debug (__func__);
connection =
connection_manager_lookup_istream (data->self->connection_manager,
istream);
if (connection == NULL) {
g_error ("%s: failed to get connection associated with istream",
__func__);
}
buf = read_tpm_buffer_alloc (istream, &buf_size);
if (buf == NULL) {
goto fail_out;
}
attributes = command_attrs_from_cc (data->self->command_attrs,
get_command_code (buf));
command = tpm2_command_new (connection, buf, buf_size, attributes);
if (command != NULL) {
sink_enqueue (data->self->sink, G_OBJECT (command));
/* the sink now owns this message */
g_object_unref (command);
} else {
goto fail_out;
}
g_object_unref (connection);
return G_SOURCE_CONTINUE;
fail_out:
if (buf != NULL) {
g_free (buf);
}
g_debug ("%s: removing connection from connection_manager", __func__);
connection_manager_remove (data->self->connection_manager,
connection);
ControlMessage *msg =
control_message_new_with_object (CONNECTION_REMOVED,
G_OBJECT (connection));
sink_enqueue (data->self->sink, G_OBJECT (msg));
g_object_unref (msg);
g_object_unref (connection);
/*
* Remove data from hash table which includes the GCancellable associated
* with the G_IN_IO condition source. Don't call the cancellable though
* since we're letting this source die at the end of this function
* (returning FALSE).
*/
g_debug ("%s: removing GCancellable", __func__);
g_hash_table_remove (data->self->istream_to_source_data_map, istream);
return G_SOURCE_REMOVE;
}
/*
* This is a callback function invoked by the ConnectionManager when a new
* Connection object is added to it. It creates and sets up the GIO
* machinery needed to monitor the Connection for I/O events.
*/
gint
command_source_on_new_connection (ConnectionManager *connection_manager,
Connection *connection,
CommandSource *self)
{
GIOStream *iostream;
GPollableInputStream *istream;
source_data_t *data;
UNUSED_PARAM(connection_manager);
g_info ("%s: adding new connection", __func__);
/*
* Take reference to socket, will be freed when the source_data_t
* structure is freed
*/
iostream = connection_get_iostream (connection);
istream = G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (iostream));
g_object_ref (istream);
data = g_malloc0 (sizeof (source_data_t));
data->cancellable = g_cancellable_new ();
data->source = g_pollable_input_stream_create_source (istream,
data->cancellable);
/* we ignore the ID returned since we keep a reference to the source around */
g_source_attach (data->source, self->main_context);
data->self = self;
g_source_set_callback (data->source,
G_SOURCE_FUNC (command_source_on_input_ready),
data,
NULL);
/*
* To stop watching this socket for G_IO_IN condition use this GHashTable
* to look up the GCancellable object. The hash table takes ownership of
* the reference to the istream and the source_data_t pointer.
*/
g_hash_table_insert (self->istream_to_source_data_map, istream, data);
return 0;
}
/*
* callback for iterating over objects in the member socket_to_source_data_map
* GHashMap to kill off the GSource. This requires cancelling it, and then
* calling the GSource destroy function. This should only happen when the
* CommandSource object is destroyed.
*/
static void
command_source_source_cancel (gpointer key,
gpointer value,
gpointer user_data)
{
UNUSED_PARAM(key);
UNUSED_PARAM(user_data);
g_debug ("%s", __func__);
source_data_t *data = (source_data_t*)value;
g_debug ("%s: canceling cancellable and destroying source", __func__);
g_cancellable_cancel (data->cancellable);
}
/*
* GObject dispose function. It's used to unref / release all GObjects held
* by the CommandSource before chaining up to the parent.
*/
static void
command_source_dispose (GObject *object) {
CommandSource *self = COMMAND_SOURCE (object);
g_clear_object (&self->sink);
g_clear_object (&self->connection_manager);
g_clear_object (&self->command_attrs);
/* cancel all outstanding G_IO_IN condition GSources and destroy them */
if (self->istream_to_source_data_map != NULL) {
g_hash_table_foreach (self->istream_to_source_data_map,
command_source_source_cancel,
NULL);
}
g_clear_pointer (&self->istream_to_source_data_map, g_hash_table_unref);
if (self->main_loop != NULL && g_main_loop_is_running (self->main_loop)) {
g_main_loop_quit (self->main_loop);
}
g_clear_pointer (&self->main_loop, g_main_loop_unref);
g_clear_pointer (&self->main_context, g_main_context_unref);
G_OBJECT_CLASS (command_source_parent_class)->dispose (object);
}
/*
* GObject finalize function releases all resources not freed in 'dispose'
* & chain up to parent.
*/
static void
command_source_finalize (GObject *object)
{
G_OBJECT_CLASS (command_source_parent_class)->finalize (object);
}
/*
* Cause the GMainLoop to stop monitoring whatever GSources are attached to
* it and return
*/
static void
command_source_unblock (Thread *self)
{
CommandSource *source = COMMAND_SOURCE (self);
g_main_loop_quit (source->main_loop);
}
/*
* This function creates it's very own GMainLoop thread. This is used to
* monitor client connections for incoming data (TPM2 command buffers).
*/
void*
command_source_thread (void *data)
{
CommandSource *source;
g_assert (data != NULL);
source = COMMAND_SOURCE (data);
g_assert (source->main_loop != NULL);
if (!g_main_loop_is_running (source->main_loop)) {
g_main_loop_run (source->main_loop);
}
return NULL;
}
static void
command_source_class_init (CommandSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ThreadClass *thread_class = THREAD_CLASS (klass);
g_debug ("command_source_class_init");
if (command_source_parent_class == NULL)
command_source_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = command_source_dispose;
object_class->finalize = command_source_finalize;
object_class->get_property = command_source_get_property;
object_class->set_property = command_source_set_property;
thread_class->thread_run = command_source_thread;
thread_class->thread_unblock = command_source_unblock;
obj_properties [PROP_COMMAND_ATTRS] =
g_param_spec_object ("command-attrs",
"CommandAttrs object",
"CommandAttrs instance.",
TYPE_COMMAND_ATTRS,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_CONNECTION_MANAGER] =
g_param_spec_object ("connection-manager",
"ConnectionManager object",
"ConnectionManager instance.",
TYPE_CONNECTION_MANAGER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_SINK] =
g_param_spec_object ("sink",
"Sink",
"Reference to a Sink object.",
G_TYPE_OBJECT,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
CommandSource*
command_source_new (ConnectionManager *connection_manager,
CommandAttrs *command_attrs)
{
CommandSource *source;
if (connection_manager == NULL)
g_error ("command_source_new passed NULL ConnectionManager");
g_object_ref (connection_manager);
source = COMMAND_SOURCE (g_object_new (TYPE_COMMAND_SOURCE,
"command-attrs", command_attrs,
"connection-manager", connection_manager,
NULL));
g_signal_connect (connection_manager,
"new-connection",
(GCallback) command_source_on_new_connection,
source);
return source;
}
tpm2-abrmd-3.0.0/src/command-source.h 0000664 0000000 0000000 00000006630 14343407245 0017346 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef COMMAND_SOURCE_H
#define COMMAND_SOURCE_H
#include
#include
#include
#include "command-attrs.h"
#include "connection-manager.h"
#include "sink-interface.h"
#include "thread.h"
G_BEGIN_DECLS
/* Maximum buffer size for client data. Connections that send a single
* command larger than this size will be closed.
*/
#define BUF_MAX 4096
typedef struct _CommandSourceClass {
ThreadClass parent;
} CommandSourceClass;
typedef struct _CommandSource {
Thread parent_instance;
ConnectionManager *connection_manager;
CommandAttrs *command_attrs;
GMainContext *main_context;
GMainLoop *main_loop;
GHashTable *istream_to_source_data_map;
Sink *sink;
} CommandSource;
#define TYPE_COMMAND_SOURCE (command_source_get_type ())
#define COMMAND_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_COMMAND_SOURCE, CommandSource))
#define COMMAND_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_COMMAND_SOURCE, CommandSourceClass))
#define IS_COMMAND_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_COMMAND_SOURCE))
#define IS_COMMAND_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_COMMAND_SOURCE))
#define COMMAND_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_COMMAND_SOURCE, CommandSourceClass))
GType command_source_get_type (void);
CommandSource* command_source_new (ConnectionManager *connection_manager,
CommandAttrs *command_attrs);
gint command_source_on_new_connection (ConnectionManager *connection_manager,
Connection *connection,
CommandSource *command_source);
/*
* The following are private functions. They are exposed here for unit
* testing. Do not call these from anywhere else.
*/
gboolean command_source_on_input_ready (GInputStream *socket,
gpointer user_data);
/*
* Instances of this structure are used to track GSources and their
* GCancellable objects that have been registered with the
* GMainContext/Loop. We keep these structures in a GHashTable
* (socket_to_source_data_map) keyed on the client's GSocket.
* - When we're notified of a new connection we create a new GSource to
* receive a callback when the GSocket associated with the connection is in
* the G_IO_IN state.
* - When we receive a callback for a GSocket that has the G_IO_IN condition
* and the peer has closed their connection we use this data (passed to the
* callback when the GSource is created and the callback registered) to find
* and remove the same structure from the hash table (and free it). This way
* when the CommandSource is destroyed we won't have stale GSources hanging
* around.
* - When the CommandSource is destroyed all of the GSources registered with
* the GMainContext/Loop must be canceled and freed (see dispose function).
*/
typedef struct {
CommandSource *self;
GCancellable *cancellable;
GSource *source;
} source_data_t;
G_END_DECLS
#endif /* COMMAND_SOURCE_H */
tpm2-abrmd-3.0.0/src/connection-manager.c 0000664 0000000 0000000 00000025052 14343407245 0020173 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include
#include "connection-manager.h"
#include "util.h"
#define MAX_CONNECTIONS 100
#define MAX_CONNECTIONS_DEFAULT 27
G_DEFINE_TYPE (ConnectionManager, connection_manager, G_TYPE_OBJECT);
enum {
SIGNAL_0,
SIGNAL_NEW_CONNECTION,
N_SIGNALS,
};
enum {
PROP_0,
PROP_MAX_CONNECTIONS,
N_PROPERTIES,
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
static guint signals [N_SIGNALS] = { 0, };
/*
* GObject property setter.
*/
static void
connection_manager_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
ConnectionManager *mgr = CONNECTION_MANAGER (object);
g_debug ("%s", __func__);
switch (property_id) {
case PROP_MAX_CONNECTIONS:
mgr->max_connections = g_value_get_uint (value);
g_debug (" max_connections: %u", mgr->max_connections);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/*
* GObject property getter.
*/
static void
connection_manager_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
ConnectionManager *mgr = CONNECTION_MANAGER (object);
g_debug (__func__);
switch (property_id) {
case PROP_MAX_CONNECTIONS:
g_value_set_uint (value, mgr->max_connections);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
ConnectionManager*
connection_manager_new (guint max_connections)
{
return CONNECTION_MANAGER (g_object_new (TYPE_CONNECTION_MANAGER,
"max-connections", max_connections,
NULL));
}
/*
* Initialization function: instantiate all internal data / objects.
*/
static void
connection_manager_init (ConnectionManager *mgr)
{
if (pthread_mutex_init (&mgr->mutex, NULL) != 0)
g_error ("Failed to initialize connection _manager mutex: %s",
strerror (errno));
/* These two data structures must be kept in sync. When the
* connection-manager object is destroyed the Connection objects in these
* hash tables will be freed by the g_object_unref function. We only
* set this for one of the hash tables because we only want to free
* each Connection object once.
*/
mgr->connection_from_istream_table =
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
NULL);
mgr->connection_from_id_table =
g_hash_table_new_full (g_int64_hash,
g_int64_equal,
NULL,
(GDestroyNotify)g_object_unref);
}
static void
connection_manager_dispose (GObject *obj)
{
ConnectionManager *self = CONNECTION_MANAGER (obj);
gint ret;
ret = pthread_mutex_lock (&self->mutex);
if (ret != 0)
g_warning ("Error locking connection_manager mutex: %s",
strerror (errno));
g_hash_table_unref (self->connection_from_istream_table);
g_hash_table_unref (self->connection_from_id_table);
ret = pthread_mutex_unlock (&self->mutex);
if (ret != 0)
g_error ("Error unlocking connection_manager mutex: %s",
strerror (errno));
G_OBJECT_CLASS (connection_manager_parent_class)->dispose (obj);
}
static void
connection_manager_finalize (GObject *obj)
{
ConnectionManager *manager = CONNECTION_MANAGER (obj);
if (pthread_mutex_destroy (&manager->mutex) != 0)
g_error ("Error destroying connection_manager mutex: %s",
strerror (errno));
G_OBJECT_CLASS (connection_manager_parent_class)->finalize (obj);
}
/**
* Boilerplate GObject class init function. The only interesting thing that
* we do here is creating / registering the 'new_connection' signal. This
* signal invokes callbacks with the new_connection_callback type (see
* header). This signal is emitted by the connection_manager_insert function
* which is where we add new Connection objects to those tracked by the
* ConnectionManager.
*/
static void
connection_manager_class_init (ConnectionManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (connection_manager_parent_class == NULL)
connection_manager_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = connection_manager_dispose;
object_class->finalize = connection_manager_finalize;
object_class->get_property = connection_manager_get_property;
object_class->set_property = connection_manager_set_property;
signals [SIGNAL_NEW_CONNECTION] =
g_signal_new ("new-connection",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0,
NULL,
NULL,
NULL,
G_TYPE_INT,
1,
TYPE_CONNECTION);
obj_properties [PROP_MAX_CONNECTIONS] =
g_param_spec_uint ("max-connections",
"max connections",
"Maximum number of concurrent client connections",
0,
MAX_CONNECTIONS,
MAX_CONNECTIONS_DEFAULT,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
gint
connection_manager_insert (ConnectionManager *manager,
Connection *connection)
{
gint ret;
ret = pthread_mutex_lock (&manager->mutex);
if (ret != 0)
g_error ("Error locking connection_manager mutex: %s",
strerror (errno));
if (connection_manager_is_full (manager)) {
g_warning ("%s: max_connections of %u exceeded", __func__,
manager->max_connections);
pthread_mutex_unlock (&manager->mutex);
return -1;
}
/*
* Increase reference count on Connection object on insert. The
* corresponding call to g_hash_table_remove will cause the reference
* count to be decreased (see g_hash_table_new_full).
*/
g_object_ref (connection);
g_hash_table_insert (manager->connection_from_istream_table,
connection_key_istream (connection),
connection);
g_hash_table_insert (manager->connection_from_id_table,
connection_key_id (connection),
connection);
ret = pthread_mutex_unlock (&manager->mutex);
if (ret != 0)
g_error ("Error unlocking connection_manager mutex: %s",
strerror (errno));
/* not sure what to do about reference count on SEssionData obj */
g_signal_emit (manager,
signals [SIGNAL_NEW_CONNECTION],
0,
connection,
&ret);
return ret;
}
/*
* Lookup a Connection object from the provided connection fd. This function
* returns a reference to the Connection object. The reference count for
* this object is incremented before it is returned and must be decremented
* by the caller.
*/
Connection*
connection_manager_lookup_istream (ConnectionManager *manager,
GInputStream *istream)
{
Connection *connection;
pthread_mutex_lock (&manager->mutex);
connection = g_hash_table_lookup (manager->connection_from_istream_table,
istream);
if (connection != NULL) {
g_object_ref (connection);
} else {
g_warning ("%s returned NULL connection", __func__);
}
pthread_mutex_unlock (&manager->mutex);
return connection;
}
/*
* Lookup a Connection object from the provided Connection ID. This function
* returns a reference to the Connection object. The reference count for
* this object is incremented before it is returned and must be decremented
* by the caller.
*/
Connection*
connection_manager_lookup_id (ConnectionManager *manager,
gint64 id)
{
Connection *connection;
g_debug ("locking manager mutex");
pthread_mutex_lock (&manager->mutex);
g_debug ("g_hash_table_lookup: connection_from_id_table");
connection = g_hash_table_lookup (manager->connection_from_id_table,
&id);
if (connection != NULL) {
g_object_ref (connection);
} else {
g_warning ("connection_manager_lookup_id returned NULL connection");
}
g_debug ("unlocking manager mutex");
pthread_mutex_unlock (&manager->mutex);
return connection;
}
gboolean
connection_manager_contains_id (ConnectionManager *manager,
gint64 id)
{
return g_hash_table_contains (manager->connection_from_id_table, &id);
}
gboolean
connection_manager_remove (ConnectionManager *manager,
Connection *connection)
{
gboolean ret;
g_debug ("%s: removing Connection", __func__);
pthread_mutex_lock (&manager->mutex);
ret = g_hash_table_remove (manager->connection_from_istream_table,
connection_key_istream (connection));
if (ret != TRUE)
g_error ("%s: failed to remove Connection", __func__);
ret = g_hash_table_remove (manager->connection_from_id_table,
connection_key_id (connection));
if (ret != TRUE)
g_error ("%s: failed to remove Connection", __func__);
pthread_mutex_unlock (&manager->mutex);
return ret;
}
guint
connection_manager_size (ConnectionManager *manager)
{
return g_hash_table_size (manager->connection_from_istream_table);
}
gboolean
connection_manager_is_full (ConnectionManager *manager)
{
guint table_size;
table_size = g_hash_table_size (manager->connection_from_istream_table);
if (table_size < manager->max_connections) {
return FALSE;
} else {
return TRUE;
}
}
tpm2-abrmd-3.0.0/src/connection-manager.h 0000664 0000000 0000000 00000004641 14343407245 0020201 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef CONNECTION_MANAGER_H
#define CONNECTION_MANAGER_H
#include
#include
#include "connection.h"
G_BEGIN_DECLS
#define CONNECTION_MANAGER_MAX 100
typedef struct _ConnectionManagerClass {
GObjectClass parent;
} ConnectionManagerClass;
typedef struct _ConnectionManager {
GObject parent_instance;
pthread_mutex_t mutex;
GHashTable *connection_from_istream_table;
GHashTable *connection_from_id_table;
guint max_connections;
} ConnectionManager;
#define TYPE_CONNECTION_MANAGER (connection_manager_get_type ())
#define CONNECTION_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_CONNECTION_MANAGER, ConnectionManager))
#define CONNECTION_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_CONNECTION_MANAGER, ConnectionManagerClass))
#define IS_CONNECTION_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_CONNECTION_MANAGER))
#define IS_CONNECTION_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_CONNECTION_MANAGER))
#define CONNECTION_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_CONNECTION_MANAGER, ConnectionManagerClass))
GType connection_manager_get_type (void);
ConnectionManager* connection_manager_new (guint max_connections);
gint connection_manager_insert (ConnectionManager *manager,
Connection *connection);
gint connection_manager_remove (ConnectionManager *manager,
Connection *connection);
Connection* connection_manager_lookup_istream (ConnectionManager *manager,
GInputStream *istream);
Connection* connection_manager_lookup_id (ConnectionManager *manager,
gint64 id_in);
gboolean connection_manager_contains_id (ConnectionManager *manager,
gint64 id_in);
guint connection_manager_size (ConnectionManager *manager);
gboolean connection_manager_is_full (ConnectionManager *manager);
G_END_DECLS
#endif /* CONNECTION_MANAGER_H */
tpm2-abrmd-3.0.0/src/connection.c 0000664 0000000 0000000 00000013045 14343407245 0016562 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include
#include "connection.h"
#include "util.h"
G_DEFINE_TYPE (Connection, connection, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_ID,
PROP_IO_STREAM,
PROP_TRANSIENT_HANDLE_MAP,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
static void
connection_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
Connection *self = CONNECTION (object);
g_debug ("connection_set_property");
switch (property_id) {
case PROP_ID:
self->id = g_value_get_uint64 (value);
g_debug ("%s: set id to 0x%" PRIx64, __func__, self->id);
break;
case PROP_IO_STREAM:
self->iostream = G_IO_STREAM (g_value_dup_object (value));
g_debug ("%s: set socket", __func__);
break;
case PROP_TRANSIENT_HANDLE_MAP:
self->transient_handle_map = g_value_get_object (value);
g_object_ref (self->transient_handle_map);
g_debug ("%s: set transient_handle_map", __func__);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
connection_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
Connection *self = CONNECTION (object);
g_debug ("connection_get_property");
switch (property_id) {
case PROP_ID:
g_value_set_uint64 (value, self->id);
break;
case PROP_IO_STREAM:
g_value_set_object (value, self->iostream);
break;
case PROP_TRANSIENT_HANDLE_MAP:
g_value_set_object (value, self->transient_handle_map);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/*
* G_DEFINE_TYPE requires an instance init even though we don't use it.
*/
static void
connection_init (Connection *connection)
{
UNUSED_PARAM(connection);
/* noop */
}
static void
connection_dispose (GObject *obj)
{
Connection *connection = CONNECTION (obj);
g_clear_object (&connection->iostream);
g_object_unref (connection->transient_handle_map);
G_OBJECT_CLASS (connection_parent_class)->dispose (obj);
}
static void
connection_class_init (ConnectionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_debug ("connection_class_init");
if (connection_parent_class == NULL)
connection_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = connection_dispose;
object_class->get_property = connection_get_property;
object_class->set_property = connection_set_property;
obj_properties [PROP_ID] =
g_param_spec_uint64 ("id",
"connection identifier",
"Unique identifier for the connection",
0,
UINT64_MAX,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_IO_STREAM] =
g_param_spec_object ("iostream",
"GIOStream",
"Reference to GIOStream for exchanging data with client",
G_TYPE_IO_STREAM,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_TRANSIENT_HANDLE_MAP] =
g_param_spec_object ("transient-handle-map",
"HandleMap",
"HandleMap object to map handles to transient object contexts",
G_TYPE_OBJECT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
/* CreateConnection builds two pipes for communicating with client
* applications. It's provided with an array of two integers by the caller
* and it returns this array populated with the receiving and sending pipe fds
* respectively.
*/
Connection*
connection_new (GIOStream *iostream,
guint64 id,
HandleMap *transient_handle_map)
{
return CONNECTION (g_object_new (TYPE_CONNECTION,
"id", id,
"iostream", iostream,
"transient-handle-map", transient_handle_map,
NULL));
}
gpointer
connection_key_istream (Connection *connection)
{
return g_io_stream_get_input_stream (connection->iostream);
}
GIOStream*
connection_get_iostream (Connection *connection)
{
return connection->iostream;
}
gpointer
connection_key_id (Connection *connection)
{
return &connection->id;
}
/*
* Return a reference to the HandleMap for transient handles to the caller.
* We increment the reference count on this object before returning it. The
* caller *must* decrement the reference count when they're done using the
* object.
*/
HandleMap*
connection_get_trans_map (Connection *connection)
{
g_object_ref (connection->transient_handle_map);
return connection->transient_handle_map;
}
tpm2-abrmd-3.0.0/src/connection.h 0000664 0000000 0000000 00000003213 14343407245 0016563 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef CONNECTION_H
#define CONNECTION_H
#include
#include
#include
#include "handle-map.h"
G_BEGIN_DECLS
typedef struct _ConnectionClass {
GObjectClass parent;
} ConnectionClass;
typedef struct _Connection {
GObject parent_instance;
GIOStream *iostream;
guint64 id;
HandleMap *transient_handle_map;
} Connection;
#define TYPE_CONNECTION (connection_get_type ())
#define CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_CONNECTION, Connection))
#define CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_CONNECTION, ConnectionClass))
#define IS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_CONNECTION))
#define IS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_CONNECTION))
#define CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_CONNECTION, ConnectionClass))
GType connection_get_type (void);
Connection* connection_new (GIOStream *iostream,
guint64 id,
HandleMap *transient_handle_map);
gpointer connection_key_istream (Connection *session);
gpointer connection_key_id (Connection *session);
GIOStream* connection_get_iostream (Connection *connection);
HandleMap* connection_get_trans_map(Connection *session);
#endif /* CONNECTION_H */
tpm2-abrmd-3.0.0/src/control-message.c 0000664 0000000 0000000 00000003741 14343407245 0017527 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include "util.h"
#include "control-message.h"
G_DEFINE_TYPE (ControlMessage, control_message, G_TYPE_OBJECT);
/*
* G_DEFINE_TYPE requires an instance init even though we don't use it.
*/
static void
control_message_init (ControlMessage *obj)
{
UNUSED_PARAM(obj);
/* noop */
}
/*
* Dispose of object references and chain up to parent object.
*/
static void
control_message_dispose (GObject *obj)
{
ControlMessage *msg = CONTROL_MESSAGE (obj);
g_clear_object (&msg->object);
G_OBJECT_CLASS (control_message_parent_class)->dispose (obj);
}
/* Boiler-plate gobject code.
*/
static void
control_message_class_init (ControlMessageClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (control_message_parent_class == NULL)
control_message_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = control_message_dispose;
}
/*
* Create new ControlMessage with the provided ControlCode and object.
* Passing NULL in place of the object reference is the same as calling
* the 'control_message_new' function. The created ControlMessage will take
* a reference to the provided object.
*/
ControlMessage*
control_message_new_with_object (ControlCode code,
GObject *obj)
{
ControlMessage *msg;
if (obj != NULL) {
g_object_ref (obj);
}
msg = CONTROL_MESSAGE (g_object_new (TYPE_CONTROL_MESSAGE, NULL));
msg->code = code;
msg->object = obj;
return msg;
}
/**
* Boilerplate constructor.
*/
ControlMessage*
control_message_new (ControlCode code)
{
return control_message_new_with_object (code, NULL);
}
/*
* Simple getter to expose the ControlCode in the ControlMessage object.
*/
ControlCode
control_message_get_code (ControlMessage *msg)
{
return msg->code;
}
GObject*
control_message_get_object (ControlMessage *msg)
{
return msg->object;
}
tpm2-abrmd-3.0.0/src/control-message.h 0000664 0000000 0000000 00000003125 14343407245 0017530 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef CONTROL_MESSAGE_H
#define CONTROL_MESSAGE_H
#include
G_BEGIN_DECLS
/* Control codes.
*/
typedef enum {
CHECK_CANCEL = 1 << 0,
CONNECTION_REMOVED = 1 << 1,
} ControlCode;
typedef struct _ControlMessageClass {
GObjectClass parent;
} ControlMessageClass;
typedef struct _ControlMessage
{
GObject parent_instance;
ControlCode code;
GObject *object;
} ControlMessage;
GType control_message_get_type (void);
#define TYPE_CONTROL_MESSAGE (control_message_get_type ())
#define CONTROL_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_CONTROL_MESSAGE, ControlMessage))
#define CONTROL_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_CONTROL_MESSAGE, ControlMessageClass))
#define IS_CONTROL_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_CONTROL_MESSAGE))
#define IS_CONTROL_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_CONTROL_MESSAGE))
#define CONTROL_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_CONTROL_MESSAGE, ControlMessageClass))
ControlMessage* control_message_new (ControlCode code);
ControlMessage* control_message_new_with_object (ControlCode code,
GObject *obj);
ControlCode control_message_get_code (ControlMessage *msg);
GObject* control_message_get_object (ControlMessage* msg);
G_END_DECLS
#endif /* CONTROL_MESSAGE_H */
tpm2-abrmd-3.0.0/src/handle-map-entry.c 0000664 0000000 0000000 00000012351 14343407245 0017567 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include "util.h"
#include "handle-map-entry.h"
G_DEFINE_TYPE (HandleMapEntry, handle_map_entry, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_PHANDLE,
PROP_VHANDLE,
PROP_CONTEXT,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/*
* GObject property getter.
*/
static void
handle_map_entry_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
HandleMapEntry *self = HANDLE_MAP_ENTRY (object);
switch (property_id) {
case PROP_PHANDLE:
g_value_set_uint (value, (guint)self->phandle);
break;
case PROP_VHANDLE:
g_value_set_uint (value, (guint)self->vhandle);
break;
case PROP_CONTEXT:
g_value_set_pointer (value, &self->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
/*
* GObject property setter.
*/
static void
handle_map_entry_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
HandleMapEntry *self = HANDLE_MAP_ENTRY (object);
switch (property_id) {
case PROP_PHANDLE:
self->phandle = (TPM2_HANDLE)g_value_get_uint (value);
break;
case PROP_VHANDLE:
self->vhandle = (TPM2_HANDLE)g_value_get_uint (value);
break;
case PROP_CONTEXT:
g_error ("Cannot set context property.");
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
/*
* G_DEFINE_TYPE requires an instance init even though we don't use it.
*/
static void
handle_map_entry_init (HandleMapEntry *entry)
{
UNUSED_PARAM(entry);
/* noop */
}
/*
* Deallocate all associated resources. All are static so we just chain
* up to the parent like a good GObject.
*/
static void
handle_map_entry_finalize (GObject *object)
{
g_debug ("%s", __func__);
G_OBJECT_CLASS (handle_map_entry_parent_class)->finalize (object);
}
/*
* Class initialization function. Register function pointers and properties.
*/
static void
handle_map_entry_class_init (HandleMapEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (handle_map_entry_parent_class == NULL)
handle_map_entry_parent_class = g_type_class_peek_parent (klass);
object_class->finalize = handle_map_entry_finalize;
object_class->get_property = handle_map_entry_get_property;
object_class->set_property = handle_map_entry_set_property;
obj_properties [PROP_PHANDLE] =
g_param_spec_uint ("phandle",
"Physical handle",
"Handle from TPM.",
0,
UINT32_MAX,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_VHANDLE] =
g_param_spec_uint ("vhandle",
"Virtual handle",
"Handle exposed to client.",
0,
UINT32_MAX,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_CONTEXT] =
g_param_spec_pointer ("context",
"TPMS_CONTEXT",
"Context blob from TPM.",
G_PARAM_READABLE);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
/*
* Instance constructor.
*/
HandleMapEntry*
handle_map_entry_new (TPM2_HANDLE phandle,
TPM2_HANDLE vhandle)
{
HandleMapEntry *entry;
entry = HANDLE_MAP_ENTRY (g_object_new (TYPE_HANDLE_MAP_ENTRY,
"phandle", (guint)phandle,
"vhandle", (guint)vhandle,
NULL));
g_debug ("%s: with vhandle: 0x%" PRIx32 " and phandle: 0x%" PRIx32,
__func__, vhandle, phandle);
return entry;
}
/*
* Access the TPMS_CONTEXT member.
* NOTE: This directly exposes memory from an object instance. The caller
* must be sure to hold a reference to this object to keep it from being
* garbage collected while the caller is accessing the context structure.
* Further this object provides no thread safety ... yet.
*/
TPMS_CONTEXT*
handle_map_entry_get_context (HandleMapEntry *entry)
{
return &entry->context;
}
/*
* Accessor for the physical handle member.
*/
TPM2_HANDLE
handle_map_entry_get_phandle (HandleMapEntry *entry)
{
return entry->phandle;
}
/*
* Accessor for the virtual handle member.
*/
TPM2_HANDLE
handle_map_entry_get_vhandle (HandleMapEntry *entry)
{
return entry->vhandle;
}
void
handle_map_entry_set_phandle (HandleMapEntry *entry,
TPM2_HANDLE phandle)
{
entry->phandle = phandle;
}
tpm2-abrmd-3.0.0/src/handle-map-entry.h 0000664 0000000 0000000 00000003473 14343407245 0017601 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef HANDLE_MAP_ENTRY_H
#define HANDLE_MAP_ENTRY_H
#include
#include
#include
G_BEGIN_DECLS
typedef struct _HandleMapEntryClass {
GObjectClass parent;
} HandleMapEntryClass;
typedef struct _HandleMapEntry {
GObject parent_instance;
TPM2_HANDLE phandle;
TPM2_HANDLE vhandle;
TPMS_CONTEXT context;
} HandleMapEntry;
#define TYPE_HANDLE_MAP_ENTRY (handle_map_entry_get_type ())
#define HANDLE_MAP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_HANDLE_MAP_ENTRY, HandleMapEntry))
#define HANDLE_MAP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_HANDLE_MAP_ENTRY, HandleMapEntryClass))
#define IS_HANDLE_MAP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_HANDLE_MAP_ENTRY))
#define IS_HANDLE_MAP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_HANDLE_MAP_ENTRY))
#define HANDLE_MAP_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_HANDLE_MAP_ENTRY, HandleMapEntryClass))
GType handle_map_entry_get_type (void);
HandleMapEntry* handle_map_entry_new (TPM2_HANDLE phandle,
TPM2_HANDLE vhandle);
TPM2_HANDLE handle_map_entry_get_phandle (HandleMapEntry *entry);
TPM2_HANDLE handle_map_entry_get_vhandle (HandleMapEntry *entry);
TPMS_CONTEXT* handle_map_entry_get_context (HandleMapEntry *entry);
void handle_map_entry_set_phandle (HandleMapEntry *entry,
TPM2_HANDLE phandle);
G_END_DECLS
#endif /* HANDLE_MAP_ENTRY_H */
tpm2-abrmd-3.0.0/src/handle-map.c 0000664 0000000 0000000 00000024434 14343407245 0016435 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include "handle-map.h"
#include "util.h"
G_DEFINE_TYPE (HandleMap, handle_map, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_HANDLE_TYPE,
PROP_MAX_ENTRIES,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/*
* Property getter.
*/
static void
handle_map_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
HandleMap *map = HANDLE_MAP (object);
switch (property_id) {
case PROP_HANDLE_TYPE:
g_value_set_uint (value, map->handle_count);
break;
case PROP_MAX_ENTRIES:
g_value_set_uint (value, map->max_entries);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/*
* Property setter.
*/
static void
handle_map_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
HandleMap *map = HANDLE_MAP (object);
switch (property_id) {
case PROP_HANDLE_TYPE:
map->handle_type = (TPM2_HT)g_value_get_uint (value);
break;
case PROP_MAX_ENTRIES:
map->max_entries = g_value_get_uint (value);
g_debug ("%s: max-entries: %u", __func__, map->max_entries);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/*
* Initialize object. This requires:
* 1) initializing the mutex that mediate access to the hash tables
* 2) creating the hash tables
* 3) initializing the handle_count
* The handle_count is currently initialized to start allocating handles
* @ 0xff. This is an arbitrary way we differentiate them from the handles
* allocated by the TPM.
*/
static void
handle_map_init (HandleMap *map)
{
g_debug ("handle_map_init");
pthread_mutex_init (&map->mutex, NULL);
map->vhandle_to_entry_table =
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
(GDestroyNotify)g_object_unref);
map->handle_count = 0xff;
}
/*
* GObject dispose function: release all references to GObjects. Currently
* this is only the GHashTable mapping vhandles to HandleMapEntry objects.
*/
static void
handle_map_dispose (GObject *object)
{
HandleMap *self = HANDLE_MAP (object);
g_clear_pointer (&self->vhandle_to_entry_table, g_hash_table_unref);
G_OBJECT_CLASS (handle_map_parent_class)->dispose (object);
}
/*
* GObject finalize function: release all non-GObject resources. Currently
* this is the mutex used to lock the GHashTable.
*/
static void
handle_map_finalize (GObject *object)
{
HandleMap *self = HANDLE_MAP (object);
g_debug ("handle_map_finalize");
pthread_mutex_destroy (&self->mutex);
G_OBJECT_CLASS (handle_map_parent_class)->finalize (object);
}
/*
* boiler-plate GObject class init function. Registers function pointers
* and properties.
*/
static void
handle_map_class_init (HandleMapClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (handle_map_parent_class == NULL)
handle_map_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = handle_map_dispose;
object_class->finalize = handle_map_finalize;
object_class->get_property = handle_map_get_property;
object_class->set_property = handle_map_set_property;
obj_properties [PROP_HANDLE_TYPE] =
g_param_spec_uint ("handle-type",
"type of handle",
"type of handle tracked in this map",
0x0,
0xff,
0x80,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_MAX_ENTRIES] =
g_param_spec_uint ("max-entries",
"max number of entries",
"maximum number of entries permitted",
0,
MAX_ENTRIES_MAX,
MAX_ENTRIES_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
/*
* Instance initialization function. Create the resources the HandleMap needs
* and create the instance.
*/
HandleMap*
handle_map_new (TPM2_HT handle_type,
guint max_entries)
{
g_debug ("handle_map_new with handle_type 0x%" PRIx32
", max_entries: 0x%x", handle_type, max_entries);
return HANDLE_MAP (g_object_new (TYPE_HANDLE_MAP,
"handle-type", handle_type,
"max-entries", max_entries,
NULL));
}
/*
* Lock the mutex that protects the hash table object.
*/
static inline void
handle_map_lock (HandleMap *map)
{
if (pthread_mutex_lock (&map->mutex) != 0)
g_error ("Error locking HandleMap: %s", strerror (errno));
}
/*
* Unlock the mutex that protects the hash table object.
*/
static inline void
handle_map_unlock (HandleMap *map)
{
if (pthread_mutex_unlock (&map->mutex) != 0)
g_error ("Error unlocking HandleMap: %s", strerror (errno));
}
/*
* Return false if the number of entries in the map is greater than or equal
* to max_entries.
*/
gboolean
handle_map_is_full (HandleMap *map)
{
guint table_size;
table_size = g_hash_table_size (map->vhandle_to_entry_table);
if (table_size < map->max_entries + 1) {
return FALSE;
} else {
return TRUE;
}
}
/*
* Insert GObject into the hash table with the key being the provided handle.
* We take a reference to the object before we insert the object since when
* it is removed or if the hash table is destroyed the object will be unref'd.
* If a handle provided is 0 we do not insert the entry in the corresponding
* map.
* If there is an entry with the given key already in the table we don't insert
* anything, because it would overwrite the original entry.
*/
gboolean
handle_map_insert (HandleMap *map,
TPM2_HANDLE vhandle,
HandleMapEntry *entry)
{
g_debug ("%s: vhandle: 0x%" PRIx32, __func__, vhandle);
handle_map_lock (map);
if (handle_map_is_full (map)) {
g_warning ("%s: max_entries of %u exceeded", __func__, map->max_entries);
handle_map_unlock (map);
return FALSE;
}
if (entry && vhandle != 0) {
TPM2_HANDLE *orig_key;
HandleMapEntry *orig_value;
/* Check if an entry for the key is already in the table */
if (!g_hash_table_lookup_extended (map->vhandle_to_entry_table,
GINT_TO_POINTER (vhandle),
(gpointer *) &orig_key,
(gpointer *) &orig_value)) {
g_object_ref (entry);
g_hash_table_insert (map->vhandle_to_entry_table,
GINT_TO_POINTER (vhandle),
entry);
}
}
handle_map_unlock (map);
return TRUE;
}
/*
* Remove the entry from the hash table associated with the provided handle.
* Returns TRUE on success, FALSE on failure.
*/
gboolean
handle_map_remove (HandleMap *map,
TPM2_HANDLE vhandle)
{
gboolean ret;
handle_map_lock (map);
ret = g_hash_table_remove (map->vhandle_to_entry_table,
GINT_TO_POINTER (vhandle));
handle_map_unlock (map);
return ret;
}
/*
* Generic lookup function to find an entry in a GHashTable for the given
* handle.
*/
static HandleMapEntry*
handle_map_lookup (HandleMap *map,
TPM2_HANDLE handle,
GHashTable *table)
{
HandleMapEntry *entry;
handle_map_lock (map);
entry = g_hash_table_lookup (table, GINT_TO_POINTER (handle));
if (entry)
g_object_ref (entry);
handle_map_unlock (map);
return entry;
}
/*
* Look up the GObject associated with the virtual handle in the hash
* table. The object is not removed from the hash table. The reference
* count for the object is incremented before it is returned to the caller.
* The caller must free this reference when they are done with it.
* NULL is returned if no entry matches the provided handle.
*/
HandleMapEntry*
handle_map_vlookup (HandleMap *map,
TPM2_HANDLE vhandle)
{
return handle_map_lookup (map, vhandle, map->vhandle_to_entry_table);
}
/*
* Simple wrapper around the function that reports the number of entries in
* the hash table.
*/
guint
handle_map_size (HandleMap *map)
{
guint ret;
handle_map_lock (map);
ret = g_hash_table_size (map->vhandle_to_entry_table);
handle_map_unlock (map);
return ret;
}
/*
* Combine the handle_type and the handle_count to create a new handle.
* The handle_count is advanced as part of this. If the handle_count has
* a bit set in the upper byte (the handle_type bits) then it has rolled
* over and we return an error.
* NOTE: recycling handles is something we should be able to do for long
* lived programs / daemons that may want to use the TPM.
*/
TPM2_HANDLE
handle_map_next_vhandle (HandleMap *map)
{
TPM2_HANDLE handle;
/* (2 ^ 24) - 1 handles max, return 0 if we roll over*/
if (map->handle_count & TPM2_HR_RANGE_MASK)
return 0;
handle = map->handle_count + (TPM2_HANDLE)(map->handle_type << TPM2_HR_SHIFT);
++map->handle_count;
return handle;
}
void
handle_map_foreach (HandleMap *map,
GHFunc callback,
gpointer user_data)
{
g_hash_table_foreach (map->vhandle_to_entry_table,
callback,
user_data);
}
/*
* Get a GList containing all keys from the map. These will be returned in no
* particular order so don't assume that it's sorted in any way.
*/
GList*
handle_map_get_keys (HandleMap *map)
{
return g_hash_table_get_keys (map->vhandle_to_entry_table);
}
tpm2-abrmd-3.0.0/src/handle-map.h 0000664 0000000 0000000 00000004772 14343407245 0016445 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef HANDLE_MAP_H
#define HANDLE_MAP_H
#include
#include
#include
#include
#include "handle-map-entry.h"
G_BEGIN_DECLS
#define MAX_ENTRIES_DEFAULT 27
#define MAX_ENTRIES_MAX 100
typedef struct _HandleMapClass {
GObjectClass parent;
} HandleMapClass;
typedef struct _HandleMap {
GObject parent_instance;
pthread_mutex_t mutex;
TPM2_HT handle_type;
TPM2_HANDLE handle_count;
GHashTable *vhandle_to_entry_table;
guint max_entries;
} HandleMap;
#define TYPE_HANDLE_MAP (handle_map_get_type ())
#define HANDLE_MAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_HANDLE_MAP, HandleMap))
#define HANDLE_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_HANDLE_MAP, HandleMapClass))
#define IS_HANDLE_MAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_HANDLE_MAP))
#define IS_HANDLE_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_HANDLE_MAP))
#define HANDLE_MAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_HANDLE_MAP, HandleMapClass))
GType handle_map_get_type (void);
HandleMap* handle_map_new (TPM2_HT handle_type,
guint max_entries);
gboolean handle_map_insert (HandleMap *map,
TPM2_HANDLE vhandle,
HandleMapEntry *entry);
gint handle_map_remove (HandleMap *map,
TPM2_HANDLE vremove);
HandleMapEntry* handle_map_plookup (HandleMap *map,
TPM2_HANDLE phandle);
HandleMapEntry* handle_map_vlookup (HandleMap *map,
TPM2_HANDLE vhandle);
guint handle_map_size (HandleMap *map);
TPM2_HANDLE handle_map_next_vhandle (HandleMap *map);
void handle_map_foreach (HandleMap *map,
GHFunc callback,
gpointer user_data);
gboolean handle_map_is_full (HandleMap *map);
GList* handle_map_get_keys (HandleMap *map);
G_END_DECLS
#endif /* HANDLE_MAP_H */
tpm2-abrmd-3.0.0/src/include/ 0000775 0000000 0000000 00000000000 14343407245 0015677 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/src/include/tss2-tcti-tabrmd.h 0000664 0000000 0000000 00000000711 14343407245 0021152 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef TSS2_TCTI_TABD_H
#define TSS2_TCTI_TABD_H
#ifdef __cplusplus
extern "C" {
#endif
#include
TSS2_RC Tss2_Tcti_Tabrmd_Init (TSS2_TCTI_CONTEXT *context,
size_t *size,
const char *conf);
#ifdef __cplusplus
}
#endif
#endif /* TSS2_TCTI_TABD_H */
tpm2-abrmd-3.0.0/src/ipc-frontend-dbus.c 0000664 0000000 0000000 00000062312 14343407245 0017747 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#include
#include
#include "ipc-frontend-dbus.h"
#include "tabrmd-defaults.h"
#include "tabrmd.h"
#include "util.h"
G_DEFINE_TYPE (IpcFrontendDbus, ipc_frontend_dbus, TYPE_IPC_FRONTEND);
typedef enum {
TABRMD_ERROR_INTERNAL = TSS2_RESMGR_RC_INTERNAL_ERROR,
TABRMD_ERROR_MAX_CONNECTIONS = TSS2_RESMGR_RC_GENERAL_FAILURE,
TABRMD_ERROR_ID_GENERATION = TSS2_RESMGR_RC_GENERAL_FAILURE,
TABRMD_ERROR_NOT_IMPLEMENTED = TSS2_RESMGR_RC_NOT_IMPLEMENTED,
TABRMD_ERROR_NOT_PERMITTED = TSS2_RESMGR_RC_NOT_PERMITTED,
} TabrmdErrorEnum;
enum {
PROP_0,
PROP_BUS_NAME,
PROP_BUS_TYPE,
PROP_CONNECTION_MANAGER,
PROP_MAX_TRANS,
PROP_RANDOM,
N_PROPERTIES
};
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL };
static void
ipc_frontend_dbus_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
IpcFrontendDbus *self = IPC_FRONTEND_DBUS (object);
switch (property_id) {
case PROP_BUS_NAME:
self->bus_name = g_value_dup_string (value);
g_debug ("IpcFrontendDbus set bus_name: %s", self->bus_name);
break;
case PROP_BUS_TYPE:
self->bus_type = g_value_get_int (value);
break;
case PROP_CONNECTION_MANAGER:
self->connection_manager = g_value_get_object (value);
g_object_ref (self->connection_manager);
break;
case PROP_MAX_TRANS:
self->max_transient_objects = g_value_get_uint (value);
break;
case PROP_RANDOM:
self->random = g_value_get_object (value);
g_object_ref (self->random);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
ipc_frontend_dbus_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
IpcFrontendDbus *self = IPC_FRONTEND_DBUS (object);
switch (property_id) {
case PROP_BUS_NAME:
g_value_set_string (value, self->bus_name);
break;
case PROP_BUS_TYPE:
g_value_set_int (value, self->bus_type);
break;
case PROP_CONNECTION_MANAGER:
g_value_set_object (value, self->connection_manager);
break;
case PROP_MAX_TRANS:
g_value_set_uint (value, self->max_transient_objects);
break;
case PROP_RANDOM:
g_value_set_object (value, self->random);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
ipc_frontend_dbus_init (IpcFrontendDbus *self)
{
self->dbus_name_acquired = FALSE;
}
/*
* Dispose method where where we free up references to other objects.
*/
static void
ipc_frontend_dbus_dispose (GObject *obj)
{
IpcFrontendDbus *self = IPC_FRONTEND_DBUS (obj);
g_clear_object (&self->connection_manager);
g_clear_object (&self->random);
g_clear_object (&self->skeleton);
G_OBJECT_CLASS (ipc_frontend_dbus_parent_class)->dispose (obj);
}
/*
* Finalize method where we free resources.
*/
static void
ipc_frontend_dbus_finalize (GObject *obj)
{
IpcFrontendDbus *self = IPC_FRONTEND_DBUS (obj);
g_clear_pointer (&self->bus_name, g_free);
G_OBJECT_CLASS (ipc_frontend_dbus_parent_class)->finalize (obj);
}
static void
ipc_frontend_dbus_class_init (IpcFrontendDbusClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
IpcFrontendClass *ipc_frontend_class = IPC_FRONTEND_CLASS (klass);
if (ipc_frontend_dbus_parent_class == NULL)
ipc_frontend_dbus_parent_class = g_type_class_peek_parent (klass);
/* GObject functions */
object_class->dispose = ipc_frontend_dbus_dispose;
object_class->finalize = ipc_frontend_dbus_finalize;
object_class->get_property = ipc_frontend_dbus_get_property;
object_class->set_property = ipc_frontend_dbus_set_property;
/* IpcFrontend functions */
ipc_frontend_class->connect = (IpcFrontendConnect)ipc_frontend_dbus_connect;
ipc_frontend_class->disconnect = (IpcFrontendDisconnect)ipc_frontend_dbus_disconnect;
obj_properties [PROP_BUS_NAME] =
g_param_spec_string ("bus-name",
"Bus name",
"GIO Bus name",
IPC_FRONTEND_DBUS_NAME_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_BUS_TYPE] =
g_param_spec_int ("bus-type",
"Bus type",
"GIO Bus type",
G_BUS_TYPE_STARTER,
G_BUS_TYPE_SESSION,
IPC_FRONTEND_DBUS_TYPE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_CONNECTION_MANAGER] =
g_param_spec_object ("connection-manager",
"ConnectionManager object",
"ConnectionManager object for connection",
TYPE_CONNECTION_MANAGER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_MAX_TRANS] =
g_param_spec_uint ("max-trans",
"maximum transient objects",
"maximum number of transient objects for the handle map",
1,
TABRMD_TRANSIENT_MAX,
TABRMD_TRANSIENT_MAX_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_RANDOM] =
g_param_spec_object ("random",
"Random object",
"Source of random numbers.",
TYPE_RANDOM,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
IpcFrontendDbus*
ipc_frontend_dbus_new (GBusType bus_type,
gchar const *bus_name,
ConnectionManager *connection_manager,
guint max_trans,
Random *random)
{
GObject *object = NULL;
object = g_object_new (TYPE_IPC_FRONTEND_DBUS,
"bus-name", bus_name,
"bus-type", bus_type,
"connection-manager", connection_manager,
"max-trans", max_trans,
"random", random,
NULL);
return IPC_FRONTEND_DBUS (object);
}
/* TabrmdSkeleton signal handlers */
/*
* Give this function a dbus proxy and invocation object from a method
* invocation and it will get the PID of the process associated with the
* invocation. If an error occurs this function returns false.
*/
static gboolean
get_pid_from_dbus_invocation (GDBusProxy *proxy,
GDBusMethodInvocation *invocation,
guint32 *pid)
{
const gchar *name = NULL;
GError *error = NULL;
GVariant *result = NULL;
if (proxy == NULL || invocation == NULL || pid == NULL)
return FALSE;
name = g_dbus_method_invocation_get_sender (invocation);
result = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
"GetConnectionUnixProcessID",
g_variant_new("(s)", name),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (error) {
g_warning ("Unable to get PID for %s: %s", name, error->message);
g_error_free (error);
return FALSE;
} else {
g_variant_get (result, "(u)", pid);
g_variant_unref (result);
return TRUE;
}
}
/*
* Generate a random uint64 returned in the id out parameter.
* Mix this random ID with the PID from the caller. This is obtained
* through the invocation parameter. Mix the two together using xor and
* return the result through the id_pid_mix out parameter.
* NOTE: if an error occurs then a response is sent through the invocation
* to the client and FALSE is returned to the caller.
*
* Returns FALSE on error, TRUE otherwise.
*/
static gboolean
generate_id_pid_mix_from_invocation (IpcFrontendDbus *self,
GDBusMethodInvocation *invocation,
guint64 *id,
guint64 *id_pid_mix)
{
gboolean pid_ret = FALSE;
guint32 pid = 0;
pid_ret = get_pid_from_dbus_invocation (self->dbus_daemon_proxy,
invocation,
&pid);
if (pid_ret == TRUE) {
*id = random_get_uint64 (self->random);
*id_pid_mix = *id ^ pid;
} else {
g_dbus_method_invocation_return_error (invocation,
TABRMD_ERROR,
TABRMD_ERROR_INTERNAL,
"Failed to get client PID");
}
return pid_ret;
}
/*
* Mix PID into provide id. Returns mixed value in id_pid_mix out parameter.
* NOTE: if an error occurs then an error response is sent through the
* invocation to the client and FALSE is returned to the caller
*
* Returns FALSE on error, TRUE otherwise.
*/
static gboolean
get_id_pid_mix_from_invocation (GDBusProxy *proxy,
GDBusMethodInvocation *invocation,
guint64 id,
guint64 *id_pid_mix)
{
guint32 pid = 0;
gboolean pid_ret = FALSE;
g_debug ("get_id_pid_mix_from_invocation");
pid_ret = get_pid_from_dbus_invocation (proxy,
invocation,
&pid);
g_debug ("id 0x%" PRIx64 " pid: 0x%" PRIx32, id, pid);
if (pid_ret == TRUE) {
*id_pid_mix = id ^ pid;
g_debug ("mixed: 0x%" PRIx64, *id_pid_mix);
} else {
g_dbus_method_invocation_return_error (
invocation,
TABRMD_ERROR,
TABRMD_ERROR_INTERNAL,
"Failed to get client PID");
}
return pid_ret;
}
/*
* This is a signal handler for the handle-create-connection signal from
* the DBus interface. This signal is triggered by a request from a client
* to create a new connection with the daemon. This requires a few things
* be done:
* - Create a new ID (uint64) for the connection.
* - Create a new Connection object.
* - Build up a dbus response to the client with their connection ID and
* FD for the client side of the connection.
* - Send the response message back to the client.
* - Insert the new Connection object into the ConnectionManager.
*/
static gboolean
on_handle_create_connection (TctiTabrmd *skeleton,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
IpcFrontendDbus *self = NULL;
HandleMap *handle_map = NULL;
Connection *connection = NULL;
gint client_fd = 0, ret = 0;
GIOStream *iostream;
GVariant *response, *response_tuple;
GUnixFDList *fd_list = NULL;
guint64 id = 0, id_pid_mix = 0;
gboolean id_ret = FALSE;
UNUSED_PARAM(skeleton);
self = IPC_FRONTEND_DBUS (user_data);
ipc_frontend_init_guard (IPC_FRONTEND (user_data));
if (connection_manager_is_full (self->connection_manager)) {
g_dbus_method_invocation_return_error (invocation,
TABRMD_ERROR,
TABRMD_ERROR_MAX_CONNECTIONS,
"MAX_COMMANDS exceeded. Try again later.");
return TRUE;
}
id_ret = generate_id_pid_mix_from_invocation (self,
invocation,
&id,
&id_pid_mix);
/* error already returned to caller over dbus */
if (id_ret == FALSE) {
return TRUE;
}
g_debug ("Creating connection with id: 0x%" PRIx64, id_pid_mix);
if (connection_manager_contains_id (self->connection_manager,
id_pid_mix)) {
g_warning ("ID collision in ConnectionManager: %" PRIu64, id_pid_mix);
g_dbus_method_invocation_return_error (
invocation,
TABRMD_ERROR,
TABRMD_ERROR_ID_GENERATION,
"Failed to allocate connection ID. Try again later.");
return TRUE;
}
handle_map = handle_map_new (TPM2_HT_TRANSIENT, self->max_transient_objects);
if (handle_map == NULL)
g_error ("Failed to allocate new HandleMap");
iostream = create_connection_iostream (&client_fd);
connection = connection_new (iostream, id_pid_mix, handle_map);
g_object_unref (handle_map);
g_object_unref (iostream);
if (connection == NULL)
g_error ("Failed to allocate new connection.");
g_debug ("Created connection with client FD: %d and id: 0x%" PRIx64,
client_fd, id_pid_mix);
/* prepare tuple variant for response message */
fd_list = g_unix_fd_list_new_from_array (&client_fd, 1);
response = g_variant_new_uint64 (id);
response_tuple = g_variant_new_tuple (&response, 1);
/*
* Issue the callback to notify subscribers that a new connection has
* been created.
*/
ret = connection_manager_insert (self->connection_manager, connection);
if (ret != 0) {
g_warning ("Failed to add new connection to connection_manager.");
}
/* send response */
g_dbus_method_invocation_return_value_with_unix_fd_list (
invocation,
response_tuple,
fd_list);
g_object_unref (fd_list);
g_object_unref (connection);
return TRUE;
}
/*
* This is a signal handler for the Cancel event emitted by the
* Tpm2 AccessBroker. It is invoked by a signal generated by a user
* requesting that an outstanding TPM command should be canceled. It is
* registered with the Tabrmd in response to acquiring a name
* on the dbus (on_name_acquired). It does X things:
* - Locate the Connection object associated with the 'id' parameter in
* the ConnectionManager.
* - If the connection has a command being processed by the tabrmd then it's
* removed from the processing queue.
* - If the connection has a command being processed by the TPM then the
* request to cancel the command will be sent down to the TPM.
* - If the connection has no commands outstanding then an error is
* returned.
*/
static gboolean
on_handle_cancel (TctiTabrmd *skeleton,
GDBusMethodInvocation *invocation,
gint64 id,
gpointer user_data)
{
IpcFrontendDbus *self = IPC_FRONTEND_DBUS (user_data);
Connection *connection = NULL;
guint64 id_pid_mix = 0;
gboolean mix_ret = FALSE;
UNUSED_PARAM(skeleton);
g_info ("on_handle_cancel for id 0x%" PRIx64, id);
ipc_frontend_init_guard (IPC_FRONTEND (self));
mix_ret = get_id_pid_mix_from_invocation (self->dbus_daemon_proxy,
invocation,
id,
&id_pid_mix);
/* error already sent over dbus */
if (mix_ret == FALSE) {
return TRUE;
}
connection = connection_manager_lookup_id (self->connection_manager,
id_pid_mix);
if (connection == NULL) {
g_warning ("no active connection for id_pid_mix: 0x%" PRIx64,
id_pid_mix);
g_dbus_method_invocation_return_error (invocation,
TABRMD_ERROR,
TABRMD_ERROR_NOT_PERMITTED,
"No connection.");
return TRUE;
}
g_info ("%s: canceling command for connection with id_pid_mix: 0x%" PRIx64,
__func__, id_pid_mix);
/* cancel any existing commands for the connection */
g_dbus_method_invocation_return_error (invocation,
TABRMD_ERROR,
TABRMD_ERROR_NOT_IMPLEMENTED,
"Cancel function not implemented.");
g_object_unref (connection);
return TRUE;
}
/*
* This is a signal handler for the handle-set-locality signal from the
* Tabrmd DBus interface. This signal is triggered by a request
* from a client to set the locality for TPM commands associated with the
* connection (the 'id' parameter). This requires a few things be done:
* - Find the Connection object associated with the 'id' parameter.
* - Set the locality for the Connection object.
* - Pass result of the operation back to the user.
*/
static gboolean
on_handle_set_locality (TctiTabrmd *skeleton,
GDBusMethodInvocation *invocation,
gint64 id,
guint8 locality,
gpointer user_data)
{
IpcFrontendDbus *self = IPC_FRONTEND_DBUS (user_data);
Connection *connection = NULL;
guint64 id_pid_mix = 0;
gboolean mix_ret = FALSE;
UNUSED_PARAM(skeleton);
g_info ("on_handle_set_locality for id 0x%" PRIx64, id);
ipc_frontend_init_guard (IPC_FRONTEND (self));
mix_ret = get_id_pid_mix_from_invocation (self->dbus_daemon_proxy,
invocation,
id,
&id_pid_mix);
/* error already sent over dbus */
if (mix_ret == FALSE) {
return TRUE;
}
connection = connection_manager_lookup_id (self->connection_manager,
id_pid_mix);
if (connection == NULL) {
g_warning ("%s: no active connection for id", __func__);
g_dbus_method_invocation_return_error (invocation,
TABRMD_ERROR,
TABRMD_ERROR_NOT_PERMITTED,
"No connection.");
return TRUE;
}
/* set locality for an existing connection */
g_dbus_method_invocation_return_error (invocation,
TABRMD_ERROR,
TABRMD_ERROR_NOT_IMPLEMENTED,
"setLocality function not implemented.");
g_object_unref (connection);
return TRUE;
}
/* D-Bus signal handlers */
/*
* This is a signal handler of type GBusAcquiredCallback. It is registered
* by the g_bus_own_name function and invoked then a connection to a bus
* is acquired in response to a request for the parameter 'name'.
*/
static void
on_bus_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
UNUSED_PARAM(connection);
UNUSED_PARAM(user_data);
g_info ("on_bus_acquired: %s", name);
}
/*
* This is a signal handler of type GBusNameAcquiredCallback. It is
* registered by the g_bus_own_name function and invoked when the parameter
* 'name' is acquired on the requested bus. It does 3 things:
* - Obtains a new TctiTabrmd instance and stores a reference in
* the 'user_data' parameter (which is a reference to the gmain_data_t.
* - Register signal handlers for the CreateConnection, Cancel and
* SetLocality signals.
* - Export the TctiTabrmd interface (skeleton) on the DBus
* connection.
*/
static void
on_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_info ("on_name_acquired: %s", name);
IpcFrontendDbus *self = NULL;
GError *error = NULL;
gboolean ret;
if (user_data == NULL)
g_error ("bus_acquired but user_data is NULL");
self = IPC_FRONTEND_DBUS (user_data);
if (self->skeleton == NULL)
self->skeleton = tcti_tabrmd_skeleton_new ();
g_signal_connect (self->skeleton,
"handle-create-connection",
G_CALLBACK (on_handle_create_connection),
user_data);
g_signal_connect (self->skeleton,
"handle-cancel",
G_CALLBACK (on_handle_cancel),
user_data);
g_signal_connect (self->skeleton,
"handle-set-locality",
G_CALLBACK (on_handle_set_locality),
user_data);
ret = g_dbus_interface_skeleton_export (
G_DBUS_INTERFACE_SKELETON (self->skeleton),
connection,
TABRMD_DBUS_PATH,
&error);
if (ret == FALSE)
g_warning ("failed to export interface: %s", error->message);
self->dbus_name_acquired = TRUE;
}
/*
* This is a signal handler of type GBusNameLostCallback. It is
* registered with the g_dbus_own_name function and is invoked when the
* parameter 'name' is lost on the requested bus. This signal is propagated
* to any subscribers through the IpcFrontend 'disconnected' signal.
*/
static void
on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_debug ("%s: %s", __func__, name);
IpcFrontend *ipc_frontend = IPC_FRONTEND (user_data);
IpcFrontendDbus *self = IPC_FRONTEND_DBUS (user_data);
UNUSED_PARAM(connection);
if (self->dbus_name_acquired == FALSE) {
g_critical ("Failed to acquire DBus name %s. UID %d must be "
"allowed to \"own\" this name. Check DBus config "
"and check that this is running as user tss or root.",
name, getuid ());
} else {
self->dbus_name_acquired = FALSE;
}
ipc_frontend_disconnected_invoke (ipc_frontend);
}
/*
* Callback handling the acquisition of a GDBusProxy object for communication
* with the well known org.freedesktop.DBus object. This is an object exposed
* by the dbus daemon.
*/
static void
on_get_dbus_daemon_proxy (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GError *error = NULL;
IpcFrontendDbus *self = IPC_FRONTEND_DBUS (user_data);
UNUSED_PARAM(source_object);
self->dbus_daemon_proxy = g_dbus_proxy_new_finish (result, &error);
if (error) {
g_warning ("Failed to get proxy for DBus daemon "
"(org.freedesktop.DBus): %s", error->message);
g_error_free (error);
self->dbus_daemon_proxy = NULL;
}
g_debug ("Got proxy object for DBus daemon.");
self->dbus_name_owner_id = g_bus_own_name (self->bus_type,
self->bus_name,
G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired,
on_name_acquired,
on_name_lost,
self,
NULL);
}
/*
* This function overrides the ipc_frontend_connect function from the
* IpcFrontend base class. It causes the IpcFrontendDbus object to connect
* to the D-Bus instance provided in the constructor while claiming the name
* provided in the same.
* This function registers several callbacks with the GDbus machinery. A
* reference to the IpcFrontendDbus parameter (self) is passed as data to
* these callbacks.
*/
void
ipc_frontend_dbus_connect (IpcFrontendDbus *self,
GMutex *init_mutex)
{
IpcFrontend *frontend = IPC_FRONTEND (self);
g_return_if_fail (IS_IPC_FRONTEND_DBUS (self));
frontend->init_mutex = init_mutex;
g_dbus_proxy_new_for_bus (self->bus_type,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
NULL,
(GAsyncReadyCallback)on_get_dbus_daemon_proxy,
self);
}
/*
* This function overrides the ipc_frontend_disconnect function from the
* IpcFrontend base class. When successfully disconnected this object will
* emit the 'disconnected' signal.
*/
void
ipc_frontend_dbus_disconnect (IpcFrontendDbus *self)
{
g_bus_unown_name (self->dbus_name_owner_id);
IPC_FRONTEND (self)->init_mutex = NULL;
}
tpm2-abrmd-3.0.0/src/ipc-frontend-dbus.h 0000664 0000000 0000000 00000004625 14343407245 0017757 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef IPC_FRONTEND_DBUS_H
#define IPC_FRONTEND_DBUS_H
#include
#include
#include "connection-manager.h"
#include "ipc-frontend.h"
#include "random.h"
#include "tabrmd-generated.h"
G_BEGIN_DECLS
#define IPC_FRONTEND_DBUS_NAME_DEFAULT "com.intel.tss2.Tabrmd"
#define IPC_FRONTEND_DBUS_TYPE_DEFAULT G_BUS_TYPE_SYSTEM
typedef struct _IpcFrontendDbusClass {
IpcFrontendClass parent;
} IpcFrontendDbusClass;
typedef struct _IpcFrontendDbus
{
IpcFrontend parent_instance;
/* data set by GObject properties */
gchar *bus_name;
GBusType bus_type;
/* private data */
gboolean dbus_name_acquired;
guint dbus_name_owner_id;
guint max_transient_objects;
ConnectionManager *connection_manager;
GDBusProxy *dbus_daemon_proxy;
Random *random;
TctiTabrmd *skeleton;
} IpcFrontendDbus;
#define TYPE_IPC_FRONTEND_DBUS (ipc_frontend_dbus_get_type ())
#define IPC_FRONTEND_DBUS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_IPC_FRONTEND_DBUS, IpcFrontendDbus))
#define IPC_FRONTEND_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_IPC_FRONTEND_DBUS, IpcFrontendDbusClass))
#define IS_IPC_FRONTEND_DBUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_IPC_FRONTEND_DBUS))
#define IS_IPC_FRONTEND_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_IPC_FRONTEND_DBUS))
#define IPC_FRONTEND_DBUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_IPC_FRONTEND_DBUS, IpcFrontendDbusClass))
GType ipc_frontend_dbus_get_type (void);
IpcFrontendDbus* ipc_frontend_dbus_new (GBusType bus_type,
gchar const *bus_name,
ConnectionManager *connection_manager,
guint max_trans,
Random *random);
void ipc_frontend_dbus_connect (IpcFrontendDbus *self,
GMutex *init_mutex);
void ipc_frontend_dbus_disconnect (IpcFrontendDbus *self);
G_END_DECLS
#endif /* IPC_FRONTEND_DBUS_H */
tpm2-abrmd-3.0.0/src/ipc-frontend.c 0000664 0000000 0000000 00000005454 14343407245 0017020 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include "util.h"
#include "ipc-frontend.h"
G_DEFINE_ABSTRACT_TYPE (IpcFrontend, ipc_frontend, G_TYPE_OBJECT);
enum {
SIGNAL_0,
SIGNAL_DISCONNECTED,
N_SIGNALS,
};
static guint signals [N_SIGNALS] = { 0 };
static void
ipc_frontend_init (IpcFrontend *ipc_frontend)
{
UNUSED_PARAM(ipc_frontend);
/* noop, required by G_DEFINE_ABSTRACT_TYPE */
}
static void
ipc_frontend_class_init (IpcFrontendClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (ipc_frontend_parent_class == NULL)
ipc_frontend_parent_class = g_type_class_peek_parent (klass);
klass->connect = ipc_frontend_connect;
klass->disconnect = ipc_frontend_disconnect;
signals [SIGNAL_DISCONNECTED] =
g_signal_new ("disconnected",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0,
NULL);
}
/*
* The init_mutex is not meant to be held for any length of time. It's only
* used as a way for an external entity to prevent the IpcFrontend from accepting
* new connections until the command processing machinery is setup.
*/
void
ipc_frontend_init_guard (IpcFrontend *self)
{
if (self->init_mutex) {
g_mutex_lock (self->init_mutex);
g_mutex_unlock (self->init_mutex);
}
}
/*
* This function is boiler plate. It is used to invoke the function pointed
* to by the 'connect' member data / function pointer in the class object.
* Derived classes should set this pointer in their class initializer. This
* function will use this function pointer to invoke the right function in
* derived classes when they're being referenced through this abstract type.
*/
void
ipc_frontend_connect (IpcFrontend *self,
GMutex *init_mutex)
{
g_debug ("%s", __func__);
IPC_FRONTEND_GET_CLASS (self)->connect (self, init_mutex);
}
/*
* This function is used to invoke the 'disconnect' function from a child
* class. It explicitly does *not* cause the 'disconnected' event to be
* emitted. The classes inheriting from this class are in a better position
* to know when they're actually disconnected and thus when the signal should
* be emitted.
*/
void
ipc_frontend_disconnect (IpcFrontend *self)
{
g_debug ("%s", __func__);
IPC_FRONTEND_GET_CLASS (self)->disconnect (self);
}
void
ipc_frontend_disconnected_invoke (IpcFrontend *ipc_frontend)
{
g_signal_emit (ipc_frontend,
signals [SIGNAL_DISCONNECTED],
0);
}
tpm2-abrmd-3.0.0/src/ipc-frontend.h 0000664 0000000 0000000 00000003452 14343407245 0017021 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef IPC_FRONTEND_H
#define IPC_FRONTEND_H
#include
#include "connection.h"
G_BEGIN_DECLS
typedef struct _IpcFrontend IpcFrontend;
typedef struct _IpcFrontendClass IpcFrontendClass;
typedef void (*IpcFrontendConnect) (IpcFrontend *self,
GMutex *mutex);
typedef void (*IpcFrontendDisconnect) (IpcFrontend *self);
struct _IpcFrontend {
GObject parent;
GMutex *init_mutex;
};
struct _IpcFrontendClass {
GObjectClass parent;
IpcFrontendConnect connect;
IpcFrontendDisconnect disconnect;
};
#define TYPE_IPC_FRONTEND (ipc_frontend_get_type ())
#define IPC_FRONTEND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_IPC_FRONTEND, IpcFrontend))
#define IS_IPC_FRONTEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_IPC_FRONTEND))
#define IPC_FRONTEND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_IPC_FRONTEND, IpcFrontendClass))
#define IS_IPC_FRONTEND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_IPC_FRONTEND))
#define IPC_FRONTEND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_IPC_FRONTEND, IpcFrontendClass))
GType ipc_frontend_get_type (void);
void ipc_frontend_connect (IpcFrontend *self,
GMutex *mutex);
void ipc_frontend_disconnect (IpcFrontend *self);
void ipc_frontend_disconnected_invoke (IpcFrontend *self);
void ipc_frontend_init_guard (IpcFrontend *self);
G_END_DECLS
#endif /* IPC_FRONTEND_H */
tpm2-abrmd-3.0.0/src/logging.c 0000664 0000000 0000000 00000005114 14343407245 0016047 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include "util.h"
#include "logging.h"
/**
* This function that implements the GLogFunc prototype. It is intended
* for use as a log handler function for glib logging.
*/
void
syslog_log_handler (const char *log_domain,
GLogLevelFlags log_level,
const char *message,
gpointer log_config_list)
{
UNUSED_PARAM(log_domain);
UNUSED_PARAM(log_config_list);
switch (log_level) {
case G_LOG_FLAG_FATAL:
syslog (LOG_ALERT, "%s", message);
break;
case G_LOG_LEVEL_ERROR:
syslog (LOG_ERR, "%s", message);
break;
case G_LOG_LEVEL_CRITICAL:
syslog (LOG_CRIT, "%s", message);
break;
case G_LOG_LEVEL_WARNING:
syslog (LOG_WARNING, "%s", message);
break;
case G_LOG_LEVEL_MESSAGE:
syslog (LOG_NOTICE, "%s", message);
break;
case G_LOG_LEVEL_INFO:
syslog (LOG_INFO, "%s", message);
break;
case G_LOG_LEVEL_DEBUG:
syslog (LOG_DEBUG, "%s", message);
break;
default:
syslog (LOG_INFO, "%s", message);
}
}
/*
* The G_MESSAGES_DEBUG environment variable is a space separated list of
* glib logging domains that we want to see debug and info messages from.
* The right way to do this is to declare a logging domain for the application
* but for now we simply look for the special value of "all" and enable info
* and debug messages if it's set.
*/
int
get_enabled_log_levels (void)
{
gchar *g_log_domains = NULL;
g_log_domains = getenv ("G_MESSAGES_DEBUG");
if (g_log_domains == NULL) {
return LOG_LEVEL_DEFAULT;
}
if (g_strcmp0 (g_log_domains, "all") == 0) {
return LOG_LEVEL_ALL;
} else {
return LOG_LEVEL_DEFAULT;
}
}
/**
* Convenience function to set logger for GLog.
*/
gint
set_logger (gchar *name)
{
int enabled_log_levels = 0;
if (g_strcmp0 (name, "syslog") == 0) {
enabled_log_levels = get_enabled_log_levels ();
g_log_set_handler (NULL,
enabled_log_levels | G_LOG_FLAG_FATAL | \
G_LOG_FLAG_RECURSION,
syslog_log_handler,
NULL);
return 0;
} else if (g_strcmp0 (name, "stdout") == 0) {
/* stdout is the default for g_log, nothing to do but return 0 */
g_info ("logging to stdout");
return 0;
}
return -1;
}
tpm2-abrmd-3.0.0/src/logging.h 0000664 0000000 0000000 00000001633 14343407245 0016056 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef LOGGING_H
#define LOGGING_H
#include
/* Macro to log "critical" events, then exit indicating failure. */
#define tabrmd_critical(fmt, ...) \
do { \
g_critical (fmt, ##__VA_ARGS__); \
exit (EXIT_FAILURE); \
} while (0)
#define LOG_LEVEL_DEFAULT (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | \
G_LOG_LEVEL_WARNING)
#define LOG_LEVEL_ALL (LOG_LEVEL_DEFAULT | G_LOG_LEVEL_MESSAGE | \
G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG)
void
syslog_log_handler (const char *log_domain,
GLogLevelFlags log_level,
const char *message,
gpointer log_config_list);
int get_enabled_log_levels (void);
gint set_logger (gchar *name);
#endif /* LOGGING_H */
tpm2-abrmd-3.0.0/src/message-queue.c 0000664 0000000 0000000 00000004316 14343407245 0017172 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include "message-queue.h"
#include "util.h"
G_DEFINE_TYPE (MessageQueue, message_queue, G_TYPE_OBJECT);
/**
* The init function is a noop but it's required by the G_DEFINE_TYPE
* macro.
*/
static void
message_queue_init (MessageQueue *self)
{
self->queue = g_async_queue_new_full (g_object_unref);
}
/*
* To finalize the MessageQueue we need only to unref the internal
* GAsyncQueue object.
*/
static void
message_queue_dispose (GObject *obj)
{
MessageQueue *message_queue = MESSAGE_QUEUE (obj);
g_clear_pointer (&message_queue->queue, g_async_queue_unref);
G_OBJECT_CLASS (message_queue_parent_class)->dispose (obj);
}
/**
* Boilerplate GObject class init with custom dispose function.
*/
static void
message_queue_class_init (MessageQueueClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = message_queue_dispose;
}
/**
* Allocate a new message_queue_t object.
* The caller owns the returned object reference and must unref it.
* When the MessageQueue object is destroyed each object in its internal
* queue will be unref'd as well.
*/
MessageQueue*
message_queue_new ()
{
return MESSAGE_QUEUE (g_object_new (TYPE_MESSAGE_QUEUE, NULL));
}
/**
* Enqueue a blob in the blob_queue_t.
* This function is a thin wrapper around the GQueue. When we enqueue blobs
* we push them to the head of the queue.
*/
void
message_queue_enqueue (MessageQueue *message_queue,
GObject *object)
{
g_assert (message_queue != NULL);
g_debug ("%s", __func__);
g_object_ref (object);
g_async_queue_push (message_queue->queue, object);
}
/**
* Dequeue a blob from the blob_queue_t.
* This function is a thin wrapper around the GQueue. When we dequeue blobs
* we pop them from the tail of the queue.
*/
GObject*
message_queue_dequeue (MessageQueue *message_queue)
{
GObject *obj;
g_assert (message_queue != NULL);
g_debug ("%s", __func__);
obj = g_async_queue_pop (message_queue->queue);
return obj;
}
tpm2-abrmd-3.0.0/src/message-queue.h 0000664 0000000 0000000 00000002540 14343407245 0017174 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef MESSAGE_QUEUE_H
#define MESSAGE_QUEUE_H
#include
#include
G_BEGIN_DECLS
typedef struct _MessageQueueClass {
GObjectClass parent;
} MessageQueueClass;
typedef struct _MessageQueue {
GObject parent_instance;
GAsyncQueue *queue;
} MessageQueue;
#define TYPE_MESSAGE_QUEUE (message_queue_get_type ())
#define MESSAGE_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MESSAGE_QUEUE, MessageQueue))
#define MESSAGE_QUEUE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_MESSAGE_QUEUE, MessageQueueClass))
#define IS_MESSAGE_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MESSAGE_QUEUE))
#define IS_MESSAGE_QUEUE_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_MESSAGE_QUEUE))
#define MESSAGE_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_MESSAGE_QUEUE, MessageQueueClass))
GType message_queue_get_type (void);
MessageQueue* message_queue_new (void);
void message_queue_enqueue (MessageQueue *message_queue,
GObject *obj);
GObject* message_queue_dequeue (MessageQueue *message_queue);
G_END_DECLS
#endif /* MESSAGE_QUEUE_H */
tpm2-abrmd-3.0.0/src/random.c 0000664 0000000 0000000 00000011506 14343407245 0015703 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "util.h"
#include "random.h"
G_DEFINE_TYPE (Random, random, G_TYPE_OBJECT);
/*
* G_DEFINE_TYPE requires an instance init even though we don't need it.
*/
static void
random_init (Random *obj)
{
UNUSED_PARAM(obj);
/* noop */
}
/*
* Chain up to parent class finalize.
*/
static void
random_finalize (GObject *obj)
{
g_debug ("random_finalize");
G_OBJECT_CLASS (random_parent_class)->finalize (obj);
}
static void
random_class_init (RandomClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_debug ("random_class_init");
if (random_parent_class == NULL)
random_parent_class = g_type_class_peek_parent (klass);
object_class->finalize = random_finalize;
}
/*
* Allocate a new Random object. We do no initialization of the seed for
* the RNG.
*/
Random*
random_new (void)
{
return RANDOM (g_object_new (TYPE_RANDOM, NULL));
}
/*
* Seed the underlying RNG from the provided file. The number of bytes
* read should be sizeof (long int). If we can't get this
* much entropy we return -1. Otherwise 0 on success.
*/
int
random_seed_from_file (Random *random,
const char *fname)
{
gint rand_fd, ret = 0;
long int rand_seed = 0;
ssize_t read_ret;
if (random == NULL) {
g_error ("NULL random pointer passed to random_seed_from_file");
return -1;
}
g_debug ("opening entropy source: %s", fname);
g_assert_nonnull (random);
rand_fd = open (fname, O_RDONLY);
if (rand_fd == -1) {
g_warning ("failed to open entropy source %s: %s",
fname,
strerror (errno));
ret = -1;
goto out;
}
g_debug ("reading from entropy source: %s", fname);
read_ret = read (rand_fd, &rand_seed, sizeof (rand_seed));
if (read_ret == -1) {
g_warning ("failed to read from entropy source %s, %s",
fname,
strerror (errno));
ret = -1;
goto close_out;
} else if (read_ret < (ssize_t) sizeof (rand_seed)) {
g_warning ("short read on entropy source %s: got %zu bytes, expecting %zu",
fname, read_ret, sizeof (rand_seed));
ret = -1;
goto close_out;
}
random->rand_state[0] = 0x330E;
random->rand_state[1] = rand_seed & 0xffff;
random->rand_state[2] = (rand_seed >> 16) & 0xffff;
close_out:
if (close (rand_fd) != 0)
g_warning ("failed to close entropy source %s",
strerror (errno));
out:
return ret;
}
/*
* Get 'count' bytes of random data out of the provided Random object.
* On success the number of bytes obtained is returned. On error 0 is
* returned.
* NOTE: This algorithm is pretty inefficient. For each byte we return
* to the caller we get a long int of data from rand48. The rest of
* the sizeof (long int) bytes are wasted.
*/
size_t
random_get_bytes (Random *random,
uint8_t dest[],
size_t count)
{
size_t i;
uint8_t rand[sizeof (long int)] = { 0, };
g_assert_nonnull (random);
assert (random->rand_state);
for (i = 0; i < count; ++i) {
*(&rand[0]) = nrand48 (random->rand_state);
memcpy (&dest[i], &rand[0], sizeof (uint8_t));
}
return i;
}
/*
* Get 64 bits of random data from the parameter 'random' object.
* On error, return 0 instead of random data.
*/
uint64_t
random_get_uint64 (Random *random)
{
size_t ret;
uint64_t dest;
if (random == NULL) {
g_error ("NULL random pointer passed to random_get_uint64");
return -1;
}
ret = random_get_bytes (random, (uint8_t*)&dest, sizeof (uint64_t));
g_assert_true (ret == sizeof (uint64_t));
return dest;
}
/*
* Get 32 bits of random data from the parameter 'random' object.
* On error, return 0 instead of random data.
*/
uint32_t
random_get_uint32 (Random *random)
{
size_t ret;
uint32_t dest;
if (random == NULL) {
g_error ("NULL random pointer passed to random_get_uint32");
return -1;
}
ret = random_get_bytes (random, (uint8_t*)&dest, sizeof (uint32_t));
g_assert_true (ret == sizeof (uint32_t));
return dest;
}
/*
* Get random 32bit number in the given range.
*/
uint32_t
random_get_uint32_range (Random *random,
uint32_t high,
uint32_t low)
{
size_t ret;
uint32_t dest;
ret = random_get_bytes (random, (uint8_t*)&dest, sizeof (dest));
if (ret != sizeof (uint32_t))
return -1;
return low + (dest / (UINT32_MAX / (high - low)));
}
tpm2-abrmd-3.0.0/src/random.h 0000664 0000000 0000000 00000003327 14343407245 0015712 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef RANDOM_H
#define RANDOM_H
#include
#include
#include
#include
G_BEGIN_DECLS
typedef struct _RandomClass {
GObjectClass parent;
} RandomClass;
typedef struct _Random {
GObject parent_instance;
unsigned short rand_state[3];
} Random;
#define TYPE_RANDOM (random_get_type ())
#define RANDOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_RANDOM, Random))
#define RANDOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_RANDOM, RandomClass))
#define IS_RANDOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_RANDOM))
#define IS_RANDOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_RANDOM))
#define RANDOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_RANDOM, RandomClass))
GType random_get_type (void);
Random* random_new (void);
int random_seed_from_file (Random *random,
const char *fname);
size_t random_get_bytes (Random *random,
uint8_t dest[],
size_t count);
uint32_t random_get_uint32 (Random *random);
uint32_t random_get_uint32_range (Random *random,
uint32_t high,
uint32_t low);
uint64_t random_get_uint64 (Random *random);
G_END_DECLS
#endif /* RANDOM_H */
tpm2-abrmd-3.0.0/src/resource-manager-session.c 0000664 0000000 0000000 00000013734 14343407245 0021350 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
#include
#include
#include "tpm2.h"
#include "resource-manager.h"
#include "resource-manager-session.h"
#include "session-list.h"
#include "session-entry.h"
#include "tabrmd.h"
#include "tpm2-command.h"
#include "tpm2-header.h"
#include "tpm2-response.h"
#include "util.h"
/*
* Load the TPMS_CONTEXT associated with the provided SessionEntry*.
* This function will always return a Tpm2Response object (never NULL)
* though the response object may indicate a failure in the response code.
*/
Tpm2Response*
load_session (ResourceManager *resmgr,
SessionEntry *entry)
{
Tpm2Command *cmd = NULL;
Tpm2Response *resp = NULL;
size_buf_t *size_buf = NULL;
TSS2_RC rc = TSS2_RC_SUCCESS;
size_buf = session_entry_get_context (entry);
cmd = tpm2_command_new_context_load (size_buf->buf, size_buf->size);
if (cmd == NULL) {
g_critical ("%s: failed to allcoate ContextLoad Tpm2Command",
__func__);
resp = tpm2_response_new_rc (NULL, TSS2_RESMGR_RC_GENERAL_FAILURE);
goto out;
}
resp = tpm2_send_command (resmgr->tpm2, cmd, &rc);
if (rc != TSS2_RC_SUCCESS) {
g_critical ("%s: TCTI failed while loading session context from "
"SessionEntry, got RC 0x%" PRIx32, __func__, rc);
resp = tpm2_response_new_rc (NULL, rc);
goto out;
}
rc = tpm2_response_get_code (resp);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: failed to ContextLoad SessionEntry, got RC 0x%"
PRIx32, __func__, rc);
goto out;
}
session_entry_set_state (entry, SESSION_ENTRY_LOADED);
out:
g_clear_object (&cmd);
return resp;
}
/*
* This function flushes the context associated with the provided SessionEntry
* from the TPM. Once a session is flushed it cannot be re-loaded and so we
* remove the SessionEntry from the session_list as well.
*/
gboolean
flush_session (ResourceManager *resmgr,
SessionEntry *entry)
{
g_assert_nonnull (resmgr);
g_assert_nonnull (entry);
TPM2_HANDLE handle = session_entry_get_handle (entry);
TSS2_RC rc;
g_debug ("%s: flushing stale SessionEntry with handle: 0x%08" PRIx32,
__func__, handle);
rc = tpm2_context_flush (resmgr->tpm2, handle);
session_list_remove (resmgr->session_list, entry);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: failed to flush session context with "
"handle 0x%" PRIx32 ": 0x%" PRIx32,
__func__, handle, rc);
return FALSE;
}
return TRUE;
}
/*
* Saves context of the provided SessionEntry. If successful the
* SessionEntry state will be udpated and the context blob updated. The
* function will always return a Tpm2Response. If there was any error
* encounterd it will be reflected in the Tpm2Response object RC.
*/
Tpm2Response*
save_session (ResourceManager *resmgr,
SessionEntry *entry)
{
Tpm2Command *cmd = NULL;
Tpm2Response *resp = NULL;
TSS2_RC rc = TSS2_RC_SUCCESS;
g_assert_nonnull (resmgr);
g_assert_nonnull (entry);
if (session_entry_get_state (entry) != SESSION_ENTRY_LOADED) {
g_critical ("%s: SessionEntry already loaded", __func__);
resp = tpm2_response_new_rc (NULL, TSS2_RESMGR_RC_GENERAL_FAILURE);
goto out;
}
cmd = tpm2_command_new_context_save (session_entry_get_handle (entry));
if (cmd == NULL) {
g_critical ("%s: failed to allocate ContextSave Tpm2Command",
__func__);
resp = tpm2_response_new_rc (NULL, TSS2_RESMGR_RC_GENERAL_FAILURE);
goto out;
}
resp = tpm2_send_command (resmgr->tpm2, cmd, &rc);
if (rc != TSS2_RC_SUCCESS) {
g_critical ("%s: TCTI failed while saving session context from "
"SessionEntry, got RC 0x%" PRIx32, __func__, rc);
resp = tpm2_response_new_rc (NULL, rc);
goto out;
}
rc = tpm2_response_get_code (resp);
if (rc != TSS2_RC_SUCCESS) {
g_info ("%s: failed to ContextSave SessionEntry, got RC 0x%" PRIx32,
__func__, rc);
goto out;
}
session_entry_set_context (entry,
&tpm2_response_get_buffer (resp)[TPM_HEADER_SIZE],
tpm2_response_get_size (resp) - TPM_HEADER_SIZE);
session_entry_set_state (entry, SESSION_ENTRY_SAVED_RM);
out:
g_clear_object (&cmd);
return resp;
}
/*
* This function will send the required commands in order to bring a
* session back into the gap interval. This means loading and saving
* the associated context. If the SessionEntry is not saved then this
* function is a no-op.
*/
gboolean
regap_session (ResourceManager *resmgr,
SessionEntry *entry)
{
SessionEntryStateEnum state;
Tpm2Response *resp = NULL;
gboolean ret = TRUE;
g_assert_nonnull (resmgr);
g_assert_nonnull (entry);
state = session_entry_get_state (entry);
g_debug ("%s: swapping SessionEntry in state \"%s\"", __func__,
session_entry_state_to_str (state));
if (state == SESSION_ENTRY_SAVED_CLIENT ||
state == SESSION_ENTRY_SAVED_CLIENT_CLOSED ||
state == SESSION_ENTRY_SAVED_RM)
{
resp = load_session (resmgr, entry);
if (tpm2_response_get_code (resp) != TSS2_RC_SUCCESS) {
g_critical ("%s: Failed to save SessionEntry removing from list",
__func__);
flush_session (resmgr, entry);
ret = FALSE;
goto out;
}
g_clear_object (&resp);
resp = save_session (resmgr, entry);
if (tpm2_response_get_code (resp) != TSS2_RC_SUCCESS) {
g_critical ("%s: Failed to load SessionEntry. Got RC 0x%" PRIx32
", removing from list", __func__,
tpm2_response_get_code (resp));
flush_session (resmgr, entry);
ret = FALSE;
}
}
out:
g_clear_object (&resp);
return ret;
}
tpm2-abrmd-3.0.0/src/resource-manager-session.h 0000664 0000000 0000000 00000001136 14343407245 0021346 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
#ifndef RESOURCE_MANAGER_SESSION_H
#define RESOURCE_MANAGER_SESSION_H
#include
#include
#include
#include "resource-manager.h"
#include "session-entry.h"
Tpm2Response*
load_session (ResourceManager *resmgr,
SessionEntry *entry);
gboolean
flush_session (ResourceManager *resmgr,
SessionEntry *entry);
Tpm2Response*
save_session (ResourceManager *resmgr,
SessionEntry *entry);
gboolean
regap_session (ResourceManager *resmgr,
SessionEntry *entry);
#endif
tpm2-abrmd-3.0.0/src/resource-manager.c 0000664 0000000 0000000 00000202174 14343407245 0017665 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include "connection.h"
#include "connection-manager.h"
#include "control-message.h"
#include "logging.h"
#include "message-queue.h"
#include "resource-manager-session.h"
#include "resource-manager.h"
#include "sink-interface.h"
#include "source-interface.h"
#include "tabrmd.h"
#include "tpm2-header.h"
#include "tpm2-command.h"
#include "tpm2-response.h"
#include "util.h"
#define MAX_ABANDONED 4
static void resource_manager_sink_interface_init (gpointer g_iface);
static void resource_manager_source_interface_init (gpointer g_iface);
G_DEFINE_TYPE_WITH_CODE (
ResourceManager,
resource_manager,
TYPE_THREAD,
G_IMPLEMENT_INTERFACE (TYPE_SINK,
resource_manager_sink_interface_init);
G_IMPLEMENT_INTERFACE (TYPE_SOURCE,
resource_manager_source_interface_init);
);
enum {
PROP_0,
PROP_QUEUE_IN,
PROP_SINK,
PROP_TPM2,
PROP_SESSION_LIST,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/*
* This is a helper function that does everything required to convert
* a virtual handle to a physical one in a Tpm2Command object.
* - load the context from the provided HandleMapEntry
* - store the newly assigned TPM handle (physical handle) in the entry
* - set this handle in the comamnd at the position indicated by
* 'handle_number' (0-based index)
*/
TSS2_RC
resource_manager_virt_to_phys (ResourceManager *resmgr,
Tpm2Command *command,
HandleMapEntry *entry,
guint8 handle_number)
{
TPM2_HANDLE phandle = 0;
TPMS_CONTEXT *context;
TSS2_RC rc = TSS2_RC_SUCCESS;
context = handle_map_entry_get_context (entry);
if (handle_map_entry_get_phandle(entry)) {
phandle = handle_map_entry_get_phandle(entry);
g_debug ("remembered phandle: 0x%" PRIx32, phandle);
tpm2_command_set_handle (command, phandle, handle_number);
return TSS2_RC_SUCCESS;
}
rc = tpm2_context_load (resmgr->tpm2, context, &phandle);
g_debug ("loaded phandle: 0x%" PRIx32, phandle);
if (rc == TSS2_RC_SUCCESS) {
handle_map_entry_set_phandle (entry, phandle);
tpm2_command_set_handle (command, phandle, handle_number);
} else {
g_warning ("Failed to load context: 0x%" PRIx32, rc);
}
return rc;
}
TSS2_RC
resource_manager_load_transient (ResourceManager *resmgr,
Tpm2Command *command,
GSList **entry_slist,
TPM2_HANDLE handle,
guint8 handle_index)
{
HandleMap *map;
HandleMapEntry *entry;
Connection *connection;
TSS2_RC rc = TSS2_RC_SUCCESS;
g_debug ("processing TPM2_HT_TRANSIENT: 0x%" PRIx32, handle);
connection = tpm2_command_get_connection (command);
map = connection_get_trans_map (connection);
g_object_unref (connection);
g_debug ("handle 0x%" PRIx32 " is virtual TPM2_HT_TRANSIENT, "
"loading", handle);
/* we don't unref the entry since we're adding it to the entry_slist below */
entry = handle_map_vlookup (map, handle);
if (entry) {
g_debug ("mapped virtual handle 0x%" PRIx32 " to entry", handle);
} else {
g_warning ("No HandleMapEntry for vhandle: 0x%" PRIx32, handle);
goto out;
}
rc = resource_manager_virt_to_phys (resmgr, command, entry, handle_index);
if (rc != TSS2_RC_SUCCESS) {
goto out;
}
*entry_slist = g_slist_prepend (*entry_slist, entry);
out:
g_object_unref (map);
return rc;
}
/*
* This structure is used by the regap_session_callback to allow the
* response code from the callbacks to be passed to the caller.
*/
typedef struct {
ResourceManager *resmgr;
gboolean ret;
} regap_session_data_t;
/*
* This is a version of the 'regap_session' function with the with the
* appropriate prototype for use with GList 'foreach' iterator.
*/
void
regap_session_callback (gpointer data_entry,
gpointer data_user)
{
regap_session_data_t *data = (regap_session_data_t*)data_user;
SessionEntry *entry = SESSION_ENTRY (data_entry);
g_debug ("%s: SessionEntry", __func__);
if (data->ret == TRUE) {
data->ret = regap_session (data->resmgr, entry);
} else {
g_critical ("%s: previous attempt to regap failed, skipping"
" SessionEntry", __func__);
}
}
/*
* This function is a handler for response codes that we may get from the
* TPM in response to commands. It may result in addtional commands being
* sent to the TPM so use it with caution.
*/
gboolean
handle_rc (ResourceManager *resmgr,
TSS2_RC rc)
{
regap_session_data_t data = {
.resmgr = resmgr,
.ret = TRUE,
};
gboolean ret;
g_debug ("%s: handling RC 0x%" PRIx32, __func__, rc);
switch (rc) {
case TPM2_RC_CONTEXT_GAP:
g_debug ("%s: handling TPM2_RC_CONTEXT_GAP", __func__);
session_list_foreach (resmgr->session_list,
regap_session_callback,
&data);
ret = data.ret;
break;
default:
g_debug ("%s: Unable to recover gracefully from RC 0x%" PRIx32,
__func__, rc);
ret = TRUE;
break;
}
return ret;
}/*
* This is a somewhat generic function used to load session contexts into
* the TPM2 device. The ResourceManager uses this function when loading
* sessions in the handle or auth area of a command. It will refuse to load
* sessions that:
* - aren't tracked by the ResourceManager (must have SessionEntry in
* SessionList)
* - that were last saved by the client instead of the RM
* - aren't owned by the Connection object associated with the Tpm2Command
*/
TSS2_RC
resource_manager_load_session_from_handle (ResourceManager *resmgr,
Connection *command_conn,
TPM2_HANDLE handle,
gboolean will_flush)
{
Connection *entry_conn = NULL;
SessionEntry *session_entry = NULL;
Tpm2Response *response = NULL;
SessionEntryStateEnum session_entry_state;
TSS2_RC rc = TSS2_RC_SUCCESS;
session_entry = session_list_lookup_handle (resmgr->session_list,
handle);
if (session_entry == NULL) {
g_debug ("no session with handle 0x%08" PRIx32 " known to "
"ResourceManager.", handle);
goto out;
}
g_debug ("%s: mapped session handle 0x%08" PRIx32 " to "
"SessionEntry", __func__, handle);
entry_conn = session_entry_get_connection (session_entry);
if (command_conn != entry_conn) {
g_warning ("%s: Connection from Tpm2Command and SessionEntry do not "
"match. Refusing to load.", __func__);
goto out;
}
session_entry_state = session_entry_get_state (session_entry);
if (session_entry_state != SESSION_ENTRY_SAVED_RM) {
g_warning ("%s: Handle in handle area references SessionEntry "
"for session in state \"%s\". Must be in state: "
"SESSION_ENTRY_SAVED_RM for us manage it, ignoring.",
__func__, session_entry_state_to_str (session_entry_state));
goto out;
}
response = load_session (resmgr, session_entry);
rc = tpm2_response_get_code (response);
if (rc != TSS2_RC_SUCCESS) {
if (handle_rc (resmgr, rc) != TRUE) {
g_warning ("Failed to load context for session with handle "
"0x%08" PRIx32 " RC: 0x%" PRIx32, handle, rc);
flush_session (resmgr, session_entry);
goto out;
}
response = load_session (resmgr, session_entry);
rc = tpm2_response_get_code (response);
if (rc != TSS2_RC_SUCCESS) {
flush_session (resmgr, session_entry);
goto out;
}
}
if (will_flush) {
g_debug ("%s: will_flush: removing SessionEntry from SessionList",
__func__);
session_list_remove (resmgr->session_list, session_entry);
}
out:
g_clear_object (&entry_conn);
g_clear_object (&response);
g_clear_object (&session_entry);
return rc;
}
typedef struct {
ResourceManager *resmgr;
Tpm2Command *command;
} auth_callback_data_t;
void
resource_manager_load_auth_callback (gpointer auth_offset_ptr,
gpointer user_data)
{
Connection *connection = NULL;
TPM2_HANDLE handle;
auth_callback_data_t *data = (auth_callback_data_t*)user_data;
TPMA_SESSION attrs;
gboolean will_flush = TRUE;
size_t auth_offset = *(size_t*)auth_offset_ptr;
handle = tpm2_command_get_auth_handle (data->command, auth_offset);
switch (handle >> TPM2_HR_SHIFT) {
case TPM2_HT_HMAC_SESSION:
case TPM2_HT_POLICY_SESSION:
attrs = tpm2_command_get_auth_attrs (data->command, auth_offset);
if (attrs & TPMA_SESSION_CONTINUESESSION) {
will_flush = FALSE;
}
connection = tpm2_command_get_connection (data->command);
resource_manager_load_session_from_handle (data->resmgr,
connection,
handle,
will_flush);
break;
default:
g_debug ("not loading object with handle: 0x%08" PRIx32 " from "
"command auth area: not a session", handle);
break;
}
g_clear_object (&connection);
}
/*
* This function operates on the provided command. It iterates over each
* handle in the commands handle area. For each relevant handle it loads
* the related context and fixes up the handles in the command.
*/
TSS2_RC
resource_manager_load_handles (ResourceManager *resmgr,
Tpm2Command *command,
GSList **loaded_transients)
{
Connection *connection = NULL;
TSS2_RC rc = TSS2_RC_SUCCESS;
TPM2_HANDLE handles[TPM2_COMMAND_MAX_HANDLES] = { 0, };
size_t i, handle_count = TPM2_COMMAND_MAX_HANDLES;
gboolean handle_ret;
g_debug ("%s", __func__);
if (!resmgr || !command) {
g_warning ("%s: received NULL parameter.", __func__);
return RM_RC (TSS2_BASE_RC_GENERAL_FAILURE);
}
handle_ret = tpm2_command_get_handles (command, handles, &handle_count);
if (handle_ret == FALSE) {
g_error ("Unable to get handles from command");
}
g_debug ("%s: for %zu handles in command handle area",
__func__, handle_count);
for (i = 0; i < handle_count; ++i) {
switch (handles [i] >> TPM2_HR_SHIFT) {
case TPM2_HT_TRANSIENT:
g_debug ("processing TPM2_HT_TRANSIENT: 0x%" PRIx32, handles [i]);
rc = resource_manager_load_transient (resmgr,
command,
loaded_transients,
handles [i],
i);
break;
case TPM2_HT_HMAC_SESSION:
case TPM2_HT_POLICY_SESSION:
g_debug ("processing TPM2_HT_HMAC_SESSION or "
"TPM2_HT_POLICY_SESSION: 0x%" PRIx32, handles [i]);
connection = tpm2_command_get_connection (command);
rc = resource_manager_load_session_from_handle (resmgr,
connection,
handles [i],
FALSE);
break;
default:
break;
}
}
g_debug ("%s: end", __func__);
g_clear_object (&connection);
return rc;
}
/*
* Remove the context associated with the provided HandleMapEntry
* from the TPM. Only handles in the TRANSIENT range will be flushed.
* Any entry with a context that's flushed will have the physical handle
* to 0.
*/
void
resource_manager_flushsave_context (gpointer data_entry,
gpointer data_resmgr)
{
ResourceManager *resmgr = RESOURCE_MANAGER (data_resmgr);
HandleMapEntry *entry = HANDLE_MAP_ENTRY (data_entry);
TPMS_CONTEXT *context;
TPM2_HANDLE phandle;
TSS2_RC rc = TSS2_RC_SUCCESS;
g_debug ("%s: for entry", __func__);
if (resmgr == NULL || entry == NULL)
g_error ("%s: passed NULL parameter", __func__);
phandle = handle_map_entry_get_phandle (entry);
g_debug ("%s: phandle: 0x%" PRIx32, __func__, phandle);
switch (phandle >> TPM2_HR_SHIFT) {
case TPM2_HT_TRANSIENT:
if (!handle_map_entry_get_phandle (entry)) {
g_debug ("phandle for vhandle 0x%" PRIx32 " was already flushed.",
handle_map_entry_get_vhandle (entry));
break;
}
g_debug ("%s: handle is transient, saving context", __func__);
context = handle_map_entry_get_context (entry);
rc = tpm2_context_saveflush (resmgr->tpm2,
phandle,
context);
if (rc == TSS2_RC_SUCCESS) {
handle_map_entry_set_phandle (entry, 0);
} else {
g_warning ("%s: tpm2_context_saveflush failed for "
"handle: 0x%" PRIx32 " rc: 0x%" PRIx32,
__func__, phandle, rc);
}
break;
default:
break;
}
}
/*
* Remove the context associated with the provided SessionEntry from the
* TPM. Only session objects should be saved by this function.
*/
void
save_session_callback (gpointer data_entry,
gpointer data_resmgr)
{
ResourceManager *resmgr = RESOURCE_MANAGER (data_resmgr);
SessionEntry *entry = SESSION_ENTRY (data_entry);
Tpm2Response *resp = NULL;
TSS2_RC rc;
g_debug ("%s: SessionEntry", __func__);
if (session_entry_get_state (entry) != SESSION_ENTRY_LOADED) {
g_debug ("%s: cannot save SessionEntry, not loaded", __func__);
return;
}
resp = save_session (resmgr, entry);
rc = tpm2_response_get_code (resp);
if (rc != TSS2_RC_SUCCESS) {
if (handle_rc (resmgr, rc) != TRUE) {
g_warning ("%s: Failed to save SessionEntry",
__func__);
flush_session (resmgr, entry);
goto out;
}
resp = save_session (resmgr, entry);
if (tpm2_response_get_code (resp) != TSS2_RC_SUCCESS) {
g_critical ("%s: failed to save SessionEntry, flushing",
__func__);
flush_session (resmgr, entry);
}
}
out:
g_clear_object (&resp);
return;
}
static void
dump_command (Tpm2Command *command)
{
g_assert (command != NULL);
g_debug ("Tpm2Command");
g_debug_bytes (tpm2_command_get_buffer (command),
tpm2_command_get_size (command),
16,
4);
g_debug_tpma_cc (tpm2_command_get_attributes (command));
}
static void
dump_response (Tpm2Response *response)
{
g_assert (response != NULL);
g_debug ("Tpm2Response");
g_debug_bytes (tpm2_response_get_buffer (response),
tpm2_response_get_size (response),
16,
4);
g_debug_tpma_cc (tpm2_response_get_attributes (response));
}
/*
* This function performs the special processing required when a client
* attempts to save a session context. In short: handling the context gap /
* contextCounter roll over is the only reason we have to get involved. To do
* this we must be able to load and save every active session from oldest to
* newest. This is discussed in detail in section 30.5 from part 1 of the TPM2
* spec.
*
* The recommended algorithm for doing this is documented in one of the TSS2 specs.
*/
Tpm2Response*
resource_manager_save_context_session (ResourceManager *resmgr,
Tpm2Command *command)
{
Connection *conn_cmd = NULL, *conn_entry = NULL;
SessionEntry *entry = NULL;
Tpm2Response *response = NULL;
TPM2_HANDLE handle = 0;
handle = tpm2_command_get_handle (command, 0);
g_debug ("save_context for session handle: 0x%" PRIx32, handle);
entry = session_list_lookup_handle (resmgr->session_list, handle);
if (entry == NULL) {
g_warning ("Client attempting to save unknown session.");
goto out;
}
/* the lookup function should check this for us? */
conn_cmd = tpm2_command_get_connection (command);
conn_entry = session_entry_get_connection (entry);
if (conn_cmd != conn_entry) {
g_warning ("%s: session belongs to a different connection", __func__);
goto out;
}
session_entry_set_state (entry, SESSION_ENTRY_SAVED_CLIENT);
response = tpm2_response_new_context_save (conn_cmd, entry);
g_debug ("%s: Tpm2Response from TPM2_ContextSave", __func__);
g_debug_bytes (tpm2_response_get_buffer (response),
tpm2_response_get_size (response),
16, 4);
out:
g_clear_object (&conn_cmd);
g_clear_object (&conn_entry);
g_clear_object (&entry);
return response;
}
/*
* This function performs the special processing associated with the
* TPM2_ContextSave command. How much we can "virtualize of this command
* depends on the parameters / handle type as well as how much work we
* actually *want* to do.
*
* Transient objects that are tracked by the RM require no special handling.
* It's possible that the whole of this command could be virtualized: When a
* command is received all transient objects have been saved and flushed. The
* saved context held by the RM could very well be returned to the caller
* with no interaction with the TPM. This would however require that the RM
* marshal the context object into the response buffer. This is less easy
* than it should be and so we suffer the inefficiency to keep the RM code
* more simple. Simple in this case means no special handling.
*
* Session objects are handled much in the same way with a specific caveat:
* A session can be either loaded or saved. Unlike a transient object saving
* it changes its state. And so for the caller to save the context it must
* be loaded first. This is exactly what we do for transient objects, but
* knowing that the caller wants to save the context we also know that the RM
* can't have a saved copy as well. And so we must load the context and
* destroy the mapping maintained by the RM. Since this function is called
* after the session contexts are loaded we just need to drop the mapping.
*/
Tpm2Response*
resource_manager_save_context (ResourceManager *resmgr,
Tpm2Command *command)
{
TPM2_HANDLE handle = tpm2_command_get_handle (command, 0);
g_debug ("%s", __func__);
switch (handle >> TPM2_HR_SHIFT) {
case TPM2_HT_HMAC_SESSION:
case TPM2_HT_POLICY_SESSION:
return resource_manager_save_context_session (resmgr, command);
default:
g_debug ("save_context: not virtualizing TPM2_CC_ContextSave for "
"handles: 0x%08" PRIx32, handle);
break;
}
return NULL;
}
/*
* This function performs the special processing required when handling a
* TPM2_CC_ContextLoad command:
* - First we look at the TPMS_CONTEXT provided in the command body. If we've
* never see the context then we do nothing and let the command be processed
* in its current form. If the context is valid and the TPM loads it the
* ResourceManager will intercept the response and begin tracking the
* session.
* - Second we check to be sure the session can be loaded by the connection
* over which we received the command. This requires that it's either:
* - the same connection associated with the SessionEntry
* or
* - that the SessionEntry has been abandoned
* - If the access control check pacsses we return the handle to the caller
* in a Tpm2Response that we craft.
*/
Tpm2Response*
resource_manager_load_context_session (ResourceManager *resmgr,
Tpm2Command *command)
{
Connection *conn_cmd = NULL, *conn_entry = NULL;
SessionEntry *entry = NULL;
Tpm2Response *response = NULL;
g_debug ("%s", __func__);
entry = session_list_lookup_context_client (resmgr->session_list,
&tpm2_command_get_buffer (command) [TPM_HEADER_SIZE],
tpm2_command_get_size (command) - TPM_HEADER_SIZE);
if (entry == NULL) {
g_debug ("%s: Tpm2Command contains unknown TPMS_CONTEXT.", __func__);
goto out;
}
conn_cmd = tpm2_command_get_connection (command);
conn_entry = session_entry_get_connection (entry);
if (conn_cmd != conn_entry) {
if (!session_list_claim (resmgr->session_list, entry, conn_cmd)) {
goto out;
}
}
session_entry_set_state (entry, SESSION_ENTRY_SAVED_RM);
g_debug ("%s: SessionEntry context savedHandle: 0x%08" PRIx32, __func__,
session_entry_get_handle (entry));
response = tpm2_response_new_context_load (conn_cmd, entry);
out:
g_debug ("%s: returning Tpm2Response", __func__);
g_clear_object (&conn_cmd);
g_clear_object (&conn_entry);
g_clear_object (&entry);
return response;
}
/*
* This function performs the special processing associated with the
* TPM2_ContextLoad command.
*/
Tpm2Response*
resource_manager_load_context (ResourceManager *resmgr,
Tpm2Command *command)
{
/* why all the marshalling */
uint8_t *buf = tpm2_command_get_buffer (command);
TPMS_CONTEXT tpms_context;
TSS2_RC rc;
size_t offset = TPM_HEADER_SIZE;
/* Need to be able to get handle from Tpm2Command */
rc = Tss2_MU_TPMS_CONTEXT_Unmarshal (buf,
tpm2_command_get_size (command),
&offset,
&tpms_context);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: Failed to unmarshal TPMS_CONTEXT from Tpm2Command, "
"rc: 0x%" PRIx32, __func__, rc);
/* Generate Tpm2Response with "appropriate" RC */
}
switch (tpms_context.savedHandle >> TPM2_HR_SHIFT) {
case TPM2_HT_HMAC_SESSION:
case TPM2_HT_POLICY_SESSION:
return resource_manager_load_context_session (resmgr, command);
default:
g_debug ("%s: not virtualizing TPM2_ContextLoad for "
"handles: 0x%08" PRIx32, __func__, tpms_context.savedHandle);
break;
}
return NULL;
}
/*
* This function performs the special processing associated with the
* TPM2_FlushContext command. How much we can "virtualize" of this command
* depends on the parameters / handle type.
*
* Transient objects that are tracked by the RM (stored in the transient
* HandleMap in the Connection object) then we can simply delete the mapping
* since transient objects are always saved / flushed after each command is
* processed. So for this handle type we just delete the mapping, create a
* Tpm2Response object and return it to the caller.
*
* Session objects are not so simple. Sessions cannot be flushed after each
* use. The TPM will only allow us to save the context as it must maintain
* some internal tracking information. So when the caller wishes to flush a
* session context we must remove the entry tracking the mapping between
* session and connection from the session_slist (if any exists). The comman
* must then be passed to the TPM as well. We return NULL here to let the
* caller know.
*/
Tpm2Response*
resource_manager_flush_context (ResourceManager *resmgr,
Tpm2Command *command)
{
Connection *connection;
HandleMap *map;
HandleMapEntry *entry;
Tpm2Response *response = NULL;
TPM2_HANDLE handle;
TPM2_HT handle_type;
TSS2_RC rc;
if (tpm2_command_get_code (command) != TPM2_CC_FlushContext) {
g_warning ("resource_manager_flush_context with wrong command");
return NULL;
}
rc = tpm2_command_get_flush_handle (command, &handle);
if (rc != TSS2_RC_SUCCESS) {
connection = tpm2_command_get_connection (command);
response = tpm2_response_new_rc (connection, rc);
g_object_unref (connection);
goto out;
}
g_debug ("resource_manager_flush_context handle: 0x%" PRIx32, handle);
handle_type = handle >> TPM2_HR_SHIFT;
switch (handle_type) {
case TPM2_HT_TRANSIENT:
g_debug ("handle is TPM2_HT_TRANSIENT, virtualizing");
connection = tpm2_command_get_connection (command);
map = connection_get_trans_map (connection);
entry = handle_map_vlookup (map, handle);
if (entry != NULL) {
handle_map_remove (map, handle);
g_object_unref (entry);
rc = TSS2_RC_SUCCESS;
} else {
/*
* If the handle doesn't map to a HandleMapEntry then it's not one
* that we're managing and so we can't flush it. Return an error
* indicating that error is related to a handle, that it's a parameter
* and that it's the first parameter.
*/
rc = RM_RC (TPM2_RC_HANDLE + TPM2_RC_P + TPM2_RC_1);
}
g_object_unref (map);
response = tpm2_response_new_rc (connection, rc);
g_object_unref (connection);
break;
case TPM2_HT_HMAC_SESSION:
case TPM2_HT_POLICY_SESSION:
g_debug ("%s: handle 0x%08" PRIx32 "is a session, removing from "
"SessionList", __func__, handle);
session_list_remove_handle (resmgr->session_list, handle);
break;
}
out:
return response;
}
/*
* Ensure that executing the provided command will not exceed any of the
* per-connection quotas enforced by the RM. This is currently limited to
* transient objects and sessions.
*/
TSS2_RC
resource_manager_quota_check (ResourceManager *resmgr,
Tpm2Command *command)
{
HandleMap *handle_map = NULL;
Connection *connection = NULL;
TSS2_RC rc = TSS2_RC_SUCCESS;
switch (tpm2_command_get_code (command)) {
/* These commands load transient objects. */
case TPM2_CC_CreatePrimary:
case TPM2_CC_Load:
case TPM2_CC_LoadExternal:
connection = tpm2_command_get_connection (command);
handle_map = connection_get_trans_map (connection);
if (handle_map_is_full (handle_map)) {
g_info ("%s: Connection has exceeded transient object limit",
__func__);
rc = TSS2_RESMGR_RC_OBJECT_MEMORY;
}
break;
/* These commands create sessions. */
case TPM2_CC_StartAuthSession:
connection = tpm2_command_get_connection (command);
if (session_list_is_full (resmgr->session_list, connection)) {
g_info ("%s: Connectionhas exceeded session limit", __func__);
rc = TSS2_RESMGR_RC_SESSION_MEMORY;
}
break;
}
g_clear_object (&connection);
g_clear_object (&handle_map);
return rc;
}
/*
* This is a callback function invoked by the GSList foreach function. It is
* called when the object associated with a HandleMapEntry is no longer valid
* (usually when it is flushed) and the HandleMap shouldn't be tracking it.
*/
void
remove_entry_from_handle_map (gpointer data_entry,
gpointer data_connection)
{
HandleMapEntry *entry = HANDLE_MAP_ENTRY (data_entry);
Connection *connection = CONNECTION (data_connection);
HandleMap *map = connection_get_trans_map (connection);
TPM2_HANDLE handle = handle_map_entry_get_vhandle (entry);
TPM2_HT handle_type = 0;
handle_type = handle >> TPM2_HR_SHIFT;
g_debug ("remove_entry_from_handle_map");
switch (handle_type) {
case TPM2_HT_TRANSIENT:
g_debug ("%s: entry is transient, removing from map", __func__);
handle_map_remove (map, handle);
break;
default:
g_debug ("%s: entry not transient, leaving entry alone", __func__);
break;
}
}
/*
* This function handles the required post-processing on the HandleMapEntry
* objects in the GSList that represent objects loaded into the TPM as part of
* executing a command.
*/
void
post_process_loaded_transients (ResourceManager *resmgr,
GSList **transient_slist,
Connection *connection,
TPMA_CC command_attrs)
{
/* if flushed bit is clear we need to flush & save contexts */
if (!(command_attrs & TPMA_CC_FLUSHED)) {
g_debug ("flushsave_context for %" PRIu32 " entries",
g_slist_length (*transient_slist));
g_slist_foreach (*transient_slist,
resource_manager_flushsave_context,
resmgr);
} else {
/*
* if flushed bit is set the transient object entry has been flushed
* and so we just remove it
*/
g_debug ("TPMA_CC flushed bit set");
g_slist_foreach (*transient_slist,
remove_entry_from_handle_map,
connection);
}
g_slist_free_full (*transient_slist, g_object_unref);
}
/*
* This structure is used to keep state while iterating over a list of
* TPM2_HANDLES.
* 'cap_data' : this parameter is used to collect the list of handles that
* we want as well as the number of handles in the structure
* 'max_count' : is the maximum number of handles to return
* 'more_data' : once max_count handles have been collected this variable
* tells the caller whether additional handles would have been returned had
* max_count been larger
* 'start_handle': is the numerically smallest handle that should be collected
* into cap_data.
*/
typedef struct {
TPMS_CAPABILITY_DATA *cap_data;
size_t max_count;
gboolean more_data;
TPM2_HANDLE start_handle;
} vhandle_iterator_state_t;
/*
* This callback function is invoked as part of iterating over a list of
* handles. The first parameter is an entry from the collection being
* traversed. The second is a reference to a vhandle_iterator_state_t
* structure.
* This structure is used to maintain state while iterating over the
* collection.
*/
void
vhandle_iterator_callback (gpointer entry,
gpointer data)
{
TPM2_HANDLE vhandle = (uintptr_t)entry;
vhandle_iterator_state_t *state = (vhandle_iterator_state_t*)data;
TPMS_CAPABILITY_DATA *cap_data = state->cap_data;
/* if vhandle is numerically smaller than the start value just return */
if (vhandle < state->start_handle) {
return;
}
g_debug ("vhandle_iterator_callback with max_count: %zu and count: %"
PRIu32, state->max_count, cap_data->data.handles.count);
/* if we've collected max_count handles set 'more_data' and return */
if (!(cap_data->data.handles.count < state->max_count)) {
state->more_data = TRUE;
return;
}
cap_data->data.handles.handle [cap_data->data.handles.count] = vhandle;
++cap_data->data.handles.count;
}
/*
* This is a GCompareFunc used to sort a list of TPM2_HANDLES.
*/
int
handle_compare (gconstpointer a,
gconstpointer b)
{
TPM2_HANDLE handle_a = (uintptr_t)a;
TPM2_HANDLE handle_b = (uintptr_t)b;
if (handle_a < handle_b) {
return -1;
} else if (handle_a > handle_b) {
return 1;
} else {
return 0;
}
}
/*
* The get_cap_transient function populates a TPMS_CAPABILITY_DATA structure
* with the handles in the provided HandleMap 'map'. The 'prop' parameter
* is the lowest numerical handle to return. The 'count' parameter is the
* maximum number of handles to return in the capability data structure.
* Returns:
* TRUE when more handles are present
* FALSE when there are no more handles
*/
gboolean
get_cap_handles (HandleMap *map,
TPM2_HANDLE prop,
UINT32 count,
TPMS_CAPABILITY_DATA *cap_data)
{
GList *vhandle_list;
vhandle_iterator_state_t state = {
.cap_data = cap_data,
.max_count = count,
.more_data = FALSE,
.start_handle = prop,
};
cap_data->capability = TPM2_CAP_HANDLES;
cap_data->data.handles.count = 0;
vhandle_list = handle_map_get_keys (map);
vhandle_list = g_list_sort (vhandle_list, handle_compare);
g_list_foreach (vhandle_list, vhandle_iterator_callback, &state);
g_debug ("iterating over %" PRIu32 " vhandles from g_list_foreach",
cap_data->data.handles.count);
size_t i;
for (i = 0; i < cap_data->data.handles.count; ++i) {
g_debug (" vhandle: 0x%" PRIx32, cap_data->data.handles.handle [i]);
}
return state.more_data;
}
/*
* These macros are used to set fields in a Tpm2Response buffer that we
* create in response to the TPM2 GetCapability command. They are very
* specifically tailored and should not be used elsewhere.
*/
#define YES_NO_OFFSET TPM_HEADER_SIZE
#define YES_NO_SET(buffer, value) \
(*(TPMI_YES_NO*)(buffer + YES_NO_OFFSET) = value)
#define CAP_OFFSET (TPM_HEADER_SIZE + sizeof (TPMI_YES_NO))
#define CAP_SET(buffer, value) \
(*(TPM2_CAP*)(buffer + CAP_OFFSET) = htobe32 (value))
#define HANDLE_COUNT_OFFSET (CAP_OFFSET + sizeof (TPM2_CAP))
#define HANDLE_COUNT_SET(buffer, value) \
(*(UINT32*)(buffer + HANDLE_COUNT_OFFSET) = htobe32 (value))
#define HANDLE_OFFSET (HANDLE_COUNT_OFFSET + sizeof (UINT32))
#define HANDLE_INDEX(i) (sizeof (TPM2_HANDLE) * i)
#define HANDLE_SET(buffer, i, value) \
(*(TPM2_HANDLE*)(buffer + HANDLE_OFFSET + HANDLE_INDEX (i)) = \
htobe32 (value))
#define CAP_RESP_SIZE(value) \
(TPM_HEADER_SIZE + \
sizeof (TPMI_YES_NO) + \
sizeof ((value)->capability) + \
sizeof ((value)->data.handles.count) + \
((value)->data.handles.count * \
sizeof ((value)->data.handles.handle [0])))
/*
* This function is used to build a response buffer that contains the provided
* TPMS_CAPABILITY_DATA and TPMI_YES_NO. These are the two response parameters
* to the TPM2_GetCapability function.
* The 'cap_data' parameter *must* have the TPMU_CAPABILITY union populated
* with the TPM2_CAP_HANDLES selector.
*/
uint8_t*
build_cap_handles_response (TPMS_CAPABILITY_DATA *cap_data,
TPMI_YES_NO more_data)
{
size_t i;
uint8_t *buf;
buf = calloc (1, CAP_RESP_SIZE (cap_data));
if (buf == NULL) {
tabrmd_critical ("failed to allocate buffer for handle capability "
"response");
}
set_response_tag (buf, TPM2_ST_NO_SESSIONS);
set_response_size (buf, CAP_RESP_SIZE (cap_data));
set_response_code (buf, TSS2_RC_SUCCESS);
YES_NO_SET (buf, more_data);
CAP_SET (buf, cap_data->capability);
HANDLE_COUNT_SET (buf, cap_data->data.handles.count);
for (i = 0; i < cap_data->data.handles.count; ++i) {
HANDLE_SET (buf, i, cap_data->data.handles.handle [i]);
}
return buf;
}
/*
* In cases where the GetCapability command isn't fully virtualized we may
* need to perform some 'post processing' of the results returned from the
* TPM2 device. Specifically, in the case of the TPM2_PT_CONTEXT_GAP_MAX
* property, we overrite the value in the response body. This function
* manually unmarshals the response, iterates over the values modifying
* them if necessary and then marshals them back into the Tpm2Response.
*/
TSS2_RC
get_cap_post_process (Tpm2Response *resp)
{
g_assert (resp != NULL);
g_assert (tpm2_response_get_code (resp) == TSS2_RC_SUCCESS);
TPMS_CAPABILITY_DATA cap_data = { .capability = 0 };
TSS2_RC rc;
uint8_t *buf = tpm2_response_get_buffer (resp);
size_t buf_size = tpm2_response_get_size (resp);
size_t offset = TPM_HEADER_SIZE + sizeof (TPMI_YES_NO);
size_t i;
rc = Tss2_MU_TPMS_CAPABILITY_DATA_Unmarshal (buf,
buf_size,
&offset,
&cap_data);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: Failed to unmarshal TPMS_CAPABILITY_DATA", __func__);
return rc;
}
g_debug ("%s: capability 0x%" PRIx32, __func__, cap_data.capability);
switch (cap_data.capability) {
case TPM2_CAP_TPM_PROPERTIES:
for (i = 0; i < cap_data.data.tpmProperties.count; ++i) {
g_debug ("%s: property 0x%" PRIx32 ", value 0x%" PRIx32,
__func__,
cap_data.data.tpmProperties.tpmProperty [i].property,
cap_data.data.tpmProperties.tpmProperty [i].value);
switch (cap_data.data.tpmProperties.tpmProperty [i].property) {
case TPM2_PT_CONTEXT_GAP_MAX:
g_debug ("%s: changing TPM2_PT_CONTEXT_GAP_MAX, from 0x%"
PRIx32 " to UINT32_MAX: 0x%" PRIx32, __func__,
cap_data.data.tpmProperties.tpmProperty [i].value,
UINT32_MAX);
cap_data.data.tpmProperties.tpmProperty [i].value = UINT32_MAX;
break;
default:
break;
}
}
break;
default:
break;
}
offset = TPM_HEADER_SIZE + sizeof (TPMI_YES_NO);
rc = Tss2_MU_TPMS_CAPABILITY_DATA_Marshal (&cap_data,
buf,
buf_size,
&offset);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: Failed to unmarshal TPMS_CAPABILITY_DATA", __func__);
return rc;
}
return rc;
}
/*
* This function takes a Tpm2Command and the associated connection object
* as parameters. The Tpm2Command *must* have the 'code' attribute set to
* TPM2_CC_GetCapability. If it's a GetCapability command that we
* "virtualize" then we'll build a Tpm2Response object and return it. If
* not, then we send the command to the TPM2 device and perform whatever
* post-processing is necessary.
*/
Tpm2Response*
get_cap_gen_response (ResourceManager *resmgr,
Tpm2Command *command)
{
TPM2_CAP cap = tpm2_command_get_cap (command);
UINT32 prop = tpm2_command_get_prop (command);
UINT32 prop_count = tpm2_command_get_prop_count (command);
TPM2_HT handle_type;
TSS2_RC rc = TSS2_RC_SUCCESS;
Connection *connection = NULL;
HandleMap *map;
TPMS_CAPABILITY_DATA cap_data = { .capability = cap };
gboolean more_data = FALSE;
uint8_t *resp_buf;
Tpm2Response *response = NULL;
g_debug ("processing TPM2_CC_GetCapability with cap: 0x%" PRIx32
" prop: 0x%" PRIx32 " prop_count: 0x%" PRIx32,
cap, prop, prop_count);
switch (cap) {
case TPM2_CAP_HANDLES:
handle_type = prop >> TPM2_HR_SHIFT;
switch (handle_type) {
case TPM2_HT_TRANSIENT:
g_debug ("%s: TPM2_CAP_HANDLES && TPM2_HT_TRANSIENT", __func__);
connection = tpm2_command_get_connection (command);
map = connection_get_trans_map (connection);
more_data = get_cap_handles (map, prop, prop_count, &cap_data);
g_object_unref (map);
resp_buf = build_cap_handles_response (&cap_data, more_data);
response = tpm2_response_new (connection,
resp_buf,
CAP_RESP_SIZE (&cap_data),
tpm2_command_get_attributes (command));
break;
default:
g_debug ("%s: TPM2_CAP_HANDLES not virtualized for handle type: "
"0x%" PRIx32, __func__, handle_type);
break;
}
break;
default:
g_debug ("%s: cap 0x%" PRIx32 " not handled", __func__, cap);
break;
}
if (response == NULL) {
response = tpm2_send_command (resmgr->tpm2, command, &rc);
if (response != NULL && rc == TSS2_RC_SUCCESS) {
get_cap_post_process (response);
}
}
g_clear_object (&connection);
return response;
}
/*
* If the provided command is something that the ResourceManager "virtualizes"
* then this function will do so and return a Tpm2Response object that will be
* returned to the same connection. If the command isn't something that we
* virtualize then we just return NULL.
*
* NOTE: Both the ContextSave and ContextLoad commands, when sent by clients,
* do not result in the command being executed by the TPM. This makes
* handling the context gap unnecessary for these commands.
*/
Tpm2Response*
command_special_processing (ResourceManager *resmgr,
Tpm2Command *command)
{
Tpm2Response *response = NULL;
switch (tpm2_command_get_code (command)) {
case TPM2_CC_FlushContext:
g_debug ("processing TPM2_CC_FlushContext");
response = resource_manager_flush_context (resmgr, command);
break;
case TPM2_CC_ContextSave:
g_debug ("processing TPM2_CC_ContextSave");
response = resource_manager_save_context (resmgr, command);
break;
case TPM2_CC_ContextLoad:
g_debug ("%s: processing TPM2_CC_ContextLoad", __func__);
response = resource_manager_load_context (resmgr, command);
break;
case TPM2_CC_GetCapability:
if (!tpm2_command_has_auths(command)) {
g_debug ("processing TPM2_CC_GetCapability");
response = get_cap_gen_response (resmgr, command);
}
break;
default:
break;
}
return response;
}
/*
* This function creates a mapping from the transient physical to a virtual
* handle in the provided response object. This mapping is then added to
* the transient HandleMap for the associated connection, as well as the
* list of currently loaded transient objects.
*/
void
create_context_mapping_transient (ResourceManager *resmgr,
Tpm2Response *response,
GSList **loaded_transient_slist)
{
HandleMap *handle_map;
HandleMapEntry *handle_entry;
TPM2_HANDLE phandle, vhandle;
Connection *connection;
UNUSED_PARAM(resmgr);
g_debug ("create_context_mapping_transient");
phandle = tpm2_response_get_handle (response);
g_debug (" physical handle: 0x%08" PRIx32, phandle);
connection = tpm2_response_get_connection (response);
handle_map = connection_get_trans_map (connection);
g_object_unref (connection);
vhandle = handle_map_next_vhandle (handle_map);
if (vhandle == 0) {
g_error ("vhandle rolled over!");
}
g_debug (" vhandle:0x%08" PRIx32, vhandle);
handle_entry = handle_map_entry_new (phandle, vhandle);
if (handle_entry == NULL) {
g_warning ("failed to create new HandleMapEntry for handle 0x%"
PRIx32, phandle);
}
*loaded_transient_slist = g_slist_prepend (*loaded_transient_slist,
handle_entry);
handle_map_insert (handle_map, vhandle, handle_entry);
g_object_unref (handle_map);
tpm2_response_set_handle (response, vhandle);
}
/*
* This function after a Tpm2Command is sent to the TPM and:
* - we receive a Tpm2Response object with a handle in the response buffers
* handle area
* - the handle is a session handle
* Since there's a handle in the response handle area the caller is being
* returned a new handle after a context was successfully created or loaded.
* So we know that the response is to one of two possible commands:
* - a session being loaded by a call to LoadContext
* - a session was newly created by a call to StartAuthSession
* We differentiate between these two situations as follows:
* - A call to 'LoadContext' implies that the session already exists and so
* it must already be in the session_list.
* - If it's in the session_list *AND NOT* in the abandoned_session_queue
* then the caller is just loading a context they already own and so we
* set the session state to SAVED_RM and add the session to the list of
* loaded sessions.
* - If it's *NOT* in the session_list *AND* in the abandoned_session_queue
* then the caller is loading a context saved by a different connection
* and so we make the current connection the owner of the session, set
* the session state to SAVED_RM and add the session to the list of loaded
* sessions.
* - A call to 'StartAuthSession' will return a handle for a session object
* that is not in either the session_list or the abandoned_session_queue.
* In this case we just create a new SessionEntry and add it to the
* session_list and the list of loaded sessions.
* NOTE: If the response doesn't indicate 'success' then we just ignore it
* since there's nothing useful that we can do.
*/
void
create_context_mapping_session (ResourceManager *resmgr,
Tpm2Response *response,
TPM2_HANDLE handle)
{
SessionEntry *entry = NULL;
Connection *conn_resp = NULL, *conn_entry = NULL;
entry = session_list_lookup_handle (resmgr->session_list, handle);
conn_resp = tpm2_response_get_connection (response);
if (entry != NULL) {
g_debug ("%s: got SessionEntry that's in the SessionList", __func__);
conn_entry = session_entry_get_connection (entry);
if (conn_resp != conn_entry) {
g_warning ("%s: connections do not match!", __func__);
}
} else {
g_debug ("%s: handle is a session, creating entry for SessionList "
"and SessionList", __func__);
entry = session_entry_new (conn_resp, handle);
session_entry_set_state (entry, SESSION_ENTRY_LOADED);
session_list_insert (resmgr->session_list, entry);
}
g_clear_object (&conn_resp);
g_clear_object (&conn_entry);
g_clear_object (&entry);
}
/*
* Each Tpm2Response object can have at most one handle in it.
* This function assumes that the handle in the parameter Tpm2Response
* object is the handle assigned by the TPM. Depending on the type of the
* handle we do to possible things:
* For TPM2_HT_TRANSIENT handles we create a new virtual handle and
* allocates a new HandleMapEntry to map the virtual handle to a
* TPMS_CONTEXT structure when processing future commands associated
* with the same connection. This HandleMapEntry is inserted into the
* handle map for the connection. It is also added to the list of loaded
* transient objects so that it can be saved / flushed by the typical code
* path.
* For TPM2_HT_HMAC_SESSION or TPM2_HT_POLICY_SESSION handles we create a
* new session_entry_t object, populate the connection field with the
* connection associated with the response object, and set the savedHandle
* field. We then add this entry to the list of sessions we're tracking
* (session_slist) and the list of loaded sessions (loaded_session_slist).
*/
void
resource_manager_create_context_mapping (ResourceManager *resmgr,
Tpm2Response *response,
GSList **loaded_transient_slist)
{
TPM2_HANDLE handle;
g_debug ("%s", __func__);
if (!tpm2_response_has_handle (response)) {
g_debug ("response has no handles");
return;
}
handle = tpm2_response_get_handle (response);
switch (handle >> TPM2_HR_SHIFT) {
case TPM2_HT_TRANSIENT:
create_context_mapping_transient (resmgr, response, loaded_transient_slist);
break;
case TPM2_HT_HMAC_SESSION:
case TPM2_HT_POLICY_SESSION:
create_context_mapping_session (resmgr, response, handle);
break;
default:
g_debug (" not creating context for handle: 0x%08" PRIx32, handle);
break;
}
}
Tpm2Response*
send_command_handle_rc (ResourceManager *resmgr,
Tpm2Command *cmd)
{
regap_session_data_t data = {
.resmgr = resmgr,
.ret = TRUE,
};
Tpm2Response *resp = NULL;
TSS2_RC rc;
/* Send command and create response object. */
resp = tpm2_send_command (resmgr->tpm2, cmd, &rc);
rc = tpm2_response_get_code (resp);
if (rc == TPM2_RC_CONTEXT_GAP) {
g_debug ("%s: handling TPM2_RC_CONTEXT_GAP", __func__);
session_list_foreach (resmgr->session_list,
regap_session_callback,
&data);
g_clear_object (&resp);
resp = tpm2_send_command (resmgr->tpm2, cmd, &rc);
}
return resp;
}
/**
* This function is invoked in response to the receipt of a Tpm2Command.
* This is the place where we send the command buffer out to the TPM
* through the Tpm2 which will eventually get it to the TPM for us.
* The Tpm2 will send us back a Tpm2Response that we send back to
* the client by way of our Sink object. The flow is roughly:
* - Receive the Tpm2Command as a parameter
* - Load all virtualized objects required by the command.
* - Send the Tpm2Command out through the Tpm2.
* - Receive the response from the Tpm2.
* - Virtualize the new objects created by the command & referenced in the
* response.
* - Enqueue the response back out to the processing pipeline through the
* Sink object.
* - Flush all objects loaded for the command or as part of executing the
* command..
*/
void
resource_manager_process_tpm2_command (ResourceManager *resmgr,
Tpm2Command *command)
{
Connection *connection;
Tpm2Response *response;
TSS2_RC rc = TSS2_RC_SUCCESS;
GSList *transient_slist = NULL;
TPMA_CC command_attrs;
command_attrs = tpm2_command_get_attributes (command);
g_debug ("%s", __func__);
dump_command (command);
connection = tpm2_command_get_connection (command);
/* If executing the command would exceed a per connection quota */
rc = resource_manager_quota_check (resmgr, command);
if (rc != TSS2_RC_SUCCESS) {
response = tpm2_response_new_rc (connection, rc);
goto send_response;
}
/* Do command-specific processing. */
response = command_special_processing (resmgr, command);
if (response != NULL) {
goto send_response;
}
/* Load objects associated with the handles in the command handle area. */
if (tpm2_command_get_handle_count (command) > 0) {
resource_manager_load_handles (resmgr,
command,
&transient_slist);
}
/* Load objets associated with the authorizations in the command. */
if (tpm2_command_has_auths (command)) {
g_info ("%s, Processing auths for command", __func__);
auth_callback_data_t auth_callback_data = {
.resmgr = resmgr,
.command = command,
};
tpm2_command_foreach_auth (command,
resource_manager_load_auth_callback,
&auth_callback_data);
}
/* Send command and create response object. */
response = send_command_handle_rc (resmgr, command);
dump_response (response);
/* transform virtualized handles in Tpm2Response if necessary */
resource_manager_create_context_mapping (resmgr,
response,
&transient_slist);
send_response:
sink_enqueue (resmgr->sink, G_OBJECT (response));
g_object_unref (response);
/* save contexts that were previously loaded */
session_list_foreach (resmgr->session_list,
save_session_callback,
resmgr);
post_process_loaded_transients (resmgr, &transient_slist, connection, command_attrs);
g_object_unref (connection);
return;
}
/*
* Return FALSE to terminate main thread.
*/
gboolean
resource_manager_process_control (ResourceManager *resmgr,
ControlMessage *msg)
{
ControlCode code = control_message_get_code (msg);
Connection *conn;
g_debug ("%s", __func__);
switch (code) {
case CHECK_CANCEL:
sink_enqueue (resmgr->sink, G_OBJECT (msg));
return FALSE;
case CONNECTION_REMOVED:
conn = CONNECTION (control_message_get_object (msg));
g_debug ("%s: received CONNECTION_REMOVED message for connection",
__func__);
resource_manager_remove_connection (resmgr, conn);
sink_enqueue (resmgr->sink, G_OBJECT (msg));
return TRUE;
default:
g_warning ("%s: Unknown control code: %d ... ignoring",
__func__, code);
return TRUE;
}
}
/**
* This function acts as a thread. It simply:
* - Blocks on the in_queue. Then wakes up and
* - Dequeues a message from the in_queue.
* - Processes the message (depending on TYPE)
* - Does it all over again.
*/
gpointer
resource_manager_thread (gpointer data)
{
ResourceManager *resmgr = RESOURCE_MANAGER (data);
GObject *obj = NULL;
gboolean done = FALSE;
g_debug ("resource_manager_thread start");
while (!done) {
obj = message_queue_dequeue (resmgr->in_queue);
g_debug ("%s: message_queue_dequeue got obj", __func__);
if (obj == NULL) {
g_debug ("%s: dequeued a null object", __func__);
break;
}
if (IS_TPM2_COMMAND (obj)) {
resource_manager_process_tpm2_command (resmgr, TPM2_COMMAND (obj));
} else if (IS_CONTROL_MESSAGE (obj)) {
gboolean ret =
resource_manager_process_control (resmgr, CONTROL_MESSAGE (obj));
if (ret == FALSE) {
done = TRUE;
}
}
g_object_unref (obj);
}
return NULL;
}
static void
resource_manager_unblock (Thread *self)
{
ControlMessage *msg;
ResourceManager *resmgr = RESOURCE_MANAGER (self);
if (resmgr == NULL)
g_error ("resource_manager_cancel passed NULL ResourceManager");
msg = control_message_new (CHECK_CANCEL);
g_debug ("%s: enqueuing ControlMessage", __func__);
message_queue_enqueue (resmgr->in_queue, G_OBJECT (msg));
g_object_unref (msg);
}
/**
* Implement the 'enqueue' function from the Sink interface. This is how
* new messages / commands get into the Tpm2.
*/
void
resource_manager_enqueue (Sink *sink,
GObject *obj)
{
ResourceManager *resmgr = RESOURCE_MANAGER (sink);
g_debug ("%s", __func__);
message_queue_enqueue (resmgr->in_queue, obj);
}
/**
* Implement the 'add_sink' function from the SourceInterface. This adds a
* reference to an object that implements the SinkInterface to this objects
* internal structure. We pass it data.
*/
void
resource_manager_add_sink (Source *self,
Sink *sink)
{
ResourceManager *resmgr = RESOURCE_MANAGER (self);
GValue value = G_VALUE_INIT;
g_debug ("%s", __func__);
g_value_init (&value, G_TYPE_OBJECT);
g_value_set_object (&value, sink);
g_object_set_property (G_OBJECT (resmgr), "sink", &value);
g_value_unset (&value);
}
/**
* GObject property setter.
*/
static void
resource_manager_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
ResourceManager *resmgr = RESOURCE_MANAGER (object);
g_debug ("%s", __func__);
switch (property_id) {
case PROP_QUEUE_IN:
resmgr->in_queue = g_value_get_object (value);
break;
case PROP_SINK:
if (resmgr->sink != NULL) {
g_warning (" sink already set");
break;
}
resmgr->sink = SINK (g_value_get_object (value));
g_object_ref (resmgr->sink);
break;
case PROP_TPM2:
if (resmgr->tpm2 != NULL) {
g_warning (" tpm2 already set");
break;
}
resmgr->tpm2 = g_value_get_object (value);
g_object_ref (resmgr->tpm2);
break;
case PROP_SESSION_LIST:
resmgr->session_list = SESSION_LIST (g_value_dup_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* GObject property getter.
*/
static void
resource_manager_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
ResourceManager *resmgr = RESOURCE_MANAGER (object);
g_debug ("%s", __func__);
switch (property_id) {
case PROP_QUEUE_IN:
g_value_set_object (value, resmgr->in_queue);
break;
case PROP_SINK:
g_value_set_object (value, resmgr->sink);
break;
case PROP_TPM2:
g_value_set_object (value, resmgr->tpm2);
break;
case PROP_SESSION_LIST:
g_value_set_object (value, resmgr->session_list);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* Bring down the ResourceManager as gracefully as we can.
*/
static void
resource_manager_dispose (GObject *obj)
{
ResourceManager *resmgr = RESOURCE_MANAGER (obj);
Thread *thread = THREAD (obj);
g_debug ("%s", __func__);
if (resmgr == NULL)
g_error ("%s: passed NULL parameter", __func__);
if (thread->thread_id != 0)
g_error ("%s: thread running, cancel thread first", __func__);
g_clear_object (&resmgr->in_queue);
g_clear_object (&resmgr->sink);
g_clear_object (&resmgr->tpm2);
g_clear_object (&resmgr->session_list);
G_OBJECT_CLASS (resource_manager_parent_class)->dispose (obj);
}
static void
resource_manager_init (ResourceManager *manager)
{
UNUSED_PARAM (manager);
}
/**
* GObject class initialization function. This function boils down to:
* - Setting up the parent class.
* - Set dispose, property get/set.
* - Install properties.
*/
static void
resource_manager_class_init (ResourceManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ThreadClass *thread_class = THREAD_CLASS (klass);
if (resource_manager_parent_class == NULL)
resource_manager_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = resource_manager_dispose;
object_class->get_property = resource_manager_get_property;
object_class->set_property = resource_manager_set_property;
thread_class->thread_run = resource_manager_thread;
thread_class->thread_unblock = resource_manager_unblock;
obj_properties [PROP_QUEUE_IN] =
g_param_spec_object ("queue-in",
"input queue",
"Input queue for messages.",
G_TYPE_OBJECT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_SINK] =
g_param_spec_object ("sink",
"Sink",
"Reference to a Sink object that we pass messages to.",
G_TYPE_OBJECT,
G_PARAM_READWRITE);
obj_properties [PROP_TPM2] =
g_param_spec_object ("tpm2",
"Tpm2 object",
"Object used to communicate with TPM2",
TYPE_TPM2,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_SESSION_LIST] =
g_param_spec_object ("session-list",
"SessionList object",
"Data structure to hold session tracking data",
TYPE_SESSION_LIST,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
/**
* Boilerplate code to register functions with the SourceInterface.
*/
static void
resource_manager_source_interface_init (gpointer g_iface)
{
SourceInterface *source = (SourceInterface*)g_iface;
source->add_sink = resource_manager_add_sink;
}
/**
* Boilerplate code to register function with the SinkInterface.
*/
static void
resource_manager_sink_interface_init (gpointer g_iface)
{
SinkInterface *sink = (SinkInterface*)g_iface;
sink->enqueue = resource_manager_enqueue;
}
/*
* A callback function implementing the PruneFunc type for use with the
* session_list_prune_abaonded function.
*/
gboolean
flush_session_callback (SessionEntry *entry,
gpointer data)
{
return flush_session (RESOURCE_MANAGER (data), entry);
}
/*
* This structure is used to pass required data into the
* connection_close_session_callback function.
*/
typedef struct {
Connection *connection;
ResourceManager *resource_manager;
} connection_close_data_t;
/*
* This is a callback function invoked foreach SessionEntry in the SessionList
* when a client Connection is closed. As it iterates over each SessionEntry,
* it identifies sessions associated with the connection being closed and,
* performs some task depending on its state.
*
* If session is in state SESSION_ENTRY_SAVED_CLIENT:
* - take a reference to the SessionEntry
* - remove SessionEntry from session list
* - change state to SESSION_ENTRY_SAVED_CLIENT_CLOSED
* - "prune" other abandoned sessions
* - add SessionEntry to queue of abandoned sessions
* If session is in state SESSION_ENTRY_SAVED_RM:
* - flush session from TPM
* - remove SessionEntry from session list
* If session is in any other state
* - panic
*/
void
connection_close_session_callback (gpointer data,
gpointer user_data)
{
SessionEntry *session_entry = SESSION_ENTRY (data);
SessionEntryStateEnum session_state = session_entry_get_state (session_entry);
connection_close_data_t *callback_data = (connection_close_data_t*)user_data;
Connection *connection = callback_data->connection;
ResourceManager *resource_manager = callback_data->resource_manager;
TPM2_HANDLE handle;
TSS2_RC rc;
g_debug ("%s", __func__);
if (session_entry->connection != connection) {
g_debug ("%s: connection mismatch", __func__);
return;
}
handle = session_entry_get_handle (session_entry);
g_debug ("%s: SessionEntry is in state %s", __func__,
session_entry_state_to_str (session_state));
switch (session_state) {
case SESSION_ENTRY_SAVED_CLIENT:
g_debug ("%s: abandoning.", __func__);
session_list_abandon_handle (resource_manager->session_list,
connection,
handle);
session_list_prune_abandoned (resource_manager->session_list,
flush_session_callback,
resource_manager);
break;
case SESSION_ENTRY_SAVED_RM:
g_debug ("%s: flushing.", __func__);
rc = tpm2_context_flush (resource_manager->tpm2,
handle);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: failed to flush context", __func__);
}
session_list_remove (resource_manager->session_list,
session_entry);
break;
default:
/* This is a situation that should never happen */
g_error ("%s: Connection closed with session in unexpected state: %s",
__func__, session_entry_state_to_str (session_state));
break;
}
}
/*
* This function is invoked when a connection is removed from the
* ConnectionManager. This is if how we know a connection has been closed.
* When a connection is removed, we need to remove all associated sessions
* from the TPM.
*/
void
resource_manager_remove_connection (ResourceManager *resource_manager,
Connection *connection)
{
connection_close_data_t connection_close_data = {
.connection = connection,
.resource_manager = resource_manager,
};
g_info ("%s: flushing session contexts", __func__);
session_list_foreach (resource_manager->session_list,
connection_close_session_callback,
&connection_close_data);
g_debug ("%s: done", __func__);
}
/**
* Create new ResourceManager object.
*/
ResourceManager*
resource_manager_new (Tpm2 *tpm2,
SessionList *session_list)
{
if (tpm2 == NULL)
g_error ("resource_manager_new passed NULL Tpm2");
MessageQueue *queue = message_queue_new ();
return RESOURCE_MANAGER (g_object_new (TYPE_RESOURCE_MANAGER,
"queue-in", queue,
"tpm2", tpm2,
"session-list", session_list,
NULL));
}
tpm2-abrmd-3.0.0/src/resource-manager.h 0000664 0000000 0000000 00000005676 14343407245 0017702 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef RESOURCE_MANAGER_H
#define RESOURCE_MANAGER_H
#include
#include
#include
#include
#include "tpm2.h"
#include "connection-manager.h"
#include "message-queue.h"
#include "session-list.h"
#include "sink-interface.h"
#include "thread.h"
G_BEGIN_DECLS
typedef struct _ResourceManagerClass {
ThreadClass parent;
} ResourceManagerClass;
typedef struct _ResourceManager {
Thread parent_instance;
Tpm2 *tpm2;
MessageQueue *in_queue;
Sink *sink;
SessionList *session_list;
} ResourceManager;
#define TYPE_RESOURCE_MANAGER (resource_manager_get_type ())
#define RESOURCE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_RESOURCE_MANAGER, ResourceManager))
#define RESOURCE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_RESOURCE_MANAGER, ResourceManagerClass))
#define IS_RESOURCE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_RESOURCE_MANAGER))
#define IS_RESOURCE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_RESOURCE_MANAGER))
#define RESOURCE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_RESOURCE_MANAGER, ResourceManagerClass))
GType resource_manager_get_type (void);
ResourceManager* resource_manager_new (Tpm2 *tpm2,
SessionList *session_list);
void resource_manager_process_tpm2_command (ResourceManager *resmgr,
Tpm2Command *command);
void resource_manager_flushsave_context (gpointer entry,
gpointer resmgr);
TSS2_RC resource_manager_load_handles (ResourceManager *resmgr,
Tpm2Command *command,
GSList **slist);
TSS2_RC resource_manager_virt_to_phys (ResourceManager *resmgr,
Tpm2Command *command,
HandleMapEntry *entry,
guint8 handle_number);
void resource_manager_enqueue (Sink *sink,
GObject *obj);
void resource_manager_remove_connection (ResourceManager *resource_manager,
Connection *connection);
TSS2_RC get_cap_post_process (Tpm2Response *resp);
G_END_DECLS
#endif /* RESOURCE_MANAGER_H */
tpm2-abrmd-3.0.0/src/response-sink.c 0000664 0000000 0000000 00000015572 14343407245 0017232 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include "connection.h"
#include "sink-interface.h"
#include "response-sink.h"
#include "control-message.h"
#include "tpm2-response.h"
#include "util.h"
#define RESPONSE_SINK_TIMEOUT 1e6
static void response_sink_sink_interface_init (gpointer g_iface);
G_DEFINE_TYPE_WITH_CODE (
ResponseSink,
response_sink,
TYPE_THREAD,
G_IMPLEMENT_INTERFACE (TYPE_SINK,
response_sink_sink_interface_init)
);
enum {
PROP_0,
PROP_IN_QUEUE,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/**
* enqueue function to implement Sink interface.
*/
void
response_sink_enqueue (Sink *self,
GObject *obj)
{
ResponseSink *sink = RESPONSE_SINK (self);
g_debug ("response_sink_enqueue:");
if (sink == NULL)
g_error (" passed NULL sink");
if (obj == NULL)
g_error (" passed NULL object");
message_queue_enqueue (sink->in_queue, obj);
}
/**
* GObject property setter.
*/
static void
response_sink_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
ResponseSink *self = RESPONSE_SINK (object);
g_debug ("response_sink_set_property");
switch (property_id) {
case PROP_IN_QUEUE:
g_debug (" setting PROP_IN_QUEUE");
self->in_queue = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* GObject property getter.
*/
static void
response_sink_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
ResponseSink *self = RESPONSE_SINK (object);
switch (property_id) {
case PROP_IN_QUEUE:
g_value_set_object (value, self->in_queue);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* Bring down the ResponseSink as gracefully as we can.
*/
static void
response_sink_dispose (GObject *obj)
{
ResponseSink *sink = RESPONSE_SINK (obj);
Thread *thread = THREAD (obj);
if (sink == NULL)
g_error ("%s: passed NULL pointer", __func__);
if (thread->thread_id != 0)
g_error ("%s: thread running, cancel first", __func__);
g_clear_object (&sink->in_queue);
G_OBJECT_CLASS (response_sink_parent_class)->dispose (obj);
}
void* response_sink_thread (void *data);
static void
response_sink_init (ResponseSink *response)
{
UNUSED_PARAM(response);
/* noop */
}
static void
response_sink_unblock (Thread *self)
{
ResponseSink *sink = RESPONSE_SINK (self);
ControlMessage *msg;
if (sink == NULL)
g_error ("%s: passed NULL sink", __func__);
msg = control_message_new (CHECK_CANCEL);
message_queue_enqueue (sink->in_queue, G_OBJECT (msg));
g_object_unref (msg);
}
/**
* GObject class initialization function. This function boils down to:
* - Setting up the parent class.
* - Set dispose, property get/set.
* - Install properties.
*/
static void
response_sink_class_init (ResponseSinkClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ThreadClass *thread_class = THREAD_CLASS (klass);
if (response_sink_parent_class == NULL)
response_sink_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = response_sink_dispose;
object_class->get_property = response_sink_get_property;
object_class->set_property = response_sink_set_property;
thread_class->thread_run = response_sink_thread;
thread_class->thread_unblock = response_sink_unblock;
obj_properties [PROP_IN_QUEUE] =
g_param_spec_object ("in-queue",
"MessageQueue",
"Input MessageQueue.",
G_TYPE_OBJECT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
/**
* Boilerplate code to register functions with the SinkInterface.
*/
static void
response_sink_sink_interface_init (gpointer g_iface)
{
SinkInterface *sink_interface = (SinkInterface*)g_iface;
sink_interface->enqueue = response_sink_enqueue;
}
/**
*/
ResponseSink*
response_sink_new ()
{
MessageQueue *in_queue = message_queue_new ();
return RESPONSE_SINK (g_object_new (TYPE_RESPONSE_SINK,
"in-queue", in_queue,
NULL));
}
ssize_t
response_sink_process_response (Tpm2Response *response)
{
ssize_t written = 0;
guint32 size = tpm2_response_get_size (response);
guint8 *buffer = tpm2_response_get_buffer (response);
Connection *connection = tpm2_response_get_connection (response);
GIOStream *iostream = connection_get_iostream (connection);
GOutputStream *ostream = g_io_stream_get_output_stream (iostream);
g_debug ("%s: writing 0x%x bytes", __func__, size);
g_debug_bytes (buffer, size, 16, 4);
written = write_all (ostream, buffer, size);
g_object_unref (connection);
return written;
}
gboolean
response_sink_process_control (ResponseSink *sink,
ControlMessage *msg)
{
ControlCode code = control_message_get_code (msg);
UNUSED_PARAM (sink);
g_debug ("%s", __func__);
switch (code) {
case CHECK_CANCEL:
g_debug ("%s: Received CHECK_CANCEL control code, terminating.",
__func__);
return FALSE;
case CONNECTION_REMOVED:
g_debug ("%s: Received CONNECTION_REMOVED message, nothing to do.",
__func__);
return TRUE;
default:
g_warning ("%s: Unknown control code: %d ... ignoring",
__func__, code);
return TRUE;
}
}
void*
response_sink_thread (void *data)
{
ResponseSink *sink = RESPONSE_SINK (data);
GObject *obj;
gboolean done = FALSE;
while (!done) {
g_debug ("%s: blocking on input queue", __func__);
obj = message_queue_dequeue (sink->in_queue);
if (IS_TPM2_RESPONSE (obj)) {
response_sink_process_response (TPM2_RESPONSE (obj));
} else if (IS_CONTROL_MESSAGE (obj)) {
gboolean ret =
response_sink_process_control (sink, CONTROL_MESSAGE (obj));
if (ret == FALSE) {
done = TRUE;
}
}
g_object_unref (obj);
}
return NULL;
}
tpm2-abrmd-3.0.0/src/response-sink.h 0000664 0000000 0000000 00000002753 14343407245 0017234 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef RESPONSE_SINK_H
#define RESPONSE_SINK_H
#include
#include
#include
#include "message-queue.h"
#include "thread.h"
G_BEGIN_DECLS
typedef struct _ResponseSinkClass {
ThreadClass parent;
} ResponseSinkClass;
/** DON'T TOUCH!
* Direct access to data in this structure his highly discouraged. Use the
* functions in this header to interact with this "object". If you're tempted
* to access the structure directly you probably need to update the API.
*/
typedef struct _ResponseSink {
Thread parent_instance;
MessageQueue *in_queue;
} ResponseSink;
#define TYPE_RESPONSE_SINK (response_sink_get_type ())
#define RESPONSE_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_RESPONSE_SINK, ResponseSink))
#define RESPONSE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_RESPONSE_SINK, ResponseSinkClass))
#define IS_RESPONSE_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_RESPONSE_SINK))
#define IS_RESPONSE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_RESPONSE_SINK))
#define RESPONSE_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_RESPONSE_SINK, ResponseSinkClass))
GType response_sink_get_type (void);
ResponseSink* response_sink_new (void);
G_END_DECLS
#endif /* RESPONSE_SINK_H */
tpm2-abrmd-3.0.0/src/session-entry-state-enum.c 0000664 0000000 0000000 00000003350 14343407245 0021323 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#include "session-entry-state-enum.h"
char*
session_entry_state_to_str (SessionEntryStateEnum state)
{
switch (state) {
case SESSION_ENTRY_LOADED:
return "loaded";
case SESSION_ENTRY_SAVED_CLIENT:
return "saved-client";
case SESSION_ENTRY_SAVED_CLIENT_CLOSED:
return "saved_client-closed";
case SESSION_ENTRY_SAVED_RM:
return "saved-rm";
default:
return NULL;
}
}
GType
session_entry_state_enum_get_type (void)
{
static gsize g_define_type_id = 0;
if (g_once_init_enter (&g_define_type_id)) {
static const GEnumValue my_enum_values[] = {
{
SESSION_ENTRY_LOADED,
"SessionEntry loaded in TPM",
"Loaded",
},
{
SESSION_ENTRY_SAVED_RM,
"SessionEntry populated with latest context saved by RM",
"SavedRM"
},
{
SESSION_ENTRY_SAVED_CLIENT,
"SessionEntry for context saved by the client",
"SavedClient"
},
{
SESSION_ENTRY_SAVED_CLIENT_CLOSED,
"SesssionEntry for context saved by client over a connection "
"that has been closed",
"SavedClientClosed",
},
{ 0, NULL, NULL }
};
GType session_entry_state_enum_type =
g_enum_register_static ("SessionEntryStateEnum", my_enum_values);
g_once_init_leave (&g_define_type_id,
session_entry_state_enum_type);
}
return g_define_type_id;
}
tpm2-abrmd-3.0.0/src/session-entry-state-enum.h 0000664 0000000 0000000 00000003442 14343407245 0021332 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef SESSION_ENTRY_STATE_ENUM_H
#define SESSION_ENTRY_STATE_ENUM_H
#include
G_BEGIN_DECLS
/*
* These define the possible states that a SessionEntry object may be in. The
* states are defined by some previous action performed over the connection
* that created the session / SessionEntry object.
* - LOADED: The session is currently loaded in the TPM2 device. All
* SessionEntry objects are in this state when they are first created.
* - SESSION_ENTRY_SAVED_RM: A SessionEntry transitions to this state when
* the session is saved by the daemon.
* - SESSION_ENTRY_SAVED_CLIENT: A SessionEntry transitions to this state when
* the client saves the session context by way of the TPM_CC_ContextSave
* command. Once a SessionEntry is in this state it must be explicitly
* loaded by a client before it can be used again.
* - SESSION_ETNRY_SAVED_CLIENT_CLOSED: A SessionEntry transitions to this
* state from the SESSION_ENTRY_SAVED_CLIENT state when the connection used
* to create and save the session is closed. A session previously saved by
* the client will not be flushed by the ResourceManager which allows other
* connections to claim the session but only if they're in posession of the
* context blob.
*/
typedef enum SESSION_ENTRY_STATE {
SESSION_ENTRY_LOADED,
SESSION_ENTRY_SAVED_RM,
SESSION_ENTRY_SAVED_CLIENT,
SESSION_ENTRY_SAVED_CLIENT_CLOSED,
} SessionEntryStateEnum;
#define TYPE_SESSION_ENTRY_STATE_ENUM (session_entry_state_enum_get_type ())
GType session_entry_state_enum_get_type (void);
char* session_entry_state_to_str (SessionEntryStateEnum state);
G_END_DECLS
#endif /* SESSION_ENTRY_STATE_ENUM_H */
tpm2-abrmd-3.0.0/src/session-entry.c 0000664 0000000 0000000 00000025276 14343407245 0017256 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include "tpm2-header.h"
#include "util.h"
#include "session-entry.h"
G_DEFINE_TYPE (SessionEntry, session_entry, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_CONNECTION,
PROP_CONTEXT,
PROP_HANDLE,
PROP_STATE,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/*
* GObject property getter.
*/
static void
session_entry_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
SessionEntry *self = SESSION_ENTRY (object);
switch (property_id) {
case PROP_CONNECTION:
g_value_set_pointer (value, self->connection);
break;
case PROP_CONTEXT:
g_value_set_pointer (value, &self->context);
break;
case PROP_HANDLE:
g_value_set_uint (value, session_entry_get_handle (self));
break;
case PROP_STATE:
g_value_set_enum (value, self->state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
/*
* GObject property setter.
*/
static void
session_entry_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
SessionEntry *self = SESSION_ENTRY (object);
switch (property_id) {
case PROP_CONNECTION:
self->connection = CONNECTION (g_value_get_pointer (value));
g_object_ref (self->connection);
break;
case PROP_CONTEXT:
g_error ("Cannot set context property.");
break;
case PROP_HANDLE:
self->handle = (TPM2_HANDLE)g_value_get_uint (value);
break;
case PROP_STATE:
self->state = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
/*
* G_DEFINE_TYPE requires an instance init even though we don't use it.
*/
static void
session_entry_init (SessionEntry *entry)
{
UNUSED_PARAM(entry);
/* noop */
}
/*
* Deallocate all associated resources. All are static so we just chain
* up to the parent like a good GObject.
*/
static void
session_entry_dispose (GObject *object)
{
SessionEntry *entry = SESSION_ENTRY (object);
g_debug ("%s", __func__);
g_clear_object (&entry->connection);
G_OBJECT_CLASS (session_entry_parent_class)->dispose (object);
}
/*
* Class initialization function. Register function pointers and properties.
*/
static void
session_entry_class_init (SessionEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (session_entry_parent_class == NULL)
session_entry_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = session_entry_dispose;
object_class->get_property = session_entry_get_property;
object_class->set_property = session_entry_set_property;
obj_properties [PROP_CONNECTION] =
g_param_spec_pointer ("connection",
"Connection",
"Associated Connection.",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_CONTEXT] =
g_param_spec_pointer ("context",
"TPMS_CONTEXT",
"Context blob from TPM.",
G_PARAM_READABLE);
obj_properties [PROP_HANDLE] =
g_param_spec_uint ("handle",
"TPM2_HANDLE",
"Handle from TPM.",
0,
UINT32_MAX,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_STATE] =
g_param_spec_enum ("state",
"SessionEntryStateEnum",
"State of SessionEntry",
TYPE_SESSION_ENTRY_STATE_ENUM,
SESSION_ENTRY_SAVED_RM,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
/*
* Instance constructor.
*/
SessionEntry*
session_entry_new (Connection *connection,
TPM2_HANDLE handle)
{
g_debug ("%s", __func__);
return SESSION_ENTRY (g_object_new (TYPE_SESSION_ENTRY,
"connection", connection,
"handle", handle,
NULL));
}
/*
* Access the 'context' member.
* NOTE: This directly exposes memory from an object instance. The caller
* must be sure to hold a reference to this object to keep it from being
* garbage collected while the caller is accessing the context structure.
* Further this object provides no thread safety ... yet.
*/
size_buf_t*
session_entry_get_context (SessionEntry *entry)
{
return &entry->context;
}
size_buf_t*
session_entry_get_context_client (SessionEntry *entry)
{
return &entry->context_client;
}
/*
* Access the Connection associated with this SessionEntry. The reference
* count for the Connection is incremented and must be decremented by the
* caller.
*/
Connection*
session_entry_get_connection (SessionEntry *entry)
{
if (entry->connection) {
g_object_ref (entry->connection);
}
return entry->connection;
}
/*
* Accessor for the savedHandle member of the associated context.
*/
TPM2_HANDLE
session_entry_get_handle (SessionEntry *entry)
{
g_assert_nonnull (entry);
return entry->handle;
}
/*
* Simple accessor to the state of the SessionEntry.
*/
SessionEntryStateEnum
session_entry_get_state (SessionEntry *entry)
{
return entry->state;
}
/*
* This function allows the caller to set the state of the SessionEntry. It
* also ensures that if the SessionEntry is put into the 'SAVED_CLIENT_CLOSED'
* state that the connection field is clear / NULL.
*/
void
session_entry_set_state (SessionEntry *entry,
SessionEntryStateEnum state)
{
assert (entry != NULL);
if (state == SESSION_ENTRY_SAVED_CLIENT_CLOSED) {
g_clear_object (&entry->connection);
}
entry->state = state;
}
/*
* Set the contents of the 'context' blob. This blob holds the TPMS_CONTEXT
* in its marshalled form (ready to be sent to the TPM in the body of a
* ContextLoad command). We also copy this same blob to the 'context_client'
* blob (the TPMS_CONTEXT that we expose to clients) if it has not yet been
* initialized.
*/
void
session_entry_set_context (SessionEntry *entry,
uint8_t *buf,
size_t size)
{
assert (entry != NULL && buf != NULL && size <= SIZE_BUF_MAX);
memcpy (entry->context.buf, buf, size);
entry->context.size = size;
if (entry->context_client.size == 0) {
memcpy (entry->context_client.buf, buf, size);
entry->context_client.size = size;
}
}
/*
* When the connection is set the previous connection, if there was one, must
* have its reference count decremented and the internal pointer NULLed.
*/
void
session_entry_set_connection (SessionEntry *entry,
Connection *connection)
{
g_object_ref (connection);
g_clear_object (&entry->connection);
entry->connection = connection;
}
/*
* This function is used with the g_list_find_custom function to find
* session_entry_t's in the list with a given handle. The first parameter
* is a reference to an entry in the list. The second is a reference to the
* handle we're looking for.
*/
gint
session_entry_compare (gconstpointer a,
gconstpointer b)
{
if (a == NULL || b == NULL) {
g_error ("session_entry_compare_on_handle received NULL parameter");
}
SessionEntry *entry_a = SESSION_ENTRY (a);
SessionEntry *entry_b = SESSION_ENTRY (b);
if (entry_a < entry_b) {
return -1;
} else if (entry_a > entry_b) {
return 1;
} else {
return 0;
}
}
/*
* GCompareFunc to search list of session_entry_t structures for a match on
* the connection object.
*/
gint
session_entry_compare_on_connection (gconstpointer a,
gconstpointer b)
{
gint ret;
if (a == NULL || b == NULL) {
g_error ("session_entry_compare_on_connection received NULL parameter");
}
SessionEntry *entry_a = SESSION_ENTRY (a);
Connection *connection_param = CONNECTION (b);
Connection *connection_entry = session_entry_get_connection (entry_a);
if (connection_entry < connection_param) {
ret = -1;
} else if (connection_entry > connection_param) {
ret = 1;
} else {
ret = 0;
}
g_object_unref (connection_entry);
return ret;
}
/*
* GCompareFunc used to compare SessionEntry objects on their handle members.
* This was taken directly from the SessionEntry code. My abstractions are
* leaking and this is a sign that the objects involved in session handling
* need to be refactored.
*/
gint
session_entry_compare_on_handle (gconstpointer a,
gconstpointer b)
{
if (a == NULL || b == NULL) {
g_error ("session_entry_compare_on_handle received NULL parameter");
}
SessionEntry *entry_a = SESSION_ENTRY (a);
TPM2_HANDLE handle_a = session_entry_get_handle (entry_a);
TPM2_HANDLE handle_b = *(TPM2_HANDLE*)b;
if (handle_a < handle_b) {
return -1;
} else if (handle_a > handle_b) {
return 1;
} else {
return 0;
}
}
void
session_entry_abandon (SessionEntry *entry)
{
g_clear_object (&entry->connection);
entry->state = SESSION_ENTRY_SAVED_CLIENT_CLOSED;
}
/*
* This function is used to compare the context_client field the TPMS_CONTEXT
* provided by the caller. The result of this function is byte-for-byte
* comparison between the two context structures. Both the context_client and
* the provided context structure are marshalled to their network
* representation before comparison to eliminate possible errors caused by
* comparing structure padding.
*/
gint
session_entry_compare_on_context_client (SessionEntry *entry,
uint8_t *buf,
size_t size)
{
size_buf_t *size_buf = NULL;
g_assert (size <= SIZE_BUF_MAX);
size_buf = session_entry_get_context_client (entry);
return memcmp (size_buf->buf, buf, size);
}
tpm2-abrmd-3.0.0/src/session-entry.h 0000664 0000000 0000000 00000006126 14343407245 0017254 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef SESSION_ENTRY_H
#define SESSION_ENTRY_H
#include
#include
#include
#include "connection.h"
#include "session-entry-state-enum.h"
G_BEGIN_DECLS
#define SIZE_BUF_MAX sizeof (TPMS_CONTEXT)
typedef struct size_buf {
size_t size;
uint8_t buf [SIZE_BUF_MAX];
} size_buf_t;
typedef struct _SessionEntryClass {
GObjectClass parent;
} SessionEntryClass;
typedef struct _SessionEntry {
GObject parent_instance;
Connection *connection;
SessionEntryStateEnum state;
TPM2_HANDLE handle;
size_buf_t context;
size_buf_t context_client;
} SessionEntry;
#define TYPE_SESSION_ENTRY (session_entry_get_type ())
#define SESSION_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SESSION_ENTRY, SessionEntry))
#define SESSION_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_SESSION_ENTRY, SessionEntryClass))
#define IS_SESSION_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SESSION_ENTRY))
#define IS_SESSION_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_SESSION_ENTRY))
#define SESSION_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_SESSION_ENTRY, SessionEntryClass))
GType session_entry_get_type (void);
SessionEntry* session_entry_new (Connection *connection,
TPM2_HANDLE handle);
size_buf_t* session_entry_get_context_client (SessionEntry *entry);
Connection* session_entry_get_connection (SessionEntry *entry);
TPM2_HANDLE session_entry_get_handle (SessionEntry *entry);
size_buf_t* session_entry_get_context (SessionEntry *entry);
void session_entry_set_context (SessionEntry *entry,
uint8_t *buf,
size_t size);
SessionEntryStateEnum session_entry_get_state (SessionEntry *entry);
void session_entry_set_connection (SessionEntry *entry,
Connection *connection);
void session_entry_set_state (SessionEntry *entry,
SessionEntryStateEnum state);
gint session_entry_compare (gconstpointer a,
gconstpointer b);
gint session_entry_compare_on_connection (gconstpointer a,
gconstpointer b);
gint session_entry_compare_on_handle (gconstpointer a,
gconstpointer b);
gint session_entry_compare_on_context_client (SessionEntry *entry,
uint8_t *buf,
size_t size);
void session_entry_abandon (SessionEntry *entry);
G_END_DECLS
#endif /* SESSION_ENTRY_H */
tpm2-abrmd-3.0.0/src/session-list.c 0000664 0000000 0000000 00000036245 14343407245 0017066 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include "util.h"
#include "session-list.h"
G_DEFINE_TYPE (SessionList, session_list, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_MAX_ABANDONED,
PROP_MAX_PER_CONNECTION,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/*
* Property getter.
*/
static void
session_list_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
SessionList *list = SESSION_LIST (object);
switch (property_id) {
case PROP_MAX_ABANDONED:
g_value_set_uint (value, list->max_abandoned);
break;
case PROP_MAX_PER_CONNECTION:
g_value_set_uint (value, list->max_per_connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/*
* Property setter.
*/
static void
session_list_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
SessionList *list = SESSION_LIST (object);
switch (property_id) {
case PROP_MAX_ABANDONED:
list->max_abandoned = g_value_get_uint (value);
break;
case PROP_MAX_PER_CONNECTION:
list->max_per_connection = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/*
* Initialize object.
* GQueue for 'abandoned_queue' must be explicitly created.
* GList for 'session_entry_list' does not.
*/
static void
session_list_init (SessionList *list)
{
g_debug ("session_list_init");
list->abandoned_queue = g_queue_new ();
list->session_entry_list = NULL;
}
/*
* GObject dispose function: unref all SessionEntry objects in the internal
* GSList and unref the list itself. NULL the pointer to the internal list
* as well.
*/
static void
session_list_dispose (GObject *object)
{
SessionList *self = SESSION_LIST (object);
g_debug ("%s: SessionList with %" PRIu32 " entries", __func__,
g_list_length (self->session_entry_list));
g_queue_free (self->abandoned_queue);
self->abandoned_queue = NULL;
g_list_free_full (self->session_entry_list, g_object_unref);
self->session_entry_list = NULL;
G_OBJECT_CLASS (session_list_parent_class)->dispose (object);
}
/*
* Deallocate all associated resources.
*/
static void
session_list_finalize (GObject *object)
{
SessionList *self = SESSION_LIST (object);
g_debug ("%s: SessionList with %" PRIu32 " entries", __func__,
g_list_length (self->session_entry_list));
g_list_free_full (self->session_entry_list, g_object_unref);
G_OBJECT_CLASS (session_list_parent_class)->finalize (object);
}
/*
* boiler-plate GObject class init function. Registers function pointers
* and properties.
*/
static void
session_list_class_init (SessionListClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (session_list_parent_class == NULL)
session_list_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = session_list_dispose;
object_class->finalize = session_list_finalize;
object_class->get_property = session_list_get_property;
object_class->set_property = session_list_set_property;
obj_properties [PROP_MAX_ABANDONED] =
g_param_spec_uint ("max-abandoned",
"max abandoned sessions",
"maximum number of entries permitted in the abandoned state",
0,
SESSION_LIST_MAX_ABANDONED_MAX,
SESSION_LIST_MAX_ABANDONED_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_MAX_PER_CONNECTION] =
g_param_spec_uint ("max-per-connection",
"max entries per connection",
"maximum number of entries permitted for each connection",
0,
MAX_ENTRIES_MAX,
MAX_ENTRIES_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
/*
* Instance initialization function. Create the resources the SessionList needs
* and create the instance.
*/
SessionList*
session_list_new (guint max_per_conn,
guint max_abandoned)
{
g_debug ("session_list_new with max-per-connection: 0x%x", max_per_conn);
return SESSION_LIST (g_object_new (TYPE_SESSION_LIST,
"max-abandoned", max_abandoned,
"max-per-connection", max_per_conn,
NULL));
}
/*
* Insert GObject into the session list. We take a reference to the object
* before we insert the object. When it is removed or if the SessionList
* object is destroyed the object will be unref'd.
*/
gboolean
session_list_insert (SessionList *list,
SessionEntry *entry)
{
if (list == NULL || entry == NULL) {
g_error ("session_list_insert passed NULL parameter");
}
if (session_list_is_full (list, entry->connection)) {
g_warning ("%s: max_per_connection of %u exceeded", __func__,
list->max_per_connection);
return FALSE;
}
g_object_ref (entry);
list->session_entry_list = g_list_append (list->session_entry_list,
entry);
return TRUE;
}
static gboolean
session_list_remove_custom (SessionList *list,
gconstpointer data,
GCompareFunc func)
{
GList *list_entry;
SessionEntry *entry_data;
list_entry = g_list_find_custom (list->session_entry_list, data, func);
if (list_entry == NULL) {
return FALSE;
}
entry_data = SESSION_ENTRY (list_entry->data);
list->session_entry_list = g_list_remove_link (list->session_entry_list,
list_entry);
g_object_unref (entry_data);
return TRUE;
}
/*
* Remove the entry from the GList. The SessionList assumes that since the
* entry is in the container it must hold a reference to the object and so
* upon successful removal
* Returns TRUE on success, FALSE on failure.
*/
gboolean
session_list_remove_handle (SessionList *list,
TPM2_HANDLE handle)
{
return session_list_remove_custom (list,
&handle,
session_entry_compare_on_handle);
}
/*
* Returns TRUE on success, FALSE on failure.
*/
gboolean
session_list_remove_connection (SessionList *list,
Connection *connection)
{
return session_list_remove_custom (list,
connection,
session_entry_compare_on_connection);
}
/*
* This function is a thin wrapper around the g_list_remove function. Pass it
* a SessionEntry. It will find it in the list and remove the associated entry
* and then unref it (to account for the SessionList no longer holding a
* reference).
*/
void
session_list_remove (SessionList *list,
SessionEntry *entry)
{
g_debug ("%s", __func__);
list->session_entry_list = g_list_remove (list->session_entry_list, entry);
g_object_unref (entry);
}
/*
* This is a lookup function to find an entry in the SessionList given
* handle. This function increases the reference count on the
* SessionEntry returned. The caller must decrement the reference count
* when it is done with the entry.
*/
SessionEntry*
session_list_lookup_handle (SessionList *list,
TPM2_HANDLE handle)
{
GList *list_entry;
list_entry = g_list_find_custom (list->session_entry_list,
&handle,
session_entry_compare_on_handle);
if (list_entry != NULL) {
g_object_ref (list_entry->data);
return SESSION_ENTRY (list_entry->data);
} else {
return NULL;
}
}
/*
* This type is used as a container for passing a buffer and its size to the
* 'session_list_compare_context' callback. This callback must have the
* prototype defined as 'GCompareFunc' which forces us to pass only one
* arbitrary parameter.
*/
typedef struct size_buf_ptr {
uint8_t *buf;
size_t size;
} size_buf_ptr_t;
static gint
session_list_compare_context (gconstpointer a,
gconstpointer b)
{
SessionEntry *entry = SESSION_ENTRY (a);
size_buf_ptr_t *size_buf_ptr = (size_buf_ptr_t*)b;
return session_entry_compare_on_context_client (entry,
size_buf_ptr->buf,
size_buf_ptr->size);
}
SessionEntry*
session_list_lookup_context_client (SessionList *list,
uint8_t *buf,
size_t size)
{
GList *list_entry;
size_buf_ptr_t size_buf_ptr = {
.size = size,
.buf = buf,
};
list_entry = g_list_find_custom (list->session_entry_list,
&size_buf_ptr,
session_list_compare_context);
if (list_entry != NULL) {
g_object_ref (list_entry->data);
return SESSION_ENTRY (list_entry->data);
} else {
return NULL;
}
}
/*
* Simple wrapper around the function that reports the number of entries in
* the hash table.
*/
guint
session_list_size (SessionList *list)
{
guint ret;
ret = g_list_length (list->session_entry_list);
return ret;
}
/*
* Structure used to hold data needed to count SessionEntry objects associated
* with a given connection.
*/
typedef struct {
Connection *connection;
size_t count;
} connection_count_data_t;
/*
* Callback function used to count the number of SessionEntry objects in the
* list associated with a given connection.
*/
static void
session_list_connection_counter (gpointer data,
gpointer user_data)
{
SessionEntry *entry = SESSION_ENTRY (data);
Connection *conn = session_entry_get_connection (entry);
connection_count_data_t *count_data = (connection_count_data_t*)user_data;
if (count_data->connection == conn) {
++count_data->count;
}
g_clear_object (&conn);
}
/*
* Returns the number of entries associated with the provided connection.
*/
size_t
session_list_connection_count (SessionList *list,
Connection *connection)
{
connection_count_data_t count_data = {
.connection = connection,
.count = 0,
};
session_list_foreach (list, session_list_connection_counter, &count_data);
return count_data.count;
}
/*
* Return false if the number of entries in the list is greater than or equal
* to max_per_connection.
*/
gboolean
session_list_is_full (SessionList *session_list,
Connection *connection)
{
size_t session_count;
gboolean ret;
session_count = session_list_connection_count (session_list,
connection);
if (session_count >= session_list->max_per_connection) {
g_info ("%s: Connection has exceeded session limit", __func__);
ret = TRUE;
} else {
ret= FALSE;
}
return ret;
}
/*
*/
void
session_list_foreach (SessionList *list,
GFunc func,
gpointer user_data)
{
g_list_foreach (list->session_entry_list,
func,
user_data);
}
/*
* Find the associated SessionEntry in the list.
* Check that the SessionEntry has the same
*/
gboolean
session_list_abandon_handle (SessionList *list,
Connection *connection,
TPM2_HANDLE handle)
{
SessionEntry *entry = NULL;
entry = session_list_lookup_handle (list, handle);
if (entry == NULL) {
g_debug ("%s: Handle 0x%08" PRIx32 " doesn't exist in SessionList",
__func__, handle);
return FALSE;
}
if (session_entry_compare_on_connection (entry, connection)) {
g_warning ("%s: Connection attempted to abandon SessionEntry with "
"handle 0x%08" PRIx32, __func__, handle);
g_clear_object (&entry);
return FALSE;
}
session_entry_abandon (entry);
g_queue_push_head (list->abandoned_queue, entry);
g_clear_object (&entry);
return TRUE;
}
/*
* SessionEntry objects can be claimed when they're in two states:
* - If the SessionEntry has been abandoned by another connection it will be
* in the 'abandoned_queue'. In this case we remove it from the
* 'abandoned_queue', set the state to 'LOADED' and associate the provided
* connection with the object.
* - If the SessionEntry has been saved BY THE CLIENT then it will *not* be
* in the 'abandoned_queue'. In this case we find the SessionEntry in the
* 'session_entry_list' and change the connection.
*/
gboolean
session_list_claim (SessionList *list,
SessionEntry *entry,
Connection *connection)
{
GList *link = NULL;
link = g_queue_find (list->abandoned_queue, entry);
if (link != NULL) {
g_assert (link->data == entry);
g_debug ("%s: GQueue of abandoned sessions does not contain "
"SessionEntry", __func__);
session_entry_set_state (entry, SESSION_ENTRY_LOADED);
session_entry_set_connection (entry, connection);
g_queue_remove (list->abandoned_queue, link->data);
return TRUE;
}
link = g_list_find (list->session_entry_list, entry);
if (link != NULL) {
g_assert (link->data == entry);
g_debug ("%s: SessionEntry found in SessionList", __func__);
session_entry_set_state (entry, SESSION_ENTRY_LOADED);
session_entry_set_connection (entry, connection);
} else {
return FALSE;
}
return TRUE;
}
/*
* Remove oldest from abandoned queue and call the caller provided function
* on it.
*/
gboolean
session_list_prune_abandoned (SessionList *list,
PruneFunc func,
gpointer data)
{
SessionEntry *entry = NULL;
gboolean ret = FALSE;
if (g_queue_get_length (list->abandoned_queue) <= list->max_abandoned) {
g_debug ("%s: abandoned_queue has not exceeded 'max_abandoned', "
"nothing to do.", __func__);
return TRUE;
}
entry = g_queue_pop_tail (list->abandoned_queue);
if (entry == NULL) {
g_debug ("%s: Abandoned queue is empty.", __func__);
return TRUE;
}
g_object_ref (entry);
ret = func (entry, data);
g_clear_object (&entry);
return ret;
}
tpm2-abrmd-3.0.0/src/session-list.h 0000664 0000000 0000000 00000007740 14343407245 0017071 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef SESSION_LIST_H
#define SESSION_LIST_H
#include
#include
#include
#include
#include "connection.h"
#include "session-entry.h"
G_BEGIN_DECLS
#define SESSION_LIST_MAX_ABANDONED_MAX 4
#define SESSION_LIST_MAX_ABANDONED_DEFAULT SESSION_LIST_MAX_ABANDONED_MAX
#define SESSION_LIST_MAX_ENTRIES_DEFAULT 4
#define SESSION_LIST_MAX_ENTRIES_MAX 64
typedef gboolean (*PruneFunc) (SessionEntry *entry, gpointer data);
typedef struct _SessionListClass {
GObjectClass parent;
} SessionListClass;
typedef struct _SessionList {
GObject parent_instance;
GQueue *abandoned_queue;
guint max_abandoned;
guint max_per_connection;
GList *session_entry_list;
} SessionList;
#define TYPE_SESSION_LIST (session_list_get_type ())
#define SESSION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SESSION_LIST, SessionList))
#define SESSION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_SESSION_LIST, SessionListClass))
#define IS_SESSION_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SESSION_LIST))
#define IS_SESSION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_SESSION_LIST))
#define SESSION_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_SESSION_LIST, SessionListClass))
GType session_list_get_type (void);
SessionList* session_list_new (guint max_per_conn,
guint max_abandoned);
gboolean session_list_insert (SessionList *list,
SessionEntry *entry);
SessionEntry* session_list_lookup_handle (SessionList *list,
TPM2_HANDLE handle);
SessionEntry* session_list_lookup_context_client (SessionList *list,
uint8_t *buf,
size_t size);
gint session_list_remove_handle (SessionList *list,
TPM2_HANDLE handle);
gint session_list_remove_connection (SessionList *list,
Connection *connection);
void session_list_remove (SessionList *list,
SessionEntry *entry);
guint session_list_size (SessionList *list);
gboolean session_list_is_full (SessionList *list,
Connection *connection);
void session_list_prettyprint (SessionList *list);
void session_list_foreach (SessionList *list,
GFunc func,
gpointer user_data);
size_t session_list_connection_count (SessionList *list,
Connection *connection);
gboolean session_list_abandon_handle (SessionList *list,
Connection *connection,
TPM2_HANDLE handle);
gboolean session_list_claim (SessionList *list,
SessionEntry *entry,
Connection *connection);
gboolean session_list_prune_abandoned (SessionList *list,
PruneFunc func,
gpointer data);
G_END_DECLS
#endif /* SESSION_LIST_H */
tpm2-abrmd-3.0.0/src/sink-interface.c 0000664 0000000 0000000 00000001323 14343407245 0017321 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include "util.h"
#include "sink-interface.h"
G_DEFINE_INTERFACE (Sink, sink, G_TYPE_INVALID);
static void
sink_default_init (SinkInterface *iface)
{
UNUSED_PARAM(iface);
/* noop, required by G_DEFINE_INTERFACE */
}
/**
* boilerplate code to call enqueue function in class implementing the
* interface
*/
void
sink_enqueue (Sink *self,
GObject *obj)
{
SinkInterface *iface;
g_debug ("sink_enqueue");
g_return_if_fail (IS_SINK (self));
iface = SINK_GET_INTERFACE (self);
g_return_if_fail (iface->enqueue != NULL);
iface->enqueue (self, obj);
}
tpm2-abrmd-3.0.0/src/sink-interface.h 0000664 0000000 0000000 00000002055 14343407245 0017331 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef SINK_INTERFACE_H
#define SINK_INTERFACE_H
#include
G_BEGIN_DECLS
#define TYPE_SINK (sink_get_type ())
#define SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SINK, Sink))
#define IS_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SINK))
#define SINK_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), TYPE_SINK, SinkInterface))
typedef struct _Sink Sink;
typedef struct _SinkInterface SinkInterface;
/* types for function pointers defined by interface */
typedef void (*SinkEnqueue) (Sink *self,
GObject *obj);
struct _SinkInterface {
GTypeInterface parent;
SinkEnqueue enqueue;
};
GType sink_get_type (void);
void sink_enqueue (Sink *self,
GObject *obj);
G_END_DECLS
#endif
tpm2-abrmd-3.0.0/src/source-interface.c 0000664 0000000 0000000 00000001364 14343407245 0017662 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include "util.h"
#include "source-interface.h"
G_DEFINE_INTERFACE (Source, source, G_TYPE_INVALID);
static void
source_default_init (SourceInterface *iface)
{
UNUSED_PARAM(iface);
/* noop, required by G_DEFINE_INTERFACE */
}
/**
* boilerplate code to call enqueue function in class implementing the
* interface
*/
void
source_add_sink (Source *self,
Sink *sink)
{
SourceInterface *iface;
g_debug ("source_add_sink");
g_return_if_fail (IS_SOURCE (self));
iface = SOURCE_GET_INTERFACE (self);
g_return_if_fail (iface->add_sink != NULL);
iface->add_sink (self, sink);
}
tpm2-abrmd-3.0.0/src/source-interface.h 0000664 0000000 0000000 00000002240 14343407245 0017661 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef SOURCE_INTERFACE_H
#define SOURCE_INTERFACE_H
#include
#include "sink-interface.h"
G_BEGIN_DECLS
#define TYPE_SOURCE (source_get_type ())
#define SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SOURCE, Source))
#define IS_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SOURCE))
#define SOURCE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), TYPE_SOURCE, SourceInterface))
typedef struct _Source Source;
typedef struct _SourceInterface SourceInterface;
/* types for function pointers defined by interface */
typedef void (*SourceAddSink) (Source *self,
Sink *sink);
struct _SourceInterface {
GTypeInterface parent;
SourceAddSink add_sink;
};
GType source_get_type (void);
void source_add_sink (Source *self,
Sink *sink);
G_END_DECLS
#endif /* SOURCE_INTERFACE_H */
tpm2-abrmd-3.0.0/src/tabrmd-defaults.h 0000664 0000000 0000000 00000001417 14343407245 0017506 0 ustar 00root root 0000000 0000000 /*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright (c) 2019, Intel Corporation
*/
#ifndef TABRMD_DEFAULTS_H
#define TABRMD_DEFAULTS_H
#define TABRMD_CONNECTIONS_MAX_DEFAULT 27
#define TABRMD_CONNECTION_MAX 100
#define TABRMD_DBUS_NAME_DEFAULT "com.intel.tss2.Tabrmd"
#define TABRMD_DBUS_TYPE_DEFAULT G_BUS_TYPE_SYSTEM
#define TABRMD_DBUS_PATH "/com/intel/tss2/Tabrmd/Tcti"
#define TABRMD_DBUS_METHOD_CREATE_CONNECTION "CreateConnection"
#define TABRMD_DBUS_METHOD_CANCEL "Cancel"
#define TABRMD_ERROR tabrmd_error_quark ()
#define TABRMD_ENTROPY_SRC_DEFAULT "/dev/urandom"
#define TABRMD_SESSIONS_MAX_DEFAULT 4
#define TABRMD_SESSIONS_MAX 64
#define TABRMD_TCTI_CONF_DEFAULT "device:/dev/tpm0"
#define TABRMD_TRANSIENT_MAX_DEFAULT 27
#define TABRMD_TRANSIENT_MAX 100
#endif
tpm2-abrmd-3.0.0/src/tabrmd-error.c 0000664 0000000 0000000 00000002445 14343407245 0017025 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
#include
#include
#include "tabrmd.h"
static const GDBusErrorEntry tabrmd_error_entries[] = {
{
.error_code = TSS2_RESMGR_RC_GENERAL_FAILURE,
.dbus_error_name = "com.intel.tss2.Tabrmd.Error.General"
},
{
.error_code = TSS2_RESMGR_RC_INTERNAL_ERROR,
.dbus_error_name = "com.intel.tss2.Tabrmd.Error.Internal"
},
{ /* this one looks sketchy */
.error_code = TSS2_RESMGR_RC_SAPI_INIT,
.dbus_error_name = "com.intel.tss2.Tabrmd.Error.Init"
},
{
.error_code = TSS2_RESMGR_RC_NOT_IMPLEMENTED,
.dbus_error_name = "com.intel.tss2.Tabrmd.Error.NotImplemented"
},
{
.error_code = TSS2_RESMGR_RC_NOT_PERMITTED,
.dbus_error_name = "com.intel.tss2.Tabrmd.Error.NotPermitted",
},
};
/*
* Register mapping of error codes to dbus error names.
*/
GQuark
tabrmd_error_quark (void)
{
static volatile gsize quark_volatile = 0;
g_dbus_error_register_error_domain ("tabrmd-error",
&quark_volatile,
tabrmd_error_entries,
G_N_ELEMENTS (tabrmd_error_entries));
return (GQuark) quark_volatile;
}
tpm2-abrmd-3.0.0/src/tabrmd-init.c 0000664 0000000 0000000 00000020115 14343407245 0016631 0 ustar 00root root 0000000 0000000 #include
#include
#include
#include
#include
#include
#include
#include "tpm2.h"
#include "command-source.h"
#include "logging.h"
#include "ipc-frontend.h"
#include "ipc-frontend-dbus.h"
#include "random.h"
#include "resource-manager.h"
#include "response-sink.h"
#include "source-interface.h"
#include "tabrmd-init.h"
#include "tabrmd-options.h"
#include "tabrmd.h"
#include "util.h"
/*
* This is a simple function to do sanity checks before calling
* g_main_loop_quit.
*/
static void
main_loop_quit (GMainLoop *loop)
{
g_info ("main_loop_quit");
if (loop && g_main_loop_is_running (loop))
g_main_loop_quit (loop);
}
/*
* This is a very poorly named signal handling function. It is invoked in
* response to a Unix signal. It does one thing:
* - Shuts down the GMainLoop.
*/
static gboolean
signal_handler (gpointer user_data)
{
g_info ("handling signal");
main_loop_quit ((GMainLoop*)user_data);
return G_SOURCE_CONTINUE;
}
/*
* This function is a callback invoked by the IpcFrontend object
* when a 'disconnect' event occurs. It sets a flag in the gmain_data_t
* structure before killing off the gmain loop. This flag is checked by the
* 'main' function that started the gmain loop and, if set, an exit code
* is returned to indicate a failure.
* Currently the only known reasons for the IPC disconnecting are not
* recoverable.
*/
void
on_ipc_frontend_disconnect (IpcFrontend *ipc_frontend,
gmain_data_t *data)
{
data->ipc_disconnected = TRUE;
if (data->loop)
main_loop_quit (data->loop);
}
static void
thread_cleanup (Thread **thread)
{
thread_cancel (*thread);
thread_join (*thread);
g_clear_object (thread);
}
void
gmain_data_cleanup (gmain_data_t *data)
{
g_debug ("%s", __func__);
Thread* thread;
if (data->command_source != NULL) {
thread = THREAD (data->command_source);
thread_cleanup (&thread);
}
if (data->resource_manager != NULL) {
thread = THREAD (data->resource_manager);
thread_cleanup (&thread);
}
if (data->response_sink != NULL) {
thread = THREAD (data->response_sink);
thread_cleanup (&thread);
}
if (data->ipc_frontend != NULL) {
ipc_frontend_disconnect (data->ipc_frontend);
g_clear_object (&data->ipc_frontend);
}
if (data->random != NULL) {
g_clear_object (&data->random);
}
if (data->loop != NULL) {
main_loop_quit (data->loop);
}
if (data->tpm2) {
g_clear_object (&data->tpm2);
}
tabrmd_options_free(&data->options);
}
/*
* This function initializes and configures all of the long-lived objects
* in the tabrmd system. It is invoked on a thread separate from the main
* thread as a way to get the main thread listening for connections on
* DBus as quickly as possible. Any incoming DBus requests will block
* on the 'init_mutex' until this thread completes but they won't be
* timing etc. This function does X things:
* - Locks the init_mutex.
* - Registers a handler for UNIX signals for SIGINT and SIGTERM.
* - Seeds the RNG state from an entropy source.
* - Creates the ConnectionManager.
* - Creates the TCTI instance used by the Tab.
* - Creates an tpm2 and verify the current state of the TPM.
* - Creates and wires up the objects that make up the TPM command
* processing pipeline.
* - Starts all of the threads in the command processing pipeline.
* - Unlocks the init_mutex.
*/
gpointer
init_thread_func (gpointer user_data)
{
gmain_data_t *data = (gmain_data_t*)user_data;
gint ret;
TSS2_RC rc;
CommandAttrs *command_attrs;
ConnectionManager *connection_manager = NULL;
SessionList *session_list;
Tcti *tcti = NULL;
TSS2_TCTI_CONTEXT *tcti_ctx = NULL;
g_info ("init_thread_func start");
g_mutex_lock (&data->init_mutex);
/* Setup program signals */
if (g_unix_signal_add(SIGINT, signal_handler, data->loop) <= 0 ||
g_unix_signal_add(SIGTERM, signal_handler, data->loop) <= 0)
{
g_critical ("failed to setup signal handlers");
ret = EX_OSERR;
goto err_out;
}
data->random = random_new();
ret = random_seed_from_file (data->random, data->options.prng_seed_file);
if (ret != 0) {
g_critical ("failed to seed Random object from seed source: %s",
data->options.prng_seed_file);
ret = EX_OSERR;
goto err_out;
}
connection_manager = connection_manager_new(data->options.max_connections);
/* setup IpcFrontend */
data->ipc_frontend =
IPC_FRONTEND (ipc_frontend_dbus_new (data->options.bus,
data->options.dbus_name,
connection_manager,
data->options.max_transients,
data->random));
g_signal_connect (data->ipc_frontend,
"disconnected",
(GCallback) on_ipc_frontend_disconnect,
data);
ipc_frontend_connect (data->ipc_frontend,
&data->init_mutex);
rc = Tss2_TctiLdr_Initialize (data->options.tcti_conf, &tcti_ctx);
if (rc != TSS2_RC_SUCCESS || tcti_ctx == NULL) {
g_critical ("%s: failed to create TCTI with conf \"%s\", got RC: 0x%x",
__func__, data->options.tcti_conf, rc);
ret = EX_IOERR;
goto err_out;
}
tcti = tcti_new (tcti_ctx);
data->tpm2 = tpm2_new (tcti);
g_clear_object (&tcti);
rc = tpm2_init_tpm (data->tpm2);
if (rc != TSS2_RC_SUCCESS) {
ret = EX_UNAVAILABLE;
g_critical ("failed to initialize Tpm2: 0x%" PRIx32, rc);
goto err_out;
}
if (data->options.flush_all) {
tpm2_flush_all_context (data->tpm2);
}
/*
* Instantiate and the objects that make up the TPM command processing
* pipeline.
*/
command_attrs = command_attrs_new ();
ret = command_attrs_init_tpm (command_attrs, data->tpm2);
if (ret != 0) {
g_critical ("%s: failed to initialize CommandAttribute object", __func__);
ret = EX_UNAVAILABLE;
goto err_out;
}
data->command_source =
command_source_new (connection_manager, command_attrs);
g_object_unref (connection_manager);
session_list = session_list_new (data->options.max_sessions,
SESSION_LIST_MAX_ABANDONED_DEFAULT);
data->resource_manager = resource_manager_new (data->tpm2,
session_list);
g_clear_object (&session_list);
data->response_sink = response_sink_new ();
g_object_unref (command_attrs);
g_clear_object (&data->tpm2);
/*
* Wire up the TPM command processing pipeline. TPM command buffers
* flow from the CommandSource, to the Tab then finally back to the
* caller through the ResponseSink.
*/
source_add_sink (SOURCE (data->command_source),
SINK (data->resource_manager));
source_add_sink (SOURCE (data->resource_manager),
SINK (data->response_sink));
/*
* Start the TPM command processing pipeline.
*/
ret = thread_start (THREAD (data->command_source));
if (ret != 0) {
g_critical ("failed to start connection_source");
ret = EX_OSERR;
goto err_out;
}
ret = thread_start (THREAD (data->resource_manager));
if (ret != 0) {
g_critical ("failed to start ResourceManager: %s", strerror (errno));
ret = EX_OSERR;
goto err_out;
}
ret = thread_start (THREAD (data->response_sink));
if (ret != 0) {
g_critical ("failed to start response_source");
ret = EX_OSERR;
goto err_out;
}
g_mutex_unlock (&data->init_mutex);
g_info ("init_thread_func done");
return GINT_TO_POINTER (0);
err_out:
g_mutex_unlock (&data->init_mutex);
g_debug ("%s: calling gmain_data_cleanup", __func__);
gmain_data_cleanup (data);
return GINT_TO_POINTER (ret);
}
tpm2-abrmd-3.0.0/src/tabrmd-init.h 0000664 0000000 0000000 00000002077 14343407245 0016645 0 ustar 00root root 0000000 0000000 #ifndef TABRMD_INIT_H
#define TABRMD_INIT_H
#include
#include "tpm2.h"
#include "command-source.h"
#include "ipc-frontend.h"
#include "random.h"
#include "resource-manager.h"
#include "response-sink.h"
#include "tabrmd-options.h"
/*
* Structure to hold data that we pass to the gmain loop as 'user_data'.
* This data will be available to events from gmain including events from
* the DBus.
*/
typedef struct gmain_data {
tabrmd_options_t options;
GMainLoop *loop;
Tpm2 *tpm2;
ResourceManager *resource_manager;
CommandSource *command_source;
Random *random;
ResponseSink *response_sink;
GMutex init_mutex;
IpcFrontend *ipc_frontend;
gboolean ipc_disconnected;
} gmain_data_t;
gpointer
init_thread_func (gpointer user_data);
void
gmain_data_cleanup (gmain_data_t *data);
void
on_ipc_frontend_disconnect (IpcFrontend *ipc_frontend,
gmain_data_t *data);
#endif /* TABRMD_INIT_H */
tpm2-abrmd-3.0.0/src/tabrmd-options.c 0000664 0000000 0000000 00000014204 14343407245 0017363 0 ustar 00root root 0000000 0000000 /*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright (c) 2019, Intel Corporation
*/
#include
#include
#include
#include
#include "logging.h"
#include "tabrmd-options.h"
#include "util.h"
/* work around older glib versions missing this symbol */
#ifndef G_OPTION_FLAG_NONE
#define G_OPTION_FLAG_NONE 0
#endif
#define SET_STR_IF_NULL(var, value) \
do { \
var = var == NULL ? g_strdup(value) : var; \
g_assert(var); \
} while(0)
/*
* This is a GOptionArgFunc callback invoked from the GOption processor from
* the parse_opts function below. It will be called when the daemon is
* invoked with the -v/--version option. This will cause the daemon to
* display a version string and exit.
*/
gboolean
show_version (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
UNUSED_PARAM(option_name);
UNUSED_PARAM(value);
UNUSED_PARAM(data);
UNUSED_PARAM(error);
g_print ("tpm2-abrmd version %s\n", VERSION);
exit (0);
}
/**
* Frees internal memory associated with a tabrmd_options_t struct.
* @param opts
* The options to free, note it doesn't free opts itself.
*/
void
tabrmd_options_free(tabrmd_options_t *opts)
{
g_assert(opts);
g_clear_pointer(&opts->dbus_name, g_free);
g_clear_pointer(&opts->prng_seed_file, g_free);
g_clear_pointer(&opts->tcti_conf, g_free);
}
/**
* This function parses the parameter argument vector and populates the
* parameter 'options' structure with data needed to configure the tabrmd.
* We rely heavily on the GOption module here and we get our GOptionEntry
* array from two places:
* - The TctiOption module.
* - The local application options specified here.
* Then we do a bit of sanity checking and setting up default values if
* none were supplied.
*/
gboolean
parse_opts (gint argc,
gchar *argv[],
tabrmd_options_t *options)
{
gchar *logger_name = NULL;
GOptionContext *ctx;
GError *err = NULL;
gboolean session_bus = FALSE;
GOptionEntry entries[] = {
{ "dbus-name", 'n', 0, G_OPTION_ARG_STRING, &options->dbus_name,
"Name for daemon to \"own\" on the D-Bus",
TABRMD_DBUS_NAME_DEFAULT },
{ "logger", 'l', 0, G_OPTION_ARG_STRING, &logger_name,
"The name of desired logger, stdout is default.", "[stdout|syslog]"},
{ "session", 's', 0, G_OPTION_ARG_NONE, &session_bus,
"Connect to the session bus (system bus is default).", NULL },
{ "flush-all", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
&options->flush_all,
"Flush all objects and sessions from TPM on startup.", NULL },
{ "max-connections", 'm', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT,
&options->max_connections, "Maximum number of client connections.",
NULL },
{ "max-sessions", 'e', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT,
&options->max_sessions,
"Maximum number of sessions per connection.", NULL },
{ "max-transients", 'r', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT,
&options->max_transients,
"Maximum number of loaded transient objects per client.", NULL },
{ "prng-seed-file", 'g', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING,
&options->prng_seed_file, "File to read seed value for PRNG",
options->prng_seed_file },
{ "version", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
show_version, "Show version string", NULL },
{ "allow-root", 'o', 0, G_OPTION_ARG_NONE,
&options->allow_root,
"Allow the daemon to run as root, which is not recommended", NULL },
{
.long_name = "tcti",
.short_name = 't',
.flags = G_OPTION_FLAG_NONE,
.arg = G_OPTION_ARG_STRING,
.arg_data = &options->tcti_conf,
.description = "TCTI configuration string. See tpm2-abrmd (8) for search rules.",
.arg_description = "tcti-conf",
},
{ NULL, '\0', 0, 0, NULL, NULL, NULL },
};
g_debug ("tcti_conf before: \"%s\"", options->tcti_conf);
ctx = g_option_context_new (" - TPM2 software stack Access Broker Daemon (tabrmd)");
g_option_context_add_main_entries (ctx, entries, NULL);
if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
g_critical ("Failed to parse options: %s", err->message);
return FALSE;
}
g_option_context_free (ctx);
/*
* Set unset STRING options to defaults, we do this so we can free allocated
* string options with gfree, having a mix of const and allocated ptr's
* causes leaks
*/
SET_STR_IF_NULL(options->dbus_name, TABRMD_DBUS_NAME_DEFAULT);
SET_STR_IF_NULL(options->prng_seed_file, TABRMD_ENTROPY_SRC_DEFAULT);
SET_STR_IF_NULL(options->tcti_conf, TABRMD_TCTI_CONF_DEFAULT);
SET_STR_IF_NULL(logger_name, "stdout");
/* select the bus type, default to G_BUS_TYPE_SESSION */
options->bus = session_bus ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM;
gint ret = set_logger (logger_name);
if (ret == -1) {
g_critical ("Unknown logger: %s, try --help\n", logger_name);
g_free(logger_name);
goto error;
}
g_free(logger_name);
if (options->max_connections < 1 ||
options->max_connections > TABRMD_CONNECTION_MAX)
{
g_critical ("maximum number of connections must be between 1 "
"and %d", TABRMD_CONNECTION_MAX);
goto error;
}
if (options->max_sessions < 1 ||
options->max_sessions > TABRMD_SESSIONS_MAX_DEFAULT)
{
g_critical ("max-sessions must be between 1 and %d",
TABRMD_SESSIONS_MAX_DEFAULT);
goto error;
}
if (options->max_transients < 1 ||
options->max_transients > TABRMD_TRANSIENT_MAX)
{
g_critical ("max-trans-obj parameter must be between 1 and %d",
TABRMD_TRANSIENT_MAX);
goto error;
}
g_debug ("tcti_conf after: \"%s\"", options->tcti_conf);
return TRUE;
error:
tabrmd_options_free(options);
return FALSE;
}
tpm2-abrmd-3.0.0/src/tabrmd-options.h 0000664 0000000 0000000 00000002122 14343407245 0017364 0 ustar 00root root 0000000 0000000 /*
* SPDX-License-Identifier: BSD-2-Clause
* Copyright (c) 2019, Intel Corporation
*/
#ifndef TABRMD_OPTIONS_H
#define TABRMD_OPTIONS_H
#include
#include "tabrmd-defaults.h"
#define TABRMD_OPTIONS_INIT_DEFAULT { \
.bus = (GBusType)TABRMD_DBUS_TYPE_DEFAULT, \
.flush_all = FALSE, \
.max_connections = TABRMD_CONNECTIONS_MAX_DEFAULT, \
.max_transients = TABRMD_TRANSIENT_MAX_DEFAULT, \
.max_sessions = TABRMD_SESSIONS_MAX_DEFAULT, \
.dbus_name = NULL, \
.prng_seed_file = NULL, \
.allow_root = FALSE, \
.tcti_conf = NULL, \
}
typedef struct tabrmd_options {
GBusType bus;
gboolean flush_all;
guint max_connections;
guint max_transients;
guint max_sessions;
gchar *dbus_name;
gchar *prng_seed_file;
gboolean allow_root;
gchar *tcti_conf;
} tabrmd_options_t;
gboolean
parse_opts (gint argc,
gchar *argv[],
tabrmd_options_t *options);
void
tabrmd_options_free(tabrmd_options_t *opts);
#endif /* TABRMD_OPTIONS_H */
tpm2-abrmd-3.0.0/src/tabrmd.c 0000664 0000000 0000000 00000004430 14343407245 0015672 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018 - 2018, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include "tabrmd-options.h"
#include "tabrmd-init.h"
#include "tabrmd.h"
#include "util.h"
/*
* This is the entry point for the TPM2 Access Broker and Resource Manager
* daemon. It is responsible for the top most initialization and
* coordination before blocking on the GMainLoop (g_main_loop_run):
* - Collects / parses command line options.
* - Creates the initialization thread and kicks it off.
* - Registers / owns a name on a DBus.
* - Blocks on the main loop.
* At this point all of the tabrmd processing is being done on other threads.
* When the daemon shutsdown (for any reason) we do cleanup here:
* - Join / cleanup the initialization thread.
* - Release the name on the DBus.
* - Cancel and join all of the threads started by the init thread.
* - Cleanup all of the objects created by the init thread.
*/
int
main (int argc, char *argv[])
{
gmain_data_t gmain_data = { .options = TABRMD_OPTIONS_INIT_DEFAULT };
GThread *init_thread;
gint ret;
g_info ("tabrmd startup");
if (!parse_opts (argc, argv, &gmain_data.options)) {
return 1;
}
if (geteuid() == 0 && ! gmain_data.options.allow_root) {
g_print ("Refusing to run as root. Pass --allow-root if you know what you are doing.\n");
ret = 1;
goto out;
}
g_mutex_init (&gmain_data.init_mutex);
gmain_data.loop = g_main_loop_new (NULL, FALSE);
/*
* Initialize program data on a separate thread. The main thread needs to
* get into the GMainLoop ASAP to acquire a dbus name and become
* responsive to clients.
*/
init_thread = g_thread_new (TABD_INIT_THREAD_NAME,
init_thread_func,
&gmain_data);
g_info ("entering g_main_loop");
g_main_loop_run (gmain_data.loop);
g_info ("g_main_loop_run done, cleaning up");
ret = GPOINTER_TO_INT (g_thread_join (init_thread));
if (ret == 0 && gmain_data.ipc_disconnected) {
ret = EX_IOERR;
}
out:
gmain_data_cleanup (&gmain_data);
return ret;
}
tpm2-abrmd-3.0.0/src/tabrmd.h 0000664 0000000 0000000 00000002665 14343407245 0015707 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef TSS2_TABD_H
#define TSS2_TABD_H
#include
#include
#include
#define TABD_INIT_THREAD_NAME "tss2-tabrmd_init-thread"
/* implementation specific RCs */
#define TSS2_RESMGR_RC_INTERNAL_ERROR (TSS2_RC)(TSS2_RESMGR_RC_LAYER | (1 << TSS2_LEVEL_IMPLEMENTATION_SPECIFIC_SHIFT))
#define TSS2_RESMGR_RC_SAPI_INIT (TSS2_RC)(TSS2_RESMGR_RC_LAYER | (2 << TSS2_LEVEL_IMPLEMENTATION_SPECIFIC_SHIFT))
#define TSS2_RESMGR_RC_OUT_OF_MEMORY (TSS2_RC)(TSS2_RESMGR_RC_LAYER | (3 << TSS2_LEVEL_IMPLEMENTATION_SPECIFIC_SHIFT))
/* RCs in the RESMGR layer */
#define TSS2_RESMGR_RC_BAD_VALUE (TSS2_RC)(TSS2_RESMGR_RC_LAYER | TSS2_BASE_RC_BAD_VALUE)
#define TSS2_RESMGR_RC_NOT_PERMITTED (TSS2_RC)(TSS2_RESMGR_RC_LAYER | TSS2_BASE_RC_NOT_PERMITTED)
#define TSS2_RESMGR_RC_NOT_IMPLEMENTED (TSS2_RC)(TSS2_RESMGR_RC_LAYER | TSS2_BASE_RC_NOT_IMPLEMENTED)
#define TSS2_RESMGR_RC_GENERAL_FAILURE (TSS2_RC)(TSS2_RESMGR_RC_LAYER | TSS2_BASE_RC_GENERAL_FAILURE)
#define TSS2_RESMGR_RC_OBJECT_MEMORY (TSS2_RC)(TSS2_RESMGR_RC_LAYER | TPM2_RC_OBJECT_MEMORY)
#define TSS2_RESMGR_RC_SESSION_MEMORY (TSS2_RC)(TSS2_RESMGR_RC_LAYER | TPM2_RC_SESSION_MEMORY)
GQuark tabrmd_error_quark (void);
TSS2_RC tss2_tcti_tabrmd_dump_trans_state (TSS2_TCTI_CONTEXT *tcti_context);
#endif /* TSS2_TABD_H */
tpm2-abrmd-3.0.0/src/tabrmd.xml 0000664 0000000 0000000 00000001376 14343407245 0016256 0 ustar 00root root 0000000 0000000
tpm2-abrmd-3.0.0/src/tcti-tabrmd-priv.h 0000664 0000000 0000000 00000012530 14343407245 0017616 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef TSS2TCTI_TABRMD_PRIV_H
#define TSS2TCTI_TABRMD_PRIV_H
#include
#include
#include
#include "tabrmd-defaults.h"
#include "tabrmd-generated.h"
#include "tpm2-header.h"
#include "util.h"
#define TSS2_TCTI_TABRMD_MAGIC 0x1c8e03ff00db0f92
#define TSS2_TCTI_TABRMD_VERSION 2
#define TSS2_TCTI_TABRMD_ID(context) \
((TSS2_TCTI_TABRMD_CONTEXT*)context)->id
#define TSS2_TCTI_TABRMD_IOSTREAM(context) \
G_IO_STREAM (TSS2_TCTI_TABRMD_SOCK_CONNECT (context))
#define TSS2_TCTI_TABRMD_PROXY(context) \
((TSS2_TCTI_TABRMD_CONTEXT*)context)->proxy
#define TSS2_TCTI_TABRMD_HEADER(context) \
((TSS2_TCTI_TABRMD_CONTEXT*)context)->header
#define TSS2_TCTI_TABRMD_STATE(context) \
((TSS2_TCTI_TABRMD_CONTEXT*)context)->state
/*
* Macros for accessing the internals of the I/O stream. These are helpers
* for getting at the underlying GSocket and raw fds that we need to
* implement polling etc.
*/
#define TSS2_TCTI_TABRMD_ISTREAM(context) \
g_io_stream_get_input_stream (TSS2_TCTI_TABRMD_IOSTREAM(context))
#define TSS2_TCTI_TABRMD_SOCK_CONNECT(context) \
((TSS2_TCTI_TABRMD_CONTEXT*)context)->sock_connect
#define TSS2_TCTI_TABRMD_SOCKET(context) \
g_socket_connection_get_socket (TSS2_TCTI_TABRMD_SOCK_CONNECT (context))
#define TSS2_TCTI_TABRMD_FD(context) \
g_socket_get_fd (TSS2_TCTI_TABRMD_SOCKET (context))
/*
* The elements in this enumeration represent the possible states that the
* tabrmd TCTI can be in. The state machine is as follows:
* An instantiated TCTI context begins in the TRANSMIT state:
* TRANSMIT:
* transmit: success transitions the state machine to RECEIVE
* failure leaves the state unchanged
* receive: produces TSS2_TCTI_RC_BAD_SEQUENCE
* finalize: transitions state machine to FINAL state
* cancel: produces TSS2_TCTI_RC_BAD_SEQUENCE
* setLocality: success or failure leaves state unchanged
* RECEIVE:
* transmit: produces TSS2_TCTI_RC_BAD_SEQUENCE
* receive: success transitions the state machine to TRANSMIT
* failure with the following RCs leave the state unchanged:
* TRY_AGAIN, INSUFFICIENT_BUFFER, BAD_CONTEXT,
* BAD_REFERENCE, BAD_VALUE, BAD_SEQUENCE
* all other failures transition state machine to
* TRANSMIT (not recoverable)
* finalize: transitions state machine to FINAL state
* cancel: success transitions state machine to READY_TRANSMIT
* failure leaves state unchanged
* setLocality: produces TSS2_TCTI_RC_BAD_SEQUENCE
* FINAL:
* all function calls produce TSS2_TCTI_RC_BAD_SEQUENCE
*/
typedef enum {
TABRMD_STATE_FINAL,
TABRMD_STATE_RECEIVE,
TABRMD_STATE_TRANSMIT,
} tcti_tabrmd_state_t;
/* This is our private TCTI structure. We're required by the spec to have
* the same structure as the non-opaque area defined by the
* TSS2_TCTI_CONTEXT_COMMON_V1 structure. Anything after this data is opaque
* and private to our implementation. See section 7.3 of the SAPI / TCTI spec
* for the details.
*/
typedef struct {
TSS2_TCTI_CONTEXT_COMMON_V1 common;
guint64 id;
GSocketConnection *sock_connect;
TctiTabrmd *proxy;
tpm_header_t header;
tcti_tabrmd_state_t state;
size_t index;
uint8_t header_buf [TPM_HEADER_SIZE];
} TSS2_TCTI_TABRMD_CONTEXT;
#define TABRMD_CONF_INIT_DEFAULT { \
.bus_name = TABRMD_DBUS_NAME_DEFAULT, \
.bus_type = TABRMD_DBUS_TYPE_DEFAULT, \
}
typedef struct {
const char *bus_name;
GBusType bus_type;
} tabrmd_conf_t;
/*
* This function is not exposed through the public header. It is meant to be
* dynamically discovered using dlopen at run time. We include it here in the
* private header so that we can invoke it in the test harness.
*/
const TSS2_TCTI_INFO* Tss2_Tcti_Info (void);
GBusType tabrmd_bus_type_from_str (const char* const bus_type);
TSS2_RC tabrmd_kv_callback (const key_value_t *key_value,
gpointer user_data);
TSS2_RC tss2_tcti_tabrmd_transmit (TSS2_TCTI_CONTEXT *context,
size_t size,
const uint8_t *command);
TSS2_RC tss2_tcti_tabrmd_receive (TSS2_TCTI_CONTEXT *context,
size_t *size,
uint8_t *response,
int32_t timeout);
void tss2_tcti_tabrmd_finalize (TSS2_TCTI_CONTEXT *context);
TSS2_RC tss2_tcti_tabrmd_cancel (TSS2_TCTI_CONTEXT *context);
TSS2_RC tss2_tcti_tabrmd_get_poll_handles (TSS2_TCTI_CONTEXT *context,
TSS2_TCTI_POLL_HANDLE *handles,
size_t *num_handles);
TSS2_RC tss2_tcti_tabrmd_set_locality (TSS2_TCTI_CONTEXT *context,
guint8 locality);
int tcti_tabrmd_poll (int fd, int32_t timeout);
TSS2_RC tcti_tabrmd_read (TSS2_TCTI_TABRMD_CONTEXT *ctx,
uint8_t *buf,
size_t size,
int32_t timeout);
#endif /* TSS2TCTI_TABRMD_PRIV_H */
tpm2-abrmd-3.0.0/src/tcti-tabrmd.c 0000664 0000000 0000000 00000051741 14343407245 0016642 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "tabrmd.h"
#include "tss2-tcti-tabrmd.h"
#include "tcti-tabrmd-priv.h"
#include "tpm2-header.h"
#include "util.h"
TSS2_RC
tss2_tcti_tabrmd_transmit (TSS2_TCTI_CONTEXT *context,
size_t size,
const uint8_t *command)
{
ssize_t write_ret;
TSS2_RC tss2_ret = TSS2_RC_SUCCESS;
GOutputStream *ostream;
g_debug ("tss2_tcti_tabrmd_transmit");
if (context == NULL || command == NULL) {
return TSS2_TCTI_RC_BAD_REFERENCE;
}
if (size == 0) {
return TSS2_TCTI_RC_BAD_VALUE;
}
if (TSS2_TCTI_MAGIC (context) != TSS2_TCTI_TABRMD_MAGIC ||
TSS2_TCTI_VERSION (context) != TSS2_TCTI_TABRMD_VERSION) {
return TSS2_TCTI_RC_BAD_CONTEXT;
}
if (TSS2_TCTI_TABRMD_STATE (context) != TABRMD_STATE_TRANSMIT) {
return TSS2_TCTI_RC_BAD_SEQUENCE;
}
g_debug_bytes (command, size, 16, 4);
ostream = g_io_stream_get_output_stream (TSS2_TCTI_TABRMD_IOSTREAM (context));
g_debug ("%s: blocking write on ostream", __func__);
write_ret = write_all (ostream, command, size);
/* should switch on possible errors to translate to TSS2 error codes */
switch (write_ret) {
case -1:
g_debug ("tss2_tcti_tabrmd_transmit: error writing to pipe: %s",
strerror (errno));
tss2_ret = TSS2_TCTI_RC_IO_ERROR;
break;
case 0:
g_debug ("tss2_tcti_tabrmd_transmit: EOF returned writing to pipe");
tss2_ret = TSS2_TCTI_RC_NO_CONNECTION;
break;
default:
if (write_ret == (ssize_t) size) {
TSS2_TCTI_TABRMD_STATE (context) = TABRMD_STATE_RECEIVE;
} else {
g_debug ("tss2_tcti_tabrmd_transmit: short write");
tss2_ret = TSS2_TCTI_RC_GENERAL_FAILURE;
}
break;
}
return tss2_ret;
}
/*
* This function maps errno values to TCTI RCs.
*/
static TSS2_RC
errno_to_tcti_rc (int error_number)
{
switch (error_number) {
case -1:
return TSS2_TCTI_RC_NO_CONNECTION;
case 0:
return TSS2_RC_SUCCESS;
case EAGAIN:
#if EAGAIN != EWOULDBLOCK
case EWOULDBLOCK:
#endif
return TSS2_TCTI_RC_TRY_AGAIN;
case EIO:
return TSS2_TCTI_RC_IO_ERROR;
default:
g_debug ("mapping errno %d with message \"%s\" to "
"TSS2_TCTI_RC_GENERAL_FAILURE",
error_number, strerror (error_number));
return TSS2_TCTI_RC_GENERAL_FAILURE;
}
}
/*
* This function maps GError code values to TCTI RCs.
*/
static TSS2_RC
gerror_code_to_tcti_rc (int error_number)
{
switch (error_number) {
case -1:
return TSS2_TCTI_RC_NO_CONNECTION;
case G_IO_ERROR_WOULD_BLOCK:
return TSS2_TCTI_RC_TRY_AGAIN;
case G_IO_ERROR_FAILED:
case G_IO_ERROR_HOST_UNREACHABLE:
case G_IO_ERROR_NETWORK_UNREACHABLE:
#if G_IO_ERROR_BROKEN_PIPE != G_IO_ERROR_CONNECTION_CLOSED
case G_IO_ERROR_CONNECTION_CLOSED:
#endif
#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 44
case G_IO_ERROR_NOT_CONNECTED:
#endif
return TSS2_TCTI_RC_IO_ERROR;
default:
g_debug ("mapping errno %d with message \"%s\" to "
"TSS2_TCTI_RC_GENERAL_FAILURE",
error_number, strerror (error_number));
return TSS2_TCTI_RC_GENERAL_FAILURE;
}
}
#if defined(__FreeBSD__)
#ifndef POLLRDHUP
#define POLLRDHUP 0x0
#endif
#endif
/*
* This is a thin wrapper around a call to poll. It packages up the provided
* file descriptor and timeout and polls on that same FD for data or a hangup.
* Returns:
* -1 on timeout
* 0 when data is ready
* errno on error
*/
int
tcti_tabrmd_poll (int fd,
int32_t timeout)
{
struct pollfd pollfds [] = {
{
.fd = fd,
.events = POLLIN | POLLPRI | POLLRDHUP,
}
};
int ret;
int errno_tmp;
ret = TABRMD_ERRNO_EINTR_RETRY (poll (pollfds,
sizeof (pollfds) / sizeof (struct pollfd),
timeout));
errno_tmp = errno;
switch (ret) {
case -1:
g_debug ("poll produced error: %d, %s",
errno_tmp, strerror (errno_tmp));
return errno_tmp;
case 0:
g_debug ("poll timed out after %" PRId32 " milliseconds", timeout);
return -1;
default:
g_debug ("poll has %d fds ready", ret);
if (pollfds[0].revents & POLLIN) {
g_debug (" POLLIN");
}
if (pollfds[0].revents & POLLPRI) {
g_debug (" POLLPRI");
}
if (pollfds[0].revents & POLLRDHUP) {
g_debug (" POLLRDHUP");
}
return 0;
}
}
/*
* Read as much of the requested data as possible into the provided buffer.
* If the read would block, return TSS2_TCTI_RC_TRY_AGAIN (a short read will
* write data into the buffer first).
* If an error occurs map that to the appropriate TSS2_RC and return.
*/
TSS2_RC
tcti_tabrmd_read (TSS2_TCTI_TABRMD_CONTEXT *ctx,
uint8_t *buf,
size_t size,
int32_t timeout)
{
GError *error = NULL;
ssize_t num_read;
int ret;
ret = tcti_tabrmd_poll (TSS2_TCTI_TABRMD_FD (ctx), timeout);
switch (ret) {
case -1:
return TSS2_TCTI_RC_TRY_AGAIN;
case 0:
break;
default:
return errno_to_tcti_rc (ret);
}
num_read = g_input_stream_read (TSS2_TCTI_TABRMD_ISTREAM (ctx),
(gchar*)&buf [ctx->index],
size,
NULL,
&error);
switch (num_read) {
case 0:
g_debug ("read produced EOF");
return TSS2_TCTI_RC_NO_CONNECTION;
case -1:
g_assert (error != NULL);
g_warning ("%s: read on istream produced error: %s", __func__,
error->message);
ret = error->code;
g_error_free (error);
return gerror_code_to_tcti_rc (ret);
default:
g_debug ("successfully read %zd bytes", num_read);
g_debug_bytes (&buf [ctx->index], num_read, 16, 4);
/* Advance index by the number of bytes read. */
ctx->index += num_read;
/* short read means try again */
if (size - num_read != 0) {
return TSS2_TCTI_RC_TRY_AGAIN;
} else {
return TSS2_RC_SUCCESS;
}
}
}
/*
* This is the receive function that is exposed to clients through the TCTI
* API.
*/
TSS2_RC
tss2_tcti_tabrmd_receive (TSS2_TCTI_CONTEXT *context,
size_t *size,
uint8_t *response,
int32_t timeout)
{
TSS2_RC rc = TSS2_RC_SUCCESS;
TSS2_TCTI_TABRMD_CONTEXT *tabrmd_ctx = (TSS2_TCTI_TABRMD_CONTEXT*)context;
g_debug ("tss2_tcti_tabrmd_receive");
if (context == NULL || size == NULL) {
return TSS2_TCTI_RC_BAD_REFERENCE;
}
if (response == NULL && *size != 0) {
return TSS2_TCTI_RC_BAD_VALUE;
}
if (TSS2_TCTI_MAGIC (context) != TSS2_TCTI_TABRMD_MAGIC ||
TSS2_TCTI_VERSION (context) != TSS2_TCTI_TABRMD_VERSION) {
return TSS2_TCTI_RC_BAD_CONTEXT;
}
if (tabrmd_ctx->state != TABRMD_STATE_RECEIVE) {
return TSS2_TCTI_RC_BAD_SEQUENCE;
}
if (timeout < TSS2_TCTI_TIMEOUT_BLOCK) {
return TSS2_TCTI_RC_BAD_VALUE;
}
if (size == NULL || (response == NULL && *size != 0)) {
return TSS2_TCTI_RC_BAD_REFERENCE;
}
/* response buffer must be at least as large as the header */
if (response != NULL && *size < TPM_HEADER_SIZE) {
return TSS2_TCTI_RC_INSUFFICIENT_BUFFER;
}
/* make sure we've got the response header */
if (tabrmd_ctx->index < TPM_HEADER_SIZE) {
rc = tcti_tabrmd_read (tabrmd_ctx,
tabrmd_ctx->header_buf,
TPM_HEADER_SIZE - tabrmd_ctx->index,
timeout);
if (rc != TSS2_RC_SUCCESS)
return rc;
if (tabrmd_ctx->index == TPM_HEADER_SIZE) {
tabrmd_ctx->header.tag = get_response_tag (tabrmd_ctx->header_buf);
tabrmd_ctx->header.size = get_response_size (tabrmd_ctx->header_buf);
tabrmd_ctx->header.code = get_response_code (tabrmd_ctx->header_buf);
if (tabrmd_ctx->header.size < TPM_HEADER_SIZE) {
tabrmd_ctx->state = TABRMD_STATE_TRANSMIT;
return TSS2_TCTI_RC_MALFORMED_RESPONSE;
}
}
}
/* if response is NULL, caller is querying size, we know size isn't NULL */
if (response == NULL) {
*size = tabrmd_ctx->header.size;
return TSS2_RC_SUCCESS;
} else if (tabrmd_ctx->index == TPM_HEADER_SIZE) {
/* once we have the header and a buffer from the caller, copy */
memcpy (response, tabrmd_ctx->header_buf, TPM_HEADER_SIZE);
}
if (*size < tabrmd_ctx->header.size) {
return TSS2_TCTI_RC_INSUFFICIENT_BUFFER;
}
/* the whole response has been read already */
if (tabrmd_ctx->header.size == tabrmd_ctx->index) {
goto out;
}
rc = tcti_tabrmd_read (tabrmd_ctx,
response,
tabrmd_ctx->header.size - tabrmd_ctx->index,
timeout);
out:
if (rc == TSS2_RC_SUCCESS) {
/* We got all the bytes we asked for, reset the index & state: done */
*size = tabrmd_ctx->index;
tabrmd_ctx->index = 0;
tabrmd_ctx->state = TABRMD_STATE_TRANSMIT;
}
return rc;
}
void
tss2_tcti_tabrmd_finalize (TSS2_TCTI_CONTEXT *context)
{
g_debug ("tss2_tcti_tabrmd_finalize");
if (context == NULL) {
g_warning ("Invalid parameter");
return;
}
TSS2_TCTI_TABRMD_STATE (context) = TABRMD_STATE_FINAL;
g_clear_object (&TSS2_TCTI_TABRMD_SOCK_CONNECT (context));
g_clear_object (&TSS2_TCTI_TABRMD_PROXY (context));
}
TSS2_RC
tss2_tcti_tabrmd_cancel (TSS2_TCTI_CONTEXT *context)
{
TSS2_RC ret = TSS2_RC_SUCCESS;
GError *error = NULL;
gboolean cancel_ret;
if (context == NULL) {
return TSS2_TCTI_RC_BAD_CONTEXT;
}
g_info("tss2_tcti_tabrmd_cancel: id 0x%" PRIx64,
TSS2_TCTI_TABRMD_ID (context));
if (TSS2_TCTI_TABRMD_STATE (context) != TABRMD_STATE_RECEIVE) {
return TSS2_TCTI_RC_BAD_SEQUENCE;
}
cancel_ret = tcti_tabrmd_call_cancel_sync (
TSS2_TCTI_TABRMD_PROXY (context),
TSS2_TCTI_TABRMD_ID (context),
&ret,
NULL,
&error);
if (cancel_ret == FALSE) {
g_warning ("cancel command failed with error code: 0x%" PRIx32
", messag: %s", error->code, error->message);
ret = error->code;
g_error_free (error);
}
return ret;
}
TSS2_RC
tss2_tcti_tabrmd_get_poll_handles (TSS2_TCTI_CONTEXT *context,
TSS2_TCTI_POLL_HANDLE *handles,
size_t *num_handles)
{
if (context == NULL) {
return TSS2_TCTI_RC_BAD_CONTEXT;
}
if (num_handles == NULL) {
return TSS2_TCTI_RC_BAD_REFERENCE;
}
if (handles != NULL && *num_handles < 1) {
return TSS2_TCTI_RC_INSUFFICIENT_BUFFER;
}
*num_handles = 1;
if (handles != NULL) {
handles [0].fd = TSS2_TCTI_TABRMD_FD (context);
}
return TSS2_RC_SUCCESS;
}
TSS2_RC
tss2_tcti_tabrmd_set_locality (TSS2_TCTI_CONTEXT *context,
guint8 locality)
{
gboolean status;
TSS2_RC ret = TSS2_RC_SUCCESS;
GError *error = NULL;
if (context == NULL) {
return TSS2_TCTI_RC_BAD_CONTEXT;
}
g_info ("tss2_tcti_tabrmd_set_locality: id 0x%" PRIx64,
TSS2_TCTI_TABRMD_ID (context));
if (TSS2_TCTI_TABRMD_STATE (context) != TABRMD_STATE_TRANSMIT) {
return TSS2_TCTI_RC_BAD_SEQUENCE;
}
status = tcti_tabrmd_call_set_locality_sync (
TSS2_TCTI_TABRMD_PROXY (context),
TSS2_TCTI_TABRMD_ID (context),
locality,
&ret,
NULL,
&error);
if (status == FALSE) {
g_warning ("set locality command failed with error code: 0x%" PRIx32
", message: %s", error->code, error->message);
ret = error->code;
g_error_free (error);
}
return ret;
}
/*
* Initialization function to set context data values and function pointers.
*/
void
init_tcti_data (TSS2_TCTI_CONTEXT *context)
{
memset (context, 0, sizeof (TSS2_TCTI_TABRMD_CONTEXT));
TSS2_TCTI_MAGIC (context) = TSS2_TCTI_TABRMD_MAGIC;
TSS2_TCTI_VERSION (context) = TSS2_TCTI_TABRMD_VERSION;
TSS2_TCTI_TABRMD_STATE (context) = TABRMD_STATE_TRANSMIT;
TSS2_TCTI_TRANSMIT (context) = tss2_tcti_tabrmd_transmit;
TSS2_TCTI_RECEIVE (context) = tss2_tcti_tabrmd_receive;
TSS2_TCTI_FINALIZE (context) = tss2_tcti_tabrmd_finalize;
TSS2_TCTI_CANCEL (context) = tss2_tcti_tabrmd_cancel;
TSS2_TCTI_GET_POLL_HANDLES (context) = tss2_tcti_tabrmd_get_poll_handles;
TSS2_TCTI_SET_LOCALITY (context) = tss2_tcti_tabrmd_set_locality;
}
static gboolean
tcti_tabrmd_call_create_connection_sync_fdlist (TctiTabrmd *proxy,
guint64 *out_id,
GUnixFDList **out_fd_list,
GCancellable *cancellable,
GError **error)
{
GVariant *_ret;
_ret = g_dbus_proxy_call_with_unix_fd_list_sync (G_DBUS_PROXY (proxy),
"CreateConnection",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
out_fd_list,
cancellable,
error);
if (_ret == NULL) {
goto _out;
}
g_variant_get (_ret, "(t)", out_id);
g_variant_unref (_ret);
_out:
return _ret != NULL;
}
typedef struct {
char *name;
GBusType type;
} bus_name_type_entry_t;
static const bus_name_type_entry_t bus_name_type_map[] = {
{
.name = "session",
.type = G_BUS_TYPE_SESSION,
},
{
.name = "system",
.type = G_BUS_TYPE_SYSTEM,
},
};
#define BUS_NAME_TYPE_MAP_LENGTH (sizeof (bus_name_type_map) / sizeof (bus_name_type_entry_t))
GBusType
tabrmd_bus_type_from_str (const char* const bus_type)
{
size_t i;
g_debug ("BUS_NAME_TYPE_MAP_LENGTH: %zu", BUS_NAME_TYPE_MAP_LENGTH);
g_debug ("looking up type for bus_type string: %s", bus_type);
for (i = 0; i < BUS_NAME_TYPE_MAP_LENGTH; ++i) {
if (strcmp (bus_name_type_map [i].name, bus_type) == 0) {
g_debug ("matched bus_type string \"%s\" to type %d",
bus_name_type_map [i].name,
bus_name_type_map [i].type);
return bus_name_type_map [i].type;
}
}
g_debug ("no match for bus_type string %s", bus_type);
return G_BUS_TYPE_NONE;
}
TSS2_RC
tabrmd_kv_callback (const key_value_t *key_value,
gpointer user_data)
{
tabrmd_conf_t *tabrmd_conf = (tabrmd_conf_t*)user_data;
if (key_value == NULL || user_data == NULL) {
g_warning ("%s passed NULL parameter", __func__);
return TSS2_TCTI_RC_GENERAL_FAILURE;
}
g_debug ("key: %s / value: %s\n", key_value->key, key_value->value);
if (strcmp (key_value->key, "bus_name") == 0) {
tabrmd_conf->bus_name = key_value->value;
return TSS2_RC_SUCCESS;
} else if (strcmp (key_value->key, "bus_type") == 0) {
tabrmd_conf->bus_type = tabrmd_bus_type_from_str (key_value->value);
if (tabrmd_conf->bus_type == G_BUS_TYPE_NONE) {
return TSS2_TCTI_RC_BAD_VALUE;
}
return TSS2_RC_SUCCESS;
} else {
return TSS2_TCTI_RC_BAD_VALUE;
}
}
/*
* Establish a connection with the daemon. This includes calling the
* CreateConnection dbus method, extracting the file descriptor used for
* sending commands and receiving responses, and extracting the connection
* ID used when sending commands over the dbus interface.
*
* The proxy object in the context structure must be created / valid before
* calling this function.
*/
TSS2_RC
tcti_tabrmd_connect (TSS2_TCTI_CONTEXT *context)
{
GError *error = NULL;
GSocket *sock = NULL;
GUnixFDList *fd_list = NULL;
gboolean call_ret;
guint64 id;
TSS2_RC rc = TSS2_RC_SUCCESS;
call_ret = tcti_tabrmd_call_create_connection_sync_fdlist (
TSS2_TCTI_TABRMD_PROXY (context),
&id,
&fd_list,
NULL,
&error);
if (call_ret == FALSE) {
g_warning ("Failed to create connection with service: %s",
error->message);
rc = TSS2_TCTI_RC_NO_CONNECTION;
goto out;
}
if (fd_list == NULL) {
g_critical ("call to CreateConnection returned a NULL GUnixFDList");
rc = TSS2_TCTI_RC_NO_CONNECTION;
goto out;
}
gint num_handles = g_unix_fd_list_get_length (fd_list);
if (num_handles != 1) {
g_critical ("CreateConnection expected to return 1 handles, received %d",
num_handles);
rc = TSS2_TCTI_RC_GENERAL_FAILURE;
goto out;
}
gint fd = g_unix_fd_list_get (fd_list, 0, &error);
if (fd == -1) {
g_critical ("unable to get receive handle from GUnixFDList: %s",
error->message);
rc = TSS2_TCTI_RC_GENERAL_FAILURE;
goto out;
}
sock = g_socket_new_from_fd (fd, NULL);
TSS2_TCTI_TABRMD_SOCK_CONNECT (context) = \
g_socket_connection_factory_create_connection (sock);
TSS2_TCTI_TABRMD_ID (context) = id;
out:
g_clear_error (&error);
g_clear_object (&sock);
g_clear_object (&fd_list);
return rc;
}
/*
* The longest configuration string we'll take. Each dbus name can be 255
* characters long (see dbus spec). The bus_types that we support are
* 'system' or 'session' (255 + 7 = 262). 'bus_type=' and 'bus_name=' are
* each another 9 characters for a total of 280.
*/
#define CONF_STRING_MAX 280
TSS2_RC
Tss2_Tcti_Tabrmd_Init (TSS2_TCTI_CONTEXT *context,
size_t *size,
const char *conf)
{
GError *error = NULL;
size_t conf_len;
char *conf_copy = NULL;
TSS2_RC rc;
tabrmd_conf_t tabrmd_conf = TABRMD_CONF_INIT_DEFAULT;
if (context == NULL && size != NULL) {
*size = sizeof (TSS2_TCTI_TABRMD_CONTEXT);
return TSS2_RC_SUCCESS;
}
if (size == NULL) {
return TSS2_TCTI_RC_BAD_VALUE;
}
if (conf != NULL) {
conf_len = strlen (conf);
if (conf_len > CONF_STRING_MAX) {
return TSS2_TCTI_RC_BAD_VALUE;
}
conf_copy = g_strdup (conf);
if (conf_copy == NULL) {
g_critical ("Failed to duplicate config string: %s", strerror (errno));
return TSS2_TCTI_RC_GENERAL_FAILURE;
}
rc = parse_key_value_string (conf_copy,
tabrmd_kv_callback,
&tabrmd_conf);
if (rc != TSS2_RC_SUCCESS) {
goto out;
}
}
/* Register dbus error mapping for tabrmd. Gets us RCs from Gerror codes */
TABRMD_ERROR;
init_tcti_data (context);
TSS2_TCTI_TABRMD_PROXY (context) =
tcti_tabrmd_proxy_new_for_bus_sync (tabrmd_conf.bus_type,
G_DBUS_PROXY_FLAGS_NONE,
tabrmd_conf.bus_name,
TABRMD_DBUS_PATH,
NULL,
&error);
if (TSS2_TCTI_TABRMD_PROXY (context) == NULL) {
g_critical ("failed to allocate dbus proxy object: %s", error->message);
rc = TSS2_TCTI_RC_NO_CONNECTION;
goto out;
}
rc = tcti_tabrmd_connect (context);
if (rc == TSS2_RC_SUCCESS) {
g_debug ("initialized tabrmd TCTI context with id: 0x%" PRIx64,
TSS2_TCTI_TABRMD_ID (context));
}
out:
g_clear_pointer (&conf_copy, g_free);
g_clear_error (&error);
return rc;
}
/* public info structure */
static const TSS2_TCTI_INFO tss2_tcti_info = {
.version = TSS2_TCTI_TABRMD_VERSION,
.name = "tcti-abrmd",
.description = "TCTI module for communication with tabrmd.",
.config_help = "This conf string is a series of key / value pairs " \
"where keys and values are separated by the '=' character and " \
"each pair is separated by the ',' character. Valid keys are " \
"\"bus_name\" and \"bus_type\".",
.init = Tss2_Tcti_Tabrmd_Init,
};
const TSS2_TCTI_INFO*
Tss2_Tcti_Info (void)
{
return &tss2_tcti_info;
}
tpm2-abrmd-3.0.0/src/tcti-tabrmd.map 0000664 0000000 0000000 00000000136 14343407245 0017165 0 ustar 00root root 0000000 0000000 {
global:
Tss2_Tcti_Tabrmd_Init;
Tss2_Tcti_Info;
local:
*;
};
tpm2-abrmd-3.0.0/src/tcti.c 0000664 0000000 0000000 00000007442 14343407245 0015372 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#include
#include
#include "tcti.h"
#include "util.h"
G_DEFINE_TYPE (Tcti, tcti, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_CONTEXT,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL };
/*
* GObject property setter.
*/
static void
tcti_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
Tcti *self = TCTI (object);
g_debug ("%s", __func__);
switch (property_id) {
case PROP_CONTEXT:
self->tcti_context = (TSS2_TCTI_CONTEXT*)g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/*
* GObject property getter.
*/
static void
tcti_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
Tcti *self = TCTI (object);
switch (property_id) {
case PROP_CONTEXT:
g_value_set_pointer (value, self->tcti_context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
tcti_dispose (GObject *object)
{
Tcti *self = TCTI (object);
if (self->tcti_context) {
Tss2_TctiLdr_Finalize (&self->tcti_context);
}
G_OBJECT_CLASS (tcti_parent_class)->dispose (object);
}
static void
tcti_finalize (GObject *object)
{
G_OBJECT_CLASS (tcti_parent_class)->finalize (object);
}
static void
tcti_init (Tcti *tcti)
{
UNUSED_PARAM (tcti);
}
static void
tcti_class_init (TctiClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (tcti_parent_class == NULL)
tcti_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = tcti_dispose;
object_class->finalize = tcti_finalize;
object_class->get_property = tcti_get_property;
object_class->set_property = tcti_set_property;
obj_properties [PROP_CONTEXT] =
g_param_spec_pointer ("tcti-context",
"TSS2_TCTI_CONTEXT",
"TSS2_TCTI_CONTEXT",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
Tcti*
tcti_new (TSS2_TCTI_CONTEXT *tcti_context)
{
return TCTI (g_object_new (TYPE_TCTI,
"tcti-context", tcti_context,
NULL));
}
TSS2_TCTI_CONTEXT*
tcti_peek_context (Tcti *self)
{
return self->tcti_context;
}
/**
* The rest of these functions are just wrappers around the macros provided
* by the TSS for calling the TCTI functions. There's no implementation for
* 'getPollHandles' yet since I've got no need for it yet.
*/
TSS2_RC
tcti_transmit (Tcti *self,
size_t size,
uint8_t *command)
{
TSS2_RC rc;
rc = Tss2_Tcti_Transmit (self->tcti_context,
size,
command);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Tcti_Transmit", rc);
}
return rc;
}
TSS2_RC
tcti_receive (Tcti *self,
size_t *size,
uint8_t *response,
int32_t timeout)
{
TSS2_RC rc;
rc = Tss2_Tcti_Receive (self->tcti_context,
size,
response,
timeout);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Tcti_Receive", rc);
}
return rc;
}
tpm2-abrmd-3.0.0/src/tcti.h 0000664 0000000 0000000 00000003403 14343407245 0015370 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef TCTI_H
#define TCTI_H
#include
#include
G_BEGIN_DECLS
typedef struct _Tcti Tcti;
typedef struct _TctiClass TctiClass;
struct _TctiClass {
GObjectClass parent;
};
struct _Tcti {
GObject parent;
TSS2_TCTI_CONTEXT *tcti_context;
};
#define TYPE_TCTI (tcti_get_type ())
#define TCTI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TCTI, Tcti))
#define IS_TCTI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TCTI))
#define TCTI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_TCTI, TctiClass))
#define IS_TCTI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_TCTI))
#define TCTI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_TCTI, TctiClass))
GType tcti_get_type (void);
Tcti* tcti_new (TSS2_TCTI_CONTEXT *ctx);
TSS2_RC tcti_transmit (Tcti *self,
size_t size,
uint8_t *command);
TSS2_RC tcti_receive (Tcti *self,
size_t *size,
uint8_t *response,
int32_t timeout);
TSS2_RC tcti_cancel (Tcti *self);
TSS2_RC tcti_set_locality (Tcti *self,
uint8_t locality);
TSS2_TCTI_CONTEXT* tcti_peek_context (Tcti *self);
G_END_DECLS
#endif /* TCTI_H */
tpm2-abrmd-3.0.0/src/thread.c 0000664 0000000 0000000 00000002276 14343407245 0015676 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include "util.h"
#include "thread.h"
G_DEFINE_ABSTRACT_TYPE (Thread, thread, G_TYPE_OBJECT);
static void
thread_init (Thread *iface)
{
UNUSED_PARAM(iface);
/* noop, required by G_DEFINE_INTERFACE */
}
static void
thread_class_init (ThreadClass *klass)
{
klass->thread_run = NULL;
}
gint
thread_start (Thread *self)
{
if (self->thread_id != 0) {
g_warning ("thread running");
return -1;
}
return pthread_create (&self->thread_id,
NULL,
THREAD_GET_CLASS (self)->thread_run,
self);
}
void
thread_cancel (Thread *self)
{
ThreadClass *class = THREAD_GET_CLASS (self);
if (self->thread_id == 0) {
g_warning ("thread not running");
return;
}
if (class->thread_unblock != NULL)
class->thread_unblock (self);
}
gint
thread_join (Thread *self)
{
if (self->thread_id == 0) {
g_warning ("thread not running");
return -1;
}
gint ret = pthread_join (self->thread_id, NULL);
self->thread_id = 0;
return ret;
}
tpm2-abrmd-3.0.0/src/thread.h 0000664 0000000 0000000 00000002535 14343407245 0015701 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#ifndef THREAD_INTERFACE_H
#define THREAD_INTERFACE_H
#include
G_BEGIN_DECLS
typedef struct _Thread Thread;
typedef struct _ThreadClass ThreadClass;
typedef void* (*ThreadFunc) (void *user_data);
typedef void (*ThreadUnblockFunc) (Thread *self);
struct _ThreadClass {
GObjectClass parent;
ThreadFunc thread_run;
ThreadUnblockFunc thread_unblock;
};
struct _Thread {
GObject parent;
pthread_t thread_id;
};
#define TYPE_THREAD (thread_get_type ())
#define THREAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_THREAD, Thread))
#define IS_THREAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_THREAD))
#define THREAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_THREAD, ThreadClass))
#define IS_THREAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_THREAD))
#define THREAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_THREAD, ThreadClass))
GType thread_get_type (void);
void thread_cancel (Thread *self);
gint thread_join (Thread *self);
gint thread_start (Thread *self);
G_END_DECLS
#endif /* THREAD_INTERFACE_H */
tpm2-abrmd-3.0.0/src/tpm2-command.c 0000664 0000000 0000000 00000055537 14343407245 0016735 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include "tpm2-command.h"
#include "tpm2-header.h"
#include "util.h"
/* macros to make getting at fields in the command more simple */
#define HANDLE_AREA_OFFSET TPM_HEADER_SIZE
#define HANDLE_OFFSET(i) (HANDLE_AREA_OFFSET + sizeof (TPM2_HANDLE) * i)
#define HANDLE_END_OFFSET(i) (HANDLE_OFFSET (i) + sizeof (TPM2_HANDLE))
#define HANDLE_GET(buffer, count) \
(*(TPM2_HANDLE*)(&buffer [HANDLE_OFFSET (count)]))
/*
* Offset of capability field in TPM2_GetCapability command buffer is
* immediately after the header.
*/
#define CAP_OFFSET TPM_HEADER_SIZE
#define CAP_END_OFFSET (CAP_OFFSET + sizeof (TPM2_CAP))
#define CAP_GET(buffer) (*(TPM2_CAP*)(&buffer [CAP_OFFSET]))
/*
* Offset of property field in TPM2_GetCapability command buffer is
* immediately after the capability field.
*/
#define PROPERTY_OFFSET CAP_END_OFFSET
#define PROPERTY_END_OFFSET (PROPERTY_OFFSET + sizeof (UINT32))
#define PROPERTY_GET(buffer) (*(UINT32*)(&buffer [PROPERTY_OFFSET]))
/*
* The offset of the property count field is immediately following the
* property field.
*/
#define PROPERTY_COUNT_OFFSET PROPERTY_END_OFFSET
#define PROPERTY_COUNT_END_OFFSET (PROPERTY_COUNT_OFFSET + sizeof (UINT32))
#define PROPERTY_COUNT_GET(buffer) (*(UINT32*)(&buffer [PROPERTY_COUNT_OFFSET]))
/*
* Helper macros to aid in the access of various parts of the command
* authorization area.
*/
#define AUTH_AREA_OFFSET(cmd) \
(TPM_HEADER_SIZE + (tpm2_command_get_handle_count (cmd) * sizeof (TPM2_HANDLE)))
#define AUTH_AREA_SIZE_OFFSET(cmd) AUTH_AREA_OFFSET (cmd)
#define AUTH_AREA_SIZE_END_OFFSET(cmd) \
(AUTH_AREA_SIZE_OFFSET (cmd) + sizeof (UINT32))
#define AUTH_AREA_GET_SIZE(cmd) \
(be32toh (*(UINT32*)(&cmd->buffer [AUTH_AREA_SIZE_OFFSET (cmd)])))
#define AUTH_AREA_FIRST_OFFSET(cmd) (AUTH_AREA_SIZE_END_OFFSET (cmd))
#define AUTH_AREA_END_OFFSET(cmd) \
(AUTH_AREA_FIRST_OFFSET (cmd) + AUTH_AREA_GET_SIZE (cmd))
/*
* Helper macros to aid in accessing parts of a session authorization. These
* are the individual authorizations in the authorization area of the command.
* The 'cmd' parameter must be a reference to a valid Tpm2Command object.
* The 'index' parameter must be the index into the Tpm2Command buffer where
* the authorization we wish to query resides.
*/
#define AUTH_HANDLE_OFFSET(index) (index + 0)
#define AUTH_HANDLE_END_OFFSET(index) \
(AUTH_HANDLE_OFFSET(index) + sizeof (TPM2_HANDLE))
#define AUTH_GET_HANDLE(cmd, index) \
(be32toh (*(TPM2_HANDLE*)&cmd->buffer [AUTH_HANDLE_OFFSET (index)]))
/* nonce */
#define AUTH_NONCE_SIZE_OFFSET(index) (AUTH_HANDLE_END_OFFSET (index))
#define AUTH_NONCE_SIZE_END_OFFSET(index) \
(AUTH_NONCE_SIZE_OFFSET(index) + sizeof (UINT16))
#define AUTH_GET_NONCE_SIZE(cmd, index) \
(be16toh (*(UINT16*)&cmd->buffer [AUTH_NONCE_SIZE_OFFSET (index)]))
#define AUTH_NONCE_BUF_OFFSET(index) \
(AUTH_NONCE_SIZE_END_OFFSET (index))
#define AUTH_NONCE_BUF_END_OFFSET(cmd, index) \
(AUTH_NONCE_BUF_OFFSET (index) + AUTH_GET_NONCE_SIZE (cmd, index))
/* session attributes */
#define AUTH_SESSION_ATTRS_OFFSET(cmd, index) \
(AUTH_NONCE_BUF_END_OFFSET (cmd, index))
/*
* in AUTH_SESSION_ATTRS_END_OFFSET the UINT8 should be TPMA_SESSION but the
* TSS headers got this wrong
*/
#define AUTH_SESSION_ATTRS_END_OFFSET(cmd, index) \
(AUTH_SESSION_ATTRS_OFFSET (cmd, index) + sizeof (UINT8))
#define AUTH_GET_SESSION_ATTRS(cmd, index) \
((TPMA_SESSION)(UINT8)cmd->buffer [AUTH_SESSION_ATTRS_OFFSET (cmd, index)])
/* authorization */
#define AUTH_AUTH_SIZE_OFFSET(cmd, index) \
(AUTH_SESSION_ATTRS_END_OFFSET (cmd, index))
#define AUTH_AUTH_SIZE_END_OFFSET(cmd, index) \
(AUTH_AUTH_SIZE_OFFSET (cmd, index) + sizeof (UINT16))
#define AUTH_GET_AUTH_SIZE(cmd, index) \
(be16toh (*(UINT16*)&cmd->buffer [AUTH_AUTH_SIZE_OFFSET (cmd, index)]))
#define AUTH_AUTH_BUF_OFFSET(cmd, index) \
(AUTH_AUTH_SIZE_END_OFFSET (cmd, index))
#define AUTH_AUTH_BUF_END_OFFSET(cmd, index) \
(AUTH_AUTH_BUF_OFFSET(cmd, index) + AUTH_GET_AUTH_SIZE (cmd, index))
G_DEFINE_TYPE (Tpm2Command, tpm2_command, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_ATTRIBUTES,
PROP_SESSION,
PROP_BUFFER,
PROP_BUFFER_SIZE,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/**
* GObject property setter.
*/
static void
tpm2_command_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
Tpm2Command *self = TPM2_COMMAND (object);
switch (property_id) {
case PROP_ATTRIBUTES:
self->attributes = (TPMA_CC)g_value_get_uint (value);
break;
case PROP_BUFFER:
if (self->buffer != NULL) {
g_warning (" buffer already set");
break;
}
self->buffer = (guint8*)g_value_get_pointer (value);
break;
case PROP_BUFFER_SIZE:
self->buffer_size = g_value_get_uint (value);
break;
case PROP_SESSION:
if (self->connection != NULL) {
g_warning (" connection already set");
break;
}
self->connection = g_value_get_object (value);
if (self->connection) {
g_object_ref (self->connection);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* GObject property getter.
*/
static void
tpm2_command_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
Tpm2Command *self = TPM2_COMMAND (object);
switch (property_id) {
case PROP_ATTRIBUTES:
g_value_set_uint (value, self->attributes);
break;
case PROP_BUFFER:
g_value_set_pointer (value, self->buffer);
break;
case PROP_BUFFER_SIZE:
g_value_set_uint (value, self->buffer_size);
break;
case PROP_SESSION:
g_value_set_object (value, self->connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
tpm2_command_dispose (GObject *obj)
{
Tpm2Command *cmd = TPM2_COMMAND (obj);
g_clear_object (&cmd->connection);
G_OBJECT_CLASS (tpm2_command_parent_class)->dispose (obj);
}
/**
* override the parent finalize method so we can free the data associated with
* the DataMessage instance.
*/
static void
tpm2_command_finalize (GObject *obj)
{
Tpm2Command *cmd = TPM2_COMMAND (obj);
g_debug ("tpm2_command_finalize");
g_clear_pointer (&cmd->buffer, g_free);
G_OBJECT_CLASS (tpm2_command_parent_class)->finalize (obj);
}
static void
tpm2_command_init (Tpm2Command *command)
{
UNUSED_PARAM(command);
/* noop */
}
/**
* Boilerplate GObject initialization. Get a pointer to the parent class,
* setup a finalize function.
*/
static void
tpm2_command_class_init (Tpm2CommandClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (tpm2_command_parent_class == NULL)
tpm2_command_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = tpm2_command_dispose;
object_class->finalize = tpm2_command_finalize;
object_class->get_property = tpm2_command_get_property;
object_class->set_property = tpm2_command_set_property;
obj_properties [PROP_ATTRIBUTES] =
g_param_spec_uint ("attributes",
"TPMA_CC",
"Attributes for command.",
0,
UINT32_MAX,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_BUFFER] =
g_param_spec_pointer ("buffer",
"TPM2 command buffer",
"memory buffer holding a TPM2 command",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_BUFFER_SIZE] =
g_param_spec_uint ("buffer-size",
"sizeof command buffer",
"size of buffer holding the TPM2 command",
0,
UTIL_BUF_MAX,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_SESSION] =
g_param_spec_object ("connection",
"Session object",
"The Connection object that sent the command",
TYPE_CONNECTION,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
/**
* Boilerplate constructor, but some GObject properties would be nice.
*/
Tpm2Command*
tpm2_command_new (Connection *connection,
guint8 *buffer,
size_t size,
TPMA_CC attributes)
{
return TPM2_COMMAND (g_object_new (TYPE_TPM2_COMMAND,
"attributes", attributes,
"buffer", buffer,
"buffer-size", size,
"connection", connection,
NULL));
}
#define CONTEXT_SAVE_CMD_SIZE (TPM_HEADER_SIZE + sizeof (TPM2_HANDLE))
Tpm2Command*
tpm2_command_new_context_save (TPM2_HANDLE handle)
{
TSS2_RC rc;
uint8_t *buf = g_malloc0 (CONTEXT_SAVE_CMD_SIZE);
size_t offset = TPM_HEADER_SIZE;
rc = tpm2_header_init (buf,
CONTEXT_SAVE_CMD_SIZE,
TPM2_ST_NO_SESSIONS,
CONTEXT_SAVE_CMD_SIZE,
TPM2_CC_ContextSave);
if (rc != TSS2_RC_SUCCESS) {
goto err_out;
}
rc = Tss2_MU_TPM2_HANDLE_Marshal (handle,
buf,
CONTEXT_SAVE_CMD_SIZE,
&offset);
if (rc != TSS2_RC_SUCCESS) {
goto err_out;
}
/* TPMA_CC here is hard coded to the appropriate value for ContextSave */
return tpm2_command_new (NULL, buf, CONTEXT_SAVE_CMD_SIZE, 0x02000162);
err_out:
g_warning ("%s: failed", __func__);
g_free (buf);
return NULL;
}
Tpm2Command*
tpm2_command_new_context_load (uint8_t *buf,
size_t size)
{
TSS2_RC rc;
UINT32 size_new = TPM_HEADER_SIZE + size;
uint8_t *buf_tmp = g_malloc0 (size_new);
rc = tpm2_header_init (buf_tmp,
size_new,
TPM2_ST_NO_SESSIONS,
size_new,
TPM2_CC_ContextLoad);
if (rc != TSS2_RC_SUCCESS) {
g_free (buf_tmp);
return NULL;
}
memcpy (&buf_tmp [TPM_HEADER_SIZE], buf, size);
/* TPMA_CC here is hard coded to the appropriate value for ContextLoad */
return tpm2_command_new (NULL, buf_tmp, size_new, 0x10000161);
}
/* Simple "getter" to expose the attributes associated with the command. */
TPMA_CC
tpm2_command_get_attributes (Tpm2Command *command)
{
return command->attributes;
}
/**
*/
guint8*
tpm2_command_get_buffer (Tpm2Command *command)
{
return command->buffer;
}
/**
*/
TPM2_CC
tpm2_command_get_code (Tpm2Command *command)
{
return get_command_code (command->buffer);
}
/**
*/
guint32
tpm2_command_get_size (Tpm2Command *command)
{
return get_command_size (command->buffer);
}
/**
*/
TPMI_ST_COMMAND_TAG
tpm2_command_get_tag (Tpm2Command *command)
{
return get_command_tag (command->buffer);
}
/*
* Return the Connection object associated with this Tpm2Command. This
* is the Connection object representing the client that sent this command.
* The reference count on this object is incremented before the Connection
* object is returned to the caller. The caller is responsible for
* decrementing the reference count when it is done using the connection object.
*/
Connection*
tpm2_command_get_connection (Tpm2Command *command)
{
if (command->connection) {
g_object_ref (command->connection);
}
return command->connection;
}
/* Return the number of handles in the command. */
guint8
tpm2_command_get_handle_count (Tpm2Command *command)
{
g_debug ("tpm2_command_get_handle_count");
uint32_t tmp;
if (command == NULL) {
g_warning ("tpm2_command_get_handle_count received NULL parameter");
return 0;
}
tmp = tpm2_command_get_attributes (command);
return (guint8)((tmp & TPMA_CC_CHANDLES_MASK) >> TPMA_CC_CHANDLES_SHIFT);
}
/*
* Simple function to access handles in the provided Tpm2Command. The
* 'handle_number' parameter is a 0-based index into the handle area
* which is effectively an array. If the handle_number requests a handle
* beyond the end of this array 0 is returned.
*/
TPM2_HANDLE
tpm2_command_get_handle (Tpm2Command *command,
guint8 handle_number)
{
guint8 real_count;
size_t end;
if (command == NULL)
return 0;
real_count = tpm2_command_get_handle_count (command);
end = HANDLE_END_OFFSET (handle_number);
if (real_count > handle_number && end <= command->buffer_size) {
return be32toh (HANDLE_GET (command->buffer, handle_number));
} else {
return 0;
}
}
/*
* Simple function to set a handle at the 0-based index into the Tpm2Command
* handle area to the provided value. If the handle_number is past the bounds
* FALSE is returned.
*/
gboolean
tpm2_command_set_handle (Tpm2Command *command,
TPM2_HANDLE handle,
guint8 handle_number)
{
guint8 real_count;
size_t end;
if (command == NULL)
return FALSE;
real_count = tpm2_command_get_handle_count (command);
end = HANDLE_END_OFFSET (handle_number);
if (real_count > handle_number && end <= command->buffer_size) {
HANDLE_GET (command->buffer, handle_number) = htobe32 (handle);
return TRUE;
} else {
return FALSE;
}
}
/*
* Return the handles from the Tpm2Command back to the caller by way of the
* handles parameter. The caller must allocate sufficient space to hold
* however many handles are in this command. Take a look @
* tpm2_command_get_handle_count.
*/
gboolean
tpm2_command_get_handles (Tpm2Command *command,
TPM2_HANDLE handles[],
size_t *count)
{
guint8 real_count;
size_t i;
if (command == NULL || handles == NULL || count == NULL) {
g_warning ("tpm2_command_get_handles passed NULL parameter");
return FALSE;
}
real_count = tpm2_command_get_handle_count (command);
if (real_count > *count) {
g_warning ("tpm2_command_get_handles passed insufficient handle array");
return FALSE;
}
for (i = 0; i < real_count; ++i) {
handles[i] = tpm2_command_get_handle (command, i);
if (handles[i] == 0) {
/* no more handles could be extracted */
break;
}
}
*count = i;
return TRUE;
}
/*
* This function is a work around. The handle in a command buffer for the
* FlushContext command is not in the handle area and no handles are reported
* in the attributes structure. This means that in order to cope with
* virtualized handles in this command we must reach into the parameter area
* which breaks the abstractions we've built for accessing handles. Thus we
* provide a special function for getting at this single handle.
* Use this function with caution.
*/
TSS2_RC
tpm2_command_get_flush_handle (Tpm2Command *command,
TPM2_HANDLE *handle)
{
if (command == NULL || handle == NULL) {
g_error ("tpm2_command_get_flush_handle passed null parameter");
}
if (tpm2_command_get_code (command) != TPM2_CC_FlushContext) {
g_warning ("tpm2_command_get_flush_handle called with wrong command");
*handle = 0;
return RM_RC (TPM2_RC_TYPE);
}
if (command->buffer_size < TPM_HEADER_SIZE + sizeof (TPM2_HANDLE)) {
g_warning ("%s: command buffer_size insufficient", __func__);
*handle = 0;
return RM_RC (TPM2_RC_INSUFFICIENT);
}
/*
* Despite not technically being in the "handle area" of the command we
* are still able to access the handle as though it were. This is because
* there are no other handles or authorizations allowed in the command and
* the handle being flushed is the first parameter.
*/
*handle = be32toh (HANDLE_GET (tpm2_command_get_buffer (command), 0));
return TSS2_RC_SUCCESS;
}
/*
* When provided with a Tpm2Command that represents a call to the
* GetCapability command this function will extract the 'capability' field.
* On error 0 is returned.
*/
TPM2_CAP
tpm2_command_get_cap (Tpm2Command *command)
{
if (command == NULL) {
g_warning ("tpm2_command_get_cap passed NULL parameter");
return 0;
}
if (tpm2_command_get_code (command) != TPM2_CC_GetCapability) {
g_warning ("tpm2_command_get_cap provided a Tpm2Command buffer "
"containing the wrong command code.");
return 0;
}
if (command->buffer_size < CAP_END_OFFSET) {
g_warning ("%s insufficient buffer", __func__);
return 0;
}
return (TPM2_CAP)be32toh (CAP_GET (tpm2_command_get_buffer (command)));
}
/*
* When provided with a Tpm2Command that represents a call to the
* GetCapability command this function will extract the 'property' field.
* On error 0 is returned.
*/
UINT32
tpm2_command_get_prop (Tpm2Command *command)
{
if (command == NULL) {
g_warning ("tpm2_command_get_prop passed NULL parameter");
return 0;
}
if (tpm2_command_get_code (command) != TPM2_CC_GetCapability) {
g_warning ("tpm2_command_get_cap provided a Tpm2Command buffer "
"containing the wrong command code.");
return 0;
}
if (command->buffer_size < PROPERTY_END_OFFSET) {
g_warning ("%s insufficient buffer", __func__);
return 0;
}
return (UINT32)be32toh (PROPERTY_GET (tpm2_command_get_buffer (command)));
}
/*
* When provided with a Tpm2Command that represents a call to the
* GetCapability command this function will extract the 'propertyCount' field.
* On error 0 is returned.
*/
UINT32
tpm2_command_get_prop_count (Tpm2Command *command)
{
if (command == NULL) {
g_warning ("tpm2_command_get_prop_count passed NULL parameter");
return 0;
}
if (tpm2_command_get_code (command) != TPM2_CC_GetCapability) {
g_warning ("tpm2_command_get_cap provided a Tpm2Command buffer "
"containing the wrong command code.");
return 0;
}
if (command->buffer_size < PROPERTY_COUNT_END_OFFSET) {
g_warning ("%s insufficient buffer", __func__);
return 0;
}
return (UINT32)be32toh (PROPERTY_COUNT_GET (tpm2_command_get_buffer (command)));
}
/*
* This is a convenience function to keep from having to compare the tag
* value to TPM2_ST_(NO_)?_SESSIONS repeatedly.
*/
gboolean
tpm2_command_has_auths (Tpm2Command *command)
{
if (command == NULL) {
g_warning ("tpm2_command_has_auths passed NULL parameter");
return FALSE;
}
if (tpm2_command_get_tag (command) == TPM2_ST_NO_SESSIONS) {
return FALSE;
} else {
return TRUE;
}
}
/*
* When provided with a Tpm2Command with auths in the auth area this function
* will return the total size of the auth area.
*/
UINT32
tpm2_command_get_auths_size (Tpm2Command *command)
{
size_t auth_size_end;
if (command == NULL) {
g_warning ("tpm2_command_get_auths_size passed NULL parameter");
return 0;
}
if (!tpm2_command_has_auths (command)) {
g_warning ("%s: Tpm2Command has no auths", __func__);
return 0;
}
auth_size_end = AUTH_AREA_SIZE_END_OFFSET (command);
g_debug ("%s: auth_size_end: %zu", __func__, auth_size_end);
g_debug ("%s: buffer_size: %zu", __func__, command->buffer_size);
if (AUTH_AREA_SIZE_END_OFFSET (command) > command->buffer_size) {
g_warning ("%s reading size of auth area would overrun command buffer."
" Returning 0", __func__);
return 0;
}
return AUTH_AREA_GET_SIZE (command);
}
/*
* This function extracts the authorization handle from the entry in the
* auth area that begins at offset 'auth_offset'. Any failure to read this
* value will return 0.
*/
TPM2_HANDLE
tpm2_command_get_auth_handle (Tpm2Command *command,
size_t auth_index)
{
if (command == NULL) {
return 0;
}
if (AUTH_HANDLE_END_OFFSET (auth_index) > command->buffer_size) {
g_warning ("%s attempt to access authorization handle overruns "
" command buffer", __func__);
return 0;
}
return AUTH_GET_HANDLE (command, auth_index);
}
TPMA_SESSION
tpm2_command_get_auth_attrs (Tpm2Command *command,
size_t auth_offset)
{
size_t attrs_end;
if (command == NULL) {
return (TPMA_SESSION)(UINT8)0;
}
attrs_end = AUTH_SESSION_ATTRS_END_OFFSET (command, auth_offset);
if (attrs_end > command->buffer_size) {
g_warning ("%s attempt to access session attributes overruns command "
"buffer", __func__);
return (TPMA_SESSION)(UINT8)0;
}
return AUTH_GET_SESSION_ATTRS (command, auth_offset);
}
/*
* The caller provided GFunc is invoked once for each authorization in the
* command authorization area. The first parameter passed to 'func' is a
* pointer to the start of the authorization data. The second parameter is
* the caller provided 'user_data'.
*/
gboolean
tpm2_command_foreach_auth (Tpm2Command *command,
GFunc callback,
gpointer user_data)
{
size_t offset;
if (command == NULL || callback == NULL) {
g_warning ("%s passed NULL parameter", __func__);
return FALSE;
}
if (AUTH_AREA_FIRST_OFFSET (command) > command->buffer_size) {
g_warning ("%s: auth area begins after end of buffer", __func__);
return FALSE;
}
if (AUTH_AREA_END_OFFSET (command) > command->buffer_size) {
g_warning ("%s: command buffer size insufficient to iterate all auths",
__func__);
return FALSE;
}
for (offset = AUTH_AREA_FIRST_OFFSET (command);
offset < AUTH_AREA_END_OFFSET (command);
offset = AUTH_AUTH_BUF_END_OFFSET (command, offset))
{
size_t offset_tmp = offset;
callback (&offset_tmp, user_data);
}
return TRUE;
}
tpm2-abrmd-3.0.0/src/tpm2-command.h 0000664 0000000 0000000 00000010111 14343407245 0016715 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef TPM2_COMMAND_H
#define TPM2_COMMAND_H
#include
#include
#include "connection.h"
G_BEGIN_DECLS
typedef struct _Tpm2CommandClass {
GObjectClass parent;
} Tpm2CommandClass;
typedef struct _Tpm2Command {
GObject parent_instance;
TPMA_CC attributes;
Connection *connection;
guint8 *buffer;
size_t buffer_size;
} Tpm2Command;
#include "command-attrs.h"
#define TYPE_TPM2_COMMAND (tpm2_command_get_type ())
#define TPM2_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TPM2_COMMAND, Tpm2Command))
#define TPM2_COMMAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_TPM2_COMMAND, Tpm2CommandClass))
#define IS_TPM2_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TPM2_COMMAND))
#define IS_TPM2_COMMAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_TPM2_COMMAND))
#define TPM2_COMMAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_TPM2_COMMAND, Tpm2CommandClass))
#define TPM2_COMMAND_MAX_HANDLES 3
GType tpm2_command_get_type (void);
Tpm2Command* tpm2_command_new (Connection *connection,
guint8 *buffer,
size_t size,
TPMA_CC attrs);
Tpm2Command* tpm2_command_new_context_save (TPM2_HANDLE);
Tpm2Command* tpm2_command_new_context_load (uint8_t *buf,
size_t size);
TPMA_CC tpm2_command_get_attributes (Tpm2Command *command);
TPMA_SESSION tpm2_command_get_auth_attrs (Tpm2Command *command,
size_t auth_offset);
TPM2_HANDLE tpm2_command_get_auth_handle (Tpm2Command *command,
size_t offset);
guint8* tpm2_command_get_buffer (Tpm2Command *command);
TPM2_CC tpm2_command_get_code (Tpm2Command *command);
guint8 tpm2_command_get_handle_count (Tpm2Command *command);
TPM2_HANDLE tpm2_command_get_handle (Tpm2Command *command,
guint8 handle_number);
gboolean tpm2_command_get_handles (Tpm2Command *command,
TPM2_HANDLE handles[],
size_t *count);
gboolean tpm2_command_set_handle (Tpm2Command *command,
TPM2_HANDLE handle,
guint8 handle_number);
TSS2_RC tpm2_command_get_flush_handle (Tpm2Command *command,
TPM2_HANDLE *handle);
guint32 tpm2_command_get_size (Tpm2Command *command);
TPMI_ST_COMMAND_TAG tpm2_command_get_tag (Tpm2Command *command);
Connection* tpm2_command_get_connection (Tpm2Command *command);
TPM2_CAP tpm2_command_get_cap (Tpm2Command *command);
UINT32 tpm2_command_get_prop (Tpm2Command *command);
UINT32 tpm2_command_get_prop_count (Tpm2Command *command);
gboolean tpm2_command_has_auths (Tpm2Command *command);
UINT32 tpm2_command_get_auths_size (Tpm2Command *command);
gboolean tpm2_command_foreach_auth (Tpm2Command *command,
GFunc func,
gpointer user_data);
G_END_DECLS
#endif /* TPM2_COMMAND_H */
tpm2-abrmd-3.0.0/src/tpm2-header.c 0000664 0000000 0000000 00000007514 14343407245 0016537 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include "tpm2-header.h"
/**
* Extract the 'tag' field from the tpm command header. This is a
* TPMI_ST_COMMAND_TAG, which is a fancy word for UINT16
*/
TPMI_ST_COMMAND_TAG
get_command_tag (uint8_t *command_header)
{
return be16toh (*(TPMI_ST_COMMAND_TAG*)command_header);
}
/**
* Get the commandSize field from the tpm command buffer supplied in the
* 'command_header' parameter. We assume that the command buffer is a valid TPM
* command (or response) and so it must be at least COMMAND_HEADER_SIZE
* bytes long.
* NOTE: The TPM2 architecture spec states in section 18.2.2 that the
* commandSize field from the header is the total size of the
* command. This includes the header itself.
*/
UINT32
get_command_size (uint8_t *command_header)
{
return be32toh (*(UINT32*)(command_header + sizeof (TPMI_ST_COMMAND_TAG)));
}
/**
* Extract the commandCode from a tpm command buffer. It is the 3rd field
* in the header so we do some pointer math to get the offset.
*/
TPM2_CC
get_command_code (uint8_t *command_header)
{
return be32toh (*(TPM2_CC*)(command_header + sizeof (TPMI_ST_COMMAND_TAG) + sizeof (UINT32)));
}
/**
* Get the 'tag' field from a TPM response buffer. This is the first field
* in the header.
*/
TPM2_ST
get_response_tag (uint8_t *response_header)
{
return be16toh (*(TPM2_ST*)response_header);
}
/*
* Set the 'tag' field in a response buffer.
*/
void
set_response_tag (uint8_t *response_header,
TPM2_ST tag)
{
*(TPM2_ST*)response_header = htobe16 (tag);
}
/**
* Get the 'responseSize' field from a TPM response header.
*/
UINT32
get_response_size (uint8_t *response_header)
{
return be32toh (*(UINT32*)(response_header + sizeof (TPM2_ST)));
}
/*
* Set the 'size' field in a response buffer.
*/
void
set_response_size (uint8_t *response_header,
UINT32 size)
{
*(UINT32*)(response_header + sizeof (TPM2_ST)) = htobe32 (size);
}
/**
* Get the responseCode field from the TPM response buffer supplied in the
* 'response_header' parameter. We assume that the response buffer is a valid TPM
* response header so it must be at least RESPONSE_HEADER_SIZE bytes long.
*/
TSS2_RC
get_response_code (uint8_t *response_header)
{
return be32toh (*(TSS2_RC*)(response_header + sizeof (TPM2_ST) + sizeof (UINT32)));
}
/*
* Set the responseCode field in a TPM response buffer.
*/
void
set_response_code (uint8_t *response_header,
TSS2_RC rc)
{
*(TSS2_RC*)(response_header + sizeof (TPM2_ST) + sizeof (UINT32)) = \
htobe32 (rc);
}
/*
* create response object, populate with SessionEntry member
* 'context_client'
*/
TSS2_RC
tpm2_header_init (uint8_t *buf,
size_t buf_size,
TPM2_ST tag,
UINT32 size,
TSS2_RC code)
{
size_t offset = 0;
TSS2_RC rc;
g_assert (buf_size >= TPM_HEADER_SIZE);
rc = Tss2_MU_TPM2_ST_Marshal (tag, buf, buf_size, &offset);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: failed to write TPM2_ST tag to response header: 0x%"
PRIx32, __func__, rc);
return rc;
}
rc = Tss2_MU_UINT32_Marshal (size, buf, buf_size, &offset);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: failed to write UINT32 size to response header: 0x%"
PRIx32, __func__, rc);
return rc;
}
rc = Tss2_MU_UINT32_Marshal (code, buf, buf_size, &offset);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: failed to write UINT32 responseCode to response header: 0x%"
PRIx32, __func__, rc);
}
return rc;
}
tpm2-abrmd-3.0.0/src/tpm2-header.h 0000664 0000000 0000000 00000003761 14343407245 0016544 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef TPM2_HEADER_H
#define TPM2_HEADER_H
#include
#include
#if defined(__FreeBSD__)
#include
#else
#include
#endif
/* A convenience macro to get us the size of the TPM header. */
#define TPM_HEADER_SIZE (UINT32)(sizeof (TPM2_ST) + sizeof (UINT32) + sizeof (TPM2_CC))
/*
* A generic tpm header structure, could be command or response.
* NOTE: Do not expect sizeof (tpm_header_t) to get your the size of the
* header. The compiler pads this structure. Use the macro above.
*/
typedef struct {
TPM2_ST tag;
UINT32 size;
UINT32 code;
} tpm_header_t;
TPMI_ST_COMMAND_TAG get_command_tag (uint8_t *command_header);
UINT32 get_command_size (uint8_t *command_header);
TPM2_CC get_command_code (uint8_t *command_header);
TPM2_ST get_response_tag (uint8_t *response_header);
void set_response_tag (uint8_t *response_header,
TPM2_ST tag);
UINT32 get_response_size (uint8_t *response_header);
void set_response_size (uint8_t *response_header,
UINT32 size);
TSS2_RC get_response_code (uint8_t *response_header);
void set_response_code (uint8_t *response_header,
TSS2_RC rc);
TSS2_RC tpm2_header_init (uint8_t *buf,
size_t buf_size,
TPM2_ST tag,
UINT32 size,
TSS2_RC code);
#endif /* TPM2_HEADER_H */
tpm2-abrmd-3.0.0/src/tpm2-response.c 0000664 0000000 0000000 00000033014 14343407245 0017137 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include "tpm2-header.h"
#include "tpm2-response.h"
#include "util.h"
#define HANDLE_AREA_OFFSET TPM_HEADER_SIZE
#define HANDLE_OFFSET (HANDLE_AREA_OFFSET)
#define HANDLE_END_OFFSET (HANDLE_OFFSET + sizeof (TPM2_HANDLE))
#define HANDLE_GET(buffer) (*(TPM2_HANDLE*)(&buffer [HANDLE_OFFSET]))
#define TPM_RESPONSE_TAG(buffer) (*(TPM2_ST*)buffer)
#define TPM_RESPONSE_SIZE(buffer) (*(guint32*)(buffer + \
sizeof (TPM2_ST)))
#define TPM_RESPONSE_CODE(buffer) (*(TSS2_RC*)(buffer + \
sizeof (TPM2_ST) + \
sizeof (UINT32)))
G_DEFINE_TYPE (Tpm2Response, tpm2_response, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_ATTRIBUTES,
PROP_SESSION,
PROP_BUFFER,
PROP_BUFFER_SIZE,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/**
* GObject property setter.
*/
static void
tpm2_response_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
Tpm2Response *self = TPM2_RESPONSE (object);
switch (property_id) {
case PROP_ATTRIBUTES:
self->attributes = (TPMA_CC)g_value_get_uint (value);
break;
case PROP_BUFFER:
if (self->buffer != NULL) {
g_warning (" buffer already set");
break;
}
self->buffer = (guint8*)g_value_get_pointer (value);
break;
case PROP_BUFFER_SIZE:
self->buffer_size = g_value_get_uint (value);
break;
case PROP_SESSION:
if (self->connection != NULL) {
g_warning (" connection already set");
break;
}
self->connection = g_value_get_object (value);
if (self->connection) {
g_object_ref (self->connection);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* GObject property getter.
*/
static void
tpm2_response_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
Tpm2Response *self = TPM2_RESPONSE (object);
switch (property_id) {
case PROP_ATTRIBUTES:
g_value_set_uint (value, self->attributes);
break;
case PROP_BUFFER:
g_value_set_pointer (value, self->buffer);
break;
case PROP_BUFFER_SIZE:
g_value_set_uint (value, self->buffer_size);
break;
case PROP_SESSION:
g_value_set_object (value, self->connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
tpm2_response_dispose (GObject *obj)
{
Tpm2Response *self = TPM2_RESPONSE (obj);
g_clear_object (&self->connection);
G_OBJECT_CLASS (tpm2_response_parent_class)->dispose (obj);
}
/**
* override the parent finalize method so we can free the data associated with
* the DataMessage instance.
*/
static void
tpm2_response_finalize (GObject *obj)
{
Tpm2Response *self = TPM2_RESPONSE (obj);
g_debug ("tpm2_response_finalize");
g_clear_pointer (&self->buffer, g_free);
G_OBJECT_CLASS (tpm2_response_parent_class)->finalize (obj);
}
static void
tpm2_response_init (Tpm2Response *response)
{
UNUSED_PARAM(response);
/* noop */
}
/**
* Boilerplate GObject initialization. Get a pointer to the parent class,
* setup a finalize function.
*/
static void
tpm2_response_class_init (Tpm2ResponseClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (tpm2_response_parent_class == NULL)
tpm2_response_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = tpm2_response_dispose;
object_class->finalize = tpm2_response_finalize;
object_class->get_property = tpm2_response_get_property;
object_class->set_property = tpm2_response_set_property;
obj_properties [PROP_ATTRIBUTES] =
g_param_spec_uint ("attributes",
"TPMA_CC",
"Attributes for command.",
0,
UINT32_MAX,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_BUFFER] =
g_param_spec_pointer ("buffer",
"TPM2 response buffer",
"memory buffer holding a TPM2 response",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_BUFFER_SIZE] =
g_param_spec_uint ("buffer-size",
"sizeof command buffer",
"size of buffer holding the TPM2 command",
0,
UTIL_BUF_MAX,
0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_SESSION] =
g_param_spec_object ("connection",
"Connection object",
"The Connection object that sent the response",
TYPE_CONNECTION,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
/**
* Boilerplate constructor, but some GObject properties would be nice.
*/
Tpm2Response*
tpm2_response_new (Connection *connection,
guint8 *buffer,
size_t buffer_size,
TPMA_CC attributes)
{
return TPM2_RESPONSE (g_object_new (TYPE_TPM2_RESPONSE,
"attributes", attributes,
"buffer", buffer,
"buffer-size", buffer_size,
"connection", connection,
NULL));
}
void
response_buffer_set_rc(uint8_t buffer[TPM_HEADER_SIZE],
TSS2_RC rc) {
TPM_RESPONSE_TAG (buffer) = htobe16 (TPM2_ST_NO_SESSIONS);
TPM_RESPONSE_SIZE (buffer) = htobe32 (TPM_RESPONSE_HEADER_SIZE);
TPM_RESPONSE_CODE (buffer) = htobe32 (rc);
}
/**
* This is a convenience wrapper that is used to create an error response
* where all we need is a header with the RC set to something specific.
*/
Tpm2Response*
tpm2_response_new_rc (Connection *connection,
TSS2_RC rc)
{
guint8 *buffer;
buffer = calloc (1, TPM_RESPONSE_HEADER_SIZE);
if (buffer == NULL) {
g_warning ("tpm2_response_new_rc: failed to allocate 0x%zx"
" bytes for response: errno: %d: %s",
TPM_RESPONSE_HEADER_SIZE, errno, strerror (errno));
return NULL;
}
response_buffer_set_rc (buffer, rc);
return tpm2_response_new (connection, buffer, be32toh (TPM_RESPONSE_SIZE (buffer)), (TPMA_CC){ 0 });
}
/*
* Create a new Tpm2Response object with a message body / buffer formatted
* for the response to the TPM2_ContextLoad command. This command has no
* session and the body of the response is set to the parameter TPM2_HANDLE.
*/
Tpm2Response*
tpm2_response_new_context_load (Connection *connection,
SessionEntry *entry)
{
Tpm2Response *response = NULL;
size_t offset = TPM_HEADER_SIZE;
/* allocate buffer be large enough to hold TPM2_ContextSave response */
uint8_t *buf = g_malloc0 (TPM_HEADER_SIZE + sizeof (TPM2_HANDLE));
TSS2_RC rc;
rc = Tss2_MU_TPM2_HANDLE_Marshal (session_entry_get_handle (entry),
buf,
TPM_HEADER_SIZE + sizeof (TPM2_HANDLE),
&offset);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: Failed to write TPMS_CONTEXT to response header: 0x%"
PRIx32, __func__, rc);
goto out;
}
/* offset now has size of response */
rc = tpm2_header_init (buf, offset, TPM2_ST_NO_SESSIONS, offset, TSS2_RC_SUCCESS);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: Failed to initialize header: 0x%" PRIx32,
__func__, rc);
goto out;
}
response = tpm2_response_new (connection, buf, offset, 0x10000161);
out:
if (response == NULL) {
g_free (buf);
}
return response;
}
/*
* Create a new Tpm2Response object with a message body / buffer formatted
* for the response to the TPM2_ContextSave command. This command has no
* session and the body of the response is set to the parameter TPMS_CONTEXT
* structure. This Tpm2Response will be sent to the client so we ensure that
* the 'context_client' field is sent.
*/
Tpm2Response*
tpm2_response_new_context_save (Connection *connection,
SessionEntry *entry)
{
Tpm2Response *response = NULL;
size_buf_t *size_buf;
/* allocate buffer be large enough to hold TPM2_ContextSave response */
uint8_t *buf;
TSS2_RC rc;
size_buf = session_entry_get_context_client (entry);
buf = g_malloc0 (TPM_HEADER_SIZE + size_buf->size);
memcpy (&buf[TPM_HEADER_SIZE], size_buf->buf, size_buf->size);
/* offset now has size of response */
rc = tpm2_header_init (buf,
TPM_HEADER_SIZE + size_buf->size,
TPM2_ST_NO_SESSIONS,
TPM_HEADER_SIZE + size_buf->size,
TSS2_RC_SUCCESS);
if (rc != TSS2_RC_SUCCESS) {
g_warning ("%s: Failed to initialize header: 0x%" PRIx32,
__func__, rc);
goto out;
}
response = tpm2_response_new (connection, buf, TPM_HEADER_SIZE + size_buf->size, 0x02000162);
out:
if (response == NULL) {
g_free (buf);
}
return response;
}
/* Simple "getter" to expose the attributes associated with the command. */
TPMA_CC
tpm2_response_get_attributes (Tpm2Response *response)
{
return response->attributes;
}
/**
*/
guint8*
tpm2_response_get_buffer (Tpm2Response *response)
{
return response->buffer;
}
/**
*/
TSS2_RC
tpm2_response_get_code (Tpm2Response *response)
{
return be32toh (TPM_RESPONSE_CODE (response->buffer));
}
/**
*/
guint32
tpm2_response_get_size (Tpm2Response *response)
{
return be32toh (TPM_RESPONSE_SIZE (response->buffer));
}
/**
*/
TPM2_ST
tpm2_response_get_tag (Tpm2Response *response)
{
return be16toh (TPM_RESPONSE_TAG (response->buffer));
}
/*
* Return the Connection object associated with this Tpm2Response. This
* is the Connection object representing the client that should receive
* this response. The reference count on this object is incremented before
* the object is returned to the caller. The caller is responsible for
* decrementing the reference count when it is done using the connection
* object.
*/
Connection*
tpm2_response_get_connection (Tpm2Response *response)
{
if (response->connection) {
g_object_ref (response->connection);
}
return response->connection;
}
/*
* Return the number of handles in the response. For a response to contain
* a handle it must:
* 1) the size of the response must be larger than the headder size
* 2) the response code must indicate SUCCESS
* 3) the associated attributes must have the TPMA_CC_RHANDLE bit set
* If all of these conditions are met then the response has a handle in it.
* Otherwise it does now.
*/
gboolean
tpm2_response_has_handle (Tpm2Response *response)
{
g_debug ("%s", __func__);
if (tpm2_response_get_size (response) > TPM_HEADER_SIZE &&
tpm2_response_get_code (response) == TSS2_RC_SUCCESS &&
tpm2_response_get_attributes (response) & TPMA_CC_RHANDLE)
{
return TRUE;
}
return FALSE;
}
/*
* Return the handle from the response handle area. Always check to be sure
* the response has a handle in it before calling this function. If the
* Tpm2Response has no handle in the handle area the return value from this
* function will be indeterminate.
*/
TPM2_HANDLE
tpm2_response_get_handle (Tpm2Response *response)
{
if (response == NULL) {
g_error ("%s passed NULL parameter", __func__);
}
if (HANDLE_END_OFFSET > response->buffer_size) {
g_warning ("%s: insufficient buffer to get handle", __func__);
return 0;
}
return be32toh (HANDLE_GET (response->buffer));
}
/*
*/
void
tpm2_response_set_handle (Tpm2Response *response,
TPM2_HANDLE handle)
{
if (response == NULL) {
g_error ("%s passed NULL parameter", __func__);
}
if (HANDLE_END_OFFSET > response->buffer_size) {
g_warning ("%s: insufficient buffer to set handle", __func__);
return;
}
HANDLE_GET (response->buffer) = htobe32 (handle);
}
/*
* Return the type of the handle from the Tpm2Response object.
*/
TPM2_HT
tpm2_response_get_handle_type (Tpm2Response *response)
{
/*
* Explicit cast required to keep compiler type checking happy. The
* shift operation preserves the 8bits we want to return but compiler
* must consider the result a TPM2_HANDLE (32bits).
*/
return (TPM2_HT)(tpm2_response_get_handle (response) >> TPM2_HR_SHIFT);
}
tpm2-abrmd-3.0.0/src/tpm2-response.h 0000664 0000000 0000000 00000006036 14343407245 0017150 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef TPM2_RESPONSE_H
#define TPM2_RESPONSE_H
#include
#include
#include "connection.h"
#include "session-entry.h"
#include "tpm2-header.h"
G_BEGIN_DECLS
typedef struct _Tpm2ResponseClass {
GObjectClass parent;
} Tpm2ResponseClass;
typedef struct _Tpm2Response {
GObject parent_instance;
Connection *connection;
guint8 *buffer;
size_t buffer_size;
TPMA_CC attributes;
} Tpm2Response;
#define TPM_RESPONSE_HEADER_SIZE (sizeof (TPM2_ST) + sizeof (UINT32) + sizeof (TPM2_RC))
#define TYPE_TPM2_RESPONSE (tpm2_response_get_type ())
#define TPM2_RESPONSE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TPM2_RESPONSE, Tpm2Response))
#define TPM2_RESPONSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_TPM2_RESPONSE, Tpm2ResponseClass))
#define IS_TPM2_RESPONSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TPM2_RESPONSE))
#define IS_TPM2_RESPONSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_TPM2_RESPONSE))
#define TPM2_RESPONSE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_TPM2_RESPONSE, Tpm2ResponseClass))
GType tpm2_response_get_type (void);
Tpm2Response* tpm2_response_new (Connection *connection,
guint8 *buffer,
size_t buffer_size,
TPMA_CC attributes);
Tpm2Response* tpm2_response_new_rc (Connection *connection,
TSS2_RC rc);
Tpm2Response* tpm2_response_new_context_save (Connection *connection,
SessionEntry *entry);
Tpm2Response* tpm2_response_new_context_load (Connection *connection,
SessionEntry *entry);
TPMA_CC tpm2_response_get_attributes (Tpm2Response *response);
guint8* tpm2_response_get_buffer (Tpm2Response *response);
TSS2_RC tpm2_response_get_code (Tpm2Response *response);
TPM2_HANDLE tpm2_response_get_handle (Tpm2Response *response);
TPM2_HT tpm2_response_get_handle_type (Tpm2Response *response);
gboolean tpm2_response_has_handle (Tpm2Response *response);
guint32 tpm2_response_get_size (Tpm2Response *response);
TPM2_ST tpm2_response_get_tag (Tpm2Response *response);
Connection* tpm2_response_get_connection (Tpm2Response *response);
void tpm2_response_set_handle (Tpm2Response *response,
TPM2_HANDLE handle);
G_END_DECLS
void
response_buffer_set_rc(uint8_t buffer[TPM_HEADER_SIZE],
TSS2_RC rc);
#endif /* TPM2_RESPONSE_H */
tpm2-abrmd-3.0.0/src/tpm2.c 0000664 0000000 0000000 00000052515 14343407245 0015312 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include "tabrmd.h"
#include "tpm2.h"
#include "tcti.h"
#include "tpm2-command.h"
#include "tpm2-response.h"
#include "util.h"
G_DEFINE_TYPE (Tpm2, tpm2, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_SAPI_CTX,
PROP_TCTI,
N_PROPERTIES
};
static GParamSpec *obj_properties [N_PROPERTIES] = { NULL, };
/**
* GObject property setter.
*/
static void
tpm2_set_property (GObject *object,
guint property_id,
GValue const *value,
GParamSpec *pspec)
{
Tpm2 *self = TPM2 (object);
g_debug (__func__);
switch (property_id) {
case PROP_SAPI_CTX:
self->sapi_context = g_value_get_pointer (value);
break;
case PROP_TCTI:
self->tcti = g_value_get_object (value);
g_object_ref (self->tcti);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* GObject property getter.
*/
static void
tpm2_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
Tpm2 *self = TPM2 (object);
g_debug (__func__);
switch (property_id) {
case PROP_SAPI_CTX:
g_value_set_pointer (value, self->sapi_context);
break;
case PROP_TCTI:
g_value_set_object (value, self->tcti);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/*
* Dispose function: release references to gobjects. We also free the SAPI
* context here as well. Typically freeing this data would be done in the
* finalize function but it can't be used w/o the underlying TCTI.
*/
static void
tpm2_dispose (GObject *obj)
{
Tpm2 *self = TPM2 (obj);
if (self->sapi_context != NULL) {
Tss2_Sys_Finalize (self->sapi_context);
}
g_clear_pointer (&self->sapi_context, g_free);
g_clear_object (&self->tcti);
G_OBJECT_CLASS (tpm2_parent_class)->dispose (obj);
}
/*
* G_DEFINE_TYPE requires an instance init even though we don't use it.
*/
static void
tpm2_init (Tpm2 *tpm2)
{
UNUSED_PARAM(tpm2);
/* noop */
}
/**
* GObject class initialization function. This function boils down to:
* - Setting up the parent class.
* - Set dispose, property get/set.
* - Install properties.
*/
static void
tpm2_class_init (Tpm2Class *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
if (tpm2_parent_class == NULL)
tpm2_parent_class = g_type_class_peek_parent (klass);
object_class->dispose = tpm2_dispose;
object_class->get_property = tpm2_get_property;
object_class->set_property = tpm2_set_property;
obj_properties [PROP_SAPI_CTX] =
g_param_spec_pointer ("sapi-ctx",
"SAPI context",
"TSS2_SYS_CONTEXT instance",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
obj_properties [PROP_TCTI] =
g_param_spec_object ("tcti",
"Tcti object",
"Tcti for communication with TPM",
TYPE_TCTI,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
#define SUPPORTED_ABI_VERSION \
{ \
.tssCreator = 1, \
.tssFamily = 2, \
.tssLevel = 1, \
.tssVersion = 108, \
}
TSS2_SYS_CONTEXT*
sapi_context_init (Tcti *tcti)
{
TSS2_SYS_CONTEXT *sapi_context;
TSS2_TCTI_CONTEXT *tcti_context;
TSS2_RC rc;
size_t size;
TSS2_ABI_VERSION abi_version = SUPPORTED_ABI_VERSION;
assert (tcti != NULL);
tcti_context = tcti_peek_context (tcti);
assert (tcti_context != NULL);
size = Tss2_Sys_GetContextSize (0);
g_debug ("Allocating 0x%zx bytes for SAPI context", size);
/* NOTE: g_malloc0 will terminate the program if allocation fails */
sapi_context = (TSS2_SYS_CONTEXT*)g_malloc0 (size);
rc = Tss2_Sys_Initialize (sapi_context, size, tcti_context, &abi_version);
if (rc != TSS2_RC_SUCCESS) {
g_free (sapi_context);
RC_WARN ("Tss2_Sys_Initialize", rc);
return NULL;
}
return sapi_context;
}
TSS2_RC
tpm2_send_tpm_startup (Tpm2 *tpm2)
{
TSS2_RC rc;
assert (tpm2 != NULL);
rc = Tss2_Sys_Startup (tpm2->sapi_context, TPM2_SU_CLEAR);
if (rc != TSS2_RC_SUCCESS && rc != TPM2_RC_INITIALIZE)
RC_WARN ("Tss2_Sys_Startup", rc);
else
rc = TSS2_RC_SUCCESS;
return rc;
}
/**
* This is a very thin wrapper around the mutex mediating access to the
* TSS2_SYS_CONTEXT. It locks the mutex.
*
* NOTE: Any errors locking the mutex are fatal and will cause the program
* to halt.
*/
void
tpm2_lock (Tpm2 *tpm2)
{
gint error;
assert (tpm2 != NULL);
error = pthread_mutex_lock (&tpm2->sapi_mutex);
if (error != 0) {
switch (error) {
case EINVAL:
g_error ("Tpm2: attempted to lock uninitialized mutex");
break;
default:
g_error ("Tpm2: unknown error attempting to lock SAPI "
"mutex: 0x%x", error);
break;
}
}
}
/**
* This is a very thin wrapper around the mutex mediating access to the
* TSS2_SYS_CONTEXT. It unlocks the mutex.
*
* NOTE: Any errors locking the mutex are fatal and will cause the program
* to halt.
*/
void
tpm2_unlock (Tpm2 *tpm2)
{
gint error;
assert (tpm2 != NULL);
error= pthread_mutex_unlock (&tpm2->sapi_mutex);
if (error != 0) {
switch (error) {
case EINVAL:
g_error ("Tpm2: attempted to unlock uninitialized mutex");
break;
default:
g_error ("Tpm2: unknown error attempting to unlock SAPI "
"mutex: 0x%x", error);
break;
}
}
}
/**
* Query the TPM for fixed (TPM2_PT_FIXED) TPM properties.
* This function is intended for internal use only. The caller MUST
* hold the sapi_mutex lock before calling.
*/
TSS2_RC
tpm2_get_tpm_properties_fixed (TSS2_SYS_CONTEXT *sapi_context,
TPMS_CAPABILITY_DATA *capability_data)
{
TSS2_RC rc;
TPMI_YES_NO more_data;
assert (sapi_context != NULL);
assert (capability_data != NULL);
g_debug ("tpm2_get_tpm_properties_fixed");
rc = Tss2_Sys_GetCapability (sapi_context,
NULL,
TPM2_CAP_TPM_PROPERTIES,
/* get TPM2_PT_FIXED TPM property group */
TPM2_PT_FIXED,
/* get all properties in the group */
TPM2_MAX_TPM_PROPERTIES,
&more_data,
capability_data,
NULL);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_GetCapability", rc);
return rc;
}
/* sanity check the property returned */
if (capability_data->capability != TPM2_CAP_TPM_PROPERTIES)
g_warning ("GetCapability returned wrong capability: 0x%x",
capability_data->capability);
return rc;
}
/**
* Query the TM for a specific tagged property from the collection of
* fixed TPM properties. If the requested property is found then the
* 'value' parameter will be set accordingly. If no such property exists
* then TSS2_TABRMD_BAD_VALUE will be returned.
*/
TSS2_RC
tpm2_get_fixed_property (Tpm2 *tpm2,
TPM2_PT property,
guint32 *value)
{
unsigned int i;
assert (tpm2 != NULL);
assert (value != NULL);
if (tpm2->properties_fixed.data.tpmProperties.count == 0) {
return TSS2_RESMGR_RC_INTERNAL_ERROR;
}
for (i = 0; i < tpm2->properties_fixed.data.tpmProperties.count; ++i) {
if (tpm2->properties_fixed.data.tpmProperties.tpmProperty[i].property == property) {
*value = tpm2->properties_fixed.data.tpmProperties.tpmProperty[i].value;
return TSS2_RC_SUCCESS;
}
}
return TSS2_RESMGR_RC_BAD_VALUE;
}
/*
* This function exposes the underlying SAPI context in the Tpm2.
* It locks the Tpm2 object and returns the SAPI context for use
* by the caller. Do not call this function if you already hold the
* Tpm2 lock. If you do you'll deadlock.
* When done with the context the caller must unlock the Tpm2.
*/
TSS2_SYS_CONTEXT*
tpm2_lock_sapi (Tpm2 *tpm2)
{
assert (tpm2 != NULL);
assert (tpm2->sapi_context != NULL);
tpm2_lock (tpm2);
return tpm2->sapi_context;
}
/**
* Return the TPM2_PT_TPM2_MAX_RESPONSE_SIZE fixed TPM property.
*/
TSS2_RC
tpm2_get_max_response (Tpm2 *tpm2,
guint32 *value)
{
return tpm2_get_fixed_property (tpm2,
TPM2_PT_MAX_RESPONSE_SIZE,
value);
}
/*
* Get a response buffer from the TPM. Return the TSS2_RC through the
* 'rc' parameter. Returns a buffer (that must be freed by the caller)
* containing the response from the TPM. Determine the size of the buffer
* by reading the size field from the TPM command header.
*/
static TSS2_RC
tpm2_get_response (Tpm2 *tpm2,
uint8_t **buffer,
size_t *buffer_size)
{
TSS2_RC rc;
guint32 max_size;
assert (tpm2 != NULL);
assert (buffer != NULL);
assert (buffer_size != NULL);
rc = tpm2_get_max_response (tpm2, &max_size);
if (rc != TSS2_RC_SUCCESS)
return rc;
*buffer = calloc (1, max_size);
if (*buffer == NULL) {
g_warning ("failed to allocate buffer for Tpm2Response: %s",
strerror (errno));
return RM_RC (TPM2_RC_MEMORY);
}
*buffer_size = max_size;
rc = tcti_receive (tpm2->tcti, buffer_size, *buffer, TSS2_TCTI_TIMEOUT_BLOCK);
if (rc != TSS2_RC_SUCCESS) {
free (*buffer);
return rc;
}
*buffer = realloc (*buffer, *buffer_size);
return rc;
}
/**
* In the most simple case the caller will want to send just a single
* command represented by a Tpm2Command object. The response is passed
* back as the return value. The response code from the TCTI (not the TPM)
* is returned through the 'rc' out parameter.
* The caller MUST NOT hold the lock when calling. This function will take
* the lock for itself.
* Additionally this function *WILL ONLY* return a NULL Tpm2Response
* pointer if it's unable to allocate memory for the object. In all other
* error cases this function will create a Tpm2Response object with the
* appropriate RC populated.
*/
Tpm2Response*
tpm2_send_command (Tpm2 *tpm2,
Tpm2Command *command,
TSS2_RC *rc)
{
Tpm2Response *response = NULL;
Connection *connection = NULL;
guint8 *buffer = NULL;
size_t buffer_size = 0;
g_debug (__func__);
assert (tpm2 != NULL);
assert (command != NULL);
assert (rc != NULL);
tpm2_lock (tpm2);
*rc = tcti_transmit (tpm2->tcti,
tpm2_command_get_size (command),
tpm2_command_get_buffer (command));
if (*rc != TSS2_RC_SUCCESS)
goto unlock_out;
*rc = tpm2_get_response (tpm2, &buffer, &buffer_size);
if (*rc != TSS2_RC_SUCCESS) {
goto unlock_out;
}
tpm2_unlock (tpm2);
connection = tpm2_command_get_connection (command);
response = tpm2_response_new (connection,
buffer,
buffer_size,
tpm2_command_get_attributes (command));
g_clear_object (&connection);
return response;
unlock_out:
tpm2_unlock (tpm2);
if (!connection)
connection = tpm2_command_get_connection (command);
response = tpm2_response_new_rc (connection, *rc);
g_object_unref (connection);
return response;
}
/**
* Create new TPM access tpm2 (TPM2) object. This includes
* using the provided TCTI to send the TPM the startup command and
* creating the TCTI mutex.
*/
Tpm2*
tpm2_new (Tcti *tcti)
{
Tpm2 *tpm2;
TSS2_SYS_CONTEXT *sapi_context;
assert (tcti != NULL);
sapi_context = sapi_context_init (tcti);
tpm2 = TPM2 (g_object_new (TYPE_TPM2,
"sapi-ctx", sapi_context,
"tcti", tcti,
NULL));
return tpm2;
}
/*
* Initialize the Tpm2. This is all about initializing internal data
* that normally we would want to do in a constructor. But since this
* initialization requires reaching out to the TPM and could fail we don't
* want to do it in the constructor / _new function. So we put it here in
* an explicit _init function that must be executed after the object has
* been instantiated.
*/
TSS2_RC
tpm2_init_tpm (Tpm2 *tpm2)
{
TSS2_RC rc;
g_debug (__func__);
assert (tpm2 != NULL);
if (tpm2->initialized)
return TSS2_RC_SUCCESS;
pthread_mutex_init (&tpm2->sapi_mutex, NULL);
rc = tpm2_send_tpm_startup (tpm2);
if (rc != TSS2_RC_SUCCESS)
goto out;
rc = tpm2_get_tpm_properties_fixed (tpm2->sapi_context,
&tpm2->properties_fixed);
if (rc != TSS2_RC_SUCCESS)
goto out;
tpm2->initialized = true;
out:
return rc;
}
/*
* Query the TPM for the current number of loaded transient objects.
*/
TSS2_RC
tpm2_get_trans_object_count (Tpm2 *tpm2,
uint32_t *count)
{
TSS2_RC rc = TSS2_RC_SUCCESS;
TSS2_SYS_CONTEXT *sapi_context;
TPMI_YES_NO more_data;
TPMS_CAPABILITY_DATA capability_data = { 0, };
assert (tpm2 != NULL);
assert (count != NULL);
sapi_context = tpm2_lock_sapi (tpm2);
/*
* GCC gets confused by the TPM2_TRANSIENT_FIRST constant being used for
* the 4th parameter. It assumes that it's a signed type which causes
* -Wsign-conversion to complain. Casting to UINT32 is all we can do.
*/
rc = Tss2_Sys_GetCapability (sapi_context,
NULL,
TPM2_CAP_HANDLES,
(UINT32)TPM2_TRANSIENT_FIRST,
TPM2_TRANSIENT_LAST - TPM2_TRANSIENT_FIRST,
&more_data,
&capability_data,
NULL);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_GetCapability", rc);
goto out;
}
*count = capability_data.data.handles.count;
out:
tpm2_unlock (tpm2);
return rc;
}
TSS2_RC
tpm2_context_load (Tpm2 *tpm2,
TPMS_CONTEXT *context,
TPM2_HANDLE *handle)
{
TSS2_RC rc;
TSS2_SYS_CONTEXT *sapi_context;
assert (tpm2 != NULL);
assert (context != NULL);
assert (handle != NULL);
sapi_context = tpm2_lock_sapi (tpm2);
rc = Tss2_Sys_ContextLoad (sapi_context, context, handle);
tpm2_unlock (tpm2);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_ContextLoad", rc);
}
return rc;
}
/*
* This function is a simple wrapper around the TPM2_ContextSave command.
* It will save the context associated with the provided handle, returning
* the TPMS_CONTEXT to the caller. The response code returned will be
* TSS2_RC_SUCCESS or an RC indicating failure from the TPM.
*/
TSS2_RC
tpm2_context_save (Tpm2 *tpm2,
TPM2_HANDLE handle,
TPMS_CONTEXT *context)
{
TSS2_RC rc;
TSS2_SYS_CONTEXT *sapi_context;
assert (tpm2 != NULL);
assert (context != NULL);
g_debug ("tpm2_context_save: handle 0x%08" PRIx32, handle);
sapi_context = tpm2_lock_sapi (tpm2);
rc = Tss2_Sys_ContextSave (sapi_context, handle, context);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_ContextSave", rc);
}
tpm2_unlock (tpm2);
return rc;
}
/*
* This function is a simple wrapper around the TPM2_FlushContext command.
*/
TSS2_RC
tpm2_context_flush (Tpm2 *tpm2,
TPM2_HANDLE handle)
{
TSS2_RC rc;
TSS2_SYS_CONTEXT *sapi_context;
assert (tpm2 != NULL);
g_debug ("tpm2_context_flush: handle 0x%08" PRIx32, handle);
sapi_context = tpm2_lock_sapi (tpm2);
rc = Tss2_Sys_FlushContext (sapi_context, handle);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_FlushContext", rc);
}
tpm2_unlock (tpm2);
return rc;
}
TSS2_RC
tpm2_context_saveflush (Tpm2 *tpm2,
TPM2_HANDLE handle,
TPMS_CONTEXT *context)
{
TSS2_RC rc;
TSS2_SYS_CONTEXT *sapi_context;
assert (tpm2 != NULL);
assert (context != NULL);
g_debug ("tpm2_context_save: handle 0x%" PRIx32, handle);
sapi_context = tpm2_lock_sapi (tpm2);
rc = Tss2_Sys_ContextSave (sapi_context, handle, context);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_ContextSave", rc);
goto out;
}
g_debug ("tpm2_context_flush: handle 0x%" PRIx32, handle);
rc = Tss2_Sys_FlushContext (sapi_context, handle);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_FlushContext", rc);
}
out:
tpm2_unlock (tpm2);
return rc;
}
/*
* Flush all handles in a given range. This function will return an error if
* we're unable to query for handles within the requested range. Failures to
* flush handles returned will be ignored since our goal here is to flush as
* many as possible.
*/
TSS2_RC
tpm2_flush_all_unlocked (Tpm2 *tpm2,
TSS2_SYS_CONTEXT *sapi_context,
TPM2_RH first,
TPM2_RH last)
{
TSS2_RC rc = TSS2_RC_SUCCESS;
TPMI_YES_NO more_data;
TPMS_CAPABILITY_DATA capability_data = { 0, };
TPM2_HANDLE handle;
size_t i;
g_debug ("%s: first: 0x%08" PRIx32 ", last: 0x%08" PRIx32,
__func__, first, last);
assert (tpm2 != NULL);
assert (sapi_context != NULL);
rc = Tss2_Sys_GetCapability (sapi_context,
NULL,
TPM2_CAP_HANDLES,
first,
last - first,
&more_data,
&capability_data,
NULL);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_GetCapability", rc);
return rc;
}
g_debug ("%s: got %u handles", __func__, capability_data.data.handles.count);
for (i = 0; i < capability_data.data.handles.count; ++i) {
handle = capability_data.data.handles.handle [i];
g_debug ("%s: flushing context with handle: 0x%08" PRIx32, __func__,
handle);
rc = Tss2_Sys_FlushContext (sapi_context, handle);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_FlushContext", rc);
}
}
return TSS2_RC_SUCCESS;
}
void
tpm2_flush_all_context (Tpm2 *tpm2)
{
TSS2_SYS_CONTEXT *sapi_context;
g_debug (__func__);
assert (tpm2 != NULL);
sapi_context = tpm2_lock_sapi (tpm2);
tpm2_flush_all_unlocked (tpm2,
sapi_context,
TPM2_ACTIVE_SESSION_FIRST,
TPM2_ACTIVE_SESSION_LAST);
tpm2_flush_all_unlocked (tpm2,
sapi_context,
TPM2_LOADED_SESSION_FIRST,
TPM2_LOADED_SESSION_LAST);
tpm2_flush_all_unlocked (tpm2,
sapi_context,
TPM2_TRANSIENT_FIRST,
TPM2_TRANSIENT_LAST);
tpm2_unlock (tpm2);
}
TSS2_RC
tpm2_get_command_attrs (Tpm2 *tpm2,
UINT32 *count,
TPMA_CC **attrs)
{
TSS2_RC rc;
TPMI_YES_NO more = TPM2_NO;
TPMS_CAPABILITY_DATA cap_data = { 0, };
TSS2_SYS_CONTEXT *sys_ctx;
assert (tpm2 != NULL);
assert (count != NULL);
assert (attrs != NULL);
sys_ctx = tpm2_lock_sapi (tpm2);
rc = Tss2_Sys_GetCapability (sys_ctx,
NULL,
TPM2_CAP_COMMANDS,
TPM2_CC_FIRST,
TPM2_MAX_CAP_CC,
&more,
&cap_data,
NULL);
tpm2_unlock (tpm2);
if (rc != TSS2_RC_SUCCESS) {
RC_WARN ("Tss2_Sys_GetCapability", rc);
return rc;
}
*count = cap_data.data.command.count;
*attrs = g_malloc0 (*count * sizeof (TPMA_CC));
memcpy (*attrs,
cap_data.data.command.commandAttributes,
*count * sizeof (TPMA_CC));
return rc;
}
tpm2-abrmd-3.0.0/src/tpm2.h 0000664 0000000 0000000 00000005014 14343407245 0015307 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef TPM2_H
#define TPM2_H
#include
#include
#include
#include
#include "tcti.h"
#include "tpm2-response.h"
G_BEGIN_DECLS
typedef struct _Tpm2Class {
GObjectClass parent;
} Tpm2Class;
typedef struct _Tpm2 {
GObject parent_instance;
pthread_mutex_t sapi_mutex;
TSS2_SYS_CONTEXT *sapi_context;
Tcti *tcti;
TPMS_CAPABILITY_DATA properties_fixed;
gboolean initialized;
} Tpm2;
#include "tpm2-command.h"
#define TYPE_TPM2 (tpm2_get_type ())
#define TPM2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TPM2, Tpm2))
#define TPM2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_TPM2, Tpm2Class))
#define IS_TPM2(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TPM2))
#define IS_TPM2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_TPM2))
#define TPM2_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_TPM2, Tpm2Class))
GType tpm2_get_type (void);
Tpm2* tpm2_new (Tcti *tcti);
TSS2_RC tpm2_init_tpm (Tpm2 *tpm2);
void tpm2_lock (Tpm2 *tpm2);
void tpm2_unlock (Tpm2 *tpm2);
Tpm2Response* tpm2_send_command (Tpm2 *tpm2,
Tpm2Command *command,
TSS2_RC *rc);
TSS2_RC tpm2_get_max_response (Tpm2 *tpm2, guint32 *value);
TSS2_SYS_CONTEXT* tpm2_lock_sapi (Tpm2 *tpm2);
TSS2_RC tpm2_get_trans_object_count (Tpm2 *tpm2, uint32_t *count);
TSS2_RC tpm2_context_load (Tpm2 *tpm2,
TPMS_CONTEXT *context,
TPM2_HANDLE *handle);
TSS2_RC tpm2_context_flush (Tpm2 *tpm2, TPM2_HANDLE handle);
TSS2_RC tpm2_context_saveflush (Tpm2 *tpm2,
TPM2_HANDLE handle,
TPMS_CONTEXT *context);
TSS2_RC tpm2_context_save (Tpm2 *tpm2,
TPM2_HANDLE handle,
TPMS_CONTEXT *context);
void tpm2_flush_all_context (Tpm2 *tpm2);
TSS2_RC tpm2_send_tpm_startup (Tpm2 *tpm2);
TSS2_SYS_CONTEXT* sapi_context_init (Tcti *tcti);
TSS2_RC tpm2_flush_all_unlocked (Tpm2 *tpm2,
TSS2_SYS_CONTEXT *sapi_context,
TPM2_RH first,
TPM2_RH last);
TSS2_RC tpm2_get_command_attrs (Tpm2 *tpm2, UINT32 *count, TPMA_CC **attrs);
G_END_DECLS
#endif
tpm2-abrmd-3.0.0/src/util.c 0000664 0000000 0000000 00000030260 14343407245 0015376 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "random.h"
#include "util.h"
#include "tpm2-header.h"
/**
* This is a wrapper around g_debug to dump a binary buffer in a human
* readable format. Since g_debug appends a new line to each string that
* it builds we dump a single line at a time. Each line is indented by
* 'indent' spaces. The 'width' parameter determines how many bytes are
* output on each line.
*/
#define MAX_LINE_LENGTH 200
void
g_debug_bytes (uint8_t const *byte_array,
size_t array_size,
size_t width,
size_t indent)
{
guint byte_ctr;
guint indent_ctr;
size_t line_length = indent + width * 3 + 1;
char line [MAX_LINE_LENGTH] = { 0 };
char *line_position = NULL;
if (line_length > MAX_LINE_LENGTH) {
g_warning ("g_debug_bytes: MAX_LINE_LENGTH exceeded");
return;
}
for (byte_ctr = 0; byte_ctr < array_size; ++byte_ctr) {
/* index into line where next byte is written */
line_position = line + indent + (byte_ctr % width) * 3;
/* detect the beginning of a line, pad indent spaces */
if (byte_ctr % width == 0)
for (indent_ctr = 0; indent_ctr < indent; ++indent_ctr)
line [indent_ctr] = ' ';
sprintf (line_position, "%02x", byte_array [byte_ctr]);
/**
* If we're not width bytes into the array AND we're not at the end
* of the byte array: print a space. This is padding between the
* current byte and the next.
*/
if (byte_ctr % width != width - 1 && byte_ctr != array_size - 1) {
sprintf (line_position + 2, " ");
} else {
g_debug ("%s", line);
}
}
}
/** Write as many of the size bytes from buf to fd as possible.
*/
ssize_t
write_all (GOutputStream *ostream,
const uint8_t *buf,
const size_t size)
{
ssize_t written = 0;
size_t written_total = 0;
GError *error = NULL;
do {
g_debug ("%s: writing %zu bytes to ostream", __func__,
size - written_total);
written = g_output_stream_write (ostream,
(const gchar*)&buf [written_total],
size - written_total,
NULL,
&error);
switch (written) {
case -1:
g_assert (error != NULL);
g_warning ("%s: failed to write to ostream: %s", __func__, error->message);
g_error_free (error);
return written;
case 0:
return (ssize_t)written_total;
default:
g_debug ("%s: wrote %zd bytes to ostream", __func__, written);
}
written_total += (size_t)written;
} while (written_total < size);
g_debug ("returning %zu", written_total);
return (ssize_t)written_total;
}
/*
* Read data from a GSocket.
* Parameters:
* socket: A connected GSocket.
* *index: A reference to the location in the buffer where data will be
* written. This reference is updated to the end of the location
* where data is written.
* buf: destination buffer
* count: number of bytes to read
* Returns:
* -1: when EOF is reached
* 0: if requested number of bytes received
* errno: in the event of an error from the 'read' call
* NOTE: The caller must ensure that 'buf' is large enough to hold count
* bytes.
*/
int
read_data (GInputStream *istream,
size_t *index,
uint8_t *buf,
size_t count)
{
ssize_t num_read = 0;
size_t bytes_left = count;
gint error_code;
GError *error = NULL;
g_assert (index != NULL);
do {
g_debug ("%s: reading %zu bytes from istream", __func__, bytes_left);
num_read = g_input_stream_read (istream,
(gchar*)&buf [*index],
bytes_left,
NULL,
&error);
if (num_read > 0) {
g_debug ("successfully read %zd bytes", num_read);
g_debug_bytes ((uint8_t*)&buf [*index], num_read, 16, 4);
/* Advance index by the number of bytes read. */
*index += num_read;
bytes_left -= num_read;
} else if (num_read == 0) {
g_debug ("read produced EOF");
return -1;
} else { /* num_read < 0 */
g_assert (error != NULL);
g_warning ("%s: read on istream produced error: %s", __func__,
error->message);
error_code = error->code;
g_error_free (error);
return error_code;
}
} while (bytes_left);
return 0;
}
/*
* This function attempts to read a TPM2 command or response into the provided
* buffer. It specifically handles the details around reading the command /
* response header, determining the size of the data that it needs to read and
* keeping track of past / partial reads.
* Returns:
* -1: If the underlying syscall results in an EOF
* 0: If data is successfully read.
* NOTE: The index will be updated to the size of the command buffer.
* errno: In the event of an error from the underlying 'read' syscall.
* EPROTO: If buf_size is less than the size from the command buffer.
*/
int
read_tpm_buffer (GInputStream *istream,
size_t *index,
uint8_t *buf,
size_t buf_size)
{
ssize_t ret = 0;
uint32_t size = 0;
/* if the buf_size isn't at least large enough to hold the header */
if (buf_size < TPM_HEADER_SIZE) {
return EPROTO;
}
/* If we don't have the whole header yet try to get it. */
if (*index < TPM_HEADER_SIZE) {
ret = read_data (istream, index, buf, TPM_HEADER_SIZE - *index);
if (ret != 0) {
/* Pass errors up to the caller. */
return ret;
}
}
/* Once we have the header we can get the size of the whole blob. */
size = get_command_size (buf);
/* If size from header is size of header, there's nothing more to read. */
if (size == TPM_HEADER_SIZE) {
return ret;
}
/* Not enough space in buf to for data in the buffer (header.size). */
if (size > buf_size) {
return EPROTO;
}
/* Now that we have the header, we know the whole buffer size. Get it. */
return read_data (istream, index, buf, size - *index);
}
/*
* This fucntion is a wrapper around the read_tpm_buffer function above. It
* adds the memory allocation logic necessary to create the buffer to hold
* the TPM command / response buffer.
* Returns NULL on error, and a pointer to the allocated buffer on success.
* The size of the allocated buffer is returned through the *buf_size
* parameter on success.
*/
uint8_t*
read_tpm_buffer_alloc (GInputStream *istream,
size_t *buf_size)
{
uint8_t *buf = NULL;
size_t size_tmp = TPM_HEADER_SIZE, index = 0;
int ret = 0;
if (istream == NULL || buf_size == NULL) {
g_warning ("%s: got null parameter", __func__);
return NULL;
}
do {
buf = g_realloc (buf, size_tmp);
ret = read_tpm_buffer (istream, &index, buf, size_tmp);
switch (ret) {
case EPROTO:
size_tmp = get_command_size (buf);
if (size_tmp < TPM_HEADER_SIZE || size_tmp > UTIL_BUF_MAX) {
g_warning ("%s: tpm buffer size is ouside of acceptable bounds: %zd",
__func__, size_tmp);
goto err_out;
}
break;
case 0:
/* done */
break;
default:
goto err_out;
}
} while (ret == EPROTO);
g_debug ("%s: read TPM buffer of size: %zd", __func__, index);
g_debug_bytes (buf, index, 16, 4);
*buf_size = size_tmp;
return buf;
err_out:
g_debug ("%s: err_out freeing buffer", __func__);
if (buf != NULL) {
g_free (buf);
}
return NULL;
}
/*
* Create a GSocket for use by the daemon for communicating with the client.
* The client end of the socket is returned through the client_fd
* parameter.
*/
GIOStream*
create_connection_iostream (int *client_fd)
{
GIOStream *iostream;
GSocket *sock;
int server_fd, ret;
ret = create_socket_pair (client_fd,
&server_fd,
SOCK_CLOEXEC | SOCK_NONBLOCK);
if (ret == -1) {
g_error ("CreateConnection failed to make fd pair %s", strerror (errno));
}
sock = g_socket_new_from_fd (server_fd, NULL);
iostream = G_IO_STREAM (g_socket_connection_factory_create_connection (sock));
g_object_unref (sock);
return iostream;
}
/*
* Create a socket and return the fds for both ends of the communication
* channel.
*/
int
create_socket_pair (int *fd_a,
int *fd_b,
int flags)
{
int ret, fds[2] = { 0, };
ret = socketpair (PF_LOCAL, SOCK_STREAM | flags, 0, fds);
if (ret == -1) {
g_warning ("%s: failed to create socket pair with errno: %d",
__func__, errno);
return ret;
}
*fd_a = fds [0];
*fd_b = fds [1];
return 0;
}
/* pretty print */
void
g_debug_tpma_cc (TPMA_CC tpma_cc)
{
g_debug ("TPMA_CC: 0x%08" PRIx32, tpma_cc);
g_debug (" commandIndex: 0x%" PRIx16, (tpma_cc & TPMA_CC_COMMANDINDEX_MASK) >> TPMA_CC_COMMANDINDEX_SHIFT);
g_debug (" reserved1: 0x%" PRIx8, (tpma_cc & TPMA_CC_RESERVED1_MASK));
g_debug (" nv: %s", prop_str (tpma_cc & TPMA_CC_NV));
g_debug (" extensive: %s", prop_str (tpma_cc & TPMA_CC_EXTENSIVE));
g_debug (" flushed: %s", prop_str (tpma_cc & TPMA_CC_FLUSHED));
g_debug (" cHandles: 0x%" PRIx8, (tpma_cc & TPMA_CC_CHANDLES_MASK) >> TPMA_CC_CHANDLES_SHIFT);
g_debug (" rHandle: %s", prop_str (tpma_cc & TPMA_CC_RHANDLE));
g_debug (" V: %s", prop_str (tpma_cc & TPMA_CC_V));
g_debug (" Res: 0x%" PRIx8, (tpma_cc & TPMA_CC_RES_MASK) >> TPMA_CC_RES_SHIFT);
}
/*
* Parse the provided string containing a key / value pair separated by the
* '=' character.
* NOTE: The 'kv_str' parameter is not 'const' and this function will modify
* it as part of the parsing process.
*/
gboolean
parse_key_value (char *key_value_str,
key_value_t *key_value)
{
const char *delim = "=";
char *tok, *state;
tok = strtok_r (key_value_str, delim, &state);
if (tok == NULL) {
g_warning ("key / value string is null.");
return FALSE;
}
key_value->key = tok;
tok = strtok_r (NULL, delim, &state);
if (tok == NULL) {
g_warning ("key / value string is invalid");
return FALSE;
}
key_value->value = tok;
return TRUE;
}
/*
* This function parses the provided configuration string extracting the
* key/value pairs, and then passing the provided provided callback function
* for processing.
* NOTE: The 'kv_str' parameter is not 'const' and this function will modify
* it as part of the parsing process.
*/
TSS2_RC
parse_key_value_string (char *kv_str,
KeyValueFunc callback,
gpointer user_data)
{
const char *delim = ",";
char *state, *tok;
key_value_t key_value = { .key = 0, .value = 0 };
gboolean ret;
TSS2_RC rc = TSS2_RC_SUCCESS;
for (tok = strtok_r (kv_str, delim, &state);
tok;
tok = strtok_r (NULL, delim, &state)) {
ret = parse_key_value (tok, &key_value);
if (ret != TRUE) {
return TSS2_TCTI_RC_BAD_VALUE;
}
rc = callback (&key_value, user_data);
if (rc != TSS2_RC_SUCCESS) {
goto out;
}
}
out:
return rc;
}
tpm2-abrmd-3.0.0/src/util.h 0000664 0000000 0000000 00000010372 14343407245 0015405 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#ifndef UTIL_H
#define UTIL_H
#include
#include
#include
#include
#include
#include "control-message.h"
/* Use to suppress "unused parameter" warnings: */
#define UNUSED_PARAM(p) ((void)(p))
/* Use to suppress "unused variable" warnings: */
#define UNUSED_VAR(p) ((void)(p))
/* Used to suppress scan-build NULL dereference warnings: */
#ifdef SCANBUILD
#define ASSERT_NON_NULL(x) assert_non_null(x); \
if ((x) == NULL) return
#else
#define ASSERT_NON_NULL(x) assert_non_null(x)
#endif
/*
* Substitute for GNU TEMP_FAILURE_RETRY for environments that
* don't have the GNU C library.
*/
#define TABRMD_ERRNO_EINTR_RETRY(exp) \
({ \
long int __result = 0; \
do { \
__result = (long int)(exp); \
} while ((__result == -1) && (errno == EINTR)); \
__result; \
})
/* set the layer / component to indicate the RC comes from the RM */
#define RM_RC(rc) TSS2_RESMGR_RC_LAYER + rc
/* allocate read blocks in BUF_SIZE increments */
#define UTIL_BUF_SIZE 1024
/* stop allocating at BUF_MAX */
#define UTIL_BUF_MAX 8*UTIL_BUF_SIZE
#define prop_str(val) val ? "set" : "clear"
/*
* Print warning message for a given response code.
* Parameters:
* cmd: string name of Tss2_* function that produced the RC
* rc: response code from function 'cmd'
*/
#define RC_WARN(cmd, rc) \
g_warning ("[%s:%d] %s failed: %s (RC: 0x%" PRIx32 ")", \
__FILE__, __LINE__, cmd, Tss2_RC_Decode (rc), rc)
typedef struct {
char *key;
char *value;
} key_value_t;
typedef TSS2_RC (*KeyValueFunc) (const key_value_t* key_value,
gpointer user_data);
/*
#define TPM2_CC_FROM_TPMA_CC(attrs) (attrs.val & 0x0000ffff)
#define TPMA_CC_RESERVED(attrs) (attrs.val & 0x003f0000)
#define TPMA_CC_NV(attrs) (attrs.val & 0x00400000)
#define TPMA_CC_EXTENSIVE(attrs) (attrs.val & 0x00800000)
#define TPMA_CC_FLUSHED(attrs) (attrs.val & 0x01000000)
#define TPMA_CC_CHANDLES(attrs) (attrs.val & 0x02000000)
#define TPMA_CC_RHANDLES(attrs) (attrs.val & 0x10000000)
#define TPMA_CC_V(attrs) (attrs.val & 0x20000000)
#define TPMA_CC_RES(attrs) (attrs.val & 0xc0000000)
*/
ssize_t write_all (GOutputStream *ostream,
const uint8_t *buf,
const size_t size);
int read_data (GInputStream *istream,
size_t *index,
uint8_t *buf,
size_t count);
int read_tpm_buffer (GInputStream *istream,
size_t *index,
uint8_t *buf,
size_t buf_size);
uint8_t* read_tpm_buffer_alloc (GInputStream *istream,
size_t *buf_size);
void g_debug_bytes (uint8_t const *byte_array,
size_t array_size,
size_t width,
size_t indent);
GIOStream* create_connection_iostream (int *client_fd);
int create_socket_pair (int *fd_a,
int *fd_b,
int flags);
void g_debug_tpma_cc (TPMA_CC tpma_cc);
TSS2_RC parse_key_value_string (char *kv_str,
KeyValueFunc callback,
gpointer user_data);
#endif /* UTIL_H */
tpm2-abrmd-3.0.0/test/ 0000775 0000000 0000000 00000000000 14343407245 0014444 5 ustar 00root root 0000000 0000000 tpm2-abrmd-3.0.0/test/command-attrs_unit.c 0000664 0000000 0000000 00000014524 14343407245 0020426 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017 - 2018, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include "util.h"
#include "tpm2.h"
#include "command-attrs.h"
#include "tcti.h"
#include "tcti-mock.h"
typedef struct test_data {
Tpm2 *tpm2;
CommandAttrs *command_attrs;
} test_data_t;
/* Setup function to allocate our Random gobject. */
static int
command_attrs_setup (void **state)
{
test_data_t *data;
TSS2_TCTI_CONTEXT *context;
Tcti *tcti = NULL;
context = tcti_mock_init_full ();
if (context == NULL) {
g_critical ("tcti_mock_init_full failed");
return 1;
}
data = calloc (1, sizeof (test_data_t));
tcti = tcti_new (context);
data->tpm2 = tpm2_new (tcti);
data->command_attrs = command_attrs_new ();
g_clear_object (&tcti);
*state = data;
return 0;
}
/* Setup function to allocate and initialize the object. */
static int
command_attrs_init_tpm_setup (void **state)
{
test_data_t *data;
gint ret;
TPMA_CC hierarchy_attrs = TPM2_CC_HierarchyControl + 0xff0000;
TPMA_CC change_pps_attrs = TPM2_CC_ChangePPS + 0xff0000;
TPMA_CC command_attributes [2] = { hierarchy_attrs, change_pps_attrs };
command_attrs_setup (state);
data = *state;
will_return (__wrap_tpm2_get_command_attrs, TSS2_RC_SUCCESS);
will_return (__wrap_tpm2_get_command_attrs, 2);
will_return (__wrap_tpm2_get_command_attrs, command_attributes);
ret = command_attrs_init_tpm (data->command_attrs, data->tpm2);
assert_int_equal (ret, 0);
return 0;
}
/* Teardown function to deallocate the Random object. */
static int
command_attrs_teardown (void **state)
{
test_data_t *data = *state;
g_object_unref (data->command_attrs);
g_object_unref (data->tpm2);
free (data);
return 0;
}
/* Simple test to test type checking macros. */
static void
command_attrs_type_test (void **state)
{
test_data_t *data = *state;
assert_true (G_IS_OBJECT (data->command_attrs));
assert_true (IS_COMMAND_ATTRS (data->command_attrs));
}
/* */
TSS2_RC
__wrap_tpm2_get_command_attrs (Tpm2 *tpm2,
UINT32 *count,
TPMA_CC **attrs)
{
TSS2_RC rc;
TPMA_CC *command_attrs;
UNUSED_PARAM(tpm2);
rc = mock_type (TSS2_RC);
if (rc != TSS2_RC_SUCCESS)
return rc;
*count = mock_type (UINT32);
command_attrs = mock_ptr_type (TPMA_CC*);
*attrs = g_malloc0 (*count * sizeof (TPMA_CC));
memcpy (*attrs, command_attrs, *count * sizeof (TPMA_CC));
return rc;
}
/*
* Test case that initializes the CommandAttrs object. All wrapped
* function calls return the values expected by the function under test.
*/
static void
command_attrs_init_tpm_success_test (void **state)
{
test_data_t *data = *state;
gint ret = -1;
TPMA_CC command_attributes [2] = { 0xdeadbeef, 0xfeebdaed };
will_return (__wrap_tpm2_get_command_attrs, TSS2_RC_SUCCESS);
will_return (__wrap_tpm2_get_command_attrs, 2);
will_return (__wrap_tpm2_get_command_attrs, command_attributes);
ret = command_attrs_init_tpm (data->command_attrs, data->tpm2);
assert_int_equal (ret, 0);
assert_memory_equal (command_attributes,
data->command_attrs->command_attrs,
sizeof (TPMA_CC) * data->command_attrs->count);
}
/*
* Test case taht exercises the error handling path for a failed call to
* Tss2_Sys_GetCapability.
*/
static void
command_attrs_init_tpm_fail_get_capability_test (void **state)
{
test_data_t *data = *state;
gint ret = -1;
will_return (__wrap_tpm2_get_command_attrs, TPM2_RC_FAILURE);
ret = command_attrs_init_tpm (data->command_attrs, data->tpm2);
assert_int_equal (ret, -1);
}
/*
* Test a successful call to the command_attrs_from_cc function. This relies
* on command_attrs_init_tpm_setup to call the _init function successfully which
* populates the CommandAttrs object with TPMA_CCs.
*/
static void
command_attrs_from_cc_success_test (void **state)
{
test_data_t *data = *state;
TPMA_CC ret_attrs;
/*
* TPM2_CC_HierarchyControl *is* one of the TPM2_CCs populated in the
* init_setup function.
*/
ret_attrs = command_attrs_from_cc (data->command_attrs,
TPM2_CC_HierarchyControl);
assert_int_equal (ret_attrs & 0x7fff, TPM2_CC_HierarchyControl);
}
/*
* Test a failed call to the command_attrs_from_cc function. This relies
* on command_attrs_init_tpm_setup to call the _init function successfully which
* populates the CommandAttrs object with TPMA_CCs. This time we supply a
* TPM2_CC that isn't populated in the _init function so the call fails.
*/
static void
command_attrs_from_cc_fail_test (void **state)
{
test_data_t *data = *state;
TPMA_CC ret_attrs;
/*
* TPM2_CC_EvictControl is *not* one of the TPM2_CCs populated in the
* init_setup function
*/
ret_attrs = command_attrs_from_cc (data->command_attrs,
TPM2_CC_EvictControl);
assert_int_equal (ret_attrs, 0);
}
gint
main (void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown (command_attrs_type_test,
command_attrs_setup,
command_attrs_teardown),
cmocka_unit_test_setup_teardown (command_attrs_init_tpm_success_test,
command_attrs_setup,
command_attrs_teardown),
cmocka_unit_test_setup_teardown (command_attrs_init_tpm_fail_get_capability_test,
command_attrs_setup,
command_attrs_teardown),
cmocka_unit_test_setup_teardown (command_attrs_from_cc_success_test,
command_attrs_init_tpm_setup,
command_attrs_teardown),
cmocka_unit_test_setup_teardown (command_attrs_from_cc_fail_test,
command_attrs_init_tpm_setup,
command_attrs_teardown),
};
return cmocka_run_group_tests (tests, NULL, NULL);
}
tpm2-abrmd-3.0.0/test/command-source_unit.c 0000664 0000000 0000000 00000031024 14343407245 0020563 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "connection-manager.h"
#include "sink-interface.h"
#include "source-interface.h"
#include "command-attrs.h"
#include "command-source.h"
#include "tabrmd-defaults.h"
#include "tpm2-command.h"
#include "util.h"
typedef struct source_test_data {
ConnectionManager *manager;
CommandAttrs *command_attrs;
CommandSource *source;
Connection *connection;
gboolean match;
} source_test_data_t;
/* mock function to return TPM command attributes TPMA_CC */
TPMA_CC
__wrap_command_attrs_from_cc (CommandAttrs *attrs,
TPM2_CC command_code)
{
UNUSED_PARAM(attrs);
UNUSED_PARAM(command_code);
return (TPMA_CC)mock_type (UINT32);
}
Connection*
__wrap_connection_manager_lookup_istream (ConnectionManager *manager,
GInputStream *istream)
{
UNUSED_PARAM(manager);
UNUSED_PARAM(istream);
g_debug ("%s", __func__);
return CONNECTION (mock_ptr_type (GObject*));
}
gint
__wrap_connection_manager_remove (ConnectionManager *manager,
Connection *connection)
{
UNUSED_PARAM(manager);
UNUSED_PARAM(connection);
return mock_type (int);
}
uint8_t*
__wrap_read_tpm_buffer_alloc (GSocket *socket,
size_t *buf_size)
{
uint8_t *buf_src = mock_type (uint8_t*);
uint8_t *buf_dst = NULL;
size_t size = mock_type (size_t);
UNUSED_PARAM(socket);
g_debug ("%s", __func__);
buf_dst = g_malloc0 (size);
memcpy (buf_dst, buf_src, size);
*buf_size = size;
return buf_dst;
}
void
__wrap_sink_enqueue (Sink *sink,
GObject *obj)
{
GObject **object;
UNUSED_PARAM(sink);
g_debug ("%s", __func__);
object = mock_ptr_type (GObject**);
*object = G_OBJECT (obj);
g_object_ref (*object);
}
/*
* This wrap function allows us to gain access to the data that will be
* passed to the source callback registered by the CommandSource object when
* a new Connection is added. Without this callback it's not possible to get
* access to the source_data_t structure created by the CommandSource insert
* function which is required for us to simulate the callback function.
*/
void
__wrap_g_source_set_callback (GSource *source,
GSourceFunc func,
gpointer data,
GDestroyNotify notify)
{
source_data_t *source_data = (source_data_t*)data;
source_data_t **source_data_param = mock_type (source_data_t**);
UNUSED_PARAM(source);
UNUSED_PARAM(func);
UNUSED_PARAM(notify);
*source_data_param = source_data;
}
/* command_source_allocate_test begin
* Test to allocate and destroy a CommandSource.
*/
static void
command_source_allocate_test (void **state)
{
source_test_data_t *data = (source_test_data_t *)*state;
data->command_attrs = command_attrs_new ();
data->source = command_source_new (data->manager,
data->command_attrs);
assert_non_null (data->source);
}
static int
command_source_allocate_setup (void **state)
{
source_test_data_t *data;
data = calloc (1, sizeof (source_test_data_t));
data->manager = connection_manager_new (TABRMD_CONNECTIONS_MAX_DEFAULT);
*state = data;
return 0;
}
/* command_source_allocate end */
/* command_source_start_test
* This is a basic usage flow test. Can it be started, canceled and joined.
* We're testing the underlying pthread usage ... mostly.
*/
void
command_source_start_test (void **state)
{
source_test_data_t *data;
gint ret;
data = (source_test_data_t*)*state;
thread_start(THREAD (data->source));
sleep (1);
thread_cancel (THREAD (data->source));
ret = thread_join (THREAD (data->source));
assert_int_equal (ret, 0);
}
static int
command_source_start_setup (void **state)
{
source_test_data_t *data;
data = calloc (1, sizeof (source_test_data_t));
data->manager = connection_manager_new (TABRMD_CONNECTIONS_MAX_DEFAULT);
if (data->manager == NULL)
g_error ("failed to allocate new connection_manager");
data->command_attrs = command_attrs_new ();
data->source = command_source_new (data->manager,
data->command_attrs);
if (data->source == NULL)
g_error ("failed to allocate new command_source");
*state = data;
return 0;
}
static int
command_source_teardown (void **state)
{
source_test_data_t *data = (source_test_data_t*)*state;
g_clear_object (&data->source);
g_clear_object (&data->manager);
g_clear_object (&data->command_attrs);
g_clear_pointer (&data, g_free);
return 0;
}
/* command_source_start_test end */
/* command_source_connection_insert_test begin
* In this test we create a connection source and all that that entails. We then
* create a new connection and insert it into the connection manager. We then signal
* to the source that there's a new connection in the manager by sending data to
* it over the send end of the wakeup pipe "wakeup_send_fd". We then check the
* receive_fdset in the source structure to be sure the receive end of the
* connection pipe is set. This is how we know that the source is now watching
* for data from the new connection.
*/
static int
command_source_connection_setup (void **state)
{
source_test_data_t *data;
g_debug ("%s", __func__);
data = calloc (1, sizeof (source_test_data_t));
data->manager = connection_manager_new (TABRMD_CONNECTIONS_MAX_DEFAULT);
data->command_attrs = command_attrs_new ();
data->source = command_source_new (data->manager,
data->command_attrs);
*state = data;
return 0;
}
static void
command_source_connection_insert_test (void **state)
{
struct source_test_data *data = (struct source_test_data*)*state;
source_data_t *source_data;
CommandSource *source = data->source;
GIOStream *iostream;
HandleMap *handle_map;
Connection *connection;
gint ret, client_fd;
g_debug ("%s", __func__);
handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
iostream = create_connection_iostream (&client_fd);
connection = connection_new (iostream, 5, handle_map);
g_object_unref (handle_map);
g_object_unref (iostream);
/* starts the main loop in the CommandSource */
ret = thread_start(THREAD (source));
will_return (__wrap_g_source_set_callback, &source_data);
assert_int_equal (ret, 0);
/* normally a callback from the connection manager but we fake it here */
sleep (1);
command_source_on_new_connection (data->manager, connection, source);
/* check internal state of the CommandSource*/
assert_int_equal (g_hash_table_size (source->istream_to_source_data_map),
1);
thread_cancel (THREAD (source));
thread_join (THREAD (source));
g_object_unref (connection);
}
/* command_source_session_insert_test end */
/**
* A test: Test the command_source_connection_responder function. We do this
* by creating a new Connection object, associating it with a new
* Tpm2Command object (that we populate with a command body), and then
* calling the command_source_connection_responder.
* This function will in turn call the connection_manager_lookup_fd,
* tpm2_command_new_from_fd, before finally calling the sink_enqueue function.
* We mock these 3 functions to control the flow through the function under
* test.
* The most tricky bit to this is the way the __wrap_sink_enqueue function
* works. Since this thing has no return value we pass it a reference to a
* Tpm2Command pointer. It sets this to the Tpm2Command that it receives.
* We determine success /failure for this test by verifying that the
* sink_enqueue function receives the same Tpm2Command that we passed to
* the command under test (command_source_connection_responder).
*/
static void
command_source_on_io_ready_success_test (void **state)
{
struct source_test_data *data = (struct source_test_data*)*state;
GIOStream *iostream;
HandleMap *handle_map;
Connection *connection;
Tpm2Command *command_out;
gint client_fd;
guint8 data_in [] = { 0x80, 0x01, 0x0, 0x0, 0x0, 0x17,
0x0, 0x0, 0x01, 0x7a, 0x0, 0x0,
0x0, 0x06, 0x0, 0x0, 0x01, 0x0,
0x0, 0x0, 0x0, 0x7f, 0x0a };
handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
iostream = create_connection_iostream (&client_fd);
connection = connection_new (iostream, 0, handle_map);
g_object_unref (handle_map);
g_object_unref (iostream);
/* prime wraps */
will_return (__wrap_connection_manager_lookup_istream, connection);
/* setup read of tpm buffer */
will_return (__wrap_read_tpm_buffer_alloc, data_in);
will_return (__wrap_read_tpm_buffer_alloc, sizeof (data_in));
/* setup query for command attributes */
will_return (__wrap_command_attrs_from_cc, 0);
will_return (__wrap_sink_enqueue, &command_out);
command_source_on_input_ready (NULL, data->source);
assert_memory_equal (tpm2_command_get_buffer (command_out),
data_in,
sizeof (data_in));
g_object_unref (command_out);
}
/*
* This tests the CommandSource on_io_ready function for situations where
* the GSocket associated with a client connection is closed. This causes
* the attempt to read data from the socket to return an error indicating
* that the socket was closed. In this case the function should return a
* value telling GLib to remove the GSource from the main loop. Additionally
* the data held internally by the CommandSource must be freed.
*/
static void
command_source_on_io_ready_eof_test (void **state)
{
struct source_test_data *data = (struct source_test_data*)*state;
source_data_t *source_data;
GIOStream *iostream;
HandleMap *handle_map;
Connection *connection;
ControlMessage *msg;
gint client_fd, hash_table_size;
gboolean ret;
handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
iostream = create_connection_iostream (&client_fd);
connection = connection_new (iostream, 0, handle_map);
g_object_unref (handle_map);
g_object_unref (iostream);
/* prime wraps */
will_return (__wrap_g_source_set_callback, &source_data);
will_return (__wrap_connection_manager_lookup_istream, connection);
will_return (__wrap_read_tpm_buffer_alloc, NULL);
will_return (__wrap_read_tpm_buffer_alloc, 0);
will_return (__wrap_sink_enqueue, &msg);
will_return (__wrap_connection_manager_remove, TRUE);
command_source_on_new_connection (data->manager, connection, data->source);
ret = command_source_on_input_ready (g_io_stream_get_input_stream (connection->iostream), source_data);
assert_int_equal (ret, G_SOURCE_REMOVE);
hash_table_size = g_hash_table_size (data->source->istream_to_source_data_map);
assert_int_equal (hash_table_size, 0);
g_object_unref (msg);
}
/* command_source_connection_test end */
int
main (void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown (command_source_allocate_test,
command_source_allocate_setup,
command_source_teardown),
cmocka_unit_test_setup_teardown (command_source_start_test,
command_source_start_setup,
command_source_teardown),
cmocka_unit_test_setup_teardown (command_source_connection_insert_test,
command_source_connection_setup,
command_source_teardown),
cmocka_unit_test_setup_teardown (command_source_on_io_ready_success_test,
command_source_connection_setup,
command_source_teardown),
cmocka_unit_test_setup_teardown (command_source_on_io_ready_eof_test,
command_source_connection_setup,
command_source_teardown),
};
return cmocka_run_group_tests (tests, NULL, NULL);
}
tpm2-abrmd-3.0.0/test/connection-manager_unit.c 0000664 0000000 0000000 00000012012 14343407245 0021412 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "connection.h"
#include "connection-manager.h"
#include "tabrmd-defaults.h"
#include "util.h"
static void
connection_manager_allocate_test (void **state)
{
ConnectionManager *manager = NULL;
UNUSED_PARAM(state);
manager = connection_manager_new (TABRMD_CONNECTIONS_MAX_DEFAULT);
assert_non_null (manager);
g_object_unref (manager);
}
static int
connection_manager_setup (void **state)
{
ConnectionManager *manager = NULL;
manager = connection_manager_new (TABRMD_CONNECTIONS_MAX_DEFAULT);
assert_non_null (manager);
*state = manager;
return 0;
}
static int
connection_manager_teardown (void **state)
{
ConnectionManager *manager = CONNECTION_MANAGER (*state);
g_object_unref (manager);
return 0;
}
static void
connection_manager_insert_test (void **state)
{
ConnectionManager *manager = CONNECTION_MANAGER (*state);
Connection *connection = NULL;
HandleMap *handle_map = NULL;
gint ret, client_fd;
GIOStream *iostream;
handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
iostream = create_connection_iostream (&client_fd);
connection = connection_new (iostream, 5, handle_map);
g_object_unref (handle_map);
g_object_unref (iostream);
ret = connection_manager_insert (manager, connection);
assert_int_equal (ret, 0);
}
static void
connection_manager_lookup_fd_test (void **state)
{
ConnectionManager *manager = CONNECTION_MANAGER (*state);
Connection *connection = NULL, *connection_lookup = NULL;
HandleMap *handle_map = NULL;
gint ret, client_fd;
GIOStream *iostream;
handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
iostream = create_connection_iostream (&client_fd);
connection = connection_new (iostream, 5, handle_map);
g_object_unref (handle_map);
g_object_unref (iostream);
ret = connection_manager_insert (manager, connection);
assert_int_equal (ret, TSS2_RC_SUCCESS);
connection_lookup = connection_manager_lookup_istream (manager,
connection_key_istream (connection));
assert_int_equal (connection, connection_lookup);
g_object_unref (connection_lookup);
}
static void
connection_manager_lookup_id_test (void **state)
{
ConnectionManager *manager = CONNECTION_MANAGER (*state);
Connection *connection = NULL, *connection_lookup = NULL;
HandleMap *handle_map = NULL;
GIOStream *iostream;
gint ret, client_fd;
handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
iostream = create_connection_iostream (&client_fd);
connection = connection_new (iostream, 5, handle_map);
g_object_unref (handle_map);
g_object_unref (iostream);
ret = connection_manager_insert (manager, connection);
assert_int_equal (ret, TSS2_RC_SUCCESS);
connection_lookup = connection_manager_lookup_id (manager, *(gint64*)connection_key_id (connection));
assert_int_equal (connection, connection_lookup);
}
static void
connection_manager_remove_test (void **state)
{
ConnectionManager *manager = CONNECTION_MANAGER (*state);
Connection *connection = NULL;
GIOStream *iostream;
HandleMap *handle_map = NULL;
gint ret_int, client_fd;
gboolean ret_bool;
handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT);
iostream = create_connection_iostream (&client_fd);
connection = connection_new (iostream, 5, handle_map);
g_object_unref (handle_map);
g_object_unref (iostream);
ret_int = connection_manager_insert (manager, connection);
assert_int_equal (ret_int, 0);
ret_bool = connection_manager_remove (manager, connection);
assert_true (ret_bool);
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test (connection_manager_allocate_test),
cmocka_unit_test_setup_teardown (connection_manager_insert_test,
connection_manager_setup,
connection_manager_teardown),
cmocka_unit_test_setup_teardown (connection_manager_lookup_fd_test,
connection_manager_setup,
connection_manager_teardown),
cmocka_unit_test_setup_teardown (connection_manager_lookup_id_test,
connection_manager_setup,
connection_manager_teardown),
cmocka_unit_test_setup_teardown (connection_manager_remove_test,
connection_manager_setup,
connection_manager_teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
tpm2-abrmd-3.0.0/test/connection_unit.c 0000664 0000000 0000000 00000013057 14343407245 0020014 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*/
#include