175 changes: 170 additions & 5 deletions lib/tpm2_session.c
Expand Up @@ -25,16 +25,18 @@
// THE POSSIBILITY OF SUCH DAMAGE.
//**********************************************************************;

#include "tpm2_session.h"

#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#include <sapi/tpm20.h>

#include "files.h"
#include "log.h"
#include "tpm_kdfa.h"
#include "tpm2_alg_util.h"
#include "tpm2_session.h"
#include "tpm2_util.h"

struct tpm2_session_data {
Expand Down Expand Up @@ -137,9 +139,11 @@ static bool start_auth_session(TSS2_SYS_CONTEXT *sapi_context,
void tpm2_session_free(tpm2_session **session) {

tpm2_session *s = *session;
free(s->input);
free(s);
*session = NULL;
if (s) {
free(s->input);
free(s);
*session = NULL;
}
}

tpm2_session *tpm2_session_new(TSS2_SYS_CONTEXT *sapi_context,
Expand All @@ -156,6 +160,10 @@ tpm2_session *tpm2_session_new(TSS2_SYS_CONTEXT *sapi_context,

session->internal.nonceNewer.size = session->input->nonce_caller.size;

if (!sapi_context) {
return session;
}

bool result = start_auth_session(sapi_context, session);
if (!result) {
tpm2_session_free(&session);
Expand All @@ -164,3 +172,160 @@ tpm2_session *tpm2_session_new(TSS2_SYS_CONTEXT *sapi_context,

return session;
}

#define SESSION_VERSION 1

/*
* Checks that two types are equal in size.
*
* It works by leveraging the fact that C does not allow negative array sizes.
* If the sizes are equal, the boolean equality operator will return 1, thus
* a subtraction of 1 yields 0, which is a legal array size in C. In the false
* case (ie sizes not equal), 0 - 1 is -1, which will cause the compiler to
* complain.
*/
#define COMPILE_ASSERT_SIZE(a, b) \
typedef char WRONG_SIZE_##a[(sizeof(a) == sizeof(b)) - 1]

// We check that the TSS library does not change sizes unbeknownst to us.
COMPILE_ASSERT_SIZE(TPM2_HANDLE, UINT32);
COMPILE_ASSERT_SIZE(TPMI_ALG_HASH, UINT16);
COMPILE_ASSERT_SIZE(TPM2_SE, UINT8);

tpm2_session *tpm2_session_restore(const char *path) {

tpm2_session *s = NULL;

FILE *f = fopen(path, "rb");
if (!f) {
LOG_ERR("Could not open path \"%s\", due to error: \"%s\"",
path, strerror(errno));
return NULL;
}

uint32_t version;
bool result = files_read_header(f, &version);

TPM2_SE type;
result = files_read_bytes(f, &type, sizeof(type));
if (!result) {
LOG_ERR("Could not read session type");
goto out;
}

TPMI_ALG_HASH auth_hash;
result = files_read_16(f, &auth_hash);
if (!result) {
LOG_ERR("Could not read session digest algorithm");
goto out;
}

TPM2_HANDLE handle;
result = files_read_32(f, &handle);
if (!result) {
LOG_ERR("Could not read session handle");
goto out;
}

tpm2_session_data *d = tpm2_session_data_new(type);
if (!d) {
LOG_ERR("oom");
goto out;
}

tpm2_session_set_authhash(d, auth_hash);

s = tpm2_session_new(NULL, d);
if (s) {
s->output.session_handle = handle;
}

out:
fclose(f);
return s;
}

bool tpm2_session_save(TSS2_SYS_CONTEXT *sapi_context, tpm2_session *session,
const char *path) {

char *ptr = NULL;
bool result = false;

FILE *mem = NULL;
FILE *session_file = NULL;

/*
* Perform a tpm context save/load but just write the
* context data in memory. This will mark the session
* with some RMs (like abrmd) as not-to-flush on client
* exit.
*/
size_t size = sizeof(TPMS_CONTEXT);
mem = open_memstream(&ptr, &size);
if (!mem) {
LOG_ERR("oom");
return false;
}

TPM2_HANDLE handle = tpm2_session_get_session_handle(session);
result = files_save_tpm_context_to_file(sapi_context, handle, mem);
if (!result) {
goto out;
}

rewind(mem);

TPM2_HANDLE dummy;
result = files_load_tpm_context_from_file(sapi_context, &dummy, mem);

session_file = fopen(path, "w+b");
if (!session_file) {
LOG_ERR("Could not open path \"%s\", due to error: \"%s\"",
path, strerror(errno));
goto out;
}

/*
* Now write the session_type, handle and auth hash data to disk
*/
result = files_write_header(session_file, SESSION_VERSION);
if (!result) {
LOG_ERR("Could not write context file header");
goto out;
}

// UINT8 session type:
TPM2_SE session_type = session->input->session_type;
result = files_write_bytes(session_file, &session_type, sizeof(session_type));
if (!result) {
LOG_ERR("Could not write session type");
goto out;
}

// UINT16 - auth hash digest
result = files_write_16(session_file, tpm2_session_get_authhash(session));
if (!result) {
LOG_ERR("Could not write savedHandle");
goto out;
}

// UINT32 - Handle
result = files_write_32(session_file, handle);
if (!result) {
LOG_ERR("Could not write handle");
goto out;
}

/* result is set by files_write_32() */

out:
if (mem) {
fclose(mem);
}
if (session_file) {
fclose(session_file);
}
free(ptr);

return result;
}
33 changes: 33 additions & 0 deletions lib/tpm2_session.h
Expand Up @@ -28,6 +28,8 @@
#ifndef SRC_TPM2_SESSION_H_
#define SRC_TPM2_SESSION_H_

#include <stdbool.h>

#include <sapi/tpm20.h>

typedef struct tpm2_session_data tpm2_session_data;
Expand Down Expand Up @@ -146,6 +148,37 @@ TPMI_SH_AUTH_SESSION tpm2_session_get_session_handle(tpm2_session *session);
tpm2_session *tpm2_session_new(TSS2_SYS_CONTEXT *sapi_context,
tpm2_session_data *data);

/**
* Saves session data to disk allowing tpm2_session_from_file() to
* restore the session.
*
* @Note
* This is accomplished by calling:
* - Tss2_Sys_ContextSave - marks to some RMs like tpm2-abrmd not to flush this session
* handle on client disconnection.
* - Tss2_Sys_ContextLoad - restores the session so it can be used.
* - Saving a custom file format at path - records the handle and algorithm.
* @param session
* The session context to save
* @param sapi_context
* The system api context
* @param path
* The path to save the session context too.
* @return
* True on success, false otherwise.
*/
bool tpm2_session_save(TSS2_SYS_CONTEXT *sapi_context, tpm2_session *session,
const char *path);

/**
* Restores a session saved with tpm2_session_save().
* @param path
* The path to restore from.
* @return
* NULL on failure or a session pointer on success.
*/
tpm2_session *tpm2_session_restore(const char *path);

/**
* Frees a tpm2_sessio but DOES NOT FLUSH the handle. Frees the associated
* tpm2_session_data object as well.
Expand Down
71 changes: 71 additions & 0 deletions man/tpm2_startauthsession.1.md
@@ -0,0 +1,71 @@
% tpm2_startauthsession(1) tpm2-tools | General Commands Manual
%
% JANUARY 2018

# NAME

**tpm2_startauthsession**(1) - Start a session with the TPM.

# SYNOPSIS

**tpm2_startauthsession** [*OPTIONS*]

# DESCRIPTION

**tpm2_startauthsession**(1) Starts a session with the TPM. The default is
to start a *trial* session unless the **-a** option is specified. It outputs
the session handle in a yaml format to stdout as key value "session-handle"
and the handle in hex (0x1234) format.

# OPTIONS

* **-a**, **--auth-policy-session**:

Change the policy session from a *trial* session to a *policy* session.
**NOTE**: A *trial* session is used when building a policy and a *policy*
session is used when authenticating with a policy.

* **-g**, **--policy-digest-alg**=_HASH\_ALGORITHM_:

The hash algorithm used in computation of the policy digest. Algorithms
should follow the "formatting standards", see section "Algorithm Specifiers".
Also, see section "Supported Hash Algorithms" for a list of supported hash
algorithms.

* **-S**, **--session**=_SESSION_FILE_:

Saves the policy session data to a file. This file can then be used in subsequent
tools that can use a policy file for authorization or policy events. **NOTE**: That
this will not work with resource managers (RMs) outside of tpm2-abrmd, as most RMs will
flush session handles when a client disconnects from the IPC channel. This will work
with direct TPM access, but note that internally this calls a *ContextSave* and a
*ContextLoad* on the session handle, thus the session **cannot** be saved/loaded
again.

[common options](common/options.md)

[common tcti options](common/tcti.md)

[supported hash algorithms](common/hash.md)

[algorithm specifiers](common/alg.md)

# EXAMPLES

Start a *trial* session and save the session data to a file.
```
tpm2_startauthsession -S session.dat
session-handle: 0x3000000
```

Start a *policy* session and save the session data to a file.
```
tpm2_startauthsession -S session.dat -a
session-handle: 0x3000000
```

# RETURNS

0 on success or 1 on failure.

[footer](common/footer.md)
4 changes: 2 additions & 2 deletions tools/tpm2_activatecredential.c
Expand Up @@ -309,15 +309,15 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
}

if (ctx.file.context) {
bool res = files_load_tpm_context_from_file(sapi_context, &ctx.handle.activate,
bool res = files_load_tpm_context_from_path(sapi_context, &ctx.handle.activate,
ctx.file.context);
if (!res) {
return 1;
}
}

if (ctx.file.key_context) {
bool res = files_load_tpm_context_from_file(sapi_context, &ctx.handle.key,
bool res = files_load_tpm_context_from_path(sapi_context, &ctx.handle.key,
ctx.file.key_context) != true;
if (!res) {
return 1;
Expand Down
4 changes: 2 additions & 2 deletions tools/tpm2_certify.c
Expand Up @@ -305,15 +305,15 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {

/* Load input files */
if (ctx.flags.C) {
result = files_load_tpm_context_from_file(sapi_context, &ctx.handle.obj,
result = files_load_tpm_context_from_path(sapi_context, &ctx.handle.obj,
ctx.context_file);
if (!result) {
return 1;
}
}

if (ctx.flags.c) {
result = files_load_tpm_context_from_file(sapi_context, &ctx.handle.key,
result = files_load_tpm_context_from_path(sapi_context, &ctx.handle.key,
ctx.context_key_file);
if (!result) {
return 1;
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_create.c
Expand Up @@ -375,7 +375,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
} else if(flagCnt == 3 && (ctx.flags.H == 1 || ctx.flags.c == 1) &&
ctx.flags.g == 1 && ctx.flags.G == 1) {
if(ctx.flags.c)
returnVal = files_load_tpm_context_from_file(sapi_context,
returnVal = files_load_tpm_context_from_path(sapi_context,
&ctx.parent_handle, ctx.context_parent_path) != true;
if(returnVal == 0)
returnVal = create(sapi_context);
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_createpolicy.c
Expand Up @@ -234,7 +234,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
const char *file = pctx.common_policy_options.context_file;

if (file) {
return files_save_tpm_context_to_file(sapi_context,
return files_save_tpm_context_to_path(sapi_context,
handle, file) != true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_createprimary.c
Expand Up @@ -325,7 +325,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
returnVal = create_primary(sapi_context);

if (returnVal == 0 && ctx.flags.C) {
returnVal = files_save_tpm_context_to_file(sapi_context, ctx.handle2048rsa,
returnVal = files_save_tpm_context_to_path(sapi_context, ctx.handle2048rsa,
ctx.context_file) != true;
}

Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_encryptdecrypt.c
Expand Up @@ -200,7 +200,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
}

if (ctx.flags.c) {
result = files_load_tpm_context_from_file(sapi_context, &ctx.key_handle,
result = files_load_tpm_context_from_path(sapi_context, &ctx.key_handle,
ctx.context_key_file);
if (!result) {
return 1;
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_evictcontrol.c
Expand Up @@ -197,7 +197,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
}

if (ctx.flags.c) {
bool result = files_load_tpm_context_from_file(sapi_context, &ctx.handle.object,
bool result = files_load_tpm_context_from_path(sapi_context, &ctx.handle.object,
ctx.context_file);
if (!result) {
return 1;
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_hmac.c
Expand Up @@ -304,7 +304,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
}

if (ctx.flags.c) {
result = files_load_tpm_context_from_file(sapi_context, &ctx.key_handle,
result = files_load_tpm_context_from_path(sapi_context, &ctx.key_handle,
ctx.context_key_file_path);
if (!result) {
LOG_ERR("Loading tpm context from file \"%s\" failed.",
Expand Down
4 changes: 2 additions & 2 deletions tools/tpm2_load.c
Expand Up @@ -207,7 +207,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
}

if(ctx.flags.c) {
returnVal = files_load_tpm_context_from_file(sapi_context,
returnVal = files_load_tpm_context_from_path(sapi_context,
&ctx.parent_handle,
ctx.context_parent_file) != true;
if (returnVal) {
Expand All @@ -221,7 +221,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
}

if (ctx.flags.C) {
returnVal = files_save_tpm_context_to_file (sapi_context,
returnVal = files_save_tpm_context_to_path (sapi_context,
handle2048rsa,
ctx.context_file) != true;
if (returnVal) {
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_loadexternal.c
Expand Up @@ -171,7 +171,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
}

if(ctx.save_to_context_file) {
return files_save_tpm_context_to_file(sapi_context, ctx.rsa2048_handle,
return files_save_tpm_context_to_path(sapi_context, ctx.rsa2048_handle,
ctx.context_file_path) != true;
}

Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_quote.c
Expand Up @@ -259,7 +259,7 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
}

if(c_flag) {
bool result = files_load_tpm_context_from_file(sapi_context, &akHandle, contextFilePath);
bool result = files_load_tpm_context_from_path(sapi_context, &akHandle, contextFilePath);
if (!result) {
return 1;
}
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_readpublic.c
Expand Up @@ -149,7 +149,7 @@ static bool init(TSS2_SYS_CONTEXT *sapi_context) {
}

if (ctx.flags.c) {
bool result = files_load_tpm_context_from_file(sapi_context, &ctx.objectHandle,
bool result = files_load_tpm_context_from_path(sapi_context, &ctx.objectHandle,
ctx.context_file);
if (!result) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_rsadecrypt.c
Expand Up @@ -171,7 +171,7 @@ static bool init(TSS2_SYS_CONTEXT *sapi_context) {
}

if (ctx.flags.c) {
bool result = files_load_tpm_context_from_file(sapi_context,
bool result = files_load_tpm_context_from_path(sapi_context,
&ctx.key_handle, ctx.context_key_file);
if (!result) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_rsaencrypt.c
Expand Up @@ -148,7 +148,7 @@ static bool init(TSS2_SYS_CONTEXT *sapi_context) {
}

if (ctx.flags.c) {
bool result = files_load_tpm_context_from_file(sapi_context, &ctx.key_handle,
bool result = files_load_tpm_context_from_path(sapi_context, &ctx.key_handle,
ctx.context_key_file);
if (!result) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_sign.c
Expand Up @@ -135,7 +135,7 @@ static bool init(TSS2_SYS_CONTEXT *sapi_context) {
* load tpm context from a file if -c is provided
*/
if (ctx.flags.c) {
bool result = files_load_tpm_context_from_file(sapi_context, &ctx.keyHandle,
bool result = files_load_tpm_context_from_path(sapi_context, &ctx.keyHandle,
ctx.contextKeyFile);
if (!result) {
return false;
Expand Down
138 changes: 138 additions & 0 deletions tools/tpm2_startauthsession.c
@@ -0,0 +1,138 @@
//**********************************************************************;
// Copyright (c) 2018, Intel Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of Intel Corporation nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//**********************************************************************;

#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>

#include <sapi/tpm20.h>

#include "files.h"
#include "log.h"
#include "tpm2_alg_util.h"
#include "tpm2_options.h"
#include "tpm2_session.h"
#include "tpm2_tool.h"
#include "tpm2_util.h"

typedef struct tpm2_startauthsession_ctx tpm2_startauthsession_ctx;
struct tpm2_startauthsession_ctx {
struct {
TPM2_SE type;
TPMI_ALG_HASH halg;
} session;
struct {
const char *path;
} output;
};

static tpm2_startauthsession_ctx ctx = {
.session = {
.type = TPM2_SE_TRIAL,
.halg = TPM2_ALG_SHA256
}
};

static bool on_option(char key, char *value) {

switch (key) {
case 'a':
ctx.session.type = TPM2_SE_POLICY;
break;
case 'g':
ctx.session.halg = tpm2_alg_util_from_optarg(value);
if(ctx.session.halg == TPM2_ALG_ERROR) {
LOG_ERR("Invalid choice for policy digest hash algorithm");
return false;
}
break;
case 'S':
ctx.output.path = value;
break;
}

return true;
}

bool tpm2_tool_onstart(tpm2_options **opts) {

static struct option topts[] = {
{ "auth-policy-session", no_argument, NULL, 'a'},
{ "policy-digest-alg", required_argument, NULL, 'g'},
{ "session", required_argument, NULL, 'S'},
};

*opts = tpm2_options_new("ag:S:", ARRAY_LEN(topts), topts, on_option,
NULL, false);

return *opts != NULL;
}

int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {

UNUSED(flags);

int rc = 1;
tpm2_session_data *session_data = tpm2_session_data_new(ctx.session.type);
if (!session_data) {
LOG_ERR("oom");
return rc;
}

tpm2_session_set_authhash(session_data, ctx.session.halg);

tpm2_session *s = tpm2_session_new(sapi_context,
session_data);
if (!s) {
return rc;
}

TPMI_SH_AUTH_SESSION handle = tpm2_session_get_session_handle(s);
tpm2_tool_output("session-handle: 0x%" PRIx32 "\n", handle);

if (ctx.output.path) {
bool result = tpm2_session_save(sapi_context, s, ctx.output.path);
if (!result) {
goto out;
}
}

rc = 0;

out:
tpm2_session_free(&s);

return rc;
}
2 changes: 1 addition & 1 deletion tools/tpm2_unseal.c
Expand Up @@ -98,7 +98,7 @@ static bool init(TSS2_SYS_CONTEXT *sapi_context) {
}

if (ctx.flags.c) {
bool result = files_load_tpm_context_from_file(sapi_context, &ctx.itemHandle,
bool result = files_load_tpm_context_from_path(sapi_context, &ctx.itemHandle,
ctx.contextItemFile);
if (!result) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion tools/tpm2_verifysignature.c
Expand Up @@ -160,7 +160,7 @@ static bool init(TSS2_SYS_CONTEXT *sapi_context) {
}

if (ctx.flags.key_context) {
bool result = files_load_tpm_context_from_file(sapi_context, &ctx.keyHandle,
bool result = files_load_tpm_context_from_path(sapi_context, &ctx.keyHandle,
ctx.context_key_file_path);
if (!result) {
goto err;
Expand Down