Add support for tpm2-tools 4.0
The tpm2-tools package in Fedora 32 was updated to version 4.0, but clevis still only has 3.0 support. Support for the latest release is in the works and will probable make it to the next clevis release. But until that happens, let's backport the patches that add tpm2-tools 4.0 support for clevis so it continues to work in Fedora 32. Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
This commit is contained in:
parent
03eb6fb719
commit
0f1aa4e16b
57
clevis-encrypt-tpm2-fix-TPM-object-attributes.patch
Normal file
57
clevis-encrypt-tpm2-fix-TPM-object-attributes.patch
Normal file
@ -0,0 +1,57 @@
|
||||
From b48c1096766f8fd1a9edc1ac5c1c0eea16dc2e5b Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Witschel <diabonas@gmx.de>
|
||||
Date: Fri, 6 Sep 2019 15:04:35 +0200
|
||||
Subject: [PATCH] clevis-encrypt-tpm2: fix TPM object attributes
|
||||
|
||||
Fix two problems with the current specification of the object
|
||||
attributes:
|
||||
|
||||
1. According to the Trusted Platform Module Library Family 2.0
|
||||
Specification - Part 2: Structures, Revision 1.38, Section 8.3.3.5,
|
||||
sensitiveDataOrigin shall not be set for data objects:
|
||||
|
||||
NOTE 3 The inSensitive.sensitive.data.size parameter may not be zero for
|
||||
a data object so sensitiveDataOrigin is required to be CLEAR. A data
|
||||
object has type = TPM_ALG_KEYEDHASH and its sign and decrypt attributes
|
||||
are CLEAR.
|
||||
|
||||
tpm2-tools 3.X silently removes the inconsistent 'sensitivedataorigin'
|
||||
attribute.
|
||||
|
||||
2. If the key is sealed against a certain PCR configuration,
|
||||
'userwithauth' needs to be clear so that the key cannot be unsealed with
|
||||
the default empty authorisation password. On the other hand, if the key
|
||||
is not sealed against a specific PCR configuration, 'userwithauth' must
|
||||
be set because there is no PCR policy to fulfil.
|
||||
|
||||
tpm2-tools 3.X silently adds 'userwithauth' if no policy is specified
|
||||
for tpm2_create.
|
||||
---
|
||||
src/pins/tpm2/clevis-encrypt-tpm2 | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/pins/tpm2/clevis-encrypt-tpm2 b/src/pins/tpm2/clevis-encrypt-tpm2
|
||||
index c70187d7f4c..a7f333269bc 100755
|
||||
--- a/src/pins/tpm2/clevis-encrypt-tpm2
|
||||
+++ b/src/pins/tpm2/clevis-encrypt-tpm2
|
||||
@@ -24,7 +24,7 @@ auth="o"
|
||||
# Algorithm type must be keyedhash for object with user provided sensitive data.
|
||||
alg_create_key="keyedhash"
|
||||
# Attributes for the created TPM2 object with the JWK as sensitive data.
|
||||
-obj_attr="fixedtpm|fixedparent|sensitivedataorigin|noda|adminwithpolicy"
|
||||
+obj_attr="fixedtpm|fixedparent|noda|adminwithpolicy"
|
||||
|
||||
function on_exit() {
|
||||
if ! rm -rf $TMP; then
|
||||
@@ -130,6 +130,8 @@ if [ -n "$pcr_ids" ]; then
|
||||
fi
|
||||
|
||||
policy_options="-L $TMP/pcr.policy"
|
||||
+else
|
||||
+ obj_attr="$obj_attr|userwithauth"
|
||||
fi
|
||||
|
||||
if ! tpm2_create -Q -g "$hash" -G "$alg_create_key" -c $TMP/primary.context -u $TMP/jwk.pub \
|
||||
--
|
||||
2.21.0
|
||||
|
64
clevis-pin-tpm2-module-setup.sh-test-for-required-bi.patch
Normal file
64
clevis-pin-tpm2-module-setup.sh-test-for-required-bi.patch
Normal file
@ -0,0 +1,64 @@
|
||||
From a9177d2dd4deadc3fa65ace235f4b35c43760fa4 Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Witschel <diabonas@gmx.de>
|
||||
Date: Fri, 6 Sep 2019 15:20:08 +0200
|
||||
Subject: [PATCH] clevis-pin-tpm2/module-setup.sh: test for required binaries
|
||||
in check()
|
||||
|
||||
If some of the dependencies are missing, dracut will now fail with a
|
||||
warning of the form
|
||||
|
||||
dracut: dracut module 'clevis-pin-tpm2' will not be installed, because command '...' could not be found!
|
||||
|
||||
This is much better than silently failing during module installation.
|
||||
---
|
||||
src/luks/systemd/dracut/module-setup.sh.in | 27 ++++++----------------
|
||||
1 file changed, 7 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/src/luks/systemd/dracut/module-setup.sh.in b/src/luks/systemd/dracut/module-setup.sh.in
|
||||
index 399e468e8e0..2dcdb68549d 100755
|
||||
--- a/src/luks/systemd/dracut/module-setup.sh.in
|
||||
+++ b/src/luks/systemd/dracut/module-setup.sh.in
|
||||
@@ -18,6 +18,11 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
+check() {
|
||||
+ require_binaries clevis-decrypt-tpm2 tpm2_createprimary tpm2_pcrlist tpm2_unseal tpm2_load || return 1
|
||||
+ return 0
|
||||
+}
|
||||
+
|
||||
depends() {
|
||||
echo crypt systemd
|
||||
return 0
|
||||
@@ -48,26 +53,8 @@ install() {
|
||||
jose \
|
||||
nc
|
||||
|
||||
- for cmd in clevis-decrypt-tpm2 \
|
||||
- tpm2_createprimary \
|
||||
- tpm2_pcrlist \
|
||||
- tpm2_unseal \
|
||||
- tpm2_load; do
|
||||
-
|
||||
- if ! find_binary "$cmd" &>/dev/null; then
|
||||
- ((ret++))
|
||||
- fi
|
||||
- done
|
||||
-
|
||||
- if (($ret == 0)); then
|
||||
- inst_multiple clevis-decrypt-tpm2 \
|
||||
- tpm2_createprimary \
|
||||
- tpm2_pcrlist \
|
||||
- tpm2_unseal \
|
||||
- tpm2_load
|
||||
- inst_libdir_file "libtss2-tcti-device.so*"
|
||||
- fi
|
||||
-
|
||||
+ inst_multiple clevis-decrypt-tpm2 tpm2_createprimary tpm2_pcrlist tpm2_unseal tpm2_load
|
||||
+ inst_libdir_file "libtss2-tcti-device.so*"
|
||||
dracut_need_initqueue
|
||||
}
|
||||
|
||||
--
|
||||
2.21.0
|
||||
|
10
clevis.spec
10
clevis.spec
@ -2,7 +2,7 @@
|
||||
|
||||
Name: clevis
|
||||
Version: 11
|
||||
Release: 6%{?dist}
|
||||
Release: 7%{?dist}
|
||||
Summary: Automated decryption framework
|
||||
|
||||
License: GPLv3+
|
||||
@ -13,6 +13,11 @@ Patch0: Delete-remaining-references-to-the-removed-http-pin.patch
|
||||
Patch1: Install-cryptsetup-and-tpm2_pcrlist-in-the-initramfs.patch
|
||||
Patch2: Add-device-TCTI-library-to-the-initramfs.patch
|
||||
Patch3: 0001-Drop-rd.neednet-1-for-the-time-being-so-tpm2-unlock-.patch
|
||||
# Support for tpm2-tools 4.0, backported from the following pull-request:
|
||||
# https://github.com/latchset/clevis/pull/114
|
||||
Patch4: clevis-encrypt-tpm2-fix-TPM-object-attributes.patch
|
||||
Patch5: clevis-pin-tpm2-module-setup.sh-test-for-required-bi.patch
|
||||
Patch6: pins-tpm2-add-support-for-tpm2-tools-4.X.patch
|
||||
|
||||
BuildRequires: gcc
|
||||
BuildRequires: meson
|
||||
@ -160,6 +165,9 @@ exit 0
|
||||
%attr(4755, root, root) %{_libexecdir}/%{name}-luks-udisks2
|
||||
|
||||
%changelog
|
||||
* Fri Sep 06 2019 Javier Martinez Canillas <javierm@redhat.com> - 11-7
|
||||
- Add support for tpm2-tools 4.0
|
||||
|
||||
* Wed Jul 24 2019 Fedora Release Engineering <releng@fedoraproject.org> - 11-6
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
|
||||
|
||||
|
236
pins-tpm2-add-support-for-tpm2-tools-4.X.patch
Normal file
236
pins-tpm2-add-support-for-tpm2-tools-4.X.patch
Normal file
@ -0,0 +1,236 @@
|
||||
From 7d4425dc1b96f4a0efeb4383c6a32ab664d7e3cc Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Witschel <diabonas@gmx.de>
|
||||
Date: Fri, 6 Sep 2019 15:27:14 +0200
|
||||
Subject: [PATCH] pins/tpm2: add support for tpm2-tools 4.X
|
||||
|
||||
tpm2-tools renamed tpm2_pcrlist to tpm2_pcrread and changed a lot of
|
||||
option names. Only the new unified environment variable TPM2TOOLS_TCTI
|
||||
is supported, TPM2TOOLS_TCTI_NAME and TPM2TOOLS_DEVICE_FILE are no
|
||||
longer recognised. Determine the tpm2-tools version from the output of
|
||||
$(tpm2_createprimary -v) and switch accordingly.
|
||||
---
|
||||
src/luks/systemd/dracut/module-setup.sh.in | 6 ++-
|
||||
src/pins/tpm2/clevis-decrypt-tpm2 | 40 ++++++++++++++----
|
||||
src/pins/tpm2/clevis-encrypt-tpm2 | 48 ++++++++++++++++++----
|
||||
src/pins/tpm2/meson.build | 5 ++-
|
||||
4 files changed, 77 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/src/luks/systemd/dracut/module-setup.sh.in b/src/luks/systemd/dracut/module-setup.sh.in
|
||||
index 2dcdb68549d..89cc42cd226 100755
|
||||
--- a/src/luks/systemd/dracut/module-setup.sh.in
|
||||
+++ b/src/luks/systemd/dracut/module-setup.sh.in
|
||||
@@ -19,7 +19,8 @@
|
||||
#
|
||||
|
||||
check() {
|
||||
- require_binaries clevis-decrypt-tpm2 tpm2_createprimary tpm2_pcrlist tpm2_unseal tpm2_load || return 1
|
||||
+ require_binaries clevis-decrypt-tpm2 tpm2_createprimary tpm2_unseal tpm2_load || return 1
|
||||
+ require_any_binary tpm2_pcrread tpm2_pcrlist || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -53,7 +54,8 @@ install() {
|
||||
jose \
|
||||
nc
|
||||
|
||||
- inst_multiple clevis-decrypt-tpm2 tpm2_createprimary tpm2_pcrlist tpm2_unseal tpm2_load
|
||||
+ inst_multiple clevis-decrypt-tpm2 tpm2_createprimary tpm2_unseal tpm2_load
|
||||
+ inst_multiple -o tpm2_pcrread tpm2_pcrlist
|
||||
inst_libdir_file "libtss2-tcti-device.so*"
|
||||
dracut_need_initqueue
|
||||
}
|
||||
diff --git a/src/pins/tpm2/clevis-decrypt-tpm2 b/src/pins/tpm2/clevis-decrypt-tpm2
|
||||
index 4fc1c5858e3..e603e9a7275 100755
|
||||
--- a/src/pins/tpm2/clevis-decrypt-tpm2
|
||||
+++ b/src/pins/tpm2/clevis-decrypt-tpm2
|
||||
@@ -37,16 +37,22 @@ if [ -t 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
-TPM2TOOLS_INFO=`tpm2_pcrlist -v`
|
||||
+TPM2TOOLS_INFO="$(tpm2_createprimary -v)"
|
||||
|
||||
-if [[ $TPM2TOOLS_INFO != *version=\"3.* ]]; then
|
||||
- echo "The tpm2 pin requires tpm2-tools version 3" >&2
|
||||
+match='version="(.)\.'
|
||||
+[[ $TPM2TOOLS_INFO =~ $match ]] && TPM2TOOLS_VERSION="${BASH_REMATCH[1]}"
|
||||
+if [[ $TPM2TOOLS_VERSION != 3 ]] && [[ $TPM2TOOLS_VERSION != 4 ]]; then
|
||||
+ echo "The tpm2 pin requires tpm2-tools version 3 or 4" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
+# Old environment variables for tpm2-tools 3.0
|
||||
export TPM2TOOLS_TCTI_NAME=device
|
||||
export TPM2TOOLS_DEVICE_FILE=`ls /dev/tpmrm? 2>/dev/null`
|
||||
|
||||
+# New environment variable for tpm2-tools >= 3.1
|
||||
+export TPM2TOOLS_TCTI="$TPM2TOOLS_TCTI_NAME:${TPM2TOOLS_DEVICE_FILE[0]}"
|
||||
+
|
||||
if [ -z "${TPM2TOOLS_DEVICE_FILE[0]}" ]; then
|
||||
echo "A TPM2 device with the in-kernel resource manager is needed!" >&2
|
||||
exit 1
|
||||
@@ -98,9 +104,10 @@ trap 'on_exit' EXIT
|
||||
|
||||
pcr_ids=`jose fmt -j- -Og clevis -g tpm2 -g pcr_ids -Su- <<< "$jhd"` || true
|
||||
|
||||
+pcr_spec=''
|
||||
if [ -n "$pcr_ids" ]; then
|
||||
pcr_bank=`jose fmt -j- -Og clevis -g tpm2 -g pcr_bank -Su- <<< "$jhd"`
|
||||
- policy_options="-L $pcr_bank:$pcr_ids"
|
||||
+ pcr_spec="$pcr_bank:$pcr_ids"
|
||||
fi
|
||||
|
||||
if ! `jose b64 dec -i- -O $TMP/jwk.pub <<< "$jwk_pub"`; then
|
||||
@@ -113,19 +120,34 @@ if ! `jose b64 dec -i- -O $TMP/jwk.priv <<< "$jwk_priv"`; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
-if ! tpm2_createprimary -Q -H "$auth" -g "$hash" -G "$key" \
|
||||
- -C $TMP/primary.context 2>/dev/null; then
|
||||
+case "$TPM2TOOLS_VERSION" in
|
||||
+ 3) tpm2_createprimary -Q -H "$auth" -g "$hash" -G "$key" -C "$TMP"/primary.context || fail=$?;;
|
||||
+ 4) tpm2_createprimary -Q -C "$auth" -g "$hash" -G "$key" -c "$TMP"/primary.context || fail=$?;;
|
||||
+ *) fail=1;;
|
||||
+esac
|
||||
+if [ -n "$fail" ]; then
|
||||
echo "Creating TPM2 primary key failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
-if ! tpm2_load -Q -c $TMP/primary.context -u $TMP/jwk.pub -r $TMP/jwk.priv \
|
||||
- -C $TMP/load.context 2>/dev/null; then
|
||||
+case "$TPM2TOOLS_VERSION" in
|
||||
+ 3) tpm2_load -Q -c "$TMP"/primary.context -u "$TMP"/jwk.pub -r "$TMP"/jwk.priv \
|
||||
+ -C "$TMP"/load.context || fail=$?;;
|
||||
+ 4) tpm2_load -Q -C "$TMP"/primary.context -u "$TMP"/jwk.pub -r "$TMP"/jwk.priv \
|
||||
+ -c "$TMP"/load.context || fail=$?;;
|
||||
+ *) fail=1;;
|
||||
+esac
|
||||
+if [ -n "$fail" ]; then
|
||||
echo "Loading jwk to TPM2 failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
-if ! jwk=`tpm2_unseal -c $TMP/load.context $policy_options 2>/dev/null`; then
|
||||
+case "$TPM2TOOLS_VERSION" in
|
||||
+ 3) jwk="$(tpm2_unseal -c "$TMP"/load.context ${pcr_spec:+-L $pcr_spec})" || fail=$?;;
|
||||
+ 4) jwk="$(tpm2_unseal -c "$TMP"/load.context ${pcr_spec:+-p pcr:$pcr_spec})" || fail=$?;;
|
||||
+ *) fail=1;;
|
||||
+esac
|
||||
+if [ -n "$fail" ]; then
|
||||
echo "Unsealing jwk from TPM failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
diff --git a/src/pins/tpm2/clevis-encrypt-tpm2 b/src/pins/tpm2/clevis-encrypt-tpm2
|
||||
index a7f333269bc..90321963d1e 100755
|
||||
--- a/src/pins/tpm2/clevis-encrypt-tpm2
|
||||
+++ b/src/pins/tpm2/clevis-encrypt-tpm2
|
||||
@@ -59,16 +59,22 @@ if [ -t 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
-TPM2TOOLS_INFO=`tpm2_pcrlist -v`
|
||||
+TPM2TOOLS_INFO="$(tpm2_createprimary -v)"
|
||||
|
||||
-if [[ $TPM2TOOLS_INFO != *version=\"3.* ]]; then
|
||||
- echo "The tpm2 pin requires tpm2-tools version 3" >&2
|
||||
+match='version="(.)\.'
|
||||
+[[ $TPM2TOOLS_INFO =~ $match ]] && TPM2TOOLS_VERSION="${BASH_REMATCH[1]}"
|
||||
+if [[ $TPM2TOOLS_VERSION != 3 ]] && [[ $TPM2TOOLS_VERSION != 4 ]]; then
|
||||
+ echo "The tpm2 pin requires tpm2-tools version 3 or 4" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
+# Old environment variables for tpm2-tools 3.0
|
||||
export TPM2TOOLS_TCTI_NAME=device
|
||||
export TPM2TOOLS_DEVICE_FILE=`ls /dev/tpmrm? 2>/dev/null`
|
||||
|
||||
+# New environment variable for tpm2-tools >= 3.1
|
||||
+export TPM2TOOLS_TCTI="$TPM2TOOLS_TCTI_NAME:${TPM2TOOLS_DEVICE_FILE[0]}"
|
||||
+
|
||||
if [ -z "${TPM2TOOLS_DEVICE_FILE[0]}" ]; then
|
||||
echo "A TPM2 device with the in-kernel resource manager is needed!" >&2
|
||||
exit 1
|
||||
@@ -106,14 +112,25 @@ fi
|
||||
|
||||
trap 'on_exit' EXIT
|
||||
|
||||
-if ! tpm2_createprimary -Q -H "$auth" -g "$hash" -G "$key" -C $TMP/primary.context; then
|
||||
+case "$TPM2TOOLS_VERSION" in
|
||||
+ 3) tpm2_createprimary -Q -H "$auth" -g "$hash" -G "$key" -C "$TMP"/primary.context || fail=$?;;
|
||||
+ 4) tpm2_createprimary -Q -C "$auth" -g "$hash" -G "$key" -c "$TMP"/primary.context || fail=$?;;
|
||||
+ *) fail=1;;
|
||||
+esac
|
||||
+if [ -n "$fail" ]; then
|
||||
echo "Creating TPM2 primary key failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
+policy_options=()
|
||||
if [ -n "$pcr_ids" ]; then
|
||||
if [ -z "$pcr_digest" ]; then
|
||||
- if ! tpm2_pcrlist -Q -L "$pcr_bank":"$pcr_ids" -o $TMP/pcr.digest; then
|
||||
+ case "$TPM2TOOLS_VERSION" in
|
||||
+ 3) tpm2_pcrlist -Q -L "$pcr_bank":"$pcr_ids" -o "$TMP"/pcr.digest || fail=$?;;
|
||||
+ 4) tpm2_pcrread -Q "$pcr_bank":"$pcr_ids" -o "$TMP"/pcr.digest || fail=$?;;
|
||||
+ *) fail=1;;
|
||||
+ esac
|
||||
+ if [ -n "$fail" ]; then
|
||||
echo "Creating PCR hashes file failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -124,18 +141,31 @@ if [ -n "$pcr_ids" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
- if ! tpm2_createpolicy -Q -P -L "$pcr_bank":"$pcr_ids" -F $TMP/pcr.digest -f $TMP/pcr.policy; then
|
||||
+ case "$TPM2TOOLS_VERSION" in
|
||||
+ 3) tpm2_createpolicy -Q -g "$hash" -P -L "$pcr_bank":"$pcr_ids" \
|
||||
+ -F "$TMP"/pcr.digest -f "$TMP"/pcr.policy || fail=$?;;
|
||||
+ 4) tpm2_createpolicy -Q -g "$hash" --policy-pcr -l "$pcr_bank":"$pcr_ids" \
|
||||
+ -f "$TMP"/pcr.digest -L "$TMP"/pcr.policy || fail=$?;;
|
||||
+ *) fail=1;;
|
||||
+ esac
|
||||
+ if [ -n "$fail" ]; then
|
||||
echo "create policy fail, please check the environment or parameters!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- policy_options="-L $TMP/pcr.policy"
|
||||
+ policy_options+=(-L "$TMP/pcr.policy")
|
||||
else
|
||||
obj_attr="$obj_attr|userwithauth"
|
||||
fi
|
||||
|
||||
-if ! tpm2_create -Q -g "$hash" -G "$alg_create_key" -c $TMP/primary.context -u $TMP/jwk.pub \
|
||||
- -r $TMP/jwk.priv -A "$obj_attr" $policy_options -I- <<< "$jwk"; then
|
||||
+case "$TPM2TOOLS_VERSION" in
|
||||
+ 3) tpm2_create -Q -g "$hash" -G "$alg_create_key" -c "$TMP"/primary.context -u "$TMP"/jwk.pub \
|
||||
+ -r "$TMP"/jwk.priv -A "$obj_attr" "${policy_options[@]}" -I- <<< "$jwk" || fail=$?;;
|
||||
+ 4) tpm2_create -Q -g "$hash" -C "$TMP"/primary.context -u "$TMP"/jwk.pub \
|
||||
+ -r "$TMP"/jwk.priv -a "$obj_attr" "${policy_options[@]}" -i- <<< "$jwk" || fail=$?;;
|
||||
+ *) fail=1;;
|
||||
+esac
|
||||
+if [ -n "$fail" ]; then
|
||||
echo "Creating TPM2 object for jwk failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
diff --git a/src/pins/tpm2/meson.build b/src/pins/tpm2/meson.build
|
||||
index 8121ec49cb3..4041a9a16d4 100644
|
||||
--- a/src/pins/tpm2/meson.build
|
||||
+++ b/src/pins/tpm2/meson.build
|
||||
@@ -1,8 +1,9 @@
|
||||
-cmds = ['createprimary', 'pcrlist', 'createpolicy', 'create', 'load', 'unseal']
|
||||
+cmds = ['tpm2_createprimary', ['tpm2_pcrread', 'tpm2_pcrlist'],
|
||||
+ 'tpm2_createpolicy', 'tpm2_create', 'tpm2_load', 'tpm2_unseal']
|
||||
|
||||
all = true
|
||||
foreach cmd : cmds
|
||||
- all = all and find_program('tpm2_' + cmd, required: false).found()
|
||||
+ all = all and find_program(cmd, required: false).found()
|
||||
endforeach
|
||||
|
||||
if all
|
||||
--
|
||||
2.21.0
|
||||
|
Loading…
Reference in New Issue
Block a user