Open nicowilliams opened 3 years ago
Ok! I've a trivial test script to demo sbin/tpm2-policy
.
I've also added long options for bash scripts. I should probably separate getopts_long()
into its own commit though.
Also, I should make a policyor()
shell function so that alternations can be handled without having to run the policyor
restricted bash scripting wrapper.
So, I have explored enough that I can write a jq+bash+tpm2-tools thing now. A policy consists of: a) parameters, b) some (possibly none) bindings for those parameters, possibly referenced by URI, or possibly to be prompted for, c) the actual policy commands. Because one might want to create a policy as just a reference to another but with some parameters bound, a policy should itself be possible to refer to either by value or by URI, and, really, any subset of the policy should be possible to express either directly or by reference.
So we're looking at something like:
{
"name":"...",
"params":[...],
"bindings":[...],
"policy":"<URI>"
}
or
{
"name":"...",
"params":[...],
"bindings":[...],
"policy":[...]
}
Each policy command would be either a string containing a URI or an object naming the policy command and its command parameters, and each command parameter could be inlined or a reference to a parameter to the policy, or -in the case of PolicyOr
- a sub-policy. URIs could be relative, in which case they might be defined elsewhere in the same JSON text or be a reference to another policy provided by the caller or lying around alongside the referrer.
Each parameter would be an object with a name and a type, and possibly some default content.
Each binding would be an object with a name of a parameter and contents for it.
Parameter types would include:
TPM2_LoadExternal()
TPM2B_PUBLIC
values, base64-encoded, for loading with TPM2_LoadExternal()
TPM2_Duplicate()
, base64-encoded, for loading with TPM2_Import()
and TPM2_load()
TPM2_VerifySignature()
TPM2_VerifySignature()
, TPM2_PolicySigned() or
TPM2_PolicySecret()`authValue
sPerhaps a parameter could specify some default content but not all (e.g., hash and signature algorithms, but not signatures, naturally), so that a binding need only provide the missing parts.
References to parameters would become appropriate values of the appropriate types in the actual policy commands. E.g., object contexts might get loaded and referred to by handle, tickets might be passed in by value, etc.
PolicySigned
and PolicySecret
commands could be replaced automatically with PolicyTicket
as needed.
Perhaps a policy could refer to or include another and specify alternatives for PolicyOr
commands in the other. For example, one might have a policy that allows for N
users to authenticate with a smartcard or password, say, and one might then define a policy that refers to that policy and specifies the PolicyOr
alternatives identifying one particular user.
There would be two evaluation modes: trial (to compute a policyDigest
for a policy), and authorization (to satisfy a policy and get access to a protected resource). For trial sessions fewer parameters may need bindings at evaluation time than for authorization sessions -- the evaluator needs to understand this specifically, and more generally needs to know which parameters need bindings and which don't. Indeed, an evaluator might choose alternatives to evaluate based on the parameters for which there are bindings, that or it must be told explicitly. The evaluator should then import and/or load all the TPM objects specified, save their contexts as a needed, flush TPM objects as needed, verify signatures, and load saved objects as needed right before executing policy commands that refer to them.
Parameters have to go with the policy definitions. Default bindings can too. External bindings would be provided by other policies or command-line arguments.
Internal parameters would be a thing (tickets, for example, things output by TPM commands internal to the policy that are needed by other commands).
So maybe something more like:
{
"name":"...",
"bindings":[...],
"policy": "<URI>"
}
or
{
"name":"...",
"bindings":[...],
"policy": {
"parameters":[...],
"policyDef":[...]
}
A policyDef
would be an array of:
{
"command":"policyX",
"cp<Name>":"<value> or else an object with a param reference",
"...":"...",
"rp<Name>":{"name":"<internal-param-name>","display":true},
"...":...
}
A parameter reference would be:
{ "name":"<parameter-name>", "sub":"<sub-name>" }
where <sub-name>
might be something like "name"
, "hash-alg"
, "sign-alg"
, etc., for referring to an aspect of a parameter.
Policy holes (PolicyAuthorize
, PolicyAuthorizeNV
, and PolicyOr
) have to be first. The alternatives to PolicyOr
would be policies, either defined inline or referenced by URI.
A parameter needs:
We might want to make things like hash and signature algorithms ancillary properties of a parameter naming a key, or we might want to make them separate parameters. I think the former.
{
"name":"...",
"internal":false,
"type":"TPM2B_PUBLIC",
"default":"<base64-encoded>"
}
A binding might be a prompt for a value or an actual value. If the parameter is something like a cryptographic key, then the value might be multipart (one part might be the key, another might be the various associated algorithms).
I'm also thinking that this rbash prototype may be good enough for now because it's... simple and easy.
I've pushed a WIP of take 3, this time using JSON and jq, all inspired by take 2.
https://trustedcomputinggroup.org/wp-content/uploads/TSS_JSON_Policy_v0p7_r08_pub.pdf
The code in take 3 is not implementing that.
Let's try a different approach to expressing complex policies:
Policies as restricted bash scripts...
...that can do very little besides evaluate TPM policies, and which policy scripts are invoked via...
...a driver script,
sbin/tpm2-policy
, that sets up the environment for running the policy, and takes optional additional artifacts to make available to the policy, then runs it via/bin/rbash
.Policy script would run in the following restricted environment:
a temp dir as the current directory, with
$TMPDIR
set to$PWD
and in which all the necessary artifacts, including the policy script itself, shall have been placedvarious TPM2_POLICY... env vars, mainly TPM2_POLICY_SESSION (naming a saved TPM authorization context file)
$PATH
set to have just one element: anrbin
(see below).Artifacts can be supplied by the caller of
sbin/tpm2-policy
, or they can be embedded in the policy script as here documents, which can be extracted into the current directory (without clobbering any files) via therbin/writeartifact
program.All artifacts end up being files in
.
.There are two types of artifacts:
"constant artifacts" that are either embedded in the script or provided along with the script and named via the
-F
/--file
argument tosbin/tpm2-policy
:loadexternal
ed for use withpolicysigned
andpolicyauthorize
policyticket
and suchimport
ing andload
ingpolicyRef
valuesdynamic artifacts that are constructed while the policy is running, such as:
saved object context files for signer keys for
policysigned
andpolicyauthorize
tickets from
verifysignature
,policysecret
, andpolicysigned
timeout files
etc.
The "rbin" directory in the PATH for the execution of policy scripts, with:
tpm2_policy...
commandstpm2_policyor
is a bit special, naturallytpm2
,tpm2_import
,tpm2_loadexternal
andtpm2_verifysignature
, and possibly otherswriteartifact
-- a wrapper aroundxxd
andcat
for creating files from stdin so that artifacts can be embedded as here documents in the scriptThis way policies get all the expressive power of bash, and access to all the functionality of tpm2-tools' policy commands.
TBD:
Test, debug, test, ...
Maybe allow execution of
sbin/tpm2-recv
by wrapping it in the rbin?Add sample policies that are interesting, like using a combination of
policysigned
andpolicyauthorize
andcurl
(outside the policy script) to execute an external script that varies at runtime, or policies that use different passwords for N different users, or which usepolicysigned
to let some other entity authenticate N different users (usingpolicyRef
to name them, say), policies requiring golden PCRs OR external policy, etc. Showcasepolicyor
!It'd be very nice to be able to have a policy like this:
Perhaps with multiple superadmin authValues via
policyor
.One possible and interesting policy would be to use
policyauthorize
to allow a policy to be pluggable, but to have the signer only sign policy plugins that containpolicysigned
w/nonceTpm
. This is interesting becauseTPM2_PolicyAuthorize()
does not provide any way to expire or revoke signatures, butTPM2_PolicySigned()
does have a way to strongly bind a signature to a particular session (vianonceTpm
inclusion in the hash extended into thepolicyDigest
), and also a way to expire the signature (via time relative to the start of the session).Use of keys
TPM2_Duplicate()
ed to the TPM to deliverauthValues
as passwords for different users also seems interesting.Such a policy would allow a laptop to boot normally with a user password, and after emergency updates boot with an admin OTP, or after any mishaps via a superadmin password, or with a separate policy that blesses the current PCRs as golden.
Such a policy could be used for sealing an NV index, or it could be set on local storage keys encrypted to the TPM's EKpub via
sbin/tpm2-send
, allowing for unattended server booting post-attestation, or attended server booting post-mishap (if the encrypted assets get stored "in the clear").