diff --git a/.codeql-config.yml b/.codeql-config.yml new file mode 100644 index 0000000..1311657 --- /dev/null +++ b/.codeql-config.yml @@ -0,0 +1,31 @@ +name: "Cryptsetup CodeQL config" + +query-filters: +- exclude: + id: cpp/fixme-comment +- exclude: + id: cpp/empty-block +- exclude: + id: cpp/poorly-documented-function +- exclude: + id: cpp/loop-variable-changed +- exclude: + id: cpp/empty-if +- exclude: + id: cpp/long-switch +- exclude: + id: cpp/complex-condition +- exclude: + id: cpp/commented-out-code + +# These produce many false positives +- exclude: + id: cpp/uninitialized-local +- exclude: + id: cpp/path-injection +- exclude: + id: cpp/missing-check-scanf + +# CodeQL should understand coverity [toctou] comments +- exclude: + id: cpp/toctou-race-condition diff --git a/.gitignore b/.gitignore index 3b59b1b..537c3a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ po/*gmo *~ -#Makefile +Makefile Makefile.in Makefile.in.in *.lo @@ -17,6 +17,7 @@ ABOUT-NLS aclocal.m4 autom4te.cache/ compile +compile_commands.json config.guess config.h config.h.in diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3153145..c5c3f26 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,23 +1,23 @@ stages: - test + - test-opal -.dump_kernel_log: +.fail_if_coredump_generated: after_script: - - sudo dmesg > /mnt/artifacts/dmesg.log - - sudo journalctl > /mnt/artifacts/journalctl.log - '[ "$(ls -A /var/coredumps)" ] && exit 1 || true' include: - local: .gitlab/ci/debian.yml - local: .gitlab/ci/fedora.yml - - local: .gitlab/ci/rhel.yml + - local: .gitlab/ci/fedora-opal.yml - local: .gitlab/ci/centos.yml - - local: .gitlab/ci/annocheck.yml +# - local: .gitlab/ci/annocheck.yml - local: .gitlab/ci/csmock.yml - local: .gitlab/ci/gitlab-shared-docker.yml - local: .gitlab/ci/compilation-various-disables.yml - local: .gitlab/ci/compilation-gcc.gitlab-ci.yml - local: .gitlab/ci/compilation-clang.gitlab-ci.yml + - local: .gitlab/ci/compilation-spellcheck.yml - local: .gitlab/ci/alpinelinux.yml - - local: .gitlab/ci/ubuntu-32bit.yml + - local: .gitlab/ci/debian-i686.yml - local: .gitlab/ci/cifuzz.yml diff --git a/.gitlab/ci/alpinelinux.yml b/.gitlab/ci/alpinelinux.yml index 81bd6cb..5c9f587 100644 --- a/.gitlab/ci/alpinelinux.yml +++ b/.gitlab/ci/alpinelinux.yml @@ -1,14 +1,16 @@ .alpinelinux-dependencies: - after_script: - - sudo dmesg > /mnt/artifacts/dmesg.log - - sudo cp /var/log/messages /mnt/artifacts/ - - '[ "$(ls -A /var/coredumps)" ] && exit 1 || true' + variables: + DISTRO: cryptsetup-alpine-edge + extends: + - .fail_if_coredump_generated before_script: - > sudo apk add - lvm2-dev openssl1.1-compat-dev popt-dev util-linux-dev json-c-dev - argon2-dev device-mapper which sharutils gettext gettext-dev automake + lvm2-dev openssl-dev popt-dev util-linux-dev json-c-dev + argon2-dev device-mapper which sharutils gettext-dev argp-standalone automake autoconf libtool build-base keyutils tar jq expect git asciidoctor + # Be sure we have updated basic tools and system + - sudo apk upgrade gcc binutils build-base musl - ./autogen.sh - ./configure --prefix=/usr --libdir=/lib --sbindir=/sbin --disable-static --enable-libargon2 --with-crypto_backend=openssl --disable-external-tokens --disable-ssh-token --enable-asciidoc @@ -17,7 +19,7 @@ test-main-commit-job-alpinelinux: - .alpinelinux-dependencies tags: - libvirt - - alpinelinux + - cryptsetup-alpine-edge stage: test interruptible: true variables: @@ -38,7 +40,7 @@ test-mergerq-job-alpinelinux: - .alpinelinux-dependencies tags: - libvirt - - alpinelinux + - cryptsetup-alpine-edge stage: test interruptible: true variables: diff --git a/.gitlab/ci/annocheck.yml b/.gitlab/ci/annocheck.yml index 5b3a715..8d12dfb 100644 --- a/.gitlab/ci/annocheck.yml +++ b/.gitlab/ci/annocheck.yml @@ -1,19 +1,18 @@ test-main-commit-job-annocheck: extends: - - .dump_kernel_log + - .fail_if_coredump_generated tags: - libvirt - - rhel9-annocheck + - cryptsetup-rhel-9 stage: test interruptible: true allow_failure: true variables: + DISTRO: cryptsetup-rhel-9 RUN_SSH_PLUGIN_TEST: "1" rules: - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" when: never - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ script: - - /opt/build-rpm-script.sh > /dev/null 2>&1 - - annocheck /var/lib/mock/rhel-9.0.0-candidate-x86_64/result/*.rpm --profile=el9 - - annocheck /var/lib/mock/rhel-9.0.0-candidate-x86_64/result/*.rpm --profile=el8 + - sudo /opt/run-annocheck.sh diff --git a/.gitlab/ci/build_srpm b/.gitlab/ci/build_srpm new file mode 100755 index 0000000..240276c --- /dev/null +++ b/.gitlab/ci/build_srpm @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +SAVED_PWD=$(pwd) +GIT_DIR="$SAVED_PWD/upstream_git" +SPEC="$GIT_DIR/misc/fedora/cryptsetup.spec" + +rm -fr $GIT_DIR + +git clone -q --depth 1 https://gitlab.com/cryptsetup/cryptsetup.git $GIT_DIR +cd $GIT_DIR + +GIT_COMMIT=$(git rev-parse --short=8 HEAD) +[ -z "$GIT_COMMIT" ] && exit 1 + +sed -i "s/^AC_INIT.*/AC_INIT([cryptsetup],[$GIT_COMMIT])/" $GIT_DIR/configure.ac +sed -i "s/^Version:.*/Version: $GIT_COMMIT/" $SPEC +sed -i "s/%{version_no_tilde}/$GIT_COMMIT/" $SPEC +sed -i "2i %global source_date_epoch_from_changelog 0" $SPEC +sed -i "3i %define _unpackaged_files_terminate_build 0" $SPEC + +./autogen.sh +./configure +make -j dist + +rpmbuild --define "_sourcedir $GIT_DIR" --define "_srcrpmdir $SAVED_PWD" -bs $SPEC + +cd $SAVED_PWD +rm -fr $GIT_DIR + +exit 0 diff --git a/.gitlab/ci/centos.yml b/.gitlab/ci/centos.yml index 6f5559c..310aa15 100644 --- a/.gitlab/ci/centos.yml +++ b/.gitlab/ci/centos.yml @@ -1,14 +1,16 @@ .centos-openssl-backend: extends: - - .dump_kernel_log + - .fail_if_coredump_generated before_script: + - sudo dnf clean all - > sudo dnf -y -q install autoconf automake device-mapper-devel gcc gettext-devel json-c-devel libblkid-devel libpwquality-devel libselinux-devel libssh-devel libtool libuuid-devel make popt-devel libsepol-devel nc openssh-clients passwd pkgconfig sharutils sshpass tar uuid-devel vim-common device-mapper - expect gettext git jq keyutils openssl-devel openssl gem + expect gettext git jq keyutils openssl-devel openssl gem swtpm swtpm-tools + tpm2-tools - sudo gem install asciidoctor - sudo -E git clean -xdf - ./autogen.sh @@ -21,11 +23,13 @@ test-main-commit-centos-stream9: - .centos-openssl-backend tags: - libvirt - - centos-stream9 + - cryptsetup-centos-stream-9 stage: test interruptible: true variables: + DISTRO: cryptsetup-centos-stream-9 RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" rules: - if: $RUN_SYSTEMD_PLUGIN_TEST != null when: never @@ -42,11 +46,59 @@ test-mergerq-centos-stream9: - .centos-openssl-backend tags: - libvirt - - centos-stream9 + - cryptsetup-centos-stream-9 stage: test interruptible: true variables: + DISTRO: cryptsetup-centos-stream-9 RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + rules: + - if: $RUN_SYSTEMD_PLUGIN_TEST != null + when: never + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + script: + - make -j + - make -j -C tests check-programs + - sudo -E make check + +test-main-commit-centos-stream10: + extends: + - .centos-openssl-backend + tags: + - libvirt + - cryptsetup-centos-stream-10 + stage: test + interruptible: true + variables: + DISTRO: cryptsetup-centos-stream-10 + RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + rules: + - if: $RUN_SYSTEMD_PLUGIN_TEST != null + when: never + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + script: + - make -j + - make -j -C tests check-programs + - sudo -E make check + +test-mergerq-centos-stream10: + extends: + - .centos-openssl-backend + tags: + - libvirt + - cryptsetup-centos-stream-10 + stage: test + interruptible: true + variables: + DISTRO: cryptsetup-centos-stream-10 + RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" rules: - if: $RUN_SYSTEMD_PLUGIN_TEST != null when: never diff --git a/.gitlab/ci/cibuild-setup-ubuntu.sh b/.gitlab/ci/cibuild-setup-ubuntu.sh index 07b0990..12649d4 100755 --- a/.gitlab/ci/cibuild-setup-ubuntu.sh +++ b/.gitlab/ci/cibuild-setup-ubuntu.sh @@ -5,17 +5,20 @@ set -ex PACKAGES=( git make autoconf automake autopoint pkg-config libtool libtool-bin gettext libssl-dev libdevmapper-dev libpopt-dev uuid-dev libsepol-dev - libjson-c-dev libssh-dev libblkid-dev tar libargon2-0-dev libpwquality-dev - sharutils dmsetup jq xxd expect keyutils netcat passwd openssh-client sshpass - asciidoctor + libjson-c-dev libssh-dev libblkid-dev tar libargon2-dev libpwquality-dev + sharutils dmsetup jq xxd expect keyutils netcat-openbsd passwd openssh-client + sshpass asciidoctor ) COMPILER="${COMPILER:?}" COMPILER_VERSION="${COMPILER_VERSION:?}" -grep -E '^deb' /etc/apt/sources.list > /etc/apt/sources.list~ -sed -Ei 's/^deb /deb-src /' /etc/apt/sources.list~ -cat /etc/apt/sources.list~ >> /etc/apt/sources.list +sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources + +# use this on older Ubuntu +# grep -E '^deb' /etc/apt/sources.list > /etc/apt/sources.list~ +# sed -Ei 's/^deb /deb-src /' /etc/apt/sources.list~ +# cat /etc/apt/sources.list~ >> /etc/apt/sources.list apt-get -y update --fix-missing DEBIAN_FRONTEND=noninteractive apt-get -yq install software-properties-common wget lsb-release @@ -28,7 +31,7 @@ if [[ $COMPILER == "gcc" ]]; then PACKAGES+=(gcc-$COMPILER_VERSION) elif [[ $COMPILER == "clang" ]]; then wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - - add-apt-repository "deb http://apt.llvm.org/${RELEASE}/ llvm-toolchain-${RELEASE}-${COMPILER_VERSION} main" + add-apt-repository -n "deb http://apt.llvm.org/${RELEASE}/ llvm-toolchain-${RELEASE}-${COMPILER_VERSION} main" # scan-build PACKAGES+=(clang-tools-$COMPILER_VERSION clang-$COMPILER_VERSION lldb-$COMPILER_VERSION lld-$COMPILER_VERSION clangd-$COMPILER_VERSION) @@ -37,14 +40,8 @@ else exit 1 fi -apt-get -y update --fix-missing +#apt-get -y update --fix-missing +(r=3;while ! apt-get -y update --fix-missing ; do ((--r))||exit;sleep 5;echo "Retrying";done) + DEBIAN_FRONTEND=noninteractive apt-get -yq install "${PACKAGES[@]}" apt-get -y build-dep cryptsetup - -echo "====================== VERSIONS ===================" -if [[ $COMPILER == "clang" ]]; then - echo "Using scan-build${COMPILER_VERSION:+-$COMPILER_VERSION}" -fi - -${COMPILER}-$COMPILER_VERSION -v -echo "====================== END VERSIONS ===================" diff --git a/.gitlab/ci/cifuzz.yml b/.gitlab/ci/cifuzz.yml index 063b912..add288c 100644 --- a/.gitlab/ci/cifuzz.yml +++ b/.gitlab/ci/cifuzz.yml @@ -1,46 +1,25 @@ cifuzz: - variables: - OSS_FUZZ_PROJECT_NAME: cryptsetup - CFL_PLATFORM: gitlab - CIFUZZ_DEBUG: "True" - FUZZ_SECONDS: 300 # 5 minutes per fuzzer - ARCHITECTURE: "x86_64" - DRY_RUN: "False" - LOW_DISK_SPACE: "True" - BAD_BUILD_CHECK: "True" - LANGUAGE: "c" - DOCKER_HOST: "tcp://docker:2375" - DOCKER_IN_DOCKER: "true" - DOCKER_DRIVER: overlay2 - DOCKER_TLS_CERTDIR: "" - image: - name: gcr.io/oss-fuzz-base/cifuzz-base - entrypoint: [""] - services: - - docker:dind - + image: ubuntu:noble + tags: + - gitlab-org-docker stage: test - parallel: - matrix: - - SANITIZER: [address, undefined, memory] + interruptible: true rules: - # Default code change. - # - if: $CI_PIPELINE_SOURCE == "merge_request_event" - # variables: - # MODE: "code-change" - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" when: never - if: $BUILD_AND_RUN_FUZZERS != null before_script: - # Get gitlab's container id. - - export CFL_CONTAINER_ID=`cut -c9- < /proc/1/cpuset` + - apt-get -y update + - > + apt-get -y install -y -qq git clang make autoconf automake autopoint + pkgconf libtool libtool-bin gettext libssl-dev libdevmapper-dev + libpopt-dev uuid-dev libsepol-dev libjson-c-dev libssh-dev libblkid-dev + flex bison cmake ninja-build + parallel: + matrix: + # memory does not work for now + - SANITIZER: [address, undefined] script: - # Will build and run the fuzzers. - # We use a hack to override CI_JOB_ID, because otherwise a bad path is used - # in GitLab CI environment - - CI_JOB_ID="$CI_PROJECT_NAMESPACE/$CI_PROJECT_TITLE" python3 "/opt/oss-fuzz/infra/cifuzz/cifuzz_combined_entrypoint.py" - artifacts: - # Upload artifacts when a crash makes the job fail. - when: always - paths: - - artifacts/ + - cd tests/fuzz + - ./oss-fuzz-build.sh + - ls -l out diff --git a/.gitlab/ci/clang-Wall b/.gitlab/ci/clang-Wall index d09e154..52c2dad 100755 --- a/.gitlab/ci/clang-Wall +++ b/.gitlab/ci/clang-Wall @@ -25,10 +25,9 @@ EXTRA="\ -Wswitch \ -Wmissing-format-attribute \ -Winit-self \ - -Wdeclaration-after-statement \ -Wold-style-definition \ -Wno-missing-field-initializers \ - -Wno-unused-parameter \ + -Wunused-parameter \ -Wno-long-long" exec $CLANG $PEDANTIC $CONVERSION \ diff --git a/.gitlab/ci/compilation-clang.gitlab-ci.yml b/.gitlab/ci/compilation-clang.gitlab-ci.yml index 6f5cd42..734348b 100644 --- a/.gitlab/ci/compilation-clang.gitlab-ci.yml +++ b/.gitlab/ci/compilation-clang.gitlab-ci.yml @@ -3,25 +3,86 @@ test-clang-compilation: - .gitlab-shared-clang script: - export CFLAGS="-Wall -Werror" + - ./autogen.sh + - $CC --version - ./configure - make -j - make -j check-programs -test-clang-Wall-script: +test-clang-Wall-script-ubuntu: extends: - .gitlab-shared-clang script: - export CFLAGS="-g -O0" - export CC="$CI_PROJECT_DIR/.gitlab/ci/clang-Wall" + - ./autogen.sh + - $CC --version - ./configure - make -j CFLAGS="-g -O0 -Werror" - make -j CFLAGS="-g -O0 -Werror" check-programs -test-scan-build: +test-clang-Wall-script-alpine: + extends: + - .gitlab-shared-clang-alpine + allow_failure: true + script: + - export CFLAGS="-g -O0" + - export CC="$CI_PROJECT_DIR/.gitlab/ci/clang-Wall" + - ./autogen.sh + - $CC --version + - ./configure + - make -j CFLAGS="-g -O0 -Werror" + - make -j CFLAGS="-g -O0 -Werror" check-programs + +test-scan-build-ubuntu: extends: - .gitlab-shared-clang script: + - ./autogen.sh + - echo "scan-build${COMPILER_VERSION:+-$COMPILER_VERSION}" + - scan-build${COMPILER_VERSION:+-$COMPILER_VERSION} -V ./configure CFLAGS="-g -O0" + - make clean + - scan-build${COMPILER_VERSION:+-$COMPILER_VERSION} --status-bugs -maxloop 10 make -j + - scan-build${COMPILER_VERSION:+-$COMPILER_VERSION} --status-bugs -maxloop 10 make -j check-programs + +test-scan-build-alpine: + extends: + - .gitlab-shared-clang-alpine + allow_failure: true + script: + - ./autogen.sh + - echo "scan-build${COMPILER_VERSION:+-$COMPILER_VERSION}" - scan-build${COMPILER_VERSION:+-$COMPILER_VERSION} -V ./configure CFLAGS="-g -O0" - make clean - scan-build${COMPILER_VERSION:+-$COMPILER_VERSION} --status-bugs -maxloop 10 make -j - scan-build${COMPILER_VERSION:+-$COMPILER_VERSION} --status-bugs -maxloop 10 make -j check-programs + +test-scan-build-backends: + extends: + - .gitlab-shared-clang + parallel: + matrix: + - BACKENDS: [ + "openssl", + "gcrypt", + "nss", + "kernel", + "nettle", + "mbedtls" + ] + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + changes: + - lib/crypto_backend/* + script: + - DEBIAN_FRONTEND=noninteractive apt-get -yq install libgcrypt20-dev libnss3-dev nettle-dev libmbedtls-dev + - ./autogen.sh + - echo "Configuring with crypto backend $BACKENDS" + - echo "scan-build${COMPILER_VERSION:+-$COMPILER_VERSION}" + - scan-build${COMPILER_VERSION:+-$COMPILER_VERSION} -V ./configure CFLAGS="-g -O0" --with-crypto_backend=$BACKENDS + - make clean + - scan-build${COMPILER_VERSION:+-$COMPILER_VERSION} --status-bugs -maxloop 10 make -j + - scan-build${COMPILER_VERSION:+-$COMPILER_VERSION} --status-bugs -maxloop 10 make -j check-programs + - ./tests/vectors-test diff --git a/.gitlab/ci/compilation-gcc.gitlab-ci.yml b/.gitlab/ci/compilation-gcc.gitlab-ci.yml index 00fae36..482c817 100644 --- a/.gitlab/ci/compilation-gcc.gitlab-ci.yml +++ b/.gitlab/ci/compilation-gcc.gitlab-ci.yml @@ -3,25 +3,83 @@ test-gcc-compilation: - .gitlab-shared-gcc script: - export CFLAGS="-Wall -Werror" + - ./autogen.sh + - $CC --version - ./configure - make -j - make -j check-programs -test-gcc-Wall-script: +test-gcc-Wall-script-ubuntu: extends: - .gitlab-shared-gcc script: - export CFLAGS="-g -O0" - export CC="$CI_PROJECT_DIR/.gitlab/ci/gcc-Wall" + - ./autogen.sh + - $CC --version - ./configure - make -j CFLAGS="-g -O0 -Werror" - make -j CFLAGS="-g -O0 -Werror" check-programs -test-gcc-fanalyzer: +test-gcc-Wall-script-alpine: extends: - - .gitlab-shared-gcc + - .gitlab-shared-gcc-alpine + allow_failure: true script: - - export CFLAGS="-Wall -Werror -g -O0 -fanalyzer -fdiagnostics-path-format=separate-events" + - export CFLAGS="-g -O0" + - export CC="$CI_PROJECT_DIR/.gitlab/ci/gcc-Wall" + - ./autogen.sh + - $CC --version - ./configure + - make -j CFLAGS="-g -O0 -Werror" + - make -j CFLAGS="-g -O0 -Werror" check-programs + +test-gcc-fanalyzer-ubuntu: + extends: + - .gitlab-shared-gcc + script: + - ./autogen.sh + - $CC --version + - ./configure CFLAGS="-Wall -Werror -g -O0 -fanalyzer -fdiagnostics-path-format=separate-events" --host=x86_64 + - make -j + - make -j check-programs + +test-gcc-fanalyzer-alpine: + extends: + - .gitlab-shared-gcc-alpine + allow_failure: true + script: + - ./autogen.sh + - $CC --version + - ./configure CFLAGS="-Wall -Werror -g -O0 -fanalyzer -fdiagnostics-path-format=separate-events -Wno-analyzer-fd-leak" --host=x86_64 + - make -j + - make -j check-programs + +test-gcc-fanalyzer-backends: + extends: + - .gitlab-shared-gcc + parallel: + matrix: + - BACKENDS: [ + "openssl", + "gcrypt", + "nss", + "kernel", + "nettle", + "mbedtls" + ] + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + changes: + - lib/crypto_backend/* + script: + - DEBIAN_FRONTEND=noninteractive apt-get -yq install libgcrypt20-dev libnss3-dev nettle-dev libmbedtls-dev + - ./autogen.sh + - $CC --version + - echo "Configuring with crypto backend $BACKENDS" + - ./configure CFLAGS="-Wall -Werror -g -O0 -fanalyzer -fdiagnostics-path-format=separate-events" --host=x86_64 --with-crypto_backend=$BACKENDS - make -j - make -j check-programs + - ./tests/vectors-test diff --git a/.gitlab/ci/compilation-spellcheck.yml b/.gitlab/ci/compilation-spellcheck.yml new file mode 100644 index 0000000..d53a197 --- /dev/null +++ b/.gitlab/ci/compilation-spellcheck.yml @@ -0,0 +1,20 @@ +test-run-spellcheck: + image: ubuntu:noble + tags: + - gitlab-org-docker + stage: test + interruptible: true + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + artifacts: + name: "spellcheck-$CI_COMMIT_REF_NAME" + paths: + - _spellcheck + before_script: + - apt-get -y update --fix-missing + - apt-get -y install git lintian codespell + script: + - echo "Running spellcheck" + - .gitlab/ci/spellcheck diff --git a/.gitlab/ci/compilation-various-disables.yml b/.gitlab/ci/compilation-various-disables.yml index 1414f9e..00ceae3 100644 --- a/.gitlab/ci/compilation-various-disables.yml +++ b/.gitlab/ci/compilation-various-disables.yml @@ -4,18 +4,30 @@ test-gcc-disable-compiles: parallel: matrix: - DISABLE_FLAGS: [ - "--disable-keyring", - "--disable-external-tokens --disable-ssh-token", - "--disable-luks2-reencryption", - "--disable-cryptsetup --disable-veritysetup --disable-integritysetup", - "--disable-kernel_crypto", - "--disable-selinux", - "--disable-udev", - "--disable-internal-argon2", - "--disable-blkid" + "keyring", + "external-tokens ssh-token", + "luks2-reencryption", + "cryptsetup veritysetup integritysetup", + "kernel_crypto", + "udev", + "internal-argon2", + "blkid", + "hw-opal" ] + artifacts: + name: "meson-build-logs-$CI_COMMIT_REF_NAME" + paths: + - meson_builddir/meson-logs script: + - DEBIAN_FRONTEND=noninteractive apt-get -yq install meson ninja-build - export CFLAGS="-Wall -Werror" - - ./configure $DISABLE_FLAGS + - ./autogen.sh + - echo "Configuring with --disable-$DISABLE_FLAGS" + - ./configure $(for i in $DISABLE_FLAGS; do echo "--disable-$i"; done) - make -j - make -j check-programs + - git checkout -f && git clean -xdf + - meson -v + - echo "Configuring with -D$DISABLE_FLAGS=false" + - meson setup meson_builddir $(for i in $DISABLE_FLAGS; do [ "$i" == "internal-argon2" ] && echo "-Dargon-implementation=internal" || echo "-D$i=false"; done) + - ninja -C meson_builddir diff --git a/.gitlab/ci/csmock.yml b/.gitlab/ci/csmock.yml index 72b53ed..ada2d0c 100644 --- a/.gitlab/ci/csmock.yml +++ b/.gitlab/ci/csmock.yml @@ -1,17 +1,36 @@ +.dnf-csmock: + variables: + DISTRO: cryptsetup-fedora-rawhide + DISK_SIZE: 20 + extends: + - .fail_if_coredump_generated + before_script: + - > + sudo dnf -y -q install + autoconf automake device-mapper-devel gcc gettext-devel json-c-devel + libblkid-devel libpwquality-devel libselinux-devel + libssh-devel libtool libuuid-devel make popt-devel + libsepol-devel.x86_64 pkgconfig tar uuid-devel git + openssl-devel asciidoctor meson ninja-build + rpm-build csmock + test-commit-job-csmock: extends: - - .dump_kernel_log + - .dnf-csmock tags: - libvirt - - rhel7-csmock + - cryptsetup-fedora-rawhide stage: test interruptible: true allow_failure: true - variables: - RUN_SSH_PLUGIN_TEST: "1" rules: - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" when: never - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ || $CI_PIPELINE_SOURCE == "merge_request_event" script: - - /opt/csmock-run-script.sh + - .gitlab/ci/build_srpm + - .gitlab/ci/run_csmock + artifacts: + when: always + paths: + - cryptsetup-csmock-results.tar.xz diff --git a/.gitlab/ci/ubuntu-32bit.yml b/.gitlab/ci/debian-i686.yml similarity index 81% rename from .gitlab/ci/ubuntu-32bit.yml rename to .gitlab/ci/debian-i686.yml index f51c059..09db9e0 100644 --- a/.gitlab/ci/ubuntu-32bit.yml +++ b/.gitlab/ci/debian-i686.yml @@ -1,12 +1,13 @@ -test-mergerq-job-ubuntu-32bit: +test-mergerq-job-debian-i686: extends: - .debian-prep tags: - libvirt - - ubuntu-bionic-32bit + - cryptsetup-debian-12i686 stage: test interruptible: true variables: + DISTRO: cryptsetup-debian-12i686 RUN_SSH_PLUGIN_TEST: "1" rules: - if: $RUN_SYSTEMD_PLUGIN_TEST != null @@ -19,15 +20,16 @@ test-mergerq-job-ubuntu-32bit: - make -j -C tests check-programs - sudo -E make check -test-main-commit-job-ubuntu-32bit: +test-main-commit-job-debian-i686: extends: - .debian-prep tags: - libvirt - - ubuntu-bionic-32bit + - cryptsetup-debian-12i686 stage: test interruptible: true variables: + DISTRO: cryptsetup-debian-12i686 RUN_SSH_PLUGIN_TEST: "1" rules: - if: $RUN_SYSTEMD_PLUGIN_TEST != null diff --git a/.gitlab/ci/debian.yml b/.gitlab/ci/debian.yml index fad9d97..0cb4480 100644 --- a/.gitlab/ci/debian.yml +++ b/.gitlab/ci/debian.yml @@ -1,17 +1,16 @@ .debian-prep: extends: - - .dump_kernel_log + - .fail_if_coredump_generated before_script: - - > - [ -z "$RUN_SYSTEMD_PLUGIN_TEST" ] || - sudo apt-get -y install -y -qq swtpm meson ninja-build python3-jinja2 - gperf libcap-dev tpm2-tss-engine-dev libmount-dev swtpm-tools + - sudo apt-get -y update - > sudo apt-get -y install -y -qq git gcc make autoconf automake autopoint pkgconf libtool libtool-bin gettext libssl-dev libdevmapper-dev libpopt-dev uuid-dev libsepol-dev libjson-c-dev libssh-dev libblkid-dev - tar libargon2-0-dev libpwquality-dev sharutils dmsetup jq xxd expect - keyutils netcat passwd openssh-client sshpass asciidoctor + tar libargon2-dev libpwquality-dev sharutils dmsetup jq xxd expect + keyutils netcat-openbsd passwd openssh-client sshpass asciidoctor + swtpm meson ninja-build python3-jinja2 gperf libcap-dev libtss2-dev + libmount-dev swtpm-tools tpm2-tools - sudo apt-get -y build-dep cryptsetup - sudo -E git clean -xdf - ./autogen.sh @@ -22,11 +21,13 @@ test-mergerq-job-debian: - .debian-prep tags: - libvirt - - debian11 + - cryptsetup-debian-unstable stage: test interruptible: true variables: + DISTRO: cryptsetup-debian-unstable RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" rules: - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" when: never @@ -41,11 +42,55 @@ test-main-commit-job-debian: - .debian-prep tags: - libvirt - - debian11 + - cryptsetup-debian-unstable + stage: test + interruptible: true + variables: + DISTRO: cryptsetup-debian-unstable + RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + script: + - make -j + - make -j -C tests check-programs + - sudo -E make check + +test-mergerq-job-debian12: + extends: + - .debian-prep + tags: + - libvirt + - cryptsetup-debian-12 + stage: test + interruptible: true + variables: + DISTRO: cryptsetup-debian-12 + RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + script: + - make -j + - make -j -C tests check-programs + - sudo -E make check + +test-main-commit-job-debian12: + extends: + - .debian-prep + tags: + - libvirt + - cryptsetup-debian-12 stage: test interruptible: true variables: + DISTRO: cryptsetup-debian-12 RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" rules: - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" when: never @@ -54,3 +99,92 @@ test-main-commit-job-debian: - make -j - make -j -C tests check-programs - sudo -E make check + +# meson tests +test-mergerq-job-debian-meson: + extends: + - .debian-prep + tags: + - libvirt + - cryptsetup-debian-unstable + stage: test + interruptible: true + variables: + DISTRO: cryptsetup-debian-unstable + RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + script: + - sudo apt-get -y install -y -qq meson ninja-build + - meson setup build + - ninja -C build + - cd build && sudo -E meson test --verbose --print-errorlogs + +test-main-commit-job-debian-meson: + extends: + - .debian-prep + tags: + - libvirt + - cryptsetup-debian-unstable + stage: test + interruptible: true + variables: + DISTRO: cryptsetup-debian-unstable + RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + script: + - sudo apt-get -y install -y -qq meson ninja-build + - meson setup build + - ninja -C build + - cd build && sudo -E meson test --verbose --print-errorlogs + +test-mergerq-job-debian12-meson: + extends: + - .debian-prep + tags: + - libvirt + - cryptsetup-debian-12 + stage: test + interruptible: true + variables: + DISTRO: cryptsetup-debian-12 + RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + script: + - sudo apt-get -y install -y -qq meson ninja-build + - meson setup build + - ninja -C build + - cd build && sudo -E meson test --verbose --print-errorlogs + +test-main-commit-job-debian12-meson: + extends: + - .debian-prep + tags: + - libvirt + - cryptsetup-debian-12 + stage: test + interruptible: true + variables: + DISTRO: cryptsetup-debian-12 + RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + script: + - sudo apt-get -y install -y -qq meson ninja-build + - meson setup build + - ninja -C build + - cd build && sudo -E meson test --verbose --print-errorlogs diff --git a/.gitlab/ci/fedora-opal.yml b/.gitlab/ci/fedora-opal.yml new file mode 100644 index 0000000..d7072bf --- /dev/null +++ b/.gitlab/ci/fedora-opal.yml @@ -0,0 +1,145 @@ +.opal-template-fedora: + extends: + - .dnf-openssl-backend + tags: + - libvirt + - cryptsetup-fedora-rawhide + stage: test-opal + interruptible: false + variables: + OPAL2_DEV: "/dev/nvme0n1" + OPAL2_PSID_FILE: "/home/gitlab-runner/psid.txt" + VOLATILE: 1 + script: + - sudo dnf install -y -q nvme-cli + - sudo nvme list + - make -j + - make -j -C tests check-programs + - sudo -E make check TESTS="00modules-test compat-test-opal" + +# Samsung SSD 980 500GB (on tiber machine) +test-commit-rawhide-samsung980: + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + extends: + - .opal-template-fedora + tags: + - tiber + resource_group: samsung980-on-tiber + interruptible: false + variables: + PCI_PASSTHROUGH_VENDOR_ID: "144d" + PCI_PASSTHROUGH_DEVICE_ID: "a809" + +test-mergerq-rawhide-samsung980: + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + extends: + - .opal-template-fedora + tags: + - tiber + resource_group: samsung980-on-tiber + interruptible: false + variables: + PCI_PASSTHROUGH_VENDOR_ID: "144d" + PCI_PASSTHROUGH_DEVICE_ID: "a809" + +# WD PC SN740 SDDQNQD-512G-1014 (on tiber machine) +# Disabled on 2025-03-20, seems broken +#test-commit-rawhide-sn740: +# rules: +# - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" +# when: never +# - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ +# extends: +# - .opal-template-fedora +# tags: +# - tiber +# resource_group: sn740-on-tiber +# interruptible: false +# variables: +# PCI_PASSTHROUGH_VENDOR_ID: "15b7" +# PCI_PASSTHROUGH_DEVICE_ID: "5017" +# +#test-mergerq-rawhide-sn740: +# rules: +# - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" +# when: never +# - if: $CI_PIPELINE_SOURCE == "merge_request_event" +# extends: +# - .opal-template-fedora +# tags: +# - tiber +# resource_group: sn740-on-tiber +# interruptible: false +# variables: +# PCI_PASSTHROUGH_VENDOR_ID: "15b7" +# PCI_PASSTHROUGH_DEVICE_ID: "5017" + +# Samsung SSD 980 PRO 1TB (on trantor machine) +test-commit-rawhide-samsung980pro: + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + extends: + - .opal-template-fedora + tags: + - trantor + resource_group: samsung980pro-on-trantor + interruptible: false + variables: + PCI_PASSTHROUGH_VENDOR_ID: "144d" + PCI_PASSTHROUGH_DEVICE_ID: "a80a" + +test-mergerq-rawhide-samsung980pro: + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + extends: + - .opal-template-fedora + tags: + - trantor + resource_group: samsung980pro-on-trantor + interruptible: false + variables: + PCI_PASSTHROUGH_VENDOR_ID: "144d" + PCI_PASSTHROUGH_DEVICE_ID: "a80a" + +# # UMIS RPETJ256MGE2MDQ (on tiber machine) +# test-commit-rawhide-umis: +# rules: +# - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" +# when: never +# - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ +# extends: +# - .opal-template-fedora +# tags: +# - tiber +# resource_group: umis-on-tiber +# stage: test +# interruptible: false +# variables: +# PCI_PASSTHROUGH_VENDOR_ID: "1cc4" +# PCI_PASSTHROUGH_DEVICE_ID: "6302" +# +# test-mergerq-rawhide-umis: +# rules: +# - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" +# when: never +# - if: $CI_PIPELINE_SOURCE == "merge_request_event" +# extends: +# - .opal-template-fedora +# tags: +# - tiber +# resource_group: umis-on-tiber +# stage: test +# interruptible: false +# variables: +# PCI_PASSTHROUGH_VENDOR_ID: "1cc4" +# PCI_PASSTHROUGH_DEVICE_ID: "6302" diff --git a/.gitlab/ci/fedora.yml b/.gitlab/ci/fedora.yml index 7fd9c7e..cbd1e13 100644 --- a/.gitlab/ci/fedora.yml +++ b/.gitlab/ci/fedora.yml @@ -1,20 +1,19 @@ .dnf-openssl-backend: - extends: - - .dump_kernel_log - before_script: - - > - [ -z "$RUN_SYSTEMD_PLUGIN_TEST" ] || - sudo dnf -y -q install - swtpm meson ninja-build python3-jinja2 gperf libcap-devel tpm2-tss-devel - libmount-devel swtpm-tools - - > - sudo dnf -y -q install + variables: + DISTRO: cryptsetup-fedora-rawhide + PKGS: >- autoconf automake device-mapper-devel gcc gettext-devel json-c-devel libargon2-devel libblkid-devel libpwquality-devel libselinux-devel libssh-devel libtool libuuid-devel make popt-devel libsepol-devel.x86_64 netcat openssh-clients passwd pkgconfig sharutils sshpass tar uuid-devel vim-common device-mapper expect gettext git jq - keyutils openssl-devel openssl asciidoctor + keyutils openssl-devel openssl asciidoctor swtpm meson ninja-build + python3-jinja2 gperf libcap-devel tpm2-tss-devel libmount-devel swtpm-tools + extends: + - .fail_if_coredump_generated + before_script: + - sudo dnf clean all + - (r=3;while ! sudo dnf -y -q install $PKGS ; do ((--r))||exit;sleep 5;echo "Retrying";done) - sudo -E git clean -xdf - ./autogen.sh - ./configure --enable-fips --enable-pwquality --enable-libargon2 --with-crypto_backend=openssl --enable-asciidoc @@ -24,12 +23,14 @@ test-main-commit-job-rawhide: - .dnf-openssl-backend tags: - libvirt - - fedora-rawhide + - cryptsetup-fedora-rawhide stage: test interruptible: true allow_failure: true variables: RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + RUN_SYSTEMD_PLUGIN_TEST: "1" rules: - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" when: never @@ -44,12 +45,14 @@ test-mergerq-job-rawhide: - .dnf-openssl-backend tags: - libvirt - - fedora-rawhide + - cryptsetup-fedora-rawhide stage: test interruptible: true allow_failure: true variables: RUN_SSH_PLUGIN_TEST: "1" + RUN_KEYRING_TRUSTED_TEST: "1" + RUN_SYSTEMD_PLUGIN_TEST: "1" rules: - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" when: never diff --git a/.gitlab/ci/gcc-Wall b/.gitlab/ci/gcc-Wall index 6669504..bc78705 100755 --- a/.gitlab/ci/gcc-Wall +++ b/.gitlab/ci/gcc-Wall @@ -31,12 +31,13 @@ EXTRA="-Wextra \ -Wunsafe-loop-optimizations \ -Wold-style-definition \ -Wno-missing-field-initializers \ - -Wno-unused-parameter \ + -Wunused-parameter \ -Wno-long-long \ -Wmaybe-uninitialized \ -Wvla \ -Wformat-overflow \ - -Wformat-truncation" + -Wformat-truncation \ + -Wstringop-overread" exec $GCC $PEDANTIC $CONVERSION \ -Wall $Wuninitialized \ diff --git a/.gitlab/ci/gitlab-shared-docker.yml b/.gitlab/ci/gitlab-shared-docker.yml index 1edacc8..59659e8 100644 --- a/.gitlab/ci/gitlab-shared-docker.yml +++ b/.gitlab/ci/gitlab-shared-docker.yml @@ -1,5 +1,6 @@ -.gitlab-shared-docker: - image: ubuntu:focal +# Ubuntu +.gitlab-shared-docker-ubuntu: + image: ubuntu:noble tags: - gitlab-org-docker stage: test @@ -12,20 +13,49 @@ - .gitlab/ci/cibuild-setup-ubuntu.sh - export CC="${COMPILER}${COMPILER_VERSION:+-$COMPILER_VERSION}" - export CXX="${COMPILER}++${COMPILER_VERSION:+-$COMPILER_VERSION}" - - ./autogen.sh + +# Alpine +.gitlab-shared-docker-alpine: + image: alpine:latest + tags: + - gitlab-org-docker + stage: test + interruptible: true + rules: + - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ + before_script: + - apk add bash build-base clang clang-analyzer argp-standalone lvm2-dev openssl-dev popt-dev util-linux-dev json-c-dev device-mapper gettext-dev libssh-dev automake autoconf libtool tar asciidoctor + - export CC="${COMPILER}${COMPILER_VERSION:+-$COMPILER_VERSION}" + - export CXX="${COMPILER}++${COMPILER_VERSION:+-$COMPILER_VERSION}" .gitlab-shared-gcc: extends: - - .gitlab-shared-docker + - .gitlab-shared-docker-ubuntu variables: COMPILER: "gcc" - COMPILER_VERSION: "11" - RUN_SSH_PLUGIN_TEST: "1" + COMPILER_VERSION: "14" + CC: "gcc-14" .gitlab-shared-clang: extends: - - .gitlab-shared-docker + - .gitlab-shared-docker-ubuntu + variables: + COMPILER: "clang" + COMPILER_VERSION: "20" + CC: "clang-20" + +.gitlab-shared-gcc-alpine: + extends: + - .gitlab-shared-docker-alpine + variables: + COMPILER: "gcc" + CC: "gcc" + +.gitlab-shared-clang-alpine: + extends: + - .gitlab-shared-docker-alpine variables: COMPILER: "clang" - COMPILER_VERSION: "13" - RUN_SSH_PLUGIN_TEST: "1" + CC: "clang" diff --git a/.gitlab/ci/rhel.yml b/.gitlab/ci/rhel.yml deleted file mode 100644 index f71533c..0000000 --- a/.gitlab/ci/rhel.yml +++ /dev/null @@ -1,106 +0,0 @@ -.rhel-openssl-backend: - extends: - - .dump_kernel_log - before_script: - - > - sudo yum -y -q install - autoconf automake device-mapper-devel gcc gettext-devel json-c-devel - libblkid-devel libpwquality-devel libselinux-devel libssh-devel libtool - libuuid-devel make popt-devel libsepol-devel nc openssh-clients passwd - pkgconfig sharutils sshpass tar uuid-devel vim-common device-mapper - expect gettext git jq keyutils openssl-devel openssl gem > /dev/null 2>&1 - - sudo gem install asciidoctor - - sudo -E git clean -xdf - - ./autogen.sh - - ./configure --enable-fips --enable-pwquality --with-crypto_backend=openssl --enable-asciidoc - -# non-FIPS jobs - -test-main-commit-rhel8: - extends: - - .rhel-openssl-backend - tags: - - libvirt - - rhel8 - stage: test - interruptible: true - variables: - RUN_SSH_PLUGIN_TEST: "1" - rules: - - if: $RUN_SYSTEMD_PLUGIN_TEST != null - when: never - - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" - when: never - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ - script: - - make -j - - make -j -C tests check-programs - - sudo -E make check - -test-main-commit-rhel9: - extends: - - .rhel-openssl-backend - tags: - - libvirt - - rhel9 - stage: test - interruptible: true - variables: - RUN_SSH_PLUGIN_TEST: "1" - rules: - - if: $RUN_SYSTEMD_PLUGIN_TEST != null - when: never - - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" - when: never - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ - script: - - make -j - - make -j -C tests check-programs - - sudo -E make check - -# FIPS jobs - -test-main-commit-rhel8-fips: - extends: - - .rhel-openssl-backend - tags: - - libvirt - - rhel8-fips - stage: test - interruptible: true - variables: - RUN_SSH_PLUGIN_TEST: "1" - rules: - - if: $RUN_SYSTEMD_PLUGIN_TEST != null - when: never - - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" - when: never - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ - script: - - fips-mode-setup --check || exit 1 - - make -j - - make -j -C tests check-programs - - sudo -E make check - -test-main-commit-rhel9-fips: - extends: - - .rhel-openssl-backend - tags: - - libvirt - - rhel9-fips - stage: test - interruptible: true - allow_failure: true - variables: - RUN_SSH_PLUGIN_TEST: "1" - rules: - - if: $RUN_SYSTEMD_PLUGIN_TEST != null - when: never - - if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup" - when: never - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/ - script: - - fips-mode-setup --check || exit 1 - - make -j - - make -j -C tests check-programs - - sudo -E make check diff --git a/.gitlab/ci/run_csmock b/.gitlab/ci/run_csmock new file mode 100755 index 0000000..707e0ff --- /dev/null +++ b/.gitlab/ci/run_csmock @@ -0,0 +1,22 @@ +#!/bin/bash + +CSMOCK="sudo /usr/bin/csmock" +CSMOCK_TOOLS="gcc,clang,cppcheck,shellcheck" +CSMOCK_TXZ="cryptsetup-csmock-results.tar.xz" +CSMOCK_ERR="cryptsetup-csmock-results/scan-results.err" + +$CSMOCK cryptsetup-*.src.rpm \ + --keep-going --force \ + --cswrap-timeout 300 \ + --skip-patches \ + --tools $CSMOCK_TOOLS \ + --output $CSMOCK_TXZ \ + --gcc-analyze \ + --cppcheck-add-flag=--check-level=exhaustive \ + || { echo "csmock command failed"; exit 2; } + +tar xJf $CSMOCK_TXZ $CSMOCK_ERR --strip-components 1 \ + && test -s $CSMOCK_ERR \ + && { echo "csmock discovered important errors"; echo 3; } + +exit 0 diff --git a/.gitlab/ci/spellcheck b/.gitlab/ci/spellcheck new file mode 100755 index 0000000..6a92f62 --- /dev/null +++ b/.gitlab/ci/spellcheck @@ -0,0 +1,31 @@ +#!/bin/bash + +set -e +DIR="_spellcheck" + +[ ! -d $DIR ] && mkdir $DIR + +echo "[SPELLINTIAN]" +git ls-tree -rz --name-only HEAD | grep -Evz -e '\.(pdf|xz)$' -e ^po/ | \ + xargs -r0 spellintian | \ + grep -v "(duplicate word)" | \ + grep -v "docs/" | tee $DIR/spell1.txt + +echo "[CODESPELL]" +git ls-tree -rz --name-only HEAD | grep -Evz -e '\.(pdf|xz)$' -e ^po/ | \ + xargs -r0 codespell | \ + grep -v "EXPCT" | \ + grep -v "params, prams" | \ + grep -v "pad, padded" | \ + grep -v "CIPHER, CHIP" | \ + grep -v "gost" | \ + grep -v "userA" | \ + grep -v "re-use" | \ + grep -v "fo ==" | \ + grep -v "docs/" | tee $DIR/spell2.txt + + +[ -s $DIR/spell1.txt ] && exit 1 +[ -s $DIR/spell2.txt ] && exit 2 + +exit 0 diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index f8837aa..88ad54b 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -9,7 +9,10 @@ ### Debug log + + ``` Output with --debug option: ``` + diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index 64d9cc8..0000000 --- a/.lgtm.yml +++ /dev/null @@ -1,11 +0,0 @@ -queries: - - exclude: cpp/fixme-comment - - exclude: cpp/empty-block -# symver attribute detection cannot be used, disable it for lgtm -extraction: - cpp: - configure: - command: - - "./autogen.sh" - - "./configure --enable-external-tokens --enable-ssh-token" - - "echo \"#undef HAVE_ATTRIBUTE_SYMVER\" >> config.h" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 01bea7c..e929b6e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,7 +119,7 @@ libtool --mode=execute gdb --args ./cryptsetup --debug $@ This will ensure that a properly compiled libcryptsetup file is used. ### Coding style -Cryptsetup uses [Linux kernel coding style](https://www.kernel.org/doc/html/latest/process/coding-style.html) for libcryptsetup and tools (where applicable) with some additional notes: +Cryptsetup uses [Linux kernel coding style](https://cdn.kernel.org/doc/html/latest/process/coding-style.html) for libcryptsetup and tools (where applicable) with some additional notes: - Use tabulators for indentation; the line should not exceed 100 characters with an 8-character tabulator. Otherwise, use a tab of any length. :-). - The minimal C standard required is C99. - The ``goto`` use is allowed only for error path (``goto out`` for common code path, ``goto err`` for specific error code path). @@ -127,7 +127,8 @@ Cryptsetup uses [Linux kernel coding style](https://www.kernel.org/doc/html/late - Use an elaborative description in the patch header. - No need to use sign-off-by lines. - Use name prefixes (``crypt_``, ``LUKS2_`` and similar). -- Avoid extensive preprocessor use (specifically ``#ifdef`` sections). +- Avoid extensive preprocessor use (specifically conditional ``#if`` or ``#ifdef`` sections). +- To check detected configuration options stored in config.h, always use ``#if SOMETHING`` (do NOT use ``#ifdef``). - Use output only through ``log_err, log_std, log_verbose, log_dbg`` macros. The ``log_dbg`` is always in English; the others should be wrapped in the ``_()`` macro for translation. - Use ``assert()`` but only for simple invariants and variables (avoid calling functions). diff --git a/FAQ.md b/FAQ.md index 4441ff5..21e806f 100644 --- a/FAQ.md +++ b/FAQ.md @@ -38,7 +38,7 @@ LUKS1 and LUKS2. The LUKS1 on-disk format specification is at - https://www.kernel.org/pub/linux/utils/cryptsetup/LUKS_docs/on-disk-format.pdf + https://cdn.kernel.org/pub/linux/utils/cryptsetup/LUKS_docs/on-disk-format.pdf The LUKS2 on-disk format specification is at https://gitlab.com/cryptsetup/LUKS2-docs @@ -705,9 +705,12 @@ this. The only legitimate reason I can think of is if you want to have two LUKS devices with the same volume key. Even then, I think it would be preferable to just use key-slots with the same passphrase, or to use - plain dm-crypt instead. If you really have a good reason, please tell - me. If I am convinced, I will add how to do this here. + plain dm-crypt instead. + Use the --volume-key-file option, like this: +``` + cryptsetup luksFormat --volume-key-file keyfile /dev/loop0 +``` * **2.12 What are the security requirements for a key read from file?** @@ -1923,10 +1926,6 @@ Hence, LUKS has no kill option because it would do much more harm than good. - Still, if you have a good use-case (i.e. non-abstract real-world - situation) where a Nuke-Option would actually be beneficial, please let - me know. - * **5.22 Does cryptsetup open network connections to websites, etc. ?** @@ -2680,8 +2679,7 @@ can be converted to the raw volume key for example via: Note that at the time this FAQ item was written, 1.5.4 was the latest 1.5.x version and it has the flaw, i.e. works with the old Whirlpool - version. Possibly later 1.5.x versions will work as well. If not, - please let me know. + version. Possibly later 1.5.x versions will work as well. The only two ways to access older LUKS containers created with Whirlpool are to either decrypt with an old gcrypt version that has the flaw or to @@ -2797,8 +2795,7 @@ can be converted to the raw volume key for example via: 03) Creating your own initrd The two examples below should give you most of what is needed. This is - tested with LUKS1 and should work with LUKS2 as well. If not, please - let me know. + tested with LUKS1 and should work with LUKS2 as well. Here is a really minimal example. It does nothing but set up some things and then drop to an interactive shell. It is perfect to try out diff --git a/Makefile.am b/Makefile.am index 1be9ed7..688cb77 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -EXTRA_DIST = README.md SECURITY.md COPYING.LGPL CONTRIBUTING.md FAQ.md docs misc autogen.sh +EXTRA_DIST = README.md SECURITY.md README.licensing CONTRIBUTING.md FAQ.md docs misc autogen.sh EXTRA_DIST += meson_options.txt \ meson.build \ lib/crypto_backend/argon2/meson.build \ @@ -9,6 +9,7 @@ EXTRA_DIST += meson_options.txt \ scripts/meson.build \ src/meson.build \ tests/meson.build \ + tests/fuzz/meson.build \ tokens/meson.build \ tokens/ssh/meson.build @@ -24,8 +25,7 @@ AM_CPPFLAGS = \ -DLIBDIR=\""$(libdir)"\" \ -DPREFIX=\""$(prefix)"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ - -DVERSION=\""$(VERSION)"\" \ - -DEXTERNAL_LUKS2_TOKENS_PATH=\"${EXTERNAL_LUKS2_TOKENS_PATH}\" + -DVERSION=\""$(VERSION)"\" AM_CFLAGS = -Wall AM_CXXFLAGS = -Wall AM_LDFLAGS = diff --git a/README.licensing b/README.licensing new file mode 100644 index 0000000..c20b52a --- /dev/null +++ b/README.licensing @@ -0,0 +1,20 @@ +The cryptsetup project does not use the same license for all of the code and documentation. + +There is code and documentation under: + + * GPL-2.0-or-later - GNU General Public License version 2, or any later version + + * LGPL-2.1-or-later WITH cryptsetup-OpenSSL-exception + * LGPL-2.1-or-later - GNU Lesser General Public License 2.1 or any later version, + (with cryptsetup-OpenSSL-exception where applicable) + + * Apache-2.0 - Apache License 2.0 + + * CC-BY-SA-4.0 - Creative Commons Attribution Share Alike 4.0 International + + * Public Domain + +Please, check the source code for more details. + +The ./COPYING file (GPL-2.0-or-later) is the default license for code without +an explicitly defined license. diff --git a/README.md b/README.md index 7fc88a3..7b57c17 100644 --- a/README.md +++ b/README.md @@ -30,28 +30,22 @@ which enables users to transport or migrate data seamlessly. * The latest version of the [LUKS2 format specification](https://gitlab.com/cryptsetup/LUKS2-docs). * The latest version of the - [LUKS1 format specification](https://www.kernel.org/pub/linux/utils/cryptsetup/LUKS_docs/on-disk-format.pdf). + [LUKS1 format specification](https://cdn.kernel.org/pub/linux/utils/cryptsetup/LUKS_docs/on-disk-format.pdf). * [Project home page](https://gitlab.com/cryptsetup/cryptsetup/). * [Frequently asked questions (FAQ)](https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions) Download -------- Release notes and tarballs are available at -[kernel.org](https://www.kernel.org/pub/linux/utils/cryptsetup/). +[kernel.org](https://cdn.kernel.org/pub/linux/utils/cryptsetup/). -**The latest stable cryptsetup release version is 2.7.5** - * [cryptsetup-2.7.5.tar.xz](https://www.kernel.org/pub/linux/utils/cryptsetup/v2.7/cryptsetup-2.7.5.tar.xz) - * Signature [cryptsetup-2.7.5.tar.sign](https://www.kernel.org/pub/linux/utils/cryptsetup/v2.7/cryptsetup-2.7.5.tar.sign) +**The latest stable cryptsetup release version is 2.8.2** + * [cryptsetup-2.8.2.tar.xz](https://cdn.kernel.org/pub/linux/utils/cryptsetup/v2.8/cryptsetup-2.8.2.tar.xz) + * Signature [cryptsetup-2.8.2.tar.sign](https://cdn.kernel.org/pub/linux/utils/cryptsetup/v2.8/cryptsetup-2.8.2.tar.sign) _(You need to decompress file first to check signature.)_ - * [Cryptsetup 2.7.5 Release Notes](https://www.kernel.org/pub/linux/utils/cryptsetup/v2.7/v2.7.5-ReleaseNotes). + * [Cryptsetup 2.8.2 Release Notes](https://cdn.kernel.org/pub/linux/utils/cryptsetup/v2.8/v2.8.2-ReleaseNotes). -Previous versions - * [Version 2.6.1](https://www.kernel.org/pub/linux/utils/cryptsetup/v2.6/cryptsetup-2.6.1.tar.xz) - - [Signature](https://www.kernel.org/pub/linux/utils/cryptsetup/v2.6/cryptsetup-2.6.1.tar.sign) - - [Release Notes](https://www.kernel.org/pub/linux/utils/cryptsetup/v2.5/v2.5.0-ReleaseNotes). - * [Version 1.7.5](https://www.kernel.org/pub/linux/utils/cryptsetup/v1.7/cryptsetup-1.7.5.tar.xz) - - [Signature](https://www.kernel.org/pub/linux/utils/cryptsetup/v1.7/cryptsetup-1.7.5.tar.sign) - - [Release Notes](https://www.kernel.org/pub/linux/utils/cryptsetup/v1.7/v1.7.5-ReleaseNotes). +[Previous versions](https://cdn.kernel.org/pub/linux/utils/cryptsetup) Source and API documentation ---------------------------- @@ -76,8 +70,7 @@ Below are the packages needed to build for certain Linux distributions: **For Fedora**: ``` -git gcc make autoconf automake gettext-devel pkgconfig openssl-devel popt-devel device-mapper-devel -libuuid-devel json-c-devel libblkid-devel findutils libtool libssh-devel tar +git gcc make autoconf automake gettext-devel pkgconfig openssl-devel popt-devel device-mapper-devel libuuid-devel json-c-devel libblkid-devel findutils libtool libssh-devel tar rubygem-asciidoctor Optionally: libargon2-devel libpwquality-devel ``` @@ -88,14 +81,13 @@ sharutils device-mapper jq vim-common expect keyutils netcat shadow-utils openss **For Debian and Ubuntu**: ``` -git gcc make autoconf automake autopoint pkg-config libtool gettext libssl-dev libdevmapper-dev -libpopt-dev uuid-dev libsepol-dev libjson-c-dev libssh-dev libblkid-dev tar +git gcc make autoconf automake autopoint pkg-config libtool gettext libssl-dev libdevmapper-dev libpopt-dev uuid-dev libsepol-dev libjson-c-dev libssh-dev libblkid-dev tar asciidoctor Optionally: libargon2-0-dev libpwquality-dev ``` To run the internal testsuite (make check) you also need to install ``` -sharutils dmsetup jq xxd expect keyutils netcat passwd openssh-client sshpass +sharutils dmsetup jq xxd expect keyutils netcat-openbsd passwd openssh-client sshpass ``` Note that the list may change as Linux distributions evolve. diff --git a/configure.ac b/configure.ac index e2e73db..4ceece7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,9 @@ AC_PREREQ([2.67]) -AC_INIT([cryptsetup],[2.7.5]) +AC_INIT([cryptsetup],[2.8.2]) dnl library version from ..[-] LIBCRYPTSETUP_VERSION=$(echo $PACKAGE_VERSION | cut -f1 -d-) -LIBCRYPTSETUP_VERSION_INFO=22:0:10 +LIBCRYPTSETUP_VERSION_INFO=23:0:11 AM_SILENT_RULES([yes]) AC_CONFIG_SRCDIR(src/cryptsetup.c) @@ -399,6 +399,23 @@ AC_DEFUN([CONFIGURE_NETTLE], [ NO_FIPS([]) ]) +AC_DEFUN([CONFIGURE_MBEDTLS], [ + AC_CHECK_HEADERS(mbedtls/version.h,, + [AC_MSG_ERROR([You need mbedTLS cryptographic library.])]) + + saved_LIBS=$LIBS + AC_CHECK_LIB(mbedcrypto, mbedtls_md_init,, + [AC_MSG_ERROR([You need mbedTLS cryptographic library.])]) + AC_CHECK_FUNCS(mbedtls_pkcs5_pbkdf2_hmac_ext) + CRYPTO_LIBS=$LIBS + LIBS=$saved_LIBS + + CRYPTO_STATIC_LIBS=$CRYPTO_LIBS + use_internal_pbkdf2=0 + use_internal_argon2=1 + NO_FIPS([]) +]) + dnl ========================================================================== saved_LIBS=$LIBS @@ -481,7 +498,7 @@ fi dnl Crypto backend configuration. AC_ARG_WITH([crypto_backend], - AS_HELP_STRING([--with-crypto_backend=BACKEND], [crypto backend (gcrypt/openssl/nss/kernel/nettle) [openssl]]), + AS_HELP_STRING([--with-crypto_backend=BACKEND], [crypto backend (gcrypt/openssl/nss/kernel/nettle/mbedtls) [openssl]]), [], [with_crypto_backend=openssl]) dnl Kernel crypto API backend needed for benchmark and tcrypt @@ -501,6 +518,7 @@ case $with_crypto_backend in nss) CONFIGURE_NSS([]) ;; kernel) CONFIGURE_KERNEL([]) ;; nettle) CONFIGURE_NETTLE([]) ;; + mbedtls) CONFIGURE_MBEDTLS([]) ;; *) AC_MSG_ERROR([Unknown crypto backend.]) ;; esac AM_CONDITIONAL(CRYPTO_BACKEND_GCRYPT, test "$with_crypto_backend" = "gcrypt") @@ -508,6 +526,7 @@ AM_CONDITIONAL(CRYPTO_BACKEND_OPENSSL, test "$with_crypto_backend" = "openssl") AM_CONDITIONAL(CRYPTO_BACKEND_NSS, test "$with_crypto_backend" = "nss") AM_CONDITIONAL(CRYPTO_BACKEND_KERNEL, test "$with_crypto_backend" = "kernel") AM_CONDITIONAL(CRYPTO_BACKEND_NETTLE, test "$with_crypto_backend" = "nettle") +AM_CONDITIONAL(CRYPTO_BACKEND_MBEDTLS, test "$with_crypto_backend" = "mbedtls") AM_CONDITIONAL(CRYPTO_INTERNAL_PBKDF2, test $use_internal_pbkdf2 = 1) AC_DEFINE_UNQUOTED(USE_INTERNAL_PBKDF2, [$use_internal_pbkdf2], [Use internal PBKDF2]) @@ -661,8 +680,36 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]) CFLAGS=$saved_CFLAGS +dnl Force compiler to use zero_call_used_regs("used") to check for the function attribute support. +dnl Otherwise the compiler may falsely advertise it with __has_attribute operator, even though +dnl it does not implement it on some archs. +AC_MSG_CHECKING([for zero_call_used_regs(user)]) +saved_CFLAGS=$CFLAGS +CFLAGS="-O0 -Werror" +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + void _test_function(void); + __attribute__((zero_call_used_regs("used"))) void _test_function(void) { + volatile int *i; volatile int j = 0; if (j) *i = 0; + } +]], +[[ _test_function() ]] +)],[ + AC_DEFINE([HAVE_ATTRIBUTE_ZEROCALLUSEDREGS], 1, [Define to 1 to use __attribute__((zero_call_used_regs("used")))]) + AC_MSG_RESULT([yes]) +], [ + AC_MSG_RESULT([no]) +]) +CFLAGS=$saved_CFLAGS + AC_MSG_CHECKING([for systemd tmpfiles config directory]) -PKG_CHECK_VAR([systemd_tmpfilesdir], [systemd], [tmpfilesdir], [], [systemd_tmpfilesdir=no]) +if test "x$prefix" != "xNONE"; then + saved_PKG_CONFIG=$PKG_CONFIG + PKG_CONFIG="$PKG_CONFIG --define-variable=prefix='${prefix}'" + PKG_CHECK_VAR([systemd_tmpfilesdir], [systemd], [tmpfilesdir], [], [systemd_tmpfilesdir=no]) + PKG_CONFIG=$saved_PKG_CONFIG +else + PKG_CHECK_VAR([systemd_tmpfilesdir], [systemd], [tmpfilesdir], [], [systemd_tmpfilesdir=no]) +fi AC_MSG_RESULT([$systemd_tmpfilesdir]) AC_SUBST([DEVMAPPER_LIBS]) @@ -776,8 +823,9 @@ CS_NUM_WITH([verity-hash-block], [hash block size for verity mode], [4096]) CS_NUM_WITH([verity-salt-size], [salt size for verity mode], [32]) CS_NUM_WITH([verity-fec-roots], [parity bytes for verity FEC], [2]) -CS_STR_WITH([tmpfilesdir], [override default path to directory with systemd temporary files], []) -test -z "$with_tmpfilesdir" && with_tmpfilesdir=$systemd_tmpfilesdir +AC_ARG_WITH([tmpfilesdir], + AS_HELP_STRING([--with-tmpfilesdir=DIR], [override default path to directory with systemd temporary files]), + [], [with_tmpfilesdir=$systemd_tmpfilesdir]) test "x$with_tmpfilesdir" = "xno" || { CS_ABSPATH([${with_tmpfilesdir}],[with-tmpfilesdir]) DEFAULT_TMPFILESDIR=$with_tmpfilesdir @@ -796,7 +844,9 @@ test -z "$with_luks2_lock_dir_perms" && with_luks2_lock_dir_perms=0700 DEFAULT_LUKS2_LOCK_DIR_PERMS=$with_luks2_lock_dir_perms AC_SUBST(DEFAULT_LUKS2_LOCK_DIR_PERMS) -CS_STR_WITH([luks2-external-tokens-path], [path to directory with LUKSv2 external token handlers (plugins)], [LIBDIR/cryptsetup]) +AC_ARG_WITH([luks2-external-tokens-path], + AS_HELP_STRING([--with-luks2-external-tokens-path=DIR], [path to directory with LUKSv2 external token handlers (plugins)]), + [], [with_luks2_external_tokens_path=""]) if test -n "$with_luks2_external_tokens_path"; then CS_ABSPATH([${with_luks2_external_tokens_path}],[with-luks2-external-tokens-path]) EXTERNAL_LUKS2_TOKENS_PATH=$with_luks2_external_tokens_path @@ -804,6 +854,17 @@ else EXTERNAL_LUKS2_TOKENS_PATH="\${libdir}/cryptsetup" fi AC_SUBST(EXTERNAL_LUKS2_TOKENS_PATH) +dnl We need to define expanded EXTERNAL_LUKS2_TOKENS_PATH, but some other code can depend on prefix=NONE. +dnl Pretend you do not see this hack :-) +saved_prefix=$prefix +saved_exec_prefix=$exec_prefix +test "x$prefix" = "xNONE" && prefix="$ac_default_prefix" +test "x$exec_prefix" = "xNONE" && exec_prefix="$prefix" +expanded_EXTERNAL_LUKS2_TOKENS_PATH=$(eval echo "$EXTERNAL_LUKS2_TOKENS_PATH") +expanded_EXTERNAL_LUKS2_TOKENS_PATH=$(eval echo "$expanded_EXTERNAL_LUKS2_TOKENS_PATH") +AC_DEFINE_UNQUOTED([EXTERNAL_LUKS2_TOKENS_PATH], ["$expanded_EXTERNAL_LUKS2_TOKENS_PATH"], [path to directory with LUKSv2 external token handlers (plugins)]) +prefix=$saved_prefix +exec_prefix=$saved_exec_prefix dnl Override default LUKS format version (for cryptsetup or cryptsetup-reencrypt format actions only). AC_ARG_WITH([default_luks_format], diff --git a/debian/.gitattributes b/debian/.gitattributes deleted file mode 100644 index 592f8c4..0000000 --- a/debian/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -/changelog merge=dpkg-mergechangelogs diff --git a/debian/README.Debian b/debian/README.Debian index 99633bf..d8e28bc 100644 --- a/debian/README.Debian +++ b/debian/README.Debian @@ -277,7 +277,7 @@ manage the keyslots for both original and backup device independently. 10. Changing the boot order of cryptdisks init scripts ------------------------------------------------------ +------------------------------------------------------ In order to support non-standard setups, it might be necessary to change the order of init scripts in the boot process. Cryptsetup already installs two diff --git a/debian/README.gnupg b/debian/README.gnupg index 837d151..5aa26d8 100644 --- a/debian/README.gnupg +++ b/debian/README.gnupg @@ -10,7 +10,7 @@ The following example assumes that you store the encrypted keyfile in First, you'll have to create the encrypted keyfile: dd if=/dev/random bs=1 count=256 | gpg --no-options --no-random-seed-file \ - --no-default-keyring --keyring /dev/null --secret-keyring /dev/null \ + --no-default-keyring --keyring /dev/null \ --trustdb-name /dev/null --symmetric --output /etc/keys/cryptkey.gpg Next the LUKS device needs to be formated with the key. For that, the diff --git a/debian/README.initramfs b/debian/README.initramfs index d85ae9c..1afbc90 100644 --- a/debian/README.initramfs +++ b/debian/README.initramfs @@ -247,13 +247,13 @@ this limitation: [#671037]: https://bugs.debian.org/671037 -12. Storing keyfiles directly in the initrd -------------------------------------------- +12. Storing keyfiles directly in the initramfs +---------------------------------------------- Normally devices using a keyfile are ignored (with a loud warning), and -the key file itself is not included in the initrd, because the initramfs +the key file itself is not included in the initramfs, because the initramfs image typically lives on an unencrypted `/boot` partition. However in -some cases it is desirable to include the key file in the initrd; for +some cases it is desirable to include the key file in the initramfs; for instance recent versions of GRUB support booting from encrypted block devices, allowing an encrypted `/boot` partition. @@ -262,7 +262,7 @@ of the environment variable KEYFILE_PATTERN (interpreted as a shell pattern) will be included in the initramfs image. For instance if `/etc/crypttab` lists two key files `/etc/keys/{root,swap}.key`, you can add the following to `/etc/cryptsetup-initramfs/conf-hook` to add them to -the initrd. +the initramfs. KEYFILE_PATTERN="/etc/keys/*.key" @@ -273,6 +273,17 @@ following to `/etc/initramfs-tools/initramfs.conf`. UMASK=0077 +13. The stages in the initramfs at which dm-crypt devices are mapped +-------------------------------------------------------------------- + +The devices necessary for the root filesystem, /usr, any resume swap device and +any device with the `initramfs`-option in `crypttab` are first tried to be +mapped (that is: "opened" or "decrypted") in `initramfs-tools`'s `local-top`- +phase. +Any which couldn't be mapped there are retried in the `local-block`-phase. + +This may be subject to change. + -- David Härdeman -- Jonas Meurer Thu, 01 Nov 2012 13:44:31 +0100 diff --git a/debian/changelog b/debian/changelog index f9a68ae..f7d3489 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,20 +1,108 @@ -cryptsetup (2:2.7.5-1deepin2.3) unstable; urgency=medium - - * add Chinese localization to cryptroot - - -- Liang Bo Thu, 26 Apr 2025 16:40:22 +0800 - -cryptsetup (2:2.7.5-1deepin2.2) unstable; urgency=medium - - * skip ERROR on usr-overlay device when updating initramfs - - -- Liang Bo Mon, 28 Apr 2024 16:00:51 +0800 - -cryptsetup (2:2.7.5-1deepin2.1) unstable; urgency=medium - - * revert to 2:2.7.5-1 - - -- Liang Bo Tue, 01 Apr 2024 14:52:13 +0800 +cryptsetup (2:2.8.2-1) unstable; urgency=medium + + * New upstream bugfix release. + * d/t/cryptdisks: Add calls to `udevadm settle`. + * d/control: Remove `Rules-Requires-Root: no`. + * d/watch: Port to Version 5. + + -- Guilhem Moulin Thu, 18 Dec 2025 22:04:25 +0100 + +cryptsetup (2:2.8.1-2) unstable; urgency=medium + + * Exclude `reencryption-compat-test` from Makefile.localtest. + (Closes: #1122539) + + -- Guilhem Moulin Thu, 11 Dec 2025 23:29:35 +0100 + +cryptsetup (2:2.8.1-1) unstable; urgency=medium + + * New upstream release. + * DEP-8: Don't skip TPM trusted upstream tests. This adds `Depends: + swtpm, swtpm-tools, tpm2-tools` to the upstream testsuite autopkgtest. + * DEP-8: Don't skip upstream's systemd plugin tests. This adds `Depends: + systemd-cryptsetup` to the upstream testsuite autopkgtest. + + -- Guilhem Moulin Tue, 19 Aug 2025 13:38:37 +0200 + +cryptsetup (2:2.8.0-3) unstable; urgency=medium + + [ Nuri KÜÇÜKLER ] + * Add Turkish debconf templates translation. + + [ Luca Boccassi ] + * DEP-8: Remove 80-systemd-osc-context.sh bash profile drop-in from + cryptroot-* test images in order to fix compatibility with systemd v258. + (Closes: #1110818) + + -- Guilhem Moulin Wed, 13 Aug 2025 13:38:32 +0200 + +cryptsetup (2:2.8.0-2) unstable; urgency=medium + + * Upload to unstable. + + -- Guilhem Moulin Sat, 09 Aug 2025 22:21:31 +0200 + +cryptsetup (2:2.8.0-1) experimental; urgency=medium + + * New upstream release. + + -- Guilhem Moulin Tue, 24 Jun 2025 11:40:36 +0200 + +cryptsetup (2:2.8.0~rc1-1) experimental; urgency=low + + * New upstream release candidate. + * Drop d/patches/* applied upstream. + + -- Guilhem Moulin Mon, 16 Jun 2025 15:44:19 +0200 + +cryptsetup (2:2.8.0~rc0-1) experimental; urgency=low + + * New upstream release candidate 2.8.0, with support for inline mode (use HW + sectors with additional hardware metadata space). + * DEP-8: Add Depends: e2fsprogs. + * d/libcryptsetup12.symbols: Add new symbols. + * Adjust d/copyright. + * DEP-8: Build libcrypto_backend.la and crypto-check before running the + upstream test suite. + + -- Guilhem Moulin Thu, 12 Jun 2025 19:00:45 +0200 + +cryptsetup (2:2.7.5-2) unstable; urgency=medium + + [ Christoph Anton Mitterer ] + * d/README.Debian: Minor improvements. + * d/README.Debian: Document when during the initramfs devices are mapped. + * d/README.Debian: Change initrd to initramfs. + + [ Stephen Gildea ] + * initramfs hook: Improve the "Source mismatch" error message when + /etc/crypttab gives a source device that does not match the current + source. + + [ Guilhem Moulin ] + * DEP-8: No longer mark cryptroot-* as flaky and only run them on amd64, see + https://bugs.debian.org/1073052#50. + * Fix d/t/initramfs-hook and d/t/initramfs-hook-legacy with initramfs-tools + ≥0.146. (Closes: #1099818) + * cryptsetup-suspend-wrapper, DEP-8: Don't hardcode unmkinitramfs destdir. + * d/t/cryptroot-*: Pass --bitmap=internal to mdadm(8). + * decrypt_gnupg: Drop obsolete option --secret-keyring. (Closes: #1099760) + * initramfs hook: Add vmx_crypto module. (Closes: #1087271) + * d/copyright: Replace FSF's old postal address with an URL. + * Update Standards-Version to 4.7.2 (no changes necessary). + * Boot script: Fix prereq() logic between directories. (Closes: #1081552) + + [ Carles Pina i Estany ] + * Added po-debconf Catalan translation. (Closes: #1101115) + + [ Vladimir Petko ] + * Fix cryptroot-* autopkgtests on Ubuntu. (Closes: #1031198) + + [ Nicolas Melot ] + * initramfs: Process crypttab entries with the 'initramfs' option first and + preserve their order. (Closes: #1055024) + + -- Guilhem Moulin Sun, 04 May 2025 21:55:13 +0200 cryptsetup (2:2.7.5-1) unstable; urgency=medium diff --git a/debian/control b/debian/control index 69372a4..16663f2 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,6 @@ Priority: optional Maintainer: Debian Cryptsetup Team Uploaders: Jonas Meurer , Guilhem Moulin -Rules-Requires-Root: no Build-Depends: asciidoctor , autoconf, automake (>= 1:1.12), @@ -24,13 +23,14 @@ Build-Depends: asciidoctor , libssh-dev, libssl-dev (>> 3.2~), libtool, + openssl-provider-legacy , pkgconf, po-debconf, procps , uuid-dev, xsltproc , xxd -Standards-Version: 4.7.0 +Standards-Version: 4.7.2 Homepage: https://gitlab.com/cryptsetup/cryptsetup Vcs-Browser: https://salsa.debian.org/cryptsetup-team/cryptsetup Vcs-Git: https://salsa.debian.org/cryptsetup-team/cryptsetup.git -b debian/latest diff --git a/debian/copyright b/debian/copyright index 2334d87..963374a 100644 --- a/debian/copyright +++ b/debian/copyright @@ -6,8 +6,8 @@ Upstream-Name: cryptsetup Files: * Copyright: © 2004 Christophe Saout © 2004-2008 Clemens Fruhwirth - © 2008-2023 Red Hat, Inc. - © 2008-2023 Milan Broz + © 2008-2025 Red Hat, Inc. + © 2008-2025 Milan Broz License: GPL-2+ with OpenSSL exception Files: debian/* @@ -15,7 +15,7 @@ Copyright: © 2004-2005 Wesley W. Terpstra © 2005-2006 Michael Gebetsroither © 2006-2008 David Härdeman © 2005-2015 Jonas Meurer - © 2016-2023 Guilhem Moulin + © 2016-2025 Guilhem Moulin License: GPL-2+ Files: debian/scripts/suspend/cryptsetup-suspend.c @@ -57,37 +57,37 @@ Copyright: © 2005-2015 Jonas Meurer License: GPL-2+ Files: debian/tests/* -Copyright: © 2021-2022 Guilhem Moulin +Copyright: © 2021-2025 Guilhem Moulin License: GPL-3+ -Files: docs/examples/* tests/all-symbols-test.c -Copyright: © 2011-2023 Red Hat, Inc. +Files: docs/examples/* tests/all-symbols-test.c tests/crypto-check.c +Copyright: © 2011-2025 Red Hat, Inc. License: LGPL-2.1+ Files: lib/bitlk/* -Copyright: © 2019-2023 Red Hat, Inc. - © 2019-2023 Milan Broz - © 2019-2023 Vojtech Trefny +Copyright: © 2019-2025 Red Hat, Inc. + © 2019-2025 Milan Broz + © 2019-2025 Vojtech Trefny License: LGPL-2.1+ Files: tokens/ssh/* -Copyright: © 2016-2023 Milan Broz - © 2020-2023 Vojtech Trefny +Copyright: © 2016-2025 Milan Broz + © 2020-2025 Vojtech Trefny License: LGPL-2.1+ Files: tokens/ssh/cryptsetup-ssh.c -Copyright: © 2016-2023 Milan Broz - © 2021-2023 Vojtech Trefny +Copyright: © 2016-2025 Milan Broz + © 2021-2025 Vojtech Trefny License: GPL-2+ -Files: lib/crypto_backend/* lib/integrity/* lib/loopaes/* lib/tcrypt/* lib/verity/* -Copyright: © 2009-2023 Red Hat, Inc. - © 2010-2023 Milan Broz +Files: lib/crypto_backend/* lib/integrity/* lib/loopaes/* lib/luks2/hw_opal/* lib/tcrypt/* lib/utils_storage_wrappers.* lib/verity/* +Copyright: © 2009-2025 Red Hat, Inc. + © 2010-2025 Milan Broz License: LGPL-2.1+ Files: lib/crypto_backend/base64.c Copyright: © 2010 Lennart Poettering - © 2021-2023 Milan Broz + © 2021-2025 Milan Broz License: LGPL-2.1+ Files: lib/crypto_backend/utf8.c @@ -98,8 +98,8 @@ Copyright: © 2010 Lennart Poettering License: GPL-2+ Files: lib/crypto_backend/crypto_openssl.c -Copyright: © 2009-2023 Red Hat, Inc. - © 2010-2023 Milan Broz +Copyright: © 2009-2025 Red Hat, Inc. + © 2010-2025 Milan Broz License: LGPL-2.1+ with OpenSSL exception Files: lib/fvault2/fvault2.c lib/fvault2/fvault2.h @@ -107,8 +107,8 @@ Copyright: © 2021-2022 Pavel Tobias License: LGPL-2.1+ with OpenSSL exception Files: lib/keyslot_context.c lib/keyslot_context.h -Copyright: © 2022-2023 Red Hat, Inc. - © 2022-2023 Ondrej Kozina +Copyright: © 2022-2025 Red Hat, Inc. + © 2022-2025 Ondrej Kozina License: GPL-2+ Files: lib/crypto_backend/argon2/* @@ -122,6 +122,14 @@ Files: lib/crypto_backend/argon2/encoding.c Copyright: © 2015 Thomas Pornin License: CC0 or Apache-2.0 +Files: tests/fuzz/FuzzerInterface.h +Copyright: © LLVM Project +License: Apache-2.0 + +Files: tests/fuzz/json_proto_converter.cc tests/fuzz/json_proto_converter.h +Copyright: © 2020 Google, Inc. +License: Apache-2.0 + Files: lib/crypto_backend/crc32.c Copyright: © 1986 Gary S. Brown License: public-domain @@ -139,7 +147,7 @@ License: public-domain what you wish. Files: misc/luks-header-from-active -Copyright: © 2011-2024 Milan Broz +Copyright: © 2011-2025 Milan Broz License: LGPL-2.1+ Files: FAQ.md @@ -158,10 +166,8 @@ License: GPL-2+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. + You should have received a copy of the GNU General Public License along + with this program. If not, see . . On Debian systems, the complete text of the GNU General Public License version 2 can be found in `/usr/share/common-licenses/GPL-2'. @@ -177,10 +183,8 @@ License: GPL-2+ with OpenSSL exception MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. + You should have received a copy of the GNU General Public License along + with this program. If not, see . . On Debian systems, the complete text of the GNU General Public License version 2 can be found in `/usr/share/common-licenses/GPL-2'. @@ -208,9 +212,8 @@ License: GPL-3+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + You should have received a copy of the GNU General Public License along + with this program. If not, see . . On Debian systems, the complete text of the GNU General Public License version 3 can be found in `/usr/share/common-licenses/GPL-3`. @@ -226,10 +229,8 @@ License: LGPL-2.1+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. . - You should have received a copy of the GNU Lesser General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . . On Debian systems, the complete text of the GNU Lesser General Public License version 2.1 can be found in `/usr/share/common-licenses/LGPL-2.1'. @@ -245,10 +246,8 @@ License: LGPL-2.1+ with OpenSSL exception MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. . - You should have received a copy of the GNU Lesser General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . . On Debian systems, the complete text of the GNU Lesser General Public License version 2.1 can be found in `/usr/share/common-licenses/LGPL-2.1'. diff --git a/debian/functions b/debian/functions index 3845b9a..63ecf5d 100644 --- a/debian/functions +++ b/debian/functions @@ -162,7 +162,6 @@ crypttab_validate_option() { submit-from-crypt-cpus) OPTION="submit_from_crypt_cpus";; no-read-workqueue) OPTION="no_read_workqueue";; no-write-workqueue) OPTION="no_write_workqueue";; - tpm2-device) OPTION="tpm2_device";; esac case "$o" in @@ -248,7 +247,6 @@ crypttab_validate_option() { submit-from-crypt-cpus) ;; no-read-workqueue) ;; no-write-workqueue) ;; - tpm2-device) ;; x-initrd.attach) unset -v OPTION ;; # ignored, cf. #1072058 *) @@ -597,11 +595,9 @@ _resolve_device() { MAJ="$maj" MIN="$min" return 0 + else + cryptsetup_message "ERROR: Couldn't resolve device $spec" fi - case $spec in - overlay|usr-overlay) ;; - *) cryptsetup_message "ERROR: Couldn't resolve device $spec";; - esac return 1 } diff --git a/debian/initramfs/hooks/cryptroot b/debian/initramfs/hooks/cryptroot index a667354..ca759bd 100644 --- a/debian/initramfs/hooks/cryptroot +++ b/debian/initramfs/hooks/cryptroot @@ -51,10 +51,11 @@ crypttab_find_and_print_entry() { # crypttab_parse_options(). # Return 0 on success, 1 on error. crypttab_print_entry() { - local DEV MAJ MIN uuid keyfile + local DEV MAJ MIN name_uses uuid keyfile if _resolve_device "$CRYPTTAB_SOURCE"; then - if [ "$(dmsetup info -c --noheadings -o devnos_used -- "$CRYPTTAB_NAME" 2>/dev/null)" != "$MAJ:$MIN" ]; then - cryptsetup_message "ERROR: $CRYPTTAB_NAME: Source mismatch" + name_uses="$(dmsetup info -c --noheadings -o devnos_used -- "$CRYPTTAB_NAME" 2>/dev/null)" || name_uses="N/A" + if [ "$name_uses" != "$MAJ:$MIN" ]; then + cryptsetup_message "ERROR: Source mismatch: $CRYPTTAB_NAME uses $name_uses, but $CRYPTTAB_SOURCE is $MAJ:$MIN" elif [ "${_CRYPTTAB_SOURCE#[A-Za-z]*=}" = "$_CRYPTTAB_SOURCE" ] && \ [ "${CRYPTTAB_SOURCE#/dev/disk/by-}" = "$CRYPTTAB_SOURCE" ] && \ [ "${CRYPTTAB_SOURCE#/dev/mapper/}" = "$CRYPTTAB_SOURCE" ] && \ @@ -177,6 +178,9 @@ generate_initrd_crypttab() { true >"$DESTDIR/cryptroot/targets" { + # add crypttab entries with the 'initramfs' option set + crypttab_foreach_entry crypttab_print_initramfs_entry + if devnos="$(get_mnt_devno /)"; then usage=rootfs foreach_cryptdev crypttab_find_and_print_entry $devnos else @@ -190,9 +194,6 @@ generate_initrd_crypttab() { if devnos="$(get_mnt_devno /usr)"; then usage="" foreach_cryptdev crypttab_find_and_print_entry $devnos fi - - # add crypttab entries with the 'initramfs' option set - crypttab_foreach_entry crypttab_print_initramfs_entry } 3>"$DESTDIR/cryptroot/crypttab" rm -f "$DESTDIR/cryptroot/targets" } @@ -351,10 +352,6 @@ copy_exec /sbin/dmsetup [ "$ASKPASS" = n ] || copy_exec /lib/cryptsetup/askpass -copy_exec /bin/gettext -mkdir -p $DESTDIR/usr/share/locale/zh_CN/LC_MESSAGES/ -cp /usr/share/locale/zh_CN/LC_MESSAGES/cryptroot.mo $DESTDIR/usr/share/locale/zh_CN/LC_MESSAGES/ - # We need sed. Either via busybox or as standalone binary. if [ "$BUSYBOX" = n ] || [ -z "$BUSYBOXDIR" ]; then copy_exec /bin/sed @@ -380,7 +377,7 @@ if [ "$MODULES" = most ]; then else if [ "$MODULES" != "dep" ]; then # with large initramfs, we always add a basic subset of modules - add_crypto_modules aes cbc chainiv cryptomgr krng sha256 xts + add_crypto_modules aes cbc chainiv cryptomgr krng sha256 xts vmx_crypto fi add_crypto_modules $(printf '%s' "${CRYPTO_MODULES-}" | tr ' ' '\n' | sort -u) fi diff --git a/debian/initramfs/po/Makefile b/debian/initramfs/po/Makefile deleted file mode 100644 index c5fce87..0000000 --- a/debian/initramfs/po/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -XGETTEXT = xgettext -MSGFMT = msgfmt -MSGMERGE = msgmerge - -LOCALEDIR = /usr/share/locale - -.SUFFIXES: .po .mo .pot - -%.mo: %.po - $(MSGFMT) -o $@ $< - -PO = $(wildcard *.po) -LANG = $(basename $(PO)) -MO = $(addsuffix .mo,$(LANG)) -SOURCES = ../scripts/local-top/cryptroot - -all: update $(MO) -update: cryptroot.pot - -@for po in $(PO); do \ - echo -n "Updating $$po"; \ - $(MSGMERGE) -U $$po cryptroot.pot; \ - done; - -cryptroot.pot: $(SOURCES) - $(XGETTEXT) -c -L Shell --keyword=get_loc_str \ - -o $@ $(SOURCES) - -install: all - for i in $(MO) ; do \ - t=$(DESTDIR)/$(LOCALEDIR)/`basename $$i .mo`/LC_MESSAGES ;\ - install -d $$t ;\ - install -m 644 $$i $$t/cryptroot.mo ;\ - done - -clean: - $(RM) $(MO) *~ - -.PHONY: update diff --git a/debian/initramfs/po/cryptroot.pot b/debian/initramfs/po/cryptroot.pot deleted file mode 100644 index ae5338e..0000000 --- a/debian/initramfs/po/cryptroot.pot +++ /dev/null @@ -1,42 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-01-03 14:13+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../scripts/local-top/cryptroot:186 -msgid "Failed to unlock automatically, please check TPM or input recovery key." -msgstr "" - -#: ../scripts/local-top/cryptroot:188 -msgid "Wrong password! You have %s input chances more." -msgstr "" - -#: ../scripts/local-top/cryptroot:191 -msgid "Wrong recovery key! You have %s input chances more" -msgstr "" - -#: ../scripts/local-top/cryptroot:194 -msgid "Wrong password! You have %s input chances more" -msgstr "" - -#: ../scripts/local-top/cryptroot:225 -msgid "Unlocking successfully!" -msgstr "" - -#: ../scripts/local-top/cryptroot:230 -msgid "Please reboot!" -msgstr "" diff --git a/debian/initramfs/po/zh_CN.po b/debian/initramfs/po/zh_CN.po deleted file mode 100644 index dcd2357..0000000 --- a/debian/initramfs/po/zh_CN.po +++ /dev/null @@ -1,43 +0,0 @@ -# Chinese translations for PACKAGE package -# PACKAGE 软件包的简体中文翻译. -# Copyright (C) 2024 THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# , 2024. -# -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-01-03 14:13+0800\n" -"PO-Revision-Date: 2024-01-03 14:19+0800\n" -"Last-Translator: liangbo@uniontech.com\n" -"Language-Team: Chinese (simplified)\n" -"Language: zh_CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.2.1\n" - -#: ../scripts/local-top/cryptroot:186 -msgid "Failed to unlock automatically, please check TPM or input recovery key." -msgstr "自动解密失败,请检查TPM硬件或输入恢复密钥解密." - -#: ../scripts/local-top/cryptroot:188 -msgid "Wrong password! You have %s input chances more." -msgstr "密码错误,您还可以输入%s次." - -#: ../scripts/local-top/cryptroot:191 -msgid "Wrong recovery key! You have %s input chances more" -msgstr "恢复密钥错误,您还可以输入%s次" - -#: ../scripts/local-top/cryptroot:194 -msgid "Wrong password! You have %s input chances more" -msgstr "密码错误,您还可以输入%s次" - -#: ../scripts/local-top/cryptroot:225 -msgid "Unlocking successfully!" -msgstr "解锁成功!" - -#: ../scripts/local-top/cryptroot:230 -msgid "Please reboot!" -msgstr "多次输入错误,请重启系统后再尝试" diff --git a/debian/initramfs/scripts/local-top/cryptroot b/debian/initramfs/scripts/local-top/cryptroot index c8318b1..cf32118 100644 --- a/debian/initramfs/scripts/local-top/cryptroot +++ b/debian/initramfs/scripts/local-top/cryptroot @@ -5,16 +5,17 @@ PREREQ="cryptroot-prepare" # # Standard initramfs preamble # -prereqs() -{ - # Make sure that cryptroot is run last in local-top - local req - for req in "${0%/*}"/*; do - script="${req##*/}" - if [ "$script" != "${0##*/}" ]; then - printf '%s\n' "$script" - fi - done +prereqs() { + # Make sure that cryptroot is run last in local-top + local req script="${0##*/}" dir + dir="$DESTDIR/scripts/${CRYPTROOT_STAGE-local-top}" + for req in "$dir"/*; do + test -x "$req" || continue + req="${req##*/}" + if [ "$req" != "$script" ]; then + printf '%s\n' "$req" + fi + done } case $1 in @@ -29,19 +30,6 @@ esac [ -f /lib/cryptsetup/functions ] || return 0 . /lib/cryptsetup/functions -if [ -f /etc/default/locale ]; then - . /etc/default/locale -fi -if [ -z $LANG ]; then - if [ -n "$locales" ]; then - LANG=$locales - LANGUAGE=${locales%.*} - fi -fi -export LANG -export LANGUAGE - -alias get_loc_str='gettext "cryptroot"' # wait_for_source() # Wait for encrypted $CRYPTTAB_SOURCE . Set $CRYPTTAB_SOURCE @@ -161,7 +149,7 @@ setup_mapping() { fi fi - local count=0 maxtries="${CRYPTTAB_OPTION_tries:-10}" recovery_tries=5 fstype vg rv + local count=0 maxtries="${CRYPTTAB_OPTION_tries:-3}" fstype vg rv while [ $maxtries -le 0 ] || [ $count -lt $maxtries ]; do if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ] && [ "$CRYPTTAB_KEY" != "none" ]; then # unlock via keyfile @@ -174,29 +162,8 @@ setup_mapping() { count=$(( $count + 1 )) if [ $rv -ne 0 ]; then - left_count=$(( $maxtries - $count)) - if [ -f "/tmp/crypt_mode" ]; then - if [ $left_count -ge $recovery_tries ]; then - left_count=$(($left_count - $recovery_tries)) - crypt_mode=$(cat /tmp/crypt_mode) - if [ "$crypt_mode" = "tpm" ]; then - count=5 - # make askpass accept recovery key - echo "$CRYPTTAB_NAME 6" > /tmp/crypt-tries.cache - pattern=$(get_loc_str "Failed to unlock automatically, please check TPM or input recovery key.") - else - pattern=$(get_loc_str "Wrong password! You have %s input chances more.") - fi - else - pattern=$(get_loc_str "Wrong recovery key! You have %s input chances more") - fi - else - pattern=$(get_loc_str "Wrong password! You have %s input chances more") - fi - wrong_pass_tip=$(printf $pattern $left_count) - plymouth message --text="$wrong_pass_tip" - sleep 2 - plymouth message --text="" + cryptsetup_message "ERROR: $CRYPTTAB_NAME: cryptsetup failed, bad password or options?" + sleep 1 continue elif ! dev="$(dm_blkdevname "$CRYPTTAB_NAME")"; then cryptsetup_message "ERROR: $CRYPTTAB_NAME: unknown error setting up device mapping" @@ -214,15 +181,12 @@ setup_mapping() { fi fi - plymouth message --text="$(get_loc_str "Unlocking successfully!")" + cryptsetup_message "$CRYPTTAB_NAME: set up successfully" wait_for_udev 10 return 0 done - plymouth message --text="$(get_loc_str "Please reboot!")" - while true; do - sleep 100 - done + cryptsetup_message "ERROR: $CRYPTTAB_NAME: maximum number of tries exceeded" exit 1 } diff --git a/debian/libcryptsetup12.symbols b/debian/libcryptsetup12.symbols index 5b30b7f..76548c9 100644 --- a/debian/libcryptsetup12.symbols +++ b/debian/libcryptsetup12.symbols @@ -5,6 +5,7 @@ libcryptsetup.so.12 libcryptsetup12 #MINVER# CRYPTSETUP_2.5@CRYPTSETUP_2.5 2:2.5 CRYPTSETUP_2.6@CRYPTSETUP_2.6 2:2.6 CRYPTSETUP_2.7@CRYPTSETUP_2.7 2:2.7 + CRYPTSETUP_2.8@CRYPTSETUP_2.8 2:2.8 crypt_activate_by_keyfile@CRYPTSETUP_2.0 2:1.4 crypt_activate_by_keyfile_offset@CRYPTSETUP_2.0 2:1.4.3 crypt_activate_by_keyring@CRYPTSETUP_2.0 2:2.0 @@ -25,6 +26,7 @@ libcryptsetup.so.12 libcryptsetup12 #MINVER# crypt_dump_json@CRYPTSETUP_2.4 2:2.4 crypt_format@CRYPTSETUP_2.0 2:1.4 crypt_format@CRYPTSETUP_2.4 2:2.4 + crypt_format_inline@CRYPTSETUP_2.8 2:2.8 crypt_format_luks2_opal@CRYPTSETUP_2.7 2:2.7 crypt_free@CRYPTSETUP_2.0 2:1.4 crypt_get_active_device@CRYPTSETUP_2.0 2:1.4 @@ -43,6 +45,7 @@ libcryptsetup.so.12 libcryptsetup12 #MINVER# crypt_get_label@CRYPTSETUP_2.5 2:2.5 crypt_get_metadata_device_name@CRYPTSETUP_2.0 2:2.1 crypt_get_metadata_size@CRYPTSETUP_2.0 2:2.1 + crypt_get_old_volume_key_size@CRYPTSETUP_2.8 2:2.8 crypt_get_pbkdf_default@CRYPTSETUP_2.0 2:2.0.3 crypt_get_pbkdf_type@CRYPTSETUP_2.0 2:2.0 crypt_get_pbkdf_type_params@CRYPTSETUP_2.0 2:2.1 @@ -74,12 +77,19 @@ libcryptsetup.so.12 libcryptsetup12 #MINVER# crypt_keyslot_context_get_error@CRYPTSETUP_2.6 2:2.6 crypt_keyslot_context_get_type@CRYPTSETUP_2.6 2:2.6 crypt_keyslot_context_init_by_keyfile@CRYPTSETUP_2.6 2:2.6 + crypt_keyslot_context_init_by_keyfile@CRYPTSETUP_2.8 2:2.8 crypt_keyslot_context_init_by_keyring@CRYPTSETUP_2.7 2:2.7 + crypt_keyslot_context_init_by_keyring@CRYPTSETUP_2.8 2:2.8 crypt_keyslot_context_init_by_passphrase@CRYPTSETUP_2.6 2:2.6 + crypt_keyslot_context_init_by_passphrase@CRYPTSETUP_2.8 2:2.8 crypt_keyslot_context_init_by_signed_key@CRYPTSETUP_2.7 2:2.7 + crypt_keyslot_context_init_by_signed_key@CRYPTSETUP_2.8 2:2.8 crypt_keyslot_context_init_by_token@CRYPTSETUP_2.6 2:2.6 + crypt_keyslot_context_init_by_token@CRYPTSETUP_2.8 2:2.8 crypt_keyslot_context_init_by_vk_in_keyring@CRYPTSETUP_2.7 2:2.7 + crypt_keyslot_context_init_by_vk_in_keyring@CRYPTSETUP_2.8 2:2.8 crypt_keyslot_context_init_by_volume_key@CRYPTSETUP_2.6 2:2.6 + crypt_keyslot_context_init_by_volume_key@CRYPTSETUP_2.8 2:2.8 crypt_keyslot_context_set_pin@CRYPTSETUP_2.6 2:2.6 crypt_keyslot_destroy@CRYPTSETUP_2.0 2:1.4 crypt_keyslot_get_encryption@CRYPTSETUP_2.0 2:2.1 @@ -99,6 +109,7 @@ libcryptsetup.so.12 libcryptsetup12 #MINVER# crypt_persistent_flags_set@CRYPTSETUP_2.0 2:2.0 crypt_reencrypt@CRYPTSETUP_2.0 2:2.2 crypt_reencrypt_init_by_keyring@CRYPTSETUP_2.0 2:2.2 + crypt_reencrypt_init_by_keyslot_context@CRYPTSETUP_2.8 2:2.8 crypt_reencrypt_init_by_passphrase@CRYPTSETUP_2.0 2:2.2 crypt_reencrypt_run@CRYPTSETUP_2.4 2:2.4 crypt_reencrypt_status@CRYPTSETUP_2.0 2:2.2 @@ -113,6 +124,7 @@ libcryptsetup.so.12 libcryptsetup12 #MINVER# crypt_resume_by_volume_key@CRYPTSETUP_2.0 2:2.3 crypt_safe_alloc@CRYPTSETUP_2.0 2:2.3 crypt_safe_free@CRYPTSETUP_2.0 2:2.3 + crypt_safe_memcpy@CRYPTSETUP_2.8 2:2.8 crypt_safe_memzero@CRYPTSETUP_2.0 2:2.3 crypt_safe_realloc@CRYPTSETUP_2.0 2:2.3 crypt_set_compatibility@CRYPTSETUP_2.0 2:2.3 diff --git a/debian/patches/Exclude-reencryption-compat-test-from-Makefile.localtest.patch b/debian/patches/Exclude-reencryption-compat-test-from-Makefile.localtest.patch new file mode 100644 index 0000000..cab0e91 --- /dev/null +++ b/debian/patches/Exclude-reencryption-compat-test-from-Makefile.localtest.patch @@ -0,0 +1,49 @@ +From: Guilhem Moulin +Date: Thu, 11 Dec 2025 23:23:36 +0100 +Subject: Exclude `reencryption-compat-test` from Makefile.localtest + +On Debian unstable (linux 6.17.11+deb14-amd64, udev 259~rc3-1) the test +appears to fail with + + $ sudo make -f Makefile.localtest -j tests CRYPTSETUP_PATH=/sbin TESTSUITE_NOSKIP=y + […] + [reencryption-compat-test] + [1] Reencryption + [2] Reencryption with data shift + [3] Reencryption with keyfile + [4] Encryption of not yet encrypted device + [5] Reencryption using specific keyslot + [6] Reencryption using all active keyslots + [7] Reencryption of block devices with different block size + [512 sector]Cannot use scsi_debug module (in use or compiled-in), test skipped. + make: *** [Makefile.localtest:56: tests] Error 1 + +or (linux 6.17.9+deb14-amd64, udev 259~rc2-1) + + […] + [reencryption-compat-test] + [1] Reencryption + [2] Reencryption with data shift + [3] Reencryption with keyfile + [4] Encryption of not yet encrypted device + [5] Reencryption using specific keyslot + losetup: reenc-data: failed to set up loop device: Device or resource busy + +Bug-Debian: https://bugs.debian.org/1122539 +--- + tests/Makefile.localtest | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/Makefile.localtest b/tests/Makefile.localtest +index 89ce2c3..22d0d7e 100644 +--- a/tests/Makefile.localtest ++++ b/tests/Makefile.localtest +@@ -7,7 +7,7 @@ CPPFLAGS=-I../lib/ -I../lib/luks1 -DHAVE_DECL_DM_TASK_RETRY_REMOVE -DKERNEL_KEYR + -DHAVE_SYS_SYSMACROS_H -DNO_CRYPTSETUP_PATH + CFLAGS=-O2 -g -Wall -D_GNU_SOURCE + LDLIBS=-lcryptsetup -ldevmapper +-TESTS=$(wildcard *-test *-test2) api-test api-test-2 all-symbols-test unit-utils-crypt-test ++TESTS=$(filter-out reencryption-compat-test,$(wildcard *-test *-test2)) api-test api-test-2 all-symbols-test unit-utils-crypt-test + TESTS_UTILS=differ unit-utils-io unit-wipe + + ifneq ($(RUN_SSH_PLUGIN_TEST),) diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..0d4147c --- /dev/null +++ b/debian/patches/series @@ -0,0 +1 @@ +Exclude-reencryption-compat-test-from-Makefile.localtest.patch diff --git a/debian/po/ca.po b/debian/po/ca.po new file mode 100644 index 0000000..161e458 --- /dev/null +++ b/debian/po/ca.po @@ -0,0 +1,54 @@ +# Catalan translation of cryptsetup's debconf messages +# Copyright © 2024 Free Software Foundation, Inc. +# This file is distributed under the same license as the cryptsetup package. +# poc senderi , 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: cryptsetup\n" +"Report-Msgid-Bugs-To: cryptsetup@packages.debian.org\n" +"POT-Creation-Date: 2018-06-18 01:42+0200\n" +"PO-Revision-Date: 2024-12-07 16:56+0100\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Last-Translator: poc senderi \n" +"Language-Team: Catalan \n" +"X-Generator: Poedit 2.4.2\n" + +#. Type: boolean +#. Description +#: ../cryptsetup.templates:1001 +msgid "Continue with cryptsetup removal?" +msgstr "Voleu continuar amb l'eliminació del «cryptsetup»?" + +#. Type: boolean +#. Description +#: ../cryptsetup.templates:1001 +msgid "This system has unlocked dm-crypt devices: ${cryptmap}" +msgstr "Aquest sistema té dispositius «dm-crypt» desbloquejats: ${cryptmap}" + +#. Type: boolean +#. Description +#: ../cryptsetup.templates:1001 +msgid "" +"If these devices are managed with cryptsetup, you might be unable to lock the " +"devices after the package removal, though other tools can be used for " +"managing dm-crypt devices. Any system shutdown or reboot will lock the " +"devices." +msgstr "" +"Si aquests dispositius es gestionen amb el «cryptsetup», és possible que no " +"pugueu bloquejar els dispositius després de l'eliminació del paquet, tot i " +"que es poden utilitzar altres eines per a gestionar dispositius «dm-crypt». " +"Qualsevol apagada o reinici del sistema bloquejarà els dispositius." + +#. Type: boolean +#. Description +#: ../cryptsetup.templates:1001 +msgid "" +"Do not choose this option if you want to lock the dm-crypt devices before " +"package removal." +msgstr "" +"No trieu aquesta opció si voleu bloquejar els dispositius «dm-crypt» abans de " +"l'eliminació del paquet." diff --git a/debian/po/tr.po b/debian/po/tr.po new file mode 100644 index 0000000..0d2fa98 --- /dev/null +++ b/debian/po/tr.po @@ -0,0 +1,57 @@ +# Turkish debconf translation of cryptsetup package. +# Copyright (C) 2025 Debian Turkish L10n Team +# This file is distributed under the same license as the cryptsetup package. +# Translator: +# Nuri KÜÇÜKLER , 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: cryptsetup\n" +"Report-Msgid-Bugs-To: cryptsetup@packages.debian.org\n" +"POT-Creation-Date: 2018-06-18 01:42+0200\n" +"PO-Revision-Date: 2025-07-09 17:25+0300\n" +"Last-Translator: Nuri KÜÇÜKLER \n" +"Language-Team: Debian L10n Turkish \n" +"Language: tr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Poedit 3.2.2\n" + +#. Type: boolean +#. Description +#: ../cryptsetup.templates:1001 +msgid "Continue with cryptsetup removal?" +msgstr "cryptsetup paketinin kaldırılmasına devam edilsin mi?" + +#. Type: boolean +#. Description +#: ../cryptsetup.templates:1001 +msgid "This system has unlocked dm-crypt devices: ${cryptmap}" +msgstr "Bu sistemde kilidi açılmış dm-crypt aygıtları var: ${cryptmap}" + +#. Type: boolean +#. Description +#: ../cryptsetup.templates:1001 +msgid "" +"If these devices are managed with cryptsetup, you might be unable to lock " +"the devices after the package removal, though other tools can be used for " +"managing dm-crypt devices. Any system shutdown or reboot will lock the " +"devices." +msgstr "" +"Bu aygıtlar cryptsetup ile yönetiliyorsa, paket kaldırıldıktan sonra " +"bunları yeniden kilitleyemeyebilirsiniz. Ancak dm-crypt aygıtlarını " +"yönetmek için başka araçlar da mevcuttur. Sistem kapatılır ya da yeniden " +"başlatılırsa, aygıtlar kilitlenecektir." + +#. Type: boolean +#. Description +#: ../cryptsetup.templates:1001 +msgid "" +"Do not choose this option if you want to lock the dm-crypt devices before " +"package removal." +msgstr "" +"Paket kaldırılmadan önce dm-crypt aygıtlarını kilitlemek için bu seçeneği " +"tercih etmeyin." diff --git a/debian/rules b/debian/rules index 7727646..6c32fa1 100755 --- a/debian/rules +++ b/debian/rules @@ -51,7 +51,6 @@ endif # generate gettext po files (for luksformat) $(MAKE) -C debian/scripts/po all luksformat.pot - $(MAKE) -C debian/initramfs/po all cryptroot.pot execute_before_dh_auto_test: # tests/fake_token_path.so is built without global $(CFLAGS) @@ -60,7 +59,6 @@ execute_before_dh_auto_test: execute_after_dh_auto_install: # install gettext po files (for luksformat) $(MAKE) -C debian/scripts/po DESTDIR=$(CURDIR)/debian/cryptsetup-bin install - $(MAKE) -C debian/initramfs/po DESTDIR=$(CURDIR)/debian/cryptsetup-initramfs install execute_after_dh_install: # install apport files when building on Ubuntu @@ -76,7 +74,6 @@ override_dh_installinit: execute_after_dh_auto_clean: $(MAKE) -C debian/scripts/po update clean - $(MAKE) -C debian/initramfs/po update clean if [ -f $(CURDIR)/debian/cryptsetup-initramfs.preinst.in ]; then \ mv -fT $(CURDIR)/debian/cryptsetup-initramfs.preinst.in $(CURDIR)/debian/cryptsetup-initramfs.preinst; \ fi diff --git a/debian/scripts/decrypt_gnupg b/debian/scripts/decrypt_gnupg index 18ab575..5b0a1d9 100644 --- a/debian/scripts/decrypt_gnupg +++ b/debian/scripts/decrypt_gnupg @@ -5,7 +5,7 @@ decrypt_gpg () { if ! /lib/cryptsetup/askpass "Enter passphrase for key $1: " | \ /usr/bin/gpg -q --batch --no-options \ --no-random-seed-file --no-default-keyring \ - --keyring /dev/null --secret-keyring /dev/null \ + --keyring /dev/null \ --trustdb-name /dev/null --passphrase-fd 0 --decrypt -- "$1"; then return 1 fi diff --git a/debian/scripts/suspend/cryptsetup-suspend-wrapper b/debian/scripts/suspend/cryptsetup-suspend-wrapper index 953196c..0c60ebc 100644 --- a/debian/scripts/suspend/cryptsetup-suspend-wrapper +++ b/debian/scripts/suspend/cryptsetup-suspend-wrapper @@ -102,14 +102,18 @@ mount_initramfs() { new="y" fi - # unmkinitramfs(8) extracts microcode into folders "early*" and the actual initramfs into "main" - if [ -f "$INITRAMFS_MNT/sbin/cryptsetup" ]; then - INITRAMFS_DIR="$INITRAMFS_MNT" - elif [ -f "$INITRAMFS_MNT/main/sbin/cryptsetup" ]; then - INITRAMFS_DIR="$INITRAMFS_MNT/main" - else - log_error "Directory $INITRAMFS_MNT has unpected content" >&2 - exit 1 + INITRAMFS_DIR="$INITRAMFS_MNT" + if [ ! -f "$INITRAMFS_DIR/sbin/cryptsetup" ]; then + # unmkinitramfs(8) extracts microcode and actual initramfs into different folders + for p in "$INITRAMFS_MNT"/*/sbin/cryptsetup; do + if [ -f "$p" ] && [ -d "${p%"/sbin/cryptsetup"}/usr" ]; then + INITRAMFS_DIR="${p%"/sbin/cryptsetup"}" + fi + done + if [ ! -f "$INITRAMFS_DIR/sbin/cryptsetup" ]; then + log_error "Directory $INITRAMFS_MNT has unpected content" >&2 + exit 1 + fi fi if [ "$new" = "y" ]; then diff --git a/debian/tests/control b/debian/tests/control index 9fedc5f..0168594 100644 --- a/debian/tests/control +++ b/debian/tests/control @@ -1,12 +1,20 @@ # Run the installed binaries and libraries through the full upstream test suite. Features: test-name=upstream-testsuite -Test-Command: make -C ./tests -f Makefile.localtest -j tests CRYPTSETUP_PATH=/sbin TESTSUITE_NOSKIP=y +Test-Command: + dh_update_autotools_config && + dh_autoreconf && + make -f debian/rules override_dh_auto_configure DEB_BUILD_OPTIONS="nocheck nodoc" DEB_BUILD_PROFILES="noudeb" && + make libcrypto_backend.la && + cd ./tests && + gcc -I ../lib -c ./crypto-check.c && + gcc -o ./crypto-check ./crypto-check.o ../.libs/libcrypto_backend.a -lcrypto && + make -f Makefile.localtest -j tests CRYPTSETUP_PATH=/sbin TESTSUITE_NOSKIP=y + RUN_KEYRING_TRUSTED_TEST=y + RUN_SYSTEMD_PLUGIN_TEST=y Depends: cryptsetup-bin, # to compile tests/*.c - gcc, libcryptsetup-dev, - libdevmapper-dev, -# + @builddeps@, # for hexdump(1) bsdextrautils, # for dmsetup(8) @@ -25,6 +33,12 @@ Depends: cryptsetup-bin, procps, # for uuencode(1) sharutils, +# for swtpm(8) and swtpm_ioctl(8) + swtpm, + swtpm-tools, + tpm2-tools, +# for systemd-cryptenroll(1) + systemd-cryptsetup, # for xxd(1) xxd # @@ -66,6 +80,7 @@ Tests: cryptroot-lvm, cryptroot-legacy # cryptsetup is not listed since we only install it in the VM. Depends: cryptsetup-bin, dosfstools [arm64 armhf], + e2fsprogs, fdisk, genext2fs, initramfs-tools-core, @@ -83,6 +98,7 @@ Architecture: amd64 Tests: cryptroot-md Depends: cryptsetup-bin, dosfstools [arm64 armhf], + e2fsprogs, fdisk, genext2fs, initramfs-tools-core, @@ -100,6 +116,7 @@ Tests: cryptroot-nested Depends: btrfs-progs, cryptsetup-bin, dosfstools [arm64 armhf], + e2fsprogs, fdisk, genext2fs, initramfs-tools-core, @@ -116,6 +133,7 @@ Architecture: amd64 Tests: cryptroot-sysvinit Depends: cryptsetup-bin, dosfstools [arm64 armhf], + e2fsprogs, fdisk, genext2fs, initramfs-tools-core, diff --git a/debian/tests/cryptdisks b/debian/tests/cryptdisks index b8c6bcc..4276536 100755 --- a/debian/tests/cryptdisks +++ b/debian/tests/cryptdisks @@ -197,9 +197,11 @@ EOF # having an existing file system before the offset has no effect (cf. #994056) dmsetup create hidden --table "0 $offset linear $CRYPT_DEV 0" +udevadm settle mke2fs -t ext2 -m0 -Fq /dev/mapper/hidden u="$(blkid -p -s UUID -o value /dev/mapper/hidden)" dd if=/dev/mapper/hidden of="$TMPDIR/hidden.img" bs=512 +udevadm settle dmsetup remove hidden u2="$(blkid -p -s UUID -o value -- "$CRYPT_DEV")" test "$u" = "$u2" @@ -593,8 +595,10 @@ cat >/etc/crypttab <<-EOF EOF dmsetup create hidden --table "0 4096 linear $CRYPT_DEV $offset" +udevadm settle mke2fs -t ext2 -m0 -Fq /dev/mapper/hidden u="$(blkid -p -s UUID -o value /dev/mapper/hidden)" +udevadm settle dmsetup remove hidden u2="$(blkid -p -O$((offset*512)) -s UUID -o value -- "$CRYPT_DEV")" test "$u" = "$u2" diff --git a/debian/tests/cryptroot-lvm.d/mock b/debian/tests/cryptroot-lvm.d/mock index f57e42f..f777763 100755 --- a/debian/tests/cryptroot-lvm.d/mock +++ b/debian/tests/cryptroot-lvm.d/mock @@ -36,8 +36,13 @@ else { expect($SERIAL => qr/(?:^|\s)?PM: suspend exit\r\n/m); unlock_disk("topsecret"); - # consume PS1 to make sure we're at a shell prompt - expect($CONSOLE => qr/\A $PS1 \z/aamsx); + # suspend() leaves clutter in the console due to the retries + # that prevents test from succeeding. + consume($CONSOLE); + + # ensure that shell is available + shell(q{echo ready}, rv => 0); + my $out = shell(q{dmsetup info -c --noheadings -omangled_name,suspended --separator ' '}); die if grep !/[:[:blank:]]Active$/i, split(/\r?\n/, $out); diff --git a/debian/tests/cryptroot-md.d/setup b/debian/tests/cryptroot-md.d/setup index a8f49ed..ad633c8 100644 --- a/debian/tests/cryptroot-md.d/setup +++ b/debian/tests/cryptroot-md.d/setup @@ -55,9 +55,9 @@ for d in vda3 vda4 vdb3 vdb4; do udevadm settle done -mdadm --create /dev/md0 --metadata=default --level=1 --raid-devices=2 /dev/vda2 /dev/vdb2 +mdadm --create /dev/md0 --metadata=default --level=1 --raid-devices=2 --bitmap=internal /dev/vda2 /dev/vdb2 mdadm --create /dev/md1 --metadata=default --level=0 --raid-devices=2 /dev/mapper/vda3_crypt /dev/mapper/vdb3_crypt -mdadm --create /dev/md2 --metadata=default --level=1 --raid-devices=2 /dev/mapper/vda4_crypt /dev/mapper/vdb4_crypt +mdadm --create /dev/md2 --metadata=default --level=1 --raid-devices=2 --bitmap=internal /dev/mapper/vda4_crypt /dev/mapper/vdb4_crypt udevadm settle lvm pvcreate /dev/md2 diff --git a/debian/tests/cryptroot-nested.d/config b/debian/tests/cryptroot-nested.d/config index 995200c..fcfba32 100644 --- a/debian/tests/cryptroot-nested.d/config +++ b/debian/tests/cryptroot-nested.d/config @@ -1,6 +1,13 @@ PKGS_EXTRA+=( btrfs-progs lvm2 mdadm ) PKGS_EXTRA+=( cryptsetup-initramfs ) +# "$DISTRIBUTOR_ID" is defined in ../utils/cryptroot-common +# Workaround for LP1831747 https://bugs.launchpad.net/ubuntu/+source/initramfs-tools/+bug/1831747 +# Add implicit dependency of cryptsetup-initramfs +if [ "$DISTRIBUTOR_ID" = "ubuntu" ]; then + PKGS_EXTRA+=( e2fsprogs ) +fi + # /dev/mapper/testvg-lv1_crypt and /dev/vdc are both 1G and used in RAID1 mode DRIVE_SIZES=( "1G" "264M" "1G" "512M" ) diff --git a/debian/tests/cryptroot-nested.d/setup b/debian/tests/cryptroot-nested.d/setup index b08da17..1b3aa7d 100644 --- a/debian/tests/cryptroot-nested.d/setup +++ b/debian/tests/cryptroot-nested.d/setup @@ -60,7 +60,7 @@ cryptsetup luksOpen --key-file=/keyfile --allow-discards \ -- "/dev/testvg/lv1" "testvg-lv1_crypt" udevadm settle -mdadm --create /dev/md0 --metadata=default --level=1 --raid-devices=2 \ +mdadm --create /dev/md0 --metadata=default --level=1 --raid-devices=2 --bitmap=internal \ /dev/mapper/testvg-lv1_crypt /dev/vdc udevadm settle diff --git a/debian/tests/cryptroot-sysvinit.d/config b/debian/tests/cryptroot-sysvinit.d/config index f6b7392..1d41c24 100644 --- a/debian/tests/cryptroot-sysvinit.d/config +++ b/debian/tests/cryptroot-sysvinit.d/config @@ -1,5 +1,10 @@ PKGS_EXTRA+=( e2fsprogs ) # for fsck.ext4 PKGS_EXTRA+=( cryptsetup-initramfs cryptsetup ) -PKG_INIT="sysvinit-core" - +# "$DISTRIBUTOR_ID" is defined in ../utils/cryptroot-common +case "$DISTRIBUTOR_ID" in + debian) PKG_INIT="sysvinit-core";; + ubuntu) PKG_INIT="systemd-sysv";; + *) echo "ERROR: Unknown distributor ID '$DISTRIBUTOR_ID', can't determine default init package" >&2; + exit 1;; +esac # vim: set filetype=bash : diff --git a/debian/tests/cryptroot-sysvinit.d/mock b/debian/tests/cryptroot-sysvinit.d/mock index b729022..ff9b05b 100755 --- a/debian/tests/cryptroot-sysvinit.d/mock +++ b/debian/tests/cryptroot-sysvinit.d/mock @@ -22,7 +22,8 @@ die unless $out =~ m#\Avda5\s.*\r?\n^`-vda5_crypt\s+crypt\s+/\s*\r?\n\z#m; # make sure only vda5 is processed at initramfs stage # XXX unmkinitramfs doesn't work on /initrd.img with COMPRESS=zstd, cf. #1015954 shell(q{unmkinitramfs /boot/initrd.img-`uname -r` /tmp/initramfs}); -shell(q{grep -E '^vd\S+_crypt\s' /tmp/out}); +shell(q{find /tmp/initramfs -path "*/cryptroot/crypttab" -type f -print0 >/tmp/paths}); +shell(q{xargs -r0a/tmp/paths grep -Eh '^vd\S+_crypt\s' >/tmp/out}); shell(q{grep -E '^vda5_crypt\s' 0); shell(q{grep -Ev '^vda5_crypt\s' 1); diff --git a/debian/tests/utils/cryptroot-common b/debian/tests/utils/cryptroot-common index 6617670..4146a84 100755 --- a/debian/tests/utils/cryptroot-common +++ b/debian/tests/utils/cryptroot-common @@ -81,6 +81,7 @@ load_os_release() { } case "${DISTRIBUTOR_ID:="$(load_os_release && printf "%s" "${ID,,[A-Z]}")"}" in debian) APT_REPO_ORIGIN="Debian"; APT_REPO_URI="http://deb.debian.org/debian";; + ubuntu) APT_REPO_ORIGIN="Ubuntu"; APT_REPO_URI="http://archive.ubuntu.com/ubuntu";; # suitable values for derivative can be added here *) echo "ERROR: Unknown distributor ID '$DISTRIBUTOR_ID', can't extract APT origin" >&2; exit 1;; @@ -164,6 +165,12 @@ case "$BOOT" in efi) PKG_BOOTLOADER="grub-efi";; *) echo "ERROR unknown boot method '$BOOT'" >&2; exit 1;; esac + +if [ "$DISTRIBUTOR_ID" = "ubuntu" ]; then + echo "Overriding kernel arch to generic" + KERNEL_ARCH="generic" +fi + PKG_KERNEL="linux-image-$KERNEL_ARCH" PKG_INIT="systemd-sysv" # default pid1 MERGED_USR="" # use default layout for the target version @@ -302,6 +309,12 @@ setup_apt() { esac >"$TEMPDIR/apt/sources.list" fi + # ubuntu CI populates sources.list.d with PPA source, append them to the list + if [ "$DISTRIBUTOR_ID" = "ubuntu" ] && [ -d /etc/apt/sources.list.d ]; then + echo "Append contents of /etc/apt/sources.list.d to $TEMPDIR/apt/sources.list" + find /etc/apt/sources.list.d -type f -name "*.list" -execdir cat {} + >> "$TEMPDIR/apt/sources.list" + fi + local apt_repo for apt_repo in "${EXTRA_REPOS[@]}"; do printf "%s\\n" "$apt_repo" >>"$TEMPDIR/apt/sources.list" diff --git a/debian/tests/utils/init b/debian/tests/utils/init index 331cd6f..94f3b41 100755 --- a/debian/tests/utils/init +++ b/debian/tests/utils/init @@ -245,6 +245,13 @@ if [ -f /init.postinst ]; then rm -f "$ROOT/init.postinst" fi +# These break 'expect' so remove it from the image under test +if [ -f "$ROOT/usr/lib/tmpfiles.d/20-systemd-osc-context.conf" ]; then + rm -f "$ROOT/etc/profile.d/80-systemd-osc-context.sh" + mkdir -p "$ROOT/etc/tmpfiles.d" + ln -s /dev/null "$ROOT/etc/tmpfiles.d/20-systemd-osc-context.conf" +fi + # allow service startup again mv "$ROOT/sbin/start-stop-daemon.REAL" "$ROOT/sbin/start-stop-daemon" rm "$ROOT/usr/sbin/policy-rc.d" diff --git a/debian/tests/utils/initramfs-hook.common b/debian/tests/utils/initramfs-hook.common index 863d08d..4332a13 100644 --- a/debian/tests/utils/initramfs-hook.common +++ b/debian/tests/utils/initramfs-hook.common @@ -41,22 +41,36 @@ cat >"$TMPDIR/initramfs-tools/initramfs.conf" <<-EOF EOF INITRD_IMG="$TMPDIR/initrd.img" -INITRD_DIR="$TMPDIR/initrd" +UNMKINITRAMFS_DESTDIR="$TMPDIR/initrd" +unset INITRD_DIR cleanup_initrd_dir() { local d - for d in dev proc sys; do - mountpoint -q "$INITRD_DIR/$d" && umount "$INITRD_DIR/$d" || true - done - rm -rf --one-file-system -- "$INITRD_DIR" + if [ -n "${INITRD_DIR+x}" ] && [ -d "$INITRD_DIR" ]; then + for d in dev proc sys; do + mountpoint -q "$INITRD_DIR/$d" && umount "$INITRD_DIR/$d" || true + done + rm -rf --one-file-system -- "$INITRD_DIR" + fi + rm -rf --one-file-system -- "$UNMKINITRAMFS_DESTDIR" + unset INITRD_DIR } trap cleanup_initrd_dir EXIT INT TERM mkinitramfs() { - local d + local d p command mkinitramfs -d "$TMPDIR/initramfs-tools" -o "$INITRD_IMG" # `mkinitramfs -k` would be better but we can't set $DESTDIR in advance cleanup_initrd_dir - command unmkinitramfs "$INITRD_IMG" "$INITRD_DIR" + command unmkinitramfs "$INITRD_IMG" "$UNMKINITRAMFS_DESTDIR" + if [ -f "$UNMKINITRAMFS_DESTDIR/sbin/cryptsetup" ]; then + INITRD_DIR="$UNMKINITRAMFS_DESTDIR" + else + for p in "$UNMKINITRAMFS_DESTDIR"/*/sbin/cryptsetup; do + if [ -f "$p" ] && [ -d "${p%"/sbin/cryptsetup"}/usr" ]; then + INITRD_DIR="${p%"/sbin/cryptsetup"}" + fi + done + fi for d in dev proc sys; do mkdir -p "$INITRD_DIR/$d" mount --bind "/$d" "$INITRD_DIR/$d" diff --git a/debian/tests/utils/mock.pm b/debian/tests/utils/mock.pm index 4db861d..36f44d2 100644 --- a/debian/tests/utils/mock.pm +++ b/debian/tests/utils/mock.pm @@ -98,6 +98,26 @@ sub expect(;$$) { #print STDERR "INFO done reading\n"; } +sub consume($) { + my $chan = shift; + my $buffer = defined $chan ? \$BUFFER{$chan} : undef; + if (! defined $buffer) { + return; + } + + while(unpack("b*", $RBITS) != 0) { + my $rout = $RBITS; + if (select($rout, undef, undef, 1) == -1) { + return; + } + read_data($rout); + if (length($$buffer) == 0) { + return; + } + $$buffer = ""; + } +} + sub write_data($$%) { my $chan = shift; my $data = shift; @@ -168,11 +188,13 @@ BEGIN { hibernate poweroff expect + consume /; } *expect = \&CryptrootTest::Utils::expect; *write_data = \&CryptrootTest::Utils::write_data; +*consume = \&CryptrootTest::Utils::consume; sub unlock_disk($) { my $passphrase = shift; @@ -231,7 +253,9 @@ sub shell($%) { sub suspend() { @QMP::EVENTS = (); # flush the event queue - write_data($CONSOLE => q{systemctl suspend}); + # there is a race condition that causes suspend to fail. + # retry until success. Note, this may leave clutter in the console + write_data($CONSOLE => q{until systemctl suspend; do sleep 1; done}); # while the command is asynchronous the system might suspend before # we have a chance to read the next $PS1 diff --git a/debian/watch b/debian/watch index dabcd8b..13d9675 100644 --- a/debian/watch +++ b/debian/watch @@ -1,6 +1,7 @@ -version=4 -options="mode=git,pgpmode=gittag, \ - uversionmangle=s/-(alpha|beta|rc)(\d*)$/~$1$2/, \ - compression=gzip" \ - https://gitlab.com/cryptsetup/cryptsetup.git \ - refs/tags/v?@ANY_VERSION@ +Version: 5 +Mode: git +Pgp-Mode: gittag +Uversion-Mangle: s/-(alpha|beta|rc)(\d*)$/~$1$2/ +Compression: gzip +Source: https://gitlab.com/cryptsetup/cryptsetup.git +Matching-Pattern: refs/tags/@ANY_VERSION@ diff --git a/docs/examples/crypt_log_usage.c b/docs/examples/crypt_log_usage.c index 05e3c97..de8d45c 100644 --- a/docs/examples/crypt_log_usage.c +++ b/docs/examples/crypt_log_usage.c @@ -2,7 +2,7 @@ /* * libcryptsetup API log example * - * Copyright (C) 2011-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2011-2025 Red Hat, Inc. All rights reserved. */ #include diff --git a/docs/examples/crypt_luks_usage.c b/docs/examples/crypt_luks_usage.c index 002b744..784d368 100644 --- a/docs/examples/crypt_luks_usage.c +++ b/docs/examples/crypt_luks_usage.c @@ -2,7 +2,7 @@ /* * libcryptsetup API - using LUKS device example * - * Copyright (C) 2011-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2011-2025 Red Hat, Inc. All rights reserved. */ #include diff --git a/docs/licenses/COPYING.Apache-2.0 b/docs/licenses/COPYING.Apache-2.0 new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/docs/licenses/COPYING.Apache-2.0 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docs/licenses/COPYING.CC-BY-SA-4.0 b/docs/licenses/COPYING.CC-BY-SA-4.0 new file mode 100644 index 0000000..2d58298 --- /dev/null +++ b/docs/licenses/COPYING.CC-BY-SA-4.0 @@ -0,0 +1,428 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/docs/licenses/COPYING.GPL-2.0-or-later-WITH-cryptsetup-OpenSSL-exception b/docs/licenses/COPYING.GPL-2.0-or-later-WITH-cryptsetup-OpenSSL-exception new file mode 100644 index 0000000..86289a1 --- /dev/null +++ b/docs/licenses/COPYING.GPL-2.0-or-later-WITH-cryptsetup-OpenSSL-exception @@ -0,0 +1,354 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + +----- +In addition, as a special exception, the copyright holders give +permission to link the code of portions of this program with the +OpenSSL library under certain conditions as described in each +individual source file, and distribute linked combinations +including the two. + +You must obey the GNU General Public License in all respects +for all of the code used other than OpenSSL. If you modify +file(s) with this exception, you may extend this exception to your +version of the file(s), but you are not obligated to do so. If you +do not wish to do so, delete this exception statement from your +version. If you delete this exception statement from all source +files in the program, then also delete it here. diff --git a/COPYING.LGPL b/docs/licenses/COPYING.LGPL-2.1-or-later-WITH-cryptsetup-OpenSSL-exception similarity index 100% rename from COPYING.LGPL rename to docs/licenses/COPYING.LGPL-2.1-or-later-WITH-cryptsetup-OpenSSL-exception diff --git a/docs/on-disk-format-luks2.pdf b/docs/on-disk-format-luks2.pdf index eb7eaa6..6a9df01 100644 Binary files a/docs/on-disk-format-luks2.pdf and b/docs/on-disk-format-luks2.pdf differ diff --git a/docs/v2.8.0-ReleaseNotes b/docs/v2.8.0-ReleaseNotes new file mode 100644 index 0000000..f08909e --- /dev/null +++ b/docs/v2.8.0-ReleaseNotes @@ -0,0 +1,328 @@ +Cryptsetup 2.8.0 Release Notes +============================== +Stable release with new features and bug fixes + +All users of cryptsetup 2.7 must upgrade to this version. + +Changes since version 2.7.5 +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Introduce support for inline mode (use HW sectors with additional hardware metadata space). + + Some enterprise NVMe drives allow formatting sector size with additional metadata space, + for example, sector size 4096 bytes + 64 bytes for metadata. + We hope common firmware will soon support such features in more recent models. + + If this metadata space is available (not internally used by a data integrity profile), + it removes the need to use the dm-integrity layer for sector metadata allocation. + This means that the performance bottleneck caused by the dm-integrity journal is eliminated. + + Note: such drive must be reformatted with an external nvme tool. + You can check for support (reported as LBA format) by running the command + "nvme id-ns -H " and then you can reformat to the selected profile + (with complete data loss) with "nvme format -l . + This way, you can also reformat NVMe drive to 4096-byte sectors,which is strongly recommended + for encryption performance. + + The required device mapper for inline mode was introduced in Linux kernel version 6.11. + + The inline mode can be used with the new --integrity-inline option. + + For integritysetup, the kernel dm-integrity layer is still used, but it directly maps metadata + to the hardware (eliminating the journal). + For cryptsetup, the dm-integrity layer is eliminated, and only the dm-crypt kernel driver is used. + The libcryptsetup exports a new crypt_format_inline API call. + + Examples (underlying device must provide inline HW metadata space): + + Use integritysetup format with inline mode with default CRC32 checksums: + + # integritysetup format --sector-size 4096 --integrity-inline [--no-wipe] + # integritysetup open test + # integritysetup status test + /dev/mapper/test is active. + type: INTEGRITY + tag size: 4 [bytes] + integrity: crc32c + device: + sector size: 4096 [bytes] + ... + inline mode + journal: not active + + Use LUKS2 with authenticated encryption (here with AEGIS AEAD cipher): + + # cryptsetup luksFormat --integrity-inline --integrity aead --sector-size 4096 \ + -c aegis128-random --key-size 128 [--integrity-no-wipe] + # cryptsetup open test + # cryptsetup luksDump + ... + Requirements: inline-hw-tags + + After format, the inline mode is used automatically, and no special options are needed. + Please check the manual pages for more details about used options. + + Note that the LUKS2 authenticated encryption is still an experimental feature. + The inline mode only improves performance by removing the dm-integrity layer. + +* Finalize use of keyslot context API. + + Keyslot context is a generic abstraction over keyslot manipulation. + It extends many exiting commands by additional functions like tokens in activation, resume, + reencryption and similar commands without introducing new specific API functions. + +* Make all keyslot context types fully self-contained. + + In the previous version, the caller is responsible for releasing of some allocated memory. + In this version, all memory is allocated internally. The existing keyslot context API function + provides backward compatibility through versioned symbols. + +* Add --key-description and --new-key-description cryptsetup options. + + These can be used for the specification of the keyring with passphrase retrieval in the open, + resize, luksResume, luksFormat, luksAddKey and luksDump. + +* Support more precise keyslot selection in reencryption initialization. + + Reencryption must update stored keys in keyslots, so it needs to unlock all keyslots first. + + When no specific keyslot is selected by the --key-slot option, all active keyslots are updated. + + Users may narrow down the selection of keyslots by specifying either --token-id, --token-type + or --token-only option. Only keyslots associated with the specific token (--token-id) or + a specific type (--token-type) or any token (--token-only) will be updated. + All other keyslots will be erased after reencryption is finished. + + During reencryption, there are two volume keys (old and new). + For very specific use cases, reencryption can also be initialized by providing + volume keys directly by --volume-key-file, --new-volume-key-file, --volume-key-keyring + or --new-volume-key-keyring options. These options allow reencryption of the device with + no active keyslots (these can be added later). + If the --force-no-keyslots option is specified, all active keyslots will be erased after + the reencryption operation is finished. + +* Allow reencryption to resume using token and volume keys. + + The reencryption can be resumed using tokens (similar to initialization described above). + For very specific use cases, reencryption can be resumed by providing volume keys. + +* Cryptsetup repair command now tries to check LUKS keyslot areas for corruption. + + A keyslot binary area contains an encrypted volume key diffused to a larger area by + the anti-forensic splitter. If this area is corrupted, the keyslot can no longer be unlocked, + even with the correct password. + + Active keyslot area should look like random data, so some specific corruption can be detected + by randomness analysis. + + Cryptsetup repair command now tries to analyze the area expecting a uniform distribution + of bytes in 4096-byte blocks. If a problem is detected, it tries to localize corruption + in a smaller block (using the expected bit count). + Both tests are based on the Chi-squared statistical test. + + This analysis can replace the external keyslot check program and usually is more sensitive. + However, it cannot detect all corruptions and can produce false positives. + + Please use it as a hint when your password is no longer accepted, and you suspect + header corruption. This is the example output of the analysis: + + # cryptsetup repair + Keyslot 2 binary data could be corrupted. + Suspected offset: 0x88000 + You can use hexdump -v -C -n 128 -s to inspect the data. + + The test does not modify the header. A keyslot corruption cannot be repaired. + You have to use a backup header. + +* Opal2 SED: PSID keyfile is now expected to be 32 alphanumeric characters. + + If the keyfile size is not explicitly set, it uses only first 32 bytes. + All Opal2 manufacturers seem to use PSID of this length. + +* Opal2: Avoid the Erase method and use Secure Erase for locking range. + + The Erase method is defined for Single-user mode (SUM) and works on SUM-enabled locking ranges. + As we do not use SUM yet, this always fails and falls back to Secure erase anyway. + +* Opal2: Fix some error description (in debug only). + + Some Opal error messages were incorrect. + Cryptsetup now use all codes according to TCG specifications. + +* Opal2: Do not allow deferred deactivation. + + The self-encrypting drive must be locked immediately; deferred deactivation is not supported. + +* Allow --reduce-device-size and --device-size combination for reencryption (encrypt) action. + + For some very specific cases, this can be used to encrypt only part of the device together + with allocation a new space for the LUKS header. + +* Fix the userspace storage backend to support kernel "capi:" cipher specification format. + + This avoids unnecessary fallback to the device-mapper instead of the userspace crypto library + in luksFormat. The "capi:" is Linux kernel cryptographic format. + For example, capi:xts(aes)-plain64 is equivalent of aes-xts-plain64. + +* Disallow conversion from LUKS2 to LUKS1 if kernel "capi:" cipher specification is used. + + LUKS1 never officially supported this cipher specification format. + Such devices cannot be converted to LUKS1 (while existing devices can still be activated). + +* Explicitly disallow kernel "capi:" cipher specification format for LUKS2 keyslot encryption. + + This specification is intended to be used for data encryption, not for keyslots. + +* Do not allow conversion of LUKS2 to LUKS1 if an unbound keyslot is present. + + LUKS1 does not support unbound keyslots. Such devices cannot be converted. + +* cryptsetup: Adjust the XTS key size for kernel "capi:" cipher specification. + + Double key size as there are two keys the same way as for dm-crypt format. + +* Remove keyslot warning about possible failure due to low memory. + + This check was intended to warn users about possible out-of-memory situations + but produced many false positives. + +* Do not limit Argon2 KDF memory cost on systems with more than 4GB of available memory. + + The memory cost is intended to be limited only in low-memory situations (like virtual machines + without swap), not on systems with plenty of RAM. + +* Properly report out of memory error for cryptographic backends implementing Argon2. + +* Avoid KDF2 memory cost overflow on 32-bit platforms. + +* Do not use page size as a fallback for device block size. + + This check produced wrong values if used on platforms with larger page sizes (64kB) + and specific underlying storage (like ZFS). + +* veritysetup: Check hash device size in advance. + + If hashes are stored in a file image, allocate the size in advance. + For a block device, check if hashes (Merkle tree) fits the device. + +* Print a better error message for unsupported LUKS2 AEAD device resize. + +* Optimize LUKS2 metadata writes. + + LUKS2 supports several JSON area length configurations. Do not write full metadata + (including padding), as it may generate noticeable overhead with LUKS2. + +* veritysetup: support --error-as-corruption option. + + The panic/restart_on_error options were introduced in Linux kernel 6.12 and process errors + (like media read error) the same way as data corruption. + Use this flag in combination with --panic-on-corruption or --restart-on-corruption. + +* Report all sizes in status and dump command output in the correct units. + + Since the support of --sector-size option, the meaning of "sectors" became ambiguous as it + usually means 512-byte sectors (device-mapper unit). Confusion occurs when the sector size + is 4096 bytes while units used for display are 512-byte sectors. + + All status commands in tools now display units explicitly to avoid confusion. + + For example: + # cryptsetup status test + ... + sector size: 4096 [bytes] + offset: 32768 [512-byte units] (134217728 [bytes]) + size: 7501443760 [512-byte units] (30725913640960 [bytes]) + + If you parse the output of status commands, please check your scripts to ensure they work + with the new output properly. + +* Add --integrity-key-size option to cryptsetup. + + This option can be used to set up non-standard integrity key size (e.g. for HMAC). + It adds a new (optional) JSON "key_size" attribute in the segment.integrity JSON object + (see updated LUKS2 specification). If not set, the code uses selected hash length size. + +* Support trusted & encrypted keyrings for plain devices. + +* Support plain format resize with a keyring key. + + If a plain dm-crypt device references the keyring, cryptsetup now allows resizing. + The user must ensure that the key in the keyring is unchanged since activation. + Otherwise, reloading the key can cause data corruption after an unexpected key change. + +* TCRYPT: Clear mapping of system-encrypted partitions. + + TrueCrypt/VeraCrypt supports full system encryption (only a partition table is not encrypted) + or system partition encryption (only a system partition is encrypted). + The metadata header then contains the offset and size of the encrypted area. + Cryptsetup needs to know the specific partition offset to calculate encryption parameters. + + To properly map a partition, the user must specify a real partition device so cryptsetup + can calculate this offset. As the partition can be an image in a file, cryptsetup now tries + to determine proper parameters and use device size stored in VeraCrypt metadata. + + Please see the manual page description (TCRYPT section) for a detailed description. + +* TCRYPT: Print all information from the decrypted metadata header in the tcryptDump command. + + Print also volume sizes (if present) and flags. + +* Always lock the volume key structure in memory. + + Some memory for safe allocation was not allocated from locked (unswappable) memory. + Older cryptsetup locked all memory. Selective locking was introduced in version 2.6.0. + +* Do not run direct-io read check on block devices. + + Block devices always support direct-io. + This check produced unnecessary error with locked Opal2 devices. + +* Fix a possible segfault in deferred deactivation. + + Thanks Clément Guérin for the report. + +* Exclude cipher allocation time from the cryptsetup benchmark. + +* Add Mbed-TLS optional crypto backend. + + Mbed-TLS is a tiny TLS implementation designed for embedded environments. + The backend can be enabled with the --with-crypto_backend=mbedtls configure option. + +* Fix the wrong preprocessor use of #ifdef for config.h processed by Meson. + + Cryptsetup supports Autoconf and, optionally, Meson configuration. + Part of the code wrongly used #ifdef instead of #if conditional sections. + This caused problems with Meson-generated config.h. + +* Reorganize license files. + + The license text files are now in docs/licenses. + The COPYING file in the root directory is the default license. + +Libcryptsetup API extensions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The libcryptsetup API is backward compatible with all existing symbols. + +Due to the self-contained memory allocation, these symbols have the new version + crypt_keyslot_context_init_by_passphrase; + crypt_keyslot_context_init_by_keyfile; + crypt_keyslot_context_init_by_token; + crypt_keyslot_context_init_by_volume_key; + crypt_keyslot_context_init_by_signed_key; + crypt_keyslot_context_init_by_keyring; + crypt_keyslot_context_init_by_vk_in_keyring; + +New symbols: + crypt_format_inline + crypt_get_old_volume_key_size + crypt_reencrypt_init_by_keyslot_context + crypt_safe_memcpy + +New defines: + CRYPT_ACTIVATE_HIGH_PRIORITY + CRYPT_ACTIVATE_ERROR_AS_CORRUPTION + CRYPT_ACTIVATE_INLINE_MODE + CRYPT_REENCRYPT_CREATE_NEW_DIGEST + +New requirement flag: + CRYPT_REQUIREMENT_INLINE_HW_TAGS diff --git a/docs/v2.8.1-ReleaseNotes b/docs/v2.8.1-ReleaseNotes new file mode 100644 index 0000000..92f2e96 --- /dev/null +++ b/docs/v2.8.1-ReleaseNotes @@ -0,0 +1,40 @@ +Cryptsetup 2.8.1 Release Notes +============================== +Stable bug-fix release with minor extensions. + +All users of cryptsetup 2.8.0 must upgrade to this version. + +Changes since version 2.8.0 +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Fix status and deactivation of TCRYPT (VeraCrypt compatible) devices that use chained ciphers. + +* Fix unlocking BITLK (BitLocker compatible) devices with multibyte UTF8 characters in the passphrase. + +* Do not allow activation of the LUKS2 device if the used keyslot is not encrypted (it uses a null cipher). + + Such a configuration cannot be created by cryptsetup, but can be crafted outside of it. + Null cipher is sometimes used to create an empty container for later reencryption. + Only an empty passphrase can activate such a container (the same as in LUKS1). + +* Do not silently decrease PBKDF parallel cost (threads) if set by an option. + The maximum parallel cost is limited to 4 threads. + +* Fixes to configuration and installation scripts. + + Meson and autoconf tools now properly support --prefix option for temporary directory installation. + Multiple fixes and cleanups to config.h for compatibility between Meson and autoconf. + Fix the luks2-external-tokens-path Meson option to work the same as in autoconf. + Fix Meson install for tool binaries, install fvault2Open man page and include test/fuzz/meson.build in release. + +* Major update to manual pages. + + Try to explain the PBKDF hardcoded limits. + Add a better explanation for automatic integrity tag recalculation. + Mention crypt/verity/integritytab. + Remove or reformulate some misleading warnings present only with old and no longer supported kernels. + Clarify that some commands do not wipe data and unify OPAL reset wording. + Clarify the --label option. + There are also many other grammar and stylistic fixes to unify the man-page style. + +* Fixes for false-positive and annoying (optional) warnings added in recent compilers. diff --git a/docs/v2.8.2-ReleaseNotes b/docs/v2.8.2-ReleaseNotes new file mode 100644 index 0000000..bc78cd8 --- /dev/null +++ b/docs/v2.8.2-ReleaseNotes @@ -0,0 +1,58 @@ +Cryptsetup 2.8.2 Release Notes +============================== +Stable bug-fix release with minor extensions. + +All users of cryptsetup 2.8.x must upgrade to this version. + +Changes since version 2.8.1 +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Fix cryptsetup LUKS2 status for HW inline integrity device. + Cryptsetup status did not print the inline flag if the underlying device with + HW integrity tags was used. + +* Fix LUKS2 format with detached header and data device with HW integrity tags. + +* Fix PBKDF serialization flag during device activation. + The --serialize-memory-hard-pbkdf and CRYPT_ACTIVATE_SERIALIZE_MEMORY_HARD_PBKDF API flag + is now properly supported again. This option is an optional workaround for situations where + multiple devices are activated in parallel (e.g., systemd crypttab activation). + +* BITLK: Add support for opening devices with Clear Key in BitLocker compatible mode. + BitLocker devices that are not yet encrypted can contain a Clear Key that is not protected + by a password. Cryptsetup can now map such devices and allow the user to access data on them. + Note that while such a device is detected as BitLocker, it must be treated as an unencrypted + device. Cryptsetup still does not allow mapping of partially encrypted BitLocker devices + (those in the middle of the encryption process). + +* BITLK: Harden metadata check by properly validating BitLocker metadata. + BitLocker metadata store checksums and authentication tags to detect random or malicious + manipulation. BITLK code now properly validates these and uses a backup metadata block + if validation fails. Previously, only the first metadata block was used. + +* Fix documentation to explicitly mention units for various API functions and in help messages. + Note that due to compatibility reasons, cryptsetup arguments use key sizes in bits while + integritysetup uses bytes. + +* Fix handling of too-long labels and subsystem fields. + LUKS2 labels are stored in the binary header area, which has a limited size. + Cryptsetup no longer silently truncates too-long labels; it prints an error instead. + +* Optimize reencryption to not repeatedly test access to the device. + +* Allow to use PHMAC (protected HMAC) with integritysetup and cryptsetup. + PHMAC is used by S390 mainframes. Support was added in Linux kernel 6.17. Configuration requires + steps using s390-tools; once that's done, it can be handled as a common LUKS2 or integrity device. + +* Opal2 SED: Fix misleading error messages during the self-encrypting drives format. + Cryptsetup misinterpreted some error codes when the kernel interface was not available + or the system call failed. + +* Opal2 SED: Ensure the system tries to rescan the device after the PSID reset. + Udev should now receive change events, allowing rescan of partition table after PSID reset. + +* Fix typos in volume-key-file help and integritysetup man page. + +* Fix detection of supported compiler attributes on PPC64 architecture. + +* Fix const compilation warnings with new gcc and glibc headers. diff --git a/lib/bitlk/bitlk.c b/lib/bitlk/bitlk.c index 686b42c..5f1762a 100644 --- a/lib/bitlk/bitlk.c +++ b/lib/bitlk/bitlk.c @@ -2,9 +2,9 @@ /* * BITLK (BitLocker-compatible) volume handling * - * Copyright (C) 2019-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2019-2024 Milan Broz - * Copyright (C) 2019-2024 Vojtech Trefny + * Copyright (C) 2019-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2019-2025 Milan Broz + * Copyright (C) 2019-2025 Vojtech Trefny */ #include @@ -111,6 +111,7 @@ struct bitlk_superblock { struct bitlk_fve_metadata { /* FVE metadata block header */ uint8_t signature[8]; + /* size of this block (in 16-byte units) */ uint16_t fve_size; uint16_t fve_version; uint16_t curr_state; @@ -132,6 +133,32 @@ struct bitlk_fve_metadata { uint64_t creation_time; } __attribute__ ((packed)); +struct bitlk_validation_hash { + uint16_t size; + uint16_t role; + uint16_t type; + uint16_t flags; + /* likely a hash type code, anything other than 0x2005 isn't supported */ + uint16_t hash_type; + uint16_t unknown1; + /* SHA-256 */ + uint8_t hash[32]; +} __attribute__ ((packed)); + +struct bitlk_fve_metadata_validation { + /* FVE metadata validation block header */ + uint16_t validation_size; + uint16_t validation_version; + uint32_t fve_crc32; + /* this is a single nested structure's header defined here for simplicity */ + uint16_t nested_struct_size; + uint16_t nested_struct_role; + uint16_t nested_struct_type; + uint16_t nested_struct_flags; + /* datum containing a similar nested structure (encrypted using VMK) with hash (SHA256) */ + uint8_t nested_struct_data[BITLK_VALIDATION_VMK_DATA_SIZE]; +} __attribute__ ((packed)); + struct bitlk_entry_header_block { uint64_t offset; uint64_t size; @@ -237,10 +264,11 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in bool supported = false; int r = 0; - /* only passphrase or recovery passphrase vmks are supported (can be used to activate) */ + /* only passphrase, recovery passphrase, startup key and clearkey vmks are supported (can be used to activate) */ supported = (*vmk)->protection == BITLK_PROTECTION_PASSPHRASE || (*vmk)->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE || - (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY; + (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY || + (*vmk)->protection == BITLK_PROTECTION_CLEAR_KEY; while ((end - start) >= (ssize_t)(sizeof(key_entry_size) + sizeof(key_entry_type) + sizeof(key_entry_value))) { /* size of this entry */ @@ -297,17 +325,13 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in crypt_volume_key_add_next(&((*vmk)->vk), vk); /* clear key for a partially decrypted volume */ } else if (key_entry_value == BITLK_ENTRY_VALUE_KEY) { - /* We currently don't want to support opening a partially decrypted - * device so we don't need to store this key. - * - * key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4); - * key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4; - * vk = crypt_alloc_volume_key(key_size, key); - * if (vk == NULL) - * return -ENOMEM; - * crypt_volume_key_add_next(&((*vmk)->vk), vk); - */ - log_dbg(cd, "Skipping clear key metadata entry."); + /* For clearkey protection, we need to store this key */ + key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4); + key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4; + vk = crypt_alloc_volume_key(key_size, key); + if (vk == NULL) + return -ENOMEM; + crypt_volume_key_add_next(&((*vmk)->vk), vk); /* unknown timestamps in recovery protected VMK */ } else if (key_entry_value == BITLK_ENTRY_VALUE_RECOVERY_TIME) { ; @@ -361,6 +385,54 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in return 0; } +static bool check_fve_metadata(struct bitlk_fve_metadata *fve) +{ + if (memcmp(fve->signature, BITLK_SIGNATURE, sizeof(fve->signature)) || le16_to_cpu(fve->fve_version) != 2 || + (fve->fve_size << 4) > BITLK_FVE_METADATA_SIZE) + return false; + + return true; +} + +static bool check_fve_metadata_validation(struct bitlk_fve_metadata_validation *validation) +{ + /* only check if there is room for CRC-32, the actual size must be larger */ + if (le16_to_cpu(validation->validation_size) < 8 || le16_to_cpu(validation->validation_version > 2)) + return false; + + return true; +} + +static bool parse_fve_metadata_validation(struct bitlk_metadata *params, struct bitlk_fve_metadata_validation *validation) +{ + /* extra checks for a nested structure (MAC) and BITLK FVE metadata */ + + if (le16_to_cpu(validation->validation_size) < sizeof(struct bitlk_fve_metadata_validation)) + return false; + + if (le16_to_cpu(validation->nested_struct_size != BITLK_VALIDATION_VMK_HEADER_SIZE + BITLK_VALIDATION_VMK_DATA_SIZE) || + le16_to_cpu(validation->nested_struct_role) != 0 || + le16_to_cpu(validation->nested_struct_type) != 5) + return false; + + /* nonce */ + memcpy(params->validation->nonce, + validation->nested_struct_data, + BITLK_NONCE_SIZE); + + /* MAC tag */ + memcpy(params->validation->mac_tag, + validation->nested_struct_data + BITLK_NONCE_SIZE, + BITLK_VMK_MAC_TAG_SIZE); + + /* AES-CCM encrypted datum with SHA256 hash */ + memcpy(params->validation->enc_datum, + validation->nested_struct_data + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE, + BITLK_VALIDATION_VMK_DATA_SIZE - BITLK_NONCE_SIZE - BITLK_VMK_MAC_TAG_SIZE); + + return true; +} + void BITLK_bitlk_fvek_free(struct bitlk_fvek *fvek) { if (!fvek) @@ -375,10 +447,8 @@ void BITLK_bitlk_vmk_free(struct bitlk_vmk *vmk) struct bitlk_vmk *vmk_next = NULL; while (vmk) { - if (vmk->guid) - free(vmk->guid); - if (vmk->name) - free(vmk->name); + free(vmk->guid); + free(vmk->name); crypt_free_volume_key(vmk->vk); vmk_next = vmk->next; free(vmk); @@ -392,8 +462,8 @@ void BITLK_bitlk_metadata_free(struct bitlk_metadata *metadata) return; free(metadata->guid); - if (metadata->description) - free(metadata->description); + free(metadata->description); + free(metadata->validation); BITLK_bitlk_vmk_free(metadata->vmks); BITLK_bitlk_fvek_free(metadata->fvek); } @@ -405,20 +475,25 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) struct bitlk_signature sig = {}; struct bitlk_superblock sb = {}; struct bitlk_fve_metadata fve = {}; + struct bitlk_fve_metadata_validation validation = {}; struct bitlk_entry_vmk entry_vmk = {}; uint8_t *fve_entries = NULL; + uint8_t *fve_validated_block = NULL; size_t fve_entries_size = 0; uint32_t fve_metadata_size = 0; + uint32_t fve_size_real = 0; int fve_offset = 0; char guid_buf[UUID_STR_LEN] = {0}; uint16_t entry_size = 0; uint16_t entry_type = 0; int i = 0; int r = 0; + int valid_fve_metadata_idx = -1; int start = 0; size_t key_size = 0; const char *key = NULL; char *description = NULL; + struct crypt_hash *hash; struct bitlk_vmk *vmk = NULL; struct bitlk_vmk *vmk_p = params->vmks; @@ -493,15 +568,80 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) for (i = 0; i < 3; i++) params->metadata_offset[i] = le64_to_cpu(sb.fve_offset[i]); - log_dbg(cd, "Reading BITLK FVE metadata of size %zu on device %s, offset %" PRIu64 ".", - sizeof(fve), device_path(device), params->metadata_offset[0]); + fve_validated_block = malloc(BITLK_FVE_METADATA_SIZE); + if (fve_validated_block == NULL) { + r = -ENOMEM; + goto out; + } - /* read FVE metadata from the first metadata area */ - if (read_lseek_blockwise(devfd, device_block_size(cd, device), - device_alignment(device), &fve, sizeof(fve), params->metadata_offset[0]) != sizeof(fve) || - memcmp(fve.signature, BITLK_SIGNATURE, sizeof(fve.signature)) || - le16_to_cpu(fve.fve_version) != 2) { - log_err(cd, _("Failed to read BITLK FVE metadata from %s."), device_path(device)); + for (i = 0; i < 3; i++) { + /* iterate over FVE metadata copies and pick the valid one */ + log_dbg(cd, "Reading BITLK FVE metadata copy #%d of size %zu on device %s, offset %" PRIu64 ".", + i, sizeof(fve), device_path(device), params->metadata_offset[i]); + + if (read_lseek_blockwise(devfd, device_block_size(cd, device), + device_alignment(device), &fve, sizeof(fve), params->metadata_offset[i]) != sizeof(fve) || + !check_fve_metadata(&fve) || + (fve_size_real = le16_to_cpu(fve.fve_size) << 4, read_lseek_blockwise(devfd, device_block_size(cd, device), + device_alignment(device), &validation, sizeof(validation), params->metadata_offset[i] + fve_size_real) != sizeof(validation)) || + !check_fve_metadata_validation(&validation) || + /* double-fetch is here, but we aren't validating MAC */ + read_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device), fve_validated_block, fve_size_real, + params->metadata_offset[i]) != fve_size_real || + (crypt_crc32(~0, fve_validated_block, fve_size_real) ^ ~0) != le32_to_cpu(validation.fve_crc32)) { + /* found an invalid FVE metadata copy, log and skip */ + log_dbg(cd, _("Failed to read or validate BITLK FVE metadata copy #%d from %s."), i, device_path(device)); + } else { + /* found a valid FVE metadata copy, use it */ + valid_fve_metadata_idx = i; + break; + } + } + + if (valid_fve_metadata_idx < 0) { + /* all FVE metadata copies are invalid, fail */ + log_err(cd, _("Failed to read and validate BITLK FVE metadata from %s."), device_path(device)); + r = -EINVAL; + goto out; + } + + /* check that a valid FVE metadata block is in its expected location */ + if (params->metadata_offset[valid_fve_metadata_idx] != le64_to_cpu(fve.fve_offset[valid_fve_metadata_idx])) { + log_err(cd, _("Failed to validate the location of BITLK FVE metadata from %s."), device_path(device)); + r = -EINVAL; + goto out; + } + + /* update offsets from a valid FVE metadata copy */ + for (i = 0; i < 3; i++) + params->metadata_offset[i] = le64_to_cpu(fve.fve_offset[i]); + + /* check that the FVE metadata hasn't changed between reads, because we are preparing for the MAC check */ + if (memcmp(&fve, fve_validated_block, sizeof(fve)) != 0) { + log_err(cd, _("BITLK FVE metadata changed between reads from %s."), device_path(device)); + r = -EINVAL; + goto out; + } + + crypt_backend_memzero(¶ms->sha256_fve, 32); + if (crypt_hash_init(&hash, "sha256")) { + log_err(cd, _("Failed to hash BITLK FVE metadata read from %s."), device_path(device)); + r = -EINVAL; + goto out; + } + crypt_hash_write(hash, (const char *)fve_validated_block, fve_size_real); + crypt_hash_final(hash, (char *)¶ms->sha256_fve, 32); + crypt_hash_destroy(hash); + + /* do some extended checks against FVE metadata, but not including MAC verification */ + params->validation = malloc(sizeof(struct bitlk_validation)); + if (!params->validation) { + r = -ENOMEM; + goto out; + } + + if (!parse_fve_metadata_validation(params, &validation)) { + log_err(cd, _("Failed to parse BITLK FVE validation metadata from %s."), device_path(device)); r = -EINVAL; goto out; } @@ -586,17 +726,18 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) } memset(fve_entries, 0, fve_entries_size); - log_dbg(cd, "Reading BITLK FVE metadata entries of size %zu on device %s, offset %" PRIu64 ".", - fve_entries_size, device_path(device), params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN); + log_dbg(cd, "Getting BITLK FVE metadata entries of size %zu on device %s, offset %" PRIu64 ".", + fve_entries_size, device_path(device), params->metadata_offset[valid_fve_metadata_idx] + BITLK_FVE_METADATA_HEADERS_LEN); - if (read_lseek_blockwise(devfd, device_block_size(cd, device), - device_alignment(device), fve_entries, fve_entries_size, - params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN) != (ssize_t)fve_entries_size) { - log_err(cd, _("Failed to read BITLK metadata entries from %s."), device_path(device)); + if (BITLK_FVE_METADATA_HEADERS_LEN + fve_entries_size > fve_size_real) { + log_err(cd, _("Failed to check BITLK metadata entries previously read from %s."), device_path(device)); r = -EINVAL; goto out; } + /* fetch these entries from validated buffer to avoid double-fetch */ + memcpy(fve_entries, fve_validated_block + BITLK_FVE_METADATA_HEADERS_LEN, fve_entries_size); + while ((fve_entries_size - start) >= (sizeof(entry_size) + sizeof(entry_type))) { /* size of this entry */ @@ -717,10 +858,10 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) start += entry_size; } - out: - if (fve_entries) - free(fve_entries); + free(fve_entries); + free(fve_validated_block); + return r; } @@ -742,7 +883,7 @@ int BITLK_dump(struct crypt_device *cd, struct device *device, struct bitlk_meta log_std(cd, "Description: \t%s\n", params->description); log_std(cd, "Cipher name: \t%s\n", params->cipher); log_std(cd, "Cipher mode: \t%s\n", params->cipher_mode); - log_std(cd, "Cipher key: \t%u bits\n", params->key_size); + log_std(cd, "Cipher key: \t%u [bits]\n", params->key_size); log_std(cd, "\n"); @@ -761,15 +902,15 @@ int BITLK_dump(struct crypt_device *cd, struct device *device, struct bitlk_meta vk_p = vmk_p->vk; while (vk_p) { - log_std(cd, "\tKey data size:\t%zu [bytes]\n", vk_p->keylength); - vk_p = vk_p->next; + log_std(cd, "\tKey data size:\t%zu [bytes]\n", crypt_volume_key_length(vk_p)); + vk_p = crypt_volume_key_next(vk_p); } vmk_p = vmk_p->next; next_id++; } log_std(cd, " %d: FVEK\n", next_id); - log_std(cd, "\tKey data size:\t%zu [bytes]\n", params->fvek->vk->keylength); + log_std(cd, "\tKey data size:\t%zu [bytes]\n", crypt_volume_key_length(params->fvek->vk)); log_std(cd, "\n"); @@ -987,9 +1128,13 @@ static int bitlk_kdf(const char *password, struct crypt_hash *hd = NULL; int len = 0; char16_t *utf16Password = NULL; + size_t utf16Len = 0; int i = 0; int r = 0; + if (!password) + return -EINVAL; + memcpy(kdf.salt, salt, 16); r = crypt_hash_init(&hd, BITLK_KDF_HASH); @@ -1012,7 +1157,8 @@ static int bitlk_kdf(const char *password, if (r < 0) goto out; - crypt_hash_write(hd, (char*)utf16Password, passwordLen * 2); + utf16Len = crypt_char16_strlen(utf16Password); + crypt_hash_write(hd, (char*)utf16Password, utf16Len * 2); r = crypt_hash_final(hd, kdf.initial_sha256, len); if (r < 0) goto out; @@ -1058,11 +1204,14 @@ static int decrypt_key(struct crypt_device *cd, int r; uint16_t key_size = 0; - outbuf = crypt_safe_alloc(enc_key->keylength); + outbuf = crypt_safe_alloc(crypt_volume_key_length(enc_key)); if (!outbuf) return -ENOMEM; - r = crypt_bitlk_decrypt_key(key->key, key->keylength, enc_key->key, outbuf, enc_key->keylength, + r = crypt_bitlk_decrypt_key(crypt_volume_key_get_key(key), + crypt_volume_key_length(key), + crypt_volume_key_get_key(enc_key), outbuf, + crypt_volume_key_length(enc_key), (const char*)iv, iv_size, (const char*)tag, tag_size); if (r < 0) { if (r == -ENOTSUP) @@ -1073,9 +1222,10 @@ static int decrypt_key(struct crypt_device *cd, /* key_data has it's size as part of the metadata */ memcpy(&key_size, outbuf, 2); key_size = le16_to_cpu(key_size); - if (enc_key->keylength != key_size) { + if (crypt_volume_key_length(enc_key) != key_size) { log_err(cd, _("Unexpected key data size.")); - log_dbg(cd, "Expected key data size: %zu, got %" PRIu16 "", enc_key->keylength, key_size); + log_dbg(cd, "Expected key data size: %zu, got %" PRIu16 "", + crypt_volume_key_length(enc_key), key_size); r = -EINVAL; goto out; @@ -1085,7 +1235,7 @@ static int decrypt_key(struct crypt_device *cd, crypt_get_volume_key_size(cd) == 32) { /* 128bit AES-CBC with Elephant -- key size is 256 bit (2 keys) but key data is 512 bits, data: 16B CBC key, 16B empty, 16B elephant key, 16B empty */ - memcpy(outbuf + 16 + BITLK_OPEN_KEY_METADATA_LEN, + crypt_safe_memcpy(outbuf + 16 + BITLK_OPEN_KEY_METADATA_LEN, outbuf + 2 * 16 + BITLK_OPEN_KEY_METADATA_LEN, 16); key_size = 32 + BITLK_OPEN_KEY_METADATA_LEN; } @@ -1099,6 +1249,41 @@ static int decrypt_key(struct crypt_device *cd, return r; } +static int get_clear_key(struct crypt_device *cd, const struct bitlk_vmk *vmk, struct volume_key **vmk_dec_key) +{ + struct volume_key *nested_key = vmk->vk; + + if (!nested_key) { + log_dbg(cd, "Clearkey VMK structure incomplete - missing nested key"); + return -ENOTSUP; + } + + struct volume_key *encrypted_vmk = crypt_volume_key_next(nested_key); + + if (!encrypted_vmk) { + log_dbg(cd, "Clearkey VMK structure incomplete - missing encrypted VMK"); + return -ENOTSUP; + } + + /** + * For clearkey protection, we need to decrypt the encrypted VMK using the nested key + * and return the decrypted VMK as vmk_dec_key + */ + struct volume_key *decrypted_vmk = NULL; + int r = decrypt_key(cd, &decrypted_vmk, encrypted_vmk, nested_key, + vmk->mac_tag, BITLK_VMK_MAC_TAG_SIZE, + vmk->nonce, BITLK_NONCE_SIZE, false); + + if (r == 0 && decrypted_vmk) { + log_dbg(cd, "Successfully decrypted VMK using nested key"); + *vmk_dec_key = decrypted_vmk; + return 0; + } else { + log_dbg(cd, "Failed to decrypt VMK using nested key (error: %d)", r); + return r; + } +} + int BITLK_get_volume_key(struct crypt_device *cd, const char *password, size_t passwordLen, @@ -1109,10 +1294,12 @@ int BITLK_get_volume_key(struct crypt_device *cd, struct volume_key *open_vmk_key = NULL; struct volume_key *vmk_dec_key = NULL; struct volume_key *recovery_key = NULL; + struct bitlk_validation_hash dec_hash = {}; const struct bitlk_vmk *next_vmk = NULL; next_vmk = params->vmks; while (next_vmk) { + bool is_decrypted = false; if (next_vmk->protection == BITLK_PROTECTION_PASSPHRASE) { r = bitlk_kdf(password, passwordLen, false, next_vmk->salt, &vmk_dec_key); if (r) { @@ -1134,7 +1321,8 @@ int BITLK_get_volume_key(struct crypt_device *cd, continue; } log_dbg(cd, "Trying to use given password as a recovery key."); - r = bitlk_kdf(recovery_key->key, recovery_key->keylength, + r = bitlk_kdf(crypt_volume_key_get_key(recovery_key), + crypt_volume_key_length(recovery_key), true, next_vmk->salt, &vmk_dec_key); crypt_free_volume_key(recovery_key); if (r) @@ -1146,8 +1334,18 @@ int BITLK_get_volume_key(struct crypt_device *cd, continue; } log_dbg(cd, "Trying to use external key found in provided password."); + } else if (next_vmk->protection == BITLK_PROTECTION_CLEAR_KEY) { + r = get_clear_key(cd, next_vmk, &vmk_dec_key); + if (r) { + /* something wrong happened, but we still want to check other key slots */ + next_vmk = next_vmk->next; + continue; + } + is_decrypted = true; + open_vmk_key = vmk_dec_key; + log_dbg(cd, "Extracted VMK using clearkey."); } else { - /* only passphrase, recovery passphrase and startup key VMKs supported right now */ + /* only passphrase, recovery passphrase, startup key and clearkey VMKs supported right now */ log_dbg(cd, "Skipping %s", get_vmk_protection_string(next_vmk->protection)); next_vmk = next_vmk->next; if (r == 0) @@ -1156,19 +1354,51 @@ int BITLK_get_volume_key(struct crypt_device *cd, continue; } - log_dbg(cd, "Trying to decrypt %s.", get_vmk_protection_string(next_vmk->protection)); - r = decrypt_key(cd, &open_vmk_key, next_vmk->vk, vmk_dec_key, - next_vmk->mac_tag, BITLK_VMK_MAC_TAG_SIZE, - next_vmk->nonce, BITLK_NONCE_SIZE, false); + if (!is_decrypted) { + r = decrypt_key(cd, &open_vmk_key, next_vmk->vk, vmk_dec_key, + next_vmk->mac_tag, BITLK_VMK_MAC_TAG_SIZE, + next_vmk->nonce, BITLK_NONCE_SIZE, false); + + crypt_free_volume_key(vmk_dec_key); + } if (r < 0) { log_dbg(cd, "Failed to decrypt VMK using provided passphrase."); - crypt_free_volume_key(vmk_dec_key); + if (r == -ENOTSUP) return r; next_vmk = next_vmk->next; continue; } - crypt_free_volume_key(vmk_dec_key); + + log_dbg(cd, "Trying to decrypt validation metadata using VMK."); + r = crypt_bitlk_decrypt_key(crypt_volume_key_get_key(open_vmk_key), + crypt_volume_key_length(open_vmk_key), + (const char*)params->validation->enc_datum, + (char *)&dec_hash, + BITLK_VALIDATION_VMK_DATA_SIZE - BITLK_NONCE_SIZE - BITLK_VMK_MAC_TAG_SIZE, + (const char*)params->validation->nonce, BITLK_NONCE_SIZE, + (const char*)params->validation->mac_tag, BITLK_VMK_MAC_TAG_SIZE); + if (r < 0) { + log_dbg(cd, "Failed to decrypt validation metadata using VMK."); + crypt_free_volume_key(open_vmk_key); + if (r == -ENOTSUP) + return r; + break; + } + + /* now, do the MAC validation */ + if (le16_to_cpu(dec_hash.role) != 0 ||le16_to_cpu(dec_hash.type) != 1 || + (le16_to_cpu(dec_hash.hash_type) != 0x2005)) { + log_dbg(cd, "Failed to parse decrypted validation metadata."); + crypt_free_volume_key(open_vmk_key); + return -ENOTSUP; + } + + if (memcmp(dec_hash.hash, params->sha256_fve, sizeof(dec_hash.hash)) != 0) { + log_dbg(cd, "Failed MAC validation of BITLK FVE metadata."); + crypt_free_volume_key(open_vmk_key); + return -EINVAL; + } r = decrypt_key(cd, open_fvek_key, params->fvek->vk, open_vmk_key, params->fvek->mac_tag, BITLK_VMK_MAC_TAG_SIZE, @@ -1197,8 +1427,6 @@ int BITLK_get_volume_key(struct crypt_device *cd, static int _activate_check(struct crypt_device *cd, const struct bitlk_metadata *params) { - const struct bitlk_vmk *next_vmk = NULL; - if (!params->state) { log_err(cd, _("This BITLK device is in an unsupported state and cannot be activated.")); return -ENOTSUP; @@ -1209,15 +1437,6 @@ static int _activate_check(struct crypt_device *cd, return -ENOTSUP; } - next_vmk = params->vmks; - while (next_vmk) { - if (next_vmk->protection == BITLK_PROTECTION_CLEAR_KEY) { - log_err(cd, _("Activation of partially decrypted BITLK device is not supported.")); - return -ENOTSUP; - } - next_vmk = next_vmk->next; - } - return 0; } @@ -1241,7 +1460,7 @@ static int _activate(struct crypt_device *cd, uint64_t next_start = 0; uint64_t next_end = 0; uint64_t last_segment = 0; - uint32_t dmt_flags = 0; + uint64_t dmt_flags = 0; r = _activate_check(cd, params); if (r) @@ -1365,7 +1584,7 @@ static int _activate(struct crypt_device *cd, crypt_get_cipher_spec(cd), segments[i].iv_offset, segments[i].iv_offset, - NULL, 0, + NULL, 0, 0, params->sector_size); if (r) goto out; @@ -1401,54 +1620,17 @@ static int _activate(struct crypt_device *cd, return r; } -int BITLK_activate_by_passphrase(struct crypt_device *cd, - const char *name, - const char *password, - size_t passwordLen, - const struct bitlk_metadata *params, - uint32_t flags) -{ - int r = 0; - struct volume_key *open_fvek_key = NULL; - - r = _activate_check(cd, params); - if (r) - return r; - - r = BITLK_get_volume_key(cd, password, passwordLen, params, &open_fvek_key); - if (r < 0) - goto out; - - /* Password verify only */ - if (!name) - goto out; - - r = _activate(cd, name, open_fvek_key, params, flags); -out: - crypt_free_volume_key(open_fvek_key); - return r; -} - int BITLK_activate_by_volume_key(struct crypt_device *cd, const char *name, - const char *volume_key, - size_t volume_key_size, + struct volume_key *vk, const struct bitlk_metadata *params, uint32_t flags) { - int r = 0; - struct volume_key *open_fvek_key = NULL; + int r; r = _activate_check(cd, params); if (r) return r; - open_fvek_key = crypt_alloc_volume_key(volume_key_size, volume_key); - if (!open_fvek_key) - return -ENOMEM; - - r = _activate(cd, name, open_fvek_key, params, flags); - - crypt_free_volume_key(open_fvek_key); - return r; + return _activate(cd, name, vk, params, flags); } diff --git a/lib/bitlk/bitlk.h b/lib/bitlk/bitlk.h index f175683..dde32bf 100644 --- a/lib/bitlk/bitlk.h +++ b/lib/bitlk/bitlk.h @@ -2,9 +2,9 @@ /* * BITLK (BitLocker-compatible) header definition * - * Copyright (C) 2019-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2019-2024 Milan Broz - * Copyright (C) 2019-2024 Vojtech Trefny + * Copyright (C) 2019-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2019-2025 Milan Broz + * Copyright (C) 2019-2025 Vojtech Trefny */ #ifndef _CRYPTSETUP_BITLK_H @@ -21,6 +21,8 @@ struct volume_key; #define BITLK_NONCE_SIZE 12 #define BITLK_SALT_SIZE 16 #define BITLK_VMK_MAC_TAG_SIZE 16 +#define BITLK_VALIDATION_VMK_HEADER_SIZE 8 +#define BITLK_VALIDATION_VMK_DATA_SIZE 72 #define BITLK_STATE_NORMAL 0x0004 @@ -85,6 +87,13 @@ struct bitlk_fvek { struct volume_key *vk; }; +struct bitlk_validation { + uint8_t mac_tag[BITLK_VMK_MAC_TAG_SIZE]; + uint8_t nonce[BITLK_NONCE_SIZE]; + /* technically, this is not "VMK", but some sources call it this way */ + uint8_t enc_datum[BITLK_VALIDATION_VMK_DATA_SIZE]; +}; + struct bitlk_metadata { uint16_t sector_size; uint64_t volume_size; @@ -101,8 +110,10 @@ struct bitlk_metadata { uint32_t metadata_version; uint64_t volume_header_offset; uint64_t volume_header_size; + const char *sha256_fve[32]; struct bitlk_vmk *vmks; struct bitlk_fvek *fvek; + struct bitlk_validation *validation; }; int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params); @@ -115,17 +126,9 @@ int BITLK_get_volume_key(struct crypt_device *cd, const struct bitlk_metadata *params, struct volume_key **open_fvek_key); -int BITLK_activate_by_passphrase(struct crypt_device *cd, - const char *name, - const char *password, - size_t passwordLen, - const struct bitlk_metadata *params, - uint32_t flags); - int BITLK_activate_by_volume_key(struct crypt_device *cd, const char *name, - const char *volume_key, - size_t volume_key_size, + struct volume_key *vk, const struct bitlk_metadata *params, uint32_t flags); diff --git a/lib/bitops.h b/lib/bitops.h index a991687..1ef9718 100644 --- a/lib/bitops.h +++ b/lib/bitops.h @@ -10,13 +10,13 @@ #include #include -#if defined(HAVE_BYTESWAP_H) +#if HAVE_BYTESWAP_H # include #endif -#if defined(HAVE_ENDIAN_H) +#if HAVE_ENDIAN_H # include -#elif defined(HAVE_SYS_ENDIAN_H) /* BSDs have them here */ +#elif HAVE_SYS_ENDIAN_H /* BSDs have them here */ # include #endif diff --git a/lib/crypt_plain.c b/lib/crypt_plain.c index 3886d29..21e008e 100644 --- a/lib/crypt_plain.c +++ b/lib/crypt_plain.c @@ -3,8 +3,8 @@ * cryptsetup plain device helper functions * * Copyright (C) 2004 Jana Saout - * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2010-2024 Milan Broz + * Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2025 Milan Broz */ #include @@ -92,7 +92,7 @@ int crypt_plain_hash(struct crypt_device *cd, log_dbg(cd, "Too short plain passphrase."); return -EINVAL; } - memcpy(key, passphrase, hash_size); + crypt_safe_memcpy(key, passphrase, hash_size); r = 0; } else r = hash(hash_name_buf, hash_size, key, passphrase_size, passphrase); diff --git a/lib/crypto_backend/Makemodule.am b/lib/crypto_backend/Makemodule.am index 7507763..a983637 100644 --- a/lib/crypto_backend/Makemodule.am +++ b/lib/crypto_backend/Makemodule.am @@ -13,7 +13,8 @@ libcrypto_backend_la_SOURCES = \ lib/crypto_backend/utf8.c \ lib/crypto_backend/argon2_generic.c \ lib/crypto_backend/cipher_generic.c \ - lib/crypto_backend/cipher_check.c + lib/crypto_backend/cipher_check.c \ + lib/crypto_backend/memutils.c if CRYPTO_BACKEND_GCRYPT libcrypto_backend_la_SOURCES += lib/crypto_backend/crypto_gcrypt.c @@ -30,6 +31,9 @@ endif if CRYPTO_BACKEND_NETTLE libcrypto_backend_la_SOURCES += lib/crypto_backend/crypto_nettle.c endif +if CRYPTO_BACKEND_MBEDTLS +libcrypto_backend_la_SOURCES += lib/crypto_backend/crypto_mbedtls.c +endif if CRYPTO_INTERNAL_PBKDF2 libcrypto_backend_la_SOURCES += lib/crypto_backend/pbkdf2_generic.c diff --git a/lib/crypto_backend/argon2/blake2/blake2b.c b/lib/crypto_backend/argon2/blake2/blake2b.c index d8f69e8..66b2df6 100644 --- a/lib/crypto_backend/argon2/blake2/blake2b.c +++ b/lib/crypto_backend/argon2/blake2/blake2b.c @@ -360,7 +360,7 @@ int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { TRY(blake2b_final(&blake_state, out, outlen)); } else { uint32_t toproduce; - uint8_t out_buffer[BLAKE2B_OUTBYTES]; + uint8_t out_buffer[BLAKE2B_OUTBYTES] = {0}; uint8_t in_buffer[BLAKE2B_OUTBYTES]; TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); diff --git a/lib/crypto_backend/argon2/core.c b/lib/crypto_backend/argon2/core.c index f128d84..1f74a44 100644 --- a/lib/crypto_backend/argon2/core.c +++ b/lib/crypto_backend/argon2/core.c @@ -128,7 +128,7 @@ void secure_wipe_memory(void *v, size_t n) { void secure_wipe_memory(void *v, size_t n) { memset_s(v, n, 0, n); } -#elif defined(HAVE_EXPLICIT_BZERO) +#elif HAVE_EXPLICIT_BZERO void secure_wipe_memory(void *v, size_t n) { explicit_bzero(v, n); } @@ -356,12 +356,9 @@ static int fill_memory_blocks_mt(argon2_instance_t *instance) { } fail: - if (thread != NULL) { - free(thread); - } - if (thr_data != NULL) { - free(thr_data); - } + free(thread); + free(thr_data); + return rc; } diff --git a/lib/crypto_backend/argon2/encoding.c b/lib/crypto_backend/argon2/encoding.c index a717263..4020977 100644 --- a/lib/crypto_backend/argon2/encoding.c +++ b/lib/crypto_backend/argon2/encoding.c @@ -83,7 +83,7 @@ static int b64_byte_to_char(unsigned x) { return (LT(x, 26) & (x + 'A')) | (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) | - (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') | + (GE(x, 52) & LT(x, 62) & (x - (52 - '0'))) | (EQ(x, 62) & '+') | (EQ(x, 63) & '/'); } diff --git a/lib/crypto_backend/argon2_generic.c b/lib/crypto_backend/argon2_generic.c index e69799e..d50bb9b 100644 --- a/lib/crypto_backend/argon2_generic.c +++ b/lib/crypto_backend/argon2_generic.c @@ -2,8 +2,8 @@ /* * Argon2 PBKDF2 library wrapper * - * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2024 Milan Broz + * Copyright (C) 2016-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2025 Milan Broz */ #include diff --git a/lib/crypto_backend/base64.c b/lib/crypto_backend/base64.c index e196f6b..d9733a0 100644 --- a/lib/crypto_backend/base64.c +++ b/lib/crypto_backend/base64.c @@ -5,7 +5,7 @@ * Copyright (C) 2010 Lennart Poettering * * cryptsetup related changes - * Copyright (C) 2021-2024 Milan Broz + * Copyright (C) 2021-2025 Milan Broz */ #include @@ -19,7 +19,7 @@ /* https://tools.ietf.org/html/rfc4648#section-4 */ static char base64char(int x) { - static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + static const char table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; return table[x & 63]; diff --git a/lib/crypto_backend/cipher_check.c b/lib/crypto_backend/cipher_check.c index 524ffe3..d8c7a60 100644 --- a/lib/crypto_backend/cipher_check.c +++ b/lib/crypto_backend/cipher_check.c @@ -2,8 +2,8 @@ /* * Cipher performance check * - * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2018-2024 Milan Broz + * Copyright (C) 2018-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2018-2025 Milan Broz */ #include @@ -42,43 +42,36 @@ static int time_ms(struct timespec *start, struct timespec *end, double *ms) return 0; } -static int cipher_perf_one(const char *name, const char *mode, char *buffer, size_t buffer_size, - const char *key, size_t key_size, const char *iv, size_t iv_size, int enc) +static int cipher_perf_one(struct crypt_cipher_kernel *cipher, char *buffer, size_t buffer_size, + const char *iv, size_t iv_size, int enc) { - struct crypt_cipher_kernel cipher; size_t done = 0, block = CIPHER_BLOCK_BYTES; int r; if (buffer_size < block) block = buffer_size; - r = crypt_cipher_init_kernel(&cipher, name, mode, key, key_size); - if (r < 0) - return r; - while (done < buffer_size) { if ((done + block) > buffer_size) block = buffer_size - done; if (enc) - r = crypt_cipher_encrypt_kernel(&cipher, &buffer[done], &buffer[done], + r = crypt_cipher_encrypt_kernel(cipher, &buffer[done], &buffer[done], block, iv, iv_size); else - r = crypt_cipher_decrypt_kernel(&cipher, &buffer[done], &buffer[done], + r = crypt_cipher_decrypt_kernel(cipher, &buffer[done], &buffer[done], block, iv, iv_size); if (r < 0) - break; + return r; done += block; } - crypt_cipher_destroy_kernel(&cipher); - - return r; + return 0; } -static int cipher_measure(const char *name, const char *mode, char *buffer, size_t buffer_size, - const char *key, size_t key_size, const char *iv, size_t iv_size, - int encrypt, double *ms) + +static int cipher_measure(struct crypt_cipher_kernel *cipher, char *buffer, size_t buffer_size, + const char *iv, size_t iv_size, int encrypt, double *ms) { struct timespec start, end; int r; @@ -90,7 +83,7 @@ static int cipher_measure(const char *name, const char *mode, char *buffer, size if (clock_gettime(CLOCK_MONOTONIC_RAW, &start) < 0) return -EINVAL; - r = cipher_perf_one(name, mode, buffer, buffer_size, key, key_size, iv, iv_size, encrypt); + r = cipher_perf_one(cipher, buffer, buffer_size, iv, iv_size, encrypt); if (r < 0) return r; @@ -118,15 +111,20 @@ int crypt_cipher_perf_kernel(const char *name, const char *mode, char *buffer, s const char *key, size_t key_size, const char *iv, size_t iv_size, double *encryption_mbs, double *decryption_mbs) { + struct crypt_cipher_kernel cipher; double ms_enc, ms_dec, ms; int r, repeat_enc, repeat_dec; + r = crypt_cipher_init_kernel(&cipher, name, mode, key, key_size); + if (r < 0) + return r; + ms_enc = 0.0; repeat_enc = 1; while (ms_enc < 1000.0) { - r = cipher_measure(name, mode, buffer, buffer_size, key, key_size, iv, iv_size, 1, &ms); + r = cipher_measure(&cipher, buffer, buffer_size, iv, iv_size, 1, &ms); if (r < 0) - return r; + goto out; ms_enc += ms; repeat_enc++; } @@ -134,9 +132,9 @@ int crypt_cipher_perf_kernel(const char *name, const char *mode, char *buffer, s ms_dec = 0.0; repeat_dec = 1; while (ms_dec < 1000.0) { - r = cipher_measure(name, mode, buffer, buffer_size, key, key_size, iv, iv_size, 0, &ms); + r = cipher_measure(&cipher, buffer, buffer_size, iv, iv_size, 0, &ms); if (r < 0) - return r; + goto out; ms_dec += ms; repeat_dec++; } @@ -144,5 +142,8 @@ int crypt_cipher_perf_kernel(const char *name, const char *mode, char *buffer, s *encryption_mbs = speed_mbs(buffer_size * repeat_enc, ms_enc); *decryption_mbs = speed_mbs(buffer_size * repeat_dec, ms_dec); - return 0; + r = 0; +out: + crypt_cipher_destroy_kernel(&cipher); + return r; } diff --git a/lib/crypto_backend/cipher_generic.c b/lib/crypto_backend/cipher_generic.c index 41774a2..be7e4a0 100644 --- a/lib/crypto_backend/cipher_generic.c +++ b/lib/crypto_backend/cipher_generic.c @@ -2,8 +2,8 @@ /* * Linux kernel cipher generic utilities * - * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2018-2024 Milan Broz + * Copyright (C) 2018-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2018-2025 Milan Broz */ #include diff --git a/lib/crypto_backend/crypto_backend.h b/lib/crypto_backend/crypto_backend.h index 6a5db9b..9c37cf1 100644 --- a/lib/crypto_backend/crypto_backend.h +++ b/lib/crypto_backend/crypto_backend.h @@ -2,8 +2,8 @@ /* * crypto backend implementation * - * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2010-2024 Milan Broz + * Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2025 Milan Broz */ #ifndef _CRYPTO_BACKEND_H @@ -14,13 +14,17 @@ #include #include #include -#ifdef HAVE_UCHAR_H +#if HAVE_UCHAR_H #include #else #define char32_t uint32_t #define char16_t uint16_t #endif +# ifdef __cplusplus +extern "C" { +# endif + struct crypt_hash; struct crypt_hmac; struct crypt_cipher; @@ -89,6 +93,7 @@ int crypt_base64_decode(char **out, size_t *out_length, const char *in, size_t i /* UTF8/16 */ int crypt_utf16_to_utf8(char **out, const char16_t *s, size_t length /* bytes! */); int crypt_utf8_to_utf16(char16_t **out, const char *s, size_t length); +size_t crypt_char16_strlen(const char16_t *s); /* Block ciphers */ int crypt_cipher_ivsize(const char *name, const char *mode); @@ -132,15 +137,10 @@ int crypt_bitlk_decrypt_key(const void *key, size_t key_length, const char *tag, size_t tag_length); /* Memzero helper (memset on stack can be optimized out) */ -static inline void crypt_backend_memzero(void *s, size_t n) -{ -#ifdef HAVE_EXPLICIT_BZERO - explicit_bzero(s, n); -#else - volatile uint8_t *p = (volatile uint8_t *)s; - while(n--) *p++ = 0; -#endif -} +void crypt_backend_memzero(void *s, size_t n); + +/* Memcpy helper to avoid spilling sensitive data through additional registers */ +void *crypt_backend_memcpy(void *dst, const void *src, size_t n); /* Memcmp helper (memcmp in constant time) */ int crypt_backend_memeq(const void *m1, const void *m2, size_t n); @@ -148,4 +148,8 @@ int crypt_backend_memeq(const void *m1, const void *m2, size_t n); /* crypto backend running in FIPS mode */ bool crypt_fips_mode(void); +# ifdef __cplusplus +} +# endif + #endif /* _CRYPTO_BACKEND_H */ diff --git a/lib/crypto_backend/crypto_backend_internal.h b/lib/crypto_backend/crypto_backend_internal.h index 9f01968..5326094 100644 --- a/lib/crypto_backend/crypto_backend_internal.h +++ b/lib/crypto_backend/crypto_backend_internal.h @@ -2,8 +2,8 @@ /* * crypto backend implementation * - * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2010-2024 Milan Broz + * Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2025 Milan Broz */ #ifndef _CRYPTO_BACKEND_INTERNAL_H @@ -47,17 +47,6 @@ int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length, const char *tag, size_t tag_length); /* Internal implementation for constant time memory comparison */ -static inline int crypt_internal_memeq(const void *m1, const void *m2, size_t n) -{ - const unsigned char *_m1 = (const unsigned char *) m1; - const unsigned char *_m2 = (const unsigned char *) m2; - unsigned char result = 0; - size_t i; - - for (i = 0; i < n; i++) - result |= _m1[i] ^ _m2[i]; - - return result; -} +int crypt_internal_memeq(const void *m1, const void *m2, size_t n); #endif /* _CRYPTO_BACKEND_INTERNAL_H */ diff --git a/lib/crypto_backend/crypto_cipher_kernel.c b/lib/crypto_backend/crypto_cipher_kernel.c index 7019798..72918eb 100644 --- a/lib/crypto_backend/crypto_cipher_kernel.c +++ b/lib/crypto_backend/crypto_cipher_kernel.c @@ -2,8 +2,8 @@ /* * Linux kernel userspace API crypto backend implementation (skcipher) * - * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2012-2024 Milan Broz + * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Milan Broz */ #include @@ -14,7 +14,7 @@ #include #include "crypto_backend_internal.h" -#ifdef ENABLE_AF_ALG +#if ENABLE_AF_ALG #include @@ -40,6 +40,8 @@ static int _crypt_cipher_init(struct crypt_cipher_kernel *ctx, const void *key, size_t key_length, size_t tag_length, struct sockaddr_alg *sa) { + void *optval = NULL; + if (!ctx) return -EINVAL; @@ -60,7 +62,7 @@ static int _crypt_cipher_init(struct crypt_cipher_kernel *ctx, return -EINVAL; } - if (tag_length && setsockopt(ctx->tfmfd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, tag_length) < 0) { + if (tag_length && setsockopt(ctx->tfmfd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, &optval, tag_length) < 0) { crypt_cipher_destroy_kernel(ctx); return -EINVAL; } @@ -97,6 +99,20 @@ int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name, return _crypt_cipher_init(ctx, key, key_length, 0, &sa); } +/* musl has broken CMSG_NXTHDR macro in system headers */ +static inline struct cmsghdr *_CMSG_NXTHDR(struct msghdr* mhdr, struct cmsghdr* cmsg) +{ +#if !defined(__GLIBC__) && defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-align" +#pragma clang diagnostic ignored "-Wsign-compare" + return CMSG_NXTHDR(mhdr, cmsg); +#pragma clang diagnostic pop +#else + return CMSG_NXTHDR(mhdr, cmsg); +#endif +} + /* The in/out should be aligned to page boundary */ /* coverity[ -taint_source : arg-3 ] */ static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx, @@ -144,7 +160,7 @@ static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx, /* Set IV */ if (iv) { - header = CMSG_NXTHDR(&msg, header); + header = _CMSG_NXTHDR(&msg, header); if (!header) return -EINVAL; @@ -153,7 +169,7 @@ static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx, header->cmsg_len = iv_msg_size; alg_iv = (void*)CMSG_DATA(header); alg_iv->ivlen = iv_length; - memcpy(alg_iv->iv, iv, iv_length); + crypt_backend_memcpy(alg_iv->iv, iv, iv_length); } len = sendmsg(ctx->opfd, &msg, 0); @@ -200,8 +216,8 @@ int crypt_cipher_check_kernel(const char *name, const char *mode, const char *integrity, size_t key_length) { struct crypt_cipher_kernel c; - char mode_name[64], tmp_salg_name[180], *real_mode = NULL, *cipher_iv = NULL, *key; - const char *salg_type; + char mode_name[64], tmp_salg_name[180], *cipher_iv = NULL, *key; + const char *salg_type, *real_mode; bool aead; int r; struct sockaddr_alg sa = { @@ -209,6 +225,7 @@ int crypt_cipher_check_kernel(const char *name, const char *mode, }; aead = integrity && strcmp(integrity, "none"); + real_mode = NULL; /* Remove IV if present */ if (mode) { @@ -229,14 +246,22 @@ int crypt_cipher_check_kernel(const char *name, const char *mode, memset(tmp_salg_name, 0, sizeof(tmp_salg_name)); /* FIXME: this is duplicating a part of devmapper backend */ - if (aead && !strcmp(integrity, "poly1305")) - r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc7539(%s,%s)", name, integrity); - else if (!real_mode) - r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name); - else if (aead && !strcmp(real_mode, "ccm")) - r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc4309(%s(%s))", real_mode, name); - else - r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode, name); + if (aead) { + /* In AEAD, mode parameter can be just IV like "random" */ + if (!strcmp(integrity, "poly1305")) + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc7539(%s,%s)", name, integrity); + else if (!real_mode) + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name); + else if (!strcmp(real_mode, "ccm")) + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc4309(%s(%s))", real_mode, name); + else + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode, name); + } else { + if (!mode) + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name); + else + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode ?: mode_name, name); + } if (r < 0 || (size_t)r >= sizeof(tmp_salg_name)) return -EINVAL; diff --git a/lib/crypto_backend/crypto_gcrypt.c b/lib/crypto_backend/crypto_gcrypt.c index d188e02..a50cb45 100644 --- a/lib/crypto_backend/crypto_gcrypt.c +++ b/lib/crypto_backend/crypto_gcrypt.c @@ -2,8 +2,8 @@ /* * GCRYPT crypto backend implementation * - * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2010-2024 Milan Broz + * Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2025 Milan Broz */ #include @@ -249,7 +249,7 @@ int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length) if (!hash) return -EINVAL; - memcpy(buffer, hash, length); + crypt_backend_memcpy(buffer, hash, length); crypt_hash_restart(ctx); return 0; @@ -323,7 +323,7 @@ int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length) if (!hash) return -EINVAL; - memcpy(buffer, hash, length); + crypt_backend_memcpy(buffer, hash, length); crypt_hmac_restart(ctx); return 0; @@ -451,6 +451,7 @@ static int gcrypt_argon2(const char *type, .dispatch_job = gcrypt_dispatch_job, .wait_all_jobs = gcrypt_wait_all_jobs }; + gpg_error_t err; if (!strcmp(type, "argon2i")) atype = GCRY_KDF_ARGON2I; @@ -464,12 +465,11 @@ static int gcrypt_argon2(const char *type, param[2] = memory; param[3] = parallel; - if (gcry_kdf_open(&hd, GCRY_KDF_ARGON2, atype, param, 4, + err = gcry_kdf_open(&hd, GCRY_KDF_ARGON2, atype, param, 4, password, password_length, salt, salt_length, - NULL, 0, NULL, 0)) { - free(threads.jobs_ctx); - return -EINVAL; - } + NULL, 0, NULL, 0); + if (err) + return ((err & GPG_ERR_CODE_MASK) == GPG_ERR_ENOMEM) ? -ENOMEM : -EINVAL; if (parallel == 1) { /* Do not use threads here */ diff --git a/lib/crypto_backend/crypto_kernel.c b/lib/crypto_backend/crypto_kernel.c index 719dcb3..8b96e9e 100644 --- a/lib/crypto_backend/crypto_kernel.c +++ b/lib/crypto_backend/crypto_kernel.c @@ -2,8 +2,8 @@ /* * Linux kernel userspace API crypto backend implementation * - * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2010-2024 Milan Broz + * Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2025 Milan Broz */ #include diff --git a/lib/crypto_backend/crypto_mbedtls.c b/lib/crypto_backend/crypto_mbedtls.c new file mode 100644 index 0000000..09d7e16 --- /dev/null +++ b/lib/crypto_backend/crypto_mbedtls.c @@ -0,0 +1,535 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Mbed TLS crypto backend implementation + * + * Copyright (C) 2024-2025 Yiyuan Zhong + */ + +#include "crypto_backend.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crypto_backend_internal.h" + +struct crypt_hash { + const mbedtls_md_info_t *info; + mbedtls_md_context_t md; +}; + +struct crypt_hmac { + const mbedtls_md_info_t *info; + mbedtls_md_context_t md; +}; + +struct crypt_cipher { + const mbedtls_cipher_info_t *info; + mbedtls_cipher_context_t enc; + mbedtls_cipher_context_t dec; + int ecb; +}; + +static bool g_initialized = false; +static char g_backend_version[32]; +static mbedtls_entropy_context g_entropy; +static mbedtls_ctr_drbg_context g_ctr_drbg; + +static const mbedtls_md_info_t *crypt_get_hash(const char *name) +{ + static const struct hash_alg { + const char *name; + mbedtls_md_type_t type; + } kHash[] = { + {"sha1", MBEDTLS_MD_SHA1 }, + {"sha224", MBEDTLS_MD_SHA224 }, + {"sha256", MBEDTLS_MD_SHA256 }, + {"sha384", MBEDTLS_MD_SHA384 }, + {"sha512", MBEDTLS_MD_SHA512 }, + {"ripemd160", MBEDTLS_MD_RIPEMD160}, + {NULL, 0, } + }; + + size_t i = 0; + + while (name && kHash[i].name) { + if (strcmp(kHash[i].name, name) == 0) + return mbedtls_md_info_from_type(kHash[i].type); + i++; + } + + return NULL; +} + +int crypt_backend_init(bool fips) +{ + int ret; + + if (g_initialized) + return 0; + + if (fips) + return -ENOTSUP; + + mbedtls_version_get_string_full(g_backend_version); + + mbedtls_entropy_init(&g_entropy); + mbedtls_ctr_drbg_init(&g_ctr_drbg); + + ret = mbedtls_ctr_drbg_seed( + &g_ctr_drbg, mbedtls_entropy_func, + &g_entropy, NULL, MBEDTLS_CTR_DRBG_ENTROPY_LEN); + + if (ret) + return -EINVAL; + + g_initialized = true; + return 0; +} + +void crypt_backend_destroy(void) +{ + if (!g_initialized) + return; + + mbedtls_ctr_drbg_free(&g_ctr_drbg); + mbedtls_entropy_free(&g_entropy); + g_initialized = false; +} + +uint32_t crypt_backend_flags(void) +{ + return 0; +} + +const char *crypt_backend_version(void) +{ + return g_backend_version; +} + +bool crypt_fips_mode(void) +{ + return false; +} + +int crypt_backend_memeq(const void *m1, const void *m2, size_t n) +{ + return mbedtls_ct_memcmp(m1, m2, n); +} + +/* HASH */ +int crypt_hash_size(const char *name) +{ + const mbedtls_md_info_t *info; + info = crypt_get_hash(name); + return info ? mbedtls_md_get_size(info) : -ENOENT; +} + +int crypt_hash_init(struct crypt_hash **ctx, const char *name) +{ + struct crypt_hash *h; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + h->info = crypt_get_hash(name); + if (!h->info) { + free(h); + return -ENOENT; + } + + mbedtls_md_init(&h->md); + + if (mbedtls_md_setup(&h->md, h->info, 0)) { + mbedtls_md_free(&h->md); + free(h); + return -EINVAL; + } + + if (mbedtls_md_starts(&h->md)) { + mbedtls_md_free(&h->md); + free(h); + return -EINVAL; + } + + *ctx = h; + return 0; +} + +int crypt_hash_write(struct crypt_hash *ctx, const char *buffer, size_t length) +{ + if (mbedtls_md_update(&ctx->md, (const unsigned char *)buffer, length)) + return -EINVAL; + + return 0; +} + +int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length) +{ + unsigned char tmp[MBEDTLS_MD_MAX_SIZE]; + + if (length > mbedtls_md_get_size(ctx->info)) + return -EINVAL; + + if (mbedtls_md_finish(&ctx->md, tmp)) + return -EINVAL; + + crypt_backend_memcpy(buffer, tmp, length); + crypt_backend_memzero(tmp, sizeof(tmp)); + + if (mbedtls_md_starts(&ctx->md)) + return -EINVAL; + + return 0; +} + +void crypt_hash_destroy(struct crypt_hash *ctx) +{ + mbedtls_md_free(&ctx->md); + crypt_backend_memzero(ctx, sizeof(*ctx)); + free(ctx); +} + +/* HMAC */ +int crypt_hmac_size(const char *name) +{ + return crypt_hash_size(name); +} + +int crypt_hmac_init(struct crypt_hmac **ctx, const char *name, + const void *key, size_t key_length) +{ + struct crypt_hmac *h; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + h->info = crypt_get_hash(name); + if (!h->info) { + free(h); + return -ENOENT; + } + + mbedtls_md_init(&h->md); + + if (mbedtls_md_setup(&h->md, h->info, 1)) { + mbedtls_md_free(&h->md); + free(h); + return -EINVAL; + } + + if (mbedtls_md_hmac_starts(&h->md, key, key_length)) { + mbedtls_md_free(&h->md); + free(h); + return -EINVAL; + } + + *ctx = h; + return 0; +} + +int crypt_hmac_write(struct crypt_hmac *ctx, const char *buffer, size_t length) +{ + if (mbedtls_md_hmac_update(&ctx->md, (const unsigned char *)buffer, length)) + return -EINVAL; + + return 0; +} + +int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length) +{ + unsigned char tmp[MBEDTLS_MD_MAX_SIZE]; + + if (length > mbedtls_md_get_size(ctx->info)) + return -EINVAL; + + if (mbedtls_md_hmac_finish(&ctx->md, tmp)) + return -EINVAL; + + crypt_backend_memcpy(buffer, tmp, length); + crypt_backend_memzero(tmp, sizeof(tmp)); + + if (mbedtls_md_hmac_reset(&ctx->md)) + return -EINVAL; + + return 0; +} + +void crypt_hmac_destroy(struct crypt_hmac *ctx) +{ + mbedtls_md_free(&ctx->md); + crypt_backend_memzero(ctx, sizeof(*ctx)); + free(ctx); +} + +/* RNG */ +int crypt_backend_rng(char *buffer, size_t length, int quality, int fips) +{ + if (fips) + return -ENOTSUP; + + /* Allow skipping reseeding for non-cryptographic strong random numbers */ + if (quality == CRYPT_RND_NORMAL || quality == CRYPT_RND_SALT) + mbedtls_ctr_drbg_set_prediction_resistance(&g_ctr_drbg, MBEDTLS_CTR_DRBG_PR_OFF); + else + mbedtls_ctr_drbg_set_prediction_resistance(&g_ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON); + + if (mbedtls_ctr_drbg_random(&g_ctr_drbg, (unsigned char *)buffer, length)) + return -EINVAL; + + return 0; +} + +/* CIPHER */ +int crypt_cipher_init(struct crypt_cipher **ctx, const char *name, + const char *mode, const void *key, size_t key_length) +{ + static const struct { + const char *name; + mbedtls_cipher_id_t id; + } kCipher[] = { + { "aes", MBEDTLS_CIPHER_ID_AES }, + { "aria", MBEDTLS_CIPHER_ID_ARIA }, + { "camellia", MBEDTLS_CIPHER_ID_CAMELLIA }, + { NULL, 0 } + }; + + static const struct { + const char *name; + mbedtls_cipher_mode_t mode; + } kMode[] = { + { "ecb", MBEDTLS_MODE_ECB }, + { "cbc", MBEDTLS_MODE_CBC }, + { "cfb", MBEDTLS_MODE_CFB }, + { "ofb", MBEDTLS_MODE_OFB }, + { "ctr", MBEDTLS_MODE_CTR }, + { "xts", MBEDTLS_MODE_XTS }, + { NULL, 0 } + }; + + mbedtls_cipher_id_t cid = MBEDTLS_CIPHER_ID_NONE; + mbedtls_cipher_mode_t cmode = MBEDTLS_MODE_NONE; + struct crypt_cipher *h; + size_t i; + int bits; + + for (i = 0; kCipher[i].name; i++) { + if (strcmp(kCipher[i].name, name) == 0) { + cid = kCipher[i].id; + break; + } + } + + for (i = 0; kMode[i].name; i++) { + if (strcmp(kMode[i].name, mode) == 0) { + cmode = kMode[i].mode; + break; + } + } + + if (cid == MBEDTLS_CIPHER_ID_NONE || cmode == MBEDTLS_MODE_NONE) + return -ENOENT; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + bits = key_length * 8; + h->info = mbedtls_cipher_info_from_values(cid, bits, cmode); + if (!h->info) { + free(h); + return -ENOENT; + } + + mbedtls_cipher_init(&h->enc); + mbedtls_cipher_init(&h->dec); + if (mbedtls_cipher_setup(&h->enc, h->info) || + mbedtls_cipher_setup(&h->dec, h->info) || + mbedtls_cipher_setkey(&h->enc, key, bits, MBEDTLS_ENCRYPT) || + mbedtls_cipher_setkey(&h->dec, key, bits, MBEDTLS_DECRYPT)) { + + mbedtls_cipher_free(&h->dec); + mbedtls_cipher_free(&h->enc); + free(h); + return -EINVAL; + } + + if (cmode == MBEDTLS_MODE_CBC) { + if (mbedtls_cipher_set_padding_mode(&h->enc, MBEDTLS_PADDING_NONE) || + mbedtls_cipher_set_padding_mode(&h->dec, MBEDTLS_PADDING_NONE)) { + + mbedtls_cipher_free(&h->dec); + mbedtls_cipher_free(&h->enc); + free(h); + return -EINVAL; + } + } + + h->ecb = cmode == MBEDTLS_MODE_ECB; + *ctx = h; + return 0; +} + +void crypt_cipher_destroy(struct crypt_cipher *ctx) +{ + mbedtls_cipher_free(&ctx->dec); + mbedtls_cipher_free(&ctx->enc); + free(ctx); +} + +static int crypt_cipher_crypt( + mbedtls_cipher_context_t *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length, + int ecb) +{ + const unsigned char *input; + unsigned char *output; + size_t outlen; + size_t block; + size_t len; + + if (ecb) /* ECB requires exactly block length input */ + block = mbedtls_cipher_get_block_size(ctx); + else + block = length; + + input = (const unsigned char *)in; + output = (unsigned char *)out; + + if (mbedtls_cipher_set_iv(ctx, (const unsigned char *)iv, iv_length)) + return -EINVAL; + + if (mbedtls_cipher_reset(ctx)) + return -EINVAL; + + while (length) { + len = length < block ? length : block; + if (mbedtls_cipher_update(ctx, input, len, output, &outlen)) + return -EINVAL; + + output += outlen; + length -= len; + input += len; + } + + if (mbedtls_cipher_finish(ctx, output, &outlen)) + return -EINVAL; + + return 0; +} + +int crypt_cipher_encrypt(struct crypt_cipher *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) +{ + return crypt_cipher_crypt(&ctx->enc, in, out, length, iv, iv_length, ctx->ecb); +} + +int crypt_cipher_decrypt(struct crypt_cipher *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) +{ + return crypt_cipher_crypt(&ctx->dec, in, out, length, iv, iv_length, ctx->ecb); +} + +bool crypt_cipher_kernel_only(struct crypt_cipher *ctx __attribute__((unused))) +{ + return false; +} + +int crypt_pbkdf(const char *kdf, const char *hash, + const char *password, size_t password_length, + const char *salt, size_t salt_length, + char *key, size_t key_length, + uint32_t iterations, uint32_t memory, uint32_t parallel) +{ + const mbedtls_md_info_t *info; +#if !HAVE_MBEDTLS_PKCS5_PBKDF2_HMAC_EXT + mbedtls_md_context_t md; +#endif + + if (!kdf) + return -EINVAL; + + if (strcmp(kdf, "pbkdf2") == 0) { + info = crypt_get_hash(hash); + if (!info) + return -EINVAL; + +#if HAVE_MBEDTLS_PKCS5_PBKDF2_HMAC_EXT + if (mbedtls_pkcs5_pbkdf2_hmac_ext(mbedtls_md_get_type(info), + (const unsigned char *)password, password_length, + (const unsigned char *)salt, salt_length, + iterations, key_length, (unsigned char *)key)) { + + return -EINVAL; + } +#else + mbedtls_md_init(&md); + if (mbedtls_md_setup(&md, info, 1)) + return -EINVAL; + + if (mbedtls_pkcs5_pbkdf2_hmac(&md, + (const unsigned char *)password, password_length, + (const unsigned char *)salt, salt_length, + iterations, key_length, (unsigned char *)key)) { + + mbedtls_md_free(&md); + return -EINVAL; + } + + mbedtls_md_free(&md); +#endif + return 0; + + } else if (strncmp(kdf, "argon2", 6) == 0) { + return argon2(kdf, password, password_length, salt, salt_length, + key, key_length, iterations, memory, parallel); + } + + return -EINVAL; +} + +int crypt_bitlk_decrypt_key(const void *key, size_t key_length, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length, + const char *tag, size_t tag_length) +{ + const unsigned char *tagptr; + const unsigned char *input; + const unsigned char *ivptr; + mbedtls_ccm_context ctx; + unsigned char *output; + + tagptr = (const unsigned char *)tag; + ivptr = (const unsigned char *)iv; + input = (const unsigned char *)in; + output = (unsigned char *)out; + mbedtls_ccm_init(&ctx); + + if (mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key, key_length * 8)) { + mbedtls_ccm_free(&ctx); + return -EINVAL; + } + + if (mbedtls_ccm_auth_decrypt(&ctx, length, ivptr, iv_length, NULL, 0, + input, output, tagptr, tag_length)) { + + mbedtls_ccm_free(&ctx); + return -EINVAL; + } + + mbedtls_ccm_free(&ctx); + return 0; +} diff --git a/lib/crypto_backend/crypto_nettle.c b/lib/crypto_backend/crypto_nettle.c index 4c91271..3ce239a 100644 --- a/lib/crypto_backend/crypto_nettle.c +++ b/lib/crypto_backend/crypto_nettle.c @@ -2,8 +2,8 @@ /* * Nettle crypto backend implementation * - * Copyright (C) 2011-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2011-2024 Milan Broz + * Copyright (C) 2011-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2011-2025 Milan Broz */ #include @@ -298,7 +298,7 @@ int crypt_hmac_init(struct crypt_hmac **ctx, const char *name, return -ENOMEM; } - memcpy(h->key, key, key_length); + crypt_backend_memcpy(h->key, key, key_length); h->key_length = key_length; h->hash->init(&h->nettle_ctx); diff --git a/lib/crypto_backend/crypto_nss.c b/lib/crypto_backend/crypto_nss.c index 19b2202..bbf6533 100644 --- a/lib/crypto_backend/crypto_nss.c +++ b/lib/crypto_backend/crypto_nss.c @@ -2,8 +2,8 @@ /* * NSS crypto backend implementation * - * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2010-2024 Milan Broz + * Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2025 Milan Broz */ #include @@ -164,7 +164,7 @@ int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length) if (PK11_DigestFinal(ctx->md, tmp, &tmp_len, length) != SECSuccess) return -EINVAL; - memcpy(buffer, tmp, length); + crypt_backend_memcpy(buffer, tmp, length); crypt_backend_memzero(tmp, sizeof(tmp)); if (tmp_len < length) @@ -264,7 +264,7 @@ int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length) if (PK11_DigestFinal(ctx->md, tmp, &tmp_len, length) != SECSuccess) return -EINVAL; - memcpy(buffer, tmp, length); + crypt_backend_memcpy(buffer, tmp, length); crypt_backend_memzero(tmp, sizeof(tmp)); if (tmp_len < length) diff --git a/lib/crypto_backend/crypto_openssl.c b/lib/crypto_backend/crypto_openssl.c index 60e43ca..762cd71 100644 --- a/lib/crypto_backend/crypto_openssl.c +++ b/lib/crypto_backend/crypto_openssl.c @@ -2,8 +2,8 @@ /* * OPENSSL crypto backend implementation * - * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2010-2024 Milan Broz + * Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2025 Milan Broz */ #include @@ -19,6 +19,7 @@ #include #include #include +#include static OSSL_PROVIDER *ossl_legacy = NULL; static OSSL_PROVIDER *ossl_default = NULL; static OSSL_LIB_CTX *ossl_ctx = NULL; @@ -381,7 +382,7 @@ int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length) if (EVP_DigestFinal_ex(ctx->md, tmp, &tmp_len) != 1) return -EINVAL; - memcpy(buffer, tmp, length); + crypt_backend_memcpy(buffer, tmp, length); crypt_backend_memzero(tmp, sizeof(tmp)); if (tmp_len < length) @@ -510,7 +511,7 @@ int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length) HMAC_Final(ctx->md, tmp, &tmp_len); #endif - memcpy(buffer, tmp, length); + crypt_backend_memcpy(buffer, tmp, length); crypt_backend_memzero(tmp, sizeof(tmp)); if (tmp_len < length) @@ -638,6 +639,10 @@ static int openssl_argon2(const char *type, const char *password, size_t passwor EVP_KDF_CTX_free(ctx); EVP_KDF_free(argon2); + /* Memory allocation is common issue with memory-hard Argon2 */ + if (r == 0 && ERR_GET_REASON(ERR_get_error()) == ERR_R_MALLOC_FAILURE) + return -ENOMEM; + /* _derive() returns 0 or negative value on error, 1 on success */ return r == 1 ? 0 : -EINVAL; #else diff --git a/lib/crypto_backend/crypto_storage.c b/lib/crypto_backend/crypto_storage.c index 0eed502..edd4db7 100644 --- a/lib/crypto_backend/crypto_storage.c +++ b/lib/crypto_backend/crypto_storage.c @@ -3,16 +3,19 @@ * Generic wrapper for storage encryption modes and Initial Vectors * (reimplementation of some functions from Linux dm-crypt kernel) * - * Copyright (C) 2014-2024 Milan Broz + * Copyright (C) 2014-2025 Milan Broz */ +#include #include #include #include #include "bitops.h" #include "crypto_backend.h" -#define SECTOR_SHIFT 9 +#define SECTOR_SHIFT 9 +#define MAX_CAPI_LEN 64 +#define MAX_CAPI_LEN_STR "63" /* * Internal IV helper @@ -74,7 +77,7 @@ static int crypt_sector_iv_init(struct crypt_sector_iv *ctx, ctx->type = IV_PLAIN; } else if (!strncasecmp(iv_name, "essiv:", 6)) { struct crypt_hash *h = NULL; - char *hash_name = strchr(iv_name, ':'); + const char *hash_name = strchr(iv_name, ':'); int hash_size; char tmp[256]; @@ -212,36 +215,49 @@ int crypt_storage_init(struct crypt_storage **ctx, bool large_iv) { struct crypt_storage *s; - char mode_name[64]; + char cipher_name[MAX_CAPI_LEN], mode_name[MAX_CAPI_LEN], mode_tmp[MAX_CAPI_LEN]; char *cipher_iv = NULL; - int r = -EIO; + int r; if (sector_size < (1 << SECTOR_SHIFT) || sector_size > (1 << (SECTOR_SHIFT + 3)) || sector_size & (sector_size - 1)) return -EINVAL; - s = malloc(sizeof(*s)); - if (!s) - return -ENOMEM; - memset(s, 0, sizeof(*s)); + /* Convert from capi mode */ + if (!strncmp(cipher, "capi:", 5)) { + r = sscanf(cipher, "capi:%" MAX_CAPI_LEN_STR "[^(](%" MAX_CAPI_LEN_STR "[^)])", mode_tmp, cipher_name); + if (r != 2) + return -EINVAL; + r = snprintf(mode_name, sizeof(mode_name), "%s-%s", mode_tmp, cipher_mode); + if (r < 0 || (size_t)r >= sizeof(mode_name)) + return -EINVAL; + } else { + strncpy(cipher_name, cipher, sizeof(cipher_name)); + cipher_name[sizeof(cipher_name) - 1] = 0; + strncpy(mode_name, cipher_mode, sizeof(mode_name)); + mode_name[sizeof(mode_name) - 1] = 0; + } /* Remove IV if present */ - strncpy(mode_name, cipher_mode, sizeof(mode_name)); - mode_name[sizeof(mode_name) - 1] = 0; cipher_iv = strchr(mode_name, '-'); if (cipher_iv) { *cipher_iv = '\0'; cipher_iv++; } - r = crypt_cipher_init(&s->cipher, cipher, mode_name, key, key_length); + s = malloc(sizeof(*s)); + if (!s) + return -ENOMEM; + memset(s, 0, sizeof(*s)); + + r = crypt_cipher_init(&s->cipher, cipher_name, mode_name, key, key_length); if (r) { crypt_storage_destroy(s); return r; } - r = crypt_sector_iv_init(&s->cipher_iv, cipher, mode_name, cipher_iv, key, key_length, sector_size); + r = crypt_sector_iv_init(&s->cipher_iv, cipher_name, mode_name, cipher_iv, key, key_length, sector_size); if (r) { crypt_storage_destroy(s); return r; diff --git a/lib/crypto_backend/memutils.c b/lib/crypto_backend/memutils.c new file mode 100644 index 0000000..a041b3e --- /dev/null +++ b/lib/crypto_backend/memutils.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Safe memory utilities + * + * Copyright (C) 2024-2025 Milan Broz + */ + +#include "crypto_backend_internal.h" + +#define ATTR_NOINLINE __attribute__ ((noinline)) +#define ATTR_ZERO_REGS +#if HAVE_ATTRIBUTE_ZEROCALLUSEDREGS +# undef ATTR_ZERO_REGS +# define ATTR_ZERO_REGS __attribute__ ((zero_call_used_regs("used"))) +#endif + +/* Workaround for https://github.com/google/sanitizers/issues/1507 */ +#if defined __has_feature +# if __has_feature (memory_sanitizer) +# undef HAVE_EXPLICIT_BZERO +# endif +#endif + +/* Memzero helper (memset on stack can be optimized out) */ +ATTR_NOINLINE ATTR_ZERO_REGS +void crypt_backend_memzero(void *s, size_t n) +{ +#if HAVE_EXPLICIT_BZERO + explicit_bzero(s, n); +#else + volatile uint8_t *p = (volatile uint8_t *)s; + while(n--) *p++ = 0; +#endif +} + +/* Memcpy helper to avoid spilling sensitive data through additional registers */ +ATTR_NOINLINE ATTR_ZERO_REGS +void *crypt_backend_memcpy(void *dst, const void *src, size_t n) +{ + volatile uint8_t *d = (volatile uint8_t *)dst; + const volatile uint8_t *s = (const volatile uint8_t *)src; + + while(n--) *d++ = *s++; + + return dst; +} + +/* Internal implementation for constant time memory comparison */ +ATTR_NOINLINE ATTR_ZERO_REGS +int crypt_internal_memeq(const void *m1, const void *m2, size_t n) +{ + const unsigned char *_m1 = (const unsigned char *) m1; + const unsigned char *_m2 = (const unsigned char *) m2; + unsigned char result = 0; + size_t i; + + for (i = 0; i < n; i++) + result |= _m1[i] ^ _m2[i]; + + return result; +} diff --git a/lib/crypto_backend/meson.build b/lib/crypto_backend/meson.build index d6c31fd..43cf40e 100644 --- a/lib/crypto_backend/meson.build +++ b/lib/crypto_backend/meson.build @@ -11,6 +11,7 @@ libcrypto_backend_link_with = [] libcrypto_backend_sources = files( 'argon2_generic.c', 'base64.c', + 'memutils.c', 'cipher_check.c', 'cipher_generic.c', 'crc32.c', diff --git a/lib/crypto_backend/pbkdf2_generic.c b/lib/crypto_backend/pbkdf2_generic.c index cebaea4..e5e5a9c 100644 --- a/lib/crypto_backend/pbkdf2_generic.c +++ b/lib/crypto_backend/pbkdf2_generic.c @@ -5,8 +5,8 @@ * Copyright (C) 2004 Free Software Foundation * * cryptsetup related changes - * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2012-2024 Milan Broz + * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Milan Broz */ #include diff --git a/lib/crypto_backend/pbkdf_check.c b/lib/crypto_backend/pbkdf_check.c index d5e1257..5eb21f3 100644 --- a/lib/crypto_backend/pbkdf_check.c +++ b/lib/crypto_backend/pbkdf_check.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: LGPL-2.1-or-later /* * PBKDF performance check - * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2012-2024 Milan Broz + * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Milan Broz * Copyright (C) 2016-2020 Ondrej Mosnacek */ @@ -300,6 +300,7 @@ static int crypt_argon2_check(const char *kdf, const char *password, } while (ms < ms_atleast || ms > ms_atmost); out: if (key) { + /* Key can be derived from a real provided password */ crypt_backend_memzero(key, key_length); free(key); } @@ -381,6 +382,7 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash, } out: if (key) { + /* Key can be derived from a real provided password */ crypt_backend_memzero(key, key_length); free(key); } @@ -406,6 +408,9 @@ int crypt_pbkdf_perf(const char *kdf, const char *hash, if (r < 0) return r; + if (parallel_threads > pbkdf_limits.max_parallel) + return -EINVAL; + min_memory = pbkdf_limits.min_bench_memory; if (min_memory > max_memory_kb) min_memory = max_memory_kb; diff --git a/lib/crypto_backend/utf8.c b/lib/crypto_backend/utf8.c index 07a45eb..c68d1b7 100644 --- a/lib/crypto_backend/utf8.c +++ b/lib/crypto_backend/utf8.c @@ -5,7 +5,7 @@ * Copyright (C) 2010 Lennart Poettering * * cryptsetup related changes - * Copyright (C) 2021-2024 Vojtech Trefny + * Copyright (C) 2021-2025 Vojtech Trefny * Parts of the original systemd implementation are based on the GLIB utf8 * validation functions. @@ -274,3 +274,20 @@ int crypt_utf8_to_utf16(char16_t **out, const char *s, size_t length) *p = 0; return 0; } + +/** + * crypt_char16_strlen() + * @s: string to get length of + * + * Returns: number of 16-bit words in the string + */ +size_t crypt_char16_strlen(const char16_t *s) { + size_t n = 0; + + assert(s); + + while (*s != 0) + n++, s++; + + return n; +} diff --git a/lib/fvault2/fvault2.c b/lib/fvault2/fvault2.c index f55f2c4..59f4570 100644 --- a/lib/fvault2/fvault2.c +++ b/lib/fvault2/fvault2.c @@ -513,6 +513,7 @@ static int _read_volume_header( int r = 0; struct device *dev = crypt_metadata_device(cd); struct volume_header *vol_header = NULL; + void *enc_key = NULL; assert(sizeof(*vol_header) == FVAULT2_VOL_HEADER_SIZE); @@ -557,8 +558,8 @@ static int _read_volume_header( goto out; } - *enc_md_key = crypt_alloc_volume_key(FVAULT2_XTS_KEY_SIZE, NULL); - if (*enc_md_key == NULL) { + enc_key = crypt_safe_alloc(FVAULT2_XTS_KEY_SIZE); + if (!enc_key) { r = -ENOMEM; goto out; } @@ -566,9 +567,15 @@ static int _read_volume_header( *block_size = le32_to_cpu(vol_header->block_size); *disklbl_blkoff = le64_to_cpu(vol_header->disklbl_blkoff); uuid_unparse(vol_header->ph_vol_uuid, ph_vol_uuid); - memcpy((*enc_md_key)->key, vol_header->key_data, FVAULT2_AES_KEY_SIZE); - memcpy((*enc_md_key)->key + FVAULT2_AES_KEY_SIZE, + crypt_safe_memcpy(enc_key, vol_header->key_data, FVAULT2_AES_KEY_SIZE); + crypt_safe_memcpy((char *)enc_key + FVAULT2_AES_KEY_SIZE, vol_header->ph_vol_uuid, FVAULT2_AES_KEY_SIZE); + + *enc_md_key = crypt_alloc_volume_key_by_safe_alloc(&enc_key); + if (*enc_md_key == NULL) { + crypt_safe_free(enc_key); + r = -ENOMEM; + } out: free(vol_header); return r; @@ -704,7 +711,7 @@ static int _read_encrypted_metadata( goto out; } - r = crypt_cipher_init(&cipher, "aes", "xts", key->key, FVAULT2_XTS_KEY_SIZE); + r = crypt_cipher_init(&cipher, "aes", "xts", crypt_volume_key_get_key(key), FVAULT2_XTS_KEY_SIZE); if (r < 0) goto out; @@ -835,8 +842,7 @@ static int _activate( r = dm_crypt_target_set(&dm_dev.segment, 0, dm_dev.size, crypt_data_device(cd), vol_key, cipher, crypt_get_iv_offset(cd), crypt_get_data_offset(cd), - crypt_get_integrity(cd), crypt_get_integrity_tag_size(cd), - crypt_get_sector_size(cd)); + NULL, 0, 0, crypt_get_sector_size(cd)); if (!r) r = dm_create_device(cd, name, CRYPT_FVAULT2, &dm_dev); @@ -893,15 +899,14 @@ int FVAULT2_get_volume_key( const char *passphrase, size_t passphrase_len, const struct fvault2_params *params, - struct volume_key **vol_key) + struct volume_key **r_vol_key) { int r = 0; uint8_t family_uuid_bin[FVAULT2_UUID_BIN_SIZE]; - struct volume_key *passphrase_key = NULL; - struct volume_key *kek = NULL; struct crypt_hash *hash = NULL; + void *passphrase_key = NULL, *kek = NULL, *vol_key= NULL; - *vol_key = NULL; + *r_vol_key = NULL; if (uuid_parse(params->family_uuid, family_uuid_bin) < 0) { log_dbg(cd, "Could not parse logical volume family UUID: %s.", @@ -910,61 +915,62 @@ int FVAULT2_get_volume_key( goto out; } - passphrase_key = crypt_alloc_volume_key(FVAULT2_AES_KEY_SIZE, NULL); + passphrase_key = crypt_safe_alloc(FVAULT2_AES_KEY_SIZE); if (passphrase_key == NULL) { r = -ENOMEM; goto out; } r = crypt_pbkdf("pbkdf2", "sha256", passphrase, passphrase_len, - params->pbkdf2_salt, FVAULT2_PBKDF2_SALT_SIZE, passphrase_key->key, + params->pbkdf2_salt, FVAULT2_PBKDF2_SALT_SIZE, passphrase_key, FVAULT2_AES_KEY_SIZE, params->pbkdf2_iters, 0, 0); if (r < 0) goto out; - kek = crypt_alloc_volume_key(FVAULT2_AES_KEY_SIZE, NULL); + kek = crypt_safe_alloc(FVAULT2_AES_KEY_SIZE); if (kek == NULL) { r = -ENOMEM; goto out; } - r = _unwrap_key(passphrase_key->key, FVAULT2_AES_KEY_SIZE, params->wrapped_kek, - FVAULT2_WRAPPED_KEY_SIZE, kek->key, FVAULT2_AES_KEY_SIZE); + r = _unwrap_key(passphrase_key, FVAULT2_AES_KEY_SIZE, params->wrapped_kek, + FVAULT2_WRAPPED_KEY_SIZE, kek, FVAULT2_AES_KEY_SIZE); if (r < 0) goto out; - *vol_key = crypt_alloc_volume_key(FVAULT2_XTS_KEY_SIZE, NULL); - if (*vol_key == NULL) { + vol_key = crypt_safe_alloc(FVAULT2_XTS_KEY_SIZE); + if (vol_key == NULL) { r = -ENOMEM; goto out; } - r = _unwrap_key(kek->key, FVAULT2_AES_KEY_SIZE, params->wrapped_vk, - FVAULT2_WRAPPED_KEY_SIZE, (*vol_key)->key, FVAULT2_AES_KEY_SIZE); + r = _unwrap_key(kek, FVAULT2_AES_KEY_SIZE, params->wrapped_vk, + FVAULT2_WRAPPED_KEY_SIZE, vol_key, FVAULT2_AES_KEY_SIZE); if (r < 0) goto out; r = crypt_hash_init(&hash, "sha256"); if (r < 0) goto out; - r = crypt_hash_write(hash, (*vol_key)->key, FVAULT2_AES_KEY_SIZE); + r = crypt_hash_write(hash, vol_key, FVAULT2_AES_KEY_SIZE); if (r < 0) goto out; r = crypt_hash_write(hash, (char *)family_uuid_bin, FVAULT2_UUID_BIN_SIZE); if (r < 0) goto out; - r = crypt_hash_final(hash, (*vol_key)->key + FVAULT2_AES_KEY_SIZE, + r = crypt_hash_final(hash, (char *)vol_key + FVAULT2_AES_KEY_SIZE, FVAULT2_AES_KEY_SIZE); if (r < 0) goto out; + + *r_vol_key = crypt_alloc_volume_key_by_safe_alloc(&vol_key); + if (!*r_vol_key) + r = -ENOMEM; out: - crypt_free_volume_key(passphrase_key); - crypt_free_volume_key(kek); - if (r < 0) { - crypt_free_volume_key(*vol_key); - *vol_key = NULL; - } + crypt_safe_free(passphrase_key); + crypt_safe_free(kek); + crypt_safe_free(vol_key); if (hash != NULL) crypt_hash_destroy(hash); return r; @@ -997,48 +1003,19 @@ int FVAULT2_dump( return 0; } -int FVAULT2_activate_by_passphrase( +int FVAULT2_activate_by_volume_key( struct crypt_device *cd, const char *name, - const char *passphrase, - size_t passphrase_len, + struct volume_key *vk, const struct fvault2_params *params, uint32_t flags) { - int r; - struct volume_key *vol_key = NULL; - - r = FVAULT2_get_volume_key(cd, passphrase, passphrase_len, params, &vol_key); - if (r < 0) - return r; + assert(crypt_volume_key_length(vk) == FVAULT2_XTS_KEY_SIZE); - if (name) - r = _activate(cd, name, vol_key, params, flags); - - crypt_free_volume_key(vol_key); - return r; + return _activate(cd, name, vk, params, flags); } -int FVAULT2_activate_by_volume_key( - struct crypt_device *cd, - const char *name, - const char *key, - size_t key_size, - const struct fvault2_params *params, - uint32_t flags) +size_t FVAULT2_volume_key_size(void) { - int r = 0; - struct volume_key *vol_key = NULL; - - if (key_size != FVAULT2_XTS_KEY_SIZE) - return -EINVAL; - - vol_key = crypt_alloc_volume_key(FVAULT2_XTS_KEY_SIZE, key); - if (vol_key == NULL) - return -ENOMEM; - - r = _activate(cd, name, vol_key, params, flags); - - crypt_free_volume_key(vol_key); - return r; + return FVAULT2_XTS_KEY_SIZE; } diff --git a/lib/fvault2/fvault2.h b/lib/fvault2/fvault2.h index 8a03280..ad6ad5e 100644 --- a/lib/fvault2/fvault2.h +++ b/lib/fvault2/fvault2.h @@ -41,27 +41,20 @@ int FVAULT2_get_volume_key( const char *passphrase, size_t passphrase_len, const struct fvault2_params *params, - struct volume_key **vol_key); + struct volume_key **r_vol_key); int FVAULT2_dump( struct crypt_device *cd, struct device *device, const struct fvault2_params *params); -int FVAULT2_activate_by_passphrase( - struct crypt_device *cd, - const char *name, - const char *passphrase, - size_t passphrase_len, - const struct fvault2_params *params, - uint32_t flags); - int FVAULT2_activate_by_volume_key( struct crypt_device *cd, const char *name, - const char *key, - size_t key_size, + struct volume_key *vk, const struct fvault2_params *params, uint32_t flags); +size_t FVAULT2_volume_key_size(void); + #endif diff --git a/lib/integrity/integrity.c b/lib/integrity/integrity.c index 37d0737..a10050d 100644 --- a/lib/integrity/integrity.c +++ b/lib/integrity/integrity.c @@ -2,7 +2,7 @@ /* * Integrity volume handling * - * Copyright (C) 2016-2024 Milan Broz + * Copyright (C) 2016-2025 Milan Broz */ #include @@ -31,16 +31,22 @@ static int INTEGRITY_read_superblock(struct crypt_device *cd, { int devfd, r; + log_dbg(cd, "Reading kernel dm-integrity metadata on %s.", device_path(device)); + devfd = device_open(cd, device, O_RDONLY); if(devfd < 0) return -EINVAL; if (read_lseek_blockwise(devfd, device_block_size(cd, device), - device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb) || - memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic))) { + device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb)) { + log_dbg(cd, "Cannot read kernel dm-integrity metadata on %s.", device_path(device)); + return -EINVAL; + } + + if (memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic))) { log_dbg(cd, "No kernel dm-integrity metadata detected on %s.", device_path(device)); r = -EINVAL; - } else if (sb->version < SB_VERSION_1 || sb->version > SB_VERSION_5) { + } else if (sb->version < SB_VERSION_1 || sb->version > SB_VERSION_6) { log_err(cd, _("Incompatible kernel dm-integrity metadata (version %u) detected on %s."), sb->version, device_path(device)); r = -EINVAL; @@ -67,8 +73,10 @@ int INTEGRITY_read_sb(struct crypt_device *cd, if (r) return r; - params->sector_size = SECTOR_SIZE << sb.log2_sectors_per_block; - params->tag_size = sb.integrity_tag_size; + if (params) { + params->sector_size = SECTOR_SIZE << sb.log2_sectors_per_block; + params->tag_size = sb.integrity_tag_size; + } if (flags) *flags = sb.flags; @@ -79,28 +87,32 @@ int INTEGRITY_read_sb(struct crypt_device *cd, int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offset) { struct superblock sb; + uint64_t sector_size; int r; r = INTEGRITY_read_superblock(cd, device, offset, &sb); if (r) return r; - log_std(cd, "Info for integrity device %s.\n", device_path(device)); - log_std(cd, "superblock_version %d\n", (unsigned)sb.version); - log_std(cd, "log2_interleave_sectors %d\n", sb.log2_interleave_sectors); - log_std(cd, "integrity_tag_size %u\n", sb.integrity_tag_size); - log_std(cd, "journal_sections %u\n", sb.journal_sections); - log_std(cd, "provided_data_sectors %" PRIu64 "\n", sb.provided_data_sectors); - log_std(cd, "sector_size %u\n", SECTOR_SIZE << sb.log2_sectors_per_block); + sector_size = (uint64_t)SECTOR_SIZE << sb.log2_sectors_per_block; + log_std(cd, "INTEGRITY header information for %s.\n", device_path(device)); + log_std(cd, "version: %d\n", (unsigned)sb.version); + log_std(cd, "tag size: %u [bytes]\n", sb.integrity_tag_size); + log_std(cd, "sector size: %" PRIu64 " [bytes]\n", sector_size); + log_std(cd, "data size: %" PRIu64 " [512-byte units] (%" PRIu64 " [bytes])\n", + sb.provided_data_sectors, sb.provided_data_sectors * SECTOR_SIZE); if (sb.version >= SB_VERSION_2 && (sb.flags & SB_FLAG_RECALCULATING)) - log_std(cd, "recalc_sector %" PRIu64 "\n", sb.recalc_sector); - log_std(cd, "log2_blocks_per_bitmap %u\n", sb.log2_blocks_per_bitmap_bit); - log_std(cd, "flags %s%s%s%s%s\n", + log_std(cd, "recalculate sector: %" PRIu64 "\n", sb.recalc_sector); + log_std(cd, "journal sections: %u\n", sb.journal_sections); + log_std(cd, "log2 interleave sectors: %d\n", sb.log2_interleave_sectors); + log_std(cd, "log2 blocks per bitmap: %u\n", sb.log2_blocks_per_bitmap_bit); + log_std(cd, "flags: %s%s%s%s%s%s\n", sb.flags & SB_FLAG_HAVE_JOURNAL_MAC ? "have_journal_mac " : "", sb.flags & SB_FLAG_RECALCULATING ? "recalculating " : "", sb.flags & SB_FLAG_DIRTY_BITMAP ? "dirty_bitmap " : "", sb.flags & SB_FLAG_FIXED_PADDING ? "fix_padding " : "", - sb.flags & SB_FLAG_FIXED_HMAC ? "fix_hmac " : ""); + sb.flags & SB_FLAG_FIXED_HMAC ? "fix_hmac " : "", + sb.flags & SB_FLAG_INLINE ? "inline " : ""); return 0; } @@ -120,26 +132,42 @@ int INTEGRITY_data_sectors(struct crypt_device *cd, return 0; } -int INTEGRITY_key_size(const char *integrity) +int INTEGRITY_key_size(const char *integrity, int required_key_size) { + int ks = 0; + + if (!integrity && required_key_size) + return -EINVAL; + if (!integrity) return 0; //FIXME: use crypto backend hash size if (!strcmp(integrity, "aead")) - return 0; + ks = 0; else if (!strcmp(integrity, "hmac(sha1)")) - return 20; + ks = required_key_size ?: 20; else if (!strcmp(integrity, "hmac(sha256)")) - return 32; + ks = required_key_size ?: 32; else if (!strcmp(integrity, "hmac(sha512)")) - return 64; + ks = required_key_size ?: 64; + else if (!strcmp(integrity, "phmac(sha1)")) + ks = required_key_size ?: -EINVAL; + else if (!strcmp(integrity, "phmac(sha256)")) + ks = required_key_size ?: -EINVAL; + else if (!strcmp(integrity, "phmac(sha512)")) + ks = required_key_size ?: -EINVAL; else if (!strcmp(integrity, "poly1305")) - return 0; + ks = 0; else if (!strcmp(integrity, "none")) - return 0; + ks = 0; + else + return -EINVAL; - return -EINVAL; + if (required_key_size && ks != required_key_size) + return -EINVAL; + + return ks; } /* Return hash or hmac(hash) size, if known */ @@ -158,6 +186,8 @@ int INTEGRITY_hash_tag_size(const char *integrity) return 8; r = sscanf(integrity, "hmac(%" MAX_CIPHER_LEN_STR "[^)]s", hash); + if (r != 1) + r = sscanf(integrity, "phmac(%" MAX_CIPHER_LEN_STR "[^)]s", hash); if (r == 1) r = crypt_hash_size(hash); else @@ -200,6 +230,12 @@ int INTEGRITY_tag_size(const char *integrity, auth_tag_size = 32; else if (!strcmp(integrity, "hmac(sha512)")) auth_tag_size = 64; + else if (!strcmp(integrity, "phmac(sha1)")) + auth_tag_size = 20; + else if (!strcmp(integrity, "phmac(sha256)")) + auth_tag_size = 32; + else if (!strcmp(integrity, "phmac(sha512)")) + auth_tag_size = 64; else if (!strcmp(integrity, "poly1305")) { if (iv_tag_size) iv_tag_size = 12; @@ -230,6 +266,9 @@ int INTEGRITY_create_dmd_device(struct crypt_device *cd, if (sb_flags & SB_FLAG_RECALCULATING) dmd->flags |= CRYPT_ACTIVATE_RECALCULATE; + if (sb_flags & SB_FLAG_INLINE) + dmd->flags |= (CRYPT_ACTIVATE_NO_JOURNAL | CRYPT_ACTIVATE_INLINE_MODE); + r = INTEGRITY_data_sectors(cd, INTEGRITY_metadata_device(cd), crypt_get_data_offset(cd) * SECTOR_SIZE, &dmd->size); if (r < 0) @@ -249,14 +288,15 @@ int INTEGRITY_activate_dmd_device(struct crypt_device *cd, uint32_t sb_flags) { int r; - uint32_t dmi_flags; + uint64_t dmi_flags; struct dm_target *tgt = &dmd->segment; if (!single_segment(dmd) || tgt->type != DM_INTEGRITY) return -EINVAL; - log_dbg(cd, "Trying to activate INTEGRITY device on top of %s, using name %s, tag size %d, provided sectors %" PRIu64".", - device_path(tgt->data_device), name, tgt->u.integrity.tag_size, dmd->size); + log_dbg(cd, "Trying to activate INTEGRITY device on top of %s, using name %s, tag size %d%s, provided sectors %" PRIu64".", + device_path(tgt->data_device), name, tgt->u.integrity.tag_size, + (sb_flags & SB_FLAG_INLINE) ? " (inline)" :"", dmd->size); r = create_or_reload_device(cd, name, type, dmd); @@ -280,6 +320,12 @@ int INTEGRITY_activate_dmd_device(struct crypt_device *cd, return -ENOTSUP; } + if (r < 0 && (sb_flags & SB_FLAG_INLINE) && !dm_flags(cd, DM_INTEGRITY, &dmi_flags) && + !(dmi_flags & DM_INTEGRITY_INLINE_MODE_SUPPORTED)) { + log_err(cd, _("Kernel does not support dm-integrity inline mode.")); + return -ENOTSUP; + } + return r; } @@ -372,11 +418,14 @@ static int _create_reduced_device(struct crypt_device *cd, int INTEGRITY_format(struct crypt_device *cd, const struct crypt_params_integrity *params, + struct volume_key *integrity_key, struct volume_key *journal_crypt_key, struct volume_key *journal_mac_key, - uint64_t backing_device_sectors) + uint64_t backing_device_sectors, + uint32_t *sb_flags, + bool integrity_inline) { - uint32_t dmi_flags; + uint64_t dmi_flags; char reduced_device_name[70], tmp_name[64], tmp_uuid[40]; struct crypt_dm_active_device dmdi = { .size = 8, @@ -387,7 +436,6 @@ int INTEGRITY_format(struct crypt_device *cd, uuid_t tmp_uuid_bin; uint64_t data_offset_sectors; struct device *p_metadata_device, *p_data_device, *reduced_device = NULL; - struct volume_key *vk = NULL; uuid_generate(tmp_uuid_bin); uuid_unparse(tmp_uuid_bin, tmp_uuid); @@ -422,19 +470,18 @@ int INTEGRITY_format(struct crypt_device *cd, p_data_device = crypt_data_device(cd); } - /* There is no data area, we can actually use fake zeroed key */ - if (params && params->integrity_key_size) - vk = crypt_alloc_volume_key(params->integrity_key_size, NULL); + if (integrity_inline) + dmdi.flags |= (CRYPT_ACTIVATE_NO_JOURNAL | CRYPT_ACTIVATE_INLINE_MODE); r = dm_integrity_target_set(cd, tgt, 0, dmdi.size, p_metadata_device, p_data_device, crypt_get_integrity_tag_size(cd), - data_offset_sectors, crypt_get_sector_size(cd), vk, + data_offset_sectors, crypt_get_sector_size(cd), integrity_key, journal_crypt_key, journal_mac_key, params); if (r < 0) goto err; - log_dbg(cd, "Trying to format INTEGRITY device on top of %s, tmp name %s, tag size %d.", - device_path(tgt->data_device), tmp_name, tgt->u.integrity.tag_size); + log_dbg(cd, "Trying to format INTEGRITY device on top of %s, tmp name %s, tag size %d%s.", + device_path(tgt->data_device), tmp_name, tgt->u.integrity.tag_size, integrity_inline ? " (inline)" : ""); r = device_block_adjust(cd, tgt->data_device, DEV_EXCL, tgt->u.integrity.offset, NULL, NULL); if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) { @@ -455,9 +502,14 @@ int INTEGRITY_format(struct crypt_device *cd, goto err; r = dm_remove_device(cd, tmp_name, CRYPT_DEACTIVATE_FORCE); + if (r) + goto err; + + /* reload sb_flags from superblock (important for SB_FLAG_INLINE) */ + if (sb_flags) + r = INTEGRITY_read_sb(cd, NULL, sb_flags); err: dm_targets_free(cd, &dmdi); - crypt_free_volume_key(vk); if (reduced_device) { dm_remove_device(cd, reduced_device_name, CRYPT_DEACTIVATE_FORCE); device_free(cd, reduced_device); diff --git a/lib/integrity/integrity.h b/lib/integrity/integrity.h index 049a299..ff1293f 100644 --- a/lib/integrity/integrity.h +++ b/lib/integrity/integrity.h @@ -2,13 +2,14 @@ /* * Integrity header definition * - * Copyright (C) 2016-2024 Milan Broz + * Copyright (C) 2016-2025 Milan Broz */ #ifndef _CRYPTSETUP_INTEGRITY_H #define _CRYPTSETUP_INTEGRITY_H #include +#include struct crypt_device; struct device; @@ -23,12 +24,14 @@ struct crypt_dm_active_device; #define SB_VERSION_3 3 #define SB_VERSION_4 4 #define SB_VERSION_5 5 +#define SB_VERSION_6 6 #define SB_FLAG_HAVE_JOURNAL_MAC (1 << 0) #define SB_FLAG_RECALCULATING (1 << 1) /* V2 only */ #define SB_FLAG_DIRTY_BITMAP (1 << 2) /* V3 only */ #define SB_FLAG_FIXED_PADDING (1 << 3) /* V4 only */ #define SB_FLAG_FIXED_HMAC (1 << 4) /* V5 only */ +#define SB_FLAG_INLINE (1 << 5) /* V6 only */ struct superblock { uint8_t magic[8]; @@ -40,8 +43,10 @@ struct superblock { uint32_t flags; uint8_t log2_sectors_per_block; uint8_t log2_blocks_per_bitmap_bit; /* V3 only */ - uint8_t pad[2]; + uint8_t pad[2]; /* (padding) */ uint64_t recalc_sector; /* V2 only */ + uint8_t pad2[8]; /* (padding) */ + uint8_t salt[16]; /* for fixed hmac, V5 only */ } __attribute__ ((packed)); int INTEGRITY_read_sb(struct crypt_device *cd, @@ -53,7 +58,7 @@ int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offs int INTEGRITY_data_sectors(struct crypt_device *cd, struct device *device, uint64_t offset, uint64_t *data_sectors); -int INTEGRITY_key_size(const char *integrity); +int INTEGRITY_key_size(const char *integrity, int required_key_size); int INTEGRITY_tag_size(const char *integrity, const char *cipher, const char *cipher_mode); @@ -61,9 +66,12 @@ int INTEGRITY_hash_tag_size(const char *integrity); int INTEGRITY_format(struct crypt_device *cd, const struct crypt_params_integrity *params, + struct volume_key *integrity_key, struct volume_key *journal_crypt_key, struct volume_key *journal_mac_key, - uint64_t backing_device_sectors); + uint64_t backing_device_sectors, + uint32_t *sb_flags, + bool integrity_inline); int INTEGRITY_activate(struct crypt_device *cd, const char *name, diff --git a/lib/internal.h b/lib/internal.h index 765bf15..56efa46 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #ifndef INTERNAL_H @@ -48,24 +48,36 @@ struct crypt_device; struct luks2_reencrypt; +struct volume_key; -struct volume_key { - int id; - size_t keylength; - const char *key_description; - struct volume_key *next; - char key[]; -}; +typedef enum { + KEY_QUALITY_KEY = 0, + KEY_QUALITY_NORMAL, + KEY_QUALITY_EMPTY +} key_quality_info; struct volume_key *crypt_alloc_volume_key(size_t keylength, const char *key); -struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, size_t keylength); +struct volume_key *crypt_alloc_volume_key_by_safe_alloc(void **safe_alloc); +struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, size_t keylength, + key_quality_info quality); void crypt_free_volume_key(struct volume_key *vk); -int crypt_volume_key_set_description(struct volume_key *key, const char *key_description); +const char *crypt_volume_key_get_key(const struct volume_key *vk); +size_t crypt_volume_key_length(const struct volume_key *vk); +int crypt_volume_key_set_description(struct volume_key *key, + const char *key_description, key_type_t keyring_key_type); +int crypt_volume_key_set_description_by_name(struct volume_key *vk, const char *key_name); +key_type_t crypt_volume_key_kernel_key_type(const struct volume_key *vk); +const char *crypt_volume_key_description(const struct volume_key *vk); void crypt_volume_key_set_id(struct volume_key *vk, int id); int crypt_volume_key_get_id(const struct volume_key *vk); void crypt_volume_key_add_next(struct volume_key **vks, struct volume_key *vk); struct volume_key *crypt_volume_key_next(struct volume_key *vk); struct volume_key *crypt_volume_key_by_id(struct volume_key *vk, int id); +void crypt_volume_key_pass_safe_alloc(struct volume_key *vk, void **safe_alloc); +bool crypt_volume_key_is_set(const struct volume_key *vk); +bool crypt_volume_key_upload_kernel_key(struct volume_key *vk); +void crypt_volume_key_drop_uploaded_kernel_key(struct crypt_device *cd, struct volume_key *vk); +void crypt_volume_key_drop_kernel_key(struct crypt_device *cd, struct volume_key *vk); struct crypt_pbkdf_type *crypt_get_pbkdf(struct crypt_device *cd); int init_pbkdf_type(struct crypt_device *cd, @@ -86,7 +98,6 @@ int device_alloc_no_check(struct device **device, const char *path); void device_close(struct crypt_device *cd, struct device *device); void device_free(struct crypt_device *cd, struct device *device); const char *device_path(const struct device *device); -const char *device_dm_name(const struct device *device); const char *device_block_path(const struct device *device); void device_topology_alignment(struct crypt_device *cd, struct device *device, @@ -104,6 +115,7 @@ int device_is_identical(struct device *device1, struct device *device2); int device_is_rotational(struct device *device); int device_is_dax(struct device *device); int device_is_zoned(struct device *device); +int device_is_nop_dif(struct device *device, uint32_t *tag_size); size_t device_alignment(struct device *device); int device_direct_io(const struct device *device); int device_fallocate(struct device *device, uint64_t size); @@ -155,6 +167,7 @@ char *crypt_lookup_dev(const char *dev_id); int crypt_dev_is_rotational(int major, int minor); int crypt_dev_is_dax(int major, int minor); int crypt_dev_is_zoned(int major, int minor); +int crypt_dev_is_nop_dif(int major, int minor, uint32_t *tag_size); int crypt_dev_is_partition(const char *dev_path); char *crypt_get_partition_device(const char *dev_path, uint64_t offset, uint64_t size); int crypt_dev_get_partition_number(const char *dev_path); @@ -162,8 +175,6 @@ char *crypt_get_base_device(const char *dev_path); uint64_t crypt_dev_partition_offset(const char *dev_path); int lookup_by_disk_id(const char *dm_uuid); int lookup_by_sysfs_uuid_field(const char *dm_uuid); -int crypt_uuid_cmp(const char *dm_uuid, const char *hdr_uuid); -int crypt_uuid_type_cmp(const char *dm_uuid, const char *type); size_t crypt_getpagesize(void); unsigned crypt_cpusonline(void); @@ -217,7 +228,7 @@ int crypt_wipe_device(struct crypt_device *cd, /* Internal integrity helpers */ const char *crypt_get_integrity(struct crypt_device *cd); -int crypt_get_integrity_key_size(struct crypt_device *cd); +int crypt_get_integrity_key_size(struct crypt_device *cd, bool dm_compat); int crypt_get_integrity_tag_size(struct crypt_device *cd); int crypt_key_in_keyring(struct crypt_device *cd); @@ -231,9 +242,18 @@ int crypt_keyring_get_key_by_name(struct crypt_device *cd, const char *key_description, char **key, size_t *key_size); + +int crypt_keyring_get_keysize_by_name(struct crypt_device *cd, + const char *key_description, + size_t *r_key_size); + int crypt_use_keyring_for_vk(struct crypt_device *cd); -void crypt_drop_keyring_key_by_description(struct crypt_device *cd, const char *key_description, key_type_t ktype); -void crypt_drop_keyring_key(struct crypt_device *cd, struct volume_key *vks); +void crypt_unlink_key_from_thread_keyring(struct crypt_device *cd, + key_serial_t key_id); +void crypt_unlink_key_by_description_from_thread_keyring(struct crypt_device *cd, + const char *key_description, + key_type_t ktype); +void crypt_drop_uploaded_keyring_key(struct crypt_device *cd, struct volume_key *vks); static inline uint64_t compact_version(uint16_t major, uint16_t minor, uint16_t patch, uint16_t release) { @@ -266,4 +286,6 @@ static inline bool uint64_mult_overflow(uint64_t *u, uint64_t b, size_t size) #define KEY_EXTERNAL_VERIFICATION -1 #define KEY_VERIFIED 0 +size_t crypt_safe_alloc_size(const void *data); + #endif /* INTERNAL_H */ diff --git a/lib/keyslot_context.c b/lib/keyslot_context.c index 5d8852f..1b13782 100644 --- a/lib/keyslot_context.c +++ b/lib/keyslot_context.c @@ -2,12 +2,14 @@ /* * LUKS - Linux Unified Key Setup, keyslot unlock helpers * - * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2022-2024 Ondrej Kozina + * Copyright (C) 2022-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2025 Ondrej Kozina */ #include +#include "bitlk/bitlk.h" +#include "fvault2/fvault2.h" #include "luks1/luks.h" #include "luks2/luks2.h" #include "keyslot_context.h" @@ -58,6 +60,44 @@ static int get_luks2_volume_key_by_passphrase(struct crypt_device *cd, return get_luks2_key_by_passphrase(cd, kc, keyslot, CRYPT_DEFAULT_SEGMENT, r_vk); } +static int get_bitlk_volume_key_by_passphrase(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + const struct bitlk_metadata *params, + struct volume_key **r_vk) +{ + int r; + + assert(cd); + assert(kc && kc->type == CRYPT_KC_TYPE_PASSPHRASE); + assert(params); + assert(r_vk); + + r = BITLK_get_volume_key(cd, kc->u.p.passphrase, kc->u.p.passphrase_size, params, r_vk); + if (r < 0) + kc->error = r; + + return r; +} + +static int get_fvault2_volume_key_by_passphrase(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + const struct fvault2_params *params, + struct volume_key **r_vk) +{ + int r; + + assert(cd); + assert(kc && kc->type == CRYPT_KC_TYPE_PASSPHRASE); + assert(params); + assert(r_vk); + + r = FVAULT2_get_volume_key(cd, kc->u.p.passphrase, kc->u.p.passphrase_size, params, r_vk); + if (r < 0) + kc->error = r; + + return r; +} + static int get_passphrase_by_passphrase(struct crypt_device *cd, struct crypt_keyslot_context *kc, const char **r_passphrase, @@ -160,6 +200,56 @@ static int get_luks1_volume_key_by_keyfile(struct crypt_device *cd, return r; } +static int get_bitlk_volume_key_by_keyfile(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + const struct bitlk_metadata *params, + struct volume_key **r_vk) +{ + int r; + const char *passphrase; + size_t passphrase_size; + + assert(cd); + assert(kc && kc->type == CRYPT_KC_TYPE_KEYFILE); + assert(params); + assert(r_vk); + + r = get_passphrase_by_keyfile(cd, kc, &passphrase, &passphrase_size); + if (r < 0) + return r; + + r = BITLK_get_volume_key(cd, passphrase, passphrase_size, params, r_vk); + if (r < 0) + kc->error = r; + + return r; +} + +static int get_fvault2_volume_key_by_keyfile(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + const struct fvault2_params *params, + struct volume_key **r_vk) +{ + int r; + const char *passphrase; + size_t passphrase_size; + + assert(cd); + assert(kc && kc->type == CRYPT_KC_TYPE_KEYFILE); + assert(params); + assert(r_vk); + + r = get_passphrase_by_keyfile(cd, kc, &passphrase, &passphrase_size); + if (r < 0) + return r; + + r = FVAULT2_get_volume_key(cd, passphrase, passphrase_size, params, r_vk); + if (r < 0) + kc->error = r; + + return r; +} + static int get_key_by_key(struct crypt_device *cd __attribute__((unused)), struct crypt_keyslot_context *kc, int keyslot __attribute__((unused)), @@ -198,6 +288,22 @@ static int get_generic_volume_key_by_key(struct crypt_device *cd, return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk); } +static int get_bitlk_volume_key_by_key(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + const struct bitlk_metadata *params __attribute__((unused)), + struct volume_key **r_vk) +{ + return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk); +} + +static int get_fvault2_volume_key_by_key(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + const struct fvault2_params *params __attribute__((unused)), + struct volume_key **r_vk) +{ + return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk); +} + static int get_generic_signed_key_by_key(struct crypt_device *cd, struct crypt_keyslot_context *kc, struct volume_key **r_vk, @@ -354,7 +460,7 @@ static int get_luks2_key_by_keyring(struct crypt_device *cd, if (r < 0) kc->error = r; - return 0; + return r; } static int get_luks2_volume_key_by_keyring(struct crypt_device *cd, @@ -373,7 +479,7 @@ static int get_luks1_volume_key_by_keyring(struct crypt_device *cd, int r; assert(cd); - assert(kc && kc->type == CRYPT_KC_TYPE_PASSPHRASE); + assert(kc && kc->type == CRYPT_KC_TYPE_KEYRING); assert(r_vk); r = get_passphrase_by_keyring(cd, kc, CONST_CAST(const char **) &kc->i_passphrase, @@ -414,9 +520,9 @@ static int get_key_by_vk_in_keyring(struct crypt_device *cd, return -EINVAL; } - *r_vk = crypt_alloc_volume_key(key_size, key); - crypt_safe_free(key); + *r_vk = crypt_alloc_volume_key_by_safe_alloc((void **)&key); if (!*r_vk) { + crypt_safe_free(key); kc->error = -ENOMEM; return kc->error; } @@ -432,16 +538,41 @@ static int get_volume_key_by_vk_in_keyring(struct crypt_device *cd, return get_key_by_vk_in_keyring(cd, kc, -2 /* unused */, -2 /* unused */, r_vk); } -static void unlock_method_init_internal(struct crypt_keyslot_context *kc) +static void crypt_keyslot_context_init_common(struct crypt_keyslot_context *kc) { assert(kc); + kc->version = KC_VERSION_BASIC; kc->error = 0; kc->i_passphrase = NULL; kc->i_passphrase_size = 0; } -void crypt_keyslot_unlock_by_keyring_internal(struct crypt_keyslot_context *kc, +static void keyring_context_free(struct crypt_keyslot_context *kc) +{ + assert(kc && kc->type == CRYPT_KC_TYPE_KEYRING); + + free(kc->u.kr.i_key_description); +} + +static int keyring_get_key_size(struct crypt_device *cd, struct crypt_keyslot_context *kc, size_t *r_key_size) +{ + int r; + + assert(kc && kc->type == CRYPT_KC_TYPE_VK_KEYRING); + assert(r_key_size); + + if (!kc->u.vk_kr.i_key_size) { + r = crypt_keyring_get_keysize_by_name(cd, kc->u.vk_kr.key_description, &kc->u.vk_kr.i_key_size); + if (r < 0) + return r; + } + + *r_key_size = kc->u.vk_kr.i_key_size; + return 0; +} + +void crypt_keyslot_context_init_by_keyring_internal(struct crypt_keyslot_context *kc, const char *key_description) { assert(kc); @@ -450,18 +581,32 @@ void crypt_keyslot_unlock_by_keyring_internal(struct crypt_keyslot_context *kc, kc->u.kr.key_description = key_description; kc->get_luks2_key = get_luks2_key_by_keyring; - kc->get_luks2_volume_key = get_luks2_volume_key_by_keyring; kc->get_luks1_volume_key = get_luks1_volume_key_by_keyring; + kc->get_luks2_volume_key = get_luks2_volume_key_by_keyring; kc->get_passphrase = get_passphrase_by_keyring; - kc->get_plain_volume_key = NULL; - kc->get_bitlk_volume_key = NULL; - kc->get_fvault2_volume_key = NULL; - kc->get_verity_volume_key = NULL; - kc->get_integrity_volume_key = NULL; - unlock_method_init_internal(kc); + kc->context_free = keyring_context_free; + crypt_keyslot_context_init_common(kc); +} + +static void key_context_free(struct crypt_keyslot_context *kc) +{ + assert(kc && kc->type == CRYPT_KC_TYPE_KEY); + + crypt_free_volume_key(kc->u.k.i_vk); } -void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc, +static int key_get_key_size(struct crypt_device *cd __attribute__((unused)), + struct crypt_keyslot_context *kc, + size_t *r_key_size) +{ + assert(kc && kc->type == CRYPT_KC_TYPE_KEY); + assert(r_key_size); + + *r_key_size = kc->u.k.volume_key_size; + return 0; +} + +void crypt_keyslot_context_init_by_key_internal(struct crypt_keyslot_context *kc, const char *volume_key, size_t volume_key_size) { @@ -470,19 +615,29 @@ void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc, kc->type = CRYPT_KC_TYPE_KEY; kc->u.k.volume_key = volume_key; kc->u.k.volume_key_size = volume_key_size; + kc->get_luks2_key = get_key_by_key; - kc->get_luks2_volume_key = get_volume_key_by_key; kc->get_luks1_volume_key = get_volume_key_by_key; - kc->get_passphrase = NULL; /* keyslot key context does not provide passphrase */ + kc->get_luks2_volume_key = get_volume_key_by_key; kc->get_plain_volume_key = get_generic_volume_key_by_key; - kc->get_bitlk_volume_key = get_generic_volume_key_by_key; - kc->get_fvault2_volume_key = get_generic_volume_key_by_key; + kc->get_bitlk_volume_key = get_bitlk_volume_key_by_key; + kc->get_fvault2_volume_key = get_fvault2_volume_key_by_key; kc->get_verity_volume_key = get_generic_signed_key_by_key; kc->get_integrity_volume_key = get_generic_volume_key_by_key; - unlock_method_init_internal(kc); + kc->get_key_size = key_get_key_size; + kc->context_free = key_context_free; + crypt_keyslot_context_init_common(kc); } -void crypt_keyslot_unlock_by_signed_key_init_internal(struct crypt_keyslot_context *kc, +static void signed_key_context_free(struct crypt_keyslot_context *kc) +{ + assert(kc && kc->type == CRYPT_KC_TYPE_SIGNED_KEY); + + crypt_free_volume_key(kc->u.ks.i_vk); + crypt_free_volume_key(kc->u.ks.i_vk_sig); +} + +void crypt_keyslot_context_init_by_signed_key_internal(struct crypt_keyslot_context *kc, const char *volume_key, size_t volume_key_size, const char *signature, @@ -495,19 +650,13 @@ void crypt_keyslot_unlock_by_signed_key_init_internal(struct crypt_keyslot_conte kc->u.ks.volume_key_size = volume_key_size; kc->u.ks.signature = signature; kc->u.ks.signature_size = signature_size; - kc->get_luks2_key = NULL; - kc->get_luks2_volume_key = NULL; - kc->get_luks1_volume_key = NULL; - kc->get_passphrase = NULL; - kc->get_plain_volume_key = NULL; - kc->get_bitlk_volume_key = NULL; - kc->get_fvault2_volume_key = NULL; + kc->get_verity_volume_key = get_generic_signed_key_by_key; - kc->get_integrity_volume_key = NULL; - unlock_method_init_internal(kc); + kc->context_free = signed_key_context_free; + crypt_keyslot_context_init_common(kc); } -void crypt_keyslot_unlock_by_passphrase_init_internal(struct crypt_keyslot_context *kc, +void crypt_keyslot_context_init_by_passphrase_internal(struct crypt_keyslot_context *kc, const char *passphrase, size_t passphrase_size) { @@ -516,19 +665,24 @@ void crypt_keyslot_unlock_by_passphrase_init_internal(struct crypt_keyslot_conte kc->type = CRYPT_KC_TYPE_PASSPHRASE; kc->u.p.passphrase = passphrase; kc->u.p.passphrase_size = passphrase_size; + kc->get_luks2_key = get_luks2_key_by_passphrase; - kc->get_luks2_volume_key = get_luks2_volume_key_by_passphrase; kc->get_luks1_volume_key = get_luks1_volume_key_by_passphrase; + kc->get_luks2_volume_key = get_luks2_volume_key_by_passphrase; + kc->get_bitlk_volume_key = get_bitlk_volume_key_by_passphrase; + kc->get_fvault2_volume_key = get_fvault2_volume_key_by_passphrase; kc->get_passphrase = get_passphrase_by_passphrase; - kc->get_plain_volume_key = NULL; - kc->get_bitlk_volume_key = NULL; - kc->get_fvault2_volume_key = NULL; - kc->get_verity_volume_key = NULL; - kc->get_integrity_volume_key = NULL; - unlock_method_init_internal(kc); + crypt_keyslot_context_init_common(kc); +} + +static void keyfile_context_free(struct crypt_keyslot_context *kc) +{ + assert(kc && kc->type == CRYPT_KC_TYPE_KEYFILE); + + free(kc->u.kf.i_keyfile); } -void crypt_keyslot_unlock_by_keyfile_init_internal(struct crypt_keyslot_context *kc, +void crypt_keyslot_context_init_by_keyfile_internal(struct crypt_keyslot_context *kc, const char *keyfile, size_t keyfile_size, uint64_t keyfile_offset) @@ -537,21 +691,28 @@ void crypt_keyslot_unlock_by_keyfile_init_internal(struct crypt_keyslot_context kc->type = CRYPT_KC_TYPE_KEYFILE; kc->u.kf.keyfile = keyfile; - kc->u.kf.keyfile_size = keyfile_size; kc->u.kf.keyfile_offset = keyfile_offset; + kc->u.kf.keyfile_size = keyfile_size; + kc->get_luks2_key = get_luks2_key_by_keyfile; - kc->get_luks2_volume_key = get_luks2_volume_key_by_keyfile; kc->get_luks1_volume_key = get_luks1_volume_key_by_keyfile; + kc->get_luks2_volume_key = get_luks2_volume_key_by_keyfile; + kc->get_bitlk_volume_key = get_bitlk_volume_key_by_keyfile; + kc->get_fvault2_volume_key = get_fvault2_volume_key_by_keyfile; kc->get_passphrase = get_passphrase_by_keyfile; - kc->get_plain_volume_key = NULL; - kc->get_bitlk_volume_key = NULL; - kc->get_fvault2_volume_key = NULL; - kc->get_verity_volume_key = NULL; - kc->get_integrity_volume_key = NULL; - unlock_method_init_internal(kc); + kc->context_free = keyfile_context_free; + crypt_keyslot_context_init_common(kc); } -void crypt_keyslot_unlock_by_token_init_internal(struct crypt_keyslot_context *kc, +static void token_context_free(struct crypt_keyslot_context *kc) +{ + assert(kc && kc->type == CRYPT_KC_TYPE_TOKEN); + + free(kc->u.t.i_type); + crypt_safe_free(kc->u.t.i_pin); +} + +void crypt_keyslot_context_init_by_token_internal(struct crypt_keyslot_context *kc, int token, const char *type, const char *pin, @@ -566,47 +727,30 @@ void crypt_keyslot_unlock_by_token_init_internal(struct crypt_keyslot_context *k kc->u.t.pin = pin; kc->u.t.pin_size = pin_size; kc->u.t.usrptr = usrptr; + kc->get_luks2_key = get_luks2_key_by_token; kc->get_luks2_volume_key = get_luks2_volume_key_by_token; - kc->get_luks1_volume_key = NULL; /* LUKS1 is not supported */ kc->get_passphrase = get_passphrase_by_token; - kc->get_plain_volume_key = NULL; - kc->get_bitlk_volume_key = NULL; - kc->get_fvault2_volume_key = NULL; - kc->get_verity_volume_key = NULL; - kc->get_integrity_volume_key = NULL; - unlock_method_init_internal(kc); + kc->context_free = token_context_free; + crypt_keyslot_context_init_common(kc); } -void crypt_keyslot_unlock_by_vk_in_keyring_internal(struct crypt_keyslot_context *kc, - const char *key_description) +static void vk_in_keyring_context_free(struct crypt_keyslot_context *kc) { - assert(kc); - - kc->type = CRYPT_KC_TYPE_VK_KEYRING; - kc->u.vk_kr.key_description = key_description; + assert(kc && kc->type == CRYPT_KC_TYPE_VK_KEYRING); - kc->get_luks2_key = get_key_by_vk_in_keyring; - kc->get_luks2_volume_key = get_volume_key_by_vk_in_keyring; - kc->get_luks1_volume_key = NULL; - kc->get_passphrase = NULL; /* keyslot key context does not provide passphrase */ - kc->get_plain_volume_key = NULL; - kc->get_bitlk_volume_key = NULL; - kc->get_fvault2_volume_key = NULL; - kc->get_verity_volume_key = NULL; - kc->get_integrity_volume_key = NULL; - unlock_method_init_internal(kc); + free(kc->u.vk_kr.i_key_description); } - void crypt_keyslot_context_destroy_internal(struct crypt_keyslot_context *kc) { if (!kc) return; + if (kc->context_free) + kc->context_free(kc); + crypt_safe_free(kc->i_passphrase); - kc->i_passphrase = NULL; - kc->i_passphrase_size = 0; } void crypt_keyslot_context_free(struct crypt_keyslot_context *kc) @@ -615,157 +759,443 @@ void crypt_keyslot_context_free(struct crypt_keyslot_context *kc) free(kc); } -int crypt_keyslot_context_init_by_passphrase(struct crypt_device *cd __attribute__((unused)), - const char *passphrase, +static int _crypt_keyslot_context_init_by_passphrase(const char *passphrase, size_t passphrase_size, - struct crypt_keyslot_context **kc) + struct crypt_keyslot_context **kc, + bool self_contained) { struct crypt_keyslot_context *tmp; + char *i_passphrase = NULL; if (!kc || !passphrase) return -EINVAL; - tmp = malloc(sizeof(*tmp)); + tmp = crypt_zalloc(sizeof(*tmp)); if (!tmp) return -ENOMEM; - crypt_keyslot_unlock_by_passphrase_init_internal(tmp, passphrase, passphrase_size); + if (self_contained) { + if (passphrase_size) { + i_passphrase = crypt_safe_alloc(passphrase_size); + if (!i_passphrase) { + free(tmp); + return -ENOMEM; + } + crypt_safe_memcpy(i_passphrase, passphrase, passphrase_size); + passphrase = i_passphrase; + } else + /* + * some crypto backend libraries expect a pointer even though + * passed passphrase size is set to zero. + */ + passphrase = ""; + } + + crypt_keyslot_context_init_by_passphrase_internal(tmp, passphrase, passphrase_size); + + if (self_contained) { + tmp->i_passphrase = i_passphrase; + tmp->i_passphrase_size = passphrase_size; + tmp->version = KC_VERSION_SELF_CONTAINED; + } *kc = tmp; return 0; } -int crypt_keyslot_context_init_by_keyfile(struct crypt_device *cd __attribute__((unused)), - const char *keyfile, +CRYPT_SYMBOL_EXPORT_NEW(int, crypt_keyslot_context_init_by_passphrase, 2, 8, + /* crypt_keyslot_context_init_by_passphrase parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *passphrase, + size_t passphrase_size, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_passphrase(passphrase, passphrase_size, kc, true); +} + +CRYPT_SYMBOL_EXPORT_OLD(int, crypt_keyslot_context_init_by_passphrase, 2, 6, + /* crypt_keyslot_context_init_by_passphrase parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *passphrase, + size_t passphrase_size, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_passphrase(passphrase, passphrase_size, kc, false); +} + +static int _crypt_keyslot_context_init_by_keyfile(const char *keyfile, size_t keyfile_size, uint64_t keyfile_offset, - struct crypt_keyslot_context **kc) + struct crypt_keyslot_context **kc, + bool self_contained) { + char *i_keyfile; struct crypt_keyslot_context *tmp; if (!kc || !keyfile) return -EINVAL; - tmp = malloc(sizeof(*tmp)); + tmp = crypt_zalloc(sizeof(*tmp)); if (!tmp) return -ENOMEM; - crypt_keyslot_unlock_by_keyfile_init_internal(tmp, keyfile, keyfile_size, keyfile_offset); + if (self_contained) { + i_keyfile = strdup(keyfile); + if (!i_keyfile) { + free(tmp); + return -ENOMEM; + } + keyfile = i_keyfile; + } + + crypt_keyslot_context_init_by_keyfile_internal(tmp, keyfile, keyfile_size, keyfile_offset); + + if (self_contained) { + tmp->u.kf.i_keyfile = i_keyfile; + tmp->version = KC_VERSION_SELF_CONTAINED; + } *kc = tmp; return 0; } -int crypt_keyslot_context_init_by_token(struct crypt_device *cd __attribute__((unused)), - int token, +CRYPT_SYMBOL_EXPORT_NEW(int, crypt_keyslot_context_init_by_keyfile, 2, 8, + /* crypt_keyslot_context_init_by_keyfile parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *keyfile, + size_t keyfile_size, + uint64_t keyfile_offset, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_keyfile(keyfile, keyfile_size, keyfile_offset, kc, true); +} + +CRYPT_SYMBOL_EXPORT_OLD(int, crypt_keyslot_context_init_by_keyfile, 2, 6, + /* crypt_keyslot_context_init_by_keyfile parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *keyfile, + size_t keyfile_size, + uint64_t keyfile_offset, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_keyfile(keyfile, keyfile_size, keyfile_offset, kc, false); +} + +static int _crypt_keyslot_context_init_by_token(int token, const char *type, const char *pin, size_t pin_size, void *usrptr, - struct crypt_keyslot_context **kc) + struct crypt_keyslot_context **kc, + bool self_contained) { + char *i_type = NULL, *i_pin = NULL; struct crypt_keyslot_context *tmp; - if (!kc || (token < 0 && token != CRYPT_ANY_TOKEN)) + if (!kc || (token < 0 && token != CRYPT_ANY_TOKEN) || + (pin && !pin_size)) return -EINVAL; - tmp = malloc(sizeof(*tmp)); + tmp = crypt_zalloc(sizeof(*tmp)); if (!tmp) return -ENOMEM; - crypt_keyslot_unlock_by_token_init_internal(tmp, token, type, pin, pin_size, usrptr); + if (self_contained && type) { + if (!(i_type = strdup(type))) + goto err; + type = i_type; + } + + if (self_contained && pin) { + if (!(i_pin = crypt_safe_alloc(pin_size))) + goto err; + crypt_safe_memcpy(i_pin, pin, pin_size); + pin = i_pin; + } + + crypt_keyslot_context_init_by_token_internal(tmp, token, type, pin, pin_size, usrptr); + + if (self_contained) { + tmp->u.t.i_pin = i_pin; + tmp->u.t.i_type = i_type; + tmp->version = KC_VERSION_SELF_CONTAINED; + } *kc = tmp; return 0; +err: + crypt_safe_free(i_pin); + free(i_type); + free(tmp); + + return -ENOMEM; } -int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd __attribute__((unused)), - const char *volume_key, - size_t volume_key_size, +CRYPT_SYMBOL_EXPORT_NEW(int, crypt_keyslot_context_init_by_token, 2, 8, + /* crypt_keyslot_context_init_by_token parameters follows */ + struct crypt_device *cd __attribute__((unused)), + int token, + const char *type, + const char *pin, size_t pin_size, + void *usrptr, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_token(token, type, pin, pin_size, usrptr, kc, true); +} + +CRYPT_SYMBOL_EXPORT_OLD(int, crypt_keyslot_context_init_by_token, 2, 6, + /* crypt_keyslot_context_init_by_token parameters follows */ + struct crypt_device *cd __attribute__((unused)), + int token, + const char *type, + const char *pin, size_t pin_size, + void *usrptr, struct crypt_keyslot_context **kc) { + return _crypt_keyslot_context_init_by_token(token, type, pin, pin_size, usrptr, kc, false); +} + +static int _crypt_keyslot_context_init_by_volume_key(const char *volume_key, + size_t volume_key_size, + struct crypt_keyslot_context **kc, + bool self_contained) +{ + struct volume_key *i_vk = NULL; struct crypt_keyslot_context *tmp; if (!kc) return -EINVAL; - tmp = malloc(sizeof(*tmp)); + tmp = crypt_zalloc(sizeof(*tmp)); if (!tmp) return -ENOMEM; - crypt_keyslot_unlock_by_key_init_internal(tmp, volume_key, volume_key_size); + if (self_contained && volume_key) { + if (!(i_vk = crypt_alloc_volume_key(volume_key_size, volume_key))) { + free(tmp); + return -ENOMEM; + } + volume_key = crypt_volume_key_get_key(i_vk); + } + + crypt_keyslot_context_init_by_key_internal(tmp, volume_key, volume_key_size); + + if (self_contained) { + tmp->u.k.i_vk = i_vk; + tmp->version = KC_VERSION_SELF_CONTAINED; + } *kc = tmp; return 0; } -int crypt_keyslot_context_init_by_signed_key(struct crypt_device *cd __attribute__((unused)), +CRYPT_SYMBOL_EXPORT_NEW(int, crypt_keyslot_context_init_by_volume_key, 2, 8, + /* crypt_keyslot_context_init_by_volume_key parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *volume_key, + size_t volume_key_size, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_volume_key(volume_key, volume_key_size, kc, true); +} + +CRYPT_SYMBOL_EXPORT_OLD(int, crypt_keyslot_context_init_by_volume_key, 2, 6, + /* crypt_keyslot_context_init_by_volume_key parameters follows */ + struct crypt_device *cd __attribute__((unused)), const char *volume_key, + size_t volume_key_size, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_volume_key(volume_key, volume_key_size, kc, false); +} + +static int _crypt_keyslot_context_init_by_signed_key(const char *volume_key, size_t volume_key_size, const char *signature, size_t signature_size, - struct crypt_keyslot_context **kc) + struct crypt_keyslot_context **kc, + bool self_contained) { + struct volume_key *i_vk = NULL, *i_vk_sig = NULL; struct crypt_keyslot_context *tmp; if (!kc) return -EINVAL; - tmp = malloc(sizeof(*tmp)); + tmp = crypt_zalloc(sizeof(*tmp)); if (!tmp) return -ENOMEM; - crypt_keyslot_unlock_by_signed_key_init_internal(tmp, volume_key, volume_key_size, + if (self_contained && volume_key) { + if (!(i_vk = crypt_alloc_volume_key(volume_key_size, volume_key))) + goto err; + volume_key = crypt_volume_key_get_key(i_vk); + } + + if (self_contained && signature) { + if (!(i_vk_sig = crypt_alloc_volume_key(signature_size, signature))) + goto err; + signature = crypt_volume_key_get_key(i_vk_sig); + } + + crypt_keyslot_context_init_by_signed_key_internal(tmp, volume_key, volume_key_size, signature, signature_size); + if (self_contained) { + tmp->u.ks.i_vk = i_vk; + tmp->u.ks.i_vk_sig = i_vk_sig; + tmp->version = KC_VERSION_SELF_CONTAINED; + } + *kc = tmp; return 0; +err: + crypt_free_volume_key(i_vk); + crypt_free_volume_key(i_vk_sig); + free(tmp); + + return -ENOMEM; } -int crypt_keyslot_context_init_by_keyring(struct crypt_device *cd __attribute__((unused)), - const char *key_description, +CRYPT_SYMBOL_EXPORT_NEW(int, crypt_keyslot_context_init_by_signed_key, 2, 8, + /* crypt_keyslot_context_init_by_signed_key parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *volume_key, + size_t volume_key_size, + const char *signature, + size_t signature_size, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_signed_key(volume_key, volume_key_size, signature, signature_size, kc, true); +} + +CRYPT_SYMBOL_EXPORT_OLD(int, crypt_keyslot_context_init_by_signed_key, 2, 7, + /* crypt_keyslot_context_init_by_signed_key parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *volume_key, + size_t volume_key_size, + const char *signature, + size_t signature_size, struct crypt_keyslot_context **kc) { + return _crypt_keyslot_context_init_by_signed_key(volume_key, volume_key_size, signature, signature_size, kc, false); +} + +static int _crypt_keyslot_context_init_by_keyring(const char *key_description, + struct crypt_keyslot_context **kc, + bool self_contained) +{ + char *i_key_description; struct crypt_keyslot_context *tmp; - if (!kc) + if (!kc || !key_description) return -EINVAL; - tmp = malloc(sizeof(*tmp)); + tmp = crypt_zalloc(sizeof(*tmp)); if (!tmp) return -ENOMEM; - crypt_keyslot_unlock_by_keyring_internal(tmp, key_description); + if (self_contained) { + if (!(i_key_description = strdup(key_description))) { + free(tmp); + return -ENOMEM; + } + key_description = i_key_description; + } + + crypt_keyslot_context_init_by_keyring_internal(tmp, key_description); + + if (self_contained) { + tmp->u.kr.i_key_description = i_key_description; + tmp->version = KC_VERSION_SELF_CONTAINED; + } *kc = tmp; return 0; } -int crypt_keyslot_context_init_by_vk_in_keyring(struct crypt_device *cd __attribute__((unused)), +CRYPT_SYMBOL_EXPORT_NEW(int, crypt_keyslot_context_init_by_keyring, 2, 8, + /* crypt_keyslot_context_init_by_keyring parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *key_description, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_keyring(key_description, kc, true); +} + +CRYPT_SYMBOL_EXPORT_OLD(int, crypt_keyslot_context_init_by_keyring, 2, 7, + /* crypt_keyslot_context_init_by_keyring parameters follows */ + struct crypt_device *cd __attribute__((unused)), const char *key_description, struct crypt_keyslot_context **kc) { + return _crypt_keyslot_context_init_by_keyring(key_description, kc, false); +} + +static int _crypt_keyslot_context_init_by_vk_in_keyring(const char *key_description, + struct crypt_keyslot_context **kc, + bool self_contained) +{ + char *i_key_description; struct crypt_keyslot_context *tmp; - if (!kc) + if (!kc || !key_description) return -EINVAL; - tmp = malloc(sizeof(*tmp)); + tmp = crypt_zalloc(sizeof(*tmp)); if (!tmp) return -ENOMEM; - crypt_keyslot_unlock_by_vk_in_keyring_internal(tmp, key_description); + if (self_contained) { + if (!(i_key_description = strdup(key_description))) { + free(tmp); + return -ENOMEM; + } + key_description = i_key_description; + } + + tmp->type = CRYPT_KC_TYPE_VK_KEYRING; + tmp->u.vk_kr.key_description = key_description; + + tmp->get_luks2_key = get_key_by_vk_in_keyring; + tmp->get_luks2_volume_key = get_volume_key_by_vk_in_keyring; + tmp->get_key_size = keyring_get_key_size; + tmp->context_free = vk_in_keyring_context_free; + crypt_keyslot_context_init_common(tmp); + + if (self_contained) { + tmp->u.vk_kr.i_key_description = i_key_description; + tmp->version = KC_VERSION_SELF_CONTAINED; + } *kc = tmp; return 0; } +CRYPT_SYMBOL_EXPORT_NEW(int, crypt_keyslot_context_init_by_vk_in_keyring, 2, 8, + /* crypt_keyslot_context_init_by_vk_in_keyring parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *key_description, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_vk_in_keyring(key_description, kc, true); +} + +CRYPT_SYMBOL_EXPORT_OLD(int, crypt_keyslot_context_init_by_vk_in_keyring, 2, 7, + /* crypt_keyslot_context_init_by_vk_in_keyring parameters follows */ + struct crypt_device *cd __attribute__((unused)), + const char *key_description, + struct crypt_keyslot_context **kc) +{ + return _crypt_keyslot_context_init_by_vk_in_keyring(key_description, kc, false); +} + int crypt_keyslot_context_get_error(struct crypt_keyslot_context *kc) { return kc ? kc->error : -EINVAL; @@ -775,10 +1205,21 @@ int crypt_keyslot_context_set_pin(struct crypt_device *cd __attribute__((unused) const char *pin, size_t pin_size, struct crypt_keyslot_context *kc) { + char *i_pin = NULL; + if (!kc || kc->type != CRYPT_KC_TYPE_TOKEN) return -EINVAL; - kc->u.t.pin = pin; + if (kc->version >= KC_VERSION_SELF_CONTAINED && pin) { + if (!(i_pin = crypt_safe_alloc(pin_size))) + return -ENOMEM; + crypt_safe_memcpy(i_pin, pin, pin_size); + } + + crypt_safe_free(kc->u.t.i_pin); + kc->u.t.i_pin = i_pin; + + kc->u.t.pin = i_pin ?: pin; kc->u.t.pin_size = pin_size; kc->error = 0; diff --git a/lib/keyslot_context.h b/lib/keyslot_context.h index a7f834a..a2e42b2 100644 --- a/lib/keyslot_context.h +++ b/lib/keyslot_context.h @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup, keyslot unlock helpers * - * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2022-2024 Ondrej Kozina + * Copyright (C) 2022-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2025 Ondrej Kozina */ #ifndef KEYSLOT_CONTEXT_H @@ -14,6 +14,9 @@ #include "internal.h" +struct bitlk_metadata; +struct fvault2_params; + typedef int (*keyslot_context_get_key) ( struct crypt_device *cd, struct crypt_keyslot_context *kc, @@ -32,6 +35,19 @@ typedef int (*keyslot_context_get_generic_volume_key) ( struct crypt_keyslot_context *kc, struct volume_key **r_vk); +typedef int (*keyslot_context_get_bitlk_volume_key) ( + struct crypt_device *cd, + struct crypt_keyslot_context *kc, + const struct bitlk_metadata *params, + struct volume_key **r_vk); + +typedef int (*keyslot_context_get_fvault2_volume_key) ( + struct crypt_device *cd, + struct crypt_keyslot_context *kc, + const struct fvault2_params *params, + struct volume_key **r_vk); + + typedef int (*keyslot_context_get_generic_signed_key) ( struct crypt_device *cd, struct crypt_keyslot_context *kc, @@ -44,10 +60,28 @@ typedef int (*keyslot_context_get_passphrase) ( const char **r_passphrase, size_t *r_passphrase_size); +typedef void (*keyslot_context_free) ( + struct crypt_keyslot_context *kc); + +typedef int (*keyslot_context_get_key_size) ( + struct crypt_device *cd, + struct crypt_keyslot_context *kc, + size_t *r_key_size); + +#define KC_VERSION_BASIC UINT8_C(1) +#define KC_VERSION_SELF_CONTAINED UINT8_C(2) + /* crypt_keyslot_context */ struct crypt_keyslot_context { int type; + /* versions: + * v1: All passed pointers (e.g.: type, passphrase, keyfile,...) must + * be valid after ctx initialization. + * v2: Fully self-contained + */ + uint8_t version; + union { struct { const char *passphrase; @@ -55,31 +89,40 @@ struct crypt_keyslot_context { } p; struct { const char *keyfile; + char *i_keyfile; uint64_t keyfile_offset; size_t keyfile_size; } kf; struct { int id; const char *type; + char *i_type; const char *pin; + char *i_pin; size_t pin_size; void *usrptr; } t; struct { const char *volume_key; size_t volume_key_size; + struct volume_key *i_vk; } k; struct { const char *volume_key; size_t volume_key_size; + struct volume_key *i_vk; const char *signature; size_t signature_size; + struct volume_key *i_vk_sig; } ks; struct { const char *key_description; + char *i_key_description; } kr; struct { const char *key_description; + char *i_key_description; + size_t i_key_size; } vk_kr; } u; @@ -92,45 +135,44 @@ struct crypt_keyslot_context { keyslot_context_get_volume_key get_luks1_volume_key; keyslot_context_get_volume_key get_luks2_volume_key; keyslot_context_get_generic_volume_key get_plain_volume_key; - keyslot_context_get_generic_volume_key get_bitlk_volume_key; - keyslot_context_get_generic_volume_key get_fvault2_volume_key; + keyslot_context_get_bitlk_volume_key get_bitlk_volume_key; + keyslot_context_get_fvault2_volume_key get_fvault2_volume_key; keyslot_context_get_generic_signed_key get_verity_volume_key; keyslot_context_get_generic_volume_key get_integrity_volume_key; keyslot_context_get_passphrase get_passphrase; + keyslot_context_get_key_size get_key_size; + keyslot_context_free context_free; }; void crypt_keyslot_context_destroy_internal(struct crypt_keyslot_context *method); -void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc, +void crypt_keyslot_context_init_by_key_internal(struct crypt_keyslot_context *kc, const char *volume_key, size_t volume_key_size); -void crypt_keyslot_unlock_by_signed_key_init_internal(struct crypt_keyslot_context *kc, +void crypt_keyslot_context_init_by_signed_key_internal(struct crypt_keyslot_context *kc, const char *volume_key, size_t volume_key_size, const char *signature, size_t signature_size); -void crypt_keyslot_unlock_by_passphrase_init_internal(struct crypt_keyslot_context *kc, +void crypt_keyslot_context_init_by_passphrase_internal(struct crypt_keyslot_context *kc, const char *passphrase, size_t passphrase_size); -void crypt_keyslot_unlock_by_keyfile_init_internal(struct crypt_keyslot_context *kc, +void crypt_keyslot_context_init_by_keyfile_internal(struct crypt_keyslot_context *kc, const char *keyfile, size_t keyfile_size, uint64_t keyfile_offset); -void crypt_keyslot_unlock_by_token_init_internal(struct crypt_keyslot_context *kc, +void crypt_keyslot_context_init_by_token_internal(struct crypt_keyslot_context *kc, int token, const char *type, const char *pin, size_t pin_size, void *usrptr); -void crypt_keyslot_unlock_by_keyring_internal(struct crypt_keyslot_context *kc, - const char *key_description); - -void crypt_keyslot_unlock_by_vk_in_keyring_internal(struct crypt_keyslot_context *kc, +void crypt_keyslot_context_init_by_keyring_internal(struct crypt_keyslot_context *kc, const char *key_description); const char *keyslot_context_type_string(const struct crypt_keyslot_context *kc); diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index afc2a8d..e645b18 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ /** @@ -74,13 +74,13 @@ int crypt_init_data_device(struct crypt_device **cd, * and, optionally, from separate metadata (header) device * and check if provided device exists. * - * @return @e 0 on success or negative errno value otherwise. - * * @param cd returns crypt device handle for active device * @param name name of active crypt device * @param header_device optional device containing on-disk header * (@e NULL if it the same as underlying device on there is no on-disk header) * + * @return @e 0 on success or negative errno value otherwise. + * * @post In case @e device points to active LUKS device but header load fails, * context device type is set to @e NULL and @e 0 is returned as if it were successful. * Context with @e NULL device type can only be deactivated by crypt_deactivate @@ -137,7 +137,7 @@ void crypt_set_confirm_callback(struct crypt_device *cd, * @param cd crypt device handle * @param device path to device * - * @returns 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. */ int crypt_set_data_device(struct crypt_device *cd, const char *device); @@ -145,13 +145,13 @@ int crypt_set_data_device(struct crypt_device *cd, const char *device); * Set data device offset in 512-byte sectors. * Used for LUKS. * This function is replacement for data alignment fields in LUKS param struct. - * If set to 0 (default), old behaviour is preserved. + * If set to @e 0 (default), old behaviour is preserved. * This value is reset on @link crypt_load @endlink. * * @param cd crypt device handle * @param data_offset data offset in bytes * - * @returns 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note Data offset must be aligned to multiple of 8 (alignment to 4096-byte sectors) * and must be big enough to accommodate the whole LUKS header with all keyslots. @@ -232,7 +232,6 @@ void crypt_logf(struct crypt_device *cd, int level, const char *format, ...); * * @param cd crypt device handle * @param rng_type kernel random number generator to use - * */ void crypt_set_rng_type(struct crypt_device *cd, int rng_type); @@ -240,8 +239,8 @@ void crypt_set_rng_type(struct crypt_device *cd, int rng_type); * Get which RNG (random number generator) is used for generating long term key. * * @param cd crypt device handle - * @return RNG type on success or negative errno value otherwise. * + * @return RNG type on success or negative errno value otherwise. */ int crypt_get_rng_type(struct crypt_device *cd); @@ -252,7 +251,7 @@ struct crypt_pbkdf_type { const char *type; /**< PBKDF algorithm */ const char *hash; /**< Hash algorithm */ uint32_t time_ms; /**< Requested time cost [milliseconds] */ - uint32_t iterations; /**< Iterations, 0 or benchmarked value. */ + uint32_t iterations; /**< Iterations, @e 0 or benchmarked value. */ uint32_t max_memory_kb; /**< Requested or benchmarked memory cost [kilobytes] */ uint32_t parallel_threads;/**< Requested parallel cost [threads] */ uint32_t flags; /**< CRYPT_PBKDF* flags */ @@ -277,7 +276,7 @@ struct crypt_pbkdf_type { * @param cd crypt device handle * @param pbkdf PBKDF parameters * - * @return 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note For LUKS1, only PBKDF2 is supported, other settings will be rejected. * @note For non-LUKS context types the call succeeds, but PBKDF is not used. @@ -290,8 +289,7 @@ int crypt_set_pbkdf_type(struct crypt_device *cd, * * @param pbkdf_type type of PBKDF * - * @return struct on success or NULL value otherwise. - * + * @return struct on success or @e NULL value otherwise. */ const struct crypt_pbkdf_type *crypt_get_pbkdf_type_params(const char *pbkdf_type); @@ -301,8 +299,7 @@ const struct crypt_pbkdf_type *crypt_get_pbkdf_type_params(const char *pbkdf_typ * * @param type type of device (see @link crypt-type @endlink) * - * @return struct on success or NULL value otherwise. - * + * @return struct on success or @e NULL value otherwise. */ const struct crypt_pbkdf_type *crypt_get_pbkdf_default(const char *type); @@ -312,8 +309,7 @@ const struct crypt_pbkdf_type *crypt_get_pbkdf_default(const char *type); * * @param cd crypt device handle * - * @return struct on success or NULL value otherwise. - * + * @return struct on success or @e NULL value otherwise. */ const struct crypt_pbkdf_type *crypt_get_pbkdf_type(struct crypt_device *cd); @@ -335,9 +331,9 @@ void crypt_set_iteration_time(struct crypt_device *cd, uint64_t iteration_time_m * \b Deprecated, only for backward compatibility. Memory with keys are locked automatically. * * @param cd crypt device handle, can be @e NULL - * @param lock 0 to unlock otherwise lock memory + * @param lock @e 0 to unlock otherwise lock memory * - * @returns Value indicating whether the memory is locked (function can be called multiple times). + * @return Value indicating whether the memory is locked (function can be called multiple times). * * @note Only root can do this. * @note It locks/unlocks all process memory, not only crypt context. @@ -348,9 +344,9 @@ int crypt_memory_lock(struct crypt_device *cd, int lock) __attribute__((deprecat * Set global lock protection for on-disk metadata (file-based locking). * * @param cd crypt device handle, can be @e NULL - * @param enable 0 to disable locking otherwise enable it (default) + * @param enable @e 0 to disable locking otherwise enable it (default) * - * @returns @e 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note Locking applied only for some metadata formats (LUKS2). * @note The switch is global on the library level. @@ -366,7 +362,7 @@ int crypt_metadata_locking(struct crypt_device *cd, int enable); * @param metadata_size size in bytes of JSON area + 4k binary header * @param keyslots_size size in bytes of binary keyslots area * - * @returns @e 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note The metadata area is stored twice and both copies contain 4k binary header. * Only 16,32,64,128,256,512,1024,2048 and 4096 kB value is allowed (see LUKS2 specification). @@ -384,7 +380,7 @@ int crypt_set_metadata_size(struct crypt_device *cd, * @param metadata_size size in bytes of JSON area + 4k binary header * @param keyslots_size size in bytes of binary keyslots area * - * @returns @e 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. */ int crypt_get_metadata_size(struct crypt_device *cd, uint64_t *metadata_size, @@ -426,6 +422,7 @@ int crypt_get_metadata_size(struct crypt_device *cd, * Get device type * * @param cd crypt device handle + * * @return string according to device type or @e NULL if not known. */ const char *crypt_get_type(struct crypt_device *cd); @@ -454,14 +451,14 @@ const char *crypt_get_default_type(void); * Get HW encryption type * * @return HW encryption type (see @link crypt-hw-encryption-types @endlink) - * or negative errno otherwise. + * or negative errno value otherwise. */ int crypt_get_hw_encryption_type(struct crypt_device *cd); /** * Get HW encryption (like OPAL) key size (in bytes) * - * @return key size or 0 if no HW encryption is used. + * @return key size or @e 0 if no HW encryption is used. */ int crypt_get_hw_encryption_key_size(struct crypt_device *cd); @@ -499,7 +496,6 @@ struct crypt_params_luks1 { * Structure used as parameter for loop-AES device type. * * @see crypt_format - * */ struct crypt_params_loopaes { const char *hash; /**< key hash function */ @@ -512,7 +508,6 @@ struct crypt_params_loopaes { * Structure used as parameter for dm-verity device type. * * @see crypt_format, crypt_load - * */ struct crypt_params_verity { const char *hash_name; /**< hash function */ @@ -545,7 +540,6 @@ struct crypt_params_verity { * Structure used as parameter for TCRYPT device type. * * @see crypt_load - * */ struct crypt_params_tcrypt { const char *passphrase; /**< passphrase to unlock header (input only) */ @@ -593,8 +587,8 @@ struct crypt_params_integrity { uint32_t tag_size; /**< tag size per-sector in bytes */ uint32_t sector_size; /**< sector size in bytes */ uint32_t buffer_sectors; /**< number of sectors in one buffer */ - const char *integrity; /**< integrity algorithm, NULL for LUKS2 */ - uint32_t integrity_key_size; /**< integrity key size in bytes, info only, 0 for LUKS2 */ + const char *integrity; /**< integrity algorithm, @e NULL for LUKS2 */ + uint32_t integrity_key_size; /**< integrity key size in bytes, info only */ const char *journal_integrity; /**< journal integrity algorithm */ const char *journal_integrity_key; /**< journal integrity key, only for crypt_load */ @@ -612,7 +606,6 @@ struct crypt_params_integrity { * * @note during crypt_format @e data_device attribute determines * if the LUKS2 header is separated from encrypted payload device - * */ struct crypt_params_luks2 { const struct crypt_pbkdf_type *pbkdf; /**< PBKDF (and hash) parameters or @e NULL*/ @@ -620,7 +613,7 @@ struct crypt_params_luks2 { const struct crypt_params_integrity *integrity_params; /**< Data integrity parameters or @e NULL*/ size_t data_alignment; /**< data area alignment in 512B sectors, data offset is multiple of this */ const char *data_device; /**< detached encrypted data device or @e NULL */ - uint32_t sector_size; /**< encryption sector size, 0 triggers auto-detection for optimal encryption sector size */ + uint32_t sector_size; /**< encryption sector size, @e 0 triggers auto-detection for optimal encryption sector size */ const char *label; /**< header label or @e NULL*/ const char *subsystem; /**< header subsystem label or @e NULL*/ }; @@ -629,7 +622,6 @@ struct crypt_params_luks2 { * Structure used as parameter for OPAL (HW encrypted) device type. * * @see crypt_format_luks2_opal - * */ struct crypt_params_hw_opal { const char *admin_key; /**< admin key */ @@ -659,7 +651,7 @@ struct crypt_params_hw_opal { * @param volume_key_size size of volume key in bytes. * @param params crypt type specific parameters (see @link crypt-type @endlink) * - * @returns @e 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note Note that crypt_format does not create LUKS keyslot (any version). To create keyslot * call any crypt_keyslot_add_* function. @@ -681,15 +673,15 @@ int crypt_format(struct crypt_device *cd, * @pre @e cd contains initialized and not formatted device context (device type must @b not be set) * * @param cd crypt device handle - * @param cipher for SW encryption (e.g. "aes") or NULL for HW encryption only - * @param cipher_mode including IV specification (e.g. "xts-plain") or NULL for HW encryption only + * @param cipher for SW encryption (e.g. "aes") or @e NULL for HW encryption only + * @param cipher_mode including IV specification (e.g. "xts-plain") or @e NULL for HW encryption only * @param uuid requested UUID or @e NULL if it should be generated * @param volume_keys pre-generated volume keys or @e NULL if it should be generated (only for LUKS2 SW encryption) * @param volume_keys_size size of volume keys in bytes (only for SW encryption). * @param params LUKS2 crypt type specific parameters (see @link crypt-type @endlink) * @param opal_params OPAL specific parameters * - * @returns @e 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note Note that crypt_format_luks2_opal does not create LUKS keyslot. * To create keyslot call any crypt_keyslot_add_* function. @@ -703,6 +695,35 @@ int crypt_format_luks2_opal(struct crypt_device *cd, struct crypt_params_luks2 *params, struct crypt_params_hw_opal *opal_params); +/** + * Create (format) new integrity-protected device using integrity inline mode (HW sector tags). + * This can be used for @e INTEGRITY and @e LUKS2 with integrity protection + * + * @pre @e cd contains initialized and not formatted device context (device type must @b not be set) + * + * @param cd crypt device handle + * @param type type of device (optional params struct must be of this type) + * @param cipher (e.g. "aes") or @e NULL for @e INTEGRITY + * @param cipher_mode including IV specification (e.g. "xts-plain") or @e NULL for @e INTEGRITY + * @param uuid requested UUID or @e NULL if it should be generated + * @param volume_key pre-generated integrity/volume key (if needed) or @e NULL + * @param volume_key_size size of volume/integrity key in bytes. + * @param params crypt type specific parameters (see @link crypt-type @endlink) + * + * @return @e 0 on success or negative errno value otherwise. + * + * @note Journal parameters must be set to zero in integrity part of @e params. + * Only tag_size, sector_size, buffer_sectors, integrity options should be set. + */ +int crypt_format_inline(struct crypt_device *cd, + const char *type, + const char *cipher, + const char *cipher_mode, + const char *uuid, + const char *volume_key, + size_t volume_key_size, + void *params); + /** * Set format compatibility flags. * @@ -716,7 +737,7 @@ void crypt_set_compatibility(struct crypt_device *cd, uint32_t flags); * * @param cd crypt device handle * - * @returns compatibility flags + * @return compatibility flags */ uint32_t crypt_get_compatibility(struct crypt_device *cd); @@ -734,7 +755,7 @@ uint32_t crypt_get_compatibility(struct crypt_device *cd); * @param type type of device (optional params struct must be of this type) * @param params crypt type specific parameters (see @link crypt-type @endlink) * - * @returns 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note Currently, only LUKS1->LUKS2 and LUKS2->LUKS1 conversions are supported. * Not all LUKS2 devices may be converted back to LUKS1. To make such a conversion @@ -755,7 +776,7 @@ int crypt_convert(struct crypt_device *cd, * @param cd crypt device handle * @param uuid requested UUID or @e NULL if it should be generated * - * @returns 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note Currently, only LUKS device type are supported */ @@ -769,7 +790,7 @@ int crypt_set_uuid(struct crypt_device *cd, * @param label requested label or @e NULL * @param subsystem requested subsystem label or @e NULL * - * @returns 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note Currently, only LUKS2 device type is supported */ @@ -803,10 +824,10 @@ const char *crypt_get_subsystem(struct crypt_device *cd); * dm-crypt target. * * @param cd crypt device handle, can be @e NULL - * @param enable 0 to disable loading of volume keys via kernel keyring + * @param enable @e 0 to disable loading of volume keys via kernel keyring * (classical method) otherwise enable it (default) * - * @returns @e 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note Currently loading of volume keys via kernel keyring is supported * (and enabled by default) only for LUKS2 devices. @@ -821,14 +842,13 @@ int crypt_volume_key_keyring(struct crypt_device *cd, int enable); * @param requested_type @link crypt-type @endlink or @e NULL for all known * @param params crypt type specific parameters (see @link crypt-type @endlink) * - * @returns 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @post In case LUKS header is read successfully but payload device is too small * error is returned and device type in context is set to @e NULL * * @note Note that load works only for device types with on-disk metadata. * @note Function does not print visible error message if metadata is not present. - * */ int crypt_load(struct crypt_device *cd, const char *requested_type, @@ -841,7 +861,7 @@ int crypt_load(struct crypt_device *cd, * @param requested_type @link crypt-type @endlink or @e NULL for all known * @param params crypt type specific parameters (see @link crypt-type @endlink) * - * @returns 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note For LUKS2 device crypt_repair bypass blkid checks and * perform auto-recovery even though there're third party device @@ -880,10 +900,9 @@ int crypt_resize(struct crypt_device *cd, * @param cd crypt device handle, can be @e NULL * @param name name of device to suspend * - * @return 0 on success or negative errno value otherwise. + * @return @e 0 on success or negative errno value otherwise. * * @note Only LUKS device type is supported - * */ int crypt_suspend(struct crypt_device *cd, const char *name); @@ -898,7 +917,7 @@ int crypt_suspend(struct crypt_device *cd, * @param passphrase passphrase used to unlock volume key * @param passphrase_size size of @e passphrase (binary data) * - * @return unlocked key slot number or negative errno otherwise. + * @return unlocked key slot number or negative errno value otherwise. * * @note Only LUKS device type is supported */ @@ -915,10 +934,10 @@ int crypt_resume_by_passphrase(struct crypt_device *cd, * @param name name of device to resume * @param keyslot requested keyslot or CRYPT_ANY_SLOT * @param keyfile key file used to unlock volume key - * @param keyfile_size number of bytes to read from keyfile, 0 is unlimited + * @param keyfile_size number of bytes to read from keyfile, @e 0 is unlimited * @param keyfile_offset number of bytes to skip at start of keyfile * - * @return unlocked key slot number or negative errno otherwise. + * @return unlocked key slot number or negative errno value otherwise. */ int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd, const char *name, @@ -951,7 +970,7 @@ int crypt_resume_by_keyfile(struct crypt_device *cd, * @param cd crypt device handle * @param name name of device to resume * @param volume_key provided volume key - * @param volume_key_size size of volume_key + * @param volume_key_size size of volume_key in bytes * * @return @e 0 on success or negative errno value otherwise. */ @@ -969,7 +988,7 @@ int crypt_resume_by_volume_key(struct crypt_device *cd, * @param pin_size size of @e pin * @param usrptr provided identification in callback * - * @return unlocked key slot number or negative errno otherwise. + * @return unlocked key slot number or negative errno value otherwise. * * @note EPERM errno means token provided passphrase successfully, but * passphrase did not unlock any keyslot associated with the token. @@ -978,7 +997,7 @@ int crypt_resume_by_volume_key(struct crypt_device *cd, * eligible to resume LUKS2 device. * * @note ENOANO errno means that token is PIN protected and was either missing - * (NULL) or wrong. + * (@e NULL) or wrong. * * @note Negative EAGAIN errno means token handler requires additional hardware * not present in the system to unlock keyslot. @@ -1007,7 +1026,7 @@ int crypt_resume_by_token_pin(struct crypt_device *cd, * @param kc keyslot context providing volume key or passphrase. * * @return unlocked key slot number for passphrase-based unlock, zero for other - * unlock methods (e.g. volume key context) or negative errno on error. + * unlock methods (e.g. volume key context) or negative errno value on error. */ int crypt_resume_by_keyslot_context(struct crypt_device *cd, const char *name, @@ -1036,7 +1055,7 @@ int crypt_resume_by_keyslot_context(struct crypt_device *cd, * @param new_passphrase passphrase for new keyslot * @param new_passphrase_size size of @e new_passphrase (binary data) * - * @return allocated key slot number or negative errno otherwise. + * @return allocated key slot number or negative errno value otherwise. */ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, int keyslot, @@ -1058,7 +1077,7 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, * @param new_passphrase passphrase for new keyslot * @param new_passphrase_size size of @e new_passphrase (binary data) * - * @return allocated key slot number or negative errno otherwise. + * @return allocated key slot number or negative errno value otherwise. */ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd, int keyslot_old, @@ -1082,7 +1101,7 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd, * @param new_keyfile_size number of bytes to read from @e new_keyfile, @e 0 is unlimited * @param new_keyfile_offset number of bytes to skip at start of new_keyfile * - * @return allocated key slot number or negative errno otherwise. + * @return allocated key slot number or negative errno value otherwise. */ int crypt_keyslot_add_by_keyfile_device_offset(struct crypt_device *cd, int keyslot, @@ -1123,11 +1142,11 @@ int crypt_keyslot_add_by_keyfile(struct crypt_device *cd, * @param cd crypt device handle * @param keyslot requested keyslot or CRYPT_ANY_SLOT * @param volume_key provided volume key or @e NULL if used after crypt_format - * @param volume_key_size size of volume_key + * @param volume_key_size size of volume_key in bytes * @param passphrase passphrase for new keyslot * @param passphrase_size size of passphrase * - * @return allocated key slot number or negative errno otherwise. + * @return allocated key slot number or negative errno value otherwise. */ int crypt_keyslot_add_by_volume_key(struct crypt_device *cd, int keyslot, @@ -1153,12 +1172,12 @@ int crypt_keyslot_add_by_volume_key(struct crypt_device *cd, * @param cd crypt device handle * @param keyslot requested keyslot or CRYPT_ANY_SLOT * @param volume_key provided volume key or @e NULL (see note below) - * @param volume_key_size size of volume_key + * @param volume_key_size size of volume_key in bytes * @param passphrase passphrase for new keyslot * @param passphrase_size size of passphrase * @param flags key flags to set * - * @return allocated key slot number or negative errno otherwise. + * @return allocated key slot number or negative errno value otherwise. * * @note in case volume_key is @e NULL following first matching rule will apply: * @li if cd is device handle used in crypt_format() by current process, the volume @@ -1203,7 +1222,11 @@ void crypt_keyslot_context_free(struct crypt_keyslot_context *kc); * @param passphrase_size size of passphrase * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_PASSPHRASE * - * @return zero on success or negative errno otherwise. + * @return zero on success or negative errno value otherwise. + * + * @note The original buffer containing passphrase passed in parameters does + * not have to be valid after context initialization. The context + * contains copy of the original before freed with @link crypt_keyslot_context_free @endlink. */ int crypt_keyslot_context_init_by_passphrase(struct crypt_device *cd, const char *passphrase, @@ -1220,7 +1243,7 @@ int crypt_keyslot_context_init_by_passphrase(struct crypt_device *cd, * @param keyfile_offset number of bytes to skip at start of keyfile * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_KEYFILE * - * @return zero on success or negative errno otherwise. + * @return zero on success or negative errno value otherwise. */ int crypt_keyslot_context_init_by_keyfile(struct crypt_device *cd, const char *keyfile, @@ -1240,7 +1263,7 @@ int crypt_keyslot_context_init_by_keyfile(struct crypt_device *cd, * @param usrptr provided identification in callback * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_TOKEN * - * @return zero on success or negative errno otherwise. + * @return zero on success or negative errno value otherwise. */ int crypt_keyslot_context_init_by_token(struct crypt_device *cd, int token, @@ -1256,10 +1279,10 @@ int crypt_keyslot_context_init_by_token(struct crypt_device *cd, * * @param volume_key provided volume key or @e NULL if used after crypt_format * or with CRYPT_VOLUME_KEY_NO_SEGMENT flag - * @param volume_key_size size of volume_key + * @param volume_key_size size of volume_key in bytes * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_KEY * - * @return zero on success or negative errno otherwise. + * @return zero on success or negative errno value otherwise. */ int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd, const char *volume_key, @@ -1272,12 +1295,12 @@ int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd, * @param cd crypt device handle initialized to device context * * @param volume_key provided volume key - * @param volume_key_size size of volume_key + * @param volume_key_size size of volume_key in bytes * @param signature buffer with signature for the key - * @param signature_size bsize of signature buffer + * @param signature_size size of signature buffer * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_SIGNED_KEY * - * @return zero on success or negative errno otherwise. + * @return zero on success or negative errno value otherwise. * * @note currently supported only with VERITY devices. */ @@ -1297,7 +1320,7 @@ int crypt_keyslot_context_init_by_signed_key(struct crypt_device *cd, * for passphrase in * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_KEYRING * - * @return zero on success or negative errno otherwise. + * @return zero on success or negative errno value otherwise. */ int crypt_keyslot_context_init_by_keyring(struct crypt_device *cd, const char *key_description, @@ -1313,7 +1336,7 @@ int crypt_keyslot_context_init_by_keyring(struct crypt_device *cd, * or a text representation in the form "%:" * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_KEYRING * - * @return zero on success or negative errno otherwise. + * @return zero on success or negative errno value otherwise. */ int crypt_keyslot_context_init_by_vk_in_keyring(struct crypt_device *cd, const char *key_description, @@ -1343,7 +1366,7 @@ int crypt_keyslot_context_get_error(struct crypt_keyslot_context *kc); * @param pin_size size of @e pin * @param kc LUKS2 keyslot context (only @link CRYPT_KC_TYPE_TOKEN @endlink is allowed) * - * @return zero on success or negative errno otherwise + * @return zero on success or negative errno value otherwise */ int crypt_keyslot_context_set_pin(struct crypt_device *cd, const char *pin, size_t pin_size, @@ -1379,7 +1402,7 @@ int crypt_keyslot_context_set_pin(struct crypt_device *cd, * * @param kc keyslot context * - * @return crypt keyslot context type id (see @link crypt-keyslot-context-types @endlink) or negative errno otherwise. + * @return crypt keyslot context type id (see @link crypt-keyslot-context-types @endlink) or negative errno value otherwise. */ int crypt_keyslot_context_get_type(const struct crypt_keyslot_context *kc); /** @} */ @@ -1398,7 +1421,7 @@ int crypt_keyslot_context_get_type(const struct crypt_keyslot_context *kc); * @param new_kc keyslot context providing passphrase for new keyslot. * @param flags key flags to set * - * @return allocated key slot number or negative errno otherwise. + * @return allocated key slot number or negative errno value otherwise. * * @note new_kc can not be @e CRYPT_KC_TYPE_KEY type keyslot context. * @@ -1504,6 +1527,12 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot); #define CRYPT_ACTIVATE_RECALCULATE_RESET (UINT32_C(1) << 26) /** dm-verity: try to use tasklets */ #define CRYPT_ACTIVATE_TASKLETS (UINT32_C(1) << 27) +/** dm-crypt: use high-priority workqueues */ +#define CRYPT_ACTIVATE_HIGH_PRIORITY (UINT32_C(1) << 28) +/** dm-verity: also restart/panic on error, use with RESTART_ON_CORRUPTION or PANIC_ON_CORRUPTION */ +#define CRYPT_ACTIVATE_ERROR_AS_CORRUPTION (UINT32_C(1) << 29) +/** dm-integrity: inline mode for compatible hardware profile */ +#define CRYPT_ACTIVATE_INLINE_MODE (UINT32_C(1) << 30) /** * Active device runtime attributes @@ -1523,7 +1552,6 @@ struct crypt_active_device { * @param cad preallocated active device attributes to fill * * @return @e 0 on success or negative errno value otherwise - * */ int crypt_get_active_device(struct crypt_device *cd, const char *name, @@ -1536,7 +1564,6 @@ int crypt_get_active_device(struct crypt_device *cd, * @param name name of active device * * @return number of integrity failures or @e 0 otherwise - * */ uint64_t crypt_get_active_integrity_failures(struct crypt_device *cd, const char *name); @@ -1557,6 +1584,8 @@ uint64_t crypt_get_active_integrity_failures(struct crypt_device *cd, #define CRYPT_REQUIREMENT_ONLINE_REENCRYPT (UINT32_C(1) << 1) /** Device configured with OPAL support */ #define CRYPT_REQUIREMENT_OPAL (UINT32_C(1) << 2) +/** Device configured with inline HW tags */ +#define CRYPT_REQUIREMENT_INLINE_HW_TAGS (UINT32_C(1) << 3) /** unknown requirement in header (output only) */ #define CRYPT_REQUIREMENT_UNKNOWN (UINT32_C(1) << 31) @@ -1579,9 +1608,11 @@ typedef enum { * * @note Valid only for LUKS2. * - * @note Not all activation flags can be stored. Only ALLOW_DISCARD, - * SAME_CPU_CRYPT, SUBMIT_FROM_CRYPT_CPU and NO_JOURNAL can be - * stored persistently. + * @note Not all activation flags can be stored. Only CRYPT_ACTIVATE_ALLOW_DISCARDS, + * CRYPT_ACTIVATE_SAME_CPU_CRYPT, CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS, + * CRYPT_ACTIVATE_NO_JOURNAL, CRYPT_ACTIVATE_NO_READ_WORKQUEUE, + * CRYPT_ACTIVATE_NO_WRITE_WORKQUEUE and CRYPT_ACTIVATE_HIGH_PRIORITY + * can be stored persistently. * * @note Only requirements flags recognised by current library may be set. * CRYPT_REQUIREMENT_UNKNOWN is illegal (output only) in set operation. @@ -1633,7 +1664,7 @@ int crypt_persistent_flags_get(struct crypt_device *cd, * @param flags activation flags * * @return unlocked key slot number for passphrase-based unlock, zero for other - * unlock methods (e.g. volume key context) or negative errno on error. + * unlock methods (e.g. volume key context) or negative errno value on error. */ int crypt_activate_by_keyslot_context(struct crypt_device *cd, const char *name, @@ -1653,7 +1684,7 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd, * @param passphrase_size size of @e passphrase * @param flags activation flags * - * @return unlocked key slot number or negative errno otherwise. + * @return unlocked key slot number or negative errno value otherwise. */ int crypt_activate_by_passphrase(struct crypt_device *cd, const char *name, @@ -1669,11 +1700,11 @@ int crypt_activate_by_passphrase(struct crypt_device *cd, * @param name name of device to create, if @e NULL only check keyfile * @param keyslot requested keyslot to check or CRYPT_ANY_SLOT * @param keyfile key file used to unlock volume key - * @param keyfile_size number of bytes to read from keyfile, 0 is unlimited + * @param keyfile_size number of bytes to read from keyfile, @e 0 is unlimited * @param keyfile_offset number of bytes to skip at start of keyfile * @param flags activation flags * - * @return unlocked key slot number or negative errno otherwise. + * @return unlocked key slot number or negative errno value otherwise. */ int crypt_activate_by_keyfile_device_offset(struct crypt_device *cd, const char *name, @@ -1710,7 +1741,7 @@ int crypt_activate_by_keyfile(struct crypt_device *cd, * @param cd crypt device handle * @param name name of device to create, if @e NULL only check volume key * @param volume_key provided volume key (or @e NULL to use internal) - * @param volume_key_size size of volume_key + * @param volume_key_size size of volume_key in bytes * @param flags activation flags * * @return @e 0 on success or negative errno value otherwise. @@ -1721,7 +1752,7 @@ int crypt_activate_by_keyfile(struct crypt_device *cd, * @note For VERITY the volume key means root hash required for activation. * Because kernel dm-verity is always read only, you have to provide * CRYPT_ACTIVATE_READONLY flag always. - * @note For TCRYPT the volume key should be always NULL + * @note For TCRYPT the volume key should be always @e NULL * the key from decrypted header is used instead. * @note For BITLK the name cannot be @e NULL checking volume key is not * supported for BITLK, the device will be activated even if the @@ -1739,9 +1770,9 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, * @param cd crypt device handle * @param name name of device to create * @param volume_key provided volume key - * @param volume_key_size size of volume_key + * @param volume_key_size size of volume_key in bytes * @param signature buffer with signature for the key - * @param signature_size bsize of signature buffer + * @param signature_size size of signature buffer * @param flags activation flags * * @return @e 0 on success or negative errno value otherwise. @@ -1797,7 +1828,6 @@ int crypt_activate_by_keyring(struct crypt_device *cd, * @param flags deactivation flags * * @return @e 0 on success or negative errno value otherwise. - * */ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, @@ -1822,11 +1852,11 @@ int crypt_deactivate(struct crypt_device *cd, const char *name); * @param keyslot use this keyslot or @e CRYPT_ANY_SLOT * @param volume_key buffer for volume key * @param volume_key_size on input, size of buffer @e volume_key, - * on output size of @e volume_key + * on output size of @e volume_key in bytes * @param passphrase passphrase used to unlock volume key * @param passphrase_size size of @e passphrase * - * @return unlocked key slot number or negative errno otherwise. + * @return unlocked key slot number or negative errno value otherwise. * * @note For TCRYPT cipher chain is the volume key concatenated * for all ciphers in chain. @@ -1849,17 +1879,17 @@ int crypt_volume_key_get(struct crypt_device *cd, * @param keyslot use this keyslot or @e CRYPT_ANY_SLOT * @param volume_key buffer for volume key * @param volume_key_size on input, size of buffer @e volume_key, - * on output size of @e volume_key + * on output size of @e volume_key in bytes * @param kc keyslot context used to unlock volume key * - * @return unlocked key slot number or negative errno otherwise. + * @return unlocked key slot number or negative errno value otherwise. * * @note See @link crypt-keyslot-context-types @endlink for info on keyslot * context initialization. * @note For TCRYPT cipher chain is the volume key concatenated - * for all ciphers in chain (kc may be NULL). + * for all ciphers in chain (kc may be @e NULL). * @note For VERITY the volume key means root hash used for activation - * (kc may be NULL). + * (kc may be @e NULL). * @note For LUKS devices, if kc is @e NULL and volume key is cached in * device context it returns the volume key generated in preceding * @link crypt_format @endlink call. @@ -1882,7 +1912,7 @@ int crypt_volume_key_get_by_keyslot_context(struct crypt_device *cd, * * @param cd crypt device handle * @param volume_key provided volume key - * @param volume_key_size size of @e volume_key + * @param volume_key_size size of @e volume_key in bytes * * @return @e 0 on success or negative errno value otherwise. * @@ -1918,7 +1948,6 @@ typedef enum { * @param name crypt device name * * @return value defined by crypt_status_info. - * */ crypt_status_info crypt_status(struct crypt_device *cd, const char *name); @@ -1935,7 +1964,7 @@ int crypt_dump(struct crypt_device *cd); * Dump JSON-formatted information about LUKS2 device * * @param cd crypt device handle (only LUKS2 format supported) - * @param json buffer with JSON, if NULL use log callback for output + * @param json buffer with JSON, if @e NULL use log callback for output * @param flags dump flags (reserved) * * @return @e 0 on success or negative errno value otherwise. @@ -1948,7 +1977,6 @@ int crypt_dump_json(struct crypt_device *cd, const char **json, uint32_t flags); * @param cd crypt device handle * * @return used cipher, e.g. "aes" or @e NULL otherwise - * */ const char *crypt_get_cipher(struct crypt_device *cd); @@ -1958,7 +1986,6 @@ const char *crypt_get_cipher(struct crypt_device *cd); * @param cd crypt device handle * * @return used cipher mode e.g. "xts-plain" or @e otherwise - * */ const char *crypt_get_cipher_mode(struct crypt_device *cd); @@ -1968,7 +1995,6 @@ const char *crypt_get_cipher_mode(struct crypt_device *cd); * @param cd crypt device handle * * @return device UUID or @e NULL if not set - * */ const char *crypt_get_uuid(struct crypt_device *cd); @@ -1978,7 +2004,6 @@ const char *crypt_get_uuid(struct crypt_device *cd); * @param cd crypt device handle * * @return path to underlying device name - * */ const char *crypt_get_device_name(struct crypt_device *cd); @@ -1988,7 +2013,6 @@ const char *crypt_get_device_name(struct crypt_device *cd); * @param cd crypt device handle * * @return path to underlying device name - * */ const char *crypt_get_metadata_device_name(struct crypt_device *cd); @@ -1998,7 +2022,6 @@ const char *crypt_get_metadata_device_name(struct crypt_device *cd); * @param cd crypt device handle * * @return device offset in sectors - * */ uint64_t crypt_get_data_offset(struct crypt_device *cd); @@ -2008,7 +2031,6 @@ uint64_t crypt_get_data_offset(struct crypt_device *cd); * @param cd crypt device handle * * @return IV offset - * */ uint64_t crypt_get_iv_offset(struct crypt_device *cd); @@ -2024,13 +2046,25 @@ uint64_t crypt_get_iv_offset(struct crypt_device *cd); */ int crypt_get_volume_key_size(struct crypt_device *cd); +/** + * Get size (in bytes) of old volume key for LUKS2 device in reencryption. + * + * @param cd crypt LUKS2 device handle + * + * @return old volume key size when device is in reencryption state + * + * @note For LUKS2, this function can be used only if there is at least + * one keyslot assigned to old data segment. Also with reencryption + * mode 'encrypt' there's no old volume key. + */ +int crypt_get_old_volume_key_size(struct crypt_device *cd); + /** * Get size (in bytes) of encryption sector for crypt device. * * @param cd crypt device handle * * @return sector size - * */ int crypt_get_sector_size(struct crypt_device *cd); @@ -2056,7 +2090,6 @@ int crypt_header_is_detached(struct crypt_device *cd); * @param vp verity device info * * @e 0 on success or negative errno value otherwise. - * */ int crypt_get_verity_info(struct crypt_device *cd, struct crypt_params_verity *vp); @@ -2068,7 +2101,6 @@ int crypt_get_verity_info(struct crypt_device *cd, * @param ip verity device info * * @e 0 on success or negative errno value otherwise. - * */ int crypt_get_integrity_info(struct crypt_device *cd, struct crypt_params_integrity *ip); @@ -2116,7 +2148,7 @@ int crypt_benchmark(struct crypt_device *cd, * @param password_size size of password * @param salt salt for benchmark * @param salt_size size of salt - * @param volume_key_size output volume key size + * @param volume_key_size output volume key size in bytes * @param progress callback function * @param usrptr provided identification in callback * @@ -2158,7 +2190,6 @@ typedef enum { * @param keyslot requested keyslot to check or CRYPT_ANY_SLOT * * @return value defined by crypt_keyslot_info - * */ crypt_keyslot_info crypt_keyslot_status(struct crypt_device *cd, int keyslot); @@ -2198,7 +2229,7 @@ int crypt_keyslot_set_priority(struct crypt_device *cd, int keyslot, crypt_keysl * * @param type crypt device type * - * @return slot count or negative errno otherwise if device + * @return slot count or negative errno value otherwise if device * doesn't not support keyslots. */ int crypt_keyslot_max(const char *type); @@ -2212,7 +2243,6 @@ int crypt_keyslot_max(const char *type); * @param length length of keyslot area (in bytes) * * @return @e 0 on success or negative errno value otherwise. - * */ int crypt_keyslot_area(struct crypt_device *cd, int keyslot, @@ -2269,7 +2299,7 @@ int crypt_keyslot_get_pbkdf(struct crypt_device *cd, int keyslot, struct crypt_p * @return @e 0 on success or negative errno value otherwise. * * @note To reset to default keyslot encryption (the same as for data) - * set cipher to NULL and key size to 0. + * set cipher to @e NULL and key size to 0. */ int crypt_keyslot_set_encryption(struct crypt_device *cd, const char *cipher, @@ -2297,7 +2327,6 @@ const char *crypt_get_dir(void); * @param backup_file file to backup header to * * @return @e 0 on success or negative errno value otherwise. - * */ int crypt_header_backup(struct crypt_device *cd, const char *requested_type, @@ -2336,7 +2365,6 @@ int crypt_header_restore(struct crypt_device *cd, * Set the debug level for library * * @param level debug level - * */ void crypt_set_debug_level(int level); /** @} */ @@ -2353,8 +2381,8 @@ void crypt_set_debug_level(int level); * @param cd crypt device handle * @param keyfile keyfile to read * @param key buffer for key - * @param key_size_read size of read key - * @param keyfile_offset key offset in keyfile + * @param key_size_read size of read key in bytes + * @param keyfile_offset key offset in bytes in keyfile * @param key_size exact key length to read from file or 0 * @param flags keyfile read flags * @@ -2481,7 +2509,7 @@ int crypt_wipe_hw_opal(struct crypt_device *cd, * * @param type crypt device type * - * @return token count or negative errno otherwise if device + * @return token count or negative errno value otherwise if device * doesn't not support tokens. * * @note Real number of supported tokens for a particular device depends @@ -2499,7 +2527,7 @@ int crypt_token_max(const char *type); * @param token token id * @param json buffer with JSON * - * @return allocated token id or negative errno otherwise. + * @return allocated token id or negative errno value otherwise. */ int crypt_token_json_get(struct crypt_device *cd, int token, @@ -2512,7 +2540,7 @@ int crypt_token_json_get(struct crypt_device *cd, * @param token token id or @e CRYPT_ANY_TOKEN to allocate new one * @param json buffer with JSON or @e NULL to remove token * - * @return allocated token id or negative errno otherwise. + * @return allocated token id or negative errno value otherwise. * * @note The buffer must be in proper JSON format and must contain at least * string "type" with slot type and an array of string names "keyslots". @@ -2542,7 +2570,7 @@ typedef enum { * @param type pointer for returned type string * * @return token status info. For any returned status (besides CRYPT_TOKEN_INVALID - * and CRYPT_TOKEN_INACTIVE) and if type parameter is not NULL it will + * and CRYPT_TOKEN_INACTIVE) and if type parameter is not @e NULL it will * contain address of type string. * * @note if required, create a copy of string referenced in *type before calling next @@ -2554,7 +2582,6 @@ crypt_token_info crypt_token_status(struct crypt_device *cd, int token, const ch * LUKS2 keyring token parameters. * * @see crypt_token_builtin_set - * */ struct crypt_token_params_luks2_keyring { const char *key_description; /**< Reference in keyring */ @@ -2567,8 +2594,7 @@ struct crypt_token_params_luks2_keyring { * @param token token id or @e CRYPT_ANY_TOKEN to allocate new one * @param params luks2 keyring token params * - * @return allocated token id or negative errno otherwise. - * + * @return allocated token id or negative errno value otherwise. */ int crypt_token_luks2_keyring_set(struct crypt_device *cd, int token, @@ -2581,7 +2607,7 @@ int crypt_token_luks2_keyring_set(struct crypt_device *cd, * @param token existing luks2 keyring token id * @param params returned luks2 keyring token params * - * @return allocated token id or negative errno otherwise. + * @return allocated token id or negative errno value otherwise. * * @note do not call free() on params members. Members are valid only * until next libcryptsetup function is called. @@ -2595,11 +2621,11 @@ int crypt_token_luks2_keyring_get(struct crypt_device *cd, * (There can be more keyslots assigned to one token id.) * * @param cd crypt device handle - * @param token token id + * @param token specific token id * @param keyslot keyslot to be assigned to token (CRYPT_ANY SLOT * assigns all active keyslots to token) * - * @return allocated token id or negative errno otherwise. + * @return requested token id to be assigned or negative errno value otherwise. */ int crypt_token_assign_keyslot(struct crypt_device *cd, int token, @@ -2610,11 +2636,11 @@ int crypt_token_assign_keyslot(struct crypt_device *cd, * (There can be more keyslots assigned to one token id.) * * @param cd crypt device handle - * @param token token id + * @param token specific token id * @param keyslot keyslot to be unassigned from token (CRYPT_ANY SLOT * unassigns all active keyslots from token) * - * @return allocated token id or negative errno otherwise. + * @return requested token id to be unassigned or negative errno value otherwise. */ int crypt_token_unassign_keyslot(struct crypt_device *cd, int token, @@ -2627,9 +2653,9 @@ int crypt_token_unassign_keyslot(struct crypt_device *cd, * @param token token id * @param keyslot keyslot * - * @return 0 on success (token exists and is assigned to the keyslot), + * @return @e 0 on success (token exists and is assigned to the keyslot), * -ENOENT if token is not assigned to a keyslot (token, keyslot - * or both may be inactive) or other negative errno otherwise. + * or both may be inactive) or other negative errno value otherwise. */ int crypt_token_is_assigned(struct crypt_device *cd, int token, @@ -2647,8 +2673,8 @@ int crypt_token_is_assigned(struct crypt_device *cd, * @param buffer_len length of the buffer * @param usrptr user data in @link crypt_activate_by_token @endlink * - * @return 0 on success (token passed LUKS2 keyslot passphrase in buffer) or - * negative errno otherwise. + * @return @e 0 on success (token passed LUKS2 keyslot passphrase in buffer) or + * negative errno value otherwise. * * @note Negative ENOANO errno means that token is PIN protected and caller should * use @link crypt_activate_by_token_pin @endlink with PIN provided. @@ -2677,8 +2703,8 @@ typedef int (*crypt_token_open_func) ( * @param buffer_len length of the buffer * @param usrptr user data in @link crypt_activate_by_token @endlink * - * @return 0 on success (token passed LUKS2 keyslot passphrase in buffer) or - * negative errno otherwise. + * @return @e 0 on success (token passed LUKS2 keyslot passphrase in buffer) or + * negative errno value otherwise. * * @note Negative ENOANO errno means that token is PIN protected and PIN was * missing or wrong. @@ -2738,7 +2764,6 @@ typedef void (*crypt_token_dump_func) (struct crypt_device *cd, const char *json * * @note The returned string is advised to contain only version. * For example '1.0.0' or 'v1.2.3.4'. - * */ typedef const char * (*crypt_token_version_func) (void); @@ -2811,7 +2836,7 @@ void crypt_token_external_disable(void); * @param usrptr provided identification in callback * @param flags activation flags * - * @return unlocked key slot number or negative errno otherwise. + * @return unlocked key slot number or negative errno value otherwise. * * @note EPERM errno means token provided passphrase successfully, but * passphrase did not unlock any keyslot associated with the token. @@ -2828,7 +2853,7 @@ void crypt_token_external_disable(void); * @note with @e token set to CRYPT_ANY_TOKEN libcryptsetup runs best effort loop * to unlock device using any available token. It may happen that various token handlers * return different error codes. At the end loop returns error codes in the following - * order (from the most significant to the least) any negative errno except those + * order (from the most significant to the least) any negative errno value except those * listed below, non negative token id (success), -ENOANO, -EAGAIN, -EPERM, -ENOENT. */ int crypt_activate_by_token(struct crypt_device *cd, @@ -2849,7 +2874,7 @@ int crypt_activate_by_token(struct crypt_device *cd, * @param usrptr provided identification in callback * @param flags activation flags * - * @return unlocked key slot number or negative errno otherwise. + * @return unlocked key slot number or negative errno value otherwise. * * @note EPERM errno means token provided passphrase successfully, but * passphrase did not unlock any keyslot associated with the token. @@ -2858,7 +2883,7 @@ int crypt_activate_by_token(struct crypt_device *cd, * eligible to unlock device. * * @note ENOANO errno means that token is PIN protected and was either missing - * (NULL) or wrong. + * (@e NULL) or wrong. * * @note Negative EAGAIN errno means token handler requires additional hardware * not present in the system. @@ -2866,7 +2891,7 @@ int crypt_activate_by_token(struct crypt_device *cd, * @note with @e token set to CRYPT_ANY_TOKEN libcryptsetup runs best effort loop * to unlock device using any available token. It may happen that various token handlers * return different error codes. At the end loop returns error codes in the following - * order (from the most significant to the least) any negative errno except those + * order (from the most significant to the least) any negative errno value except those * listed below, non negative token id (success), -ENOANO, -EAGAIN, -EPERM, -ENOENT. */ int crypt_activate_by_token_pin(struct crypt_device *cd, @@ -2899,6 +2924,12 @@ int crypt_activate_by_token_pin(struct crypt_device *cd, #define CRYPT_REENCRYPT_RECOVERY (UINT32_C(1) << 3) /** Reencryption requires metadata protection. (in/out) */ #define CRYPT_REENCRYPT_REPAIR_NEEDED (UINT32_C(1) << 4) +/** + * Calculate new (future) volume key digest directly during + * reencryption initialization. The keyslot context for new + * volume key must be CRYPT_KC_TYPE_KEY or + * CRYPT_KC_TYPE_VK_KEYRING. (in) */ +#define CRYPT_REENCRYPT_CREATE_NEW_DIGEST (UINT32_C(1) << 5) /** * Reencryption direction @@ -2955,7 +2986,7 @@ struct crypt_params_reencrypt { * @param cipher_mode cipher mode and IV (e.g. "xts-plain64") * @param params reencryption parameters @link crypt_params_reencrypt @endlink. * - * @return reencryption key slot number or negative errno otherwise. + * @return reencryption key slot number or negative errno value otherwise. */ int crypt_reencrypt_init_by_passphrase(struct crypt_device *cd, const char *name, @@ -2984,7 +3015,7 @@ int crypt_reencrypt_init_by_passphrase(struct crypt_device *cd, * @param cipher_mode cipher mode and IV (e.g. "xts-plain64") * @param params reencryption parameters @link crypt_params_reencrypt @endlink. * - * @return reencryption key slot number or negative errno otherwise. + * @return reencryption key slot number or negative errno value otherwise. */ int crypt_reencrypt_init_by_keyring(struct crypt_device *cd, const char *name, @@ -2995,6 +3026,71 @@ int crypt_reencrypt_init_by_keyring(struct crypt_device *cd, const char *cipher_mode, const struct crypt_params_reencrypt *params); +/** + * + * Initialize or reload LUKS2 reencryption operation using keyslot contexts. + * + * The function can initialize reencryption on-disk metadata or reload reencryption + * context from on-disk LUSK2 metadata to resume interrupted operation. + * + * If the device is not in reencryption state (@link crypt_reencrypt_status @endlink + * returns @link CRYPT_REENCRYPT_NONE @endlink) the function initializes on-disk + * metadata to include all necessary reencryption segments and new encryption + * parameters (cipher, cipher mode, encryption sector size) according to the + * provided parameters. + * + * If on-disk metadata already describes reencryption operation + * (@link crypt_reencrypt_status @endlink returns @link CRYPT_REENCRYPT_CLEAN @endlink), + * it loads these parameters and internally initializes reencryption context. It also verifies + * if the device is eligible to resume reencryption operation. Some reencryption parameters + * (@link crypt_params_reencrypt @endlink) may be modified depending on the original values in + * the initialization call. When resuming the operation, all parameters may be omitted except + * @e cd, @e name (offline/online),@e kc_old and @e kc_new. + * + * If on-disk metadata describes reencryption operation requiring recovery + * (@link crypt_reencrypt_status @endlink returns @link CRYPT_REENCRYPT_CRASH @endlink), + * it can be recovered by adding @link CRYPT_REENCRYPT_RECOVERY @endlink flag in @link + * crypt_params_reencrypt @endlink parameter. + * + * @param cd crypt device handle + * @param name name of the active device or @e NULL for offline reencryption + * @param kc_old keyslot context providing access to volume key in keyslot id @e keyslot_old. + * @param kc_new keyslot context providing access to volume key in keyslot id @e keyslot_new. + * @param keyslot_old keyslot id containing current volume key for the device or CRYPT_ANY_SLOT + * @param keyslot_new keyslot id containing (unbound) future volume key in encryption or reencryption + * operation. It must be set in the initialization call except when initializing the decrypt + * operation. In reencryption operation it may contain also the current volume key in case the + * volume key change is not requested. + * @param cipher new cipher specification (e.g. "aes") or @e NULL in decryption. Relevant only + * during metadata initialization. + * @param cipher_mode cipher mode and IV (e.g. "xts-plain64") or @e NULL in decryption. + * Relevant only during metadata initialization. + * @param params reencryption parameters @link crypt_params_reencrypt @endlink. + * + * @return reencryption key slot number or negative errno value otherwise. + * + * @note Only after successful reencryption initialization you may run the operation with + * @link crypt_reencrypt_run @endlink. + * + * @note During @link CRYPT_REENCRYPT_REENCRYPT @endlink operation it is highly recommended + * to use same keyslot context (same passphrase, token, keyfile, etc) in both @e kc_old + * and @e kc_new parameters for at least one keyslot containing future volume key and one + * keyslot containing current volume key. If the same keyslot context can not be used + * to unlock any current or any future volume key it would be impossible to perform reencryption + * crash recovery during device activation for example after system reboot. Any keyslot + * passphrase may be changed in-before initializing reencryption operation via @link + * crypt_keyslot_change_by_passphrase @endlink. + */ +int crypt_reencrypt_init_by_keyslot_context(struct crypt_device *cd, + const char *name, + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new, + int keyslot_old, + int keyslot_new, + const char *cipher, + const char *cipher_mode, + const struct crypt_params_reencrypt *params); + /** * Legacy data reencryption function. * @@ -3089,6 +3185,15 @@ void *crypt_safe_realloc(void *data, size_t size); */ void crypt_safe_memzero(void *data, size_t size); +/** + * Memcpy helper to avoid spilling sensitive data through additional registers + * + * @param dst pointer to memory to be written + * @param src pointer to memory to be copied + * @param size size of memory in bytes + */ +void *crypt_safe_memcpy(void *dst, const void *src, size_t size); + /** @} */ /** @@ -3107,7 +3212,7 @@ void crypt_safe_memzero(void *data, size_t size); * * The @e old_key_description argument is required only for * devices that are in re-encryption and have two volume keys at the same time - * (old and new). You can set the @e old_key_description to NULL, + * (old and new). You can set the @e old_key_description to @e NULL, * but if you supply number of keys less than required, the function will * return -ESRCH. In that case you need to call the function again and set * the missing key description. When supplying just one key description, make diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym index 89d6468..3a54431 100644 --- a/lib/libcryptsetup.sym +++ b/lib/libcryptsetup.sym @@ -180,3 +180,18 @@ CRYPTSETUP_2.7 { crypt_set_keyring_to_link; crypt_wipe_hw_opal; } CRYPTSETUP_2.6; + +CRYPTSETUP_2.8 { + global: + crypt_safe_memcpy; + crypt_keyslot_context_init_by_passphrase; + crypt_keyslot_context_init_by_keyfile; + crypt_keyslot_context_init_by_token; + crypt_keyslot_context_init_by_volume_key; + crypt_keyslot_context_init_by_signed_key; + crypt_keyslot_context_init_by_keyring; + crypt_keyslot_context_init_by_vk_in_keyring; + crypt_reencrypt_init_by_keyslot_context; + crypt_get_old_volume_key_size; + crypt_format_inline; +} CRYPTSETUP_2.7; diff --git a/lib/libcryptsetup_macros.h b/lib/libcryptsetup_macros.h index 00af58c..657c48f 100644 --- a/lib/libcryptsetup_macros.h +++ b/lib/libcryptsetup_macros.h @@ -2,8 +2,8 @@ /* * Definitions of common constant and generic macros of libcryptsetup * - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #ifndef _LIBCRYPTSETUP_MACROS_H @@ -49,9 +49,17 @@ #define DEFAULT_MEM_ALIGNMENT 4096 #define DM_UUID_LEN 129 +#define DM_NAME_LEN 128 #define DM_BY_ID_PREFIX "dm-uuid-" #define DM_BY_ID_PREFIX_LEN 8 #define DM_UUID_PREFIX "CRYPT-" #define DM_UUID_PREFIX_LEN 6 +#define OPAL_PSID_LEN 32 + +/* LUKS AF stripes, never set to any other value than 4000 */ +#ifndef LUKS_STRIPES +# define LUKS_STRIPES 4000 +#endif + #endif /* _LIBCRYPTSETUP_MACROS_H */ diff --git a/lib/libcryptsetup_symver.h b/lib/libcryptsetup_symver.h index 62d3018..6d18ab5 100644 --- a/lib/libcryptsetup_symver.h +++ b/lib/libcryptsetup_symver.h @@ -2,7 +2,7 @@ /* * Helpers for defining versioned symbols * - * Copyright (C) 2021-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2021-2025 Red Hat, Inc. All rights reserved. */ #ifndef _LIBCRYPTSETUP_SYMVER_H diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index d7eadc8..743efb9 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include @@ -15,7 +15,7 @@ #include #include #include -#ifdef HAVE_SYS_SYSMACROS_H +#if HAVE_SYS_SYSMACROS_H # include /* for major, minor */ #endif #include "internal.h" @@ -36,7 +36,7 @@ static bool _dm_integrity_checked = false; static bool _dm_zero_checked = false; static int _quiet_log = 0; -static uint32_t _dm_flags = 0; +static uint64_t _dm_flags = 0; static struct crypt_device *_context = NULL; static int _dm_use_count = 0; @@ -61,7 +61,7 @@ static int _dm_udev_wait(uint32_t cookie) { return 0; }; static int _dm_use_udev(void) { -#ifdef USE_UDEV /* cannot be enabled if devmapper is too old */ +#if USE_UDEV /* cannot be enabled if devmapper is too old */ return dm_udev_get_sync_support(); #else return 0; @@ -157,6 +157,12 @@ static void _dm_set_crypt_compat(struct crypt_device *cd, if (_dm_satisfies_version(1, 22, 0, crypt_maj, crypt_min, crypt_patch)) _dm_flags |= DM_CRYPT_NO_WORKQUEUE_SUPPORTED; + if (_dm_satisfies_version(1, 26, 0, crypt_maj, crypt_min, crypt_patch)) + _dm_flags |= DM_CRYPT_HIGH_PRIORITY_SUPPORTED; + + if (_dm_satisfies_version(1, 28, 0, crypt_maj, crypt_min, crypt_patch)) + _dm_flags |= DM_CRYPT_INTEGRITY_KEY_SIZE_OPT_SUPPORTED; + _dm_crypt_checked = true; } @@ -194,6 +200,10 @@ static void _dm_set_verity_compat(struct crypt_device *cd, if (_dm_satisfies_version(1, 9, 0, verity_maj, verity_min, verity_patch)) _dm_flags |= DM_VERITY_TASKLETS_SUPPORTED; + /* There is actually no correct version set, just use the last available */ + if (_dm_satisfies_version(1, 10, 0, verity_maj, verity_min, verity_patch)) + _dm_flags |= DM_VERITY_ERROR_AS_CORRUPTION_SUPPORTED; + _dm_verity_checked = true; } @@ -228,6 +238,9 @@ static void _dm_set_integrity_compat(struct crypt_device *cd, if (_dm_satisfies_version(1, 8, 0, integrity_maj, integrity_min, integrity_patch)) _dm_flags |= DM_INTEGRITY_RESET_RECALC_SUPPORTED; + if (_dm_satisfies_version(1, 12, 0, integrity_maj, integrity_min, integrity_patch)) + _dm_flags |= DM_INTEGRITY_INLINE_MODE_SUPPORTED; + _dm_integrity_checked = true; } @@ -358,7 +371,7 @@ static int _dm_check_versions(struct crypt_device *cd, dm_target_type target_typ return r; } -int dm_flags(struct crypt_device *cd, dm_target_type target, uint32_t *flags) +int dm_flags(struct crypt_device *cd, dm_target_type target, uint64_t *flags) { _dm_check_versions(cd, target); *flags = _dm_flags; @@ -537,6 +550,7 @@ static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags) int r, max_size, null_cipher = 0, num_options = 0, keystr_len = 0; char *params = NULL, *hexkey = NULL; char sector_feature[32], features[512], integrity_dm[256], cipher_dm[256]; + char int_ksize_feature[32]; if (!tgt) return NULL; @@ -558,22 +572,29 @@ static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags) num_options++; if (flags & CRYPT_ACTIVATE_IV_LARGE_SECTORS) num_options++; + if (flags & CRYPT_ACTIVATE_HIGH_PRIORITY) + num_options++; if (tgt->u.crypt.integrity) num_options++; if (tgt->u.crypt.sector_size != SECTOR_SIZE) num_options++; + if (tgt->u.crypt.integrity && tgt->u.crypt.integrity_key_size) + num_options++; - if (num_options) { /* MAX length int32 + 15 + 15 + 23 + 18 + 19 + 17 + 13 + int32 + integrity_str */ - r = snprintf(features, sizeof(features), " %d%s%s%s%s%s%s%s%s", num_options, + if (num_options) { /* MAX length int32 + 15 + 15 + 23 + 18 + 19 + 17 + 14 + 13 + int32 + integrity_str + 21 + int32 */ + r = snprintf(features, sizeof(features), " %d%s%s%s%s%s%s%s%s%s%s", num_options, (flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) ? " allow_discards" : "", (flags & CRYPT_ACTIVATE_SAME_CPU_CRYPT) ? " same_cpu_crypt" : "", (flags & CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) ? " submit_from_crypt_cpus" : "", (flags & CRYPT_ACTIVATE_NO_READ_WORKQUEUE) ? " no_read_workqueue" : "", (flags & CRYPT_ACTIVATE_NO_WRITE_WORKQUEUE) ? " no_write_workqueue" : "", (flags & CRYPT_ACTIVATE_IV_LARGE_SECTORS) ? " iv_large_sectors" : "", + (flags & CRYPT_ACTIVATE_HIGH_PRIORITY) ? " high_priority" : "", (tgt->u.crypt.sector_size != SECTOR_SIZE) ? _uf(sector_feature, sizeof(sector_feature), "sector_size", tgt->u.crypt.sector_size) : "", - integrity_dm); + integrity_dm, + (tgt->u.crypt.integrity && tgt->u.crypt.integrity_key_size) ? + _uf(int_ksize_feature, sizeof(int_ksize_feature), "integrity_key_size", tgt->u.crypt.integrity_key_size) : ""); if (r < 0 || (size_t)r >= sizeof(features)) goto out; } else @@ -582,19 +603,26 @@ static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags) if (crypt_is_cipher_null(cipher_dm)) null_cipher = 1; - if (null_cipher) + if (null_cipher || crypt_volume_key_length(tgt->u.crypt.vk) == 0) hexkey = crypt_bytes_to_hex(0, NULL); else if (flags & CRYPT_ACTIVATE_KEYRING_KEY) { - keystr_len = strlen(tgt->u.crypt.vk->key_description) + int_log10(tgt->u.crypt.vk->keylength) + 10; + if (!crypt_volume_key_description(tgt->u.crypt.vk) || + crypt_volume_key_kernel_key_type(tgt->u.crypt.vk) == INVALID_KEY) + goto out; + keystr_len = strlen(crypt_volume_key_description(tgt->u.crypt.vk)) + + int_log10(crypt_volume_key_length(tgt->u.crypt.vk)) + + 24 /* type and separators */; hexkey = crypt_safe_alloc(keystr_len); if (!hexkey) goto out; - r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", tgt->u.crypt.vk->keylength, - tgt->u.crypt.vk->key_description); + r = snprintf(hexkey, keystr_len, ":%zu:%s:%s", crypt_volume_key_length(tgt->u.crypt.vk), + key_type_name(crypt_volume_key_kernel_key_type(tgt->u.crypt.vk)), + crypt_volume_key_description(tgt->u.crypt.vk)); if (r < 0 || r >= keystr_len) goto out; } else - hexkey = crypt_bytes_to_hex(tgt->u.crypt.vk->keylength, tgt->u.crypt.vk->key); + hexkey = crypt_bytes_to_hex(crypt_volume_key_length(tgt->u.crypt.vk), + crypt_volume_key_get_key(tgt->u.crypt.vk)); if (!hexkey) goto out; @@ -646,6 +674,8 @@ static char *get_dm_verity_params(const struct dm_target *tgt, uint32_t flags) num_options++; if (flags & CRYPT_ACTIVATE_PANIC_ON_CORRUPTION) num_options++; + if (flags & CRYPT_ACTIVATE_ERROR_AS_CORRUPTION) + num_options++; if (flags & CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) num_options++; if (flags & CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE) @@ -683,10 +713,12 @@ static char *get_dm_verity_params(const struct dm_target *tgt, uint32_t flags) *verity_verify_args = '\0'; if (num_options) { /* MAX length int32 + 18 + 22 + 20 + 19 + 19 + 22 */ - r = snprintf(features, sizeof(features), " %d%s%s%s%s%s%s", num_options, + r = snprintf(features, sizeof(features), " %d%s%s%s%s%s%s%s", num_options, (flags & CRYPT_ACTIVATE_IGNORE_CORRUPTION) ? " ignore_corruption" : "", (flags & CRYPT_ACTIVATE_RESTART_ON_CORRUPTION) ? " restart_on_corruption" : "", (flags & CRYPT_ACTIVATE_PANIC_ON_CORRUPTION) ? " panic_on_corruption" : "", + (flags & CRYPT_ACTIVATE_ERROR_AS_CORRUPTION) ? ((flags & CRYPT_ACTIVATE_PANIC_ON_CORRUPTION) ? + " panic_on_error" : " restart_on_error") : "", (flags & CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) ? " ignore_zero_blocks" : "", (flags & CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE) ? " check_at_most_once" : "", (flags & CRYPT_ACTIVATE_TASKLETS) ? " try_verify_in_tasklet" : ""); @@ -742,13 +774,13 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags if (!tgt) return NULL; - max_integrity = (tgt->u.integrity.integrity && tgt->u.integrity.vk ? tgt->u.integrity.vk->keylength * 2 : 0) + + max_integrity = (tgt->u.integrity.integrity && tgt->u.integrity.vk ? crypt_volume_key_length(tgt->u.integrity.vk) * 2 : 0) + (tgt->u.integrity.integrity ? strlen(tgt->u.integrity.integrity) : 0) + 32; max_journal_integrity = (tgt->u.integrity.journal_integrity && tgt->u.integrity.journal_integrity_key ? - tgt->u.integrity.journal_integrity_key->keylength * 2 : 0) + + crypt_volume_key_length(tgt->u.integrity.journal_integrity_key) * 2 : 0) + (tgt->u.integrity.journal_integrity ? strlen(tgt->u.integrity.journal_integrity) : 0) + 32; max_journal_crypt = (tgt->u.integrity.journal_crypt && tgt->u.integrity.journal_crypt_key ? - tgt->u.integrity.journal_crypt_key->keylength * 2 : 0) + + crypt_volume_key_length(tgt->u.integrity.journal_crypt_key) * 2 : 0) + (tgt->u.integrity.journal_crypt ? strlen(tgt->u.integrity.journal_crypt) : 0) + 32; max_size = strlen(device_block_path(tgt->data_device)) + (tgt->u.integrity.meta_device ? strlen(device_block_path(tgt->u.integrity.meta_device)) : 0) + @@ -766,7 +798,8 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags num_options++; if (tgt->u.integrity.vk) { - hexkey = crypt_bytes_to_hex(tgt->u.integrity.vk->keylength, tgt->u.integrity.vk->key); + hexkey = crypt_bytes_to_hex(crypt_volume_key_length(tgt->u.integrity.vk), + crypt_volume_key_get_key(tgt->u.integrity.vk)); if (!hexkey) goto out; } else @@ -783,8 +816,8 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags num_options++; if (tgt->u.integrity.journal_integrity_key) { - hexkey = crypt_bytes_to_hex( tgt->u.integrity.journal_integrity_key->keylength, - tgt->u.integrity.journal_integrity_key->key); + hexkey = crypt_bytes_to_hex(crypt_volume_key_length(tgt->u.integrity.journal_integrity_key), + crypt_volume_key_get_key(tgt->u.integrity.journal_integrity_key)); if (!hexkey) goto out; } else @@ -801,8 +834,8 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags num_options++; if (tgt->u.integrity.journal_crypt_key) { - hexkey = crypt_bytes_to_hex(tgt->u.integrity.journal_crypt_key->keylength, - tgt->u.integrity.journal_crypt_key->key); + hexkey = crypt_bytes_to_hex(crypt_volume_key_length(tgt->u.integrity.journal_crypt_key), + crypt_volume_key_get_key(tgt->u.integrity.journal_crypt_key)); if (!hexkey) goto out; } else @@ -873,7 +906,9 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags if (r < 0 || r >= max_size) goto out; - if (flags & CRYPT_ACTIVATE_NO_JOURNAL_BITMAP) + if (flags & CRYPT_ACTIVATE_INLINE_MODE) + mode = 'I'; + else if (flags & CRYPT_ACTIVATE_NO_JOURNAL_BITMAP) mode = 'B'; else if (flags & CRYPT_ACTIVATE_RECOVERY) mode = 'R'; @@ -967,7 +1002,7 @@ static int _dm_remove(const char *name, int udev_wait, int deferred) return r; } -static int _dm_simple(int task, const char *name, uint32_t dmflags) +static int _dm_simple(int task, const char *name, uint64_t dmflags) { int r = 0; struct dm_task *dmt; @@ -992,7 +1027,7 @@ static int _dm_simple(int task, const char *name, uint32_t dmflags) return r; } -static int _dm_resume_device(const char *name, uint32_t flags); +static int _dm_resume_device(const char *name, uint64_t dmflags); static int _error_device(const char *name, size_t size) { @@ -1078,7 +1113,7 @@ int dm_remove_device(struct crypt_device *cd, const char *name, uint32_t flags) int retries = (flags & CRYPT_DEACTIVATE_FORCE) ? RETRY_COUNT : 1; int deferred = (flags & CRYPT_DEACTIVATE_DEFERRED) ? 1 : 0; int error_target = 0; - uint32_t dmt_flags; + uint64_t dmt_flags; if (!name) return -EINVAL; @@ -1428,7 +1463,7 @@ static int _dm_create_device(struct crypt_device *cd, const char *name, const ch return r; } -static int _dm_resume_device(const char *name, uint32_t dmflags) +static int _dm_resume_device(const char *name, uint64_t dmflags) { struct dm_task *dmt; int r = -EINVAL; @@ -1610,7 +1645,7 @@ int dm_targets_allocate(struct dm_target *first, unsigned count) return 0; } -static int check_retry(struct crypt_device *cd, uint32_t *dmd_flags, uint32_t dmt_flags) +static int check_retry(struct crypt_device *cd, uint32_t *dmd_flags, uint64_t dmt_flags) { int ret = 0; @@ -1646,6 +1681,14 @@ static int check_retry(struct crypt_device *cd, uint32_t *dmd_flags, uint32_t dm ret = 1; } + /* Drop high-priority workqueue options if not supported */ + if ((*dmd_flags & CRYPT_ACTIVATE_HIGH_PRIORITY) && + !(dmt_flags & DM_CRYPT_HIGH_PRIORITY_SUPPORTED)) { + log_dbg(cd, "dm-crypt does not support high-priority option"); + *dmd_flags = *dmd_flags & ~CRYPT_ACTIVATE_HIGH_PRIORITY; + ret = 1; + } + return ret; } @@ -1653,7 +1696,7 @@ int dm_create_device(struct crypt_device *cd, const char *name, const char *type, struct crypt_dm_active_device *dmd) { - uint32_t dmt_flags = 0; + uint64_t dmt_flags = 0; int r = -EINVAL; if (!type || !dmd) @@ -1698,6 +1741,12 @@ int dm_create_device(struct crypt_device *cd, const char *name, r = -EINVAL; } + if ((dmd->flags & CRYPT_ACTIVATE_ERROR_AS_CORRUPTION) && + !(dmt_flags & DM_VERITY_ERROR_AS_CORRUPTION_SUPPORTED)) { + log_err(cd, _("Requested dm-verity data corruption handling options are not supported.")); + r = -EINVAL; + } + if (dmd->flags & CRYPT_ACTIVATE_TASKLETS && !(dmt_flags & DM_VERITY_TASKLETS_SUPPORTED)) { log_err(cd, _("Requested dm-verity tasklets option is not supported.")); @@ -1730,6 +1779,10 @@ int dm_create_device(struct crypt_device *cd, const char *name, log_err(cd, _("The device size is not multiple of the requested sector size.")); r = -EINVAL; } + if (dmd->segment.u.crypt.integrity_key_size && !(dmt_flags & DM_CRYPT_INTEGRITY_KEY_SIZE_OPT_SUPPORTED)) { + log_err(cd, _("Requested integrity_key_size option is not supported.")); + r = -EINVAL; + } } if (dmd->segment.type == DM_INTEGRITY && (dmd->flags & CRYPT_ACTIVATE_RECALCULATE) && @@ -1755,6 +1808,12 @@ int dm_create_device(struct crypt_device *cd, const char *name, log_err(cd, _("Requested dm-integrity bitmap mode is not supported.")); r = -EINVAL; } + + if (dmd->segment.type == DM_INTEGRITY && (dmd->flags & CRYPT_ACTIVATE_INLINE_MODE) && + !(dmt_flags & DM_INTEGRITY_INLINE_MODE_SUPPORTED)) { + log_err(cd, _("Requested dm-integrity inline mode is not supported.")); + r = -EINVAL; + } out: /* * Print warning if activating dm-crypt cipher_null device unless it's reencryption helper or @@ -1769,10 +1828,10 @@ int dm_create_device(struct crypt_device *cd, const char *name, } int dm_reload_device(struct crypt_device *cd, const char *name, - struct crypt_dm_active_device *dmd, uint32_t dmflags, unsigned resume) + struct crypt_dm_active_device *dmd, uint64_t dmflags, unsigned resume) { int r; - uint32_t dmt_flags; + uint64_t dmt_flags; if (!dmd) return -EINVAL; @@ -1958,18 +2017,19 @@ int dm_status_integrity_failures(struct crypt_device *cd, const char *name, uint } /* FIXME use hex wrapper, user val wrappers for line parsing */ -static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags, +static int _dm_target_query_crypt(struct crypt_device *cd, uint64_t get_flags, char *params, struct dm_target *tgt, uint32_t *act_flags) { uint64_t val64; - char *rcipher, *rintegrity, *key_, *rdevice, *endp, buffer[3], *arg, *key_desc; + char *rcipher, *rintegrity, *key_, *rdevice, *endp, buffer[3], *arg, *key_desc, keyring[16]; unsigned int i, val; int r; size_t key_size; struct device *data_device = NULL; char *cipher = NULL, *integrity = NULL; struct volume_key *vk = NULL; + void *key = NULL; tgt->type = DM_CRYPT; tgt->direction = TARGET_QUERY; @@ -2039,12 +2099,16 @@ static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags, *act_flags |= CRYPT_ACTIVATE_NO_WRITE_WORKQUEUE; else if (!strcasecmp(arg, "iv_large_sectors")) *act_flags |= CRYPT_ACTIVATE_IV_LARGE_SECTORS; + else if (!strcasecmp(arg, "high_priority")) + *act_flags |= CRYPT_ACTIVATE_HIGH_PRIORITY; else if (sscanf(arg, "integrity:%u:", &val) == 1) { tgt->u.crypt.tag_size = val; rintegrity = strchr(arg + strlen("integrity:"), ':'); if (!rintegrity) goto err; rintegrity++; + } else if (sscanf(arg, "integrity_key_size:%u", &val) == 1) { + tgt->u.crypt.integrity_key_size = val; } else if (sscanf(arg, "sector_size:%u", &val) == 1) { tgt->u.crypt.sector_size = val; } else /* unknown option */ @@ -2086,25 +2150,35 @@ static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags, if (key_[0] == ':') { /* ::: */ key_desc = NULL; + r = -ENOMEM; endp = strpbrk(key_ + 1, ":"); - if (endp) - key_desc = strpbrk(endp + 1, ":"); - if (!key_desc) { + if (!endp) + goto err; + key_desc = strpbrk(endp + 1, ":"); + if (!key_desc) + goto err; + memcpy(keyring, endp + 1, key_desc - endp - 1); + keyring[key_desc - endp - 1] = '\0'; + key_desc++; + r = crypt_volume_key_set_description(vk, key_desc, key_type_by_name(keyring)); + if (r < 0) + goto err; + } else if (key_size) { + key = crypt_safe_alloc(key_size); + if (!key) { r = -ENOMEM; goto err; } - key_desc++; - crypt_volume_key_set_description(vk, key_desc); - } else { buffer[2] = '\0'; - for(i = 0; i < vk->keylength; i++) { - memcpy(buffer, &key_[i * 2], 2); - vk->key[i] = strtoul(buffer, &endp, 16); + for(i = 0; i < crypt_volume_key_length(vk); i++) { + crypt_safe_memcpy(buffer, &key_[i * 2], 2); + *((char *)key + i) = strtoul(buffer, &endp, 16); if (endp != &buffer[2]) { r = -EINVAL; goto err; } } + crypt_volume_key_pass_safe_alloc(vk, &key); } } } @@ -2123,12 +2197,13 @@ static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags, free(cipher); free(integrity); device_free(cd, data_device); + crypt_safe_free(key); crypt_free_volume_key(vk); return r; } static int _dm_target_query_verity(struct crypt_device *cd, - uint32_t get_flags, + uint64_t get_flags, char *params, struct dm_target *tgt, uint32_t *act_flags) @@ -2287,6 +2362,9 @@ static int _dm_target_query_verity(struct crypt_device *cd, *act_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION; else if (!strcasecmp(arg, "panic_on_corruption")) *act_flags |= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION; + else if (!strcasecmp(arg, "restart_on_error") || + !strcasecmp(arg, "panic_on_error")) + *act_flags |= CRYPT_ACTIVATE_ERROR_AS_CORRUPTION; else if (!strcasecmp(arg, "ignore_zero_blocks")) *act_flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS; else if (!strcasecmp(arg, "check_at_most_once")) @@ -2387,7 +2465,7 @@ static int _dm_target_query_verity(struct crypt_device *cd, } static int _dm_target_query_integrity(struct crypt_device *cd, - uint32_t get_flags, + uint64_t get_flags, char *params, struct dm_target *tgt, uint32_t *act_flags) @@ -2435,7 +2513,7 @@ static int _dm_target_query_integrity(struct crypt_device *cd, /* journal */ c = toupper(*(++params)); - if (!*params || *(++params) != ' ' || (c != 'D' && c != 'J' && c != 'R' && c != 'B')) + if (!*params || *(++params) != ' ' || (c != 'D' && c != 'J' && c != 'R' && c != 'B' && c != 'I')) goto err; if (c == 'D') *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL; @@ -2445,168 +2523,169 @@ static int _dm_target_query_integrity(struct crypt_device *cd, *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL; *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL_BITMAP; } + if (c == 'I') { + *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL; + *act_flags |= CRYPT_ACTIVATE_INLINE_MODE; + } tgt->u.integrity.sector_size = SECTOR_SIZE; - /* Features section */ - if (params) { - /* Number of arguments */ - val64 = strtoull(params, ¶ms, 10); - if (*params != ' ') - goto err; - params++; + /* Features section, number of arguments (always included) */ + val64 = strtoull(params, ¶ms, 10); + if (*params != ' ') + goto err; + params++; - features = (int)val64; - for (i = 0; i < features; i++) { - r = -EINVAL; - if (!params) + features = (int)val64; + for (i = 0; i < features; i++) { + r = -EINVAL; + if (!params) + goto err; + arg = strsep(¶ms, " "); + if (sscanf(arg, "journal_sectors:%u", &val) == 1) + tgt->u.integrity.journal_size = val * SECTOR_SIZE; + else if (sscanf(arg, "journal_watermark:%u", &val) == 1) + tgt->u.integrity.journal_watermark = val; + else if (sscanf(arg, "sectors_per_bit:%" PRIu64, &val64) == 1) { + if (val64 > UINT_MAX) goto err; - arg = strsep(¶ms, " "); - if (sscanf(arg, "journal_sectors:%u", &val) == 1) - tgt->u.integrity.journal_size = val * SECTOR_SIZE; - else if (sscanf(arg, "journal_watermark:%u", &val) == 1) - tgt->u.integrity.journal_watermark = val; - else if (sscanf(arg, "sectors_per_bit:%" PRIu64, &val64) == 1) { - if (val64 > UINT_MAX) + /* overloaded value for bitmap mode */ + tgt->u.integrity.journal_watermark = (unsigned int)val64; + } else if (sscanf(arg, "commit_time:%u", &val) == 1) + tgt->u.integrity.journal_commit_time = val; + else if (sscanf(arg, "bitmap_flush_interval:%u", &val) == 1) + /* overloaded value for bitmap mode */ + tgt->u.integrity.journal_commit_time = val; + else if (sscanf(arg, "interleave_sectors:%u", &val) == 1) + tgt->u.integrity.interleave_sectors = val; + else if (sscanf(arg, "block_size:%u", &val) == 1) + tgt->u.integrity.sector_size = val; + else if (sscanf(arg, "buffer_sectors:%u", &val) == 1) + tgt->u.integrity.buffer_sectors = val; + else if (!strncmp(arg, "internal_hash:", 14) && !integrity) { + str = &arg[14]; + arg = strsep(&str, ":"); + if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) { + integrity = strdup(arg); + if (!integrity) { + r = -ENOMEM; goto err; - /* overloaded value for bitmap mode */ - tgt->u.integrity.journal_watermark = (unsigned int)val64; - } else if (sscanf(arg, "commit_time:%u", &val) == 1) - tgt->u.integrity.journal_commit_time = val; - else if (sscanf(arg, "bitmap_flush_interval:%u", &val) == 1) - /* overloaded value for bitmap mode */ - tgt->u.integrity.journal_commit_time = val; - else if (sscanf(arg, "interleave_sectors:%u", &val) == 1) - tgt->u.integrity.interleave_sectors = val; - else if (sscanf(arg, "block_size:%u", &val) == 1) - tgt->u.integrity.sector_size = val; - else if (sscanf(arg, "buffer_sectors:%u", &val) == 1) - tgt->u.integrity.buffer_sectors = val; - else if (!strncmp(arg, "internal_hash:", 14) && !integrity) { - str = &arg[14]; - arg = strsep(&str, ":"); - if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) { - integrity = strdup(arg); - if (!integrity) { - r = -ENOMEM; - goto err; - } } + } - if (str) { - len = crypt_hex_to_bytes(str, &str2, 1); - if (len < 0) { - r = len; - goto err; - } - - r = 0; - if (get_flags & DM_ACTIVE_CRYPT_KEY) { - vk = crypt_alloc_volume_key(len, str2); - if (!vk) - r = -ENOMEM; - } else if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) { - vk = crypt_alloc_volume_key(len, NULL); - if (!vk) - r = -ENOMEM; - } - crypt_safe_free(str2); - if (r < 0) - goto err; - } - } else if (!strncmp(arg, "meta_device:", 12) && !meta_device) { - if (get_flags & DM_ACTIVE_DEVICE) { - str = crypt_lookup_dev(&arg[12]); - r = device_alloc(cd, &meta_device, str); - free(str); - if (r < 0 && r != -ENOTBLK) - goto err; + if (str) { + len = crypt_hex_to_bytes(str, &str2, 1); + if (len < 0) { + r = len; + goto err; } - } else if (!strncmp(arg, "journal_crypt:", 14) && !journal_crypt) { - str = &arg[14]; - arg = strsep(&str, ":"); - if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) { - journal_crypt = strdup(arg); - if (!journal_crypt) { + + r = 0; + if (get_flags & DM_ACTIVE_CRYPT_KEY) { + vk = crypt_alloc_volume_key(len, str2); + if (!vk) + r = -ENOMEM; + } else if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) { + vk = crypt_alloc_volume_key(len, NULL); + if (!vk) r = -ENOMEM; - goto err; - } } + crypt_safe_free(str2); + if (r < 0) + goto err; + } + } else if (!strncmp(arg, "meta_device:", 12) && !meta_device) { + if (get_flags & DM_ACTIVE_DEVICE) { + str = crypt_lookup_dev(&arg[12]); + r = device_alloc(cd, &meta_device, str); + free(str); + if (r < 0 && r != -ENOTBLK) + goto err; + } + } else if (!strncmp(arg, "journal_crypt:", 14) && !journal_crypt) { + str = &arg[14]; + arg = strsep(&str, ":"); + if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) { + journal_crypt = strdup(arg); + if (!journal_crypt) { + r = -ENOMEM; + goto err; + } + } - if (str) { - len = crypt_hex_to_bytes(str, &str2, 1); - if (len < 0) { - r = len; - goto err; - } - - r = 0; - if (get_flags & DM_ACTIVE_JOURNAL_CRYPT_KEY) { - journal_crypt_key = crypt_alloc_volume_key(len, str2); - if (!journal_crypt_key) - r = -ENOMEM; - } else if (get_flags & DM_ACTIVE_JOURNAL_CRYPT_KEYSIZE) { - journal_crypt_key = crypt_alloc_volume_key(len, NULL); - if (!journal_crypt_key) - r = -ENOMEM; - } - crypt_safe_free(str2); - if (r < 0) - goto err; + if (str) { + len = crypt_hex_to_bytes(str, &str2, 1); + if (len < 0) { + r = len; + goto err; } - } else if (!strncmp(arg, "journal_mac:", 12) && !journal_integrity) { - str = &arg[12]; - arg = strsep(&str, ":"); - if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) { - journal_integrity = strdup(arg); - if (!journal_integrity) { + + r = 0; + if (get_flags & DM_ACTIVE_JOURNAL_CRYPT_KEY) { + journal_crypt_key = crypt_alloc_volume_key(len, str2); + if (!journal_crypt_key) + r = -ENOMEM; + } else if (get_flags & DM_ACTIVE_JOURNAL_CRYPT_KEYSIZE) { + journal_crypt_key = crypt_alloc_volume_key(len, NULL); + if (!journal_crypt_key) r = -ENOMEM; - goto err; - } } + crypt_safe_free(str2); + if (r < 0) + goto err; + } + } else if (!strncmp(arg, "journal_mac:", 12) && !journal_integrity) { + str = &arg[12]; + arg = strsep(&str, ":"); + if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) { + journal_integrity = strdup(arg); + if (!journal_integrity) { + r = -ENOMEM; + goto err; + } + } - if (str) { - len = crypt_hex_to_bytes(str, &str2, 1); - if (len < 0) { - r = len; - goto err; - } - - r = 0; - if (get_flags & DM_ACTIVE_JOURNAL_MAC_KEY) { - journal_integrity_key = crypt_alloc_volume_key(len, str2); - if (!journal_integrity_key) - r = -ENOMEM; - } else if (get_flags & DM_ACTIVE_JOURNAL_MAC_KEYSIZE) { - journal_integrity_key = crypt_alloc_volume_key(len, NULL); - if (!journal_integrity_key) - r = -ENOMEM; - } - crypt_safe_free(str2); - if (r < 0) - goto err; + if (str) { + len = crypt_hex_to_bytes(str, &str2, 1); + if (len < 0) { + r = len; + goto err; } - } else if (!strcmp(arg, "recalculate")) { - *act_flags |= CRYPT_ACTIVATE_RECALCULATE; - } else if (!strcmp(arg, "reset_recalculate")) { - *act_flags |= CRYPT_ACTIVATE_RECALCULATE_RESET; - } else if (!strcmp(arg, "fix_padding")) { - tgt->u.integrity.fix_padding = true; - } else if (!strcmp(arg, "fix_hmac")) { - tgt->u.integrity.fix_hmac = true; - } else if (!strcmp(arg, "legacy_recalculate")) { - tgt->u.integrity.legacy_recalc = true; - } else if (!strcmp(arg, "allow_discards")) { - *act_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; - } else /* unknown option */ - goto err; - } - /* All parameters should be processed */ - if (params && *params) { - r = -EINVAL; + r = 0; + if (get_flags & DM_ACTIVE_JOURNAL_MAC_KEY) { + journal_integrity_key = crypt_alloc_volume_key(len, str2); + if (!journal_integrity_key) + r = -ENOMEM; + } else if (get_flags & DM_ACTIVE_JOURNAL_MAC_KEYSIZE) { + journal_integrity_key = crypt_alloc_volume_key(len, NULL); + if (!journal_integrity_key) + r = -ENOMEM; + } + crypt_safe_free(str2); + if (r < 0) + goto err; + } + } else if (!strcmp(arg, "recalculate")) { + *act_flags |= CRYPT_ACTIVATE_RECALCULATE; + } else if (!strcmp(arg, "reset_recalculate")) { + *act_flags |= CRYPT_ACTIVATE_RECALCULATE_RESET; + } else if (!strcmp(arg, "fix_padding")) { + tgt->u.integrity.fix_padding = true; + } else if (!strcmp(arg, "fix_hmac")) { + tgt->u.integrity.fix_hmac = true; + } else if (!strcmp(arg, "legacy_recalculate")) { + tgt->u.integrity.legacy_recalc = true; + } else if (!strcmp(arg, "allow_discards")) { + *act_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; + } else /* unknown option */ goto err; - } + } + + /* All parameters should be processed */ + if (params && *params) { + r = -EINVAL; + goto err; } if (data_device) @@ -2639,7 +2718,7 @@ static int _dm_target_query_integrity(struct crypt_device *cd, } static int _dm_target_query_linear(struct crypt_device *cd, struct dm_target *tgt, - uint32_t get_flags, char *params) + uint64_t get_flags, char *params) { uint64_t val64; char *rdevice, *arg; @@ -2701,7 +2780,7 @@ static int _dm_target_query_zero(struct dm_target *tgt) */ static int dm_target_query(struct crypt_device *cd, struct dm_target *tgt, const uint64_t *start, const uint64_t *length, const char *target_type, - char *params, uint32_t get_flags, uint32_t *act_flags) + char *params, uint64_t get_flags, uint32_t *act_flags) { int r = -ENOTSUP; @@ -2727,7 +2806,7 @@ static int dm_target_query(struct crypt_device *cd, struct dm_target *tgt, const } static int _dm_query_device(struct crypt_device *cd, const char *name, - uint32_t get_flags, struct crypt_dm_active_device *dmd) + uint64_t get_flags, struct crypt_dm_active_device *dmd) { struct dm_target *t; struct dm_task *dmt; @@ -2828,7 +2907,7 @@ static int _dm_query_device(struct crypt_device *cd, const char *name, } int dm_query_device(struct crypt_device *cd, const char *name, - uint32_t get_flags, struct crypt_dm_active_device *dmd) + uint64_t get_flags, struct crypt_dm_active_device *dmd) { int r; @@ -2980,9 +3059,9 @@ static int _dm_message(const char *name, const char *msg) return r; } -int dm_suspend_device(struct crypt_device *cd, const char *name, uint32_t dmflags) +int dm_suspend_device(struct crypt_device *cd, const char *name, uint64_t dmflags) { - uint32_t dmt_flags; + uint64_t dmt_flags; int r = -ENOTSUP; if (dm_init_context(cd, DM_UNKNOWN)) @@ -3014,7 +3093,7 @@ int dm_suspend_device(struct crypt_device *cd, const char *name, uint32_t dmflag return r; } -int dm_resume_device(struct crypt_device *cd, const char *name, uint32_t dmflags) +int dm_resume_device(struct crypt_device *cd, const char *name, uint64_t dmflags) { int r; @@ -3031,7 +3110,7 @@ int dm_resume_device(struct crypt_device *cd, const char *name, uint32_t dmflags int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name, const struct volume_key *vk) { - uint32_t dmt_flags; + uint64_t dmt_flags; int msg_size; char *msg = NULL, *key = NULL; int r = -ENOTSUP; @@ -3042,12 +3121,12 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name, if (!(dmt_flags & DM_KEY_WIPE_SUPPORTED)) goto out; - if (!vk->keylength) + if (!crypt_volume_key_length(vk)) msg_size = 11; // key set - - else if (vk->key_description) - msg_size = strlen(vk->key_description) + int_log10(vk->keylength) + 18; + else if (crypt_volume_key_description(vk)) + msg_size = strlen(crypt_volume_key_description(vk)) + int_log10(crypt_volume_key_length(vk)) + 18; else - msg_size = vk->keylength * 2 + 10; // key set + msg_size = crypt_volume_key_length(vk) * 2 + 10; // key set msg = crypt_safe_alloc(msg_size); if (!msg) { @@ -3055,11 +3134,15 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name, goto out; } - if (vk->key_description) { - r = snprintf(msg, msg_size, "key set :%zu:logon:%s", vk->keylength, - vk->key_description); - } else { - key = crypt_bytes_to_hex(vk->keylength, vk->key); + if (crypt_volume_key_description(vk)) { + r = snprintf(msg, msg_size, "key set :%zu:logon:%s", crypt_volume_key_length(vk), + crypt_volume_key_description(vk)); + } else { + if (!crypt_volume_key_length(vk)) + key = crypt_bytes_to_hex(0, NULL); + else + key = crypt_bytes_to_hex(crypt_volume_key_length(vk), + crypt_volume_key_get_key(vk)); if (!key) { r = -ENOMEM; goto out; @@ -3106,6 +3189,54 @@ int dm_get_iname(const char *name, char **iname, bool with_path) return r < 0 ? -ENOMEM : 0; } +char *dm_get_active_iname(struct crypt_device *cd, const char *name) +{ + struct crypt_dm_active_device dmd = {}, dmdi = {}; + struct dm_target *tgt = &dmd.segment, *tgti = &dmdi.segment; + char *ipath = NULL, *iname = NULL, *ret_iname = NULL; + struct stat st; + + if (!name) + return NULL; + + if (dm_query_device(cd, name, DM_ACTIVE_UUID, &dmd) < 0) + return NULL; + + if (!single_segment(&dmd)) + goto out; + + if (tgt->type != DM_CRYPT || tgt->u.crypt.tag_size == 0) + goto out; + + if (dm_get_iname(name, &iname, false) < 0) + goto out; + + if (dm_get_iname(name, &ipath, true) < 0) + goto out; + + if (stat(ipath, &st) < 0 || !S_ISBLK(st.st_mode)) + goto out; + + if (dm_query_device(cd, iname, DM_ACTIVE_UUID, &dmdi) < 0) + goto out; + + if (single_segment(&dmdi) && + tgti->type == DM_INTEGRITY && + dm_uuid_integrity_cmp(dmd.uuid, dmdi.uuid) == 0) { + ret_iname = iname; + iname = NULL; + } +out: + dm_targets_free(cd, &dmdi); + dm_targets_free(cd, &dmd); + free(CONST_CAST(void*)dmd.uuid); + free(CONST_CAST(void*)dmdi.uuid); + free(ipath); + free(iname); + + return ret_iname; +} + int dm_is_dm_device(int major) { return dm_is_dm_major((uint32_t)major); @@ -3116,9 +3247,96 @@ int dm_is_dm_kernel_name(const char *name) return strncmp(name, "dm-", 3) ? 0 : 1; } +/* + * compares UUIDs returned by device-mapper (striped by cryptsetup) and uuid in header + */ +int dm_uuid_cmp(const char *dm_uuid, const char *hdr_uuid) +{ + int i, j; + const char *str; + + if (!dm_uuid || !hdr_uuid) + return -EINVAL; + + /* skip beyond LUKS2_HW_OPAL prefix */ + if (!strncmp(dm_uuid, CRYPT_LUKS2_HW_OPAL, strlen(CRYPT_LUKS2_HW_OPAL))) + dm_uuid = dm_uuid + strlen(CRYPT_LUKS2_HW_OPAL); + + str = strchr(dm_uuid, '-'); + if (!str) + return -EINVAL; + + for (i = 0, j = 1; hdr_uuid[i]; i++) { + if (hdr_uuid[i] == '-') + continue; + + if (!str[j] || str[j] == '-') + return -EINVAL; + + if (str[j] != hdr_uuid[i]) + return -EINVAL; + j++; + } + + return 0; +} + +/* + * compares two UUIDs returned by device-mapper (striped by cryptsetup) + * used for stacked LUKS2 & INTEGRITY devices + */ +int dm_uuid_integrity_cmp(const char *dm_uuid, const char *dmi_uuid) +{ + int i; + const char *str, *stri; + + if (!dm_uuid || !dmi_uuid) + return -EINVAL; + + /* skip beyond LUKS2_HW_OPAL prefix */ + if (!strncmp(dm_uuid, CRYPT_LUKS2_HW_OPAL, strlen(CRYPT_LUKS2_HW_OPAL))) + dm_uuid = dm_uuid + strlen(CRYPT_LUKS2_HW_OPAL); + + str = strchr(dm_uuid, '-'); + if (!str) + return -EINVAL; + + stri = strchr(dmi_uuid, '-'); + if (!stri) + return -EINVAL; + + for (i = 1; str[i] && str[i] != '-'; i++) { + if (!stri[i]) + return -EINVAL; + + if (str[i] != stri[i]) + return -EINVAL; + } + + return 0; +} + +/* + * compares type of active device to provided string + */ +int dm_uuid_type_cmp(const char *dm_uuid, const char *type) +{ + size_t len; + + assert(type); + + len = strlen(type); + if (dm_uuid && strlen(dm_uuid) > len && + !strncmp(dm_uuid, type, len) && dm_uuid[len] == '-') + return 0; + + return -ENODEV; +} + int dm_crypt_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg_size, struct device *data_device, struct volume_key *vk, const char *cipher, - uint64_t iv_offset, uint64_t data_offset, const char *integrity, uint32_t tag_size, + uint64_t iv_offset, uint64_t data_offset, + const char *integrity, uint32_t integrity_key_size, uint32_t tag_size, uint32_t sector_size) { char *dm_integrity = NULL; @@ -3144,6 +3362,7 @@ int dm_crypt_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg tgt->u.crypt.offset = data_offset; tgt->u.crypt.tag_size = tag_size; tgt->u.crypt.sector_size = sector_size; + tgt->u.crypt.integrity_key_size = integrity_key_size; return 0; } @@ -3183,7 +3402,7 @@ int dm_integrity_target_set(struct crypt_device *cd, struct volume_key *journal_crypt_key, struct volume_key *journal_mac_key, const struct crypt_params_integrity *ip) { - uint32_t dmi_flags; + uint64_t dmi_flags; if (!data_device) return -EINVAL; diff --git a/lib/loopaes/loopaes.c b/lib/loopaes/loopaes.c index 8aec3de..bdd9ded 100644 --- a/lib/loopaes/loopaes.c +++ b/lib/loopaes/loopaes.c @@ -2,8 +2,8 @@ /* * loop-AES compatible volume handling * - * Copyright (C) 2011-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2011-2024 Milan Broz + * Copyright (C) 2011-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2011-2025 Milan Broz */ #include @@ -69,6 +69,7 @@ static int hash_keys(struct crypt_device *cd, char tweak, *key_ptr; unsigned int i; int r = 0; + void *key = NULL; hash_name = hash_override ?: get_hash(key_len_output); tweak = get_tweak(keys_count); @@ -79,24 +80,30 @@ static int hash_keys(struct crypt_device *cd, return -EINVAL; } - *vk = crypt_alloc_volume_key((size_t)key_len_output * keys_count, NULL); - if (!*vk) + key = crypt_safe_alloc((size_t)key_len_output * keys_count); + if (!key) return -ENOMEM; for (i = 0; i < keys_count; i++) { - key_ptr = &(*vk)->key[i * key_len_output]; + key_ptr = &((char *)key)[i * key_len_output]; r = hash_key(input_keys[i], key_len_input, key_ptr, key_len_output, hash_name); if (r < 0) - break; + goto err; key_ptr[0] ^= tweak; } - if (r < 0 && *vk) { - crypt_free_volume_key(*vk); - *vk = NULL; + *vk = crypt_alloc_volume_key_by_safe_alloc(&key); + if (!*vk) { + r = -ENOMEM; + goto err; } + + return 0; +err: + crypt_safe_free(key); + *vk = NULL; return r; } @@ -191,7 +198,7 @@ int LOOPAES_activate(struct crypt_device *cd, uint32_t flags) { int r; - uint32_t req_flags, dmc_flags; + uint64_t req_flags, dmc_flags; char *cipher = NULL; struct crypt_dm_active_device dmd = { .flags = flags, @@ -213,9 +220,8 @@ int LOOPAES_activate(struct crypt_device *cd, return -ENOMEM; r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), - vk, cipher, crypt_get_iv_offset(cd), - crypt_get_data_offset(cd), crypt_get_integrity(cd), - crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd)); + vk, cipher, crypt_get_iv_offset(cd), crypt_get_data_offset(cd), + NULL, 0, 0, crypt_get_sector_size(cd)); if (r) { free(cipher); diff --git a/lib/loopaes/loopaes.h b/lib/loopaes/loopaes.h index 878f2da..425ca22 100644 --- a/lib/loopaes/loopaes.h +++ b/lib/loopaes/loopaes.h @@ -2,8 +2,8 @@ /* * loop-AES compatible volume handling * - * Copyright (C) 2011-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2011-2024 Milan Broz + * Copyright (C) 2011-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2011-2025 Milan Broz */ #ifndef _LOOPAES_H diff --git a/lib/luks1/af.c b/lib/luks1/af.c index e312b30..7ab153d 100644 --- a/lib/luks1/af.c +++ b/lib/luks1/af.c @@ -3,7 +3,7 @@ * AFsplitter - Anti forensic information splitter * * Copyright (C) 2004 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. * * AFsplitter diffuses information over a large stripe of data, * therefore supporting secure data destruction. diff --git a/lib/luks1/af.h b/lib/luks1/af.h index 0dcd233..08f3757 100644 --- a/lib/luks1/af.h +++ b/lib/luks1/af.h @@ -3,7 +3,7 @@ * AFsplitter - Anti forensic information splitter * * Copyright (C) 2004 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. */ #ifndef INCLUDED_CRYPTSETUP_LUKS_AF_H diff --git a/lib/luks1/keyencryption.c b/lib/luks1/keyencryption.c index 63e47fc..de8aa30 100644 --- a/lib/luks1/keyencryption.c +++ b/lib/luks1/keyencryption.c @@ -3,8 +3,8 @@ * LUKS - Linux Unified Key Setup * * Copyright (C) 2004-2006 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2012-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Milan Broz */ #include @@ -18,7 +18,8 @@ static void _error_hint(struct crypt_device *ctx, const char *device, const char *cipher, const char *mode, size_t keyLength) { - char *c, cipher_spec[MAX_CIPHER_LEN * 3]; + const char *c; + char cipher_spec[MAX_CIPHER_LEN * 3]; if (snprintf(cipher_spec, sizeof(cipher_spec), "%s-%s", cipher, mode) < 0) return; @@ -88,7 +89,7 @@ static int LUKS_endec_template(char *src, size_t srcLength, r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_metadata_device(ctx), vk, cipher_spec, 0, sector, - NULL, 0, SECTOR_SIZE); + NULL, 0, 0, SECTOR_SIZE); if (r) goto out; @@ -96,7 +97,7 @@ static int LUKS_endec_template(char *src, size_t srcLength, if (r < 0) { if (r != -EACCES && r != -ENOTSUP) _error_hint(ctx, device_path(crypt_metadata_device(ctx)), - cipher, cipher_mode, vk->keylength * 8); + cipher, cipher_mode, crypt_volume_key_length(vk) * 8); r = -EIO; goto out; } @@ -140,7 +141,8 @@ int LUKS_encrypt_to_storage(char *src, size_t srcLength, return -EINVAL; /* Encrypt buffer */ - r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, vk->key, vk->keylength, false); + r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk), false); if (r) log_dbg(ctx, "Userspace crypto wrapper cannot use %s-%s (%d).", @@ -153,7 +155,7 @@ int LUKS_encrypt_to_storage(char *src, size_t srcLength, if (r) { _error_hint(ctx, device_path(device), cipher, cipher_mode, - vk->keylength * 8); + crypt_volume_key_length(vk) * 8); return r; } @@ -205,7 +207,8 @@ int LUKS_decrypt_from_storage(char *dst, size_t dstLength, if (MISALIGNED_512(dstLength)) return -EINVAL; - r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, vk->key, vk->keylength, false); + r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk), false); if (r) log_dbg(ctx, "Userspace crypto wrapper cannot use %s-%s (%d).", @@ -218,7 +221,7 @@ int LUKS_decrypt_from_storage(char *dst, size_t dstLength, if (r) { _error_hint(ctx, device_path(device), cipher, cipher_mode, - vk->keylength * 8); + crypt_volume_key_length(vk) * 8); return r; } diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c index 5d8fe96..3792981 100644 --- a/lib/luks1/keymanage.c +++ b/lib/luks1/keymanage.c @@ -3,8 +3,8 @@ * LUKS - Linux Unified Key Setup * * Copyright (C) 2004-2006 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2013-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2013-2025 Milan Broz */ #include @@ -378,7 +378,7 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx) { struct luks_phdr temp_phdr; const unsigned char *sector = (const unsigned char*)phdr; - struct volume_key *vk; + struct volume_key *fake_vk; int i, bad, r, need_write = 0; if (phdr->keyBytes != 16 && phdr->keyBytes != 32 && phdr->keyBytes != 64) { @@ -424,8 +424,8 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx) if (r < 0) return -EINVAL; - vk = crypt_alloc_volume_key(phdr->keyBytes, NULL); - if (!vk) + fake_vk = crypt_generate_volume_key(ctx, phdr->keyBytes, KEY_QUALITY_EMPTY); + if (!fake_vk) return -ENOMEM; log_verbose(ctx, _("Repairing keyslots.")); @@ -433,7 +433,7 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx) log_dbg(ctx, "Generating second header with the same parameters for check."); /* cipherName, cipherMode, hashSpec, uuid are already null terminated */ /* payloadOffset - cannot check */ - r = LUKS_generate_phdr(&temp_phdr, vk, phdr->cipherName, phdr->cipherMode, + r = LUKS_generate_phdr(&temp_phdr, fake_vk, phdr->cipherName, phdr->cipherMode, phdr->hashSpec, phdr->uuid, phdr->payloadOffset * SECTOR_SIZE, 0, 0, ctx); if (r < 0) @@ -492,7 +492,7 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx) out: if (r) log_err(ctx, _("Repair failed.")); - crypt_free_volume_key(vk); + crypt_free_volume_key(fake_vk); crypt_safe_memzero(&temp_phdr, sizeof(temp_phdr)); return r; } @@ -710,14 +710,12 @@ int LUKS_check_cipher(struct crypt_device *ctx, size_t keylength, const char *ci log_dbg(ctx, "Checking if cipher %s-%s is usable.", cipher, cipher_mode); - empty_key = crypt_alloc_volume_key(keylength, NULL); + /* No need to get KEY quality random but it must avoid known weak keys. */ + empty_key = crypt_generate_volume_key(ctx, keylength, KEY_QUALITY_NORMAL); if (!empty_key) return -ENOMEM; - /* No need to get KEY quality random but it must avoid known weak keys. */ - r = crypt_random_get(ctx, empty_key->key, empty_key->keylength, CRYPT_RND_NORMAL); - if (!r) - r = LUKS_decrypt_from_storage(buf, sizeof(buf), cipher, cipher_mode, empty_key, 0, ctx); + r = LUKS_decrypt_from_storage(buf, sizeof(buf), cipher, cipher_mode, empty_key, 0, ctx); crypt_free_volume_key(empty_key); crypt_safe_memzero(buf, sizeof(buf)); @@ -748,7 +746,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, memset(header, 0, sizeof(struct luks_phdr)); - keyslot_sectors = AF_split_sectors(vk->keylength, LUKS_STRIPES); + keyslot_sectors = AF_split_sectors(crypt_volume_key_length(vk), LUKS_STRIPES); header_sectors = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE; for (i = 0; i < LUKS_NUMKEYS; i++) { @@ -795,7 +793,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, strncpy(header->hashSpec,hashSpec,LUKS_HASHSPEC_L-1); _to_lower(header->hashSpec, LUKS_HASHSPEC_L); - header->keyBytes=vk->keylength; + header->keyBytes = crypt_volume_key_length(vk); log_dbg(ctx, "Generating LUKS header version %d using hash %s, %s, %s, MK %d bytes", header->version, header->hashSpec ,header->cipherName, header->cipherMode, @@ -809,7 +807,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, /* Compute volume key digest */ pbkdf = crypt_get_pbkdf(ctx); - r = crypt_benchmark_pbkdf_internal(ctx, pbkdf, vk->keylength); + r = crypt_benchmark_pbkdf_internal(ctx, pbkdf, crypt_volume_key_length(vk)); if (r < 0) return r; assert(pbkdf->iterations); @@ -824,7 +822,9 @@ int LUKS_generate_phdr(struct luks_phdr *header, header->mkDigestIterations = AT_LEAST((uint32_t)PBKDF2_temp, LUKS_MKD_ITERATIONS_MIN); assert(header->mkDigestIterations); - r = crypt_pbkdf(CRYPT_KDF_PBKDF2, header->hashSpec, vk->key,vk->keylength, + r = crypt_pbkdf(CRYPT_KDF_PBKDF2, header->hashSpec, + crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk), header->mkDigestSalt, LUKS_SALTSIZE, header->mkDigest,LUKS_DIGESTSIZE, header->mkDigestIterations, 0, 0); @@ -866,8 +866,9 @@ int LUKS_set_key(unsigned int keyIndex, struct luks_phdr *hdr, struct volume_key *vk, struct crypt_device *ctx) { - struct volume_key *derived_key; + struct volume_key *derived_vk = NULL; char *AfKey = NULL; + void *derived_key = NULL; size_t AFEKSize; struct crypt_pbkdf_type *pbkdf; int r; @@ -886,7 +887,7 @@ int LUKS_set_key(unsigned int keyIndex, log_dbg(ctx, "Calculating data for key slot %d", keyIndex); pbkdf = crypt_get_pbkdf(ctx); - r = crypt_benchmark_pbkdf_internal(ctx, pbkdf, vk->keylength); + r = crypt_benchmark_pbkdf_internal(ctx, pbkdf, crypt_volume_key_length(vk)); if (r < 0) return r; assert(pbkdf->iterations); @@ -899,9 +900,11 @@ int LUKS_set_key(unsigned int keyIndex, log_dbg(ctx, "Key slot %d use %" PRIu32 " password iterations.", keyIndex, hdr->keyblock[keyIndex].passwordIterations); - derived_key = crypt_alloc_volume_key(hdr->keyBytes, NULL); - if (!derived_key) - return -ENOMEM; + derived_key = crypt_safe_alloc(hdr->keyBytes); + if (!derived_key) { + r = -ENOMEM; + goto out; + } r = crypt_random_get(ctx, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, CRYPT_RND_SALT); @@ -910,7 +913,7 @@ int LUKS_set_key(unsigned int keyIndex, r = crypt_pbkdf(CRYPT_KDF_PBKDF2, hdr->hashSpec, password, passwordLen, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, - derived_key->key, hdr->keyBytes, + derived_key, hdr->keyBytes, hdr->keyblock[keyIndex].passwordIterations, 0, 0); if (r < 0) { if ((crypt_backend_flags() & CRYPT_BACKEND_PBKDF2_INT) && @@ -919,11 +922,17 @@ int LUKS_set_key(unsigned int keyIndex, goto out; } + derived_vk = crypt_alloc_volume_key_by_safe_alloc(&derived_key); + if (!derived_vk) { + r = -ENOMEM; + goto out; + } + /* * AF splitting, the volume key stored in vk->key is split to AfKey */ - assert(vk->keylength == hdr->keyBytes); - AFEKSize = AF_split_sectors(vk->keylength, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE; + assert(crypt_volume_key_length(vk) == hdr->keyBytes); + AFEKSize = AF_split_sectors(crypt_volume_key_length(vk), hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE; AfKey = crypt_safe_alloc(AFEKSize); if (!AfKey) { r = -ENOMEM; @@ -932,7 +941,8 @@ int LUKS_set_key(unsigned int keyIndex, log_dbg(ctx, "Using hash %s for AF in key slot %d, %d stripes", hdr->hashSpec, keyIndex, hdr->keyblock[keyIndex].stripes); - r = AF_split(ctx, vk->key, AfKey, vk->keylength, hdr->keyblock[keyIndex].stripes, hdr->hashSpec); + r = AF_split(ctx, crypt_volume_key_get_key(vk), AfKey, crypt_volume_key_length(vk), + hdr->keyblock[keyIndex].stripes, hdr->hashSpec); if (r < 0) goto out; @@ -942,7 +952,7 @@ int LUKS_set_key(unsigned int keyIndex, r = LUKS_encrypt_to_storage(AfKey, AFEKSize, hdr->cipherName, hdr->cipherMode, - derived_key, + derived_vk, hdr->keyblock[keyIndex].keyMaterialOffset, ctx); if (r < 0) @@ -960,7 +970,8 @@ int LUKS_set_key(unsigned int keyIndex, r = 0; out: crypt_safe_free(AfKey); - crypt_free_volume_key(derived_key); + crypt_safe_free(derived_key); + crypt_free_volume_key(derived_vk); return r; } @@ -970,7 +981,8 @@ int LUKS_verify_volume_key(const struct luks_phdr *hdr, { char checkHashBuf[LUKS_DIGESTSIZE]; - if (crypt_pbkdf(CRYPT_KDF_PBKDF2, hdr->hashSpec, vk->key, vk->keylength, + if (crypt_pbkdf(CRYPT_KDF_PBKDF2, hdr->hashSpec, crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk), hdr->mkDigestSalt, LUKS_SALTSIZE, checkHashBuf, LUKS_DIGESTSIZE, hdr->mkDigestIterations, 0, 0) < 0) @@ -987,12 +999,13 @@ static int LUKS_open_key(unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, - struct volume_key **vk, + struct volume_key **r_vk, struct crypt_device *ctx) { crypt_keyslot_info ki = LUKS_keyslot_info(hdr, keyIndex); - struct volume_key *derived_key; + struct volume_key *derived_vk = NULL, *vk = NULL; char *AfKey = NULL; + void *key = NULL, *derived_key = NULL; size_t AFEKSize; int r; @@ -1002,12 +1015,12 @@ static int LUKS_open_key(unsigned int keyIndex, if (ki < CRYPT_SLOT_ACTIVE) return -ENOENT; - derived_key = crypt_alloc_volume_key(hdr->keyBytes, NULL); + derived_key = crypt_safe_alloc(hdr->keyBytes); if (!derived_key) return -ENOMEM; - *vk = crypt_alloc_volume_key(hdr->keyBytes, NULL); - if (!*vk) { + key = crypt_safe_alloc(hdr->keyBytes); + if (!key) { r = -ENOMEM; goto out; } @@ -1021,39 +1034,57 @@ static int LUKS_open_key(unsigned int keyIndex, r = crypt_pbkdf(CRYPT_KDF_PBKDF2, hdr->hashSpec, password, passwordLen, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, - derived_key->key, hdr->keyBytes, + derived_key, hdr->keyBytes, hdr->keyblock[keyIndex].passwordIterations, 0, 0); if (r < 0) { log_err(ctx, _("Cannot open keyslot (using hash %s)."), hdr->hashSpec); goto out; } + derived_vk = crypt_alloc_volume_key_by_safe_alloc(&derived_key); + if (!derived_vk) { + r = -ENOMEM; + goto out; + } + log_dbg(ctx, "Reading key slot %d area.", keyIndex); r = LUKS_decrypt_from_storage(AfKey, AFEKSize, hdr->cipherName, hdr->cipherMode, - derived_key, + derived_vk, hdr->keyblock[keyIndex].keyMaterialOffset, ctx); if (r < 0) goto out; - r = AF_merge(AfKey, (*vk)->key, (*vk)->keylength, hdr->keyblock[keyIndex].stripes, hdr->hashSpec); + r = AF_merge(AfKey, key, hdr->keyBytes, hdr->keyblock[keyIndex].stripes, hdr->hashSpec); if (r < 0) goto out; - r = LUKS_verify_volume_key(hdr, *vk); + vk = crypt_alloc_volume_key_by_safe_alloc(&key); + if (!vk) { + r = -ENOMEM; + goto out; + } + + r = LUKS_verify_volume_key(hdr, vk); + if (r < 0) + goto out; /* Allow only empty passphrase with null cipher */ - if (!r && crypt_is_cipher_null(hdr->cipherName) && passwordLen) + if (crypt_is_cipher_null(hdr->cipherName) && passwordLen) r = -EPERM; + else + *r_vk = vk; out: if (r < 0) { - crypt_free_volume_key(*vk); - *vk = NULL; + crypt_free_volume_key(vk); + *r_vk = NULL; } crypt_safe_free(AfKey); - crypt_free_volume_key(derived_key); + crypt_safe_free(key); + crypt_safe_free(derived_key); + crypt_free_volume_key(derived_vk); return r; } @@ -1204,8 +1235,7 @@ int LUKS1_activate(struct crypt_device *cd, r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), vk, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd), - crypt_get_data_offset(cd), crypt_get_integrity(cd), - crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd)); + crypt_get_data_offset(cd), NULL, 0, 0, crypt_get_sector_size(cd)); if (!r) r = create_or_reload_device(cd, name, CRYPT_LUKS1, &dmd); diff --git a/lib/luks1/luks.h b/lib/luks1/luks.h index befdc3a..80f6544 100644 --- a/lib/luks1/luks.h +++ b/lib/luks1/luks.h @@ -3,7 +3,7 @@ * LUKS - Linux Unified Key Setup * * Copyright (C) 2004-2006 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. */ #ifndef INCLUDED_CRYPTSETUP_LUKS_LUKS_H diff --git a/lib/luks2/hw_opal/hw_opal.c b/lib/luks2/hw_opal/hw_opal.c index a56e882..3256393 100644 --- a/lib/luks2/hw_opal/hw_opal.c +++ b/lib/luks2/hw_opal/hw_opal.c @@ -3,7 +3,8 @@ * OPAL utilities * * Copyright (C) 2022-2023 Luca Boccassi - * 2023 Ondrej Kozina + * Copyright (C) 2023-2025 Ondrej Kozina + * Copyright (C) 2024-2025 Milan Broz */ #include @@ -16,7 +17,7 @@ #include #include #include -#ifdef HAVE_SYS_SYSMACROS_H +#if HAVE_SYS_SYSMACROS_H # include /* for major, minor */ #endif @@ -35,33 +36,32 @@ * Section 5.1.5: Method Status Codes * Names and values from table 166 */ typedef enum OpalStatus { - OPAL_STATUS_SUCCESS, - OPAL_STATUS_NOT_AUTHORIZED, - OPAL_STATUS_OBSOLETE0, /* Undefined but possible return values are called 'obsolete' */ - OPAL_STATUS_SP_BUSY, - OPAL_STATUS_SP_FAILED, - OPAL_STATUS_SP_DISABLED, - OPAL_STATUS_SP_FROZEN, - OPAL_STATUS_NO_SESSIONS_AVAILABLE, - OPAL_STATUS_UNIQUENESS_CONFLICT, - OPAL_STATUS_INSUFFICIENT_SPACE, - OPAL_STATUS_INSUFFICIENT_ROWS, - OPAL_STATUS_INVALID_PARAMETER, - OPAL_STATUS_OBSOLETE1, - OPAL_STATUS_OBSOLETE2, - OPAL_STATUS_TPER_MALFUNCTION, - OPAL_STATUS_TRANSACTION_FAILURE, - OPAL_STATUS_RESPONSE_OVERFLOW, - OPAL_STATUS_AUTHORITY_LOCKED_OUT, - OPAL_STATUS_FAIL = 0x3F, /* As defined by specification */ - _OPAL_STATUS_MAX, - _OPAL_STATUS_INVALID = -EINVAL, + OPAL_STATUS_SUCCESS = 0x00, + OPAL_STATUS_NOT_AUTHORIZED = 0x01, + OPAL_STATUS_OBSOLETE0 = 0x02, /* Undefined but possible return values are called 'obsolete' */ + OPAL_STATUS_SP_BUSY = 0x03, + OPAL_STATUS_SP_FAILED = 0x04, + OPAL_STATUS_SP_DISABLED = 0x05, + OPAL_STATUS_SP_FROZEN = 0x06, + OPAL_STATUS_NO_SESSIONS_AVAILABLE = 0x07, + OPAL_STATUS_UNIQUENESS_CONFLICT = 0x08, + OPAL_STATUS_INSUFFICIENT_SPACE = 0x09, + OPAL_STATUS_INSUFFICIENT_ROWS = 0x0a, + OPAL_STATUS_OBSOLETE1 = 0x0b, /* Undefined but possible return values are called 'obsolete' */ + OPAL_STATUS_INVALID_PARAMETER = 0x0c, + OPAL_STATUS_OBSOLETE2 = 0x0d, + OPAL_STATUS_OBSOLETE3 = 0x0e, + OPAL_STATUS_TPER_MALFUNCTION = 0x0f, + OPAL_STATUS_TRANSACTION_FAILURE = 0x10, + OPAL_STATUS_RESPONSE_OVERFLOW = 0x11, + OPAL_STATUS_AUTHORITY_LOCKED_OUT = 0x12, + _OPAL_STATUS_MAX = 0x13, } OpalStatus; static const char* const opal_status_table[_OPAL_STATUS_MAX] = { [OPAL_STATUS_SUCCESS] = "success", [OPAL_STATUS_NOT_AUTHORIZED] = "not authorized", - [OPAL_STATUS_OBSOLETE0] = "obsolete", + [OPAL_STATUS_OBSOLETE0] = "obsolete (0x02)", [OPAL_STATUS_SP_BUSY] = "SP busy", [OPAL_STATUS_SP_FAILED] = "SP failed", [OPAL_STATUS_SP_DISABLED] = "SP disabled", @@ -70,14 +70,14 @@ static const char* const opal_status_table[_OPAL_STATUS_MAX] = { [OPAL_STATUS_UNIQUENESS_CONFLICT] = "uniqueness conflict", [OPAL_STATUS_INSUFFICIENT_SPACE] = "insufficient space", [OPAL_STATUS_INSUFFICIENT_ROWS] = "insufficient rows", + [OPAL_STATUS_OBSOLETE1] = "obsolete (0x0b)", [OPAL_STATUS_INVALID_PARAMETER] = "invalid parameter", - [OPAL_STATUS_OBSOLETE1] = "obsolete", - [OPAL_STATUS_OBSOLETE2] = "obsolete", + [OPAL_STATUS_OBSOLETE2] = "obsolete (0x0d)", + [OPAL_STATUS_OBSOLETE3] = "obsolete (0x0e)", [OPAL_STATUS_TPER_MALFUNCTION] = "TPer malfunction", [OPAL_STATUS_TRANSACTION_FAILURE] = "transaction failure", [OPAL_STATUS_RESPONSE_OVERFLOW] = "response overflow", [OPAL_STATUS_AUTHORITY_LOCKED_OUT] = "authority locked out", - [OPAL_STATUS_FAIL] = "unknown failure", }; static const char *opal_status_to_string(int t) @@ -85,6 +85,10 @@ static const char *opal_status_to_string(int t) if (t < 0) return strerror(-t); + /* Fail, as defined by specification */ + if (t == 0x3f) + return "unknown failure"; + if (t >= _OPAL_STATUS_MAX) return "unknown error"; @@ -232,6 +236,8 @@ static int opal_ioctl(struct crypt_device *cd, int fd, unsigned long rq, void *a opal_ioctl_debug(cd, rq, args, false, 0); r = ioctl(fd, rq, args); + if (r < 0) + r = -errno; opal_ioctl_debug(cd, rq, args, true, r); return r; @@ -306,12 +312,13 @@ static int opal_range_check_attributes_fd(struct crypt_device *cd, .session = { .who = segment_number + 1, .opal_key = { - .key_len = vk->keylength, + .key_len = crypt_volume_key_length(vk), .lr = segment_number } } }; - memcpy(lrs->session.opal_key.key, vk->key, vk->keylength); + crypt_safe_memcpy(lrs->session.opal_key.key, crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk)); r = opal_ioctl(cd, fd, IOC_OPAL_GET_LR_STATUS, lrs); if (r != OPAL_STATUS_SUCCESS) { @@ -413,7 +420,7 @@ int opal_setup_ranges(struct crypt_device *cd, assert(dev); assert(vk); assert(admin_key); - assert(vk->keylength <= OPAL_KEY_MAX); + assert(crypt_volume_key_length(vk) <= OPAL_KEY_MAX); assert(opal_block_bytes >= SECTOR_SIZE); if (admin_key_len > OPAL_KEY_MAX) @@ -448,7 +455,7 @@ int opal_setup_ranges(struct crypt_device *cd, */ .lr = { 1, 2, 3, 4, 5, 6, 7, 8 }, }; - memcpy(activate->key.key, admin_key, admin_key_len); + crypt_safe_memcpy(activate->key.key, admin_key, admin_key_len); r = opal_ioctl(cd, fd, IOC_OPAL_TAKE_OWNERSHIP, &activate->key); if (r < 0) { @@ -470,6 +477,8 @@ int opal_setup_ranges(struct crypt_device *cd, } r = opal_ioctl(cd, fd, IOC_OPAL_ACTIVATE_LSP, activate); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { log_dbg(cd, "Failed to activate OPAL device '%s': %s", crypt_get_device_name(cd), opal_status_to_string(r)); @@ -490,19 +499,16 @@ int opal_setup_ranges(struct crypt_device *cd, .key_len = admin_key_len, }, }; - memcpy(user_session->opal_key.key, admin_key, admin_key_len); + crypt_safe_memcpy(user_session->opal_key.key, admin_key, admin_key_len); - r = opal_ioctl(cd, fd, IOC_OPAL_ERASE_LR, user_session); + r = opal_ioctl(cd, fd, IOC_OPAL_SECURE_ERASE_LR, user_session); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { - log_dbg(cd, "Failed to reset (erase) OPAL locking range %u on device '%s': %s", + log_dbg(cd, "Failed to reset (secure erase) OPAL locking range %u on device '%s': %s", segment_number, crypt_get_device_name(cd), opal_status_to_string(r)); - r = opal_ioctl(cd, fd, IOC_OPAL_SECURE_ERASE_LR, user_session); - if (r != OPAL_STATUS_SUCCESS) { - log_dbg(cd, "Failed to reset (secure erase) OPAL locking range %u on device '%s': %s", - segment_number, crypt_get_device_name(cd), opal_status_to_string(r)); - r = -EINVAL; - goto out; - } + r = -EINVAL; + goto out; } } @@ -519,9 +525,11 @@ int opal_setup_ranges(struct crypt_device *cd, .key_len = admin_key_len, }, }; - memcpy(user_session->opal_key.key, admin_key, admin_key_len); + crypt_safe_memcpy(user_session->opal_key.key, admin_key, admin_key_len); r = opal_ioctl(cd, fd, IOC_OPAL_ACTIVATE_USR, user_session); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { log_dbg(cd, "Failed to activate OPAL user on device '%s': %s", crypt_get_device_name(cd), opal_status_to_string(r)); @@ -544,9 +552,11 @@ int opal_setup_ranges(struct crypt_device *cd, }, .l_state = OPAL_RO, }; - memcpy(user_add_to_lr->session.opal_key.key, admin_key, admin_key_len); + crypt_safe_memcpy(user_add_to_lr->session.opal_key.key, admin_key, admin_key_len); r = opal_ioctl(cd, fd, IOC_OPAL_ADD_USR_TO_LR, user_add_to_lr); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { log_dbg(cd, "Failed to add OPAL user to locking range %u (RO) on device '%s': %s", segment_number, crypt_get_device_name(cd), opal_status_to_string(r)); @@ -555,6 +565,8 @@ int opal_setup_ranges(struct crypt_device *cd, } user_add_to_lr->l_state = OPAL_RW; r = opal_ioctl(cd, fd, IOC_OPAL_ADD_USR_TO_LR, user_add_to_lr); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { log_dbg(cd, "Failed to add OPAL user to locking range %u (RW) on device '%s': %s", segment_number, crypt_get_device_name(cd), opal_status_to_string(r)); @@ -578,15 +590,18 @@ int opal_setup_ranges(struct crypt_device *cd, .new_user_pw = { .who = segment_number + 1, .opal_key = { - .key_len = vk->keylength, + .key_len = crypt_volume_key_length(vk), .lr = segment_number, }, }, }; - memcpy(new_pw->new_user_pw.opal_key.key, vk->key, vk->keylength); - memcpy(new_pw->session.opal_key.key, admin_key, admin_key_len); + crypt_safe_memcpy(new_pw->new_user_pw.opal_key.key, crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk)); + crypt_safe_memcpy(new_pw->session.opal_key.key, admin_key, admin_key_len); r = opal_ioctl(cd, fd, IOC_OPAL_SET_PW, new_pw); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { log_dbg(cd, "Failed to set OPAL user password on device '%s': (%d) %s", crypt_get_device_name(cd), r, opal_status_to_string(r)); @@ -616,9 +631,11 @@ int opal_setup_ranges(struct crypt_device *cd, }, }, }; - memcpy(setup->session.opal_key.key, admin_key, admin_key_len); + crypt_safe_memcpy(setup->session.opal_key.key, admin_key, admin_key_len); r = opal_ioctl(cd, fd, IOC_OPAL_LR_SETUP, setup); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { log_dbg(cd, "Failed to setup locking range of length %llu at offset %llu on OPAL device '%s': %s", setup->range_length, setup->range_start, crypt_get_device_name(cd), opal_status_to_string(r)); @@ -638,14 +655,17 @@ int opal_setup_ranges(struct crypt_device *cd, .session = { .who = segment_number + 1, .opal_key = { - .key_len = vk->keylength, + .key_len = crypt_volume_key_length(vk), .lr = segment_number, }, } }; - memcpy(lock->session.opal_key.key, vk->key, vk->keylength); + crypt_safe_memcpy(lock->session.opal_key.key, crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk)); r = opal_ioctl(cd, fd, IOC_OPAL_LOCK_UNLOCK, lock); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { log_dbg(cd, "Failed to lock OPAL device '%s': %s", crypt_get_device_name(cd), opal_status_to_string(r)); @@ -696,10 +716,11 @@ static int opal_lock_unlock(struct crypt_device *cd, return -EIO; if (!lock) { - assert(vk->keylength <= OPAL_KEY_MAX); + assert(crypt_volume_key_length(vk) <= OPAL_KEY_MAX); - unlock.session.opal_key.key_len = vk->keylength; - memcpy(unlock.session.opal_key.key, vk->key, vk->keylength); + unlock.session.opal_key.key_len = crypt_volume_key_length(vk); + crypt_safe_memcpy(unlock.session.opal_key.key, crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk)); } r = opal_ioctl(cd, fd, IOC_OPAL_LOCK_UNLOCK, &unlock); @@ -734,6 +755,8 @@ static int opal_lock_unlock(struct crypt_device *cd, unlock.flags = OPAL_SAVE_FOR_LOCK; r = opal_ioctl(cd, fd, IOC_OPAL_SAVE, &unlock); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { if (!lock) log_std(cd, "Failed to prepare OPAL device '%s' for sleep resume, be aware before suspending: %s", @@ -787,11 +810,15 @@ int opal_factory_reset(struct crypt_device *cd, if (password_len > OPAL_KEY_MAX) return -EINVAL; - fd = device_open(cd, dev, O_RDONLY); + /* + * Submit PSID reset on R/W file descriptor so it + * triggers blkid rescan after we close it. + */ + fd = device_open(cd, dev, O_RDWR); if (fd < 0) return -EIO; - memcpy(reset.key, password, password_len); + crypt_safe_memcpy(reset.key, password, password_len); r = opal_ioctl(cd, fd, IOC_OPAL_PSID_REVERT_TPR, &reset); if (r < 0) { @@ -848,7 +875,7 @@ int opal_reset_segment(struct crypt_device *cd, .key_len = password_len, }, }; - memcpy(user_session->opal_key.key, password, password_len); + crypt_safe_memcpy(user_session->opal_key.key, password, password_len); fd = device_open(cd, dev, O_RDONLY); if (fd < 0) { @@ -856,42 +883,39 @@ int opal_reset_segment(struct crypt_device *cd, goto out; } - r = opal_ioctl(cd, fd, IOC_OPAL_ERASE_LR, user_session); + r = opal_ioctl(cd, fd, IOC_OPAL_SECURE_ERASE_LR, user_session); + if (r < 0) + goto out; if (r != OPAL_STATUS_SUCCESS) { - log_dbg(cd, "Failed to reset (erase) OPAL locking range %u on device '%s': %s", + log_dbg(cd, "Failed to reset (secure erase) OPAL locking range %u on device '%s': %s", segment_number, crypt_get_device_name(cd), opal_status_to_string(r)); - r = opal_ioctl(cd, fd, IOC_OPAL_SECURE_ERASE_LR, user_session); - if (r != OPAL_STATUS_SUCCESS) { - log_dbg(cd, "Failed to reset (secure erase) OPAL locking range %u on device '%s': %s", - segment_number, crypt_get_device_name(cd), opal_status_to_string(r)); - r = -EINVAL; - goto out; - } + r = -EINVAL; + goto out; + } - /* Unlike IOC_OPAL_ERASE_LR, IOC_OPAL_SECURE_ERASE_LR does not disable the locking range, - * we have to do that by hand. - */ - setup = crypt_safe_alloc(sizeof(struct opal_user_lr_setup)); - if (!setup) { - r = -ENOMEM; - goto out; - } - *setup = (struct opal_user_lr_setup) { - .range_start = 0, - .range_length = 0, - .session = { - .who = OPAL_ADMIN1, - .opal_key = user_session->opal_key, - }, - }; + /* Disable the locking range */ + setup = crypt_safe_alloc(sizeof(struct opal_user_lr_setup)); + if (!setup) { + r = -ENOMEM; + goto out; + } + *setup = (struct opal_user_lr_setup) { + .range_start = 0, + .range_length = 0, + .session = { + .who = OPAL_ADMIN1, + .opal_key = user_session->opal_key, + }, + }; - r = opal_ioctl(cd, fd, IOC_OPAL_LR_SETUP, setup); - if (r != OPAL_STATUS_SUCCESS) { - log_dbg(cd, "Failed to disable locking range on OPAL device '%s': %s", - crypt_get_device_name(cd), opal_status_to_string(r)); - r = -EINVAL; - goto out; - } + r = opal_ioctl(cd, fd, IOC_OPAL_LR_SETUP, setup); + if (r < 0) + goto out; + if (r != OPAL_STATUS_SUCCESS) { + log_dbg(cd, "Failed to disable locking range on OPAL device '%s': %s", + crypt_get_device_name(cd), opal_status_to_string(r)); + r = -EINVAL; + goto out; } out: crypt_safe_free(user_session); diff --git a/lib/luks2/hw_opal/hw_opal.h b/lib/luks2/hw_opal/hw_opal.h index f25c2b6..f0058a5 100644 --- a/lib/luks2/hw_opal/hw_opal.h +++ b/lib/luks2/hw_opal/hw_opal.h @@ -3,7 +3,8 @@ * OPAL utilities * * Copyright (C) 2022-2023 Luca Boccassi - * 2023 Ondrej Kozina + * Copyright (C) 2023-2025 Ondrej Kozina + * Copyright (C) 2024-2025 Milan Broz */ #ifndef _UTILS_OPAL diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h index 609162f..30568db 100644 --- a/lib/luks2/luks2.h +++ b/lib/luks2/luks2.h @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup v2 * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Milan Broz + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Milan Broz */ #ifndef _CRYPTSETUP_LUKS2_ONDISK_H @@ -37,6 +37,8 @@ #define LUKS2_DIGEST_MAX 8 +#define LUKS2_MIN_INTEGRITY_KEY_BYTES 16 + #define CRYPT_ANY_SEGMENT -1 #define CRYPT_DEFAULT_SEGMENT -2 #define CRYPT_ONE_SEGMENT -3 @@ -109,6 +111,7 @@ struct luks2_hdr { char uuid[LUKS2_UUID_L]; void *jobj; void *jobj_rollback; + size_t on_disk_json_end_offset; }; struct luks2_keyslot_params { @@ -196,11 +199,11 @@ int LUKS2_keyslot_open(struct crypt_device *cd, size_t password_len, struct volume_key **vk); -int LUKS2_keyslot_open_all_segments(struct crypt_device *cd, +int LUKS2_keyslot_context_open_all_segments(struct crypt_device *cd, int keyslot_old, int keyslot_new, - const char *password, - size_t password_len, + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new, struct volume_key **vks); int LUKS2_keyslot_store(struct crypt_device *cd, @@ -228,6 +231,24 @@ int LUKS2_keyslot_swap(struct crypt_device *cd, int keyslot, int keyslot2); +/* + * Segments + */ + +bool LUKS2_segment_set_size(struct luks2_hdr *hdr, + int segment, + const uint64_t *segment_size_bytes); + +bool LUKS2_segment_is_hw_opal(struct luks2_hdr *hdr, int segment); +bool LUKS2_segment_is_hw_opal_crypt(struct luks2_hdr *hdr, int segment); +bool LUKS2_segment_is_hw_opal_only(struct luks2_hdr *hdr, int segment); + +int LUKS2_get_opal_segment_number(struct luks2_hdr *hdr, int segment, + uint32_t *ret_opal_segment_number); +int LUKS2_get_opal_key_size(struct luks2_hdr *hdr, int segment); + +bool LUKS2_segments_dynamic_size(struct luks2_hdr *hdr); + /* * Generic LUKS2 token */ @@ -263,17 +284,6 @@ crypt_token_info LUKS2_token_status(struct crypt_device *cd, int token, const char **type); -int LUKS2_token_open_and_activate(struct crypt_device *cd, - struct luks2_hdr *hdr, - int keyslot, - int token, - const char *name, - const char *type, - const char *pin, - size_t pin_size, - uint32_t flags, - void *usrptr); - int LUKS2_token_unlock_key(struct crypt_device *cd, struct luks2_hdr *hdr, int keyslot, @@ -307,8 +317,7 @@ void crypt_token_unload_external_all(struct crypt_device *cd); /* * Generic LUKS2 digest */ -int LUKS2_digest_any_matching(struct crypt_device *cd, - struct luks2_hdr *hdr, +int LUKS2_digest_verify_by_any_matching(struct crypt_device *cd, const struct volume_key *vk); int LUKS2_digest_verify_by_segment(struct crypt_device *cd, @@ -371,6 +380,7 @@ int LUKS2_generate_hdr( const struct volume_key *vk, const char *cipher_spec, const char *integrity, + uint32_t integrity_key_size, /* in bytes, only if separate (HMAC) */ const char *uuid, unsigned int sector_size, uint64_t data_offset, @@ -398,8 +408,10 @@ int LUKS2_get_data_size(struct luks2_hdr *hdr, uint64_t *size, bool *dynamic); uint32_t LUKS2_get_sector_size(struct luks2_hdr *hdr); const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment); const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment); +int LUKS2_get_integrity_key_size(struct luks2_hdr *hdr, int segment); int LUKS2_keyslot_params_default(struct crypt_device *cd, struct luks2_hdr *hdr, struct luks2_keyslot_params *params); +int LUKS2_get_old_volume_key_size(struct luks2_hdr *hdr); int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment); int LUKS2_get_keyslot_stored_key_size(struct luks2_hdr *hdr, int keyslot); const char *LUKS2_get_keyslot_cipher(struct luks2_hdr *hdr, int keyslot, size_t *key_size); @@ -427,7 +439,7 @@ int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint3 /* * Requirements for device activation or header modification */ -int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *reqs); +void LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *reqs); int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs, bool commit); int LUKS2_config_set_requirement_version(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t req_id, uint8_t req_version, bool commit); @@ -435,12 +447,10 @@ int LUKS2_config_get_reencrypt_version(struct luks2_hdr *hdr, uint8_t *version); bool LUKS2_reencrypt_requirement_candidate(struct luks2_hdr *hdr); -int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs_mask, int quiet); +int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint64_t reqs_mask, int quiet); int LUKS2_key_description_by_segment(struct crypt_device *cd, struct luks2_hdr *hdr, struct volume_key *vk, int segment); -int LUKS2_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd, - struct luks2_hdr *hdr, struct volume_key *vk, int keyslot); int LUKS2_volume_key_load_in_keyring_by_digest(struct crypt_device *cd, struct volume_key *vk, int digest); @@ -454,13 +464,6 @@ int LUKS2_luks2_to_luks1(struct crypt_device *cd, /* * LUKS2 reencryption */ -int LUKS2_reencrypt_locked_recovery_by_passphrase(struct crypt_device *cd, - int keyslot_old, - int keyslot_new, - const char *passphrase, - size_t passphrase_size, - struct volume_key **vks); - int LUKS2_reencrypt_locked_recovery_by_vks(struct crypt_device *cd, struct volume_key *vks); @@ -489,20 +492,10 @@ int LUKS2_reencrypt_check_device_size(struct crypt_device *cd, bool device_exclusive_check, bool dynamic); -void LUKS2_reencrypt_lookup_key_ids(struct crypt_device *cd, - struct luks2_hdr *hdr, - struct volume_key *vk); - int LUKS2_reencrypt_digest_verify(struct crypt_device *cd, struct luks2_hdr *hdr, struct volume_key *vks); -int LUKS2_reencrypt_max_hotzone_size(struct crypt_device *cd, - struct luks2_hdr *hdr, - const struct reenc_protection *rp, - int reencrypt_keyslot, - uint64_t *r_length); - -void LUKS2_reencrypt_protection_erase(struct reenc_protection *rp); +unsigned LUKS2_reencrypt_vks_count(struct luks2_hdr *hdr); #endif diff --git a/lib/luks2/luks2_digest.c b/lib/luks2/luks2_digest.c index a38a220..fb9a902 100644 --- a/lib/luks2/luks2_digest.c +++ b/lib/luks2/luks2_digest.c @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup v2, digest handling * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Milan Broz + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Milan Broz */ #include "luks2_internal.h" @@ -75,7 +75,7 @@ int LUKS2_digest_create(struct crypt_device *cd, log_dbg(cd, "Creating new digest %d (%s).", digest, type); - return dh->store(cd, digest, vk->key, vk->keylength) ?: digest; + return dh->store(cd, digest, crypt_volume_key_get_key(vk), crypt_volume_key_length(vk)) ?: digest; } int LUKS2_digest_by_keyslot(struct luks2_hdr *hdr, int keyslot) @@ -108,7 +108,7 @@ int LUKS2_digest_verify_by_digest(struct crypt_device *cd, if (!h) return -EINVAL; - r = h->verify(cd, digest, vk->key, vk->keylength); + r = h->verify(cd, digest, crypt_volume_key_get_key(vk), crypt_volume_key_length(vk)); if (r < 0) { log_dbg(cd, "Digest %d (%s) verify failed with %d.", digest, h->name, r); return r; @@ -143,8 +143,7 @@ int LUKS2_digest_dump(struct crypt_device *cd, int digest) return h->dump(cd, digest); } -int LUKS2_digest_any_matching(struct crypt_device *cd, - struct luks2_hdr *hdr __attribute__((unused)), +int LUKS2_digest_verify_by_any_matching(struct crypt_device *cd, const struct volume_key *vk) { int digest; @@ -161,7 +160,7 @@ int LUKS2_digest_verify_by_segment(struct crypt_device *cd, int segment, const struct volume_key *vk) { - int r = -EINVAL; + int r; unsigned s; if (segment == CRYPT_ANY_SEGMENT) { @@ -173,7 +172,11 @@ int LUKS2_digest_verify_by_segment(struct crypt_device *cd, return -EPERM; } - return LUKS2_digest_verify_by_digest(cd, LUKS2_digest_by_segment(hdr, segment), vk); + r = LUKS2_digest_by_segment(hdr, segment); + if (r < 0) + return r; + + return LUKS2_digest_verify_by_digest(cd, r, vk); } /* FIXME: segment can have more digests */ @@ -420,21 +423,7 @@ int LUKS2_key_description_by_segment(struct crypt_device *cd, char *desc = get_key_description_by_digest(cd, LUKS2_digest_by_segment(hdr, segment)); int r; - r = crypt_volume_key_set_description(vk, desc); - free(desc); - return r; -} - -int LUKS2_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd, - struct luks2_hdr *hdr, struct volume_key *vk, int keyslot) -{ - char *desc = get_key_description_by_digest(cd, LUKS2_digest_by_keyslot(hdr, keyslot)); - int r; - - r = crypt_volume_key_set_description(vk, desc); - if (!r) - r = crypt_volume_key_load_in_keyring(cd, vk); - + r = crypt_volume_key_set_description(vk, desc, LOGON_KEY); free(desc); return r; } @@ -445,7 +434,7 @@ int LUKS2_volume_key_load_in_keyring_by_digest(struct crypt_device *cd, char *desc = get_key_description_by_digest(cd, digest); int r; - r = crypt_volume_key_set_description(vk, desc); + r = crypt_volume_key_set_description(vk, desc, LOGON_KEY); if (!r) r = crypt_volume_key_load_in_keyring(cd, vk); diff --git a/lib/luks2/luks2_digest_pbkdf2.c b/lib/luks2/luks2_digest_pbkdf2.c index 3e88e6a..e5e06ee 100644 --- a/lib/luks2/luks2_digest_pbkdf2.c +++ b/lib/luks2/luks2_digest_pbkdf2.c @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup v2, PBKDF2 digest handler (LUKS1 compatible) * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Milan Broz + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Milan Broz */ #include "luks2_internal.h" diff --git a/lib/luks2/luks2_disk_metadata.c b/lib/luks2/luks2_disk_metadata.c index 5939aab..e649f89 100644 --- a/lib/luks2/luks2_disk_metadata.c +++ b/lib/luks2/luks2_disk_metadata.c @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup v2 * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Milan Broz + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Milan Broz */ #include "luks2_internal.h" @@ -289,22 +289,25 @@ static int hdr_read_disk(struct crypt_device *cd, */ static int hdr_write_disk(struct crypt_device *cd, struct device *device, struct luks2_hdr *hdr, - const char *json_area, int secondary) + const char *json_area, size_t write_area_len, + int secondary) { struct luks2_hdr_disk hdr_disk; uint64_t offset = secondary ? hdr->hdr_size : 0; size_t hdr_json_len; int devfd, r; + hdr_json_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN; + + assert(write_area_len <= hdr_json_len); + log_dbg(cd, "Trying to write LUKS2 header (%zu bytes) at offset %" PRIu64 ".", - hdr->hdr_size, offset); + write_area_len, offset); devfd = device_open_locked(cd, device, O_RDWR); if (devfd < 0) return devfd == -1 ? -EINVAL : devfd; - hdr_json_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN; - hdr_to_disk(hdr, &hdr_disk, secondary, offset); /* @@ -321,8 +324,8 @@ static int hdr_write_disk(struct crypt_device *cd, */ if (write_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device), - CONST_CAST(char*)json_area, hdr_json_len, - LUKS2_HDR_BIN_LEN + offset) < (ssize_t)hdr_json_len) { + CONST_CAST(char*)json_area, write_area_len, + LUKS2_HDR_BIN_LEN + offset) < (ssize_t)write_area_len) { return -EIO; } @@ -401,7 +404,7 @@ int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr, struct { char *json_area; const char *json_text; - size_t json_area_len; + size_t json_data_len, json_area_len, json_area_write_len; int r; if (hdr->version != 2) { @@ -413,29 +416,47 @@ int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr, struct if (r) return r; - /* - * Allocate and zero JSON area (of proper header size). - */ - json_area_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN; - json_area = crypt_zalloc(json_area_len); - if (!json_area) - return -ENOMEM; - /* * Generate text space-efficient JSON representation to json area. */ - json_text = json_object_to_json_string_ext(hdr->jobj, - JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); + json_text = crypt_jobj_to_string_on_disk(hdr->jobj); if (!json_text || !*json_text) { log_dbg(cd, "Cannot parse JSON object to text representation."); - free(json_area); return -ENOMEM; } - if (strlen(json_text) > (json_area_len - 1)) { - log_dbg(cd, "JSON is too large (%zu > %zu).", strlen(json_text), json_area_len); - free(json_area); + + json_area_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN; + json_area_write_len = json_data_len = strlen(json_text); + + if (json_data_len > (json_area_len - 1)) { + log_dbg(cd, "JSON is too large (%zu > %zu).", json_data_len, json_area_len - 1); return -EINVAL; } + + /* + * Allocate and zero JSON area (of proper header size). + */ + json_area = crypt_zalloc(json_area_len); + if (!json_area) + return -ENOMEM; + + /* + * If the metadata in 'json_area' buffer is smaller than last on-disk + * metadata we also have to erase the remaining bytes between the tail + * of current metadata and the on-disk metadata end pointer. Set write + * area length large enough to overwrite it. + * + * If seqid_check is turned off (during LUKS2 format) write entire + * LUKS2 metadata size instead. + * + * Turn off the optimization also during metadata upconversion + * (hdr->on_disk_json_end_offset == 0). + */ + if (seqid_check && (json_data_len < hdr->on_disk_json_end_offset)) + json_area_write_len = hdr->on_disk_json_end_offset; + else if (!seqid_check || !hdr->on_disk_json_end_offset) + json_area_write_len = json_area_len; + strncpy(json_area, json_text, json_area_len); if (seqid_check) @@ -451,13 +472,16 @@ int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr, struct hdr->seqid++; /* Write primary and secondary header */ - r = hdr_write_disk(cd, device, hdr, json_area, 0); + r = hdr_write_disk(cd, device, hdr, json_area, json_area_write_len, 0); if (!r) - r = hdr_write_disk(cd, device, hdr, json_area, 1); + r = hdr_write_disk(cd, device, hdr, json_area, json_area_write_len, 1); if (r) log_dbg(cd, "LUKS2 header write failed (%d).", r); + /* store new json end pointer or reset it on error */ + hdr->on_disk_json_end_offset = r ? 0 : json_data_len; + device_write_unlock(cd, device); free(json_area); @@ -525,12 +549,15 @@ static int validate_luks2_json_object(struct crypt_device *cd, json_object *jobj } static json_object *parse_and_validate_json(struct crypt_device *cd, - const char *json_area, uint64_t hdr_size) + const char *json_area, uint64_t hdr_size, + uint64_t *json_area_end) { int json_len, r; json_object *jobj; uint64_t max_length; + assert(json_area_end); + if (hdr_size <= LUKS2_HDR_BIN_LEN || hdr_size > LUKS2_HDR_OFFSET_MAX) { log_dbg(cd, "LUKS2 header JSON has bogus size 0x%04" PRIx64 ".", hdr_size); return NULL; @@ -554,6 +581,8 @@ static json_object *parse_and_validate_json(struct crypt_device *cd, jobj = NULL; } + *json_area_end = json_len; + return jobj; } @@ -617,7 +646,7 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, json_object *jobj_hdr1 = NULL, *jobj_hdr2 = NULL; unsigned int i; int r; - uint64_t hdr_size; + uint64_t hdr_size, json_area_end1 = 0, json_area_end2 = 0; uint64_t hdr2_offsets[] = LUKS2_HDR2_OFFSETS; /* Skip auto-recovery if locks are disabled and we're not doing LUKS2 explicit repair */ @@ -632,7 +661,7 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, state_hdr1 = HDR_FAIL; r = hdr_read_disk(cd, device, &hdr_disk1, &json_area1, 0, 0); if (r == 0) { - jobj_hdr1 = parse_and_validate_json(cd, json_area1, be64_to_cpu(hdr_disk1.hdr_size)); + jobj_hdr1 = parse_and_validate_json(cd, json_area1, be64_to_cpu(hdr_disk1.hdr_size), &json_area_end1); state_hdr1 = jobj_hdr1 ? HDR_OK : HDR_OBSOLETE; } else if (r == -EIO) state_hdr1 = HDR_FAIL_IO; @@ -644,7 +673,7 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, if (state_hdr1 != HDR_FAIL && state_hdr1 != HDR_FAIL_IO) { r = hdr_read_disk(cd, device, &hdr_disk2, &json_area2, be64_to_cpu(hdr_disk1.hdr_size), 1); if (r == 0) { - jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size)); + jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size), &json_area_end2); state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE; } else if (r == -EIO) state_hdr2 = HDR_FAIL_IO; @@ -657,7 +686,7 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, r = hdr_read_disk(cd, device, &hdr_disk2, &json_area2, hdr2_offsets[i], 1); if (r == 0) { - jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size)); + jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size), &json_area_end2); state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE; } else if (r == -EIO) state_hdr2 = HDR_FAIL_IO; @@ -706,7 +735,7 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, log_dbg(cd, "Cannot generate header salt."); else { hdr_from_disk(&hdr_disk1, &hdr_disk2, hdr, 0); - r = hdr_write_disk(cd, device, hdr, json_area1, 1); + r = hdr_write_disk(cd, device, hdr, json_area1, hdr->hdr_size - LUKS2_HDR_BIN_LEN, 1); } if (r) log_dbg(cd, "Secondary LUKS2 header recovery failed."); @@ -727,7 +756,7 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, log_dbg(cd, "Cannot generate header salt."); else { hdr_from_disk(&hdr_disk2, &hdr_disk1, hdr, 1); - r = hdr_write_disk(cd, device, hdr, json_area2, 0); + r = hdr_write_disk(cd, device, hdr, json_area2, hdr->hdr_size - LUKS2_HDR_BIN_LEN, 0); } if (r) log_dbg(cd, "Primary LUKS2 header recovery failed."); @@ -756,6 +785,11 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, json_object_put(jobj_hdr1); } + if (json_area_end1 > json_area_end2) + hdr->on_disk_json_end_offset = json_area_end1; + else + hdr->on_disk_json_end_offset = json_area_end2; + /* * FIXME: should this fail? At least one header was read correctly. * r = (state_hdr1 == HDR_FAIL_IO || state_hdr2 == HDR_FAIL_IO) ? -EIO : -EINVAL; diff --git a/lib/luks2/luks2_internal.h b/lib/luks2/luks2_internal.h index 6f991c0..adaea91 100644 --- a/lib/luks2/luks2_internal.h +++ b/lib/luks2/luks2_internal.h @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * LUKS - Linux Unified Key Setup v2 + * LUKS - Linux Unified Key Setup v2 (with JSON internals) * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Milan Broz + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Milan Broz */ #ifndef _CRYPTSETUP_LUKS2_INTERNAL_H @@ -48,6 +48,16 @@ uint64_t crypt_jobj_get_uint64(json_object *jobj); uint32_t crypt_jobj_get_uint32(json_object *jobj); json_object *crypt_jobj_new_uint64(uint64_t value); +/* + * Generate json format string representation libcryptsetup uses + * to store json metadata on disk. + */ +static inline const char *crypt_jobj_to_string_on_disk(json_object *jobj) +{ + return json_object_to_json_string_ext(jobj, + JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); +} + int json_object_object_add_by_uint(json_object *jobj, unsigned key, json_object *jobj_val); int json_object_object_add_by_uint_by_ref(json_object *jobj, unsigned key, json_object **jobj_val_ref); void json_object_object_del_by_uint(json_object *jobj, unsigned key); @@ -89,7 +99,7 @@ json_object *LUKS2_array_remove(json_object *array, const char *num); */ /** - * LUKS2 keyslots handlers (EXPERIMENTAL) + * LUKS2 keyslots handlers */ typedef int (*keyslot_alloc_func)(struct crypt_device *cd, int keyslot, size_t volume_key_len, @@ -152,7 +162,7 @@ struct reenc_protection { }; /** - * LUKS2 digest handlers (EXPERIMENTAL) + * LUKS2 digest handlers */ typedef int (*digest_verify_func)(struct crypt_device *cd, int digest, const char *volume_key, size_t volume_key_len); @@ -292,7 +302,7 @@ void json_segment_remove_flag(json_object *jobj_segment, const char *flag); uint64_t json_segments_get_minimal_offset(json_object *jobj_segments, unsigned blockwise); json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length, unsigned reencryption); json_object *json_segment_create_crypt(uint64_t offset, uint64_t iv_offset, const uint64_t *length, - const char *cipher, const char *integrity, + const char *cipher, const char *integrity, uint32_t integrity_key_size, uint32_t sector_size, unsigned reencryption); json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length, uint32_t segment_number, uint32_t key_size); @@ -337,10 +347,6 @@ uint64_t LUKS2_segment_size(struct luks2_hdr *hdr, int segment, unsigned blockwise); -bool LUKS2_segment_set_size(struct luks2_hdr *hdr, - int segment, - const uint64_t *segment_size_bytes); - uint64_t LUKS2_opal_segment_size(struct luks2_hdr *hdr, int segment, unsigned blockwise); @@ -349,14 +355,6 @@ int LUKS2_segment_is_type(struct luks2_hdr *hdr, int segment, const char *type); -bool LUKS2_segment_is_hw_opal(struct luks2_hdr *hdr, int segment); -bool LUKS2_segment_is_hw_opal_crypt(struct luks2_hdr *hdr, int segment); -bool LUKS2_segment_is_hw_opal_only(struct luks2_hdr *hdr, int segment); - -int LUKS2_get_opal_segment_number(struct luks2_hdr *hdr, int segment, - uint32_t *ret_opal_segment_number); -int LUKS2_get_opal_key_size(struct luks2_hdr *hdr, int segment); - int LUKS2_segment_by_type(struct luks2_hdr *hdr, const char *type); @@ -365,13 +363,20 @@ int LUKS2_last_segment_by_type(struct luks2_hdr *hdr, int LUKS2_get_default_segment(struct luks2_hdr *hdr); -bool LUKS2_segments_dynamic_size(struct luks2_hdr *hdr); - int LUKS2_reencrypt_digest_new(struct luks2_hdr *hdr); int LUKS2_reencrypt_digest_old(struct luks2_hdr *hdr); -unsigned LUKS2_reencrypt_vks_count(struct luks2_hdr *hdr); +int LUKS2_reencrypt_segment_new(struct luks2_hdr *hdr); +int LUKS2_reencrypt_segment_old(struct luks2_hdr *hdr); int LUKS2_reencrypt_data_offset(struct luks2_hdr *hdr, bool blockwise); +int LUKS2_reencrypt_max_hotzone_size(struct crypt_device *cd, + struct luks2_hdr *hdr, + const struct reenc_protection *rp, + int reencrypt_keyslot, + uint64_t *r_length); + +void LUKS2_reencrypt_protection_erase(struct reenc_protection *rp); + /* * Generic LUKS2 digest */ diff --git a/lib/luks2/luks2_json_format.c b/lib/luks2/luks2_json_format.c index 2c7031c..2098028 100644 --- a/lib/luks2/luks2_json_format.c +++ b/lib/luks2/luks2_json_format.c @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup v2, LUKS2 header format code * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Milan Broz + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Milan Broz */ #include "luks2_internal.h" @@ -193,6 +193,7 @@ int LUKS2_generate_hdr( const struct volume_key *vk, const char *cipher_spec, const char *integrity, + uint32_t integrity_key_size, /* in bytes, only if separate (HMAC) */ const char *uuid, unsigned int sector_size, /* in bytes */ uint64_t data_offset, /* in bytes */ @@ -279,8 +280,8 @@ int LUKS2_generate_hdr( if (!opal_key_size) jobj_segment = json_segment_create_crypt(data_offset, 0, NULL, cipher_spec, - integrity, sector_size, - 0); + integrity, integrity_key_size, + sector_size, 0); else if (opal_key_size && cipher_spec) jobj_segment = json_segment_create_opal_crypt(data_offset, &device_size_bytes, opal_segment_number, opal_key_size, 0, diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c index d30d52d..15e83a8 100644 --- a/lib/luks2/luks2_json_metadata.c +++ b/lib/luks2/luks2_json_metadata.c @@ -2,9 +2,9 @@ /* * LUKS - Linux Unified Key Setup v2 * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Milan Broz - * Copyright (C) 2015-2024 Ondrej Kozina + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Milan Broz + * Copyright (C) 2015-2025 Ondrej Kozina */ #include "luks2_internal.h" @@ -13,8 +13,6 @@ #include #include -#define LUKS_STRIPES 4000 - struct interval { uint64_t offset; uint64_t length; @@ -467,8 +465,7 @@ static int hdr_validate_json_size(struct crypt_device *cd, json_object *hdr_jobj json_object_object_get_ex(hdr_jobj, "config", &jobj); json_object_object_get_ex(jobj, "json_size", &jobj1); - json = json_object_to_json_string_ext(hdr_jobj, - JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); + json = crypt_jobj_to_string_on_disk(hdr_jobj); if (!json) return 1; @@ -636,6 +633,11 @@ static int reqs_opal(uint32_t reqs) return reqs & CRYPT_REQUIREMENT_OPAL; } +static int reqs_inline_hw_tags(uint32_t reqs) +{ + return reqs & CRYPT_REQUIREMENT_INLINE_HW_TAGS; +} + /* * Config section requirements object must be valid. * Also general segments section must be validated first. @@ -644,14 +646,12 @@ static int validate_reencrypt_segments(struct crypt_device *cd, json_object *hdr { json_object *jobj, *jobj_backup_previous = NULL, *jobj_backup_final = NULL; uint32_t reqs; - int i, r; + int i; struct luks2_hdr dummy = { .jobj = hdr_jobj }; - r = LUKS2_config_get_requirements(cd, &dummy, &reqs); - if (r) - return 1; + LUKS2_config_get_requirements(cd, &dummy, &reqs); if (reqs_reencrypt_online(reqs)) { for (i = first_backup; i < segments_count; i++) { @@ -1272,7 +1272,11 @@ int LUKS2_hdr_uuid(struct crypt_device *cd, struct luks2_hdr *hdr, const char *u int LUKS2_hdr_labels(struct crypt_device *cd, struct luks2_hdr *hdr, const char *label, const char *subsystem, int commit) { - //FIXME: check if the labels are the same and skip this. + if ((label && strlen(label) >= LUKS2_LABEL_L) || + (subsystem && strlen(subsystem) >= LUKS2_LABEL_L)) { + log_err(cd, _("Label is too long.")); + return -EINVAL; + } memset(hdr->label, 0, LUKS2_LABEL_L); if (label) @@ -1426,7 +1430,8 @@ int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr, } /* do not allow header restore from backup with unmet requirements */ - if (LUKS2_unmet_requirements(cd, &hdr_file, CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 1)) { + if (LUKS2_unmet_requirements(cd, &hdr_file, + CRYPT_REQUIREMENT_ONLINE_REENCRYPT | CRYPT_REQUIREMENT_INLINE_HW_TAGS, 1)) { log_err(cd, _("Forbidden LUKS2 requirements detected in backup %s."), backup_file); r = -ETXTBSY; @@ -1458,9 +1463,7 @@ int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr, r = LUKS2_hdr_read(cd, &tmp_hdr, 0); if (r == 0) { log_dbg(cd, "Device %s already contains LUKS2 header, checking UUID and requirements.", device_path(device)); - r = LUKS2_config_get_requirements(cd, &tmp_hdr, &reqs); - if (r) - goto out; + LUKS2_config_get_requirements(cd, &tmp_hdr, &reqs); if (memcmp(tmp_hdr.uuid, hdr_file.uuid, LUKS2_UUID_L)) diff_uuid = 1; @@ -1544,7 +1547,7 @@ int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr, * Persistent config flags */ static const struct { - uint32_t flag; + uint64_t flag; const char *description; } persistent_flags[] = { { CRYPT_ACTIVATE_ALLOW_DISCARDS, "allow-discards" }, @@ -1553,6 +1556,7 @@ static const struct { { CRYPT_ACTIVATE_NO_JOURNAL, "no-journal" }, { CRYPT_ACTIVATE_NO_READ_WORKQUEUE, "no-read-workqueue" }, { CRYPT_ACTIVATE_NO_WRITE_WORKQUEUE, "no-write-workqueue" }, + { CRYPT_ACTIVATE_HIGH_PRIORITY, "high_priority" }, { 0, NULL } }; @@ -1642,6 +1646,7 @@ static const struct requirement_flag requirements_flags[] = { { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 2, "online-reencrypt-v2" }, { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 3, "online-reencrypt-v3" }, { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 1, "online-reencrypt" }, + { CRYPT_REQUIREMENT_INLINE_HW_TAGS, 1, "inline-hw-tags" }, { CRYPT_REQUIREMENT_OPAL, 1, "opal" }, { 0, 0, NULL } }; @@ -1764,7 +1769,7 @@ static const struct requirement_flag *stored_requirement_name_by_id(struct luks2 /* * returns count of requirements (past cryptsetup 2.0 release) */ -int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *reqs) +void LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *reqs) { json_object *jobj_mandatory, *jobj; int i, len; @@ -1777,11 +1782,11 @@ int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr jobj_mandatory = mandatory_requirements_jobj(hdr); if (!jobj_mandatory) - return 0; + return; len = (int) json_object_array_length(jobj_mandatory); if (len <= 0) - return 0; + return; log_dbg(cd, "LUKS2 requirements detected:"); @@ -1792,8 +1797,6 @@ int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr reqs_unknown(req->flag) ? "un" : ""); *reqs |= req->flag; } - - return 0; } int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs, bool commit) @@ -1801,7 +1804,7 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; int i, r = -EINVAL; const struct requirement_flag *req; - uint32_t req_id; + uint64_t req_id; if (!hdr) return -EINVAL; @@ -2128,6 +2131,10 @@ static void hdr_dump_segments(struct crypt_device *cd, json_object *hdr_jobj) json_object_object_get_ex(jobj1, "type", &jobj2)) log_std(cd, "\tintegrity: %s\n", json_object_get_string(jobj2)); + if (json_object_object_get_ex(jobj_segment, "integrity", &jobj1) && + json_object_object_get_ex(jobj1, "key_size", &jobj2)) + log_std(cd, "\tintegrity key size: %" PRIu32 " [bits]\n", crypt_jobj_get_uint32(jobj2) * 8); + if (json_object_object_get_ex(jobj_segment, "flags", &jobj1) && (flags = (int)json_object_array_length(jobj1)) > 0) { jobj2 = json_object_array_get_idx(jobj1, 0); @@ -2304,12 +2311,7 @@ crypt_reencrypt_info LUKS2_reencrypt_status(struct luks2_hdr *hdr) { uint32_t reqs; - /* - * Any unknown requirement or offline reencryption should abort - * anything related to online-reencryption handling - */ - if (LUKS2_config_get_requirements(NULL, hdr, &reqs)) - return CRYPT_REENCRYPT_INVALID; + LUKS2_config_get_requirements(NULL, hdr, &reqs); if (!reqs_reencrypt_online(reqs)) return CRYPT_REENCRYPT_NONE; @@ -2363,6 +2365,24 @@ const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment) return json_object_get_string(jobj3); } +int LUKS2_get_integrity_key_size(struct luks2_hdr *hdr, int segment) +{ + json_object *jobj1, *jobj2, *jobj3; + + jobj1 = LUKS2_get_segment_jobj(hdr, segment); + if (!jobj1) + return -1; + + if (!json_object_object_get_ex(jobj1, "integrity", &jobj2)) + return -1; + + /* The value is optional, do not fail if not present */ + if (!json_object_object_get_ex(jobj2, "key_size", &jobj3)) + return 0; + + return json_object_get_int(jobj3); +} + /* FIXME: this only ensures that once we have journal encryption, it is not ignored. */ /* implement segment count and type restrictions (crypt and only single crypt) */ static int LUKS2_integrity_compatible(struct luks2_hdr *hdr) @@ -2450,6 +2470,19 @@ int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment) return -1; } +int LUKS2_get_old_volume_key_size(struct luks2_hdr *hdr) +{ + int old_segment; + + assert(hdr); + + old_segment = LUKS2_reencrypt_segment_old(hdr); + if (old_segment < 0) + return old_segment; + + return LUKS2_get_volume_key_size(hdr, old_segment); +} + uint32_t LUKS2_get_sector_size(struct luks2_hdr *hdr) { return json_segment_get_sector_size(LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT)); @@ -2518,7 +2551,7 @@ int LUKS2_assembly_multisegment_dmd(struct crypt_device *cd, crypt_data_device(cd), vk, json_segment_get_cipher(jobj), json_segment_get_iv_offset(jobj), - segment_offset, "none", 0, + segment_offset, "none", 0, 0, json_segment_get_sector_size(jobj)); if (r) { log_err(cd, _("Failed to set dm-crypt segment.")); @@ -2632,7 +2665,7 @@ int LUKS2_activate(struct crypt_device *cd, { int r; bool dynamic, read_lock, write_lock, opal_lock_on_error = false; - uint32_t opal_segment_number; + uint32_t opal_segment_number, req_flags; uint64_t range_offset_sectors, range_length_sectors, device_length_bytes; struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2); struct crypt_dm_active_device dmdi = {}, dmd = { @@ -2641,7 +2674,8 @@ int LUKS2_activate(struct crypt_device *cd, struct crypt_lock_handle *opal_lh = NULL; /* do not allow activation when particular requirements detected */ - if ((r = LUKS2_unmet_requirements(cd, hdr, CRYPT_REQUIREMENT_OPAL, 0))) + if ((r = LUKS2_unmet_requirements(cd, hdr, + CRYPT_REQUIREMENT_OPAL | CRYPT_REQUIREMENT_INLINE_HW_TAGS, 0))) return r; /* Check that cipher is in compatible format */ @@ -2714,7 +2748,7 @@ int LUKS2_activate(struct crypt_device *cd, crypt_key, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd), crypt_get_data_offset(cd), crypt_get_integrity(cd) ?: "none", - crypt_get_integrity_tag_size(cd), + crypt_get_integrity_key_size(cd, true), crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd)); } else r = dm_linear_target_set(&dmd.segment, 0, @@ -2730,7 +2764,13 @@ int LUKS2_activate(struct crypt_device *cd, dmd.flags |= flags; - if (crypt_get_integrity_tag_size(cd)) { + if (crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &req_flags)) { + r = -EINVAL; + goto out; + } + + if (crypt_get_integrity_tag_size(cd) && + !(req_flags & CRYPT_REQUIREMENT_INLINE_HW_TAGS)) { if (!LUKS2_integrity_compatible(hdr)) { log_err(cd, _("Unsupported device integrity configuration.")); r = -EINVAL; @@ -2810,14 +2850,14 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr struct crypt_dm_active_device dmdc; uint32_t opal_segment_number; char **dep, deps_uuid_prefix[40], *deps[MAX_DM_DEPS+1] = { 0 }; - const char *namei = NULL; + char *iname = NULL; struct crypt_lock_handle *reencrypt_lock = NULL, *opal_lh = NULL; if (!dmd || !dmd->uuid || strncmp(CRYPT_LUKS2, dmd->uuid, sizeof(CRYPT_LUKS2)-1)) return -EINVAL; /* uuid mismatch with metadata (if available) */ - if (hdr && crypt_uuid_cmp(dmd->uuid, hdr->uuid)) + if (hdr && dm_uuid_cmp(dmd->uuid, hdr->uuid)) return -EINVAL; r = snprintf(deps_uuid_prefix, sizeof(deps_uuid_prefix), CRYPT_SUBDEV "-%.32s", dmd->uuid + 6); @@ -2825,15 +2865,15 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr return -EINVAL; /* check if active device has LUKS2-OPAL dm uuid prefix */ - dm_opal_uuid = !crypt_uuid_type_cmp(dmd->uuid, CRYPT_LUKS2_HW_OPAL); + dm_opal_uuid = !dm_uuid_type_cmp(dmd->uuid, CRYPT_LUKS2_HW_OPAL); if (dm_opal_uuid && hdr && !LUKS2_segment_is_hw_opal(hdr, CRYPT_DEFAULT_SEGMENT)) return -EINVAL; tgt = &dmd->segment; /* TODO: We have LUKS2 dependencies now */ - if (single_segment(dmd) && tgt->type == DM_CRYPT && tgt->u.crypt.tag_size) - namei = device_dm_name(tgt->data_device); + if (tgt->type == DM_CRYPT && tgt->u.crypt.tag_size) + iname = dm_get_active_iname(cd, name); r = dm_device_deps(cd, name, deps_uuid_prefix, deps, ARRAY_SIZE(deps)); if (r < 0) @@ -2871,23 +2911,34 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr tgt = &dmdc.segment; while (tgt) { if (tgt->type == DM_CRYPT) - crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description, - LOGON_KEY); + crypt_volume_key_drop_kernel_key(cd, tgt->u.crypt.vk); tgt = tgt->next; } } dm_targets_free(cd, &dmdc); /* TODO: We have LUKS2 dependencies now */ - if (r >= 0 && namei) { - log_dbg(cd, "Deactivating integrity device %s.", namei); - r = dm_remove_device(cd, namei, 0); + if (r >= 0 && iname) { + log_dbg(cd, "Deactivating integrity device %s.", iname); + r = dm_remove_device(cd, iname, 0); } if (!r) { ret = 0; dep = deps; while (*dep) { + /* + * FIXME: dm-integrity has now proper SUBDEV prefix so + * it would be deactivated here, but due to specific + * dm_remove_device(iname) above the iname device + * is no longer active. This will be fixed when + * we switch to SUBDEV deactivation after 2.8 release. + */ + if (iname && !strcmp(*dep, iname)) { + dep++; + continue; + } + log_dbg(cd, "Deactivating LUKS2 dependent device %s.", *dep); r = dm_query_device(cd, *dep, DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE, &dmdc); if (r < 0) { @@ -2907,8 +2958,7 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr tgt = &dmdc.segment; while (tgt) { if (tgt->type == DM_CRYPT) - crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description, - LOGON_KEY); + crypt_volume_key_drop_kernel_key(cd, tgt->u.crypt.vk); tgt = tgt->next; } } @@ -2950,6 +3000,7 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr out: opal_exclusive_unlock(cd, opal_lh); LUKS2_reencrypt_unlock(cd, reencrypt_lock); + free(iname); dep = deps; while (*dep) free(*dep++); @@ -2957,16 +3008,11 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr return r; } -int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs_mask, int quiet) +int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint64_t reqs_mask, int quiet) { uint32_t reqs; - int r = LUKS2_config_get_requirements(cd, hdr, &reqs); - if (r) { - if (!quiet) - log_err(cd, _("Failed to read LUKS2 requirements.")); - return r; - } + LUKS2_config_get_requirements(cd, hdr, &reqs); /* do not mask unknown requirements check */ if (reqs_unknown(reqs)) { @@ -2984,6 +3030,8 @@ int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uin log_err(cd, _("Operation incompatible with device marked for LUKS2 reencryption. Aborting.")); if (reqs_opal(reqs) && !quiet) log_err(cd, _("Operation incompatible with device using OPAL. Aborting.")); + if (reqs_inline_hw_tags(reqs) && !quiet) + log_err(cd, _("Operation incompatible with device using inline HW tags. Aborting.")); /* any remaining unmasked requirement fails the check */ return reqs ? -EINVAL : 0; @@ -3092,22 +3140,22 @@ int LUKS2_split_crypt_and_opal_keys(struct crypt_device *cd __attribute__((unuse if (r < 0) return -EINVAL; - if (vk->keylength < opal_user_key_size) + if (crypt_volume_key_length(vk) < opal_user_key_size) return -EINVAL; /* OPAL SEGMENT only */ - if (vk->keylength == opal_user_key_size) { + if (crypt_volume_key_length(vk) == opal_user_key_size) { *ret_crypt_key = NULL; *ret_opal_key = NULL; return 0; } - opal_key = crypt_alloc_volume_key(opal_user_key_size, vk->key); + opal_key = crypt_alloc_volume_key(opal_user_key_size, crypt_volume_key_get_key(vk)); if (!opal_key) return -ENOMEM; - crypt_key = crypt_alloc_volume_key(vk->keylength - opal_user_key_size, - vk->key + opal_user_key_size); + crypt_key = crypt_alloc_volume_key(crypt_volume_key_length(vk) - opal_user_key_size, + crypt_volume_key_get_key(vk) + opal_user_key_size); if (!crypt_key) { crypt_free_volume_key(opal_key); return -ENOMEM; diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c index bb9d453..9072570 100644 --- a/lib/luks2/luks2_keyslot.c +++ b/lib/luks2/luks2_keyslot.c @@ -2,11 +2,12 @@ /* * LUKS - Linux Unified Key Setup v2, keyslot handling * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Milan Broz + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Milan Broz */ #include "luks2_internal.h" +#include "keyslot_context.h" /* Internal implementations */ extern const keyslot_handler luks2_keyslot; @@ -75,8 +76,38 @@ int LUKS2_keyslot_find_empty(struct crypt_device *cd, struct luks2_hdr *hdr, siz /* Check if a keyslot is assigned to specific segment */ static int _keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment) { - int keyslot_digest, count = 0; + json_object *jobj_keyslots, *jobj; + crypt_keyslot_priority slot_priority; unsigned s; + int keyslot_digest, count = 0; + + /* + * Must not be called with both keyslot == CRYPT_ANY_SLOT + * and segment == CRYPT_ONE_SEGMENT. The CRYPT_DEFAULT_SEGMENT + * and CRYPT_ANY_SEGMENT are handled properly in upper layer. + */ + assert(keyslot >= 0 || segment >= 0); + + if (keyslot == CRYPT_ANY_SLOT) { + json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots); + + json_object_object_foreach(jobj_keyslots, slot, val) { + if (!json_object_object_get_ex(val, "priority", &jobj)) + slot_priority = CRYPT_SLOT_PRIORITY_NORMAL; + else + slot_priority = json_object_get_int(jobj); + + if (slot_priority < CRYPT_SLOT_PRIORITY_NORMAL) + continue; + + keyslot_digest = LUKS2_digest_by_keyslot(hdr, atoi(slot)); + if (keyslot_digest >= 0 && + keyslot_digest == LUKS2_digest_by_segment(hdr, segment)) + return 1; + } + + return 0; + } keyslot_digest = LUKS2_digest_by_keyslot(hdr, keyslot); if (keyslot_digest < 0) @@ -93,16 +124,6 @@ static int _keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment) return count; } -static int _keyslot_for_digest(struct luks2_hdr *hdr, int keyslot, int digest) -{ - int r = -EINVAL; - - r = LUKS2_digest_by_keyslot(hdr, keyslot); - if (r < 0) - return r; - return r == digest ? 0 : -ENOENT; -} - int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment) { int r = -EINVAL; @@ -144,7 +165,17 @@ int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd, const char *ciphe { char cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; - if (!cipher_spec || crypt_is_cipher_null(cipher_spec)) + if (!cipher_spec) + return 1; + + /* + * Do not allow capi format for keyslots + * Note: It always failed in ivsize check later anyway. + */ + if (!strncmp(cipher_spec, "capi:", 5)) + return 1; + + if (crypt_is_cipher_null(cipher_spec)) return 1; if (crypt_parse_name_and_mode(cipher_spec, cipher, NULL, cipher_mode) < 0) @@ -318,61 +349,43 @@ static int _open_and_verify(struct crypt_device *cd, int keyslot, const char *password, size_t password_len, - struct volume_key **vk) + struct volume_key **r_vk) { int r, key_size = LUKS2_get_keyslot_stored_key_size(hdr, keyslot); + struct volume_key *vk = NULL; + void *key = NULL; if (key_size < 0) return -EINVAL; - *vk = crypt_alloc_volume_key(key_size, NULL); - if (!*vk) + key = crypt_safe_alloc(key_size); + if (!key) return -ENOMEM; - r = h->open(cd, keyslot, password, password_len, (*vk)->key, (*vk)->keylength); - if (r < 0) - log_dbg(cd, "Keyslot %d (%s) open failed with %d.", keyslot, h->name, r); - else - r = LUKS2_digest_verify(cd, hdr, *vk, keyslot); - + r = h->open(cd, keyslot, password, password_len, key, key_size); if (r < 0) { - crypt_free_volume_key(*vk); - *vk = NULL; + log_dbg(cd, "Keyslot %d (%s) open failed with %d.", keyslot, h->name, r); + goto err; } - crypt_volume_key_set_id(*vk, r); - - return r < 0 ? r : keyslot; -} - -static int LUKS2_open_and_verify_by_digest(struct crypt_device *cd, - struct luks2_hdr *hdr, - int keyslot, - int digest, - const char *password, - size_t password_len, - struct volume_key **vk) -{ - const keyslot_handler *h; - int r; - - if (!(h = LUKS2_keyslot_handler(cd, keyslot))) - return -ENOENT; - - r = h->validate(cd, LUKS2_get_keyslot_jobj(hdr, keyslot)); - if (r) { - log_dbg(cd, "Keyslot %d validation failed.", keyslot); - return r; + vk = crypt_alloc_volume_key_by_safe_alloc(&key); + if (!vk) { + r = -ENOMEM; + goto err; } - r = _keyslot_for_digest(hdr, keyslot, digest); - if (r) { - if (r == -ENOENT) - log_dbg(cd, "Keyslot %d unusable for digest %d.", keyslot, digest); - return r; - } + r = LUKS2_digest_verify(cd, hdr, vk, keyslot); + if (r < 0) + goto err; - return _open_and_verify(cd, hdr, h, keyslot, password, password_len, vk); + crypt_volume_key_set_id(vk, r); + *r_vk = vk; + return keyslot; +err: + crypt_safe_free(key); + crypt_free_volume_key(vk); + + return r; } static int LUKS2_open_and_verify(struct crypt_device *cd, @@ -405,12 +418,12 @@ static int LUKS2_open_and_verify(struct crypt_device *cd, return _open_and_verify(cd, hdr, h, keyslot, password, password_len, vk); } -static int LUKS2_keyslot_open_priority_digest(struct crypt_device *cd, +static int LUKS2_keyslot_open_priority(struct crypt_device *cd, struct luks2_hdr *hdr, crypt_keyslot_priority priority, const char *password, size_t password_len, - int digest, + int segment, struct volume_key **vk) { json_object *jobj_keyslots, *jobj; @@ -434,7 +447,7 @@ static int LUKS2_keyslot_open_priority_digest(struct crypt_device *cd, continue; } - r = LUKS2_open_and_verify_by_digest(cd, hdr, keyslot, digest, password, password_len, vk); + r = LUKS2_open_and_verify(cd, hdr, keyslot, segment, password, password_len, vk); /* Do not retry for errors that are no -EPERM or -ENOENT, former meaning password wrong, latter key slot unusable for segment */ @@ -448,113 +461,83 @@ static int LUKS2_keyslot_open_priority_digest(struct crypt_device *cd, return r; } -static int LUKS2_keyslot_open_priority(struct crypt_device *cd, - struct luks2_hdr *hdr, - crypt_keyslot_priority priority, - const char *password, - size_t password_len, - int segment, - struct volume_key **vk) +static int keyslot_context_open_all_segments(struct crypt_device *cd, + int keyslot_old, + int keyslot_new, + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new, + struct volume_key **r_vks) { - json_object *jobj_keyslots, *jobj; - crypt_keyslot_priority slot_priority; - int keyslot, r = -ENOENT, r_old; - - json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots); - - json_object_object_foreach(jobj_keyslots, slot, val) { - r_old = r; - - if (!json_object_object_get_ex(val, "priority", &jobj)) - slot_priority = CRYPT_SLOT_PRIORITY_NORMAL; - else - slot_priority = json_object_get_int(jobj); - - keyslot = atoi(slot); - if (slot_priority != priority) { - log_dbg(cd, "Keyslot %d priority %d != %d (required), skipped.", - keyslot, slot_priority, priority); - continue; - } + int segment_old, segment_new, digest_old = -1, digest_new = -1, r = -ENOENT; + struct luks2_hdr *hdr; + struct volume_key *vk = NULL; - r = LUKS2_open_and_verify(cd, hdr, keyslot, segment, password, password_len, vk); + assert(cd); + assert(!kc_old || kc_old->get_luks2_key); + assert(!kc_new || kc_new->get_luks2_key); + assert(r_vks); - /* Do not retry for errors that are no -EPERM or -ENOENT, - former meaning password wrong, latter key slot unusable for segment */ - if ((r != -EPERM) && (r != -ENOENT)) - break; - /* If a previous keyslot failed with EPERM (bad password) prefer it */ - if (r_old == -EPERM && r == -ENOENT) - r = -EPERM; - } + hdr = crypt_get_hdr(cd, CRYPT_LUKS2); - return r; -} + segment_old = LUKS2_reencrypt_segment_old(hdr); + segment_new = LUKS2_reencrypt_segment_new(hdr); -static int LUKS2_keyslot_open_by_digest(struct crypt_device *cd, - struct luks2_hdr *hdr, - int keyslot, - int digest, - const char *password, - size_t password_len, - struct volume_key **vk) -{ - int r_prio, r = -EINVAL; + if (segment_old < 0 || segment_new < 0) + return -EINVAL; - if (digest < 0) - return r; + digest_old = LUKS2_digest_by_segment(hdr, segment_old); + digest_new = LUKS2_digest_by_segment(hdr, segment_new); - if (keyslot == CRYPT_ANY_SLOT) { - r_prio = LUKS2_keyslot_open_priority_digest(cd, hdr, CRYPT_SLOT_PRIORITY_PREFER, - password, password_len, digest, vk); - if (r_prio >= 0) - r = r_prio; - else if (r_prio != -EPERM && r_prio != -ENOENT) - r = r_prio; - else - r = LUKS2_keyslot_open_priority_digest(cd, hdr, CRYPT_SLOT_PRIORITY_NORMAL, - password, password_len, digest, vk); - /* Prefer password wrong to no entry from priority slot */ - if (r_prio == -EPERM && r == -ENOENT) - r = r_prio; - } else - r = LUKS2_open_and_verify_by_digest(cd, hdr, keyslot, digest, password, password_len, vk); + if (digest_old >= 0 && digest_new >= 0 && digest_old != digest_new && (!kc_old || !kc_new)) + return -ESRCH; - return r; -} + if (digest_old >= 0 && kc_old) { + log_dbg(cd, "Checking current volume key (digest %d, segment: %d) using keyslot %d.", + digest_old, segment_old, keyslot_old); -int LUKS2_keyslot_open_all_segments(struct crypt_device *cd, - int keyslot_old, - int keyslot_new, - const char *password, - size_t password_len, - struct volume_key **vks) -{ - struct volume_key *vk = NULL; - int digest_old, digest_new, r = -EINVAL; - struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2); + /* key and key in keyring types do not have association with any keyslot */ + if (kc_old->type != CRYPT_KC_TYPE_KEY && kc_old->type != CRYPT_KC_TYPE_VK_KEYRING) { + r = LUKS2_keyslot_for_segment(hdr, keyslot_old, segment_old); + if (r < 0) + goto out; + } - digest_old = LUKS2_reencrypt_digest_old(hdr); - if (digest_old >= 0) { - log_dbg(cd, "Trying to unlock volume key (digest: %d) using keyslot %d.", digest_old, keyslot_old); - r = LUKS2_keyslot_open_by_digest(cd, hdr, keyslot_old, digest_old, password, password_len, &vk); + r = kc_old->get_luks2_key(cd, kc_old, keyslot_old, segment_old, &vk); if (r < 0) goto out; - crypt_volume_key_add_next(vks, vk); + crypt_volume_key_add_next(r_vks, vk); + if (crypt_volume_key_get_id(vk) < 0 && LUKS2_digest_verify_by_digest(cd, digest_old, vk) == digest_old) + crypt_volume_key_set_id(vk, digest_old); + if (crypt_volume_key_get_id(vk) != digest_old) { + r = -EPERM; + goto out; + } } - digest_new = LUKS2_reencrypt_digest_new(hdr); - if (digest_new >= 0 && digest_old != digest_new) { - log_dbg(cd, "Trying to unlock volume key (digest: %d) using keyslot %d.", digest_new, keyslot_new); - r = LUKS2_keyslot_open_by_digest(cd, hdr, keyslot_new, digest_new, password, password_len, &vk); + if (digest_new >= 0 && digest_old != digest_new && kc_new) { + log_dbg(cd, "Checking new volume key (digest %d, segment: %d) using keyslot %d.", + digest_new, segment_new, keyslot_new); + + /* key and key in keyring types do not have association with any keyslot */ + if (kc_new->type != CRYPT_KC_TYPE_KEY && kc_new->type != CRYPT_KC_TYPE_VK_KEYRING) { + r = LUKS2_keyslot_for_segment(hdr, keyslot_new, segment_new); + if (r < 0) + goto out; + } + + r = kc_new->get_luks2_key(cd, kc_new, keyslot_new, segment_new, &vk); if (r < 0) goto out; - crypt_volume_key_add_next(vks, vk); + crypt_volume_key_add_next(r_vks, vk); + if (crypt_volume_key_get_id(vk) < 0 && LUKS2_digest_verify_by_digest(cd, digest_new, vk) == digest_new) + crypt_volume_key_set_id(vk, digest_new); + if (crypt_volume_key_get_id(vk) != digest_new) + r = -EPERM; } out: if (r < 0) { - crypt_free_volume_key(*vks); - *vks = NULL; + crypt_free_volume_key(*r_vks); + *r_vks = NULL; if (r == -ENOMEM) log_err(cd, _("Not enough available memory to open a keyslot.")); @@ -564,6 +547,25 @@ int LUKS2_keyslot_open_all_segments(struct crypt_device *cd, return r; } +int LUKS2_keyslot_context_open_all_segments(struct crypt_device *cd, + int keyslot1, + int keyslot2, + struct crypt_keyslot_context *kc1, + struct crypt_keyslot_context *kc2, + struct volume_key **r_vks) +{ + int r, r2; + + r = keyslot_context_open_all_segments(cd, keyslot1, keyslot2, kc1, kc2, r_vks); + if (r == -EPERM || r == -ENOENT) { + r2 = keyslot_context_open_all_segments(cd, keyslot2, keyslot1, kc2, kc1, r_vks); + if (r2 != -ENOENT) + r = r2; + } + + return r; +} + int LUKS2_keyslot_open(struct crypt_device *cd, int keyslot, int segment, @@ -646,7 +648,7 @@ int LUKS2_keyslot_store(struct crypt_device *cd, if (!h) return -EINVAL; - r = h->alloc(cd, keyslot, vk->keylength, params); + r = h->alloc(cd, keyslot, crypt_volume_key_length(vk), params); if (r) return r; } else { @@ -670,7 +672,7 @@ int LUKS2_keyslot_store(struct crypt_device *cd, return -EINVAL; return h->store(cd, keyslot, password, password_len, - vk->key, vk->keylength); + crypt_volume_key_get_key(vk), crypt_volume_key_length(vk)); } int LUKS2_keyslot_wipe(struct crypt_device *cd, @@ -861,8 +863,7 @@ int LUKS2_keyslots_validate(struct crypt_device *cd, json_object *hdr_jobj) if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots)) return -EINVAL; - if (LUKS2_config_get_requirements(cd, &dummy, &reqs)) - return -EINVAL; + LUKS2_config_get_requirements(cd, &dummy, &reqs); json_object_object_foreach(jobj_keyslots, slot, val) { keyslot = atoi(slot); diff --git a/lib/luks2/luks2_keyslot_luks2.c b/lib/luks2/luks2_keyslot_luks2.c index 2040fdc..ec68236 100644 --- a/lib/luks2/luks2_keyslot_luks2.c +++ b/lib/luks2/luks2_keyslot_luks2.c @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup v2, LUKS2 type keyslot handler * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Milan Broz + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Milan Broz */ #include @@ -14,7 +14,6 @@ #define LUKS_SALTSIZE 32 #define LUKS_SLOT_ITERATIONS_MIN 1000 -#define LUKS_STRIPES 4000 /* Serialize memory-hard keyslot access: optional workaround for parallel processing */ #define MIN_MEMORY_FOR_SERIALIZE_LOCK_KB 32*1024 /* 32MB */ @@ -25,7 +24,7 @@ static int luks2_encrypt_to_storage(char *src, size_t srcLength, struct volume_key *vk, unsigned int sector, struct crypt_device *cd) { -#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */ +#if !ENABLE_AF_ALG /* Support for old kernel without Crypto API */ return LUKS_encrypt_to_storage(src, srcLength, cipher, cipher_mode, vk, sector, cd); #else struct crypt_storage *s; @@ -37,7 +36,8 @@ static int luks2_encrypt_to_storage(char *src, size_t srcLength, return -EINVAL; /* Encrypt buffer */ - r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, vk->key, vk->keylength, false); + r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, + crypt_volume_key_get_key(vk), crypt_volume_key_length(vk), false); if (r) { log_err(cd, _("Cannot use %s-%s cipher for keyslot encryption."), cipher, cipher_mode); return r; @@ -75,7 +75,7 @@ static int luks2_decrypt_from_storage(char *dst, size_t dstLength, unsigned int sector, struct crypt_device *cd) { struct device *device = crypt_metadata_device(cd); -#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */ +#if !ENABLE_AF_ALG /* Support for old kernel without Crypto API */ int r = device_read_lock(cd, device); if (r) { log_err(cd, _("Failed to acquire read lock on device %s."), device_path(device)); @@ -92,7 +92,9 @@ static int luks2_decrypt_from_storage(char *dst, size_t dstLength, if (MISALIGNED_512(dstLength)) return -EINVAL; - r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, vk->key, vk->keylength, false); + r = crypt_storage_init(&s, SECTOR_SIZE, cipher, cipher_mode, + crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk), false); if (r) { log_err(cd, _("Cannot use %s-%s cipher for keyslot encryption."), cipher, cipher_mode); return r; @@ -190,7 +192,6 @@ static int luks2_keyslot_set_key(struct crypt_device *cd, const char *password, size_t passwordLen, const char *volume_key, size_t volume_key_len) { - struct volume_key *derived_key; char *salt = NULL, cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; char *AfKey = NULL; const char *af_hash = NULL; @@ -199,6 +200,8 @@ static int luks2_keyslot_set_key(struct crypt_device *cd, uint64_t area_offset; struct crypt_pbkdf_type pbkdf; int r; + struct volume_key *derived_vk = NULL; + void *derived_key = NULL; if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) || !json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) || @@ -236,7 +239,7 @@ static int luks2_keyslot_set_key(struct crypt_device *cd, /* * Allocate derived key storage. */ - derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL); + derived_key = crypt_safe_alloc(keyslot_key_len); if (!derived_key) { free(salt); return -ENOMEM; @@ -247,7 +250,7 @@ static int luks2_keyslot_set_key(struct crypt_device *cd, log_dbg(cd, "Running keyslot key derivation."); r = crypt_pbkdf(pbkdf.type, pbkdf.hash, password, passwordLen, salt, LUKS_SALTSIZE, - derived_key->key, derived_key->keylength, + derived_key, keyslot_key_len, pbkdf.iterations, pbkdf.max_memory_kb, pbkdf.parallel_threads); free(salt); @@ -255,16 +258,17 @@ static int luks2_keyslot_set_key(struct crypt_device *cd, if ((crypt_backend_flags() & CRYPT_BACKEND_PBKDF2_INT) && pbkdf.iterations > INT_MAX) log_err(cd, _("PBKDF2 iteration value overflow.")); - crypt_free_volume_key(derived_key); - return r; + if (r == -ENOMEM) + log_err(cd, _("Not enough memory for keyslot key derivation.")); + goto out; } // FIXME: verity key_size to AFEKSize AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE; AfKey = crypt_safe_alloc(AFEKSize); if (!AfKey) { - crypt_free_volume_key(derived_key); - return -ENOMEM; + r = -ENOMEM; + goto out; } r = crypt_hash_size(af_hash); @@ -273,15 +277,23 @@ static int luks2_keyslot_set_key(struct crypt_device *cd, else r = AF_split(cd, volume_key, AfKey, volume_key_len, LUKS_STRIPES, af_hash); - if (r == 0) { - log_dbg(cd, "Updating keyslot area [0x%04" PRIx64 "].", area_offset); - /* FIXME: sector_offset should be size_t, fix LUKS_encrypt... accordingly */ - r = luks2_encrypt_to_storage(AfKey, AFEKSize, cipher, cipher_mode, - derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd); + if (r < 0) + goto out; + + derived_vk = crypt_alloc_volume_key_by_safe_alloc(&derived_key); + if (!derived_vk) { + r = -ENOMEM; + goto out; } + log_dbg(cd, "Updating keyslot area [0x%04" PRIx64 "].", area_offset); + /* FIXME: sector_offset should be size_t, fix LUKS_encrypt... accordingly */ + r = luks2_encrypt_to_storage(AfKey, AFEKSize, cipher, cipher_mode, + derived_vk, (unsigned)(area_offset / SECTOR_SIZE), cd); +out: crypt_safe_free(AfKey); - crypt_free_volume_key(derived_key); + crypt_safe_free(derived_key); + crypt_free_volume_key(derived_vk); if (r < 0) return r; @@ -293,8 +305,7 @@ static int luks2_keyslot_get_key(struct crypt_device *cd, const char *password, size_t passwordLen, char *volume_key, size_t volume_key_len) { - struct volume_key *derived_key = NULL; - struct crypt_pbkdf_type pbkdf, *cd_pbkdf; + struct crypt_pbkdf_type pbkdf; char *AfKey = NULL; size_t AFEKSize; const char *af_hash = NULL; @@ -304,6 +315,8 @@ static int luks2_keyslot_get_key(struct crypt_device *cd, size_t keyslot_key_len; bool try_serialize_lock = false; int r; + struct volume_key *derived_vk = NULL; + void *derived_key = NULL; if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) || !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) @@ -323,6 +336,10 @@ static int luks2_keyslot_get_key(struct crypt_device *cd, if (r < 0) return r; + /* Allow only empty passphrase with null cipher */ + if (crypt_is_cipher_null(cipher) && passwordLen) + return -EPERM; + if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2)) return -EINVAL; keyslot_key_len = json_object_get_int(jobj2); @@ -334,7 +351,7 @@ static int luks2_keyslot_get_key(struct crypt_device *cd, /* * Allocate derived key storage space. */ - derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL); + derived_key = crypt_safe_alloc(keyslot_key_len); if (!derived_key) { r = -ENOMEM; goto out; @@ -347,16 +364,6 @@ static int luks2_keyslot_get_key(struct crypt_device *cd, goto out; } - /* - * Print warning when keyslot requires more memory than available - * (if maximum memory was adjusted - no swap, not enough memory), - * but be silent if user set keyslot memory cost above default limit intentionally. - */ - cd_pbkdf = crypt_get_pbkdf(cd); - if (cd_pbkdf->max_memory_kb && pbkdf.max_memory_kb > cd_pbkdf->max_memory_kb && - pbkdf.max_memory_kb <= DEFAULT_LUKS2_MEMORY_KB) - log_std(cd, _("Warning: keyslot operation could fail as it requires more than available memory.\n")); - /* * If requested, serialize unlocking for memory-hard KDF. Usually NOOP. */ @@ -371,20 +378,27 @@ static int luks2_keyslot_get_key(struct crypt_device *cd, log_dbg(cd, "Running keyslot key derivation."); r = crypt_pbkdf(pbkdf.type, pbkdf.hash, password, passwordLen, salt, LUKS_SALTSIZE, - derived_key->key, derived_key->keylength, + derived_key, keyslot_key_len, pbkdf.iterations, pbkdf.max_memory_kb, pbkdf.parallel_threads); if (try_serialize_lock) crypt_serialize_unlock(cd); - if (r == 0) { - log_dbg(cd, "Reading keyslot area [0x%04" PRIx64 "].", area_offset); - /* FIXME: sector_offset should be size_t, fix LUKS_decrypt... accordingly */ - r = luks2_decrypt_from_storage(AfKey, AFEKSize, cipher, cipher_mode, - derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd); + if (r < 0) + goto out; + + derived_vk = crypt_alloc_volume_key_by_safe_alloc(&derived_key); + if (!derived_vk) { + r = -ENOMEM; + goto out; } + log_dbg(cd, "Reading keyslot area [0x%04" PRIx64 "].", area_offset); + /* FIXME: sector_offset should be size_t, fix LUKS_decrypt... accordingly */ + r = luks2_decrypt_from_storage(AfKey, AFEKSize, cipher, cipher_mode, + derived_vk, (unsigned)(area_offset / SECTOR_SIZE), cd); + if (r == 0) { r = crypt_hash_size(af_hash); if (r < 0) @@ -394,8 +408,9 @@ static int luks2_keyslot_get_key(struct crypt_device *cd, } out: free(salt); - crypt_free_volume_key(derived_key); + crypt_free_volume_key(derived_vk); crypt_safe_free(AfKey); + crypt_safe_free(derived_key); return r; } diff --git a/lib/luks2/luks2_keyslot_reenc.c b/lib/luks2/luks2_keyslot_reenc.c index 79b96f6..180ac56 100644 --- a/lib/luks2/luks2_keyslot_reenc.c +++ b/lib/luks2/luks2_keyslot_reenc.c @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup v2, reencryption keyslot handler * - * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2024 Ondrej Kozina + * Copyright (C) 2016-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2025 Ondrej Kozina */ #include "luks2_internal.h" diff --git a/lib/luks2/luks2_luks1_convert.c b/lib/luks2/luks2_luks1_convert.c index d652761..6c75f16 100644 --- a/lib/luks2/luks2_luks1_convert.c +++ b/lib/luks2/luks2_luks1_convert.c @@ -2,9 +2,9 @@ /* * LUKS - Linux Unified Key Setup v2, LUKS1 conversion code * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Ondrej Kozina - * Copyright (C) 2015-2024 Milan Broz + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Ondrej Kozina + * Copyright (C) 2015-2025 Milan Broz */ #include "luks2_internal.h" @@ -570,6 +570,7 @@ int LUKS2_luks1_to_luks2(struct crypt_device *cd, struct luks_phdr *hdr1, struct json_object *jobj = NULL; size_t buf_size, buf_offset, luks1_size, luks1_shift = 2 * LUKS2_HDR_16K_LEN - LUKS_ALIGN_KEYSLOTS; uint64_t required_size, max_size = crypt_get_data_offset(cd) * SECTOR_SIZE; + char cipher_spec[MAX_CAPI_LEN]; /* for detached headers max size == device size */ if (!max_size && (r = device_size(crypt_metadata_device(cd), &max_size))) @@ -591,6 +592,15 @@ int LUKS2_luks1_to_luks2(struct crypt_device *cd, struct luks_phdr *hdr1, struct return -EINVAL; } + r = snprintf(cipher_spec, sizeof(cipher_spec), "%s-%s", hdr1->cipherName, hdr1->cipherMode); + if (r < 0 || (size_t)r >= sizeof(cipher_spec)) + return -EINVAL; + if (LUKS2_keyslot_cipher_incompatible(cd, cipher_spec)) { + log_err(cd, _("Unable to use cipher specification %s-%s for LUKS2 keyslot."), + hdr1->cipherName, hdr1->cipherMode); + return -EINVAL; + } + if (luksmeta_header_present(cd, luks1_size)) return -EINVAL; @@ -687,30 +697,54 @@ static int keyslot_LUKS1_compatible(struct crypt_device *cd, struct luks2_hdr *h if (!jobj_keyslot) return 1; - if (!json_object_object_get_ex(jobj_keyslot, "type", &jobj) || - strcmp(json_object_get_string(jobj), "luks2")) + /* Keyslot type */ + if (!json_object_object_get_ex(jobj_keyslot, "type", &jobj)) + return 0; + if (strcmp(json_object_get_string(jobj), "luks2")) { + log_dbg(cd, "Keyslot %d type %s is not compatible.", + keyslot, json_object_get_string(jobj)); return 0; + } - /* Using PBKDF2, this implies memory and parallel is not used. */ + /* Keyslot uses PBKDF2, this implies memory and parallel is not used. */ jobj = NULL; if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) || - !json_object_object_get_ex(jobj_kdf, "type", &jobj) || - strcmp(json_object_get_string(jobj), CRYPT_KDF_PBKDF2) || - !json_object_object_get_ex(jobj_kdf, "hash", &jobj) || - strcmp(json_object_get_string(jobj), hash)) + !json_object_object_get_ex(jobj_kdf, "type", &jobj)) + return 0; + if (strcmp(json_object_get_string(jobj), CRYPT_KDF_PBKDF2)) { + log_dbg(cd, "Keyslot %d does not use PBKDF2.", keyslot); + return 0; + } + + /* Keyslot KDF hash is the same as the digest hash. */ + jobj = NULL; + if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj)) + return 0; + if (strcmp(json_object_get_string(jobj), hash)) { + log_dbg(cd, "Keyslot %d PBKDF uses different hash %s than digest hash %s.", + keyslot, json_object_get_string(jobj), hash); return 0; + } + /* Keyslot AF use compatible striptes. */ jobj = NULL; if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) || - !json_object_object_get_ex(jobj_af, "stripes", &jobj) || - json_object_get_int(jobj) != LUKS_STRIPES) + !json_object_object_get_ex(jobj_af, "stripes", &jobj)) + return 0; + if (json_object_get_int(jobj) != LUKS_STRIPES) { + log_dbg(cd, "Keyslot %d AF uses incompatible stripes count.", keyslot); return 0; + } + /* Keyslot AF hash is the same as the digest hash. */ jobj = NULL; - if (!json_object_object_get_ex(jobj_af, "hash", &jobj) || - (crypt_hash_size(json_object_get_string(jobj)) < 0) || - strcmp(json_object_get_string(jobj), hash)) + if (!json_object_object_get_ex(jobj_af, "hash", &jobj)) + return 0; + if (strcmp(json_object_get_string(jobj), hash)) { + log_dbg(cd, "Keyslot %d AF uses different hash %s than digest hash %s.", + keyslot, json_object_get_string(jobj), hash); return 0; + } ks_cipher = LUKS2_get_keyslot_cipher(hdr, keyslot, &ks_key_size); data_cipher = LUKS2_get_cipher(hdr, CRYPT_DEFAULT_SEGMENT); @@ -743,6 +777,7 @@ int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct int i, r, last_active = 0; uint64_t offset, area_length; char *buf, luksMagic[] = LUKS_MAGIC; + crypt_keyslot_info ki; jobj_digest = LUKS2_get_digest_jobj(hdr2, 0); if (!jobj_digest) @@ -767,6 +802,8 @@ int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct if (!json_object_object_get_ex(jobj_digest, "hash", &jobj2)) return -EINVAL; hash = json_object_get_string(jobj2); + if (crypt_hash_size(hash) < 0) + return -EINVAL; r = crypt_parse_name_and_mode(LUKS2_get_cipher(hdr2, CRYPT_DEFAULT_SEGMENT), cipher, NULL, cipher_mode); if (r < 0) @@ -791,19 +828,28 @@ int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct } r = LUKS2_get_volume_key_size(hdr2, 0); - if (r < 0) + if (r < 0) { + log_err(cd, _("Cannot convert to LUKS1 format - there are no active keyslots."), r); return -EINVAL; + } key_size = r; for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) { - if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INACTIVE) + ki = LUKS2_keyslot_info(hdr2, i); + + if (ki == CRYPT_SLOT_INACTIVE) continue; - if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INVALID) { + if (ki == CRYPT_SLOT_INVALID) { log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is in invalid state."), i); return -EINVAL; } + if (ki == CRYPT_SLOT_UNBOUND) { + log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is unbound."), i); + return -EINVAL; + } + if (i >= LUKS_NUMKEYS) { log_err(cd, _("Cannot convert to LUKS1 format - slot %u (over maximum slots) is still active."), i); return -EINVAL; diff --git a/lib/luks2/luks2_reencrypt.c b/lib/luks2/luks2_reencrypt.c index 05f69d1..3210b58 100644 --- a/lib/luks2/luks2_reencrypt.c +++ b/lib/luks2/luks2_reencrypt.c @@ -2,12 +2,13 @@ /* * LUKS - Linux Unified Key Setup v2, reencryption helpers * - * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2015-2024 Ondrej Kozina + * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2015-2025 Ondrej Kozina */ #include "luks2_internal.h" #include "utils_device_locking.h" +#include "keyslot_context.h" struct luks2_reencrypt { /* reencryption window attributes */ @@ -53,6 +54,8 @@ struct luks2_reencrypt { uint32_t wflags1; uint32_t wflags2; + struct device *hotzone_device; + struct crypt_lock_handle *reenc_lock; }; #if USE_LUKS2_REENCRYPTION @@ -170,6 +173,16 @@ int LUKS2_reencrypt_digest_old(struct luks2_hdr *hdr) return reencrypt_digest(hdr, 0); } +int LUKS2_reencrypt_segment_new(struct luks2_hdr *hdr) +{ + return LUKS2_get_segment_id_by_flag(hdr, "backup-final"); +} + +int LUKS2_reencrypt_segment_old(struct luks2_hdr *hdr) +{ + return LUKS2_get_segment_id_by_flag(hdr, "backup-previous"); +} + unsigned LUKS2_reencrypt_vks_count(struct luks2_hdr *hdr) { int digest_old, digest_new; @@ -299,7 +312,7 @@ static json_object *reencrypt_make_hot_segments_encrypt_shift(struct luks2_hdr * rh->offset >> SECTOR_SHIFT, &rh->length, reencrypt_segment_cipher_new(hdr), - NULL, /* integrity */ + NULL, 0, /* integrity */ reencrypt_get_sector_size_new(hdr), 1); @@ -355,7 +368,7 @@ static json_object *reencrypt_make_segment_new(struct crypt_device *cd, crypt_get_iv_offset(cd) + (iv_offset >> SECTOR_SHIFT), segment_length, reencrypt_segment_cipher_new(hdr), - NULL, /* integrity */ + NULL, 0, /* integrity */ reencrypt_get_sector_size_new(hdr), 0); case CRYPT_REENCRYPT_DECRYPT: return json_segment_create_linear(data_offset + segment_offset, segment_length, 0); @@ -467,7 +480,7 @@ static json_object *reencrypt_make_segment_reencrypt(struct crypt_device *cd, crypt_get_iv_offset(cd) + (iv_offset >> SECTOR_SHIFT), segment_length, reencrypt_segment_cipher_new(hdr), - NULL, /* integrity */ + NULL, 0, /* integrity */ reencrypt_get_sector_size_new(hdr), 1); case CRYPT_REENCRYPT_DECRYPT: return json_segment_create_linear(data_offset + segment_offset, segment_length, 1); @@ -492,7 +505,7 @@ static json_object *reencrypt_make_segment_old(struct crypt_device *cd, crypt_get_iv_offset(cd) + (segment_offset >> SECTOR_SHIFT), segment_length, reencrypt_segment_cipher_old(hdr), - NULL, /* integrity */ + NULL, 0, /* integrity */ reencrypt_get_sector_size_old(hdr), 0); break; @@ -871,11 +884,13 @@ void LUKS2_reencrypt_free(struct crypt_device *cd, struct luks2_reencrypt *rh) rh->cw1 = NULL; crypt_storage_wrapper_destroy(rh->cw2); rh->cw2 = NULL; + device_free(cd, rh->hotzone_device); + rh->hotzone_device = NULL; free(rh->device_name); free(rh->overlay_name); free(rh->hotzone_name); - crypt_drop_keyring_key(cd, rh->vks); + crypt_drop_uploaded_keyring_key(cd, rh->vks); crypt_free_volume_key(rh->vks); device_release_excl(cd, crypt_data_device(cd)); crypt_unlock_internal(cd, rh->reenc_lock); @@ -1471,8 +1486,7 @@ static int reencrypt_update_flag(struct crypt_device *cd, uint8_t version, return LUKS2_config_set_requirement_version(cd, hdr, CRYPT_REQUIREMENT_ONLINE_REENCRYPT, version, commit); } - if (LUKS2_config_get_requirements(cd, hdr, &reqs)) - return -EINVAL; + LUKS2_config_get_requirements(cd, hdr, &reqs); reqs &= ~CRYPT_REQUIREMENT_ONLINE_REENCRYPT; @@ -1920,14 +1934,13 @@ static int reencrypt_assign_segments(struct crypt_device *cd, } static int reencrypt_set_encrypt_segments(struct crypt_device *cd, struct luks2_hdr *hdr, - uint64_t dev_size, uint64_t data_shift, bool move_first_segment, + uint64_t dev_size, uint64_t data_size, uint64_t data_shift, bool move_first_segment, crypt_reencrypt_direction_info di) { int r; uint64_t first_segment_offset, first_segment_length, second_segment_offset, second_segment_length, - data_offset = LUKS2_get_data_offset(hdr) << SECTOR_SHIFT, - data_size = dev_size - data_shift; + data_offset = LUKS2_get_data_offset(hdr) << SECTOR_SHIFT; json_object *jobj_segment_first = NULL, *jobj_segment_second = NULL, *jobj_segments; if (dev_size < data_shift) @@ -1994,13 +2007,17 @@ static int reencrypt_set_decrypt_shift_segments(struct crypt_device *cd, uint64_t moved_segment_length, crypt_reencrypt_direction_info di) { - int r; + int digest, r; uint64_t data_offset = LUKS2_get_data_offset(hdr) << SECTOR_SHIFT; json_object *jobj_segment_first = NULL, *jobj_segment_second = NULL, *jobj_segments; if (di == CRYPT_REENCRYPT_BACKWARD) return -ENOTSUP; + digest = LUKS2_digest_by_segment(hdr, CRYPT_DEFAULT_SEGMENT); + if (digest < 0) + return -EINVAL; + /* * future data_device layout: * [encrypted first segment (max data shift size)][gap (data shift size)][second encrypted data segment] @@ -2012,7 +2029,7 @@ static int reencrypt_set_decrypt_shift_segments(struct crypt_device *cd, r = -EINVAL; jobj_segment_first = json_segment_create_crypt(0, crypt_get_iv_offset(cd), &moved_segment_length, crypt_get_cipher_spec(cd), - NULL, crypt_get_sector_size(cd), 0); + NULL, 0, crypt_get_sector_size(cd), 0); if (!jobj_segment_first) { log_dbg(cd, "Failed generate 1st segment."); @@ -2028,7 +2045,7 @@ static int reencrypt_set_decrypt_shift_segments(struct crypt_device *cd, crypt_get_iv_offset(cd) + (moved_segment_length >> SECTOR_SHIFT), NULL, crypt_get_cipher_spec(cd), - NULL, /* integrity */ + NULL, 0, /* integrity */ crypt_get_sector_size(cd), 0); if (!jobj_segment_second) { r = -EINVAL; @@ -2042,7 +2059,7 @@ static int reencrypt_set_decrypt_shift_segments(struct crypt_device *cd, } if (!(r = LUKS2_segments_set(cd, hdr, jobj_segments, 0))) - return LUKS2_digest_segment_assign(cd, hdr, CRYPT_ANY_SEGMENT, 0, 1, 0); + return LUKS2_digest_segment_assign(cd, hdr, CRYPT_ANY_SEGMENT, digest, 1, 0); err: json_object_put(jobj_segment_first); json_object_put(jobj_segment_second); @@ -2099,8 +2116,7 @@ static int reencrypt_make_targets(struct crypt_device *cd, json_segment_get_cipher(jobj), json_segment_get_iv_offset(jobj), segment_offset, - "none", - 0, + "none", 0, 0, json_segment_get_sector_size(jobj)); if (r) { log_err(cd, _("Failed to set dm-crypt segment.")); @@ -2130,34 +2146,22 @@ static int reencrypt_make_targets(struct crypt_device *cd, * 2) can't we derive hotzone device name from crypt context? (unlocked name, device uuid, etc?) */ static int reencrypt_load_overlay_device(struct crypt_device *cd, struct luks2_hdr *hdr, - const char *overlay, const char *hotzone, struct volume_key *vks, uint64_t size, + const char *overlay, struct device *hotzone_device, struct volume_key *vks, uint64_t size, uint32_t flags) { - char hz_path[PATH_MAX]; int r; - struct device *hz_dev = NULL; struct crypt_dm_active_device dmd = { .flags = flags, }; log_dbg(cd, "Loading new table for overlay device %s.", overlay); - r = snprintf(hz_path, PATH_MAX, "%s/%s", dm_get_dir(), hotzone); - if (r < 0 || r >= PATH_MAX) { - r = -EINVAL; - goto out; - } - - r = device_alloc(cd, &hz_dev, hz_path); - if (r) - goto out; - r = dm_targets_allocate(&dmd.segment, LUKS2_segments_count(hdr)); if (r) goto out; - r = reencrypt_make_targets(cd, hdr, hz_dev, vks, &dmd.segment, size); + r = reencrypt_make_targets(cd, hdr, hotzone_device, vks, &dmd.segment, size); if (r < 0) goto out; @@ -2166,7 +2170,6 @@ static int reencrypt_load_overlay_device(struct crypt_device *cd, struct luks2_h /* what else on error here ? */ out: dm_targets_free(cd, &dmd); - device_free(cd, hz_dev); return r; } @@ -2175,7 +2178,7 @@ static int reencrypt_replace_device(struct crypt_device *cd, const char *target, { int r, exists = 1; struct crypt_dm_active_device dmd_source, dmd_target = {}; - uint32_t dmflags = DM_SUSPEND_SKIP_LOCKFS | DM_SUSPEND_NOFLUSH; + uint64_t dmflags = DM_SUSPEND_SKIP_LOCKFS | DM_SUSPEND_NOFLUSH; log_dbg(cd, "Replacing table in device %s with table from device %s.", target, source); @@ -2293,9 +2296,13 @@ static int reencrypt_activate_hotzone_device(struct crypt_device *cd, const char } static int reencrypt_init_device_stack(struct crypt_device *cd, - const struct luks2_reencrypt *rh) + struct luks2_reencrypt *rh) { int r; + char hz_path[PATH_MAX]; + + assert(rh); + assert(!rh->hotzone_device); /* Activate hotzone device 1:1 linear mapping to data_device */ r = reencrypt_activate_hotzone_device(cd, rh->hotzone_name, rh->device_size, CRYPT_ACTIVATE_PRIVATE); @@ -2304,6 +2311,18 @@ static int reencrypt_init_device_stack(struct crypt_device *cd, return r; } + r = snprintf(hz_path, PATH_MAX, "%s/%s", dm_get_dir(), rh->hotzone_name); + if (r < 0 || r >= PATH_MAX) { + r = -EINVAL; + goto err; + } + + r = device_alloc(cd, &rh->hotzone_device, hz_path); + if (r) { + log_err(cd, _("Failed to allocate hotzone device %s."), rh->hotzone_name); + goto err; + } + /* * Activate overlay device with exactly same table as original 'name' mapping. * Note that within this step the 'name' device may already include a table @@ -2383,11 +2402,12 @@ static int reencrypt_refresh_overlay_devices(struct crypt_device *cd, struct luks2_hdr *hdr, const char *overlay, const char *hotzone, + struct device *hotzone_device, struct volume_key *vks, uint64_t device_size, uint32_t flags) { - int r = reencrypt_load_overlay_device(cd, hdr, overlay, hotzone, vks, device_size, flags); + int r = reencrypt_load_overlay_device(cd, hdr, overlay, hotzone_device, vks, device_size, flags); if (r) { log_err(cd, _("Failed to reload device %s."), overlay); return REENC_ERR; @@ -2455,23 +2475,20 @@ static int reencrypt_move_data(struct crypt_device *cd, static int reencrypt_make_backup_segments(struct crypt_device *cd, struct luks2_hdr *hdr, - int keyslot_new, + int digest_new, const char *cipher, uint64_t data_offset, const struct crypt_params_reencrypt *params) { const char *type; - int r, segment, moved_segment = -1, digest_old = -1, digest_new = -1; + int r, segment, moved_segment = -1, digest_old = -1; json_object *jobj_tmp, *jobj_segment_new = NULL, *jobj_segment_old = NULL, *jobj_segment_bcp = NULL; uint32_t sector_size = params->luks2 ? params->luks2->sector_size : SECTOR_SIZE; uint64_t segment_offset, tmp, data_shift = params->data_shift << SECTOR_SHIFT, device_size = params->device_size << SECTOR_SHIFT; - if (params->mode != CRYPT_REENCRYPT_DECRYPT) { - digest_new = LUKS2_digest_by_keyslot(hdr, keyslot_new); - if (digest_new < 0) - return -EINVAL; - } + if (params->mode != CRYPT_REENCRYPT_DECRYPT && digest_new < 0) + return -EINVAL; if (params->mode != CRYPT_REENCRYPT_ENCRYPT) { digest_old = LUKS2_digest_by_segment(hdr, CRYPT_DEFAULT_SEGMENT); @@ -2518,7 +2535,7 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd, json_segment_get_iv_offset(jobj_tmp), device_size ? &device_size : NULL, json_segment_get_cipher(jobj_tmp), - NULL, /* integrity */ + NULL, 0, /* integrity */ json_segment_get_sector_size(jobj_tmp), 0); } else { @@ -2565,7 +2582,7 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd, } jobj_segment_new = json_segment_create_crypt(segment_offset, crypt_get_iv_offset(cd), - NULL, cipher, NULL, sector_size, 0); + NULL, cipher, NULL, 0, sector_size, 0); } else if (params->mode == CRYPT_REENCRYPT_DECRYPT) { segment_offset = data_offset; if (modify_offset(&segment_offset, data_shift, params->direction)) { @@ -2668,7 +2685,7 @@ static int reencrypt_upload_keys(struct crypt_device *cd, if (digest_old >= 0 && !crypt_is_cipher_null(reencrypt_segment_cipher_old(hdr)) && (r = reencrypt_upload_single_key(cd, digest_old, vks))) { - crypt_drop_keyring_key(cd, vks); + crypt_drop_uploaded_keyring_key(cd, vks); return r; } @@ -2779,8 +2796,7 @@ static int reencrypt_decrypt_with_datashift_init(struct crypt_device *cd, uint32_t sector_size, uint64_t data_size, uint64_t data_offset, - const char *passphrase, - size_t passphrase_size, + struct crypt_keyslot_context *kc_old, int keyslot_old, const struct crypt_params_reencrypt *params, struct volume_key **vks) @@ -2839,7 +2855,7 @@ static int reencrypt_decrypt_with_datashift_init(struct crypt_device *cd, if (r) goto out; - r = reencrypt_make_backup_segments(cd, hdr, CRYPT_ANY_SLOT, NULL, data_offset, params); + r = reencrypt_make_backup_segments(cd, hdr, CRYPT_ANY_DIGEST, NULL, data_offset, params); if (r) { log_dbg(cd, "Failed to create reencryption backup device segments."); goto out; @@ -2875,8 +2891,8 @@ static int reencrypt_decrypt_with_datashift_init(struct crypt_device *cd, goto out; } - r = LUKS2_keyslot_open_all_segments(cd, keyslot_old, CRYPT_ANY_SLOT, - passphrase, passphrase_size, vks); + r = LUKS2_keyslot_context_open_all_segments(cd, keyslot_old, CRYPT_ANY_SLOT, + kc_old, NULL, vks); if (r < 0) goto out; @@ -2900,6 +2916,8 @@ static int reencrypt_decrypt_with_datashift_init(struct crypt_device *cd, jobj_segments_old = reencrypt_segments_old(hdr); if (!jobj_segments_old) { + dm_targets_free(cd, &dmd_target); + free(CONST_CAST(void*)dmd_target.uuid); r = -EINVAL; goto out; } @@ -2971,8 +2989,8 @@ static int reencrypt_decrypt_with_datashift_init(struct crypt_device *cd, static int reencrypt_init(struct crypt_device *cd, const char *name, struct luks2_hdr *hdr, - const char *passphrase, - size_t passphrase_size, + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new, int keyslot_old, int keyslot_new, const char *cipher, @@ -2983,8 +3001,9 @@ static int reencrypt_init(struct crypt_device *cd, bool move_first_segment; char _cipher[128]; uint32_t check_sector_size, new_sector_size, old_sector_size; - int r, reencrypt_keyslot, devfd = -1; - uint64_t data_offset, data_size = 0; + int digest_new, r, reencrypt_keyslot, devfd = -1; + uint64_t data_offset_bytes, data_size_bytes, data_shift_bytes, device_size_bytes; + struct volume_key *vk; struct crypt_dm_active_device dmd_target, dmd_source = { .uuid = crypt_get_uuid(cd), .flags = CRYPT_ACTIVATE_SHARED /* turn off exclusive open checks */ @@ -2997,7 +3016,8 @@ static int reencrypt_init(struct crypt_device *cd, return -EINVAL; if (params->mode != CRYPT_REENCRYPT_DECRYPT && - (!params->luks2 || !(cipher && cipher_mode) || keyslot_new < 0)) + (!params->luks2 || !(cipher && cipher_mode) || + (keyslot_new < 0 && !(params->flags & CRYPT_REENCRYPT_CREATE_NEW_DIGEST)))) return -EINVAL; log_dbg(cd, "Initializing reencryption (mode: %s) in LUKS2 metadata.", @@ -3024,31 +3044,54 @@ static int reencrypt_init(struct crypt_device *cd, if (r < 0 || (size_t)r >= sizeof(_cipher)) return -EINVAL; - data_offset = LUKS2_get_data_offset(hdr) << SECTOR_SHIFT; + data_offset_bytes = LUKS2_get_data_offset(hdr) << SECTOR_SHIFT; r = device_check_access(cd, crypt_data_device(cd), DEV_OK); if (r) return r; - r = device_check_size(cd, crypt_data_device(cd), data_offset, 1); + r = device_check_size(cd, crypt_data_device(cd), data_offset_bytes, 1); if (r) return r; - r = device_size(crypt_data_device(cd), &data_size); + r = device_size(crypt_data_device(cd), &device_size_bytes); if (r) return r; - data_size -= data_offset; + if (move_first_segment && params->mode == CRYPT_REENCRYPT_ENCRYPT && + params->data_shift < LUKS2_get_data_offset(hdr)) { + log_err(cd, _("Data shift (%" PRIu64 " sectors) is less than future data offset (%" PRIu64 " sectors)."), + params->data_shift, LUKS2_get_data_offset(hdr)); + return -EINVAL; + } + + device_size_bytes -= data_offset_bytes; + data_shift_bytes = params->data_shift << SECTOR_SHIFT; + data_size_bytes = params->device_size << SECTOR_SHIFT; + + if (device_size_bytes < data_shift_bytes && params->direction == CRYPT_REENCRYPT_BACKWARD) { + log_err(cd, _("Device %s is too small."), device_path(crypt_data_device(cd))); + return -EINVAL; + } - if (params->device_size) { - if ((params->device_size << SECTOR_SHIFT) > data_size) { + if (data_size_bytes > device_size_bytes) { + log_err(cd, _("Reduced data size is larger than real device size.")); + return -EINVAL; + } + + if (data_size_bytes && params->mode == CRYPT_REENCRYPT_ENCRYPT && + move_first_segment && data_shift_bytes) { + if (data_size_bytes > device_size_bytes - data_shift_bytes) { log_err(cd, _("Reduced data size is larger than real device size.")); return -EINVAL; - } else - data_size = params->device_size << SECTOR_SHIFT; - } + } + } else if (!data_size_bytes && params->mode == CRYPT_REENCRYPT_ENCRYPT && + move_first_segment && data_shift_bytes) + data_size_bytes = device_size_bytes - data_shift_bytes; + else if (!data_size_bytes) + data_size_bytes = device_size_bytes; - if (MISALIGNED(data_size, check_sector_size)) { + if (MISALIGNED(data_size_bytes, check_sector_size)) { log_err(cd, _("Data device is not aligned to encryption sector size (%" PRIu32 " bytes)."), check_sector_size); return -EINVAL; } @@ -3059,34 +3102,23 @@ static int reencrypt_init(struct crypt_device *cd, return -EINVAL; } - if (params->mode == CRYPT_REENCRYPT_DECRYPT && (params->data_shift > 0) && move_first_segment) + if (params->mode == CRYPT_REENCRYPT_DECRYPT && data_shift_bytes && move_first_segment) return reencrypt_decrypt_with_datashift_init(cd, name, hdr, reencrypt_keyslot, check_sector_size, - data_size, - data_offset, - passphrase, - passphrase_size, + data_size_bytes, + data_offset_bytes, + kc_old, keyslot_old, params, vks); - /* * We must perform data move with exclusive open data device * to exclude another cryptsetup process to colide with * encryption initialization (or mount) */ if (move_first_segment) { - if (data_size < (params->data_shift << SECTOR_SHIFT)) { - log_err(cd, _("Device %s is too small."), device_path(crypt_data_device(cd))); - return -EINVAL; - } - if (params->data_shift < LUKS2_get_data_offset(hdr)) { - log_err(cd, _("Data shift (%" PRIu64 " sectors) is less than future data offset (%" PRIu64 " sectors)."), - params->data_shift, LUKS2_get_data_offset(hdr)); - return -EINVAL; - } devfd = device_open_excl(cd, crypt_data_device(cd), O_RDWR); if (devfd < 0) { if (devfd == -EBUSY) @@ -3098,15 +3130,33 @@ static int reencrypt_init(struct crypt_device *cd, if (params->mode == CRYPT_REENCRYPT_ENCRYPT) { /* in-memory only */ - r = reencrypt_set_encrypt_segments(cd, hdr, data_size, - params->data_shift << SECTOR_SHIFT, + r = reencrypt_set_encrypt_segments(cd, hdr, device_size_bytes, data_size_bytes, + data_shift_bytes, move_first_segment, params->direction); if (r) goto out; } - r = reencrypt_make_backup_segments(cd, hdr, keyslot_new, _cipher, data_offset, params); + if (params->flags & CRYPT_REENCRYPT_CREATE_NEW_DIGEST) { + assert(kc_new->get_luks2_key); + r = kc_new->get_luks2_key(cd, kc_new, CRYPT_ANY_SLOT, CRYPT_ANY_SEGMENT, &vk); + if (r < 0) + goto out; + + /* do not create new digest in case it matches the current one */ + r = LUKS2_digest_verify_by_segment(cd, hdr, CRYPT_DEFAULT_SEGMENT, vk); + if (r == -EPERM || r == -ENOENT) + r = LUKS2_digest_create(cd, "pbkdf2", hdr, vk); + + crypt_free_volume_key(vk); + if (r < 0) + goto out; + digest_new = r; + } else + digest_new = LUKS2_digest_by_keyslot(hdr, keyslot_new); + + r = reencrypt_make_backup_segments(cd, hdr, digest_new, _cipher, data_offset_bytes, params); if (r) { log_dbg(cd, "Failed to create reencryption backup device segments."); goto out; @@ -3121,7 +3171,7 @@ static int reencrypt_init(struct crypt_device *cd, if (r < 0) goto out; - r = LUKS2_keyslot_open_all_segments(cd, keyslot_old, keyslot_new, passphrase, passphrase_size, vks); + r = LUKS2_keyslot_context_open_all_segments(cd, keyslot_old, keyslot_new, kc_old, kc_new, vks); if (r < 0) goto out; @@ -3155,12 +3205,12 @@ static int reencrypt_init(struct crypt_device *cd, goto out; } - if (move_first_segment && reencrypt_move_data(cd, devfd, params->data_shift << SECTOR_SHIFT, params->mode)) { + if (move_first_segment && reencrypt_move_data(cd, devfd, data_shift_bytes, params->mode)) { r = -EIO; goto out; } - /* This must be first and only write in LUKS2 metadata during _reencrypt_init */ + /* This must be first and only write in LUKS2 metadata during reencrypt_init */ r = reencrypt_update_flag(cd, LUKS2_REENCRYPT_REQ_VERSION, true, true); if (r) { log_dbg(cd, "Failed to set online-reencryption requirement."); @@ -3356,7 +3406,7 @@ int LUKS2_reencrypt_lock_by_dm_uuid(struct crypt_device *cd, const char *dm_uuid dm_uuid + 6, dm_uuid + 14, dm_uuid + 18, dm_uuid + 22, dm_uuid + 26); if (r < 0 || (size_t)r != (sizeof(hdr_uuid) - 1)) return -EINVAL; - } else if (crypt_uuid_cmp(dm_uuid, uuid)) + } else if (dm_uuid_cmp(dm_uuid, uuid)) return -EINVAL; return reencrypt_lock_internal(cd, uuid, reencrypt_lock); @@ -3385,10 +3435,8 @@ static int reencrypt_lock_and_verify(struct crypt_device *cd, struct luks2_hdr * struct crypt_lock_handle *h; ri = LUKS2_reencrypt_status(hdr); - if (ri == CRYPT_REENCRYPT_INVALID) { - log_err(cd, _("Failed to get reencryption state.")); + if (ri == CRYPT_REENCRYPT_INVALID) return -EINVAL; - } if (ri < CRYPT_REENCRYPT_CLEAN) { log_err(cd, _("Device is not in reencryption.")); return -EINVAL; @@ -3421,10 +3469,10 @@ static int reencrypt_lock_and_verify(struct crypt_device *cd, struct luks2_hdr * return -EINVAL; } -static int reencrypt_load_by_passphrase(struct crypt_device *cd, +static int reencrypt_load_by_keyslot_context(struct crypt_device *cd, const char *name, - const char *passphrase, - size_t passphrase_size, + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new, int keyslot_old, int keyslot_new, struct volume_key **vks, @@ -3504,7 +3552,8 @@ static int reencrypt_load_by_passphrase(struct crypt_device *cd, r = reencrypt_verify_keys(cd, LUKS2_reencrypt_digest_old(hdr), LUKS2_reencrypt_digest_new(hdr), *vks); if (r == -ENOENT) { log_dbg(cd, "Keys are not ready. Unlocking all volume keys."); - r = LUKS2_keyslot_open_all_segments(cd, keyslot_old, keyslot_new, passphrase, passphrase_size, vks); + r = LUKS2_keyslot_context_open_all_segments(cd, keyslot_old, keyslot_new, + kc_old, kc_new, vks); } if (r < 0) @@ -3530,7 +3579,7 @@ static int reencrypt_load_by_passphrase(struct crypt_device *cd, * above. The code checks if new VK is eligible for keyring. */ vk = crypt_volume_key_by_id(*vks, LUKS2_reencrypt_digest_new(hdr)); - if (vk && vk->key_description && crypt_is_cipher_null(reencrypt_segment_cipher_old(hdr))) { + if (vk && crypt_volume_key_description(vk) && crypt_is_cipher_null(reencrypt_segment_cipher_old(hdr))) { flags |= CRYPT_ACTIVATE_KEYRING_KEY; dmd_source.flags |= CRYPT_ACTIVATE_KEYRING_KEY; } @@ -3629,12 +3678,37 @@ static int reencrypt_load_by_passphrase(struct crypt_device *cd, return r; } -static int reencrypt_recovery_by_passphrase(struct crypt_device *cd, +static int reencrypt_locked_recovery(struct crypt_device *cd, + int keyslot_old, + int keyslot_new, + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new, + struct volume_key **r_vks) +{ + int keyslot, r = -EINVAL; + struct volume_key *_vks = NULL; + + r = LUKS2_keyslot_context_open_all_segments(cd, keyslot_old, keyslot_new, + kc_old, kc_new, &_vks); + if (r < 0) + return r; + keyslot = r; + + r = LUKS2_reencrypt_locked_recovery_by_vks(cd, _vks); + if (!r && r_vks) + MOVE_REF(*r_vks, _vks); + + crypt_free_volume_key(_vks); + + return r < 0 ? r : keyslot; +} + +static int reencrypt_recovery_by_keyslot_context(struct crypt_device *cd, struct luks2_hdr *hdr, int keyslot_old, int keyslot_new, - const char *passphrase, - size_t passphrase_size) + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new) { int r; crypt_reencrypt_info ri; @@ -3661,8 +3735,8 @@ static int reencrypt_recovery_by_passphrase(struct crypt_device *cd, } if (ri == CRYPT_REENCRYPT_CRASH) { - r = LUKS2_reencrypt_locked_recovery_by_passphrase(cd, keyslot_old, keyslot_new, - passphrase, passphrase_size, NULL); + r = reencrypt_locked_recovery(cd, keyslot_old, keyslot_new, + kc_old, kc_new, NULL); if (r < 0) log_err(cd, _("LUKS2 reencryption recovery failed.")); } else { @@ -3674,13 +3748,13 @@ static int reencrypt_recovery_by_passphrase(struct crypt_device *cd, return r; } -static int reencrypt_repair_by_passphrase( +static int reencrypt_repair( struct crypt_device *cd, struct luks2_hdr *hdr, int keyslot_old, int keyslot_new, - const char *passphrase, - size_t passphrase_size) + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new) { int r; struct crypt_lock_handle *reencrypt_lock; @@ -3745,7 +3819,7 @@ static int reencrypt_repair_by_passphrase( else requirement_version = LUKS2_REENCRYPT_REQ_VERSION; - r = LUKS2_keyslot_open_all_segments(cd, keyslot_old, keyslot_new, passphrase, passphrase_size, &vks); + r = LUKS2_keyslot_context_open_all_segments(cd, keyslot_old, keyslot_new, kc_old, kc_new, &vks); if (r < 0) goto out; @@ -3764,10 +3838,10 @@ static int reencrypt_repair_by_passphrase( } -static int reencrypt_init_by_passphrase(struct crypt_device *cd, +static int reencrypt_init_by_keyslot_context(struct crypt_device *cd, const char *name, - const char *passphrase, - size_t passphrase_size, + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new, int keyslot_old, int keyslot_new, const char *cipher, @@ -3776,30 +3850,42 @@ static int reencrypt_init_by_passphrase(struct crypt_device *cd, { int r; crypt_reencrypt_info ri; + size_t key_length; struct volume_key *vks = NULL; uint32_t flags = params ? params->flags : 0; struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2); + if (params && (params->flags & CRYPT_REENCRYPT_CREATE_NEW_DIGEST) && + (!kc_new || !kc_new->get_luks2_key || !kc_new->get_key_size || + (params->flags & CRYPT_REENCRYPT_RESUME_ONLY))) + return -EINVAL; + /* short-circuit in reencryption metadata update and finish immediately. */ if (flags & CRYPT_REENCRYPT_REPAIR_NEEDED) - return reencrypt_repair_by_passphrase(cd, hdr, keyslot_old, keyslot_new, passphrase, passphrase_size); + return reencrypt_repair(cd, hdr, keyslot_old, keyslot_new, kc_old, kc_new); /* short-circuit in recovery and finish immediately. */ if (flags & CRYPT_REENCRYPT_RECOVERY) - return reencrypt_recovery_by_passphrase(cd, hdr, keyslot_old, keyslot_new, passphrase, passphrase_size); + return reencrypt_recovery_by_keyslot_context(cd, hdr, keyslot_old, keyslot_new, kc_old, kc_new); if (name && !device_direct_io(crypt_data_device(cd))) { log_dbg(cd, "Device %s does not support direct I/O.", device_path(crypt_data_device(cd))); - /* FIXME: Add more specific error mesage for translation later. */ + /* FIXME: Add more specific error message for translation later. */ log_err(cd, _("Failed to initialize reencryption device stack.")); return -EINVAL; } if (cipher && !crypt_cipher_wrapped_key(cipher, cipher_mode)) { - r = crypt_keyslot_get_key_size(cd, keyslot_new); + if (keyslot_new == CRYPT_ANY_SLOT && kc_new && kc_new->get_key_size) + r = kc_new->get_key_size(cd, kc_new, &key_length); + else { + r = crypt_keyslot_get_key_size(cd, keyslot_new); + if (r >= 0) + key_length = r; + } if (r < 0) return r; - r = LUKS2_check_cipher(cd, r, cipher, cipher_mode); + r = LUKS2_check_cipher(cd, key_length, cipher, cipher_mode); if (r < 0) { log_err(cd, _("Unable to use cipher specification %s-%s for LUKS2."), cipher, cipher_mode); return r; @@ -3823,7 +3909,8 @@ static int reencrypt_init_by_passphrase(struct crypt_device *cd, } if (ri == CRYPT_REENCRYPT_NONE && !(flags & CRYPT_REENCRYPT_RESUME_ONLY)) { - r = reencrypt_init(cd, name, hdr, passphrase, passphrase_size, keyslot_old, keyslot_new, cipher, cipher_mode, params, &vks); + r = reencrypt_init(cd, name, hdr, kc_old, kc_new, keyslot_old, + keyslot_new, cipher, cipher_mode, params, &vks); if (r < 0) log_err(cd, _("Failed to initialize LUKS2 reencryption in metadata.")); } else if (ri > CRYPT_REENCRYPT_NONE) { @@ -3836,18 +3923,19 @@ static int reencrypt_init_by_passphrase(struct crypt_device *cd, if (r < 0 || (flags & CRYPT_REENCRYPT_INITIALIZE_ONLY)) goto out; - r = reencrypt_load_by_passphrase(cd, name, passphrase, passphrase_size, keyslot_old, keyslot_new, &vks, params); + r = reencrypt_load_by_keyslot_context(cd, name, kc_old, kc_new, keyslot_old, + keyslot_new, &vks, params); out: if (r < 0) - crypt_drop_keyring_key(cd, vks); + crypt_drop_uploaded_keyring_key(cd, vks); crypt_free_volume_key(vks); return r < 0 ? r : LUKS2_find_keyslot(hdr, "reencrypt"); } #else -static int reencrypt_init_by_passphrase(struct crypt_device *cd, +static int reencrypt_init_by_keyslot_context(struct crypt_device *cd, const char *name __attribute__((unused)), - const char *passphrase __attribute__((unused)), - size_t passphrase_size __attribute__((unused)), + struct crypt_keyslot_context *kc_old __attribute__((unused)), + struct crypt_keyslot_context *kc_new __attribute__((unused)), int keyslot_old __attribute__((unused)), int keyslot_new __attribute__((unused)), const char *cipher __attribute__((unused)), @@ -3869,8 +3957,7 @@ int crypt_reencrypt_init_by_keyring(struct crypt_device *cd, const struct crypt_params_reencrypt *params) { int r; - char *passphrase; - size_t passphrase_size; + struct crypt_keyslot_context kc = {0}; if (onlyLUKS2reencrypt(cd) || !passphrase_description) return -EINVAL; @@ -3882,17 +3969,11 @@ int crypt_reencrypt_init_by_keyring(struct crypt_device *cd, return -EINVAL; } - r = crypt_keyring_get_user_key(cd, passphrase_description, &passphrase, &passphrase_size); - if (r < 0) { - log_dbg(cd, "crypt_keyring_get_user_key failed (error %d)", r); - log_err(cd, _("Failed to read passphrase from keyring.")); - return -EINVAL; - } - - r = reencrypt_init_by_passphrase(cd, name, passphrase, passphrase_size, keyslot_old, keyslot_new, cipher, cipher_mode, params); + crypt_keyslot_context_init_by_keyring_internal(&kc, passphrase_description); + r = reencrypt_init_by_keyslot_context(cd, name, &kc, &kc, keyslot_old, + keyslot_new, cipher, cipher_mode, params); - crypt_safe_memzero(passphrase, passphrase_size); - free(passphrase); + crypt_keyslot_context_destroy_internal(&kc); return r; } @@ -3907,6 +3988,9 @@ int crypt_reencrypt_init_by_passphrase(struct crypt_device *cd, const char *cipher_mode, const struct crypt_params_reencrypt *params) { + int r; + struct crypt_keyslot_context kc = {0}; + if (onlyLUKS2reencrypt(cd) || !passphrase) return -EINVAL; if (params && (params->flags & CRYPT_REENCRYPT_INITIALIZE_ONLY) && (params->flags & CRYPT_REENCRYPT_RESUME_ONLY)) @@ -3917,7 +4001,37 @@ int crypt_reencrypt_init_by_passphrase(struct crypt_device *cd, return -EINVAL; } - return reencrypt_init_by_passphrase(cd, name, passphrase, passphrase_size, keyslot_old, keyslot_new, cipher, cipher_mode, params); + crypt_keyslot_context_init_by_passphrase_internal(&kc, passphrase, passphrase_size); + + r = reencrypt_init_by_keyslot_context(cd, name, &kc, &kc, keyslot_old, + keyslot_new, cipher, cipher_mode, params); + + crypt_keyslot_context_destroy_internal(&kc); + + return r; +} + +int crypt_reencrypt_init_by_keyslot_context(struct crypt_device *cd, + const char *name, + struct crypt_keyslot_context *kc_old, + struct crypt_keyslot_context *kc_new, + int keyslot_old, + int keyslot_new, + const char *cipher, + const char *cipher_mode, + const struct crypt_params_reencrypt *params) +{ + if (onlyLUKS2reencrypt(cd) || (!kc_old && !kc_new)) + return -EINVAL; + if (params && (params->flags & CRYPT_REENCRYPT_INITIALIZE_ONLY) && (params->flags & CRYPT_REENCRYPT_RESUME_ONLY)) + return -EINVAL; + + if (device_is_dax(crypt_data_device(cd)) > 0) { + log_err(cd, _("Reencryption is not supported for DAX (persistent memory) devices.")); + return -EINVAL; + } + + return reencrypt_init_by_keyslot_context(cd, name, kc_old, kc_new, keyslot_old, keyslot_new, cipher, cipher_mode, params); } #if USE_LUKS2_REENCRYPTION @@ -3977,7 +4091,8 @@ static reenc_status_t reencrypt_step(struct crypt_device *cd, } if (online) { - r = reencrypt_refresh_overlay_devices(cd, hdr, rh->overlay_name, rh->hotzone_name, rh->vks, rh->device_size, rh->flags); + r = reencrypt_refresh_overlay_devices(cd, hdr, rh->overlay_name, rh->hotzone_name, + rh->hotzone_device, rh->vks, rh->device_size, rh->flags); /* Teardown overlay devices with dm-error. None bio shall pass! */ if (r != REENC_OK) return r; @@ -4103,7 +4218,7 @@ static int reencrypt_wipe_unused_device_area(struct crypt_device *cd, struct luk static int reencrypt_teardown_ok(struct crypt_device *cd, struct luks2_hdr *hdr, struct luks2_reencrypt *rh) { int i, r; - uint32_t dmt_flags; + uint64_t dmt_flags; bool finished = !(rh->device_size > rh->progress); if (rh->rp.type == REENC_PROTECTION_NONE && @@ -4386,76 +4501,17 @@ int LUKS2_reencrypt_check_device_size(struct crypt_device *cd, struct luks2_hdr } #if USE_LUKS2_REENCRYPTION /* returns keyslot number on success (>= 0) or negative errnor otherwise */ -int LUKS2_reencrypt_locked_recovery_by_passphrase(struct crypt_device *cd, - int keyslot_old, - int keyslot_new, - const char *passphrase, - size_t passphrase_size, - struct volume_key **vks) -{ - uint64_t minimal_size, device_size; - int keyslot, r = -EINVAL; - struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2); - struct volume_key *vk = NULL, *_vks = NULL; - - log_dbg(cd, "Entering reencryption crash recovery."); - - if (LUKS2_get_data_size(hdr, &minimal_size, NULL)) - return r; - - r = LUKS2_keyslot_open_all_segments(cd, keyslot_old, keyslot_new, - passphrase, passphrase_size, &_vks); - if (r < 0) - goto out; - keyslot = r; - - if (crypt_use_keyring_for_vk(cd)) - vk = _vks; - - while (vk) { - r = LUKS2_volume_key_load_in_keyring_by_digest(cd, vk, crypt_volume_key_get_id(vk)); - if (r < 0) - goto out; - vk = crypt_volume_key_next(vk); - } - - if (LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size, true, false)) - goto out; - - r = reencrypt_recovery(cd, hdr, device_size, _vks); - - if (!r && vks) - MOVE_REF(*vks, _vks); -out: - if (r < 0) - crypt_drop_keyring_key(cd, _vks); - crypt_free_volume_key(_vks); - - return r < 0 ? r : keyslot; -} - int LUKS2_reencrypt_locked_recovery_by_vks(struct crypt_device *cd, struct volume_key *vks) { uint64_t minimal_size, device_size; int r = -EINVAL; struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2); - struct volume_key *vk = NULL; log_dbg(cd, "Entering reencryption crash recovery."); if (LUKS2_get_data_size(hdr, &minimal_size, NULL)) return r; - - if (crypt_use_keyring_for_vk(cd)) - vk = vks; - while (vk) { - r = LUKS2_volume_key_load_in_keyring_by_digest(cd, vk, crypt_volume_key_get_id(vk)); - if (r < 0) - goto out; - vk = crypt_volume_key_next(vk); - } - if (LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size, true, false)) goto out; @@ -4463,7 +4519,7 @@ int LUKS2_reencrypt_locked_recovery_by_vks(struct crypt_device *cd, out: if (r < 0) - crypt_drop_keyring_key(cd, vks); + crypt_drop_uploaded_keyring_key(cd, vks); return r; } #endif diff --git a/lib/luks2/luks2_reencrypt_digest.c b/lib/luks2/luks2_reencrypt_digest.c index 94e77be..9db88a4 100644 --- a/lib/luks2/luks2_reencrypt_digest.c +++ b/lib/luks2/luks2_reencrypt_digest.c @@ -2,9 +2,9 @@ /* * LUKS - Linux Unified Key Setup v2, reencryption digest helpers * - * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2022-2024 Ondrej Kozina - * Copyright (C) 2022-2024 Milan Broz + * Copyright (C) 2022-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2025 Ondrej Kozina + * Copyright (C) 2022-2025 Milan Broz */ #include "luks2_internal.h" @@ -240,10 +240,10 @@ static size_t reenc_keyslot_serialize(struct luks2_hdr *hdr, uint8_t *buffer) return srs(j, buffer); } -static size_t blob_serialize(void *blob, size_t length, uint8_t *buffer) +static size_t blob_serialize(const void *blob, size_t length, uint8_t *buffer) { if (buffer) - memcpy(buffer, blob, length); + crypt_safe_memcpy(buffer, blob, length); return length; } @@ -252,12 +252,13 @@ static int reencrypt_assembly_verification_data(struct crypt_device *cd, struct luks2_hdr *hdr, struct volume_key *vks, uint8_t version, - struct volume_key **verification_data) + struct volume_key **r_verification_data) { uint8_t *ptr; - int digest_new, digest_old; - struct volume_key *data = NULL, *vk_old = NULL, *vk_new = NULL; + int digest_new, digest_old, r = -EINVAL; + struct volume_key *verification_data = NULL, *vk_old = NULL, *vk_new = NULL; size_t keyslot_data_len, segments_data_len, data_len = 2; + void *data = NULL; /* * This works up to (including) version v207. @@ -274,7 +275,7 @@ static int reencrypt_assembly_verification_data(struct crypt_device *cd, log_dbg(cd, "Key (digest id %d) required but not unlocked.", digest_old); return -EINVAL; } - data_len += blob_serialize(vk_old->key, vk_old->keylength, NULL); + data_len += blob_serialize(crypt_volume_key_get_key(vk_old), crypt_volume_key_length(vk_old), NULL); } if (digest_new >= 0 && digest_old != digest_new) { @@ -283,7 +284,7 @@ static int reencrypt_assembly_verification_data(struct crypt_device *cd, log_dbg(cd, "Key (digest id %d) required but not unlocked.", digest_new); return -EINVAL; } - data_len += blob_serialize(vk_new->key, vk_new->keylength, NULL); + data_len += blob_serialize(crypt_volume_key_get_key(vk_new), crypt_volume_key_length(vk_new), NULL); } if (data_len == 2) @@ -299,20 +300,22 @@ static int reencrypt_assembly_verification_data(struct crypt_device *cd, data_len += segments_data_len; /* Alloc and fill serialization data */ - data = crypt_alloc_volume_key(data_len, NULL); + data = crypt_safe_alloc(data_len); if (!data) return -ENOMEM; - ptr = (uint8_t*)data->key; + ptr = (uint8_t*)data; *ptr++ = 0x76; *ptr++ = 0x30 + version; if (vk_old) - ptr += blob_serialize(vk_old->key, vk_old->keylength, ptr); + ptr += blob_serialize(crypt_volume_key_get_key(vk_old), + crypt_volume_key_length(vk_old), ptr); if (vk_new) - ptr += blob_serialize(vk_new->key, vk_new->keylength, ptr); + ptr += blob_serialize(crypt_volume_key_get_key(vk_new), + crypt_volume_key_length(vk_new), ptr); if (!reenc_keyslot_serialize(hdr, ptr)) goto bad; @@ -322,14 +325,20 @@ static int reencrypt_assembly_verification_data(struct crypt_device *cd, goto bad; ptr += segments_data_len; - assert((size_t)(ptr - (uint8_t*)data->key) == data_len); + assert((size_t)(ptr - (uint8_t*)data) == data_len); - *verification_data = data; + verification_data = crypt_alloc_volume_key_by_safe_alloc(&data); + if (!verification_data) { + r = -ENOMEM; + goto bad; + } + *r_verification_data = verification_data; return 0; bad: - crypt_free_volume_key(data); - return -EINVAL; + crypt_safe_free(data); + crypt_free_volume_key(verification_data); + return r; } int LUKS2_keyslot_reencrypt_digest_create(struct crypt_device *cd, @@ -362,22 +371,6 @@ int LUKS2_keyslot_reencrypt_digest_create(struct crypt_device *cd, return LUKS2_digest_assign(cd, hdr, keyslot_reencrypt, digest_reencrypt, 1, 0); } -void LUKS2_reencrypt_lookup_key_ids(struct crypt_device *cd, struct luks2_hdr *hdr, struct volume_key *vk) -{ - int digest_old, digest_new; - - digest_old = LUKS2_reencrypt_digest_old(hdr); - digest_new = LUKS2_reencrypt_digest_new(hdr); - - while (vk) { - if (digest_old >= 0 && LUKS2_digest_verify_by_digest(cd, digest_old, vk) == digest_old) - crypt_volume_key_set_id(vk, digest_old); - if (digest_new >= 0 && LUKS2_digest_verify_by_digest(cd, digest_new, vk) == digest_new) - crypt_volume_key_set_id(vk, digest_new); - vk = vk->next; - } -} - int LUKS2_reencrypt_digest_verify(struct crypt_device *cd, struct luks2_hdr *hdr, struct volume_key *vks) diff --git a/lib/luks2/luks2_segment.c b/lib/luks2/luks2_segment.c index c2feb92..6284ca8 100644 --- a/lib/luks2/luks2_segment.c +++ b/lib/luks2/luks2_segment.c @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup v2, internal segment handling * - * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2018-2024 Ondrej Kozina + * Copyright (C) 2018-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2018-2025 Ondrej Kozina */ #include "luks2_internal.h" @@ -270,7 +270,7 @@ json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length, } static bool json_add_crypt_fields(json_object *jobj_segment, uint64_t iv_offset, - const char *cipher, const char *integrity, + const char *cipher, const char *integrity, uint32_t integrity_key_size, uint32_t sector_size, unsigned reencryption) { json_object *jobj_integrity; @@ -289,6 +289,8 @@ static bool json_add_crypt_fields(json_object *jobj_segment, uint64_t iv_offset, json_object_object_add(jobj_integrity, "type", json_object_new_string(integrity)); json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string("none")); json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string("none")); + if (integrity_key_size) + json_object_object_add(jobj_integrity, "key_size", json_object_new_int(integrity_key_size)); json_object_object_add(jobj_segment, "integrity", jobj_integrity); } @@ -300,7 +302,7 @@ static bool json_add_crypt_fields(json_object *jobj_segment, uint64_t iv_offset, json_object *json_segment_create_crypt(uint64_t offset, uint64_t iv_offset, const uint64_t *length, - const char *cipher, const char *integrity, + const char *cipher, const char *integrity, uint32_t integrity_key_size, uint32_t sector_size, unsigned reencryption) { json_object *jobj = _segment_create_generic("crypt", offset, length); @@ -308,7 +310,7 @@ json_object *json_segment_create_crypt(uint64_t offset, if (!jobj) return NULL; - if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption)) + if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, integrity_key_size, sector_size, reencryption)) return jobj; json_object_put(jobj); @@ -350,7 +352,7 @@ json_object *json_segment_create_opal_crypt(uint64_t offset, const uint64_t *len json_add_opal_fields(jobj, length, segment_number, key_size); - if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption)) + if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, 0, sector_size, reencryption)) return jobj; json_object_put(jobj); diff --git a/lib/luks2/luks2_token.c b/lib/luks2/luks2_token.c index 83bf089..89e7682 100644 --- a/lib/luks2/luks2_token.c +++ b/lib/luks2/luks2_token.c @@ -2,16 +2,16 @@ /* * LUKS - Linux Unified Key Setup v2, token handling * - * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2024 Milan Broz + * Copyright (C) 2016-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2025 Milan Broz */ #include -#include #include "luks2_internal.h" #if USE_EXTERNAL_TOKENS +#include #define TOKENS_PATH_MAX PATH_MAX static bool external_tokens_enabled = true; static char external_tokens_path[TOKENS_PATH_MAX] = EXTERNAL_LUKS2_TOKENS_PATH; @@ -101,10 +101,11 @@ static void *token_dlvsym(struct crypt_device *cd, char *error; void *sym; -#ifdef HAVE_DLVSYM +#if HAVE_DLVSYM log_dbg(cd, "Loading symbol %s@%s.", symbol, version); sym = dlvsym(handle, symbol, version); #else + UNUSED(version); log_dbg(cd, "Loading default version of symbol %s.", symbol); sym = dlsym(handle, symbol); #endif @@ -443,12 +444,6 @@ crypt_token_info LUKS2_token_status(struct crypt_device *cd, return is_builtin_candidate(tmp) ? CRYPT_TOKEN_INTERNAL_UNKNOWN : CRYPT_TOKEN_EXTERNAL_UNKNOWN; } -static const char *token_json_to_string(json_object *jobj_token) -{ - return json_object_to_json_string_ext(jobj_token, - JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); -} - static int token_is_usable(struct luks2_hdr *hdr, json_object *jobj_token, int keyslot, int segment, crypt_keyslot_priority minimal_priority, bool requires_keyslot) { @@ -548,7 +543,7 @@ static int token_open(struct crypt_device *cd, if (!(h = LUKS2_token_handler(cd, token))) return -ENOENT; - if (h->validate && h->validate(cd, token_json_to_string(jobj_token))) { + if (h->validate && h->validate(cd, crypt_jobj_to_string_on_disk(jobj_token))) { log_dbg(cd, "Token %d (%s) validation failed.", token, h->name); return -ENOENT; } @@ -842,71 +837,6 @@ int LUKS2_token_unlock_key(struct crypt_device *cd, return r; } -int LUKS2_token_open_and_activate(struct crypt_device *cd, - struct luks2_hdr *hdr, - int keyslot, - int token, - const char *name, - const char *type, - const char *pin, - size_t pin_size, - uint32_t flags, - void *usrptr) -{ - bool use_keyring; - int r, segment; - struct volume_key *p_crypt, *p_opal, *crypt_key = NULL, *opal_key = NULL, *vk = NULL; - - if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) - segment = CRYPT_ANY_SEGMENT; - else - segment = CRYPT_DEFAULT_SEGMENT; - - r = LUKS2_token_unlock_key(cd, hdr, keyslot, token, type, pin, pin_size, segment, usrptr, &vk); - if (r < 0) - return r; - - assert(vk); - - keyslot = r; - - if (LUKS2_segment_is_hw_opal(hdr, CRYPT_DEFAULT_SEGMENT)) { - r = LUKS2_split_crypt_and_opal_keys(cd, hdr, vk, &crypt_key, &opal_key); - if (r < 0) { - crypt_free_volume_key(vk); - return r; - } - - p_crypt = crypt_key; - p_opal = opal_key ?: vk; - } else { - p_crypt = vk; - p_opal = NULL; - } - - if (!crypt_use_keyring_for_vk(cd) || !p_crypt) - use_keyring = false; - else - use_keyring = ((name && !crypt_is_cipher_null(crypt_get_cipher(cd))) || - (flags & CRYPT_ACTIVATE_KEYRING_KEY)); - - if (use_keyring) { - if (!(r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, p_crypt, keyslot))) - flags |= CRYPT_ACTIVATE_KEYRING_KEY; - } - - if (r >= 0 && name) - r = LUKS2_activate(cd, name, p_crypt, p_opal, flags); - - if (r < 0) - crypt_drop_keyring_key(cd, p_crypt); - crypt_free_volume_key(vk); - crypt_free_volume_key(crypt_key); - crypt_free_volume_key(opal_key); - - return r < 0 ? r : keyslot; -} - void LUKS2_token_dump(struct crypt_device *cd, int token) { const crypt_token_handler *h; @@ -916,8 +846,7 @@ void LUKS2_token_dump(struct crypt_device *cd, int token) if (h && h->dump) { jobj_token = LUKS2_get_token_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), token); if (jobj_token) - h->dump(cd, json_object_to_json_string_ext(jobj_token, - JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE)); + h->dump(cd, crypt_jobj_to_string_on_disk(jobj_token)); } } @@ -929,7 +858,7 @@ int LUKS2_token_json_get(struct luks2_hdr *hdr, int token, const char **json) if (!jobj_token) return -EINVAL; - *json = token_json_to_string(jobj_token); + *json = crypt_jobj_to_string_on_disk(jobj_token); return 0; } @@ -1137,7 +1066,7 @@ int LUKS2_token_unlock_passphrase(struct crypt_device *cd, if (!r) { *passphrase = crypt_safe_alloc(buffer_size); if (*passphrase) { - memcpy(*passphrase, buffer, buffer_size); + crypt_safe_memcpy(*passphrase, buffer, buffer_size); *passphrase_size = buffer_size; } else r = -ENOMEM; diff --git a/lib/luks2/luks2_token_keyring.c b/lib/luks2/luks2_token_keyring.c index c455aa0..c8a33a5 100644 --- a/lib/luks2/luks2_token_keyring.c +++ b/lib/luks2/luks2_token_keyring.c @@ -2,8 +2,8 @@ /* * LUKS - Linux Unified Key Setup v2, kernel keyring token * - * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2024 Ondrej Kozina + * Copyright (C) 2016-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2025 Ondrej Kozina */ #include "luks2_internal.h" diff --git a/lib/nls.h b/lib/nls.h index 39760b1..64a271f 100644 --- a/lib/nls.h +++ b/lib/nls.h @@ -5,14 +5,14 @@ #define LOCALEDIR "/usr/share/locale" #endif -#ifdef HAVE_LOCALE_H +#if HAVE_LOCALE_H # include #else # undef setlocale # define setlocale(Category, Locale) /* empty */ #endif -#ifdef ENABLE_NLS +#if ENABLE_NLS # include # define _(Text) gettext (Text) # ifdef gettext_noop diff --git a/lib/random.c b/lib/random.c index 4720daf..06d2713 100644 --- a/lib/random.c +++ b/lib/random.c @@ -2,7 +2,7 @@ /* * cryptsetup kernel RNG access functions * - * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved. */ #include diff --git a/lib/setup.c b/lib/setup.c index 66044f4..f1e8d92 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include @@ -18,7 +18,6 @@ #include "libcryptsetup.h" #include "luks1/luks.h" #include "luks2/luks2.h" -#include "luks2/luks2_internal.h" #include "loopaes/loopaes.h" #include "verity/verity.h" #include "tcrypt/tcrypt.h" @@ -290,16 +289,17 @@ static int process_key(struct crypt_device *cd, const char *hash_name, struct volume_key **vk) { int r; + void *key = NULL; if (!key_size) return -EINVAL; - *vk = crypt_alloc_volume_key(key_size, NULL); - if (!*vk) - return -ENOMEM; - if (hash_name) { - r = crypt_plain_hash(cd, hash_name, (*vk)->key, key_size, pass, passLen); + key = crypt_safe_alloc(key_size); + if (!key) + return -ENOMEM; + + r = crypt_plain_hash(cd, hash_name, key, key_size, pass, passLen); if (r < 0) { if (r == -ENOENT) log_err(cd, _("Hash algorithm %s not supported."), @@ -307,17 +307,27 @@ static int process_key(struct crypt_device *cd, const char *hash_name, else log_err(cd, _("Key processing error (using hash %s)."), hash_name); - crypt_free_volume_key(*vk); - *vk = NULL; + crypt_safe_free(key); return -EINVAL; } - } else if (passLen > key_size) { - memcpy((*vk)->key, pass, key_size); + *vk = crypt_alloc_volume_key_by_safe_alloc(&key); + } else if (passLen >= key_size) { + *vk = crypt_alloc_volume_key(key_size, pass); } else { - memcpy((*vk)->key, pass, passLen); + key = crypt_safe_alloc(key_size); + if (!key) + return -ENOMEM; + + crypt_safe_memcpy(key, pass, passLen); + + *vk = crypt_alloc_volume_key_by_safe_alloc(&key); } - return 0; + r = *vk ? 0 : -ENOMEM; + + crypt_safe_free(key); + + return r; } static int isPLAIN(const char *type) @@ -404,7 +414,7 @@ static int onlyLUKSnoRequirements(struct crypt_device *cd) static int onlyLUKS(struct crypt_device *cd) { - return _onlyLUKS(cd, 0, CRYPT_REQUIREMENT_OPAL); + return _onlyLUKS(cd, 0, CRYPT_REQUIREMENT_OPAL | CRYPT_REQUIREMENT_INLINE_HW_TAGS); } static int _onlyLUKS2(struct crypt_device *cd, uint32_t cdflags, uint32_t mask) @@ -437,7 +447,7 @@ static int onlyLUKS2unrestricted(struct crypt_device *cd) /* Internal only */ int onlyLUKS2(struct crypt_device *cd) { - return _onlyLUKS2(cd, 0, CRYPT_REQUIREMENT_OPAL); + return _onlyLUKS2(cd, 0, CRYPT_REQUIREMENT_OPAL | CRYPT_REQUIREMENT_INLINE_HW_TAGS); } /* Internal only */ @@ -502,92 +512,6 @@ static int keyslot_verify_or_find_empty(struct crypt_device *cd, int *keyslot) return 0; } -/* - * compares UUIDs returned by device-mapper (striped by cryptsetup) and uuid in header - */ -int crypt_uuid_cmp(const char *dm_uuid, const char *hdr_uuid) -{ - int i, j; - char *str; - - if (!dm_uuid || !hdr_uuid) - return -EINVAL; - - /* skip beyond LUKS2_HW_OPAL prefix */ - if (!strncmp(dm_uuid, CRYPT_LUKS2_HW_OPAL, strlen(CRYPT_LUKS2_HW_OPAL))) - dm_uuid = dm_uuid + strlen(CRYPT_LUKS2_HW_OPAL); - - str = strchr(dm_uuid, '-'); - if (!str) - return -EINVAL; - - for (i = 0, j = 1; hdr_uuid[i]; i++) { - if (hdr_uuid[i] == '-') - continue; - - if (!str[j] || str[j] == '-') - return -EINVAL; - - if (str[j] != hdr_uuid[i]) - return -EINVAL; - j++; - } - - return 0; -} - -/* - * compares two UUIDs returned by device-mapper (striped by cryptsetup) - * used for stacked LUKS2 & INTEGRITY devices - */ -static int crypt_uuid_integrity_cmp(const char *dm_uuid, const char *dmi_uuid) -{ - int i; - char *str, *stri; - - if (!dm_uuid || !dmi_uuid) - return -EINVAL; - - /* skip beyond LUKS2_HW_OPAL prefix */ - if (!strncmp(dm_uuid, CRYPT_LUKS2_HW_OPAL, strlen(CRYPT_LUKS2_HW_OPAL))) - dm_uuid = dm_uuid + strlen(CRYPT_LUKS2_HW_OPAL); - - str = strchr(dm_uuid, '-'); - if (!str) - return -EINVAL; - - stri = strchr(dmi_uuid, '-'); - if (!stri) - return -EINVAL; - - for (i = 1; str[i] && str[i] != '-'; i++) { - if (!stri[i]) - return -EINVAL; - - if (str[i] != stri[i]) - return -EINVAL; - } - - return 0; -} - -/* - * compares type of active device to provided string - */ -int crypt_uuid_type_cmp(const char *dm_uuid, const char *type) -{ - size_t len; - - assert(type); - - len = strlen(type); - if (dm_uuid && strlen(dm_uuid) > len && - !strncmp(dm_uuid, type, len) && dm_uuid[len] == '-') - return 0; - - return -ENODEV; -} - int PLAIN_activate(struct crypt_device *cd, const char *name, struct volume_key *vk, @@ -610,8 +534,7 @@ int PLAIN_activate(struct crypt_device *cd, r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), vk, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd), - crypt_get_data_offset(cd), crypt_get_integrity(cd), - crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd)); + crypt_get_data_offset(cd), NULL, 0, 0, crypt_get_sector_size(cd)); if (r < 0) return r; @@ -1264,7 +1187,7 @@ static int _init_by_name_crypt_none(struct crypt_device *cd) r = -EINVAL; else { cd->u.none.cipher_mode = cd->u.none.cipher_spec + strlen(cd->u.none.cipher) + 1; - cd->u.none.key_size = tgt->u.crypt.vk->keylength; + cd->u.none.key_size = crypt_volume_key_length(tgt->u.crypt.vk); r = 0; } } @@ -1301,7 +1224,8 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) bool found = false; char **dep, *cipher_spec = NULL, cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; char deps_uuid_prefix[40], *deps[MAX_DM_DEPS+1] = {}; - const char *dev, *namei; + const char *dev; + char *iname = NULL; int key_nums, r; struct crypt_dm_active_device dmd, dmdi = {}, dmdep = {}; struct dm_target *tgt = &dmd.segment, *tgti = &dmdi.segment; @@ -1349,16 +1273,13 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) dep = deps; - if (tgt->type == DM_CRYPT && tgt->u.crypt.integrity && (namei = device_dm_name(tgt->data_device))) { - r = dm_query_device(cd, namei, DM_ACTIVE_DEVICE, &dmdi); + if (tgt->type == DM_CRYPT && tgt->u.crypt.tag_size && + (iname = dm_get_active_iname(cd, name))) { + + r = dm_query_device(cd, iname, DM_ACTIVE_DEVICE, &dmdi); + free(iname); if (r < 0) goto out; - if (!single_segment(&dmdi) || tgti->type != DM_INTEGRITY) { - log_dbg(cd, "Unsupported device table detected in %s.", namei); - r = -EINVAL; - goto out; - } - /* * Data device for crypt with integrity is not dm-integrity device, * but always the device underlying dm-integrity. @@ -1409,19 +1330,21 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) cd->u.plain.hdr.offset = tgt->u.crypt.offset; cd->u.plain.hdr.skip = tgt->u.crypt.iv_offset; cd->u.plain.hdr.sector_size = tgt->u.crypt.sector_size; - cd->u.plain.key_size = tgt->u.crypt.vk->keylength; + cd->u.plain.key_size = crypt_volume_key_length(tgt->u.crypt.vk); cd->u.plain.cipher = strdup(cipher); MOVE_REF(cd->u.plain.cipher_spec, cipher_spec); cd->u.plain.cipher_mode = cd->u.plain.cipher_spec + strlen(cipher) + 1; + if (dmd.flags & CRYPT_ACTIVATE_KEYRING_KEY) + crypt_set_key_in_keyring(cd, 1); } else if (isLOOPAES(cd->type) && single_segment(&dmd) && tgt->type == DM_CRYPT) { cd->u.loopaes.hdr.offset = tgt->u.crypt.offset; cd->u.loopaes.cipher = strdup(cipher); MOVE_REF(cd->u.loopaes.cipher_spec, cipher_spec); cd->u.loopaes.cipher_mode = cd->u.loopaes.cipher_spec + strlen(cipher) + 1; /* version 3 uses last key for IV */ - if (tgt->u.crypt.vk->keylength % key_nums) + if (crypt_volume_key_length(tgt->u.crypt.vk) % key_nums) key_nums++; - cd->u.loopaes.key_size = tgt->u.crypt.vk->keylength / key_nums; + cd->u.loopaes.key_size = crypt_volume_key_length(tgt->u.crypt.vk) / key_nums; } else if (isLUKS1(cd->type) || isLUKS2(cd->type)) { if (crypt_metadata_device(cd)) { r = _crypt_load_luks(cd, cd->type, true, false); @@ -1434,7 +1357,7 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) goto out; } /* check whether UUIDs match each other */ - r = crypt_uuid_cmp(dmd.uuid, LUKS_UUID(cd)); + r = dm_uuid_cmp(dmd.uuid, LUKS_UUID(cd)); if (r < 0) { log_dbg(cd, "LUKS device header uuid: %s mismatches DM returned uuid %s", LUKS_UUID(cd), dmd.uuid); @@ -1558,11 +1481,11 @@ static int _init_by_name_integrity(struct crypt_device *cd, const char *name) MOVE_REF(cd->u.integrity.params.journal_crypt, tgt->u.integrity.journal_crypt); if (tgt->u.integrity.vk) - cd->u.integrity.params.integrity_key_size = tgt->u.integrity.vk->keylength; + cd->u.integrity.params.integrity_key_size = crypt_volume_key_length(tgt->u.integrity.vk); if (tgt->u.integrity.journal_integrity_key) - cd->u.integrity.params.journal_integrity_key_size = tgt->u.integrity.journal_integrity_key->keylength; + cd->u.integrity.params.journal_integrity_key_size = crypt_volume_key_length(tgt->u.integrity.journal_integrity_key); if (tgt->u.integrity.journal_crypt_key) - cd->u.integrity.params.integrity_key_size = tgt->u.integrity.journal_crypt_key->keylength; + cd->u.integrity.params.journal_crypt_key_size = crypt_volume_key_length(tgt->u.integrity.journal_crypt_key); MOVE_REF(cd->metadata_device, tgt->u.integrity.meta_device); } out: @@ -1796,7 +1719,7 @@ static int _crypt_format_luks1(struct crypt_device *cd, cd->volume_key = crypt_alloc_volume_key(volume_key_size, volume_key); else - cd->volume_key = crypt_generate_volume_key(cd, volume_key_size); + cd->volume_key = crypt_generate_volume_key(cd, volume_key_size, KEY_QUALITY_KEY); if (!cd->volume_key) return -ENOMEM; @@ -1886,9 +1809,11 @@ static int LUKS2_check_encryption_params(struct crypt_device *cd, const char *cipher, const char *cipher_mode, const char *integrity, + size_t required_integrity_key_size, size_t volume_key_size, const struct crypt_params_luks2 *params, - const char **ret_integrity) + const char **ret_integrity, + size_t *ret_integrity_key_size) { int r, integrity_key_size = 0; @@ -1899,8 +1824,7 @@ static int LUKS2_check_encryption_params(struct crypt_device *cd, if (integrity) { if (params->integrity_params) { /* Standalone dm-integrity must not be used */ - if (params->integrity_params->integrity || - params->integrity_params->integrity_key_size) + if (params->integrity_params->integrity) return -EINVAL; /* FIXME: journal encryption and MAC is here not yet supported */ if (params->integrity_params->journal_crypt || @@ -1914,15 +1838,19 @@ static int LUKS2_check_encryption_params(struct crypt_device *cd, else return -EINVAL; } - integrity_key_size = INTEGRITY_key_size(integrity); + integrity_key_size = INTEGRITY_key_size(integrity, required_integrity_key_size); if ((integrity_key_size < 0) || (integrity_key_size >= (int)volume_key_size)) { log_err(cd, _("Volume key is too small for encryption with integrity extensions.")); return -EINVAL; } + if (integrity_key_size && integrity_key_size < LUKS2_MIN_INTEGRITY_KEY_BYTES) { + log_err(cd, _("Integrity key size is too small.")); + return -EINVAL; + } } /* FIXME: allow this later also for normal ciphers (check AF_ALG availability. */ - if (integrity && !integrity_key_size) { + if (integrity && integrity_key_size == 0) { r = crypt_cipher_check_kernel(cipher, cipher_mode, integrity, volume_key_size); if (r < 0) { log_err(cd, _("Cipher %s-%s (key size %zd bits) is not available."), @@ -1940,6 +1868,8 @@ static int LUKS2_check_encryption_params(struct crypt_device *cd, } *ret_integrity = integrity; + if (ret_integrity_key_size) + *ret_integrity_key_size = required_integrity_key_size ? integrity_key_size : 0; return 0; } @@ -1948,7 +1878,7 @@ static int LUKS2_check_encryption_sector(struct crypt_device *cd, uint64_t devic uint64_t data_offset_bytes, uint32_t sector_size, bool modify_sector_size, bool verify_data_area_alignment, uint32_t *ret_sector_size) { - uint32_t dmc_flags; + uint64_t dmc_flags; assert(ret_sector_size); @@ -1999,7 +1929,7 @@ static int _crypt_format_luks2(struct crypt_device *cd, const char *volume_key, size_t volume_key_size, struct crypt_params_luks2 *params, - bool sector_size_autodetect) + bool sector_size_autodetect, bool integrity_inline) { int r; unsigned long required_alignment = DEFAULT_DISK_ALIGNMENT; @@ -2007,6 +1937,8 @@ static int _crypt_format_luks2(struct crypt_device *cd, unsigned int sector_size; char cipher_spec[2*MAX_CAPI_ONE_LEN]; const char *integrity = params ? params->integrity : NULL; + size_t integrity_key_size = 0; /* only for independent, separate key in HMAC */ + struct volume_key *integrity_key = NULL; uint64_t data_offset_bytes, dev_size, metadata_size_bytes, keyslots_size_bytes; cd->u.luks2.hdr.jobj = NULL; @@ -2066,7 +1998,7 @@ static int _crypt_format_luks2(struct crypt_device *cd, cd->volume_key = crypt_alloc_volume_key(volume_key_size, volume_key); else - cd->volume_key = crypt_generate_volume_key(cd, volume_key_size); + cd->volume_key = crypt_generate_volume_key(cd, volume_key_size, KEY_QUALITY_KEY); if (!cd->volume_key) return -ENOMEM; @@ -2091,8 +2023,11 @@ static int _crypt_format_luks2(struct crypt_device *cd, &required_alignment, &alignment_offset, DEFAULT_DISK_ALIGNMENT); - r = LUKS2_check_encryption_params(cd, cipher, cipher_mode, integrity, - volume_key_size, params, &integrity); + if (params && params->integrity_params && params->integrity_params->integrity_key_size) + integrity_key_size = params->integrity_params->integrity_key_size; + + r = LUKS2_check_encryption_params(cd, cipher, cipher_mode, integrity, integrity_key_size, + volume_key_size, params, &integrity, &integrity_key_size); if (r < 0) goto out; @@ -2122,7 +2057,8 @@ static int _crypt_format_luks2(struct crypt_device *cd, r = LUKS2_generate_hdr(cd, &cd->u.luks2.hdr, cd->volume_key, cipher_spec, - integrity, uuid, + integrity, integrity_key_size, + uuid, sector_size, data_offset_bytes, metadata_size_bytes, keyslots_size_bytes, @@ -2130,6 +2066,14 @@ static int _crypt_format_luks2(struct crypt_device *cd, if (r < 0) goto out; + if (integrity_inline) { + log_dbg(cd, "Adding LUKS2 inline HW tags requirement flag."); + r = LUKS2_config_set_requirement_version(cd, &cd->u.luks2.hdr, + CRYPT_REQUIREMENT_INLINE_HW_TAGS, 1, false); + if (r < 0) + goto out; + } + if (params && (params->label || params->subsystem)) { r = LUKS2_hdr_labels(cd, &cd->u.luks2.hdr, params->label, params->subsystem, 0); @@ -2167,11 +2111,24 @@ static int _crypt_format_luks2(struct crypt_device *cd, goto out; } + } - r = INTEGRITY_format(cd, params ? params->integrity_params : NULL, NULL, NULL, 0); + /* Format underlying virtual dm-integrity device */ + if (!integrity_inline && crypt_get_integrity_tag_size(cd)) { + if (integrity_key_size) { + integrity_key = crypt_alloc_volume_key(integrity_key_size, + crypt_volume_key_get_key(cd->volume_key) + volume_key_size - integrity_key_size); + if (!integrity_key) { + r = -ENOMEM; + goto out; + } + } + r = INTEGRITY_format(cd, params ? params->integrity_params : NULL, + integrity_key, NULL, NULL, 0, NULL, false); if (r) log_err(cd, _("Cannot format integrity for device %s."), data_device_path(cd)); + crypt_free_volume_key(integrity_key); } if (r < 0) @@ -2336,6 +2293,8 @@ int crypt_format_luks2_opal(struct crypt_device *cd, int r; char cipher_spec[128]; const char *integrity = params ? params->integrity : NULL; + size_t integrity_key_size = 0; /* only for independent, separate key in HMAC */ + struct volume_key *integrity_key = NULL; uint32_t sector_size, opal_block_bytes, opal_segment_number = 1; /* We'll use the partition number if available later */ uint64_t alignment_offset_bytes, data_offset_bytes, device_size_bytes, opal_alignment_granularity_blocks, partition_offset_sectors, range_offset_blocks, range_size_bytes, @@ -2415,7 +2374,7 @@ int crypt_format_luks2_opal(struct crypt_device *cd, if (volume_keys) cd->volume_key = crypt_alloc_volume_key(volume_keys_size, volume_keys); else - cd->volume_key = crypt_generate_volume_key(cd, volume_keys_size); + cd->volume_key = crypt_generate_volume_key(cd, volume_keys_size, KEY_QUALITY_KEY); if (!cd->volume_key) { r = -ENOMEM; @@ -2423,7 +2382,7 @@ int crypt_format_luks2_opal(struct crypt_device *cd, } if (cipher) { - user_key = crypt_alloc_volume_key(opal_params->user_key_size, cd->volume_key->key); + user_key = crypt_alloc_volume_key(opal_params->user_key_size, crypt_volume_key_get_key(cd->volume_key)); if (!user_key) { r = -ENOMEM; goto out; @@ -2475,9 +2434,12 @@ int crypt_format_luks2_opal(struct crypt_device *cd, opal_segment_number = r; if (cipher) { - r = LUKS2_check_encryption_params(cd, cipher, cipher_mode, integrity, + if (params->integrity_params && params->integrity_params->integrity_key_size) + integrity_key_size = params->integrity_params->integrity_key_size; + + r = LUKS2_check_encryption_params(cd, cipher, cipher_mode, integrity, 0, volume_keys_size - opal_params->user_key_size, - params, &integrity); + params, &integrity, &integrity_key_size); if (r < 0) goto out; } @@ -2524,7 +2486,9 @@ int crypt_format_luks2_opal(struct crypt_device *cd, } r = LUKS2_generate_hdr(cd, &cd->u.luks2.hdr, cd->volume_key, - cipher ? cipher_spec : NULL, integrity, uuid, + cipher ? cipher_spec : NULL, + integrity, integrity_key_size, + uuid, sector_size, data_offset_bytes, metadata_size_bytes, keyslots_size_bytes, @@ -2603,15 +2567,27 @@ int crypt_format_luks2_opal(struct crypt_device *cd, goto out; } - r = INTEGRITY_format(cd, params->integrity_params, NULL, NULL, + if (integrity_key_size) { + integrity_key = crypt_alloc_volume_key(integrity_key_size, + crypt_volume_key_get_key(cd->volume_key) + volume_keys_size - integrity_key_size); + + if (!integrity_key) { + r = -ENOMEM; + goto out; + } + } + + r = INTEGRITY_format(cd, params->integrity_params, integrity_key, NULL, NULL, /* * Create reduced dm-integrity device only if locking range size does * not match device size. */ - device_size_bytes != range_size_bytes ? range_size_bytes / SECTOR_SIZE : 0); + device_size_bytes != range_size_bytes ? range_size_bytes / SECTOR_SIZE : 0, NULL, false); if (r) log_err(cd, _("Cannot format integrity for device %s."), data_device_path(cd)); + + crypt_free_volume_key(integrity_key); if (r < 0) goto out; @@ -2887,12 +2863,14 @@ static int _crypt_format_verity(struct crypt_device *cd, static int _crypt_format_integrity(struct crypt_device *cd, const char *uuid, - struct crypt_params_integrity *params) + struct crypt_params_integrity *params, + const char *integrity_key, size_t integrity_key_size, + bool integrity_inline) { int r; uint32_t integrity_tag_size; char *integrity = NULL, *journal_integrity = NULL, *journal_crypt = NULL; - struct volume_key *journal_crypt_key = NULL, *journal_mac_key = NULL; + struct volume_key *journal_crypt_key = NULL, *journal_mac_key = NULL, *ik = NULL; if (!params) return -EINVAL; @@ -2902,6 +2880,11 @@ static int _crypt_format_integrity(struct crypt_device *cd, return -EINVAL; } + if (integrity_key_size && integrity_key_size != params->integrity_key_size) { + log_err(cd, _("Integrity key size mismatch.")); + return -EINVAL; + } + r = device_check_access(cd, crypt_metadata_device(cd), DEV_EXCL); if (r < 0) return r; @@ -2968,10 +2951,24 @@ static int _crypt_format_integrity(struct crypt_device *cd, cd->u.integrity.params.journal_integrity = journal_integrity; cd->u.integrity.params.journal_crypt = journal_crypt; - r = INTEGRITY_format(cd, params, cd->u.integrity.journal_crypt_key, cd->u.integrity.journal_mac_key, 0); + if (params->integrity_key_size) { + if (!integrity_key) + ik = crypt_generate_volume_key(cd, params->integrity_key_size, KEY_QUALITY_EMPTY); + else + ik = crypt_alloc_volume_key(params->integrity_key_size, integrity_key); + if (!ik) { + r = -ENOMEM; + goto out; + } + } + + r = INTEGRITY_format(cd, params, ik, cd->u.integrity.journal_crypt_key, + cd->u.integrity.journal_mac_key, 0, &cd->u.integrity.sb_flags, + integrity_inline); if (r) - log_err(cd, _("Cannot format integrity for device %s."), - mdata_device_path(cd)); + log_err(cd, _("Cannot format integrity for device %s."), mdata_device_path(cd)); + + crypt_free_volume_key(ik); out: if (r) { crypt_free_volume_key(journal_crypt_key); @@ -2984,6 +2981,128 @@ static int _crypt_format_integrity(struct crypt_device *cd, return r; } +int crypt_format_inline(struct crypt_device *cd, + const char *type, + const char *cipher, + const char *cipher_mode, + const char *uuid, + const char *volume_key, + size_t volume_key_size, + void *params) +{ + struct crypt_params_luks2 *lparams; + const struct crypt_params_integrity *iparams; + uint32_t device_tag_size, required_tag_size; + struct device *idevice; + size_t sector_size, required_sector_size; + int r; + + if (!cd || !params) + return -EINVAL; + + if (cd->type) { + log_dbg(cd, "Context already formatted as %s.", cd->type); + return -EINVAL; + } + + log_dbg(cd, "Formatting device %s as type %s with inline tags.", mdata_device_path(cd) ?: "(none)", type); + + crypt_reset_null_type(cd); + + r = init_crypto(cd); + if (r < 0) + return r; + + if (isINTEGRITY(type)) { + lparams = NULL; + iparams = params; + idevice = crypt_metadata_device(cd); + required_sector_size = iparams->sector_size; + required_tag_size = iparams->tag_size; + + /* Unused in standalone integrity */ + if (cipher || cipher_mode) + return -EINVAL; + } else if (isLUKS2(type)) { + lparams = params; + iparams = lparams->integrity_params; + + if (lparams->data_device) { + if (!cd->metadata_device) + cd->metadata_device = cd->device; + else + device_free(cd, cd->device); + cd->device = NULL; + if (device_alloc(cd, &cd->device, lparams->data_device) < 0) + return -ENOMEM; + } + + idevice = crypt_data_device(cd); + required_sector_size = lparams->sector_size; + + if (!lparams->integrity || !idevice) + return -EINVAL; + + required_tag_size = INTEGRITY_tag_size(lparams->integrity, cipher, cipher_mode); + } else { + log_err(cd, _("Unknown or unsupported device type %s requested."), type); + return -EINVAL; + } + + /* In inline mode journal will be never used, check that params are not set */ + if (iparams && (iparams->journal_size || iparams->journal_watermark || iparams->journal_commit_time || + iparams->interleave_sectors || iparams->journal_integrity || iparams->journal_integrity_key || + iparams->journal_integrity_key_size || iparams->journal_crypt || iparams->journal_crypt_key || + iparams->journal_integrity_key_size)) + return -EINVAL; + + r = device_is_nop_dif(idevice, &device_tag_size); + if (r < 0) + return r; + + if (!r) { + log_err(cd, _("Device %s does not provide inline integrity data fields."), mdata_device_path(cd)); + return -EINVAL; + } + + /* We can get device_tag_size = 0 as kernel provides this info only for some block devices */ + if (device_tag_size > 0 && device_tag_size < required_tag_size) { + log_err(cd, _("Inline tag size %" PRIu32 " [bytes] is larger than %" PRIu32 " provided by device %s."), + required_tag_size, device_tag_size, mdata_device_path(cd)); + return -EINVAL; + } + log_dbg(cd, "Inline integrity is supported (%" PRIu32 ").", device_tag_size); + + /* Inline must use sectors size as hardware device */ + sector_size = device_block_size(cd, idevice); + if (!sector_size) + return -EINVAL; + + /* No autodetection, use device sector size */ + if (isLUKS2(type) && lparams && !required_sector_size) + lparams->sector_size = sector_size; + else if (sector_size != required_sector_size) { + log_err(cd, _("Sector must be the same as device hardware sector (%zu bytes)."), sector_size); + return -EINVAL; + } + + if (isINTEGRITY(type)) + r = _crypt_format_integrity(cd, uuid, params, volume_key, volume_key_size, true); + else if (isLUKS2(type)) + r = _crypt_format_luks2(cd, cipher, cipher_mode, + uuid, volume_key, volume_key_size, params, false, true); + else + r = -EINVAL; + + if (r < 0) { + crypt_set_null_type(cd); + crypt_free_volume_key(cd->volume_key); + cd->volume_key = NULL; + } + + return r; +} + static int _crypt_format(struct crypt_device *cd, const char *type, const char *cipher, @@ -3020,15 +3139,15 @@ static int _crypt_format(struct crypt_device *cd, uuid, volume_key, volume_key_size, params); else if (isLUKS2(type)) r = _crypt_format_luks2(cd, cipher, cipher_mode, - uuid, volume_key, volume_key_size, params, sector_size_autodetect); + uuid, volume_key, volume_key_size, params, sector_size_autodetect, false); else if (isLOOPAES(type)) r = _crypt_format_loopaes(cd, cipher, uuid, volume_key_size, params); else if (isVERITY(type)) r = _crypt_format_verity(cd, uuid, params); else if (isINTEGRITY(type)) - r = _crypt_format_integrity(cd, uuid, params); + r = _crypt_format_integrity(cd, uuid, params, volume_key, volume_key_size, false); else { - log_err(cd, _("Unknown crypt device type %s requested."), type); + log_err(cd, _("Unknown or unsupported device type %s requested."), type); r = -EINVAL; } @@ -3102,22 +3221,62 @@ int crypt_repair(struct crypt_device *cd, } /* compare volume keys */ -static int _compare_volume_keys(struct volume_key *svk, unsigned skeyring_only, - struct volume_key *tvk, unsigned tkeyring_only) +static int _compare_volume_keys(struct volume_key *svk, struct volume_key *tvk) +{ + if (svk == tvk) + return 0; + + if (!svk || !tvk) + return 1; + + if (crypt_volume_key_length(svk) != crypt_volume_key_length(tvk)) + return 1; + + /* No switch between keyring and direct key specification */ + if ((!crypt_volume_key_description(svk) && crypt_volume_key_description(tvk)) || + (crypt_volume_key_description(svk) && !crypt_volume_key_description(tvk)) || + (!crypt_volume_key_is_set(svk) && crypt_volume_key_is_set(tvk)) || + (crypt_volume_key_is_set(svk) && !crypt_volume_key_is_set(tvk))) + return 1; + + if (crypt_volume_key_description(svk) && + (crypt_volume_key_kernel_key_type(svk) != crypt_volume_key_kernel_key_type(tvk) || + strcmp(crypt_volume_key_description(svk), crypt_volume_key_description(tvk)))) + return 1; + + if (crypt_volume_key_is_set(svk) && + crypt_backend_memeq(crypt_volume_key_get_key(svk), + crypt_volume_key_get_key(tvk), + crypt_volume_key_length(svk))) + return 1; + + return 0; +} + +static int _compare_volume_keys_luks2(struct volume_key *svk, struct volume_key *tvk) { - if (!svk && !tvk) + if (svk == tvk) return 0; - else if (!svk || !tvk) + + if (!svk || !tvk) return 1; - if (svk->keylength != tvk->keylength) + if (crypt_volume_key_length(svk) != crypt_volume_key_length(tvk)) return 1; - if (!skeyring_only && !tkeyring_only) - return crypt_backend_memeq(svk->key, tvk->key, svk->keylength); + if ((!crypt_volume_key_is_set(svk) && !crypt_volume_key_description(svk)) || + (!crypt_volume_key_is_set(tvk) && !crypt_volume_key_description(tvk))) + return 1; + + if (crypt_volume_key_is_set(svk) && crypt_volume_key_is_set(tvk) && + crypt_backend_memeq(crypt_volume_key_get_key(svk), + crypt_volume_key_get_key(tvk), + crypt_volume_key_length(svk))) + return 1; - if (svk->key_description && tvk->key_description) - return strcmp(svk->key_description, tvk->key_description); + if (crypt_volume_key_description(svk) && crypt_volume_key_description(tvk)) + return (crypt_volume_key_kernel_key_type(svk) != crypt_volume_key_kernel_key_type(tvk) || + strcmp(crypt_volume_key_description(svk), crypt_volume_key_description(tvk))); return 0; } @@ -3131,14 +3290,22 @@ static int _compare_device_types(struct crypt_device *cd, return -EINVAL; } - if (isLUKS2(cd->type) && !strncmp("INTEGRITY-", tgt->uuid, strlen("INTEGRITY-"))) { - if (crypt_uuid_cmp(tgt->uuid, src->uuid)) { + /* + * FIXME: The CRYPT_SUBDEV prefix should be enough but we need + * to keep INTEGRITY- for dm-integrity subdevices opened with + * cryptsetup version < 2.8.0. Drop the INTEGRITY condition + * in next Y release. + */ + if (isLUKS2(cd->type) && + (!strncmp("INTEGRITY-", tgt->uuid, strlen("INTEGRITY-")) || + !strncmp(CRYPT_SUBDEV, tgt->uuid, strlen(CRYPT_SUBDEV)))) { + if (dm_uuid_cmp(tgt->uuid, src->uuid)) { log_dbg(cd, "LUKS UUID mismatch."); return -EINVAL; } } else if (isLUKS(cd->type)) { if (!src->uuid || strncmp(cd->type, tgt->uuid, strlen(cd->type)) || - crypt_uuid_cmp(tgt->uuid, src->uuid)) { + dm_uuid_cmp(tgt->uuid, src->uuid)) { log_dbg(cd, "LUKS UUID mismatch."); return -EINVAL; } @@ -3183,9 +3350,14 @@ static int _compare_crypt_devices(struct crypt_device *cd, goto out; } - if (tgt->u.crypt.vk->keylength == 0 && crypt_is_cipher_null(tgt->u.crypt.cipher)) + if (crypt_volume_key_length(tgt->u.crypt.vk) == 0 && crypt_is_cipher_null(tgt->u.crypt.cipher)) log_dbg(cd, "Existing device uses cipher null. Skipping key comparison."); - else if (_compare_volume_keys(src->u.crypt.vk, 0, tgt->u.crypt.vk, tgt->u.crypt.vk->key_description != NULL)) { + else if (cd && isLUKS2(cd->type)) { + if (_compare_volume_keys_luks2(src->u.crypt.vk, tgt->u.crypt.vk)) { + log_dbg(cd, "Keys in LUKS2 context and target device do not match."); + goto out; + } + } else if (_compare_volume_keys(src->u.crypt.vk, tgt->u.crypt.vk)) { log_dbg(cd, "Keys in context and target device do not match."); goto out; } @@ -3245,9 +3417,9 @@ static int _compare_integrity_devices(struct crypt_device *cd, } /* unfortunately dm-integrity doesn't support keyring */ - if (_compare_volume_keys(src->u.integrity.vk, 0, tgt->u.integrity.vk, 0) || - _compare_volume_keys(src->u.integrity.journal_integrity_key, 0, tgt->u.integrity.journal_integrity_key, 0) || - _compare_volume_keys(src->u.integrity.journal_crypt_key, 0, tgt->u.integrity.journal_crypt_key, 0)) { + if (_compare_volume_keys(src->u.integrity.vk, tgt->u.integrity.vk) || + _compare_volume_keys(src->u.integrity.journal_integrity_key, tgt->u.integrity.journal_integrity_key) || + _compare_volume_keys(src->u.integrity.journal_crypt_key, tgt->u.integrity.journal_crypt_key)) { log_dbg(cd, "Journal keys do not match."); return -EINVAL; } @@ -3313,15 +3485,20 @@ int crypt_compare_dm_devices(struct crypt_device *cd, } static int _reload_device(struct crypt_device *cd, const char *name, - struct crypt_dm_active_device *sdmd, uint32_t dmflags) + struct crypt_dm_active_device *sdmd, uint64_t dmflags) { int r; struct crypt_dm_active_device tdmd; struct dm_target *src, *tgt = &tdmd.segment; - if (!cd || !cd->type || !name || !(sdmd->flags & CRYPT_ACTIVATE_REFRESH)) + assert(cd); + assert(sdmd); + + if (!cd->type || !name || !(sdmd->flags & CRYPT_ACTIVATE_REFRESH)) return -EINVAL; + src = &sdmd->segment; + r = dm_query_device(cd, name, DM_ACTIVE_DEVICE | DM_ACTIVE_CRYPT_CIPHER | DM_ACTIVE_UUID | DM_ACTIVE_CRYPT_KEYSIZE | DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_INTEGRITY_PARAMS | @@ -3345,25 +3522,24 @@ static int _reload_device(struct crypt_device *cd, const char *name, goto out; } - src = &sdmd->segment; - /* Changing read only flag for active device makes no sense */ if (tdmd.flags & CRYPT_ACTIVATE_READONLY) sdmd->flags |= CRYPT_ACTIVATE_READONLY; else sdmd->flags &= ~CRYPT_ACTIVATE_READONLY; - if (tgt->type == DM_CRYPT && sdmd->flags & CRYPT_ACTIVATE_KEYRING_KEY) { - r = crypt_volume_key_set_description(tgt->u.crypt.vk, src->u.crypt.vk->key_description); - if (r) - goto out; - } else if (tgt->type == DM_CRYPT) { + /* + * Only LUKS2 allows altering between volume key + * passed by hexbyte representation and reference + * to kernel keyring service. + * + * To make it easier pass src key directly after + * it was properly verified in crypt_compare_dm_devices + * call above. + */ + if (isLUKS2(cd->type) && tgt->type == DM_CRYPT && src->u.crypt.vk) { crypt_free_volume_key(tgt->u.crypt.vk); - tgt->u.crypt.vk = crypt_alloc_volume_key(src->u.crypt.vk->keylength, src->u.crypt.vk->key); - if (!tgt->u.crypt.vk) { - r = -ENOMEM; - goto out; - } + tgt->u.crypt.vk = src->u.crypt.vk; } if (tgt->type == DM_CRYPT) @@ -3383,6 +3559,10 @@ static int _reload_device(struct crypt_device *cd, const char *name, r = dm_reload_device(cd, name, &tdmd, dmflags, 1); out: + /* otherwise dm_targets_free would free src key */ + if (src->u.crypt.vk == tgt->u.crypt.vk) + tgt->u.crypt.vk = NULL; + dm_targets_free(cd, &tdmd); free(CONST_CAST(void*)tdmd.uuid); @@ -3402,9 +3582,16 @@ static int _reload_device_with_integrity(struct crypt_device *cd, struct device *data_device = NULL; bool clear = false; - if (!cd || !cd->type || !name || !iname || !(sdmd->flags & CRYPT_ACTIVATE_REFRESH)) + assert(cd); + assert(sdmd); + assert(sdmdi); + + if (!cd->type || !name || !iname || !(sdmd->flags & CRYPT_ACTIVATE_REFRESH)) return -EINVAL; + src = &sdmd->segment; + srci = &sdmdi->segment; + r = dm_query_device(cd, name, DM_ACTIVE_DEVICE | DM_ACTIVE_CRYPT_CIPHER | DM_ACTIVE_UUID | DM_ACTIVE_CRYPT_KEYSIZE | DM_ACTIVE_CRYPT_KEY, &tdmd); @@ -3439,11 +3626,10 @@ static int _reload_device_with_integrity(struct crypt_device *cd, } /* unsupported underneath dm-crypt with auth. encryption */ - if (sdmdi->segment.u.integrity.meta_device || tdmdi.segment.u.integrity.meta_device) - return -ENOTSUP; - - src = &sdmd->segment; - srci = &sdmdi->segment; + if (sdmdi->segment.u.integrity.meta_device || tdmdi.segment.u.integrity.meta_device) { + r = -ENOTSUP; + goto out; + } r = device_alloc(cd, &data_device, ipath); if (r < 0) @@ -3473,18 +3659,13 @@ static int _reload_device_with_integrity(struct crypt_device *cd, else sdmdi->flags &= ~CRYPT_ACTIVATE_READONLY; - if (sdmd->flags & CRYPT_ACTIVATE_KEYRING_KEY) { - r = crypt_volume_key_set_description(tgt->u.crypt.vk, src->u.crypt.vk->key_description); - if (r) - goto out; - } else { - crypt_free_volume_key(tgt->u.crypt.vk); - tgt->u.crypt.vk = crypt_alloc_volume_key(src->u.crypt.vk->keylength, src->u.crypt.vk->key); - if (!tgt->u.crypt.vk) { - r = -ENOMEM; - goto out; - } - } + /* + * To make it easier pass src key directly after + * it was properly verified in crypt_compare_dm_devices + * call above. + */ + crypt_free_volume_key(tgt->u.crypt.vk); + tgt->u.crypt.vk = src->u.crypt.vk; r = device_block_adjust(cd, src->data_device, DEV_OK, src->u.crypt.offset, &sdmd->size, NULL); @@ -3550,6 +3731,9 @@ static int _reload_device_with_integrity(struct crypt_device *cd, dm_resume_device(cd, iname, 0); } + /* otherwise dm_targets_free would free src key */ + if (tgt->u.crypt.vk == src->u.crypt.vk) + tgt->u.crypt.vk = NULL; dm_targets_free(cd, &tdmd); dm_targets_free(cd, &tdmdi); free(CONST_CAST(void*)tdmdi.uuid); @@ -3564,7 +3748,7 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) struct crypt_dm_active_device dmdq, dmd = {}; struct dm_target *tgt = &dmdq.segment; struct crypt_params_integrity params = {}; - uint32_t supported_flags = 0, dmflags = 0; + uint64_t supported_flags = 0, dmflags = 0; uint64_t old_size; int r; @@ -3582,6 +3766,11 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) return -EINVAL; } + if (isLUKS2(cd->type) && crypt_get_integrity_tag_size(cd)) { + log_err(cd, _("Resize of LUKS2 device with integrity protection is not supported.")); + return -ENOTSUP; + } + if (new_size) log_dbg(cd, "Resizing device %s to %" PRIu64 " sectors.", name, new_size); else @@ -3606,13 +3795,14 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) } if (crypt_key_in_keyring(cd)) { - if (!isLUKS2(cd->type)) { + if (isLUKS2(cd->type)) + r = LUKS2_key_description_by_segment(cd, &cd->u.luks2.hdr, + tgt->u.crypt.vk, CRYPT_DEFAULT_SEGMENT); + else if (isPLAIN(cd->type)) + r = 0; /* key description was set on table load */ + else r = -EINVAL; - goto out; - } - r = LUKS2_key_description_by_segment(cd, &cd->u.luks2.hdr, - tgt->u.crypt.vk, CRYPT_DEFAULT_SEGMENT); - if (r) + if (r < 0) goto out; dmdq.flags |= CRYPT_ACTIVATE_KEYRING_KEY; @@ -3695,7 +3885,7 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) r = dm_crypt_target_set(&dmd.segment, 0, new_size, crypt_data_device(cd), tgt->u.crypt.vk, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd), crypt_get_data_offset(cd), - crypt_get_integrity(cd), crypt_get_integrity_tag_size(cd), + crypt_get_integrity(cd), crypt_get_integrity_key_size(cd, true), crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd)); if (r < 0) goto out; @@ -3927,24 +4117,6 @@ void crypt_free(struct crypt_device *cd) free(cd); } -static char *crypt_get_device_key_description(struct crypt_device *cd, const char *name) -{ - char *desc = NULL; - struct crypt_dm_active_device dmd; - struct dm_target *tgt = &dmd.segment; - - if (dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE, &dmd) < 0) - return NULL; - - if (single_segment(&dmd) && tgt->type == DM_CRYPT && - (dmd.flags & CRYPT_ACTIVATE_KEYRING_KEY) && tgt->u.crypt.vk->key_description) - desc = strdup(tgt->u.crypt.vk->key_description); - - dm_targets_free(cd, &dmd); - - return desc; -} - int crypt_suspend(struct crypt_device *cd, const char *name) { @@ -3952,9 +4124,10 @@ int crypt_suspend(struct crypt_device *cd, crypt_status_info ci; int r; struct crypt_dm_active_device dmd, dmdi = {}; - uint32_t opal_segment_number = 1, dmflags = DM_SUSPEND_WIPE_KEY; + uint32_t opal_segment_number = 1; + uint64_t dmflags = DM_SUSPEND_WIPE_KEY; struct dm_target *tgt = &dmd.segment; - char *key_desc = NULL, *iname = NULL; + char *iname = NULL; struct crypt_lock_handle *opal_lh = NULL; if (!cd || !name) @@ -3971,15 +4144,17 @@ int crypt_suspend(struct crypt_device *cd, return -EINVAL; } - r = dm_query_device(cd, name, DM_ACTIVE_UUID, &dmd); + r = dm_query_device(cd, name, + DM_ACTIVE_UUID | DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE, + &dmd); if (r < 0) return r; log_dbg(cd, "Checking if active device %s has UUID type LUKS.", name); - r = crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2); + r = dm_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2); if (r < 0) - r = crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS1); + r = dm_uuid_type_cmp(dmd.uuid, CRYPT_LUKS1); if (r < 0) { log_err(cd, _("This operation is supported only for LUKS device.")); @@ -3988,45 +4163,32 @@ int crypt_suspend(struct crypt_device *cd, r = -EINVAL; - if (isLUKS2(cd->type) && crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2)) { + if (isLUKS2(cd->type) && dm_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2)) { log_dbg(cd, "LUKS device header type: %s mismatches DM device type.", cd->type); goto out; } - if (isLUKS1(cd->type) && crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS1)) { + if (isLUKS1(cd->type) && dm_uuid_type_cmp(dmd.uuid, CRYPT_LUKS1)) { log_dbg(cd, "LUKS device header type: %s mismatches DM device type.", cd->type); goto out; } /* check if active device has LUKS2-OPAL dm uuid prefix */ - dm_opal_uuid = !crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2_HW_OPAL); + dm_opal_uuid = !dm_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2_HW_OPAL); if (!dm_opal_uuid && isLUKS2(cd->type) && LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)) goto out; - if (cd->type && (r = crypt_uuid_cmp(dmd.uuid, LUKS_UUID(cd))) < 0) { + if (cd->type && (r = dm_uuid_cmp(dmd.uuid, LUKS_UUID(cd))) < 0) { log_dbg(cd, "LUKS device header uuid: %s mismatches DM returned uuid %s", LUKS_UUID(cd), dmd.uuid); goto out; } /* check UUID of integrity device underneath crypt device */ - if (crypt_get_integrity_tag_size(cd)) { - r = dm_get_iname(name, &iname, false); - if (r) - goto out; - - r = dm_query_device(cd, iname, DM_ACTIVE_UUID, &dmdi); - if (r < 0) - goto out; - - r = crypt_uuid_integrity_cmp(dmd.uuid, dmdi.uuid); - if (r < 0) { - log_dbg(cd, "Integrity device uuid: %s mismatches crypt device uuid %s", dmdi.uuid, dmd.uuid); - goto out; - } - } + if (crypt_get_integrity_tag_size(cd)) + iname = dm_get_active_iname(cd, name); r = dm_status_suspended(cd, name); if (r < 0) @@ -4038,8 +4200,6 @@ int crypt_suspend(struct crypt_device *cd, goto out; } - key_desc = crypt_get_device_key_description(cd, name); - if (dm_opal_uuid && crypt_data_device(cd)) { if (isLUKS2(cd->type)) { r = LUKS2_get_opal_segment_number(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, &opal_segment_number); @@ -4068,13 +4228,14 @@ int crypt_suspend(struct crypt_device *cd, } /* Suspend integrity device underneath; keep crypt suspended if it fails */ - if (crypt_get_integrity_tag_size(cd)) { + if (iname) { r = dm_suspend_device(cd, iname, 0); if (r) log_err(cd, _("Error during suspending device %s."), iname); } - crypt_drop_keyring_key_by_description(cd, key_desc, cd->keyring_key_type); + if (single_segment(&dmd) && tgt->type == DM_CRYPT) + crypt_volume_key_drop_kernel_key(cd, tgt->u.crypt.vk); if (dm_opal_uuid && crypt_data_device(cd)) { r = opal_exclusive_lock(cd, crypt_data_device(cd), &opal_lh); @@ -4088,7 +4249,6 @@ int crypt_suspend(struct crypt_device *cd, log_err(cd, _("Device %s was suspended but hardware OPAL device cannot be locked."), name); out: opal_exclusive_unlock(cd, opal_lh); - free(key_desc); free(iname); dm_targets_free(cd, &dmd); dm_targets_free(cd, &dmdi); @@ -4141,7 +4301,9 @@ static void crypt_unlink_key_from_custom_keyring(struct crypt_device *cd, key_se log_err(cd, _("Failed to unlink volume key from user specified keyring.")); } -static key_serial_t crypt_single_volume_key_load_in_user_keyring(struct crypt_device *cd, struct volume_key *vk, const char *user_key_name) +static key_serial_t crypt_single_volume_key_load_in_custom_keyring(struct crypt_device *cd, + struct volume_key *vk, + const char *user_key_name) { key_serial_t kid; const char *type_name; @@ -4155,15 +4317,20 @@ static key_serial_t crypt_single_volume_key_load_in_user_keyring(struct crypt_de log_dbg(cd, "Linking volume key (type %s, name %s) to the specified keyring", type_name, user_key_name); - kid = keyring_add_key_to_custom_keyring(cd->keyring_key_type, user_key_name, vk->key, vk->keylength, cd->keyring_to_link_vk); - if (kid <= 0) { - log_dbg(cd, "The keyring_link_key_to_keyring function failed (error %d).", errno); - } + kid = keyring_add_key_to_keyring(cd->keyring_key_type, user_key_name, + crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk), + cd->keyring_to_link_vk); + if (kid <= 0) + log_dbg(cd, "The keyring_add_key_to_keyring function failed (error %d).", errno); return kid; } -static int crypt_volume_key_load_in_user_keyring(struct crypt_device *cd, struct volume_key *vk, key_serial_t *kid1_out, key_serial_t *kid2_out) +static int crypt_volume_key_load_in_custom_keyring(struct crypt_device *cd, + struct volume_key *vk, + key_serial_t *kid1_out, + key_serial_t *kid2_out) { key_serial_t kid1, kid2 = 0; @@ -4174,14 +4341,14 @@ static int crypt_volume_key_load_in_user_keyring(struct crypt_device *cd, struct if (!vk || !key_type_name(cd->keyring_key_type)) return -EINVAL; - kid1 = crypt_single_volume_key_load_in_user_keyring(cd, vk, cd->user_key_name1); + kid1 = crypt_single_volume_key_load_in_custom_keyring(cd, vk, cd->user_key_name1); if (kid1 <= 0) return -EINVAL; - vk = vk->next; + vk = crypt_volume_key_next(vk); if (vk) { assert(cd->user_key_name2); - kid2 = crypt_single_volume_key_load_in_user_keyring(cd, vk, cd->user_key_name2); + kid2 = crypt_single_volume_key_load_in_custom_keyring(cd, vk, cd->user_key_name2); if (kid2 <= 0) { crypt_unlink_key_from_custom_keyring(cd, kid1); return -EINVAL; @@ -4251,7 +4418,7 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd, /* upload volume key in custom keyring if requested */ if (cd->link_vk_to_keyring) { - r = crypt_volume_key_load_in_user_keyring(cd, vk, &kid1, &kid2); + r = crypt_volume_key_load_in_custom_keyring(cd, vk, &kid1, &kid2); if (r < 0) { log_err(cd, _("Failed to link volume key in user defined keyring.")); goto out; @@ -4273,14 +4440,12 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd, } } - if (crypt_get_integrity_tag_size(cd)) { - r = dm_get_iname(name, &iname, false); - if (r) - goto out; - + if (crypt_get_integrity_tag_size(cd) && + (iname = dm_get_active_iname(cd, name))) { r = dm_resume_device(cd, iname, 0); if (r) log_err(cd, _("Error during resuming device %s."), iname); + free(iname); } if (enc_type == CRYPT_OPAL_HW_ONLY) @@ -4295,7 +4460,7 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd, out: if (r < 0) { - crypt_drop_keyring_key(cd, p_crypt); + crypt_drop_uploaded_keyring_key(cd, p_crypt); if (cd->link_vk_to_keyring && kid1) crypt_unlink_key_from_custom_keyring(cd, kid1); if (cd->link_vk_to_keyring && kid2) @@ -4309,7 +4474,6 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd, crypt_free_volume_key(zerokey); crypt_free_volume_key(opal_key); crypt_free_volume_key(crypt_key); - free(iname); return r; } @@ -4395,9 +4559,9 @@ int crypt_resume_by_passphrase(struct crypt_device *cd, size_t passphrase_size) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; - crypt_keyslot_unlock_by_passphrase_init_internal(&kc, passphrase, passphrase_size); + crypt_keyslot_context_init_by_passphrase_internal(&kc, passphrase, passphrase_size); r = crypt_resume_by_keyslot_context(cd, name, keyslot, &kc); crypt_keyslot_context_destroy_internal(&kc); @@ -4412,9 +4576,9 @@ int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd, uint64_t keyfile_offset) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; - crypt_keyslot_unlock_by_keyfile_init_internal(&kc, keyfile, keyfile_size, keyfile_offset); + crypt_keyslot_context_init_by_keyfile_internal(&kc, keyfile, keyfile_size, keyfile_offset); r = crypt_resume_by_keyslot_context(cd, name, keyslot, &kc); crypt_keyslot_context_destroy_internal(&kc); @@ -4448,9 +4612,9 @@ int crypt_resume_by_volume_key(struct crypt_device *cd, size_t volume_key_size) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; - crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size); + crypt_keyslot_context_init_by_key_internal(&kc, volume_key, volume_key_size); r = crypt_resume_by_keyslot_context(cd, name, CRYPT_ANY_SLOT /* unused */, &kc); crypt_keyslot_context_destroy_internal(&kc); @@ -4465,9 +4629,9 @@ int crypt_resume_by_token_pin(struct crypt_device *cd, const char *name, void *usrptr) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; - crypt_keyslot_unlock_by_token_init_internal(&kc, token, type, pin, pin_size, usrptr); + crypt_keyslot_context_init_by_token_internal(&kc, token, type, pin, pin_size, usrptr); r = crypt_resume_by_keyslot_context(cd, name, CRYPT_ANY_SLOT, &kc); crypt_keyslot_context_destroy_internal(&kc); @@ -4485,13 +4649,13 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, size_t new_passphrase_size) { int r; - struct crypt_keyslot_context kc, new_kc; + struct crypt_keyslot_context kc = {}, new_kc = {}; if (!passphrase || !new_passphrase) return -EINVAL; - crypt_keyslot_unlock_by_passphrase_init_internal(&kc, passphrase, passphrase_size); - crypt_keyslot_unlock_by_passphrase_init_internal(&new_kc, new_passphrase, new_passphrase_size); + crypt_keyslot_context_init_by_passphrase_internal(&kc, passphrase, passphrase_size); + crypt_keyslot_context_init_by_passphrase_internal(&new_kc, new_passphrase, new_passphrase_size); r = crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, &kc, keyslot, &new_kc, 0); @@ -4545,21 +4709,21 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd, } keyslot_old = r; - if (isLUKS2(cd->type)) { + if (isLUKS1(cd->type)) { + if (keyslot_new == CRYPT_ANY_SLOT) { + keyslot_new = LUKS_keyslot_find_empty(&cd->u.luks1.hdr); + if (keyslot_new < 0) + keyslot_new = keyslot_old; + } + } else if (isLUKS2(cd->type)) { /* If there is a free keyslot (both id and binary area) avoid in-place keyslot area overwrite */ if (keyslot_new == CRYPT_ANY_SLOT || keyslot_new == keyslot_old) { - keyslot_new = LUKS2_keyslot_find_empty(cd, &cd->u.luks2.hdr, vk->keylength); + keyslot_new = LUKS2_keyslot_find_empty(cd, &cd->u.luks2.hdr, crypt_volume_key_length(vk)); if (keyslot_new < 0) keyslot_new = keyslot_old; else keyslot_swap = true; } - } else if (isLUKS1(cd->type)) { - if (keyslot_new == CRYPT_ANY_SLOT) { - keyslot_new = LUKS_keyslot_find_empty(&cd->u.luks1.hdr); - if (keyslot_new < 0) - keyslot_new = keyslot_old; - } } log_dbg(cd, "Key change, old slot %d, new slot %d.", keyslot_old, keyslot_new); @@ -4629,13 +4793,13 @@ int crypt_keyslot_add_by_keyfile_device_offset(struct crypt_device *cd, uint64_t new_keyfile_offset) { int r; - struct crypt_keyslot_context kc, new_kc; + struct crypt_keyslot_context kc = {}, new_kc = {}; if (!keyfile || !new_keyfile) return -EINVAL; - crypt_keyslot_unlock_by_keyfile_init_internal(&kc, keyfile, keyfile_size, keyfile_offset); - crypt_keyslot_unlock_by_keyfile_init_internal(&new_kc, new_keyfile, new_keyfile_size, new_keyfile_offset); + crypt_keyslot_context_init_by_keyfile_internal(&kc, keyfile, keyfile_size, keyfile_offset); + crypt_keyslot_context_init_by_keyfile_internal(&new_kc, new_keyfile, new_keyfile_size, new_keyfile_offset); r = crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, &kc, keyslot, &new_kc, 0); @@ -4679,13 +4843,13 @@ int crypt_keyslot_add_by_volume_key(struct crypt_device *cd, size_t passphrase_size) { int r; - struct crypt_keyslot_context kc, new_kc; + struct crypt_keyslot_context kc = {}, new_kc = {}; if (!passphrase) return -EINVAL; - crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size); - crypt_keyslot_unlock_by_passphrase_init_internal(&new_kc, passphrase, passphrase_size); + crypt_keyslot_context_init_by_key_internal(&kc, volume_key, volume_key_size); + crypt_keyslot_context_init_by_passphrase_internal(&new_kc, passphrase, passphrase_size); r = crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, &kc, keyslot, &new_kc, 0); @@ -4786,7 +4950,7 @@ static int _create_device_with_integrity(struct crypt_device *cd, device_check = dmd->flags & CRYPT_ACTIVATE_SHARED ? DEV_OK : DEV_EXCL; - r = INTEGRITY_activate_dmd_device(cd, iname, CRYPT_INTEGRITY, dmdi, 0); + r = INTEGRITY_activate_dmd_device(cd, iname, CRYPT_SUBDEV, dmdi, 0); if (r) return r; @@ -4835,8 +4999,7 @@ int create_or_reload_device(struct crypt_device *cd, const char *name, int r; enum devcheck device_check; struct dm_target *tgt; - uint64_t offset; - uint32_t dmflags = 0; + uint64_t offset, dmflags = 0; if (!type || !name || !single_segment(dmd)) return -EINVAL; @@ -4919,86 +5082,6 @@ int create_or_reload_device_with_integrity(struct crypt_device *cd, const char * return r; } -static int _open_and_activate(struct crypt_device *cd, - int keyslot, - const char *name, - const char *passphrase, - size_t passphrase_size, - uint32_t flags) -{ - bool use_keyring; - int r; - struct volume_key *p_crypt = NULL, *p_opal = NULL, *crypt_key = NULL, *opal_key = NULL, *vk = NULL; - key_serial_t kid1 = 0, kid2 = 0; - - r = LUKS2_keyslot_open(cd, keyslot, - (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) ? - CRYPT_ANY_SEGMENT : CRYPT_DEFAULT_SEGMENT, - passphrase, passphrase_size, &vk); - if (r < 0) - return r; - keyslot = r; - - /* split the key only if we do activation */ - if (name && LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)) { - r = LUKS2_split_crypt_and_opal_keys(cd, &cd->u.luks2.hdr, - vk, &crypt_key, - &opal_key); - if (r < 0) - goto out; - - /* copy volume key digest id in crypt subkey */ - crypt_volume_key_set_id(crypt_key, crypt_volume_key_get_id(vk)); - - p_crypt = crypt_key; - p_opal = opal_key ?: vk; - } else - p_crypt = vk; - - if (!crypt_use_keyring_for_vk(cd)) - use_keyring = false; - else - use_keyring = ((name && !crypt_is_cipher_null(crypt_get_cipher(cd))) || - (flags & CRYPT_ACTIVATE_KEYRING_KEY)); - - if (use_keyring) { - /* upload dm-crypt part of volume key in thread keyring if requested */ - if (p_crypt) { - r = LUKS2_volume_key_load_in_keyring_by_digest(cd, p_crypt, - crypt_volume_key_get_id(p_crypt)); - if (r < 0) - goto out; - flags |= CRYPT_ACTIVATE_KEYRING_KEY; - } - - /* upload the volume key in custom user keyring if requested */ - if (cd->link_vk_to_keyring) { - r = crypt_volume_key_load_in_user_keyring(cd, vk, &kid1, &kid2); - if (r < 0) { - log_err(cd, _("Failed to link volume key in user defined keyring.")); - goto out; - } - } - } - - if (name) - r = LUKS2_activate(cd, name, p_crypt, p_opal, flags); -out: - if (r < 0) { - crypt_drop_keyring_key(cd, p_crypt); - if (cd->link_vk_to_keyring && kid1) - crypt_unlink_key_from_custom_keyring(cd, kid1); - if (cd->link_vk_to_keyring && kid2) - crypt_unlink_key_from_custom_keyring(cd, kid2); - } - crypt_free_volume_key(vk); - crypt_free_volume_key(crypt_key); - crypt_free_volume_key(opal_key); - - return r < 0 ? r : keyslot; -} - -#if USE_LUKS2_REENCRYPTION static int load_all_keys(struct crypt_device *cd, struct volume_key *vks) { int r; @@ -5014,55 +5097,8 @@ static int load_all_keys(struct crypt_device *cd, struct volume_key *vks) return 0; } -static int _open_all_keys(struct crypt_device *cd, - struct luks2_hdr *hdr, - int keyslot, - const char *passphrase, - size_t passphrase_size, - uint32_t flags, - struct volume_key **vks) -{ - int r, segment; - struct volume_key *_vks = NULL; - crypt_reencrypt_info ri = LUKS2_reencrypt_status(hdr); - - segment = (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) ? CRYPT_ANY_SEGMENT : CRYPT_DEFAULT_SEGMENT; - - switch (ri) { - case CRYPT_REENCRYPT_NONE: - r = LUKS2_keyslot_open(cd, keyslot, segment, passphrase, passphrase_size, &_vks); - break; - case CRYPT_REENCRYPT_CLEAN: - case CRYPT_REENCRYPT_CRASH: - if (segment == CRYPT_ANY_SEGMENT) - r = LUKS2_keyslot_open(cd, keyslot, segment, passphrase, - passphrase_size, &_vks); - else - r = LUKS2_keyslot_open_all_segments(cd, keyslot, - keyslot, passphrase, passphrase_size, - &_vks); - break; - default: - r = -EINVAL; - } - - if (keyslot == CRYPT_ANY_SLOT) - keyslot = r; - - if (r >= 0 && (flags & CRYPT_ACTIVATE_KEYRING_KEY)) - r = load_all_keys(cd, _vks); - - if (r >= 0 && vks) - MOVE_REF(*vks, _vks); - - if (r < 0) - crypt_drop_keyring_key(cd, _vks); - crypt_free_volume_key(_vks); - - return r < 0 ? r : keyslot; -} - -static int _open_and_activate_reencrypt_device_by_vk(struct crypt_device *cd, +#if USE_LUKS2_REENCRYPTION +static int _activate_reencrypt_device_by_vk(struct crypt_device *cd, struct luks2_hdr *hdr, const char *name, struct volume_key *vks, @@ -5073,14 +5109,10 @@ static int _open_and_activate_reencrypt_device_by_vk(struct crypt_device *cd, uint64_t minimal_size, device_size; int r = 0; struct crypt_lock_handle *reencrypt_lock = NULL; - key_serial_t kid1 = 0, kid2 = 0; struct volume_key *vk; - if (!vks) - return -EINVAL; - - if (crypt_use_keyring_for_vk(cd)) - flags |= CRYPT_ACTIVATE_KEYRING_KEY; + assert(hdr); + assert(vks); r = LUKS2_reencrypt_lock(cd, &reencrypt_lock); if (r) { @@ -5095,17 +5127,29 @@ static int _open_and_activate_reencrypt_device_by_vk(struct crypt_device *cd, goto out; ri = LUKS2_reencrypt_status(hdr); + if (ri == CRYPT_REENCRYPT_INVALID) { + r = -EINVAL; + goto out; + } - if (ri == CRYPT_REENCRYPT_CRASH) { - r = LUKS2_reencrypt_locked_recovery_by_vks(cd, vks); - if (r < 0) { - log_err(cd, _("LUKS2 reencryption recovery using volume key(s) failed.")); + if (ri > CRYPT_REENCRYPT_NONE) { + /* it's sufficient to force re-verify the reencrypt digest only */ + r = LUKS2_reencrypt_digest_verify(cd, &cd->u.luks2.hdr, vks); + if (r < 0) goto out; - } - ri = LUKS2_reencrypt_status(hdr); + if (ri == CRYPT_REENCRYPT_CRASH) { + r = LUKS2_reencrypt_locked_recovery_by_vks(cd, vks); + if (r < 0) { + log_err(cd, _("LUKS2 reencryption recovery using volume key(s) failed.")); + goto out; + } + + ri = LUKS2_reencrypt_status(hdr); + } } - /* recovery finished reencryption or it's already finished */ + + /* recovery finished reencryption or it was already finished after metadata reload */ if (ri == CRYPT_REENCRYPT_NONE) { vk = crypt_volume_key_by_id(vks, LUKS2_digest_by_segment(hdr, CRYPT_DEFAULT_SEGMENT)); if (!vk) { @@ -5114,13 +5158,6 @@ static int _open_and_activate_reencrypt_device_by_vk(struct crypt_device *cd, } r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk); - if (r == -EPERM || r == -ENOENT) - log_err(cd, _("Volume key does not match the volume.")); - if (r >= 0 && cd->link_vk_to_keyring) { - kid1 = crypt_single_volume_key_load_in_user_keyring(cd, vk, cd->user_key_name1); - if (kid1 <= 0) - r = -EINVAL; - } if (r >= 0) r = LUKS2_activate(cd, name, vk, NULL, flags); goto out; @@ -5130,185 +5167,26 @@ static int _open_and_activate_reencrypt_device_by_vk(struct crypt_device *cd, goto out; } - if ((flags & CRYPT_ACTIVATE_KEYRING_KEY)) { - r = load_all_keys(cd, vks); - if (r < 0) - goto out; - } - if ((r = LUKS2_get_data_size(hdr, &minimal_size, &dynamic_size))) goto out; - r = LUKS2_reencrypt_digest_verify(cd, hdr, vks); - if (r < 0) - goto out; - log_dbg(cd, "Entering clean reencryption state mode."); - r = LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size, true, dynamic_size); + r = LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size, + !(flags & CRYPT_ACTIVATE_SHARED), + dynamic_size); if (r < 0) goto out; - if (cd->link_vk_to_keyring) { - r = crypt_volume_key_load_in_user_keyring(cd, vks, &kid1, &kid2); - if (r < 0) { - log_err(cd, _("Failed to link volume keys in user defined keyring.")); - goto out; - } - } r = LUKS2_activate_multi(cd, name, vks, device_size >> SECTOR_SHIFT, flags); out: LUKS2_reencrypt_unlock(cd, reencrypt_lock); - crypt_drop_keyring_key(cd, vks); return r; } -static int _open_and_activate_reencrypt_device(struct crypt_device *cd, - struct luks2_hdr *hdr, - int keyslot, - const char *name, - const char *passphrase, - size_t passphrase_size, - uint32_t flags) -{ - bool dynamic_size; - crypt_reencrypt_info ri; - uint64_t minimal_size, device_size; - struct volume_key *vks = NULL; - int r = 0; - struct crypt_lock_handle *reencrypt_lock = NULL; - key_serial_t kid1 = 0, kid2 = 0; - - if (crypt_use_keyring_for_vk(cd)) - flags |= CRYPT_ACTIVATE_KEYRING_KEY; - - r = LUKS2_reencrypt_lock(cd, &reencrypt_lock); - if (r) { - if (r == -EBUSY) - log_err(cd, _("Reencryption in-progress. Cannot activate device.")); - else - log_err(cd, _("Failed to get reencryption lock.")); - return r; - } - - if ((r = crypt_load(cd, CRYPT_LUKS2, NULL))) - goto out; - - ri = LUKS2_reencrypt_status(hdr); - - if (ri == CRYPT_REENCRYPT_CRASH) { - r = LUKS2_reencrypt_locked_recovery_by_passphrase(cd, keyslot, - keyslot, passphrase, passphrase_size, &vks); - if (r < 0) { - log_err(cd, _("LUKS2 reencryption recovery failed.")); - goto out; - } - keyslot = r; - - ri = LUKS2_reencrypt_status(hdr); - } - - /* recovery finished reencryption or it's already finished */ - if (ri == CRYPT_REENCRYPT_NONE) { - crypt_drop_keyring_key(cd, vks); - crypt_free_volume_key(vks); - LUKS2_reencrypt_unlock(cd, reencrypt_lock); - return _open_and_activate(cd, keyslot, name, passphrase, passphrase_size, flags); - } - - if (ri > CRYPT_REENCRYPT_CLEAN) { - r = -EINVAL; - goto out; - } - - if (LUKS2_get_data_size(hdr, &minimal_size, &dynamic_size)) - goto out; - - if (!vks) { - r = _open_all_keys(cd, hdr, keyslot, passphrase, passphrase_size, flags, &vks); - if (r >= 0) - keyslot = r; - } - - if (r >= 0) { - r = LUKS2_reencrypt_digest_verify(cd, hdr, vks); - if (r < 0) - goto out; - } - - log_dbg(cd, "Entering clean reencryption state mode."); - - if (cd->link_vk_to_keyring) { - r = crypt_volume_key_load_in_user_keyring(cd, vks, &kid1, &kid2); - if (r < 0) { - log_err(cd, _("Failed to link volume keys in user defined keyring.")); - goto out; - } - } - - if (r >= 0) - r = LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size, - !(flags & CRYPT_ACTIVATE_SHARED), - dynamic_size); - - if (r >= 0) - r = LUKS2_activate_multi(cd, name, vks, device_size >> SECTOR_SHIFT, flags); -out: - LUKS2_reencrypt_unlock(cd, reencrypt_lock); - if (r < 0) { - crypt_drop_keyring_key(cd, vks); - if (cd->link_vk_to_keyring && kid1) - crypt_unlink_key_from_custom_keyring(cd, kid1); - if (cd->link_vk_to_keyring && kid2) - crypt_unlink_key_from_custom_keyring(cd, kid2); - } - - crypt_free_volume_key(vks); - - return r < 0 ? r : keyslot; -} - /* * Activation/deactivation of a device */ -static int _open_and_activate_luks2(struct crypt_device *cd, - int keyslot, - const char *name, - const char *passphrase, - size_t passphrase_size, - uint32_t flags) -{ - crypt_reencrypt_info ri; - int r, rv; - struct luks2_hdr *hdr = &cd->u.luks2.hdr; - struct volume_key *vks = NULL; - - ri = LUKS2_reencrypt_status(hdr); - if (ri == CRYPT_REENCRYPT_INVALID) - return -EINVAL; - - if (ri > CRYPT_REENCRYPT_NONE) { - if (name) - r = _open_and_activate_reencrypt_device(cd, hdr, keyslot, name, passphrase, - passphrase_size, flags); - else { - r = _open_all_keys(cd, hdr, keyslot, passphrase, - passphrase_size, flags, &vks); - if (r < 0) - return r; - - rv = LUKS2_reencrypt_digest_verify(cd, hdr, vks); - crypt_free_volume_key(vks); - if (rv < 0) - return rv; - } - } else - r = _open_and_activate(cd, keyslot, name, passphrase, - passphrase_size, flags); - - return r; -} - static int _activate_luks2_by_volume_key(struct crypt_device *cd, const char *name, struct volume_key *vk, @@ -5317,27 +5195,13 @@ static int _activate_luks2_by_volume_key(struct crypt_device *cd, { int r; crypt_reencrypt_info ri; - int digest_new, digest_old; - struct volume_key *vk_old = NULL, *vk_new = NULL; ri = LUKS2_reencrypt_status(&cd->u.luks2.hdr); if (ri == CRYPT_REENCRYPT_INVALID) return -EINVAL; if (ri > CRYPT_REENCRYPT_NONE) { - digest_new = LUKS2_reencrypt_digest_new(&cd->u.luks2.hdr); - digest_old = LUKS2_reencrypt_digest_old(&cd->u.luks2.hdr); - - if (digest_new >= 0) { - vk_new = crypt_volume_key_by_id(vk, digest_new); - assert(vk_new); - assert(crypt_volume_key_get_id(vk_new) == digest_new); - } - if (digest_old >= 0) { - vk_old = crypt_volume_key_by_id(vk, digest_old); - assert(vk_old); - assert(crypt_volume_key_get_id(vk_old) == digest_old); - } - r = _open_and_activate_reencrypt_device_by_vk(cd, &cd->u.luks2.hdr, name, vk, flags); + /* reencryption must reverify keys after taking the reencryption lock and reloading metadata */ + r = _activate_reencrypt_device_by_vk(cd, &cd->u.luks2.hdr, name, vk, flags); } else { /* hw-opal data segment type does not require volume key for activation */ assert(!vk || crypt_volume_key_get_id(vk) == LUKS2_digest_by_segment(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)); @@ -5347,27 +5211,6 @@ static int _activate_luks2_by_volume_key(struct crypt_device *cd, return r; } #else -static int _open_and_activate_luks2(struct crypt_device *cd, - int keyslot, - const char *name, - const char *passphrase, - size_t passphrase_size, - uint32_t flags) -{ - crypt_reencrypt_info ri; - - ri = LUKS2_reencrypt_status(&cd->u.luks2.hdr); - if (ri == CRYPT_REENCRYPT_INVALID) - return -EINVAL; - - if (ri > CRYPT_REENCRYPT_NONE) { - log_err(cd, _("This operation is not supported for this device type.")); - return -ENOTSUP; - } - - return _open_and_activate(cd, keyslot, name, passphrase, passphrase_size, flags); -} - static int _activate_luks2_by_volume_key(struct crypt_device *cd, const char *name, struct volume_key *vk, @@ -5392,76 +5235,6 @@ static int _activate_luks2_by_volume_key(struct crypt_device *cd, } #endif -static int _activate_by_passphrase(struct crypt_device *cd, - const char *name, - int keyslot, - const char *passphrase, - size_t passphrase_size, - uint32_t flags) -{ - int r; - struct volume_key *vk = NULL; - - if ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd)) - return -EINVAL; - - if ((flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) && name) - return -EINVAL; - - r = _check_header_data_overlap(cd, name); - if (r < 0) - return r; - - if (flags & CRYPT_ACTIVATE_SERIALIZE_MEMORY_HARD_PBKDF) - cd->memory_hard_pbkdf_lock_enabled = true; - - /* plain, use hashed passphrase */ - if (isPLAIN(cd->type)) { - r = -EINVAL; - if (!name) - goto out; - - r = process_key(cd, cd->u.plain.hdr.hash, - cd->u.plain.key_size, - passphrase, passphrase_size, &vk); - if (r < 0) - goto out; - - r = PLAIN_activate(cd, name, vk, cd->u.plain.hdr.size, flags); - keyslot = 0; - } else if (isLUKS1(cd->type)) { - r = LUKS_open_key_with_hdr(keyslot, passphrase, - passphrase_size, &cd->u.luks1.hdr, &vk, cd); - if (r >= 0) { - keyslot = r; - if (name) - r = LUKS1_activate(cd, name, vk, flags); - } - } else if (isLUKS2(cd->type)) { - r = _open_and_activate_luks2(cd, keyslot, name, passphrase, passphrase_size, flags); - keyslot = r; - } else if (isBITLK(cd->type)) { - r = BITLK_activate_by_passphrase(cd, name, passphrase, passphrase_size, - &cd->u.bitlk.params, flags); - keyslot = 0; - } else if (isFVAULT2(cd->type)) { - r = FVAULT2_activate_by_passphrase(cd, name, passphrase, passphrase_size, - &cd->u.fvault2.params, flags); - keyslot = 0; - } else { - log_err(cd, _("Device type is not properly initialized.")); - r = -EINVAL; - } -out: - if (r < 0) - crypt_drop_keyring_key(cd, vk); - crypt_free_volume_key(vk); - - cd->memory_hard_pbkdf_lock_enabled = false; - - return r < 0 ? r : keyslot; -} - static int _activate_loopaes(struct crypt_device *cd, const char *name, const char *buffer, @@ -5476,7 +5249,7 @@ static int _activate_loopaes(struct crypt_device *cd, buffer_copy = crypt_safe_alloc(buffer_size); if (!buffer_copy) return -ENOMEM; - memcpy(buffer_copy, buffer, buffer_size); + crypt_safe_memcpy(buffer_copy, buffer, buffer_size); r = LOOPAES_parse_keyfile(cd, &vk, cd->u.loopaes.hdr.hash, &key_count, buffer_copy, buffer_size); @@ -5515,45 +5288,45 @@ static int _activate_check_status(struct crypt_device *cd, const char *name, uns return r; } +static int _verify_reencrypt_keys(struct crypt_device *cd, struct volume_key *vks) +{ + int r; + + assert(cd && (isLUKS2(cd->type))); + + r = LUKS2_reencrypt_digest_verify(cd, &cd->u.luks2.hdr, vks); + if (r == -EPERM || r == -ENOENT || r == -EINVAL) + log_err(cd, _("Reencryption volume keys do not match the volume.")); + + return r; +} + static int _verify_key(struct crypt_device *cd, - int segment, + bool unbound_key, struct volume_key *vk) { int r = -EINVAL; - crypt_reencrypt_info ri; - struct luks2_hdr *hdr = &cd->u.luks2.hdr; assert(cd); if (isPLAIN(cd->type)) { - if (vk && vk->keylength == cd->u.plain.key_size) { + if (vk && crypt_volume_key_length(vk) == cd->u.plain.key_size) { r = KEY_VERIFIED; } else log_err(cd, _("Incorrect volume key specified for plain device.")); } else if (isLUKS1(cd->type)) { + if (!vk) + return -EINVAL; + r = LUKS_verify_volume_key(&cd->u.luks1.hdr, vk); - if (r == -EPERM) - log_err(cd, _("Volume key does not match the volume.")); } else if (isLUKS2(cd->type)) { - ri = LUKS2_reencrypt_status(hdr); - if (ri == CRYPT_REENCRYPT_INVALID) + if (!vk) return -EINVAL; - if (ri > CRYPT_REENCRYPT_NONE) { - LUKS2_reencrypt_lookup_key_ids(cd, hdr, vk); - r = LUKS2_reencrypt_digest_verify(cd, hdr, vk); - if (r == -EPERM || r == -ENOENT || r == -EINVAL) - log_err(cd, _("Reencryption volume keys do not match the volume.")); - return r; - } - - if (segment == CRYPT_ANY_SEGMENT) - r = LUKS2_digest_any_matching(cd, &cd->u.luks2.hdr, vk); - else { - r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, segment, vk); - if (r == -EPERM || r == -ENOENT) - log_err(cd, _("Volume key does not match the volume.")); - } + if (unbound_key) + r = LUKS2_digest_verify_by_any_matching(cd, vk); + else + r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk); } else if (isVERITY(cd->type)) r = KEY_VERIFIED; else if (isTCRYPT(cd->type)) @@ -5562,7 +5335,10 @@ static int _verify_key(struct crypt_device *cd, r = KEY_VERIFIED; else if (isBITLK(cd->type)) r = KEY_VERIFIED; - else + else if (isFVAULT2(cd->type)) { + if (vk && crypt_volume_key_length(vk) == FVAULT2_volume_key_size()) + r = KEY_VERIFIED; + } else log_err(cd, _("Device type is not properly initialized.")); if (r >= KEY_VERIFIED) @@ -5617,8 +5393,12 @@ static int _activate_by_volume_key(struct crypt_device *cd, cd->u.integrity.sb_flags); } else if (isBITLK(cd->type)) { assert(!external_key); - r = BITLK_activate_by_volume_key(cd, name, vk->key, vk->keylength, - &cd->u.bitlk.params, flags); + assert(crypt_volume_key_get_id(vk) == KEY_VERIFIED); + r = BITLK_activate_by_volume_key(cd, name, vk, &cd->u.bitlk.params, flags); + } else if (isFVAULT2(cd->type)) { + assert(!external_key); + assert(crypt_volume_key_get_id(vk) == KEY_VERIFIED); + r = FVAULT2_activate_by_volume_key(cd, name, vk, &cd->u.fvault2.params, flags); } else { log_err(cd, _("Device type is not properly initialized.")); r = -EINVAL; @@ -5628,19 +5408,19 @@ static int _activate_by_volume_key(struct crypt_device *cd, } int crypt_activate_by_keyslot_context(struct crypt_device *cd, -const char *name, + const char *name, int keyslot, struct crypt_keyslot_context *kc, int additional_keyslot, struct crypt_keyslot_context *additional_kc, uint32_t flags) { - bool use_keyring; + bool use_keyring, luks2_reencryption = false; struct volume_key *p_ext_key, *crypt_key = NULL, *opal_key = NULL, *vk = NULL, *vk_sign = NULL, *p_crypt = NULL; size_t passphrase_size; const char *passphrase = NULL; - int unlocked_keyslot, required_keys, unlocked_keys = 0, r = -EINVAL; + int unlocked_keyslot, r = -EINVAL; key_serial_t kid1 = 0, kid2 = 0; struct luks2_hdr *hdr = &cd->u.luks2.hdr; @@ -5655,6 +5435,8 @@ const char *name, return -EINVAL; if ((flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) && name) return -EINVAL; + if (!additional_kc && (additional_keyslot != CRYPT_ANY_SLOT)) + return -EINVAL; if ((kc->type == CRYPT_KC_TYPE_KEYRING) && !kernel_keyring_support()) { log_err(cd, _("Kernel keyring is not supported by the kernel.")); return -EINVAL; @@ -5670,61 +5452,67 @@ const char *name, if (r < 0) return r; - /* for TCRYPT and token skip passphrase activation */ - if (kc->get_passphrase && kc->type != CRYPT_KC_TYPE_TOKEN && !isTCRYPT(cd->type)) { + if (kc->get_passphrase && kc->type != CRYPT_KC_TYPE_TOKEN && + isLOOPAES(cd->type)) { r = kc->get_passphrase(cd, kc, &passphrase, &passphrase_size); if (r < 0) return r; - /* TODO: Only loopaes should by activated by passphrase method */ - if (passphrase) { - if (isLOOPAES(cd->type)) - return _activate_loopaes(cd, name, passphrase, passphrase_size, flags); - else - return _activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags); - } + + return _activate_loopaes(cd, name, passphrase, passphrase_size, flags); } - /* only passphrase unlock is supported with loopaes */ - if (isLOOPAES(cd->type)) - return -EINVAL; - /* activate by volume key */ + if (flags & CRYPT_ACTIVATE_SERIALIZE_MEMORY_HARD_PBKDF) + cd->memory_hard_pbkdf_lock_enabled = true; + + /* acquire the volume key(s) */ r = -EINVAL; if (isLUKS1(cd->type)) { if (kc->get_luks1_volume_key) r = kc->get_luks1_volume_key(cd, kc, keyslot, &vk); } else if (isLUKS2(cd->type)) { - required_keys = LUKS2_reencrypt_vks_count(hdr); - - if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY && kc->get_luks2_key) - r = kc->get_luks2_key(cd, kc, keyslot, CRYPT_ANY_SEGMENT, &vk); - else if (kc->get_luks2_volume_key) - r = kc->get_luks2_volume_key(cd, kc, keyslot, &vk); - if (r >= 0) { - unlocked_keys++; - - if (required_keys > 1 && vk && additional_kc) { - if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY && additional_kc->get_luks2_key) - r = additional_kc->get_luks2_key(cd, additional_kc, additional_keyslot, CRYPT_ANY_SEGMENT, &vk->next); - else if (additional_kc->get_luks2_volume_key) - r = additional_kc->get_luks2_volume_key(cd, additional_kc, additional_keyslot, &vk->next); - if (r >= 0) - unlocked_keys++; + if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) { + if (kc->get_luks2_key) + r = kc->get_luks2_key(cd, kc, keyslot, CRYPT_ANY_SEGMENT, &vk); + } else { + switch (LUKS2_reencrypt_status(hdr)) { + case CRYPT_REENCRYPT_NONE: + if (kc->get_luks2_volume_key) + r = kc->get_luks2_volume_key(cd, kc, keyslot, &vk); + break; + case CRYPT_REENCRYPT_CLEAN: /* fall-through */ + case CRYPT_REENCRYPT_CRASH: + luks2_reencryption = true; + r = LUKS2_keyslot_context_open_all_segments(cd, keyslot, additional_keyslot, kc, additional_kc, &vk); + /* fall-through */ + default: + break; } - - if (unlocked_keys < required_keys) - r = -ESRCH; } } else if (isTCRYPT(cd->type)) { r = 0; } else if (name && isPLAIN(cd->type)) { - if (kc->get_plain_volume_key) + if (kc->type == CRYPT_KC_TYPE_VK_KEYRING) { + vk = crypt_alloc_volume_key(cd->u.plain.key_size, NULL); + if (!vk) + return -ENOMEM; + r = crypt_volume_key_set_description_by_name(vk, kc->u.vk_kr.key_description); + if (r < 0) + log_err(cd, _("Cannot use keyring key %s."), kc->u.vk_kr.key_description); + } else if (kc->get_passphrase && kc->type != CRYPT_KC_TYPE_TOKEN) { + r = kc->get_passphrase(cd, kc, &passphrase, &passphrase_size); + if (r < 0) + return r; + r = process_key(cd, cd->u.plain.hdr.hash, + cd->u.plain.key_size, + passphrase, passphrase_size, &vk); + } else if (kc->get_plain_volume_key) r = kc->get_plain_volume_key(cd, kc, &vk); - } else if (name && isBITLK(cd->type)) { - if (kc->get_bitlk_volume_key) - r = kc->get_bitlk_volume_key(cd, kc, &vk); + } else if (isBITLK(cd->type)) { + if (kc->get_bitlk_volume_key && (name || kc->type != CRYPT_KC_TYPE_KEY)) + r = kc->get_bitlk_volume_key(cd, kc, &cd->u.bitlk.params, &vk); } else if (isFVAULT2(cd->type)) { if (kc->get_fvault2_volume_key) - r = kc->get_fvault2_volume_key(cd, kc, &vk); + r = kc->get_fvault2_volume_key(cd, kc, &cd->u.fvault2.params, &vk); } else if (isVERITY(cd->type) && (name || kc->type != CRYPT_KC_TYPE_SIGNED_KEY)) { if (kc->get_verity_volume_key) r = kc->get_verity_volume_key(cd, kc, &vk, &vk_sign); @@ -5744,7 +5532,8 @@ const char *name, unlocked_keyslot = r; if (r == -ENOENT && isLUKS(cd->type) && cd->volume_key) { - vk = crypt_alloc_volume_key(cd->volume_key->keylength, cd->volume_key->key); + vk = crypt_alloc_volume_key(crypt_volume_key_length(cd->volume_key), + crypt_volume_key_get_key(cd->volume_key)); r = vk ? 0 : -ENOMEM; } if (r == -ENOENT && isINTEGRITY(cd->type)) @@ -5753,9 +5542,11 @@ const char *name, if (r < 0) goto out; - r = _verify_key(cd, - flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY ? CRYPT_ANY_SEGMENT : CRYPT_DEFAULT_SEGMENT, - vk); + if (luks2_reencryption) + r = _verify_reencrypt_keys(cd, vk); + else + r = _verify_key(cd, flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY, vk); + if (r < 0) goto out; @@ -5781,13 +5572,14 @@ const char *name, if (!crypt_use_keyring_for_vk(cd)) use_keyring = false; else - use_keyring = (name && !crypt_is_cipher_null(crypt_get_cipher(cd))) || + /* Force keyring use for activation of LUKS2 device in reencryption */ + use_keyring = (name && (luks2_reencryption || !crypt_is_cipher_null(crypt_get_cipher(cd)))) || (flags & CRYPT_ACTIVATE_KEYRING_KEY); if (use_keyring) { /* upload dm-crypt part of volume key in thread keyring if requested */ if (p_crypt) { - r = LUKS2_volume_key_load_in_keyring_by_digest(cd, p_crypt, crypt_volume_key_get_id(p_crypt)); + r = load_all_keys(cd, p_crypt); if (r < 0) goto out; flags |= CRYPT_ACTIVATE_KEYRING_KEY; @@ -5795,7 +5587,7 @@ const char *name, /* upload the volume key in custom user keyring if requested */ if (cd->link_vk_to_keyring) { - r = crypt_volume_key_load_in_user_keyring(cd, vk, &kid1, &kid2); + r = crypt_volume_key_load_in_custom_keyring(cd, vk, &kid1, &kid2); if (r < 0) { log_err(cd, _("Failed to link volume key in user defined keyring.")); goto out; @@ -5814,8 +5606,8 @@ const char *name, r = unlocked_keyslot; out: if (r < 0) { - crypt_drop_keyring_key(cd, vk); - crypt_drop_keyring_key(cd, p_crypt); + crypt_drop_uploaded_keyring_key(cd, vk); + crypt_drop_uploaded_keyring_key(cd, crypt_key); if (cd->link_vk_to_keyring && kid1) crypt_unlink_key_from_custom_keyring(cd, kid1); if (cd->link_vk_to_keyring && kid2) @@ -5837,10 +5629,10 @@ int crypt_activate_by_passphrase(struct crypt_device *cd, uint32_t flags) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; - crypt_keyslot_unlock_by_passphrase_init_internal(&kc, passphrase, passphrase_size); - r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, CRYPT_ANY_SLOT, NULL, flags); + crypt_keyslot_context_init_by_passphrase_internal(&kc, passphrase, passphrase_size); + r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, CRYPT_ANY_SLOT, &kc, flags); crypt_keyslot_context_destroy_internal(&kc); return r; @@ -5855,10 +5647,10 @@ int crypt_activate_by_keyfile_device_offset(struct crypt_device *cd, uint32_t flags) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; - crypt_keyslot_unlock_by_keyfile_init_internal(&kc, keyfile, keyfile_size, keyfile_offset); - r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, CRYPT_ANY_SLOT, NULL, flags); + crypt_keyslot_context_init_by_keyfile_internal(&kc, keyfile, keyfile_size, keyfile_offset); + r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, CRYPT_ANY_SLOT, &kc, flags); crypt_keyslot_context_destroy_internal(&kc); return r; @@ -5894,10 +5686,10 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, uint32_t flags) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; - crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size); - r = crypt_activate_by_keyslot_context(cd, name, CRYPT_ANY_SLOT /* unused */, &kc, CRYPT_ANY_SLOT, NULL, flags); + crypt_keyslot_context_init_by_key_internal(&kc, volume_key, volume_key_size); + r = crypt_activate_by_keyslot_context(cd, name, CRYPT_ANY_SLOT /* unused */, &kc, CRYPT_ANY_SLOT, &kc, flags); crypt_keyslot_context_destroy_internal(&kc); return r; @@ -5912,7 +5704,7 @@ int crypt_activate_by_signed_key(struct crypt_device *cd, uint32_t flags) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; if (!cd || !isVERITY(cd->type)) return -EINVAL; @@ -5923,10 +5715,10 @@ int crypt_activate_by_signed_key(struct crypt_device *cd, } if (signature) - crypt_keyslot_unlock_by_signed_key_init_internal(&kc, volume_key, volume_key_size, + crypt_keyslot_context_init_by_signed_key_internal(&kc, volume_key, volume_key_size, signature, signature_size); else - crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size); + crypt_keyslot_context_init_by_key_internal(&kc, volume_key, volume_key_size); r = crypt_activate_by_keyslot_context(cd, name, -2 /* unused */, &kc, CRYPT_ANY_SLOT, NULL, flags); crypt_keyslot_context_destroy_internal(&kc); @@ -5939,7 +5731,7 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t struct luks2_hdr *hdr2 = NULL; struct crypt_dm_active_device dmd = {}; int r; - uint32_t get_flags = DM_ACTIVE_DEVICE | DM_ACTIVE_UUID | DM_ACTIVE_HOLDERS; + uint64_t get_flags = DM_ACTIVE_DEVICE | DM_ACTIVE_UUID | DM_ACTIVE_HOLDERS; if (!name) return -EINVAL; @@ -5957,13 +5749,10 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t } if (flags & (CRYPT_DEACTIVATE_DEFERRED | CRYPT_DEACTIVATE_DEFERRED_CANCEL)) { - struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2); - if (hdr) { - json_object *jobj = json_segments_get_segment(LUKS2_get_segments_jobj(hdr), 0); - if (jobj && !strcmp(json_segment_type(jobj), "hw-opal")) { - log_err(cd, _("OPAL does not support deferred deactivation.")); - return -EINVAL; - } + r = crypt_get_hw_encryption_type(cd); + if (r == CRYPT_SW_AND_OPAL_HW || r == CRYPT_OPAL_HW_ONLY) { + log_err(cd, _("OPAL does not support deferred deactivation.")); + return -EINVAL; } } @@ -5974,13 +5763,6 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t switch (crypt_status(cd, name)) { case CRYPT_ACTIVE: case CRYPT_BUSY: - if (flags & CRYPT_DEACTIVATE_DEFERRED_CANCEL) { - r = dm_cancel_deferred_removal(name); - if (r < 0) - log_err(cd, _("Could not cancel deferred remove from device %s."), name); - break; - } - r = dm_query_device(cd, name, get_flags, &dmd); if (r >= 0) { if (dmd.holders) { @@ -5990,8 +5772,23 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t } } - if (isLUKS2(cd->type)) - hdr2 = crypt_get_hdr(cd, CRYPT_LUKS2); + /* For detached header case or missing metadata we need to check for OPAL2 devices + * from DM UUID */ + if (dmd.uuid && (flags & (CRYPT_DEACTIVATE_DEFERRED | CRYPT_DEACTIVATE_DEFERRED_CANCEL)) && + !strncmp(CRYPT_LUKS2_HW_OPAL, dmd.uuid, sizeof(CRYPT_LUKS2_HW_OPAL)-1)) { + log_err(cd, _("OPAL does not support deferred deactivation.")); + r = -EINVAL; + break; + } + + if (flags & CRYPT_DEACTIVATE_DEFERRED_CANCEL) { + r = dm_cancel_deferred_removal(name); + if (r < 0) + log_err(cd, _("Could not cancel deferred remove from device %s."), name); + break; + } + + hdr2 = crypt_get_hdr(cd, CRYPT_LUKS2); if ((dmd.uuid && !strncmp(CRYPT_LUKS2, dmd.uuid, sizeof(CRYPT_LUKS2)-1)) || hdr2) r = LUKS2_deactivate(cd, name, hdr2, &dmd, flags); @@ -6030,7 +5827,7 @@ int crypt_get_active_device(struct crypt_device *cd, const char *name, { int r; struct crypt_dm_active_device dmd, dmdi = {}; - const char *namei = NULL; + char *iname = NULL; struct dm_target *tgt = &dmd.segment; uint64_t min_offset = UINT64_MAX; @@ -6041,11 +5838,18 @@ int crypt_get_active_device(struct crypt_device *cd, const char *name, if (r < 0) return r; - /* For LUKS2 with integrity we need flags from underlying dm-integrity */ - if (isLUKS2(cd->type) && crypt_get_integrity_tag_size(cd) && single_segment(&dmd)) { - namei = device_dm_name(tgt->data_device); - if (namei && dm_query_device(cd, namei, 0, &dmdi) >= 0) - dmd.flags |= dmdi.flags; + /* + * For integrity and LUKS2 (and detached header where context is NULL) + * we need flags from underlying dm-integrity device. + * This check must be skipped for non-LUKS2 integrity device. + */ + if ((isLUKS2(cd->type) || !cd->type) && crypt_get_integrity_tag_size(cd)) { + if ((iname = dm_get_active_iname(cd, name))) { + if (dm_query_device(cd, iname, 0, &dmdi) >= 0) + dmd.flags |= dmdi.flags; + free(iname); + } else + dmd.flags |= (CRYPT_ACTIVATE_NO_JOURNAL | CRYPT_ACTIVATE_INLINE_MODE); } if (cd && isTCRYPT(cd->type)) { @@ -6111,12 +5915,12 @@ int crypt_volume_key_get(struct crypt_device *cd, size_t passphrase_size) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; if (!passphrase) return crypt_volume_key_get_by_keyslot_context(cd, keyslot, volume_key, volume_key_size, NULL); - crypt_keyslot_unlock_by_passphrase_init_internal(&kc, passphrase, passphrase_size); + crypt_keyslot_context_init_by_passphrase_internal(&kc, passphrase, passphrase_size); r = crypt_volume_key_get_by_keyslot_context(cd, keyslot, volume_key, volume_key_size, &kc); @@ -6137,7 +5941,7 @@ int crypt_volume_key_get_by_keyslot_context(struct crypt_device *cd, struct volume_key *vk = NULL; if (!cd || !volume_key || !volume_key_size || - (!kc && !isLUKS(cd->type) && !isTCRYPT(cd->type) && !isVERITY(cd->type))) + (!kc && !isLUKS(cd->type) && !isTCRYPT(cd->type) && !isVERITY(cd->type) && !isBITLK(cd->type))) return -EINVAL; if (isLUKS2(cd->type) && keyslot != CRYPT_ANY_SLOT) @@ -6156,12 +5960,6 @@ int crypt_volume_key_get_by_keyslot_context(struct crypt_device *cd, if (kc && (!kc->get_passphrase || kc->type == CRYPT_KC_TYPE_KEY)) return -EINVAL; - if (kc) { - r = kc->get_passphrase(cd, kc, &passphrase, &passphrase_size); - if (r < 0) - return r; - } - r = -EINVAL; if (isLUKS2(cd->type)) { @@ -6180,16 +5978,20 @@ int crypt_volume_key_get_by_keyslot_context(struct crypt_device *cd, r = -ENOENT; else r = kc->get_luks1_volume_key(cd, kc, keyslot, &vk); - } else if (isPLAIN(cd->type)) { - if (passphrase && cd->u.plain.hdr.hash) + } else if (isPLAIN(cd->type) && cd->u.plain.hdr.hash) { + if (kc && kc->get_passphrase && kc->type != CRYPT_KC_TYPE_TOKEN) { + r = kc->get_passphrase(cd, kc, &passphrase, &passphrase_size); + if (r < 0) + return r; r = process_key(cd, cd->u.plain.hdr.hash, key_len, passphrase, passphrase_size, &vk); + } if (r < 0) log_err(cd, _("Cannot retrieve volume key for plain device.")); } else if (isVERITY(cd->type)) { /* volume_key == root hash */ if (cd->u.verity.root_hash) { - memcpy(volume_key, cd->u.verity.root_hash, cd->u.verity.root_hash_size); + crypt_safe_memcpy(volume_key, cd->u.verity.root_hash, cd->u.verity.root_hash_size); *volume_key_size = cd->u.verity.root_hash_size; r = 0; } else @@ -6197,26 +5999,29 @@ int crypt_volume_key_get_by_keyslot_context(struct crypt_device *cd, } else if (isTCRYPT(cd->type)) { r = TCRYPT_get_volume_key(cd, &cd->u.tcrypt.hdr, &cd->u.tcrypt.params, &vk); } else if (isBITLK(cd->type)) { - if (passphrase) - r = BITLK_get_volume_key(cd, passphrase, passphrase_size, &cd->u.bitlk.params, &vk); + if (kc && kc->get_bitlk_volume_key) + r = kc->get_bitlk_volume_key(cd, kc, &cd->u.bitlk.params, &vk); + else if (!kc) + r = BITLK_get_volume_key(cd, NULL, 0, &cd->u.bitlk.params, &vk); if (r < 0) log_err(cd, _("Cannot retrieve volume key for BITLK device.")); } else if (isFVAULT2(cd->type)) { - if (passphrase) - r = FVAULT2_get_volume_key(cd, passphrase, passphrase_size, &cd->u.fvault2.params, &vk); + if (kc && kc->get_fvault2_volume_key) + r = kc->get_fvault2_volume_key(cd, kc, &cd->u.fvault2.params, &vk); if (r < 0) log_err(cd, _("Cannot retrieve volume key for FVAULT2 device.")); } else log_err(cd, _("This operation is not supported for %s crypt device."), cd->type ?: "(none)"); if (r == -ENOENT && isLUKS(cd->type) && cd->volume_key) { - vk = crypt_alloc_volume_key(cd->volume_key->keylength, cd->volume_key->key); + vk = crypt_alloc_volume_key(crypt_volume_key_length(cd->volume_key), + crypt_volume_key_get_key(cd->volume_key)); r = vk ? 0 : -ENOMEM; } if (r >= 0 && vk) { - memcpy(volume_key, vk->key, vk->keylength); - *volume_key_size = vk->keylength; + crypt_safe_memcpy(volume_key, crypt_volume_key_get_key(vk), crypt_volume_key_length(vk)); + *volume_key_size = crypt_volume_key_length(vk); } crypt_free_volume_key(vk); @@ -6518,12 +6323,20 @@ const char *crypt_get_integrity(struct crypt_device *cd) } /* INTERNAL only */ -int crypt_get_integrity_key_size(struct crypt_device *cd) +int crypt_get_integrity_key_size(struct crypt_device *cd, bool dm_compat) { int key_size = 0; - if (isINTEGRITY(cd->type) || isLUKS2(cd->type) || !cd->type) - key_size = INTEGRITY_key_size(crypt_get_integrity(cd)); + if (isLUKS2(cd->type)) { + key_size = INTEGRITY_key_size(crypt_get_integrity(cd), + LUKS2_get_integrity_key_size(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)); + if (dm_compat && key_size > 0 && + key_size == INTEGRITY_key_size(crypt_get_integrity(cd), 0)) + return 0; + } + + if (isINTEGRITY(cd->type) || !cd->type) + key_size = INTEGRITY_key_size(crypt_get_integrity(cd), 0); return key_size > 0 ? key_size : 0; } @@ -6628,7 +6441,7 @@ int crypt_get_volume_key_size(struct crypt_device *cd) if (isLUKS2(cd->type)) { r = LUKS2_get_volume_key_size(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT); if (r < 0 && cd->volume_key) - r = cd->volume_key->keylength; + r = crypt_volume_key_length(cd->volume_key); return r < 0 ? 0 : r; } @@ -6653,6 +6466,19 @@ int crypt_get_volume_key_size(struct crypt_device *cd) return 0; } +int crypt_get_old_volume_key_size(struct crypt_device *cd) +{ + int r = _onlyLUKS2(cd, CRYPT_CD_QUIET, + CRYPT_REQUIREMENT_ONLINE_REENCRYPT | CRYPT_REQUIREMENT_OPAL); + + if (r < 0) + return 0; + + r = LUKS2_get_old_volume_key_size(&cd->u.luks2.hdr); + + return r < 0 ? 0 : r; +} + int crypt_get_hw_encryption_key_size(struct crypt_device *cd) { if (!cd || !isLUKS2(cd->type)) @@ -7002,7 +6828,7 @@ int crypt_get_integrity_info(struct crypt_device *cd, ip->buffer_sectors = cd->u.integrity.params.buffer_sectors; ip->integrity = cd->u.integrity.params.integrity; - ip->integrity_key_size = crypt_get_integrity_key_size(cd); + ip->integrity_key_size = crypt_get_integrity_key_size(cd, false); ip->journal_integrity = cd->u.integrity.params.journal_integrity; ip->journal_integrity_key_size = cd->u.integrity.params.journal_integrity_key_size; @@ -7021,7 +6847,7 @@ int crypt_get_integrity_info(struct crypt_device *cd, ip->buffer_sectors = 0; // FIXME ip->integrity = LUKS2_get_integrity(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT); - ip->integrity_key_size = crypt_get_integrity_key_size(cd); + ip->integrity_key_size = crypt_get_integrity_key_size(cd, false); ip->tag_size = INTEGRITY_tag_size(ip->integrity, crypt_get_cipher(cd), crypt_get_cipher_mode(cd)); ip->journal_integrity = NULL; @@ -7035,7 +6861,7 @@ int crypt_get_integrity_info(struct crypt_device *cd, } else if (!cd->type) { memset(ip, 0, sizeof(*ip)); ip->integrity = crypt_get_integrity(cd); - ip->integrity_key_size = crypt_get_integrity_key_size(cd); + ip->integrity_key_size = crypt_get_integrity_key_size(cd, false); ip->tag_size = crypt_get_integrity_tag_size(cd); } @@ -7081,12 +6907,11 @@ int crypt_convert(struct crypt_device *cd, /* Internal access function to header pointer */ void *crypt_get_hdr(struct crypt_device *cd, const char *type) { - /* One type can be OPAL */ - if (isLUKS2(type) && isLUKS2(cd->type)) - return &cd->u.luks2.hdr; + assert(cd); + assert(type); /* If requested type differs, ignore it */ - if (strcmp(cd->type, type)) + if (!cd->type || strcmp(cd->type, type)) return NULL; if (isPLAIN(cd->type)) @@ -7095,6 +6920,9 @@ void *crypt_get_hdr(struct crypt_device *cd, const char *type) if (isLUKS1(cd->type)) return &cd->u.luks1.hdr; + if (isLUKS2(type)) + return &cd->u.luks2.hdr; + if (isLOOPAES(cd->type)) return &cd->u.loopaes; @@ -7127,10 +6955,10 @@ int crypt_activate_by_token_pin(struct crypt_device *cd, const char *name, void *usrptr, uint32_t flags) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; - crypt_keyslot_unlock_by_token_init_internal(&kc, token, type, pin, pin_size, usrptr); - r = crypt_activate_by_keyslot_context(cd, name, CRYPT_ANY_SLOT, &kc, CRYPT_ANY_SLOT, NULL, flags); + crypt_keyslot_context_init_by_token_internal(&kc, token, type, pin, pin_size, usrptr); + r = crypt_activate_by_keyslot_context(cd, name, CRYPT_ANY_SLOT, &kc, CRYPT_ANY_SLOT, &kc, flags); crypt_keyslot_context_destroy_internal(&kc); return r; @@ -7252,6 +7080,9 @@ int crypt_token_assign_keyslot(struct crypt_device *cd, int token, int keyslot) if ((r = onlyLUKS2(cd))) return r; + if (token == CRYPT_ANY_TOKEN) + return -EINVAL; + return LUKS2_token_assign(cd, &cd->u.luks2.hdr, keyslot, token, 1, 1); } @@ -7262,6 +7093,9 @@ int crypt_token_unassign_keyslot(struct crypt_device *cd, int token, int keyslot if ((r = onlyLUKS2(cd))) return r; + if (token == CRYPT_ANY_TOKEN) + return -EINVAL; + return LUKS2_token_assign(cd, &cd->u.luks2.hdr, keyslot, token, 0, 1); } @@ -7319,8 +7153,10 @@ int crypt_persistent_flags_get(struct crypt_device *cd, crypt_flags_type type, u if (type == CRYPT_FLAGS_ACTIVATION) return LUKS2_config_get_flags(cd, &cd->u.luks2.hdr, flags); - if (type == CRYPT_FLAGS_REQUIREMENTS) - return LUKS2_config_get_requirements(cd, &cd->u.luks2.hdr, flags); + if (type == CRYPT_FLAGS_REQUIREMENTS) { + LUKS2_config_get_requirements(cd, &cd->u.luks2.hdr, flags); + return 0; + } return -EINVAL; } @@ -7483,17 +7319,22 @@ static int keyslot_add_by_key(struct crypt_device *cd, digest = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk); if (digest >= 0) /* if key matches volume key digest tear down new vk flag */ flags &= ~CRYPT_VOLUME_KEY_SET; - else { + else if (digest == -EPERM) { /* if key matches any existing digest, do not create new digest */ if ((flags & CRYPT_VOLUME_KEY_DIGEST_REUSE)) - digest = LUKS2_digest_any_matching(cd, &cd->u.luks2.hdr, vk); + digest = LUKS2_digest_verify_by_any_matching(cd, vk); + + /* Anything other than -EPERM or -ENOENT suggests broken metadata. Abort */ + if (digest < 0 && digest != -ENOENT && digest != -EPERM) + return digest; /* no segment flag or new vk flag requires new key digest */ if (flags & (CRYPT_VOLUME_KEY_NO_SEGMENT | CRYPT_VOLUME_KEY_SET)) { if (digest < 0 || !(flags & CRYPT_VOLUME_KEY_DIGEST_REUSE)) digest = LUKS2_digest_create(cd, "pbkdf2", &cd->u.luks2.hdr, vk); } - } + } else /* Anything other than -EPERM suggests broken metadata. Abort */ + return digest; r = digest; if (r < 0) { @@ -7524,7 +7365,7 @@ int crypt_keyslot_add_by_key(struct crypt_device *cd, uint32_t flags) { int r; - struct crypt_keyslot_context kc, new_kc; + struct crypt_keyslot_context kc = {}, new_kc = {}; if (!passphrase || ((flags & CRYPT_VOLUME_KEY_NO_SEGMENT) && (flags & CRYPT_VOLUME_KEY_SET))) @@ -7536,9 +7377,9 @@ int crypt_keyslot_add_by_key(struct crypt_device *cd, if ((flags & CRYPT_VOLUME_KEY_SET) && crypt_keyslot_status(cd, keyslot) > CRYPT_SLOT_INACTIVE && isLUKS2(cd->type)) { if (volume_key) - crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size); + crypt_keyslot_context_init_by_key_internal(&kc, volume_key, volume_key_size); else - crypt_keyslot_unlock_by_passphrase_init_internal(&kc, passphrase, passphrase_size); + crypt_keyslot_context_init_by_passphrase_internal(&kc, passphrase, passphrase_size); r = verify_and_update_segment_digest(cd, &cd->u.luks2.hdr, keyslot, &kc); @@ -7547,8 +7388,8 @@ int crypt_keyslot_add_by_key(struct crypt_device *cd, return r; } - crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size); - crypt_keyslot_unlock_by_passphrase_init_internal(&new_kc, passphrase, passphrase_size); + crypt_keyslot_context_init_by_key_internal(&kc, volume_key, volume_key_size); + crypt_keyslot_context_init_by_passphrase_internal(&new_kc, passphrase, passphrase_size); r = crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, &kc, keyslot, &new_kc, flags); @@ -7614,11 +7455,12 @@ int crypt_keyslot_add_by_keyslot_context(struct crypt_device *cd, if (r == -ENOENT) { if ((flags & CRYPT_VOLUME_KEY_NO_SEGMENT) && kc->type == CRYPT_KC_TYPE_KEY) { - if (!(vk = crypt_generate_volume_key(cd, kc->u.k.volume_key_size))) + if (!(vk = crypt_generate_volume_key(cd, kc->u.k.volume_key_size, KEY_QUALITY_KEY))) return -ENOMEM; r = 0; } else if (cd->volume_key) { - if (!(vk = crypt_alloc_volume_key(cd->volume_key->keylength, cd->volume_key->key))) + if (!(vk = crypt_alloc_volume_key(crypt_volume_key_length(cd->volume_key), + crypt_volume_key_get_key(cd->volume_key)))) return -ENOMEM; r = 0; } else if (active_slots == 0) { @@ -7652,10 +7494,13 @@ int crypt_keyslot_add_by_keyslot_context(struct crypt_device *cd, */ int crypt_use_keyring_for_vk(struct crypt_device *cd) { - uint32_t dmc_flags; + uint64_t dmc_flags; /* dm backend must be initialized */ - if (!cd || !isLUKS2(cd->type)) + if (!cd) + return 0; + + if (!isPLAIN(cd->type) && !isLUKS2(cd->type)) return 0; if (!_vk_via_keyring || !kernel_keyring_support()) @@ -7676,26 +7521,25 @@ int crypt_volume_key_keyring(struct crypt_device *cd __attribute__((unused)), in /* internal only */ int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk) { - key_serial_t kid; - if (!vk || !cd) return -EINVAL; - if (!vk->key_description) { + if (!crypt_volume_key_description(vk)) { log_dbg(cd, "Invalid key description"); return -EINVAL; } - log_dbg(cd, "Loading key (type logon, name %s) in thread keyring.", vk->key_description); + log_dbg(cd, "Loading key (type logon, name %s) in thread keyring.", + crypt_volume_key_description(vk)); - kid = keyring_add_key_in_thread_keyring(LOGON_KEY, vk->key_description, vk->key, vk->keylength); - if (kid < 0) { + if (crypt_volume_key_upload_kernel_key(vk)) { + crypt_set_key_in_keyring(cd, 1); + return 0; + } else { log_dbg(cd, "keyring_add_key_in_thread_keyring failed (error %d)", errno); log_err(cd, _("Failed to load key in kernel keyring.")); - } else - crypt_set_key_in_keyring(cd, 1); - - return kid < 0 ? -EINVAL : 0; + return -EINVAL; + } } /* internal only */ @@ -7742,7 +7586,34 @@ int crypt_keyring_get_key_by_name(struct crypt_device *cd, if (!key_description || !key || !key_size) return -EINVAL; - log_dbg(cd, "Searching for key by name %s.", key_description); + log_dbg(cd, "Searching for kernel key by name %s.", key_description); + + kid = keyring_find_key_id_by_name(key_description); + if (kid == 0) { + log_dbg(cd, "keyring_find_key_id_by_name failed with errno %d.", errno); + return -ENOENT; + } + + log_dbg(cd, "Reading content of kernel key (id %" PRIi32 ").", kid); + + r = keyring_read_key(kid, key, key_size); + if (r < 0) + log_dbg(cd, "keyring_read_key failed with errno %d.", errno); + + return r; +} + +int crypt_keyring_get_keysize_by_name(struct crypt_device *cd, + const char *key_description, + size_t *r_key_size) +{ + int r; + key_serial_t kid; + + if (!key_description || !r_key_size) + return -EINVAL; + + log_dbg(cd, "Searching for kernel key by name %s.", key_description); kid = keyring_find_key_id_by_name(key_description); if (kid == -ENOTSUP) { @@ -7759,9 +7630,9 @@ int crypt_keyring_get_key_by_name(struct crypt_device *cd, log_dbg(cd, "Reading content of kernel key (id %" PRIi32 ").", kid); - r = keyring_read_key(kid, key, key_size); + r = keyring_read_keysize(kid, r_key_size); if (r < 0) - log_dbg(cd, "keyring_read_key failed with errno %d.", errno); + log_dbg(cd, "keyring_read_keysize failed with errno %d.", errno); return r; } @@ -7782,7 +7653,18 @@ void crypt_set_key_in_keyring(struct crypt_device *cd, unsigned key_in_keyring) } /* internal only */ -void crypt_drop_keyring_key_by_description(struct crypt_device *cd, const char *key_description, key_type_t ktype) +void crypt_unlink_key_from_thread_keyring(struct crypt_device *cd, + key_serial_t key_id) +{ + log_dbg(cd, "Unlinking volume key (id: %" PRIi32 ") from thread keyring.", key_id); + + if (keyring_unlink_key_from_thread_keyring(key_id)) + log_dbg(cd, "keyring_unlink_key_from_thread_keyring failed with errno %d.", errno); +} + +void crypt_unlink_key_by_description_from_thread_keyring(struct crypt_device *cd, + const char *key_description, + key_type_t ktype) { key_serial_t kid; const char *type_name = key_type_name(ktype); @@ -7790,7 +7672,7 @@ void crypt_drop_keyring_key_by_description(struct crypt_device *cd, const char * if (!key_description || !type_name) return; - log_dbg(cd, "Requesting kernel key %s (type %s) for unlink from thread keyring.", key_description, type_name); + log_dbg(cd, "Requesting kernel key %s (type %s).", key_description, type_name); crypt_set_key_in_keyring(cd, 0); @@ -7803,14 +7685,7 @@ void crypt_drop_keyring_key_by_description(struct crypt_device *cd, const char * return; } - log_dbg(cd, "Unlinking volume key (id: %" PRIi32 ") from thread keyring.", kid); - - if (!keyring_unlink_key_from_thread_keyring(kid)) - return; - - log_dbg(cd, "keyring_unlink_key_from_thread_keyring failed with errno %d.", errno); - log_err(cd, _("Failed to unlink volume key from thread keyring.")); - + crypt_unlink_key_from_thread_keyring(cd, kid); } int crypt_set_keyring_to_link(struct crypt_device *cd, const char *key_description, @@ -7877,12 +7752,12 @@ int crypt_set_keyring_to_link(struct crypt_device *cd, const char *key_descripti } /* internal only */ -void crypt_drop_keyring_key(struct crypt_device *cd, struct volume_key *vks) +void crypt_drop_uploaded_keyring_key(struct crypt_device *cd, struct volume_key *vks) { struct volume_key *vk = vks; while (vk) { - crypt_drop_keyring_key_by_description(cd, vk->key_description, LOGON_KEY); + crypt_volume_key_drop_uploaded_kernel_key(cd, vk); vk = crypt_volume_key_next(vk); } } @@ -7894,13 +7769,13 @@ int crypt_activate_by_keyring(struct crypt_device *cd, uint32_t flags) { int r; - struct crypt_keyslot_context kc; + struct crypt_keyslot_context kc = {}; if (!cd || !key_description) return -EINVAL; - crypt_keyslot_unlock_by_keyring_internal(&kc, key_description); - r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, CRYPT_ANY_SLOT, NULL, flags); + crypt_keyslot_context_init_by_keyring_internal(&kc, key_description); + r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, CRYPT_ANY_SLOT, &kc, flags); crypt_keyslot_context_destroy_internal(&kc); return r; diff --git a/lib/tcrypt/tcrypt.c b/lib/tcrypt/tcrypt.c index f1ed858..8fbe651 100644 --- a/lib/tcrypt/tcrypt.c +++ b/lib/tcrypt/tcrypt.c @@ -2,8 +2,8 @@ /* * TCRYPT (TrueCrypt-compatible) and VeraCrypt volume handling * - * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2012-2024 Milan Broz + * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Milan Broz */ #include @@ -17,30 +17,30 @@ /* TCRYPT PBKDF variants */ static const struct { - unsigned int legacy:1; - unsigned int veracrypt:1; + bool legacy; + bool veracrypt; const char *name; const char *hash; unsigned int iterations; uint32_t veracrypt_pim_const; uint32_t veracrypt_pim_mult; } tcrypt_kdf[] = { - { 0, 0, "pbkdf2", "ripemd160", 2000, 0, 0 }, - { 0, 0, "pbkdf2", "ripemd160", 1000, 0, 0 }, - { 0, 0, "pbkdf2", "sha512", 1000, 0, 0 }, - { 0, 0, "pbkdf2", "whirlpool", 1000, 0, 0 }, - { 1, 0, "pbkdf2", "sha1", 2000, 0, 0 }, - { 0, 1, "pbkdf2", "sha512", 500000, 15000, 1000 }, - { 0, 1, "pbkdf2", "whirlpool", 500000, 15000, 1000 }, - { 0, 1, "pbkdf2", "sha256", 500000, 15000, 1000 }, // VeraCrypt 1.0f - { 0, 1, "pbkdf2", "sha256", 200000, 0, 2048 }, // boot only - { 0, 1, "pbkdf2", "blake2s-256", 500000, 15000, 1000 }, // VeraCrypt 1.26.2 - { 0, 1, "pbkdf2", "blake2s-256", 200000, 0, 2048 }, // boot only - { 0, 1, "pbkdf2", "ripemd160", 655331, 15000, 1000 }, - { 0, 1, "pbkdf2", "ripemd160", 327661, 0, 2048 }, // boot only - { 0, 1, "pbkdf2", "stribog512",500000, 15000, 1000 }, -// { 0, 1, "pbkdf2", "stribog512",200000, 0, 2048 }, // boot only - { 0, 0, NULL, NULL, 0, 0, 0 } + { false, false, "pbkdf2", "ripemd160", 2000, 0, 0 }, + { false, false, "pbkdf2", "ripemd160", 1000, 0, 0 }, + { false, false, "pbkdf2", "sha512", 1000, 0, 0 }, + { false, false, "pbkdf2", "whirlpool", 1000, 0, 0 }, + { true, false, "pbkdf2", "sha1", 2000, 0, 0 }, + { false, true, "pbkdf2", "sha512", 500000, 15000, 1000 }, + { false, true, "pbkdf2", "whirlpool", 500000, 15000, 1000 }, + { false, true, "pbkdf2", "sha256", 500000, 15000, 1000 }, // VeraCrypt 1.0f + { false, true, "pbkdf2", "sha256", 200000, 0, 2048 }, // boot only + { false, true, "pbkdf2", "blake2s-256", 500000, 15000, 1000 }, // VeraCrypt 1.26.2 + { false, true, "pbkdf2", "blake2s-256", 200000, 0, 2048 }, // boot only + { false, true, "pbkdf2", "ripemd160", 655331, 15000, 1000 }, + { false, true, "pbkdf2", "ripemd160", 327661, 0, 2048 }, // boot only + { false, true, "pbkdf2", "stribog512",500000, 15000, 1000 }, +// { false, true, "pbkdf2", "stribog512",200000, 0, 2048 }, // boot only + { false, false, NULL, NULL, 0, 0, 0 } }; struct tcrypt_alg { @@ -53,95 +53,95 @@ struct tcrypt_alg { }; struct tcrypt_algs { - unsigned int legacy:1; + bool legacy; unsigned int chain_count; unsigned int chain_key_size; const char *long_name; const char *mode; - struct tcrypt_alg cipher[3]; + const struct tcrypt_alg cipher[3]; }; /* TCRYPT cipher variants */ -static struct tcrypt_algs tcrypt_cipher[] = { +static const struct tcrypt_algs tcrypt_cipher[] = { /* XTS mode */ -{0,1,64,"aes","xts-plain64", +{false,1,64,"aes","xts-plain64", {{"aes", 64,16,0,32,0}}}, -{0,1,64,"serpent","xts-plain64", +{false,1,64,"serpent","xts-plain64", {{"serpent",64,16,0,32,0}}}, -{0,1,64,"twofish","xts-plain64", +{false,1,64,"twofish","xts-plain64", {{"twofish",64,16,0,32,0}}}, -{0,2,128,"twofish-aes","xts-plain64", +{false,2,128,"twofish-aes","xts-plain64", {{"twofish",64,16, 0,64,0}, {"aes", 64,16,32,96,0}}}, -{0,3,192,"serpent-twofish-aes","xts-plain64", +{false,3,192,"serpent-twofish-aes","xts-plain64", {{"serpent",64,16, 0, 96,0}, {"twofish",64,16,32,128,0}, {"aes", 64,16,64,160,0}}}, -{0,2,128,"aes-serpent","xts-plain64", +{false,2,128,"aes-serpent","xts-plain64", {{"aes", 64,16, 0,64,0}, {"serpent",64,16,32,96,0}}}, -{0,3,192,"aes-twofish-serpent","xts-plain64", +{false,3,192,"aes-twofish-serpent","xts-plain64", {{"aes", 64,16, 0, 96,0}, {"twofish",64,16,32,128,0}, {"serpent",64,16,64,160,0}}}, -{0,2,128,"serpent-twofish","xts-plain64", +{false,2,128,"serpent-twofish","xts-plain64", {{"serpent",64,16, 0,64,0}, {"twofish",64,16,32,96,0}}}, -{0,1,64,"camellia","xts-plain64", +{false,1,64,"camellia","xts-plain64", {{"camellia", 64,16,0,32,0}}}, -{0,1,64,"kuznyechik","xts-plain64", +{false,1,64,"kuznyechik","xts-plain64", {{"kuznyechik", 64,16,0,32,0}}}, -{0,2,128,"kuznyechik-camellia","xts-plain64", +{false,2,128,"kuznyechik-camellia","xts-plain64", {{"kuznyechik",64,16, 0,64,0}, {"camellia", 64,16,32,96,0}}}, -{0,2,128,"twofish-kuznyechik","xts-plain64", +{false,2,128,"twofish-kuznyechik","xts-plain64", {{"twofish", 64,16, 0,64,0}, {"kuznyechik",64,16,32,96,0}}}, -{0,2,128,"serpent-camellia","xts-plain64", +{false,2,128,"serpent-camellia","xts-plain64", {{"serpent", 64,16, 0,64,0}, {"camellia", 64,16,32,96,0}}}, -{0,2,128,"aes-kuznyechik","xts-plain64", +{false,2,128,"aes-kuznyechik","xts-plain64", {{"aes", 64,16, 0,64,0}, {"kuznyechik",64,16,32,96,0}}}, -{0,3,192,"camellia-serpent-kuznyechik","xts-plain64", +{false,3,192,"camellia-serpent-kuznyechik","xts-plain64", {{"camellia", 64,16, 0, 96,0}, {"serpent", 64,16,32,128,0}, {"kuznyechik",64,16,64,160,0}}}, /* LRW mode */ -{0,1,48,"aes","lrw-benbi", +{false,1,48,"aes","lrw-benbi", {{"aes", 48,16,32,0,0}}}, -{0,1,48,"serpent","lrw-benbi", +{false,1,48,"serpent","lrw-benbi", {{"serpent",48,16,32,0,0}}}, -{0,1,48,"twofish","lrw-benbi", +{false,1,48,"twofish","lrw-benbi", {{"twofish",48,16,32,0,0}}}, -{0,2,96,"twofish-aes","lrw-benbi", +{false,2,96,"twofish-aes","lrw-benbi", {{"twofish",48,16,32,0,0}, {"aes", 48,16,64,0,0}}}, -{0,3,144,"serpent-twofish-aes","lrw-benbi", +{false,3,144,"serpent-twofish-aes","lrw-benbi", {{"serpent",48,16,32,0,0}, {"twofish",48,16,64,0,0}, {"aes", 48,16,96,0,0}}}, -{0,2,96,"aes-serpent","lrw-benbi", +{false,2,96,"aes-serpent","lrw-benbi", {{"aes", 48,16,32,0,0}, {"serpent",48,16,64,0,0}}}, -{0,3,144,"aes-twofish-serpent","lrw-benbi", +{false,3,144,"aes-twofish-serpent","lrw-benbi", {{"aes", 48,16,32,0,0}, {"twofish",48,16,64,0,0}, {"serpent",48,16,96,0,0}}}, -{0,2,96,"serpent-twofish", "lrw-benbi", +{false,2,96,"serpent-twofish", "lrw-benbi", {{"serpent",48,16,32,0,0}, {"twofish",48,16,64,0,0}}}, /* Kernel LRW block size is fixed to 16 bytes for GF(2^128) * thus cannot be used with blowfish where block is 8 bytes. * There also no GF(2^64) support. -{1,1,64,"blowfish_le","lrw-benbi", +{true,1,64,"blowfish_le","lrw-benbi", {{"blowfish_le",64,8,32,0,0}}}, -{1,2,112,"blowfish_le-aes","lrw-benbi", +{true,2,112,"blowfish_le-aes","lrw-benbi", {{"blowfish_le",64, 8,32,0,0}, {"aes", 48,16,88,0,0}}}, -{1,3,160,"serpent-blowfish_le-aes","lrw-benbi", +{true,3,160,"serpent-blowfish_le-aes","lrw-benbi", {{"serpent", 48,16, 32,0,0}, {"blowfish_le",64, 8, 64,0,0}, {"aes", 48,16,120,0,0}}},*/ @@ -150,39 +150,39 @@ static struct tcrypt_algs tcrypt_cipher[] = { * CBC + "outer" CBC (both with whitening) * chain_key_size: alg_keys_bytes + IV_seed_bytes + whitening_bytes */ -{1,1,32+16+16,"aes","cbc-tcw", +{true,1,32+16+16,"aes","cbc-tcw", {{"aes", 32,16,32,0,32}}}, -{1,1,32+16+16,"serpent","cbc-tcw", +{true,1,32+16+16,"serpent","cbc-tcw", {{"serpent",32,16,32,0,32}}}, -{1,1,32+16+16,"twofish","cbc-tcw", +{true,1,32+16+16,"twofish","cbc-tcw", {{"twofish",32,16,32,0,32}}}, -{1,2,64+16+16,"twofish-aes","cbci-tcrypt", +{true,2,64+16+16,"twofish-aes","cbci-tcrypt", {{"twofish",32,16,32,0,0}, {"aes", 32,16,64,0,32}}}, -{1,3,96+16+16,"serpent-twofish-aes","cbci-tcrypt", +{true,3,96+16+16,"serpent-twofish-aes","cbci-tcrypt", {{"serpent",32,16,32,0,0}, {"twofish",32,16,64,0,0}, {"aes", 32,16,96,0,32}}}, -{1,2,64+16+16,"aes-serpent","cbci-tcrypt", +{true,2,64+16+16,"aes-serpent","cbci-tcrypt", {{"aes", 32,16,32,0,0}, {"serpent",32,16,64,0,32}}}, -{1,3,96+16+16,"aes-twofish-serpent", "cbci-tcrypt", +{true,3,96+16+16,"aes-twofish-serpent", "cbci-tcrypt", {{"aes", 32,16,32,0,0}, {"twofish",32,16,64,0,0}, {"serpent",32,16,96,0,32}}}, -{1,2,64+16+16,"serpent-twofish", "cbci-tcrypt", +{true,2,64+16+16,"serpent-twofish", "cbci-tcrypt", {{"serpent",32,16,32,0,0}, {"twofish",32,16,64,0,32}}}, -{1,1,16+8+16,"cast5","cbc-tcw", +{true,1,16+8+16,"cast5","cbc-tcw", {{"cast5", 16,8,32,0,24}}}, -{1,1,24+8+16,"des3_ede","cbc-tcw", +{true,1,24+8+16,"des3_ede","cbc-tcw", {{"des3_ede",24,8,32,0,24}}}, -{1,1,56+8+16,"blowfish_le","cbc-tcrypt", +{true,1,56+8+16,"blowfish_le","cbc-tcrypt", {{"blowfish_le",56,8,32,0,24}}}, -{1,2,88+16+16,"blowfish_le-aes","cbc-tcrypt", +{true,2,88+16+16,"blowfish_le-aes","cbc-tcrypt", {{"blowfish_le",56, 8,32,0,0}, {"aes", 32,16,88,0,32}}}, -{1,3,120+16+16,"serpent-blowfish_le-aes","cbc-tcrypt", +{true,3,120+16+16,"serpent-blowfish_le-aes","cbc-tcrypt", {{"serpent", 32,16, 32,0,0}, {"blowfish_le",56, 8, 64,0,0}, {"aes", 32,16,120,0,32}}}, @@ -258,7 +258,7 @@ static void TCRYPT_swab_le(char *buf) *r = swab32(*r); } -static int decrypt_blowfish_le_cbc(struct tcrypt_alg *alg, +static int decrypt_blowfish_le_cbc(const struct tcrypt_alg *alg, const char *key, char *buf) { int bs = alg->iv_size; @@ -301,27 +301,27 @@ static void TCRYPT_remove_whitening(char *buf, const char *key) buf[j] ^= key[j % 8]; } -static void TCRYPT_copy_key(struct tcrypt_alg *alg, const char *mode, +static void TCRYPT_copy_key(const struct tcrypt_alg *alg, const char *mode, char *out_key, const char *key) { int ks2; if (!strncmp(mode, "xts", 3)) { ks2 = alg->key_size / 2; - memcpy(out_key, &key[alg->key_offset], ks2); - memcpy(&out_key[ks2], &key[alg->iv_offset], ks2); + crypt_safe_memcpy(out_key, &key[alg->key_offset], ks2); + crypt_safe_memcpy(&out_key[ks2], &key[alg->iv_offset], ks2); } else if (!strncmp(mode, "lrw", 3)) { ks2 = alg->key_size - TCRYPT_LRW_IKEY_LEN; - memcpy(out_key, &key[alg->key_offset], ks2); - memcpy(&out_key[ks2], key, TCRYPT_LRW_IKEY_LEN); + crypt_safe_memcpy(out_key, &key[alg->key_offset], ks2); + crypt_safe_memcpy(&out_key[ks2], key, TCRYPT_LRW_IKEY_LEN); } else if (!strncmp(mode, "cbc", 3)) { - memcpy(out_key, &key[alg->key_offset], alg->key_size); + crypt_safe_memcpy(out_key, &key[alg->key_offset], alg->key_size); /* IV + whitening */ - memcpy(&out_key[alg->key_size], &key[alg->iv_offset], + crypt_safe_memcpy(&out_key[alg->key_size], &key[alg->iv_offset], alg->key_extra_size); } } -static int TCRYPT_decrypt_hdr_one(struct tcrypt_alg *alg, const char *mode, +static int TCRYPT_decrypt_hdr_one(const struct tcrypt_alg *alg, const char *mode, const char *key,struct tcrypt_phdr *hdr) { char backend_key[TCRYPT_HDR_KEY_LEN]; @@ -365,7 +365,7 @@ static int TCRYPT_decrypt_hdr_one(struct tcrypt_alg *alg, const char *mode, * For chained ciphers and CBC mode we need "outer" decryption. * Backend doesn't provide this, so implement it here directly using ECB. */ -static int TCRYPT_decrypt_cbci(struct tcrypt_algs *ciphers, +static int TCRYPT_decrypt_cbci(const struct tcrypt_algs *ciphers, const char *key, struct tcrypt_phdr *hdr) { struct crypt_cipher *cipher[3]; @@ -606,7 +606,7 @@ static int TCRYPT_init_hdr(struct crypt_device *cd, if ((r < 0 && skipped && skipped == i) || r == -ENOTSUP) { log_err(cd, _("Required kernel crypto interface not available.")); -#ifdef ENABLE_AF_ALG +#if ENABLE_AF_ALG log_err(cd, _("Ensure you have algif_skcipher kernel module loaded.")); #endif r = -ENOTSUP; @@ -707,7 +707,7 @@ int TCRYPT_read_phdr(struct crypt_device *cd, return r; } -static struct tcrypt_algs *TCRYPT_get_algs(const char *cipher, const char *mode) +static const struct tcrypt_algs *TCRYPT_get_algs(const char *cipher, const char *mode) { int i; @@ -732,11 +732,12 @@ int TCRYPT_activate(struct crypt_device *cd, char *part_path; unsigned int i; int r; - uint32_t req_flags, dmc_flags; - struct tcrypt_algs *algs; + uint64_t req_flags, dmc_flags; + const struct tcrypt_algs *algs; enum devcheck device_check; - uint64_t offset = crypt_get_data_offset(cd); + uint64_t offset, iv_offset; struct volume_key *vk = NULL; + void *key = NULL; struct device *ptr_dev = crypt_data_device(cd), *device = NULL, *part_device = NULL; struct crypt_dm_active_device dmd = { .flags = flags @@ -779,24 +780,64 @@ int TCRYPT_activate(struct crypt_device *cd, else device_check = DEV_EXCL; - if ((params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) && - !crypt_dev_is_partition(device_path(crypt_data_device(cd)))) { - part_path = crypt_get_partition_device(device_path(crypt_data_device(cd)), - crypt_get_data_offset(cd), dmd.size); - if (part_path) { - if (!device_alloc(cd, &part_device, part_path)) { - log_verbose(cd, _("Activating TCRYPT system encryption for partition %s."), - part_path); - ptr_dev = part_device; + offset = crypt_get_data_offset(cd); + iv_offset = crypt_get_iv_offset(cd); + + /* + * System encryption is tricky, as the TCRYPT header is outside the partition area. + * It can be a system partition only (TCRYPT header offset contains MK offset to + * a particular partition) or the whole system (then MK offset starts on the header itself). + * IV offset is always partition offset, but device offset depends on whether the user + * copied the whole disk or just one encrypted partition. + * This code tries to guess the most common situations but can still fail and use wrong offsets. + * Recent UEFI systems never use whole system encryption. + */ + if (params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) { + if (crypt_dev_is_partition(device_path(crypt_data_device(cd)))) { + /* One partition */ + offset = 0; + iv_offset = crypt_dev_partition_offset(device_path(crypt_data_device(cd))); + } else if (crypt_dev_is_partition(device_path(crypt_metadata_device(cd)))) { + /* One partition image, header is the original partition */ + offset = 0; + iv_offset = crypt_dev_partition_offset(device_path(crypt_metadata_device(cd))); + } else { + /* No partition info, try partition-only mode searching for partition. */ + part_path = crypt_get_partition_device(device_path(crypt_data_device(cd)), + iv_offset, hdr->d.volume_size / SECTOR_SIZE); + if (!part_path) + part_path = crypt_get_partition_device(device_path(crypt_metadata_device(cd)), + iv_offset, hdr->d.volume_size / SECTOR_SIZE); + if (part_path) { + if (!device_alloc(cd, &part_device, part_path)) { + log_verbose(cd, _("Activating TCRYPT system encryption for partition %s."), + part_path); + ptr_dev = part_device; + offset = 0; + iv_offset = crypt_dev_partition_offset(part_path); + } + free(part_path); + } else if (device_is_identical(crypt_metadata_device(cd), crypt_data_device(cd))) { + /* + * We have no partition offset and TCRYPT system header is on the data device. + * Use the whole device mapping. + * There can be active partitions, do not use exclusive flag. + */ + device_check = DEV_OK; + dmd.size = hdr->d.volume_size / SECTOR_SIZE; + log_err(cd, _("Cannot determine TCRYPT system partition offset, activating whole encrypted area.")); + } else { + /* + * We have no partition offset and TCRYPT system header is on the metadata device + * (TCRYPT system header was NOT read from data device). + * Expect that data device is a copy of partition and not the whole device. + * This will not work for whole system encryption, though. + */ offset = 0; + log_err(cd, _("Cannot determine TCRYPT system partition offset, activating device as a system partition.")); } - free(part_path); - } else - /* - * System encryption use the whole device mapping, there can - * be active partitions. - */ - device_check = DEV_OK; + } + log_dbg(cd, "TCRYPT system encryption data_offset %" PRIu64 ", iv_offset %" PRIu64 ".", offset, iv_offset); } r = device_block_adjust(cd, ptr_dev, device_check, @@ -825,8 +866,16 @@ int TCRYPT_activate(struct crypt_device *cd, dmd.flags = flags | CRYPT_ACTIVATE_PRIVATE; } + key = crypt_safe_alloc(crypt_volume_key_length(vk)); + if (!key) { + r = -ENOMEM; + break; + } + TCRYPT_copy_key(&algs->cipher[i-1], algs->mode, - vk->key, hdr->d.keys); + key, hdr->d.keys); + + crypt_volume_key_pass_safe_alloc(vk, &key); if (algs->chain_count != i) { if (snprintf(dm_dev_name, sizeof(dm_dev_name), "%s/%s_%d", dm_get_dir(), name, i) < 0) { @@ -847,16 +896,13 @@ int TCRYPT_activate(struct crypt_device *cd, } r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, ptr_dev, vk, - cipher_spec, crypt_get_iv_offset(cd), offset, - crypt_get_integrity(cd), - crypt_get_integrity_tag_size(cd), - crypt_get_sector_size(cd)); + cipher_spec, iv_offset, offset, NULL, 0, 0, crypt_get_sector_size(cd)); if (r) break; log_dbg(cd, "Trying to activate TCRYPT device %s using cipher %s.", dm_name, dmd.segment.u.crypt.cipher); - r = dm_create_device(cd, dm_name, CRYPT_TCRYPT, &dmd); + r = dm_create_device(cd, dm_name, i == 1 ? CRYPT_TCRYPT : CRYPT_SUBDEV, &dmd); dm_targets_free(cd, &dmd); device_free(cd, device); @@ -873,12 +919,33 @@ int TCRYPT_activate(struct crypt_device *cd, } out: + crypt_safe_free(key); crypt_free_volume_key(vk); device_free(cd, device); device_free(cd, part_device); return r; } +static bool is_tcrypt_subdev(const char *dm_uuid, const char *base_uuid) +{ + const char *base_uuid_name; + + assert(base_uuid); + base_uuid_name = strchr(base_uuid, '-'); + + if (!dm_uuid || !base_uuid_name) + return false; + + if (!strncmp(dm_uuid, "SUBDEV-", 7)) + return !strncmp(dm_uuid + 6, base_uuid_name, strlen(base_uuid_name)); + + /* + * FIXME: Drop after shift to dependency based deactivation (CRYPT_SUBDEV) + * in later releases + */ + return !strncmp(dm_uuid, base_uuid, strlen(base_uuid)); +} + static int TCRYPT_remove_one(struct crypt_device *cd, const char *name, const char *base_uuid, int index, uint32_t flags) { @@ -894,7 +961,7 @@ static int TCRYPT_remove_one(struct crypt_device *cd, const char *name, return r; r = dm_query_device(cd, dm_name, DM_ACTIVE_UUID, &dmd); - if (!r && !strncmp(dmd.uuid, base_uuid, strlen(base_uuid))) + if (!r && is_tcrypt_subdev(dmd.uuid, base_uuid)) r = dm_remove_device(cd, dm_name, flags); free(CONST_CAST(void*)dmd.uuid); @@ -916,6 +983,7 @@ int TCRYPT_deactivate(struct crypt_device *cd, const char *name, uint32_t flags) if (r < 0) goto out; + /* FIXME: replace with dependency based deactivation (CRYPT_SUBDEV) in later releases */ r = TCRYPT_remove_one(cd, name, dmd.uuid, 1, flags); if (r < 0) goto out; @@ -934,8 +1002,10 @@ static int TCRYPT_status_one(struct crypt_device *cd, const char *name, { struct crypt_dm_active_device dmd; struct dm_target *tgt = &dmd.segment; - char dm_name[PATH_MAX], *c; + const char *c; + char dm_name[PATH_MAX]; int r; + size_t cipher_len = MAX_CIPHER_LEN; if (snprintf(dm_name, sizeof(dm_name), "%s_%d", name, index) < 0) return -ENOMEM; @@ -957,12 +1027,12 @@ static int TCRYPT_status_one(struct crypt_device *cd, const char *name, r = 0; - if (!strncmp(dmd.uuid, base_uuid, strlen(base_uuid))) { + if (is_tcrypt_subdev(dmd.uuid, base_uuid)) { if ((c = strchr(tgt->u.crypt.cipher, '-'))) - *c = '\0'; + cipher_len = c - tgt->u.crypt.cipher; strcat(cipher, "-"); - strncat(cipher, tgt->u.crypt.cipher, MAX_CIPHER_LEN); - *key_size += tgt->u.crypt.vk->keylength; + strncat(cipher, tgt->u.crypt.cipher, cipher_len); + *key_size += crypt_volume_key_length(tgt->u.crypt.vk); tcrypt_hdr->d.mk_offset = tgt->u.crypt.offset * SECTOR_SIZE; device_free(cd, *device); MOVE_REF(*device, tgt->data_device); @@ -981,7 +1051,7 @@ int TCRYPT_init_by_name(struct crypt_device *cd, const char *name, struct crypt_params_tcrypt *tcrypt_params, struct tcrypt_phdr *tcrypt_hdr) { - struct tcrypt_algs *algs; + const struct tcrypt_algs *algs; char cipher[MAX_CIPHER_LEN * 4], mode[MAX_CIPHER_LEN+1], *tmp; size_t key_size; int r; @@ -999,7 +1069,7 @@ int TCRYPT_init_by_name(struct crypt_device *cd, const char *name, mode[MAX_CIPHER_LEN] = '\0'; strncpy(mode, ++tmp, MAX_CIPHER_LEN); - key_size = tgt->u.crypt.vk->keylength; + key_size = crypt_volume_key_length(tgt->u.crypt.vk); r = TCRYPT_status_one(cd, name, uuid, 1, &key_size, cipher, tcrypt_hdr, device); if (!r) @@ -1028,9 +1098,7 @@ uint64_t TCRYPT_get_data_offset(struct crypt_device *cd, if (!hdr->d.version) { /* No real header loaded, initialized by active device, use default mk_offset */ } else if (params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) { - /* Mapping through whole device, not partition! */ - if (crypt_dev_is_partition(device_path(crypt_data_device(cd)))) - return 0; + /* Mapping through whole device or partition, return mk_offset */ } else if (params->mode && !strncmp(params->mode, "xts", 3)) { if (hdr->d.version < 3) return 1; @@ -1057,25 +1125,12 @@ uint64_t TCRYPT_get_iv_offset(struct crypt_device *cd, struct tcrypt_phdr *hdr, struct crypt_params_tcrypt *params) { - uint64_t iv_offset, partition_offset; - if (params->mode && !strncmp(params->mode, "xts", 3)) - iv_offset = TCRYPT_get_data_offset(cd, hdr, params); + return TCRYPT_get_data_offset(cd, hdr, params); else if (params->mode && !strncmp(params->mode, "lrw", 3)) - iv_offset = 0; - else - iv_offset = hdr->d.mk_offset / SECTOR_SIZE; + return 0; - if (params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) { - partition_offset = crypt_dev_partition_offset(device_path(crypt_data_device(cd))); - /* FIXME: we need to deal with overflow sooner */ - if (iv_offset > (UINT64_MAX - partition_offset)) - iv_offset = UINT64_MAX; - else - iv_offset += partition_offset; - } - - return iv_offset; + return hdr->d.mk_offset / SECTOR_SIZE; } int TCRYPT_get_volume_key(struct crypt_device *cd, @@ -1083,8 +1138,9 @@ int TCRYPT_get_volume_key(struct crypt_device *cd, struct crypt_params_tcrypt *params, struct volume_key **vk) { - struct tcrypt_algs *algs; + const struct tcrypt_algs *algs; unsigned int i, key_index; + void *key = NULL; if (!hdr->d.version) { log_err(cd, _("This function is not supported without TCRYPT header load.")); @@ -1095,16 +1151,22 @@ int TCRYPT_get_volume_key(struct crypt_device *cd, if (!algs) return -EINVAL; - *vk = crypt_alloc_volume_key(params->key_size, NULL); - if (!*vk) + key = crypt_safe_alloc(params->key_size); + if (!key) return -ENOMEM; for (i = 0, key_index = 0; i < algs->chain_count; i++) { TCRYPT_copy_key(&algs->cipher[i], algs->mode, - &(*vk)->key[key_index], hdr->d.keys); + &((char *)key)[key_index], hdr->d.keys); key_index += algs->cipher[i].key_size; } + *vk = crypt_alloc_volume_key_by_safe_alloc(&key); + if (!*vk) { + crypt_safe_free(key); + return -ENOMEM; + } + return 0; } @@ -1119,9 +1181,14 @@ int TCRYPT_dump(struct crypt_device *cd, log_std(cd, "Version: \t%d\n", hdr->d.version); log_std(cd, "Driver req.:\t%x.%x\n", hdr->d.version_tc >> 8, hdr->d.version_tc & 0xFF); - - log_std(cd, "Sector size:\t%" PRIu32 "\n", hdr->d.sector_size); - log_std(cd, "MK offset:\t%" PRIu64 "\n", hdr->d.mk_offset); + log_std(cd, "Flags: \t0x%x\n", hdr->d.flags); + + log_std(cd, "Sector size:\t%" PRIu32 " [bytes]\n", hdr->d.sector_size); + log_std(cd, "MK offset:\t%" PRIu64 " [bytes]\n", hdr->d.mk_offset); + if (hdr->d.volume_size) + log_std(cd, "Volume size:\t%" PRIu64 " [bytes]\n", hdr->d.volume_size); + if (hdr->d.hidden_volume_size) + log_std(cd, "Hidden size:\t%" PRIu64 " [bytes]\n", hdr->d.hidden_volume_size); log_std(cd, "PBKDF2 hash:\t%s\n", params->hash_name); } log_std(cd, "Cipher chain:\t%s\n", params->cipher); diff --git a/lib/tcrypt/tcrypt.h b/lib/tcrypt/tcrypt.h index 8efccb8..db3b723 100644 --- a/lib/tcrypt/tcrypt.h +++ b/lib/tcrypt/tcrypt.h @@ -2,8 +2,8 @@ /* * TCRYPT (TrueCrypt-compatible) header definition * - * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2012-2024 Milan Broz + * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Milan Broz */ #ifndef _CRYPTSETUP_TCRYPT_H diff --git a/lib/utils.c b/lib/utils.c index 7a6df4c..afde568 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include diff --git a/lib/utils_benchmark.c b/lib/utils_benchmark.c index 51f7e65..3694f27 100644 --- a/lib/utils_benchmark.c +++ b/lib/utils_benchmark.c @@ -2,8 +2,8 @@ /* * libcryptsetup - cryptsetup library, cipher benchmark * - * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2012-2024 Milan Broz + * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Milan Broz */ #include diff --git a/lib/utils_blkid.c b/lib/utils_blkid.c index a5cf5ad..44e501b 100644 --- a/lib/utils_blkid.c +++ b/lib/utils_blkid.c @@ -2,7 +2,7 @@ /* * blkid probe utilities * - * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2018-2025 Red Hat, Inc. All rights reserved. */ #include @@ -15,7 +15,7 @@ #include "utils_blkid.h" #include "utils_io.h" -#ifdef HAVE_BLKID +#if HAVE_BLKID #include /* make bad checksums flag optional */ @@ -26,7 +26,7 @@ struct blkid_handle { int fd; blkid_probe pr; }; -#ifndef HAVE_BLKID_WIPE +#if !HAVE_BLKID_WIPE static size_t crypt_getpagesize(void) { long r = sysconf(_SC_PAGESIZE); @@ -38,7 +38,7 @@ void blk_set_chains_for_wipes(struct blkid_handle *h) { blkid_probe_enable_partitions(h->pr, 1); blkid_probe_set_partitions_flags(h->pr, 0 -#ifdef HAVE_BLKID_WIPE +#if HAVE_BLKID_WIPE | BLKID_PARTS_MAGIC #endif ); @@ -198,10 +198,10 @@ void blk_free(struct blkid_handle *h) free(h); } -#ifndef HAVE_BLKID_WIPE +#if !HAVE_BLKID_WIPE static int blk_step_back(struct blkid_handle *h) { -#ifdef HAVE_BLKID_STEP_BACK +#if HAVE_BLKID_STEP_BACK return blkid_probe_step_back(h->pr); #else blkid_reset_probe(h->pr); @@ -213,7 +213,7 @@ static int blk_step_back(struct blkid_handle *h) int blk_do_wipe(struct blkid_handle *h) { -#ifdef HAVE_BLKID_WIPE +#if HAVE_BLKID_WIPE return blkid_do_wipe(h->pr, 0); #else const char *offset; diff --git a/lib/utils_blkid.h b/lib/utils_blkid.h index 6d7f70f..2112575 100644 --- a/lib/utils_blkid.h +++ b/lib/utils_blkid.h @@ -2,7 +2,7 @@ /* * blkid probe utilities * - * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2018-2025 Red Hat, Inc. All rights reserved. */ #ifndef _UTILS_BLKID_H diff --git a/lib/utils_crypt.c b/lib/utils_crypt.c index 31ac678..a5eca18 100644 --- a/lib/utils_crypt.c +++ b/lib/utils_crypt.c @@ -3,8 +3,8 @@ * utils_crypt - cipher utilities for cryptsetup * * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include @@ -95,7 +95,7 @@ int crypt_parse_hash_integrity_mode(const char *s, char *integrity) } int crypt_parse_integrity_mode(const char *s, char *integrity, - int *integrity_key_size) + int *integrity_key_size, int required_key_size) { int ks = 0, r = 0; @@ -108,18 +108,37 @@ int crypt_parse_integrity_mode(const char *s, char *integrity, !strcmp(s, "none")) { strncpy(integrity, s, MAX_CIPHER_LEN); ks = 0; + if (required_key_size != ks) + r = -EINVAL; } else if (!strcmp(s, "hmac-sha1")) { strncpy(integrity, "hmac(sha1)", MAX_CIPHER_LEN); - ks = 20; + ks = required_key_size ?: 20; } else if (!strcmp(s, "hmac-sha256")) { strncpy(integrity, "hmac(sha256)", MAX_CIPHER_LEN); - ks = 32; + ks = required_key_size ?: 32; } else if (!strcmp(s, "hmac-sha512")) { - ks = 64; strncpy(integrity, "hmac(sha512)", MAX_CIPHER_LEN); + ks = required_key_size ?: 64; + } else if (!strcmp(s, "phmac-sha1")) { + strncpy(integrity, "phmac(sha1)", MAX_CIPHER_LEN); + ks = required_key_size; + if (!required_key_size) + r = -EINVAL; + } else if (!strcmp(s, "phmac-sha256")) { + strncpy(integrity, "phmac(sha256)", MAX_CIPHER_LEN); + ks = required_key_size; + if (!required_key_size) + r = -EINVAL; + } else if (!strcmp(s, "phmac-sha512")) { + strncpy(integrity, "phmac(sha512)", MAX_CIPHER_LEN); + ks = required_key_size; + if (!required_key_size) + r = -EINVAL; } else if (!strcmp(s, "cmac-aes")) { - ks = 16; strncpy(integrity, "cmac(aes)", MAX_CIPHER_LEN); + ks = 16; + if (required_key_size && required_key_size != ks) + r = -EINVAL; } else r = -EINVAL; diff --git a/lib/utils_crypt.h b/lib/utils_crypt.h index 470cbeb..21cf4b5 100644 --- a/lib/utils_crypt.h +++ b/lib/utils_crypt.h @@ -3,8 +3,8 @@ * utils_crypt - cipher utilities for cryptsetup * * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #ifndef _UTILS_CRYPT_H @@ -28,7 +28,7 @@ int crypt_parse_name_and_mode(const char *s, char *cipher, int *key_nums, char *cipher_mode); int crypt_parse_hash_integrity_mode(const char *s, char *integrity); int crypt_parse_integrity_mode(const char *s, char *integrity, - int *integrity_key_size); + int *integrity_key_size, int required_key_size); int crypt_parse_pbkdf(const char *s, const char **pbkdf); ssize_t crypt_hex_to_bytes(const char *hex, char **result, int safe_alloc); diff --git a/lib/utils_device.c b/lib/utils_device.c index 6b7af6e..1cdbcc6 100644 --- a/lib/utils_device.c +++ b/lib/utils_device.c @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include @@ -16,10 +16,10 @@ #include #include #include -#ifdef HAVE_SYS_SYSMACROS_H +#if HAVE_SYS_SYSMACROS_H # include /* for major, minor */ #endif -#ifdef HAVE_SYS_STATVFS_H +#if HAVE_SYS_STATVFS_H # include #endif #include "internal.h" @@ -48,19 +48,19 @@ struct device { static size_t device_fs_block_size_fd(int fd) { - size_t page_size = crypt_getpagesize(); + size_t max_size = MAX_SECTOR_SIZE; -#ifdef HAVE_SYS_STATVFS_H +#if HAVE_SYS_STATVFS_H struct statvfs buf; /* * NOTE: some filesystems (NFS) returns bogus blocksize (1MB). * Page-size io should always work and avoids increasing IO beyond aligned LUKS header. */ - if (!fstatvfs(fd, &buf) && buf.f_bsize && buf.f_bsize <= page_size) + if (!fstatvfs(fd, &buf) && buf.f_bsize && buf.f_bsize <= max_size) return (size_t)buf.f_bsize; #endif - return page_size; + return max_size; } static size_t device_block_size_fd(int fd, size_t *min_size) @@ -127,19 +127,19 @@ static size_t device_alignment_fd(int devfd) return (size_t)alignment; } -static int device_read_test(struct crypt_device *cd, int devfd, struct device *device) +static int device_read_test(struct crypt_device *cd, int devfd) { char buffer[512]; - int r = -EIO; + int r; size_t minsize = 0, blocksize, alignment; - const char *dm_name; + struct stat st; + + /* skip check for block devices, direct-io must work there */ + if (fstat(devfd, &st) < 0) + return -EINVAL; - /* skip check for suspended DM devices */ - dm_name = device_dm_name(device); - if (dm_name && dm_status_suspended(cd, dm_name) > 0) { - log_dbg(cd, "Device %s is suspended, assuming direct-io is supported.", dm_name); + if (S_ISBLK(st.st_mode)) return 0; - } blocksize = device_block_size_fd(devfd, &minsize); alignment = device_alignment_fd(devfd); @@ -153,10 +153,13 @@ static int device_read_test(struct crypt_device *cd, int devfd, struct device *d if (minsize > sizeof(buffer)) minsize = sizeof(buffer); - if (read_blockwise(devfd, blocksize, alignment, buffer, minsize) == (ssize_t)minsize) + if (read_blockwise(devfd, blocksize, alignment, buffer, minsize) == (ssize_t)minsize) { + log_dbg(cd, "Direct-io read works."); r = 0; - - log_dbg(cd, "Direct-io is supported and works."); + } else { + log_dbg(cd, "Direct-io read failed."); + r = -EIO; + } crypt_safe_memzero(buffer, sizeof(buffer)); return r; @@ -180,12 +183,12 @@ static int device_ready(struct crypt_device *cd, struct device *device) return -EINVAL; if (device->o_direct) { - log_dbg(cd, "Trying to open and read device %s with direct-io.", + log_dbg(cd, "Trying to open device %s with direct-io.", device_path(device)); device->o_direct = 0; devfd = open(device_path(device), O_RDONLY | O_DIRECT); if (devfd >= 0) { - if (device_read_test(cd, devfd, device) == 0) { + if (device_read_test(cd, devfd) == 0) { device->o_direct = 1; } else { close(devfd); @@ -473,21 +476,6 @@ const char *device_block_path(const struct device *device) return device->path; } -/* Get device-mapper name of device (if possible) */ -const char *device_dm_name(const struct device *device) -{ - const char *dmdir = dm_get_dir(); - size_t dmdir_len = strlen(dmdir); - - if (!device) - return NULL; - - if (strncmp(device->path, dmdir, dmdir_len)) - return NULL; - - return &device->path[dmdir_len+1]; -} - /* Get path to device / file */ const char *device_path(const struct device *device) { @@ -530,7 +518,7 @@ void device_topology_alignment(struct crypt_device *cd, /* minimum io size */ if (ioctl(fd, BLKIOMIN, &min_io_size) == -1) { - log_dbg(cd, "Topology info for %s not supported, using default offset %lu bytes.", + log_dbg(cd, "Topology info for %s not supported, using default alignment %lu bytes.", device->path, default_alignment); goto out; } @@ -1014,6 +1002,36 @@ int device_is_zoned(struct device *device) return crypt_dev_is_zoned(major(st.st_rdev), minor(st.st_rdev)); } +int device_is_nop_dif(struct device *device, uint32_t *tag_size) +{ + char *base_device_path; + int r; + struct stat st; + + if (!device) + return -EINVAL; + + /* + * For partition devices, check integrity profile on the base device. + * Partition device nodes don't advertise integrity profile directly + * via sysfs attributes. + */ + base_device_path = crypt_get_base_device(device_path(device)); + if (base_device_path) { + r = stat(base_device_path, &st); + free(base_device_path); + } else + r = stat(device_path(device), &st); + + if (r < 0) + return -EINVAL; + + if (!S_ISBLK(st.st_mode)) + return 0; + + return crypt_dev_is_nop_dif(major(st.st_rdev), minor(st.st_rdev), tag_size); +} + size_t device_alignment(struct device *device) { int devfd; diff --git a/lib/utils_device_locking.c b/lib/utils_device_locking.c index 57a467d..8859223 100644 --- a/lib/utils_device_locking.c +++ b/lib/utils_device_locking.c @@ -2,8 +2,8 @@ /* * Metadata on-disk locking for processes serialization * - * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2024 Ondrej Kozina + * Copyright (C) 2016-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2025 Ondrej Kozina */ #include @@ -15,7 +15,7 @@ #include #include #include -#ifdef HAVE_SYS_SYSMACROS_H +#if HAVE_SYS_SYSMACROS_H # include /* for major, minor */ #endif #include diff --git a/lib/utils_device_locking.h b/lib/utils_device_locking.h index 4287dbf..9cc25ca 100644 --- a/lib/utils_device_locking.h +++ b/lib/utils_device_locking.h @@ -2,8 +2,8 @@ /* * Metadata on-disk locking for processes serialization * - * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2024 Ondrej Kozina + * Copyright (C) 2016-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2025 Ondrej Kozina */ #ifndef _CRYPTSETUP_UTILS_LOCKING_H diff --git a/lib/utils_devpath.c b/lib/utils_devpath.c index 2c94048..28039f0 100644 --- a/lib/utils_devpath.c +++ b/lib/utils_devpath.c @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include @@ -17,7 +17,7 @@ #include #include #include -#ifdef HAVE_SYS_SYSMACROS_H +#if HAVE_SYS_SYSMACROS_H # include /* for major, minor */ #endif #include "internal.h" @@ -262,6 +262,29 @@ int crypt_dev_is_zoned(int major, int minor) return strncmp(buf, "none", 4) ? 1 : 0; } +int crypt_dev_is_nop_dif(int major, int minor, uint32_t *tag_size) +{ + char buf[64] = {}; + uint64_t val = 0; + + if (!_sysfs_get_string(major, minor, buf, sizeof(buf), "integrity/format")) + return 0; + + if (strncmp(buf, "nop", 3)) + return 0; + + /* this field is currently supported only for NVMe */ + _sysfs_get_uint64(major, minor, &val, "metadata_bytes"); + + /* tag_size should be 0, but it is set by dm-integrity, try it as a fallback */ + if (val == 0) + _sysfs_get_uint64(major, minor, &val, "integrity/tag_size"); + + /* we can still return 0 and support metadata, caller must handle it */ + *tag_size = (uint32_t)val; + return 1; +} + int crypt_dev_is_partition(const char *dev_path) { uint64_t val; @@ -420,7 +443,7 @@ int lookup_by_disk_id(const char *dm_uuid) { struct dirent *entry; struct stat st; - int r = 0; /* not found */ + int dfd, r = 0; /* not found */ DIR *dir = opendir("/dev/disk/by-id"); if (!dir) @@ -432,7 +455,8 @@ int lookup_by_disk_id(const char *dm_uuid) !strncmp(entry->d_name, "..", 2)) continue; - if (fstatat(dirfd(dir), entry->d_name, &st, AT_SYMLINK_NOFOLLOW)) { + dfd = dirfd(dir); + if (dfd < 0 || fstatat(dfd, entry->d_name, &st, AT_SYMLINK_NOFOLLOW)) { r = -EINVAL; break; } @@ -457,7 +481,7 @@ int lookup_by_sysfs_uuid_field(const char *dm_uuid) char subpath[PATH_MAX], uuid[DM_UUID_LEN]; ssize_t s; struct stat st; - int fd, len, r = 0; /* not found */ + int fd, dfd, len, r = 0; /* not found */ DIR *dir = opendir("/sys/block/"); if (!dir) @@ -476,7 +500,10 @@ int lookup_by_sysfs_uuid_field(const char *dm_uuid) } /* looking for dm-X/dm/uuid file, symlinks are fine */ - fd = openat(dirfd(dir), subpath, O_RDONLY | O_CLOEXEC); + dfd = dirfd(dir); + if (dfd < 0) + continue; + fd = openat(dfd, subpath, O_RDONLY | O_CLOEXEC); if (fd < 0) continue; diff --git a/lib/utils_dm.h b/lib/utils_dm.h index 29ff777..4a55811 100644 --- a/lib/utils_dm.h +++ b/lib/utils_dm.h @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #ifndef _UTILS_DM_H @@ -22,72 +22,71 @@ struct device; struct crypt_params_integrity; /* Device mapper internal flags */ -#define DM_RESUME_PRIVATE (1 << 4) /* CRYPT_ACTIVATE_PRIVATE */ -#define DM_SUSPEND_SKIP_LOCKFS (1 << 5) -#define DM_SUSPEND_WIPE_KEY (1 << 6) -#define DM_SUSPEND_NOFLUSH (1 << 7) +#define DM_RESUME_PRIVATE (UINT64_C(1) << 4) /* CRYPT_ACTIVATE_PRIVATE */ +#define DM_SUSPEND_SKIP_LOCKFS (UINT64_C(1) << 5) +#define DM_SUSPEND_WIPE_KEY (UINT64_C(1) << 6) +#define DM_SUSPEND_NOFLUSH (UINT64_C(1) << 7) -static inline uint32_t act2dmflags(uint32_t act_flags) +static inline uint64_t act2dmflags(uint64_t act_flags) { return (act_flags & DM_RESUME_PRIVATE); } /* Device mapper backend - kernel support flags */ -#define DM_KEY_WIPE_SUPPORTED (1 << 0) /* key wipe message */ -#define DM_LMK_SUPPORTED (1 << 1) /* lmk mode */ -#define DM_SECURE_SUPPORTED (1 << 2) /* wipe (secure) buffer flag */ -#define DM_PLAIN64_SUPPORTED (1 << 3) /* plain64 IV */ -#define DM_DISCARDS_SUPPORTED (1 << 4) /* discards/TRIM option is supported */ -#define DM_VERITY_SUPPORTED (1 << 5) /* dm-verity target supported */ -#define DM_TCW_SUPPORTED (1 << 6) /* tcw (TCRYPT CBC with whitening) */ -#define DM_SAME_CPU_CRYPT_SUPPORTED (1 << 7) /* same_cpu_crypt */ -#define DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED (1 << 8) /* submit_from_crypt_cpus */ -#define DM_VERITY_ON_CORRUPTION_SUPPORTED (1 << 9) /* ignore/restart_on_corruption, ignore_zero_block */ -#define DM_VERITY_FEC_SUPPORTED (1 << 10) /* Forward Error Correction (FEC) */ -#define DM_KERNEL_KEYRING_SUPPORTED (1 << 11) /* dm-crypt allows loading kernel keyring keys */ -#define DM_INTEGRITY_SUPPORTED (1 << 12) /* dm-integrity target supported */ -#define DM_SECTOR_SIZE_SUPPORTED (1 << 13) /* support for sector size setting in dm-crypt/dm-integrity */ -#define DM_CAPI_STRING_SUPPORTED (1 << 14) /* support for cryptoapi format cipher definition */ -#define DM_DEFERRED_SUPPORTED (1 << 15) /* deferred removal of device */ -#define DM_INTEGRITY_RECALC_SUPPORTED (1 << 16) /* dm-integrity automatic recalculation supported */ -#define DM_INTEGRITY_BITMAP_SUPPORTED (1 << 17) /* dm-integrity bitmap mode supported */ -#define DM_GET_TARGET_VERSION_SUPPORTED (1 << 18) /* dm DM_GET_TARGET version ioctl supported */ -#define DM_INTEGRITY_FIX_PADDING_SUPPORTED (1 << 19) /* supports the parameter fix_padding that fixes a bug that caused excessive padding */ -#define DM_BITLK_EBOIV_SUPPORTED (1 << 20) /* EBOIV for BITLK supported */ -#define DM_BITLK_ELEPHANT_SUPPORTED (1 << 21) /* Elephant diffuser for BITLK supported */ -#define DM_VERITY_SIGNATURE_SUPPORTED (1 << 22) /* Verity option root_hash_sig_key_desc supported */ -#define DM_INTEGRITY_DISCARDS_SUPPORTED (1 << 23) /* dm-integrity discards/TRIM option is supported */ -#define DM_INTEGRITY_RESIZE_SUPPORTED (1 << 23) /* dm-integrity resize of the integrity device supported (introduced in the same version as discards)*/ -#define DM_VERITY_PANIC_CORRUPTION_SUPPORTED (1 << 24) /* dm-verity panic on corruption */ -#define DM_CRYPT_NO_WORKQUEUE_SUPPORTED (1 << 25) /* dm-crypt support for bypassing workqueues */ -#define DM_INTEGRITY_FIX_HMAC_SUPPORTED (1 << 26) /* hmac covers also superblock */ -#define DM_INTEGRITY_RESET_RECALC_SUPPORTED (1 << 27) /* dm-integrity automatic recalculation supported */ -#define DM_VERITY_TASKLETS_SUPPORTED (1 << 28) /* dm-verity tasklets supported */ +#define DM_KEY_WIPE_SUPPORTED (UINT64_C(1) << 0) /* key wipe message */ +#define DM_LMK_SUPPORTED (UINT64_C(1) << 1) /* lmk mode */ +#define DM_SECURE_SUPPORTED (UINT64_C(1) << 2) /* wipe (secure) buffer flag */ +#define DM_PLAIN64_SUPPORTED (UINT64_C(1) << 3) /* plain64 IV */ +#define DM_DISCARDS_SUPPORTED (UINT64_C(1) << 4) /* discards/TRIM option is supported */ +#define DM_VERITY_SUPPORTED (UINT64_C(1) << 5) /* dm-verity target supported */ +#define DM_TCW_SUPPORTED (UINT64_C(1) << 6) /* tcw (TCRYPT CBC with whitening) */ +#define DM_SAME_CPU_CRYPT_SUPPORTED (UINT64_C(1) << 7) /* same_cpu_crypt */ +#define DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED (UINT64_C(1) << 8) /* submit_from_crypt_cpus */ +#define DM_VERITY_ON_CORRUPTION_SUPPORTED (UINT64_C(1) << 9) /* ignore/restart_on_corruption, ignore_zero_block */ +#define DM_VERITY_FEC_SUPPORTED (UINT64_C(1) << 10) /* Forward Error Correction (FEC) */ +#define DM_KERNEL_KEYRING_SUPPORTED (UINT64_C(1) << 11) /* dm-crypt allows loading kernel keyring keys */ +#define DM_INTEGRITY_SUPPORTED (UINT64_C(1) << 12) /* dm-integrity target supported */ +#define DM_SECTOR_SIZE_SUPPORTED (UINT64_C(1) << 13) /* support for sector size setting in dm-crypt/dm-integrity */ +#define DM_CAPI_STRING_SUPPORTED (UINT64_C(1) << 14) /* support for cryptoapi format cipher definition */ +#define DM_DEFERRED_SUPPORTED (UINT64_C(1) << 15) /* deferred removal of device */ +#define DM_INTEGRITY_RECALC_SUPPORTED (UINT64_C(1) << 16) /* dm-integrity automatic recalculation supported */ +#define DM_INTEGRITY_BITMAP_SUPPORTED (UINT64_C(1) << 17) /* dm-integrity bitmap mode supported */ +#define DM_GET_TARGET_VERSION_SUPPORTED (UINT64_C(1) << 18) /* dm DM_GET_TARGET version ioctl supported */ +#define DM_INTEGRITY_FIX_PADDING_SUPPORTED (UINT64_C(1) << 19) /* supports the parameter fix_padding that fixes a bug that caused excessive padding */ +#define DM_BITLK_EBOIV_SUPPORTED (UINT64_C(1) << 20) /* EBOIV for BITLK supported */ +#define DM_BITLK_ELEPHANT_SUPPORTED (UINT64_C(1) << 21) /* Elephant diffuser for BITLK supported */ +#define DM_VERITY_SIGNATURE_SUPPORTED (UINT64_C(1) << 22) /* Verity option root_hash_sig_key_desc supported */ +#define DM_INTEGRITY_DISCARDS_SUPPORTED (UINT64_C(1) << 23) /* dm-integrity discards/TRIM option is supported */ +#define DM_INTEGRITY_RESIZE_SUPPORTED (UINT64_C(1) << 23) /* dm-integrity resize of the integrity device supported (introduced in the same version as discards)*/ +#define DM_VERITY_PANIC_CORRUPTION_SUPPORTED (UINT64_C(1) << 24) /* dm-verity panic on corruption */ +#define DM_CRYPT_NO_WORKQUEUE_SUPPORTED (UINT64_C(1) << 25) /* dm-crypt support for bypassing workqueues */ +#define DM_INTEGRITY_FIX_HMAC_SUPPORTED (UINT64_C(1) << 26) /* hmac covers also superblock */ +#define DM_INTEGRITY_RESET_RECALC_SUPPORTED (UINT64_C(1) << 27) /* dm-integrity automatic recalculation supported */ +#define DM_VERITY_TASKLETS_SUPPORTED (UINT64_C(1) << 28) /* dm-verity tasklets supported */ +#define DM_CRYPT_HIGH_PRIORITY_SUPPORTED (UINT64_C(1) << 29) /* dm-crypt high priority workqueue flag supported */ +#define DM_CRYPT_INTEGRITY_KEY_SIZE_OPT_SUPPORTED (UINT64_C(1) << 30) /* dm-crypt support for integrity_key_size option */ +#define DM_VERITY_ERROR_AS_CORRUPTION_SUPPORTED (UINT64_C(1) << 31) /* dm-verity restart/panic on corruption supported */ +#define DM_INTEGRITY_INLINE_MODE_SUPPORTED (UINT64_C(1) << 32) /* dm-integrity inline mode supported */ typedef enum { DM_CRYPT = 0, DM_VERITY, DM_INTEGRITY, DM_LINEAR, DM_ERROR, DM_ZERO, DM_UNKNOWN } dm_target_type; enum tdirection { TARGET_EMPTY = 0, TARGET_SET, TARGET_QUERY }; -int dm_flags(struct crypt_device *cd, dm_target_type target, uint32_t *flags); - -#define DM_ACTIVE_DEVICE (1 << 0) -#define DM_ACTIVE_UUID (1 << 1) -#define DM_ACTIVE_HOLDERS (1 << 2) - -#define DM_ACTIVE_CRYPT_CIPHER (1 << 3) -#define DM_ACTIVE_CRYPT_KEYSIZE (1 << 4) -#define DM_ACTIVE_CRYPT_KEY (1 << 5) - -#define DM_ACTIVE_VERITY_ROOT_HASH (1 << 6) -#define DM_ACTIVE_VERITY_HASH_DEVICE (1 << 7) -#define DM_ACTIVE_VERITY_PARAMS (1 << 8) - -#define DM_ACTIVE_INTEGRITY_PARAMS (1 << 9) - -#define DM_ACTIVE_JOURNAL_CRYPT_KEY (1 << 10) -#define DM_ACTIVE_JOURNAL_CRYPT_KEYSIZE (1 << 11) - -#define DM_ACTIVE_JOURNAL_MAC_KEY (1 << 12) -#define DM_ACTIVE_JOURNAL_MAC_KEYSIZE (1 << 13) +int dm_flags(struct crypt_device *cd, dm_target_type target, uint64_t *flags); + +#define DM_ACTIVE_DEVICE (UINT64_C(1) << 0) +#define DM_ACTIVE_UUID (UINT64_C(1) << 1) +#define DM_ACTIVE_HOLDERS (UINT64_C(1) << 2) +#define DM_ACTIVE_CRYPT_CIPHER (UINT64_C(1) << 3) +#define DM_ACTIVE_CRYPT_KEYSIZE (UINT64_C(1) << 4) +#define DM_ACTIVE_CRYPT_KEY (UINT64_C(1) << 5) +#define DM_ACTIVE_VERITY_ROOT_HASH (UINT64_C(1) << 6) +#define DM_ACTIVE_VERITY_HASH_DEVICE (UINT64_C(1) << 7) +#define DM_ACTIVE_VERITY_PARAMS (UINT64_C(1) << 8) +#define DM_ACTIVE_INTEGRITY_PARAMS (UINT64_C(1) << 9) +#define DM_ACTIVE_JOURNAL_CRYPT_KEY (UINT64_C(1) << 10) +#define DM_ACTIVE_JOURNAL_CRYPT_KEYSIZE (UINT64_C(1) << 11) +#define DM_ACTIVE_JOURNAL_MAC_KEY (UINT64_C(1) << 12) +#define DM_ACTIVE_JOURNAL_MAC_KEYSIZE (UINT64_C(1) << 13) struct dm_target { dm_target_type type; @@ -108,6 +107,7 @@ struct dm_target { uint64_t iv_offset; /* IV initialisation sector */ uint32_t tag_size; /* additional on-disk tag size */ uint32_t sector_size; /* encryption sector size */ + uint32_t integrity_key_size; /* explicit integrity key size (HMAC), 0 for default */ } crypt; struct { struct device *hash_device; @@ -182,8 +182,9 @@ void dm_targets_free(struct crypt_device *cd, struct crypt_dm_active_device *dmd int dm_crypt_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg_size, struct device *data_device, struct volume_key *vk, const char *cipher, - uint64_t iv_offset, uint64_t data_offset, const char *integrity, - uint32_t tag_size, uint32_t sector_size); + uint64_t iv_offset, uint64_t data_offset, + const char *integrity, uint32_t integrity_key_size, uint32_t tag_size, + uint32_t sector_size); int dm_verity_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg_size, struct device *data_device, struct device *hash_device, struct device *fec_device, const char *root_hash, uint32_t root_hash_size, const char* root_hash_sig_key_desc, @@ -205,15 +206,15 @@ int dm_status_suspended(struct crypt_device *cd, const char *name); int dm_status_verity_ok(struct crypt_device *cd, const char *name); int dm_status_integrity_failures(struct crypt_device *cd, const char *name, uint64_t *count); int dm_query_device(struct crypt_device *cd, const char *name, - uint32_t get_flags, struct crypt_dm_active_device *dmd); + uint64_t get_flags, struct crypt_dm_active_device *dmd); int dm_device_deps(struct crypt_device *cd, const char *name, const char *prefix, char **names, size_t names_length); int dm_create_device(struct crypt_device *cd, const char *name, const char *type, struct crypt_dm_active_device *dmd); int dm_reload_device(struct crypt_device *cd, const char *name, - struct crypt_dm_active_device *dmd, uint32_t dmflags, unsigned resume); -int dm_suspend_device(struct crypt_device *cd, const char *name, uint32_t dmflags); -int dm_resume_device(struct crypt_device *cd, const char *name, uint32_t dmflags); + struct crypt_dm_active_device *dmd, uint64_t dmflags, unsigned resume); +int dm_suspend_device(struct crypt_device *cd, const char *name, uint64_t dmflags); +int dm_resume_device(struct crypt_device *cd, const char *name, uint64_t dmflags); int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name, const struct volume_key *vk); int dm_error_device(struct crypt_device *cd, const char *name); @@ -222,6 +223,11 @@ int dm_cancel_deferred_removal(const char *name); const char *dm_get_dir(void); int dm_get_iname(const char *name, char **iname, bool with_path); +char *dm_get_active_iname(struct crypt_device *cd, const char *name); + +int dm_uuid_cmp(const char *dm_uuid, const char *hdr_uuid); +int dm_uuid_type_cmp(const char *dm_uuid, const char *type); +int dm_uuid_integrity_cmp(const char *dm_uuid, const char *dmi_uuid); int lookup_dm_dev_by_uuid(struct crypt_device *cd, const char *uuid, const char *type); diff --git a/lib/utils_io.c b/lib/utils_io.c index 1e47673..801f6d6 100644 --- a/lib/utils_io.c +++ b/lib/utils_io.c @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include @@ -278,7 +278,7 @@ ssize_t read_lseek_blockwise(int fd, size_t bsize, size_t alignment, length -= innerCount; } - ret = read_blockwise(fd, bsize, alignment, buf, length); + ret = length ? read_blockwise(fd, bsize, alignment, buf, length) : 0; if (ret >= 0) ret += innerCount; out: diff --git a/lib/utils_io.h b/lib/utils_io.h index 404c0f5..f80cf17 100644 --- a/lib/utils_io.h +++ b/lib/utils_io.h @@ -4,8 +4,8 @@ * * Copyright (C) 2004 Jana Saout * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #ifndef _CRYPTSETUP_UTILS_IO_H diff --git a/lib/utils_keyring.c b/lib/utils_keyring.c index 4a0cc53..73df346 100644 --- a/lib/utils_keyring.c +++ b/lib/utils_keyring.c @@ -2,8 +2,8 @@ /* * kernel keyring utilities * - * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2024 Ondrej Kozina + * Copyright (C) 2016-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2025 Ondrej Kozina */ #include @@ -21,17 +21,17 @@ #include "libcryptsetup_macros.h" #include "utils_keyring.h" -#ifdef KERNEL_KEYRING +#if KERNEL_KEYRING static const struct { key_type_t type; const char *type_name; } key_types[] = { - { LOGON_KEY, "logon" }, - { USER_KEY, "user" }, - { BIG_KEY, "big_key" }, - { TRUSTED_KEY, "trusted" }, - { ENCRYPTED_KEY, "encrypted" }, + { LOGON_KEY, "logon" }, + { USER_KEY, "user" }, + { BIG_KEY, "big_key" }, + { TRUSTED_KEY, "trusted" }, + { ENCRYPTED_KEY, "encrypted" }, }; #include @@ -150,7 +150,11 @@ static key_serial_t find_key_by_type_and_desc(const char *type, const char *desc do { id = request_key(type, desc, NULL, 0); } while (id < 0 && errno == EINTR); - if (id >= 0 || errno == ENOMEM) + + if (id < 0 && errno == ENOMEM) + return 0; + + if (id >= 0) return id; f = open("/proc/keys", O_RDONLY); @@ -191,7 +195,7 @@ int keyring_check(void) return syscall(__NR_request_key, "logon", "dummy", NULL, 0) == -1l && errno != ENOSYS; } -static key_serial_t keyring_add_key_in_keyring(key_type_t ktype, +key_serial_t keyring_add_key_to_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size, @@ -207,7 +211,7 @@ static key_serial_t keyring_add_key_in_keyring(key_type_t ktype, key_serial_t keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size) { - return keyring_add_key_in_keyring(ktype, key_desc, key, key_size, KEY_SPEC_THREAD_KEYRING); + return keyring_add_key_to_keyring(ktype, key_desc, key, key_size, KEY_SPEC_THREAD_KEYRING); } key_serial_t keyring_request_key_id(key_type_t key_type, @@ -222,29 +226,45 @@ key_serial_t keyring_request_key_id(key_type_t key_type, return kid; } +int keyring_read_keysize(key_serial_t kid, + size_t *r_key_size) +{ + long r; + + assert(r_key_size); + + /* just get payload size */ + r = keyctl_read(kid, NULL, 0); + if (r > 0) { + *r_key_size = r; + return 0; + } + + return -EINVAL; +} + int keyring_read_key(key_serial_t kid, char **key, size_t *key_size) { - long r; + int r; + size_t len; char *buf = NULL; - size_t len = 0; assert(key); assert(key_size); /* just get payload size */ - r = keyctl_read(kid, NULL, 0); - if (r > 0) { - len = r; - buf = crypt_safe_alloc(len); - if (!buf) - return -ENOMEM; + r = keyring_read_keysize(kid, &len); + if (r < 0) + return r; - /* retrieve actual payload data */ - r = keyctl_read(kid, buf, len); - } + buf = crypt_safe_alloc(len); + if (!buf) + return -ENOMEM; + /* retrieve actual payload data */ + r = keyctl_read(kid, buf, len); if (r < 0) { crypt_safe_free(buf); return -EINVAL; @@ -277,6 +297,37 @@ const char *key_type_name(key_type_t type) return NULL; } +key_type_t keyring_type_and_name(const char *key_name, const char **name) +{ + const char *name_tmp; + char type[16]; + size_t type_len; + + if (!key_name || key_name[0] != '%') + return INVALID_KEY; + + key_name++; + if (!*key_name || *key_name == ':') + return INVALID_KEY; + + name_tmp = strchr(key_name, ':'); + if (!name_tmp) + return INVALID_KEY; + name_tmp++; + + type_len = name_tmp - key_name - 1; + if (type_len >= sizeof(type) - 1) + return INVALID_KEY; + + memcpy(type, key_name, type_len); + type[type_len] = '\0'; + + if (name) + *name = name_tmp; + + return key_type_by_name(type); +} + key_serial_t keyring_find_key_id_by_name(const char *key_name) { key_serial_t id = 0; @@ -333,8 +384,7 @@ key_serial_t keyring_find_key_id_by_name(const char *key_name) id = 0; out: - if (name_copy) - free(name_copy); + free(name_copy); return id; } @@ -375,20 +425,6 @@ key_type_t key_type_by_name(const char *name) return INVALID_KEY; } -key_serial_t keyring_add_key_to_custom_keyring(key_type_t ktype, - const char *key_desc, - const void *key, - size_t key_size, - key_serial_t keyring_to_link) -{ - const char *type_name = key_type_name(ktype); - - if (!type_name || !key_desc) - return -EINVAL; - - return add_key(type_name, key_desc, key, key_size, keyring_to_link); -} - #else /* KERNEL_KEYRING */ #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -408,6 +444,12 @@ key_serial_t keyring_request_key_id(key_type_t key_type, return -ENOTSUP; } +int keyring_read_keysize(key_serial_t kid, + size_t *r_key_size) +{ + return -ENOTSUP; +} + int keyring_read_key(key_serial_t kid, char **key, size_t *key_size) @@ -425,6 +467,11 @@ const char *key_type_name(key_type_t type) return NULL; } +key_type_t keyring_type_and_name(const char *key_name, const char **name) +{ + return INVALID_KEY; +} + key_serial_t keyring_find_key_id_by_name(const char *key_name) { return 0; @@ -440,11 +487,11 @@ key_type_t key_type_by_name(const char *name) return INVALID_KEY; } -key_serial_t keyring_add_key_to_custom_keyring(key_type_t ktype, - const char *key_desc, - const void *key, - size_t key_size, - key_serial_t keyring_to_link) +key_serial_t keyring_add_key_to_keyring(key_type_t ktype, + const char *key_desc, + const void *key, + size_t key_size, + key_serial_t keyring_to_link) { return -ENOTSUP; } diff --git a/lib/utils_keyring.h b/lib/utils_keyring.h index 33d8f22..67debf6 100644 --- a/lib/utils_keyring.h +++ b/lib/utils_keyring.h @@ -2,8 +2,8 @@ /* * kernel keyring syscall wrappers * - * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2024 Ondrej Kozina + * Copyright (C) 2016-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2025 Ondrej Kozina */ #ifndef _UTILS_KEYRING @@ -21,6 +21,7 @@ typedef enum { LOGON_KEY = 0, USER_KEY, BIG_KEY, TRUSTED_KEY, ENCRYPTED_KEY, INV const char *key_type_name(key_type_t ktype); key_type_t key_type_by_name(const char *name); +key_type_t keyring_type_and_name(const char *key_name, const char **name); key_serial_t keyring_find_key_id_by_name(const char *key_name); key_serial_t keyring_find_keyring_id_by_name(const char *keyring_name); @@ -29,6 +30,9 @@ int keyring_check(void); key_serial_t keyring_request_key_id(key_type_t key_type, const char *key_description); +int keyring_read_keysize(key_serial_t kid, + size_t *r_key_size); + int keyring_read_key(key_serial_t kid, char **key, size_t *key_size); @@ -39,7 +43,7 @@ key_serial_t keyring_add_key_in_thread_keyring( const void *key, size_t key_size); -key_serial_t keyring_add_key_to_custom_keyring(key_type_t ktype, const char *key_desc, const void *key, +key_serial_t keyring_add_key_to_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size, key_serial_t keyring_to_link); int keyring_unlink_key_from_keyring(key_serial_t kid, key_serial_t keyring_id); int keyring_unlink_key_from_thread_keyring(key_serial_t kid); diff --git a/lib/utils_loop.c b/lib/utils_loop.c index 6af47f5..ec85678 100644 --- a/lib/utils_loop.c +++ b/lib/utils_loop.c @@ -2,8 +2,8 @@ /* * loopback block device utilities * - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include @@ -15,7 +15,7 @@ #include #include #include -#ifdef HAVE_SYS_SYSMACROS_H +#if HAVE_SYS_SYSMACROS_H # include /* for major, minor */ #endif #include diff --git a/lib/utils_loop.h b/lib/utils_loop.h index a0c7491..4edeff3 100644 --- a/lib/utils_loop.h +++ b/lib/utils_loop.h @@ -2,8 +2,8 @@ /* * loopback block device utilities * - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #ifndef _UTILS_LOOP_H diff --git a/lib/utils_pbkdf.c b/lib/utils_pbkdf.c index 56971f2..87e9419 100644 --- a/lib/utils_pbkdf.c +++ b/lib/utils_pbkdf.c @@ -2,8 +2,8 @@ /* * utils_pbkdf - PBKDF settings for libcryptsetup * - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include @@ -63,9 +63,11 @@ uint32_t pbkdf_adjusted_phys_memory_kb(void) memory_kb /= 2; /* - * Never use more that half of available free memory on system without swap. + * On systems with < 4GB RAM without swap + * never use more that half of available free memory. + * This is a temporary hack to avoid OOM on small systems. */ - if (!crypt_swapavailable()) { + if (memory_kb < (2 * 1024 * 1024) && !crypt_swapavailable()) { free_kb = crypt_getphysmemoryfree_kb(); /* @@ -159,10 +161,19 @@ int verify_pbkdf_params(struct crypt_device *cd, pbkdf_limits.max_memory); r = -EINVAL; } + if (1024ULL * pbkdf->max_memory_kb > SIZE_MAX) { + log_err(cd, _("Requested maximum PBKDF memory cost is too high (limited by the integer maximal size).")); + r = -EINVAL; + } if (!pbkdf->max_memory_kb) { log_err(cd, _("Requested maximum PBKDF memory cannot be zero.")); r = -EINVAL; } + if (pbkdf->parallel_threads > pbkdf_limits.max_parallel) { + log_err(cd, _("Requested maximum PBKDF parallel cost is too high (maximum is %d)."), + pbkdf_limits.max_parallel); + r = -EINVAL; + } if (!pbkdf->parallel_threads) { log_err(cd, _("Requested PBKDF parallel threads cannot be zero.")); r = -EINVAL; @@ -235,12 +246,6 @@ int init_pbkdf_type(struct crypt_device *cd, cd_pbkdf->max_memory_kb = pbkdf->max_memory_kb; cd_pbkdf->parallel_threads = pbkdf->parallel_threads; - if (cd_pbkdf->parallel_threads > pbkdf_limits.max_parallel) { - log_dbg(cd, "Maximum PBKDF threads is %d (requested %d).", - pbkdf_limits.max_parallel, cd_pbkdf->parallel_threads); - cd_pbkdf->parallel_threads = pbkdf_limits.max_parallel; - } - /* Do not limit threads by online CPUs if user forced values (no benchmark). */ if (cd_pbkdf->parallel_threads && !(cd_pbkdf->flags & CRYPT_PBKDF_NO_BENCHMARK)) { cpus = crypt_cpusonline(); diff --git a/lib/utils_safe_memory.c b/lib/utils_safe_memory.c index 0b1dc7e..5ee68d2 100644 --- a/lib/utils_safe_memory.c +++ b/lib/utils_safe_memory.c @@ -2,15 +2,13 @@ /* * utils_safe_memory - safe memory helpers * - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ -#include -#include #include #include -#include "libcryptsetup.h" +#include "internal.h" struct safe_allocation { size_t size; @@ -28,14 +26,16 @@ void crypt_safe_memzero(void *data, size_t size) if (!data) return; -#ifdef HAVE_EXPLICIT_BZERO - explicit_bzero(data, size); -#else - volatile uint8_t *p = (volatile uint8_t *)data; + return crypt_backend_memzero(data, size); +} + +/* Memcpy helper to avoid spilling sensitive data through additional registers */ +void *crypt_safe_memcpy(void *dst, const void *src, size_t size) +{ + if (!dst || !src) + return NULL; - while(size--) - *p++ = 0; -#endif + return crypt_backend_memcpy(dst, src, size); } /* safe allocations */ @@ -50,7 +50,7 @@ void *crypt_safe_alloc(size_t size) if (!alloc) return NULL; - crypt_safe_memzero(alloc, size + OVERHEAD); + crypt_backend_memzero(alloc, size + OVERHEAD); alloc->size = size; /* Ignore failure if it is over limit. */ @@ -73,7 +73,7 @@ void crypt_safe_free(void *data) p = (char *)data - OVERHEAD; alloc = (struct safe_allocation *)p; - crypt_safe_memzero(data, alloc->size); + crypt_backend_memzero(data, alloc->size); if (alloc->locked) { munlock(alloc, alloc->size + OVERHEAD); @@ -101,9 +101,21 @@ void *crypt_safe_realloc(void *data, size_t size) if (size > alloc->size) size = alloc->size; - memcpy(new_data, data, size); + crypt_backend_memcpy(new_data, data, size); } crypt_safe_free(data); return new_data; } + +size_t crypt_safe_alloc_size(const void *data) +{ + const void *p; + + if (!data) + return 0; + + p = (const char *)data - OVERHEAD; + + return ((const struct safe_allocation *)p)->size; +} diff --git a/lib/utils_storage_wrappers.c b/lib/utils_storage_wrappers.c index 52f065c..54492d0 100644 --- a/lib/utils_storage_wrappers.c +++ b/lib/utils_storage_wrappers.c @@ -3,7 +3,7 @@ * Generic wrapper for storage functions * (experimental only) * - * Copyright (C) 2018-2024 Ondrej Kozina + * Copyright (C) 2018-2025 Ondrej Kozina */ #include @@ -49,7 +49,9 @@ static int crypt_storage_backend_init(struct crypt_device *cd, struct crypt_storage *s; /* iv_start, sector_size */ - r = crypt_storage_init(&s, sector_size, cipher, cipher_mode, vk->key, vk->keylength, flags & LARGE_IV); + r = crypt_storage_init(&s, sector_size, cipher, cipher_mode, + crypt_volume_key_get_key(vk), + crypt_volume_key_length(vk), flags & LARGE_IV); if (r) return r; @@ -103,18 +105,11 @@ static int crypt_storage_dmcrypt_init( if (dmd.flags & CRYPT_ACTIVATE_READONLY) mode = (open_flags & ~O_ACCMODE) | O_RDONLY; - if (vk->key_description) + if (crypt_volume_key_description(vk)) dmd.flags |= CRYPT_ACTIVATE_KEYRING_KEY; - r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, - device, - vk, - cipher_spec, - iv_start, - device_offset, - NULL, - 0, - sector_size); + r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, device, vk, cipher_spec, iv_start, + device_offset, NULL, 0, 0, sector_size); if (r) return r; diff --git a/lib/utils_storage_wrappers.h b/lib/utils_storage_wrappers.h index ebc7ed3..c7a64b2 100644 --- a/lib/utils_storage_wrappers.h +++ b/lib/utils_storage_wrappers.h @@ -3,7 +3,7 @@ * Generic wrapper for storage functions * (experimental only) * - * Copyright (C) 2018-2024 Ondrej Kozina + * Copyright (C) 2018-2025 Ondrej Kozina */ #ifndef _UTILS_STORAGE_WRAPPERS_H diff --git a/lib/utils_wipe.c b/lib/utils_wipe.c index 2aa74e6..6a26ded 100644 --- a/lib/utils_wipe.c +++ b/lib/utils_wipe.c @@ -3,8 +3,8 @@ * utils_wipe - wipe a device * * Copyright (C) 2004-2007 Clemens Fruhwirth - * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2024 Milan Broz + * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2025 Milan Broz */ #include @@ -50,7 +50,7 @@ static void wipeSpecial(char *buffer, size_t buffer_size, unsigned int turn) { unsigned int i; - unsigned char write_modes[][3] = { + const unsigned char write_modes[27][4] = { {"\x55\x55\x55"}, {"\xaa\xaa\xaa"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"}, {"\x00\x00\x00"}, {"\x11\x11\x11"}, {"\x22\x22\x22"}, {"\x33\x33\x33"}, @@ -172,6 +172,10 @@ int crypt_wipe_device(struct crypt_device *cd, /* Note: LUKS1 calls it with wipe_block not aligned to multiple of bsize */ bsize = device_block_size(cd, device); alignment = device_alignment(device); + + log_dbg(cd, "Wipe device %s [%u], offset %" PRIu64 ", length %" PRIu64 ", block %zu, bsize %zu, align %zu.", + device_path(device), (unsigned)pattern, offset, length, wipe_block_size, bsize, alignment); + if (!bsize || !alignment || !wipe_block_size) return -EINVAL; @@ -287,9 +291,6 @@ int crypt_wipe(struct crypt_device *cd, if (!wipe_block_size) wipe_block_size = 1024*1024; - log_dbg(cd, "Wipe [%u] device %s, offset %" PRIu64 ", length %" PRIu64 ", block %zu.", - (unsigned)pattern, device_path(device), offset, length, wipe_block_size); - r = crypt_wipe_device(cd, device, pattern, offset, length, wipe_block_size, progress, usrptr); diff --git a/lib/verity/rs.h b/lib/verity/rs.h index 48f3a09..195e00c 100644 --- a/lib/verity/rs.h +++ b/lib/verity/rs.h @@ -4,7 +4,7 @@ * * Copyright (C) 2004 Phil Karn, KA9Q * libcryptsetup modifications - * Copyright (C) 2017-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2017-2025 Red Hat, Inc. All rights reserved. */ #ifndef _LIBFEC_RS_H diff --git a/lib/verity/rs_decode_char.c b/lib/verity/rs_decode_char.c index 0fcceae..f819dea 100644 --- a/lib/verity/rs_decode_char.c +++ b/lib/verity/rs_decode_char.c @@ -4,7 +4,7 @@ * * Copyright (C) 2002, Phil Karn, KA9Q * libcryptsetup modifications - * Copyright (C) 2017-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2017-2025 Red Hat, Inc. All rights reserved. */ #include diff --git a/lib/verity/rs_encode_char.c b/lib/verity/rs_encode_char.c index 259b29d..8d1b6c3 100644 --- a/lib/verity/rs_encode_char.c +++ b/lib/verity/rs_encode_char.c @@ -4,7 +4,7 @@ * * Copyright (C) 2002, Phil Karn, KA9Q * libcryptsetup modifications - * Copyright (C) 2017-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2017-2025 Red Hat, Inc. All rights reserved. */ #include diff --git a/lib/verity/verity.c b/lib/verity/verity.c index 5fe1cf7..65f5f4e 100644 --- a/lib/verity/verity.c +++ b/lib/verity/verity.c @@ -2,7 +2,7 @@ /* * dm-verity volume handling * - * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. */ #include @@ -271,7 +271,8 @@ int VERITY_verify_params(struct crypt_device *cd, return 0; log_dbg(cd, "Verification of VERITY data in userspace required."); - r = VERITY_verify(cd, hdr, root_hash->key, root_hash->keylength); + r = VERITY_verify(cd, hdr, crypt_volume_key_get_key(root_hash), + crypt_volume_key_length(root_hash)); if ((r == -EPERM || r == -EFAULT) && fec_device) { v = r; @@ -300,9 +301,9 @@ int VERITY_activate(struct crypt_device *cd, struct crypt_params_verity *verity_hdr, uint32_t activation_flags) { - uint32_t dmv_flags; + uint64_t dmv_flags; int r; - key_serial_t kid; + key_serial_t kid = 0; char *description = NULL; struct crypt_dm_active_device dmd = { 0 }; @@ -324,7 +325,9 @@ int VERITY_activate(struct crypt_device *cd, return -EINVAL; log_dbg(cd, "Adding signature %s (type user) into thread keyring.", description); - kid = keyring_add_key_in_thread_keyring(USER_KEY, description, signature->key, signature->keylength); + kid = keyring_add_key_in_thread_keyring(USER_KEY, description, + crypt_volume_key_get_key(signature), + crypt_volume_key_length(signature)); if (kid < 0) { log_dbg(cd, "keyring_add_key_in_thread_keyring failed with errno %d.", errno); log_err(cd, _("Failed to load key in kernel keyring.")); @@ -352,8 +355,8 @@ int VERITY_activate(struct crypt_device *cd, } r = dm_verity_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), - crypt_metadata_device(cd), fec_device, root_hash->key, - root_hash->keylength, description, + crypt_metadata_device(cd), fec_device, crypt_volume_key_get_key(root_hash), + crypt_volume_key_length(root_hash), description, VERITY_hash_offset_block(verity_hdr), VERITY_FEC_blocks(cd, fec_device, verity_hdr), verity_hdr); @@ -381,7 +384,12 @@ int VERITY_activate(struct crypt_device *cd, r = 0; out: - crypt_drop_keyring_key_by_description(cd, description, USER_KEY); + if (signature) { + log_dbg(cd, "Unlinking signature (id: %" PRIi32 ") from thread keyring.", kid); + + if (keyring_unlink_key_from_thread_keyring(kid)) + log_dbg(cd, "keyring_unlink_key_from_thread_keyring failed with errno %d.", errno); + } free(description); dm_targets_free(cd, &dmd); return r; @@ -411,13 +419,13 @@ int VERITY_dump(struct crypt_device *cd, verity_blocks += rs_blocks; } - log_std(cd, "VERITY header information for %s\n", device_path(crypt_metadata_device(cd))); + log_std(cd, "VERITY header information for %s.\n", device_path(crypt_metadata_device(cd))); log_std(cd, "UUID: \t%s\n", crypt_get_uuid(cd) ?: ""); log_std(cd, "Hash type: \t%u\n", verity_hdr->hash_type); log_std(cd, "Data blocks: \t%" PRIu64 "\n", verity_hdr->data_size); - log_std(cd, "Data block size: \t%u\n", verity_hdr->data_block_size); + log_std(cd, "Data block size: \t%u [bytes]\n", verity_hdr->data_block_size); log_std(cd, "Hash blocks: \t%" PRIu64 "\n", hash_blocks); - log_std(cd, "Hash block size: \t%u\n", verity_hdr->hash_block_size); + log_std(cd, "Hash block size: \t%u [bytes]\n", verity_hdr->hash_block_size); log_std(cd, "Hash algorithm: \t%s\n", verity_hdr->hash_name); if (fec_device && fec_blocks) { log_std(cd, "FEC RS roots: \t%" PRIu32 "\n", verity_hdr->fec_roots); diff --git a/lib/verity/verity.h b/lib/verity/verity.h index f0c43a7..7d79810 100644 --- a/lib/verity/verity.h +++ b/lib/verity/verity.h @@ -2,7 +2,7 @@ /* * dm-verity volume handling * - * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. */ #ifndef _VERITY_H diff --git a/lib/verity/verity_fec.c b/lib/verity/verity_fec.c index ca60b2e..2dfb4ac 100644 --- a/lib/verity/verity_fec.c +++ b/lib/verity/verity_fec.c @@ -3,7 +3,7 @@ * dm-verity Forward Error Correction (FEC) support * * Copyright (C) 2015 Google, Inc. All rights reserved. - * Copyright (C) 2017-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2017-2025 Red Hat, Inc. All rights reserved. */ #include diff --git a/lib/verity/verity_hash.c b/lib/verity/verity_hash.c index 56b1569..02333de 100644 --- a/lib/verity/verity_hash.c +++ b/lib/verity/verity_hash.c @@ -2,7 +2,7 @@ /* * dm-verity volume handling * - * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. */ #include @@ -302,6 +302,11 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, bool verify, hash_device_offset_max - params->hash_area_offset); log_dbg(cd, "Using %d hash levels.", levels); + r = device_check_size(cd, crypt_metadata_device(cd), + hash_device_offset_max - params->hash_area_offset, 1); + if (r < 0) + return r; + data_file = fopen(device_path(crypt_data_device(cd)), "r"); if (!data_file) { log_err(cd, _("Cannot open device %s."), diff --git a/lib/volumekey.c b/lib/volumekey.c index 034e16e..afa0cb8 100644 --- a/lib/volumekey.c +++ b/lib/volumekey.c @@ -3,7 +3,7 @@ * cryptsetup volume key implementation * * Copyright (C) 2004-2006 Clemens Fruhwirth - * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2010-2025 Red Hat, Inc. All rights reserved. */ #include @@ -13,6 +13,16 @@ #include "internal.h" +struct volume_key { + int id; + size_t keylength; /* length in bytes */ + const char *key_description; /* keyring key name/description */ + key_type_t keyring_key_type; /* kernel keyring key type */ + key_serial_t key_id; /* kernel key id of volume key representation linked in thread keyring */ + struct volume_key *next; + char *key; +}; + struct volume_key *crypt_alloc_volume_key(size_t keylength, const char *key) { struct volume_key *vk; @@ -20,39 +30,117 @@ struct volume_key *crypt_alloc_volume_key(size_t keylength, const char *key) if (keylength > (SIZE_MAX - sizeof(*vk))) return NULL; - vk = malloc(sizeof(*vk) + keylength); + vk = crypt_zalloc(sizeof(*vk)); if (!vk) return NULL; - vk->key_description = NULL; + vk->keyring_key_type = INVALID_KEY; + vk->key_id = -1; vk->keylength = keylength; vk->id = KEY_NOT_VERIFIED; - vk->next = NULL; /* keylength 0 is valid => no key */ - if (vk->keylength) { - if (key) - memcpy(&vk->key, key, keylength); - else - crypt_safe_memzero(&vk->key, keylength); + if (vk->keylength && key) { + vk->key = crypt_safe_alloc(keylength); + if (!vk->key) { + free(vk); + return NULL; + } + crypt_safe_memcpy(vk->key, key, keylength); } return vk; } -int crypt_volume_key_set_description(struct volume_key *vk, const char *key_description) +struct volume_key *crypt_alloc_volume_key_by_safe_alloc(void **safe_alloc) +{ + size_t keylength; + struct volume_key *vk; + + if (!safe_alloc) + return NULL; + + keylength = crypt_safe_alloc_size(*safe_alloc); + if (!keylength) + return NULL; + + vk = crypt_alloc_volume_key(keylength, NULL); + if (!vk) + return NULL; + + vk->key = *safe_alloc; + *safe_alloc = NULL; + + return vk; +} + +void crypt_volume_key_pass_safe_alloc(struct volume_key *vk, void **safe_alloc) +{ + assert(vk); + assert(vk->keylength); + assert(safe_alloc); + assert(crypt_safe_alloc_size(*safe_alloc) == vk->keylength); + + crypt_safe_free(vk->key); + vk->key = *safe_alloc; + *safe_alloc = NULL; +} + +const char *crypt_volume_key_get_key(const struct volume_key *vk) +{ + assert(vk && vk->key); + + return vk->key; +} + +size_t crypt_volume_key_length(const struct volume_key *vk) +{ + assert(vk); + + return vk->keylength; +} + +int crypt_volume_key_set_description(struct volume_key *vk, + const char *key_description, key_type_t keyring_key_type) { if (!vk) return -EINVAL; free(CONST_CAST(void*)vk->key_description); vk->key_description = NULL; + vk->keyring_key_type = keyring_key_type; if (key_description && !(vk->key_description = strdup(key_description))) return -ENOMEM; return 0; } +int crypt_volume_key_set_description_by_name(struct volume_key *vk, const char *key_name) +{ + const char *key_description = NULL; + key_type_t keyring_key_type = keyring_type_and_name(key_name, &key_description); + + if (keyring_key_type == INVALID_KEY) + return -EINVAL; + + return crypt_volume_key_set_description(vk, key_description, keyring_key_type); +} + +const char *crypt_volume_key_description(const struct volume_key *vk) +{ + assert(vk); + + return vk->key_description; +} + + +key_type_t crypt_volume_key_kernel_key_type(const struct volume_key *vk) +{ + assert(vk); + + return vk->keyring_key_type; +} + void crypt_volume_key_set_id(struct volume_key *vk, int id) { if (vk && id >= 0) @@ -107,28 +195,88 @@ void crypt_free_volume_key(struct volume_key *vk) struct volume_key *vk_next; while (vk) { - crypt_safe_memzero(vk->key, vk->keylength); - vk->keylength = 0; free(CONST_CAST(void*)vk->key_description); + crypt_safe_free(vk->key); vk_next = vk->next; free(vk); vk = vk_next; } } -struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, size_t keylength) +struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, size_t keylength, + key_quality_info quality) { int r; - struct volume_key *vk; + void *key; + struct volume_key *vk = NULL; - vk = crypt_alloc_volume_key(keylength, NULL); - if (!vk) + key = crypt_safe_alloc(keylength); + if (!key) return NULL; - r = crypt_random_get(cd, vk->key, keylength, CRYPT_RND_KEY); - if(r < 0) { - crypt_free_volume_key(vk); - return NULL; + switch (quality) { + case KEY_QUALITY_KEY: + r = crypt_random_get(cd, key, keylength, CRYPT_RND_KEY); + break; + case KEY_QUALITY_NORMAL: + r = crypt_random_get(cd, key, keylength, CRYPT_RND_NORMAL); + break; + case KEY_QUALITY_EMPTY: + r = 0; + break; + default: + abort(); } + + if (!r) + vk = crypt_alloc_volume_key(keylength, NULL); + if (vk) + vk->key = key; + else + crypt_safe_free(key); + return vk; } + +bool crypt_volume_key_is_set(const struct volume_key *vk) +{ + return vk && vk->key; +} + +bool crypt_volume_key_upload_kernel_key(struct volume_key *vk) +{ + key_serial_t kid; + + assert(vk && vk->key && vk->key_description && vk->keyring_key_type != INVALID_KEY); + + kid = keyring_add_key_in_thread_keyring(vk->keyring_key_type, vk->key_description, + vk->key, vk->keylength); + if (kid >= 0) { + vk->key_id = kid; + return true; + } + + return false; +} + +void crypt_volume_key_drop_kernel_key(struct crypt_device *cd, struct volume_key *vk) +{ + assert(vk); + assert(vk->key_description || vk->keyring_key_type == INVALID_KEY); + assert(!vk->key_description || vk->keyring_key_type != INVALID_KEY); + + crypt_unlink_key_by_description_from_thread_keyring(cd, + vk->key_description, + vk->keyring_key_type); +} + +void crypt_volume_key_drop_uploaded_kernel_key(struct crypt_device *cd, struct volume_key *vk) +{ + assert(vk); + + if (vk->key_id < 0) + return; + + crypt_unlink_key_from_thread_keyring(cd, vk->key_id); + vk->key_id = -1; +} diff --git a/man/common_footer.adoc b/man/common_footer.adoc index 21302eb..6747aee 100644 --- a/man/common_footer.adoc +++ b/man/common_footer.adoc @@ -1,17 +1,15 @@ - == REPORTING BUGS -Report bugs at mailto:cryptsetup@lists.linux.dev[*cryptsetup mailing list*] -or in https://gitlab.com/cryptsetup/cryptsetup/-/issues/new[*Issues project section*]. +Report bugs at mailto:cryptsetup@lists.linux.dev[cryptsetup mailing list] or in https://gitlab.com/cryptsetup/cryptsetup/-/issues/new[Issues project section]. -Please attach output of the failed command with --debug option added. +Please attach the output of the failed command with --debug option added. == SEE ALSO -https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions[*Cryptsetup FAQ*] +https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions[Cryptsetup FAQ] *cryptsetup*(8), *integritysetup*(8) and *veritysetup*(8) == CRYPTSETUP -Part of https://gitlab.com/cryptsetup/cryptsetup/[*cryptsetup project*]. +Part of https://gitlab.com/cryptsetup/cryptsetup/[cryptsetup project]. diff --git a/man/common_options.adoc b/man/common_options.adoc index 523b775..31ca962 100644 --- a/man/common_options.adoc +++ b/man/common_options.adoc @@ -1,48 +1,42 @@ == OPTIONS ifdef::ACTION_LUKSFORMAT,ACTION_REENCRYPT[] -*--align-payload *:: +*--align-payload* __ (DEPRECATED, use --offset):: Align payload at a boundary of _value_ 512-byte sectors. + -If not specified, cryptsetup tries to use the topology info provided by -the kernel for the underlying device to get the optimal alignment. If -not available (or the calculated value is a multiple of the default) -data is by default aligned to a 1MiB boundary (i.e. 2048 512-byte -sectors). +If not specified, cryptsetup tries to use the topology info provided by the kernel for the underlying device to get the optimal alignment. +If not available (or the calculated value is a multiple of the default), data is by default aligned to a 1MiB boundary (i.e., 2048 512-byte sectors). + -For a detached LUKS header, this option specifies the offset on the data -device. See also the --header option. +For a detached LUKS header, this option specifies the offset on the data device. +See also the --header option. + -*WARNING:* This option is DEPRECATED and has often unexpected impact to -the data offset and keyslot area size (for LUKS2) due to the complex -rounding. For fixed data device offset use _--offset_ option instead. +This option is DEPRECATED and has an unexpected impact on the data offset and keyslot area size (for LUKS2) due to the complex rounding. +For fixed data device offset, use --offset option instead. endif::[] ifdef::ACTION_OPEN,ACTION_REFRESH[] *--allow-discards*:: -Allow the use of discard (TRIM) requests for the device. This is also not -supported for LUKS2 devices with data integrity protection. +Allow the use of discard (TRIM) requests for the device. +This is also not supported for LUKS2 devices with data integrity protection. + -*WARNING:* This command can have a negative security impact because it -can make filesystem-level operations visible on the physical device. For -example, information leaking filesystem type, used space, etc. may be -extractable from the physical device if the discarded blocks can be -located later. If in doubt, do not use it. +*WARNING:* This command can have a negative security impact because it can make filesystem-level operations visible on the physical device. +For example, information leaking filesystem type, used space, etc., may be extractable from the physical device if the discarded blocks can be located later. +If in doubt, do not use it. + -A kernel version of 3.1 or later is needed. For earlier kernels, this -option is ignored. +A kernel version of 3.1 or later is needed. +For earlier kernels, this option is ignored. endif::[] ifdef::COMMON_OPTIONS[] -*--batch-mode, -q*:: -Suppresses all confirmation questions. Use with care! +*--batch-mode*, *-q*:: +Suppresses all confirmation questions. +Use with care! + -If the --verify-passphrase option is not specified, this option also -switches off the passphrase verification. +If the --verify-passphrase option is not specified, this option also switches off the passphrase verification. endif::[] ifdef::ACTION_REENCRYPT[] -*--block-size* _value_ *(LUKS1 only)*:: +*--block-size* _value_ (LUKS1 only):: Use re-encryption block size of _value_ in MiB. + Values can be between 1 and 64 MiB. @@ -50,50 +44,47 @@ endif::[] ifdef::ACTION_CLOSE[] *--cancel-deferred*:: -Removes a previously configured deferred device removal in _close_ -command. +Removes a previously configured deferred device removal in the _close_ command. endif::[] ifdef::ACTION_OPEN,ACTION_LUKSFORMAT,ACTION_REENCRYPT,ACTION_TCRYPTDUMP,ACTION_BENCHMARK[] -*--cipher, -c* __:: +*--cipher*, *-c* __:: ifdef::ACTION_OPEN,ACTION_TCRYPTDUMP[] -Set the cipher specification string for _plain_ device type. +Set the cipher specification string for the _plain_ device type. + -For _tcrypt_ device type it restricts checked cipher chains when looking for header. +For the _tcrypt_ device type, it restricts checked cipher chains when looking for the header. endif::[] ifndef::ACTION_REENCRYPT,ACTION_OPEN,ACTION_TCRYPTDUMP[] Set the cipher specification string. endif::[] ifdef::ACTION_REENCRYPT[] *LUKS2*: -Set the cipher specification string for data segment only. +Set the cipher specification string for the data segment only. + *LUKS1*: -Set the cipher specification string for data segment and keyslots. +Set the cipher specification string for the data segment and keyslots. + -*NOTE*: In encrypt mode, if cipher specification is omitted the default cipher is applied. -In reencrypt mode, if no new cipher specification is requested, the existing cipher will remain -in use. Unless the existing cipher was "cipher_null". In that case default cipher would -be applied as in encrypt mode. +The default cipher is applied if the cipher specification is omitted in encrypt mode. ++ +In reencrypt mode, if no new cipher specification is requested, the existing cipher will remain. +The only exception is if the cipher is "cipher_null", then the default cipher is used. endif::[] ifdef::ACTION_OPEN,ACTION_LUKSFORMAT,ACTION_REENCRYPT[] + _cryptsetup --help_ shows the compiled-in defaults. + -If a hash is part of the cipher specification, then it is used as part -of the IV generation. For example, ESSIV needs a hash function, while -"plain64" does not and hence none is specified. +If a hash is part of the cipher specification, then it is used as part of the IV generation. +For example, ESSIV needs a hash function, while "plain64" does not and hence none is specified. + -For XTS mode you can optionally set a key size of 512 bits with the -s -option. Key size for XTS mode is twice that for other modes for the same -security level. +For XTS mode, you can optionally set a key size of 512 bits with the -s option. +Key size for XTS mode is twice that for other modes for the same security level. endif::[] endif::[] ifdef::COMMON_OPTIONS[] -*--debug or --debug-json*:: -Run in debug mode with full diagnostic logs. Debug output lines are -always prefixed by *#*. +*--debug* or *--debug-json*:: +Run in debug mode with full diagnostic logs. +Debug output lines are always prefixed by *#*. + If --debug-json is used, additional LUKS2 JSON data structures are printed. endif::[] @@ -105,38 +96,41 @@ endif::[] ifdef::ACTION_CLOSE[] *--deferred*:: -Defers device removal in _close_ command until the last user closes -it. +Defers device removal in the _close_ command until the last user closes it. endif::[] ifdef::ACTION_OPEN,ACTION_REENCRYPT,ACTION_RESIZE[] *--device-size* _size[units]_:: ifndef::ACTION_RESIZE[] -Instead of real device size, use specified value. +Instead of the real device size, use the specified value. endif::[] ifdef::ACTION_RESIZE[] -Sets new size of the device. If unset real device size is used. +Sets the new size of the device. +If unset, the real device size is used. endif::[] ifdef::ACTION_OPEN[] Usable only with _plain_ device type. endif::[] ifdef::ACTION_REENCRYPT[] -It means that only specified area (from the start of the device -to the specified size) will be reencrypted. +It means that only the specified area (from the start of the device to the specified size) will be reencrypted. ++ +*LUKS2*: +When used together with --reduce-device-size, only the initial _size_ value (--device-size parameter) of data is shifted backwards while being encrypted. + -*WARNING:* This is destructive operation. Data beyond --device-size limit may -be lost after operation gets finished. +The sum of --device-size and --reduce-device-size values must not exceed the real device size. ++ +*WARNING:* This is a destructive operation. +Data beyond --device-size limit may be lost after the operation is finished. endif::[] + If no unit suffix is specified, the size is in bytes. + -Unit suffix can be S for 512 byte sectors, K/M/G/T (or KiB,MiB,GiB,TiB) -for units with 1024 base or KB/MB/GB/TB for 1000 base (SI scale). +Unit suffix can be S for 512 byte sectors, K/M/G/T (or KiB, MiB, GiB, TiB) for units with 1024 base or KB/MB/GB/TB for 1000 base (SI scale). endif::[] ifdef::ACTION_LUKSFORMAT,ACTION_REENCRYPT[] *--disable-blkid*:: -Disable use of blkid library for checking and wiping on-disk signatures. +Disable use of the blkid library for checking and wiping on-disk signatures. endif::[] ifdef::ACTION_OPEN,ACTION_LUKSRESUME,ACTION_RESIZE,ACTION_TOKEN[] @@ -146,102 +140,102 @@ endif::[] ifdef::ACTION_OPEN,ACTION_RESIZE,ACTION_REFRESH,ACTION_LUKSFORMAT,ACTION_LUKSRESUME,ACTION_TOKEN,ACTION_REENCRYPT[] *--disable-keyring*:: -Do not load volume key in kernel keyring and store it directly in the -dm-crypt target instead. This option is supported only for the LUKS2 type. +Do not load the volume key in the kernel keyring; store it directly in the dm-crypt target instead. +This option is supported only for the LUKS2 type. endif::[] ifndef::ACTION_BENCHMARK,ACTION_BITLKDUMP,ACTION_TCRYPTDUMP[] *--disable-locks*:: -Disable lock protection for metadata on disk. This option is valid -only for LUKS2 and ignored for other formats. +Disable lock protection for metadata on disk. +This option is valid only for LUKS2 and is ignored for other formats. + ifdef::ACTION_REENCRYPT[] -*NOTE:* With locking disabled LUKS2 images in files can be fully (re)encrypted -offline without need for super user privileges provided used block ciphers are -available in crypto backend. +With locking disabled, LUKS2 images in files can be fully (re)encrypted offline without the need for superuser privileges provided that the used block ciphers are available in the crypto backend. + endif::[] -*WARNING:* Do not use this option unless you run cryptsetup in a -restricted environment where locking is impossible to perform (where -/run directory cannot be used). +*WARNING:* Do not use this option unless you run cryptsetup in a restricted environment where locking is impossible to perform (where /run directory cannot be used). endif::[] ifdef::ACTION_OPEN,ACTION_TCRYPTDUMP[] *--disable-veracrypt*:: -This option can be used to disable VeraCrypt compatible mode (only -TrueCrypt devices are recognized). Only for TCRYPT extension. See -_TCRYPT_ section in *cryptsetup*(8) for more info. +This option can be used to disable VeraCrypt compatible mode (only TrueCrypt devices are recognized). +See the _TCRYPT_ section in *cryptsetup*(8) for more info. endif::[] ifdef::ACTION_LUKSDUMP[] *--dump-json-metadata*:: -For _luksDump_ (LUKS2 only) this option prints content of LUKS2 header -JSON metadata area. +For _luksDump_ (LUKS2 only), this option prints the content of the LUKS2 header JSON metadata area. endif::[] ifdef::ACTION_LUKSDUMP,ACTION_TCRYPTDUMP,ACTION_BITLKDUMP[] -*--dump-volume-key, --dump-master-key (OBSOLETE alias)*:: -Print the volume key in the displayed information. Use with care, -as the volume key can be used to bypass -the passphrases, see also option --volume-key-file. +*--dump-volume-key*:: +--dump-master-key (OBSOLETE alias):: +Print the volume key in the displayed information. +Use with care, as the volume key can be used to bypass the passphrases, see also option --volume-key-file. endif::[] ifdef::ACTION_REENCRYPT[] -*--encrypt, --new, -N*:: -Initialize (and run) device in-place encryption mode. +*--encrypt*, *--new*, *-N*:: +Initialize (and run) the device in-place encryption mode. endif::[] ifdef::ACTION_RESIZE,ACTION_OPEN,ACTION_LUKSADDKEY,ACTION_LUKSDUMP,ACTION_LUKSRESUME,ACTION_TOKEN[] -*--external-tokens-path* _absolute_path_:: -Override system directory path where cryptsetup searches for external token -handlers (or token plugins). It must be absolute path (starting with '/' character). +*--external-tokens-path* __:: +Override the system directory path where cryptsetup searches for external token handlers (or token plugins). +It must be an absolute path (starting with '/' character). +endif::[] + +ifdef::ACTION_REENCRYPT[] +*--force-no-keyslots* (LUKS2 only):: +Enforce initialization of reencryption operation with additional --volume-key-file, --new-volume-key-file, --volume-key-keyring or --new-volume-key-keyring parameters. +It would result in the deletion of all remaining LUKS2 keyslots containing the volume key. ++ +LUKS2 keyslot with the new volume key may be added after the reencryption operation is finished. +See *cryptsetup-luksAddKey*(8) command. ++ +*WARNING:* Use with extreme caution! +If you lose the volume key stored in a file or in a kernel keyring before adding the LUKS2 keyslot containing the new volume key, the device will become unusable, and all data will be lost. endif::[] ifdef::ACTION_REENCRYPT[] -*--force-offline-reencrypt (LUKS2 only)*:: +*--force-offline-reencrypt* (LUKS2 only):: Bypass active device auto-detection and enforce offline reencryption. + -This option is useful especially for reencryption of LUKS2 images put in -files (auto-detection is not reliable in this scenario). +This option is useful especially for reencryption of LUKS2 images put in files (auto-detection is not reliable in this scenario). + -It may also help in case active device auto-detection on particular -data device does not work or report errors. +It may also help in case active device auto-detection on a particular data device does not work or report errors. + -*WARNING:* Use with extreme caution! This may destroy data if the device -is activated and/or actively used. +*WARNING:* Use with extreme caution! This may destroy data if the device is activated and/or actively used. endif::[] ifdef::ACTION_LUKSFORMAT,ACTION_LUKSADDKEY,ACTION_LUKSCHANGEKEY,ACTION_LUKSCONVERTKEY,ACTION_REENCRYPT[] *--force-password*:: Do not use password quality checking for new LUKS passwords. + -This option is ignored if cryptsetup is built without password -quality checking support. +This option is ignored if cryptsetup is built without password quality checking support. + -For more info about password quality check, see the manual page for -*pwquality.conf(5)* and *passwdqc.conf(5)*. +For more info about password quality check, see the manual page for *pwquality.conf*(5) and *passwdqc.conf*(5). endif::[] ifdef::ACTION_OPEN,ACTION_LUKSFORMAT,ACTION_LUKSADDKEY,ACTION_LUKSCHANGEKEY,ACTION_LUKSCONVERTKEY,ACTION_TCRYPTDUMP,ACTION_BENCHMARK,ACTION_REENCRYPT[] -*--hash, -h* __:: +*--hash*, *-h* __:: ifdef::ACTION_OPEN,ACTION_TCRYPTDUMP[] -Specifies the passphrase hash. Applies to _plain_ and _loopaes_ device types only. +Specifies the passphrase hash. +Applies to _plain_ and _loopaes_ device types only. + -For _tcrypt_ device type, it restricts checked PBKDF2 variants when looking for header. +For the _tcrypt_ device type, it restricts the checked PBKDF2 variants when looking for the header. endif::[] ifdef::ACTION_LUKSFORMAT[] -Specifies the hash used in the LUKS key setup scheme and volume key -digest. +Specifies the hash used in the LUKS key setup scheme and volume key digest. endif::[] ifndef::ACTION_REENCRYPT,ACTION_OPEN,ACTION_TCRYPTDUMP[] -The specified hash is used for PBKDF2 and AF splitter. +The specified hash is used for PBKDF2 and the AF splitter. endif::[] ifdef::ACTION_REENCRYPT[] *LUKS1:* Specifies the hash used in the LUKS1 key setup scheme and volume key digest. + -*NOTE*: if this parameter is not specified, default hash algorithm is always used -for new LUKS1 device header. +If this parameter is not specified, the default hash algorithm is always used for a new LUKS1 device header. + *LUKS2:* Ignored unless new keyslot pbkdf algorithm is set to PBKDF2 (see --pbkdf). endif::[] @@ -254,34 +248,28 @@ endif::[] endif::[] ifndef::ACTION_BENCHMARK,ACTION_BITLKDUMP[] -*--header *:: +*--header* __:: ifndef::ACTION_OPEN,ACTION_ERASE[] -Use a detached (separated) metadata device or file where the LUKS -header is stored. This option allows one to store ciphertext and LUKS -header on different devices. +Use a detached (separated) metadata device or file where the LUKS header is stored. +This option allows one to store the ciphertext and LUKS header on different devices. + endif::[] ifdef::ACTION_OPEN[] -Specify detached (separated) metadata device or file where the header is stored. +Specify a detached (separated) metadata device or file where the header is stored. + -*WARNING:* There is no check whether the ciphertext device specified -actually belongs to the header given. In fact, you can specify an -arbitrary device as the ciphertext device with the --header option. +*WARNING:* There is no check whether the ciphertext device specified actually belongs to the header given. +In fact, you can specify an arbitrary device as the ciphertext device with the --header option. Use with care. endif::[] ifndef::ACTION_REENCRYPT[] ifdef::ACTION_LUKSFORMAT[] -With a file name as the argument to --header, the file -will be automatically created if it does not exist. See the cryptsetup -FAQ for header size calculation. +With a file name as the argument to --header, the file will be automatically created if it does not exist. +See the cryptsetup FAQ for header size calculation. + -The --align-payload option is taken as absolute sector alignment on ciphertext -device and can be zero. +The --align-payload option is taken as absolute sector alignment on the ciphertext device and can be zero. endif::[] ifndef::ACTION_LUKSFORMAT,ACTION_OPEN,ACTION_ERASE[] -For commands that change the LUKS header (e.g. _luksAddKey_), -specify the device or file with the LUKS header directly as the LUKS -device. +For commands that change the LUKS header (e.g., _luksAddKey_), specify the device or file with the LUKS header directly as the LUKS device. endif::[] endif::[] ifdef::ACTION_REENCRYPT[] @@ -289,208 +277,202 @@ If used with --encrypt/--new option, the header file will be created (or overwri Use with care. + *LUKS2*: -For decryption mode the option may be used to export original LUKS2 header -to a detached file. The passed future file must not exist at the time -of initializing the decryption operation. This frees space in head of data -device so that data can be moved at original LUKS2 header location. Later on -decryption operation continues as if the ordinary detached header was passed. +For decryption mode, the option may be used to export the original LUKS2 header to a detached file. +The passed future file must not exist at the time of initializing the decryption operation. +This frees space in the head of the data device so that data can be moved at the original LUKS2 header location. +Later on, the decryption operation continues as if the ordinary detached header was passed. + -*WARNING:* Never put exported header file in a filesystem on top of device -you are about to decrypt! It would cause a deadlock. +*WARNING:* Never put an exported header file in a filesystem on top of the device you are about to decrypt! +It would cause a deadlock. endif::[] ifdef::ACTION_ERASE[] -Use to specify detached LUKS2 header when erasing HW OPAL enabled data device. +Use to specify a detached LUKS2 header when erasing OPAL self-encrypting drive. endif::[] endif::[] ifdef::ACTION_LUKSHEADERBACKUP,ACTION_LUKSHEADERRESTORE[] -*--header-backup-file *:: -Specify file with header backup file. +*--header-backup-file* _file_:: +Specify a file with the header backup file. endif::[] ifdef::COMMON_OPTIONS[] -*--help, -?*:: +*--help*, *-?*:: Show help text and default parameters. endif::[] ifdef::ACTION_REENCRYPT[] -*--hotzone-size* _size_ *(LUKS2 only)*:: -This option can be used to set an upper limit on the size of -reencryption area (hotzone). The _size_ can be specified with unit -suffix (for example 50M). Note that actual hotzone size may be less -than specified due to other limitations (free space in keyslots -area or available memory). +*--hotzone-size* _size_ (LUKS2 only):: +This option can be used to set an upper limit on the size of the reencryption area (hotzone). +The _size_ can be specified with a unit suffix (for example, 50M). +Note that the actual hotzone size may be less than specified due to other limitations (free space in keyslots area or available memory). + -With decryption mode for devices with LUKS2 header placed in head of data -device, the option specifies how large is the first data segment moved -from original data offset pointer. +With decryption mode for devices with LUKS2 header placed in the head of the data device, the option specifies how large is the first data segment moved from the original data offset pointer. endif::[] ifdef::ACTION_LUKSFORMAT[] *--hw-opal*:: -Format LUKS2 device with dm-crypt encryption stacked on top HW based encryption configured -on SED OPAL locking range. This option enables both SW and HW based data encryption. +Format LUKS2 device with dm-crypt encryption stacked on top of HW-based encryption configured on SED OPAL locking range. +This option enables both SW and HW based data encryption. endif::[] ifdef::ACTION_ERASE[] *--hw-opal-factory-reset*:: -Erase *ALL* data on the OPAL self-encrypted device, regardless of the partition it is ran on, if any, -and does not require a valid LUKS2 header to be present on the device to run. After providing -correct PSID via interactive prompt or via *--key-file* parameter the device is erased. -PSID is usually printed on the OPAL device label (either directly or as a QR code). +Erase *ALL* data on the OPAL self-encrypting drive. +The option does not require a valid LUKS2 header to be present on the device to run. +After providing the correct PSID via interactive prompt or via --key-file parameter the device is erased. ++ +PSID is usually printed on the OPAL drive label (either directly or as a QR code). +PSID must be entered without any dashes, spaces or underscores. ++ +PSID should be treated as sensitive information as it allows anyone with remote access to the OPAL drive to destroy data even if the device is locked. +Be sure you do not leak PSID through transparent packaging during transport or images of the drive posted online. endif::[] ifdef::ACTION_LUKSFORMAT[] *--hw-opal-only*:: -Format LUKS2 device with HW based encryption configured on SED OPAL locking range only. LUKS2 -format only manages locking range unlock key. This option enables HW based data encryption managed -by SED OPAL drive only. +Format LUKS2 device with HW based encryption configured on SED OPAL locking range only. +LUKS2 format only manages the locking range unlock key. +This option enables HW-based data encryption managed by the SED OPAL drive only. + -*NOTE*: Please note that with OPAL-only (--hw-opal-only) encryption, -the configured OPAL administrator PIN (passphrase) allows unlocking -all configured locking ranges without LUKS keyslot decryption -(without knowledge of LUKS passphrase). -Because of many observed problems with compatibility, cryptsetup -currently DOES NOT use OPAL single-user mode, which would allow such -decoupling of OPAL admin PIN access. +Please note that with OPAL-only (--hw-opal-only) encryption, the configured OPAL administrator PIN (passphrase) allows unlocking all configured locking ranges without LUKS keyslot decryption (without knowledge of LUKS passphrase). +Because of many observed problems with compatibility, cryptsetup currently DOES NOT use OPAL single-user mode, which would allow such decoupling of OPAL admin PIN access. endif::[] ifdef::ACTION_REENCRYPT[] -*--init-only (LUKS2 only)*:: -Initialize reencryption (any mode) operation in LUKS2 metadata only -and exit. If any reencrypt operation is already initialized in -metadata, the command with --init-only parameter fails. +*--init-only* (LUKS2 only):: +Initialize reencryption (any mode) operation in LUKS2 metadata only and exit. +If any reencrypt operation is already initialized in metadata, the command with --init-only parameter fails. endif::[] ifdef::ACTION_LUKSFORMAT[] -*--integrity *:: -Specify integrity algorithm to be used for authenticated disk -encryption in LUKS2. +*--integrity* __:: +Specify the integrity algorithm to be used for authenticated disk encryption in LUKS2. + -*WARNING: This extension is EXPERIMENTAL* and requires dm-integrity -kernel target (available since kernel version 4.12). For native AEAD -modes, also enable "User-space interface for AEAD cipher algorithms" in -"Cryptographic API" section (CONFIG_CRYPTO_USER_API_AEAD .config -option). +*WARNING: This extension is EXPERIMENTAL* and requires dm-integrity kernel target. +For native AEAD modes, also enable "User-space interface for AEAD cipher algorithms" in the "Cryptographic API" section (CONFIG_CRYPTO_USER_API_AEAD .config option). + -For more info, see _AUTHENTICATED DISK ENCRYPTION_ section in *cryptsetup*(8). +For more info, see the _AUTHENTICATED DISK ENCRYPTION_ section in *cryptsetup*(8). +endif::[] + +ifdef::ACTION_LUKSFORMAT[] +*--integrity-inline*:: +Store integrity tags in hardware sector integrity fields. +The device must support sectors with additional protection information (PI, also known as DIF - data integrity field) of the requested size. +Another storage subsystem must not use the additional field (the device must present a "nop" profile in the kernel). +Note that some devices must be reformatted at a low level to support this option; for NVMe devices, see nvme(1) id-ns LBA profiles. ++ +No journal or bitmap is used in this mode. +The device should operate with native speed (without any overhead). +This option is available since the Linux kernel version 6.11. +endif::[] + +ifdef::ACTION_LUKSFORMAT[] +*--integrity-key-size* _bits_:: +The size of the data integrity key. +The argument has to be a multiple of 8. +Configurable only for HMAC integrity. +The default integrity key size is set to the same as the hash output length. endif::[] ifdef::ACTION_LUKSFORMAT[] *--integrity-legacy-padding*:: Use inefficient legacy padding. + -*WARNING*: Do not use this option until you need compatibility with specific -old kernel. +Do not use this option until you need compatibility with a specific old kernel. endif::[] ifdef::ACTION_REFRESH[] *--integrity-no-journal*:: -Activate device with integrity protection without using data journal -(direct write of data and integrity tags). Note that without journal -power fail can cause non-atomic write and data corruption. Use only if -journalling is performed on a different storage layer. +Activate device with integrity protection without using data journal (direct write of data and integrity tags). +Note that without a journal, a power failure can cause non-atomic writes and data corruption. +Use only if journaling is performed on a different storage layer. endif::[] ifdef::ACTION_LUKSFORMAT[] *--integrity-no-wipe*:: -Skip wiping of device authentication (integrity) tags. If you skip -this step, sectors will report invalid integrity tag until an -application write to the sector. +Skip wiping of device authentication (integrity) tags. +If you skip this step, sectors will report an invalid integrity tag until an application writes to the sector. + -*NOTE:* Even some writes to the device can fail if the write is not -aligned to page size and page-cache initiates read of a sector with -invalid integrity tag. +Skipping this step could also cause write failures due to IO operation alignments. +For example, kernel page cache can request a read of a full page that fails due to an uninitialized integrity tag. +It is usually a bug in the application that tries to read data that was not written before. endif::[] ifdef::ACTION_LUKSFORMAT,ACTION_LUKSADDKEY,ACTION_LUKSCHANGEKEY,ACTION_LUKSCONVERTKEY,ACTION_REENCRYPT,ACTION_BENCHMARK[] -*--iter-time, -i *:: +*--iter-time*, *-i* __:: ifndef::ACTION_REENCRYPT[] The number of milliseconds to spend with PBKDF passphrase processing. -Specifying 0 as parameter selects the compiled-in default. +Specifying 0 as a parameter selects the compiled-in default. endif::[] ifdef::ACTION_REENCRYPT[] -The number of milliseconds to spend with PBKDF passphrase processing for the -new LUKS header. +The number of milliseconds to spend with PBKDF passphrase processing for the new LUKS header. endif::[] endif::[] ifdef::ACTION_OPEN[] *--iv-large-sectors*:: -Count Initialization Vector (IV) in larger sector size (if set) -instead of 512 bytes sectors. This option can be used only with _plain_ -device type. +Count Initialization Vector (IV) in larger sector size (if set) instead of 512-byte sectors. +This option can be used only with the _plain_ device type. + -*NOTE:* This option does not have any performance or security impact, -use it only for accessing incompatible existing disk images from other -systems that require this option. +This option does not have any performance or security impact; use it only for accessing incompatible existing disk images from other systems that require this option. endif::[] ifdef::ACTION_TOKEN[] *--json-file*:: -Read token JSON from a file or write token to it. --json-file=- reads JSON from -standard input or writes it to standard output respectively. +Read the token JSON from a file or write the token to it. +Option --json-file=- reads JSON from standard input or writes it to standard output, respectively. endif::[] ifdef::ACTION_REENCRYPT[] *--keep-key*:: *LUKS2*: -Do not change effective volume key and change other parameters provided -it is requested. +Do not change the effective volume key, and change other parameters if requested. + *LUKS1*: -Reencrypt only the LUKS1 header and keyslots. Skips data in-place reencryption. +Reencrypt only the LUKS1 header and keyslots. +Skips data in-place reencryption. endif::[] -ifdef::ACTION_TOKEN[] -*--key-description *:: -Set key description in keyring for use with _token_ command. +ifdef::ACTION_OPEN,ACTION_LUKSFORMAT,ACTION_LUKSDUMP,ACTION_RESIZE,ACTION_LUKSRESUME,ACTION_LUKSADDKEY,ACTION_TOKEN[] +*--key-description* _text_:: +Set the key description in the keyring that will be used for passphrase retrieval. endif::[] ifdef::ACTION_OPEN,ACTION_RESIZE,ACTION_LUKSFORMAT,ACTION_LUKSRESUME,ACTION_LUKSADDKEY,ACTION_LUKSREMOVEKEY,ACTION_LUKSCHANGEKEY,ACTION_LUKSCONVERTKEY,ACTION_LUKSKILLSLOT,ACTION_LUKSDUMP,ACTION_TCRYPTDUMP,ACTION_REENCRYPT,ACTION_REPAIR,ACTION_BITLKDUMP[] -*--key-file, -d* _name_:: -Read the passphrase from file. +*--key-file*, *-d* _file_:: +Read the passphrase from the file. + If the name given is "-", then the passphrase will be read from stdin. In this case, reading will not stop at newline characters. + ifdef::ACTION_LUKSADDKEY,ACTION_LUKSCHANGEKEY[] -The passphrase supplied via --key-file is always the passphrase for existing -keyslot requested by the command. +The passphrase supplied via --key-file is always the passphrase for the existing keyslot requested by the command. + ifdef::ACTION_LUKSADDKEY[] -If you want to set a new passphrase via key file, you have to use a -positional argument or parameter --new-keyfile. +If you want to set a new passphrase via key file, you have to use a positional argument or parameter --new-keyfile. endif::[] ifdef::ACTION_LUKSCHANGEKEY[] -If you want to set a new passphrase via key file, you have to use a -positional argument. +If you want to set a new passphrase via a key file, you have to use a positional argument. endif::[] + endif::[] ifdef::ACTION_OPEN[] -*NOTE:* With _plain_ device type, the passphrase obtained via --key-file option is -passed directly in dm-crypt. Unlike the interactive mode (stdin) -where digest (--hash option) of the passphrase is passed in dm-crypt instead. +With _plain_ device type, the passphrase obtained via --key-file option is passed directly in dm-crypt. +Unlike the interactive mode (stdin), where the digest of the passphrase is passed in dm-crypt instead. + endif::[] ifndef::ACTION_REENCRYPT[] See section _NOTES ON PASSPHRASE PROCESSING_ in *cryptsetup*(8) for more information. endif::[] ifdef::ACTION_REENCRYPT[] -*WARNING:* --key-file option can be used only if there is only one active keyslot, -or alternatively, also if --key-slot option is specified (then all other keyslots -will be disabled in new LUKS device). +The --key-file option can be used only if there is only one active keyslot, or alternatively, also if --key-slot option is specified (then all other keyslots will be disabled in the new LUKS device). + -If this option is not used, cryptsetup will ask for all active keyslot -passphrases. +If this option is not used, cryptsetup will ask for all active keyslot passphrases. endif::[] endif::[] ifdef::ACTION_ERASE[] -*--key-file, -d* _name_ *(LUKS2 with HW OPAL only)*:: - -Read the Admin PIN or PSID (with --hw-opal-factory-reset) from file -depending on options used. +*--key-file*, *-d* _file_ (LUKS2 with HW OPAL only):: +Read the Admin PIN or PSID (with --hw-opal-factory-reset) from the file, depending on options used. + If the name given is "-", then the secret will be read from stdin. In this case, reading will not stop at newline characters. @@ -503,190 +485,218 @@ Skip _value_ bytes at the beginning of the key file. endif::[] ifdef::ACTION_OPEN,ACTION_RESIZE,ACTION_LUKSFORMAT,ACTION_LUKSRESUME,ACTION_LUKSADDKEY,ACTION_LUKSREMOVEKEY,ACTION_LUKSCHANGEKEY,ACTION_LUKSCONVERTKEY,ACTION_LUKSKILLSLOT,ACTION_LUKSDUMP,ACTION_REENCRYPT,ACTION_REPAIR,ACTION_BITLKDUMP[] -*--keyfile-size, -l* _value_:: -Read a maximum of _value_ bytes from the key file. The default is to -read the whole file up to the compiled-in maximum that can be queried -with --help. Supplying more data than the compiled-in maximum aborts -the operation. +*--keyfile-size*, *-l* _value_:: +Read a maximum of _value_ bytes from the key file. +The default is to read the whole file up to the compiled-in maximum that can be queried with --help. +Supplying more data than the compiled-in maximum aborts the operation. + -This option is useful to cut trailing newlines, for example. If ---keyfile-offset is also given, the size count starts after the offset. +This option is useful to cut trailing newlines, for example. +If --keyfile-offset is also given, the size count starts after the offset. endif::[] ifdef::ACTION_OPEN,ACTION_LUKSFORMAT,ACTION_REENCRYPT,ACTION_BENCHMARK,ACTION_LUKSADDKEY[] -*--key-size, -s* _bits_:: -ifndef::ACTION_LUKSADDKEY[] -Sets key size in _bits_. The argument has to be a multiple of 8. The -possible key-sizes are limited by the cipher and mode used. +*--key-size*, *-s* _bits_:: +ifndef::ACTION_LUKSADDKEY,ACTION_REENCRYPT[] +Sets key size in _bits_. +The argument has to be a multiple of 8. +The possible key sizes are limited by the cipher and mode used. + -See /proc/crypto for more information. Note that key-size in -/proc/crypto is stated in bytes. +See /proc/crypto for more information. +Note that the key size in /proc/crypto is stated in bytes. + endif::[] ifdef::ACTION_LUKSADDKEY[] -Provide volume key size in _bits_. The argument has to be a multiple of 8. +Provide volume key size in _bits_. +The argument has to be a multiple of 8. + -This option is required when parameter --volume-key-file is used to provide -current volume key. Also, it is used when new unbound keyslot is created by -specifying --unbound parameter. +This option is required when the parameter --volume-key-file is used to provide current volume key. +Also, it is used when a new unbound keyslot is created by specifying --unbound parameter. endif::[] ifdef::ACTION_OPEN[] -This option can be used for _plain_ device type only. +This option can be used for _plain_ and _luks_ devices. +For LUKS2 devices in reencryption, you may use the parameter twice to specify both old and new volume key sizes. +Each --key-size option corresponds to the respective --volume-key-file parameter (also allowed to be used up to two times). endif::[] ifndef::ACTION_REENCRYPT,ACTION_OPEN,ACTION_LUKSADDKEY[] -This option can be used for _open --type plain_ or _luksFormat_. All -other LUKS actions will use the key-size specified in the LUKS header. +This option can be used for _open --type plain_ or _luksFormat_. +All other LUKS actions will use the key size specified in the LUKS header. Use _cryptsetup --help_ to show the compiled-in defaults. endif::[] ifdef::ACTION_REENCRYPT[] -*LUKS1*: -If you are increasing key size, there must be enough space in the LUKS header -for enlarged keyslots (data offset must be large enough) or reencryption -cannot be performed. +*LUKS2*: +Provide current key size in _bits_. +The argument has to be a multiple of 8. +Useful when specifying the size of the current volume key when no keyslot is active. + -If there is not enough space for keyslots with new key size, -you can destructively shrink device with --reduce-device-size option. +*LUKS1*: +See --new-key-size. endif::[] endif::[] ifdef::ACTION_OPEN,ACTION_RESIZE,ACTION_LUKSFORMAT,ACTION_LUKSADDKEY,ACTION_LUKSCHANGEKEY,ACTION_LUKSCONVERTKEY,ACTION_LUKSDUMP,ACTION_LUKSRESUME,ACTION_TOKEN,ACTION_CONFIG,ACTION_TOKEN,ACTION_REPAIR,ACTION_REENCRYPT[] -*--key-slot, -S <0-N>*:: +*--key-slot*, *-S* _<0-N>_:: ifdef::ACTION_LUKSADDKEY[] -When used together with parameter --new-key-slot this option allows you to specify which -key slot is selected for unlocking volume key. +When used together with the parameter --new-key-slot, this option allows you to specify which keyslot is selected for unlocking the volume key. + -*NOTE:* This option is ignored if existing volume key gets unlocked -via LUKS2 token (--token-id, --token-type or --token-only parameters) or -when volume key is provided directly via --volume-key-file parameter. +This option is ignored if the existing volume key gets unlocked via LUKS2 token (--token-id, --token-type or --token-only parameters) or when volume key is provided directly via --volume-key-file parameter. + -*NOTE:* To maintain backward compatibility, without --new-key-slot parameter, -this option allows you to specify which key slot is selected for the new key. +To maintain backward compatibility, without --new-key-slot parameter, this option allows you to specify which keyslot is selected for the new key. endif::[] ifndef::ACTION_OPEN,ACTION_LUKSADDKEY[] -For LUKS operations that add key material, this option allows you to -specify which key slot is selected for the new key. +For LUKS operations that add key material, this option allows you to specify which keyslot is selected for the new key. endif::[] ifdef::ACTION_OPEN[] -This option selects a specific key-slot to -compare the passphrase against. If the given passphrase would only -match a different key-slot, the operation fails. +This option selects a specific keyslot to compare the passphrase against. +If the given passphrase would only matches a different keyslot, the operation fails. endif::[] + ifdef::ACTION_REENCRYPT[] -For reencryption mode it selects specific keyslot (and passphrase) that can be used to unlock new volume key. -If used all other keyslots get removed after reencryption operation is finished. +For reencryption mode, it selects a specific keyslot (and passphrase) that can be used to unlock the new volume key. +If used, all other keyslots get removed after the reencryption operation is finished. + endif::[] -The maximum number of key slots depends on the LUKS version. LUKS1 can have up -to 8 key slots. LUKS2 can have up to 32 key slots based on key slot area -size and key size, but a valid key slot ID can always be between 0 and -31 for LUKS2. +The maximum number of keyslots depends on the LUKS version. +LUKS1 can have up to 8 keyslots. +LUKS2 can have up to 32 keyslots based on keyslot area size and key size, but a valid keyslot ID can always be between 0 and 31 for LUKS2. endif::[] ifdef::ACTION_LUKSFORMAT,ACTION_LUKSADDKEY,ACTION_LUKSCHANGEKEY,ACTION_LUKSCONVERTKEY,ACTION_REENCRYPT[] -*--keyslot-cipher *:: -This option can be used to set specific cipher encryption for the -LUKS2 keyslot area. +*--keyslot-cipher* __:: +This option can be used to set specific cipher encryption for the LUKS2 keyslot area. endif::[] ifdef::ACTION_LUKSFORMAT,ACTION_LUKSADDKEY,ACTION_LUKSCHANGEKEY,ACTION_LUKSCONVERTKEY,ACTION_REENCRYPT[] -*--keyslot-key-size *:: -This option can be used to set specific key size for the LUKS2 keyslot -area. +*--keyslot-key-size* __:: +This option can be used to set a specific key size for the LUKS2 keyslot area. endif::[] ifdef::ACTION_LUKSFORMAT,ACTION_CONFIG,ACTION_REENCRYPT[] -*--label