mirror of
https://git.sr.ht/~nabijaczleweli/tzpfms
synced 2025-04-25 09:52:11 +03:00
Compare commits
118 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fd16dbbcd3 | ||
|
82b5118fcb | ||
|
2335a22bc7 | ||
|
588cce3882 | ||
|
2764c6e0d3 | ||
|
44f9b25314 | ||
|
d6a2d4683d | ||
|
dcf545a6f6 | ||
|
48bebe9be3 | ||
|
81add25df9 | ||
|
56ada0e31c | ||
|
f3bb25ee57 | ||
|
887f9b6386 | ||
|
1d39364a02 | ||
|
7e4ea2c2da | ||
|
3d082a3efd | ||
|
fc57e89e37 | ||
|
a77e6c1d80 | ||
|
52a4871ce0 | ||
|
984c5d5354 | ||
|
7c0393e894 | ||
|
2118cc3679 | ||
|
83664adbc8 | ||
|
a20c275ceb | ||
|
1f9fe814fe | ||
|
5df51d163e | ||
|
e271fc1ea2 | ||
|
31f4ef215c | ||
|
f5996ad11a | ||
|
f81d510053 | ||
|
2fc0bffe7d | ||
|
a4294c83c5 | ||
|
6a143b6e8b | ||
|
b31a131eb3 | ||
|
d3d43f281b | ||
|
f0dfcc2110 | ||
|
a0246cb48e | ||
|
fcd89b3a93 | ||
|
ba81298aaa | ||
|
69a1bcc2ce | ||
|
bf78dcbc80 | ||
|
f322a48302 | ||
|
cfd02ff6f3 | ||
|
8997b47acf | ||
|
a85bcbeca2 | ||
|
d0cd0c93f9 | ||
|
7329bfb5f6 | ||
|
12d4b36c69 | ||
|
f4b3cd202a | ||
|
b3764d8626 | ||
|
b8b856870c | ||
|
1f0d78963e | ||
|
31dbb443af | ||
|
6b3cc6971d | ||
|
286180b138 | ||
|
c651450b5d | ||
|
1ae0e258a7 | ||
|
47cd20aa32 | ||
|
e78ba80bc9 | ||
|
8e02233ad5 | ||
|
c727114dce | ||
|
aa1c21fc27 | ||
|
1345229369 | ||
|
d51d8a3c86 | ||
|
39c3201f63 | ||
|
6a8d97e43a | ||
|
f403ffebad | ||
|
d2dcf95b0f | ||
|
720a0103a7 | ||
|
b5ff262534 | ||
|
0f2642ba92 | ||
|
d950de0ae4 | ||
|
b811862e0a | ||
|
02195933bc | ||
|
023ffc98ae | ||
|
af6835d586 | ||
|
005ee41545 | ||
|
29c58cfe9b | ||
|
62e896642b | ||
|
aef018ba1e | ||
|
d26a07511f | ||
|
fa91ff6c67 | ||
|
3651105e0f | ||
|
b45a331747 | ||
|
af6928d525 | ||
|
ce3560b797 | ||
|
00703a1517 | ||
|
32da9f7c81 | ||
|
1937610e54 | ||
|
5d7ebf3d19 | ||
|
503ac72235 | ||
|
e0b0de31b9 | ||
|
49f0a05c33 | ||
|
de9b591546 | ||
|
c9cd46ab95 | ||
|
303ea58c2f | ||
|
09748712fb | ||
|
2757a01c92 | ||
|
a4cc3cd7e0 | ||
|
98bd432a50 | ||
|
4e41ae1f14 | ||
|
f02bf1c094 | ||
|
ea0a5bd52e | ||
|
8df40d5506 | ||
|
12189bc0d5 | ||
|
9c8ee8d68c | ||
|
74ec450e16 | ||
|
a0ca4c3a97 | ||
|
d8d29927c7 | ||
|
c1ab9a5a1a | ||
|
dc8bc7acb7 | ||
|
d5107f9415 | ||
|
01fa1b6031 | ||
|
9817d9d8ae | ||
|
6ae9df1c95 | ||
|
5b0685ec94 | ||
|
3174a66e9e | ||
|
1a5f1f9498 |
51
.build.yml
51
.build.yml
@ -1,35 +1,44 @@
|
|||||||
image: debian/sid
|
image: debian/sid
|
||||||
secrets:
|
secrets:
|
||||||
- ccb6777e-650b-4fa2-87e1-e2342f5bb605 # tzpfms SSH key
|
- ccb6777e-650b-4fa2-87e1-e2342f5bb605 # tzpfms SSH key
|
||||||
- 513a97a6-300c-4749-93a7-7816b2c4f41d # tzpfms auth token
|
- b26aef46-5e87-45b1-a64d-d136ccde36a7 # tzpfms auth token
|
||||||
packages:
|
packages:
|
||||||
- clang
|
- clang
|
||||||
- llvm-dev
|
- llvm-dev
|
||||||
- pkg-config
|
- pkg-config
|
||||||
- libtss2-dev
|
- libtss2-dev
|
||||||
- libtspi-dev
|
- libtspi-dev
|
||||||
- ronn
|
- libssl-dev
|
||||||
|
- gettext
|
||||||
|
- mandoc
|
||||||
- shellcheck
|
- shellcheck
|
||||||
- curl
|
- curl
|
||||||
|
- groff
|
||||||
|
- ghostscript
|
||||||
tasks:
|
tasks:
|
||||||
- get-zfs: |
|
- get-zfs: |
|
||||||
sudo sed -i 's/main/main contrib non-free/' /etc/apt/sources.list
|
sudo sed -i 's/main/main contrib/' /etc/apt/sources.list
|
||||||
sudo apt update
|
sudo apt-get update
|
||||||
sudo apt install -y libzfslinux-dev
|
sudo apt-get install -y libzfslinux-dev
|
||||||
- build-gcc: |
|
- build-gcc: |
|
||||||
cd tzpfms
|
cd tzpfms
|
||||||
|
(cd src/bin/; ln -s ../../contrib/*.cpp .)
|
||||||
make
|
make
|
||||||
find out/ -maxdepth 1 -type f -exec readelf -d {} +
|
find out/ -maxdepth 1 -type f -exec readelf -d {} + | tee /dev/stderr | grep Shared | LC_ALL=C sort -u
|
||||||
make clean
|
make clean
|
||||||
|
find src/bin/ -type l -delete
|
||||||
- build-clang: |
|
- build-clang: |
|
||||||
cd tzpfms
|
cd tzpfms
|
||||||
CC=clang CXX=clang++ make
|
CC=clang CXX=clang++ make
|
||||||
find out/ -maxdepth 1 -type f -exec readelf -d {} +
|
find out/ -maxdepth 1 -type f -exec readelf -d {} + | tee /dev/stderr | grep Shared | LC_ALL=C sort -u
|
||||||
- manpages: |
|
- manpages: |
|
||||||
|
sudo sh -c 'curl https://git.sr.ht/~nabijaczleweli/groff-1.23-unfucking/blob/trunk/mdoc.local >> /etc/groff/mdoc.local'
|
||||||
git -C tzpfms/ worktree add ../tzpfms-man man
|
git -C tzpfms/ worktree add ../tzpfms-man man
|
||||||
cd tzpfms-man
|
cd tzpfms-man
|
||||||
git ls-tree -z --name-only HEAD | xargs -0 rm -r
|
git ls-tree -z --name-only HEAD | grep -z tpm | xargs -0 rm -r
|
||||||
cp -pr ../tzpfms/out/man/* .
|
cp -pr ../tzpfms/out/man/* .
|
||||||
|
sed -e 's/…/.../g' $(ls *.[12345678] | awk -F. '{print $2 "\t" $1}' | sort | awk -F'\t' '{print $2 "." $1}') | groff -K utf8 -tpe -mdoc -Tps -dpaper=a4 -P-pa4 > tzpfms.ps
|
||||||
|
ps2pdf -sPAPERSIZE=a4 tzpfms.ps tzpfms.pdf
|
||||||
git add .
|
git add .
|
||||||
git config user.email "nabijaczleweli/autouploader@nabijaczleweli.xyz"
|
git config user.email "nabijaczleweli/autouploader@nabijaczleweli.xyz"
|
||||||
git config user.name "наб autouploader"
|
git config user.name "наб autouploader"
|
||||||
@ -38,20 +47,34 @@ tasks:
|
|||||||
ssh-keyscan git.sr.ht > ~/.ssh/known_hosts
|
ssh-keyscan git.sr.ht > ~/.ssh/known_hosts
|
||||||
git push
|
git push
|
||||||
- release: |
|
- release: |
|
||||||
|
set -o posix # bash in non-POSIX mode doesn't process aliases non-interactively. good shell.
|
||||||
tag="$(git -C tzpfms describe --abbrev=0 --tags || echo HEAD~1)"
|
tag="$(git -C tzpfms describe --abbrev=0 --tags || echo HEAD~1)"
|
||||||
[ "$(git -C tzpfms rev-list -n1 "$tag")" = "$(git -C tzpfms rev-list -n1 HEAD)" ] || exit 0
|
[ "$(git -C tzpfms rev-list -n1 "$tag")" = "$(git -C tzpfms rev-list -n1 HEAD)" ] || exit 0
|
||||||
mkdir "tzpfms-$tag-bin-amd64"
|
mkdir "tzpfms-$tag-bin-amd64"
|
||||||
mv tzpfms/out/zfs-tpm* "tzpfms-$tag-bin-amd64"
|
mv tzpfms/out/zfs-tpm* "tzpfms-$tag-bin-amd64"
|
||||||
|
mv tzpfms/out/locale "tzpfms-$tag-bin-amd64"
|
||||||
mv tzpfms/out/dracut "tzpfms-$tag-dracut"
|
mv tzpfms/out/dracut "tzpfms-$tag-dracut"
|
||||||
mv tzpfms/out/initramfs-tools "tzpfms-$tag-initramfs-tools"
|
mv tzpfms/out/initramfs-tools "tzpfms-$tag-initramfs-tools"
|
||||||
|
mv tzpfms/out/systemd "tzpfms-$tag-systemd"
|
||||||
mv tzpfms/out/man "tzpfms-$tag-man"
|
mv tzpfms/out/man "tzpfms-$tag-man"
|
||||||
for s in bin-amd64 dracut initramfs-tools man; do
|
mv tzpfms-man/tzpfms.ps "tzpfms-$tag-manual.ps"
|
||||||
|
mv tzpfms-man/tzpfms.pdf "tzpfms-$tag-manual.pdf"
|
||||||
|
for s in bin-amd64 dracut initramfs-tools systemd man; do
|
||||||
tar -caf "tzpfms-$tag-$s.tbz2" "tzpfms-$tag-$s"
|
tar -caf "tzpfms-$tag-$s.tbz2" "tzpfms-$tag-$s"
|
||||||
done
|
done
|
||||||
|
sha256sum *.tbz2 "tzpfms-$tag-manual.ps" "tzpfms-$tag-manual.pdf"
|
||||||
set +x # Avoid echoing the token
|
set +x # Avoid echoing the token
|
||||||
for f in *.tbz2; do
|
alias curl="curl --oauth2-bearer $(cat ~/.release-token)"
|
||||||
curl -H "Authorization: Bearer $(cat ~/.release-token)" \
|
id="$(curl -F operations='{"query": "{me {repository(name: \"tzpfms\") {id}}}"}' -F map='{}' https://git.sr.ht/query | tr -cd 0-9)"
|
||||||
-XPOST \
|
for f in *.tbz2 "tzpfms-$tag-manual.ps" "tzpfms-$tag-manual.pdf"; do
|
||||||
-F "file=@$f" \
|
curl -F operations='{
|
||||||
"https://git.sr.ht/api/repos/tzpfms/artifacts/$tag"
|
"query": "mutation($id: Int!, $rev: String!, $file: Upload!) {uploadArtifact(repoId: $id, revspec: $rev, file: $file) { created, filename, checksum } }",
|
||||||
|
"variables": { "file": null, "rev": "'"$(git -C tzpfms rev-list -n1 "$tag")"'", "id": '"$id"' }
|
||||||
|
}' \
|
||||||
|
-F map='{
|
||||||
|
"a": ["variables.file"]
|
||||||
|
}' \
|
||||||
|
-F a=@"$f" \
|
||||||
|
https://git.sr.ht/query
|
||||||
|
echo
|
||||||
done
|
done
|
||||||
|
13
.gitignore
vendored
13
.gitignore
vendored
@ -4,17 +4,22 @@
|
|||||||
!.build.yml
|
!.build.yml
|
||||||
!.travis.yml
|
!.travis.yml
|
||||||
!gh_rsa.enc
|
!gh_rsa.enc
|
||||||
!LICENSE
|
|
||||||
!.clang-format
|
!.clang-format
|
||||||
!*Makefile
|
!Makefile
|
||||||
!*.sublime-project
|
!*.sublime-project
|
||||||
!*.md
|
!*.md
|
||||||
!*.awk
|
!*.awk
|
||||||
|
!LICENSES
|
||||||
|
!LICENSES/**
|
||||||
!src
|
!src
|
||||||
!src/**
|
!src/**
|
||||||
!man
|
!man
|
||||||
!man/**
|
!man/**
|
||||||
!ext
|
!contrib
|
||||||
!ext/**
|
!contrib/**
|
||||||
!initrd
|
!initrd
|
||||||
!initrd/**
|
!initrd/**
|
||||||
|
!init.d
|
||||||
|
!init.d/**
|
||||||
|
!po
|
||||||
|
!po/**
|
||||||
|
10
LICENSES/0BSD.txt
Normal file
10
LICENSES/0BSD.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
200
Makefile
200
Makefile
@ -1,93 +1,163 @@
|
|||||||
# The MIT License (MIT)
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
# Copyright (c) 2020 наб <nabijaczleweli@nabijaczleweli.xyz>
|
|
||||||
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
# this software and associated documentation files (the "Software"), to deal in
|
|
||||||
# the Software without restriction, including without limitation the rights to
|
|
||||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
# subject to the following conditions:
|
|
||||||
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
include configMakefile
|
-include Makefile.local
|
||||||
|
|
||||||
|
|
||||||
|
TZPFMS_VERSION ?= "$(patsubst v%,%,$(shell git describe))"
|
||||||
|
SOURCE_DATE_EPOCH ?=
|
||||||
|
TZPFMS_DATE ?=
|
||||||
|
|
||||||
|
|
||||||
|
AWK ?= awk
|
||||||
|
SED ?= sed
|
||||||
|
MANDOC ?= mandoc
|
||||||
|
PKG_CONFIG ?= pkgconf
|
||||||
|
SHELLCHECK ?= shellcheck
|
||||||
|
|
||||||
|
NOLOCREGEN ?=
|
||||||
|
OUTDIR := out/
|
||||||
|
BLDDIR := out/build/
|
||||||
|
|
||||||
|
SYSTEMD_SYSTEM_UNITDIR := $(shell $(PKG_CONFIG) --variable=systemd_system_unit_dir systemd 2>/dev/null || echo /usr/lib/systemd/system)
|
||||||
|
ifneq "$(TZPFMS_DATE)" ""
|
||||||
|
MANUAL_DATE := $(TZPFMS_DATE)
|
||||||
|
else ifneq "$(SOURCE_DATE_EPOCH)" ""
|
||||||
|
MANUAL_DATE := $(shell LC_ALL=C date -d@$(SOURCE_DATE_EPOCH) '+%B %e, %Y')
|
||||||
|
else
|
||||||
|
MANUAL_DATE = $(shell LC_ALL=C date -d@$(shell git log -1 --no-show-signature --format=%at $(1)) '+%B %e, %Y')
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
LDDLLS := rt tspi $(OS_LD_LIBS)
|
|
||||||
PKGS := libzfs libzfs_core tss2-esys tss2-rc
|
PKGS := libzfs libzfs_core tss2-esys tss2-rc
|
||||||
LDAR := $(LNCXXAR) $(foreach l,,-L$(BLDDIR)$(l)) $(foreach dll,$(LDDLLS),-l$(dll)) $(shell pkg-config --libs $(PKGS))
|
PKG_CONFIG_LIBS := $(shell $(PKG_CONFIG) --libs $(PKGS))
|
||||||
INCAR := $(foreach l,$(foreach l,,$(l)/include),-isystemext/$(l)) $(foreach l,,-isystem$(BLDDIR)$(l)/include) $(shell pkg-config --cflags $(PKGS))
|
PKG_CONFIG_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PKGS))
|
||||||
VERAR := $(foreach l,TZPFMS,-D$(l)_VERSION='$($(l)_VERSION)')
|
|
||||||
BINARY_SOURCES := $(sort $(wildcard $(SRCDIR)bin/*.cpp $(SRCDIR)bin/**/*.cpp))
|
LDFLAGS += -Wl,--as-needed
|
||||||
COMMON_SOURCES := $(filter-out $(BINARY_SOURCES),$(sort $(wildcard $(SRCDIR)*.cpp $(SRCDIR)**/*.cpp $(SRCDIR)**/**/*.cpp $(SRCDIR)**/**/**/*.cpp)))
|
LDLIBS += -lrt -ltspi -lcrypto $(PKG_CONFIG_LIBS)
|
||||||
MANPAGE_HEADERS := $(sort $(wildcard $(MANDIR)*.h))
|
CXXFLAGS += -g -O3 -std=c++17 -fno-exceptions -fno-rtti -Wall -Wextra -pipe -fPIC $(PKG_CONFIG_CFLAGS)
|
||||||
MANPAGE_SOURCES := $(sort $(wildcard $(MANDIR)*.md.pp))
|
CPPFLAGS += -MD -DTZPFMS_VERSION='$(TZPFMS_VERSION)'
|
||||||
INITRD_HEADERS := $(sort $(wildcard $(INITRDDIR)*.h))
|
BINARY_SOURCES := $(sort $(wildcard src/bin/*.cpp src/bin/**/*.cpp))
|
||||||
|
COMMON_SOURCES := $(filter-out $(BINARY_SOURCES),$(sort $(wildcard src/*.cpp src/**/*.cpp src/**/**/*.cpp src/**/**/**/*.cpp)))
|
||||||
|
MANPAGE_HEADERS := $(sort $(wildcard man/*.h))
|
||||||
|
MANPAGE_SOURCES := $(sort $(wildcard man/*.[0-8].pp))
|
||||||
|
INITRD_HEADERS := $(sort $(wildcard initrd/*.h))
|
||||||
|
ifneq (,$(shell command -v msgfmt))
|
||||||
|
LOCALES := $(wildcard po/*.po)
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
.PHONY : all clean build shellcheck i-t dracut man
|
ifdef TZPFMS_PASSPHRASE_HELPER
|
||||||
.SECONDARY:
|
CPPFLAGS += -DTZPFMS_PASSPHRASE_HELPER='$(TZPFMS_PASSPHRASE_HELPER)'
|
||||||
|
endif
|
||||||
|
ifdef TZPFMS_PASSPHRASE_HELPER_MAN
|
||||||
|
DEF_TPH_MAN := .Pp\nDefault:\n.No '\'' Ns $(TZPFMS_PASSPHRASE_HELPER_MAN) Ns '\'' .
|
||||||
|
else
|
||||||
|
DEF_TPH_MAN ?= .
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
all : build man shellcheck i-t dracut
|
ifneq "$(findstring clang,$(shell $(CXX) --version))" ""
|
||||||
|
# GCC doesn't have this granularity
|
||||||
|
CXXFLAGS += -flto=full -pedantic -Wno-gnu-statement-expression -Wno-gnu-include-next -Wno-gnu-conditional-omitted-operand -Wno-c++20-designator -Wno-variadic-macros -Wno-c99-extensions
|
||||||
|
ifeq "$(AR)" "ar"
|
||||||
|
# Need this because -flto=full produces objects FSF ranlib (ar s) can't index
|
||||||
|
override AR := llvm-ar
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
CXXFLAGS += -flto
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY : all locales clean build shellcheck i-t dracut init.d-systemd manpages htmlpages
|
||||||
|
.SECONDARY :
|
||||||
|
|
||||||
|
|
||||||
|
all : build locales manpages htmlpages shellcheck i-t init.d-systemd dracut
|
||||||
|
|
||||||
shellcheck : i-t dracut
|
shellcheck : i-t dracut
|
||||||
find $(OUTDIR)initramfs-tools/ $(OUTDIR)dracut -name '*.sh' -exec echo $(SHELLCHECK) --exclude SC1091 {} + | sh -x
|
$(SHELLCHECK) --exclude SC1091,SC2093 $(foreach l,$(OUTDIR)initramfs-tools/ $(OUTDIR)dracut/ init.d/,$(wildcard $(l)*.sh $(l)**/*.sh $(l)**/**/*.sh $(l)**/**/**/*.sh $(l)**/**/**/**/*.sh $(l)**/**/**/**/**/*.sh $(l)**/**/**/**/**/**/*.sh $(l)**/**/**/**/**/**/**/*.sh $(l)**/**/**/**/**/**/**/**/*.sh))
|
||||||
|
|
||||||
clean :
|
clean :
|
||||||
rm -rf $(OUTDIR)
|
rm -rf $(OUTDIR)
|
||||||
|
|
||||||
build : $(subst $(SRCDIR)bin/,$(OUTDIR),$(subst .cpp,$(EXE),$(BINARY_SOURCES)))
|
build : $(subst src/bin/,$(OUTDIR),$(subst .cpp,,$(BINARY_SOURCES)))
|
||||||
man : $(OUTDIR)man/index.txt
|
manpages : $(patsubst man/%.pp,$(OUTDIR)man/%,$(MANPAGE_SOURCES))
|
||||||
|
htmlpages : $(patsubst man/%.pp,$(OUTDIR)man/%.html,$(MANPAGE_SOURCES)) $(OUTDIR)man/style.css
|
||||||
|
locales : $(patsubst po/%.po,$(OUTDIR)locale/%/LC_MESSAGES/tzpfms.mo,$(LOCALES))
|
||||||
i-t : $(OUTDIR)initramfs-tools/usr/share/initramfs-tools/hooks/tzpfms $(OUTDIR)initramfs-tools/usr/share/tzpfms/initramfs-tools-zfs-patch.sh
|
i-t : $(OUTDIR)initramfs-tools/usr/share/initramfs-tools/hooks/tzpfms $(OUTDIR)initramfs-tools/usr/share/tzpfms/initramfs-tools-zfs-patch.sh
|
||||||
dracut : $(patsubst $(INITRDDIR)dracut/%,$(OUTDIR)dracut/usr/lib/dracut/modules.d/91tzpfms/%,$(sort $(wildcard $(INITRDDIR)dracut/*.sh)))
|
dracut : $(patsubst initrd/dracut/%,$(OUTDIR)dracut/usr/lib/dracut/modules.d/91tzpfms/%,$(sort $(wildcard initrd/dracut/*.sh)))
|
||||||
|
init.d-systemd : $(OUTDIR)systemd/$(SYSTEMD_SYSTEM_UNITDIR)/zfs-load-key@.service.d/tzpfms.conf $(OUTDIR)systemd/usr/libexec/tzpfms-zfs-load-key@ $(foreach l,$(subst init.d/systemd/usr-share-tzpfms/,,$(wildcard init.d/systemd/usr-share-tzpfms/*)),$(OUTDIR)systemd/usr/share/tzpfms/$(l))
|
||||||
|
|
||||||
|
|
||||||
$(OUTDIR)man/index.txt : $(MANDIR)index.txt $(patsubst $(MANDIR)%.pp,$(OUTDIR)man/%,$(MANPAGE_SOURCES))
|
$(BLDDIR)tzpfms.pot: src/*.[ch]pp src/bin/*.[ch]pp
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(@D)
|
||||||
cp $< $(dir $@)
|
$(NOLOCREGEN)xgettext --check=ellipsis-unicode --from-code=UTF-8 -c -io- $^ | sed -n '/^#[:.]/,$$p' > $@
|
||||||
$(RONN) --organization="tzpfms developers" $(filter-out $<,$^)
|
$(NOLOCREGEN)$(foreach l,$(LOCALES),msgmerge --backup=off --no-wrap -Uiq $(l) $@ &&) :
|
||||||
$(RONN) --organization="tzpfms developers" -f $(filter-out $<,$^)
|
@>> $@
|
||||||
|
|
||||||
$(OUTDIR)initramfs-tools/usr/share/initramfs-tools/hooks/tzpfms: $(INITRDDIR)initramfs-tools/hook $(INITRD_HEADERS)
|
$(OUTDIR)locale/%/LC_MESSAGES/tzpfms.mo : po/%.po $(BLDDIR)tzpfms.pot
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
msgfmt --statistics --check-format --check-domain -o $@ $<
|
||||||
|
|
||||||
|
|
||||||
|
$(OUTDIR)initramfs-tools/usr/share/initramfs-tools/hooks/tzpfms: initrd/initramfs-tools/hook $(INITRD_HEADERS)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(AWK) -f pp.awk $< > $@
|
$(AWK) -f pp.awk $< > $@
|
||||||
chmod --reference $< $@
|
chmod --reference $< $@
|
||||||
|
|
||||||
$(OUTDIR)initramfs-tools/usr/share/tzpfms/initramfs-tools-zfs-patch.sh: $(INITRDDIR)initramfs-tools/zfs-patch.sh $(INITRD_HEADERS)
|
$(OUTDIR)initramfs-tools/usr/share/tzpfms/initramfs-tools-zfs-patch.sh: initrd/initramfs-tools/zfs-patch.sh $(INITRD_HEADERS)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(AWK) -f pp.awk $< > $@
|
||||||
|
chmod --reference $< $@
|
||||||
|
|
||||||
|
$(OUTDIR)systemd/$(SYSTEMD_SYSTEM_UNITDIR)/zfs-load-key@.service.d/tzpfms.conf : init.d/systemd/zfs-load-key@.service.d-tzpfms.conf
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
ln -f $< $@ || cp $< $@
|
||||||
|
|
||||||
|
$(OUTDIR)systemd/usr/libexec/tzpfms-zfs-load-key@ : init.d/systemd/libexec-tzpfms-zfs-load-key@.sh
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
ln -f $< $@ || cp $< $@
|
||||||
|
|
||||||
|
$(OUTDIR)systemd/usr/share/tzpfms/% : init.d/systemd/usr-share-tzpfms/%
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
ln -f $< $@ || cp $< $@
|
||||||
|
|
||||||
|
# The d-v-o-s string starts at "BSD" (hence the "BSD General Commands Manual" default); we're not BSD, so hide it
|
||||||
|
# Can't put it at the very top, since man(1) only loads mdoc *after* the first mdoc macro (.Dd in our case)
|
||||||
|
$(OUTDIR)man/% : man/%.pp $(MANPAGE_HEADERS)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(AWK) -f pp.awk $< TZPFMS_PASSPHRASE_HELPER_MAN='$(DEF_TPH_MAN)' | $(AWK) '/^$$/ {prev_empty=1; next} $$1 == "#" && $$2 ~ /^[0-9]*$$/ {prev_empty=0; next} {if(prev_empty) print ""; prev_empty=0; print}' | $(AWK) '$$0 == ".Dd" {$$2 = "$(call MANUAL_DATE,$<)"} $$1 == ".Dt" { print ".ds doc-volume-operating-system" } $$0 == ".Os" {$$2 = "tzpfms"; $$3 = $(TZPFMS_VERSION)} {print}' > $@
|
||||||
|
! $(MANDOC) -Tlint $@ 2>&1 | grep -vE -e 'mandoc: outdated mandoc.db' -e 'STYLE: referenced manual not found' -e 'STYLE: operating system explicitly specified: Os tzpfms' -e 'WARNING: cross reference to self: Xr zfs-tpm.*-change-key 8' -e 'STYLE: input text line longer than 80 bytes'
|
||||||
|
# The "WARNING: unknown font, skipping request: TS.+fC[RBI]" one: see https://bugs.debian.org/992002
|
||||||
|
|
||||||
|
$(OUTDIR)man/%.html : $(OUTDIR)man/%
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
( cd $(OUTDIR)man/ && $(MANDOC) -Thtml -Ostyle="style.css",man="%N.%S.html;https://manpages.debian.org/bookworm/%N.%S" ) < $< | \
|
||||||
|
$(AWK) '/^<h1/ {in_syn = $$0 ~ /id="SYNOPSIS"/} /^<br/ {if(in_syn) next} {print}' | \
|
||||||
|
$(SED) -Ee 's/ title=".."//g' -e 's/<a class="permalink" href="#([^"]*)"><span class="No" id="([^"]*)">/<a><span class="No">/g' -e 's#manpages.debian.org/[^/]*/ESYS_CONTEXT.3#mankier.com/3/ESYS_CONTEXT#g' > $@
|
||||||
|
|
||||||
|
$(OUTDIR)man/style.css : man/style.css
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cp $^ $@
|
||||||
|
|
||||||
|
$(BLDDIR)libtzpfms.a : $(subst src/,$(BLDDIR)obj/,$(subst .cpp,.o,$(COMMON_SOURCES)))
|
||||||
|
$(AR) --thin crs $@ $^
|
||||||
|
|
||||||
|
|
||||||
|
$(OUTDIR)% : $(BLDDIR)obj/bin/%.o $(BLDDIR)libtzpfms.a
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||||
|
|
||||||
|
$(BLDDIR)obj/%.o : src/%.cpp
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
$(OUTDIR)dracut/usr/lib/dracut/modules.d/91tzpfms/% : initrd/dracut/% $(INITRD_HEADERS)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(AWK) -f pp.awk $< > $@
|
$(AWK) -f pp.awk $< > $@
|
||||||
chmod --reference $< $@
|
chmod --reference $< $@
|
||||||
|
|
||||||
|
|
||||||
$(OUTDIR)%$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(SRCDIR)bin/%.cpp $(COMMON_SOURCES)))
|
include $(wildcard $(BLDDIR)*/*.d $(BLDDIR)*/*/*.d)
|
||||||
@mkdir -p $(dir $@)
|
|
||||||
$(CXX) $(CXXAR) -o$@ $^ $(PIC) -Wl,--as-needed $(LDAR)
|
|
||||||
$(STRIP) $(STRIPAR) $@
|
|
||||||
|
|
||||||
$(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp
|
|
||||||
@mkdir -p $(dir $@)
|
|
||||||
$(CXX) $(CXXAR) $(INCAR) $(VERAR) -c -o$@ $^
|
|
||||||
|
|
||||||
$(BLDDIR)test/%$(OBJ) : $(TSTDIR)%.cpp
|
|
||||||
@mkdir -p $(dir $@)
|
|
||||||
$(CXX) $(CXXAR) $(INCAR) -I$(SRCDIR) $(VERAR) -c -o$@ $^
|
|
||||||
|
|
||||||
$(OUTDIR)man/%.md : $(MANDIR)%.md.pp $(MANPAGE_HEADERS)
|
|
||||||
@mkdir -p $(dir $@)
|
|
||||||
$(AWK) -f pp.awk $< > $@
|
|
||||||
|
|
||||||
$(OUTDIR)dracut/usr/lib/dracut/modules.d/91tzpfms/% : $(INITRDDIR)dracut/% $(INITRD_HEADERS)
|
|
||||||
@mkdir -p $(dir $@)
|
|
||||||
$(AWK) -f pp.awk $< > $@
|
|
||||||
chmod --reference $< $@
|
|
||||||
|
79
README.md
79
README.md
@ -1,7 +1,9 @@
|
|||||||
# tzpfms [](https://builds.sr.ht/~nabijaczleweli/tzpfms) [](LICENSE)
|
# tzpfms [](//builds.sr.ht/~nabijaczleweli/tzpfms)
|
||||||
TPM-based encryption keys for ZFS datasets.
|
TPM-based encryption keys for ZFS datasets.
|
||||||
|
|
||||||
## [Manpages](//git.sr.ht/~nabijaczleweli/tzpfms/tree/man)
|
See also [fzifdso](//git.sr.ht/~nabijaczleweli/fzifdso) for FIDO2/WebAuthn-based (YubiKey, Somu, &c.) keys.
|
||||||
|
|
||||||
|
## [Manpages](//ra.ws.co.ls/~nabijaczleweli/tzpfms/blob/man/zfs-tpm-list.8.html) ([PDF](//ra.ws.co.ls/~nabijaczleweli/tzpfms/blob/man/tzpfms.pdf))
|
||||||
|
|
||||||
### Why?
|
### Why?
|
||||||
|
|
||||||
@ -18,51 +20,71 @@ Essentially BitLocker, but for ZFS –
|
|||||||
a random raw key is generated and sealed to the TPM (both 2 and 1.x supported) with an additional optional password in front of it,
|
a random raw key is generated and sealed to the TPM (both 2 and 1.x supported) with an additional optional password in front of it,
|
||||||
tying the dataset to the platform and an additional optional secret (or to the posession of the back-up).
|
tying the dataset to the platform and an additional optional secret (or to the posession of the back-up).
|
||||||
|
|
||||||
|
Additionally, 1.x TPMs support PCR binding with and without passwords.
|
||||||
|
2 TPMs support PCR binding without a password and PCR binding *OR* a password – both may be set, and any can be used to unseal (exclusive by default to prevent foot-guns).
|
||||||
|
|
||||||
Both dracut (with/without Plymouth) (with/without hostonly) (only on systemd systems, I don't have a test-bed for the non-systemd path)
|
Both dracut (with/without Plymouth) (with/without hostonly) (only on systemd systems, I don't have a test-bed for the non-systemd path)
|
||||||
and initramfs-tools (with/without Plymouth) are supported for [ZFS-on-root](https://nabijaczleweli.xyz/content/blogn_t/005-low-curse-zfs-on-root.html) set-ups.
|
and initramfs-tools (with/without Plymouth) are supported for [ZFS-on-root](//nabijaczleweli.xyz/content/blogn_t/005-low-curse-zfs-on-root.html) set-ups.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
You'll need `pkg-config`, `ronn`, `shellcheck`, `libzfslinux-dev`, `libtss2-dev`, `libtspi-dev`, and `make` should hopefully Just Work™ if you have a C++17-capable compiler.
|
You'll need `pkgconf`, `shellcheck`, `libzfslinux-dev` (0.8.x and 2.[012].x work), `libtss2-dev`, `libtspi-dev`, `libssl-dev`, and `make` should hopefully Just Work™ if you have a C++17-capable compiler.
|
||||||
The output binaries are trimmed of extraneous dependencies, so they're all just libc + libzfs and friends + the chosen TPM back-end, if any.
|
The output binaries are trimmed of extraneous dependencies, so they're all just libc + libzfs and friends + the chosen TPM back-end, if any + libcrypto for TPM2 PCR handling.
|
||||||
|
|
||||||
|
`mandoc` is required for HTML manuals. Set `MANDOC=true` to forgo this.
|
||||||
|
|
||||||
|
The default `$TZPFMS_PASSPHRASE_HELPER` is the null string.
|
||||||
|
To set a different default, set `TZPFMS_PASSPHRASE_HELPER` and `TZPFMS_PASSPHRASE_HELPER_MAN` for `make` — `$`s need to be double-escaped and `'`s need to be full-`'` escaped (i.e. `'\''`).
|
||||||
|
|
||||||
|
As an example, for a sensible default value of `exec systemd-ask-password --id="tzpfms:$2" "$1:"` for OOB systemd integration, pass `TZPFMS_PASSPHRASE_HELPER='exec systemd-ask-password --id="tzpfms:$$2" "$$1:"'` and `TZPFMS_PASSPHRASE_HELPER_MAN='Ic exec Nm systemd-ask-password Fl -id Ns Li = Ns Qo Li tzpfms:\& Ns Ar $$2 Qc Qo Ar $$1 Ns Li ":\&" Qc'`.
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
Copy the `out/zfs-tpm*` binaries corresponding to the back-ends you want to `/sbin`,
|
Copy the `out/zfs-tpm*` binaries corresponding to the back-ends you want to `/sbin`,
|
||||||
continue as the [manual](//git.sr.ht/~nabijaczleweli/tzpfms/tree/man/zfs-tpm2-change-key.md) [page](//git.sr.ht/~nabijaczleweli/tzpfms/tree/man/zfs-tpm1x-change-key.md) instructs.
|
continue as the [manual](//ra.ws.co.ls/~nabijaczleweli/tzpfms/blob/man/zfs-tpm2-change-key.8.html) [page](//ra.ws.co.ls/~nabijaczleweli/tzpfms/blob/man/zfs-tpm1x-change-key.8.html) instructs.
|
||||||
|
|
||||||
For initrd support, copy the content of either `out/dracut/` or `out/initramfs-tools/` over `/`;
|
For initrd support, copy the content of either `out/dracut/` or `out/initramfs-tools/` over `/`;
|
||||||
these need `zfs-tpm-list` but will work with any combination of back-end `*-load-key` binaries.
|
these need `zfs-tpm-list` but will work with any combination of back-end `*-load-key` binaries
|
||||||
|
(local TPM1.X initrds need to be updated when the system state changes (e.g. the TPM is taken ownership of)).
|
||||||
|
|
||||||
<!-- #### From Debian repository
|
To integrate with [zfs-mount-generator(8)](//manpages.debian.org/bookworm/zfsutils-linux/zfs-mount-generator.8.html)
|
||||||
|
[copy](//lfs.nabijaczleweli.xyz/0017-twitter-export#1472986504272261124) `out/systemd/` over `/`.
|
||||||
|
|
||||||
|
#### From Debian repository
|
||||||
|
|
||||||
The following line in `/etc/apt/sources.list` or equivalent:
|
The following line in `/etc/apt/sources.list` or equivalent:
|
||||||
```apt
|
```apt
|
||||||
deb https://debian.nabijaczleweli.xyz sid main
|
deb [signed-by=/etc/apt/keyrings/nabijaczleweli.asc] https://debian.nabijaczleweli.xyz sid main
|
||||||
```
|
```
|
||||||
|
|
||||||
With [my PGP key](//nabijaczleweli.xyz/pgp.txt) (the two URLs are interchangeable):
|
With [my PGP key](//nabijaczleweli.xyz/pgp.txt) (the two URLs are interchangeable):
|
||||||
```sh
|
```sh
|
||||||
wget -O- https://debian.nabijaczleweli.xyz/nabijaczleweli.gpg.key | sudo apt-key add
|
sudo wget -O/etc/apt/keyrings/nabijaczleweli.asc https://debian.nabijaczleweli.xyz/nabijaczleweli.gpg.key
|
||||||
# or
|
sudo wget -O/etc/apt/keyrings/nabijaczleweli.asc https://nabijaczleweli.xyz/pgp.txt
|
||||||
sudo wget -O/etc/apt/trusted.gpg.d/nabijaczleweli.asc //keybase.io/nabijaczleweli/pgp_keys.asc
|
|
||||||
```
|
```
|
||||||
|
(you may need to create /etc/apt/keyrings on apt <2.4.0 (<=bullseye) manually).
|
||||||
|
|
||||||
Then the usual
|
Then the usual
|
||||||
```sh
|
```sh
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install tzpfms
|
sudo apt install tzpfms-tpm2 tzpfms-dracut
|
||||||
```
|
```
|
||||||
will work on amd64, x32, and i386.
|
will work on amd64, x32, and i386.
|
||||||
|
|
||||||
See the [repository README](//debian.nabijaczleweli.xyz/README) for more information. -->
|
See the [repository README](//debian.nabijaczleweli.xyz/README) for more information.
|
||||||
|
|
||||||
|
#### From tar-ball
|
||||||
|
|
||||||
|
Release tarballs are signed with <nabijaczleweli@nabijaczleweli.xyz> (pull with WKD, but `7D69 474E 8402 8C5C C0C4 4163 BCFD 0B01 8D26 58F1`).
|
||||||
|
аnd stored in git notes as-if via [the example program](//man.sr.ht/git.sr.ht/#signing-tagsx27-tarballs)
|
||||||
|
and are thus available on the [refs listing](https://git.sr.ht/~nabijaczleweli/tzpfms/refs)/tag page as .tar.gz.asc.
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
#### TPM2
|
#### TPM2
|
||||||
|
|
||||||
Build [`swtpm`](//github.com/stefanberger/swtpm), then prepare and run it:
|
Install [`swtpm`](//github.com/stefanberger/swtpm), then prepare and run it:
|
||||||
```sh
|
```sh
|
||||||
swtpm_setup --tpmstate tpm2-state --tpm2 --createek --display --logfile /dev/stdout --overwrite
|
swtpm_setup --tpmstate tpm2-state --tpm2 --createek --display --logfile /dev/tty --overwrite
|
||||||
swtpm socket --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --tpm2 --tpmstate dir=tpm2-state --flags not-need-init --log level=10
|
swtpm socket --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --tpm2 --tpmstate dir=tpm2-state --flags not-need-init --log level=10
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -72,10 +94,10 @@ ln -s /usr/lib/i386-linux-gnu/libtss2-tcti-{swtpm,default}.so
|
|||||||
```
|
```
|
||||||
#### TPM1.x
|
#### TPM1.x
|
||||||
|
|
||||||
Build [`swtpm`](//github.com/stefanberger/swtpm), then prepare and run it and
|
Install [`swtpm`](//github.com/stefanberger/swtpm), then prepare and run it and
|
||||||
([hopefully](https://github.com/stefanberger/swtpm/issues/5#issuecomment-210607890)) [TrouSerS](//sourceforge.net/projects/trousers), as `root`/`tpm`:
|
([hopefully](//github.com/stefanberger/swtpm/issues/5#issuecomment-210607890)) [TrouSerS](//sourceforge.net/projects/trousers), as `root`/`tpm`:
|
||||||
```sh
|
```sh
|
||||||
swtpm_setup --tpmstate tpm1x-state --createek --display --logfile /dev/stdout --overwrite
|
swtpm_setup --tpmstate tpm1x-state --createek --display --logfile /dev/tty --overwrite
|
||||||
swtpm cuse -n tpm --tpmstate dir=tpm1x-state --seccomp action=none --log level=10,file=/dev/fd/4 4>&1
|
swtpm cuse -n tpm --tpmstate dir=tpm1x-state --seccomp action=none --log level=10,file=/dev/fd/4 4>&1
|
||||||
swtpm_ioctl -i /dev/tpm
|
swtpm_ioctl -i /dev/tpm
|
||||||
TPM_DEVICE=/dev/tpm swtpm_bios
|
TPM_DEVICE=/dev/tpm swtpm_bios
|
||||||
@ -107,12 +129,25 @@ There's [the tracker](//todo.sr.ht/~nabijaczleweli/tzpfms), but also see the lis
|
|||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Send a patch inline, as an attachment, or a git link and a ref to pull from to
|
Send a patch inline, as an attachment, or a git link and a ref to pull from to
|
||||||
[the list](//lists.sr.ht/~nabijaczleweli/tzpfms) ([~nabijaczleweli/tzpfms@lists.sr.ht](mailto:~nabijaczleweli/tzpfms)) or [me](mailto:nabijaczleweli@nabijaczleweli.xyz)
|
[the list](//lists.sr.ht/~nabijaczleweli/tzpfms) ([~nabijaczleweli/tzpfms@lists.sr.ht](mailto:~nabijaczleweli/tzpfms@lists.sr.ht)) or [me](mailto:nabijaczleweli@nabijaczleweli.xyz)
|
||||||
directly. I'm not picky, just please include the repo name in the subject prefix.
|
directly. I'm not picky, just please include the repo name in the subject prefix.
|
||||||
|
|
||||||
|
### "Ecosystem"
|
||||||
|
|
||||||
|
Put a line containing the `zfs-{...}-load-key` program name followed by a list of required services (if any) into `/usr/share/tzpfms/${tzpfms.backend}`
|
||||||
|
and the `zfs-load-key@.service` drop-in will understand it and use it to load.
|
||||||
|
|
||||||
|
Known compatible backends:
|
||||||
|
|
||||||
|
| `/usr/share/tzpfms/` | data | from | for |
|
||||||
|
| -------------------- | ------------------------------------- | ---------------------------------------------- | -------------- |
|
||||||
|
| `TPM1.X` | `zfs-tpm1x-load-key trousers.service` | [tzpfms](//sr.ht/~nabijaczleweli/tzpfms) | TPM |
|
||||||
|
| `TPM2` | `zfs-tpm2-load-key` | [tzpfms](//sr.ht/~nabijaczleweli/tzpfms) | TPM |
|
||||||
|
| `FIDO2` | `zfs-fido2-load-key` | [fzifdso](//git.sr.ht/~nabijaczleweli/fzifdso) | FIDO2/WebAuthn |
|
||||||
|
|
||||||
## Discussion
|
## Discussion
|
||||||
|
|
||||||
Please use the tracker, the list, or [Twitter](//twitter.com/nabijaczleweli/status/1315137083380559873).
|
Please use the tracker, the list, or [mastussy](//101010.pl/@nabijaczleweli) (formerly [Twitter](//lfs.nabijaczleweli.xyz/0017-twitter-export#1315137083380559873)).
|
||||||
|
|
||||||
## Special thanks
|
## Special thanks
|
||||||
|
|
||||||
@ -120,3 +155,5 @@ To all who support further development on Patreon, in particular:
|
|||||||
|
|
||||||
* ThePhD
|
* ThePhD
|
||||||
* Embark Studios
|
* Embark Studios
|
||||||
|
* Jasper Bekkers
|
||||||
|
* EvModder
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
# The MIT License (MIT)
|
|
||||||
|
|
||||||
# Copyright (c) 2020 наб <nabijaczleweli@nabijaczleweli.xyz>
|
|
||||||
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
# this software and associated documentation files (the "Software"), to deal in
|
|
||||||
# the Software without restriction, including without limitation the rights to
|
|
||||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
# subject to the following conditions:
|
|
||||||
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
EXE :=
|
|
||||||
DLL := .so
|
|
||||||
PIC := -fPIC
|
|
||||||
OS_LD_LIBS :=
|
|
||||||
|
|
||||||
CXXVER := $(shell $(CXX) --version)
|
|
||||||
ifneq "$(findstring clang,$(CXXVER))" ""
|
|
||||||
# GCC doesn't have this granularity
|
|
||||||
CXXSPECIFIC := -flto=full -pedantic -Wno-gnu-statement-expression -Wno-gnu-include-next
|
|
||||||
else
|
|
||||||
CXXSPECIFIC := -flto
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
ifneq "$(ADDITIONAL_INCLUDE_DIR)" ""
|
|
||||||
INCCXXAR := -isystem$(ADDITIONAL_INCLUDE_DIR)
|
|
||||||
else
|
|
||||||
INCCXXAR :=
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq "$(ADDITIONAL_LINK_DIR)" ""
|
|
||||||
LNCXXAR := -L$(ADDITIONAL_LINK_DIR)
|
|
||||||
else
|
|
||||||
LNCXXAR :=
|
|
||||||
endif
|
|
||||||
|
|
||||||
TZPFMS_VERSION := "$(patsubst v%,%,$(shell git describe))"
|
|
||||||
|
|
||||||
INCCMAKEAR := CXXFLAGS="$(INCCXXAR)"
|
|
||||||
LNCMAKEAR := LDFLAGS="$(LNCXXAR)"
|
|
||||||
|
|
||||||
AWK ?= awk
|
|
||||||
RONN ?= ronn
|
|
||||||
SHELLCHECK ?= shellcheck
|
|
||||||
OBJ := .o
|
|
||||||
CXXAR := -O3 -std=c++17 -fno-exceptions -Wall -Wextra $(CXXSPECIFIC) -pipe $(INCCXXAR) $(PIC)
|
|
||||||
STRIP ?= strip
|
|
||||||
STRIPAR := --strip-all --remove-section=.comment --remove-section=.note
|
|
||||||
|
|
||||||
OUTDIR := out/
|
|
||||||
BLDDIR := out/build/
|
|
||||||
OBJDIR := $(BLDDIR)obj/
|
|
||||||
SRCDIR := src/
|
|
||||||
TSTDIR := test/
|
|
||||||
MANDIR := man/
|
|
||||||
INITRDDIR := initrd/
|
|
2
contrib/README
Normal file
2
contrib/README
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
These are development aids, not for distribution.
|
||||||
|
Link them to src/bin/ to build.
|
59
contrib/zfs-tpm1x-muddle-pcrs.cpp
Normal file
59
contrib/zfs-tpm1x-muddle-pcrs.cpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "../main.hpp"
|
||||||
|
#include "../tpm1x.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
#define THIS_BACKEND "TPM1.X"
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char ** argv) {
|
||||||
|
uint32_t * pcrs{};
|
||||||
|
size_t pcrs_len{};
|
||||||
|
bool just_read{};
|
||||||
|
return do_main(
|
||||||
|
argc, argv, "RP:", "[-R] [-P PCR[,PCR]...]",
|
||||||
|
[&](auto o) {
|
||||||
|
switch(o) {
|
||||||
|
case 'R':
|
||||||
|
return just_read = true, 0;
|
||||||
|
case 'P':
|
||||||
|
return tpm1x_parse_pcrs(optarg, pcrs, pcrs_len);
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&](auto) {
|
||||||
|
return with_tpm1x_session([&](auto ctx, auto, auto) {
|
||||||
|
TSS_HTPM tpm_h{};
|
||||||
|
TRY_TPM1X("extract TPM from context", Tspi_Context_GetTpmObject(ctx, &tpm_h));
|
||||||
|
|
||||||
|
|
||||||
|
for(size_t i = 0; i < pcrs_len; i++) {
|
||||||
|
char buf[512];
|
||||||
|
snprintf(buf, sizeof(buf), "muddle PCR %" PRIu32 "", pcrs[i]);
|
||||||
|
|
||||||
|
BYTE * val{};
|
||||||
|
uint32_t val_len{};
|
||||||
|
|
||||||
|
if(just_read)
|
||||||
|
TRY_TPM1X(buf, Tspi_TPM_PcrRead(tpm_h, pcrs[i], &val_len, &val));
|
||||||
|
else {
|
||||||
|
BYTE data[TPM_SHA1_160_HASH_LEN];
|
||||||
|
getentropy(data, sizeof(data));
|
||||||
|
TRY_TPM1X(buf, Tspi_TPM_PcrExtend(tpm_h, pcrs[i], sizeof(data), data, nullptr, &val_len, &val));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
printf("PCR%u: ", pcrs[i]);
|
||||||
|
for(auto i = 0u; i < val_len; ++i)
|
||||||
|
printf("%02hhX", ((uint8_t *)val)[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
30
init.d/systemd/libexec-tzpfms-zfs-load-key@.sh
Executable file
30
init.d/systemd/libexec-tzpfms-zfs-load-key@.sh
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
DSET="$1"
|
||||||
|
exec 2>>/dev/kmsg
|
||||||
|
|
||||||
|
zfs-tpm-list -H "$DSET" | while read -r _ backend keystatus coherent; do
|
||||||
|
[ "$keystatus" = 'available' ] && exit
|
||||||
|
|
||||||
|
[ "$coherent" = 'yes' ] || {
|
||||||
|
printf "%s\n" "${0##*/}[$$]: $DSET: incoherent tzpfms back-end $backend." "You might need to restore from back-up!" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock=; deps=
|
||||||
|
read -r unlock deps 2>/dev/null < "/usr/share/tzpfms/$backend"
|
||||||
|
command -v "$unlock" >/dev/null || {
|
||||||
|
printf "%s\n" "${0##*/}[$$]: $DSET: unknown tzpfms back-end $backend." >&2
|
||||||
|
exit # fall through, maybe there's another handler
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=2086
|
||||||
|
[ -n "$deps" ] && systemctl start $deps
|
||||||
|
|
||||||
|
# shellcheck disable=2016
|
||||||
|
[ -z "$TZPFMS_PASSPHRASE_HELPER" ] && export TZPFMS_PASSPHRASE_HELPER='exec systemd-ask-password --id="tzpfms:$2" "$1:"'
|
||||||
|
exec "$unlock" "$DSET"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Dataset doesn't exist, fall through
|
1
init.d/systemd/usr-share-tzpfms/TPM1.X
Normal file
1
init.d/systemd/usr-share-tzpfms/TPM1.X
Normal file
@ -0,0 +1 @@
|
|||||||
|
zfs-tpm1x-load-key trousers.service
|
1
init.d/systemd/usr-share-tzpfms/TPM2
Normal file
1
init.d/systemd/usr-share-tzpfms/TPM2
Normal file
@ -0,0 +1 @@
|
|||||||
|
zfs-tpm2-load-key
|
4
init.d/systemd/zfs-load-key@.service.d-tzpfms.conf
Normal file
4
init.d/systemd/zfs-load-key@.service.d-tzpfms.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStartPre=/usr/libexec/tzpfms-zfs-load-key@ %I
|
@ -1,50 +1,57 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
|
||||||
|
|
||||||
#include "../install.h"
|
#include "../install.h"
|
||||||
|
|
||||||
|
|
||||||
_get_backend() {
|
_get_backend() {
|
||||||
rootfs="$(awk '$2 == "/" && $3 == "zfs" {print $1; exit 1}' /etc/mtab)"
|
OIFS="$IFS"
|
||||||
[ -z "$rootfs" ] && return 1
|
IFS='
|
||||||
|
'
|
||||||
|
rootfses="$(awk '$2 ~ "^(/|/etc|/bin|/lib|/lib??|/libx32|/usr)$" && $3 == "zfs" {print $1}' /etc/mtab)"
|
||||||
|
[ -z "$rootfses" ] && IFS="$OIFS" && return 1
|
||||||
|
|
||||||
eroot="$(zfs get encryptionroot -Ho value "$rootfs")"
|
eroots="$(zfs get encryptionroot -Ho value $rootfses | sort -u | grep -vFxe '' -e '-')"
|
||||||
[ -z "$eroot" ] || [ "$eroot" = "-" ] && return 1
|
[ -z "$eroots" ] && IFS="$OIFS" && return 1
|
||||||
|
|
||||||
|
backends="$(zfs-tpm-list -H $eroots | cut -f2 | sort -u)"
|
||||||
|
[ -z "$backends" ] && IFS="$OIFS" && return 1
|
||||||
|
|
||||||
backend="$(zfs-tpm-list -H "$eroot" | awk -F'\t' '{print $2}')"
|
IFS="$OIFS"
|
||||||
[ -n "$backend" ]
|
return 0
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_install_tpm2() {
|
_install_tpm2() {
|
||||||
inst_binary zfs-tpm2-load-key
|
inst_binary zfs-tpm2-load-key
|
||||||
# shellcheck disable=SC2046
|
inst_libdir_file 'libtss2-tcti*.so*'
|
||||||
inst_library $(find /usr/lib -name 'libtss2-tcti*.so*') # TODO: there's got to be a better way™!
|
command -v tpm2_dictionarylockout > /dev/null && inst_binary tpm2_dictionarylockout
|
||||||
}
|
}
|
||||||
|
|
||||||
_install_tpm1x() {
|
_install_tpm1x() {
|
||||||
inst_binary zfs-tpm1x-load-key
|
inst_binary zfs-tpm1x-load-key
|
||||||
INSTALL_TPM1X{inst_binary tcsd; inst_binary ip; inst_binary ss, initdir, inst_simple, inst_simple, inst_simple, inst_library}
|
INSTALL_TPM1X{inst_binary tcsd; inst_binary ip; inst_binary ss, initdir, inst_simple, inst_simple, inst_simple, inst_libdir_file}
|
||||||
command -v tpm_resetdalock > /dev/null && inst_binary tpm_resetdalock
|
command -v tpm_resetdalock > /dev/null && inst_binary tpm_resetdalock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
check() {
|
check() {
|
||||||
command -v zfs-tpm-list > /dev/null || return 1
|
require_binaries zfs-tpm-list || return
|
||||||
|
|
||||||
# shellcheck disable=SC2154
|
# shellcheck disable=SC2154
|
||||||
if [ -n "$hostonly" ]; then
|
if [ -n "$hostonly" ]; then
|
||||||
_get_backend || return
|
_get_backend || return
|
||||||
|
|
||||||
[ "$backend" = "TPM2" ] && command -v zfs-tpm2-load-key > /dev/null && return 0
|
for backend in $backends; do
|
||||||
[ "$backend" = "TPM1.X" ] && command -v zfs-tpm1x-load-key > /dev/null && return 0
|
[ "$backend" = "TPM2" ] && command -v zfs-tpm2-load-key > /dev/null && return 0
|
||||||
|
[ "$backend" = "TPM1.X" ] && command -v zfs-tpm1x-load-key > /dev/null && return 0
|
||||||
|
done
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -61,11 +68,13 @@ installkernel() {
|
|||||||
install() {
|
install() {
|
||||||
inst_binary zfs-tpm-list
|
inst_binary zfs-tpm-list
|
||||||
|
|
||||||
if [ -n "${hostonly}" ]; then
|
if [ -n "$hostonly" ]; then
|
||||||
_get_backend
|
_get_backend
|
||||||
|
|
||||||
[ "$backend" = "TPM2" ] && _install_tpm2
|
for backend in $backends; do
|
||||||
[ "$backend" = "TPM1.X" ] && _install_tpm1x
|
[ "$backend" = "TPM2" ] && _install_tpm2
|
||||||
|
[ "$backend" = "TPM1.X" ] && _install_tpm1x
|
||||||
|
done
|
||||||
else
|
else
|
||||||
command -v zfs-tpm2-load-key > /dev/null && _install_tpm2
|
command -v zfs-tpm2-load-key > /dev/null && _install_tpm2
|
||||||
command -v zfs-tpm1x-load-key > /dev/null && _install_tpm1x
|
command -v zfs-tpm1x-load-key > /dev/null && _install_tpm1x
|
||||||
|
@ -3,62 +3,53 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "../mount.h"
|
#include "../mount.h"
|
||||||
|
WITH_PROMPTABLE_TTY{< /dev/console > /dev/console 2>&1}
|
||||||
|
#include "../zfs-lib.sh.h"
|
||||||
|
|
||||||
|
|
||||||
# Only run on systemd systems, mimicking zfs-dracut's zfs-load-key.sh; TODO: "see mount-zfs.sh for non-systemd systems", confer README
|
# Only run on systemd systems, mimicking zfs-dracut's zfs-load-key.sh; TODO: "see mount-zfs.sh for non-systemd systems", confer README
|
||||||
[ -d /run/systemd ] || exit 0
|
[ -d /run/systemd ] || return 0
|
||||||
|
|
||||||
|
|
||||||
. "/lib/dracut-lib.sh"
|
. "/lib/dracut-lib.sh"
|
||||||
|
decode_root_args || return 0
|
||||||
|
|
||||||
|
|
||||||
# If root is not "ZFS=" or "zfs:", or rootfstype is not "zfs" then we aren't supposed to handle it
|
# There is a race between the zpool import and the pre-mount hooks, so we wait for a pool to be imported
|
||||||
root="${root:=$(getarg root=)}"
|
while ! systemctl is-active --quiet zfs-import.target; do
|
||||||
rootfstype="${rootfstype:=$(getarg rootfstype=)}"
|
systemctl is-failed --quiet zfs-import-cache.service zfs-import-scan.service && return 1
|
||||||
[ "${root##zfs:}" = "$root" ] && [ "${root##ZFS=}" = "$root" ] && [ "$rootfstype" != "zfs" ] && exit 0
|
sleep 0.1s
|
||||||
|
done
|
||||||
|
|
||||||
|
BOOTFS="$root"
|
||||||
|
if [ "$BOOTFS" = "zfs:AUTO" ]; then
|
||||||
|
BOOTFS="$(zpool get -Ho value bootfs | grep -m1 -vFx -)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "$(zpool get -Ho value feature@encryption "${BOOTFS%%/*}")" = 'active' ] || return 0
|
||||||
|
|
||||||
TZPFMS_TPM1X="$(getarg TZPFMS_TPM1X=)"
|
|
||||||
[ -z "$TZPFMS_TPM1X" ] || export TZPFMS_TPM1X
|
|
||||||
|
|
||||||
getarg 0 quiet && quiet=y
|
getarg 0 quiet && quiet=y
|
||||||
|
|
||||||
|
|
||||||
# There is a race between the zpool import and the pre-mount hooks, so we wait for a pool to be imported
|
tzpfms_load() {
|
||||||
while [ "$(zpool list -H)" = "" ]; do
|
set -- "$(zfs get -Ho value encryptionroot "$1")"
|
||||||
sleep 0.1s
|
[ "$1" = "-" ] && return 0
|
||||||
systemctl is-failed --quiet zfs-import-cache.service zfs-import-scan.service && exit 1
|
|
||||||
done
|
|
||||||
|
|
||||||
|
# Match this sexion to i-t/zfs-patch.sh
|
||||||
if [ "$root" = "zfs:AUTO" ] ; then
|
if command -v zfs-tpm2-load-key > /dev/null && [ -n "$(zfs-tpm-list -Hub TPM2 "$1")" ]; then
|
||||||
BOOTFS="$(zpool list -H -o bootfs | awk '!/^-$/ {print; exit}')"
|
with_promptable_tty zfs-tpm2-load-key "$1"
|
||||||
else
|
return
|
||||||
BOOTFS="${root##zfs:}"
|
|
||||||
BOOTFS="${BOOTFS##ZFS=}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
WITH_PROMPTABLE_TTY{< /dev/console > /dev/console 2>&1}
|
|
||||||
|
|
||||||
|
|
||||||
# If pool encryption is active and the zfs command understands '-o encryption'
|
|
||||||
if [ "$(zpool list -H -o feature@encryption "$(echo "$BOOTFS" | awk -F/ '{print $1}')")" = "active" ]; then
|
|
||||||
ENCRYPTIONROOT="$(zfs get -H -o value encryptionroot "$BOOTFS")"
|
|
||||||
|
|
||||||
if ! [ "${ENCRYPTIONROOT}" = "-" ]; then
|
|
||||||
# Match this sexion to i-t/zfs-patch.sh
|
|
||||||
if command -v zfs-tpm2-load-key > /dev/null && ! [ "$(zfs-tpm-list -Hub TPM2 "$ENCRYPTIONROOT")" = "" ]; then
|
|
||||||
with_promptable_tty zfs-tpm2-load-key "$ENCRYPTIONROOT"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v zfs-tpm1x-load-key > /dev/null && ! [ "$(zfs-tpm-list -Hub TPM1.X "$ENCRYPTIONROOT")" = "" ]; then
|
|
||||||
POTENTIALLY_START_TCSD{ss -ltO, > /dev/console 2>&1}
|
|
||||||
with_promptable_tty zfs-tpm1x-load-key "$ENCRYPTIONROOT"; err="$?"
|
|
||||||
POTENTIALLY_KILL_TCSD{}
|
|
||||||
exit "$err"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fall through to zfs-dracut's zfs-load-key.sh
|
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
if command -v zfs-tpm1x-load-key > /dev/null && [ -n "$(zfs-tpm-list -Hub TPM1.X "$1")" ]; then
|
||||||
|
POTENTIALLY_START_TCSD{ss -ltO, > /dev/console 2>&1}
|
||||||
|
with_promptable_tty zfs-tpm1x-load-key "$1"; err="$?"
|
||||||
|
POTENTIALLY_KILL_TCSD{}
|
||||||
|
return "$err"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tzpfms_load "$BOOTFS"
|
||||||
|
for_relevant_root_children "$BOOTFS" tzpfms_load
|
||||||
|
@ -5,8 +5,7 @@
|
|||||||
#include "../install.h"
|
#include "../install.h"
|
||||||
|
|
||||||
|
|
||||||
PREREQ="zfs"
|
[ "$1" = "prereqs" ] && { echo zfs; exit; }
|
||||||
[ "$1" = "prereqs" ] && exec echo "$PREREQ"
|
|
||||||
. /usr/share/initramfs-tools/hook-functions
|
. /usr/share/initramfs-tools/hook-functions
|
||||||
|
|
||||||
|
|
||||||
@ -14,12 +13,11 @@ PREREQ="zfs"
|
|||||||
# which should be compatible with other hooks doing the same thing
|
# which should be compatible with other hooks doing the same thing
|
||||||
[ "${verbose:-n}" = "y" ] && echo "Patching /scripts/zfs"
|
[ "${verbose:-n}" = "y" ] && echo "Patching /scripts/zfs"
|
||||||
sed -Ei 's/^decrypt_fs\(\)/__tzpfms__&/' "$DESTDIR/scripts/zfs"
|
sed -Ei 's/^decrypt_fs\(\)/__tzpfms__&/' "$DESTDIR/scripts/zfs"
|
||||||
cat /usr/share/tzpfms/initramfs-tools-zfs-patch.sh >> "$DESTDIR/scripts/zfs"
|
cat /usr/share/tzpfms/initramfs-tools-zfs-patch.sh >> "$DESTDIR/scripts/zfs"
|
||||||
|
|
||||||
|
|
||||||
for x in zfs-tpm-list zfs-tpm2-load-key zfs-tpm1x-load-key tpm_resetdalock tcsd $(find /usr/lib -name 'libtss2-tcti*.so*'); do # TODO: there's got to be a better way™!
|
for x in zfs-tpm-list zfs-tpm2-load-key tpm2_dictionarylockout zfs-tpm1x-load-key tpm_resetdalock tcsd $(find /usr/lib -name 'libtss2-tcti*.so*'); do # TODO: there's got to be a better way™!
|
||||||
xloc="$(command -v "$x")"
|
xloc="$(command -v "$x")" && copy_exec "$xloc"
|
||||||
[ "$xloc" = "" ] || copy_exec "$xloc"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
INSTALL_TPM1X{, DESTDIR, copy_file rule, copy_file config, copy_file state, copy_exec}
|
INSTALL_TPM1X{, DESTDIR, copy_file rule, copy_file config, copy_file state, copy_exec}
|
||||||
|
@ -17,17 +17,17 @@ decrypt_fs() {
|
|||||||
|
|
||||||
# First three lines borrowed from /scripts/zfs#decrypt_fs()
|
# First three lines borrowed from /scripts/zfs#decrypt_fs()
|
||||||
# If pool encryption is active and the zfs command understands '-o encryption'
|
# If pool encryption is active and the zfs command understands '-o encryption'
|
||||||
if [ "$(zpool list -H -o feature@encryption "$(echo "$fs" | awk -F/ '{print $1}')")" = "active" ]; then
|
if [ "$(zpool list -H -o feature@encryption "${fs%%/*}")" = "active" ]; then
|
||||||
ENCRYPTIONROOT="$(get_fs_value "$fs" encryptionroot)"
|
ENCRYPTIONROOT="$(get_fs_value "$fs" encryptionroot)"
|
||||||
|
|
||||||
if ! [ "$ENCRYPTIONROOT" = "-" ]; then
|
if ! [ "$ENCRYPTIONROOT" = "-" ]; then
|
||||||
# Match this sexion to dracut/tzpfms-load-key.sh
|
# Match this sexion to dracut/tzpfms-load-key.sh
|
||||||
if command -v zfs-tpm2-load-key > /dev/null && ! [ "$(zfs-tpm-list -Hub TPM2 "$ENCRYPTIONROOT")" = "" ]; then
|
if command -v zfs-tpm2-load-key > /dev/null && [ -n "$(zfs-tpm-list -Hub TPM2 "$ENCRYPTIONROOT")" ]; then
|
||||||
with_promptable_tty zfs-tpm2-load-key "$ENCRYPTIONROOT"
|
with_promptable_tty zfs-tpm2-load-key "$ENCRYPTIONROOT"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v zfs-tpm1x-load-key > /dev/null && ! [ "$(zfs-tpm-list -Hub TPM1.X "$ENCRYPTIONROOT")" = "" ]; then
|
if command -v zfs-tpm1x-load-key > /dev/null && [ -n "$(zfs-tpm-list -Hub TPM1.X "$ENCRYPTIONROOT")" ]; then
|
||||||
POTENTIALLY_START_TCSD{netstat -lt, }
|
POTENTIALLY_START_TCSD{netstat -lt, }
|
||||||
with_promptable_tty zfs-tpm1x-load-key "$ENCRYPTIONROOT"; err="$?"
|
with_promptable_tty zfs-tpm1x-load-key "$ENCRYPTIONROOT"; err="$?"
|
||||||
POTENTIALLY_KILL_TCSD{}
|
POTENTIALLY_KILL_TCSD{}
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
PREREQ
|
PREREQ
|
||||||
|
|
||||||
# tcsd exits if it doesn't have tss group and user
|
# tcsd exits if it doesn't have tss group and user
|
||||||
grep tss /etc/group >> "${TARGET_DIR:-MUST_EXIST}/etc/group"
|
grep tss /etc/group >> "${TARGET_DIR:?must exist}/etc/group"
|
||||||
grep tss /etc/passwd >> "${TARGET_DIR:-MUST_EXIST}/etc/passwd"
|
grep tss /etc/passwd >> "${TARGET_DIR:?must exist}/etc/passwd"
|
||||||
|
|
||||||
for f in /lib/udev/rules.d/*tpm*; do
|
for f in /lib/udev/rules.d/*tpm*; do
|
||||||
INST_UDEV "$f"
|
INST_UDEV "$f"
|
||||||
@ -15,13 +15,13 @@
|
|||||||
|
|
||||||
if [ -e /etc/tcsd.conf ]; then
|
if [ -e /etc/tcsd.conf ]; then
|
||||||
INST_CFG /etc/tcsd.conf
|
INST_CFG /etc/tcsd.conf
|
||||||
chown tss:tss "${TARGET_DIR:-MUST_EXIST}/etc/tcsd.conf"
|
chown root:tss "${TARGET_DIR:?must exist}/etc/tcsd.conf"
|
||||||
system_ps_file="$(awk -F '[[:space:]]*=[[:space:]]*' '!/^[[:space:]]*#/ && !/^$/ && $1 ~ /system_ps_file$/ {gsub(/[[:space:]]*$/, "", $2); print $2}' /etc/tcsd.conf)"
|
system_ps_file="$(awk -F '[[:space:]]*=[[:space:]]*' '!/^[[:space:]]*#/ && !/^$/ && $1 ~ /system_ps_file$/ {gsub(/[[:space:]]*$/, "", $2); print $2}' /etc/tcsd.conf)"
|
||||||
system_ps_file="${system_ps_file:-/var/lib/tpm/system.data}"
|
system_ps_file="${system_ps_file:-/var/lib/tpm/system.data}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# tcsd can't find SRK if state not present, refuses to start if it doesn't have parent to tss dir
|
# tcsd can't find SRK if state not present, refuses to start if it doesn't have parent to tss dir
|
||||||
mkdir -p "${TARGET_DIR:-MUST_EXIST}/$(dirname "$(dirname "$system_ps_file")")"
|
mkdir -p "${TARGET_DIR:?must exist}/$(dirname "$(dirname "$system_ps_file")")"
|
||||||
[ -f "$system_ps_file" ] && INST_STATE "$system_ps_file"
|
[ -f "$system_ps_file" ] && INST_STATE "$system_ps_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
if [ -e /etc/nsswitch.conf ]; then
|
if [ -e /etc/nsswitch.conf ]; then
|
||||||
INST_CFG /etc/nsswitch.conf
|
INST_CFG /etc/nsswitch.conf
|
||||||
databases="$(awk '/^group|hosts/ {for(i = 2; i <= NF; ++i) print $i}' /etc/nsswitch.conf | sort | uniq)"
|
databases="$(awk '/^(group|hosts)/ {for(i = 2; i <= NF; ++i) if($i !~ /[^a-z0-9_-]/) db[$i]=0} END {for(d in db) print d}' /etc/nsswitch.conf)"
|
||||||
for db in $databases; do
|
for db in $databases; do
|
||||||
for f in /lib/*/"libnss_$db"*; do
|
for f in /lib/*/"libnss_$db"*; do
|
||||||
INST_LIB "$f"
|
INST_LIB "$f"
|
||||||
|
@ -4,28 +4,26 @@
|
|||||||
#define WITH_PROMPTABLE_TTY(REDIREXIONS)
|
#define WITH_PROMPTABLE_TTY(REDIREXIONS)
|
||||||
# This sucks a lot of ass, since we don't know the questions or the amount thereof beforehand
|
# This sucks a lot of ass, since we don't know the questions or the amount thereof beforehand
|
||||||
# (0-2 (owner hierarchy/ownership + sealed object, both optional) best-case and 0-6 worst-case (both entered wrong twice)).
|
# (0-2 (owner hierarchy/ownership + sealed object, both optional) best-case and 0-6 worst-case (both entered wrong twice)).
|
||||||
# Plymouth doesn't allow us to actually check what the splash status was, and ioctl(KDGETMODE) isn't reliable;
|
|
||||||
# ideally, we'd only clear the screen if we were making the switch, but not if the user was already switched to the log output.
|
|
||||||
# Instead, clear if there's a "quiet", leave alone otherwise, and always restore;
|
|
||||||
# cmdline option "plymouth.ignore-show-splash" can be used to disable splashes altogether, if desired.
|
|
||||||
with_promptable_tty() {
|
with_promptable_tty() {
|
||||||
if command -v plymouth > /dev/null && plymouth --ping; then
|
if plymouth --ping 2>/dev/null; then
|
||||||
plymouth hide-splash
|
# shellcheck disable=SC2016
|
||||||
# shellcheck disable=SC2217
|
TZPFMS_PASSPHRASE_HELPER='exec plymouth ask-for-password --prompt="$1: "' "$@" 2>/run/tzpfms-err; ret="$?"
|
||||||
[ "${quiet:-n}" = "y" ] && printf '\033c' REDIREXIONS
|
[ -s /run/tzpfms-err ] && plymouth display-message --text="$(cat /run/tzpfms-err)"
|
||||||
|
elif [ -e /run/systemd/system ] && command -v systemd-ask-password > /dev/null; then # --no-tty matches zfs and actually works
|
||||||
"$@" REDIREXIONS; ret="$?"
|
# shellcheck disable=SC2016
|
||||||
|
TZPFMS_PASSPHRASE_HELPER='exec systemd-ask-password --no-tty --id="tzpfms:$2" "$1:"' "$@" 2>/run/tzpfms-err; ret="$?"
|
||||||
plymouth show-splash
|
|
||||||
else
|
else
|
||||||
# Mimic /scripts/zfs#decrypt_fs(): setting "printk" temporarily to "7" will allow prompt even if kernel option "quiet"
|
# Mimic /scripts/zfs#decrypt_fs(): setting "printk" temporarily to "7" will allow prompt even if kernel option "quiet"
|
||||||
printk="$(awk '{print $1}' /proc/sys/kernel/printk)"
|
read -r printk _ < /proc/sys/kernel/printk
|
||||||
[ "$printk" = "7" ] || echo 7 > /proc/sys/kernel/printk
|
[ "$printk" = "7" ] || echo 7 > /proc/sys/kernel/printk
|
||||||
|
|
||||||
"$@" REDIREXIONS; ret="$?"
|
TZPFMS_PASSPHRASE_HELPER="${TZPFMS_PASSPHRASE_HELPER:-}" "$@" REDIREXIONS; ret="$?" # allow overriding in cmdline, but always set to raze default
|
||||||
|
|
||||||
[ "$printk" = "7" ] || echo "$printk" > /proc/sys/kernel/printk
|
[ "$printk" = "7" ] || echo "$printk" > /proc/sys/kernel/printk
|
||||||
fi
|
fi
|
||||||
|
[ -s /run/tzpfms-err ] && cat /run/tzpfms-err >&2
|
||||||
|
[ -s /run/tzpfms-err ] && [ "$ret" -ne 0 ] && sed 's;^;'"$1"': ;' /run/tzpfms-err >> /dev/kmsg
|
||||||
|
rm -f /run/tzpfms-err
|
||||||
return "$ret"
|
return "$ret"
|
||||||
}
|
}
|
||||||
#endefine
|
#endefine
|
||||||
|
85
initrd/zfs-lib.sh.h
Normal file
85
initrd/zfs-lib.sh.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# SPDX-License-Identifier: 0BSD
|
||||||
|
# dbda45160ffa43e5ecf0498a609230f1afee7b3f (zfs-2.2.99-270-gdbda45160)
|
||||||
|
|
||||||
|
|
||||||
|
# for_relevant_root_children DATASET EXEC
|
||||||
|
# Runs "EXEC dataset mountpoint" for all children of DATASET that are needed for system bringup
|
||||||
|
# Used by zfs-nonroot-necessities.service and friends, too!
|
||||||
|
for_relevant_root_children() {
|
||||||
|
dataset="${1}"
|
||||||
|
exec="${2}"
|
||||||
|
|
||||||
|
zfs list -t filesystem -Ho name,mountpoint,canmount -r "${dataset}" |
|
||||||
|
(
|
||||||
|
_ret=0
|
||||||
|
while IFS=" " read -r dataset mountpoint canmount; do
|
||||||
|
[ "$canmount" != "on" ] && continue
|
||||||
|
|
||||||
|
case "$mountpoint" in
|
||||||
|
/etc|/bin|/lib|/lib??|/libx32|/usr)
|
||||||
|
# If these aren't mounted we may not be able to get to the real init at all, or pollute the dataset holding the rootfs
|
||||||
|
"${exec}" "${dataset}" "${mountpoint}" || _ret=$?
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Up to the real init to remount everything else it might need
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
exit "${_ret}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse root=, rootfstype=, return them decoded and normalised to zfs:AUTO for auto, plain dset for explicit
|
||||||
|
#
|
||||||
|
# True if ZFS-on-root, false if we shouldn't
|
||||||
|
#
|
||||||
|
# Supported values:
|
||||||
|
# root=
|
||||||
|
# root=zfs
|
||||||
|
# root=zfs:
|
||||||
|
# root=zfs:AUTO
|
||||||
|
#
|
||||||
|
# root=ZFS=data/set
|
||||||
|
# root=zfs:data/set
|
||||||
|
# root=zfs:ZFS=data/set (as a side-effect; allowed but undocumented)
|
||||||
|
#
|
||||||
|
# rootfstype=zfs AND root=data/set <=> root=data/set
|
||||||
|
# rootfstype=zfs AND root= <=> root=zfs:AUTO
|
||||||
|
#
|
||||||
|
# '+'es in explicit dataset decoded to ' 's.
|
||||||
|
decode_root_args() {
|
||||||
|
if [ -n "$rootfstype" ]; then
|
||||||
|
[ "$rootfstype" = zfs ]
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
xroot=$(getarg root=)
|
||||||
|
rootfstype=$(getarg rootfstype=)
|
||||||
|
|
||||||
|
# shellcheck disable=SC2249
|
||||||
|
case "$xroot" in
|
||||||
|
""|zfs|zfs:|zfs:AUTO)
|
||||||
|
root=zfs:AUTO
|
||||||
|
rootfstype=zfs
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
ZFS=*|zfs:*)
|
||||||
|
root="${xroot#zfs:}"
|
||||||
|
root="${root#ZFS=}"
|
||||||
|
root=$(echo "$root" | tr '+' ' ')
|
||||||
|
rootfstype=zfs
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$rootfstype" = "zfs" ]; then
|
||||||
|
case "$xroot" in
|
||||||
|
"") root=zfs:AUTO ;;
|
||||||
|
*) root=$(echo "$xroot" | tr '+' ' ') ;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
@ -1,15 +1,31 @@
|
|||||||
## TPM1.X back-end configuration
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
### TPM selection
|
.Sh TPM1.X back-end configuration
|
||||||
|
.Ss TPM selection
|
||||||
The tzpfms suite connects to a local tcsd(8) process (at `localhost:30003`) by default.
|
The
|
||||||
Use the environment variable `TZPFMS_TPM1X` to specify a remote TCS hostname.
|
.Nm tzpfms
|
||||||
|
suite connects to a local
|
||||||
The TrouSerS tcsd(8) daemon will try `/dev/tpm0`, then `/udev/tpm0`, then `/dev/tpm`;
|
.Xr tcsd 8
|
||||||
|
process
|
||||||
|
.Pq at Pa localhost:30003
|
||||||
|
by default.
|
||||||
|
Use the environment variable
|
||||||
|
.Ev TZPFMS_TPM1X
|
||||||
|
to specify a remote TCS hostname.
|
||||||
|
.Pp
|
||||||
|
The TrouSerS
|
||||||
|
.Xr tcsd 8
|
||||||
|
daemon will try
|
||||||
|
.Pa /dev/tpm0 ,
|
||||||
|
then
|
||||||
|
.Pa /udev/tpm0 ,
|
||||||
|
then
|
||||||
|
.Pa /dev/tpm ;
|
||||||
by occupying one of the earlier ones with, for example, shell redirection, a later one can be selected.
|
by occupying one of the earlier ones with, for example, shell redirection, a later one can be selected.
|
||||||
|
.
|
||||||
### See also
|
.Ss See also
|
||||||
|
The TrouSerS project page at
|
||||||
The TrouSerS project page at <https://sourceforge.net/projects/trousers>.
|
.Lk https:/\&/sourceforge.net/projects/trousers .
|
||||||
|
.Pp
|
||||||
The TPM 1.2 main specification index at <<https://trustedcomputinggroup.org/resource/tpm-main-specification>>.
|
The TPM 1.2 main specification index at
|
||||||
|
.Lk https:/\&/trustedcomputinggroup.org/resource/tpm-main-specification .
|
||||||
|
@ -1,17 +1,36 @@
|
|||||||
## TPM2 back-end configuration
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
### Environment variables
|
.Sh TPM2 back-end configuration
|
||||||
|
.Ss Environment variables
|
||||||
* `TSS2_LOG`=:
|
.Bl -tag -compact -width ".Ev TSS2_LOG"
|
||||||
Any of: *NONE*, *ERROR*, *WARNING*, *INFO*, *DEBUG*, *TRACE*. Default: *WARNING*.
|
.It Ev TSS2_LOG
|
||||||
|
Any of:
|
||||||
### TPM selection
|
.Sy NONE , ERROR , WARNING , INFO , DEBUG , TRACE .
|
||||||
|
Default:
|
||||||
The library `libtss2-tcti-default.so` can be linked to any of the `libtss2-tcti-*.so` libraries to select the default,
|
.Sy WARNING .
|
||||||
otherwise `/dev/tpmrm0`, then `/dev/tpm0`, then `localhost:2321` will be tried, in order (see ESYS_CONTEXT(3)).
|
.El
|
||||||
|
.
|
||||||
### See also
|
.Ss TPM selection
|
||||||
|
The library
|
||||||
The tpm2-tss git repository at <https://github.com/tpm2-software/tpm2-tss> and the documentation at <https://tpm2-tss.readthedocs.io>.
|
.Nm libtss2-tcti-default.so
|
||||||
|
can be linked to any of the
|
||||||
The TPM 2.0 specifications, mainly at <<https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-1-Architecture-01.38.pdf>> and related pages.
|
.Pa libtss2-tcti-*.so
|
||||||
|
libraries to select the default, otherwise
|
||||||
|
.Pa /dev/tpmrm0 ,
|
||||||
|
then
|
||||||
|
.Pa /dev/tpm0 ,
|
||||||
|
then
|
||||||
|
.Pa localhost:2321
|
||||||
|
will be tried, in order
|
||||||
|
.Pq see Xr ESYS_CONTEXT 3 .
|
||||||
|
.
|
||||||
|
.Ss See also
|
||||||
|
The tpm2-tss git repository at
|
||||||
|
.Lk https:/\&/github.com/tpm2-software/tpm2-tss
|
||||||
|
and the documentation at
|
||||||
|
.Lk https:/\&/tpm2-tss.readthedocs.io .
|
||||||
|
.Pp
|
||||||
|
The TPM 2.0 specifications, mainly at
|
||||||
|
.Lk https:/\&/trustedcomputinggroup.org/resource/tpm-library-specification/ ,
|
||||||
|
.Lk https:/\&/trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-1-Architecture-01.38.pdf ,
|
||||||
|
and related pages.
|
||||||
|
35
man/common.h
35
man/common.h
@ -1,16 +1,21 @@
|
|||||||
## AUTHOR
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
Written by наб <<nabijaczleweli@nabijaczleweli.xyz>>
|
.Sh SPECIAL THANKS
|
||||||
|
|
||||||
## SPECIAL THANKS
|
|
||||||
|
|
||||||
To all who support further development, in particular:
|
To all who support further development, in particular:
|
||||||
|
.Bl -bullet -offset 4n -compact -width "@"
|
||||||
* ThePhD
|
.It
|
||||||
* Embark Studios
|
ThePhD
|
||||||
|
.It
|
||||||
## REPORTING BUGS
|
Embark Studios
|
||||||
|
.It
|
||||||
<<https://todo.sr.ht/~nabijaczleweli/tzpfms>>
|
Jasper Bekkers
|
||||||
|
.It
|
||||||
<<mailto:~nabijaczleweli/tzpfms@lists.sr.ht>>, archived at <<https://lists.sr.ht/~nabijaczleweli/tzpfms>>
|
EvModder
|
||||||
|
.El
|
||||||
|
.
|
||||||
|
.Sh REPORTING BUGS
|
||||||
|
.Lk https:/\&/todo.sr.ht/\(tinabijaczleweli/tzpfms
|
||||||
|
.Pp
|
||||||
|
.Mt \(tinabijaczleweli/tzpfms@lists.sr.ht ,
|
||||||
|
archived at
|
||||||
|
.Lk https:/\&/lists.sr.ht/\(tinabijaczleweli/tzpfms .
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
zfs-tpm2-change-key(8) zfs-tpm2-change-key.8.ronn
|
|
||||||
zfs-tpm2-load-key(8) zfs-tpm2-load-key.8.ronn
|
|
||||||
zfs-tpm2-clear-key(8) zfs-tpm2-clear-key.8.ronn
|
|
||||||
zfs-tpm1x-change-key(8) zfs-tpm1x-change-key.8.ronn
|
|
||||||
zfs-tpm1x-load-key(8) zfs-tpm1x-load-key.8.ronn
|
|
||||||
zfs-tpm1x-clear-key(8) zfs-tpm1x-clear-key.8.ronn
|
|
||||||
zfs-tpm-list(8) zfs-tpm-list.8.ronn
|
|
||||||
|
|
||||||
zfs(8) https://manpages.debian.org/bullseye/zfsutils-linux/zfs.8.en.html
|
|
||||||
tcsd(8) https://manpages.debian.org/bullseye/trousers/tcsd.8.en.html
|
|
||||||
tpm2_unseal(1) https://manpages.debian.org/bullseye/tpm2-tools/tpm2_unseal.1.en.html
|
|
||||||
|
|
||||||
ESYS_CONTEXT(3) https://www.mankier.com/3/ESYS_CONTEXT
|
|
36
man/passphrase.h
Normal file
36
man/passphrase.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
|
.Sh ENVIRONMENT VARIABLES
|
||||||
|
.Bl -tag -compact -width 4n
|
||||||
|
.It Ev TZPFMS_PASSPHRASE_HELPER
|
||||||
|
By default, passphrases are prompted for and read in on the standard output and input streams.
|
||||||
|
If
|
||||||
|
.Ev TZPFMS_PASSPHRASE_HELPER
|
||||||
|
is set and nonempty, it will be run via
|
||||||
|
.Pa /bin/ Ns Nm sh Fl c
|
||||||
|
to provide each passphrase, instead.
|
||||||
|
.Pp
|
||||||
|
The standard output stream of the helper is tied to an anonymous file and used in its entirety as the passphrase, except for a trailing new-line, if any.
|
||||||
|
The arguments are:
|
||||||
|
.Bl -tag -compact -offset 2n -width ".Li $1"
|
||||||
|
.It Li $1
|
||||||
|
Pre-formatted noun phrase with all the information below, for use as a prompt
|
||||||
|
.\" Passphrase for tarta-zoot
|
||||||
|
.\" New passphrase for tarta-zoot (again)
|
||||||
|
.It Li $2
|
||||||
|
Either the dataset name or the element of the TPM hierarchy being prompted for
|
||||||
|
.It Li $3
|
||||||
|
.Qq new
|
||||||
|
if this is for a new passphrase, otherwise blank
|
||||||
|
.It Li $4
|
||||||
|
.Qq again
|
||||||
|
if it's the second prompt for that passphrase, otherwise blank
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
If the helper doesn't exist
|
||||||
|
.Pq the shell exits with Sy 127 ,
|
||||||
|
a diagnostic is issued and the normal prompt is used as fall-back.
|
||||||
|
If it fails for any other reason, the prompting is aborted.
|
||||||
|
.
|
||||||
|
TZPFMS_PASSPHRASE_HELPER_MAN{}
|
||||||
|
.El
|
297
man/style.css
Normal file
297
man/style.css
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
/* $OpenBSD: mandoc.css,v 1.33 2019/06/02 16:50:46 schwarze Exp $ */
|
||||||
|
/*
|
||||||
|
* Standard style sheet for mandoc(1) -Thtml and man.cgi(8).
|
||||||
|
*
|
||||||
|
* Written by Ingo Schwarze <schwarze@openbsd.org>.
|
||||||
|
* I place this file into the public domain.
|
||||||
|
* Permission to use, copy, modify, and distribute it for any purpose
|
||||||
|
* with or without fee is hereby granted, without any conditions.
|
||||||
|
*/
|
||||||
|
/* Tooltips removed. */
|
||||||
|
|
||||||
|
/* Global defaults. */
|
||||||
|
|
||||||
|
html { max-width: 65em;
|
||||||
|
--bg: #FFFFFF;
|
||||||
|
--fg: #000000; }
|
||||||
|
body { background: var(--bg);
|
||||||
|
color: var(--fg);
|
||||||
|
font-family: Helvetica,Arial,sans-serif; }
|
||||||
|
h1 { font-size: 110%; }
|
||||||
|
table { margin-top: 0em;
|
||||||
|
margin-bottom: 0em;
|
||||||
|
border-collapse: collapse; }
|
||||||
|
/* Some browsers set border-color in a browser style for tbody,
|
||||||
|
* but not for table, resulting in inconsistent border styling. */
|
||||||
|
tbody { border-color: inherit; }
|
||||||
|
tr { border-color: inherit; }
|
||||||
|
td { vertical-align: top;
|
||||||
|
padding-left: 0.2em;
|
||||||
|
padding-right: 0.2em;
|
||||||
|
border-color: inherit; }
|
||||||
|
ul, ol, dl { margin-top: 0em;
|
||||||
|
margin-bottom: 0em; }
|
||||||
|
li, dt { margin-top: 1em; }
|
||||||
|
|
||||||
|
.permalink { border-bottom: thin dotted;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
text-decoration: inherit; }
|
||||||
|
* { clear: both }
|
||||||
|
|
||||||
|
/* Search form and search results. */
|
||||||
|
|
||||||
|
fieldset { border: thin solid silver;
|
||||||
|
border-radius: 1em;
|
||||||
|
text-align: center; }
|
||||||
|
input[name=expr] {
|
||||||
|
width: 25%; }
|
||||||
|
|
||||||
|
table.results { margin-top: 1em;
|
||||||
|
margin-left: 2em;
|
||||||
|
font-size: smaller; }
|
||||||
|
|
||||||
|
/* Header and footer lines. */
|
||||||
|
|
||||||
|
table.head { width: 100%;
|
||||||
|
border-bottom: 1px dotted #808080;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
font-size: smaller; }
|
||||||
|
td.head-vol { text-align: center; }
|
||||||
|
td.head-rtitle {
|
||||||
|
text-align: right; }
|
||||||
|
|
||||||
|
table.foot { width: 100%;
|
||||||
|
border-top: 1px dotted #808080;
|
||||||
|
margin-top: 1em;
|
||||||
|
font-size: smaller; }
|
||||||
|
td.foot-os { text-align: right; }
|
||||||
|
|
||||||
|
/* Sections and paragraphs. */
|
||||||
|
|
||||||
|
.manual-text {
|
||||||
|
margin-left: 3.8em; }
|
||||||
|
.Nd { }
|
||||||
|
section.Sh { }
|
||||||
|
h1.Sh { margin-top: 1.2em;
|
||||||
|
margin-bottom: 0.6em;
|
||||||
|
margin-left: -3.2em; }
|
||||||
|
section.Ss { }
|
||||||
|
h2.Ss { margin-top: 1.2em;
|
||||||
|
margin-bottom: 0.6em;
|
||||||
|
margin-left: -1.2em;
|
||||||
|
font-size: 105%; }
|
||||||
|
.Pp { margin: 0.6em 0em; }
|
||||||
|
.Sx { }
|
||||||
|
.Xr { }
|
||||||
|
|
||||||
|
/* Displays and lists. */
|
||||||
|
|
||||||
|
.Bd { }
|
||||||
|
.Bd-indent { margin-left: 3.8em; }
|
||||||
|
|
||||||
|
.Bl-bullet { list-style-type: disc;
|
||||||
|
padding-left: 1em; }
|
||||||
|
.Bl-bullet > li { }
|
||||||
|
.Bl-dash { list-style-type: none;
|
||||||
|
padding-left: 0em; }
|
||||||
|
.Bl-dash > li:before {
|
||||||
|
content: "\2014 "; }
|
||||||
|
.Bl-item { list-style-type: none;
|
||||||
|
padding-left: 0em; }
|
||||||
|
.Bl-item > li { }
|
||||||
|
.Bl-compact > li {
|
||||||
|
margin-top: 0em; }
|
||||||
|
|
||||||
|
.Bl-enum { padding-left: 2em; }
|
||||||
|
.Bl-enum > li { }
|
||||||
|
.Bl-compact > li {
|
||||||
|
margin-top: 0em; }
|
||||||
|
|
||||||
|
.Bl-diag { }
|
||||||
|
.Bl-diag > dt {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold; }
|
||||||
|
.Bl-diag > dd {
|
||||||
|
margin-left: 0em; }
|
||||||
|
.Bl-hang { }
|
||||||
|
.Bl-hang > dt { }
|
||||||
|
.Bl-hang > dd {
|
||||||
|
margin-left: 5.5em; }
|
||||||
|
.Bl-inset { }
|
||||||
|
.Bl-inset > dt { }
|
||||||
|
.Bl-inset > dd {
|
||||||
|
margin-left: 0em; }
|
||||||
|
.Bl-ohang { }
|
||||||
|
.Bl-ohang > dt { }
|
||||||
|
.Bl-ohang > dd {
|
||||||
|
margin-left: 0em; }
|
||||||
|
.Bl-tag { margin-top: 0.6em;
|
||||||
|
margin-left: 5.5em; }
|
||||||
|
.Bl-tag > dt {
|
||||||
|
float: left;
|
||||||
|
margin-top: 0em;
|
||||||
|
margin-left: -5.5em;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
vertical-align: top; }
|
||||||
|
.Bl-tag > dd {
|
||||||
|
clear: right;
|
||||||
|
column-count: 1; /* Force block formatting context. */
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 0em;
|
||||||
|
margin-left: 0em;
|
||||||
|
margin-bottom: 0.6em;
|
||||||
|
vertical-align: top; }
|
||||||
|
.Bl-compact { margin-top: 0em; }
|
||||||
|
.Bl-compact > dd {
|
||||||
|
margin-bottom: 0em; }
|
||||||
|
.Bl-compact > dt {
|
||||||
|
margin-top: 0em; }
|
||||||
|
|
||||||
|
.Bl-column { }
|
||||||
|
.Bl-column > tbody > tr { }
|
||||||
|
.Bl-column > tbody > tr > td {
|
||||||
|
margin-top: 1em; }
|
||||||
|
.Bl-compact > tbody > tr > td {
|
||||||
|
margin-top: 0em; }
|
||||||
|
|
||||||
|
.Rs { font-style: normal;
|
||||||
|
font-weight: normal; }
|
||||||
|
.RsA { }
|
||||||
|
.RsB { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
.RsC { }
|
||||||
|
.RsD { }
|
||||||
|
.RsI { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
.RsJ { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
.RsN { }
|
||||||
|
.RsO { }
|
||||||
|
.RsP { }
|
||||||
|
.RsQ { }
|
||||||
|
.RsR { }
|
||||||
|
.RsT { text-decoration: underline; }
|
||||||
|
.RsU { }
|
||||||
|
.RsV { }
|
||||||
|
|
||||||
|
.eqn { }
|
||||||
|
.tbl td { vertical-align: middle; }
|
||||||
|
|
||||||
|
.HP { margin-left: 3.8em;
|
||||||
|
text-indent: -3.8em; }
|
||||||
|
|
||||||
|
/* Semantic markup for command line utilities. */
|
||||||
|
|
||||||
|
table.Nm { }
|
||||||
|
code.Nm { font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: monospace; }
|
||||||
|
.Fl { font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: monospace; }
|
||||||
|
.Cm { font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: monospace; }
|
||||||
|
.Ar { font-style: italic;
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: monospace; }
|
||||||
|
.Op { display: inline; }
|
||||||
|
.Ic { font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: monospace; }
|
||||||
|
.Ev { font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: monospace; }
|
||||||
|
.Pa { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
|
||||||
|
/* Semantic markup for function libraries. */
|
||||||
|
|
||||||
|
.Lb { }
|
||||||
|
code.In { font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: inherit; }
|
||||||
|
a.In { }
|
||||||
|
.Fd { font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: inherit; }
|
||||||
|
.Ft { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
.Fn { font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: inherit; }
|
||||||
|
.Fa { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
.Vt { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
.Va { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
.Dv { font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: monospace; }
|
||||||
|
.Er { font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: monospace; }
|
||||||
|
|
||||||
|
/* Various semantic markup. */
|
||||||
|
|
||||||
|
.An { }
|
||||||
|
.Lk { }
|
||||||
|
.Mt { }
|
||||||
|
.Cd { font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: inherit; }
|
||||||
|
.Ad { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
.Ms { font-style: normal;
|
||||||
|
font-weight: bold; }
|
||||||
|
.St { }
|
||||||
|
.Ux { }
|
||||||
|
|
||||||
|
/* Physical markup. */
|
||||||
|
|
||||||
|
.Bf { display: inline; }
|
||||||
|
.No { font-style: normal;
|
||||||
|
font-weight: normal; }
|
||||||
|
.Em { font-style: italic;
|
||||||
|
font-weight: normal; }
|
||||||
|
.Sy { font-style: normal;
|
||||||
|
font-weight: bold; }
|
||||||
|
.Li { font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: monospace; }
|
||||||
|
|
||||||
|
/* Tooltip support. */
|
||||||
|
|
||||||
|
h1.Sh, h2.Ss { position: relative; }
|
||||||
|
.Li, .An, .Ar, .Cd, .Cm, .Dv, .Em, .Er, .Ev, .Fa, .Fd, .Fl, .Fn, .Ft,
|
||||||
|
.Ic, code.In, .Lb, .Lk, .Ms, .Mt, .Nd, code.Nm, .Pa, .Rs,
|
||||||
|
.St, .Sx, .Sy, .Va, .Vt, .Xr {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative; }
|
||||||
|
|
||||||
|
/* Overrides to avoid excessive margins on small devices. */
|
||||||
|
|
||||||
|
@media (max-width: 37.5em) {
|
||||||
|
.manual-text {
|
||||||
|
margin-left: 0.5em; }
|
||||||
|
h1.Sh, h2.Ss { margin-left: 0em; }
|
||||||
|
.Bd-indent { margin-left: 2em; }
|
||||||
|
.Bl-hang > dd {
|
||||||
|
margin-left: 2em; }
|
||||||
|
.Bl-tag { margin-left: 2em; }
|
||||||
|
.Bl-tag > dt {
|
||||||
|
margin-left: -2em; }
|
||||||
|
.HP { margin-left: 2em;
|
||||||
|
text-indent: -2em; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overrides for a dark color scheme for accessibility. */
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
html { --bg: #1E1F21;
|
||||||
|
--fg: #EEEFF1; }
|
||||||
|
:link { color: #BAD7FF; }
|
||||||
|
:visited { color: #F6BAFF; }
|
||||||
|
}
|
115
man/zfs-tpm-list.8.pp
Normal file
115
man/zfs-tpm-list.8.pp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
|
.Dd
|
||||||
|
.Dt ZFS-TPM-LIST 8
|
||||||
|
.Os
|
||||||
|
.
|
||||||
|
.Sh NAME
|
||||||
|
.Nm zfs-tpm-list
|
||||||
|
.Nd print dataset tzpfms metadata
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl H
|
||||||
|
.Op Fl r Ns \&| Ns Fl d Ar depth
|
||||||
|
.Op Fl a Ns \&| Ns Fl b Ar back-end
|
||||||
|
.Op Fl u Ns \&| Ns Fl l
|
||||||
|
.Oo Ar filesystem Ns \&| Ns Ar volume Oc Ns …
|
||||||
|
.
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
Lists the following properties on encryption roots:
|
||||||
|
.Bl -tag -compact -offset 4n -width ".Li keystatus"
|
||||||
|
.It Li name
|
||||||
|
.It Li back-end
|
||||||
|
the
|
||||||
|
.Nm tzpfms
|
||||||
|
back-end
|
||||||
|
.Pq e.g. Sy TPM2 No for Xr zfs-tpm2-change-key 8 or Sy TPM1.X No for Xr zfs-tpm1x-change-key 8 ,
|
||||||
|
or
|
||||||
|
.Qq Sy -
|
||||||
|
if none is configured
|
||||||
|
.It Li keystatus
|
||||||
|
.Sy available
|
||||||
|
or
|
||||||
|
.Sy unavailable
|
||||||
|
.It Li coherent
|
||||||
|
.Sy yes
|
||||||
|
if either both
|
||||||
|
.Li xyz.nabijaczleweli:tzpfms.backend
|
||||||
|
and
|
||||||
|
.Li xyz.nabijaczleweli:tzpfms.key
|
||||||
|
are present or missing,
|
||||||
|
.Sy no
|
||||||
|
otherwise
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Incoherent datasets require immediate operator attention, with either the appropriate
|
||||||
|
.Nm zfs-tpm*-clear-key
|
||||||
|
program or
|
||||||
|
.Nm zfs Cm change-key
|
||||||
|
and
|
||||||
|
.Nm zfs Cm inherit
|
||||||
|
\(em if the key becomes unloaded, they will require restoration from back-up.
|
||||||
|
However, this should never occur, unless something went horribly wrong with the dataset properties.
|
||||||
|
.Pp
|
||||||
|
If no datasets are specified, all matching encryption roots are listed \(em by default, those managed by
|
||||||
|
.Nm tzpfms .
|
||||||
|
.
|
||||||
|
.Sh OPTIONS
|
||||||
|
.Bl -tag -compact -width ".Fl b Ar back-end"
|
||||||
|
.It Fl H
|
||||||
|
Scripting mode \(em remove headers and separate fields by a single tab instead of columnating them with spaces.
|
||||||
|
.Pp
|
||||||
|
.It Fl r
|
||||||
|
Recurse into all descendants of specified datasets.
|
||||||
|
.It Fl d Ar depth
|
||||||
|
Recurse at most
|
||||||
|
.Ar depth
|
||||||
|
datasets deep.
|
||||||
|
Default:
|
||||||
|
.Sy 0 .
|
||||||
|
.Pp
|
||||||
|
.It Fl a
|
||||||
|
List all encryption roots, even ones not managed by
|
||||||
|
.Nm tzpfms .
|
||||||
|
.It Fl b Ar back-end
|
||||||
|
List only encryption roots with the specified
|
||||||
|
.Nm tzpfms
|
||||||
|
.Ar back-end .
|
||||||
|
.Pp
|
||||||
|
.It Fl u
|
||||||
|
List only encryption roots whose keys are unavailable.
|
||||||
|
.It Fl l
|
||||||
|
List only encryption roots whose keys are available.
|
||||||
|
.El
|
||||||
|
.
|
||||||
|
.Sh EXAMPLES
|
||||||
|
.Bd -literal -compact
|
||||||
|
.Li $ Nm
|
||||||
|
NAME BACK-END KEYSTATUS COHERENT
|
||||||
|
tarta-zoot TPM1.X available yes
|
||||||
|
tarta-zoot/home TPM2 unavailable yes
|
||||||
|
|
||||||
|
.Li $ Nm Fl ad0
|
||||||
|
NAME BACK-END KEYSTATUS COHERENT
|
||||||
|
filling - available yes
|
||||||
|
|
||||||
|
.Li $ Nm Fl b Sy TPM2
|
||||||
|
NAME BACK-END KEYSTATUS COHERENT
|
||||||
|
tarta-zoot/home TPM2 unavailable yes
|
||||||
|
|
||||||
|
.Li $ Nm Fl ra Ar tarta-zoot
|
||||||
|
NAME BACK-END KEYSTATUS COHERENT
|
||||||
|
tarta-zoot TPM1.X available yes
|
||||||
|
tarta-zoot/home TPM2 unavailable yes
|
||||||
|
tarta-zoot/bkp - available yes
|
||||||
|
tarta-zoot/vm - available yes
|
||||||
|
|
||||||
|
.Li $ Nm Fl al
|
||||||
|
NAME BACK-END KEYSTATUS COHERENT
|
||||||
|
filling - available yes
|
||||||
|
tarta-zoot TPM1.X available yes
|
||||||
|
tarta-zoot/bkp - available yes
|
||||||
|
tarta-zoot/vm - available yes
|
||||||
|
.Ed
|
||||||
|
.
|
||||||
|
#include "common.h"
|
@ -1,80 +0,0 @@
|
|||||||
zfs-tpm-list(8) -- print dataset tzpfms metadata
|
|
||||||
================================================
|
|
||||||
|
|
||||||
## SYNOPSIS
|
|
||||||
|
|
||||||
`zfs-tpm-list` [-H] [-r\|-d *depth*] [-a\|-b *back-end*] [-u\|-l] [*filesystem*\|*volume*]…
|
|
||||||
|
|
||||||
## DESCRIPTION
|
|
||||||
|
|
||||||
zfs-tpm-list(8) lists the following properties on encryption roots:
|
|
||||||
|
|
||||||
* `name`,
|
|
||||||
* `back-end`: the tzpfms back-end (e.g. "TPM2" for zfs-tpm2-change-key(8) or "TPM1.X" for zfs-tpm1x-change-key(8)),
|
|
||||||
or "-" if none is configured,
|
|
||||||
* `keystatus`: "available" or "unavailable",
|
|
||||||
* `coherent`: "yes" if either both `xyz.nabijaczleweli:tzpfms.backend` and `xyz.nabijaczleweli:tzpfms.key` are present or missing, "no" otherwise.
|
|
||||||
|
|
||||||
Incoherent datasets require immediate operator attention, with either the appropriate zfs-tpm\*-clear-key program or zfs(8) change-key and zfs(8) inherit —
|
|
||||||
if the key becomes unloaded, they will require restoration from back-up.
|
|
||||||
However, they should never occur, unless something went terribly wrong with the dataset properties.
|
|
||||||
|
|
||||||
If no datasets are specified, lists all matching encryption roots.
|
|
||||||
The default filter is to list all roots managed by tzpfms.
|
|
||||||
The `-a` and `-b` [OPTIONS]() can be used to either list all roots or only ones backed by a particular end, respectively.
|
|
||||||
|
|
||||||
## OPTIONS
|
|
||||||
|
|
||||||
* `-H`:
|
|
||||||
Used for scripting mode. Do not print headers and separate fields by a single tab instead of arbitrary white space.
|
|
||||||
|
|
||||||
* `-r`:
|
|
||||||
Recurse into all descendant datasets. Default if no datasets listed on the command-line.
|
|
||||||
* `-d` *depth*:
|
|
||||||
Recurse at most *depth* datasets deep. Defaults to zero if datasets were listed on the command-line.
|
|
||||||
|
|
||||||
* `-a`:
|
|
||||||
List all encryption roots, even ones not managed by tzpfms.
|
|
||||||
* `-b` *back-end*:
|
|
||||||
List only encryption roots with tzpfms back-end *back-end*.
|
|
||||||
|
|
||||||
* `-l`:
|
|
||||||
List only encryption roots whose keys are available.
|
|
||||||
* `-u`:
|
|
||||||
List only encryption roots whose keys are unavailable.
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
$ zfs-tpm-list
|
|
||||||
NAME BACK-END KEYSTATUS COHERENT
|
|
||||||
owo/venc TPM2 unavailable yes
|
|
||||||
owo/enc TPM1.X available yes
|
|
||||||
|
|
||||||
$ zfs-tpm-list -ad0
|
|
||||||
NAME BACK-END KEYSTATUS COHERENT
|
|
||||||
awa - available yes
|
|
||||||
|
|
||||||
$ zfs-tpm-list -b TPM2
|
|
||||||
NAME BACK-END KEYSTATUS COHERENT
|
|
||||||
owo/venc TPM2 unavailable yes
|
|
||||||
|
|
||||||
$ zfs-tpm-list -ra owo
|
|
||||||
NAME BACK-END KEYSTATUS COHERENT
|
|
||||||
owo/venc TPM2 unavailable yes
|
|
||||||
owo/vtnc - available yes
|
|
||||||
owo/v nc - available yes
|
|
||||||
owo/enc TPM1.X available yes
|
|
||||||
|
|
||||||
$ zfs-tpm-list -al
|
|
||||||
NAME BACK-END KEYSTATUS COHERENT
|
|
||||||
awa - available yes
|
|
||||||
owo/vtnc - available yes
|
|
||||||
owo/v nc - available yes
|
|
||||||
owo/enc TPM1.X available yes
|
|
||||||
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
## SEE ALSO
|
|
||||||
|
|
||||||
<<https://git.sr.ht/~nabijaczleweli/tzpfms>>
|
|
125
man/zfs-tpm1x-change-key.8.pp
Normal file
125
man/zfs-tpm1x-change-key.8.pp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
|
.Dd
|
||||||
|
.Dt ZFS-TPM1X-CHANGE-KEY 8
|
||||||
|
.Os
|
||||||
|
.
|
||||||
|
.Sh NAME
|
||||||
|
.Nm zfs-tpm1x-change-key
|
||||||
|
.Nd change ZFS dataset key to one stored on the TPM
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl b Ar backup-file
|
||||||
|
.Op Fl P Ar PCR Ns Oo Ns Cm \&, Ns Ar PCR Oc Ns …
|
||||||
|
.Ar dataset
|
||||||
|
.
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
To normalise the
|
||||||
|
.Ar dataset ,
|
||||||
|
.Nm
|
||||||
|
will open its encryption root in its stead.
|
||||||
|
.Nm
|
||||||
|
will
|
||||||
|
.Em never
|
||||||
|
create or destroy encryption roots; use
|
||||||
|
.Xr zfs-change-key 8
|
||||||
|
for that.
|
||||||
|
.Pp
|
||||||
|
First, a connection is made to the TPM, which
|
||||||
|
.Em must
|
||||||
|
be TPM-1.X-compatible.
|
||||||
|
.Pp
|
||||||
|
If
|
||||||
|
.Ar dataset
|
||||||
|
was previously encrypted with
|
||||||
|
.Nm tzpfms
|
||||||
|
and the
|
||||||
|
.Sy TPM1.X
|
||||||
|
back-end was used, the metadata will be silently cleared.
|
||||||
|
Otherwise, or in case of an error, data required for manual intervention will be written to the standard error stream.
|
||||||
|
.Pp
|
||||||
|
Next, a new wrapping key is generated on the TPM, optionally backed up
|
||||||
|
.Pq see Sx OPTIONS ,
|
||||||
|
and sealed on the TPM;
|
||||||
|
the user is prompted for an optional passphrase to protect the key with,
|
||||||
|
and for the SRK passphrase, set when taking ownership, if not "well-known" (all zeroes).
|
||||||
|
.Pp
|
||||||
|
The following properties are set on
|
||||||
|
.Ar dataset :
|
||||||
|
.Bl -bullet -compact -offset 4n -width "@"
|
||||||
|
.It
|
||||||
|
.Li xyz.nabijaczleweli:tzpfms.backend Ns = Ns Sy TPM1.X
|
||||||
|
.It
|
||||||
|
.Li xyz.nabijaczleweli:tzpfms.key Ns = Ns Ar parent-key-blob Ns Cm \&: Ns Ar sealed-object-blob
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
.Li tzpfms.backend
|
||||||
|
identifies this dataset for work with
|
||||||
|
.Sy TPM1.X Ns -back-ended
|
||||||
|
.Nm tzpfms
|
||||||
|
programs
|
||||||
|
.Pq namely Xr zfs-tpm1x-change-key 8 , Xr zfs-tpm1x-load-key 8 , and Xr zfs-tpm1x-clear-key 8 .
|
||||||
|
.Pp
|
||||||
|
.Li tzpfms.key
|
||||||
|
is a colon-separated pair of hexadecimal-string (i.e. "4F7730" for "Ow0") blobs;
|
||||||
|
the first one represents the RSA key protecting the blob,
|
||||||
|
and it is protected with either the passphrase, if provided, or the SHA1 constant
|
||||||
|
.Li CE4CF677875B5EB8993591D5A9AF1ED24A3A8736 ;
|
||||||
|
the second represents the sealed object containing the wrapping key,
|
||||||
|
and is protected with the SHA1 constant
|
||||||
|
.Li B9EE715DBE4B243FAA81EA04306E063710383E35 .
|
||||||
|
There exists no other user-land tool for decrypting this; perhaps there should be.
|
||||||
|
.\"" TODO: make an LD_PRELOADable for extracting the key maybe?
|
||||||
|
.Pp
|
||||||
|
Finally, the equivalent of
|
||||||
|
.Nm zfs Cm change-key Fl o Li keylocation=prompt Fl o Li keyformat=raw Ar dataset
|
||||||
|
is performed with the new key.
|
||||||
|
If an error occurred, best effort is made to clean up the properties,
|
||||||
|
or to issue a note for manual intervention into the standard error stream.
|
||||||
|
.Pp
|
||||||
|
A final verification should be made by running
|
||||||
|
.Nm zfs-tpm1x-load-key Fl n Ar dataset .
|
||||||
|
If that command succeeds, all is well,
|
||||||
|
but otherwise the dataset can be manually rolled back to a passphrase with
|
||||||
|
.Nm zfs-tpm1x-clear-key Ar dataset
|
||||||
|
.Pq or, if that fails to work, Nm zfs Cm change-key Fl o Li keyformat=passphrase Ar dataset ,
|
||||||
|
and you are hereby asked to report a bug, please.
|
||||||
|
.Pp
|
||||||
|
.Nm zfs-tpm1x-clear-key Ar dataset
|
||||||
|
can be used to clear the properties and go back to using a passphrase.
|
||||||
|
.
|
||||||
|
.Sh OPTIONS
|
||||||
|
.Bl -tag -compact -width ".Fl b Ar backup-file"
|
||||||
|
.It Fl b Ar backup-file
|
||||||
|
Save a back-up of the key to
|
||||||
|
.Ar backup-file ,
|
||||||
|
which must not exist beforehand.
|
||||||
|
This back-up
|
||||||
|
.Em must
|
||||||
|
be stored securely, off-site.
|
||||||
|
In case of a catastrophic event, the key can be loaded by running
|
||||||
|
.Dl Nm zfs Cm load-key Ar dataset Li < Ar backup-file
|
||||||
|
.Pp
|
||||||
|
.
|
||||||
|
.It Fl P Ar PCR Ns Oo Ns Cm \&, Ns Ar PCR Oc Ns …
|
||||||
|
Bind the key to space- or comma-separated
|
||||||
|
.Ar PCR Ns s
|
||||||
|
\(em if they change, the wrapping key will not be able to be unsealed.
|
||||||
|
The minimum number of PCRs for a PC TPM is
|
||||||
|
.Sy 24 Pq numbered Bq Sy 0 , 23 .
|
||||||
|
For most, this is also the maximum.
|
||||||
|
.El
|
||||||
|
.
|
||||||
|
#include "passphrase.h"
|
||||||
|
.
|
||||||
|
#include "backend-tpm1x.h"
|
||||||
|
.
|
||||||
|
#include "common.h"
|
||||||
|
.
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.\" Match this to zfs-tpm2-change-key.8:
|
||||||
|
PCR allocations:
|
||||||
|
.Lk https:/\&/wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers
|
||||||
|
and
|
||||||
|
.Lk https:/\&/trustedcomputinggroup.org/wp-content/uploads/PC-ClientSpecific_Platform_Profile_for_TPM_2p0_Systems_v51.pdf ,
|
||||||
|
Section 2.3.4 "PCR Usage", Table 1.
|
@ -1,62 +0,0 @@
|
|||||||
zfs-tpm1x-change-key(8) -- change ZFS dataset key to one stored on the TPM
|
|
||||||
==========================================================================
|
|
||||||
|
|
||||||
## SYNOPSIS
|
|
||||||
|
|
||||||
`zfs-tpm1x-change-key` [-b file] <dataset>
|
|
||||||
|
|
||||||
## DESCRIPTION
|
|
||||||
|
|
||||||
To normalise `dataset`, zfs-tpm1x-change-key(8) will open its encryption root in its stead.
|
|
||||||
zfs-tpm1x-change-key(8) will *never* create or destroy encryption roots; use **zfs(8) change-key** for that.
|
|
||||||
|
|
||||||
First, a connection is made to the TPM, which *must* be TPM-1.X-compatible.
|
|
||||||
|
|
||||||
If `dataset` was previously encrypted with tzpfms and the *TPM1.X* back-end was used, the metadata will be silently cleared.
|
|
||||||
Otherwise, or in case of an error, data required for manual intervention will be printed to the standard error stream.
|
|
||||||
|
|
||||||
Next, a new wrapping key is be generated on the TPM, optionally backed up (see [OPTIONS][]),
|
|
||||||
and sealed on the TPM;
|
|
||||||
the user is prompted for an optional passphrase to protect the key with,
|
|
||||||
and for the SRK passphrase, set when taking ownership, if it is not "well-known" (all zeroes).
|
|
||||||
|
|
||||||
The following properties are set on `dataset`:
|
|
||||||
|
|
||||||
* `xyz.nabijaczleweli:tzpfms.backend`=`TPM1.X`
|
|
||||||
* `xyz.nabijaczleweli:tzpfms.key`=*(parent key blob)*`:`*(sealed object blob)*
|
|
||||||
|
|
||||||
`tzpfms.backend` identifies this dataset for work with *TPM1.X*-back-ended tzpfms tools
|
|
||||||
(namely zfs-tpm1x-change-key(8), zfs-tpm1x-load-key(8), and zfs-tpm1x-clear-key(8)).
|
|
||||||
|
|
||||||
`tzpfms.key` is a colon-separated pair of hexadecimal-string (i.e. "4F7730" for "Ow0") blobs;
|
|
||||||
the first one represents the RSA key protecting the blob,
|
|
||||||
and it is protected with either the password, if provided, or the SHA1 constant *CE4CF677875B5EB8993591D5A9AF1ED24A3A8736*;
|
|
||||||
the second represents the sealed object containing the wrapping key,
|
|
||||||
and is protected with the SHA1 constant *B9EE715DBE4B243FAA81EA04306E063710383E35*.
|
|
||||||
There exists no other user-land tool for decrypting this; perhaps there should be.
|
|
||||||
#comment (TODO: make an LD_PRELOADable for extracting the key maybe)
|
|
||||||
|
|
||||||
Finally, the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=raw dataset** is performed with the new key.
|
|
||||||
If an error occurred, best effort is made to clean up the properties,
|
|
||||||
or to issue a note for manual intervention into the standard error stream.
|
|
||||||
|
|
||||||
A final verification should be made by running **zfs-tpm1x-load-key(8) -n dataset**.
|
|
||||||
If that command succeeds, all is well,
|
|
||||||
but otherwise the dataset can be manually rolled back to a password with **zfs-tpm1x-clear-key(8) dataset** (or, if that fails to work, **zfs(8) change-key -o keyformat=passphrase dataset**), and you are hereby asked to report a bug, please.
|
|
||||||
|
|
||||||
**zfs-tpm1x-clear-key(8) dataset** can be used to clear the properties and go back to using a password.
|
|
||||||
|
|
||||||
## OPTIONS
|
|
||||||
|
|
||||||
* `-b` *file*:
|
|
||||||
Save a back-up of the key to *file*, which must not exist beforehand.
|
|
||||||
This back-up **must** be stored securely, off-site.
|
|
||||||
In case of a catastrophic event, the key can be loaded by running **zfs(8) load-key dataset < backup-file**.
|
|
||||||
|
|
||||||
#include "backend-tpm1x.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
## SEE ALSO
|
|
||||||
|
|
||||||
<<https://git.sr.ht/~nabijaczleweli/tzpfms>>
|
|
38
man/zfs-tpm1x-clear-key.8.pp
Normal file
38
man/zfs-tpm1x-clear-key.8.pp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
|
.Dd
|
||||||
|
.Dt ZFS-TPM1X-CLEAR-KEY 8
|
||||||
|
.Os
|
||||||
|
.
|
||||||
|
.Sh NAME
|
||||||
|
.Nm zfs-tpm1x-clear-key
|
||||||
|
.Nd rewrap ZFS dataset key in passsword and clear tzpfms TPM1.X metadata
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Ar dataset
|
||||||
|
.
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
After verifying
|
||||||
|
.Ar dataset
|
||||||
|
was encrypted with the
|
||||||
|
.Nm tzpfms
|
||||||
|
.Sy TPM1.X
|
||||||
|
backend:
|
||||||
|
.Bl -enum -compact -offset 2n -width 2n
|
||||||
|
.It
|
||||||
|
performs the equivalent of
|
||||||
|
.Nm zfs Cm change-key Fl o Li keylocation=prompt Fl o Li keyformat=passphrase Ar dataset ,
|
||||||
|
.It
|
||||||
|
removes the
|
||||||
|
.Li xyz.nabijaczleweli:tzpfms.\& Ns Brq Li backend , key
|
||||||
|
properties from
|
||||||
|
.Ar dataset .
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
See
|
||||||
|
.Xr zfs-tpm1x-change-key 8
|
||||||
|
for a detailed description.
|
||||||
|
.
|
||||||
|
#include "backend-tpm1x.h"
|
||||||
|
.
|
||||||
|
#include "common.h"
|
@ -1,23 +0,0 @@
|
|||||||
zfs-tpm1x-clear-key(8) -- rewrap ZFS dataset key in passsword and clear tzpfms TPM1.X metadata
|
|
||||||
==============================================================================================
|
|
||||||
|
|
||||||
## SYNOPSIS
|
|
||||||
|
|
||||||
`zfs-tpm1x-clear-key` <dataset>
|
|
||||||
|
|
||||||
## DESCRIPTION
|
|
||||||
|
|
||||||
zfs-tpm1x-clear-key(8), after verifying that `dataset` was encrypted with tzpfms backend *TPM1.X* will:
|
|
||||||
|
|
||||||
1. perform the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=passphrase dataset**,
|
|
||||||
2. remove the `xyz.nabijaczleweli:tzpfms.{backend,key}` properties from `dataset`.
|
|
||||||
|
|
||||||
See zfs-tpm1x-change-key(8) for a detailed description.
|
|
||||||
|
|
||||||
#include "backend-tpm1x.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
## SEE ALSO
|
|
||||||
|
|
||||||
<<https://git.sr.ht/~nabijaczleweli/tzpfms>>
|
|
46
man/zfs-tpm1x-load-key.8.pp
Normal file
46
man/zfs-tpm1x-load-key.8.pp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
|
.Dd
|
||||||
|
.Dt ZFS-TPM1X-LOAD-KEY 8
|
||||||
|
.Os
|
||||||
|
.
|
||||||
|
.Sh NAME
|
||||||
|
.Nm zfs-tpm1x-load-key
|
||||||
|
.Nd load TPM1.X-encrypted ZFS dataset key
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl n
|
||||||
|
.Ar dataset
|
||||||
|
.
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
After verifying
|
||||||
|
.Ar dataset
|
||||||
|
was encrypted with the
|
||||||
|
.Nm tzpfms
|
||||||
|
.Sy TPM1.X
|
||||||
|
backend,
|
||||||
|
unseals the key and load it into
|
||||||
|
.Ar dataset .
|
||||||
|
.Pp
|
||||||
|
The user is first prompted for the SRK passphrase, set when taking ownership, if not "well-known" (all zeroes);
|
||||||
|
then for the additional passphrase, set when creating the key, if one was set.
|
||||||
|
.Pp
|
||||||
|
See
|
||||||
|
.Xr zfs-tpm1x-change-key 8
|
||||||
|
for a detailed description.
|
||||||
|
.
|
||||||
|
.Sh OPTIONS
|
||||||
|
.Bl -tag -compact -width ".Fl n"
|
||||||
|
.It Fl n
|
||||||
|
Do a no-op/dry run, can be used even if the key is already loaded.
|
||||||
|
Equivalent to
|
||||||
|
.Nm zfs Cm load-key Ns 's
|
||||||
|
.Fl n
|
||||||
|
option.
|
||||||
|
.El
|
||||||
|
.
|
||||||
|
#include "passphrase.h"
|
||||||
|
.
|
||||||
|
#include "backend-tpm1x.h"
|
||||||
|
.
|
||||||
|
#include "common.h"
|
@ -1,28 +0,0 @@
|
|||||||
zfs-tpm1x-load-key(8) -- load tzpfms TPM1.X-encrypted ZFS dataset key
|
|
||||||
=====================================================================
|
|
||||||
|
|
||||||
## SYNOPSIS
|
|
||||||
|
|
||||||
`zfs-tpm1x-load-key` [-n] <dataset>
|
|
||||||
|
|
||||||
## DESCRIPTION
|
|
||||||
|
|
||||||
zfs-tpm1x-load-key(8), after verifying that `dataset` was encrypted with tzpfms backend *TPM1.X* will unseal the key and load it into `dataset`.
|
|
||||||
|
|
||||||
The user is prompted for, first, the SRK passphrase, set when taking ownership, if it's not "well-known" (all zeroes),
|
|
||||||
then the additional passphrase set when creating the key, if it was provided.
|
|
||||||
|
|
||||||
See zfs-tpm1x-change-key(8) for a detailed description.
|
|
||||||
|
|
||||||
## OPTIONS
|
|
||||||
|
|
||||||
* `-n`:
|
|
||||||
Do a no-op/dry run, can be used even if the key is already loaded. Equivalent to **zfs(8) load-key**'s `-n` option.
|
|
||||||
|
|
||||||
#include "backend-tpm1x.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
## SEE ALSO
|
|
||||||
|
|
||||||
<<https://git.sr.ht/~nabijaczleweli/tzpfms>>
|
|
167
man/zfs-tpm2-change-key.8.pp
Normal file
167
man/zfs-tpm2-change-key.8.pp
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
|
.Dd
|
||||||
|
.Dt ZFS-TPM2-CHANGE-KEY 8
|
||||||
|
.Os
|
||||||
|
.
|
||||||
|
.Sh NAME
|
||||||
|
.Nm zfs-tpm2-change-key
|
||||||
|
.Nd change ZFS dataset key to one stored on the TPM
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl b Ar backup-file
|
||||||
|
.Oo
|
||||||
|
.Fl P Ar algorithm Ns Cm \&: Ns Ar PCR Ns Oo Ns Cm \&, Ns Ar PCR Oc Ns … Ns \: Ns Oo Cm + Ns Ar algorithm Ns Cm \&: Ns Ar PCR Ns Oo Ns Cm \&, Ns Ar PCR Oc Ns … Oc Ns …
|
||||||
|
.Op Fl A
|
||||||
|
.Oc
|
||||||
|
.Ar dataset
|
||||||
|
.
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
To normalise
|
||||||
|
.Ar dataset ,
|
||||||
|
.Nm
|
||||||
|
will open its encryption root in its stead.
|
||||||
|
.Nm
|
||||||
|
will
|
||||||
|
.Em never
|
||||||
|
create or destroy encryption roots; use
|
||||||
|
.Xr zfs-change-key 8
|
||||||
|
for that.
|
||||||
|
.Pp
|
||||||
|
First, a connection is made to the TPM, which
|
||||||
|
.Em must
|
||||||
|
be TPM-2.0-compatible.
|
||||||
|
.Pp
|
||||||
|
If
|
||||||
|
.Ar dataset
|
||||||
|
was previously encrypted with
|
||||||
|
.Nm tzpfms
|
||||||
|
and the
|
||||||
|
.Sy TPM2
|
||||||
|
back-end was used, the previous key will be freed from the TPM.
|
||||||
|
Otherwise, or in case of an error, data required for manual intervention will be written to the standard error stream.
|
||||||
|
.Pp
|
||||||
|
Next, a new wrapping key is generated on the TPM, optionally backed up
|
||||||
|
.Pq see Sx OPTIONS ,
|
||||||
|
and sealed to a persistent object on the TPM under the owner hierarchy;
|
||||||
|
if there is a passphrase set on the owner hierarchy, the user is prompted for it;
|
||||||
|
the user is always prompted for an optional passphrase to protect the sealed object with.
|
||||||
|
.Pp
|
||||||
|
The following properties are set on
|
||||||
|
.Ar dataset :
|
||||||
|
.Bl -bullet -compact -offset 4n -width "@"
|
||||||
|
.It
|
||||||
|
.Li xyz.nabijaczleweli:tzpfms.backend Ns = Ns Sy TPM2
|
||||||
|
.It
|
||||||
|
.Li xyz.nabijaczleweli:tzpfms.key Ns = Ns Ar persistent-object-ID Ns \: Ns Op Cm ;\& Ns Ar algorithm Ns Cm \&: Ns Ar PCR Ns Oo Ns Cm \&, Ns Ar PCR Oc Ns … Ns Oo Cm + Ns Ar algorithm Ns Cm \&: Ns Ar PCR Ns Oo Ns Cm \&, Ns Ar PCR Oc Ns … Oc Ns …
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
.Li tzpfms.backend
|
||||||
|
identifies this dataset for work with
|
||||||
|
.Sy TPM2 Ns -back-ended
|
||||||
|
.Nm tzpfms
|
||||||
|
programs
|
||||||
|
.Pq namely Xr zfs-tpm2-change-key 8 , Xr zfs-tpm2-load-key 8 , and Xr zfs-tpm2-clear-key 8 .
|
||||||
|
.Pp
|
||||||
|
.Li tzpfms.key
|
||||||
|
is an integer representing the sealed object, optionally followed by a semicolon and PCR list as specified with
|
||||||
|
.Fl P ,
|
||||||
|
normalised to be
|
||||||
|
.Nm tpm-tools Ns -toolchain-compatible ;
|
||||||
|
if needed, it can be passed to
|
||||||
|
.Nm tpm2_unseal Fl c Ev ${tzpfms.key Ns Cm %% Ns Li ;* Ns Ev }\&
|
||||||
|
with
|
||||||
|
.Fl p Qq Li str:\& Ns Ev ${passphrase}
|
||||||
|
or
|
||||||
|
.Fl p Qq Li pcr:\& Ns Ev ${tzpfms.key Ns Cm # Ns Li *; Ns Ev }\& ,
|
||||||
|
as the case may be, or equivalent, for back-up
|
||||||
|
.Pq see Sx OPTIONS .
|
||||||
|
If you have a sealed key you can access with that or equivalent tool and set both of these properties, it will funxion seamlessly.
|
||||||
|
.Pp
|
||||||
|
Finally, the equivalent of
|
||||||
|
.Nm zfs Cm change-key Fl o Li keylocation=prompt Fl o Li keyformat=raw Ar dataset
|
||||||
|
is performed with the new key.
|
||||||
|
If an error occurred, best effort is made to clean up the persistent object and properties,
|
||||||
|
or to issue a note for manual intervention into the standard error stream.
|
||||||
|
.Pp
|
||||||
|
A final verification should be made by running
|
||||||
|
.Nm zfs-tpm2-load-key Fl n Ar dataset .
|
||||||
|
If that command succeeds, all is well,
|
||||||
|
but otherwise the dataset can be manually rolled back to a passphrase with
|
||||||
|
.Nm zfs-tpm2-clear-key Ar dataset
|
||||||
|
.Pq or, if that fails to work, Nm zfs Cm change-key Fl o Li keyformat=passphrase Ar dataset ,
|
||||||
|
and you are hereby asked to report a bug, please.
|
||||||
|
.Pp
|
||||||
|
.Nm zfs-tpm2-clear-key Ar dataset
|
||||||
|
can be used to free the TPM persistent object and go back to using a passphrase.
|
||||||
|
.
|
||||||
|
.Sh OPTIONS
|
||||||
|
.Bl -tag -compact -width ".Fl b Ar backup-file"
|
||||||
|
.It Fl b Ar backup-file
|
||||||
|
Save a back-up of the key to
|
||||||
|
.Ar backup-file ,
|
||||||
|
which must not exist beforehand.
|
||||||
|
This back-up
|
||||||
|
.Em must
|
||||||
|
be stored securely, off-site.
|
||||||
|
In case of a catastrophic event, the key can be loaded by running
|
||||||
|
.Dl Nm zfs Cm load-key Ar dataset Li < Ar backup-file
|
||||||
|
.Pp
|
||||||
|
.
|
||||||
|
.It Fl P Ar algorithm Ns Cm \&: Ns Ar PCR Ns Oo Ns Cm \&, Ns Ar PCR Oc Ns … Ns Oo Cm + Ns Ar algorithm Ns Cm \&: Ns Ar PCR Ns Oo Ns Cm \&, Ns Ar PCR Oc Ns … Oc Ns …
|
||||||
|
Bind the key to space- or comma-separated
|
||||||
|
.Ar PCR Ns s
|
||||||
|
within their corresponding hashing
|
||||||
|
.Ar algorithm
|
||||||
|
\(em if they change, the wrapping key will not be able to be unsealed.
|
||||||
|
There are
|
||||||
|
.Sy 24
|
||||||
|
PCRs, numbered
|
||||||
|
.Bq Sy 0 , 23 .
|
||||||
|
.Pp
|
||||||
|
.Ar algorithm
|
||||||
|
may be any of case-insensitive
|
||||||
|
.Qq Sy sha1 ,
|
||||||
|
.Qq Sy sha256 ,
|
||||||
|
.Qq Sy sha384 ,
|
||||||
|
.Qq Sy sha512 ,
|
||||||
|
.Qq Sy sm3_256 ,
|
||||||
|
.Qq Sy sm3-256 ,
|
||||||
|
.Qq Sy sha3_256 ,
|
||||||
|
.Qq Sy sha3-256 ,
|
||||||
|
.Qq Sy sha3_384 ,
|
||||||
|
.Qq Sy sha3-384 ,
|
||||||
|
.Qq Sy sha3_512 ,
|
||||||
|
or
|
||||||
|
.Qq Sy sha3-512 ,
|
||||||
|
and must be supported by the TPM.
|
||||||
|
.Pp
|
||||||
|
.
|
||||||
|
.It Fl A
|
||||||
|
With
|
||||||
|
.Fl P ,
|
||||||
|
also prompt for a passphrase.
|
||||||
|
This is skipped by default because the passphrase is
|
||||||
|
.Em OR Ns ed
|
||||||
|
with the PCR policy \(em the wrapping key can be unsealed
|
||||||
|
.Em either
|
||||||
|
passphraseless with the right PCRs
|
||||||
|
.Em or
|
||||||
|
with the passphrase, and this is usually not the intent.
|
||||||
|
.El
|
||||||
|
.
|
||||||
|
#include "passphrase.h"
|
||||||
|
.
|
||||||
|
#include "backend-tpm2.h"
|
||||||
|
.
|
||||||
|
#include "common.h"
|
||||||
|
.
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr tpm2_unseal 1
|
||||||
|
.Pp
|
||||||
|
.\" Match this to zfs-tpm1x-change-key.8:
|
||||||
|
PCR allocations:
|
||||||
|
.Lk https:/\&/wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers
|
||||||
|
and
|
||||||
|
.Lk https:/\&/trustedcomputinggroup.org/wp-content/uploads/PC-ClientSpecific_Platform_Profile_for_TPM_2p0_Systems_v51.pdf ,
|
||||||
|
Section 2.3.4 "PCR Usage", Table 1.
|
@ -1,58 +0,0 @@
|
|||||||
zfs-tpm2-change-key(8) -- change ZFS dataset key to one stored on the TPM
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
## SYNOPSIS
|
|
||||||
|
|
||||||
`zfs-tpm2-change-key` [-b file] <dataset>
|
|
||||||
|
|
||||||
## DESCRIPTION
|
|
||||||
|
|
||||||
To normalise `dataset`, zfs-tpm2-change-key(8) will open its encryption root in its stead.
|
|
||||||
zfs-tpm2-change-key(8) will *never* create or destroy encryption roots; use **zfs(8) change-key** for that.
|
|
||||||
|
|
||||||
First, a connection is made to the TPM, which *must* be TPM-2.0-compatible.
|
|
||||||
|
|
||||||
If `dataset` was previously encrypted with tzpfms and the *TPM2* back-end was used, the previous key will be freed from the TPM.
|
|
||||||
Otherwise, or in case of an error, data required for manual intervention will be printed to the standard error stream.
|
|
||||||
|
|
||||||
Next, a new wrapping key is be generated on the TPM, optionally backed up (see [OPTIONS][]),
|
|
||||||
and sealed to a persistent object on the TPM under the owner hierarchy;
|
|
||||||
if there is a passphrase set on the owner hierarchy, the user is prompted for it;
|
|
||||||
the user is always prompted for an optional passphrase to protect the sealed object with.
|
|
||||||
|
|
||||||
The following properties are set on `dataset`:
|
|
||||||
|
|
||||||
* `xyz.nabijaczleweli:tzpfms.backend`=`TPM2`
|
|
||||||
* `xyz.nabijaczleweli:tzpfms.key`=*(ID of persistent object)*
|
|
||||||
|
|
||||||
`tzpfms.backend` identifies this dataset for work with *TPM2*-back-ended tzpfms tools
|
|
||||||
(namely zfs-tpm2-change-key(8), zfs-tpm2-load-key(8), and zfs-tpm2-clear-key(8)).
|
|
||||||
|
|
||||||
`tzpfms.key` is an integer representing the sealed object;
|
|
||||||
if needed, it can be passed to **tpm2_unseal(1) -c ${tzpfms.key} [-p ${password}]** or equivalent for back-up (see [OPTIONS][]).
|
|
||||||
If you have a sealed key you can access with that or equivalent tool and set both of these properties, it will funxion seamlessly.
|
|
||||||
|
|
||||||
Finally, the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=raw dataset** is performed with the new key.
|
|
||||||
If an error occurred, best effort is made to clean up the persistent object and properties,
|
|
||||||
or to issue a note for manual intervention into the standard error stream.
|
|
||||||
|
|
||||||
A final verification should be made by running **zfs-tpm2-load-key(8) -n dataset**.
|
|
||||||
If that command succeeds, all is well,
|
|
||||||
but otherwise the dataset can be manually rolled back to a password with **zfs-tpm2-clear-key(8) dataset** (or, if that fails to work, **zfs(8) change-key -o keyformat=passphrase dataset**), and you are hereby asked to report a bug, please.
|
|
||||||
|
|
||||||
**zfs-tpm2-clear-key(8) dataset** can be used to free the TPM persistent object and go back to using a password.
|
|
||||||
|
|
||||||
## OPTIONS
|
|
||||||
|
|
||||||
* `-b` *file*:
|
|
||||||
Save a back-up of the key to *file*, which must not exist beforehand.
|
|
||||||
This back-up **must** be stored securely, off-site.
|
|
||||||
In case of a catastrophic event, the key can be loaded by running **zfs(8) load-key dataset < backup-file**.
|
|
||||||
|
|
||||||
#include "backend-tpm2.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
## SEE ALSO
|
|
||||||
|
|
||||||
<<https://git.sr.ht/~nabijaczleweli/tzpfms>>
|
|
43
man/zfs-tpm2-clear-key.8.pp
Normal file
43
man/zfs-tpm2-clear-key.8.pp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
|
.Dd
|
||||||
|
.Dt ZFS-TPM2-CLEAR-KEY 8
|
||||||
|
.Os
|
||||||
|
.
|
||||||
|
.Sh NAME
|
||||||
|
.Nm zfs-tpm2-clear-key
|
||||||
|
.Nd rewrap ZFS dataset key in passsword and clear tzpfms TPM2 metadata
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Ar dataset
|
||||||
|
.
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
After verifying
|
||||||
|
.Ar dataset
|
||||||
|
was encrypted with the
|
||||||
|
.Nm tzpfms
|
||||||
|
.Sy TPM2
|
||||||
|
backend:
|
||||||
|
.Bl -enum -compact -offset 2n -width 2n
|
||||||
|
.It
|
||||||
|
performs the equivalent of
|
||||||
|
.Nm zfs Cm change-key Fl o Li keylocation=prompt Fl o Li keyformat=passphrase Ar dataset ,
|
||||||
|
.It
|
||||||
|
frees the sealed key previously used to encrypt
|
||||||
|
.Ar dataset ,
|
||||||
|
.It
|
||||||
|
removes the
|
||||||
|
.Li xyz.nabijaczleweli:tzpfms.\& Ns Brq Li backend , key
|
||||||
|
properties from
|
||||||
|
.Ar dataset .
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
See
|
||||||
|
.Xr zfs-tpm2-change-key 8
|
||||||
|
for a detailed description.
|
||||||
|
.
|
||||||
|
#include "passphrase.h"
|
||||||
|
.
|
||||||
|
#include "backend-tpm2.h"
|
||||||
|
.
|
||||||
|
#include "common.h"
|
@ -1,24 +0,0 @@
|
|||||||
zfs-tpm2-clear-key(8) -- rewrap ZFS dataset key in passsword and clear tzpfms TPM2 metadata
|
|
||||||
===========================================================================================
|
|
||||||
|
|
||||||
## SYNOPSIS
|
|
||||||
|
|
||||||
`zfs-tpm2-clear-key` <dataset>
|
|
||||||
|
|
||||||
## DESCRIPTION
|
|
||||||
|
|
||||||
zfs-tpm2-clear-key(8), after verifying that `dataset` was encrypted with tzpfms backend *TPM2* will:
|
|
||||||
|
|
||||||
1. perform the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=passphrase dataset**,
|
|
||||||
2. free the sealed key previously used to encrypt `dataset`,
|
|
||||||
3. remove the `xyz.nabijaczleweli:tzpfms.{backend,key}` properties from `dataset`.
|
|
||||||
|
|
||||||
See zfs-tpm2-change-key(8) for a detailed description.
|
|
||||||
|
|
||||||
#include "backend-tpm2.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
## SEE ALSO
|
|
||||||
|
|
||||||
<<https://git.sr.ht/~nabijaczleweli/tzpfms>>
|
|
45
man/zfs-tpm2-load-key.8.pp
Normal file
45
man/zfs-tpm2-load-key.8.pp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
.\" SPDX-License-Identifier: MIT
|
||||||
|
.
|
||||||
|
.Dd
|
||||||
|
.Dt ZFS-TPM2-LOAD-KEY 8
|
||||||
|
.Os
|
||||||
|
.
|
||||||
|
.Sh NAME
|
||||||
|
.Nm zfs-tpm2-load-key
|
||||||
|
.Nd load TPM2-encrypted ZFS dataset key
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl n
|
||||||
|
.Ar dataset
|
||||||
|
.
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
After verifying
|
||||||
|
.Ar dataset
|
||||||
|
was encrypted with the
|
||||||
|
.Nm tzpfms
|
||||||
|
.Sy TPM2
|
||||||
|
backend,
|
||||||
|
unseals the key and loads it into
|
||||||
|
.Ar dataset .
|
||||||
|
.Pp
|
||||||
|
The user is prompted for the additional passphrase, set when creating the key, if one was set.
|
||||||
|
.Pp
|
||||||
|
See
|
||||||
|
.Xr zfs-tpm2-change-key 8
|
||||||
|
for a detailed description.
|
||||||
|
.
|
||||||
|
.Sh OPTIONS
|
||||||
|
.Bl -tag -compact -width ".Fl n"
|
||||||
|
.It Fl n
|
||||||
|
Do a no-op/dry run, can be used even if the key is already loaded.
|
||||||
|
Equivalent to
|
||||||
|
.Nm zfs Cm load-key Ns 's
|
||||||
|
.Fl n
|
||||||
|
option.
|
||||||
|
.El
|
||||||
|
.
|
||||||
|
#include "passphrase.h"
|
||||||
|
.
|
||||||
|
#include "backend-tpm1x.h"
|
||||||
|
.
|
||||||
|
#include "common.h"
|
@ -1,25 +0,0 @@
|
|||||||
zfs-tpm2-load-key(8) -- load tzpfms TPM2-encrypted ZFS dataset key
|
|
||||||
==================================================================
|
|
||||||
|
|
||||||
## SYNOPSIS
|
|
||||||
|
|
||||||
`zfs-tpm2-load-key` [-n] <dataset>
|
|
||||||
|
|
||||||
## DESCRIPTION
|
|
||||||
|
|
||||||
zfs-tpm2-load-key(8), after verifying that `dataset` was encrypted with tzpfms backend *TPM2* will unseal the key and load it into `dataset`.
|
|
||||||
|
|
||||||
See zfs-tpm2-change-key(8) for a detailed description.
|
|
||||||
|
|
||||||
## OPTIONS
|
|
||||||
|
|
||||||
* `-n`:
|
|
||||||
Do a no-op/dry run, can be used even if the key is already loaded. Equivalent to **zfs(8) load-key**'s `-n` option.
|
|
||||||
|
|
||||||
#include "backend-tpm2.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
## SEE ALSO
|
|
||||||
|
|
||||||
<<https://git.sr.ht/~nabijaczleweli/tzpfms>>
|
|
282
po/pl.po
Normal file
282
po/pl.po
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
# SPDX-License-Identifier: 0BSD
|
||||||
|
msgid ""
|
||||||
|
msgstr "Language: pl\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
|
||||||
|
#: src/fd.cpp:63
|
||||||
|
#, c-format
|
||||||
|
msgid "Passphrase for %s"
|
||||||
|
msgstr "Hasło do %s"
|
||||||
|
|
||||||
|
#: src/fd.cpp:65
|
||||||
|
#, c-format
|
||||||
|
msgid "Passphrase for %s (again)"
|
||||||
|
msgstr "Powtórz hasło do %s"
|
||||||
|
|
||||||
|
#: src/fd.cpp:67
|
||||||
|
#, c-format
|
||||||
|
msgid "New passphrase for %s"
|
||||||
|
msgstr "Nowe hasło do %s"
|
||||||
|
|
||||||
|
#. newkey && again
|
||||||
|
#: src/fd.cpp:69
|
||||||
|
#, c-format
|
||||||
|
msgid "New passphrase for %s (again)"
|
||||||
|
msgstr "Powtórz howe hasło do %s"
|
||||||
|
|
||||||
|
#. exit status
|
||||||
|
#: src/fd.cpp:116
|
||||||
|
#, c-format
|
||||||
|
msgid "Helper '%s' failed with %d.\n"
|
||||||
|
msgstr "'%s' zawiódł: %d.\n"
|
||||||
|
|
||||||
|
#: src/fd.cpp:119
|
||||||
|
#, c-format
|
||||||
|
msgid "Helper '%s' died to signal %d: %s.\n"
|
||||||
|
msgstr "'%s' zabity przez sygnał %d: %s.\n"
|
||||||
|
|
||||||
|
#: src/fd.cpp:155
|
||||||
|
#, c-format
|
||||||
|
msgid "Enter passphrase for %s: "
|
||||||
|
msgstr "Hasło do %s"
|
||||||
|
|
||||||
|
#: src/fd.cpp:157
|
||||||
|
#, c-format
|
||||||
|
msgid "Enter passphrase for new %s: "
|
||||||
|
msgstr "Hasło do nowego %s"
|
||||||
|
|
||||||
|
#: src/fd.cpp:159
|
||||||
|
#, c-format
|
||||||
|
msgid "Re-enter passphrase for %s: "
|
||||||
|
msgstr "Ponownie, hasło do %s"
|
||||||
|
|
||||||
|
#. again && newkey
|
||||||
|
#: src/fd.cpp:161
|
||||||
|
#, c-format
|
||||||
|
msgid "Re-enter passphrase for new %s: "
|
||||||
|
msgstr "Ponownie, hasło do nowego %s"
|
||||||
|
|
||||||
|
#: src/fd.cpp:238 src/fd.cpp:254
|
||||||
|
#, c-format
|
||||||
|
msgid "Passphrase too long (max %zu)\n"
|
||||||
|
msgstr "Hasło za długie (maks. %zu)\n"
|
||||||
|
|
||||||
|
#: src/fd.cpp:252
|
||||||
|
#, c-format
|
||||||
|
msgid "Passphrase too short (min %u)\n"
|
||||||
|
msgstr "Hasło za krótkie (maks. %u)\n"
|
||||||
|
|
||||||
|
#: src/fd.cpp:262
|
||||||
|
#, c-format
|
||||||
|
msgid "Provided passphrases do not match.\n"
|
||||||
|
msgstr "Hasła się różnią\n"
|
||||||
|
|
||||||
|
#: src/main.hpp:44 src/main.hpp:57 src/main.hpp:62
|
||||||
|
#, c-format
|
||||||
|
msgid "usage: %s [-hV] %s%s%s\n"
|
||||||
|
msgstr "składnia: %s [-hV] %s%s%s\n"
|
||||||
|
|
||||||
|
#. as-in argument in a usage string
|
||||||
|
#: src/main.hpp:71
|
||||||
|
msgid "dataset"
|
||||||
|
msgstr "dataset"
|
||||||
|
|
||||||
|
#: src/main.hpp:75
|
||||||
|
#, c-format
|
||||||
|
msgid "No dataset to act on?\n"
|
||||||
|
"usage: %s [-hV] %s%sdataset\n"
|
||||||
|
msgstr "Nie podano datasetu?\n"
|
||||||
|
"składnia: %s [-hV] %s%sdataset\n"
|
||||||
|
|
||||||
|
#: src/main.hpp:80
|
||||||
|
#, c-format
|
||||||
|
msgid "usage: %s [-hV] %s%sdataset\n"
|
||||||
|
msgstr "składnia: %s [-hV] %s%sdataset\n"
|
||||||
|
|
||||||
|
#: src/main.hpp:90
|
||||||
|
#, c-format
|
||||||
|
msgid "Dataset %s not encrypted?\n"
|
||||||
|
msgstr "Dataset %s nie jest zaszyfrowany?\n"
|
||||||
|
|
||||||
|
#: src/main.hpp:92
|
||||||
|
#, c-format
|
||||||
|
msgid "Using dataset %s's encryption root %s instead.\n"
|
||||||
|
msgstr "Używam zamiast %s jego korzenia szyfrowania %s.\n"
|
||||||
|
|
||||||
|
#. user-supplied parameter, errno
|
||||||
|
#: src/tpm1x.cpp:101
|
||||||
|
#, c-format
|
||||||
|
msgid "PCR %s: %s\n"
|
||||||
|
msgstr "PCR %s: %s\n"
|
||||||
|
|
||||||
|
#. user-supplied parameter
|
||||||
|
#: src/tpm1x.cpp:104
|
||||||
|
#, c-format
|
||||||
|
msgid "PCR %s: too large (max 229).\n"
|
||||||
|
msgstr "PCR %s: za duży (maks. 229)\n"
|
||||||
|
|
||||||
|
#. == TPM2_NUM_PCR_BANKS
|
||||||
|
#: src/tpm2.cpp:116
|
||||||
|
#, c-format
|
||||||
|
msgid "Too many PCR banks specified! Can only have up to %zu\n"
|
||||||
|
msgstr "Zbyt wiele grup PCRów! Mogę przyjąć maks. %zu\n"
|
||||||
|
|
||||||
|
#. comma-separated list follows
|
||||||
|
#: src/tpm2.cpp:133
|
||||||
|
#, c-format
|
||||||
|
msgid "Unknown hash algorithm %s.\n"
|
||||||
|
"Can be any of case-insensitive "
|
||||||
|
msgstr "Nie znany algorytm haszowania %s.\n"
|
||||||
|
"Można użyć dowolnego z (rozmiar liter nie ma znaczenia): "
|
||||||
|
|
||||||
|
#. user-passed parameter
|
||||||
|
#: src/tpm2.cpp:164
|
||||||
|
#, c-format
|
||||||
|
msgid "PCR bank \"%s\": no algorithm; need alg:PCR[,PCR]…\n"
|
||||||
|
msgstr "PCR bank \"%s\": nie podano algorytmu; potrzebuję alg:PCR[,PCR]…\n"
|
||||||
|
|
||||||
|
#. %s=dataset name, then TPM2. noun for "Enter passphrase for" prompt
|
||||||
|
#. %s=dataset, then TPM1.X. noun for "Enter passphrase for" prompt
|
||||||
|
#: src/tpm2.cpp:374 src/bin/zfs-tpm1x-change-key.cpp:109
|
||||||
|
#, c-format
|
||||||
|
msgid "%s %s wrapping key (or empty for none)"
|
||||||
|
msgstr "klucza zawijania %2$s dla %1$s (puste żeby nie używać żadnego)"
|
||||||
|
|
||||||
|
#. %s=dataset name, then TPM2. noun for "Enter passphrase for" prompt
|
||||||
|
#. %s=dataset name, then TPM1.x. noun for "Enter passphrase for" prompt
|
||||||
|
#: src/tpm2.cpp:425 src/bin/zfs-tpm1x-load-key.cpp:59
|
||||||
|
#, c-format
|
||||||
|
msgid "%s %s wrapping key"
|
||||||
|
msgstr "klucza zawijania %2$s dla %1$s"
|
||||||
|
|
||||||
|
#: src/tpm2.cpp:439
|
||||||
|
#, c-format
|
||||||
|
msgid "Couldn't unseal wrapping key with PCR policy: %s\n"
|
||||||
|
msgstr "Nie udało się rozpieczętować klucza zawijania z polityką PCR: %s\n"
|
||||||
|
|
||||||
|
#: src/zfs.cpp:110
|
||||||
|
#, c-format
|
||||||
|
msgid "You might need to run \"zfs inherit %s %s\" and \"zfs inherit %s %s\" to fully clear metadata!\n"
|
||||||
|
msgstr "Możliwe, że potrzebujesz uruchomić \"zfs inherit %s %s\" i \"zfs inherit %s %s\" żeby całkowicie pozbyć się metadanych!\n"
|
||||||
|
|
||||||
|
#: src/zfs.cpp:125
|
||||||
|
#, c-format
|
||||||
|
msgid "Dataset %s not encrypted with tzpfms!\n"
|
||||||
|
msgstr "Dataset %s nie jest szyfrowany tzpfms!\n"
|
||||||
|
|
||||||
|
#: src/zfs.cpp:127
|
||||||
|
#, c-format
|
||||||
|
msgid "Dataset %s encrypted with tzpfms back-end %s, but we are %s.\n"
|
||||||
|
msgstr "Dataset %s szyfrowany tzpfms %s, ale ten program rozumie %s.\n"
|
||||||
|
|
||||||
|
#: src/zfs.cpp:131
|
||||||
|
#, c-format
|
||||||
|
msgid "Dataset %s missing key data.\n"
|
||||||
|
msgstr "Dataset %s nie ma klucza.\n"
|
||||||
|
|
||||||
|
#. / Mimic libzfs error output
|
||||||
|
#: src/zfs.hpp:28
|
||||||
|
#, c-format
|
||||||
|
msgid "Key change error: Key must be loaded.\n"
|
||||||
|
msgstr "Błąd zmiany klucza: Klucz musi być załadowany.\n"
|
||||||
|
|
||||||
|
#. dataset name: (null), 0A123...
|
||||||
|
#. dataset name: TPM1.X, (null)
|
||||||
|
#: src/zfs.hpp:68
|
||||||
|
#, c-format
|
||||||
|
msgid "Inconsistent tzpfms metadata for %s: back-end is %s, but handle is %s?\n"
|
||||||
|
msgstr "Niespójne metadane tzpfms dla %s: tzpfms %s ale obiekt z kluczem %s?\n"
|
||||||
|
|
||||||
|
#: src/zfs.hpp:73
|
||||||
|
#, c-format
|
||||||
|
msgid "Dataset %s was encrypted with tzpfms back-end %s before, but we are %s. You will have to free handle %s for back-end %s manually!\n"
|
||||||
|
msgstr "Dataset %s był zaszyfrowany tzpfms %s, ale ten program rozumie %s. Konieczne będzie ręczne usunięcie obiektu z kluczem %s %s!\n"
|
||||||
|
|
||||||
|
#: src/zfs_meat.cpp:29
|
||||||
|
#, c-format
|
||||||
|
msgid "Key for %s changed\n"
|
||||||
|
msgstr "Klucz do %s zmieniony\n"
|
||||||
|
|
||||||
|
#: src/zfs_meat.cpp:42
|
||||||
|
#, c-format
|
||||||
|
msgid "Key for %s OK\n"
|
||||||
|
msgstr "Klucz do %s OK\n"
|
||||||
|
|
||||||
|
#: src/zfs_meat.cpp:44
|
||||||
|
#, c-format
|
||||||
|
msgid "Key for %s loaded\n"
|
||||||
|
msgstr "Klucz do %s załadowany\n"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm1x-change-key.cpp:26
|
||||||
|
msgid "[-b backup-file] [-P PCR[,PCR]…]"
|
||||||
|
msgstr "[-b plik-z-backupem] [-P PCR[,PCR]…]"
|
||||||
|
|
||||||
|
#. 0A1234... follows
|
||||||
|
#: src/bin/zfs-tpm1x-load-key.cpp:67
|
||||||
|
#, c-format
|
||||||
|
msgid "Wrong sealed data length (%u != %zu): "
|
||||||
|
msgstr "Zła długość zaplombowanych danych (%u != %zu): "
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm2-change-key.cpp:21
|
||||||
|
msgid "[-b backup-file] [-P algorithm:PCR[,PCR]…[+algorithm:PCR[,PCR]…]… [-A]]"
|
||||||
|
msgstr "[-b plik-z-backupem] [-P algorytm:PCR[,PCR]…[+algorytm:PCR[,PCR]…]… [-A]]"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm2-change-key.cpp:68
|
||||||
|
#, c-format
|
||||||
|
msgid "Couldn't parse previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c %s\" or equivalent!\n"
|
||||||
|
msgstr "Nie udało się rozczytać poprzedniego obiektu z kluczem dla %s. Możliwe, że potrzeba będzie uruchomić \"tpm2_evictcontrol -c %s\", albo jego ekwiwalent!\n"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm2-change-key.cpp:74
|
||||||
|
#, c-format
|
||||||
|
msgid "Couldn't free previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n"
|
||||||
|
msgstr "Nie udało się uwolnić poprzedniego obiektu z kluczem dla %s. Możliwe, że potrzeba będzie uruchomić \"tpm2_evictcontrol -c 0x%X\", albo jego ekwiwalent!\n"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm2-change-key.cpp:89
|
||||||
|
#, c-format
|
||||||
|
msgid "Couldn't free persistent handle. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n"
|
||||||
|
msgstr "Nie udało się uwolnić obiektu z kluczem. Możliwe, że potrzeba będzie uruchomić \"tpm2_evictcontrol -c 0x%X\", albo jego ekwiwalent!\n"
|
||||||
|
|
||||||
|
#. for KEYSTATUS column
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:44
|
||||||
|
msgid "unavailable"
|
||||||
|
msgstr "niedostępny"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:44
|
||||||
|
msgid "available"
|
||||||
|
msgstr "dostępny"
|
||||||
|
|
||||||
|
#. for COHERENT column
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:46
|
||||||
|
msgid "no"
|
||||||
|
msgstr "nie"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:46
|
||||||
|
msgid "yes"
|
||||||
|
msgstr "tak"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:88
|
||||||
|
msgid "[-H] [-r|-d max] [-a|-b back-end] [-u|-l]"
|
||||||
|
msgstr "[-H] [-r|-d maks.] [-a|-b rodzaj-tzpfms] [-u|-l]"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:88
|
||||||
|
msgid "[filesystem|volume]…"
|
||||||
|
msgstr "[system-plików|wolumin]…"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:149 src/bin/zfs-tpm-list.cpp:170
|
||||||
|
msgid "NAME"
|
||||||
|
msgstr "NAZWA"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:150 src/bin/zfs-tpm-list.cpp:170
|
||||||
|
msgid "BACK-END"
|
||||||
|
msgstr "RODZAJ"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:151 src/bin/zfs-tpm-list.cpp:170
|
||||||
|
msgid "KEYSTATUS"
|
||||||
|
msgstr "KLUCZ"
|
||||||
|
|
||||||
|
#: src/bin/zfs-tpm-list.cpp:170
|
||||||
|
msgid "COHERENT"
|
||||||
|
msgstr "SPÓJNY"
|
9
pp.awk
9
pp.awk
@ -10,6 +10,13 @@ BEGIN {
|
|||||||
dir = ARGV[1]
|
dir = ARGV[1]
|
||||||
sub(/[^\/]+$/, "", dir)
|
sub(/[^\/]+$/, "", dir)
|
||||||
|
|
||||||
|
for (i = 2; i < ARGC; ++i) {
|
||||||
|
eq = index(ARGV[i], "=")
|
||||||
|
v = substr(ARGV[i], eq + 1)
|
||||||
|
gsub(/\\n/, "\n", v)
|
||||||
|
macro_contents[substr(ARGV[i], 1, eq - 1)] = v
|
||||||
|
}
|
||||||
|
|
||||||
incfile = ""
|
incfile = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,8 +34,6 @@ function input() {
|
|||||||
while((getline < incfile) == 1)
|
while((getline < incfile) == 1)
|
||||||
input()
|
input()
|
||||||
incfile = ""
|
incfile = ""
|
||||||
} else if(NF >= 1 && $1 == "#comment") {
|
|
||||||
// just dont
|
|
||||||
} else if(NF >= 2 && $1 == "#define") {
|
} else if(NF >= 2 && $1 == "#define") {
|
||||||
split($2, nameargs, "(")
|
split($2, nameargs, "(")
|
||||||
macroname = nameargs[1]
|
macroname = nameargs[1]
|
||||||
|
@ -6,12 +6,13 @@
|
|||||||
#include "../zfs.hpp"
|
#include "../zfs.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
|
||||||
#define TZPFMS_BACKEND_MAX_LEN 16
|
#define TZPFMS_BACKEND_MAX_LEN 16
|
||||||
|
|
||||||
|
|
||||||
enum class key_loadedness : char {
|
enum class key_loadedness : signed char {
|
||||||
none = -1,
|
none = -1,
|
||||||
unloaded = 0,
|
unloaded = 0,
|
||||||
loaded = 1,
|
loaded = 1,
|
||||||
@ -19,8 +20,11 @@ enum class key_loadedness : char {
|
|||||||
|
|
||||||
/// zfs(8) uses struct zprop_get_cbdata_t, which is powerful, but inscrutable; we have a fixed format, which makes this easier
|
/// zfs(8) uses struct zprop_get_cbdata_t, which is powerful, but inscrutable; we have a fixed format, which makes this easier
|
||||||
struct output_line {
|
struct output_line {
|
||||||
static const char * const key_available_display[2];
|
static const char * key_available_display[2];
|
||||||
static const char * const coherent_display[2];
|
static const char * coherent_display[2];
|
||||||
|
static uint8_t key_available_display_width[2];
|
||||||
|
|
||||||
|
static void init_display();
|
||||||
|
|
||||||
|
|
||||||
char name[ZFS_MAX_DATASET_NAME_LEN + 1];
|
char name[ZFS_MAX_DATASET_NAME_LEN + 1];
|
||||||
@ -36,8 +40,42 @@ struct output_line {
|
|||||||
const char * backend_display() const { return (this->backend[0] != '\0') ? this->backend : "-"; }
|
const char * backend_display() const { return (this->backend[0] != '\0') ? this->backend : "-"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
const char * const output_line::key_available_display[2]{"unavailable", key_available_display[0] + 2};
|
// for KEYSTATUS column
|
||||||
const char * const output_line::coherent_display[2]{"no", "yes"};
|
const char * output_line::key_available_display[2]{gettext_noop("unavailable"), gettext_noop("available")};
|
||||||
|
// for COHERENT column
|
||||||
|
const char * output_line::coherent_display[2]{gettext_noop("no"), gettext_noop("yes")};
|
||||||
|
uint8_t output_line::key_available_display_width[2];
|
||||||
|
|
||||||
|
|
||||||
|
// infallible strings only
|
||||||
|
static int strwidth(const char * str) {
|
||||||
|
int ret{};
|
||||||
|
wchar_t c;
|
||||||
|
mbstate_t state{};
|
||||||
|
auto len = strlen(str);
|
||||||
|
for(;;)
|
||||||
|
switch(auto rd = mbrtowc(&c, str, len, &state)) {
|
||||||
|
case 0: // NUL
|
||||||
|
case(size_t)-1: // EILSEQ
|
||||||
|
case(size_t)-2: // ENODATA
|
||||||
|
return ret;
|
||||||
|
default:
|
||||||
|
ret += wcwidth(c) ?: 0;
|
||||||
|
str += rd;
|
||||||
|
len -= rd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_line::init_display() {
|
||||||
|
key_available_display[false] = gettext(key_available_display[false]);
|
||||||
|
key_available_display[true] = gettext(key_available_display[true]);
|
||||||
|
key_available_display_width[false] = strwidth(key_available_display[false]);
|
||||||
|
key_available_display_width[true] = strwidth(key_available_display[true]);
|
||||||
|
|
||||||
|
coherent_display[false] = gettext(coherent_display[false]);
|
||||||
|
coherent_display[true] = gettext(coherent_display[true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main(int argc, char ** argv) {
|
||||||
@ -47,7 +85,7 @@ int main(int argc, char ** argv) {
|
|||||||
const char * backend_restrixion = nullptr;
|
const char * backend_restrixion = nullptr;
|
||||||
auto key_loadedness_restrixion = key_loadedness::none;
|
auto key_loadedness_restrixion = key_loadedness::none;
|
||||||
return do_bare_main(
|
return do_bare_main(
|
||||||
argc, argv, "Hrd:ab:ul", "[-H] [-r|-d max] [-a|-b back-end] [-u|-l]", "[filesystem|volume]…",
|
argc, argv, "Hrd:ab:ul", gettext_noop("[-H] [-r|-d max] [-a|-b back-end] [-u|-l]"), gettext_noop("[filesystem|volume]…"),
|
||||||
[&](auto arg) {
|
[&](auto arg) {
|
||||||
switch(arg) {
|
switch(arg) {
|
||||||
case 'H':
|
case 'H':
|
||||||
@ -57,10 +95,8 @@ int main(int argc, char ** argv) {
|
|||||||
maxdepth = SIZE_MAX;
|
maxdepth = SIZE_MAX;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
if(parse_int(optarg, maxdepth)) {
|
if(!parse_uint(optarg, maxdepth))
|
||||||
fprintf(stderr, "%s is not an integer\n", optarg);
|
return fprintf(stderr, "%s: -d %s: %s\n", argv[0], optarg, strerror(errno)), __LINE__;
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
print_nontzpfms = true;
|
print_nontzpfms = true;
|
||||||
@ -80,8 +116,6 @@ int main(int argc, char ** argv) {
|
|||||||
[&](auto libz) {
|
[&](auto libz) {
|
||||||
output_line * lines{};
|
output_line * lines{};
|
||||||
size_t lines_len{};
|
size_t lines_len{};
|
||||||
quickscope_wrapper lines_deleter{[&] { free(lines); }};
|
|
||||||
|
|
||||||
|
|
||||||
TRY_MAIN(for_all_datasets(libz, argv + optind, maxdepth, [&](auto dataset) {
|
TRY_MAIN(for_all_datasets(libz, argv + optind, maxdepth, [&](auto dataset) {
|
||||||
boolean_t dataset_is_root;
|
boolean_t dataset_is_root;
|
||||||
@ -93,10 +127,9 @@ int main(int argc, char ** argv) {
|
|||||||
TRY_MAIN(lookup_userprop(dataset, PROPNAME_BACKEND, backend));
|
TRY_MAIN(lookup_userprop(dataset, PROPNAME_BACKEND, backend));
|
||||||
TRY_MAIN(lookup_userprop(dataset, PROPNAME_KEY, handle));
|
TRY_MAIN(lookup_userprop(dataset, PROPNAME_KEY, handle));
|
||||||
|
|
||||||
++lines_len;
|
lines = TRY_PTR("allocate line buffer", reinterpret_cast<output_line *>(reallocarray(lines, ++lines_len, sizeof(output_line))));
|
||||||
lines = TRY_PTR("allocate line buffer", reinterpret_cast<output_line *>(realloc(lines, sizeof(output_line) * lines_len)));
|
|
||||||
|
|
||||||
auto & cur_line = lines[lines_len - 1];
|
auto & cur_line = lines[lines_len - 1];
|
||||||
|
|
||||||
strncpy(cur_line.name, zfs_get_name(dataset), ZFS_MAX_DATASET_NAME_LEN);
|
strncpy(cur_line.name, zfs_get_name(dataset), ZFS_MAX_DATASET_NAME_LEN);
|
||||||
strncpy(cur_line.backend, (backend && strlen(backend) <= TZPFMS_BACKEND_MAX_LEN) ? backend : "\0", TZPFMS_BACKEND_MAX_LEN);
|
strncpy(cur_line.backend, (backend && strlen(backend) <= TZPFMS_BACKEND_MAX_LEN) ? backend : "\0", TZPFMS_BACKEND_MAX_LEN);
|
||||||
// Tristate available/unavailable/none, but it's gonna be either available or unavailable on envryption roots, so
|
// Tristate available/unavailable/none, but it's gonna be either available or unavailable on envryption roots, so
|
||||||
@ -106,35 +139,35 @@ int main(int argc, char ** argv) {
|
|||||||
return 0;
|
return 0;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
output_line::init_display();
|
||||||
|
|
||||||
size_t max_name_len = 0;
|
size_t max_name_len = 0;
|
||||||
size_t max_backend_len = 0;
|
size_t max_backend_len = 0;
|
||||||
size_t max_key_available_len = 0;
|
size_t max_key_available_len = 0;
|
||||||
size_t max_coherent_len = 0;
|
|
||||||
auto separator = "\t";
|
auto separator = "\t";
|
||||||
if(human) {
|
if(human) {
|
||||||
max_name_len = strlen("NAME");
|
max_name_len = strwidth(gettext("NAME"));
|
||||||
max_backend_len = strlen("BACK-END");
|
max_backend_len = strwidth(gettext("BACK-END"));
|
||||||
max_key_available_len = strlen("KEYSTATUS");
|
max_key_available_len = strwidth(gettext("KEYSTATUS"));
|
||||||
max_coherent_len = strlen("COHERENT");
|
|
||||||
separator = " ";
|
separator = " ";
|
||||||
|
|
||||||
for(auto cur = lines; cur != lines + lines_len; ++cur)
|
for(auto cur = lines; cur != lines + lines_len; ++cur)
|
||||||
if(cur->included(print_nontzpfms, backend_restrixion, key_loadedness_restrixion)) {
|
if(cur->included(print_nontzpfms, backend_restrixion, key_loadedness_restrixion)) {
|
||||||
max_name_len = std::max(max_name_len, strlen(cur->name));
|
max_name_len = std::max(max_name_len, strlen(cur->name));
|
||||||
max_backend_len = std::max(max_backend_len, strlen(cur->backend_display()));
|
max_backend_len = std::max(max_backend_len, strlen(cur->backend_display()));
|
||||||
max_key_available_len = std::max(max_key_available_len, strlen(output_line::key_available_display[cur->key_available]));
|
max_key_available_len = std::max(max_key_available_len, static_cast<size_t>(output_line::key_available_display_width[cur->key_available]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto println = [&](auto name, auto backend, auto key_available, auto coherent) {
|
auto println = [&](auto name, auto backend, auto key_available, auto coherent) {
|
||||||
printf("%-*s%s%-*s%s%-*s%s%-*s\n", //
|
printf("%-*s%s%-*s%s%-*s%s%s\n", //
|
||||||
static_cast<int>(max_name_len), name, separator, //
|
static_cast<int>(max_name_len), name, separator, //
|
||||||
static_cast<int>(max_backend_len), backend, separator, //
|
static_cast<int>(max_backend_len), backend, separator, //
|
||||||
static_cast<int>(max_key_available_len), key_available, separator, //
|
static_cast<int>(max_key_available_len), key_available, separator, //
|
||||||
static_cast<int>(max_coherent_len), coherent);
|
coherent);
|
||||||
};
|
};
|
||||||
if(human)
|
if(human)
|
||||||
println("NAME", "BACK-END", "KEYSTATUS", "COHERENT");
|
println(gettext("NAME"), gettext("BACK-END"), gettext("KEYSTATUS"), gettext("COHERENT"));
|
||||||
for(auto cur = lines; cur != lines + lines_len; ++cur)
|
for(auto cur = lines; cur != lines + lines_len; ++cur)
|
||||||
if(cur->included(print_nontzpfms, backend_restrixion, key_loadedness_restrixion))
|
if(cur->included(print_nontzpfms, backend_restrixion, key_loadedness_restrixion))
|
||||||
println(cur->name, cur->backend_display(), output_line::key_available_display[cur->key_available], output_line::coherent_display[cur->coherent]);
|
println(cur->name, cur->backend_display(), output_line::key_available_display[cur->key_available], output_line::coherent_display[cur->coherent]);
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
|
||||||
#include <libzfs.h>
|
#include <algorithm>
|
||||||
// #include <sys/zio_crypt.h>
|
|
||||||
#define WRAPPING_KEY_LEN 32
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "../fd.hpp"
|
#include "../fd.hpp"
|
||||||
@ -23,8 +20,20 @@
|
|||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main(int argc, char ** argv) {
|
||||||
const char * backup{};
|
const char * backup{};
|
||||||
|
uint32_t * pcrs{};
|
||||||
|
size_t pcrs_len{};
|
||||||
return do_main(
|
return do_main(
|
||||||
argc, argv, "b:", "[-b backup-file]", [&](auto) { backup = optarg; },
|
argc, argv, "b:P:", gettext_noop("[-b backup-file] [-P PCR[,PCR]…]"),
|
||||||
|
[&](auto o) {
|
||||||
|
switch(o) {
|
||||||
|
case 'b':
|
||||||
|
return backup = optarg, 0;
|
||||||
|
case 'P':
|
||||||
|
return tpm1x_parse_pcrs(optarg, pcrs, pcrs_len);
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
},
|
||||||
[&](auto dataset) {
|
[&](auto dataset) {
|
||||||
REQUIRE_KEY_LOADED(dataset);
|
REQUIRE_KEY_LOADED(dataset);
|
||||||
|
|
||||||
@ -37,10 +46,45 @@ int main(int argc, char ** argv) {
|
|||||||
TRY_TPM1X("extract TPM from context", Tspi_Context_GetTpmObject(ctx, &tpm_h));
|
TRY_TPM1X("extract TPM from context", Tspi_Context_GetTpmObject(ctx, &tpm_h));
|
||||||
|
|
||||||
|
|
||||||
uint8_t * wrap_key{};
|
/// Do it early because it's a cmdline argument and to not ask for password if it fails
|
||||||
TRY_TPM1X("get random data from TPM", Tspi_TPM_GetRandom(tpm_h, WRAPPING_KEY_LEN, &wrap_key));
|
TSS_HOBJECT bound_pcrs{};
|
||||||
|
quickscope_wrapper bound_pcrs_deleter{[&] {
|
||||||
|
if(bound_pcrs)
|
||||||
|
Tspi_Context_CloseObject(ctx, bound_pcrs);
|
||||||
|
}};
|
||||||
|
if(pcrs_len) {
|
||||||
|
auto has_big = std::find_if(pcrs, pcrs + pcrs_len, [](auto p) { return p > 15; }) != pcrs + pcrs_len;
|
||||||
|
|
||||||
|
TRY_TPM1X("create PCR list",
|
||||||
|
Tspi_Context_CreateObject(ctx, TSS_OBJECT_TYPE_PCRS, has_big ? TSS_PCRS_STRUCT_INFO_LONG : TSS_PCRS_STRUCT_DEFAULT, &bound_pcrs));
|
||||||
|
|
||||||
|
for(size_t i = 0; i < pcrs_len; i++) {
|
||||||
|
char buf[15 + 10 + 1]; // 4294967296
|
||||||
|
snprintf(buf, sizeof(buf), "read PCR %" PRIu32 "", pcrs[i]);
|
||||||
|
|
||||||
|
BYTE * val{};
|
||||||
|
uint32_t val_len{};
|
||||||
|
TRY_TPM1X(buf, Tspi_TPM_PcrRead(tpm_h, pcrs[i], &val_len, &val));
|
||||||
|
quickscope_wrapper bound_pcrs_deleter{[&] { Tspi_Context_FreeMemory(ctx, val); }};
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "save PCR %" PRIu32 " value", pcrs[i]);
|
||||||
|
TRY_TPM1X(buf, Tspi_PcrComposite_SetPcrValue(bound_pcrs, pcrs[i], val_len, val));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(has_big)
|
||||||
|
TRY_TPM1X("set PCR locality", Tspi_PcrComposite_SetPcrLocality(bound_pcrs, TSS_LOCALITY_ALL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t wrap_key[WRAPPING_KEY_LEN];
|
||||||
|
{
|
||||||
|
BYTE * rand{};
|
||||||
|
TRY_TPM1X("get random data from TPM", Tspi_TPM_GetRandom(tpm_h, sizeof(wrap_key), &rand));
|
||||||
|
memcpy(wrap_key, rand, sizeof(wrap_key));
|
||||||
|
Tspi_Context_FreeMemory(tpm_h, rand);
|
||||||
|
}
|
||||||
if(backup)
|
if(backup)
|
||||||
TRY_MAIN(write_exact(backup, wrap_key, WRAPPING_KEY_LEN, 0400));
|
TRY_MAIN(write_exact(backup, wrap_key, sizeof(wrap_key), 0400));
|
||||||
|
|
||||||
|
|
||||||
TSS_HOBJECT parent_key{};
|
TSS_HOBJECT parent_key{};
|
||||||
@ -60,9 +104,13 @@ int main(int argc, char ** argv) {
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
char what_for[ZFS_MAX_DATASET_NAME_LEN + 512 + 1];
|
||||||
|
// %s=dataset, then TPM1.X. noun for "Enter passphrase for" prompt
|
||||||
|
snprintf(what_for, sizeof(what_for), gettext("%s %s wrapping key (or empty for none)"), zfs_get_name(dataset), THIS_BACKEND);
|
||||||
|
|
||||||
uint8_t * parent_key_passphrase{};
|
uint8_t * parent_key_passphrase{};
|
||||||
size_t parent_key_passphrase_len{};
|
size_t parent_key_passphrase_len{};
|
||||||
TRY_MAIN(read_new_passphrase("wrapping key (or empty for none)", parent_key_passphrase, parent_key_passphrase_len));
|
TRY_MAIN(read_new_passphrase(what_for, parent_key_passphrase, parent_key_passphrase_len));
|
||||||
quickscope_wrapper parent_key_passphrase_deleter{[&] { free(parent_key_passphrase); }};
|
quickscope_wrapper parent_key_passphrase_deleter{[&] { free(parent_key_passphrase); }};
|
||||||
|
|
||||||
if(parent_key_passphrase_len)
|
if(parent_key_passphrase_len)
|
||||||
@ -88,12 +136,8 @@ int main(int argc, char ** argv) {
|
|||||||
Tspi_Context_CloseObject(ctx, sealed_object);
|
Tspi_Context_CloseObject(ctx, sealed_object);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
// This would need to replace the 0 below to handle PCRs
|
|
||||||
// TSS_HOBJECT bound_pcrs{}; // See tpm_sealdata.c from src:tpm-tools for more on flags here
|
|
||||||
// TRY_TPM1X("create PCR list", Tspi_Context_CreateObject(ctx, TSS_OBJECT_TYPE_PCRS, 0, &bound_pcrs));
|
|
||||||
// quickscope_wrapper bound_pcrs_deleter{[&] { Tspi_Context_CloseObject(ctx, bound_pcrs); }};
|
|
||||||
|
|
||||||
TRY_TPM1X("seal wrapping key data", Tspi_Data_Seal(sealed_object, parent_key, WRAPPING_KEY_LEN, wrap_key, 0));
|
TRY_TPM1X("seal wrapping key data", Tspi_Data_Seal(sealed_object, parent_key, sizeof(wrap_key), wrap_key, bound_pcrs));
|
||||||
|
|
||||||
|
|
||||||
uint8_t * parent_key_blob{};
|
uint8_t * parent_key_blob{};
|
||||||
@ -115,10 +159,10 @@ int main(int argc, char ** argv) {
|
|||||||
{
|
{
|
||||||
auto cur = handle;
|
auto cur = handle;
|
||||||
for(auto i = 0u; i < parent_key_blob_len; ++i, cur += 2)
|
for(auto i = 0u; i < parent_key_blob_len; ++i, cur += 2)
|
||||||
sprintf(cur, "%02X", parent_key_blob[i]);
|
sprintf(cur, "%02hhX", parent_key_blob[i]);
|
||||||
*cur++ = ':';
|
*cur++ = ':';
|
||||||
for(auto i = 0u; i < sealed_object_blob_len; ++i, cur += 2)
|
for(auto i = 0u; i < sealed_object_blob_len; ++i, cur += 2)
|
||||||
sprintf(cur, "%02X", sealed_object_blob[i]);
|
sprintf(cur, "%02hhX", sealed_object_blob[i]);
|
||||||
*cur++ = '\0';
|
*cur++ = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,14 +2,11 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "../main_clear.hpp"
|
#include "../main_clear.hpp"
|
||||||
#include "../tpm1x.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
#define THIS_BACKEND "TPM1.X"
|
#define THIS_BACKEND "TPM1.X"
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main(int argc, char ** argv) {
|
||||||
tpm1x_handle handle{}; // Not like we use this, but for symmetry with the other -clear-keys
|
return do_clear_main(argc, argv, THIS_BACKEND, [](auto, auto) { return 0; }, [] { return 0; });
|
||||||
return do_clear_main(
|
|
||||||
argc, argv, THIS_BACKEND, [&](auto dataset, auto handle_s) { return parse_key_props(dataset, THIS_BACKEND, handle_s); }, [&] { return 0; });
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
|
||||||
#include <libzfs.h>
|
|
||||||
// #include <sys/zio_crypt.h>
|
|
||||||
#define WRAPPING_KEY_LEN 32
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -35,7 +31,7 @@ int main(int argc, char ** argv) {
|
|||||||
uint8_t wrap_key[WRAPPING_KEY_LEN]{};
|
uint8_t wrap_key[WRAPPING_KEY_LEN]{};
|
||||||
TRY_MAIN(with_tpm1x_session([&](auto ctx, auto srk, auto srk_policy) {
|
TRY_MAIN(with_tpm1x_session([&](auto ctx, auto srk, auto srk_policy) {
|
||||||
TSS_HOBJECT parent_key{};
|
TSS_HOBJECT parent_key{};
|
||||||
TRY_MAIN(try_policy_or_passphrase("load sealant key from blob (did you take ownership?)", "SRK", srk_policy, [&] {
|
TRY_MAIN(try_policy_or_passphrase("load sealant key from blob (did you take ownership?)", "TPM1.X SRK", srk_policy, [&] {
|
||||||
return Tspi_Context_LoadKeyByBlob(ctx, srk, handle.parent_key_blob_len, handle.parent_key_blob, &parent_key);
|
return Tspi_Context_LoadKeyByBlob(ctx, srk, handle.parent_key_blob_len, handle.parent_key_blob, &parent_key);
|
||||||
}));
|
}));
|
||||||
quickscope_wrapper parent_key_deleter{[&] { Tspi_Key_UnloadKey(parent_key); }};
|
quickscope_wrapper parent_key_deleter{[&] { Tspi_Key_UnloadKey(parent_key); }};
|
||||||
@ -58,15 +54,19 @@ int main(int argc, char ** argv) {
|
|||||||
TRY_TPM1X("load sealed object from blob", Tspi_SetAttribData(sealed_object, TSS_TSPATTRIB_ENCDATA_BLOB, TSS_TSPATTRIB_ENCDATABLOB_BLOB,
|
TRY_TPM1X("load sealed object from blob", Tspi_SetAttribData(sealed_object, TSS_TSPATTRIB_ENCDATA_BLOB, TSS_TSPATTRIB_ENCDATABLOB_BLOB,
|
||||||
handle.sealed_object_blob_len, handle.sealed_object_blob));
|
handle.sealed_object_blob_len, handle.sealed_object_blob));
|
||||||
|
|
||||||
|
char what_for[ZFS_MAX_DATASET_NAME_LEN + 512 + 1];
|
||||||
|
// %s=dataset name, then TPM1.x. noun for "Enter passphrase for" prompt
|
||||||
|
snprintf(what_for, sizeof(what_for), gettext("%s %s wrapping key"), zfs_get_name(dataset), THIS_BACKEND);
|
||||||
|
|
||||||
uint8_t * loaded_wrap_key{};
|
uint8_t * loaded_wrap_key{};
|
||||||
uint32_t loaded_wrap_key_len{};
|
uint32_t loaded_wrap_key_len{};
|
||||||
TRY_MAIN(try_policy_or_passphrase("unseal wrapping key", "wrapping key", parent_key_policy,
|
TRY_MAIN(try_policy_or_passphrase("unseal wrapping key", what_for, parent_key_policy,
|
||||||
[&] { return Tspi_Data_Unseal(sealed_object, parent_key, &loaded_wrap_key_len, &loaded_wrap_key); }));
|
[&] { return Tspi_Data_Unseal(sealed_object, parent_key, &loaded_wrap_key_len, &loaded_wrap_key); }));
|
||||||
if(loaded_wrap_key_len != sizeof(wrap_key)) {
|
if(loaded_wrap_key_len != sizeof(wrap_key)) {
|
||||||
fprintf(stderr, "Wrong sealed data length (%u != %zu):", loaded_wrap_key_len, sizeof(wrap_key));
|
// 0A1234... follows
|
||||||
|
fprintf(stderr, gettext("Wrong sealed data length (%u != %zu): "), loaded_wrap_key_len, sizeof(wrap_key));
|
||||||
for(auto i = 0u; i < loaded_wrap_key_len; ++i)
|
for(auto i = 0u; i < loaded_wrap_key_len; ++i)
|
||||||
fprintf(stderr, "%02X", loaded_wrap_key[i]);
|
fprintf(stderr, "%02hhX", loaded_wrap_key[i]);
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
return __LINE__;
|
return __LINE__;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
|
||||||
#include <libzfs.h>
|
|
||||||
// #include <sys/zio_crypt.h>
|
|
||||||
#define WRAPPING_KEY_LEN 32
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "../fd.hpp"
|
#include "../fd.hpp"
|
||||||
@ -19,13 +15,28 @@
|
|||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main(int argc, char ** argv) {
|
||||||
const char * backup{};
|
const char * backup{};
|
||||||
|
TPML_PCR_SELECTION pcrs{};
|
||||||
|
bool allow_PCR_or_pass{};
|
||||||
return do_main(
|
return do_main(
|
||||||
argc, argv, "b:", "[-b backup-file]", [&](auto) { backup = optarg; },
|
argc, argv, "b:P:A", gettext_noop("[-b backup-file] [-P algorithm:PCR[,PCR]…[+algorithm:PCR[,PCR]…]… [-A]]"),
|
||||||
|
[&](auto o) {
|
||||||
|
switch(o) {
|
||||||
|
case 'b':
|
||||||
|
return backup = optarg, 0;
|
||||||
|
case 'P':
|
||||||
|
return tpm2_parse_pcrs(optarg, pcrs);
|
||||||
|
case 'A':
|
||||||
|
return allow_PCR_or_pass = true, 0;
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
},
|
||||||
[&](auto dataset) {
|
[&](auto dataset) {
|
||||||
REQUIRE_KEY_LOADED(dataset);
|
REQUIRE_KEY_LOADED(dataset);
|
||||||
|
|
||||||
|
|
||||||
// https://software.intel.com/content/www/us/en/develop/articles/code-sample-protecting-secret-data-and-keys-using-intel-platform-trust-technology.html
|
// https://software.intel.com/content/www/us/en/develop/articles/code-sample-protecting-secret-data-and-keys-using-intel-platform-trust-technology.html
|
||||||
|
// https://tpm2-software.github.io/2020/04/13/Disk-Encryption.html#pcr-policy-authentication---access-control-of-sealed-pass-phrase-on-tpm2-with-pcr-sealing
|
||||||
// tssstartup
|
// tssstartup
|
||||||
// tpm2_createprimary -Q --hierarchy=o --key-context=prim.ctx
|
// tpm2_createprimary -Q --hierarchy=o --key-context=prim.ctx
|
||||||
// cat /tmp/sk | tpm2_create --hash-algorithm=sha256 --public=seal.pub --private=seal.priv --sealing-input=- --parent-context=prim.ctx
|
// cat /tmp/sk | tpm2_create --hash-algorithm=sha256 --public=seal.pub --private=seal.priv --sealing-input=- --parent-context=prim.ctx
|
||||||
@ -35,44 +46,57 @@ int main(int argc, char ** argv) {
|
|||||||
// persistent-handle: 0x81000001
|
// persistent-handle: 0x81000001
|
||||||
//
|
//
|
||||||
// tpm2_unseal -Q --object-context=0x81000000
|
// tpm2_unseal -Q --object-context=0x81000000
|
||||||
|
//
|
||||||
|
// For PCRs:
|
||||||
|
// tpm2_startauthsession --session=session.ctx
|
||||||
|
// tpm2_policypcr -S session.ctx -l 'sha512:7+sha256:10' -L 5-10.policy3
|
||||||
|
// tpm2_flushcontext session.ctx; rm session.ctx
|
||||||
|
// + tpm2_create{,primary} gain -l 'sha512:7+sha256:10', tpm2_create gains -L 5-10.policy3
|
||||||
|
//
|
||||||
|
// tpm2_unseal -p pcr:'sha512:7+sha256:10' --object-context=0x81000000
|
||||||
|
// or, longhand:
|
||||||
|
// tpm2_startauthsession --policy-session --session=session3.ctx
|
||||||
|
// tpm2_policypcr --session=session3.ctx --pcr-list='sha512:7+sha256:10'
|
||||||
|
// tpm2_unseal -p session:session3.ctx --object-context=0x81000000
|
||||||
|
// tpm2_flushcontext session3.ctx; rm session3.ctx
|
||||||
|
|
||||||
return with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
|
return with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
|
||||||
TRY_MAIN(verify_backend(dataset, THIS_BACKEND, [&](auto previous_handle_s) {
|
TRY_MAIN(verify_backend(dataset, THIS_BACKEND, [&](auto previous_handle_s) {
|
||||||
TPMI_DH_PERSISTENT previous_handle{};
|
TPMI_DH_PERSISTENT previous_handle{};
|
||||||
if(parse_int(previous_handle_s, previous_handle))
|
if(tpm2_parse_prop(zfs_get_name(dataset), previous_handle_s, previous_handle, nullptr))
|
||||||
fprintf(stderr, "Couldn't parse previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c %s\" or equivalent!\n",
|
fprintf(stderr,
|
||||||
|
gettext("Couldn't parse previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c %s\" or equivalent!\n"),
|
||||||
zfs_get_name(dataset), previous_handle_s);
|
zfs_get_name(dataset), previous_handle_s);
|
||||||
else {
|
else {
|
||||||
if(tpm2_free_persistent(tpm2_ctx, tpm2_session, previous_handle))
|
if(tpm2_free_persistent(tpm2_ctx, tpm2_session, previous_handle))
|
||||||
fprintf(stderr, "Couldn't free previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n",
|
fprintf(
|
||||||
zfs_get_name(dataset), previous_handle);
|
stderr,
|
||||||
|
gettext("Couldn't free previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n"),
|
||||||
|
zfs_get_name(dataset), previous_handle);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
uint8_t wrap_key[WRAPPING_KEY_LEN];
|
uint8_t wrap_key[WRAPPING_KEY_LEN];
|
||||||
TPMI_DH_PERSISTENT persistent_handle{};
|
|
||||||
|
|
||||||
TRY_MAIN(tpm2_generate_rand(tpm2_ctx, wrap_key, sizeof(wrap_key)));
|
TRY_MAIN(tpm2_generate_rand(tpm2_ctx, wrap_key, sizeof(wrap_key)));
|
||||||
if(backup)
|
if(backup)
|
||||||
TRY_MAIN(write_exact(backup, wrap_key, sizeof(wrap_key), 0400));
|
TRY_MAIN(write_exact(backup, wrap_key, sizeof(wrap_key), 0400));
|
||||||
|
|
||||||
TRY_MAIN(tpm2_seal(tpm2_ctx, tpm2_session, persistent_handle, tpm2_creation_metadata(zfs_get_name(dataset)), wrap_key, sizeof(wrap_key)));
|
TPMI_DH_PERSISTENT persistent_handle{};
|
||||||
|
TRY_MAIN(tpm2_seal(zfs_get_name(dataset), tpm2_ctx, tpm2_session, persistent_handle, pcrs, allow_PCR_or_pass, wrap_key, sizeof(wrap_key)));
|
||||||
bool ok = false; // Try to free the persistent handle if we're unsuccessful in actually using it later on
|
bool ok = false; // Try to free the persistent handle if we're unsuccessful in actually using it later on
|
||||||
quickscope_wrapper persistent_clearer{[&] {
|
quickscope_wrapper persistent_clearer{[&] {
|
||||||
if(!ok && tpm2_free_persistent(tpm2_ctx, tpm2_session, persistent_handle))
|
if(!ok && tpm2_free_persistent(tpm2_ctx, tpm2_session, persistent_handle))
|
||||||
fprintf(stderr, "Couldn't free persistent handle. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n", persistent_handle);
|
fprintf(stderr, gettext("Couldn't free persistent handle. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n"),
|
||||||
|
persistent_handle);
|
||||||
if(!ok)
|
if(!ok)
|
||||||
clear_key_props(dataset);
|
clear_key_props(dataset);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
{
|
{
|
||||||
char persistent_handle_s[2 + sizeof(persistent_handle) * 2 + 1];
|
char * prop{};
|
||||||
if(auto written = snprintf(persistent_handle_s, sizeof(persistent_handle_s), "0x%X", persistent_handle);
|
TRY_MAIN(tpm2_unparse_prop(persistent_handle, pcrs, &prop));
|
||||||
written < 0 || written >= static_cast<int>(sizeof(persistent_handle_s))) {
|
quickscope_wrapper prop_deleter{[&] { free(prop); }};
|
||||||
fprintf(stderr, "Truncated persistent_handle name? %d/%zu\n", written, sizeof(persistent_handle_s));
|
TRY_MAIN(set_key_props(dataset, THIS_BACKEND, prop));
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
TRY_MAIN(set_key_props(dataset, THIS_BACKEND, persistent_handle_s));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TRY_MAIN(change_key(dataset, wrap_key));
|
TRY_MAIN(change_key(dataset, wrap_key));
|
||||||
@ -80,5 +104,10 @@ int main(int argc, char ** argv) {
|
|||||||
ok = true;
|
ok = true;
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
[&]() {
|
||||||
|
if(allow_PCR_or_pass && !pcrs.count)
|
||||||
|
return __LINE__;
|
||||||
|
return 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,6 @@ int main(int argc, char ** argv) {
|
|||||||
TPMI_DH_PERSISTENT persistent_handle{};
|
TPMI_DH_PERSISTENT persistent_handle{};
|
||||||
return do_clear_main(
|
return do_clear_main(
|
||||||
argc, argv, THIS_BACKEND,
|
argc, argv, THIS_BACKEND,
|
||||||
[&](auto dataset, auto persistent_handle_s) { return tpm2_parse_handle(zfs_get_name(dataset), persistent_handle_s, persistent_handle); },
|
[&](auto dataset, auto persistent_handle_s) { return tpm2_parse_prop(zfs_get_name(dataset), persistent_handle_s, persistent_handle, nullptr); },
|
||||||
[&] { return with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) { return tpm2_free_persistent(tpm2_ctx, tpm2_session, persistent_handle); }); });
|
[&] { return with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) { return tpm2_free_persistent(tpm2_ctx, tpm2_session, persistent_handle); }); });
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
|
||||||
#include <libzfs.h>
|
|
||||||
// #include <sys/zio_crypt.h>
|
|
||||||
#define WRAPPING_KEY_LEN 32
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "../fd.hpp"
|
#include "../fd.hpp"
|
||||||
@ -25,12 +21,13 @@ int main(int argc, char ** argv) {
|
|||||||
TRY_MAIN(parse_key_props(dataset, THIS_BACKEND, handle_s));
|
TRY_MAIN(parse_key_props(dataset, THIS_BACKEND, handle_s));
|
||||||
|
|
||||||
TPMI_DH_PERSISTENT handle{};
|
TPMI_DH_PERSISTENT handle{};
|
||||||
TRY_MAIN(tpm2_parse_handle(zfs_get_name(dataset), handle_s, handle));
|
TPML_PCR_SELECTION pcrs{};
|
||||||
|
TRY_MAIN(tpm2_parse_prop(zfs_get_name(dataset), handle_s, handle, &pcrs));
|
||||||
|
|
||||||
|
|
||||||
uint8_t wrap_key[WRAPPING_KEY_LEN];
|
uint8_t wrap_key[WRAPPING_KEY_LEN];
|
||||||
TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
|
TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
|
||||||
TRY_MAIN(tpm2_unseal(tpm2_ctx, tpm2_session, handle, wrap_key, sizeof(wrap_key)));
|
TRY_MAIN(tpm2_unseal(zfs_get_name(dataset), tpm2_ctx, tpm2_session, handle, pcrs, wrap_key, sizeof(wrap_key)));
|
||||||
return 0;
|
return 0;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
({ \
|
({ \
|
||||||
auto _try_ret = (__VA_ARGS__); \
|
auto _try_ret = (__VA_ARGS__); \
|
||||||
if(cond_pre _try_ret cond_post) { \
|
if(cond_pre _try_ret cond_post) { \
|
||||||
if constexpr(what != nullptr) \
|
if(what != nullptr) \
|
||||||
fprintf(stderr, "Couldn't %s: %s\n", static_cast<const char *>(what), strerr(err_src)); \
|
fprintf(stderr, "Couldn't %s: %s\n", static_cast<const char *>(what), strerr(err_src)); \
|
||||||
return err_ret; \
|
return err_ret; \
|
||||||
} \
|
} \
|
||||||
@ -30,4 +30,4 @@ struct quickscope_wrapper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <class F>
|
template <class F>
|
||||||
quickscope_wrapper(F)->quickscope_wrapper<F>;
|
quickscope_wrapper(F) -> quickscope_wrapper<F>;
|
||||||
|
184
src/fd.cpp
184
src/fd.cpp
@ -6,10 +6,10 @@
|
|||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
/// Matches libzfs
|
/// Matches libzfs
|
||||||
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
int filled_fd(int & fd, const void * with, size_t with_len) {
|
int filled_fd(int & fd, const void * with, size_t with_len) {
|
||||||
int pipes[2];
|
int pipes[2];
|
||||||
TRY("create buffer pipe", pipe(pipes));
|
TRY("create buffer pipe", pipe2(pipes, O_CLOEXEC));
|
||||||
quickscope_wrapper pipes_w_deleter{[=] { close(pipes[1]); }};
|
quickscope_wrapper pipes_w_deleter{[=] { close(pipes[1]); }};
|
||||||
fd = pipes[0];
|
fd = pipes[0];
|
||||||
|
|
||||||
@ -33,26 +33,9 @@ int filled_fd(int & fd, const void * with, size_t with_len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int read_exact(const char * path, void * data, size_t len) {
|
|
||||||
auto infd = TRY("open input file", open(path, O_RDONLY));
|
|
||||||
quickscope_wrapper infd_deleter{[=] { close(infd); }};
|
|
||||||
|
|
||||||
while(len)
|
|
||||||
if(const auto rd = TRY("read input file", read(infd, data, len))) {
|
|
||||||
len -= rd;
|
|
||||||
data = static_cast<char *>(data) + rd;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Couldn't read %zu bytes from input file: too short\n", len);
|
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int write_exact(const char * path, const void * data, size_t len, mode_t mode) {
|
int write_exact(const char * path, const void * data, size_t len, mode_t mode) {
|
||||||
auto outfd = TRY("create output file", open(path, O_WRONLY | O_CREAT | O_EXCL, mode));
|
auto outfd = TRY("create output file", open(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
|
||||||
quickscope_wrapper infd_deleter{[=] { close(outfd); }};
|
quickscope_wrapper outfd_deleter{[=] { close(outfd); }};
|
||||||
|
|
||||||
while(len) {
|
while(len) {
|
||||||
const auto rd = TRY("write to output file", write(outfd, data, len));
|
const auto rd = TRY("write to output file", write(outfd, data, len));
|
||||||
@ -64,6 +47,79 @@ int write_exact(const char * path, const void * data, size_t len, mode_t mode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define TRY_HELPER(what, ...) TRY_GENERIC(what, , == -1, errno, -1, strerror, __VA_ARGS__)
|
||||||
|
|
||||||
|
/// TRY_MAIN rules, plus -1 for ENOENT
|
||||||
|
static int get_key_material_helper(const char * helper, const char * whom, bool again, bool newkey, uint8_t *& buf, size_t & len_out) {
|
||||||
|
int pipes[2];
|
||||||
|
TRY("create IPC pipe", pipe2(pipes, O_CLOEXEC));
|
||||||
|
quickscope_wrapper pipes_r_deleter{[=] { close(pipes[0]); }};
|
||||||
|
|
||||||
|
if(auto pid = TRY_HELPER("create child", fork()); pid == 0) { // child
|
||||||
|
dup2(pipes[1], 1);
|
||||||
|
|
||||||
|
const char * fmt;
|
||||||
|
if(!newkey && !again)
|
||||||
|
fmt = gettext("Passphrase for %s");
|
||||||
|
else if(!newkey && again)
|
||||||
|
fmt = gettext("Passphrase for %s (again)");
|
||||||
|
else if(newkey && !again)
|
||||||
|
fmt = gettext("New passphrase for %s");
|
||||||
|
else // newkey && again
|
||||||
|
fmt = gettext("New passphrase for %s (again)");
|
||||||
|
char * msg;
|
||||||
|
if(asprintf(&msg, fmt, whom) == -1)
|
||||||
|
msg = const_cast<char *>(whom);
|
||||||
|
execl("/bin/sh", "sh", "-c", helper, helper, msg, whom, newkey ? "new" : "", again ? "again" : "", nullptr);
|
||||||
|
|
||||||
|
int exec_err = errno;
|
||||||
|
fprintf(stderr, "exec(/bin/sh): %s\n", strerror(errno));
|
||||||
|
_exit(exec_err == ENOENT ? 127 : 126);
|
||||||
|
}
|
||||||
|
close(pipes[1]);
|
||||||
|
|
||||||
|
|
||||||
|
buf = nullptr;
|
||||||
|
len_out = 0;
|
||||||
|
for(size_t len_cap = 0;;) { // 99% of cases this is 1 allocation and 1 read
|
||||||
|
if(len_out == len_cap) {
|
||||||
|
len_cap += 120;
|
||||||
|
buf = TRY_PTR("allocate passphrase", static_cast<uint8_t *>(realloc(buf, len_cap)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t rd;
|
||||||
|
while((rd = read(pipes[0], buf + len_out, len_cap - len_out)) == -1 && errno == EINTR)
|
||||||
|
;
|
||||||
|
TRY("read passphrase from helper", rd);
|
||||||
|
if(rd == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
len_out += rd;
|
||||||
|
}
|
||||||
|
if(len_out && buf[len_out - 1] == '\n')
|
||||||
|
--len_out;
|
||||||
|
|
||||||
|
|
||||||
|
int err, ret;
|
||||||
|
while((ret = wait(&err)) == -1 && errno == EINTR)
|
||||||
|
;
|
||||||
|
TRY("wait for helper", ret);
|
||||||
|
|
||||||
|
if(WIFEXITED(err))
|
||||||
|
switch(WEXITSTATUS(err)) {
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
|
case 127: // ENOENT, error already written by shell or child
|
||||||
|
return -1;
|
||||||
|
default:
|
||||||
|
// exit status
|
||||||
|
return fprintf(stderr, gettext("Helper '%s' failed with %d.\n"), helper, WEXITSTATUS(err)), __LINE__;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return fprintf(stderr, gettext("Helper '%s' died to signal %d: %s.\n"), helper, WTERMSIG(err), strsignal(WTERMSIG(err))), __LINE__;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Adapted from src:zfs's lib/libzfs/libzfs_crypto.c#get_key_material_raw()
|
/// Adapted from src:zfs's lib/libzfs/libzfs_crypto.c#get_key_material_raw()
|
||||||
static int get_key_material_raw(const char * whom, bool again, bool newkey, uint8_t *& buf, size_t & len_out) {
|
static int get_key_material_raw(const char * whom, bool again, bool newkey, uint8_t *& buf, size_t & len_out) {
|
||||||
static int caught_interrupt;
|
static int caught_interrupt;
|
||||||
@ -77,18 +133,33 @@ static int get_key_material_raw(const char * whom, bool again, bool newkey, uint
|
|||||||
if(from_tty) {
|
if(from_tty) {
|
||||||
// Handle SIGINT and ignore SIGSTP.
|
// Handle SIGINT and ignore SIGSTP.
|
||||||
// This is necessary to restore the state of the terminal.
|
// This is necessary to restore the state of the terminal.
|
||||||
struct sigaction act {};
|
struct sigaction act{};
|
||||||
sigemptyset(&act.sa_mask);
|
sigemptyset(&act.sa_mask);
|
||||||
|
|
||||||
caught_interrupt = 0;
|
caught_interrupt = 0;
|
||||||
act.sa_handler = [](auto sig) { caught_interrupt = sig; };
|
act.sa_handler = [](auto sig) {
|
||||||
|
caught_interrupt = sig;
|
||||||
|
|
||||||
|
struct termios cur;
|
||||||
|
tcgetattr(0, &cur);
|
||||||
|
fprintf(stderr, "%s^%c\n", (cur.c_cc[VINTR] & 0x80) ? "M-" : "", ((cur.c_cc[VINTR] & 0x7F) == 0x7F) ? '?' : ('@' + (cur.c_cc[VINTR] & 0x7F)));
|
||||||
|
};
|
||||||
sigaction(SIGINT, &act, &osigint);
|
sigaction(SIGINT, &act, &osigint);
|
||||||
|
|
||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
sigaction(SIGTSTP, &act, &osigtstp);
|
sigaction(SIGTSTP, &act, &osigtstp);
|
||||||
|
|
||||||
// Prompt for the key
|
// Prompt for the key
|
||||||
printf("%s %spassphrase for %s: ", again ? "Re-enter" : "Enter", newkey ? "new " : "", whom);
|
const char * fmt;
|
||||||
|
if(!again && !newkey)
|
||||||
|
fmt = gettext("Enter passphrase for %s: ");
|
||||||
|
else if(!again && newkey)
|
||||||
|
fmt = gettext("Enter passphrase for new %s: ");
|
||||||
|
else if(again && !newkey)
|
||||||
|
fmt = gettext("Re-enter passphrase for %s: ");
|
||||||
|
else // again && newkey
|
||||||
|
fmt = gettext("Re-enter passphrase for new %s: ");
|
||||||
|
printf(fmt, whom);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
// Disable the terminal echo for key input
|
// Disable the terminal echo for key input
|
||||||
@ -107,7 +178,7 @@ static int get_key_material_raw(const char * whom, bool again, bool newkey, uint
|
|||||||
|
|
||||||
// If we caught a signal, re-throw it now
|
// If we caught a signal, re-throw it now
|
||||||
if(caught_interrupt != 0)
|
if(caught_interrupt != 0)
|
||||||
kill(getpid(), caught_interrupt);
|
raise(caught_interrupt);
|
||||||
|
|
||||||
// Print the newline that was not echoed
|
// Print the newline that was not echoed
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
@ -121,7 +192,7 @@ static int get_key_material_raw(const char * whom, bool again, bool newkey, uint
|
|||||||
auto bytes = getline((char **)&buf, &buflen, stdin);
|
auto bytes = getline((char **)&buf, &buflen, stdin);
|
||||||
switch(bytes) {
|
switch(bytes) {
|
||||||
case -1:
|
case -1:
|
||||||
if(errno != 0)
|
if(errno)
|
||||||
TRY("read in passphrase", bytes);
|
TRY("read in passphrase", bytes);
|
||||||
else // EOF
|
else // EOF
|
||||||
bytes = 0;
|
bytes = 0;
|
||||||
@ -130,10 +201,8 @@ static int get_key_material_raw(const char * whom, bool again, bool newkey, uint
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Trim ending newline, if any
|
// Trim ending newline, if any
|
||||||
if(buf[bytes - 1] == '\n') {
|
if(buf[bytes - 1] == '\n')
|
||||||
buf[bytes - 1] = '\0';
|
buf[--bytes] = '\0';
|
||||||
--bytes;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,12 +210,32 @@ static int get_key_material_raw(const char * whom, bool again, bool newkey, uint
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef TZPFMS_PASSPHRASE_HELPER
|
||||||
|
#define TZPFMS_PASSPHRASE_HELPER
|
||||||
|
#endif
|
||||||
|
#define STRINGIFY_(...) #__VA_ARGS__
|
||||||
|
#define STRINGIFY(...) STRINGIFY_(__VA_ARGS__)
|
||||||
|
|
||||||
|
static int get_key_material_dispatch(const char * whom, bool again, bool newkey, uint8_t *& buf, size_t & len_out) {
|
||||||
|
static const char * helper{};
|
||||||
|
if(!helper)
|
||||||
|
helper = getenv("TZPFMS_PASSPHRASE_HELPER") ?: STRINGIFY(TZPFMS_PASSPHRASE_HELPER);
|
||||||
|
if(*helper) {
|
||||||
|
if(auto err = get_key_material_helper(helper, whom, again, newkey, buf, len_out); err != -1)
|
||||||
|
return err;
|
||||||
|
else
|
||||||
|
helper = "";
|
||||||
|
}
|
||||||
|
return get_key_material_raw(whom, again, newkey, buf, len_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len) {
|
int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len) {
|
||||||
TRY_MAIN(get_key_material_raw(whom, false, false, buf, len_out));
|
TRY_MAIN(get_key_material_dispatch(whom, false, false, buf, len_out));
|
||||||
if(len_out <= max_len)
|
if(len_out <= max_len)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fprintf(stderr, "Passphrase too long (max %zu)\n", max_len);
|
fprintf(stderr, gettext("Passphrase too long (max %zu)\n"), max_len);
|
||||||
free(buf);
|
free(buf);
|
||||||
buf = nullptr;
|
buf = nullptr;
|
||||||
len_out = 0;
|
len_out = 0;
|
||||||
@ -156,32 +245,25 @@ int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, s
|
|||||||
int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len) {
|
int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len) {
|
||||||
uint8_t * first_passphrase{};
|
uint8_t * first_passphrase{};
|
||||||
size_t first_passphrase_len{};
|
size_t first_passphrase_len{};
|
||||||
TRY_MAIN(get_key_material_raw(whom, false, true, first_passphrase, first_passphrase_len));
|
TRY_MAIN(get_key_material_dispatch(whom, false, true, first_passphrase, first_passphrase_len));
|
||||||
quickscope_wrapper first_passphrase_deleter{[&] { free(first_passphrase); }};
|
quickscope_wrapper first_passphrase_deleter{[&] { free(first_passphrase); }};
|
||||||
|
|
||||||
if(first_passphrase_len != 0 && first_passphrase_len < MIN_PASSPHRASE_LEN) {
|
if(first_passphrase_len != 0 && first_passphrase_len < MIN_PASSPHRASE_LEN)
|
||||||
fprintf(stderr, "Passphrase too short (min %u)\n", MIN_PASSPHRASE_LEN);
|
return fprintf(stderr, gettext("Passphrase too short (min %u)\n"), MIN_PASSPHRASE_LEN), __LINE__;
|
||||||
return __LINE__;
|
if(first_passphrase_len > max_len)
|
||||||
}
|
return fprintf(stderr, gettext("Passphrase too long (max %zu)\n"), max_len), __LINE__;
|
||||||
if(first_passphrase_len > max_len) {
|
|
||||||
fprintf(stderr, "Passphrase too long (max %zu)\n", max_len);
|
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t * second_passphrase{};
|
uint8_t * second_passphrase{};
|
||||||
size_t second_passphrase_len{};
|
size_t second_passphrase_len{};
|
||||||
TRY_MAIN(get_key_material_raw(whom, true, true, second_passphrase, second_passphrase_len));
|
TRY_MAIN(get_key_material_dispatch(whom, true, true, second_passphrase, second_passphrase_len));
|
||||||
quickscope_wrapper second_passphrase_deleter{[&] { free(second_passphrase); }};
|
quickscope_wrapper second_passphrase_deleter{[&] { free(second_passphrase); }};
|
||||||
|
|
||||||
if(second_passphrase_len != first_passphrase_len || memcmp(first_passphrase, second_passphrase, first_passphrase_len)) {
|
if(second_passphrase_len != first_passphrase_len || memcmp(first_passphrase, second_passphrase, first_passphrase_len))
|
||||||
fprintf(stderr, "Provided keys do not match.\n");
|
return fprintf(stderr, gettext("Provided passphrases do not match.\n")), __LINE__;
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(second_passphrase_len) {
|
if(second_passphrase_len)
|
||||||
buf = second_passphrase;
|
buf = std::exchange(second_passphrase, nullptr);
|
||||||
second_passphrase = nullptr;
|
else
|
||||||
} else
|
|
||||||
buf = nullptr;
|
buf = nullptr;
|
||||||
|
|
||||||
len_out = second_passphrase_len;
|
len_out = second_passphrase_len;
|
||||||
|
26
src/fd.hpp
26
src/fd.hpp
@ -4,38 +4,32 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
#include "common.hpp"
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
template <class F>
|
template <class F>
|
||||||
int with_stdin_at(int fd, F && what) {
|
int with_stdin_at(int fd, F && what) {
|
||||||
auto stdin_saved = TRY("dup() stdin", dup(0));
|
auto stdin_saved = dup(0);
|
||||||
quickscope_wrapper stdin_saved_deleter{[=] { close(stdin_saved); }};
|
|
||||||
|
|
||||||
TRY("dup2() onto stdin", dup2(fd, 0));
|
dup2(fd, 0);
|
||||||
|
clearerr(stdin);
|
||||||
|
int ret = what();
|
||||||
|
|
||||||
if(int ret = what()) {
|
dup2(stdin_saved, 0);
|
||||||
dup2(stdin_saved, 0);
|
close(stdin_saved);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
TRY("dup2() stdin back onto stdin", dup2(stdin_saved, 0));
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// with_len may not exceed pipe capacity (64k by default)
|
/// with_len may not exceed pipe capacity (64k by default)
|
||||||
extern int filled_fd(int & fd, const void * with, size_t with_len);
|
extern int filled_fd(int & fd, const void * with, size_t with_len);
|
||||||
|
|
||||||
/// Read exactly len bytes from path into data, or error
|
|
||||||
extern int read_exact(const char * path, void * data, size_t len);
|
|
||||||
|
|
||||||
/// Write exactly len bytes from data into path, or error
|
/// Write exactly len bytes from data into path, or error
|
||||||
extern int write_exact(const char * path, const void * data, size_t len, mode_t mode);
|
extern int write_exact(const char * path, const void * data, size_t len, mode_t mode);
|
||||||
|
|
||||||
/// Prompt for passphrase for whom the user knows, up to max_len bytes
|
/// Prompt for passphrase for whom the user knows, up to max_len bytes
|
||||||
extern int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len = SIZE_MAX);
|
extern int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len = SIZE_MAX);
|
||||||
|
|
||||||
/// Prompt twive for passphrase for whom the user is setting
|
/// Prompt twice for passphrase for whom the user is setting
|
||||||
extern int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len = SIZE_MAX);
|
extern int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len = SIZE_MAX);
|
||||||
|
97
src/main.hpp
97
src/main.hpp
@ -5,10 +5,13 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
#include <libintl.h>
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
#include <locale.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#define gettext_noop(s) s
|
||||||
|
|
||||||
|
|
||||||
#define TRY_PTR(what, ...) TRY_GENERIC(what, !, , errno, __LINE__, strerror, __VA_ARGS__)
|
#define TRY_PTR(what, ...) TRY_GENERIC(what, !, , errno, __LINE__, strerror, __VA_ARGS__)
|
||||||
@ -19,70 +22,80 @@
|
|||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
template <class G, class M>
|
template <class G, class M, class V = int (*)()>
|
||||||
int do_bare_main(int argc, char ** argv, const char * getoptions, const char * usage, const char * dataset_usage, G && getoptfn, M && main) {
|
static int do_bare_main(
|
||||||
const auto libz = TRY_PTR("initialise libzfs", libzfs_init());
|
int argc, char ** argv, const char * getoptions, const char * usage, const char * dataset_usage, G && getoptfn, M && main,
|
||||||
quickscope_wrapper libz_deleter{[=] { libzfs_fini(libz); }};
|
V && validate = [] { return 0; }) {
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
bindtextdomain("tzpfms", "/usr/share/locale");
|
||||||
|
// bindtextdomain("tzpfms", "out/locale");
|
||||||
|
textdomain("tzpfms");
|
||||||
|
|
||||||
|
const auto libz = TRY_PTR("initialise libzfs", libzfs_init());
|
||||||
libzfs_print_on_error(libz, B_TRUE);
|
libzfs_print_on_error(libz, B_TRUE);
|
||||||
|
|
||||||
#if __GLIBC__
|
auto gopts = reinterpret_cast<char *>(alloca(strlen(getoptions) + 2 + 1));
|
||||||
setenv("POSIXLY_CORRECT", "1", true);
|
gopts[0] = 'h', gopts[1] = 'V';
|
||||||
#endif
|
strcpy(gopts + 2, getoptions);
|
||||||
auto gopts = reinterpret_cast<char *>(TRY_PTR("allocate options string", alloca(strlen(getoptions) + 2 + 1)));
|
|
||||||
snprintf(gopts, strlen(getoptions) + 2 + 1, "%shV", getoptions);
|
|
||||||
for(int opt; (opt = getopt(argc, argv, gopts)) != -1;)
|
for(int opt; (opt = getopt(argc, argv, gopts)) != -1;)
|
||||||
switch(opt) {
|
switch(opt) {
|
||||||
case '?':
|
case '?':
|
||||||
case 'h':
|
case 'h':
|
||||||
fprintf(opt == 'h' ? stdout : stderr, "Usage: %s [-hV] %s%s%s\n", argv[0], usage, strlen(usage) ? " " : "", dataset_usage);
|
fprintf(opt == 'h' ? stdout : stderr, gettext("usage: %s [-hV] %s%s%s\n"), argv[0], *usage ? gettext(usage) : "", *usage ? " " : "",
|
||||||
|
gettext(dataset_usage));
|
||||||
return opt == 'h' ? 0 : __LINE__;
|
return opt == 'h' ? 0 : __LINE__;
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("tzpfms version %s\n", TZPFMS_VERSION);
|
puts("tzpfms " TZPFMS_VERSION);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
if constexpr(std::is_same_v<std::invoke_result_t<G, decltype(opt)>, void>)
|
if constexpr(std::is_same_v<std::invoke_result_t<G, decltype(opt)>, void>)
|
||||||
getoptfn(opt);
|
getoptfn(opt);
|
||||||
|
else if constexpr(std::is_arithmetic_v<std::invoke_result_t<G, decltype(opt)>>)
|
||||||
|
TRY_MAIN(getoptfn(opt));
|
||||||
else {
|
else {
|
||||||
if(auto err = getoptfn(opt)) {
|
if(auto err = getoptfn(opt))
|
||||||
fprintf(stderr, "Usage: %s [-hV] %s%s%s\n", argv[0], usage, strlen(usage) ? " " : "", dataset_usage);
|
return fprintf(stderr, gettext("usage: %s [-hV] %s%s%s\n"), argv[0], *usage ? gettext(usage) : "", *usage ? " " : "", gettext(dataset_usage)), err;
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(auto err = validate())
|
||||||
|
return fprintf(stderr, gettext("usage: %s [-hV] %s%s%s\n"), argv[0], *usage ? gettext(usage) : "", *usage ? " " : "", gettext(dataset_usage)), err;
|
||||||
return main(libz);
|
return main(libz);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class G, class M>
|
template <class G, class M, class V = int (*)()>
|
||||||
int do_main(int argc, char ** argv, const char * getoptions, const char * usage, G && getoptfn, M && main) {
|
static int do_main(int argc, char ** argv, const char * getoptions, const char * usage, G && getoptfn, M && main, V && validate = [] { return 0; }) {
|
||||||
return do_bare_main(argc, argv, getoptions, usage, "<dataset>", getoptfn, [&](auto libz) {
|
return do_bare_main(
|
||||||
if(optind >= argc) {
|
// as-in argument in a usage string
|
||||||
fprintf(stderr,
|
argc, argv, getoptions, usage, gettext_noop("dataset"), getoptfn,
|
||||||
"No dataset to act on?\n"
|
[&](auto libz) {
|
||||||
"Usage: %s [-hV] %s%s<dataset>\n",
|
if(!*(argv + optind))
|
||||||
argv[0], usage, strlen(usage) ? " " : "");
|
return fprintf(stderr,
|
||||||
return __LINE__;
|
gettext("No dataset to act on?\n"
|
||||||
}
|
"usage: %s [-hV] %s%sdataset\n"),
|
||||||
auto dataset = TRY_PTR(nullptr, zfs_open(libz, argv[optind], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME));
|
argv[0], usage, strlen(usage) ? " " : ""),
|
||||||
quickscope_wrapper dataset_deleter{[&] { zfs_close(dataset); }};
|
__LINE__;
|
||||||
|
if(*(argv + optind + 1))
|
||||||
|
return fprintf(stderr, gettext("usage: %s [-hV] %s%sdataset\n"), argv[0], usage, strlen(usage) ? " " : ""), __LINE__;
|
||||||
|
auto dataset = TRY_PTR(nullptr, zfs_open(libz, argv[optind], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME));
|
||||||
|
quickscope_wrapper dataset_deleter{[&] { zfs_close(dataset); }};
|
||||||
|
|
||||||
{
|
{
|
||||||
char encryption_root[MAXNAMELEN];
|
char encryption_root[MAXNAMELEN];
|
||||||
boolean_t dataset_is_root;
|
boolean_t dataset_is_root;
|
||||||
TRY("get encryption root", zfs_crypto_get_encryption_root(dataset, &dataset_is_root, encryption_root));
|
TRY("get encryption root", zfs_crypto_get_encryption_root(dataset, &dataset_is_root, encryption_root));
|
||||||
|
|
||||||
if(!dataset_is_root && !strlen(encryption_root)) {
|
if(!dataset_is_root && !strlen(encryption_root))
|
||||||
fprintf(stderr, "Dataset %s not encrypted?\n", zfs_get_name(dataset));
|
return fprintf(stderr, gettext("Dataset %s not encrypted?\n"), zfs_get_name(dataset)), __LINE__;
|
||||||
return __LINE__;
|
else if(!dataset_is_root) {
|
||||||
} else if(!dataset_is_root) {
|
fprintf(stderr, gettext("Using dataset %s's encryption root %s instead.\n"), zfs_get_name(dataset), encryption_root);
|
||||||
fprintf(stderr, "Using dataset %s's encryption root %s instead.\n", zfs_get_name(dataset), encryption_root);
|
zfs_close(dataset);
|
||||||
zfs_close(dataset);
|
dataset = TRY_PTR(nullptr, zfs_open(libz, encryption_root, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME));
|
||||||
dataset = TRY_PTR(nullptr, zfs_open(libz, encryption_root, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME));
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return main(dataset);
|
return main(dataset);
|
||||||
});
|
},
|
||||||
|
validate);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ int do_clear_main(int argc, char ** argv, const char * this_backend, H && handle
|
|||||||
return do_main(
|
return do_main(
|
||||||
argc, argv, "", "", [&](auto) {},
|
argc, argv, "", "", [&](auto) {},
|
||||||
[&](auto dataset) {
|
[&](auto dataset) {
|
||||||
REQUIRE_KEY_LOADED(dataset);
|
REQUIRE_KEY_LOADED(dataset);
|
||||||
|
|
||||||
char * handle_s{};
|
char * handle_s{};
|
||||||
TRY_MAIN(parse_key_props(dataset, this_backend, handle_s));
|
TRY_MAIN(parse_key_props(dataset, this_backend, handle_s));
|
||||||
|
@ -1,24 +1,33 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
// SPDX-License-Identifier: 0BSD
|
||||||
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
int parse_int(const char * what, T & out) {
|
bool parse_uint(const char * val, T & out) {
|
||||||
int base = 10;
|
if(val[0] == '\0')
|
||||||
if(!strncmp(what, "0x", 2) || !strncmp(what, "0X", 2)) {
|
return errno = EINVAL, false;
|
||||||
base = 16;
|
if(val[0] == '-')
|
||||||
what += 2;
|
return errno = ERANGE, false;
|
||||||
}
|
|
||||||
|
|
||||||
if(std::from_chars(what, what + strlen(what), out, base).ptr == what)
|
char * end{};
|
||||||
return __LINE__;
|
errno = 0;
|
||||||
else
|
auto res = strtoull(val, &end, 0);
|
||||||
return 0;
|
out = res;
|
||||||
|
if(errno)
|
||||||
|
return false;
|
||||||
|
if(res > std::numeric_limits<T>::max())
|
||||||
|
return errno = ERANGE, false;
|
||||||
|
if(*end != '\0')
|
||||||
|
return errno = EINVAL, false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
#include "tpm1x.hpp"
|
#include "tpm1x.hpp"
|
||||||
|
|
||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
|
#include "parse.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
@ -23,10 +25,8 @@ tpm1x_handle::~tpm1x_handle() {
|
|||||||
|
|
||||||
int tpm1x_parse_handle(const char * dataset_name, char * handle_s, tpm1x_handle & handle) {
|
int tpm1x_parse_handle(const char * dataset_name, char * handle_s, tpm1x_handle & handle) {
|
||||||
auto midpoint = strchr(handle_s, ':');
|
auto midpoint = strchr(handle_s, ':');
|
||||||
if(!midpoint) {
|
if(!midpoint)
|
||||||
fprintf(stderr, "Dataset %s's handle %s not valid.\n", dataset_name, handle_s);
|
return fprintf(stderr, "Dataset %s's handle %s not valid.\n", dataset_name, handle_s), __LINE__;
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
*midpoint = '\0';
|
*midpoint = '\0';
|
||||||
|
|
||||||
auto parent_key_wide_blob = handle_s;
|
auto parent_key_wide_blob = handle_s;
|
||||||
@ -82,6 +82,41 @@ int tpm1x_prep_sealed_object(TSS_HCONTEXT ctx, TSS_HOBJECT & sealed_object, TSS_
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://trustedcomputinggroup.org/wp-content/uploads/TPM-Main-Part-1-Design-Principles_v1.2_rev116_01032011.pdf sections 4.4.7, .8 (L1228-1236):
|
||||||
|
/// > 7. A TPM implementation MUST provide 16 or more independent PCRs. These PCRs areidentified by index and MUST be numbered from 0 (that is, PCR0 through
|
||||||
|
/// > PCR15 are required for TCG compliance). Vendors MAY implement more registers for general-purpose use. Extra registers MUST be numbered contiguously
|
||||||
|
/// > from16 up to max – 1,where max is the maximum offered by the TPM
|
||||||
|
/// > 8. The TCG-protected capabilities that expose and modify the PCRs use a 32-bit index,indicating the maximum usable PCR index. However, TCG reserves
|
||||||
|
/// > register indices 230and higher for later versions of the specification. A TPM implementation MUST NOTprovide registers with indices greater than or
|
||||||
|
/// > equal to 230.
|
||||||
|
int tpm1x_parse_pcrs(char * arg, uint32_t *& pcrs, size_t & pcrs_len) {
|
||||||
|
size_t out_cap = 16;
|
||||||
|
pcrs = reinterpret_cast<uint32_t *>(TRY_PTR("allocate PCR list", calloc(out_cap, sizeof(uint32_t))));
|
||||||
|
|
||||||
|
char * sv{};
|
||||||
|
for(arg = strtok_r(arg, ", ", &sv); arg; arg = strtok_r(nullptr, ", ", &sv)) {
|
||||||
|
uint32_t pcr;
|
||||||
|
if(!parse_uint(arg, pcr))
|
||||||
|
// user-supplied parameter, errno
|
||||||
|
return fprintf(stderr, gettext("PCR %s: %s\n"), arg, strerror(errno)), __LINE__;
|
||||||
|
if(pcr >= 230)
|
||||||
|
// user-supplied parameter
|
||||||
|
return fprintf(stderr, gettext("PCR %s: too large (max 229).\n"), arg), __LINE__;
|
||||||
|
|
||||||
|
auto idx = std::upper_bound(pcrs, pcrs + pcrs_len, pcr) - pcrs;
|
||||||
|
if(!idx || pcrs[idx - 1] != pcr) {
|
||||||
|
if(pcrs_len >= out_cap)
|
||||||
|
pcrs = reinterpret_cast<uint32_t *>(TRY_PTR("allocate PCR list", reallocarray(pcrs, out_cap *= 2, sizeof(uint32_t))));
|
||||||
|
|
||||||
|
memmove(pcrs + idx + 1, pcrs + idx, (pcrs_len - idx) * sizeof(uint32_t));
|
||||||
|
pcrs[idx] = pcr;
|
||||||
|
++pcrs_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// This feels suboptimal somehow, and yet
|
/// This feels suboptimal somehow, and yet
|
||||||
static int fromxchar(uint8_t & out, char c) {
|
static int fromxchar(uint8_t & out, char c) {
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include <tss/platform.h>
|
#include <tss/platform.h>
|
||||||
#include <tss/tspi.h>
|
#include <tss/tspi.h>
|
||||||
@ -22,13 +23,15 @@
|
|||||||
|
|
||||||
|
|
||||||
/// Used as default secret if passphrase wasn't provided for wrapping key for the sealed object
|
/// Used as default secret if passphrase wasn't provided for wrapping key for the sealed object
|
||||||
// I just got this out of /dev/random
|
// I just got this out of /dev/random, for greppers: CE4CF677875B5EB8993591D5A9AF1ED24A3A8736
|
||||||
static const constexpr uint8_t parent_key_secret[TPM_SHA1_160_HASH_LEN]{0xCE, 0x4C, 0xF6, 0x77, 0x87, 0x5B, 0x5E, 0xB8, 0x99, 0x35,
|
static const constexpr uint8_t parent_key_secret[TPM_SHA1_160_HASH_LEN]{0xCE, 0x4C, 0xF6, 0x77, 0x87, 0x5B, 0x5E, 0xB8, 0x99, 0x35,
|
||||||
0x91, 0xD5, 0xA9, 0xAF, 0x1E, 0xD2, 0x4A, 0x3A, 0x87, 0x36};
|
0x91, 0xD5, 0xA9, 0xAF, 0x1E, 0xD2, 0x4A, 0x3A, 0x87, 0x36};
|
||||||
|
|
||||||
|
|
||||||
template <class F>
|
template <class F>
|
||||||
int with_tpm1x_session(F && func) {
|
int with_tpm1x_session(F && func) {
|
||||||
|
mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||||
|
|
||||||
TSS_HCONTEXT ctx{}; // All memory lives as long as this does
|
TSS_HCONTEXT ctx{}; // All memory lives as long as this does
|
||||||
TRY_TPM1X("create TPM context", Tspi_Context_Create(&ctx));
|
TRY_TPM1X("create TPM context", Tspi_Context_Create(&ctx));
|
||||||
|
|
||||||
@ -80,11 +83,8 @@ int try_policy_or_passphrase(const char * what, const char * what_for, TSS_HPOLI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TRY_TPM1X() unrolled because no constexpr/string-literal-template arguments until C++20, which is not supported by GCC 8, which we need for Buster
|
// TRY_TPM1X() unrolled because no constexpr/string-literal-template arguments until C++20, which is not supported by GCC 8, which we need for Buster
|
||||||
if(err != TPM_SUCCESS) {
|
if(err != TPM_SUCCESS)
|
||||||
fprintf(stderr, "Couldn't %s: %s\n", what, Trspi_Error_String(err));
|
return fprintf(stderr, "Couldn't %s: %s\n", what, Trspi_Error_String(err)), __LINE__;
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,3 +105,6 @@ extern int tpm1x_parse_handle(const char * dataset_name, char * handle_s, tpm1x_
|
|||||||
|
|
||||||
/// Create sealed object, assign a policy and a known secret to it.
|
/// Create sealed object, assign a policy and a known secret to it.
|
||||||
extern int tpm1x_prep_sealed_object(TSS_HCONTEXT ctx, TSS_HOBJECT & sealed_object, TSS_HPOLICY & sealed_object_policy);
|
extern int tpm1x_prep_sealed_object(TSS_HCONTEXT ctx, TSS_HOBJECT & sealed_object, TSS_HPOLICY & sealed_object_policy);
|
||||||
|
|
||||||
|
/// Parse a comma- or space-separated number list.
|
||||||
|
extern int tpm1x_parse_pcrs(char * arg, uint32_t *& pcrs, size_t & pcrs_len);
|
||||||
|
360
src/tpm2.cpp
360
src/tpm2.cpp
@ -7,7 +7,13 @@
|
|||||||
#include "parse.hpp"
|
#include "parse.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <time.h>
|
#include <inttypes.h>
|
||||||
|
#include <iterator>
|
||||||
|
#define OPENSSL_SUPPRESS_DEPRECATED // SHA256_*(); supposedly replaced with EVP* but that's horseshit. we'll see how she turns out, given there's no reason
|
||||||
|
// given
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
template <class F>
|
template <class F>
|
||||||
@ -15,7 +21,7 @@ static int try_or_passphrase(const char * what, const char * what_for, ESYS_CONT
|
|||||||
auto err = func();
|
auto err = func();
|
||||||
for(int i = 0; err == TPM2_RC_9 + valid_error && i < 3; ++i) {
|
for(int i = 0; err == TPM2_RC_9 + valid_error && i < 3; ++i) {
|
||||||
if(i)
|
if(i)
|
||||||
fprintf(stderr, "Couldn't %s: %s\n", what, Tss2_RC_Decode(err));
|
fprintf(stderr, "Couldn't %s: %s\n", gettext(what), Tss2_RC_Decode(err));
|
||||||
|
|
||||||
uint8_t * pass{};
|
uint8_t * pass{};
|
||||||
size_t pass_len{};
|
size_t pass_len{};
|
||||||
@ -31,37 +37,168 @@ static int try_or_passphrase(const char * what, const char * what_for, ESYS_CONT
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TRY_TPM2() unrolled because no constexpr/string-literal-template arguments until C++20, which is not supported by GCC 8, which we need for Buster
|
// TRY_TPM2() unrolled because no constexpr/string-literal-template arguments until C++20, which is not supported by GCC 8, which we need for Buster
|
||||||
if(err != TPM2_RC_SUCCESS) {
|
if(err != TPM2_RC_SUCCESS)
|
||||||
fprintf(stderr, "Couldn't %s: %s\n", what, Tss2_RC_Decode(err));
|
return fprintf(stderr, "Couldn't %s: %s\n", gettext(what), Tss2_RC_Decode(err)), __LINE__;
|
||||||
return __LINE__;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int tpm2_parse_prop(const char * dataset_name, char * handle_s, TPMI_DH_PERSISTENT & handle, TPML_PCR_SELECTION * pcrs) {
|
||||||
|
char * sv{};
|
||||||
|
if(!parse_uint(handle_s = strtok_r(handle_s, ";", &sv), handle))
|
||||||
|
return fprintf(stderr, "Dataset %s's handle %s: %s.\n", dataset_name, handle_s, strerror(errno)), __LINE__;
|
||||||
|
|
||||||
|
if(auto p = strtok_r(nullptr, ";", &sv); p && pcrs)
|
||||||
|
TRY_MAIN(tpm2_parse_pcrs(p, *pcrs));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TPM2B_DATA tpm2_creation_metadata(const char * dataset_name) {
|
/// Extension of the table used by tpm2-tools (tpm2_create et al.), which only has "s{m,ha}3_XXX", not "s{m,ha}3-XXX", and does case-sentitive comparisons
|
||||||
TPM2B_DATA metadata{};
|
#define TPM2_HASH_ALGS_MAX_NAME_LEN 8 // sha3_512
|
||||||
|
static const constexpr struct tpm2_hash_algs_t {
|
||||||
|
TPM2_ALG_ID alg;
|
||||||
|
const char * names[2];
|
||||||
|
} tpm2_hash_algs[] = {{TPM2_ALG_SHA1, {"sha1"}},
|
||||||
|
{TPM2_ALG_SHA256, {"sha256"}},
|
||||||
|
{TPM2_ALG_SHA384, {"sha384"}},
|
||||||
|
{TPM2_ALG_SHA512, {"sha512"}},
|
||||||
|
{TPM2_ALG_SM3_256, {"sm3_256", "sm3-256"}},
|
||||||
|
{TPM2_ALG_SHA3_256, {"sha3_256", "sha3-256"}},
|
||||||
|
{TPM2_ALG_SHA3_384, {"sha3_384", "sha3-384"}},
|
||||||
|
{TPM2_ALG_SHA3_512, {"sha3_512", "sha3-512"}}};
|
||||||
|
|
||||||
const auto now = time(nullptr);
|
static constexpr bool is_tpm2_hash_algs_sorted() {
|
||||||
const auto now_tm = localtime(&now);
|
for(auto itr = std::begin(tpm2_hash_algs); itr != std::end(tpm2_hash_algs) - 1; ++itr)
|
||||||
metadata.size = snprintf((char *)metadata.buffer, sizeof(metadata.buffer), "%s %d-%02d-%02dT%02d:%02d:%02d %s", dataset_name, //
|
if((itr + 1)->alg < itr->alg)
|
||||||
now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, //
|
return false;
|
||||||
TZPFMS_VERSION) +
|
return true;
|
||||||
1;
|
}
|
||||||
metadata.size = metadata.size > sizeof(metadata.buffer) ? sizeof(metadata.buffer) : metadata.size;
|
static_assert(is_tpm2_hash_algs_sorted()); // for the binary_search() below
|
||||||
|
|
||||||
// fprintf(stderr, "%d/%zu: \"%s\"\n", metadata.size, sizeof(metadata.buffer), metadata.buffer);
|
/// Assuming always != end: we always parse first
|
||||||
return metadata;
|
static const char * tpm2_hash_alg_name(TPM2_ALG_ID id) {
|
||||||
|
return std::lower_bound(std::begin(tpm2_hash_algs), std::end(tpm2_hash_algs), tpm2_hash_algs_t{id, {}},
|
||||||
|
[&](auto && lhs, auto && rhs) { return lhs.alg < rhs.alg; })
|
||||||
|
->names[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int tpm2_parse_handle(const char * dataset_name, const char * handle_s, TPMI_DH_PERSISTENT & handle) {
|
/// Nominally:
|
||||||
if(parse_int(handle_s, handle)) {
|
/// #define TPM2_MAX_PCRS 32
|
||||||
fprintf(stderr, "Dataset %s's handle %s not valid.\n", dataset_name, handle_s);
|
/// #define TPM2_PCR_SELECT_MAX ((TPM2_MAX_PCRS + 7) / 8)
|
||||||
return __LINE__;
|
/// and
|
||||||
|
/// struct TPMS_PCR_SELECT {
|
||||||
|
/// UINT8 sizeofSelect; /* the size in octets of the pcrSelect array */
|
||||||
|
/// BYTE pcrSelect[TPM2_PCR_SELECT_MAX]; /* the bit map of selected PCR */
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// This works out to TPM2_PCR_SELECT_MAX=4, but most (all?) TPM2s have only 24 PCRs, meaning *any* request with sizeofSelect=sizeof(pcrSelect)=4 fails with
|
||||||
|
/// WARNING:esys:src/tss2-esys/api/Esys_CreatePrimary.c:393:Esys_CreatePrimary_Finish() Received TPM Error
|
||||||
|
/// ERROR:esys:src/tss2-esys/api/Esys_CreatePrimary.c:135:Esys_CreatePrimary() Esys Finish ErrorCode (0x000004c4)
|
||||||
|
/// Couldn't create primary encryption key: tpm:parameter(4):value is out of range or is not correct for the context
|
||||||
|
///
|
||||||
|
/// Follow tpm2-tools and pretend TPM2_MAX_PCRS=24 => TPM2_PCR_SELECT_MAX=3 => sizeofSelect=3.
|
||||||
|
#define TPM2_MAX_PCRS_BUT_STRONGER 24
|
||||||
|
#define TPM2_PCR_SELECT_MAX_BUT_STRONGER ((TPM2_MAX_PCRS_BUT_STRONGER + 7) / 8)
|
||||||
|
static_assert(TPM2_PCR_SELECT_MAX_BUT_STRONGER <= sizeof(TPMS_PCR_SELECT::pcrSelect));
|
||||||
|
|
||||||
|
int tpm2_parse_pcrs(char * arg, TPML_PCR_SELECTION & pcrs) {
|
||||||
|
TPMS_PCR_SELECTION * bank = pcrs.pcrSelections;
|
||||||
|
|
||||||
|
char * ph_sv{};
|
||||||
|
for(auto per_hash = strtok_r(arg, "+", &ph_sv); per_hash; per_hash = strtok_r(nullptr, "+", &ph_sv), ++bank) {
|
||||||
|
while(*per_hash == ' ')
|
||||||
|
++per_hash;
|
||||||
|
|
||||||
|
if(bank == pcrs.pcrSelections + (sizeof(pcrs.pcrSelections) / sizeof(*pcrs.pcrSelections))) // == TPM2_NUM_PCR_BANKS
|
||||||
|
return fprintf(stderr, gettext("Too many PCR banks specified! Can only have up to %zu\n"), sizeof(pcrs.pcrSelections) / sizeof(*pcrs.pcrSelections)),
|
||||||
|
__LINE__;
|
||||||
|
|
||||||
|
if(auto sep = strchr(per_hash, ':')) {
|
||||||
|
*sep = '\0';
|
||||||
|
auto values = sep + 1;
|
||||||
|
|
||||||
|
if(auto alg = std::find_if(
|
||||||
|
std::begin(tpm2_hash_algs), std::end(tpm2_hash_algs),
|
||||||
|
[&](auto && alg) { return std::any_of(std::begin(alg.names), std::end(alg.names), [&](auto && nm) { return nm && !strcasecmp(per_hash, nm); }); });
|
||||||
|
alg != std::end(tpm2_hash_algs))
|
||||||
|
bank->hash = alg->alg;
|
||||||
|
else {
|
||||||
|
if(!parse_uint(per_hash, bank->hash) || !std::binary_search(std::begin(tpm2_hash_algs), std::end(tpm2_hash_algs), tpm2_hash_algs_t{bank->hash, {}},
|
||||||
|
[&](auto && lhs, auto && rhs) { return lhs.alg < rhs.alg; })) {
|
||||||
|
fprintf(stderr,
|
||||||
|
// comma-separated list follows
|
||||||
|
gettext("Unknown hash algorithm %s.\n"
|
||||||
|
"Can be any of case-insensitive "),
|
||||||
|
per_hash);
|
||||||
|
auto first = true;
|
||||||
|
for(auto && alg : tpm2_hash_algs)
|
||||||
|
for(auto && nm : alg.names)
|
||||||
|
if(nm)
|
||||||
|
fprintf(stderr, "%s%s", first ? "" : ", ", nm), first = false;
|
||||||
|
return fputs(".\n", stderr), __LINE__;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bank->sizeofSelect = TPM2_PCR_SELECT_MAX_BUT_STRONGER;
|
||||||
|
if(!strcasecmp(values, "all"))
|
||||||
|
memset(bank->pcrSelect, 0xFF, bank->sizeofSelect);
|
||||||
|
else if(!strcasecmp(values, "none"))
|
||||||
|
; // already 0
|
||||||
|
else {
|
||||||
|
char * sv{};
|
||||||
|
for(values = strtok_r(values, ", ", &sv); values; values = strtok_r(nullptr, ", ", &sv)) {
|
||||||
|
uint8_t pcr;
|
||||||
|
if(!parse_uint(values, pcr))
|
||||||
|
return fprintf(stderr, "PCR %s: %s\n", values, strerror(errno)), __LINE__;
|
||||||
|
if(pcr > TPM2_MAX_PCRS_BUT_STRONGER - 1)
|
||||||
|
return fprintf(stderr, "PCR %s: %s, max %u\n", values, strerror(ERANGE), TPM2_MAX_PCRS_BUT_STRONGER - 1), __LINE__;
|
||||||
|
|
||||||
|
bank->pcrSelect[pcr / 8] |= 1 << (pcr % 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
// user-passed parameter
|
||||||
|
return fprintf(stderr, gettext("PCR bank \"%s\": no algorithm; need alg:PCR[,PCR]…\n"), per_hash), __LINE__;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pcrs.count = bank - pcrs.pcrSelections;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tpm2_unparse_prop(TPMI_DH_PERSISTENT persistent_handle, const TPML_PCR_SELECTION & pcrs, char ** prop) {
|
||||||
|
// 0xFFFFFFFF;sha3_512:00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22+sha3_...
|
||||||
|
*prop = TRY_PTR("allocate property value",
|
||||||
|
reinterpret_cast<char *>(malloc(2 + 8 + pcrs.count * (1 + TPM2_HASH_ALGS_MAX_NAME_LEN + (TPM2_MAX_PCRS_BUT_STRONGER - 1) * 3) + 1)));
|
||||||
|
|
||||||
|
auto cur = *prop;
|
||||||
|
cur += sprintf(cur, "0x%" PRIX32 "", persistent_handle);
|
||||||
|
|
||||||
|
auto pre = ';';
|
||||||
|
for(size_t i = 0; i < pcrs.count; ++i) {
|
||||||
|
auto && sel = pcrs.pcrSelections[i];
|
||||||
|
*cur++ = std::exchange(pre, '+');
|
||||||
|
|
||||||
|
auto nm = tpm2_hash_alg_name(sel.hash);
|
||||||
|
auto nm_len = strlen(nm);
|
||||||
|
memcpy(cur, nm, nm_len), cur += nm_len;
|
||||||
|
|
||||||
|
if(std::all_of(sel.pcrSelect, sel.pcrSelect + sel.sizeofSelect, [](auto b) { return b == 0x00; }))
|
||||||
|
memcpy(cur, ":none", strlen(":none")), cur += strlen(":none");
|
||||||
|
else if(std::all_of(sel.pcrSelect, sel.pcrSelect + sel.sizeofSelect, [](auto b) { return b == 0xFF; }))
|
||||||
|
memcpy(cur, ":all", strlen(":all")), cur += strlen(":all");
|
||||||
|
else {
|
||||||
|
bool first = true;
|
||||||
|
for(size_t j = 0; j < sel.sizeofSelect; ++j)
|
||||||
|
for(uint8_t b = 0; b < 8; ++b)
|
||||||
|
if(sel.pcrSelect[j] & (1 << b))
|
||||||
|
cur += sprintf(cur, "%c%zu", std::exchange(first, false) ? ':' : ',', j * 8 + b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*cur = '\0';
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,12 +206,10 @@ int tpm2_parse_handle(const char * dataset_name, const char * handle_s, TPMI_DH_
|
|||||||
int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length) {
|
int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length) {
|
||||||
TPM2B_DIGEST * rand{};
|
TPM2B_DIGEST * rand{};
|
||||||
TRY_TPM2("get random data from TPM", Esys_GetRandom(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, length, &rand));
|
TRY_TPM2("get random data from TPM", Esys_GetRandom(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, length, &rand));
|
||||||
quickscope_wrapper rand_deleter{[=] { Esys_Free(rand); }};
|
quickscope_wrapper rand_deleter{[&] { Esys_Free(rand); }};
|
||||||
|
|
||||||
if(rand->size != length) {
|
if(rand->size != length)
|
||||||
fprintf(stderr, "Wrong random size: wanted %zu, got %u bytes.\n", length, rand->size);
|
return fprintf(stderr, "Wrong random size: wanted %zu, got %" PRIu16 " bytes.\n", length, rand->size), __LINE__;
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(into, rand->buffer, length);
|
memcpy(into, rand->buffer, length);
|
||||||
return 0;
|
return 0;
|
||||||
@ -82,10 +217,10 @@ int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length) {
|
|||||||
|
|
||||||
|
|
||||||
static int tpm2_find_unused_persistent_non_platform(ESYS_CONTEXT * tpm2_ctx, TPMI_DH_PERSISTENT & persistent_handle) {
|
static int tpm2_find_unused_persistent_non_platform(ESYS_CONTEXT * tpm2_ctx, TPMI_DH_PERSISTENT & persistent_handle) {
|
||||||
TPMS_CAPABILITY_DATA * cap; // TODO: check for more data?
|
TPMS_CAPABILITY_DATA * cap{}; // TODO: check for more data?
|
||||||
TRY_TPM2("Read used persistent TPM handles", Esys_GetCapability(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, TPM2_PERSISTENT_FIRST,
|
TRY_TPM2("Read used persistent TPM handles", Esys_GetCapability(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, TPM2_PERSISTENT_FIRST,
|
||||||
TPM2_MAX_CAP_HANDLES, nullptr, &cap));
|
TPM2_MAX_CAP_HANDLES, nullptr, &cap));
|
||||||
quickscope_wrapper cap_deleter{[=] { Esys_Free(cap); }};
|
quickscope_wrapper cap_deleter{[&] { Esys_Free(cap); }};
|
||||||
|
|
||||||
persistent_handle = 0;
|
persistent_handle = 0;
|
||||||
switch(cap->data.handles.count) {
|
switch(cap->data.handles.count) {
|
||||||
@ -102,25 +237,85 @@ static int tpm2_find_unused_persistent_non_platform(ESYS_CONTEXT * tpm2_ctx, TPM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!persistent_handle) {
|
if(!persistent_handle)
|
||||||
fprintf(stderr, "All %zu persistent handles allocated! We're fucked!\n", TPM2_MAX_CAP_HANDLES);
|
return fprintf(stderr, "All %zu persistent handles allocated! We're fucked!\n", TPM2_MAX_CAP_HANDLES), __LINE__;
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT & persistent_handle, const TPM2B_DATA & metadata, void * data,
|
template <class F>
|
||||||
size_t data_len) {
|
static int tpm2_police_pcrs(ESYS_CONTEXT * tpm2_ctx, const TPML_PCR_SELECTION & pcrs, TPM2_SE session_type, F && with_session) {
|
||||||
|
if(!pcrs.count)
|
||||||
|
return with_session(ESYS_TR_NONE);
|
||||||
|
|
||||||
|
TPM2B_DIGEST digested_pcrs{};
|
||||||
|
digested_pcrs.size = SHA256_DIGEST_LENGTH;
|
||||||
|
static_assert(sizeof(TPM2B_DIGEST::buffer) >= SHA256_DIGEST_LENGTH);
|
||||||
|
|
||||||
|
{
|
||||||
|
SHA256_CTX ctx;
|
||||||
|
new_pcrs:
|
||||||
|
std::optional<uint32_t> update_count;
|
||||||
|
SHA256_Init(&ctx);
|
||||||
|
auto pcrs_left = pcrs;
|
||||||
|
while(std::any_of(pcrs_left.pcrSelections, pcrs_left.pcrSelections + pcrs_left.count,
|
||||||
|
[](auto && sel) { return std::any_of(sel.pcrSelect, sel.pcrSelect + sel.sizeofSelect, [](auto b) { return b; }); })) {
|
||||||
|
uint32_t out_upcnt{};
|
||||||
|
TPML_PCR_SELECTION * out_sel{};
|
||||||
|
TPML_DIGEST * out_val{};
|
||||||
|
TRY_TPM2("read PCRs", Esys_PCR_Read(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pcrs_left, &out_upcnt, &out_sel, &out_val));
|
||||||
|
quickscope_wrapper out_deleter{[&] { Esys_Free(out_val), Esys_Free(out_sel); }};
|
||||||
|
|
||||||
|
if(update_count && update_count != out_upcnt)
|
||||||
|
goto new_pcrs;
|
||||||
|
update_count = out_upcnt;
|
||||||
|
|
||||||
|
if(!out_val->count) { // this can happen with SHA1 disabled, for example
|
||||||
|
auto first = true;
|
||||||
|
fputs("No PCRs when asking for ", stderr);
|
||||||
|
for(size_t i = 0; i < pcrs_left.count; ++i)
|
||||||
|
if(std::any_of(pcrs_left.pcrSelections[i].pcrSelect, pcrs_left.pcrSelections[i].pcrSelect + pcrs_left.pcrSelections[i].sizeofSelect,
|
||||||
|
[](auto b) { return b; }))
|
||||||
|
fprintf(stderr, "%s%s", std::exchange(first, false) ? "" : ", ", tpm2_hash_alg_name(pcrs_left.pcrSelections[i].hash));
|
||||||
|
return fputs(": does the TPM support the algorithm?\n", stderr), __LINE__;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < out_val->count; ++i)
|
||||||
|
SHA256_Update(&ctx, out_val->digests[i].buffer, out_val->digests[i].size);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < out_sel->count; ++i)
|
||||||
|
for(size_t j = 0u; j < out_sel->pcrSelections[i].sizeofSelect; ++j)
|
||||||
|
pcrs_left.pcrSelections[i].pcrSelect[j] &= ~out_sel->pcrSelections[i].pcrSelect[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
SHA256_Final(digested_pcrs.buffer, &ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ESYS_TR pcr_session = ESYS_TR_NONE;
|
||||||
|
quickscope_wrapper tpm2_session_deleter{[&] { Esys_FlushContext(tpm2_ctx, pcr_session); }};
|
||||||
|
|
||||||
|
TRY_TPM2("start PCR session", Esys_StartAuthSession(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, nullptr, session_type,
|
||||||
|
&tpm2_session_key, TPM2_ALG_SHA256, &pcr_session));
|
||||||
|
|
||||||
|
|
||||||
|
TRY_TPM2("create PCR policy", Esys_PolicyPCR(tpm2_ctx, pcr_session, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &digested_pcrs, &pcrs));
|
||||||
|
|
||||||
|
return with_session(pcr_session);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tpm2_seal(const char * dataset, ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT & persistent_handle, const TPML_PCR_SELECTION & pcrs,
|
||||||
|
bool allow_PCR_or_pass, void * data, size_t data_len) {
|
||||||
ESYS_TR primary_handle = ESYS_TR_NONE;
|
ESYS_TR primary_handle = ESYS_TR_NONE;
|
||||||
quickscope_wrapper primary_handle_deleter{[&] { Esys_FlushContext(tpm2_ctx, primary_handle); }};
|
quickscope_wrapper primary_handle_deleter{[&] { Esys_FlushContext(tpm2_ctx, primary_handle); }};
|
||||||
|
|
||||||
|
const TPM2B_DATA metadata{};
|
||||||
{
|
{
|
||||||
const TPM2B_SENSITIVE_CREATE primary_sens{};
|
const TPM2B_SENSITIVE_CREATE primary_sens{};
|
||||||
|
|
||||||
// Adapted from tpm2-tss-3.0.1/test/integration/esys-create-primary-hmac.int.c
|
// Adapted from tpm2-tss-3.0.1/test/integration/esys-create-primary-hmac.int.c
|
||||||
TPM2B_PUBLIC pub{};
|
TPM2B_PUBLIC pub{};
|
||||||
pub.publicArea.type = TPM2_ALG_RSA;
|
pub.publicArea.type = TPM2_ALG_RSA;
|
||||||
pub.publicArea.nameAlg = TPM2_ALG_SHA1;
|
pub.publicArea.nameAlg = TPM2_ALG_SHA256;
|
||||||
pub.publicArea.objectAttributes = TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_RESTRICTED | TPMA_OBJECT_DECRYPT | TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT |
|
pub.publicArea.objectAttributes = TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_RESTRICTED | TPMA_OBJECT_DECRYPT | TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT |
|
||||||
TPMA_OBJECT_SENSITIVEDATAORIGIN;
|
TPMA_OBJECT_SENSITIVEDATAORIGIN;
|
||||||
pub.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM2_ALG_AES;
|
pub.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM2_ALG_AES;
|
||||||
@ -129,21 +324,14 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
|
|||||||
pub.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL;
|
pub.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL;
|
||||||
pub.publicArea.parameters.rsaDetail.keyBits = 2048;
|
pub.publicArea.parameters.rsaDetail.keyBits = 2048;
|
||||||
pub.publicArea.parameters.rsaDetail.exponent = 0;
|
pub.publicArea.parameters.rsaDetail.exponent = 0;
|
||||||
|
pub.publicArea.parameters.asymDetail.scheme.scheme = TPM2_ALG_NULL;
|
||||||
const TPML_PCR_SELECTION pcrs{};
|
pub.publicArea.parameters.symDetail.sym.algorithm = TPM2_ALG_AES;
|
||||||
|
pub.publicArea.parameters.symDetail.sym.keyBits.sym = 128;
|
||||||
TPM2B_PUBLIC * public_ret{};
|
pub.publicArea.parameters.symDetail.sym.mode.sym = TPM2_ALG_CFB;
|
||||||
TPM2B_CREATION_DATA * creation_data{};
|
TRY_MAIN(try_or_passphrase("create primary encryption key", "TPM2 owner hierarchy", tpm2_ctx, TPM2_RC_BAD_AUTH, ESYS_TR_RH_OWNER, [&] {
|
||||||
TPM2B_DIGEST * creation_hash{};
|
|
||||||
TPMT_TK_CREATION * creation_ticket{};
|
|
||||||
TRY_MAIN(try_or_passphrase("create primary encryption key", "owner hierarchy", tpm2_ctx, TPM2_RC_BAD_AUTH, ESYS_TR_RH_OWNER, [&] {
|
|
||||||
return Esys_CreatePrimary(tpm2_ctx, ESYS_TR_RH_OWNER, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &primary_sens, &pub, &metadata, &pcrs, &primary_handle,
|
return Esys_CreatePrimary(tpm2_ctx, ESYS_TR_RH_OWNER, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &primary_sens, &pub, &metadata, &pcrs, &primary_handle,
|
||||||
&public_ret, &creation_data, &creation_hash, &creation_ticket);
|
nullptr, nullptr, nullptr, nullptr);
|
||||||
}));
|
}));
|
||||||
quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }};
|
|
||||||
quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }};
|
|
||||||
quickscope_wrapper creation_data_deleter{[=] { Esys_Free(creation_data); }};
|
|
||||||
quickscope_wrapper public_ret_deleter{[=] { Esys_Free(public_ret); }};
|
|
||||||
|
|
||||||
// TSS2_RC Esys_CertifyCreation ( ESYS_CONTEXT * esysContext,
|
// TSS2_RC Esys_CertifyCreation ( ESYS_CONTEXT * esysContext,
|
||||||
// ESYS_TR signHandle,
|
// ESYS_TR signHandle,
|
||||||
@ -160,11 +348,19 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
|
|||||||
// )
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TPM2B_DIGEST policy_digest{};
|
||||||
|
if(pcrs.count)
|
||||||
|
TRY_MAIN(tpm2_police_pcrs(tpm2_ctx, pcrs, TPM2_SE_TRIAL, [&](auto pcr_session) {
|
||||||
|
TPM2B_DIGEST * dgst{};
|
||||||
|
TRY_TPM2("get PCR policy digest", Esys_PolicyGetDigest(tpm2_ctx, pcr_session, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &dgst));
|
||||||
|
quickscope_wrapper dgst_deleter{[&] { Esys_Free(dgst); }};
|
||||||
|
policy_digest = *dgst;
|
||||||
|
return 0;
|
||||||
|
}));
|
||||||
|
|
||||||
TPM2B_PRIVATE * sealant_private{};
|
TPM2B_PRIVATE * sealant_private{};
|
||||||
TPM2B_PUBLIC * sealant_public{};
|
TPM2B_PUBLIC * sealant_public{};
|
||||||
quickscope_wrapper sealant_public_deleter{[=] { Esys_Free(sealant_public); }};
|
quickscope_wrapper sealant_deleter{[&] { Esys_Free(sealant_public), Esys_Free(sealant_private); }};
|
||||||
quickscope_wrapper sealant_private_deleter{[=] { Esys_Free(sealant_private); }};
|
|
||||||
|
|
||||||
/// This is the object with the actual sealed data in it
|
/// This is the object with the actual sealed data in it
|
||||||
{
|
{
|
||||||
@ -172,35 +368,35 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
|
|||||||
secret_sens.sensitive.data.size = data_len;
|
secret_sens.sensitive.data.size = data_len;
|
||||||
memcpy(secret_sens.sensitive.data.buffer, data, secret_sens.sensitive.data.size);
|
memcpy(secret_sens.sensitive.data.buffer, data, secret_sens.sensitive.data.size);
|
||||||
|
|
||||||
{
|
if(!pcrs.count || allow_PCR_or_pass) {
|
||||||
|
char what_for[ZFS_MAX_DATASET_NAME_LEN + 512 + 1];
|
||||||
|
// %s=dataset name, then TPM2. noun for "Enter passphrase for" prompt
|
||||||
|
snprintf(what_for, sizeof(what_for), gettext("%s %s wrapping key (or empty for none)"), dataset, "TPM2");
|
||||||
|
|
||||||
uint8_t * passphrase{};
|
uint8_t * passphrase{};
|
||||||
size_t passphrase_len{};
|
size_t passphrase_len{};
|
||||||
TRY_MAIN(read_new_passphrase("wrapping key (or empty for none)", passphrase, passphrase_len, sizeof(TPM2B_SENSITIVE_CREATE::sensitive.userAuth.buffer)));
|
TRY_MAIN(read_new_passphrase(what_for, passphrase, passphrase_len, sizeof(TPM2B_SENSITIVE_CREATE::sensitive.userAuth.buffer)));
|
||||||
quickscope_wrapper passphrase_deleter{[&] { free(passphrase); }};
|
quickscope_wrapper passphrase_deleter{[&] { free(passphrase); }};
|
||||||
|
|
||||||
secret_sens.sensitive.userAuth.size = passphrase_len;
|
secret_sens.sensitive.userAuth.size = passphrase_len;
|
||||||
memcpy(secret_sens.sensitive.userAuth.buffer, passphrase, secret_sens.sensitive.userAuth.size);
|
memcpy(secret_sens.sensitive.userAuth.buffer, passphrase, secret_sens.sensitive.userAuth.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Same args as tpm2-tools' tpm2_create(1)
|
// Same args as tpm2-tools' tpm2_create(1)
|
||||||
TPM2B_PUBLIC pub{};
|
TPM2B_PUBLIC pub{};
|
||||||
pub.publicArea.type = TPM2_ALG_KEYEDHASH;
|
pub.publicArea.type = TPM2_ALG_KEYEDHASH;
|
||||||
pub.publicArea.nameAlg = TPM2_ALG_SHA256;
|
pub.publicArea.nameAlg = TPM2_ALG_SHA256;
|
||||||
pub.publicArea.objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_USERWITHAUTH;
|
pub.publicArea.objectAttributes =
|
||||||
|
TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT | ((pcrs.count && !secret_sens.sensitive.userAuth.size) ? 0 : TPMA_OBJECT_USERWITHAUTH);
|
||||||
pub.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL;
|
pub.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL;
|
||||||
|
pub.publicArea.authPolicy = policy_digest;
|
||||||
|
|
||||||
const TPML_PCR_SELECTION pcrs{};
|
|
||||||
|
|
||||||
TPM2B_CREATION_DATA * creation_data{};
|
|
||||||
TPM2B_DIGEST * creation_hash{};
|
|
||||||
TPMT_TK_CREATION * creation_ticket{};
|
|
||||||
TRY_TPM2("create key seal", Esys_Create(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &secret_sens, &pub, &metadata, &pcrs,
|
TRY_TPM2("create key seal", Esys_Create(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &secret_sens, &pub, &metadata, &pcrs,
|
||||||
&sealant_private, &sealant_public, &creation_data, &creation_hash, &creation_ticket));
|
&sealant_private, &sealant_public, nullptr, nullptr, nullptr));
|
||||||
quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }};
|
|
||||||
quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }};
|
|
||||||
quickscope_wrapper creation_data_deleter{[=] { Esys_Free(creation_data); }};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ESYS_TR sealed_handle = ESYS_TR_NONE;
|
ESYS_TR sealed_handle = ESYS_TR_NONE;
|
||||||
quickscope_wrapper sealed_handle_deleter{[&] { Esys_FlushContext(tpm2_ctx, sealed_handle); }};
|
quickscope_wrapper sealed_handle_deleter{[&] { Esys_FlushContext(tpm2_ctx, sealed_handle); }};
|
||||||
|
|
||||||
@ -221,20 +417,36 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tpm2_unseal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle, void * data, size_t data_len) {
|
int tpm2_unseal(const char * dataset, ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle, const TPML_PCR_SELECTION & pcrs,
|
||||||
|
void * data, size_t data_len) {
|
||||||
|
// Esys_FlushContext(tpm2_ctx, tpm2_session);
|
||||||
|
char what_for[ZFS_MAX_DATASET_NAME_LEN + 512 + 1];
|
||||||
|
// %s=dataset name, then TPM2. noun for "Enter passphrase for" prompt
|
||||||
|
snprintf(what_for, sizeof(what_for), gettext("%s %s wrapping key"), dataset, "TPM2");
|
||||||
|
|
||||||
// Entirely fake and not flushable (tpm:parameter(1):value is out of range or is not correct for the context)
|
// Entirely fake and not flushable (tpm:parameter(1):value is out of range or is not correct for the context)
|
||||||
ESYS_TR pandle;
|
ESYS_TR pandle;
|
||||||
TRY_TPM2("convert persistent handle to object", Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle));
|
TRY_TPM2("convert persistent handle to object", Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle));
|
||||||
|
|
||||||
TPM2B_SENSITIVE_DATA * unsealed{};
|
|
||||||
quickscope_wrapper unsealed_deleter{[=] { Esys_Free(unsealed); }};
|
|
||||||
TRY_MAIN(try_or_passphrase("unseal wrapping key", "wrapping key", tpm2_ctx, TPM2_RC_AUTH_FAIL, pandle,
|
|
||||||
[&] { return Esys_Unseal(tpm2_ctx, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &unsealed); }));
|
|
||||||
|
|
||||||
if(unsealed->size != data_len) {
|
TPM2B_SENSITIVE_DATA * unsealed{};
|
||||||
fprintf(stderr, "Unsealed data has wrong length %u, expected %zu!\n", unsealed->size, data_len);
|
quickscope_wrapper unsealed_deleter{[&] { Esys_Free(unsealed); }};
|
||||||
return __LINE__;
|
auto unseal = [&](auto sess) { return Esys_Unseal(tpm2_ctx, pandle, sess, ESYS_TR_NONE, ESYS_TR_NONE, &unsealed); };
|
||||||
}
|
TRY_MAIN(tpm2_police_pcrs(tpm2_ctx, pcrs, TPM2_SE_POLICY, [&](auto pcr_session) {
|
||||||
|
// In case there's (PCR policy || passphrase): try PCR once; if it fails, fall back to passphrase
|
||||||
|
if(pcr_session != ESYS_TR_NONE) {
|
||||||
|
if(auto err = unseal(pcr_session); err != TPM2_RC_SUCCESS)
|
||||||
|
fprintf(stderr, gettext("Couldn't unseal wrapping key with PCR policy: %s\n"), Tss2_RC_Decode(err));
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return try_or_passphrase("unseal wrapping key", what_for, tpm2_ctx, TPM2_RC_AUTH_FAIL, pandle, [&] { return unseal(tpm2_session); });
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
if(unsealed->size != data_len)
|
||||||
|
return fprintf(stderr, "Unsealed data has wrong length %" PRIu16 ", expected %zu!\n", unsealed->size, data_len), __LINE__;
|
||||||
memcpy(data, unsealed->buffer, data_len);
|
memcpy(data, unsealed->buffer, data_len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -245,7 +457,7 @@ int tpm2_free_persistent(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_
|
|||||||
TRY_TPM2("convert persistent handle to object", Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle));
|
TRY_TPM2("convert persistent handle to object", Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle));
|
||||||
|
|
||||||
ESYS_TR new_handle;
|
ESYS_TR new_handle;
|
||||||
TRY_MAIN(try_or_passphrase("unpersist object", "owner hierarchy", tpm2_ctx, TPM2_RC_BAD_AUTH, ESYS_TR_RH_OWNER,
|
TRY_MAIN(try_or_passphrase("unpersist object", "TPM2 owner hierarchy", tpm2_ctx, TPM2_RC_BAD_AUTH, ESYS_TR_RH_OWNER,
|
||||||
[&] { return Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, 0, &new_handle); }));
|
[&] { return Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, 0, &new_handle); }));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
33
src/tpm2.hpp
33
src/tpm2.hpp
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <tss2/tss2_common.h>
|
#include <tss2/tss2_common.h>
|
||||||
#include <tss2/tss2_esys.h>
|
#include <tss2/tss2_esys.h>
|
||||||
#include <tss2/tss2_rc.h>
|
#include <tss2/tss2_rc.h>
|
||||||
@ -14,8 +15,14 @@
|
|||||||
#define TRY_TPM2(what, ...) TRY_GENERIC(what, , != TPM2_RC_SUCCESS, _try_ret, __LINE__, Tss2_RC_Decode, __VA_ARGS__)
|
#define TRY_TPM2(what, ...) TRY_GENERIC(what, , != TPM2_RC_SUCCESS, _try_ret, __LINE__, Tss2_RC_Decode, __VA_ARGS__)
|
||||||
|
|
||||||
|
|
||||||
|
// https://github.com/tpm2-software/tpm2-tss/blob/49146d926ccb0fd3c3ee064455eb02356e0cdf90/test/integration/esys-create-session-auth.int.c#L218
|
||||||
|
static const constexpr TPMT_SYM_DEF tpm2_session_key{.algorithm = TPM2_ALG_AES, .keyBits = {.aes = 128}, .mode = {.aes = TPM2_ALG_CFB}};
|
||||||
|
|
||||||
|
|
||||||
template <class F>
|
template <class F>
|
||||||
int with_tpm2_session(F && func) {
|
int with_tpm2_session(F && func) {
|
||||||
|
mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||||
|
|
||||||
// https://trustedcomputinggroup.org/wp-content/uploads/TSS_ESAPI_v1p00_r05_pubrev.pdf
|
// https://trustedcomputinggroup.org/wp-content/uploads/TSS_ESAPI_v1p00_r05_pubrev.pdf
|
||||||
// mainly "3.4. The ESAPI Session" and "3.5. ESAPI Use Model"
|
// mainly "3.4. The ESAPI Session" and "3.5. ESAPI Use Model"
|
||||||
// https://tpm2-tss.readthedocs.io/en/latest/group___e_s_y_s___c_o_n_t_e_x_t.html
|
// https://tpm2-tss.readthedocs.io/en/latest/group___e_s_y_s___c_o_n_t_e_x_t.html
|
||||||
@ -29,26 +36,22 @@ int with_tpm2_session(F && func) {
|
|||||||
ESYS_TR tpm2_session = ESYS_TR_NONE;
|
ESYS_TR tpm2_session = ESYS_TR_NONE;
|
||||||
quickscope_wrapper tpm2_session_deleter{[&] { Esys_FlushContext(tpm2_ctx, tpm2_session); }};
|
quickscope_wrapper tpm2_session_deleter{[&] { Esys_FlushContext(tpm2_ctx, tpm2_session); }};
|
||||||
|
|
||||||
{
|
TRY_TPM2("authenticate with TPM", Esys_StartAuthSession(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, nullptr, TPM2_SE_HMAC,
|
||||||
// https://github.com/tpm2-software/tpm2-tss/blob/master/test/integration/esys-create-session-auth.int.c#L218
|
&tpm2_session_key, TPM2_ALG_SHA256, &tpm2_session));
|
||||||
TPMT_SYM_DEF session_key{};
|
|
||||||
session_key.algorithm = TPM2_ALG_AES;
|
|
||||||
session_key.keyBits.aes = 128;
|
|
||||||
session_key.mode.aes = TPM2_ALG_CFB;
|
|
||||||
TRY_TPM2("authenticate with TPM", Esys_StartAuthSession(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, nullptr,
|
|
||||||
TPM2_SE_HMAC, &session_key, TPM2_ALG_SHA256, &tpm2_session));
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(tpm2_ctx, tpm2_session);
|
return func(tpm2_ctx, tpm2_session);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern TPM2B_DATA tpm2_creation_metadata(const char * dataset_name);
|
|
||||||
|
|
||||||
/// Parse a persistent handle name as stored in a ZFS property
|
/// Parse a persistent handle name as stored in a ZFS property
|
||||||
extern int tpm2_parse_handle(const char * dataset_name, const char * handle_s, TPMI_DH_PERSISTENT & handle);
|
extern int tpm2_parse_prop(const char * dataset_name, char * handle_s, TPMI_DH_PERSISTENT & handle, TPML_PCR_SELECTION * pcrs);
|
||||||
|
extern int tpm2_unparse_prop(TPMI_DH_PERSISTENT persistent_handle, const TPML_PCR_SELECTION & pcrs, char ** prop);
|
||||||
|
|
||||||
|
/// `alg:PCR[,PCR]...[+alg:PCR[,PCR]...]...`; all separators can have spaces
|
||||||
|
extern int tpm2_parse_pcrs(char * arg, TPML_PCR_SELECTION & pcrs);
|
||||||
|
|
||||||
extern int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length);
|
extern int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length);
|
||||||
extern int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT & persistent_handle, const TPM2B_DATA & metadata, void * data,
|
extern int tpm2_seal(const char * dataset, ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT & persistent_handle,
|
||||||
size_t data_len);
|
const TPML_PCR_SELECTION & pcrs, bool allow_PCR_or_pass, void * data, size_t data_len);
|
||||||
extern int tpm2_unseal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle, void * data, size_t data_len);
|
extern int tpm2_unseal(const char * dataset, ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle,
|
||||||
|
const TPML_PCR_SELECTION & pcrs, void * data, size_t data_len);
|
||||||
extern int tpm2_free_persistent(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle);
|
extern int tpm2_free_persistent(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle);
|
||||||
|
49
src/zfs.cpp
49
src/zfs.cpp
@ -10,10 +10,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
// Funxion statics pull in libc++'s __cxa_guard_acquire()
|
|
||||||
static nvlist_t * rrargs{};
|
|
||||||
static quickscope_wrapper rrargs_deleter{[] { nvlist_free(rrargs); }};
|
|
||||||
nvlist_t * rewrap_args() {
|
nvlist_t * rewrap_args() {
|
||||||
|
static nvlist_t * rrargs;
|
||||||
if(!rrargs)
|
if(!rrargs)
|
||||||
if(auto err =
|
if(auto err =
|
||||||
[&] {
|
[&] {
|
||||||
@ -33,9 +31,8 @@ nvlist_t * rewrap_args() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static nvlist_t * crrargs{};
|
|
||||||
static quickscope_wrapper crrargs_deleter{[] { nvlist_free(crrargs); }};
|
|
||||||
nvlist_t * clear_rewrap_args() {
|
nvlist_t * clear_rewrap_args() {
|
||||||
|
static nvlist_t * crrargs;
|
||||||
if(!crrargs)
|
if(!crrargs)
|
||||||
if(auto err =
|
if(auto err =
|
||||||
[&] {
|
[&] {
|
||||||
@ -62,6 +59,18 @@ nvlist_t * clear_rewrap_args() {
|
|||||||
TRY_NVL(what, _try_retl); \
|
TRY_NVL(what, _try_retl); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// nvlist_lookup_nvlist takes nvlist_t *, char ** on bookworm and an const nvlist_t *, const char ** on sid!
|
||||||
|
template <class>
|
||||||
|
struct third_arg;
|
||||||
|
|
||||||
|
template <class F, class A, class B, class C>
|
||||||
|
struct third_arg<F(A, B, C)> {
|
||||||
|
using third = C;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int lookup_userprop(zfs_handle_t * in, const char * name, char *& out) {
|
int lookup_userprop(zfs_handle_t * in, const char * name, char *& out) {
|
||||||
// xyz.nabijaczleweli:tzpfms.key:
|
// xyz.nabijaczleweli:tzpfms.key:
|
||||||
// value: '76B0286BEB3FAF57536C47D9A2BAD38157FD522A75A59E72867BBFD6AF167395'
|
// value: '76B0286BEB3FAF57536C47D9A2BAD38157FD522A75A59E72867BBFD6AF167395'
|
||||||
@ -71,11 +80,11 @@ int lookup_userprop(zfs_handle_t * in, const char * name, char *& out) {
|
|||||||
TRY_LOOKUP("look up user property", nvlist_lookup_nvlist(zfs_get_user_props(in), name, &vs));
|
TRY_LOOKUP("look up user property", nvlist_lookup_nvlist(zfs_get_user_props(in), name, &vs));
|
||||||
|
|
||||||
char * source{};
|
char * source{};
|
||||||
TRY_LOOKUP("look up user property source", nvlist_lookup_string(vs, "source", &source));
|
TRY_LOOKUP("look up user property source", nvlist_lookup_string(vs, "source", const_cast<third_arg<decltype(nvlist_lookup_string)>::third>(&source)));
|
||||||
if(!source || strcmp(source, zfs_get_name(in)))
|
if(!source || strcmp(source, zfs_get_name(in)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
TRY_LOOKUP("look up user property value", nvlist_lookup_string(vs, "value", &out));
|
TRY_LOOKUP("look up user property value", nvlist_lookup_string(vs, "value", const_cast<third_arg<decltype(nvlist_lookup_string)>::third>(&out)));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,15 +107,13 @@ int clear_key_props(zfs_handle_t * from) {
|
|||||||
bool ok = false;
|
bool ok = false;
|
||||||
quickscope_wrapper props_deleter{[&] {
|
quickscope_wrapper props_deleter{[&] {
|
||||||
if(!ok)
|
if(!ok)
|
||||||
fprintf(stderr, "You might need to run \"zfs inherit %s %s\" and \"zfs inherit %s %s\"!\n", PROPNAME_BACKEND, zfs_get_name(from), PROPNAME_KEY,
|
fprintf(stderr, gettext("You might need to run \"zfs inherit %s %s\" and \"zfs inherit %s %s\" to fully clear metadata!\n"), PROPNAME_BACKEND,
|
||||||
zfs_get_name(from));
|
zfs_get_name(from), PROPNAME_KEY, zfs_get_name(from));
|
||||||
}};
|
}};
|
||||||
|
|
||||||
TRY("delete tzpfms.backend", zfs_prop_inherit(from, PROPNAME_BACKEND, B_FALSE));
|
TRY("delete tzpfms.backend", zfs_prop_inherit(from, PROPNAME_BACKEND, B_FALSE));
|
||||||
TRY("delete tzpfms.key", zfs_prop_inherit(from, PROPNAME_KEY, B_FALSE));
|
TRY("delete tzpfms.key", zfs_prop_inherit(from, PROPNAME_KEY, B_FALSE));
|
||||||
|
return ok = true, 0;
|
||||||
ok = true;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -114,20 +121,14 @@ int parse_key_props(zfs_handle_t * in, const char * our_backend, char *& handle)
|
|||||||
char * backend{};
|
char * backend{};
|
||||||
TRY_MAIN(lookup_userprop(in, PROPNAME_BACKEND, backend));
|
TRY_MAIN(lookup_userprop(in, PROPNAME_BACKEND, backend));
|
||||||
|
|
||||||
if(!backend) {
|
if(!backend)
|
||||||
fprintf(stderr, "Dataset %s not encrypted with tzpfms!\n", zfs_get_name(in));
|
return fprintf(stderr, gettext("Dataset %s not encrypted with tzpfms!\n"), zfs_get_name(in)), __LINE__;
|
||||||
return __LINE__;
|
if(strcmp(backend, our_backend))
|
||||||
}
|
return fprintf(stderr, gettext("Dataset %s encrypted with tzpfms back-end %s, but we are %s.\n"), zfs_get_name(in), backend, our_backend), __LINE__;
|
||||||
if(strcmp(backend, our_backend)) {
|
|
||||||
fprintf(stderr, "Dataset %s encrypted with tzpfms back-end %s, but we are %s.\n", zfs_get_name(in), backend, our_backend);
|
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRY_MAIN(lookup_userprop(in, PROPNAME_KEY, handle));
|
TRY_MAIN(lookup_userprop(in, PROPNAME_KEY, handle));
|
||||||
if(!handle) {
|
if(!handle)
|
||||||
fprintf(stderr, "Dataset %s missing key data.\n", zfs_get_name(in));
|
return fprintf(stderr, gettext("Dataset %s missing key data.\n"), zfs_get_name(in)), __LINE__;
|
||||||
return __LINE__;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
28
src/zfs.hpp
28
src/zfs.hpp
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
#include <sys/nvpair.h>
|
#include <sys/nvpair.h>
|
||||||
|
// #include <sys/zio_crypt.h>
|
||||||
|
#define WRAPPING_KEY_LEN 32
|
||||||
|
|
||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
|
|
||||||
@ -20,12 +22,10 @@
|
|||||||
|
|
||||||
|
|
||||||
/// Mimic libzfs error output
|
/// Mimic libzfs error output
|
||||||
#define REQUIRE_KEY_LOADED(dataset) \
|
#define REQUIRE_KEY_LOADED(dataset) \
|
||||||
do { \
|
do { \
|
||||||
if(zfs_prop_get_int(dataset, ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE) { \
|
if(zfs_prop_get_int(dataset, ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE) \
|
||||||
fprintf(stderr, "Key change error: Key must be loaded.\n"); \
|
return fprintf(stderr, gettext("Key change error: Key must be loaded.\n")), __LINE__; \
|
||||||
return __LINE__; \
|
|
||||||
} \
|
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
@ -50,14 +50,10 @@ extern int parse_key_props(zfs_handle_t * in, const char * our_backend, char *&
|
|||||||
|
|
||||||
|
|
||||||
/// Rewrap key on on to wrap_key.
|
/// Rewrap key on on to wrap_key.
|
||||||
///
|
extern int change_key(zfs_handle_t * on, const uint8_t (&wrap_key)[WRAPPING_KEY_LEN]);
|
||||||
/// wrap_key must be WRAPPING_KEY_LEN long.
|
|
||||||
extern int change_key(zfs_handle_t * on, const uint8_t * wrap_key);
|
|
||||||
|
|
||||||
/// (Try to) load key wrap_key for for_d.
|
/// (Try to) load key wrap_key for for_d.
|
||||||
///
|
extern int load_key(zfs_handle_t * for_d, const uint8_t (&wrap_key)[WRAPPING_KEY_LEN], bool noop);
|
||||||
/// wrap_key must be WRAPPING_KEY_LEN long.
|
|
||||||
extern int load_key(zfs_handle_t * for_d, const uint8_t * wrap_key, bool noop);
|
|
||||||
|
|
||||||
/// Check back-end integrity; if the previous backend matches this_backend, run func(); otherwise warn.
|
/// Check back-end integrity; if the previous backend matches this_backend, run func(); otherwise warn.
|
||||||
template <class F>
|
template <class F>
|
||||||
@ -67,10 +63,14 @@ int verify_backend(zfs_handle_t * on, const char * this_backend, F && func) {
|
|||||||
TRY_MAIN(lookup_userprop(on, PROPNAME_KEY, previous_handle));
|
TRY_MAIN(lookup_userprop(on, PROPNAME_KEY, previous_handle));
|
||||||
|
|
||||||
if(!!previous_backend ^ !!previous_handle)
|
if(!!previous_backend ^ !!previous_handle)
|
||||||
fprintf(stderr, "Inconsistent tzpfms metadata for %s: back-end is %s, but handle is %s?\n", zfs_get_name(on), previous_backend, previous_handle);
|
// dataset name: (null), 0A123...
|
||||||
|
// dataset name: TPM1.X, (null)
|
||||||
|
fprintf(stderr, gettext("Inconsistent tzpfms metadata for %s: back-end is %s, but handle is %s?\n"), zfs_get_name(on), previous_backend, previous_handle);
|
||||||
else if(previous_backend && previous_handle) {
|
else if(previous_backend && previous_handle) {
|
||||||
if(strcmp(previous_backend, this_backend))
|
if(strcmp(previous_backend, this_backend))
|
||||||
fprintf(stderr, "Dataset %s was encrypted with tzpfms back-end %s before, but we are %s. You will have to free handle %s for back-end %s manually!\n",
|
// dataset name: TPM1.X, TPM2. 0A1234..., TPM1.X
|
||||||
|
fprintf(stderr,
|
||||||
|
gettext("Dataset %s was encrypted with tzpfms back-end %s before, but we are %s. You will have to free handle %s for back-end %s manually!\n"),
|
||||||
zfs_get_name(on), previous_backend, this_backend, previous_handle, previous_backend);
|
zfs_get_name(on), previous_backend, this_backend, previous_handle, previous_backend);
|
||||||
else
|
else
|
||||||
func(previous_handle);
|
func(previous_handle);
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
#include "zfs.hpp"
|
#include "zfs.hpp"
|
||||||
|
|
||||||
#include <libzfs.h>
|
|
||||||
// #include <sys/zio_crypt.h>
|
|
||||||
#define WRAPPING_KEY_LEN 32
|
|
||||||
|
|
||||||
|
|
||||||
template <class F>
|
template <class F>
|
||||||
static int with_stdin_at_buffer(const void * buf, size_t buf_len, F && func) {
|
static int with_stdin_at_buffer(const void * buf, size_t buf_len, F && func) {
|
||||||
@ -20,29 +16,32 @@ static int with_stdin_at_buffer(const void * buf, size_t buf_len, F && func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int change_key(zfs_handle_t * on, const uint8_t * wrap_key) {
|
int change_key(zfs_handle_t * on, const uint8_t (&wrap_key)[WRAPPING_KEY_LEN]) {
|
||||||
/// zfs_crypto_rewrap() with "prompt" reads from stdin, but not if it's a TTY;
|
/// zfs_crypto_rewrap() with "prompt" reads from stdin, but not if it's a TTY;
|
||||||
/// this user-proofs the set-up, and means we don't have to touch the filesysten:
|
/// this user-proofs the set-up, and means we don't have to touch the filesysten:
|
||||||
/// instead, get an FD, write the raw key data there, dup() it onto stdin,
|
/// instead, get an FD, write the raw key data there, dup() it onto stdin,
|
||||||
/// let libzfs read it, then restore stdin
|
/// let libzfs read it, then restore stdin
|
||||||
|
|
||||||
return with_stdin_at_buffer(wrap_key, WRAPPING_KEY_LEN, [&] {
|
return with_stdin_at_buffer(wrap_key, sizeof(wrap_key), [&] {
|
||||||
if(zfs_crypto_rewrap(on, TRY_PTR("get rewrap args", rewrap_args()), B_FALSE))
|
if(zfs_crypto_rewrap(on, TRY_PTR("get rewrap args", rewrap_args()), B_FALSE))
|
||||||
return __LINE__; // Error printed by libzfs
|
return __LINE__; // Error printed by libzfs
|
||||||
else
|
else
|
||||||
printf("Key for %s changed\n", zfs_get_name(on));
|
printf(gettext("Key for %s changed\n"), zfs_get_name(on));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int load_key(zfs_handle_t * for_d, const uint8_t * wrap_key, bool noop) {
|
int load_key(zfs_handle_t * for_d, const uint8_t (&wrap_key)[WRAPPING_KEY_LEN], bool noop) {
|
||||||
return with_stdin_at_buffer(wrap_key, WRAPPING_KEY_LEN, [&] {
|
return with_stdin_at_buffer(wrap_key, sizeof(wrap_key), [&] {
|
||||||
if(zfs_crypto_load_key(for_d, noop ? B_TRUE : B_FALSE, nullptr))
|
if(zfs_crypto_load_key(for_d, noop ? B_TRUE : B_FALSE, nullptr))
|
||||||
return __LINE__; // Error printed by libzfs
|
return __LINE__; // Error printed by libzfs
|
||||||
else
|
else //
|
||||||
printf("Key for %s %s\n", zfs_get_name(for_d), noop ? "OK" : "loaded");
|
if(noop)
|
||||||
|
printf(gettext("Key for %s OK\n"), zfs_get_name(for_d));
|
||||||
|
else
|
||||||
|
printf(gettext("Key for %s loaded\n"), zfs_get_name(for_d));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
@ -25,21 +25,36 @@
|
|||||||
"name": "Source",
|
"name": "Source",
|
||||||
"path": "src"
|
"path": "src"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"follow_symlinks": true,
|
||||||
|
"name": "Misc source",
|
||||||
|
"path": "contrib"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"follow_symlinks": true,
|
"follow_symlinks": true,
|
||||||
"name": "Initrd plug-ins",
|
"name": "Initrd plug-ins",
|
||||||
"path": "initrd"
|
"path": "initrd"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"follow_symlinks": true,
|
||||||
|
"name": "Init system plug-ins",
|
||||||
|
"path": "init.d"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"follow_symlinks": true,
|
"follow_symlinks": true,
|
||||||
"name": "Manpages",
|
"name": "Manpages",
|
||||||
"path": "man"
|
"path": "man"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"follow_symlinks": true,
|
||||||
|
"name": "Localisation",
|
||||||
|
"path": "po"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"follow_symlinks": true,
|
"follow_symlinks": true,
|
||||||
"name": "Build scripts",
|
"name": "Build scripts",
|
||||||
"path": ".",
|
"path": ".",
|
||||||
"file_include_patterns": [".build.yml", "*Makefile", "*.awk"],
|
"file_include_patterns": [".build.yml", "*Makefile*", "*.awk"],
|
||||||
"folder_exclude_patterns": ["*"]
|
"folder_exclude_patterns": ["*"]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user