pax_global_header00006660000000000000000000000064140103014260014501gustar00rootroot0000000000000052 comment=4f332013a02c422e186c4aaf127ab6a40b996028 tpm2-abrmd-2.4.0/000077500000000000000000000000001401030142600134515ustar00rootroot00000000000000tpm2-abrmd-2.4.0/.ci/000077500000000000000000000000001401030142600141225ustar00rootroot00000000000000tpm2-abrmd-2.4.0/.ci/ci-runner.sh000077500000000000000000000026521401030142600163700ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-2 set -exo pipefail DOCKER_SCRIPT="docker.run" COV_SCRIPT="coverity.run" # Github has a default env variable GITHUB_REPOSITORY which contains the # repo owner and repo name in the / format. Parse this # and get the project name itself. export PROJECT=$(echo $GITHUB_REPOSITORY | cut -d'/' -f 2-) export DOCKER_BUILD_DIR="/workspace/$PROJECT" # if no DOCKER_IMAGE is set, warn and default to fedora-30 if [ -z "$DOCKER_IMAGE" ]; then echo "WARN: DOCKER_IMAGE is not set, defaulting to fedora-30" export DOCKER_IMAGE="fedora-30" fi # # Docker starts you in a cloned repo of your project with the PR checkout out. # We want those changes IN the docker image, so use the -v option to mount the # project repo in the docker image. # # Also, pass in any env variables required for the build via .ci/docker.env file # # Execute the build and test procedure by running .ci/docker.run # ci_env="" if [ "$ENABLE_COVERAGE" == "true" ]; then ci_env=$(bash <(curl -s https://codecov.io/env)) fi if [ "$ENABLE_COVERITY" == "true" ]; then echo "Running coverity build" script="$COV_SCRIPT" else echo "Running non-coverity build" script="$DOCKER_SCRIPT" fi docker run --ulimit core=0 --cap-add=SYS_PTRACE $ci_env --env-file .ci/docker.env \ -v "$(pwd):$DOCKER_BUILD_DIR" "ghcr.io/tpm2-software/$DOCKER_IMAGE" \ /bin/bash -c "$DOCKER_BUILD_DIR/.ci/$script" exit 0 tpm2-abrmd-2.4.0/.ci/coverity.run000077500000000000000000000062371401030142600165270ustar00rootroot00000000000000#!/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_SLUG $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_SLUG $REPO_BRANCH" \ "https://scan.coverity.com/builds?project=$PROJECT" rm -rf $SCAN_FILE popd exit 0 tpm2-abrmd-2.4.0/.ci/docker.env000066400000000000000000000003661401030142600161100ustar00rootroot00000000000000#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_SLUG ENABLE_COVERAGE ENABLE_FUZZING DOCKER_IMAGE TPM2TSS_BRANCH MAKE_TARGET tpm2-abrmd-2.4.0/.ci/docker.run000077500000000000000000000017601401030142600161260ustar00rootroot00000000000000#!/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-2.4.0/.ci/get_deps.sh000077500000000000000000000010311401030142600162460ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-2 set -exo pipefail pushd "$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 git clone --depth=1 -b "${TPM2TSS_BRANCH}" "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-2.4.0/.cirrus.yml000066400000000000000000000023551401030142600155660ustar00rootroot00000000000000task: 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-12-1-snap 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-2.4.0/.codecov.yml000066400000000000000000000000561401030142600156750ustar00rootroot00000000000000ignore: - "test" - "src/tabrmd-generated.*" tpm2-abrmd-2.4.0/.github/000077500000000000000000000000001401030142600150115ustar00rootroot00000000000000tpm2-abrmd-2.4.0/.github/workflows/000077500000000000000000000000001401030142600170465ustar00rootroot00000000000000tpm2-abrmd-2.4.0/.github/workflows/coverity.yml000066400000000000000000000012161401030142600214350ustar00rootroot00000000000000name: Coverity Scan on: push jobs: coverity-scan: runs-on: ubuntu-latest if: contains(github.ref, 'coverity_scan') steps: - name: Check out repository uses: actions/checkout@v2 - name: Launch Container env: ENABLE_COVERITY: true TPM2TSS_BRANCH: "3.0.x" TPM2TOOLS_BRANCH: "4.0" REPO_BRANCH: ${{ github.ref }} REPO_SLUG: ${{ github.repository }} DOCKER_IMAGE: ubuntu-18.04 CC: gcc COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} COVERITY_SUBMISSION_EMAIL: william.c.roberts@intel.com run: ./.ci/ci-runner.sh tpm2-abrmd-2.4.0/.github/workflows/docker.yml000066400000000000000000000040751401030142600210460ustar00rootroot00000000000000name: 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-16.04", "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 Container env: DOCKER_IMAGE: "${{ matrix.DOCKER_IMAGE }}" TPM2TSS_BRANCH: "${{ matrix.TPM2TSS_BRANCH }}" CC: "${{ matrix.CC }}" run: ./.ci/ci-runner.sh - 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 Container env: DOCKER_IMAGE: ${{ matrix.ARCH }} TPM2TSS_BRANCH: master CC: gcc MAKE_TARGET: check run: ./.ci/ci-runner.sh - 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 Container env: ENABLE_COVERAGE: true DOCKER_IMAGE: ubuntu-18.04 TPM2TSS_BRANCH: master MAKE_TARGET: check CC: gcc run: ./.ci/ci-runner.sh - name: failure if: ${{ failure() }} run: find -name test-suite.log | xargs cat || true tpm2-abrmd-2.4.0/.github/workflows/whitespace.yml000066400000000000000000000006171401030142600217310ustar00rootroot00000000000000name: Whitespace Checker on: pull_request jobs: whitespace-test: runs-on: ubuntu-latest if: "!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-2.4.0/.gitignore000066400000000000000000000015031401030142600154400ustar00rootroot00000000000000.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-2.4.0/.lgtm.yml000066400000000000000000000011221401030142600152110ustar00rootroot00000000000000extraction: cpp: prepare: packages: - autoconf-archive after_prepare: - cd "$LGTM_WORKSPACE" - mkdir installdir - wget https://github.com/tpm2-software/tpm2-tss/archive/master.tar.gz - tar xf master.tar.gz - cd tpm2-tss-master - ./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-2.4.0/CHANGELOG.md000066400000000000000000000251251401030142600152670ustar00rootroot00000000000000# Change Log All notable changes to this project are documented in this file. The format is based on [Keep a CHANGELOG](http://keepachangelog.com/) ### 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-2.4.0/CONTRIBUTING.md000066400000000000000000000046311401030142600157060ustar00rootroot00000000000000# 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 emailed to a maintainer directly, or to Intel via the guidelines here: https://security-center.intel.com/VulnerabilityHandlingGuidelines.aspx # 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-2.4.0/INSTALL.md000066400000000000000000000423431401030142600151070ustar00rootroot00000000000000This 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 **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-2.4.0/LICENSE000066400000000000000000000023241401030142600144570ustar00rootroot00000000000000Redistribution 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-2.4.0/MAINTAINERS000066400000000000000000000001671401030142600151520ustar00rootroot00000000000000Philip Tricca Tadeusz Struk William Roberts tpm2-abrmd-2.4.0/Makefile.am000066400000000000000000000511211401030142600155050ustar00rootroot00000000000000# 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 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 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-2.4.0/README.md000066400000000000000000000113061401030142600147310ustar00rootroot00000000000000![Linux Build Status](https://github.com/tpm2-software/tpm2-abrmd/workflows/Linux%20Build%20Status/badge.svg) [![FreeBSD Build Status](https://api.cirrus-ci.com/github/tpm2-software/tpm2-abrmd.svg?branch=master)](https://cirrus-ci.com/github/tpm2-software/tpm2-abrmd) [![Coverity Scan](https://img.shields.io/coverity/scan/3997.svg)](https://scan.coverity.com/projects/01org-tpm2-abrmd) [![codecov](https://codecov.io/gh/tpm2-software/tpm2-abrmd/branch/master/graph/badge.svg)](https://codecov.io/gh/tpm2-software/tpm2-abrmd) [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/tpm2-software/tpm2-abrmd.svg?logo=lgtm&logoWidth=18)](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-2.4.0/RELEASE.md000066400000000000000000000116111401030142600150530ustar00rootroot00000000000000# Release Process: This document describes the general process that maintainers must follow when making a release of the `tabrmd`. # 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. # 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-2.4.0/bootstrap000077500000000000000000000006431401030142600154170ustar00rootroot00000000000000#!/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-2.4.0/configure.ac000066400000000000000000000220251401030142600157400ustar00rootroot00000000000000# 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 "$(which $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"], [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-2.4.0/coverity/000077500000000000000000000000001401030142600153155ustar00rootroot00000000000000tpm2-abrmd-2.4.0/coverity/coverity-model.c000066400000000000000000000010451401030142600204230ustar00rootroot00000000000000/* 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-2.4.0/dist/000077500000000000000000000000001401030142600144145ustar00rootroot00000000000000tpm2-abrmd-2.4.0/dist/com.intel.tss2.Tabrmd.service000066400000000000000000000001351401030142600217670ustar00rootroot00000000000000[D-BUS Service] Name=com.intel.tss2.Tabrmd Exec=/bin/false SystemdService=tpm2-abrmd.service tpm2-abrmd-2.4.0/dist/tpm2-abrmd.conf000066400000000000000000000010421401030142600172250ustar00rootroot00000000000000 tpm2-abrmd-2.4.0/dist/tpm2-abrmd.preset.in000066400000000000000000000000541401030142600202110ustar00rootroot00000000000000@SYSTEMD_PRESET_DEFAULT@ tpm2-abrmd.service tpm2-abrmd-2.4.0/dist/tpm2-abrmd.service.in000066400000000000000000000005631401030142600203540ustar00rootroot00000000000000[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-2.4.0/dist/tss2-tcti-tabrmd.pc.in000066400000000000000000000006131401030142600204500ustar00rootroot00000000000000prefix=@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-2.4.0/doc/000077500000000000000000000000001401030142600142165ustar00rootroot00000000000000tpm2-abrmd-2.4.0/doc/coding_standard_c.md000066400000000000000000000216311401030142600201700ustar00rootroot00000000000000# 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-2.4.0/doc/reference-counting.txt000066400000000000000000000045171401030142600205500ustar00rootroot00000000000000In 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-2.4.0/m4/000077500000000000000000000000001401030142600137715ustar00rootroot00000000000000tpm2-abrmd-2.4.0/m4/flags.m4000066400000000000000000000060231401030142600153300ustar00rootroot00000000000000dnl 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-2.4.0/man/000077500000000000000000000000001401030142600142245ustar00rootroot00000000000000tpm2-abrmd-2.4.0/man/Tss2_Tcti_Tabrmd_Init.3.in000066400000000000000000000104231401030142600210070ustar00rootroot00000000000000.\" 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-2.4.0/man/colophon.in000066400000000000000000000004211401030142600163720ustar00rootroot00000000000000.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-2.4.0/man/tpm2-abrmd.8.in000066400000000000000000000076761401030142600167070ustar00rootroot00000000000000.\" 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. .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. .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. .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. This option overrides the default behavior. .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-2.4.0/man/tss2-tcti-tabrmd.7.in000066400000000000000000000011341401030142600200230ustar00rootroot00000000000000.\" 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-2.4.0/scripts/000077500000000000000000000000001401030142600151405ustar00rootroot00000000000000tpm2-abrmd-2.4.0/scripts/int-test-funcs.sh000066400000000000000000000114311401030142600203570ustar00rootroot00000000000000#!/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-2.4.0/scripts/int-test-setup.sh000077500000000000000000000145401401030142600204100ustar00rootroot00000000000000#!/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-2.4.0/scripts/unit-count.sh000066400000000000000000000007421401030142600176040ustar00rootroot00000000000000#!/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-2.4.0/selinux/000077500000000000000000000000001401030142600151405ustar00rootroot00000000000000tpm2-abrmd-2.4.0/selinux/tabrmd.fc000066400000000000000000000002261401030142600167230ustar00rootroot00000000000000/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-2.4.0/selinux/tabrmd.if000066400000000000000000000000271401030142600167300ustar00rootroot00000000000000## tpm2-abrmd-2.4.0/selinux/tabrmd.te000066400000000000000000000012461401030142600167460ustar00rootroot00000000000000policy_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-2.4.0/src/000077500000000000000000000000001401030142600142405ustar00rootroot00000000000000tpm2-abrmd-2.4.0/src/command-attrs.c000066400000000000000000000040601401030142600171550ustar00rootroot00000000000000/* 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-2.4.0/src/command-attrs.h000066400000000000000000000031161401030142600171630ustar00rootroot00000000000000/* 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-2.4.0/src/command-source.c000066400000000000000000000343151401030142600173260ustar00rootroot00000000000000/* 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-2.4.0/src/command-source.h000066400000000000000000000066301401030142600173320ustar00rootroot00000000000000/* 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-2.4.0/src/connection-manager.c000066400000000000000000000250521401030142600201570ustar00rootroot00000000000000/* 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-2.4.0/src/connection-manager.h000066400000000000000000000046411401030142600201650ustar00rootroot00000000000000/* 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-2.4.0/src/connection.c000066400000000000000000000130451401030142600165460ustar00rootroot00000000000000/* 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-2.4.0/src/connection.h000066400000000000000000000032131401030142600165470ustar00rootroot00000000000000/* 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-2.4.0/src/control-message.c000066400000000000000000000037411401030142600175130ustar00rootroot00000000000000/* 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-2.4.0/src/control-message.h000066400000000000000000000031251401030142600175140ustar00rootroot00000000000000/* 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-2.4.0/src/handle-map-entry.c000066400000000000000000000123511401030142600175530ustar00rootroot00000000000000/* 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-2.4.0/src/handle-map-entry.h000066400000000000000000000034731401030142600175650ustar00rootroot00000000000000/* 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-2.4.0/src/handle-map.c000066400000000000000000000244341401030142600164210ustar00rootroot00000000000000/* 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-2.4.0/src/handle-map.h000066400000000000000000000047721401030142600164310ustar00rootroot00000000000000/* 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-2.4.0/src/include/000077500000000000000000000000001401030142600156635ustar00rootroot00000000000000tpm2-abrmd-2.4.0/src/include/tss2-tcti-tabrmd.h000066400000000000000000000007111401030142600211360ustar00rootroot00000000000000/* 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-2.4.0/src/ipc-frontend-dbus.c000066400000000000000000000640151401030142600177350ustar00rootroot00000000000000/* 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 */ /* * This is a utility function that builds an array of handles as a * GVariant object. The handles that make up the array are passed in * as a GUnixFDList. */ static GVariant* handle_array_variant_from_fdlist (GUnixFDList *fdlist) { GVariant *tuple; GVariantBuilder *builder; gint i = 0; /* build array of handles as GVariant */ builder = g_variant_builder_new (G_VARIANT_TYPE ("ah")); for (i = 0; i < g_unix_fd_list_get_length (fdlist); ++i) g_variant_builder_add (builder, "h", i); /* create tuple variant from builder */ tuple = g_variant_new ("ah", builder); g_variant_builder_unref (builder); return tuple; } /* * 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_variants[2], *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_variants[0] = handle_array_variant_from_fdlist (fd_list); /* return the random id to client, *not* xor'd with PID */ response_variants[1] = g_variant_new_uint64 (id); response_tuple = g_variant_new_tuple (response_variants, 2); /* * 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-2.4.0/src/ipc-frontend-dbus.h000066400000000000000000000046251401030142600177430ustar00rootroot00000000000000/* 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-2.4.0/src/ipc-frontend.c000066400000000000000000000054541401030142600170040ustar00rootroot00000000000000/* 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-2.4.0/src/ipc-frontend.h000066400000000000000000000034521401030142600170050ustar00rootroot00000000000000/* 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-2.4.0/src/logging.c000066400000000000000000000051141401030142600160330ustar00rootroot00000000000000/* 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-2.4.0/src/logging.h000066400000000000000000000016331401030142600160420ustar00rootroot00000000000000/* 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-2.4.0/src/message-queue.c000066400000000000000000000043161401030142600171560ustar00rootroot00000000000000/* 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-2.4.0/src/message-queue.h000066400000000000000000000025401401030142600171600ustar00rootroot00000000000000/* 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-2.4.0/src/random.c000066400000000000000000000115061401030142600156670ustar00rootroot00000000000000/* 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-2.4.0/src/random.h000066400000000000000000000033271401030142600156760ustar00rootroot00000000000000/* 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-2.4.0/src/resource-manager-session.c000066400000000000000000000137341401030142600213340ustar00rootroot00000000000000/* 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-2.4.0/src/resource-manager-session.h000066400000000000000000000011361401030142600213320ustar00rootroot00000000000000/* 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-2.4.0/src/resource-manager.c000066400000000000000000002020721401030142600176460ustar00rootroot00000000000000/* 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: 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-2.4.0/src/resource-manager.h000066400000000000000000000056761401030142600176660ustar00rootroot00000000000000/* 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-2.4.0/src/response-sink.c000066400000000000000000000155721401030142600172160ustar00rootroot00000000000000/* 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-2.4.0/src/response-sink.h000066400000000000000000000027531401030142600172200ustar00rootroot00000000000000/* 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-2.4.0/src/session-entry-state-enum.c000066400000000000000000000034311401030142600213070ustar00rootroot00000000000000/* 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 volatile gsize g_define_type_id__volatile = 0; if (g_once_init_enter (&g_define_type_id__volatile)) { 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__volatile, session_entry_state_enum_type); } return g_define_type_id__volatile; } tpm2-abrmd-2.4.0/src/session-entry-state-enum.h000066400000000000000000000034421401030142600213160ustar00rootroot00000000000000/* 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-2.4.0/src/session-entry.c000066400000000000000000000252761401030142600172420ustar00rootroot00000000000000/* 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-2.4.0/src/session-entry.h000066400000000000000000000061261401030142600172400ustar00rootroot00000000000000/* 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-2.4.0/src/session-list.c000066400000000000000000000362451401030142600170520ustar00rootroot00000000000000/* 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-2.4.0/src/session-list.h000066400000000000000000000077401401030142600170550ustar00rootroot00000000000000/* 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-2.4.0/src/sink-interface.c000066400000000000000000000013231401030142600173050ustar00rootroot00000000000000/* 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-2.4.0/src/sink-interface.h000066400000000000000000000020551401030142600173150ustar00rootroot00000000000000/* 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-2.4.0/src/source-interface.c000066400000000000000000000013641401030142600176460ustar00rootroot00000000000000/* 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-2.4.0/src/source-interface.h000066400000000000000000000022401401030142600176450ustar00rootroot00000000000000/* 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-2.4.0/src/tabrmd-defaults.h000066400000000000000000000014171401030142600174720ustar00rootroot00000000000000/* * 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-2.4.0/src/tabrmd-error.c000066400000000000000000000024451401030142600170110ustar00rootroot00000000000000/* 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-2.4.0/src/tabrmd-init.c000066400000000000000000000201151401030142600166150ustar00rootroot00000000000000#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-2.4.0/src/tabrmd-init.h000066400000000000000000000020771401030142600166310ustar00rootroot00000000000000#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-2.4.0/src/tabrmd-options.c000066400000000000000000000142101401030142600173440ustar00rootroot00000000000000/* * 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_warning ("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_warning ("tcti_conf after: \"%s\"", options->tcti_conf); return TRUE; error: tabrmd_options_free(options); return FALSE; } tpm2-abrmd-2.4.0/src/tabrmd-options.h000066400000000000000000000021221401030142600173500ustar00rootroot00000000000000/* * 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-2.4.0/src/tabrmd.c000066400000000000000000000044301401030142600156560ustar00rootroot00000000000000/* 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-2.4.0/src/tabrmd.h000066400000000000000000000026651401030142600156730ustar00rootroot00000000000000/* 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-2.4.0/src/tabrmd.xml000066400000000000000000000014661401030142600162420ustar00rootroot00000000000000 tpm2-abrmd-2.4.0/src/tcti-tabrmd-priv.h000066400000000000000000000125301401030142600176020ustar00rootroot00000000000000/* 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-2.4.0/src/tcti-tabrmd.c000066400000000000000000000522541401030142600166260ustar00rootroot00000000000000/* 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; 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, GVariant **out_fds, 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", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, out_fd_list, cancellable, error); if (_ret == NULL) { goto _out; } g_variant_get (_ret, "(@aht)", out_fds, 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; GVariant *fds_variant = 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), &fds_variant, &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_pointer (&fds_variant, g_variant_unref); 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-2.4.0/src/tcti-tabrmd.map000066400000000000000000000001361401030142600171510ustar00rootroot00000000000000{ global: Tss2_Tcti_Tabrmd_Init; Tss2_Tcti_Info; local: *; }; tpm2-abrmd-2.4.0/src/tcti.c000066400000000000000000000074421401030142600153560ustar00rootroot00000000000000/* 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-2.4.0/src/tcti.h000066400000000000000000000034031401030142600153540ustar00rootroot00000000000000/* 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-2.4.0/src/thread.c000066400000000000000000000022761401030142600156620ustar00rootroot00000000000000/* 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-2.4.0/src/thread.h000066400000000000000000000025351401030142600156650ustar00rootroot00000000000000/* 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-2.4.0/src/tpm2-command.c000066400000000000000000000555371401030142600167210ustar00rootroot00000000000000/* 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-2.4.0/src/tpm2-command.h000066400000000000000000000101111401030142600167010ustar00rootroot00000000000000/* 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-2.4.0/src/tpm2-header.c000066400000000000000000000075141401030142600165230ustar00rootroot00000000000000/* 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-2.4.0/src/tpm2-header.h000066400000000000000000000037611401030142600165300ustar00rootroot00000000000000/* 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-2.4.0/src/tpm2-response.c000066400000000000000000000330141401030142600171230ustar00rootroot00000000000000/* 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-2.4.0/src/tpm2-response.h000066400000000000000000000060361401030142600171340ustar00rootroot00000000000000/* 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-2.4.0/src/tpm2.c000066400000000000000000000525151401030142600152760ustar00rootroot00000000000000/* 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-2.4.0/src/tpm2.h000066400000000000000000000050141401030142600152730ustar00rootroot00000000000000/* 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-2.4.0/src/util.c000066400000000000000000000302601401030142600153620ustar00rootroot00000000000000/* 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-2.4.0/src/util.h000066400000000000000000000103721401030142600153710ustar00rootroot00000000000000/* 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-2.4.0/test/000077500000000000000000000000001401030142600144305ustar00rootroot00000000000000tpm2-abrmd-2.4.0/test/command-attrs_unit.c000066400000000000000000000145241401030142600204120ustar00rootroot00000000000000/* 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-2.4.0/test/command-source_unit.c000066400000000000000000000310241401030142600205470ustar00rootroot00000000000000/* 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-2.4.0/test/connection-manager_unit.c000066400000000000000000000120121401030142600213760ustar00rootroot00000000000000/* 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-2.4.0/test/connection_unit.c000066400000000000000000000130571401030142600200000ustar00rootroot00000000000000/* 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 "util.h" typedef struct connection_test_data { Connection *connection; GIOStream *client_iostream; } connection_test_data_t; /* * Data goes in the iostream_in, and out the iostream_out. */ static int write_read (GIOStream *iostream_in, GIOStream *iostream_out, const char *buf, ssize_t length) { char out_buf[256] = { 0 }; ssize_t ret; GInputStream *istream; GOutputStream *ostream; ostream = g_io_stream_get_output_stream (iostream_in); ret = g_output_stream_write (ostream, buf, length, NULL, NULL); if (ret != length) g_error ("error writing to fds[1]: %s", strerror (errno)); istream = g_io_stream_get_input_stream (iostream_out); ret = g_input_stream_read (istream, out_buf, length, NULL, NULL); if (ret != length) g_error ("error reading from fds[0]: %s", strerror (errno)); return ret; } static void connection_allocate_test (void **state) { HandleMap *handle_map = NULL; Connection *connection = NULL; gint client_fd; GIOStream *iostream; UNUSED_PARAM(state); 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); assert_non_null (connection); assert_true (client_fd >= 0); g_object_unref (connection); } static int connection_setup (void **state) { connection_test_data_t *data = NULL; HandleMap *handle_map = NULL; int client_fd; GIOStream *iostream; GSocket *socket; data = calloc (1, sizeof (connection_test_data_t)); assert_non_null (data); handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); data->connection = connection_new (iostream, 0, handle_map); g_object_unref (iostream); socket = g_socket_new_from_fd (client_fd, NULL); data->client_iostream = G_IO_STREAM (g_socket_connection_factory_create_connection (socket)); g_object_unref (socket); assert_non_null (data->connection); g_object_unref (handle_map); *state = data; return 0; } static int connection_teardown (void **state) { connection_test_data_t *data = (connection_test_data_t*)*state; g_object_unref (data->connection); g_object_unref (data->client_iostream); free (data); return 0; } static void connection_key_socket_test (void **state) { connection_test_data_t *data = (connection_test_data_t*)*state; Connection *connection = CONNECTION (data->connection); gpointer *key = NULL; key = connection_key_istream (connection); assert_ptr_equal (g_io_stream_get_input_stream (connection->iostream), key); } static void connection_key_id_test (void **state) { connection_test_data_t *data = (connection_test_data_t*)*state; Connection *connection = CONNECTION (data->connection); guint64 *key = NULL; key = (guint64*)connection_key_id (connection); assert_int_equal (connection->id, *key); } /* connection_client_to_server_test begin * This test creates a connection and communicates with it as though the pipes * that are created as part of connection setup. */ static void connection_client_to_server_test (void ** state) { connection_test_data_t *data = (connection_test_data_t*)*state; gint ret = 0; ret = write_read (data->connection->iostream, data->client_iostream, "test", strlen ("test")); if (ret == -1) g_print ("write_read failed: %d\n", ret); assert_int_equal (ret, strlen ("test")); } /* connection_client_to_server_test end */ /* connection_server_to_client_test begin * Do the same in the reverse direction. */ static void connection_server_to_client_test (void **state) { connection_test_data_t *data = (connection_test_data_t*)*state; gint ret = 0; ret = write_read (data->client_iostream, data->connection->iostream, "test", strlen ("test")); if (ret == -1) g_print ("write_read failed: %d\n", ret); assert_int_equal (ret, strlen ("test")); } /* connection_server_to_client_test end */ int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test (connection_allocate_test), cmocka_unit_test_setup_teardown (connection_key_socket_test, connection_setup, connection_teardown), cmocka_unit_test_setup_teardown (connection_key_id_test, connection_setup, connection_teardown), cmocka_unit_test_setup_teardown (connection_client_to_server_test, connection_setup, connection_teardown), cmocka_unit_test_setup_teardown (connection_server_to_client_test, connection_setup, connection_teardown), }; return cmocka_run_group_tests(tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/handle-map-entry_unit.c000066400000000000000000000057301401030142600210050ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include "handle-map-entry.h" #include "util.h" #define PHANDLE 0xdeadbeef #define VHANDLE 0xfeebdaed typedef struct { HandleMapEntry *handle_map_entry; } test_data_t; /* * Setup function */ static int handle_map_entry_setup (void **state) { test_data_t *data = NULL; data = calloc (1, sizeof (test_data_t)); data->handle_map_entry = handle_map_entry_new (PHANDLE, VHANDLE); *state = data; return 0; } /** * Tear down all of the data from the setup function. We don't have to * free the data buffer (data->buffer) since the Tpm2Command frees it as * part of its finalize function. */ static int handle_map_entry_teardown (void **state) { test_data_t *data = (test_data_t*)*state; g_object_unref (data->handle_map_entry); free (data); return 0; } /* * This is a test for memory management / reference counting. The setup * function does exactly that so when we get the Tpm2Command object we just * check to be sure it's a GObject and then we unref it. This test will * probably only fail when run under ASAN if the reference counting is * off. */ static void handle_map_entry_type_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_true (G_IS_OBJECT (data->handle_map_entry)); assert_true (IS_HANDLE_MAP_ENTRY (data->handle_map_entry)); } /* * This test assures us that the physical handle presented to the constructor * is the same one returned to us by the 'get_phandle' accessor function. */ static void handle_map_entry_get_phandle_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_int_equal (PHANDLE, handle_map_entry_get_phandle (data->handle_map_entry)); } /* * This test assures us that the virtual handle presented to the constructor * is the same one returned to us by the 'get_vhandle' accessor function. */ static void handle_map_entry_get_vhandle_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_int_equal (VHANDLE, handle_map_entry_get_vhandle (data->handle_map_entry)); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (handle_map_entry_type_test, handle_map_entry_setup, handle_map_entry_teardown), cmocka_unit_test_setup_teardown (handle_map_entry_get_phandle_test, handle_map_entry_setup, handle_map_entry_teardown), cmocka_unit_test_setup_teardown (handle_map_entry_get_vhandle_test, handle_map_entry_setup, handle_map_entry_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/handle-map_unit.c000066400000000000000000000112311401030142600176370ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include "handle-map.h" #include "handle-map-entry.h" #include "util.h" #define PHANDLE 0xdeadbeef #define VHANDLE 0xfeebdaed typedef struct { HandleMap *map; HandleMapEntry *entry; } test_data_t; /* * Setup and teardown functions. */ static int handle_map_setup_base (void **state) { test_data_t *data = NULL; data = calloc (1, sizeof (test_data_t)); data->map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); data->entry = handle_map_entry_new (PHANDLE, VHANDLE); *state = data; return 0; } static int handle_map_teardown (void **state) { test_data_t *data = (test_data_t*)*state; if (data->map) g_object_unref (data->map); if (data->entry) g_object_unref (data->entry); free (data); return 0; } static int handle_map_setup_with_entry (void **state) { test_data_t *data = NULL; handle_map_setup_base (state); data = (test_data_t*)*state; handle_map_insert (data->map, handle_map_entry_get_vhandle (data->entry), data->entry); return 0; } /* * This test ensures that the object instances created in the setup function * is a valid GObject and is a HandleMap to boot. */ static void handle_map_type_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_non_null (data); assert_non_null (data->entry); assert_non_null (data->map); assert_true (G_IS_OBJECT (data->map)); assert_true (IS_HANDLE_MAP (data->map)); } /* * This test ensures that the HandleMapEntry created in the setup function * can be inserted into the HandleMap. We prove to ourselves that this was * successful by asserting the size is 0 before the call, and 1 after. */ static void handle_map_insert_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_int_equal (handle_map_size (data->map), 0); handle_map_insert (data->map, VHANDLE, data->entry); assert_int_equal (handle_map_size (data->map), 1); } /* * This test ensures that we can remove a known entry from the map. The * EntryMap must already have an entry and it must be keyed to the * physical handle. */ static void handle_map_remove_test (void **state) { test_data_t *data = (test_data_t*)*state; gint ret; ret = handle_map_remove (data->map, VHANDLE); assert_int_equal (ret, TRUE); assert_int_equal (0, handle_map_size (data->map)); } /* * This test ensures that we can lookup a known entry from the map. The * EntryMap must already have an entry and it must be keyed to the virtual * handle. */ static void handle_map_vlookup_test (void **state) { test_data_t *data = (test_data_t*)*state; HandleMapEntry *entry_out; entry_out = HANDLE_MAP_ENTRY (handle_map_vlookup (data->map, VHANDLE)); assert_int_equal (data->entry, entry_out); g_object_unref (entry_out); } /* * This test ensures that the 'handle_map_next_vhandle' function returns * a different handle across two invocations. This is the lowest bar we can * set for this function. Additional tests should cover roll over and * recovery. */ static void handle_map_next_vhandle_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle1, handle2; handle1 = handle_map_next_vhandle (data->map); handle2 = handle_map_next_vhandle (data->map); assert_true (handle2 != handle1); } int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (handle_map_type_test, handle_map_setup_base, handle_map_teardown), cmocka_unit_test_setup_teardown (handle_map_insert_test, handle_map_setup_base, handle_map_teardown), cmocka_unit_test_setup_teardown (handle_map_remove_test, handle_map_setup_with_entry, handle_map_teardown), cmocka_unit_test_setup_teardown (handle_map_vlookup_test, handle_map_setup_with_entry, handle_map_teardown), cmocka_unit_test_setup_teardown (handle_map_next_vhandle_test, handle_map_setup_with_entry, handle_map_teardown), }; return cmocka_run_group_tests(tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/integration/000077500000000000000000000000001401030142600167535ustar00rootroot00000000000000tpm2-abrmd-2.4.0/test/integration/auth-session-max.int.c000066400000000000000000000033141401030142600231160ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include "common.h" #include "tabrmd-defaults.h" #include "tabrmd.h" #include "test.h" #define PRIxHANDLE "08" PRIx32 /* * This test exercises the session creation logic. We begin by creating the * most simple session we can. We then save it. When this test terminates * the RM should flush the context for us. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc; TPMI_SH_AUTH_SESSION session_handle [TABRMD_SESSIONS_MAX_DEFAULT + 1] = { 0 }; size_t i; for (i = 0; i < TABRMD_SESSIONS_MAX_DEFAULT + 1; ++i) { /* create an auth session */ g_info ("Starting unbound, unsalted auth session"); rc = start_auth_session (sapi_context, &session_handle [i]); if (rc == TSS2_RESMGR_RC_SESSION_MEMORY && i == TABRMD_SESSIONS_MAX_DEFAULT) { g_info ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxHANDLE " trying to create session #%zd", rc, i + 1); return 0; } if (rc != TSS2_RC_SUCCESS) { g_critical ("Got unexpected response code: 0x%" PRIxHANDLE, rc); return 1; } g_info ("StartAuthSession for TPM_SE_POLICY success! Session handle: " "0x%08" PRIx32, session_handle [i]); } /* * this test should never get here. An error should be caught in the loop * above which is what we want. If execution reaches this point in the * function then the upper bound isn't working properly */ return 1; } tpm2-abrmd-2.4.0/test/integration/auth-session-start-flush.int.c000066400000000000000000000026531401030142600246120ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include "common.h" #include "tpm2-struct-init.h" #include "test.h" #define PRIxHANDLE "08" PRIx32 /* * This test exercises the session creation logic. We begin by creating the * most simple session we can. We then flush it. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc; TPMI_SH_AUTH_SESSION session_handle = 0; TPMS_CONTEXT context = TPMS_CONTEXT_ZERO_INIT; /* create an auth session */ g_info ("Starting unbound, unsalted auth session"); rc = start_auth_session (sapi_context, &session_handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxHANDLE, rc); } g_info ("StartAuthSession for TPM_SE_POLICY success! Session handle: " "0x%08" PRIx32, session_handle); prettyprint_context (&context); /* flush context */ g_info ("Flushing context for session: 0x%" PRIxHANDLE, session_handle); rc = Tss2_Sys_FlushContext (sapi_context, session_handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_FlushContext failed: 0x%" PRIx32, rc); } g_info ("Flushed context for session handle: 0x%" PRIxHANDLE " success!", session_handle); return 0; } tpm2-abrmd-2.4.0/test/integration/auth-session-start-save-load.int.c000066400000000000000000000041021401030142600253330ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include "common.h" #include "tpm2-struct-init.h" #include "test.h" #define PRIxHANDLE "08" PRIx32 /* * This test exercises the session creation logic. We begin by creating the * most simple session we can. We then save it. When this test terminates * the RM should flush the context for us. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc; TPMI_SH_AUTH_SESSION session_handle = 0, session_handle_load = 0; TPMS_CONTEXT context = TPMS_CONTEXT_ZERO_INIT; /* create an auth session */ g_info ("Starting unbound, unsalted auth session"); rc = start_auth_session (sapi_context, &session_handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxHANDLE, rc); } g_info ("StartAuthSession for TPM_SE_POLICY success! Session handle: " "0x%08" PRIx32, session_handle); /* save context */ g_info ("Saving context for session: 0x%" PRIxHANDLE, session_handle); rc = Tss2_Sys_ContextSave (sapi_context, session_handle, &context); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextSave failed: 0x%" PRIxHANDLE, rc); } g_info ("Successfully saved context for session: 0x%" PRIxHANDLE, session_handle); prettyprint_context (&context); /* load context */ g_info ("Loading context for session: 0x%" PRIxHANDLE, session_handle); rc = Tss2_Sys_ContextLoad (sapi_context, &context, &session_handle_load); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextLoad failed: 0x%" PRIxHANDLE, rc); } g_info ("Successfully loaded context for session: 0x%" PRIxHANDLE, session_handle_load); if (session_handle_load == session_handle) { g_info ("session_handle == session_handle_load"); } else { g_error ("session_handle != session_handle_load"); } return 0; } tpm2-abrmd-2.4.0/test/integration/auth-session-start-save.int.c000066400000000000000000000027511401030142600244260ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include "common.h" #include "tpm2-struct-init.h" #include "test.h" #define PRIxHANDLE "08" PRIx32 /* * This test exercises the session creation logic. We begin by creating the * most simple session we can. We then save it. When this test terminates * the RM should flush the context for us. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc; TPMI_SH_AUTH_SESSION session_handle = 0; TPMS_CONTEXT context = TPMS_CONTEXT_ZERO_INIT; /* create an auth session */ g_info ("Starting unbound, unsalted auth session"); rc = start_auth_session (sapi_context, &session_handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxHANDLE, rc); } g_info ("StartAuthSession for TPM_SE_POLICY success! Session handle: " "0x%08" PRIx32, session_handle); /* save context */ g_info ("Saving context for session: 0x%" PRIxHANDLE, session_handle); rc = Tss2_Sys_ContextSave (sapi_context, session_handle, &context); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextSave failed: 0x%" PRIxHANDLE, rc); } g_info ("Successfully saved context for session: 0x%" PRIxHANDLE, session_handle); prettyprint_context (&context); return 0; } tpm2-abrmd-2.4.0/test/integration/common.c000066400000000000000000000420651401030142600204160ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ /* * These are common functions used by the integration tests. */ #include #include #include "common.h" #include "tss2-tcti-tabrmd.h" #include "tpm2-struct-init.h" TSS2_RC get_context_gap_max (TSS2_SYS_CONTEXT *sys_context, UINT32 *value) { TPMI_YES_NO more_data = TPM2_NO; TPMS_CAPABILITY_DATA capability_data; TSS2_RC rc; g_debug ("%s: GetCapability", __func__); rc = Tss2_Sys_GetCapability (sys_context, NULL, TPM2_CAP_TPM_PROPERTIES, TPM2_PT_CONTEXT_GAP_MAX, 1, &more_data, &capability_data, NULL); if (rc != TSS2_RC_SUCCESS) { g_error ("%s: GetCapability failed with RC: 0x%" PRIx32, __func__, rc); } if (capability_data.capability != TPM2_CAP_TPM_PROPERTIES) { g_error ("%s: capability returned is unexpected value: 0x%" PRIx32, __func__, capability_data.capability); } if (capability_data.data.tpmProperties.count != 1) { g_error ("%s: capability returned is unexpected count: 0x%" PRIx32, __func__, capability_data.data.tpmProperties.count); } if (capability_data.data.tpmProperties.tpmProperty[0].property != TPM2_PT_CONTEXT_GAP_MAX) { g_error ("%s: capability returned wrong property: 0x%" PRIx32, __func__, capability_data.data.tpmProperties.tpmProperty[0].property); } g_debug ("%s: TPM2_PT_CONTEXT_GAP_MAX value: 0x%" PRIx32, __func__, capability_data.data.tpmProperties.tpmProperty[0].value); *value = capability_data.data.tpmProperties.tpmProperty[0].value; return rc; } /* */ TSS2_RC create_primary (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE *handle) { TSS2_RC rc; TPM2B_SENSITIVE_CREATE in_sensitive = TPM2B_SENSITIVE_CREATE_ZERO_INIT; TPM2B_PUBLIC in_public = TPM2B_PUBLIC_ZERO_INIT; TPM2B_DATA outside_info = TPM2B_DATA_ZERO_INIT; TPML_PCR_SELECTION creation_pcr = TPML_PCR_SELECTION_ZERO_INIT; TPM2B_PUBLIC out_public = TPM2B_PUBLIC_ZERO_INIT; TPM2B_CREATION_DATA creation_data = TPM2B_CREATION_DATA_ZERO_INIT; TPM2B_DIGEST creation_digest = TPM2B_DIGEST_STATIC_INIT; TPMT_TK_CREATION creation_ticket = TPMT_TK_CREATION_ZERO_INIT; TPM2B_NAME name = TPM2B_NAME_STATIC_INIT; /* command auth stuff */ TSS2L_SYS_AUTH_COMMAND cmd_auths = { .count = 1, .auths = {{ .sessionHandle = TPM2_RS_PW, }} }; /* prepare in_sensitive */ in_sensitive.size = in_sensitive.sensitive.userAuth.size + 2; /* prepare in_public TPMT_PUBLIC */ /* TPMI_ALG_PUBLIC / publicArea */ in_public.publicArea.type = TPM2_ALG_RSA; /* TPMI_ALG_HASH / nameAlg */ in_public.publicArea.nameAlg = TPM2_ALG_SHA256; /* TPMA_OBJECT / objectAttributes */ in_public.publicArea.objectAttributes = \ TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT | \ TPMA_OBJECT_SENSITIVEDATAORIGIN | TPMA_OBJECT_USERWITHAUTH | \ TPMA_OBJECT_RESTRICTED | TPMA_OBJECT_DECRYPT; /* TPM2B_DIGEST / authPolicy */ in_public.publicArea.authPolicy.size = 0; /* TPMU_PUBLIC_PARAMS / parameters: key type is TPM2_ALG_RSA, set parameters accordingly */ in_public.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM2_ALG_AES; in_public.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; in_public.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM2_ALG_CFB; in_public.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL; in_public.publicArea.parameters.rsaDetail.keyBits = 2048; in_public.publicArea.parameters.rsaDetail.exponent = 0; /* TPMU_PUBLIC_ID / unique */ in_public.publicArea.unique.rsa.size = 0; rc = TSS2_RETRY_EXP (Tss2_Sys_CreatePrimary ( sapi_context, TPM2_RH_NULL, /* in: hierarchy */ &cmd_auths, /* in: in sessions / auths */ &in_sensitive, /* in: sensitive data? */ &in_public, /* in: key template */ &outside_info, /* in: data that will be included in the creation data */ &creation_pcr, /* in: PCR data used in creation data */ handle, /* out: handle for loaded object */ &out_public, /* out: public portion of created object */ &creation_data, /* out: TPMT_CREATION_DATA for object */ &creation_digest, /* out: digest of creationData using nameAlg */ &creation_ticket, /* out: ticket used to associate object and TPM */ &name, /* out: name of created object */ NULL /* out: sessions / auths returned */ )); if (rc == TSS2_RC_SUCCESS) { g_print (" handle returned: 0x%" PRIx32 "\n", *handle); } else { g_warning ("Tss2_Sys_CreatePrimary returned: 0x%" PRIx32, rc); } return rc; } /* */ TSS2_RC create_key (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE parent_handle, TPM2B_PRIVATE *out_private, TPM2B_PUBLIC *out_public) { TSS2_RC rc; TPM2B_SENSITIVE_CREATE in_sensitive = TPM2B_SENSITIVE_CREATE_ZERO_INIT; TPM2B_PUBLIC in_public = TPM2B_PUBLIC_ZERO_INIT; TPM2B_DATA outside_info = TPM2B_DATA_ZERO_INIT; TPML_PCR_SELECTION creation_pcr = TPML_PCR_SELECTION_ZERO_INIT; TPM2B_CREATION_DATA creation_data = TPM2B_CREATION_DATA_ZERO_INIT; TPM2B_DIGEST creation_hash = TPM2B_DIGEST_STATIC_INIT; TPMT_TK_CREATION creation_ticket = TPMT_TK_CREATION_ZERO_INIT; TSS2L_SYS_AUTH_COMMAND cmd_auths = { .count = 1, .auths = {{ .sessionHandle = TPM2_RS_PW, }} }; g_debug ("create_key with parent_handle: 0x%" PRIx32, parent_handle); in_sensitive.size = in_sensitive.sensitive.userAuth.size + 2; in_public.publicArea.type = TPM2_ALG_RSA; in_public.publicArea.nameAlg = TPM2_ALG_SHA256; in_public.publicArea.objectAttributes = \ TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT | \ TPMA_OBJECT_SENSITIVEDATAORIGIN | TPMA_OBJECT_USERWITHAUTH | \ TPMA_OBJECT_DECRYPT | TPMA_OBJECT_SIGN_ENCRYPT; /* TPM2B_DIGEST / authPolicy */ in_public.publicArea.authPolicy.size = 0; /* TPMU_PUBLIC_PARAMS / parameters: key type is TPM2_ALG_RSA, set parameters accordingly */ in_public.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM2_ALG_NULL; in_public.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL; in_public.publicArea.parameters.rsaDetail.keyBits = 2048; in_public.publicArea.parameters.rsaDetail.exponent = 0; /* TPMU_PUBLIC_ID / unique */ in_public.publicArea.unique.rsa.size = 0; g_print ("Tss2_Sys_Create with parent handle: 0x%" PRIx32 "\n", parent_handle); rc = TSS2_RETRY_EXP (Tss2_Sys_Create( sapi_context, parent_handle, &cmd_auths, &in_sensitive, &in_public, &outside_info, &creation_pcr, out_private, /* private part of new key returned to caller */ out_public, /* public part of new key returned to caller */ &creation_data, &creation_hash, &creation_ticket, NULL )); if (rc == TSS2_RC_SUCCESS) { g_print ("Tss2_Sys_Create returned TSS2_RC_SUCCESS\n parent handle: " "0x%" PRIx32 "\n out_private: 0x%" PRIxPTR "\n out_public: " "0x%" PRIxPTR "\n", parent_handle, (uintptr_t)&out_private, (uintptr_t)&out_public); } else { g_warning ("Tss2_Sys_Create returned: 0x%" PRIx32, rc); } return rc; } /* */ TSS2_RC load_key (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE parent_handle, TPM2_HANDLE *out_handle, TPM2B_PRIVATE *in_private, TPM2B_PUBLIC *in_public) { TSS2_RC rc; TPM2B_NAME name = TPM2B_NAME_STATIC_INIT; TSS2L_SYS_AUTH_COMMAND cmd_auths = { .count = 1, .auths = {{ .sessionHandle = TPM2_RS_PW, }} }; g_print ("Tss2_Sys_Load with parent handle: 0x%" PRIx32 "\n in_private: " "0x%" PRIxPTR "\n in_public: 0x%" PRIxPTR "\n", parent_handle, (uintptr_t)in_private, (uintptr_t)in_public); rc = Tss2_Sys_Load (sapi_context, parent_handle, &cmd_auths, in_private, in_public, out_handle, &name, NULL); if (rc == TSS2_RC_SUCCESS) { g_print ("Tss2_Sys_Load returned TSS2_RC_SUCCESS\n parent handle: " "0x%" PRIx32 "\n new handle: 0x%" PRIx32 "\n", parent_handle, *out_handle); } else { g_warning ("Tss2_Sys_Create returned: 0x%" PRIx32, rc); } return rc; } TSS2_RC undefine_nv_index (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE index) { TSS2_RC rc; TSS2L_SYS_AUTH_COMMAND cmd_auths = { .count = 1, .auths = {{ .sessionHandle = TPM2_RS_PW, }} }; g_debug ("undefine_nv_index: sapi_context: 0x%" PRIxPTR " index: 0x%" PRIx32, (uintptr_t)sapi_context, index); if (sapi_context == NULL) { g_error ("undefine_nv_index passed NULL reference"); } rc = Tss2_Sys_NV_UndefineSpace (sapi_context, TPM2_RH_OWNER, index, &cmd_auths, 0); if (rc != TSS2_RC_SUCCESS) { g_warning ("Tss2_Sys_Nv_UndefineSpace: failed to undefine nv index for " "index: 0x%" PRIx32 " TSS2_RC: 0x%" PRIx32, index, rc); } return rc; } TSS2_RC save_context (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE handle, TPMS_CONTEXT *context) { TSS2_RC rc; g_debug ("save_context: sapi_context: 0x%" PRIxPTR " handle: 0x%" PRIx32 " context: 0x%" PRIxPTR, (uintptr_t)sapi_context, handle, (uintptr_t)context); if (sapi_context == NULL || context == NULL) { g_error ("save_context passed NULL reference"); } rc = Tss2_Sys_ContextSave (sapi_context, handle, context); if (rc != TSS2_RC_SUCCESS) { g_warning ("Tss2_Sys_ContextSave: failed to save context for handle: " "0x%" PRIx32, handle); } return rc; } TSS2_RC flush_context (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE handle) { TSS2_RC rc; g_debug ("flush_context: sapi_context: 0x%" PRIxPTR " handle: 0x%" PRIx32, (uintptr_t)sapi_context, handle); if (sapi_context == NULL) { g_error ("flush_context passed NULL reference"); } rc = Tss2_Sys_FlushContext (sapi_context, handle); if (rc != TSS2_RC_SUCCESS) { g_warning ("Tss2_Sys_FlushContext: failed to flush context for " "handle: 0x%" PRIx32 " TSS2_RC: 0x%" PRIx32, handle, rc); } return rc; } TSS2_RC evict_persistent_objs (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE handle) { TSS2_RC rc; TSS2L_SYS_AUTH_COMMAND cmd_auths = { .count = 1, .auths = {{ .sessionHandle = TPM2_RS_PW, }} }; g_debug ("evict_persistent_objs: sapi_context: 0x%" PRIxPTR " handle: 0x%" PRIx32, (uintptr_t)sapi_context, handle); if (sapi_context == NULL) { g_error ("evict_persistent_objs passed NULL reference"); } rc = Tss2_Sys_EvictControl (sapi_context, TPM2_RH_OWNER, handle, &cmd_auths, handle, NULL); if (rc != TPM2_RC_SUCCESS) { g_warning ("Tss2_Sys_EvictControl: failed to evict control for " "handle: 0x%" PRIx32 " TSS2_RC: 0x%" PRIx32, handle, rc); } return rc; } void clean_up_all (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc; unsigned int i, j; TPMI_YES_NO more_data; TPMS_CAPABILITY_DATA capability_data; TPML_HANDLE *handles = &capability_data.data.handles; struct property_info { UINT32 property; UINT32 count; } properties[] = { { .property = TPM2_PERSISTENT_FIRST, .count = TPM2_MAX_CAP_HANDLES, }, { .property = TPM2_TRANSIENT_FIRST, .count = TPM2_MAX_CAP_HANDLES, }, { .property = TPM2_NV_INDEX_FIRST, .count = TPM2_MAX_CAP_HANDLES, }, { .property = TPM2_LOADED_SESSION_FIRST, .count = TPM2_MAX_CAP_HANDLES, }, { .property = TPM2_ACTIVE_SESSION_FIRST, .count = TPM2_MAX_CAP_HANDLES, } }; for (i = 0; i < sizeof(properties) / sizeof(struct property_info); ++i) { rc = Tss2_Sys_GetCapability (sapi_context, NULL, TPM2_CAP_HANDLES, properties[i].property, properties[i].count, &more_data, &capability_data, NULL); if (rc != TSS2_RC_SUCCESS) { g_warning ("Tss2_Sys_GetCapability: failed to get capability for " "handles property: 0x%" PRIx32 " count: 0x%" PRIx32 " TSS2_RC: 0x%" PRIx32, properties[i].property, properties[i].count, rc); continue; } for (j = 0; j < handles->count; ++j) { if (properties[i].property == (UINT32) TPM2_NV_INDEX_FIRST) { undefine_nv_index (sapi_context, handles->handle[j]); continue; } /* * TPM2_FlushContext command may not be used to remove a persistent * objects from the TPM. So we always handle persistent handles * prior to transient handles to allow evicting them on next round. */ if (properties[i].property == (UINT32) TPM2_PERSISTENT_FIRST) { evict_persistent_objs (sapi_context, handles->handle[j]); continue; } flush_context (sapi_context, handles->handle[j]); } } } /* * This function is a very simple wrapper around the TPM2_StartAuthSession * function. It uses the most simple / default values to create an unsalted, * unbound session. */ TSS2_RC start_auth_session (TSS2_SYS_CONTEXT *sapi_context, TPMI_SH_AUTH_SESSION *session_handle) { TSS2_RC rc; TPM2B_NONCE nonce_caller = { .size = TPM2_SHA256_DIGEST_SIZE, .buffer = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } }; TPM2B_NONCE nonce_tpm = { .size = TPM2_SHA256_DIGEST_SIZE, .buffer = { 0 } }; TPM2B_ENCRYPTED_SECRET encrypted_salt = TPM2B_ENCRYPTED_SECRET_ZERO_INIT; TPMT_SYM_DEF symmetric = { .algorithm = TPM2_ALG_NULL }; g_debug ("StartAuthSession for TPM_SE_POLICY (policy session)"); rc = Tss2_Sys_StartAuthSession (sapi_context, TPM2_RH_NULL, /* tpmKey */ TPM2_RH_NULL, /* bind */ 0, /* cmdAuthsArray */ &nonce_caller, /* nonceCaller */ &encrypted_salt, /* encryptedSalt */ TPM2_SE_POLICY, /* sessionType */ &symmetric, /* symmetric */ TPM2_ALG_SHA256, /* authHash */ session_handle, /* sessionHandle */ &nonce_tpm, /* nonceTPM */ 0 /* rspAuthsArray */ ); if (rc != TSS2_RC_SUCCESS) g_warning ("Tss2_Sys_StartAuthSession failed: 0x%" PRIx32, rc); return rc; } /* * This function dumps the fields of the TPMS_CONTEXT structure. The one * encrypted field (contextBlob) is dumped as an address. */ void prettyprint_context (TPMS_CONTEXT *context) { g_debug ("sequence: 0x%" PRIx64, context->sequence); g_debug ("savedHandle: 0x%" PRIxHANDLE, context->savedHandle); g_debug ("hierarchy: 0x%" PRIxHANDLE, context->hierarchy); g_debug ("contextBlob: 0x%" PRIxPTR, (uintptr_t)&context->contextBlob); } tpm2-abrmd-2.4.0/test/integration/common.h000066400000000000000000000040101401030142600204070ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ /* * These are common functions used by the integration tests. */ #include #include #include /* Current ABI version */ #define SUPPORTED_ABI_VERSION \ { \ .tssCreator = 1, \ .tssFamily = 2, \ .tssLevel = 1, \ .tssVersion = 108, \ } /* * This macro is useful as a wrapper around SAPI functions to automatically * retry function calls when the RC is TPM2_RC_RETRY. */ #define TSS2_RETRY_EXP(expression) \ ({ \ TSS2_RC __result = 0; \ do { \ __result = (expression); \ } while ((__result & 0x0000ffff) == TPM2_RC_RETRY); \ __result; \ }) #define PRIxHANDLE "08" PRIx32 TSS2_RC get_context_gap_max ( TSS2_SYS_CONTEXT *sys_context, UINT32 *value); TSS2_RC create_primary ( TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE *handle ); TSS2_RC create_key ( TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE parent_handle, TPM2B_PRIVATE *out_private, TPM2B_PUBLIC *out_public ); TSS2_RC load_key ( TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE parent_handle, TPM2_HANDLE *out_handle, TPM2B_PRIVATE *in_private, TPM2B_PUBLIC *in_public ); TSS2_RC save_context ( TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE handle, TPMS_CONTEXT *context ); TSS2_RC flush_context ( TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE handle ); TSS2_RC start_auth_session ( TSS2_SYS_CONTEXT *sapi_context, TPMI_SH_AUTH_SESSION *session_handle ); void prettyprint_context ( TPMS_CONTEXT *context ); void clean_up_all ( TSS2_SYS_CONTEXT *sapi_context ); tpm2-abrmd-2.4.0/test/integration/context-util.c000066400000000000000000000111521401030142600215560ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include #include #include "context-util.h" #include "common.h" /* * Initialize a TCTI context for the tabrmd. Currently it requires no options. */ static TSS2_TCTI_CONTEXT* tcti_tabrmd_init (const char *conf) { TSS2_RC rc; TSS2_TCTI_CONTEXT *tcti_ctx; size_t size; g_debug ("%s: with conf: \"%s\"", __func__, conf); 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); return NULL; } tcti_ctx = calloc (1, size); if (tcti_ctx == NULL) { fprintf (stderr, "Allocation for TCTI context failed: %s\n", strerror (errno)); return NULL; } rc = Tss2_Tcti_Tabrmd_Init (tcti_ctx, &size, conf); if (rc != TSS2_RC_SUCCESS) { fprintf (stderr, "Failed to initialize tabrmd TCTI context: " "0x%" PRIx32 "\n", rc); free (tcti_ctx); return NULL; } return tcti_ctx; } /* * Initialize a SAPI context using the TCTI context provided by the caller. * This function allocates memory for the SAPI context and returns it to the * caller. This memory must be freed by the caller. */ static TSS2_SYS_CONTEXT* sapi_init_from_tcti_ctx (TSS2_TCTI_CONTEXT *tcti_ctx) { TSS2_SYS_CONTEXT *sapi_ctx; TSS2_RC rc; size_t size; TSS2_ABI_VERSION abi_version = SUPPORTED_ABI_VERSION; size = Tss2_Sys_GetContextSize (0); sapi_ctx = (TSS2_SYS_CONTEXT*)calloc (1, size); if (sapi_ctx == NULL) { fprintf (stderr, "Failed to allocate 0x%zx bytes for the SAPI context\n", size); return NULL; } rc = Tss2_Sys_Initialize (sapi_ctx, size, tcti_ctx, &abi_version); if (rc != TSS2_RC_SUCCESS) { fprintf (stderr, "Failed to initialize SAPI context: 0x%x\n", rc); free (sapi_ctx); return NULL; } return sapi_ctx; } /* * Initialize a SAPI context to use a socket TCTI. Get configuration data from * the provided structure. */ TSS2_SYS_CONTEXT* sapi_init_from_opts (test_opts_t *options) { TSS2_TCTI_CONTEXT *tcti_ctx = NULL; TSS2_SYS_CONTEXT *sapi_ctx; size_t i; for (i = 0; i < options->tcti_retries && tcti_ctx == NULL; ++i) { tcti_ctx = tcti_init_from_opts (options); if (tcti_ctx == NULL) { g_debug ("sapi_init_from_opts: tcti_ctx returned NULL on try: %zd", i); sleep (1); } } if (tcti_ctx == NULL) return NULL; sapi_ctx = sapi_init_from_tcti_ctx (tcti_ctx); if (sapi_ctx == NULL) return NULL; return sapi_ctx; } /* * Initialize a TSS2_TCTI_CONTEXT using whatever TCTI data is in the options * structure. This is a mechanism that allows the calling application to be * mostly ignorant of which TCTI they're creating / initializing. */ TSS2_TCTI_CONTEXT* tcti_init_from_opts (test_opts_t *options) { TSS2_TCTI_CONTEXT *tcti_ctx = NULL; TSS2_RC rc; if (options->tcti_filename != NULL) { rc = Tss2_TctiLdr_Initialize_Ex (options->tcti_filename, options->tcti_conf, &tcti_ctx); if (rc != TSS2_RC_SUCCESS || tcti_ctx == NULL) return NULL; return tcti_ctx; } else { return tcti_tabrmd_init (options->tcti_conf); } } void tcti_free_from_opts (test_opts_t *options, TSS2_TCTI_CONTEXT **tcti_context) { if (options->tcti_filename != NULL) { Tss2_TctiLdr_Finalize (tcti_context); } else { Tss2_Tcti_Finalize (*tcti_context); free(*tcti_context); *tcti_context = NULL; } } /* * Teardown and free the resources associated with a SAPI context structure. * This includes tearing down the TCTI as well. */ void sapi_teardown_full (TSS2_SYS_CONTEXT *sapi_context) { TSS2_TCTI_CONTEXT *tcti_context = NULL; TSS2_RC rc; rc = Tss2_Sys_GetTctiContext (sapi_context, &tcti_context); if (rc != TSS2_RC_SUCCESS) return; Tss2_Sys_Finalize (sapi_context); free (sapi_context); if (tcti_context) { Tss2_TctiLdr_Finalize (&tcti_context); /* if tctildr can't finalize, try to do it the old fashioned way */ if (tcti_context != NULL) { Tss2_Tcti_Finalize (tcti_context); free (tcti_context); } } } tpm2-abrmd-2.4.0/test/integration/context-util.h000066400000000000000000000013551401030142600215670ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #ifndef CONTEXT_UTIL_H #define CONTEXT_UTIL_H #include #include #include "test-options.h" /** * functions to setup TCTIs and SAPI contexts using data from the common * options */ TSS2_TCTI_CONTEXT* tcti_init_from_opts (test_opts_t *options); TSS2_SYS_CONTEXT* sapi_init_from_opts (test_opts_t *options); void sapi_teardown_full (TSS2_SYS_CONTEXT *sapi_context); void tcti_free_from_opts (test_opts_t *options, TSS2_TCTI_CONTEXT **tcti_context); #endif /* CONTEXT_UTIL_H */ tpm2-abrmd-2.4.0/test/integration/get-capability-handles-transient.int.c000066400000000000000000000177661401030142600262500ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include "tss2-tcti-tabrmd.h" #include "tpm2-struct-init.h" #include "common.h" #define NUM_KEYS 5 #define ENV_NUM_KEYS "TABRMD_TEST_NUM_KEYS" /* * Creates a pile of keys. handles[0] will always be the primary. All others * are children of this primary. */ TSS2_RC create_keys (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE *handles[], size_t count) { TPM2B_PRIVATE out_private = TPM2B_PRIVATE_STATIC_INIT; TPM2B_PUBLIC out_public = TPM2B_PUBLIC_ZERO_INIT; TSS2_RC rc = TSS2_RC_SUCCESS; rc = create_primary (sapi_context, handles [0]); if (rc != TSS2_RC_SUCCESS) g_error ("Failed to create primary key: 0x%" PRIx32, rc); g_print ("primary handle: 0x%" PRIx32 "\n", *handles [0]); /* * Loop iteration starts @ 1 since we've already created the primary and * stored its handle in handles [0]. */ guint i; for (i = 1; i < count; ++i) { rc = create_key (sapi_context, (*handles) [0], &out_private, &out_public); if (rc != TSS2_RC_SUCCESS) g_error ("Failed to create_key: 0x%" PRIx32, rc); rc = load_key (sapi_context, (*handles) [0], &(*handles) [i], &out_private, &out_public); if (rc != TSS2_RC_SUCCESS) g_error ("Failed to create_key: 0x%" PRIx32, rc); out_public.size = 0; } return rc; } /* * Query the TPM for transient object handles. These are returned to the * caller in the `handles` parameter array. The `handle_count` parameter * must hold the size of the handles array passed in. The function will * update this value to the number of handles returned. */ TSS2_RC get_transient_handles (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE handles[], size_t *handle_count) { TSS2_RC rc = TSS2_RC_SUCCESS; TPMI_YES_NO more_data = 0; TPMS_CAPABILITY_DATA cap_data = TPMS_CAPABILITY_DATA_ZERO_INIT; size_t handles_left = *handle_count; size_t handles_got = 0; handles [handles_got] = TPM2_TRANSIENT_FIRST; do { g_print ("requesting %zu handles from TPM\n", handles_left); rc = Tss2_Sys_GetCapability (sapi_context, NULL, TPM2_CAP_HANDLES, handles [handles_got], handles_left, &more_data, &cap_data, NULL); if (rc != TSS2_RC_SUCCESS) { return rc; } if (cap_data.capability != TPM2_CAP_HANDLES) { g_error ("got weird capability: 0x%" PRIx32, cap_data.capability); } g_print ("got %" PRIu32 " handles from TPM\n", cap_data.data.handles.count); if (*handle_count < handles_got + cap_data.data.handles.count) { g_warning ("too many handles for the array"); return 1; } size_t i; for (i = 0; i < cap_data.data.handles.count; ++i) { g_print (" 0x%" PRIx32 "\n", cap_data.data.handles.handle [i]); handles [handles_got + i] = cap_data.data.handles.handle [i]; } handles_got += cap_data.data.handles.count; handles_left -= handles_got; more_data == 1 ? g_print ("more data\n") : g_print ("no more data\n"); } while (more_data == 1); *handle_count = handles_got; return TSS2_RC_SUCCESS; } /* * Query the TPM for transient object handles and dump them to stdout. 'count' * handles are retrieved for each call to GetCapability until we've obtained * all handles. */ TSS2_RC get_cap_trans_dump (TSS2_SYS_CONTEXT *sapi_context, size_t count) { TSS2_RC rc = TSS2_RC_SUCCESS; TPMI_YES_NO more_data = 0; TPMS_CAPABILITY_DATA cap_data = TPMS_CAPABILITY_DATA_ZERO_INIT; TPM2_HANDLE last_handle = TPM2_TRANSIENT_FIRST; do { g_print ("requesting %zu handles from TPM\n", count); rc = Tss2_Sys_GetCapability (sapi_context, NULL, TPM2_CAP_HANDLES, last_handle, count, &more_data, &cap_data, NULL); if (rc != TSS2_RC_SUCCESS) { return rc; } if (cap_data.capability != TPM2_CAP_HANDLES) { g_error ("got weird capability: 0x%" PRIx32, cap_data.capability); } g_print ("got %" PRIu32 " handles from TPM\n", cap_data.data.handles.count); size_t i; for (i = 0; i < cap_data.data.handles.count; ++i) { g_print (" 0x%" PRIx32 "\n", cap_data.data.handles.handle [i]); /* add one to last handle to get the next handle */ last_handle = cap_data.data.handles.handle [i] + 1; } more_data == 1 ? g_print ("more data\n") : g_print ("no more data\n"); } while (more_data == 1); return TSS2_RC_SUCCESS; } /* * This is a test program that creates and loads a configurable number of * transient objects in the NULL hierarchy. The number of keys created * under the NULL primary key can be provided as a base 10 integer on * the command line. This is the only parameter the program takes. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TPM2_HANDLE *handles_load; TPM2_HANDLE *handles_query; size_t handles_count; char *env_str = NULL, *end_ptr = NULL; uint8_t loops = NUM_KEYS; TSS2_RC rc = TSS2_RC_SUCCESS; int ret = 0; env_str = getenv (ENV_NUM_KEYS); if (env_str != NULL) loops = strtol (env_str, &end_ptr, 10); g_print ("%s: %d\n", ENV_NUM_KEYS, loops); handles_load = calloc (loops, sizeof (TPM2_HANDLE)); handles_query = calloc (loops, sizeof (TPM2_HANDLE)); handles_count = loops; rc = create_keys (sapi_context, &handles_load, handles_count); if (rc != TSS2_RC_SUCCESS) { ret = rc; goto out; } g_debug ("iterating over handles:"); size_t i; for (i = 0; i < loops; ++i) { g_print ("handle [%zu]: 0x%" PRIx32 "\n", i, handles_load [i]); } g_print ("querying handles with GetCapability in increments of 2\n"); rc = get_cap_trans_dump (sapi_context, 2); if (rc != TSS2_RC_SUCCESS) { g_warning ("get_cap_trans_dump returned 0x%" PRIx32, rc); ret = rc; goto out; } rc = get_transient_handles (sapi_context, handles_query, &handles_count); if (rc != TSS2_RC_SUCCESS) { g_warning ("get_transient_handles returned 0x%" PRIx32, rc); ret = rc; goto out; } if (handles_count != loops) { g_warning ("GetCapabilities returned %zu handles, expecting %" PRIu16, handles_count, loops); ret = -1; goto out; } g_debug ("loaded handle count: %zu", handles_count); for (i = 0; i < handles_count; ++i) { g_debug ("handles_load [%zu]: 0x%" PRIx32 " handles_query [%zu]: 0x%" PRIx32, i, handles_load [i], i, handles_query [i]); if (handles_load [i] != handles_query [i]) { ret = -1; goto out; } } ret = 0; out: free (handles_load); free (handles_query); return ret; } tpm2-abrmd-2.4.0/test/integration/hash-sequence.int.c000066400000000000000000000314111401030142600224410ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ /* * This is a port of TestHash() from TSS's test/tpmclient/tpmclient */ #include #include #include #include #include #include "tabrmd.h" #include "tss2-tcti-tabrmd.h" #include "common.h" #define INIT_SIMPLE_TPM2B_SIZE(type) (type).size = sizeof (type) - 2 #define MAX_TEST_SEQUENCES 10 const uint8_t memoryToHash [] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0xde, 0xad, 0xbe, 0xef }; /* Known good hash of above memory. */ const uint8_t goodHashValue [] = { 0xB3, 0xFD, 0x6A, 0xD2, 0x9F, 0xD0, 0x13, 0x52, 0xBA, 0xFC, 0x8B, 0x22, 0xC9, 0x6D, 0x88, 0x42, 0xA3, 0x3C, 0xB0, 0xC9 }; int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { int i; TSS2_RC rval; TPMI_DH_OBJECT sequenceHandle[MAX_TEST_SEQUENCES]; TPM2B_MAX_BUFFER dataToHash; TPM2B_DIGEST result; TPMT_TK_HASHCHECK validation; TPM2B_AUTH auth = { .size = 2, .buffer = { 0x00, 0xff }, }; TSS2L_SYS_AUTH_COMMAND cmd_auths = { .count = 1, .auths = {{ .sessionHandle = TPM2_RS_PW, .hmac = auth, }} }; TSS2L_SYS_AUTH_RESPONSE rsp_auths; rval = Tss2_Sys_HashSequenceStart (sapi_context, 0, &auth, TPM2_ALG_SHA1, &sequenceHandle[0], 0); if (rval != TSS2_RC_SUCCESS) { g_error ("Failed to initialize hash sequence. RC = 0x%x", rval); } dataToHash.size = TPM2_MAX_DIGEST_BUFFER; memcpy (&dataToHash.buffer[0], &memoryToHash[0], dataToHash.size); rval = Tss2_Sys_SequenceUpdate (sapi_context, sequenceHandle[0], &cmd_auths, &dataToHash, &rsp_auths); if (rval != TSS2_RC_SUCCESS) { g_error ("Hash sequence update failed. RC = 0x%x", rval); } /* * Now try starting a bunch of sequences to see what happens. This checks * that the resource manager properly saves and restores the context of * the interrupted original sequence. */ for (i = 1; i < 5; i++) { rval = Tss2_Sys_HashSequenceStart (sapi_context, 0, &auth, TPM2_ALG_SHA1, &sequenceHandle[i], 0); if (rval != TSS2_RC_SUCCESS) { g_error ("Failed to initialize interrupting hash sequence %d. " "RC = 0x%x", i, rval); } } /* Now end the created sequences. */ dataToHash.size = 0; for (i = 1; i < 5; i++) { INIT_SIMPLE_TPM2B_SIZE (result); rval = Tss2_Sys_SequenceComplete (sapi_context, sequenceHandle[i], &cmd_auths, &dataToHash, TPM2_RH_PLATFORM, &result, &validation, &rsp_auths); if (rval != TSS2_RC_SUCCESS) { g_error ("Interrupting hash sequence %d failed. RC = 0x%x", i, rval); } } /* Now try to finish the interrupted sequence. */ rval = Tss2_Sys_SequenceUpdate (sapi_context, sequenceHandle[0], &cmd_auths, &dataToHash, &rsp_auths); if (rval != TSS2_RC_SUCCESS) { g_error ("Failed to update original hash sequence. RC = 0x%x", rval); } dataToHash.size = sizeof (memoryToHash) - TPM2_MAX_DIGEST_BUFFER; memcpy (dataToHash.buffer, &memoryToHash[TPM2_MAX_DIGEST_BUFFER], dataToHash.size); INIT_SIMPLE_TPM2B_SIZE (result); rval = Tss2_Sys_SequenceComplete (sapi_context, sequenceHandle[0], &cmd_auths, &dataToHash, TPM2_RH_PLATFORM, &result, &validation, &rsp_auths); if (rval != TSS2_RC_SUCCESS) { g_error ("Original hash sequence failed. RC = 0x%x", rval); } /* Test the resulting hash. */ int ret = memcmp (result.buffer, goodHashValue, result.size); if (ret != 0) { g_error ("ERROR!! resulting hash is incorrect." ); } /* * Now try starting a bunch of sequences to see what happens. * This stresses the resource manager. */ for (i = 0; i < MAX_TEST_SEQUENCES; i++) { rval = Tss2_Sys_HashSequenceStart (sapi_context, 0, &auth, TPM2_ALG_SHA1, &sequenceHandle[i], 0); if (rval != TSS2_RC_SUCCESS) { g_error ("Hash sequence %d during stress test failed to " "initialize. RC = 0x%x", i, rval); } } /* Now end them all */ dataToHash.size = 0; for (i = (MAX_TEST_SEQUENCES - 1); i >= 0; i--) { INIT_SIMPLE_TPM2B_SIZE( result ); rval = Tss2_Sys_SequenceComplete (sapi_context, sequenceHandle[i], &cmd_auths, &dataToHash, TPM2_RH_PLATFORM, &result, &validation, &rsp_auths); if (rval != TSS2_RC_SUCCESS) { g_error ("Hash sequence %d during stress test failed to close. " "RC = 0x%x", i, rval); } } return 0; } tpm2-abrmd-2.4.0/test/integration/main.c000066400000000000000000000027321401030142600200470ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include "common.h" #include "tss2-tcti-tabrmd.h" #include "test.h" #include "test-options.h" #include "context-util.h" #include "util.h" /** * This program is a template for integration tests (ones that use the TCTI * and the SAPI contexts / API directly). It does nothing more than parsing * command line options that allow the caller (likely a script) to specify * which TCTI to use for the test. */ int main () { TSS2_RC rc; TSS2_SYS_CONTEXT *sapi_context; int ret; test_opts_t opts = TEST_OPTS_DEFAULT_INIT; get_test_opts_from_env (&opts); if (sanity_check_test_opts (&opts) != 0) exit (1); sapi_context = sapi_init_from_opts (&opts); if (sapi_context == NULL) exit (1); rc = Tss2_Sys_Startup (sapi_context, TPM2_SU_CLEAR); if (rc != TSS2_RC_SUCCESS && rc != TPM2_RC_INITIALIZE) g_error ("TPM Startup FAILED! Response Code : 0x%x", rc); ret = test_invoke (sapi_context); sapi_teardown_full (sapi_context); /* * Use new SAPI & TCTI to clean out TPM after test. Test code may have * left either in an unusable state. */ sapi_context = sapi_init_from_opts (&opts); if (sapi_context == NULL) { exit (1); } clean_up_all (sapi_context); sapi_teardown_full (sapi_context); return ret; } tpm2-abrmd-2.4.0/test/integration/manage-transient-keys.int.c000066400000000000000000000033411401030142600241170ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ /* * This is an integration test that is intended to be run directly against * the tpm2-abrmd. It creates transient objects (RSA keys), loads them, then * saves the context and flushes it. This is intended to exercise the special * handling of the TPM2_FlushContext function that is required in the daemon. */ #include #include "common.h" #include "tpm2-struct-init.h" int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TPM2_HANDLE primary_handle, out_handle; TPMS_CONTEXT context = TPMS_CONTEXT_ZERO_INIT; TPM2B_PRIVATE out_private = TPM2B_PRIVATE_STATIC_INIT; TPM2B_PUBLIC out_public = TPM2B_PUBLIC_ZERO_INIT; TSS2_RC rc; rc = create_primary (sapi_context, &primary_handle); if (rc != TSS2_RC_SUCCESS) g_error ("Failed to create primary key: 0x%" PRIx32, rc); rc = create_key (sapi_context, primary_handle, &out_private, &out_public); if (rc != TSS2_RC_SUCCESS) g_error ("Failed to create_key: 0x%" PRIx32, rc); rc = load_key (sapi_context, primary_handle, &out_handle, &out_private, &out_public); if (rc != TSS2_RC_SUCCESS) g_error ("failed to load_key: 0x%" PRIx32, rc); rc = save_context (sapi_context, out_handle, &context); if (rc != TSS2_RC_SUCCESS) g_error ("failed to save_context: 0x%" PRIx32, rc); rc = flush_context (sapi_context, out_handle); if (rc != TSS2_RC_SUCCESS) g_error ("filed to flush_context: 0x%" PRIx32, rc); return 0; } tpm2-abrmd-2.4.0/test/integration/max-transient-upperbound.int.c000066400000000000000000000050171401030142600246660ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include "tss2-tcti-tabrmd.h" #include "tpm2-struct-init.h" #include "common.h" #include "tabrmd-defaults.h" #include "tabrmd.h" #define NUM_KEYS (TABRMD_TRANSIENT_MAX_DEFAULT + 1) #define ENV_NUM_KEYS "TABRMD_TEST_NUM_KEYS" /* * This is a test program that creates and loads a configurable number of * transient objects in the NULL hierarchy. The number of keys created * under the NULL primary key can be provided as a base 10 integer on * the command line. This is the only parameter the program takes. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TPM2_HANDLE parent_handle, out_handle; TPM2B_PRIVATE out_private = TPM2B_PRIVATE_STATIC_INIT; TPM2B_PUBLIC out_public = TPM2B_PUBLIC_ZERO_INIT; TSS2_RC rc = TSS2_RC_SUCCESS; char *env_str = NULL, *end_ptr = NULL; uint8_t loops = NUM_KEYS; env_str = getenv (ENV_NUM_KEYS); if (env_str != NULL) loops = strtol (env_str, &end_ptr, 10); g_print ("%s: %d", ENV_NUM_KEYS, loops); rc = create_primary (sapi_context, &parent_handle); if (rc != TSS2_RC_SUCCESS) g_error ("Failed to create primary key: 0x%" PRIx32, rc); g_print ("primary handle: 0x%" PRIx32 "\n", parent_handle); guint i; for (i = 0; i < loops; ++i) { rc = create_key (sapi_context, parent_handle, &out_private, &out_public); if (rc != TSS2_RC_SUCCESS) { /* creation of the key won't fail, loading it will */ g_critical ("Failed to create_key number %d: 0x%" PRIx32, i, rc); return -1; } rc = load_key (sapi_context, parent_handle, &out_handle, &out_private, &out_public); if (rc != TSS2_RC_SUCCESS) { g_info ("Failed to load_key number %d: 0x%" PRIx32, i, rc); if (rc == TSS2_RESMGR_RC_OBJECT_MEMORY && i == TABRMD_TRANSIENT_MAX_DEFAULT) { return 0; } return rc; } out_public.size = 0; } /* * tpm2-abrmd running with default max-transient should have failed by now. * If we've made it this far the daemon isn't enforcing the upper bound * correctly. */ return -1; } tpm2-abrmd-2.4.0/test/integration/not-enough-handles-for-command.int.c000066400000000000000000000027631401030142600256170ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include "common.h" #include "test.h" /* */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc = TSS2_RC_SUCCESS; TSS2_TCTI_CONTEXT *tcti_context = NULL; /* * Command buffer for a call to TPM2_CC_PolicyNV. It is however missing * the third handle from the handle area. */ uint8_t cmd_buf [] = { 0x80, 0x02, /* TPM2_ST_SESSIONS */ 0x00, 0x00, 0x00, 0x12, /* command buffer size */ 0x00, 0x00, 0x01, 0x49, /* command code: 0x149 / TPM2_CC_PolicyNV */ 0x01, 0x02, 0x03, 0x04, /* first handle */ 0xf0, 0xe0, 0xd0, 0xc0 /* second handle */ }; size_t buf_size = sizeof (cmd_buf); rc = Tss2_Sys_GetTctiContext (sapi_context, &tcti_context); if (rc != TSS2_RC_SUCCESS || tcti_context == NULL) { g_error ("Error getting TCTI context via Tss2_Sys_GetTctiContext: 0x%" PRIx32, rc); } rc = Tss2_Tcti_Transmit (tcti_context, buf_size, cmd_buf); if (rc != TSS2_RC_SUCCESS) { g_error ("Error transmitting cmd_buf: 0x%" PRIx32, rc); } rc = Tss2_Tcti_Receive (tcti_context, &buf_size, cmd_buf, TSS2_TCTI_TIMEOUT_BLOCK); g_warning ("just received RC 0x%" PRIx32, rc); return rc; } tpm2-abrmd-2.4.0/test/integration/password-authorization.int.c000066400000000000000000000107611401030142600244550ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ /* * This is a port of PasswordTest() from TSS's test/tpmclient/tpmclient */ #include #include #include #include "tabrmd.h" #include "tss2-tcti-tabrmd.h" #include "common.h" #define TPM20_INDEX_PASSWORD_TEST 0x01500020 /* * Helper function to define NV index with password authorization */ int CreatePasswordTestNV (TSS2_SYS_CONTEXT *sapi_context, TPMI_RH_NV_INDEX nvIndex, char *password) { TSS2_RC rval; int i; TPM2B_NV_PUBLIC publicInfo = { .nvPublic = { .attributes = TPMA_NV_AUTHREAD | TPMA_NV_AUTHWRITE | \ TPMA_NV_ORDERLY, .dataSize = strlen (password), .nameAlg = TPM2_ALG_SHA1, .nvIndex = nvIndex, } }; TPM2B_AUTH nvAuth; TSS2L_SYS_AUTH_COMMAND cmd_auths = { .count = 1, .auths = {{ .sessionHandle = TPM2_RS_PW, }} }; TSS2L_SYS_AUTH_RESPONSE rsp_auths; nvAuth.size = strlen (password); for (i = 0; i < nvAuth.size; i++) { nvAuth.buffer[i] = password[i]; } rval = Tss2_Sys_NV_DefineSpace (sapi_context, TPM2_RH_OWNER, &cmd_auths, &nvAuth, &publicInfo, &rsp_auths); if (rval != TSS2_RC_SUCCESS) { g_warning("Failed to define NV space with password authorization. RC = 0x%x", rval); return 0; } return 1; } int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rval; int i, ret; TPM2B_MAX_NV_BUFFER nvWriteData; #define PASSWORD "test password" TSS2L_SYS_AUTH_COMMAND cmd_auths = { .count = 1, .auths = {{ .sessionHandle = TPM2_RS_PW, .hmac.buffer = {PASSWORD}, .hmac.size = strlen(PASSWORD), }} }; TSS2L_SYS_AUTH_RESPONSE rsp_auths; /* * Create an NV index that will use password * authorizations the password will be * "test password". */ ret = CreatePasswordTestNV (sapi_context, TPM20_INDEX_PASSWORD_TEST, PASSWORD); if (ret != 1) { /* Error message already printed in CreatePasswordTestNV() */ return 1; } /* Initialize write data. */ nvWriteData.size = 4; for (i = 0; i < nvWriteData.size; i++) { nvWriteData.buffer[i] = 0xff - i; } /* Attempt write with the correct password. It should pass. */ rval = TSS2_RETRY_EXP (Tss2_Sys_NV_Write (sapi_context, TPM20_INDEX_PASSWORD_TEST, TPM20_INDEX_PASSWORD_TEST, &cmd_auths, &nvWriteData, 0, &rsp_auths)); /* Check that the function passed as expected. Otherwise, exit. */ if (rval != TSS2_RC_SUCCESS) { g_warning("Failed to write in NV with correct password. RC = 0x%x", rval); return 1; } /* Alter the password so it's incorrect. */ cmd_auths.auths[0].hmac.buffer[4] = 0xff; rval = Tss2_Sys_NV_Write (sapi_context, TPM20_INDEX_PASSWORD_TEST, TPM20_INDEX_PASSWORD_TEST, &cmd_auths, &nvWriteData, 0, &rsp_auths); /* * Check that the function failed as expected, * since password was incorrect. If wrong * response code received, exit. */ if (rval != (TPM2_RC_S + TPM2_RC_1 + TPM2_RC_AUTH_FAIL)) { g_warning("Unexpected error while writing NV with incorrect password. RC = 0x%x", rval); return 1; } /* * Change hmac to null one, since null auth is used to undefine the index. */ cmd_auths.auths[0].hmac.size = 0; /* Now undefine the index. */ rval = Tss2_Sys_NV_UndefineSpace (sapi_context, TPM2_RH_OWNER, TPM20_INDEX_PASSWORD_TEST, &cmd_auths, 0); if (rval != TSS2_RC_SUCCESS) { g_warning("Failed to undefine NV index. RC = 0x%x", rval); return 1; } return 0; } tpm2-abrmd-2.4.0/test/integration/session-gap.int.c000066400000000000000000000121661401030142600221460ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include "common.h" #include "test.h" #define PRIxHANDLE "08" PRIx32 #define PRIxRC PRIx32 #define FIRST_CONTEXT_SEQUENCE 0x4 #define MS_SIMULATOR_GAP_MAX UINT8_MAX /* * This test exercises the resource manager's handling of the session gap * mechanism. Currently the RM does not 'virtualize' the GetCapability * TPM2_PT_CONTEXT_GAP_MAX property. This allows us to use the value * returned by the simulator to detect the proper function of the * ResourceManager. We do this by detecting a rollover in the context * sequence number. * * Under normal circumstances (when run against the simulator w/o the RM) * we will cause a TPM_RC_CONTEXT_GAP error before the sequence number of * the last saved session exceeds that of the first + GAP_MAX. If we do not, * then we consider the test a success. * * NOTE: The value of GAP_MAX returned by the RM will be UINT32_MAX while * the MS simulator (no RM) will return UINT8_MAX (counter is 1 byte). In * this case we cannot use the GAP_MAX property returned by the RM and * must assume GAP_MAX to be UINT8_MAX. This test will then become invalid * if / when the MS simulator changes the size of their GAP counter. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc; TPMI_SH_AUTH_SESSION session_handle = 0, session_handle_trans = 0; TPMS_CONTEXT context = { 0 }, context_tmp = { 0 }; UINT32 gap_max = 0; UINT64 initial_sequence = 0, last_sequence = 0; rc = get_context_gap_max (sapi_context, &gap_max); if (rc != TSS2_RC_SUCCESS) { g_error ("%s: get_context_gap_max failed with RC: 0x%" PRIx32, __func__, rc); } g_info ("%s: GAP_MAX is 0x%" PRIx32, __func__, gap_max); if (gap_max == UINT32_MAX) { g_warning ("%s: GAP_MAX is UINT32_MAX, implies RM implements context " "gap handling, assuming GAP value is same used by MS " "smiulator: 0x%" PRIx32, __func__, MS_SIMULATOR_GAP_MAX); gap_max = MS_SIMULATOR_GAP_MAX; } g_info ("%s: Starting unbound, unsalted auth session", __func__); rc = start_auth_session (sapi_context, &session_handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxRC, rc); } g_info ("%s: StartAuthSession for TPM_SE_POLICY success! Session handle: " "0x%" PRIxHANDLE, __func__, session_handle); rc = Tss2_Sys_ContextSave (sapi_context, session_handle, &context); if (rc != TSS2_RC_SUCCESS) { g_error ("Failed to save context for initial session: 0x%" PRIxRC, rc); } g_info ("%s: Saved context for session with handle: 0x%" PRIxHANDLE " and " "sequence: 0x%" PRIx64, __func__, session_handle, context.sequence); initial_sequence = last_sequence = context.sequence; g_debug ("%s: initial_sequence: 0x%" PRIx64 ", last_sequence: 0x%" PRIx64 ", gap_max: 0x%" PRIx32, __func__, initial_sequence, last_sequence, gap_max); for (last_sequence = initial_sequence; last_sequence - initial_sequence < gap_max; last_sequence = context_tmp.sequence) { /* * NOTE: To trigger the GAP RC it's not strictly necessary for us * to save the session context in this loop as the RM will save it * as part of its normal operation. To make this test work against * the simulator however we must do the save operation here even * though it's redundant when running against the RM. */ g_debug ("%s: last_sequence: %" PRIx64, __func__, last_sequence); g_debug ("%s: initial_sequence: %" PRIx64, __func__, initial_sequence); rc = start_auth_session (sapi_context, &session_handle_trans); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxRC, rc); } memset (&context_tmp, 0, sizeof (TPMS_CONTEXT)); g_info ("%s: StartAuthSession for TPM_SE_POLICY success! Session handle: " "0x%" PRIxHANDLE, __func__, session_handle_trans); rc = Tss2_Sys_ContextSave (sapi_context, session_handle_trans, &context_tmp); if (rc != TSS2_RC_SUCCESS) { g_error ("%s: Tss2_Sys_ContextSave failed for handle: 0x%" PRIxHANDLE " rc: 0x%" PRIxRC, __func__, session_handle_trans, rc); } g_debug ("%s: context_tmp.sequence: 0x%" PRIx64, __func__, context_tmp.sequence); g_info ("%s: Saved context for session with handle: 0x%" PRIxHANDLE " and " "sequence: 0x%" PRIx64, __func__, session_handle_trans, context_tmp.sequence); rc = Tss2_Sys_FlushContext (sapi_context, session_handle_trans); if (rc != TSS2_RC_SUCCESS) { g_error ("%s: Tss2_Sys_FlushContext failed: 0x%" PRIxRC, __func__, rc); } } g_info("%s: success: last_sequence - initial_sequence: 0x%" PRIx64 ", gap_max - 1: 0x%" PRIx32, __func__, last_sequence - initial_sequence, gap_max - 1); return 0; } tpm2-abrmd-2.4.0/test/integration/session-load-from-closed-connection.int.c000066400000000000000000000057131401030142600266630ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "common.h" #include "tpm2-struct-init.h" #include "test-options.h" #include "context-util.h" #define PRIxHANDLE "08" PRIx32 /* * This test exercises the session tracking logic, specifically it creates an * auth session over one connection to the RM, saves it, then closes down said * connection. A new connection is then created and the same session is loaded * from this new connection. * In this way we're testing the RMs ability to have a saved session live on * beyond the session that created it. */ int main () { unsigned i; /* * 5 is where the failures first start, things work at 4. */ for (i=0; i < 5; i++) { TSS2_RC rc; TSS2_SYS_CONTEXT *sapi_context; TPMI_SH_AUTH_SESSION session_handle = 0, session_handle_load = 0; TPMS_CONTEXT context = TPMS_CONTEXT_ZERO_INIT; test_opts_t opts = TEST_OPTS_DEFAULT_INIT; get_test_opts_from_env (&opts); if (sanity_check_test_opts (&opts) != 0) exit (1); g_info ("Creating first SAPI context"); sapi_context = sapi_init_from_opts (&opts); if (sapi_context == NULL) { g_error ("Failed to create SAPI context."); } g_info ("Got SAPI context: 0x%" PRIxPTR, (uintptr_t)sapi_context); /* create an auth session */ g_info ("Starting unbound, unsalted auth session"); rc = start_auth_session (sapi_context, &session_handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxHANDLE, rc); } g_info ("StartAuthSession for TPM_SE_POLICY success! Session handle: " "0x%08" PRIx32, session_handle); /* save context */ g_info ("Saving context for session: 0x%" PRIxHANDLE, session_handle); rc = Tss2_Sys_ContextSave (sapi_context, session_handle, &context); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextSave failed: 0x%" PRIxHANDLE, rc); } prettyprint_context (&context); g_info ("Tearing down SAPI connection 0x%" PRIxPTR, (uintptr_t)sapi_context); sapi_teardown_full (sapi_context); g_info ("Creating second SAPI context"); sapi_context = sapi_init_from_opts (&opts); if (sapi_context == NULL) { g_error ("Failed to create SAPI context."); } g_info ("Got SAPI context: 0x%" PRIxPTR, (uintptr_t)sapi_context); /* reload the session through new connection */ g_info ("Loading context for session: 0x%" PRIxHANDLE, session_handle); rc = Tss2_Sys_ContextLoad (sapi_context, &context, &session_handle_load); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextLoad failed: 0x%" PRIxHANDLE, rc); } g_info ("Successfully loaded context for session: 0x%" PRIxHANDLE, session_handle_load); if (session_handle_load != session_handle) { g_error ("session_handle != session_handle_load"); } sapi_teardown_full (sapi_context); } return 0; } tpm2-abrmd-2.4.0/test/integration/session-load-from-closed-connections-lru.int.c000066400000000000000000000134161401030142600276450ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include #include "common.h" #include "tabrmd-defaults.h" #include "test-options.h" #include "context-util.h" #define PRIxHANDLE "08" PRIx32 #define TEST_MAX_SESSIONS (TABRMD_SESSIONS_MAX_DEFAULT + 1) /* * This test exercises the session tracking logic specifically around the * requirements mentioned in section 3.7. We've started calling * this "session continuation" where a saved session can be loaded by a * different connection. * * This test is specifically designed to cause the LRU algorithm in the * tabrmd to flush continued sessions associated with connections that have * been closed. The LRU algorithm ensures that we have a bounded number of * sessions in this intermediate state (continued but unclaimed) at any * given time. * * To accomplish this we create a fixed number (TEST_MAX_SESSIONS) of * connections with a single session created by each. Each session is saved * and the connection closed. Each session should be available for another * connection to load still but the number of connections created was * specifically chosen to be one more than the number of unowned sessions * allowed by the tabrmd. This causes one session to be closed summarily * by the daemon and so the session will not be loadable by this test. */ typedef struct { TPMI_SH_AUTH_SESSION handle; TPMS_CONTEXT context; bool load_success; } test_data_t; #define TEST_DATA_ZERO_INIT { 0, { 0, 0, 0, { 0, 0 } }, false } void create_connection_and_save_sessions (test_opts_t *opts, test_data_t data [], size_t count) { TSS2_RC rc; TSS2_SYS_CONTEXT *sapi_context; size_t i; g_info ("Creating and saving %zu policy sessions", count); for (i = 0; i < count; ++i) { sapi_context = sapi_init_from_opts (opts); if (sapi_context == NULL) { g_error ("Failed to create SAPI context."); } g_info ("Got SAPI context: 0x%" PRIxPTR, (uintptr_t)sapi_context); /* create an auth session */ g_info ("Starting unbound, unsalted auth session"); rc = start_auth_session (sapi_context, &data [i].handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxHANDLE, rc); } g_info ("StartAuthSession for TPM_SE_POLICY success! Session handle: " "0x%08" PRIx32, data [i].handle); /* save context */ g_info ("Saving context for session: 0x%" PRIxHANDLE, data [i].handle); rc = Tss2_Sys_ContextSave (sapi_context, data [i].handle, &data [i].context); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextSave failed: 0x%" PRIxHANDLE, rc); } g_info ("Successfully saved context for session: 0x%" PRIxHANDLE, data [i].handle); prettyprint_context (&data [i].context); g_info ("Tearing down SAPI connection 0x%" PRIxPTR, (uintptr_t)sapi_context); sapi_teardown_full (sapi_context); } } /* * This function attempts to load 'count' session contexts from the provided * array. The handles for the loaded sessions are returned in the * handle array. The number of contexts loaded is returned. */ size_t load_sessions (TSS2_SYS_CONTEXT *sapi_context, test_data_t data [], size_t count) { TSS2_RC rc; size_t i, success_count = 0; for (i = 0; i < count; ++i) { g_debug ("Loading saved context with handle: 0x%08" PRIx32, data [i].context.savedHandle); rc = Tss2_Sys_ContextLoad (sapi_context, &data [i].context, &data [i].handle); if (rc == TSS2_RC_SUCCESS) { g_info ("Successfully loaded context for session: 0x%" PRIxHANDLE, data [i].handle); data [i].load_success = true; ++success_count; } else { g_warning ("Tss2_Sys_ContextLoad failed: 0x%" PRIxHANDLE, rc); } } return success_count; } int main () { TSS2_SYS_CONTEXT *sapi_context; test_data_t test_data [TEST_MAX_SESSIONS] = { TEST_DATA_ZERO_INIT, TEST_DATA_ZERO_INIT, TEST_DATA_ZERO_INIT, TEST_DATA_ZERO_INIT, TEST_DATA_ZERO_INIT }; test_opts_t opts = TEST_OPTS_DEFAULT_INIT; guint success_count; get_test_opts_from_env (&opts); if (sanity_check_test_opts (&opts) != 0) exit (1); /* create TEST_MAX_SESSIONS each over a unique connection to the tabrmd */ create_connection_and_save_sessions (&opts, test_data, TEST_MAX_SESSIONS); /* create fresh connection for the test */ sapi_context = sapi_init_from_opts (&opts); if (sapi_context == NULL) { g_error ("Failed to create SAPI context."); } /* * Of the continued sessions above, only TABRMD_MAX_SESSIONS should be * available to us. */ success_count = load_sessions (sapi_context, test_data, TEST_MAX_SESSIONS); if (success_count != TABRMD_SESSIONS_MAX_DEFAULT) { g_critical ("Expected to load %u sessions, got %u instead", TABRMD_SESSIONS_MAX_DEFAULT, success_count); exit (1); } /* * The first session we created is the oldest. The LRU should have selected * this one for eviction */ if (test_data [0].load_success == true) { g_critical ("Oldest session created was loaded successfully."); exit (1); } sapi_teardown_full (sapi_context); return 0; } tpm2-abrmd-2.4.0/test/integration/session-load-from-open-connection.int.c000066400000000000000000000043721401030142600263530ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "common.h" #include "tpm2-struct-init.h" #include "test-options.h" #include "context-util.h" /* * This test exercises the session tracking logic, specifically the ability * / feature that allows a session created and saved over one connection to * the tabrmd to be loaded from another connection. * This test creates an auth session using "connection0", saves it and then * loads it from "connection1" *before* "connection0" is closed. */ int main () { TSS2_RC rc; TSS2_SYS_CONTEXT *sapi_context0, *sapi_context1; TPMI_SH_AUTH_SESSION session_handle = 0, session_handle_load = 0; TPMS_CONTEXT context = TPMS_CONTEXT_ZERO_INIT; test_opts_t opts = TEST_OPTS_DEFAULT_INIT; get_test_opts_from_env (&opts); if (sanity_check_test_opts (&opts) != 0) exit (1); sapi_context0 = sapi_init_from_opts (&opts); if (sapi_context0 == NULL) { g_error ("Failed to create SAPI context."); } /* create an auth session */ rc = start_auth_session (sapi_context0, &session_handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxHANDLE, rc); } /* save context */ rc = Tss2_Sys_ContextSave (sapi_context0, session_handle, &context); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextSave failed: 0x%" PRIxHANDLE, rc); } sapi_context1 = sapi_init_from_opts (&opts); if (sapi_context1 == NULL) { g_error ("Failed to create SAPI context."); } /* reload the session through new connection */ rc = Tss2_Sys_ContextLoad (sapi_context1, &context, &session_handle_load); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextLoad failed: 0x%" PRIxHANDLE, rc); } if (session_handle_load == session_handle) { g_info ("session_handle == session_handle_load"); } else { g_error ("session_handle != session_handle_load"); } /* teardown sapi connection */ sapi_teardown_full (sapi_context0); sapi_teardown_full (sapi_context1); return 0; } tpm2-abrmd-2.4.0/test/integration/start-auth-session.int.c000066400000000000000000000172371401030142600234770ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include "common.h" #include "tpm2-struct-init.h" #include "test.h" #define PRIxHANDLE "08" PRIx32 /* * This test exercises the session creation logic. We begin by creating the * most simple session we can. We then query the TPM (using the GetCapability * command) for the handle of all loaded handles. The handle of the session * that we just created should be the only handle returned. Finally we query * the TPM for the active handles. The only session handle we've created is * still loaded so we should receive no handles back. * * We then save the context of the session. This should transition its state * from 'loaded' to 'active'. Now when we query the TPM for loaded and active * handles again. Since we've saved the context of our session there should * be no loaded sessions and one active session (the one we just saved). * * We then load the saved session again. Again we query the TPM for loaded and * active sessions. The state should be the same as our first test. * * Finally we flush our context and query the TPM for the loaded and active * sessions. Since we just flushed our session there should be none loaded * or active. */ /* * This function queries the TPM for the handle types specified in the * 'query_handle' parameter. It returns the number of this type of handle * that are currently being tracked by the TPM. */ TSS2_RC handles_count (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE query_handle, UINT32 *count) { TSS2_RC rc = TSS2_RC_SUCCESS; TPM2_CAP capability = TPM2_CAP_HANDLES; TPMI_YES_NO more_data = 0; TPMS_CAPABILITY_DATA cap_data = TPMS_CAPABILITY_DATA_ZERO_INIT; rc = Tss2_Sys_GetCapability (sapi_context, NULL, capability, query_handle, 100, &more_data, &cap_data, NULL); if (rc != TSS2_RC_SUCCESS) { g_error ("error getting capability: 0x%" PRIx32, rc); } if (more_data == 1) { g_warning ("got 'more_data' from query on loaded sessions"); } *count = cap_data.data.handles.count; return rc; } /* * This function queries the TPM for the handles of the type specified in the * 'query_handle' parameter. It then "prettyprints" them. */ TSS2_RC prettyprint_getcap_handles (TSS2_SYS_CONTEXT *sapi_context, TPM2_HANDLE query_handle) { TSS2_RC rc = TSS2_RC_SUCCESS; TPM2_CAP capability = TPM2_CAP_HANDLES; TPMI_YES_NO more_data = 0; TPMS_CAPABILITY_DATA cap_data = TPMS_CAPABILITY_DATA_ZERO_INIT; size_t count = 100; size_t i; rc = Tss2_Sys_GetCapability (sapi_context, NULL, capability, query_handle, count, &more_data, &cap_data, NULL); if (rc != TSS2_RC_SUCCESS) { g_error ("error getting capability: 0x%" PRIx32, rc); } if (more_data == 1) { g_warning ("got 'more_data' from query on loaded sessions"); } g_print ("GetCapability: cap: 0x%" PRIxHANDLE " prop: 0x%" PRIxHANDLE " got %" PRIx32 " handles\n", capability, query_handle, cap_data.data.handles.count); for (i = 0; i < cap_data.data.handles.count; ++i) { g_print (" 0x%" PRIxHANDLE "\n", cap_data.data.handles.handle [i]); } return rc; } /* * This function queries the TPM for the loaded and active session handles. * It then prints the handle IDs (UINT32) to the console. */ TSS2_RC dump_loaded_active_handles (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc = TSS2_RC_SUCCESS; g_print ("Printing LOADED_SESSION handles\n"); rc = prettyprint_getcap_handles (sapi_context, TPM2_LOADED_SESSION_FIRST); if (rc != TSS2_RC_SUCCESS) { g_error ("Failed to get handles for LOADED_SESSION_FIRST: 0x%" PRIxHANDLE, rc); } g_print ("Printing ACTIVE_SESSION handles\n"); rc = prettyprint_getcap_handles (sapi_context, TPM2_ACTIVE_SESSION_FIRST); if (rc != TSS2_RC_SUCCESS) { g_error ("Failed to get handles for ACTIVE_SESSION_FIRST: 0x%" PRIx32, rc); } return rc; } /* * See the leading comment for a description of this test. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc; TPMI_SH_AUTH_SESSION session_handle = 0, session_handle_load = 0; TPMS_CONTEXT context = TPMS_CONTEXT_ZERO_INIT; UINT32 count = 0; /* create an auth session */ g_info ("Starting unbound, unsalted auth session"); rc = start_auth_session (sapi_context, &session_handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_StartAuthSession failed: 0x%" PRIxHANDLE, rc); } g_info ("StartAuthSession for TPM_SE_POLICY success! Session handle: " "0x%08" PRIx32, session_handle); handles_count (sapi_context, TPM2_LOADED_SESSION_FIRST, &count); g_assert (count == 1); handles_count (sapi_context, TPM2_ACTIVE_SESSION_FIRST, &count); g_assert (count == 0); dump_loaded_active_handles (sapi_context); /* save context */ g_info ("Saving context for session: 0x%" PRIxHANDLE, session_handle); rc = Tss2_Sys_ContextSave (sapi_context, session_handle, &context); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextSave failed: 0x%" PRIxHANDLE, rc); } g_info ("Successfully saved context for session: 0x%" PRIxHANDLE, session_handle); prettyprint_context (&context); dump_loaded_active_handles (sapi_context); handles_count (sapi_context, TPM2_LOADED_SESSION_FIRST, &count); g_assert (count == 0); handles_count (sapi_context, TPM2_ACTIVE_SESSION_FIRST, &count); g_assert (count == 1); /* load context */ g_info ("Loading context for session: 0x%" PRIxHANDLE, session_handle); rc = Tss2_Sys_ContextLoad (sapi_context, &context, &session_handle_load); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_ContextLoad failed: 0x%" PRIxHANDLE, rc); } g_info ("Successfully loaded context for session: 0x%" PRIxHANDLE, session_handle_load); if (session_handle_load == session_handle) { g_info ("session_handle == session_handle_load"); } else { g_info ("session_handle != session_handle_load"); } dump_loaded_active_handles (sapi_context); handles_count (sapi_context, TPM2_LOADED_SESSION_FIRST, &count); g_assert (count == 1); handles_count (sapi_context, TPM2_ACTIVE_SESSION_FIRST, &count); g_assert (count == 0); g_info ("Flushing context for session: 0x%" PRIxHANDLE, session_handle); rc = Tss2_Sys_FlushContext (sapi_context, session_handle); if (rc != TSS2_RC_SUCCESS) { g_error ("Tss2_Sys_FlushContext failed: 0x%" PRIx32, rc); } g_info ("Flushed context for session handle: 0x%" PRIxHANDLE " success!", session_handle); dump_loaded_active_handles (sapi_context); handles_count (sapi_context, TPM2_LOADED_SESSION_FIRST, &count); g_assert (count == 0); handles_count (sapi_context, TPM2_ACTIVE_SESSION_FIRST, &count); g_assert (count == 0); return 0; } tpm2-abrmd-2.4.0/test/integration/tcti-cancel.int.c000066400000000000000000000030551401030142600221010ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include "tabrmd.h" #include "tss2-tcti-tabrmd.h" #include "common.h" #define NUM_KEYS 5 #define ENV_NUM_KEYS "TABRMD_TEST_NUM_KEYS" /* * This is a test program that exercises the TCTI cancel command. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_TCTI_CONTEXT *tcti_context = NULL; TSS2_RC rc = TSS2_RC_SUCCESS; uint8_t cmd_buffer [] = { 0x80, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x01, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40 }; size_t cmd_buffer_size = sizeof (cmd_buffer); rc = Tss2_Sys_GetTctiContext (sapi_context, &tcti_context); if (rc != TSS2_RC_SUCCESS || tcti_context == NULL) { g_critical ("Tss2_Sys_GetTctiContext failed: 0x%" PRIx32, rc); return 1; } g_info ("sending command buffer"); rc = Tss2_Tcti_Transmit (tcti_context, cmd_buffer_size, cmd_buffer); if (rc != TSS2_RC_SUCCESS) { g_critical ("failed to send command"); return 1; } g_info ("invoking tss2_tcti_tabrmd_cancel"); rc = Tss2_Tcti_Cancel (tcti_context); if (rc == TSS2_RESMGR_RC_NOT_IMPLEMENTED) { g_warning ("Tss2_Tcti_Cancel failed as expected: 0x%" PRIx32, rc); return 0; } else { g_critical ("Tss2_Tcti_Cancel returned unexpected rc: 0x%" PRIx32, rc); return 1; } } tpm2-abrmd-2.4.0/test/integration/tcti-connect-multiple.int.c000066400000000000000000000064601401030142600241410ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ /* * This test instantiates CONNECTION_COUNT TCTI instances sending a pre-canned * TPM command through each. It terminates each connection before receiving * the result. * * This test ensures that a single process is able to instantiate and use * multiple connections to the tabrmd. * * NOTE: this test can't and doesn't use the main.c driver from the * integration test harness since we need to instantiate more than a single * TCTI context. */ #include #include #include #include #include #include #include "tss2-tcti-tabrmd.h" #include "test-options.h" #define CONNECTION_COUNT 25 TSS2_RC tcti_tabrmd_init (TSS2_TCTI_CONTEXT **tcti_context, const char *conf, int retries) { TSS2_RC rc; size_t size, i; 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); return rc; } *tcti_context = calloc (1, size); if (*tcti_context == NULL) { fprintf (stderr, "Allocation for TCTI context failed: %s\n", strerror (errno)); return rc; } for (i = 0; i < (size_t) retries; ++i) { rc = Tss2_Tcti_Tabrmd_Init (*tcti_context, &size, conf); if (rc == TSS2_RC_SUCCESS) { break; } else { g_info ("Failed to initialize tabrmd TCTI context: 0x%" PRIx32 " on try %zd", rc, i); sleep (1); } } if (rc != TSS2_RC_SUCCESS) { fprintf (stderr, "Failed to initialize tabrmd TCTI context: " "0x%" PRIx32 "\n", rc); free (*tcti_context); } return rc; } int main (int argc, char *argv[]) { TSS2_RC rc; TSS2_TCTI_CONTEXT *tcti_context[CONNECTION_COUNT] = { 0 }; test_opts_t opts = TEST_OPTS_DEFAULT_INIT; /* This is a pre-canned TPM2 command buffer to invoke 'GetCapability' */ uint8_t cmd_buf[] = { 0x80, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x01, 0x7a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x0a }; g_info ("Executing test: %s", argv[0]); if (argc > 1) { g_error ("Unexpected argument count %d", argc); } get_test_opts_from_env (&opts); if (sanity_check_test_opts (&opts) != 0) { g_error ("option sanity test failed"); } size_t i; for (i = 0; i < CONNECTION_COUNT; ++i) { rc = tcti_tabrmd_init (&tcti_context [i], opts.tcti_conf, opts.tcti_retries); if (tcti_context [i] == NULL || rc != TSS2_RC_SUCCESS) { g_error ("failed to connect to TCTI: 0x%" PRIx32, rc); } } for (i = 0; i < CONNECTION_COUNT; ++i) { rc = Tss2_Tcti_Transmit (tcti_context [i], sizeof (cmd_buf), cmd_buf); if (rc != TSS2_RC_SUCCESS) { g_error ("failed to transmit TPM command"); } } for (i = 0; i < CONNECTION_COUNT; ++i) { Tss2_Tcti_Finalize (tcti_context [i]); free (tcti_context [i]); } } tpm2-abrmd-2.4.0/test/integration/tcti-connections-max.int.c000066400000000000000000000033631401030142600237630ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ /* * This is a test program intended to create a number of TCTI connections * with the tabrmd. The goal is to hit the upper bound on the number of * concurrent connections and get the TCTI to return an error. */ #include #include #include #include #include #include "tabrmd-defaults.h" #include "test-options.h" #include "context-util.h" #include "tss2-tcti-tabrmd.h" #define TCTI_COUNT_MAX 100 int main () { TSS2_TCTI_CONTEXT *tcti_context[TCTI_COUNT_MAX] = { NULL }; uint8_t tcti_count = TABRMD_CONNECTIONS_MAX_DEFAULT, i; test_opts_t test_opts = TEST_OPTS_DEFAULT_INIT; int ret = 1; if (tcti_count > TCTI_COUNT_MAX) { printf ("Cannot create more than %" PRIx8 " connections\n", TCTI_COUNT_MAX); return 1; } get_test_opts_from_env (&test_opts); for (i = 0; i < tcti_count; ++i) { printf ("creating TCTI %" PRIu8 "\n", i); tcti_context [i] = tcti_init_from_opts (&test_opts); if (tcti_context [i] == NULL) { if (i == TABRMD_CONNECTIONS_MAX_DEFAULT - 1) { g_info ("Failed to initialize TCTI after default connection max, success"); goto out; } else { g_warning ("failed to initialize TCTI connection number %" PRIu8 " with tabrmd", i); ret = 2; goto out; } } } ret = 0; out: tcti_count = i; for (i=0; i < tcti_count; i++) { tcti_free_from_opts (&test_opts, &tcti_context [i]); } return ret; } tpm2-abrmd-2.4.0/test/integration/tcti-double-finalize.int.c000066400000000000000000000013431401030142600237230ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2018, Intel Corporation * All rights reserved. */ #include #include #include #include "tss2-tcti-tabrmd.h" /* * This is a test program that exercises the TCTI cancel command. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_TCTI_CONTEXT *tcti_context = NULL; TSS2_RC rc = TSS2_RC_SUCCESS; rc = Tss2_Sys_GetTctiContext (sapi_context, &tcti_context); if (rc != TSS2_RC_SUCCESS || tcti_context == NULL) { g_critical ("Tss2_Sys_GetTctiContext failed: 0x%" PRIx32, rc); return 1; } Tss2_Tcti_Finalize (tcti_context); Tss2_Tcti_Finalize (tcti_context); return 0; } tpm2-abrmd-2.4.0/test/integration/tcti-set-locality.int.c000066400000000000000000000021751401030142600232670ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include "tabrmd.h" #include "tss2-tcti-tabrmd.h" #include "common.h" #define NUM_KEYS 5 #define ENV_NUM_KEYS "TABRMD_TEST_NUM_KEYS" /* * This is a test program to exercise the TCTI set locality command. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_TCTI_CONTEXT *tcti_context = NULL; TSS2_RC rc = TSS2_RC_SUCCESS; rc = Tss2_Sys_GetTctiContext (sapi_context, &tcti_context); if (rc != TSS2_RC_SUCCESS || tcti_context == NULL) { g_critical ("Tss2_Sys_GetTctiContext failed: 0x%" PRIx32, rc); return 1; } g_info ("invoking tss2_tcti_tabrmd_set_locality"); rc = Tss2_Tcti_SetLocality (tcti_context, 1); if (rc == TSS2_RESMGR_RC_NOT_IMPLEMENTED) { g_info ("tss2_tcti_tabrmd_set_locality failed as expected: 0x%" PRIx32, rc); return 0; } else { g_critical ("tss2_tcti_tabrmd_set_locality returned unexpected rc: 0x%" PRIx32, rc); return 1; } } tpm2-abrmd-2.4.0/test/integration/test-options.c000066400000000000000000000042461401030142600215750ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include #include "util.h" #include "test-options.h" #include "tss2-tcti-tabrmd.h" GBusType bus_type_from_str (const char *bus_type_str) { if (strcmp (bus_type_str, "system") == 0) { return G_BUS_TYPE_SYSTEM; } else if (strcmp (bus_type_str, "session") == 0) { return G_BUS_TYPE_SESSION; } else { g_error ("Invalid bus type for %s", bus_type_str); return G_BUS_TYPE_NONE; } } const char* bus_str_from_type (GBusType bus_type) { switch (bus_type) { case G_BUS_TYPE_SESSION: return "session"; case G_BUS_TYPE_SYSTEM: return "system"; default: return NULL; } } /* * return 0 if sanity test passes * return 1 if sanity test fails */ int sanity_check_test_opts (test_opts_t *opts) { UNUSED_PARAM(opts); return 0; } /* * Extract configuration from environment and populate the provided * `test_opts_t` with the relevant data. */ int get_test_opts_from_env (test_opts_t *test_opts) { char *env_str; if (test_opts == NULL) return 1; env_str = getenv (ENV_TCTI); if (env_str != NULL) { g_debug ("%s: %s is \"%s\"", __func__, ENV_TCTI, env_str); test_opts->tcti_filename = env_str; } env_str = getenv (ENV_TCTI_CONF); if (env_str != NULL) { g_debug ("%s: %s is \"%s\"", __func__, ENV_TCTI_CONF, env_str); test_opts->tcti_conf = env_str; } env_str = getenv (ENV_TCTI_RETRIES); if (env_str != NULL) { g_debug ("%s: %s is \"%s\"", __func__, ENV_TCTI_RETRIES, env_str); test_opts->tcti_retries = strtoumax (env_str, NULL, 10); } return 0; } /* * Dump the contents of the test_opts_t structure to stdout. */ void dump_test_opts (test_opts_t *opts) { printf ("test_opts_t:\n"); printf (" tcti_filename: %s\n", opts->tcti_filename); printf (" tcti_conf: %s\n", opts->tcti_conf); printf (" retries: %" PRIuMAX "\n", opts->tcti_retries); } tpm2-abrmd-2.4.0/test/integration/test-options.h000066400000000000000000000023121401030142600215720ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #ifndef TEST_OPTIONS_H #define TEST_OPTIONS_H #include #include "tss2-tcti-tabrmd.h" /* Default number of attempts to init selected TCTI */ #define TCTI_RETRIES_DEFAULT 5 /* environment variables holding TCTI config */ #define ENV_TCTI "TABRMD_TEST_TCTI" #define ENV_TCTI_CONF "TABRMD_TEST_TCTI_CONF" #define ENV_TCTI_RETRIES "TABRMD_TEST_TCTI_RETRIES" #define TEST_OPTS_DEFAULT_INIT { \ .tcti_filename = NULL, \ .tcti_conf = NULL, \ .tcti_retries = TCTI_RETRIES_DEFAULT, \ } typedef struct { const char *tcti_filename; const char *tcti_conf; uintmax_t tcti_retries; } test_opts_t; /* functions to get test options from the user and to print helpful stuff */ const char *bus_str_from_type (GBusType bus_type); GBusType bus_type_from_str (const char* bus_type_str); int get_test_opts_from_env (test_opts_t *opts); int sanity_check_test_opts (test_opts_t *opts); void dump_test_opts (test_opts_t *opts); #endif /* TEST_OPTIONS_H */ tpm2-abrmd-2.4.0/test/integration/test.h000066400000000000000000000011241401030142600201010ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include /* * This is the prototype for all integration tests in the TPM2.0-TSS * project. Integration tests are intended to exercise the combined * components in the software stack. This typically means executing some * SAPI function using the socket TCTI to communicate with a software * TPM2 simulator. * Return values: * A successful test will return 0, any other value indicates failure. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context); tpm2-abrmd-2.4.0/test/integration/tpm2-command-flush-no-handle.int.c000066400000000000000000000033731401030142600251760ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "common.h" #include "test.h" /* * This test sends the tabrmd a malformed ContextFlush command. The command * size is set to the size of the header which omits the handle being flushed. * This is designed to get the tabrmd to attempt to access the handle * parameter that will be outside of the buffer allocated by the * CommandSource. */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc = TSS2_RC_SUCCESS; TSS2_TCTI_CONTEXT *tcti_context = NULL; uint8_t cmd_buf [] = { 0x80, 0x01, /* TPM2_ST_NO_SESSIONS */ 0x00, 0x00, 0x00, 0x0a, /* size: 10 bytes */ 0x00, 0x00, 0x01, 0x65, /* command code for ContextFlush */ }; size_t buf_size = sizeof (cmd_buf); rc = Tss2_Sys_GetTctiContext (sapi_context, &tcti_context); if (rc != TSS2_RC_SUCCESS || tcti_context == NULL) { g_error ("Error getting TCTI context via Tss2_Sys_GetTctiContext: 0x%" PRIx32, rc); } rc = Tss2_Tcti_Transmit (tcti_context, sizeof (cmd_buf), cmd_buf); if (rc != TSS2_RC_SUCCESS) { g_error ("Error transmitting cmd_buf: 0x%" PRIx32, rc); } rc = Tss2_Tcti_Receive (tcti_context, &buf_size, cmd_buf, TSS2_TCTI_TIMEOUT_BLOCK); if (rc != TSS2_RC_SUCCESS) { return rc; } /* we're expecting a response buffer with just an RC */ rc = 0x0000ffff & be32toh (cmd_buf [6]); if (rc != TPM2_RC_INSUFFICIENT) { return rc; } return 0; } tpm2-abrmd-2.4.0/test/integration/tpm2-struct-init.h000066400000000000000000000054661401030142600223040ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #ifndef TPM2_STRUCT_INIT #define TPM2_STRUCT_INIT #define TPM2B_STATIC_INIT(type) { .size = sizeof (type) - sizeof (UINT16) } #define TPM2B_DIGEST_STATIC_INIT TPM2B_STATIC_INIT(TPM2B_DIGEST) #define TPM2B_NAME_STATIC_INIT TPM2B_STATIC_INIT(TPM2B_NAME) #define TPM2B_PRIVATE_STATIC_INIT TPM2B_STATIC_INIT(TPM2B_PRIVATE) #define TPM2B_ZERO_INIT(type) { .size = 0 } #define TPM2B_DIGEST_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_DIGEST) #define TPM2B_NAME_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_NAME) #define TPM2B_PUBLIC_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_PUBLIC) #define TPM2B_PRIVATE_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_PRIVATE) #define TPM2B_DATA_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_DATA) #define TPM2B_CONTEXT_DATA_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_CONTEXT_DATA) #define TPM2B_CREATION_DATA_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_CREATION_DATA) #define TPM2B_SENSITIVE_DATA_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_SENSITIVE_DATA) #define TPM2B_SENSITIVE_CREATE_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_SENSITIVE_CREATE) #define TPM2B_ENCRYPTED_SECRET_ZERO_INIT TPM2B_ZERO_INIT(TPM2B_ENCRYPTED_SECRET) #define TPMS_CONTEXT_ZERO_INIT { .sequence = 0, .savedHandle = 0, \ .hierarchy = 0, .contextBlob = TPM2B_CONTEXT_DATA_ZERO_INIT \ } #define TPMT_TK_CREATION_ZERO_INIT { .tag = 0, .hierarchy = 0, \ TPM2B_DIGEST_ZERO_INIT \ } #define TPMS_SENSITIVE_ZERO_INIT { \ .userAuth = 0, .data = TPM2B_SENSITIVE_DATA_ZERO_INIT \ } #define TPMS_PCR_SELECTION_ZERO_INIT { .hash = 0, \ .sizeofSelect = 0, .pcrSelect = 0 \ } #define TPML_PCR_SELECTION_ZERO_INIT { .count = 0, \ .pcrSelections = { TPMS_PCR_SELECTION_ZERO_INIT, \ TPMS_PCR_SELECTION_ZERO_INIT, TPMS_PCR_SELECTION_ZERO_INIT } \ } #define TPMS_CAPABILITY_DATA_ZERO_INIT { 0, { 0, { \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } } #endif /* TPM2_STRUCT_INIT */ tpm2-abrmd-2.4.0/test/integration/util-buf-max-upper-bound.int.c000066400000000000000000000020751401030142600244640ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include "common.h" #include "test.h" /* */ int test_invoke (TSS2_SYS_CONTEXT *sapi_context) { TSS2_RC rc = TSS2_RC_SUCCESS; TSS2_TCTI_CONTEXT *tcti_context = NULL; uint8_t cmd_buf [] = { 0x80, 0x01, /* TPM2_ST_NO_SESSIONS */ 0x00, 0x00, 0x20, 0x00, /* size: 8192 bytes */ 0x00, 0x00, 0x01, 0x62, /* command code for ContextSave */ 0x00, 0x00, 0x00, 0x00, /* handle not used or required */ }; rc = Tss2_Sys_GetTctiContext (sapi_context, &tcti_context); if (rc != TSS2_RC_SUCCESS || tcti_context == NULL) { g_error ("Error getting TCTI context via Tss2_Sys_GetTctiContext: 0x%" PRIx32, rc); } rc = Tss2_Tcti_Transmit (tcti_context, sizeof (cmd_buf), cmd_buf); if (rc != TSS2_RC_SUCCESS) { g_error ("Error transmitting cmd_buf: 0x%" PRIx32, rc); } return rc; } tpm2-abrmd-2.4.0/test/ipc-frontend-dbus_unit.c000066400000000000000000000042011401030142600211530ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include "ipc-frontend-dbus.h" #include "util.h" static int ipc_frontend_dbus_setup (void **state) { IpcFrontendDbus *ipc_frontend_dbus = NULL; ConnectionManager *connection_manager = NULL; Random *random = NULL; gint ret = 0; random = random_new (); ret = random_seed_from_file (random, "/dev/urandom"); assert_int_equal (ret, 0); connection_manager = connection_manager_new (100); ipc_frontend_dbus = ipc_frontend_dbus_new (IPC_FRONTEND_DBUS_TYPE_DEFAULT, IPC_FRONTEND_DBUS_NAME_DEFAULT, connection_manager, 100, random); assert_non_null (ipc_frontend_dbus); *state = ipc_frontend_dbus; g_object_unref (random); g_object_unref (connection_manager); return 0; } static int ipc_frontend_dbus_teardown (void **state) { IpcFrontendDbus *ipc_frontend_dbus = NULL; assert_non_null (state); ipc_frontend_dbus = IPC_FRONTEND_DBUS (*state); g_object_unref (ipc_frontend_dbus); return 0; } /* * This test relies on the setup / teardown functions to instantiate an * instance of the TestIpcFrontend. It extracts this object from the state * parameter and uses the GObject type macros to ensure that the object * system identifies it as both the abstract base type and the derived * type. */ static void ipc_frontend_dbus_type_test (void **state) { assert_non_null (state); assert_true (IS_IPC_FRONTEND (*state)); assert_true (IS_IPC_FRONTEND_DBUS (*state)); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (ipc_frontend_dbus_type_test, ipc_frontend_dbus_setup, ipc_frontend_dbus_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/ipc-frontend_unit.c000066400000000000000000000145571401030142600202370ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "util.h" #include "handle-map.h" #include "ipc-frontend.h" /* * Begin definition of TestIpcFrontend. * This is a GObject that derives from the IpcFrontend abstract base class. */ G_BEGIN_DECLS typedef struct _TestIpcFrontend TestIpcFrontend; typedef struct _TestIpcFrontendClass TestIpcFrontendClass; struct _TestIpcFrontend { IpcFrontend parent; gboolean connected; }; struct _TestIpcFrontendClass { IpcFrontendClass parent; }; #define TYPE_TEST_IPC_FRONTEND (test_ipc_frontend_get_type ()) #define TEST_IPC_FRONTEND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TEST_IPC_FRONTEND, TestIpcFrontend)) #define IS_TEST_IPC_FRONTEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TEST_IPC_FRONTEND)) GType test_ipc_frontend_get_type (void); G_END_DECLS G_DEFINE_TYPE (TestIpcFrontend, test_ipc_frontend, TYPE_IPC_FRONTEND); static void test_ipc_frontend_init (TestIpcFrontend *self) { self->connected = FALSE; } static void test_ipc_frontend_finalize (GObject *obj) { if (test_ipc_frontend_parent_class != NULL) G_OBJECT_CLASS (test_ipc_frontend_parent_class)->finalize (obj); } static void test_ipc_frontend_connect (TestIpcFrontend *self, GMutex *init_mutex) { IpcFrontend *frontend = IPC_FRONTEND (self); self->connected = TRUE; frontend->init_mutex = init_mutex; } static void test_ipc_frontend_disconnect (TestIpcFrontend *self) { IpcFrontend *frontend = IPC_FRONTEND (self); self->connected = FALSE; frontend->init_mutex = NULL; /* * This is where a child class would emit the 'disconnected' signal. * This test class however doesn't need to do this since it's tested * elsewhere. */ } static void test_ipc_frontend_class_init (TestIpcFrontendClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); IpcFrontendClass *ipc_frontend_class = IPC_FRONTEND_CLASS (klass); if (test_ipc_frontend_parent_class == NULL) test_ipc_frontend_parent_class = g_type_class_peek_parent (klass); object_class->finalize = test_ipc_frontend_finalize; ipc_frontend_class->connect = (IpcFrontendConnect)test_ipc_frontend_connect; ipc_frontend_class->disconnect = (IpcFrontendDisconnect)test_ipc_frontend_disconnect; } static TestIpcFrontend* test_ipc_frontend_new (void) { return TEST_IPC_FRONTEND (g_object_new (TYPE_TEST_IPC_FRONTEND, NULL)); } /* * End definition of TestIpcFrontend GObject. */ /* * This is a 'setup' function used by the cmocka machinery to setup the * test state before a test is executed. */ static int ipc_frontend_setup (void **state) { TestIpcFrontend *test_ipc_frontend = NULL; test_ipc_frontend = test_ipc_frontend_new (); assert_non_null (test_ipc_frontend); *state = test_ipc_frontend; return 0; } /* * This 'teardown' function is used by the cmocka machinery to cleanup the * test state. */ static int ipc_frontend_teardown (void **state) { TestIpcFrontend *test_ipc_frontend = NULL; assert_non_null (state); test_ipc_frontend = TEST_IPC_FRONTEND (*state); g_object_unref (test_ipc_frontend); return 0; } /* * This test relies on the setup / teardown functions to instantiate an * instance of the TestIpcFrontend. It extracts this object from the state * parameter and uses the GObject type macros to ensure that the object * system identifies it as both the abstract base type and the derived * type. */ static void ipc_frontend_type_test (void **state) { assert_non_null (state); assert_true (IS_IPC_FRONTEND (*state)); assert_true (IS_TEST_IPC_FRONTEND (*state)); } /* * This callback function is used by the ipc_frontend_event_test to set a * boolean flag when the signal is emitted. */ static void ipc_frontend_on_disconnected (IpcFrontend *ipc_frontend, bool *called_flag) { UNUSED_PARAM(ipc_frontend); *called_flag = true; } /* * This test exercises the 'disconnected' event emitted by the IpcFrontend * abstract class. */ static void ipc_frontend_event_test (void **state) { IpcFrontend *ipc_frontend = NULL; bool called_flag = false; ipc_frontend = IPC_FRONTEND (*state); g_signal_connect (ipc_frontend, "disconnected", (GCallback) ipc_frontend_on_disconnected, &called_flag); /* pickup here and test the signal emission */ ipc_frontend_disconnected_invoke (ipc_frontend); assert_true (called_flag); } /* */ static void ipc_frontend_connect_test (void **state) { IpcFrontend *ipc_frontend = NULL; TestIpcFrontend *test_ipc_frontend = NULL; ipc_frontend = IPC_FRONTEND (*state); test_ipc_frontend = TEST_IPC_FRONTEND (*state); ipc_frontend_connect (ipc_frontend, NULL); assert_true (test_ipc_frontend->connected); } /* */ static void ipc_frontend_disconnect_test (void **state) { IpcFrontend *ipc_frontend = NULL; TestIpcFrontend *test_ipc_frontend = NULL; ipc_frontend = IPC_FRONTEND (*state); test_ipc_frontend = TEST_IPC_FRONTEND (*state); ipc_frontend_connect (ipc_frontend, NULL); assert_true (test_ipc_frontend->connected); ipc_frontend_disconnect (ipc_frontend); assert_false (test_ipc_frontend->connected); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (ipc_frontend_type_test, ipc_frontend_setup, ipc_frontend_teardown), cmocka_unit_test_setup_teardown (ipc_frontend_event_test, ipc_frontend_setup, ipc_frontend_teardown), cmocka_unit_test_setup_teardown (ipc_frontend_connect_test, ipc_frontend_setup, ipc_frontend_teardown), cmocka_unit_test_setup_teardown (ipc_frontend_disconnect_test, ipc_frontend_setup, ipc_frontend_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/logging_unit.c000066400000000000000000000113371401030142600172660ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "util.h" #include "logging.h" static const char *env_str_all = "all"; static const char *env_str_foo = "foo"; char* __real_getenv (const char *name); char* __wrap_getenv (const char *name) { if (strcmp (name, "G_MESSAGES_DEBUG")) { return __real_getenv (name); } return mock_type (char*); } static void logging_get_enabled_log_levels_default_test (void **state) { int levels; UNUSED_PARAM(state); will_return (__wrap_getenv, NULL); levels = get_enabled_log_levels (); assert_int_equal (levels, LOG_LEVEL_DEFAULT); } static void logging_get_enabled_log_levels_all_test (void **state) { int levels; UNUSED_PARAM(state); will_return (__wrap_getenv, env_str_all); levels = get_enabled_log_levels (); assert_int_equal (levels, LOG_LEVEL_ALL); } static void logging_get_enabled_log_levels_foo_test (void **state) { int levels; UNUSED_PARAM(state); will_return (__wrap_getenv, env_str_foo); levels = get_enabled_log_levels (); assert_int_equal (levels, LOG_LEVEL_DEFAULT); } static void logging_set_logger_foo_test (void **state) { UNUSED_PARAM(state); assert_int_equal (set_logger ("foo"), -1); } static void logging_set_logger_stdout_test (void **state) { UNUSED_PARAM(state); assert_int_equal (set_logger ("stdout"), 0); } static void logging_set_logger_syslog_test (void **state) { UNUSED_PARAM(state); will_return (__wrap_getenv, NULL); assert_int_equal (set_logger ("syslog"), 0); } void __wrap_syslog (int priority, const char *format, ...) { UNUSED_PARAM(priority); UNUSED_PARAM(format); return; } static void logging_syslog_log_handler_fatal_test (void **state) { UNUSED_PARAM(state); syslog_log_handler ("domain", G_LOG_FLAG_FATAL, "foo", NULL); } static void logging_syslog_log_handler_error_test (void **state) { UNUSED_PARAM(state); syslog_log_handler ("domain", G_LOG_LEVEL_ERROR, "foo", NULL); } static void logging_syslog_log_handler_critical_test (void **state) { UNUSED_PARAM(state); syslog_log_handler ("domain", G_LOG_LEVEL_CRITICAL, "foo", NULL); } static void logging_syslog_log_handler_warning_test (void **state) { UNUSED_PARAM(state); syslog_log_handler ("domain", G_LOG_LEVEL_WARNING, "foo", NULL); } static void logging_syslog_log_handler_message_test (void **state) { UNUSED_PARAM(state); syslog_log_handler ("domain", G_LOG_LEVEL_MESSAGE, "foo", NULL); } static void logging_syslog_log_handler_info_test (void **state) { UNUSED_PARAM(state); syslog_log_handler ("domain", G_LOG_LEVEL_INFO, "foo", NULL); } static void logging_syslog_log_handler_debug_test (void **state) { UNUSED_PARAM(state); syslog_log_handler ("domain", G_LOG_LEVEL_DEBUG, "foo", NULL); } static void logging_syslog_log_handler_default_test (void **state) { UNUSED_PARAM(state); syslog_log_handler ("domain", G_LOG_LEVEL_DEBUG|G_LOG_LEVEL_MESSAGE, "foo", NULL); } int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test (logging_get_enabled_log_levels_default_test), cmocka_unit_test (logging_get_enabled_log_levels_all_test), cmocka_unit_test (logging_get_enabled_log_levels_foo_test), cmocka_unit_test (logging_set_logger_foo_test), cmocka_unit_test (logging_set_logger_stdout_test), cmocka_unit_test (logging_set_logger_syslog_test), cmocka_unit_test (logging_syslog_log_handler_fatal_test), cmocka_unit_test (logging_syslog_log_handler_error_test), cmocka_unit_test (logging_syslog_log_handler_critical_test), cmocka_unit_test (logging_syslog_log_handler_warning_test), cmocka_unit_test (logging_syslog_log_handler_message_test), cmocka_unit_test (logging_syslog_log_handler_info_test), cmocka_unit_test (logging_syslog_log_handler_debug_test), cmocka_unit_test (logging_syslog_log_handler_default_test), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/message-queue_unit.c000066400000000000000000000114711401030142600204050ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "message-queue.h" #include "connection.h" #include "control-message.h" #include "util.h" typedef struct msgq_test_data { MessageQueue *queue; } msgq_test_data_t; static int message_queue_setup (void **state) { msgq_test_data_t *data = NULL; data = calloc (1, sizeof (msgq_test_data_t)); assert_non_null (data); data->queue = message_queue_new (); *state = data; return 0; } static int message_queue_teardown (void **state) { msgq_test_data_t *data = (msgq_test_data_t*)*state; g_object_unref (data->queue); free (data); return 0; } static void message_queue_allocate_test (void **state) { message_queue_setup (state); message_queue_teardown (state); } static void message_queue_enqueue_dequeue_test (void **state) { msgq_test_data_t *data = (msgq_test_data_t*)*state; MessageQueue *queue = data->queue; Connection *obj_in, *obj_out; HandleMap *handle_map; gint client_fd; GIOStream *iostream; handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); obj_in = connection_new (iostream, 0, handle_map); message_queue_enqueue (queue, G_OBJECT (obj_in)); obj_out = CONNECTION (message_queue_dequeue (queue)); /* ptr != int but they're the same size usually? */ assert_int_equal (obj_in, obj_out); g_object_unref (handle_map); g_object_unref (iostream); } static void message_queue_dequeue_order_test (void **state) { msgq_test_data_t *data = (msgq_test_data_t*)*state; HandleMap *map_0, *map_1, *map_2; MessageQueue *queue = data->queue; Connection *obj_0, *obj_1, *obj_2, *obj_tmp; gint client_fd; GIOStream *iostream; map_0 = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); map_1 = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); map_2 = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); obj_0 = connection_new (iostream, 0, map_0); g_object_unref (iostream); iostream = create_connection_iostream (&client_fd); obj_1 = connection_new (iostream, 0, map_1); g_object_unref (iostream); iostream = create_connection_iostream (&client_fd); obj_2 = connection_new (iostream, 0, map_2); g_object_unref (iostream); message_queue_enqueue (queue, G_OBJECT (obj_0)); message_queue_enqueue (queue, G_OBJECT (obj_1)); message_queue_enqueue (queue, G_OBJECT (obj_2)); obj_tmp = CONNECTION (message_queue_dequeue (queue)); assert_int_equal (obj_tmp, obj_0); obj_tmp = CONNECTION (message_queue_dequeue (queue)); assert_int_equal (obj_tmp, obj_1); obj_tmp = CONNECTION (message_queue_dequeue (queue)); assert_int_equal (obj_tmp, obj_2); g_object_unref (map_0); g_object_unref (map_1); g_object_unref (map_2); g_object_unref (obj_0); g_object_unref (obj_1); g_object_unref (obj_2); } /* * This function is used in the thread_unblock_test function as the thread * that blocks on the MessageQueue waiting for a message. */ void* thread_func (void* arg) { MessageQueue *queue = MESSAGE_QUEUE (arg); GObject *obj; obj = message_queue_dequeue (queue); assert_true (CONTROL_MESSAGE (obj)); g_object_unref (obj); return NULL; } static void message_queue_thread_unblock_test (void **state) { msgq_test_data_t *data = (msgq_test_data_t*)*state; ControlMessage *msg = control_message_new (CHECK_CANCEL); pthread_t thread_id; int ret; ret = pthread_create (&thread_id, NULL, thread_func, data->queue); assert_int_equal (ret, 0); message_queue_enqueue (data->queue, G_OBJECT (msg)); g_object_unref (msg); ret = pthread_join (thread_id, NULL); assert_int_equal (ret, 0); } int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test (message_queue_allocate_test), cmocka_unit_test_setup_teardown (message_queue_enqueue_dequeue_test, message_queue_setup, message_queue_teardown), cmocka_unit_test_setup_teardown (message_queue_dequeue_order_test, message_queue_setup, message_queue_teardown), cmocka_unit_test_setup_teardown (message_queue_thread_unblock_test, message_queue_setup, message_queue_teardown), }; return cmocka_run_group_tests(tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/mock-funcs.c000066400000000000000000000072021401030142600166420ustar00rootroot00000000000000/* * SPDX-License-Identifier: BSD-2-Clause * Copyright (c) 2019, Intel Corporation */ #include #include #include #include #include #include #include "mock-funcs.h" int __real_poll (struct pollfd *fds, nfds_t nfds, int timeout); int __wrap_poll (struct pollfd *fds, nfds_t nfds, int timeout) { assert_non_null (fds); if (fds->fd != TEST_FD) { return __real_poll (fds, nfds, timeout); } else { fds->revents = mock_type (short); errno = mock_type (int); return mock_type (int); } } /* * The mock functions for the g_socket_* stuff are required to test the * tcti_tabrmd_read function. They are used to extract the GInputStream * from the GSocketConnection stored in the TCTI context structure. * * These mock functions are written to mock the implementation only when * passed known values used in this module. It will call the 'real' * if passed a real glib object. */ GSocket* __real_g_socket_connection_get_socket (GSocketConnection *connection); GSocket* __wrap_g_socket_connection_get_socket (GSocketConnection *connection) { g_debug ("%s: 0x%" PRIxPTR, __func__, (uintptr_t)connection); if (connection != TEST_CONNECTION) { return __real_g_socket_connection_get_socket (connection); } else { return mock_type (GSocket*); } } int __real_g_socket_get_fd (GSocket *socket); int __wrap_g_socket_get_fd (GSocket *socket) { g_debug ("%s: 0x%" PRIxPTR, __func__, (uintptr_t)socket); if (socket != TEST_SOCKET) { return __real_g_socket_get_fd (socket); } else { return mock_type (int); } } GInputStream* __real_g_io_stream_get_input_stream (GIOStream *stream); GInputStream* __wrap_g_io_stream_get_input_stream (GIOStream *stream) { g_debug ("%s iostream: 0x%" PRIxPTR, __func__, (uintptr_t)stream); if (stream != (GIOStream*)TEST_CONNECTION) { return __real_g_io_stream_get_input_stream (stream); } else { return mock_type (GInputStream*); } } /* * The mock functions for the GInputStream reading function is required to * test the * tcti_tabrmd_read function. This is almost exactly like a * mock function the 'read' system call but handles the glib stuff too. * * This mock function is written to mock the implementation only when * passed known values used in this module. It will call the 'real' * if passed a real glib object. */ gssize __real_g_input_stream_read (GInputStream *stream, void *buffer, gsize count, GCancellable *cancellable, GError **error); gssize __wrap_g_input_stream_read (GInputStream *stream, void *buffer, gsize count, GCancellable *cancellable, GError **error) { gssize resp_size; uint8_t *resp; g_debug ("%s stream: 0x%" PRIxPTR, __func__, (uintptr_t)stream); if (stream != (GInputStream*)TEST_CONNECTION) { return __real_g_input_stream_read (stream, buffer, count, cancellable, error); } else { resp_size = mock_type (gssize); if (resp_size > 0) { resp = mock_type (uint8_t*); memcpy (buffer, resp, resp_size); } else if (resp_size == -1) { *error = mock_type (GError*); } return resp_size; } } tpm2-abrmd-2.4.0/test/mock-funcs.h000066400000000000000000000016441401030142600166530ustar00rootroot00000000000000/* * SPDX-License-Identifier: BSD-2-Clause * Copyright (c) 2019, Intel Corporation */ #include #include #include #include #include #include #include #include #define TEST_FD 5551555 #define TEST_SOCKET (GSocket*)8675309 #define TEST_CONNECTION ((GSocketConnection*)9035768) #define TEST_ISTREAM (GInputStream*)583 int __wrap_poll (struct pollfd *fds, nfds_t nfds, int timeout); GSocket* __wrap_g_socket_connection_get_socket (GSocketConnection *connection); int __wrap_g_socket_get_fd (GSocket *socket); GInputStream* __wrap_g_io_stream_get_input_stream (GIOStream *stream); gssize __wrap_g_input_stream_read (GInputStream *stream, void *buffer, gsize count, GCancellable *cancellable, GError **error); tpm2-abrmd-2.4.0/test/mock-io-stream.c000066400000000000000000000114341401030142600174260ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include #include "mock-io-stream.h" #include "util.h" G_DEFINE_TYPE (MockIOStream, mock_io_stream, G_TYPE_IO_STREAM); enum { PROP_0, PROP_ISTREAM, PROP_OSTREAM, N_PROPERTIES }; static GParamSpec *obj_properties [N_PROPERTIES] = { NULL }; static void mock_io_stream_set_property (GObject *object, guint property_id, GValue const *value, GParamSpec *pspec) { MockIOStream *self = MOCK_IO_STREAM (object); g_debug ("%s: MockIOStream 0x%" PRIxPTR, __func__, (uintptr_t)self); switch (property_id) { case PROP_ISTREAM: g_debug ("%s: GInputStream: 0x%" PRIxPTR, __func__, (uintptr_t)self->istream); self->istream = g_value_dup_object (value); break; case PROP_OSTREAM: self->ostream = g_value_dup_object (value); g_debug ("%s: GOutputStream: 0x%" PRIxPTR, __func__, (uintptr_t)self->ostream); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void mock_io_stream_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MockIOStream *self = MOCK_IO_STREAM (object); g_debug ("%s: MockIOStream 0x%" PRIxPTR, __func__, (uintptr_t)self); switch (property_id) { case PROP_ISTREAM: g_value_set_object (value, self->istream); break; case PROP_OSTREAM: g_value_set_object (value, self->ostream); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static GInputStream* mock_io_stream_get_istream (GIOStream *iostream) { return MOCK_IO_STREAM (iostream)->istream; } static GOutputStream* mock_io_stream_get_ostream (GIOStream *iostream) { return MOCK_IO_STREAM (iostream)->ostream; } static void mock_io_stream_init (MockIOStream *self) { UNUSED_PARAM (self); } /* * This dispose function is kinda weird: * The parent class closes the underlying stream objects for us in its own * dispose function. Typically we would unref all of our objects here and then * chain up to the parent but if we do that the stream objects will have been * destroyed and the parent class will throw a bunch of CRITICAL error * messages on account of it. * * This leaves us with the option of either doing the unref-ing in the * finalize function (which all the gobject docs says is the wrong place to * do this) or we can do this after we chain up to the parent. I've opted for * the later. */ static void mock_io_stream_dispose (GObject *obj) { MockIOStream *self = MOCK_IO_STREAM (obj); G_OBJECT_CLASS (mock_io_stream_parent_class)->dispose (obj); g_clear_object (&self->istream); g_clear_object (&self->ostream); } static void mock_io_stream_class_init (MockIOStreamClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); GIOStreamClass *stream_class = G_IO_STREAM_CLASS (klass); if (mock_io_stream_parent_class == NULL) { mock_io_stream_parent_class = g_type_class_peek_parent (klass); } obj_class->dispose = mock_io_stream_dispose; obj_class->get_property = mock_io_stream_get_property; obj_class->set_property = mock_io_stream_set_property; stream_class->get_input_stream = mock_io_stream_get_istream; stream_class->get_output_stream = mock_io_stream_get_ostream; obj_properties [PROP_ISTREAM] = g_param_spec_object ("input-stream", "input stream", "input stream", G_TYPE_INPUT_STREAM, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); obj_properties [PROP_OSTREAM] = g_param_spec_object ("output-stream", "output stream", "output stream", G_TYPE_OUTPUT_STREAM, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (obj_class, N_PROPERTIES, obj_properties); } GIOStream* mock_io_stream_new (GInputStream *istream, GOutputStream *ostream) { return G_IO_STREAM (g_object_new (TYPE_MOCK_IO_STREAM, "input-stream", istream, "output-stream", ostream, NULL)); } tpm2-abrmd-2.4.0/test/mock-io-stream.h000066400000000000000000000026041401030142600174320ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2018, Intel Corporation * All rights reserved. */ #ifndef MOCK_IO_STREAM_H #define MOCK_IO_STREAM_H #include #include #include #include G_BEGIN_DECLS typedef struct _MockIOStreamClass { GIOStreamClass parent; } MockIOStreamClass; typedef struct _MockIOStream { GIOStream parent_instance; GInputStream *istream; GOutputStream *ostream; } MockIOStream; #define TYPE_MOCK_IO_STREAM (mock_io_stream_get_type ()) #define MOCK_IO_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MOCK_IO_STREAM, MockIOStream)) #define MOCK_IO_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_MOCK_IO_STREAM, MockIOStreamClass)) #define IS_MOCK_IO_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MOCK_IO_STREAM)) #define IS_MOCK_IO_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_MOCK_IO_STREAM)) #define MOCK_IO_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_MOCK_IO_STREAM, MockIOStreamClass)) GType mock_io_stream_get_type (void); GIOStream* mock_io_stream_new (GInputStream *istream, GOutputStream *ostream); G_END_DECLS #endif /* MOCK_IO_STREAM_H */ tpm2-abrmd-2.4.0/test/msg_unit.c000066400000000000000000000015201401030142600164170ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include "msg.h" #include "session-data.h" static void check_cancel_msg_allocate_test (void **state) { check_cancel_msg_t *msg; msg = check_cancel_msg_new (); assert_non_null (msg); assert_int_equal (MSG_TYPE (msg), CHECK_CANCEL_MSG_TYPE); check_cancel_msg_free (msg); } static void data_msg_allocate_allnull_test (void **state) { data_msg_t *msg = data_msg_new (NULL, NULL, 0); assert_non_null (msg); data_msg_free (msg); } gint main(void) { const UnitTest tests[] = { unit_test (check_cancel_msg_allocate_test), unit_test (data_msg_allocate_allnull_test), }; return run_tests(tests); } tpm2-abrmd-2.4.0/test/random_unit.c000066400000000000000000000221061401030142600171140ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "util.h" #include "random.h" #define ENTROPY_SRC "/dev/urandom" typedef struct test_data { Random *random; } test_data_t; /* Setup function to allocate our Random gobject. */ static int random_setup (void **state) { test_data_t *data; data = calloc (1, sizeof (test_data_t)); data->random = RANDOM (random_new ()); *state = data; return 0; } /* Teardown function to deallocate the Random object. */ static int random_teardown (void **state) { test_data_t *data = *state; g_object_unref (data->random); free (data); return 0; } /* wrap function for the 'open' system call */ int __real_open (const char *path, int flags, int mode); int __wrap_open(const char *pathname, int flags, mode_t mode) { /* * Mock calls to 'open' only for operations on the default entropy * source used by the Random object (ENTROPY_SRC). */ if (strcmp (pathname, ENTROPY_SRC)) { return __real_open (pathname, flags, mode); } return mock_type (int); } /* wrap function for the 'read' system call */ ssize_t __wrap_read (int fd, void *buf, size_t count) { UNUSED_PARAM(fd); UNUSED_PARAM(buf); UNUSED_PARAM(count); return mock_type (ssize_t); } /* wrap function for the 'close' system call */ int __wrap_close (int fd) { UNUSED_PARAM(fd); return mock_type (int); } /* Simple test to test type checking macros. */ static void random_type_test (void **state) { test_data_t *data = *state; assert_true (G_IS_OBJECT (data->random)); assert_true (IS_RANDOM (data->random)); } /* * Seed the Random object from a file source. The file will never actually * be opened or read. All syscalls are wrapped. The wrap functions are * prepared such that the random_seed_from_file function executes the * common / success code path. */ static void random_seed_from_file_success_test (void **state) { test_data_t *data = *state; int ret; will_return (__wrap_open, 5); will_return (__wrap_read, sizeof (long int)); will_return (__wrap_close, 0); ret = random_seed_from_file (data->random, ENTROPY_SRC); assert_int_equal (ret, 0); } /* * Test a failure condition for the random_seed_from_file function. We * cause the 'open' system call to fail and we ensure that the function * under test returns an integer indicating the error. */ static void random_seed_from_file_open_fail_test (void **state) { test_data_t *data = *state; int ret; will_return (__wrap_open, -1); ret = random_seed_from_file (data->random, ENTROPY_SRC); assert_int_equal (ret, -1); } /* * Test a failure condition for the random_seed_from_file function. We * cause the 'read' system call to fail and we ensure that the function * under test returns an integer indicating the error. Additionally we must * mock a successful call to 'open' first, and the 'close' as well. */ static void random_seed_from_file_read_fail_test (void **state) { test_data_t *data = *state; int ret; will_return (__wrap_open, 5); will_return (__wrap_read, -1); will_return (__wrap_close, 0); ret = random_seed_from_file (data->random, ENTROPY_SRC); assert_int_equal (ret, -1); } /* * Test a success condition for the random_seed_from_file function, but * force the 'close' system call to fail. In this case we've successfully * opened and read from the entropy source we just weren't able to close * the file. The response code from the function should still indicate * sucess since we got all of the data we need. A warning message will * however be output. */ static void random_seed_from_file_close_fail_test (void **state) { test_data_t *data = *state; int ret; will_return (__wrap_open, 5); will_return (__wrap_read, sizeof (long int)); will_return (__wrap_close, -1); ret = random_seed_from_file (data->random, ENTROPY_SRC); assert_int_equal (ret, 0); } /* * Test a failure condition for the random_seed_from_file_function. In * this case both the 'read' and 'close' functions fail. The function under * test should then return -1. */ static void random_seed_from_file_read_close_fail_test (void **state) { test_data_t *data = *state; int ret; will_return (__wrap_open, 5); will_return (__wrap_read, -1); will_return (__wrap_close, -1); ret = random_seed_from_file (data->random, ENTROPY_SRC); assert_int_equal (ret, -1); } /* * Test a failure condition for the random_seed_from_file function. In * this case the 'read' function succeeds but reads less data than * requested / necessary. This should result in the function under test * returning an error indicator. */ static void random_seed_from_file_read_short_test (void **state) { test_data_t *data = *state; int ret; will_return (__wrap_open, 5); will_return (__wrap_read, sizeof (long int) - 1); will_return (__wrap_close, 0); ret = random_seed_from_file (data->random, ENTROPY_SRC); assert_int_equal (ret, -1); } static int random_get_bytes_setup (void **state) { test_data_t *data; int ret; random_setup (state); data = *state; will_return (__wrap_open, 5); will_return (__wrap_read, sizeof (long int)); will_return (__wrap_close, 0); ret = random_seed_from_file (data->random, ENTROPY_SRC); assert_int_equal (ret, 0); return 0; } /* * Test a successful call to the random_get_bytes function. */ static void random_get_bytes_success_test (void **state) { test_data_t *data = *state; int ret; uint8_t dest[sizeof (long int) * 3 - 3] = { 0, }; ret = random_get_bytes (data->random, dest, sizeof (dest)); assert_int_equal (ret, sizeof (long int) * 3 - 3); } /* * Test case to execute a successful call to random_get_uint64. * If 0 or UINT64_MAX, it's very likely the data is not random. */ static void random_get_uint64_success_test (void **state) { test_data_t *data = *state; uint64_t dest = 0; dest = random_get_uint64 (data->random); assert_true (dest != 0 && dest != UINT64_MAX); } /* * Test case to execute a successful call to random_get_uint32. * If 0 or UINT32_MAX, it's very likely the data is not random. */ static void random_get_uint32_success_test (void **state) { test_data_t *data = *state; uint32_t dest; dest = random_get_uint32 (data->random); assert_true (dest != 0 && dest != UINT32_MAX); } /* Test case to check that a random number is within the given range. */ static void random_get_uint32_range_success_test (void **state) { test_data_t *data = *state; uint32_t dest, low = 6, high = 20; dest = random_get_uint32_range (data->random, high, low); assert_true (low < dest); assert_true (dest < high); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (random_type_test, random_setup, random_teardown), cmocka_unit_test_setup_teardown (random_seed_from_file_success_test, random_setup, random_teardown), cmocka_unit_test_setup_teardown (random_seed_from_file_open_fail_test, random_setup, random_teardown), cmocka_unit_test_setup_teardown (random_seed_from_file_read_fail_test, random_setup, random_teardown), cmocka_unit_test_setup_teardown (random_seed_from_file_close_fail_test, random_setup, random_teardown), cmocka_unit_test_setup_teardown (random_seed_from_file_read_close_fail_test, random_setup, random_teardown), cmocka_unit_test_setup_teardown (random_seed_from_file_read_short_test, random_setup, random_teardown), cmocka_unit_test_setup_teardown (random_get_bytes_success_test, random_get_bytes_setup, random_teardown), cmocka_unit_test_setup_teardown (random_get_uint64_success_test, random_get_bytes_setup, random_teardown), cmocka_unit_test_setup_teardown (random_get_uint32_success_test, random_get_bytes_setup, random_teardown), cmocka_unit_test_setup_teardown (random_get_uint32_range_success_test, random_get_bytes_setup, random_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/resource-manager_unit.c000066400000000000000000000510411401030142600210730ustar00rootroot00000000000000/* 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 "tpm2.h" #include "resource-manager.h" #include "sink-interface.h" #include "source-interface.h" #include "tcti.h" #include "tcti-mock.h" #include "tpm2-command.h" #include "tpm2-header.h" #include "util.h" typedef struct test_data { Tpm2 *tpm2; ResourceManager *resource_manager; Connection *connection; Tpm2Command *command; Tpm2Response *response; gint client_fd; TPM2_HANDLE vhandles [2]; TPMA_CC command_attrs; } test_data_t; /** * Mock function for testing the resource_manager_process_tpm2_command * function which depends on the tpm2_send_command and must * handle the call and the associated error conditions. */ Tpm2Response* __wrap_tpm2_send_command (Tpm2 *tpm2, Tpm2Command *command, TSS2_RC *rc) { Tpm2Response *response; UNUSED_PARAM(tpm2); UNUSED_PARAM(command); *rc = mock_type (TSS2_RC); response = TPM2_RESPONSE (mock_ptr_type (GObject*)); return response; } /** * Mock function for testing the resource_manager_process_tpm2_command * function. When the Tpm2 returns a Tpm2Response object the * ResourceManager passes this object to whatever sink has been provided * to it. For the purposes of testing we don't provide a valid Sink. * Instead we mock this function as a way to take the response object * away from the ResourceManager and verify that it was produced * correctly. * * We do something weird here though: we pass the test data structure into * this function by way of the will_return / wrap mechanism. This isn't how * these are intended to be used but it's the only way to get the test * data structure into the function so that we can verify the sink was * passed the Tpm2Response object that we expect. */ void __wrap_sink_enqueue (Sink *self, GObject *obj) { UNUSED_PARAM(self); test_data_t *data = mock_ptr_type (test_data_t*); data->response = TPM2_RESPONSE (obj); } TSS2_RC __wrap_tpm2_context_saveflush (Tpm2 *broker, TPM2_HANDLE handle, TPMS_CONTEXT *context) { UNUSED_PARAM(broker); UNUSED_PARAM(handle); UNUSED_PARAM(context); return mock_type (TSS2_RC); } /* * Wrap call to tpm2_context_load. Pops two parameters off the * stack with the 'mock' command. The first is the RC which is returned * directly to the caller. The other is the new physical handle that was * allocated by the TPM for the context that was just loaded. */ TSS2_RC __wrap_tpm2_context_load (Tpm2 *tpm2, TPMS_CONTEXT *context, TPM2_HANDLE *handle) { TSS2_RC rc = mock_type (TSS2_RC); TPM2_HANDLE phandle = mock_type (TPM2_HANDLE); UNUSED_PARAM(tpm2); UNUSED_PARAM(context); assert_non_null (handle); *handle = phandle; return rc; } static int resource_manager_setup (void **state) { test_data_t *data; GIOStream *iostream; HandleMap *handle_map; SessionList *session_list; Tcti *tcti = NULL; TSS2_TCTI_CONTEXT *context; context = tcti_mock_init_full (); tcti = tcti_new (context); data = calloc (1, sizeof (test_data_t)); handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); data->tpm2 = tpm2_new (tcti); g_clear_object (&tcti); session_list = session_list_new (SESSION_LIST_MAX_ENTRIES_DEFAULT, SESSION_LIST_MAX_ABANDONED_DEFAULT); data->resource_manager = resource_manager_new (data->tpm2, session_list); g_clear_object (&session_list); iostream = create_connection_iostream (&data->client_fd); data->connection = connection_new (iostream, 10, handle_map); g_object_unref (handle_map); g_object_unref (iostream); *state = data; return 0; } static int resource_manager_setup_two_transient_handles (void **state) { test_data_t *data; guint8 *buffer; size_t buffer_size; resource_manager_setup (state); data = *state; data->vhandles [0] = TPM2_HR_TRANSIENT + 0x1; data->vhandles [1] = TPM2_HR_TRANSIENT + 0x2; data->command_attrs = (2 << 25) + TPM2_CC_StartAuthSession; /* 2 handles + TPM2_StartAuthSession */ /* create Tpm2Command that we'll be transforming */ buffer_size = TPM_HEADER_SIZE + 2 * sizeof (TPM2_HANDLE); buffer = calloc (1, buffer_size); *(TPM2_ST*)buffer = htobe16 (TPM2_ST_NO_SESSIONS); buffer [2] = 0x00; buffer [3] = 0x00; buffer [4] = 0x00; buffer [5] = buffer_size; buffer [6] = 0x00; buffer [7] = 0x00; buffer [8] = TPM2_CC_StartAuthSession >> 8; buffer [9] = TPM2_CC_StartAuthSession & 0xff; buffer [10] = TPM2_HT_TRANSIENT; buffer [11] = 0x00; buffer [12] = 0x00; buffer [13] = data->vhandles [0] & 0xff; /* first virtual handle */ buffer [14] = TPM2_HT_TRANSIENT; buffer [15] = 0x00; buffer [16] = 0x00; buffer [17] = data->vhandles [1] & 0xff; /* second virtual handle */ data->command = tpm2_command_new (data->connection, buffer, buffer_size, data->command_attrs); return 0; } static int resource_manager_teardown (void **state) { test_data_t *data = (test_data_t*)*state; g_debug ("resource_manager_teardown"); g_object_unref (data->resource_manager); if (data->tpm2) { g_debug ("resource_manager unref tpm2"); g_object_unref (data->tpm2); } if (data->connection) { g_debug ("resource_manager unref Connection"); g_object_unref (data->connection); } if (data->command) { g_debug ("resource_manager unref Tpm2Command"); g_object_unref (data->command); } free (data); return 0; } /** * A test: ensure that that ResourceManager created in the 'setup' function * is identified by the type system as such. */ static void resource_manager_type_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_true (IS_RESOURCE_MANAGER (data->resource_manager)); } /** * A test: Ensure that the Sink interface to the ResourceManager works. We * create a Tpm2Command, send it through the ResourceManager enqueue * function then pull it out the other end by reaching in to the * ResourceManagers internal MessageQueue. * We *DO NOT* use the sink interface here since we've mock'd that for * other purposes and it would make the test largely meaningless. */ static void resource_manager_sink_enqueue_test (void **state) { test_data_t *data = (test_data_t*)*state; Tpm2Command *command_out; guint8 *buffer; buffer = calloc (1, TPM_HEADER_SIZE); data->command = tpm2_command_new (data->connection, buffer, TPM_HEADER_SIZE, (TPMA_CC){ 0, }); resource_manager_enqueue (SINK (data->resource_manager), G_OBJECT (data->command)); command_out = TPM2_COMMAND (message_queue_dequeue (data->resource_manager->in_queue)); assert_int_equal (data->command, command_out); assert_int_equal (1, 1); } /** * A test: exercise the resource_manager_process_tpm2_command function. * This function is normally invoked by the ResourceManager internal * thread. We invoke it directly here to control variables and timing * issues with the thread. */ static void resource_manager_process_tpm2_command_success_test (void **state) { test_data_t *data = (test_data_t*)*state; Tpm2Response *response; guint8 *buffer; buffer = calloc (1, TPM_HEADER_SIZE); /** * we don't use the test data structure to hold the command object since * it will be freed by the call to resource_manager_process_tpm2_command * and the teardown function will attempt to free it again if set. */ data->command = tpm2_command_new (data->connection, buffer, TPM_HEADER_SIZE, (TPMA_CC){ 0, }); response = tpm2_response_new_rc (data->connection, TSS2_RC_SUCCESS); /* * This response object will be freed by the process_tpm2_command * function. We take an extra reference and free when we're done. */ g_object_ref (response); will_return (__wrap_tpm2_send_command, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_send_command, response); /** * The sink_enqueue wrap function will assign the Tpm2Response it's passed * to the test data structure. */ will_return (__wrap_sink_enqueue, data); resource_manager_process_tpm2_command (data->resource_manager, data->command); assert_int_equal (data->response, response); g_object_unref (response); } static void resource_manager_flushsave_context_test (void **state) { test_data_t *data = (test_data_t*)*state; HandleMapEntry *entry; TPM2_HANDLE vhandle = TPM2_HR_TRANSIENT + 0x1, phandle = TPM2_HR_TRANSIENT + 0x2; entry = handle_map_entry_new (phandle, vhandle); will_return (__wrap_tpm2_context_saveflush, TSS2_RC_SUCCESS); resource_manager_flushsave_context (entry, data->resource_manager); assert_int_equal (handle_map_entry_get_phandle (entry), 0); g_object_unref (entry); } static void resource_manager_flushsave_context_same_entries_test (void **state) { test_data_t *data = (test_data_t*)*state; HandleMapEntry *entry1, *entry2, *entry3; HandleMap *map; TPM2_HANDLE vhandle = TPM2_HR_TRANSIENT + 0x1, phandle1 = TPM2_HR_TRANSIENT + 0x2, phandle2 = TPM2_HR_TRANSIENT + 0x3; /* Create two map entries for the same vhandle and different phandle */ entry1 = handle_map_entry_new (phandle1, vhandle); entry2 = handle_map_entry_new (phandle2, vhandle); map = handle_map_new(TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); /* Insert the two mappings, which bumps the ref count */ handle_map_insert(map, vhandle, entry1); handle_map_insert(map, vhandle, entry2); /* Call flushsave_context on the second entry */ will_return (__wrap_tpm2_context_saveflush, TSS2_RC_SUCCESS); resource_manager_flushsave_context (entry2, data->resource_manager); /* and check if the first is still valid */ entry3 = handle_map_vlookup(map, vhandle); assert_int_equal (handle_map_entry_get_phandle (entry3), phandle1); /* Cleanup */ handle_map_remove (map, vhandle); g_object_unref (entry1); g_object_unref (entry2); g_object_unref (entry3); g_object_unref (map); } /* * This test case pushes an error RC on to the mock stack for the * tpm2_context_flushsave function. The flushsave_context function * in the RM will then successfully find the phandle in the provided entry * but the call to the Tpm2 will fail. * The function under test should propagate this RC to the caller (us) and * should *NOT* update the phandle in the provided entry. */ static void resource_manager_flushsave_context_fail_test (void **state) { test_data_t *data = (test_data_t*)*state; HandleMapEntry *entry; TPM2_HANDLE vhandle = TPM2_HR_TRANSIENT + 0x1, phandle = TPM2_HR_TRANSIENT + 0x2; entry = handle_map_entry_new (phandle, vhandle); will_return (__wrap_tpm2_context_saveflush, TPM2_RC_INITIALIZE); resource_manager_flushsave_context (entry, data->resource_manager); assert_int_equal (handle_map_entry_get_phandle (entry), phandle); g_object_unref (entry); } /* */ static void resource_manager_virt_to_phys_test (void **state) { test_data_t *data = (test_data_t*)*state; HandleMapEntry *entry; TPM2_HANDLE phandle = TPM2_HR_TRANSIENT + 0x1, vhandle = 0; TPM2_HANDLE handle_ret = 0; TSS2_RC rc = TSS2_RC_SUCCESS; will_return (__wrap_tpm2_context_load, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_context_load, phandle); /* and the rest of it */ /* create & populate HandleMap for transient handle */ vhandle = tpm2_command_get_handle (data->command, 0); entry = handle_map_entry_new (0, vhandle); /* function under test, */ rc = resource_manager_virt_to_phys (data->resource_manager, data->command, entry, 0); g_object_unref (entry); handle_ret = tpm2_command_get_handle (data->command, 0); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (phandle, handle_ret); } /* */ static void resource_manager_load_handles_test(void **state) { test_data_t *data = (test_data_t*)*state; HandleMapEntry *entry; GSList *entry_slist; HandleMap *map; TPM2_HANDLE phandles [3] = { TPM2_HR_TRANSIENT + 0xeb, TPM2_HR_TRANSIENT + 0xbe, 0 }; TPM2_HANDLE vhandles [3] = { 0, 0, 0 }; TPM2_HANDLE handle_ret; TSS2_RC rc = TSS2_RC_SUCCESS; size_t handle_count = 2, i; will_return (__wrap_tpm2_context_load, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_context_load, phandles [0]); will_return (__wrap_tpm2_context_load, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_context_load, phandles [1]); tpm2_command_get_handles (data->command, vhandles, &handle_count); map = connection_get_trans_map (data->connection); if (handle_count > 2) { assert (FALSE); } for (i = 0; i < handle_count; ++i) { entry = handle_map_entry_new (0, vhandles [i]); handle_map_insert (map, vhandles [i], entry); g_object_unref (entry); } rc = resource_manager_load_handles (data->resource_manager, data->command, &entry_slist); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_true (handle_count <= 2); for (i = 0; i < handle_count; ++i) { handle_ret = tpm2_command_get_handle (data->command, i); assert_int_equal (phandles [i], handle_ret); } } /* * This setup function calls the 'resource_manager_setup' function to create * the ResourceManager object etc. It then creates a Tpm2Response object * approprite for use in testing the 'get_cap_post_process' function. */ int resource_manager_setup_getcap (void **state) { int setup_ret; test_data_t *data = NULL; size_t offset = 0, buf_size = 0; uint8_t *buf = NULL; TSS2_RC rc; /* structures that define the Tpm2Response object used in GetCap tests */ TPMI_YES_NO yes_no = TPM2_NO; TPMS_CAPABILITY_DATA cap_data = { .capability = TPM2_CAP_TPM_PROPERTIES, /* TPMU_CAPABILITIES, union type defined by 'capability' selector */ .data = { /* TPML_TAGGED_TPM_PROPERTY */ .tpmProperties = { .count = 1, /* TPMS_TAGGED_PROPERTY */ .tpmProperty = { { .property = TPM2_PT_CONTEXT_GAP_MAX, .value = 0xff, } } } } }; setup_ret = resource_manager_setup (state); if (setup_ret != 0) { g_critical ("%s: resource_manager_setup failed", __func__); return setup_ret; } data = (test_data_t*)*state; buf_size = TPM2_MAX_RESPONSE_SIZE; g_debug ("%s: sizeof buffer required for TPMS_CAPABILITY_DATA: 0x%zx", __func__, offset); buf = calloc (1, buf_size); if (buf == NULL) { g_critical ("%s: failed to allocate buffer: %s", __func__, strerror (errno)); return 1; } offset = TPM_HEADER_SIZE; rc = Tss2_MU_BYTE_Marshal (yes_no, buf, buf_size, &offset); if (rc != TSS2_RC_SUCCESS) { g_critical ("%s: failed to marshal TPMI_YES_NO into response body, " "rc: 0x%" PRIx32, __func__, rc); return 1; } rc = Tss2_MU_TPMS_CAPABILITY_DATA_Marshal (&cap_data, buf, buf_size, &offset); if (rc != TSS2_RC_SUCCESS) { g_critical ("%s: failed to marshal TPMS_CAPABILITY_DATA into response " "body, rc: 0x%" PRIx32, __func__, rc); return 1; } rc = tpm2_header_init (buf, buf_size, TPM2_ST_NO_SESSIONS, offset, TSS2_RC_SUCCESS); if (rc != TSS2_RC_SUCCESS) { g_critical ("%s: failed to initialize TPM2 Header, rc: 0x%" PRIx32, __func__, rc); return 1; } data->response = tpm2_response_new (data->connection, buf, offset, TPM2_CC_GetCapability); return setup_ret; } /* * Test 'getcap_post_process' function to ensure that TPM2_PT_CONTEXT_GAP_MAX * property is properly modified from its initial value of UINT8_MAX to * UINT32_MAX. */ void resource_manager_getcap_gap_max_test (void **state) { test_data_t *data = (test_data_t*)*state; size_t offset = TPM_HEADER_SIZE, buf_size; uint8_t *buf; TSS2_RC rc; TPMI_YES_NO yes_no = TPM2_NO; TPMS_CAPABILITY_DATA cap_data = { 0 }; /* get buffer & size from Tpm2Response created in setup function */ buf = tpm2_response_get_buffer (data->response); buf_size = tpm2_response_get_size (data->response); assert_non_null (buf); /* verify defaults from setup function */ rc = Tss2_MU_BYTE_Unmarshal (buf, buf_size, &offset, &yes_no); assert_int_equal (rc, TSS2_RC_SUCCESS); rc = Tss2_MU_TPMS_CAPABILITY_DATA_Unmarshal (buf, buf_size, &offset, &cap_data); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (cap_data.data.tpmProperties.tpmProperty [0].value, UINT8_MAX); offset = TPM_HEADER_SIZE; /* execute function under test */ rc = get_cap_post_process (data->response); assert_int_equal (rc, TSS2_RC_SUCCESS); rc = Tss2_MU_BYTE_Unmarshal (buf, buf_size, &offset, &yes_no); assert_int_equal (rc, TSS2_RC_SUCCESS); rc = Tss2_MU_TPMS_CAPABILITY_DATA_Unmarshal (buf, buf_size, &offset, &cap_data); assert_int_equal (rc, TSS2_RC_SUCCESS); /* verify property was modified by the RM */ assert_int_equal (cap_data.data.tpmProperties.tpmProperty [0].value, UINT32_MAX); } int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (resource_manager_type_test, resource_manager_setup, resource_manager_teardown), cmocka_unit_test_setup_teardown (resource_manager_sink_enqueue_test, resource_manager_setup, resource_manager_teardown), cmocka_unit_test_setup_teardown (resource_manager_process_tpm2_command_success_test, resource_manager_setup, resource_manager_teardown), cmocka_unit_test_setup_teardown (resource_manager_flushsave_context_test, resource_manager_setup, resource_manager_teardown), cmocka_unit_test_setup_teardown (resource_manager_flushsave_context_same_entries_test, resource_manager_setup, resource_manager_teardown), cmocka_unit_test_setup_teardown (resource_manager_flushsave_context_fail_test, resource_manager_setup, resource_manager_teardown), cmocka_unit_test_setup_teardown (resource_manager_virt_to_phys_test, resource_manager_setup_two_transient_handles, resource_manager_teardown), cmocka_unit_test_setup_teardown (resource_manager_load_handles_test, resource_manager_setup_two_transient_handles, resource_manager_teardown), cmocka_unit_test_setup_teardown (resource_manager_getcap_gap_max_test, resource_manager_setup_getcap, resource_manager_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/response-sink_unit.c000066400000000000000000000012071401030142600204330ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include "util.h" #include "response-sink.h" /** * Test to allocate and destroy a ResponseSink. */ static void response_sink_allocate_test (void **state) { ResponseSink *sink; UNUSED_PARAM(state); sink = response_sink_new (); g_object_unref (sink); } int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test (response_sink_allocate_test), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/session-entry_unit.c000066400000000000000000000074401401030142600204620ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include "session-entry.h" #include "util.h" #define CLIENT_ID 1ULL #define TEST_HANDLE 0x03000000 typedef struct { Connection *connection; gint client_fd; HandleMap *handle_map; SessionEntry *session_entry; } test_data_t; /* * Setup function: * Creates a structure to hold test data. * Creates supporting objects for tests: HandleMap, IOStream, Connection. * Creates a SessionEntry (the object under test). */ static int session_entry_setup (void **state) { test_data_t *data = NULL; GIOStream *iostream; data = calloc (1, sizeof (test_data_t)); data->handle_map = handle_map_new (TPM2_HT_TRANSIENT, 100); iostream = create_connection_iostream (&data->client_fd); data->connection = connection_new (iostream, CLIENT_ID, data->handle_map); data->session_entry = session_entry_new (data->connection, TEST_HANDLE); g_object_unref (iostream); *state = data; return 0; } /** * Tear down all of the data from the setup function. We don't have to * free the data buffer (data->buffer) since the Tpm2Command frees it as * part of its finalize function. */ static int session_entry_teardown (void **state) { test_data_t *data = (test_data_t*)*state; g_object_unref (data->connection); g_object_unref (data->handle_map); g_object_unref (data->session_entry); free (data); return 0; } /* * This is a test for memory management / reference counting. The setup * function does exactly that so when we get the Tpm2Command object we just * check to be sure it's a GObject and then we unref it. This test will * probably only fail when run under ASAN if the reference counting is * off. */ static void session_entry_type_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_true (G_IS_OBJECT (data->session_entry)); assert_true (IS_SESSION_ENTRY (data->session_entry)); } static void session_entry_get_context_test (void **state) { test_data_t *data = (test_data_t*)*state; size_buf_t *size_buf; size_buf = session_entry_get_context (data->session_entry); assert_non_null (size_buf); } static void session_entry_get_connection_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *connection; connection = session_entry_get_connection (data->session_entry); assert_true (IS_CONNECTION (connection)); g_object_unref (connection); } static void session_entry_get_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle; handle = session_entry_get_handle (data->session_entry); assert_int_equal (handle, TEST_HANDLE); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (session_entry_type_test, session_entry_setup, session_entry_teardown), cmocka_unit_test_setup_teardown (session_entry_get_context_test, session_entry_setup, session_entry_teardown), cmocka_unit_test_setup_teardown (session_entry_get_connection_test, session_entry_setup, session_entry_teardown), cmocka_unit_test_setup_teardown (session_entry_get_handle_test, session_entry_setup, session_entry_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/session-list_unit.c000066400000000000000000000262571401030142600203030ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "mock-io-stream.h" #include "connection.h" #include "session-entry.h" #include "session-list.h" #include "util.h" #define INSERT_TEST_ID 0x1 typedef struct test_data { SessionList *session_list; } test_data_t; static GIOStream* test_iostream_new (void) { GInputStream *input; GOutputStream *output; GIOStream *iostream; int fds[2]; socketpair (AF_LOCAL, SOCK_STREAM, 0, fds); input = g_unix_input_stream_new (fds[0], TRUE); output = g_unix_output_stream_new (fds[1], TRUE); iostream = mock_io_stream_new (input, output); g_object_unref (input); g_object_unref (output); return iostream; } static Connection* test_connection_new (guint64 id) { Connection *conn = NULL; GIOStream *iostream = NULL; HandleMap *handle_map = NULL; iostream = test_iostream_new (); handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); conn = connection_new (iostream, id, handle_map); g_clear_object (&iostream); g_clear_object (&handle_map); return conn; } /* * Allocate a test_data_t structure to hold test data. * Create a SessionList object for use in testing. * Create a Connection object for use by tests. */ static int session_list_setup (void **state) { test_data_t *data = NULL; data = calloc (1, sizeof (test_data_t)); g_debug ("%s: data 0x%" PRIxPTR, __func__, (uintptr_t)data); data->session_list = session_list_new (SESSION_LIST_MAX_ENTRIES_DEFAULT, SESSION_LIST_MAX_ABANDONED_DEFAULT); *state = data; return 0; } static int session_list_teardown (void **state) { test_data_t *data = (test_data_t*)*state; g_debug ("%s: start", __func__); if (data) { g_debug ("%s: data 0x%" PRIxPTR, __func__, (uintptr_t)data); g_clear_object (&data->session_list); free (data); } g_debug ("%s: end", __func__); return 0; } /* * Simple object type checks. */ static void session_list_type_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_true (G_IS_OBJECT (data->session_list)); assert_true (IS_SESSION_LIST (data->session_list)); } /* Simple test of SessionList insert function */ #define INSERT_TEST_ID 0x1 #define INSERT_TEST_HANDLE (TPM2_HR_TRANSIENT + 1) static void session_list_insert_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *conn = NULL; SessionEntry *entry = NULL; conn = test_connection_new (INSERT_TEST_ID); entry = session_entry_new (conn, INSERT_TEST_HANDLE); assert_true (session_list_insert (data->session_list, entry)); g_clear_object (&conn); g_clear_object (&entry); } /* * Add 3 SessionEntry objects to a SessionList then verify size is reported * correctly. */ #define SIZE_TEST_ID 0x1 #define SIZE_TEST_HANDLE_1 (TPM2_HR_TRANSIENT + 1) #define SIZE_TEST_HANDLE_2 (TPM2_HR_TRANSIENT + 2) #define SIZE_TEST_HANDLE_3 (TPM2_HR_TRANSIENT + 3) static void session_list_size_three_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *conn = NULL; SessionEntry *entry = NULL; conn = test_connection_new (SIZE_TEST_ID); entry = session_entry_new (conn, SIZE_TEST_HANDLE_1); session_list_insert (data->session_list, entry); g_clear_object (&entry); entry = session_entry_new (conn, SIZE_TEST_HANDLE_2); session_list_insert (data->session_list, entry); g_clear_object (&entry); entry = session_entry_new (conn, SIZE_TEST_HANDLE_3); g_clear_object (&conn); session_list_insert (data->session_list, entry); g_clear_object (&entry); assert_int_equal (3, session_list_size (data->session_list)); } /* * Test removal of a SessionEntry using the handle as an identifier. This * relies on the session_list_insert_test to add the SessionEntry required * by this test. */ static void session_list_remove_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; gboolean ret = FALSE; session_list_insert_test (state); ret = session_list_remove_handle (data->session_list, INSERT_TEST_HANDLE); assert_true (ret); assert_int_equal (session_list_size (data->session_list), 0); } /* * Test the error handling logic in the remove_handle function. This test * passes in a handle unknown to the SessionList and so the search for the * associated SessionEntry will fail. We should get FALSE back from the call. */ static void session_list_remove_handle_bad_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_false (session_list_remove_handle (data->session_list, 0x55)); } /* * Test our ability to detect handles for sessions that aren't tracked by the * SessionList while we're abandoning an entry. The code currently evaluates * the handle parameter first so the connection parameter is irrelevant. */ #define ABANDON_HANDLE_CONN_BAD ((Connection*)0x55) #define ABANDON_HANDLE_HANDLE 0x45628376 static void session_list_abandon_handle_bad_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; gboolean ret; ret = session_list_abandon_handle (data->session_list, ABANDON_HANDLE_CONN_BAD, ABANDON_HANDLE_HANDLE); assert_false (ret); } /* */ #define ABANDON_HANDLE_ID 0x1 #define ABANDON_HANDLE_ID_2 0x2 static void session_list_abandon_handle_bad_connection_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *conn = NULL; SessionEntry *entry = NULL; gboolean ret; conn = test_connection_new (ABANDON_HANDLE_ID); entry = session_entry_new (conn, ABANDON_HANDLE_HANDLE); session_list_insert (data->session_list, entry); g_clear_object (&conn); g_clear_object (&entry); conn = test_connection_new (ABANDON_HANDLE_ID_2); ret = session_list_abandon_handle (data->session_list, conn, ABANDON_HANDLE_HANDLE); g_clear_object (&conn); assert_false (ret); } static void session_list_abandon_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *conn = NULL; SessionEntry *entry = NULL; gboolean ret; conn = test_connection_new (ABANDON_HANDLE_ID); entry = session_entry_new (conn, ABANDON_HANDLE_HANDLE); session_list_insert (data->session_list, entry); g_clear_object (&entry); ret = session_list_abandon_handle (data->session_list, conn, ABANDON_HANDLE_HANDLE); g_clear_object (&conn); assert_true (ret); } #define CLAIM_CONNECTION_ID_0 0x0 #define CLAIM_CONNECTION_ID_1 0x1 #define CLAIM_HANDLE 0xdeadbee0 static void session_list_claim_abandoned_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *conn = NULL; SessionEntry *entry = NULL; gboolean ret; conn = test_connection_new (CLAIM_CONNECTION_ID_0); entry = session_entry_new (conn, CLAIM_HANDLE); session_list_insert (data->session_list, entry); ret = session_list_abandon_handle (data->session_list, conn, CLAIM_HANDLE); assert_true (ret); assert_true (session_entry_get_state (entry) == SESSION_ENTRY_SAVED_CLIENT_CLOSED); g_clear_object (&conn); /* claim abandoned handle from different connection */ conn = test_connection_new (CLAIM_CONNECTION_ID_1); ret = session_list_claim (data->session_list, entry, conn); assert_true (ret); g_clear_object (&entry); } static void session_list_claim_saved_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *conn = NULL; SessionEntry *entry = NULL; gboolean ret; conn = test_connection_new (CLAIM_CONNECTION_ID_0); entry = session_entry_new (conn, CLAIM_HANDLE); session_list_insert (data->session_list, entry); session_entry_set_state (entry, SESSION_ENTRY_SAVED_CLIENT); /* claim abandoned handle from different connection */ conn = test_connection_new (CLAIM_CONNECTION_ID_1); ret = session_list_claim (data->session_list, entry, conn); assert_true (ret); assert_true (session_entry_get_state (entry) == SESSION_ENTRY_LOADED); g_clear_object (&entry); } static void session_list_claim_fail_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *conn = NULL; SessionEntry *entry = NULL; gboolean ret; conn = test_connection_new (CLAIM_CONNECTION_ID_0); entry = session_entry_new (conn, CLAIM_HANDLE); ret = session_list_claim (data->session_list, entry, conn); assert_false (ret); g_clear_object (&entry); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (session_list_type_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_insert_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_size_three_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_remove_handle_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_remove_handle_bad_handle_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_abandon_handle_bad_handle_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_abandon_handle_bad_connection_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_abandon_handle_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_claim_abandoned_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_claim_saved_test, session_list_setup, session_list_teardown), cmocka_unit_test_setup_teardown (session_list_claim_fail_test, session_list_setup, session_list_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/tab_unit.c000066400000000000000000000044611401030142600164060ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "source-interface.h" #include "response-sink.h" #include "tcti-echo.h" #include "tab.h" static void tab_allocate_deallocate_test (void **state) { Tcti *tcti; Tab *tab; ResponseSink *sink; sink = response_sink_new (); tcti = TCTI (tcti_echo_new (TCTI_ECHO_MIN_BUF)); tab = tab_new (tcti); source_add_sink (SOURCE (tab), SINK (sink)); g_object_unref (tab); } static void tab_start_stop_test_setup (void **state) { Tcti *tcti; Tab *tab; ResponseSink *sink; sink = response_sink_new (); tcti = TCTI (tcti_echo_new (TCTI_ECHO_MIN_BUF)); tab = tab_new (tcti); source_add_sink (SOURCE (tab), SINK (sink)); *state = tab; } static void tab_start_stop_test_teardown (void **state) { Tab *tab = *state; tab_cancel (tab); tab_join (tab); g_object_unref (tab); } static void tab_start_stop_test (void **state) { Tab *tab = *state; tab_start (tab); } static void tab_add_no_remove_test_setup (void **state) { Tcti *tcti; Tab *tab = *state; ResponseSink *sink; sink = response_sink_new (); tcti = TCTI (tcti_echo_new (TCTI_ECHO_MIN_BUF)); tab = tab_new (tcti); source_add_sink (SOURCE (tab), SINK (sink)); tab_start (tab); *state = tab; } /* This test adds a message to the tab but doesn't remove it. * The tab is expected to free this message as part of its destruction. */ static void tab_add_no_remove_test (void **state) { DataMessage *msg = NULL; Tab *tab = *state; msg = data_message_new (NULL, NULL, 0); tab_enqueue (tab, G_OBJECT (msg)); g_object_unref (msg); } gint main (void) { const UnitTest tests[] = { unit_test (tab_allocate_deallocate_test), unit_test_setup_teardown (tab_start_stop_test, tab_start_stop_test_setup, tab_start_stop_test_teardown), unit_test_setup_teardown (tab_add_no_remove_test, tab_add_no_remove_test_setup, tab_start_stop_test_teardown), }; return run_tests (tests); } tpm2-abrmd-2.4.0/test/tabrmd-init_unit.c000066400000000000000000000242631401030142600200540ustar00rootroot00000000000000/* * SPDX-License-Identifier: BSD-2-Clause * Copyright (c) 2019, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "tabrmd-init.h" #include "tabrmd-options.h" #include "tcti-mock.h" #include "tcti.h" #include "util.h" #define ID_IPCFRONT (IpcFrontend*)0x10293847 #define ID_GMAIN (GMainLoop*)0x64738291 #define ID_RANDOM (Random*)0x65132784 /* * This flag is set to TRUE by the mock function for g_main_loop_quit. * This is a hack to simplify state / mem management when we want to * test whether or not the gmain loop is killed by code under test. */ static gboolean gmain_quit = FALSE; static int test_setup (void **state) { UNUSED_PARAM (state); gmain_quit = FALSE; return 0; } static TSS2_TCTI_CONTEXT * tcti_ctx_from_state (void **state) { assert_non_null (*state); return (TSS2_TCTI_CONTEXT *)(*state); } static int test_setup_tcti (void **state) { test_setup (state); TSS2_TCTI_CONTEXT *tcti_ctx = tcti_mock_init_full (); *state = tcti_ctx; return 0; } static int test_teardown_tcti (void **state) { TSS2_TCTI_CONTEXT *tcti_ctx = tcti_ctx_from_state (state); free (tcti_ctx); return 0; } void __wrap_g_main_loop_quit (GMainLoop *loop) { UNUSED_PARAM (loop); gmain_quit = mock_type (gboolean); } gboolean __wrap_g_main_loop_is_running (GMainLoop *loop) { UNUSED_PARAM (loop); return mock_type (gboolean); } static void on_ipc_frontend_disconnect_test (void **state) { UNUSED_PARAM (state); IpcFrontend *ipc_frontend = ID_IPCFRONT; gmain_data_t data = { .loop = ID_GMAIN, }; will_return (__wrap_g_main_loop_is_running, TRUE); will_return (__wrap_g_main_loop_quit, TRUE); on_ipc_frontend_disconnect (ipc_frontend, &data); assert_true (gmain_quit); } static gmain_data_t data; guint __wrap_g_unix_signal_add (gint signum, GSourceFunc handler, gpointer user_data) { UNUSED_PARAM (signum); UNUSED_PARAM (handler); UNUSED_PARAM (user_data); return mock_type (guint); } static int init_thread_func_setup (void **state) { UNUSED_PARAM (state); gmain_data_t data_src = { .options = TABRMD_OPTIONS_INIT_DEFAULT, }; data_src.options.flush_all = TRUE; memcpy (&data, &data_src, sizeof (data)); g_mutex_init (&data.init_mutex); return 0; } static void init_thread_func_signal_add_fail (void **state) { UNUSED_PARAM (state); will_return (__wrap_g_unix_signal_add, 0); assert_int_equal (init_thread_func (&data), EX_OSERR); } int __wrap_random_seed_from_file (Random *random, const char *fname) { UNUSED_PARAM (random); UNUSED_PARAM (fname); return mock_type (int); } int __wrap_random_get_bytes (Random *random, uint8_t dest[], size_t count) { UNUSED_PARAM (random); UNUSED_PARAM (count); uint8_t *src = mock_type (uint8_t*); size_t size = mock_type (size_t); assert_true (size <= count); memcpy (dest, &src, size); return size; } static void init_thread_func_random_fail (void **state) { UNUSED_PARAM (state); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_random_seed_from_file, 1); assert_int_equal (init_thread_func (&data), EX_OSERR); } /* ipc_frontend_connect */ void __wrap_ipc_frontend_connect (IpcFrontend *self, GMutex *mutex) { UNUSED_PARAM (self); UNUSED_PARAM (mutex); return; } TSS2_RC __wrap_Tss2_TctiLdr_Initialize (const char* conf, TSS2_TCTI_CONTEXT **tcti_ctx) { UNUSED_PARAM (conf); *tcti_ctx = mock_type (TSS2_TCTI_CONTEXT*); return mock_type (TSS2_RC); } static void init_thread_func_tcti_factory_fail (void **state) { UNUSED_PARAM (state); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_random_seed_from_file, 0); will_return (__wrap_Tss2_TctiLdr_Initialize, NULL); will_return (__wrap_Tss2_TctiLdr_Initialize, TSS2_TCTI_RC_IO_ERROR); assert_int_equal (init_thread_func (&data), EX_IOERR); } TSS2_RC __wrap_tpm2_init_tpm (Tpm2 *tpm2) { UNUSED_PARAM (tpm2); printf ("ftw\n"); return mock_type (TSS2_RC); } static void init_thread_func_tpm2_init_fail (void **state) { UNUSED_PARAM (state); TSS2_TCTI_CONTEXT *tcti_ctx = tcti_ctx_from_state (state); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_random_seed_from_file, 0); will_return (__wrap_Tss2_TctiLdr_Initialize, tcti_ctx); will_return (__wrap_Tss2_TctiLdr_Initialize, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_init_tpm, EX_UNAVAILABLE); assert_int_equal (init_thread_func (&data), EX_UNAVAILABLE); } gint __wrap_command_attrs_init_tpm (CommandAttrs *attrs, Tpm2 *tpm2) { UNUSED_PARAM (attrs); UNUSED_PARAM (tpm2); return mock_type (gint); } void __wrap_tpm2_flush_all_context (Tpm2 *tpm2) { UNUSED_PARAM (tpm2); return; } static void init_thread_func_cmdattrs_fail (void **state) { UNUSED_PARAM (state); TSS2_TCTI_CONTEXT *tcti_ctx = tcti_ctx_from_state (state); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_random_seed_from_file, 0); will_return (__wrap_Tss2_TctiLdr_Initialize, tcti_ctx); will_return (__wrap_Tss2_TctiLdr_Initialize, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_init_tpm, TSS2_RC_SUCCESS); will_return (__wrap_command_attrs_init_tpm, EX_UNAVAILABLE); assert_int_equal (init_thread_func (&data), EX_UNAVAILABLE); } gint __wrap_thread_start (Thread *thread) { UNUSED_PARAM (thread); return mock_type (gint); } static void init_thread_func_cmdsrc_fail (void **state) { UNUSED_PARAM (state); TSS2_TCTI_CONTEXT *tcti_ctx = tcti_ctx_from_state (state); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_random_seed_from_file, 0); will_return (__wrap_Tss2_TctiLdr_Initialize, tcti_ctx); will_return (__wrap_Tss2_TctiLdr_Initialize, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_init_tpm, TSS2_RC_SUCCESS); will_return (__wrap_command_attrs_init_tpm, 0); will_return (__wrap_thread_start, 1); will_return (__wrap_g_main_loop_is_running, FALSE); assert_int_equal (init_thread_func (&data), EX_OSERR); } static void init_thread_func_resmgr_fail (void **state) { UNUSED_PARAM (state); TSS2_TCTI_CONTEXT *tcti_ctx = tcti_ctx_from_state (state); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_random_seed_from_file, 0); will_return (__wrap_Tss2_TctiLdr_Initialize, tcti_ctx); will_return (__wrap_Tss2_TctiLdr_Initialize, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_init_tpm, TSS2_RC_SUCCESS); will_return (__wrap_command_attrs_init_tpm, 0); will_return (__wrap_thread_start, 0); will_return (__wrap_thread_start, 1); will_return (__wrap_g_main_loop_is_running, FALSE); assert_int_equal (init_thread_func (&data), EX_OSERR); } static void init_thread_func_respsnk_fail (void **state) { UNUSED_PARAM (state); TSS2_TCTI_CONTEXT *tcti_ctx = tcti_ctx_from_state (state); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_random_seed_from_file, 0); will_return (__wrap_Tss2_TctiLdr_Initialize, tcti_ctx); will_return (__wrap_Tss2_TctiLdr_Initialize, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_init_tpm, TSS2_RC_SUCCESS); will_return (__wrap_command_attrs_init_tpm, 0); will_return (__wrap_thread_start, 0); will_return (__wrap_thread_start, 0); will_return (__wrap_thread_start, 1); will_return (__wrap_g_main_loop_is_running, FALSE); assert_int_equal (init_thread_func (&data), EX_OSERR); } static void init_thread_func_success (void **state) { UNUSED_PARAM (state); TSS2_TCTI_CONTEXT *tcti_ctx = tcti_ctx_from_state (state); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_g_unix_signal_add, 1); will_return (__wrap_random_seed_from_file, 0); will_return (__wrap_Tss2_TctiLdr_Initialize, tcti_ctx); will_return (__wrap_Tss2_TctiLdr_Initialize, TSS2_RC_SUCCESS); will_return (__wrap_tpm2_init_tpm, TSS2_RC_SUCCESS); will_return (__wrap_command_attrs_init_tpm, 0); will_return (__wrap_thread_start, 0); will_return (__wrap_thread_start, 0); will_return (__wrap_thread_start, 0); assert_int_equal (init_thread_func (&data), 0); } int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup (on_ipc_frontend_disconnect_test, test_setup), cmocka_unit_test_setup (init_thread_func_signal_add_fail, init_thread_func_setup), cmocka_unit_test_setup (init_thread_func_random_fail, init_thread_func_setup), cmocka_unit_test_setup (init_thread_func_tcti_factory_fail, init_thread_func_setup), cmocka_unit_test_setup_teardown (init_thread_func_tpm2_init_fail, test_setup_tcti, test_teardown_tcti), cmocka_unit_test_setup_teardown (init_thread_func_cmdattrs_fail, test_setup_tcti, test_teardown_tcti), cmocka_unit_test_setup_teardown (init_thread_func_cmdsrc_fail, test_setup_tcti, test_teardown_tcti), cmocka_unit_test_setup_teardown (init_thread_func_resmgr_fail, test_setup_tcti, test_teardown_tcti), cmocka_unit_test_setup_teardown (init_thread_func_respsnk_fail, test_setup_tcti, test_teardown_tcti), cmocka_unit_test_setup_teardown (init_thread_func_success, test_setup_tcti, test_teardown_tcti), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/tabrmd-options_unit.c000066400000000000000000000143251401030142600206020ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include #include "tabrmd-options.h" #include "util.h" GOptionContext* __wrap_g_option_context_new (const gchar *parameter_string) { UNUSED_PARAM (parameter_string); g_warning ("%s", __func__); return mock_type (GOptionContext*); } void __wrap_g_option_context_add_main_entries ( GOptionContext *context, const GOptionEntry *entries, const gchar *translation_domain) { UNUSED_PARAM (context); UNUSED_PARAM (entries); UNUSED_PARAM (translation_domain); char *long_name = mock_type (char*); size_t i; for (i = 0; entries [i].long_name != NULL; ++i) { if (strcmp (long_name, entries [i].long_name) == 0) { if (strcmp (long_name, "max-connections") == 0 || strcmp (long_name, "max-sessions") == 0 || strcmp (long_name, "max-transients") == 0) { *(guint*)entries [i].arg_data = mock_type (guint); } if (strcmp (long_name, "tcti") == 0) { *(char**)entries [i].arg_data = g_strdup (mock_type (char*)); } } } g_warning ("%s", __func__); return; } gboolean __wrap_g_option_context_parse (GOptionContext *context, gint *argc, gchar ***argv, GError **error) { UNUSED_PARAM (context); UNUSED_PARAM (argc); UNUSED_PARAM (argv); g_warning ("%s", __func__); *error = mock_type (GError*); return mock_type (gboolean); } static void tcti_conf_parse_opts_parse_fail (void **state) { UNUSED_PARAM (state); tabrmd_options_t options = TABRMD_OPTIONS_INIT_DEFAULT; GOptionContext *ctx = NULL; int argc = 0; char **argv = NULL; GError error = { .message = "foo", }; will_return (__wrap_g_option_context_new, ctx); will_return (__wrap_g_option_context_add_main_entries, "foo"); will_return (__wrap_g_option_context_parse, &error); will_return (__wrap_g_option_context_parse, FALSE); assert_false (parse_opts (argc, argv, &options)); } gint __wrap_set_logger (gchar *name) { UNUSED_PARAM (name); return mock_type (gint); } static void tcti_conf_parse_opts_logger_fail (void **state) { UNUSED_PARAM (state); tabrmd_options_t options = TABRMD_OPTIONS_INIT_DEFAULT; GOptionContext *ctx = NULL; int argc = 0; char **argv = NULL; GError error = { .message = "foo", }; will_return (__wrap_g_option_context_new, ctx); will_return (__wrap_g_option_context_add_main_entries, "foo"); will_return (__wrap_g_option_context_parse, &error); will_return (__wrap_g_option_context_parse, TRUE); will_return (__wrap_set_logger, -1); assert_false (parse_opts (argc, argv, &options)); } static void tcti_conf_parse_opts_max_connections_fail (void **state) { UNUSED_PARAM (state); tabrmd_options_t options = TABRMD_OPTIONS_INIT_DEFAULT; GOptionContext *ctx = NULL; int argc = 0; char **argv = NULL; GError error = { .message = "foo", }; will_return (__wrap_g_option_context_new, ctx); will_return (__wrap_g_option_context_add_main_entries, "max-connections"); will_return (__wrap_g_option_context_add_main_entries, 0); will_return (__wrap_g_option_context_parse, &error); will_return (__wrap_g_option_context_parse, TRUE); will_return (__wrap_set_logger, 0); assert_false (parse_opts (argc, argv, &options)); } static void tcti_conf_parse_opts_max_sessions_fail (void **state) { UNUSED_PARAM (state); tabrmd_options_t options = TABRMD_OPTIONS_INIT_DEFAULT; GOptionContext *ctx = NULL; int argc = 0; char **argv = NULL; GError error = { .message = "foo", }; will_return (__wrap_g_option_context_new, ctx); will_return (__wrap_g_option_context_add_main_entries, "max-sessions"); will_return (__wrap_g_option_context_add_main_entries, 0); will_return (__wrap_g_option_context_parse, &error); will_return (__wrap_g_option_context_parse, TRUE); will_return (__wrap_set_logger, 0); assert_false (parse_opts (argc, argv, &options)); } static void tcti_conf_parse_opts_max_transient_fail (void **state) { UNUSED_PARAM (state); tabrmd_options_t options = TABRMD_OPTIONS_INIT_DEFAULT; GOptionContext *ctx = NULL; int argc = 0; char **argv = NULL; GError error = { .message = "foo", }; will_return (__wrap_g_option_context_new, ctx); will_return (__wrap_g_option_context_add_main_entries, "max-transients"); will_return (__wrap_g_option_context_add_main_entries, 0); will_return (__wrap_g_option_context_parse, &error); will_return (__wrap_g_option_context_parse, TRUE); will_return (__wrap_set_logger, 0); assert_false (parse_opts (argc, argv, &options)); } void __wrap_g_option_context_free (GOptionContext *context) { UNUSED_PARAM (context); g_warning ("%s", __func__); return; } static void tcti_conf_parse_opts_success (void **state) { UNUSED_PARAM (state); tabrmd_options_t options = TABRMD_OPTIONS_INIT_DEFAULT; GOptionContext *ctx = NULL; int argc = 0; char **argv = NULL; GError error = { .message = "foo", }; will_return (__wrap_g_option_context_new, ctx); will_return (__wrap_g_option_context_add_main_entries, "tcti"); will_return (__wrap_g_option_context_add_main_entries, "foo"); will_return (__wrap_g_option_context_parse, &error); will_return (__wrap_g_option_context_parse, TRUE); will_return (__wrap_set_logger, 0); assert_true (parse_opts (argc, argv, &options)); tabrmd_options_free (&options); } int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test (tcti_conf_parse_opts_parse_fail), cmocka_unit_test (tcti_conf_parse_opts_logger_fail), cmocka_unit_test (tcti_conf_parse_opts_max_connections_fail), cmocka_unit_test (tcti_conf_parse_opts_max_sessions_fail), cmocka_unit_test (tcti_conf_parse_opts_max_transient_fail), cmocka_unit_test (tcti_conf_parse_opts_success), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/tcti-factory_unit.c000066400000000000000000000045071401030142600202510ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include "tcti-mock.h" #include "tcti-factory.h" #include "util.h" #define NAME_CONF "foo:bar" TSS2_TCTI_CONTEXT_COMMON_V2 v2_ctx = { 0, }; TSS2_RC __wrap_Tss2_TctiLdr_Initialize (char *name_conf, TSS2_TCTI_CONTEXT **tcti_ctx) { TSS2_RC rc = mock_type (TSS2_RC); UNUSED_PARAM (name_conf); assert_non_null (tcti_ctx); if (rc == TSS2_RC_SUCCESS) *tcti_ctx = mock_type (TSS2_TCTI_CONTEXT*); return rc; } void __wrap_Tss2_TctiLdr_Finalize (TSS2_TCTI_CONTEXT **tcti_ctx) { UNUSED_PARAM (tcti_ctx); } static int tcti_factory_setup (void **state) { *state = tcti_factory_new (NAME_CONF); return 0; } static int tcti_factory_teardown (void **state) { TctiFactory *factory = TCTI_FACTORY (*state); g_clear_object (&factory); return 0; } static void tcti_factory_type_test (void **state) { assert_true (IS_TCTI_FACTORY (*state)); } static void tcti_factory_create_fail (void **state) { TctiFactory *factory = TCTI_FACTORY (*state); Tcti *tcti; will_return (__wrap_Tss2_TctiLdr_Initialize, TSS2_TCTI_RC_BAD_VALUE); tcti = tcti_factory_create (factory); assert_null (tcti); } static void tcti_factory_create_success (void **state) { TctiFactory *factory = TCTI_FACTORY (*state); Tcti *tcti; will_return (__wrap_Tss2_TctiLdr_Initialize, TSS2_RC_SUCCESS); will_return (__wrap_Tss2_TctiLdr_Initialize, &v2_ctx); tcti = tcti_factory_create (factory); assert_non_null (tcti); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (tcti_factory_type_test, tcti_factory_setup, tcti_factory_teardown), cmocka_unit_test_setup_teardown (tcti_factory_create_fail, tcti_factory_setup, tcti_factory_teardown), cmocka_unit_test_setup_teardown (tcti_factory_create_success, tcti_factory_setup, tcti_factory_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/tcti-mock.c000066400000000000000000000074671401030142600165040ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include "tcti-mock.h" #include "util.h" /* * This function assumes that the unit test invoking this function (or * causing it to be invoked) has added the following types in the * specified order: * - TSS2_RC - response code */ TSS2_RC tcti_mock_transmit (TSS2_TCTI_CONTEXT *context, size_t size, uint8_t const *command) { TCTI_MOCK_CONTEXT *tcti_mock = (TCTI_MOCK_CONTEXT*)context; TSS2_RC rc = mock_type (TSS2_RC); UNUSED_PARAM (size); UNUSED_PARAM (command); if (rc != TSS2_RC_SUCCESS) { return rc; } if (context == NULL) { return TSS2_TCTI_RC_BAD_CONTEXT; } if (TSS2_TCTI_VERSION (context) < 1) { return TSS2_TCTI_RC_ABI_MISMATCH; } if (tcti_mock->state != SEND) { return TSS2_TCTI_RC_BAD_SEQUENCE; } tcti_mock->state = RECEIVE; return rc; } /* * This function assumes that the unit test invoking this function (or * causing it to be invoked) has added the following types in the * specified order: * - uint8_t* - pointer to response buffer * - size_t - size of buffer * - TSS2_RC - response code */ TSS2_RC tcti_mock_receive (TSS2_TCTI_CONTEXT *context, size_t *size, uint8_t *response, int32_t timeout) { TCTI_MOCK_CONTEXT *tcti_mock = (TCTI_MOCK_CONTEXT*)context; uint8_t *buf; size_t buf_size; TSS2_RC rc; UNUSED_PARAM (timeout); buf = mock_type (uint8_t*); buf_size = mock_type (size_t); rc = mock_type (TSS2_RC); if (rc != TSS2_RC_SUCCESS) { return rc; } if (context == NULL) { return TSS2_TCTI_RC_BAD_CONTEXT; } if (TSS2_TCTI_VERSION (context) < 1) { return TSS2_TCTI_RC_ABI_MISMATCH; } if (tcti_mock->state != RECEIVE) { return TSS2_TCTI_RC_BAD_SEQUENCE; } if (size == NULL) { return TSS2_TCTI_RC_BAD_VALUE; } if (response == NULL) { *size = sizeof (TPM2_MAX_COMMAND_SIZE); return TSS2_RC_SUCCESS; } if (*size > TPM2_MAX_COMMAND_SIZE) { return TSS2_TCTI_RC_INSUFFICIENT_BUFFER; } if (buf != NULL) { memcpy (response, buf, buf_size); *size = buf_size; } tcti_mock->state = SEND; return rc; } TSS2_RC Tss2_Tcti_Mock_Init (TSS2_TCTI_CONTEXT *context, size_t *size, const char *conf) { TCTI_MOCK_CONTEXT *tcti_mock = (TCTI_MOCK_CONTEXT*)context; UNUSED_PARAM (conf); if (context == NULL && size != NULL) { *size = sizeof (TCTI_MOCK_CONTEXT); return TSS2_RC_SUCCESS; } if (size == NULL) { return TSS2_TCTI_RC_BAD_VALUE; } memset (context, 0, sizeof (TCTI_MOCK_CONTEXT)); TSS2_TCTI_MAGIC (tcti_mock) = 0x1; TSS2_TCTI_VERSION (tcti_mock) = 2; TSS2_TCTI_TRANSMIT (tcti_mock) = tcti_mock_transmit; TSS2_TCTI_RECEIVE (tcti_mock) = tcti_mock_receive; tcti_mock->state = SEND; return TSS2_RC_SUCCESS; } /* * non-standard init function to automate the TCTI init protocol. */ TSS2_TCTI_CONTEXT* tcti_mock_init_full (void) { TSS2_RC rc; TSS2_TCTI_CONTEXT *context; size_t size = 0; rc = Tss2_Tcti_Mock_Init (NULL, &size, NULL); if (rc != TSS2_RC_SUCCESS) { g_critical ("first Tss2_Tcti_Mock_Init failed with RC 0x%" PRIx32, rc); return NULL; } context = malloc (size); rc = Tss2_Tcti_Mock_Init (context, &size, NULL); if (rc != TSS2_RC_SUCCESS) { g_critical ("second Tss2_Tcti_Mock_Init failed with RC 0x%" PRIx32, rc); return NULL; } return context; } tpm2-abrmd-2.4.0/test/tcti-mock.h000066400000000000000000000012641401030142600164760ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ #ifndef TCTI_MOCK_H #define TCTI_MOCK_H #include typedef enum { STATE_0, SEND, RECEIVE, N_STATE, } TCTI_STATE; /* * Normally this structure would not be exposed in a TCTI's header. For the * purposes of testing we need access to this structure in order to setup * and verify the results of tests. */ typedef struct { TSS2_TCTI_CONTEXT_COMMON_V2 v2; TCTI_STATE state; } TCTI_MOCK_CONTEXT; TSS2_RC Tss2_Tcti_Mock_Init (TSS2_TCTI_CONTEXT *context, size_t *size, const char *conf); TSS2_TCTI_CONTEXT* tcti_mock_init_full (void); #endif /* TSS2_TCTI_TABD_H */ tpm2-abrmd-2.4.0/test/tcti-tabrmd-receive_unit.c000066400000000000000000000640051401030142600214720ustar00rootroot00000000000000/* * SPDX-License-Identifier: BSD-2-Clause * Copyright (c) 2019, Intel Corporation */ #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #else #include #endif #include #include #include #include "tcti-tabrmd-priv.h" #include "mock-funcs.h" /* * This tests the tcti_tabrmd_poll function, ensuring that it returns the * expected response code for the POLIN event. */ static void tcti_tabrmd_poll_fd_ready_pollin (void **state) { UNUSED_PARAM (state); int ret; will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); ret = tcti_tabrmd_poll (TEST_FD, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (ret, 0); } /* * This tests the tcti_tabrmd_poll function, ensuring that it returns the * expected response code for the POLLPRI event. */ static void tcti_tabrmd_poll_fd_ready_pollpri (void **state) { UNUSED_PARAM (state); int ret; will_return (__wrap_poll, POLLPRI); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); ret = tcti_tabrmd_poll (TEST_FD, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (ret, 0); } /* * This tests the tcti_tabrmd_poll function, ensuring that it returns the * expected response code for the POLLRDHUP event. */ #if defined(__FreeBSD__) #ifndef POLLRDHUP #define POLLRDHUP 0x0 #endif #endif static void tcti_tabrmd_poll_fd_ready_pollrdhup (void **state) { UNUSED_PARAM (state); int ret; will_return (__wrap_poll, POLLRDHUP); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); ret = tcti_tabrmd_poll (TEST_FD, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (ret, 0); } /* * This tests the tcti_tabrmd_poll function, ensuring that it returns the * expected response code when a timeout occurs. */ static void tcti_tabrmd_poll_timeout (void **state) { UNUSED_PARAM (state); int ret; will_return (__wrap_poll, 0); will_return (__wrap_poll, 0); will_return (__wrap_poll, 0); ret = tcti_tabrmd_poll (TEST_FD, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (ret, -1); } /* * This tests the tcti_tabrmd_poll function, ensuring that it returns the * expected response when an error occurs. */ static void tcti_tabrmd_poll_error (void **state) { UNUSED_PARAM (state); int ret; will_return (__wrap_poll, 0); will_return (__wrap_poll, EINVAL); will_return (__wrap_poll, -1); ret = tcti_tabrmd_poll (TEST_FD, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (ret, EINVAL); } /* * This is the setup function used to create a skeletal / minimal context * for testing purposes. */ static int tcti_tabrmd_setup (void **state) { TSS2_TCTI_TABRMD_CONTEXT *tcti_ctx; TSS2_TCTI_CONTEXT_COMMON_V1 *common_ctx; tcti_ctx = calloc (1, sizeof (TSS2_TCTI_TABRMD_CONTEXT)); tcti_ctx->state = TABRMD_STATE_RECEIVE; tcti_ctx->sock_connect = TEST_CONNECTION; tcti_ctx->index = 0; tcti_ctx->state = TABRMD_STATE_TRANSMIT; common_ctx = (TSS2_TCTI_CONTEXT_COMMON_V1*)tcti_ctx; common_ctx->magic = TSS2_TCTI_TABRMD_MAGIC; common_ctx->version = TSS2_TCTI_TABRMD_VERSION; *state = tcti_ctx; return 0; } /* * This is the setup function used to create a skeletal / minimal context * for testing the 'receive' function from the TCTI. */ static int tcti_tabrmd_receive_setup (void **state) { TSS2_TCTI_TABRMD_CONTEXT *tcti_ctx; tcti_tabrmd_setup (state); tcti_ctx = (TSS2_TCTI_TABRMD_CONTEXT*)*state; tcti_ctx->state = TABRMD_STATE_RECEIVE; *state = tcti_ctx; return 0; } /* * This is a teardown function to deallocate / cleanup all resources * associated with these tests. */ static int tcti_tabrmd_teardown (void **state) { free (*state); return 0; } /* * This test ensures that a call to tcti_tabrmd_read that causes poll to * timeout will return the appropriate RC. */ static void tcti_tabrmd_read_poll_timeout (void **state) { TSS2_RC rc; uint8_t resp [TPM2_MAX_RESPONSE_SIZE] = { 0, }; size_t resp_size = sizeof (resp); uint32_t timeout = TSS2_TCTI_TIMEOUT_BLOCK; TSS2_TCTI_TABRMD_CONTEXT *tcti_ctx = (TSS2_TCTI_TABRMD_CONTEXT*)*state; will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll, will return 0 indicating timeout */ will_return (__wrap_poll, 0); will_return (__wrap_poll, 0); will_return (__wrap_poll, 0); rc = tcti_tabrmd_read (tcti_ctx, resp, resp_size, timeout); assert_int_equal (rc, TSS2_TCTI_RC_TRY_AGAIN); } /* * This test ensures that a call to tcti_tabrmd_read that causes poll to * fail / return an error that it will return the appropriate RC. */ static void tcti_tabrmd_read_poll_fail (void **state) { TSS2_RC rc; uint8_t resp [TPM2_MAX_RESPONSE_SIZE] = { 0, }; size_t resp_size = sizeof (resp); uint32_t timeout = TSS2_TCTI_TIMEOUT_BLOCK; TSS2_TCTI_TABRMD_CONTEXT *tcti_ctx = (TSS2_TCTI_TABRMD_CONTEXT*)*state; will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); will_return (__wrap_poll, 0); will_return (__wrap_poll, EINVAL); will_return (__wrap_poll, -1); rc = tcti_tabrmd_read (tcti_ctx, resp, resp_size, timeout); assert_int_equal (rc, TSS2_TCTI_RC_GENERAL_FAILURE); } /* * This test ensures that a call to tcti_tabrmd_read that causes * g_input_stream_read to return EOF will return the appropriate RC. */ static void tcti_tabrmd_read_eof (void **state) { int ret; uint8_t resp [TPM2_MAX_RESPONSE_SIZE] = { 0, }; size_t resp_size = sizeof (resp); uint32_t timeout = TSS2_TCTI_TIMEOUT_BLOCK; TSS2_TCTI_TABRMD_CONTEXT *tcti_ctx = (TSS2_TCTI_TABRMD_CONTEXT*)*state; /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to extract GIStream */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); /* cause g_input_stream_read to return 0 indicating EOF */ will_return (__wrap_g_input_stream_read, 0); ret = tcti_tabrmd_read (tcti_ctx, resp, resp_size, timeout); assert_int_equal (ret, TSS2_TCTI_RC_NO_CONNECTION); } /* * This test ensures that a call to tcti_tabrmd_read that causes * g_input_stream_read to indicate that it would block, returns the * appropriate RC. */ static void tcti_tabrmd_read_block_error (void **state) { int ret; uint8_t resp [TPM2_MAX_RESPONSE_SIZE] = { 0, }; size_t resp_size = sizeof (resp); uint32_t timeout = TSS2_TCTI_TIMEOUT_BLOCK; TSS2_TCTI_TABRMD_CONTEXT *tcti_ctx = (TSS2_TCTI_TABRMD_CONTEXT*)*state; GError* error; /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to extract GIStream & read data */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); error = g_error_new (1, G_IO_ERROR_WOULD_BLOCK, __func__); will_return (__wrap_g_input_stream_read, -1); will_return (__wrap_g_input_stream_read, error); ret = tcti_tabrmd_read (tcti_ctx, resp, resp_size, timeout); assert_int_equal (ret, TSS2_TCTI_RC_TRY_AGAIN); } /* * This test forces the call to 'g_input_stream_read' to read fewer bytes * than requested by the caller (the 'tcti_tabrmd_read' in this case). This * is a "short read" and should return an RC telling the caller to retry. */ static void tcti_tabrmd_read_short (void **state) { int ret; uint8_t resp [TPM2_MAX_RESPONSE_SIZE] = { 0, }; size_t resp_size = sizeof (resp), read_size = resp_size / 2; uint32_t timeout = TSS2_TCTI_TIMEOUT_BLOCK; TSS2_TCTI_TABRMD_CONTEXT *tcti_ctx = (TSS2_TCTI_TABRMD_CONTEXT*)*state; uint8_t buf [sizeof (resp)] = { 0, }; /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to extract GIStream & read data */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); will_return (__wrap_g_input_stream_read, read_size); will_return (__wrap_g_input_stream_read, buf); ret = tcti_tabrmd_read (tcti_ctx, resp, resp_size, timeout); assert_int_equal (ret, TSS2_TCTI_RC_TRY_AGAIN); } static void tcti_tabrmd_read_success (void **state) { int ret; uint8_t resp [TPM2_MAX_RESPONSE_SIZE] = { 0, }; uint8_t buf [TPM2_MAX_RESPONSE_SIZE] = { 0, }; size_t resp_size = sizeof (resp); uint32_t timeout = TSS2_TCTI_TIMEOUT_BLOCK; TSS2_TCTI_TABRMD_CONTEXT *tcti_ctx = (TSS2_TCTI_TABRMD_CONTEXT*)*state; /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* mock stack required to extract GIStream & read data */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); will_return (__wrap_g_input_stream_read, resp_size); will_return (__wrap_g_input_stream_read, buf); ret = tcti_tabrmd_read (tcti_ctx, resp, resp_size, timeout); assert_int_equal (ret, TSS2_RC_SUCCESS); } static void tcti_tabrmd_receive_null_context (void **state) { TSS2_RC rc; UNUSED_PARAM(state); rc = tss2_tcti_tabrmd_receive (NULL, NULL, NULL, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_BAD_REFERENCE); } /* * This test ensures that when passed a non-zero value in the 'size' param * and a NULL response buffer the receive function will return the * appropriate RC (this seems overly restrictive). */ static void tcti_tabrmd_receive_null_response (void **state) { TSS2_RC rc; UNUSED_PARAM(state); TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)1; size_t size = 1; rc = tss2_tcti_tabrmd_receive (ctx, &size, NULL, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); } /* * Test that required checks are done on the MAGIC value in the context * structure and that the appropriate RC is returned. */ static void tcti_tabrmd_receive_bad_magic (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; TSS2_TCTI_CONTEXT_COMMON_V1 *common_ctx = (TSS2_TCTI_CONTEXT_COMMON_V1*)*state; uint8_t buf[1] = { 0, }; size_t size = sizeof (buf); common_ctx->magic = 0; rc = tss2_tcti_tabrmd_receive (ctx, &size, buf, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT); } /* Same for the version field. */ static void tcti_tabrmd_receive_bad_version (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; TSS2_TCTI_CONTEXT_COMMON_V1 *common_ctx = (TSS2_TCTI_CONTEXT_COMMON_V1*)*state; uint8_t buf[1] = { 0, }; size_t size = sizeof (buf); common_ctx->version = 0; rc = tss2_tcti_tabrmd_receive (ctx, &size, buf, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT); } /* * Test to be sure the 'receive' function is returning the right RC when * the context is in the wrong state. */ static void tcti_tabrmd_receive_bad_state (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; uint8_t buf[1] = { 0, }; size_t size = sizeof (buf); rc = tss2_tcti_tabrmd_receive (ctx, &size, buf, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_BAD_SEQUENCE); } /* * Test that the 'receive' function returns the right RC when passed a * invalid timeout value. */ static void tcti_tabrmd_receive_bad_timeout (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; uint8_t buf[1] = { 0, }; size_t size = sizeof (buf); rc = tss2_tcti_tabrmd_receive (ctx, &size, buf, -2); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); } /* * Test that the 'receive' funciton returns the rght RC when passed a * buffer that is too small. */ static void tcti_tabrmd_receive_size_lt_header (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; uint8_t buf[1] = { 0, }; size_t size = sizeof (buf); rc = tss2_tcti_tabrmd_receive (ctx, &size, buf, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_INSUFFICIENT_BUFFER); } /* * Test that the 'receive' function handles errors returned by 'poll' * correctly. */ static void tcti_tabrmd_receive_header_fail (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; size_t size = 0; /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll, will return 0 indicating timeout */ will_return (__wrap_poll, 0); will_return (__wrap_poll, EINVAL); will_return (__wrap_poll, -1); rc = tss2_tcti_tabrmd_receive (ctx, &size, NULL, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_GENERAL_FAILURE); } /* * This test causes the header to be read successfully, but the size field * indicates a size < TPM_HEADER_SIZE which makes it impossible for us to * get the rest of the buffer. */ static void tcti_tabrmd_receive_header_lt_expected (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; uint8_t buf [TPM_HEADER_SIZE] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x01, /* invalid size < TPM_HEADER_SIZE */ 0x00, 0x00, 0x00, 0x00, }; size_t size = 0; /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to get 10 bytes back from the GInputStream */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); will_return (__wrap_g_input_stream_read, TPM_HEADER_SIZE); will_return (__wrap_g_input_stream_read, buf); rc = tss2_tcti_tabrmd_receive (ctx, &size, NULL, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_MALFORMED_RESPONSE); } /* * This test covers a use case where the caller provides a NULL buffer to * hold the response and a zero'd size parameter. This signals to the * receive function that the caller wants to know how much space the * response will require. */ static void tcti_tabrmd_receive_get_size (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; uint8_t buf [TPM_HEADER_SIZE] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0a, /* TPM_HEADER_SIZE -> resp is just header */ 0x00, 0x00, 0x00, 0x00, }; size_t size = 0; /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to get 10 bytes back from the GInputStream */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); will_return (__wrap_g_input_stream_read, TPM_HEADER_SIZE); will_return (__wrap_g_input_stream_read, buf); rc = tss2_tcti_tabrmd_receive (ctx, &size, NULL, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (size, TPM_HEADER_SIZE); } /* * This test ensures that a response from the TPM that is exactly 10 * bytes can be 'receive'd in a single call. */ static void tcti_tabrmd_receive_header_only (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; uint8_t resp [TPM_HEADER_SIZE] = { 0, }; size_t resp_size = sizeof (resp); uint8_t buf [TPM_HEADER_SIZE] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0a, /* TPM_HEADER_SIZE -> resp is just header */ 0x00, 0x00, 0x00, 0x00, }; /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to get 10 bytes back from the GInputStream */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); will_return (__wrap_g_input_stream_read, TPM_HEADER_SIZE); will_return (__wrap_g_input_stream_read, buf); rc = tss2_tcti_tabrmd_receive (ctx, &resp_size, resp, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_memory_equal (buf, resp, resp_size); } /* * Receive response that is only a header in two reads. The first read will * be a "short" read and we should get a response indicating that we need * to try again. */ #define FIRST_READ_SIZE 3 #define SECOND_READ_SIZE 7 static void tcti_tabrmd_receive_header_only_retry (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; uint8_t resp [TPM_HEADER_SIZE] = { 0, }; size_t resp_size = sizeof (resp); uint8_t buf [TPM_HEADER_SIZE] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0a, /* TPM_HEADER_SIZE -> resp is just header */ 0x00, 0x00, 0x00, 0x00, }; /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to get first bytes back from the GInputStream */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); will_return (__wrap_g_input_stream_read, FIRST_READ_SIZE); will_return (__wrap_g_input_stream_read, buf); rc = tss2_tcti_tabrmd_receive (ctx, &resp_size, resp, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_TCTI_RC_TRY_AGAIN); /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to get first bytes back from the GInputStream */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); will_return (__wrap_g_input_stream_read, SECOND_READ_SIZE); will_return (__wrap_g_input_stream_read, &buf[FIRST_READ_SIZE]); rc = tss2_tcti_tabrmd_receive (ctx, &resp_size, resp, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_memory_equal (buf, resp, resp_size); } /* * This test implements a calling pattern referred to as "partial reads" * in the system API implementation from tpm2-tss. It is also prescribed * by the TCTI spec. */ static void tcti_tabrmd_receive_partial_reads (void **state) { TSS2_RC rc; TSS2_TCTI_CONTEXT *ctx = (TSS2_TCTI_CONTEXT*)*state; uint8_t buf [] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0e, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xd0, 0x0d, }; uint8_t resp [sizeof (buf)] = { 0, }; size_t resp_size = 0; /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to get 10 bytes back from the GInputStream */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); will_return (__wrap_g_input_stream_read, TPM_HEADER_SIZE); will_return (__wrap_g_input_stream_read, buf); rc = tss2_tcti_tabrmd_receive (ctx, &resp_size, NULL, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (resp_size, sizeof (buf)); /* mock stack required to extract the fd from the GSocketConnection */ will_return (__wrap_g_socket_connection_get_socket, TEST_SOCKET); will_return (__wrap_g_socket_get_fd, TEST_FD); /* prime mock stack for poll to indicate data is ready */ will_return (__wrap_poll, POLLIN); will_return (__wrap_poll, 0); will_return (__wrap_poll, 1); /* mock stack required to get 10 bytes back from the GInputStream */ will_return (__wrap_g_io_stream_get_input_stream, TEST_CONNECTION); will_return (__wrap_g_input_stream_read, 4); will_return (__wrap_g_input_stream_read, &buf[TPM_HEADER_SIZE]); rc = tss2_tcti_tabrmd_receive (ctx, &resp_size, resp, TSS2_TCTI_TIMEOUT_BLOCK); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_memory_equal (buf, resp, sizeof (buf)); } int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test (tcti_tabrmd_poll_fd_ready_pollin), cmocka_unit_test (tcti_tabrmd_poll_fd_ready_pollpri), cmocka_unit_test (tcti_tabrmd_poll_fd_ready_pollrdhup), cmocka_unit_test (tcti_tabrmd_poll_timeout), cmocka_unit_test (tcti_tabrmd_poll_error), cmocka_unit_test_setup_teardown (tcti_tabrmd_read_poll_timeout, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_read_poll_fail, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_read_eof, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_read_block_error, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_read_short, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_read_success, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test (tcti_tabrmd_receive_null_context), cmocka_unit_test (tcti_tabrmd_receive_null_response), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_bad_magic, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_bad_version, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_bad_state, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_bad_timeout, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_size_lt_header, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_header_fail, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_header_lt_expected, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_get_size, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_header_only, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_header_only_retry, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_receive_partial_reads, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/tcti_unit.c000066400000000000000000000060061401030142600166000ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include "tcti.h" #include "tcti-mock.h" #include "util.h" typedef struct { TSS2_TCTI_CONTEXT *context; Tcti *tcti; } test_data_t; static int tcti_setup (void **state) { test_data_t *data; data = calloc (1, sizeof (test_data_t)); if (data == NULL) { g_critical ("%s: failed to allocate test_data_t", __func__); return 1; } data->context = tcti_mock_init_full (); if (data->context == NULL) { g_critical ("tcti_mock_init_full failed"); return 1; } data->tcti = tcti_new (data->context); *state = data; return 0; } static int tcti_teardown (void **state) { test_data_t *data = (test_data_t*)*state; g_clear_object (&data->tcti); g_clear_pointer (&data, free); return 0; } static void tcti_type_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_true (IS_TCTI (data->tcti)); } static void tcti_peek_context_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_ptr_equal (data->context, tcti_peek_context (data->tcti)); } static void tcti_transmit_test (void **state) { test_data_t *data = (test_data_t*)*state; TSS2_RC rc, rc_expected = TSS2_RC_SUCCESS; size_t size = 10; uint8_t command [10] = { 0 }; will_return (tcti_mock_transmit, rc_expected); rc = tcti_transmit (data->tcti, size, command); assert_int_equal (rc, rc_expected); } static void tcti_receive_test (void **state) { test_data_t *data = (test_data_t*)*state; TSS2_RC rc, rc_expected = TSS2_RC_SUCCESS; size_t size = 20; uint8_t response [20] = { 0 }, response_expected [10] = { 0x1, }; ((TCTI_MOCK_CONTEXT*)data->context)->state = RECEIVE; will_return (tcti_mock_receive, response_expected); will_return (tcti_mock_receive, sizeof (response_expected)); will_return (tcti_mock_receive, rc_expected); rc = tcti_receive (data->tcti, &size, response, TSS2_TCTI_TIMEOUT_BLOCK); assert_memory_equal (response, response_expected, size); assert_int_equal (size, sizeof (response_expected)); assert_int_equal (rc, rc_expected); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (tcti_type_test, tcti_setup, tcti_teardown), cmocka_unit_test_setup_teardown (tcti_peek_context_test, tcti_setup, tcti_teardown), cmocka_unit_test_setup_teardown (tcti_transmit_test, tcti_setup, tcti_teardown), cmocka_unit_test_setup_teardown (tcti_receive_test, tcti_setup, tcti_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/test-skeleton_unit.c000066400000000000000000000024741401030142600204430ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #define EXPECTED_VALUE 1 typedef struct test_data { int value; } test_data_t; /** * Test setup function: allocate structure to hold test data, initialize * some value in said structure. */ static int test_setup (void **state) { test_data_t *data; data = calloc (1, sizeof (test_data_t)); data->value = EXPECTED_VALUE; *state = data; return 0; } /** * Test teardown function: deallocate whatever resources are allocated in * the setup and test functions. */ static int test_teardown (void **state) { test_data_t *data = (test_data_t*)*state; free (data); *state = NULL; return 0; } /** * A test: verify that something functioned properly. */ static void test_unit (void **state) { test_data_t *data = (test_data_t*)*state; assert_int_equal (data->value, EXPECTED_VALUE); } /** * Test driver. */ int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (test_unit, test_setup, test_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/thread_unit.c000066400000000000000000000111771401030142600171110ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include #include #include "thread.h" /* * This test exercises the Thread abstract class. We do so by creating a * derived class (the TestThread object). We can then use the thread_* * functions (from the abstract base class) to drive the internal thread. * To test the underlying Thread functions, the TestThread instance has * a few boolean flags (canceled / cleaned_up) that it sets when its * 'unblock' and cleanup functions are invoked. */ /* * Begin TestThread GObject implementation */ G_BEGIN_DECLS typedef struct _TestThreadClass { ThreadClass parent; } TestThreadClass; typedef struct _TestThread { Thread parent_instance; gboolean canceled; gboolean cleaned_up; gboolean running; } TestThread; #define TYPE_TEST_THREAD (test_thread_get_type ()) #define TEST_THREAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TEST_THREAD, TestThread)) #define IS_TEST_THREAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TEST_THREAD)) G_END_DECLS G_DEFINE_TYPE (TestThread, test_thread, TYPE_THREAD); static void test_thread_cleanup (void *data) { TestThread *self = TEST_THREAD (data); self->cleaned_up = TRUE; } /* * This is the "active" part of the TestThread. When 'thread_start' is * invoked (from the Thread base class) this function is run as a pthread. */ static void* test_thread_run (void *data) { TestThread *self = TEST_THREAD (data); pthread_cleanup_push (test_thread_cleanup, self); while (TRUE) { self->running = TRUE; g_debug ("test_thread_run before sleep"); sleep (1); g_debug ("test_thread_run after sleep"); } pthread_cleanup_pop (0); } /* * This function is invoked as part of canceling ('thread_cancel') the Thread. * It is used to unblock the thread in case it is blocked on something that * isn't a cancelation point. The TestThread just sleeps in a loop and sleep * is a pthread cancelation point so we just set a flag so we know this was * called. */ static void test_thread_unblock (Thread *thread) { TestThread *self = TEST_THREAD (thread); self->canceled = TRUE; pthread_cancel (thread->thread_id); } static void test_thread_init (TestThread *self) { self->canceled = FALSE; self->cleaned_up = FALSE; self->running = FALSE; } static void test_thread_class_init (TestThreadClass *klass) { ThreadClass *thread_klass = THREAD_CLASS (klass); if (test_thread_parent_class == NULL) { test_thread_parent_class = g_type_class_peek_parent (klass); } thread_klass->thread_run = test_thread_run; thread_klass->thread_unblock = test_thread_unblock; } TestThread* test_thread_new (void) { return TEST_THREAD (g_object_new (TYPE_TEST_THREAD, NULL)); } /* * End of TestThread GObject implementation. */ static int test_thread_setup (void **state) { *state = test_thread_new (); return 0; } static int test_thread_teardown (void **state) { g_object_unref (*state); return 0; } /* * This test ensures that the object created in the _setup function can be * recognized as both a Thread and as a TestThread. */ static void test_thread_type_test (void **state) { assert_true (G_IS_OBJECT (*state)); assert_true (IS_TEST_THREAD (*state)); } /* * This test runs the TestThread through the typical thread lifecycle: start, * cancel, then join. */ static void test_thread_lifecycle_test (void **state) { Thread *thread = THREAD (*state); TestThread *test_thread = TEST_THREAD (*state); int ret; ret = thread_start (thread); assert_int_equal (ret, 0); sched_yield (); thread_cancel (thread); sched_yield (); ret = thread_join (thread); assert_int_equal (ret, 0); sched_yield (); /* * We check these flags at the end of the test to give the thread a * chance to run. */ assert_true (test_thread->running); assert_true (test_thread->canceled); assert_true (test_thread->cleaned_up); } int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (test_thread_type_test, test_thread_setup, test_thread_teardown), cmocka_unit_test_setup_teardown (test_thread_lifecycle_test, test_thread_setup, test_thread_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/tpm2-command_unit.c000066400000000000000000000575321401030142600201450ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include #include "tpm2-command.h" #include "util.h" #define HANDLE_FIRST 0x80000000 #define HANDLE_SECOND 0x80000001 uint8_t cmd_with_auths [] = { 0x80, 0x02, /* TPM2_ST_SESSIONS */ 0x00, 0x00, 0x00, 0x73, /* command buffer size */ 0x00, 0x00, 0x01, 0x37, /* command code: 0x137 / TPM2_CC_NV_Write */ 0x01, 0x50, 0x00, 0x20, /* auth handle */ 0x01, 0x50, 0x00, 0x20, /* nv index handle */ 0x00, 0x00, 0x00, 0x92, /* size of auth area (2x73 byte auths) */ 0x02, 0x00, 0x00, 0x00, /* auth session handle */ 0x00, 0x20, /* sizeof caller nonce */ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x01, /* session attributes */ 0x00, 0x20, /* sizeof hmac */ 0x4d, 0x91, 0x26, 0xa3, 0xd9, 0xf6, 0x74, 0xde, 0x98, 0x94, 0xb1, 0x0f, 0xe6, 0xb1, 0x5c, 0x72, 0x7d, 0x36, 0xeb, 0x39, 0x6b, 0xf2, 0x31, 0x72, 0x89, 0xb6, 0xc6, 0x8e, 0x54, 0xa9, 0x4c, 0x3e, 0x02, 0x00, 0x00, 0x01, /* auth session handle */ 0x00, 0x20, /* sizeof caller nonce */ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x01, /* session attributes */ 0x00, 0x20, /* sizeof hmac */ 0x4d, 0x91, 0x26, 0xa3, 0xd9, 0xf6, 0x74, 0xde, 0x98, 0x94, 0xb1, 0x0f, 0xe6, 0xb1, 0x5c, 0x72, 0x7d, 0x36, 0xeb, 0x39, 0x6b, 0xf2, 0x31, 0x72, 0x89, 0xb6, 0xc6, 0x8e, 0x54, 0xa9, 0x4c, 0x3e, 0x00, 0x10, /* sizeof data */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00 }; /* command buffer for ContextFlush command with no handle */ uint8_t cmd_buf_context_flush_no_handle [] = { 0x80, 0x01, /* TPM2_ST_NO_SESSIONS */ 0x00, 0x00, 0x00, 0x0a, /* size: 10 bytes */ 0x00, 0x00, 0x01, 0x65, /* command code for ContextFlush */ }; typedef struct { Tpm2Command *command; guint8 *buffer; size_t buffer_size; Connection *connection; } test_data_t; /** * This is the minimum work required to instantiate a Tpm2Command. It needs * a data buffer to hold the command and a Connection object. We also * allocate a structure to hold these things so that we can free them in * the teardown. */ static int tpm2_command_setup_base (void **state) { test_data_t *data = NULL; gint client_fd; GIOStream *iostream; HandleMap *handle_map; data = calloc (1, sizeof (test_data_t)); /* allocate a buffer large enough to hold a TPM2 header and 3 handles */ data->buffer_size = TPM_RESPONSE_HEADER_SIZE + sizeof (TPM2_HANDLE) * 3; data->buffer = calloc (1, data->buffer_size); handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); data->connection = connection_new (iostream, 0, handle_map); g_object_unref (handle_map); g_object_unref (iostream); *state = data; return 0; } static int tpm2_command_setup (void **state) { test_data_t *data = NULL; TPMA_CC attributes = 0x0; tpm2_command_setup_base (state); data = (test_data_t*)*state; data->command = tpm2_command_new (data->connection, data->buffer, data->buffer_size, attributes); *state = data; return 0; } static int tpm2_command_setup_two_handles (void **state) { test_data_t *data = NULL; TPMA_CC attributes = 2 << 25; tpm2_command_setup_base (state); data = (test_data_t*)*state; data->command = tpm2_command_new (data->connection, data->buffer, data->buffer_size, attributes); /* * This sets the two handles to 0x80000000 and 0x80000001, assuming the * buffer was initialized to all 0's */ data->buffer [10] = 0x80; data->buffer [14] = 0x80; data->buffer [17] = 0x01; return 0; } uint8_t two_handles_not_three [] = { 0x80, 0x02, /* TPM2_ST_SESSIONS */ 0x00, 0x00, 0x00, 0x12, /* command buffer size */ 0x00, 0x00, 0x01, 0x49, /* command code: 0x149 / TPM2_CC_PolicyNV */ 0x01, 0x02, 0x03, 0x04, /* first handle */ 0xf0, 0xe0, 0xd0, 0xc0 /* second handle */ }; /* * This setup function creates all of the components necessary to carry out * a tpm2_command test. Additionally it creates a tpm2_command with a command * buffer that should have 3 handles (per the TPMA_CC) but is only large * enough to hold 2. */ static int tpm2_command_setup_two_handles_not_three (void **state) { test_data_t *data = NULL; gint client_fd; GIOStream *iostream; HandleMap *handle_map; data = calloc (1, sizeof (test_data_t)); *state = data; handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); data->connection = connection_new (iostream, 0, handle_map); g_object_unref (handle_map); g_object_unref (iostream); /* */ data->buffer_size = sizeof (two_handles_not_three); data->buffer = calloc (1, data->buffer_size); memcpy (data->buffer, two_handles_not_three, data->buffer_size); data->command = tpm2_command_new (data->connection, data->buffer, data->buffer_size, (TPMA_CC)((UINT32)0x06000149)); return 0; } /* * This test setup function is much like the others with the exception of the * Tpm2Command buffer being set to the 'cmd_with_auths'. This allows testing * of the functions that parse / process the auth are of the command. */ static int tpm2_command_setup_with_auths (void **state) { test_data_t *data = NULL; gint client_fd; GIOStream *iostream; HandleMap *handle_map; TPMA_CC attributes = 2 << 25; data = calloc (1, sizeof (test_data_t)); /* allocate a buffer large enough to hold the cmd_with_auths buffer */ data->buffer_size = sizeof (cmd_with_auths); data->buffer = calloc (1, data->buffer_size); memcpy (data->buffer, cmd_with_auths, sizeof (cmd_with_auths)); handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); data->connection = connection_new (iostream, 0, handle_map); g_object_unref (handle_map); g_object_unref (iostream); data->command = tpm2_command_new (data->connection, data->buffer, data->buffer_size, attributes); *state = data; return 0; } static int tpm2_command_setup_flush_context_no_handle (void **state) { test_data_t *data = NULL; gint client_fd; GIOStream *iostream; HandleMap *handle_map; TPMA_CC attributes = 2 << 25; data = calloc (1, sizeof (test_data_t)); /* allocate a buffer large enough to hold the cmd_with_auths buffer */ data->buffer_size = sizeof (cmd_buf_context_flush_no_handle); data->buffer = calloc (1, data->buffer_size); memcpy (data->buffer, cmd_buf_context_flush_no_handle, data->buffer_size); handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); data->connection = connection_new (iostream, 0, handle_map); g_object_unref (handle_map); g_object_unref (iostream); data->command = tpm2_command_new (data->connection, data->buffer, data->buffer_size, attributes); *state = data; return 0; } /** * Tear down all of the data from the setup function. We don't have to * free the data buffer (data->buffer) since the Tpm2Command frees it as * part of its finalize function. */ static int tpm2_command_teardown (void **state) { test_data_t *data = (test_data_t*)*state; g_object_unref (data->connection); g_object_unref (data->command); free (data); return 0; } /** * This is a test for memory management / reference counting. The setup * function does exactly that so when we get the Tpm2Command object we just * check to be sure it's a GObject and then we unref it. This test will * probably only fail when run under ASAN if the reference counting is * off. */ static void tpm2_command_type_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_true (G_IS_OBJECT (data->command)); assert_true (IS_TPM2_COMMAND (data->command)); } static void tpm2_command_get_connection_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_int_equal (data->connection, tpm2_command_get_connection (data->command)); } static void tpm2_command_get_buffer_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_int_equal (data->buffer, tpm2_command_get_buffer (data->command)); } static void tpm2_command_get_tag_test (void **state) { test_data_t *data = (test_data_t*)*state; guint8 *buffer = tpm2_command_get_buffer (data->command); TPMI_ST_COMMAND_TAG tag_ret; /* this is TPM2_ST_SESSIONS in network byte order */ buffer[0] = 0x80; buffer[1] = 0x02; tag_ret = tpm2_command_get_tag (data->command); assert_int_equal (tag_ret, TPM2_ST_SESSIONS); } static void tpm2_command_get_size_test (void **state) { test_data_t *data = (test_data_t*)*state; guint8 *buffer = tpm2_command_get_buffer (data->command); guint32 size_ret = 0; /* this is tpm_st_connections in network byte order */ buffer[0] = 0x80; buffer[1] = 0x02; buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = 0x00; buffer[5] = 0x06; size_ret = tpm2_command_get_size (data->command); assert_int_equal (0x6, size_ret); } static void tpm2_command_get_code_test (void **state) { test_data_t *data = (test_data_t*)*state; guint8 *buffer = tpm2_command_get_buffer (data->command); TPM2_CC command_code; /** * This is TPM2_ST_SESSIONS + a size of 0x0a + the command code for * GetCapability in network byte order */ buffer[0] = 0x80; buffer[1] = 0x02; buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = 0x00; buffer[5] = 0x0a; buffer[6] = 0x00; buffer[7] = 0x00; buffer[8] = 0x01; buffer[9] = 0x7a; command_code = tpm2_command_get_code (data->command); assert_int_equal (command_code, TPM2_CC_GetCapability); } static void tpm2_command_get_two_handle_count_test (void **state) { test_data_t *data = (test_data_t*)*state; guint8 command_handles; command_handles = tpm2_command_get_handle_count (data->command); assert_int_equal (command_handles, 2); } static void tpm2_command_get_handles_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handles [3] = { 0, 0, 0 }; size_t count = 3; gboolean ret; ret = tpm2_command_get_handles (data->command, handles, &count); assert_true (ret == TRUE); assert_int_equal (handles [0], HANDLE_FIRST); assert_int_equal (handles [1], HANDLE_SECOND); } /* * Get the handle at the first position in the handle area of the command. */ static void tpm2_command_get_handle_first_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle_out; handle_out = tpm2_command_get_handle (data->command, 0); assert_int_equal (handle_out, HANDLE_FIRST); } /* * Get the handle at the second position in the handle area of the command. */ static void tpm2_command_get_handle_second_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle_out; handle_out = tpm2_command_get_handle (data->command, 1); assert_int_equal (handle_out, HANDLE_SECOND); } /* * Attempt to get the handle at the third position in the handle area of the * command. This should fail since the command has only two handles. */ static void tpm2_command_get_handle_fail_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle_out; handle_out = tpm2_command_get_handle (data->command, 2); assert_int_equal (handle_out, 0); } /* */ static void tpm2_command_set_handle_first_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle_in = 0xdeadbeef, handle_out = 0; gboolean ret; ret = tpm2_command_set_handle (data->command, handle_in, 0); assert_true (ret); handle_out = tpm2_command_get_handle (data->command, 0); assert_int_equal (handle_out, handle_in); } static void tpm2_command_set_handle_second_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle_in = 0xdeadbeef, handle_out = 0; gboolean ret; ret = tpm2_command_set_handle (data->command, handle_in, 1); assert_true (ret); handle_out = tpm2_command_get_handle (data->command, 1); assert_int_equal (handle_out, handle_in); } static void tpm2_command_set_handle_fail_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle_in = 0xdeadbeef; gboolean ret; ret = tpm2_command_set_handle (data->command, handle_in, 2); assert_false (ret); } static void tpm2_command_get_auth_size_test (void **state) { test_data_t *data = (test_data_t*)*state; UINT32 auths_area_size = 0; auths_area_size = tpm2_command_get_auths_size (data->command); assert_int_equal (auths_area_size, 0x92); } /* * This structure is used to track state while processing the authorizations * from the command authorization area. */ typedef struct { Tpm2Command *command; size_t counter; size_t handles_count; TPM2_HANDLE handles [3]; } callback_auth_state_t; /* * The tpm2_command_foreach_auth function invokes this function for each * authorization in the command authorization area. The 'user_data' is an * instance of the callback_auth_state_t structure that we use to track state. * The expected handles from the auth area are in the handles array in the * order that they should be received. We then use the 'counter' to identify * which handle we should receive for each callback (assuming that the * callback is invoked for each auth IN ORDER). */ static void tpm2_command_foreach_auth_callback (gpointer authorization, gpointer user_data) { size_t auth_offset = *(size_t*)authorization; callback_auth_state_t *callback_state = (callback_auth_state_t*)user_data; TPM2_HANDLE handle; handle = tpm2_command_get_auth_handle (callback_state->command, auth_offset); g_debug ("tpm2_command_foreach_auth_callback:\n counter: %zd\n" " handles_count: %zd\n handle: 0x%08" PRIx32, callback_state->counter, callback_state->handles_count, callback_state->handles [callback_state->counter]); g_debug (" auth_offset: %zu", auth_offset); g_debug (" AUTH_HANDLE_GET: 0x%08" PRIx32, handle); assert_true (callback_state->counter < callback_state->handles_count); assert_int_equal (handle, callback_state->handles [callback_state->counter]); ++callback_state->counter; } /* * This test exercises the tpm2_command_foreach_auth function. The state * structure must be initialized with the handles that are expected from * the command. Each time the callback is invoked we compare the authorization * handle to the handles array. */ static void tpm2_command_foreach_auth_test (void **state) { test_data_t *data = (test_data_t*)*state; /* this data is highly dependent on the */ callback_auth_state_t callback_state = { .command = data->command, .counter = 0, .handles_count = 2, .handles = { 0x02000000, 0x02000001, }, }; tpm2_command_foreach_auth (data->command, tpm2_command_foreach_auth_callback, &callback_state); } static void tpm2_command_flush_context_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; TSS2_RC rc; TPM2_HANDLE handle; rc = tpm2_command_get_flush_handle (data->command, &handle); assert_int_equal (rc, RM_RC (TPM2_RC_INSUFFICIENT)); } /* * This test attempts to read 3 handles from a command. It's paired with * the `tpm2_command_setup_two_handles_not_three` setup function which * populates the command with data appropriate for this test. The command * body is for a command with 3 handles but only enough room is provided * for two handles. Attempts to read 3 should stop at the end of the buffer. */ static void tpm2_command_two_handles_not_three (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handles [3] = { 0 }; size_t handle_count = 3; gboolean ret = TRUE; ret = tpm2_command_get_handles (data->command, handles, &handle_count); assert_true (ret); } uint8_t get_cap_no_cap [] = { 0x80, 0x02, /* TPM2_ST_SESSIONS */ 0x00, 0x00, 0x00, 0x0a, /* command buffer size */ 0x00, 0x00, 0x01, 0x7a, /* TPM2_CC_GetCapability */ }; /* * This setup function creates all of the components necessary to carry out * a tpm2_command test. Additionally it creates a tpm2_command with a command * buffer that should have 3 handles (per the TPMA_CC) but is only large * enough to hold 2. */ static int tpm2_command_setup_get_cap_no_cap (void **state) { test_data_t *data = NULL; gint client_fd; GIOStream *iostream; HandleMap *handle_map; data = calloc (1, sizeof (test_data_t)); *state = data; handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); data->connection = connection_new (iostream, 0, handle_map); g_object_unref (handle_map); g_object_unref (iostream); data->buffer_size = sizeof (get_cap_no_cap); data->buffer = calloc (1, data->buffer_size); memcpy (data->buffer, get_cap_no_cap, data->buffer_size); data->command = tpm2_command_new (data->connection, data->buffer, data->buffer_size, (TPMA_CC)((UINT32)0x0600017a)); return 0; } static void tpm2_command_get_cap_no_cap (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_CAP cap; cap = tpm2_command_get_cap (data->command); assert_int_equal (cap, 0); } static void tpm2_command_get_cap_no_prop (void **state) { test_data_t *data = (test_data_t*)*state; UINT32 prop; prop = tpm2_command_get_prop (data->command); assert_int_equal (prop, 0); } static void tpm2_command_get_cap_no_count (void **state) { test_data_t *data = (test_data_t*)*state; UINT32 count; count = tpm2_command_get_prop_count (data->command); assert_int_equal (count, 0); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (tpm2_command_type_test, tpm2_command_setup, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_connection_test, tpm2_command_setup, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_buffer_test, tpm2_command_setup, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_tag_test, tpm2_command_setup, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_size_test, tpm2_command_setup, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_code_test, tpm2_command_setup, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_two_handle_count_test, tpm2_command_setup_two_handles, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_handles_test, tpm2_command_setup_two_handles, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_handle_first_test, tpm2_command_setup_two_handles, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_handle_second_test, tpm2_command_setup_two_handles, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_handle_fail_test, tpm2_command_setup_two_handles, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_set_handle_first_test, tpm2_command_setup_two_handles, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_set_handle_second_test, tpm2_command_setup_two_handles, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_set_handle_fail_test, tpm2_command_setup_two_handles, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_auth_size_test, tpm2_command_setup_with_auths, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_foreach_auth_test, tpm2_command_setup_with_auths, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_flush_context_handle_test, tpm2_command_setup_flush_context_no_handle, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_two_handles_not_three, tpm2_command_setup_two_handles_not_three, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_cap_no_cap, tpm2_command_setup_get_cap_no_cap, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_cap_no_prop, tpm2_command_setup_get_cap_no_cap, tpm2_command_teardown), cmocka_unit_test_setup_teardown (tpm2_command_get_cap_no_count, tpm2_command_setup_get_cap_no_cap, tpm2_command_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/tpm2-response_unit.c000066400000000000000000000406421401030142600203570ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "tpm2-header.h" #include "tpm2-response.h" #include "util.h" #define HANDLE_TEST 0xdeadbeef #define HANDLE_TYPE 0xde typedef struct { Tpm2Response *response; guint8 *buffer; size_t buffer_size; Connection *connection; } test_data_t; /* * This function does all of the setup required to run a test *except* * for instantiating the Tpm2Response object. */ static int tpm2_response_setup_base (void **state) { test_data_t *data = NULL; gint client_fd; HandleMap *handle_map; GIOStream *iostream; data = calloc (1, sizeof (test_data_t)); /* allocate a buffer large enough to hold a TPM2 header and a handle */ data->buffer_size = TPM_RESPONSE_HEADER_SIZE + sizeof (TPM2_HANDLE); data->buffer = calloc (1, data->buffer_size); handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); data->connection = connection_new (iostream, 0, handle_map); g_object_unref (handle_map); g_object_unref (iostream); *state = data; return 0; } /* * This function sets up all required test data with a Tpm2Response * object that has no attributes set. */ static int tpm2_response_setup (void **state) { test_data_t *data; tpm2_response_setup_base (state); data = (test_data_t*)*state; data->response = tpm2_response_new (data->connection, data->buffer, data->buffer_size, (TPMA_CC){ 0, }); return 0; } /* * This function sets up all required test data with a Tpm2Response * object that has attributes indicating the response has a handle * in it. */ static int tpm2_response_setup_with_handle (void **state) { test_data_t *data; TPMA_CC attributes = 1 << 28; tpm2_response_setup_base (state); data = (test_data_t*)*state; data->buffer_size = TPM_RESPONSE_HEADER_SIZE + sizeof (TPM2_HANDLE); data->response = tpm2_response_new (data->connection, data->buffer, data->buffer_size, attributes); set_response_size (data->buffer, data->buffer_size); data->buffer [TPM_RESPONSE_HEADER_SIZE] = 0xde; data->buffer [TPM_RESPONSE_HEADER_SIZE + 1] = 0xad; data->buffer [TPM_RESPONSE_HEADER_SIZE + 2] = 0xbe; data->buffer [TPM_RESPONSE_HEADER_SIZE + 3] = 0xef; return 0; } /** * Tear down all of the data from the setup function. We don't have to * free the data buffer (data->buffer) since the Tpm2Response frees it as * part of its finalize function. */ static int tpm2_response_teardown (void **state) { test_data_t *data = (test_data_t*)*state; g_object_unref (data->connection); g_object_unref (data->response); free (data); return 0; } /** * Here we check to be sure that the object instantiated in the setup * function is actually the type that we expect. This is a test to be sure * the type is registered properly with the type system. */ static void tpm2_response_type_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_true (G_IS_OBJECT (data->response)); assert_true (IS_TPM2_RESPONSE (data->response)); } /** * In the setup function we save a reference to the Connection object * instantiated as well as passing it into the Tpm2Response object. Here we * check to be sure that the Connection object returned by the Tpm2Response * object is the same one that we passed it. */ static void tpm2_response_get_connection_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *connection; connection = tpm2_response_get_connection (data->response); assert_int_equal (data->connection, connection); g_object_unref (connection); } /** * In the setup function we passed the Tpm2Response object a data buffer. * Here we check to be sure it passes the same one back to us when we ask * for it. */ static void tpm2_response_get_buffer_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_int_equal (data->buffer, tpm2_response_get_buffer (data->response)); } /** * Here we retrieve the data buffer from the Tpm2Response and manually set * the tag part of the response buffer to TPM2_ST_SESSIONS in network byte * order (aka big endian). We then use the tpm2_response_get_tag function * to retrieve this data and we compare it to TPM2_ST_SESSIONS to be sure we * got the value in host byte order. */ static void tpm2_response_get_tag_test (void **state) { test_data_t *data = (test_data_t*)*state; guint8 *buffer = tpm2_response_get_buffer (data->response); TPM2_ST tag_ret; /* this is TPM2_ST_SESSIONS in network byte order */ buffer[0] = 0x80; buffer[1] = 0x02; tag_ret = tpm2_response_get_tag (data->response); assert_int_equal (tag_ret, TPM2_ST_SESSIONS); } /** * Again we're getting the response buffer from the Tpm2Response object * but this time we're setting the additional size field (uint32) to 0x6 * in big endian format. We then use the tpm2_response_get_size function * to get this value back and we compare it to 0x6 to be sure it's returned * in host byte order. */ static void tpm2_response_get_size_test (void **state) { test_data_t *data = (test_data_t*)*state; guint8 *buffer = tpm2_response_get_buffer (data->response); guint32 size_ret = 0; /* this is TPM2_ST_SESSIONS in network byte order */ buffer[0] = 0x80; buffer[1] = 0x02; buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = 0x00; buffer[5] = 0x06; size_ret = tpm2_response_get_size (data->response); assert_int_equal (0x6, size_ret); } /** * Again we're getting the response buffer from the Tpm2Response object * but this time we're setting the additional response code field (TPM_RC) * to a known value in big endian format. We then use tpm2_response_get_code * to retrieve the RC and compare it to the appropriate constant to be sure * it's sent back to us in host byte order. */ static void tpm2_response_get_code_test (void **state) { test_data_t *data = (test_data_t*)*state; guint8 *buffer = tpm2_response_get_buffer (data->response); TPM2_CC response_code; /** * This is TPM2_ST_SESSIONS + a size of 0x0a + the response code for * GetCapability in network byte order */ buffer[0] = 0x80; buffer[1] = 0x02; buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = 0x00; buffer[5] = 0x0a; buffer[6] = 0x00; buffer[7] = 0x00; buffer[8] = 0x01; buffer[9] = 0x00; response_code = tpm2_response_get_code (data->response); assert_int_equal (response_code, TPM2_RC_INITIALIZE); } /** * This is a setup function for testing the "short-cut" constructor for the * Tpm2Response object that creates the response buffer with the provided RC. */ static int tpm2_response_new_rc_setup (void **state) { test_data_t *data = NULL; gint client_fd; GIOStream *iostream; HandleMap *handle_map; data = calloc (1, sizeof (test_data_t)); /* allocate a buffer large enough to hold a TPM2 header */ handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); data->connection = connection_new (iostream, 0, handle_map); g_object_unref (handle_map); g_object_unref (iostream); data->response = tpm2_response_new_rc (data->connection, TPM2_RC_BINDING); *state = data; return 0; } /** * The tpm2_response_new_rc sets the TPM2_ST_NO_SESSIONS tag for us since * it's just returning an RC and cannot have connections. Here we check to be * sure we can retrieve this tag and that's it's returned in host byte order. */ static void tpm2_response_new_rc_tag_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_ST tag = 0; tag = tpm2_response_get_tag (data->response); assert_int_equal (tag, TPM2_ST_NO_SESSIONS); } /** * The tpm2_response_new_rc sets the size to the appropriate * TPM_RESPONSE_HEADER size (which is 10 bytes) for us since the response * buffer is just a header. Here we check to be sure that we get this value * back in the proper host byte order. */ static void tpm2_response_new_rc_size_test (void **state) { test_data_t *data = (test_data_t*)*state; guint32 size = 0; size = tpm2_response_get_size (data->response); assert_int_equal (size, TPM_RESPONSE_HEADER_SIZE); } /** * The tpm2_response_new_rc sets the response code to whatever RC is passed * as a parameter. In the paired setup function we passed it TPM2_RC_BINDING. * This function ensures that we get the same value back from the * tpm2_response_get_code function in the proper host byte order. */ static void tpm2_response_new_rc_code_test (void **state) { test_data_t *data = (test_data_t*)*state; TSS2_RC rc = TPM2_RC_SUCCESS; rc = tpm2_response_get_code (data->response); assert_int_equal (rc, TPM2_RC_BINDING); } /** * The tpm2_response_new_rc takes a connection as a parameter. We save a * reference to this in the test_data_t structure. Here we ensure that the * tpm2_response_get_connection function returns the same connection to us. */ static void tpm2_response_new_rc_connection_test (void **state) { test_data_t *data = (test_data_t*)*state; Connection *connection = data->connection; assert_int_equal (connection, tpm2_response_get_connection (data->response)); } /* * This test ensures that a tpm2_response_has_handle reports the * actual value in the TPMA_CC attributes field when the rHandle bit * in the attributes is *not* set. */ static void tpm2_response_no_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; gboolean has_handle; has_handle = tpm2_response_has_handle (data->response); assert_false (has_handle); } /* * This test ensures that a tpm2_response_has_handle reports the * actual value in the TPMA_CC attributes field when the rHandle bit * in the attributes *is* set. */ static void tpm2_response_has_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; gboolean has_handle = FALSE; has_handle = tpm2_response_has_handle (data->response); assert_true (has_handle); } /* * This tests the Tpm2Response objects ability to return the handle that * we set in the setup function. */ static void tpm2_response_get_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle; handle = tpm2_response_get_handle (data->response); assert_int_equal (handle, HANDLE_TEST); } /* * This tests the Tpm2Response object's ability detect an attempt to read * beyond the end of its internal buffer. We fake this by changing the * buffer_size field manually and then trying to access the handle. */ static void tpm2_response_get_handle_no_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle; data->response->buffer_size = TPM_HEADER_SIZE; handle = tpm2_response_get_handle (data->response); assert_int_equal (handle, 0); } /* * This tests the Tpm2Response objects ability to return the handle type * from the handle we set in the setup function. */ static void tpm2_response_get_handle_type_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HT handle_type; handle_type = tpm2_response_get_handle_type (data->response); assert_int_equal (handle_type, HANDLE_TYPE); } /* * This tests the Tpm2Response objects ability to set the response handle * field. We do this by first setting the handle to some value and then * querying the Tpm2Response object for the handle. The value returned * should be the handle that we set in the previous call. */ static void tpm2_response_set_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle_in = 0x80fffffe, handle_out = 0; tpm2_response_set_handle (data->response, handle_in); handle_out = tpm2_response_get_handle (data->response); assert_int_equal (handle_in, handle_out); } /* * This tests the Tpm2Response object's ability detect an attempt to write * beyond the end of its internal buffer. We fake this by changing the * buffer_size field manually and then trying to set the handle. */ static void tpm2_response_set_handle_no_handle_test (void **state) { test_data_t *data = (test_data_t*)*state; TPM2_HANDLE handle_in = 0x80fffffe, handle_out = 0; data->response->buffer_size = TPM_HEADER_SIZE; tpm2_response_set_handle (data->response, handle_in); handle_out = tpm2_response_get_handle (data->response); assert_int_equal (handle_out, 0); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (tpm2_response_type_test, tpm2_response_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_get_connection_test, tpm2_response_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_get_buffer_test, tpm2_response_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_get_tag_test, tpm2_response_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_get_size_test, tpm2_response_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_get_code_test, tpm2_response_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_new_rc_tag_test, tpm2_response_new_rc_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_new_rc_size_test, tpm2_response_new_rc_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_new_rc_code_test, tpm2_response_new_rc_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_new_rc_connection_test, tpm2_response_new_rc_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_no_handle_test, tpm2_response_setup, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_has_handle_test, tpm2_response_setup_with_handle, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_get_handle_test, tpm2_response_setup_with_handle, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_get_handle_no_handle_test, tpm2_response_setup_with_handle, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_get_handle_type_test, tpm2_response_setup_with_handle, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_set_handle_test, tpm2_response_setup_with_handle, tpm2_response_teardown), cmocka_unit_test_setup_teardown (tpm2_response_set_handle_no_handle_test, tpm2_response_setup_with_handle, tpm2_response_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/tpm2_unit.c000066400000000000000000000530041401030142600165170ustar00rootroot00000000000000/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2017 - 2018, Intel Corporation * All rights reserved. */ #include #include #include #include #include #include "tpm2.h" #include "tpm2-header.h" #include "tpm2-response.h" #include "tcti-mock.h" #include "util.h" #define MAX_COMMAND_VALUE 2048 #define MAX_RESPONSE_VALUE 1024 #define TAGGED_PROPS_INIT { \ .count = 2, \ .tpmProperty = { \ { \ .property = TPM2_PT_MAX_COMMAND_SIZE, \ .value = MAX_COMMAND_VALUE, \ }, \ { \ .property = TPM2_PT_MAX_RESPONSE_SIZE, \ .value = MAX_RESPONSE_VALUE, \ }, \ } \ }; typedef struct test_data { Tpm2 *tpm2; Connection *connection; Tpm2Command *command; Tpm2Response *response; gboolean acquired_lock; } test_data_t; TSS2_RC __wrap_Tss2_Sys_FlushContext (TSS2_SYS_CONTEXT *sysContext, TPM2_HANDLE handle) { UNUSED_PARAM (sysContext); UNUSED_PARAM (handle); return mock_type (TSS2_RC); } TSS2_RC __wrap_Tss2_Sys_ContextSave (TSS2_SYS_CONTEXT *sysContext, TPM2_HANDLE handle, TPMS_CONTEXT *context) { UNUSED_PARAM (sysContext); UNUSED_PARAM (handle); UNUSED_PARAM (context); return mock_type (TSS2_RC); } TSS2_RC __wrap_Tss2_Sys_ContextLoad (TSS2_SYS_CONTEXT *sysContext, TPMS_CONTEXT *context, TPM2_HANDLE *handle) { UNUSED_PARAM (sysContext); UNUSED_PARAM (context); UNUSED_PARAM (handle); return mock_type (TSS2_RC); } TSS2_RC __wrap_Tss2_Sys_Initialize (TSS2_SYS_CONTEXT *sysContext, size_t contextSize, TSS2_TCTI_CONTEXT *tctiContext, TSS2_ABI_VERSION *abiVersion) { UNUSED_PARAM (sysContext); UNUSED_PARAM (contextSize); UNUSED_PARAM (tctiContext); UNUSED_PARAM (abiVersion); return mock_type (TSS2_RC); } /** * The tpm2 initialize function calls the Startup function so we * must mock it here. */ TSS2_RC __wrap_Tss2_Sys_Startup (TSS2_SYS_CONTEXT *sapi_context, TPM2_SU startup_type) { TSS2_RC rc; UNUSED_PARAM(sapi_context); UNUSED_PARAM(startup_type); rc = mock_type (TSS2_RC); g_debug ("__wrap_Tss2_Sys_Startup returning: 0x%x", rc); return rc; } /* * mock stack: * - TSS2_RC: an RC indicating failure will be returned immediately * - TPML: the TPML from the TPMU_CAPABILITIES structure to be returned */ TSS2_RC __wrap_Tss2_Sys_GetCapability (TSS2_SYS_CONTEXT *sysContext, TSS2L_SYS_AUTH_COMMAND const *cmdAuthsArray, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA *capabilityData, TSS2L_SYS_AUTH_RESPONSE *rspAuthsArray) { UNUSED_PARAM(sysContext); UNUSED_PARAM(cmdAuthsArray); UNUSED_PARAM(property); UNUSED_PARAM(propertyCount); UNUSED_PARAM(moreData); UNUSED_PARAM(rspAuthsArray); TPML_TAGGED_TPM_PROPERTY *tpmProperties; TPML_HANDLE *handles; TSS2_RC rc; rc = mock_type (TSS2_RC); g_debug ("__wrap_Tss2_Sys_GetCapability returning: 0x%x", rc); if (rc != TSS2_RC_SUCCESS) goto out; switch (capability) { case TPM2_CAP_TPM_PROPERTIES: capabilityData->capability = TPM2_CAP_TPM_PROPERTIES; tpmProperties = mock_type (TPML_TAGGED_TPM_PROPERTY*); memcpy (&capabilityData->data.tpmProperties, tpmProperties, sizeof (*tpmProperties)); break; case TPM2_CAP_HANDLES: capabilityData->capability = TPM2_CAP_HANDLES; handles = mock_type (TPML_HANDLE*); memcpy (&capabilityData->data.handles, handles, sizeof (*handles)); break; default: g_error ("%s does not understand this capability type", __func__); } out: return rc; } static Tcti* mock_tcti_setup (void) { TSS2_TCTI_CONTEXT *context; context = tcti_mock_init_full (); if (context == NULL) { g_critical ("tcti_mock_init_full failed"); return NULL; } return tcti_new (context); } /** * Do the minimum setup required by the Tpm2 object. This does not * call the tpm2_init_tpm function intentionally. We test that function * in a separate test. */ static int tpm2_setup (void **state) { test_data_t *data; Tcti *tcti = NULL; data = calloc (1, sizeof (test_data_t)); tcti = mock_tcti_setup (); will_return (__wrap_Tss2_Sys_Initialize, TSS2_RC_SUCCESS); data->tpm2 = tpm2_new (tcti); g_clear_object (&tcti); *state = data; return 0; } /* * This setup function chains up to the base setup function. Additionally * it done some special handling to wrap the TCTI transmit / receive * function since the standard linker tricks don't work with the TCTI * function pointers. Finally we also invoke the Tpm2 init function * while preparing return values for various SAPI commands that it invokes. */ static int tpm2_setup_with_init (void **state) { test_data_t *data; TPML_TAGGED_TPM_PROPERTY tagged_data = TAGGED_PROPS_INIT; tpm2_setup (state); data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_Startup, TPM2_RC_SUCCESS); will_return (__wrap_Tss2_Sys_GetCapability, TPM2_RC_SUCCESS); will_return (__wrap_Tss2_Sys_GetCapability, &tagged_data); tpm2_init_tpm (data->tpm2); return 0; } /* * This setup function chains up to the 'setup_with_init' function. * Additionally it creates a Connection and Tpm2Command object for use in * the unit test. */ static int tpm2_setup_with_command (void **state) { test_data_t *data; gint client_fd; guint8 *buffer; size_t buffer_size; HandleMap *handle_map; GIOStream *iostream; tpm2_setup_with_init (state); data = (test_data_t*)*state; buffer_size = TPM_HEADER_SIZE; buffer = calloc (1, buffer_size); handle_map = handle_map_new (TPM2_HT_TRANSIENT, MAX_ENTRIES_DEFAULT); iostream = create_connection_iostream (&client_fd); data->connection = connection_new (iostream, 0, handle_map); g_object_unref (handle_map); g_object_unref (iostream); data->command = tpm2_command_new (data->connection, buffer, buffer_size, (TPMA_CC){ 0, }); return 0; } /* * Function to teardown data created for tests. */ static int tpm2_teardown (void **state) { test_data_t *data = (test_data_t*)*state; g_object_unref (data->tpm2); if (G_IS_OBJECT (data->connection)) g_object_unref (data->connection); if (G_IS_OBJECT (data->command)) g_object_unref (data->command); if (G_IS_OBJECT (data->response)) g_object_unref (data->response); free (data); return 0; } /** * Test to ensure that the GObject type system gives us back the type that * we expect. */ static void tpm2_type_test (void **state) { test_data_t *data = (test_data_t*)*state; assert_true (IS_TPM2 (data->tpm2)); } static void tpm2_startup_fail (void **state) { test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_Startup, TSS2_SYS_RC_BAD_SIZE); assert_int_equal (tpm2_send_tpm_startup (data->tpm2), TSS2_SYS_RC_BAD_SIZE); } /** * Test the initialization route for the Tpm2. A successful call * to the init function should return TSS2_RC_SUCCESS and the 'initialized' * flag should be set to 'true'. */ static void tpm2_init_tpm_test (void **state) { test_data_t *data = (test_data_t*)*state; TPML_TAGGED_TPM_PROPERTY tagged_data = TAGGED_PROPS_INIT; will_return (__wrap_Tss2_Sys_Startup, TSS2_RC_SUCCESS); will_return (__wrap_Tss2_Sys_GetCapability, TSS2_RC_SUCCESS); will_return (__wrap_Tss2_Sys_GetCapability, &tagged_data); assert_int_equal (tpm2_init_tpm (data->tpm2), TSS2_RC_SUCCESS); assert_true (data->tpm2->initialized); } static void tpm2_init_tpm_initialized (void **state) { test_data_t *data = (test_data_t*)*state; assert_int_equal (tpm2_init_tpm (data->tpm2), TSS2_RC_SUCCESS); assert_true (data->tpm2->initialized); } static void tpm2_init_tpm_startup_fail (void **state) { test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_Startup, TPM2_RC_FAILURE); assert_int_equal (tpm2_init_tpm (data->tpm2), TPM2_RC_FAILURE); } static void tpm2_get_max_response_test (void **state) { test_data_t *data = (test_data_t*)*state; guint32 value; assert_int_equal (tpm2_get_max_response (data->tpm2, &value), TSS2_RC_SUCCESS); assert_int_equal (value, MAX_RESPONSE_VALUE); } static void* lock_thread (void *param) { test_data_t *data = (test_data_t*)param; tpm2_lock (data->tpm2); data->acquired_lock = TRUE; tpm2_unlock (data->tpm2); return NULL; } static void tpm2_lock_test (void **state) { test_data_t *data = (test_data_t*)*state; pthread_t thread_id; data->acquired_lock = FALSE; tpm2_lock (data->tpm2); assert_int_equal (pthread_create (&thread_id, NULL, lock_thread, data), 0); sleep (1); assert_false (data->acquired_lock); tpm2_unlock (data->tpm2); pthread_join (thread_id, NULL); } /** * Here we're testing the internals of the 'tpm2_send_command' * function. We're wrapping the tcti_transmit command in the TCTI that * the tpm2 is using. This test causes the TCTI transmit * function to return an arbitrary response code. In this case we should * receive a NULL pointer back in place of the Tpm2Response and the * out parameter RC is set to the RC value. */ static void tpm2_send_command_tcti_transmit_fail_test (void **state) { test_data_t *data = (test_data_t*)*state; TSS2_RC rc = TSS2_RC_SUCCESS, rc_expected = 99; Connection *connection; will_return (tcti_mock_transmit, rc_expected); data->response = tpm2_send_command (data->tpm2, data->command, &rc); assert_int_equal (rc, rc_expected); /* the Tpm2Response object we get back should have the same RC */ assert_int_equal (tpm2_response_get_code (data->response), rc_expected); /** * the Tpm2Response object we get back should have the same connection as * the Tpm2Command we tried to send. */ connection = tpm2_response_get_connection (data->response); assert_int_equal (connection, data->connection); g_object_unref (connection); } /* * This test exercises a failure path through the tpm2_send_command * function. We use the 'mock' pattern to cause the tcti_receive command to * return an RC indicating failure. */ static void tpm2_send_command_tcti_receive_fail_test (void **state) { test_data_t *data = (test_data_t*)*state; TSS2_RC rc = TSS2_RC_SUCCESS, rc_expected = 99; Connection *connection; will_return (tcti_mock_transmit, TSS2_RC_SUCCESS); will_return (tcti_mock_receive, NULL); will_return (tcti_mock_receive, 0); will_return (tcti_mock_receive, rc_expected); data->response = tpm2_send_command (data->tpm2, data->command, &rc); /* the response code should be the one we passed to __wrap_tcti_echo_receive */ assert_int_equal (rc, rc_expected); /* the Tpm2Response object we get back should have the same RC */ assert_int_equal (tpm2_response_get_code (data->response), rc_expected); /** * the Tpm2Response object we get back should have the same connection as * the Tpm2Command we tried to send. */ connection = tpm2_response_get_connection (data->response); assert_int_equal (connection, data->connection); g_object_unref (connection); } /* * This test exercises the success path through the * tpm2_send_command function. */ static void tpm2_send_command_success (void **state) { test_data_t *data = (test_data_t*)*state; TSS2_RC rc; Connection *connection; uint8_t buf [TPM_RESPONSE_HEADER_SIZE] = { 0 }; size_t size = sizeof (buf); /* initialize a response buffer with RC success */ response_buffer_set_rc (buf, TSS2_RC_SUCCESS); will_return (tcti_mock_transmit, TSS2_RC_SUCCESS); will_return (tcti_mock_receive, buf); will_return (tcti_mock_receive, size); will_return (tcti_mock_receive, TSS2_RC_SUCCESS); data->response = tpm2_send_command (data->tpm2, data->command, &rc); /* the response code should be the one we passed to __wrap_tcti_echo_receive */ assert_int_equal (rc, TSS2_RC_SUCCESS); /* the Tpm2Response object we get back should have the same RC */ assert_int_equal (tpm2_response_get_code (data->response), TSS2_RC_SUCCESS); /** * the Tpm2Response object we get back should have the same connection as * the Tpm2Command we tried to send. */ connection = tpm2_response_get_connection (data->response); assert_int_equal (connection, data->connection); g_object_unref (connection); } static void tpm2_get_trans_object_count_caps_fail (void **state) { TSS2_RC rc; uint32_t count = 0; test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_GetCapability, TPM2_RC_DISABLED); rc = tpm2_get_trans_object_count (data->tpm2, &count); assert_int_equal (rc, TPM2_RC_DISABLED); } static void tpm2_get_trans_object_count_success (void **state) { TSS2_RC rc; uint32_t count = 0; test_data_t *data = (test_data_t*)*state; TPML_HANDLE handle = { .count = 2, .handle = { 555, 444 }, }; will_return (__wrap_Tss2_Sys_GetCapability, TPM2_RC_SUCCESS); will_return (__wrap_Tss2_Sys_GetCapability, &handle); rc = tpm2_get_trans_object_count (data->tpm2, &count); assert_int_equal (rc, TPM2_RC_SUCCESS); assert_int_equal (count, handle.count); } static void tpm2_sapi_context_init_fail (void **state) { UNUSED_PARAM (state); TSS2_SYS_CONTEXT *ctx = NULL; Tcti *tcti = NULL; tcti = mock_tcti_setup (); will_return (__wrap_Tss2_Sys_Initialize, TPM2_RC_FAILURE); ctx = sapi_context_init (tcti); assert_null (ctx); g_clear_object (&tcti); } static void tpm2_context_load_test (void **state) { TSS2_RC rc; TPMS_CONTEXT context = { 0, }; TPM2_HANDLE handle = 0; test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_ContextLoad, TSS2_RC_SUCCESS); rc = tpm2_context_load (data->tpm2, &context, &handle); assert_int_equal (rc, TSS2_RC_SUCCESS); } static void tpm2_context_load_fail (void **state) { TSS2_RC rc; TPMS_CONTEXT context = { 0, }; TPM2_HANDLE handle = 0; test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_ContextLoad, TPM2_RC_FAILURE); rc = tpm2_context_load (data->tpm2, &context, &handle); assert_int_equal (rc, TPM2_RC_FAILURE); } static void tpm2_context_save_test (void **state) { TSS2_RC rc; TPMS_CONTEXT context = { 0, }; TPM2_HANDLE handle = 0; test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_ContextSave, TPM2_RC_SUCCESS); rc = tpm2_context_save (data->tpm2, handle, &context); assert_int_equal (rc, TPM2_RC_SUCCESS); } static void tpm2_context_flush_fail (void **state) { TSS2_RC rc; TPM2_HANDLE handle = 0; test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_FlushContext, TPM2_RC_FAILURE); rc = tpm2_context_flush (data->tpm2, handle); assert_int_equal (rc, TPM2_RC_FAILURE); } static void tpm2_context_saveflush_save_fail (void **state) { TSS2_RC rc; TPMS_CONTEXT context = { 0, }; TPM2_HANDLE handle = 0; test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_ContextSave, TPM2_RC_FAILURE); rc = tpm2_context_saveflush (data->tpm2, handle, &context); assert_int_equal (rc, TPM2_RC_FAILURE); } static void tpm2_context_saveflush_flush_fail (void **state) { TSS2_RC rc; TPMS_CONTEXT context = { 0, }; TPM2_HANDLE handle = 0; test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_ContextSave, TPM2_RC_SUCCESS); will_return (__wrap_Tss2_Sys_FlushContext, TPM2_RC_FAILURE); rc = tpm2_context_saveflush (data->tpm2, handle, &context); assert_int_equal (rc, TPM2_RC_FAILURE); } static void tpm2_flush_all_unlocked_getcap_fail (void **state) { TSS2_RC rc; TSS2_SYS_CONTEXT *sys_ctx = (TSS2_SYS_CONTEXT*)0x1; test_data_t *data = (test_data_t*)*state; will_return (__wrap_Tss2_Sys_GetCapability, TPM2_RC_FAILURE); rc = tpm2_flush_all_unlocked (data->tpm2, sys_ctx, TPM2_ACTIVE_SESSION_FIRST, TPM2_ACTIVE_SESSION_LAST); assert_int_equal (rc, TPM2_RC_FAILURE); } static void tpm2_flush_all_unlocked_flush_fail (void **state) { TSS2_RC rc; TSS2_SYS_CONTEXT *sys_ctx = (TSS2_SYS_CONTEXT*)0x1; test_data_t *data = (test_data_t*)*state; TPML_HANDLE handle = { .count = 1, .handle = { 555 }, }; will_return (__wrap_Tss2_Sys_GetCapability, TPM2_RC_SUCCESS); will_return (__wrap_Tss2_Sys_GetCapability, &handle); will_return (__wrap_Tss2_Sys_FlushContext, TPM2_RC_FAILURE); rc = tpm2_flush_all_unlocked (data->tpm2, sys_ctx, TPM2_ACTIVE_SESSION_FIRST, TPM2_ACTIVE_SESSION_LAST); assert_int_equal (rc, TPM2_RC_SUCCESS); } int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown (tpm2_type_test, tpm2_setup, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_startup_fail, tpm2_setup, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_init_tpm_test, tpm2_setup, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_init_tpm_initialized, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_init_tpm_startup_fail, tpm2_setup, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_get_max_response_test, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_lock_test, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_send_command_tcti_transmit_fail_test, tpm2_setup_with_command, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_send_command_tcti_receive_fail_test, tpm2_setup_with_command, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_send_command_success, tpm2_setup_with_command, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_get_trans_object_count_caps_fail, tpm2_setup_with_command, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_get_trans_object_count_success, tpm2_setup_with_command, tpm2_teardown), cmocka_unit_test (tpm2_sapi_context_init_fail), cmocka_unit_test_setup_teardown (tpm2_context_load_test, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_context_load_fail, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_context_save_test, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_context_flush_fail, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_context_saveflush_save_fail, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_context_saveflush_flush_fail, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_flush_all_unlocked_getcap_fail, tpm2_setup_with_init, tpm2_teardown), cmocka_unit_test_setup_teardown (tpm2_flush_all_unlocked_flush_fail, tpm2_setup_with_init, tpm2_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/tss2-tcti-tabrmd_unit.c000066400000000000000000000757361401030142600207600ustar00rootroot00000000000000/* 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 #include #include "tabrmd.h" #include "tss2-tcti-tabrmd.h" #include "tcti-tabrmd-priv.h" #include "tpm2-header.h" #include "util.h" #if defined(__linux__) || defined(__unix__) || defined(__APPLE__) #define TSS2_TCTI_POLL_HANDLE_ZERO_INIT { .fd = 0, .events = 0, .revents = 0 } #else #define TSS2_TCTI_POLL_HANDLE_ZERO_INIT { 0 } #endif /* * when given an NULL context and a pointer to a size_t, set the size_t * parameter to the size of the TSS2_TCTI_TABRMD_CONTEXT structure. */ static void tcti_tabrmd_init_size_test (void **state) { size_t tcti_size; UNUSED_PARAM(state); Tss2_Tcti_Tabrmd_Init (NULL, &tcti_size, NULL); assert_int_equal (tcti_size, sizeof (TSS2_TCTI_TABRMD_CONTEXT)); } /* * when given a NULL context and a pointer to size_t, the init function * returns TSS2_RC_SUCCESS */ static void tcti_tabrmd_init_success_return_value_test (void **state) { size_t tcti_size; TSS2_RC ret; UNUSED_PARAM(state); ret = Tss2_Tcti_Tabrmd_Init (NULL, &tcti_size, NULL); assert_int_equal (ret, TSS2_RC_SUCCESS); } /* * when given NULL pointers for both parameters the init function returns * an error indicating that the values are bad (TSS2_TCTI_RC_BAD_VALUE) */ static void tcti_tabrmd_init_allnull_is_bad_value_test (void **state) { TSS2_RC ret; UNUSED_PARAM(state); ret = Tss2_Tcti_Tabrmd_Init (NULL, NULL, NULL); assert_int_equal (ret, TSS2_TCTI_RC_BAD_VALUE); } /* * Ensure that the standard Tss2_Tcti_Info discovery function returns a * reference to the config structure. */ static void tcti_tabrmd_info_test (void **state) { const TSS2_TCTI_INFO *tcti_info; UNUSED_PARAM(state); /* * This function isn't meant to be called directly. We do it here for the * test coverage */ tcti_info = Tss2_Tcti_Info (); assert_non_null (tcti_info); assert_non_null (tcti_info->init); } /* * Ensure that when we pass the string "session" to the * tabrmd_bus_type_from_str returns G_BUS_TYPE_SESSION. */ static void tcti_tabrmd_bus_type_from_str_session_test (void **state) { UNUSED_PARAM(state); assert_int_equal (tabrmd_bus_type_from_str ("session"), G_BUS_TYPE_SESSION); } /* * Ensure that when we pass the string "system" to the * tabrmd_bus_type_from_str returns G_BUS_TYPE_SYSTEM. */ static void tcti_tabrmd_bus_type_from_str_system_test (void **state) { UNUSED_PARAM(state); assert_int_equal (tabrmd_bus_type_from_str ("system"), G_BUS_TYPE_SYSTEM); } /* * Ensure that when we pass an unexpected string to the * tabrmd_bus_type_from_str returns G_BUS_TYPE_NONE. */ static void tcti_tabrmd_bus_type_from_str_bad_test (void **state) { UNUSED_PARAM(state); assert_int_equal (tabrmd_bus_type_from_str ("foobar"), G_BUS_TYPE_NONE); } /* * Ensure that when we pass the key "bus_name" and value "any string" * to tabrmd_kv_callback that it returns an RC indicating success while * the conf structure 'bus_name' field is set to the value string. */ static void tcti_tabrmd_kv_callback_name_test (void **state) { tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; key_value_t key_value = { .key = "bus_name", .value = "foo.bar", }; TSS2_RC rc; UNUSED_PARAM(state); rc = tabrmd_kv_callback (&key_value, &conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_string_equal ("foo.bar", conf.bus_name); } /* * Ensure that when we pass the key "bus_type" and the value "system" * to tabrmd_kv_callback that it returns an RC indicating success while * the conf structure 'bus_type' field is set to G_BUS_TYPE_SYSTEM. */ static void tcti_tabrmd_kv_callback_type_good_test (void **state) { tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; key_value_t key_value = { .key = "bus_type", .value = "system", }; TSS2_RC rc; UNUSED_PARAM(state); rc = tabrmd_kv_callback (&key_value, &conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (conf.bus_type, G_BUS_TYPE_SYSTEM); } /* * Ensure that when we pass the key "bus_type" and with an invalid value * string (not "system" or "session") that it returns the BAD_VALUE RC and * sets the 'bus_type' field of the conf structure to * G_BUS_TYPE_NONE. */ static void tcti_tabrmd_kv_callback_type_bad_test (void **state) { tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; key_value_t key_value = { .key = "bus_type", .value = "foo", }; TSS2_RC rc; UNUSED_PARAM(state); rc = tabrmd_kv_callback (&key_value, &conf); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); assert_int_equal (conf.bus_type, G_BUS_TYPE_NONE); } /* * Ensure that when we pass an invalid key (not 'bus_type' or 'bus_name') * that it returns an RC indicating BAD_VALUE. */ static void tcti_tabrmd_kv_callback_bad_key_test (void **state) { tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; key_value_t key_value = { .key = "foo", .value = "bar", }; TSS2_RC rc; UNUSED_PARAM(state); rc = tabrmd_kv_callback (&key_value, &conf); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); } /* * Ensure that a common config string selecting the session bus with * a user supplied name is parsed correctly. */ static void tcti_tabrmd_conf_parse_named_session_test (void **state) { TSS2_RC rc; tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; char conf_str[] = "bus_type=session,bus_name=com.example.Session"; UNUSED_PARAM(state); rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_string_equal (conf.bus_name, "com.example.Session"); assert_int_equal (conf.bus_type, G_BUS_TYPE_SESSION); } /* * Ensure that a common config string selecting the system bus with * a user supplied name is parsed correctly. */ static void tcti_tabrmd_conf_parse_named_system_test (void **state) { TSS2_RC rc; tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; char conf_str[] = "bus_type=system,bus_name=com.example.System"; UNUSED_PARAM(state); rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_string_equal (conf.bus_name, "com.example.System"); assert_int_equal (conf.bus_type, G_BUS_TYPE_SYSTEM); } /* * Ensure that an unknown bus_type string results in the appropriate RC. */ static void tcti_tabrmd_conf_parse_bad_type_test (void **state) { TSS2_RC rc; tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; char conf_str[] = "bus_type=foobar"; UNUSED_PARAM(state); rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); } /* * Ensure that a config string that omits the bus_name results in a conf * structure with the associated field set to the default. */ static void tcti_tabrmd_conf_parse_no_name_test (void **state) { TSS2_RC rc; tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; char conf_str[] = "bus_type=session"; UNUSED_PARAM(state); rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_string_equal (conf.bus_name, TABRMD_DBUS_NAME_DEFAULT); assert_int_equal (conf.bus_type, G_BUS_TYPE_SESSION); } /* * Ensure that a config string that omits the bus_type results in a conf * structure with the associated field set to the default. */ static void tcti_tabrmd_conf_parse_no_type_test (void **state) { TSS2_RC rc; tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; char conf_str[] = "bus_name=com.example.FooBar"; UNUSED_PARAM(state); rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_string_equal (conf.bus_name, "com.example.FooBar"); assert_int_equal (conf.bus_type, TABRMD_DBUS_TYPE_DEFAULT); } /* * Ensure that a missing value results in the appropriate RC. */ static void tcti_tabrmd_conf_parse_no_value_test (void **state) { TSS2_RC rc; tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; char conf_str[] = "bus_name="; UNUSED_PARAM(state); rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); } /* * Ensure that a missing key results in the appropriate RC */ static void tcti_tabrmd_conf_parse_no_key_test (void **state) { TSS2_RC rc; tabrmd_conf_t conf = TABRMD_CONF_INIT_DEFAULT; char conf_str[] = "=some-string"; UNUSED_PARAM(state); rc = parse_key_value_string (conf_str, tabrmd_kv_callback, &conf); assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); } /* * This mock function is used to insert error conditions in the TCTI * initialization function. The function mocked is generated by * gdbus-codegen from the dbus xml. Much of the functionality exposed by * that code uses a 'proxy' object used to interact with the remote * object (tabrmd in this case). The function mocked here is the one used * to connect to the tabrmd. * * The only tricky part to this function is the handling of the GError * object. If the function being mocked failes it returns NULL *and* * populates the error parameter passed by the caller with a GError object * describing the failure. The first item that *must* be on the mock stack * is the TctiTabrmd* being returned. If this is NULL then there MUST be * a second object on the mock stack and this MUST be a GError object. The * caller will g_clear_error this object for us. */ TctiTabrmd * __wrap_tcti_tabrmd_proxy_new_for_bus_sync ( GBusType bus_type, GDBusProxyFlags flags, const gchar *name, const gchar *object_path, GCancellable *cancellable, GError **error) { UNUSED_PARAM (bus_type); UNUSED_PARAM (flags); UNUSED_PARAM (name); UNUSED_PARAM (object_path); UNUSED_PARAM (cancellable); TctiTabrmd* proxy = mock_type (TctiTabrmd*); if (proxy == NULL && error != NULL) { *error = mock_type (GError*); } return proxy; } /* * Force failure acquiring proxy object. */ static void tcti_tabrmd_proxy_new_fail (void **state) { UNUSED_PARAM (state); size_t size = sizeof (TSS2_TCTI_TABRMD_CONTEXT); uint8_t buf [sizeof (TSS2_TCTI_TABRMD_CONTEXT)] = { 0 }; TSS2_RC rc = TSS2_RC_SUCCESS; will_return (__wrap_tcti_tabrmd_proxy_new_for_bus_sync, NULL); will_return (__wrap_tcti_tabrmd_proxy_new_for_bus_sync, g_error_new_literal (1, 1, __func__)); rc = Tss2_Tcti_Tabrmd_Init ((TSS2_TCTI_CONTEXT*)buf, &size, NULL); assert_int_equal (rc, TSS2_TCTI_RC_NO_CONNECTION); } /* * This is a mock function to control return values from the connection * creation logic that invokes the DBus "CreateConnection" function exposed * by the tabrmd. This is called by the tcti as part of initializing the * TCTI context. */ GVariant * __wrap_g_dbus_proxy_call_with_unix_fd_list_sync ( GDBusProxy *proxy, const gchar *method_name, GVariant *parameters, GDBusCallFlags flags, gint timeout_msec, GUnixFDList *fd_list, GUnixFDList **out_fd_list, GCancellable *cancellable, GError **error) { GVariant *variant_array[2] = { 0 }, *variant_tuple; gint client_fd; guint64 id; UNUSED_PARAM(proxy); UNUSED_PARAM(method_name); UNUSED_PARAM(parameters); UNUSED_PARAM(flags); UNUSED_PARAM(timeout_msec); UNUSED_PARAM(fd_list); UNUSED_PARAM(cancellable); UNUSED_PARAM(error); client_fd = mock_type (gint); id = mock_type (guint64); *out_fd_list = g_unix_fd_list_new_from_array (&client_fd, 1); variant_array[0] = g_variant_new_fixed_array (G_VARIANT_TYPE ("h"), &client_fd, 1, sizeof (gint32)); variant_array[1] = g_variant_new_uint64 (id); variant_tuple = g_variant_new_tuple (variant_array, 2); return variant_tuple; } /* * This is a mock function to control behavior of the * tcti_tabrmd_call_cancel_sync function. This is a function generated from * the DBus xml specification. It returns only a TSS2_RC. */ gboolean __wrap_tcti_tabrmd_call_cancel_sync ( TctiTabrmd *proxy, guint64 arg_id, guint *out_return_code, GCancellable *cancellable, GError **error) { UNUSED_PARAM(proxy); UNUSED_PARAM(arg_id); UNUSED_PARAM(cancellable); UNUSED_PARAM(error); *out_return_code = mock_type (guint); return mock_type (gboolean); } /* * This is a mock function to control behavior of the * tcti_tabrmd_call_set_locality_sync function. This is a function generated from * the DBus xml specification. It returns only a TSS2_RC. */ gboolean __wrap_tcti_tabrmd_call_set_locality_sync ( TctiTabrmd *proxy, guint64 arg_id, guchar arg_locality, guint *out_return_code, GCancellable *cancellable, GError **error) { UNUSED_PARAM(proxy); UNUSED_PARAM(arg_id); UNUSED_PARAM(arg_locality); UNUSED_PARAM(cancellable); UNUSED_PARAM(error); *out_return_code = mock_type (guint); return mock_type (gboolean); } /* * Structure to hold data relevant to tabrmd TCTI unit tests. */ typedef struct { guint64 id; gint client_fd; gint server_fd; TSS2_TCTI_CONTEXT *context; TctiTabrmdProxy *proxy; } data_t; /* * This is the setup function used to create the TCTI context structure for * tests that require the initialization be done before the test can be * executed. It *must* be paired with a call to the teardown function. */ static int tcti_tabrmd_setup (void **state) { data_t *data; TSS2_RC ret = TSS2_RC_SUCCESS; size_t tcti_size = 0; gint fds [2]; guint64 id = 666; data = calloc (1, sizeof (data_t)); ret = Tss2_Tcti_Tabrmd_Init (NULL, &tcti_size, NULL); if (ret != TSS2_RC_SUCCESS) { free (data); printf ("tss2_tcti_tabrmd_init failed: %d\n", ret); return 1; } data->context = calloc (1, tcti_size); if (data->context == NULL) { free (data); perror ("calloc"); return 1; } data->proxy = calloc (1, sizeof (TctiTabrmdProxy)); will_return (__wrap_tcti_tabrmd_proxy_new_for_bus_sync, data->proxy); g_debug ("preparing g_dbus_proxy_call_with_unix_fd_list_sync mock wrapper"); assert_int_equal (socketpair (PF_LOCAL, SOCK_STREAM, 0, fds), 0); data->client_fd = fds [0]; data->server_fd = fds [1]; will_return (__wrap_g_dbus_proxy_call_with_unix_fd_list_sync, data->client_fd); data->id = id; will_return (__wrap_g_dbus_proxy_call_with_unix_fd_list_sync, id); ret = Tss2_Tcti_Tabrmd_Init (data->context, &tcti_size, "bus_type=session"); assert_int_equal (ret, TSS2_RC_SUCCESS); *state = data; return 0; } /* * This is a teardown function to deallocate / cleanup all resources * associated with these tests. */ static int tcti_tabrmd_teardown (void **state) { data_t *data = *state; tss2_tcti_tabrmd_finalize (data->context); close (data->client_fd); close (data->server_fd); if (data->context) free (data->context); if (data->proxy) free (data->proxy); free (data); return 0; } /* * Ensure that after initialization the 'magic' value in the TCTI structure * is the one that we expect. */ static void tcti_tabrmd_magic_test (void **state) { data_t *data = *state; assert_int_equal (TSS2_TCTI_MAGIC (data->context), TSS2_TCTI_TABRMD_MAGIC); } /* * Ensure that after initialization the 'version' value in the TCTI structure * is the one that we expect. */ static void tcti_tabrmd_version_test (void **state) { data_t *data = *state; assert_int_equal (TSS2_TCTI_VERSION (data->context), TSS2_TCTI_TABRMD_VERSION); } /* * Ensure that after initialization the 'id' value set for the connection is * the one taht we expect. */ static void tcti_tabrmd_init_success_test (void **state) { data_t *data = *state; assert_int_equal (data->id, TSS2_TCTI_TABRMD_ID (data->context)); } /* * These are a series of tests to ensure that the exposed TCTI functions * return the appropriate RC when passed NULL contexts. * * NOTE: These RCs come from the tss2_tcti.h header and not the tabrmd TCTI * code. Still we test for it here as it's a useful reminder that the * case is covered. Also there's a bug in the TSS2 currently as these * should return BAD_REFERENCE, not BAD_CONTEXT */ static void tcti_tabrmd_transmit_null_context_test (void **state) { TSS2_RC rc; UNUSED_PARAM(state); rc = tss2_tcti_tabrmd_transmit (NULL, 5, NULL); assert_int_equal (rc, TSS2_TCTI_RC_BAD_REFERENCE); } static void tcti_tabrmd_cancel_null_context_test (void **state) { TSS2_RC rc; UNUSED_PARAM(state); rc = tss2_tcti_tabrmd_cancel (NULL); assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT); } static void tcti_tabrmd_get_poll_handles_null_context_test (void **state) { TSS2_RC rc; UNUSED_PARAM(state); rc = tss2_tcti_tabrmd_get_poll_handles (NULL, NULL, NULL); assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT); } static void tcti_tabrmd_set_locality_null_context_test (void **state) { TSS2_RC rc; UNUSED_PARAM(state); rc = tss2_tcti_tabrmd_set_locality (NULL, 5); assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT); } /* * This test makes a single call to the transmit function in the the * TSS2_TCTI_CONTEXT function pointer table. It sends a fixed 12 byte TPM * command buffer: * tag: TPM2_ST_NO_SESSIONS * size: 12 bytes * command: 0x0 * data: 0x01, 0x02 */ static void tcti_tabrmd_transmit_success_test (void **state) { data_t *data = *state; uint8_t command_in [] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}; size_t size = sizeof (command_in); uint8_t command_out [sizeof (command_in)] = { 0 }; TSS2_RC rc; ssize_t ret; rc = tss2_tcti_tabrmd_transmit (data->context, size, command_in); assert_int_equal (rc, TSS2_RC_SUCCESS); ret = read (data->server_fd, command_out, size); assert_int_equal (ret, size); ASSERT_NON_NULL(data); ASSERT_NON_NULL(data->context); assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context), TABRMD_STATE_RECEIVE); assert_memory_equal (command_in, command_out, size); } /* * This test ensures that the magic value in the context structure is checked * before the transmit function executes and that the RC is what we expect. */ static void tcti_tabrmd_transmit_bad_magic_test (void **state) { data_t *data = *state; TSS2_RC rc; uint8_t command [12] = { 0 }; size_t size = 12; TSS2_TCTI_MAGIC (data->context) = 1; rc = tss2_tcti_tabrmd_transmit (data->context, size, command); assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT); assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context), TABRMD_STATE_TRANSMIT); } /* * This test ensures that the version in the context structure is checked * before the transmit function executes and that the RC is what we expect. */ static void tcti_tabrmd_transmit_bad_version_test (void **state) { data_t *data = *state; TSS2_RC rc; uint8_t command [12] = { 0 }; size_t size = 12; TSS2_TCTI_VERSION (data->context) = -1; rc = tss2_tcti_tabrmd_transmit (data->context, size, command); assert_int_equal (rc, TSS2_TCTI_RC_BAD_CONTEXT); assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context), TABRMD_STATE_TRANSMIT); } /* * This test sets the state to RECEIVE and then calls transmit. This should * produce a BAD_SEQUENCE error. */ static void tcti_tabrmd_transmit_bad_sequence_receive_test (void **state) { data_t *data = *state; TSS2_RC rc; uint8_t command [12] = { 0 }; size_t size = 12; TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_RECEIVE; rc = tss2_tcti_tabrmd_transmit (data->context, size, command); assert_int_equal (rc, TSS2_TCTI_RC_BAD_SEQUENCE); assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context), TABRMD_STATE_RECEIVE); } /* * This test sets the state to FINAL and then calls transmit. This should * produce a BAD_SEQUENCE error. */ static void tcti_tabrmd_transmit_bad_sequence_final_test (void **state) { data_t *data = *state; TSS2_RC rc; uint8_t command [12] = { 0 }; size_t size = 12; TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_FINAL; rc = tss2_tcti_tabrmd_transmit (data->context, size, command); assert_int_equal (rc, TSS2_TCTI_RC_BAD_SEQUENCE); assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context), TABRMD_STATE_FINAL); } /* * This setup function is a thin wrapper around the main setup. The only * additional thing done is to set the state machine to the RECEIVE state * to simplify testing the receive function. */ static int tcti_tabrmd_receive_setup (void **state) { data_t *data = NULL; tcti_tabrmd_setup (state); data = *state; TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_RECEIVE; return 0; } /* * This test sets up the call_cancel mock function to return values * indicating success. It then ensures that an invocation of the cancel * convenience macro invokes the underlying function correctly. */ static void tcti_tabrmd_cancel_test (void **state) { data_t *data = *state; TSS2_RC rc; will_return (__wrap_tcti_tabrmd_call_cancel_sync, TSS2_RC_SUCCESS); will_return (__wrap_tcti_tabrmd_call_cancel_sync, TRUE); rc = tss2_tcti_tabrmd_cancel (data->context); assert_int_equal (rc, TSS2_RC_SUCCESS); ASSERT_NON_NULL(data); ASSERT_NON_NULL(data->context); assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context), TABRMD_STATE_RECEIVE); } /* * This test initializes the TCTI context state to TRANSMIT and then calls * the cancel function. This should produce a BAD_SEQUENCE error. */ static void tcti_tabrmd_cancel_bad_sequence_test (void **state) { data_t *data = *state; TSS2_RC rc; TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_TRANSMIT; rc = tss2_tcti_tabrmd_cancel (data->context); assert_int_equal (rc, TSS2_TCTI_RC_BAD_SEQUENCE); assert_int_equal (TSS2_TCTI_TABRMD_STATE (data->context), TABRMD_STATE_TRANSMIT); } /* * This test ensures that when passed a NULL reference for both the handles * and num_handles parameters the appropriate RC is returned. */ static void tcti_tabrmd_get_poll_handles_all_null_test (void **state) { data_t *data = *state; TSS2_RC rc; rc = tss2_tcti_tabrmd_get_poll_handles (data->context, NULL, NULL); assert_int_equal (rc, TSS2_TCTI_RC_BAD_REFERENCE); } /* * This test invokes the get_poll_handles function passing in a NULL pointer * to the handles variable and a valid reference to the count. It then ensures * that the result is some number of handles in num_handles out parameter. */ static void tcti_tabrmd_get_poll_handles_count_test (void **state) { data_t *data = *state; size_t num_handles = 0; TSS2_RC rc; rc = tss2_tcti_tabrmd_get_poll_handles (data->context, NULL, &num_handles); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_true (num_handles > 0); } /* * This test ensures that when passed a valid handles reference with a * num_handles parameter that indicates an insufficient number of elements * in the handles array, that we get back the appropriate RC. */ static void tcti_tabrmd_get_poll_handles_bad_handles_count_test (void **state) { data_t *data = *state; TSS2_TCTI_POLL_HANDLE handles[5] = { TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT }; size_t num_handles = 0; TSS2_RC rc; rc = tss2_tcti_tabrmd_get_poll_handles (data->context, handles, &num_handles); assert_int_equal (rc, TSS2_TCTI_RC_INSUFFICIENT_BUFFER); } /* * This test invokes the get_poll_handles function passing in valid pointer * to an array for the handles and a valid reference to the count. It then * ensures that the result is some number of handles in num_handles out * parameter as well as valid handles in the array. */ static void tcti_tabrmd_get_poll_handles_handles_test (void **state) { data_t *data = *state; TSS2_TCTI_POLL_HANDLE handles[5] = { TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT, TSS2_TCTI_POLL_HANDLE_ZERO_INIT }; size_t num_handles = 5; TSS2_RC rc; int fd = -1; rc = tss2_tcti_tabrmd_get_poll_handles (data->context, handles, &num_handles); assert_int_equal (rc, TSS2_RC_SUCCESS); assert_int_equal (1, num_handles); ASSERT_NON_NULL(data); ASSERT_NON_NULL(data->context); fd = TSS2_TCTI_TABRMD_FD (data->context); assert_int_equal (handles [0].fd, fd); } /* * This test sets up the call_set_locality mock function to return values * indicating success. It then ensures that an invocation of the set_locality * convenience macro invokes the underlying function correctly. */ static void tcti_tabrmd_set_locality_test (void **state) { data_t *data = *state; uint8_t locality = 0; TSS2_RC rc; will_return (__wrap_tcti_tabrmd_call_set_locality_sync, TSS2_RC_SUCCESS); will_return (__wrap_tcti_tabrmd_call_set_locality_sync, TRUE); rc = tss2_tcti_tabrmd_set_locality (data->context, locality); assert_int_equal (rc, TSS2_RC_SUCCESS); } /* * This test invokes the set_locality function with the context in the RECEIVE * state. This should produce a BAD_SEQUENCE error. */ static void tcti_tabrmd_set_locality_bad_sequence_test (void **state) { data_t *data = *state; uint8_t locality = 0; TSS2_RC rc; TSS2_TCTI_TABRMD_STATE (data->context) = TABRMD_STATE_RECEIVE; rc = tss2_tcti_tabrmd_set_locality (data->context, locality); assert_int_equal (rc, TSS2_TCTI_RC_BAD_SEQUENCE); } int main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test (tcti_tabrmd_init_size_test), cmocka_unit_test (tcti_tabrmd_init_success_return_value_test), cmocka_unit_test (tcti_tabrmd_init_allnull_is_bad_value_test), cmocka_unit_test (tcti_tabrmd_proxy_new_fail), cmocka_unit_test_setup_teardown (tcti_tabrmd_init_success_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test (tcti_tabrmd_info_test), cmocka_unit_test (tcti_tabrmd_bus_type_from_str_session_test), cmocka_unit_test (tcti_tabrmd_bus_type_from_str_system_test), cmocka_unit_test (tcti_tabrmd_bus_type_from_str_bad_test), cmocka_unit_test (tcti_tabrmd_kv_callback_name_test), cmocka_unit_test (tcti_tabrmd_kv_callback_type_good_test), cmocka_unit_test (tcti_tabrmd_kv_callback_type_bad_test), cmocka_unit_test (tcti_tabrmd_kv_callback_bad_key_test), cmocka_unit_test (tcti_tabrmd_conf_parse_named_session_test), cmocka_unit_test (tcti_tabrmd_conf_parse_named_system_test), cmocka_unit_test (tcti_tabrmd_conf_parse_bad_type_test), cmocka_unit_test (tcti_tabrmd_conf_parse_no_name_test), cmocka_unit_test (tcti_tabrmd_conf_parse_no_type_test), cmocka_unit_test (tcti_tabrmd_conf_parse_no_value_test), cmocka_unit_test (tcti_tabrmd_conf_parse_no_key_test), cmocka_unit_test_setup_teardown (tcti_tabrmd_magic_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_version_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test (tcti_tabrmd_transmit_null_context_test), cmocka_unit_test (tcti_tabrmd_cancel_null_context_test), cmocka_unit_test (tcti_tabrmd_get_poll_handles_null_context_test), cmocka_unit_test (tcti_tabrmd_set_locality_null_context_test), cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_success_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_bad_magic_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_bad_version_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_bad_sequence_receive_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_transmit_bad_sequence_final_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_cancel_test, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_cancel_bad_sequence_test, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_get_poll_handles_all_null_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_get_poll_handles_bad_handles_count_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_get_poll_handles_count_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_get_poll_handles_handles_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_set_locality_test, tcti_tabrmd_setup, tcti_tabrmd_teardown), cmocka_unit_test_setup_teardown (tcti_tabrmd_set_locality_bad_sequence_test, tcti_tabrmd_receive_setup, tcti_tabrmd_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); } tpm2-abrmd-2.4.0/test/util_unit.c000066400000000000000000000613141401030142600166150ustar00rootroot00000000000000/* 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 "tpm2-header.h" #define MAX_BUF 4096 #define READ_SIZE UTIL_BUF_SIZE #define WRITE_SIZE 10 #define UTIL_UNIT_ERROR util_unit_error_quark () GQuark util_unit_error_quark (void) { return g_quark_from_static_string ("util-unit-error-quark"); } ssize_t __wrap_g_output_stream_write (GOutputStream *ostream, const void *buf, size_t count, GCancellable *cancellable, GError **error) { GError *error_tmp = mock_type (GError*); UNUSED_PARAM(ostream); UNUSED_PARAM(buf); UNUSED_PARAM(count); UNUSED_PARAM(cancellable); if (error_tmp != NULL && error != NULL) { *error = error_tmp; } return mock_type (ssize_t); } void write_in_one (void **state) { ssize_t written; UNUSED_PARAM(state); will_return (__wrap_g_output_stream_write, NULL); will_return (__wrap_g_output_stream_write, WRITE_SIZE); written = write_all (0, NULL, WRITE_SIZE); assert_int_equal (written, WRITE_SIZE); } void write_in_two (void **state) { ssize_t written; UNUSED_PARAM(state); will_return (__wrap_g_output_stream_write, NULL); will_return (__wrap_g_output_stream_write, 5); will_return (__wrap_g_output_stream_write, NULL); will_return (__wrap_g_output_stream_write, 5); written = write_all (0, NULL, WRITE_SIZE); assert_int_equal (written, WRITE_SIZE); } void write_in_three (void **state) { ssize_t written; UNUSED_PARAM(state); will_return (__wrap_g_output_stream_write, NULL); will_return (__wrap_g_output_stream_write, 3); will_return (__wrap_g_output_stream_write, NULL); will_return (__wrap_g_output_stream_write, 3); will_return (__wrap_g_output_stream_write, NULL); will_return (__wrap_g_output_stream_write, 4); written = write_all (0, NULL, WRITE_SIZE); assert_int_equal (written, WRITE_SIZE); } void write_error (void **state) { ssize_t written; GError *error; UNUSED_PARAM(state); /* this is free'd by the 'write_all' function */ error = g_error_new (UTIL_UNIT_ERROR, G_IO_ERROR_WOULD_BLOCK, "g-io-error-would-block"); will_return (__wrap_g_output_stream_write, error); will_return (__wrap_g_output_stream_write, -1); written = write_all (0, NULL, WRITE_SIZE); assert_int_equal (written, -1); } void write_zero (void **state) { ssize_t written; UNUSED_PARAM(state); will_return (__wrap_g_output_stream_write, NULL); will_return (__wrap_g_output_stream_write, 0); written = write_all (0, NULL, WRITE_SIZE); assert_int_equal (written, 0); } /* * mock 'read' function: This function expects 3 things to be on the mock * queue: * input buffer * offset into input buffer where read starts * GError * return value */ ssize_t __wrap_g_input_stream_read (GInputStream *istream, gint *buf, size_t count, GCancellable *cancellable, GError **error) { uint8_t *buf_in = mock_type (uint8_t*); size_t buf_index = mock_type (size_t); GError *error_in = mock_type (GError*); ssize_t ret = mock_type (ssize_t); UNUSED_PARAM(istream); UNUSED_PARAM(cancellable); g_debug ("%s", __func__); if (error_in != NULL && error != NULL) { *error = error_in; } /* be careful comparing signed to unsigned values */ if (ret > 0) { assert_true (ret <= (ssize_t)count); memcpy (buf, &buf_in [buf_index], ret); } return ret; } /* global static input array used by read_data* tests */ static uint8_t buf_in [MAX_BUF] = { /* header */ 0x80, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, /* body */ 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef }; /* * Data structure to hold data for read tests. */ typedef struct { GIOStream *iostream; size_t index; uint8_t buf_out [MAX_BUF]; size_t buf_size; } data_t; static int read_data_setup (void **state) { data_t *data; data = calloc (1, sizeof (data_t)); data->buf_size = 26; data->iostream = (GIOStream*)1; *state = data; return 0; } static int read_data_teardown (void **state) { data_t *data = *state; if (data != NULL) { free (data); } return 0; } /* */ static void create_socket_pair_success_test (void **state) { int ret, client_fd, server_fd, flags = 0; UNUSED_PARAM(state); #if !defined(__FreeBSD__) flags = O_CLOEXEC; #endif ret = create_socket_pair (&client_fd, &server_fd, flags); if (ret == -1) g_error ("create_pipe_pair failed: %s", strerror (errno)); close (client_fd); } /* * Simple call to read wrapper function. Returns exactly what we ask for. * We check to be sure return value is 0, the index variable is updated * properly and that the output data buffer is the same as the input. */ static void read_data_success_test (void **state) { data_t *data = *state; int ret = 0; /* prime the wrap queue for a successful read */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, data->index); will_return (__wrap_g_input_stream_read, NULL); will_return (__wrap_g_input_stream_read, data->buf_size); ret = read_data (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, 0); assert_int_equal (data->index, data->buf_size); assert_memory_equal (data->buf_out, buf_in, data->buf_size); } /* * This tests a simple error case where read returns -1 and errno is set to * EIO. In this case the index should remain unchanged (0). */ static void read_data_error_test (void **state) { data_t *data = *state; int ret = 0; GError *error; error = g_error_new (UTIL_UNIT_ERROR, G_IO_ERROR_WOULD_BLOCK, "g-io-error-would-block"); will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, data->index); will_return (__wrap_g_input_stream_read, error); will_return (__wrap_g_input_stream_read, -1); ret = read_data (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, G_IO_ERROR_WOULD_BLOCK); assert_int_equal (data->index, 0); } /* * This test covers the 'short read'. A single call to 'read_data' results in * a the first read returning half the data (not an error), followed by * the second half obtained through a second 'read' call. */ static void read_data_short_success_test (void **state) { data_t *data = *state; int ret = 0; /* prime the wrap queue for a short read (half the requested size) */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, data->index); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, data->buf_size / 2); /* do it again for the second half of the read */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, data->buf_size / 2); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, data->buf_size / 2); ret = read_data (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, 0); assert_int_equal (data->index, data->buf_size); assert_memory_equal (data->buf_out, buf_in, data->buf_size); } /* * This test covers a short read followed by a failing 'read'. NOTE: half of * the buffer is filled due to the first read succeeding. */ static void read_data_short_err_test (void **state) { data_t *data = *state; int ret = 0; GError *error; /* * Prime the wrap queue for another short read, again half the requested * size. */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, data->index); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, data->buf_size / 2); /* */ error = g_error_new (UTIL_UNIT_ERROR, G_IO_ERROR_WOULD_BLOCK, "g-io-error-would-block"); will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, data->buf_size / 2); will_return (__wrap_g_input_stream_read, error); will_return (__wrap_g_input_stream_read, -1); /* read the second half of the buffer, the index maintains the state */ ret = read_data (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, G_IO_ERROR_WOULD_BLOCK); assert_int_equal (data->index, data->buf_size / 2); assert_memory_equal (data->buf_out, buf_in, data->buf_size / 2); } /* * This test covers a single call returning EOF. This is signaled to the * through the return value which is -1. */ static void read_data_eof_test (void **state) { data_t *data = *state; int ret = 0; will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, data->index); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 0); ret = read_data (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, -1); assert_int_equal (data->index, 0); } /* * This test covers the common case when reading a tpm command / response * buffer. read_tpm_buffer first reads the header (10 bytes), extracts the * size field from the header, and then reads this many bytes into the * provided buffer. */ static void read_tpm_buf_success_test (void **state) { data_t *data = *state; int ret = 0; /* prime read to successfully produce the header */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 10); /* prime read to successfully produce the rest of the buffer */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 10); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, data->buf_size - 10); ret = read_tpm_buffer (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, 0); assert_int_equal (data->index, data->buf_size); assert_memory_equal (data->buf_out, buf_in, data->buf_size); } /* * This tests the code path where a header is successfully read and the size * field in the header is the size of the header. */ static void read_tpm_buf_header_only_success_test (void **state) { data_t *data = *state; int ret = 0; uint8_t buf [10] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00 }; /* prime read to successfully produce the header */ data->buf_size = 10; will_return (__wrap_g_input_stream_read, buf); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 10); ret = read_tpm_buffer (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, 0); assert_int_equal (data->index, data->buf_size); assert_memory_equal (data->buf_out, buf, data->buf_size); } /* * Test the condition where the header read has a size larger than the * provided buffer. */ static void read_tpm_buf_lt_header_test (void **state) { data_t *data = *state; int ret = 0; ret = read_tpm_buffer (NULL, &data->index, data->buf_out, 8); assert_int_equal (ret, EPROTO); assert_int_equal (data->index, 0); } static void read_tpm_buf_short_header_test (void **state) { data_t *data = *state; int ret = 0; GError *error; /* * Prime read to successfully produce 4 bytes. This is a short read to * exercise the error handling path in the function under test. */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 4); error = g_error_new (UTIL_UNIT_ERROR, G_IO_ERROR_WOULD_BLOCK, "g-io-error-would-block"); will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 4); will_return (__wrap_g_input_stream_read, error); will_return (__wrap_g_input_stream_read, -1); ret = read_tpm_buffer (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, G_IO_ERROR_WOULD_BLOCK); assert_int_equal (data->index, 4); assert_memory_equal (data->buf_out, buf_in, data->index); } /* * Test the condition where the header read has a size larger than the * provided buffer. */ static void read_tpm_buf_lt_body_test (void **state) { data_t *data = *state; int ret = 0; /* * Prime read to successfully produce 10 bytes. This is the number of * bytes that the first 'read' syscall is asked to produce (header). */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, data->index); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 10); ret = read_tpm_buffer (NULL, &data->index, data->buf_out, 11); assert_int_equal (ret, EPROTO); assert_int_equal (data->index, 10); assert_memory_equal (data->buf_out, buf_in, 10); } /* * Read the header in one go. The second call to 'read' will be an attempt to * read the body of the command. We setup the mock stuff such that we get a * short read, followed by the rest of the command. */ static void read_tpm_buf_short_body_test (void **state) { data_t *data = *state; int ret = 0; /* * Prime read to successfully produce 10 bytes. This is the number of * bytes that the first 'read' syscall is asked to produce (header). */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 10); /* Now cause a short read when getting the body of the buffer. */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 10); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 5); /* And then the rest of the buffer */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 15); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, data->buf_size - 15); ret = read_tpm_buffer (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, 0); assert_int_equal (data->index, data->buf_size); assert_memory_equal (data->buf_out, buf_in, data->buf_size); } /* * The following tests cover cases where the 'read_tpm_buffer' function is * called with the tpm buffer is partially populated (from previous calls to * same function) with the index value set appropriately. */ /* * This test sets up a buffer such that the first 4 bytes of the header have * already been populated and the index set accordingly. The read_tpm_buffer * function is then invoked as though it has bee previously interrupted. * This results in the last 6 bytes of the header being read, followed by * the rest of the buffer. */ static void read_tpm_buf_populated_header_half_test (void **state) { data_t *data = *state; int ret = 0; /* buffer already has 4 bytes of data */ memcpy (data->buf_out, buf_in, 4); /* prime read to successfully produce the header */ data->index = 4; /* read the last 6 bytes of the header */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 4); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 6); /* read the rest of the body (26 - 10) */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 10); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 16); ret = read_tpm_buffer (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, 0); assert_int_equal (data->index, data->buf_size); assert_memory_equal (data->buf_out, buf_in, data->buf_size); } /* * This test is a bit weird. We pass a buffer that has already had the header * populated and the index incremented to the end of the header. The call to * read_tpm_buffer is smart enough to know the header has already been read * so it extracts the size from the header. This is however the size of the * header and so the function returns before any read operation happens. */ static void read_tpm_buf_populated_header_only_test (void **state) { data_t *data = *state; int ret = 0; uint8_t buf [10] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00 }; /* prime read to successfully produce the header */ data->buf_size = 10; data->index = 10; memcpy (data->buf_out, buf, data->buf_size); ret = read_tpm_buffer (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, 0); assert_int_equal (data->index, data->buf_size); assert_memory_equal (data->buf_out, buf, data->buf_size); } /* * This test initializes a buffer as though it has already been populated * beyond the header (16 bytes) with the index set accordingly. The call to * read_tpm_buffer then picks up where it left off filling in the remainder * of the buffer. */ static void read_tpm_buf_populated_body_test (void **state) { data_t *data = *state; int ret = 0; memcpy (data->buf_out, buf_in, 16); /* prime read to successfully produce the header */ data->index = 16; will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 16); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 10); ret = read_tpm_buffer (NULL, &data->index, data->buf_out, data->buf_size); assert_int_equal (ret, 0); assert_int_equal (data->index, data->buf_size); assert_memory_equal (data->buf_out, buf_in, data->buf_size); } static void read_tpm_buf_alloc_success_test (void **state) { data_t *data = *state; uint8_t *buf; size_t buf_size; /* prime read to successfully produce the header */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 10); /* prime read to successfully produce the rest of the buffer */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 10); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, data->buf_size - 10); buf = read_tpm_buffer_alloc ((GInputStream*)1, &buf_size); assert_non_null (buf); assert_int_equal (buf_size, data->buf_size); assert_memory_equal (buf, buf_in, data->buf_size); g_free (buf); } static void read_tpm_buf_alloc_header_only_test (void **state) { data_t *data = *state; uint8_t buf [10] = { 0x80, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00 }; uint8_t *buf_out = NULL; /* prime read to successfully produce the header */ data->buf_size = 10; will_return (__wrap_g_input_stream_read, buf); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 10); buf_out = read_tpm_buffer_alloc ((GInputStream*)1, &data->buf_size); assert_non_null (buf_out); assert_int_equal (data->buf_size, TPM_HEADER_SIZE); assert_memory_equal (buf_out, buf, data->buf_size); g_free (buf_out); } static void read_tpm_buf_alloc_eof_test (void **state) { uint8_t *buf; size_t buf_size; UNUSED_PARAM(state); /* prime read to successfully produce the header */ will_return (__wrap_g_input_stream_read, buf_in); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 0); will_return (__wrap_g_input_stream_read, 0); buf = read_tpm_buffer_alloc ((GInputStream*)1, &buf_size); assert_null (buf); } gint main (void) { const struct CMUnitTest tests[] = { cmocka_unit_test (write_in_one), cmocka_unit_test (write_in_two), cmocka_unit_test (write_in_three), cmocka_unit_test (write_error), cmocka_unit_test (write_zero), cmocka_unit_test (create_socket_pair_success_test), /* read_data tests */ cmocka_unit_test_setup_teardown (read_data_success_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_data_short_success_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_data_short_err_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_data_error_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_data_eof_test, read_data_setup, read_data_teardown), /* read_tpm_buf tests */ cmocka_unit_test_setup_teardown (read_tpm_buf_success_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_header_only_success_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_short_header_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_lt_header_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_lt_body_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_short_body_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_populated_header_half_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_populated_header_only_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_populated_body_test, read_data_setup, read_data_teardown), /* read_tpm_buffer_alloc*/ cmocka_unit_test_setup_teardown (read_tpm_buf_alloc_success_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_alloc_header_only_test, read_data_setup, read_data_teardown), cmocka_unit_test_setup_teardown (read_tpm_buf_alloc_eof_test, read_data_setup, read_data_teardown), }; return cmocka_run_group_tests (tests, NULL, NULL); }