swtpm_setup: add abstract swtpm_backend_ops with dir:// implementation

Abstract away implementation specific code for handling TPM state in
swtpm_setup. The current code for handling directories is moved to
'swtpm_backend_dir.c'.

Where possible, the input argument is simply passed verbatim as
'backend-uri' to swtpm.

No functional change intended, aside from supporting 'dir://' as
optional prefix. The checks for lock-file accessibility are moved to
check_access(), but that shouldn't affect anything AFAICT.

Signed-off-by: Stefan Reiter <stefan@pimaker.at>
This commit is contained in:
Stefan Reiter 2021-09-30 10:03:05 +02:00 committed by Stefan Berger
parent 5d9edaf401
commit 81371f665f
5 changed files with 153 additions and 69 deletions

View File

@ -18,7 +18,8 @@ bin_PROGRAMS = \
swtpm_setup_SOURCES = \
swtpm.c \
swtpm_setup.c \
swtpm_setup_utils.c
swtpm_setup_utils.c \
swtpm_backend_dir.c
$(top_builddir)/src/utils/libswtpm_utils.la:
$(MAKE) -C$(dir $@)

View File

@ -56,7 +56,7 @@ struct tpm_resp_header {
static int swtpm_start(struct swtpm *self)
{
g_autofree gchar *tpmstate_dir = g_strdup_printf("dir=%s", self->state_path);
g_autofree gchar *tpmstate = g_strdup_printf("backend-uri=%s", self->state_path);
g_autofree gchar *pidfile_arg = NULL;
g_autofree gchar *server_fd = NULL;
g_autofree gchar *ctrl_fd = NULL;
@ -81,7 +81,7 @@ static int swtpm_start(struct swtpm *self)
argv = concat_arrays(self->swtpm_exec_l,
(gchar*[]){
"--flags", "not-need-init,startup-clear",
"--tpmstate", tpmstate_dir,
"--tpmstate", tpmstate,
"--pid", pidfile_arg,
#if 0
"--log", "file=/tmp/log,level=20",

View File

@ -11,6 +11,7 @@
#define SWTPM_SETUP_SWTPM_H
#include <glib.h>
#include <pwd.h>
#include <openssl/sha.h>
@ -93,4 +94,14 @@ struct swtpm2 *swtpm2_new(gchar **swtpm_prg_l, const gchar *tpm_state_path,
void swtpm_free(struct swtpm *);
/* backend-specific implementations */
struct swtpm_backend_ops {
void* (*parse_backend)(const gchar* uri);
int (*check_access)(void *backend, int mode, const struct passwd *curr_user);
int (*delete_state)(void *backend);
void (*free_backend)(void *backend);
};
extern struct swtpm_backend_ops swtpm_backend_dir;
#endif /* SWTPM_SETUP_SWTPM_H */

View File

@ -0,0 +1,118 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* swtpm_backend_dir.c: storage backend specific functions for dir://
*
* Originally by: Stefan Berger, stefanb@linux.ibm.com
* Refactored as module: Stefan Reiter, stefan@pimaker.at
*/
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "swtpm.h"
#include "swtpm_utils.h"
struct dir_state {
gchar* dir;
};
/* Parse a dir:// URI by removing the prefix if given. */
static void *parse_dir_state(const gchar* uri) {
struct dir_state *ret;
if (strncmp(uri, "dir://", 6) == 0) {
uri += 6;
}
ret = g_malloc(sizeof(struct dir_state));
ret->dir = g_strdup(uri);
return (void*)ret;
}
/* Check user access in 'mode' to directory specified in backend state. */
static int check_access(void *state, int mode, const struct passwd *curr_user) {
gchar *tpm_state_path = ((struct dir_state*)state)->dir;
gchar *p;
struct stat statbuf;
char path[PATH_MAX];
/* check lockfile */
p = pathjoin(path, sizeof(path), tpm_state_path, ".lock", NULL);
if (!p)
return 1;
if (stat(p, &statbuf) == 0 && access(p, R_OK|W_OK) != 0) {
logerr(gl_LOGFILE, "User %s cannot read/write lockfile %s.\n",
curr_user ? curr_user->pw_name : "<unknown>", p);
return 1;
}
/* check access to state directory itself */
return check_directory_access(tpm_state_path, mode, curr_user);
}
/* Delete swtpm's state file. Those are the files with suffixes
* 'permall', 'volatilestate', and 'savestate'.
*/
static int delete_statefiles(void *state)
{
gchar *tpm_state_path = ((struct dir_state*)state)->dir;
GError *error = NULL;
GDir *dir = g_dir_open(tpm_state_path, 0, &error);
int ret = 1;
if (dir == NULL) {
logerr(gl_LOGFILE, "%s\n", error->message);
g_error_free(error);
return 1;
}
while (1) {
const gchar *fn = g_dir_read_name(dir);
if (fn == NULL) {
if (errno != 0 && errno != ENOENT
#ifdef __FreeBSD__
&& errno != EINVAL
#endif
) {
logerr(gl_LOGFILE, "Error getting next filename: %s\n", strerror(errno));
break;
} else {
ret = 0;
break;
}
}
if (g_str_has_suffix(fn, "permall") ||
g_str_has_suffix(fn, "volatilestate") ||
g_str_has_suffix(fn, "savestate")) {
g_autofree gchar *fullname = g_strjoin(G_DIR_SEPARATOR_S,
tpm_state_path, fn, NULL);
if (unlink(fullname) != 0) {
logerr(gl_LOGFILE, "Could not remove %s: %s\n", fn, strerror(errno));
break;
}
}
}
g_dir_close(dir);
return ret;
}
/* Free an instance of dir_state. */
static void free_dir_state(void *state) {
if (state) {
struct dir_state *dstate = (struct dir_state*)state;
g_free(dstate->dir);
g_free(dstate);
}
}
struct swtpm_backend_ops swtpm_backend_dir = {
.parse_backend = parse_dir_state,
.check_access = check_access,
.delete_state = delete_statefiles,
.free_backend = free_dir_state,
};

View File

@ -703,14 +703,14 @@ static int check_state_overwrite(gchar **swtpm_prg_l, unsigned int flags,
int exit_status = 0;
g_autoptr(GError) error = NULL;
g_autofree gchar **argv = NULL;
g_autofree gchar *dirop = g_strdup_printf("dir=%s", tpm_state_path);
g_autofree gchar *statearg = g_strdup_printf("backend-uri=%s", tpm_state_path);
g_autofree gchar *logop = NULL;
g_autofree gchar **my_argv = NULL;
my_argv = concat_arrays((gchar*[]) {
"--print-states",
"--tpmstate",
dirop,
statearg,
NULL
}, NULL, FALSE);
@ -1081,53 +1081,6 @@ error:
return ret;
}
/* Delete swtpm's state file. Those are the files with suffixes
* 'permall', 'volatilestate', and 'savestate'.
*/
static int delete_swtpm_statefiles(const gchar *tpm_state_path)
{
GError *error = NULL;
GDir *dir = g_dir_open(tpm_state_path, 0, &error);
int ret = 1;
if (dir == NULL) {
logerr(gl_LOGFILE, "%s\n", error->message);
g_error_free(error);
return 1;
}
while (1) {
const gchar *fn = g_dir_read_name(dir);
if (fn == NULL) {
if (errno != 0 && errno != ENOENT
#ifdef __FreeBSD__
&& errno != EINVAL
#endif
) {
logerr(gl_LOGFILE, "Error getting next filename: %s\n", strerror(errno));
break;
} else {
ret = 0;
break;
}
}
if (g_str_has_suffix(fn, "permall") ||
g_str_has_suffix(fn, "volatilestate") ||
g_str_has_suffix(fn, "savestate")) {
g_autofree gchar *fullname = g_strjoin(G_DIR_SEPARATOR_S,
tpm_state_path, fn, NULL);
if (unlink(fullname) != 0) {
logerr(gl_LOGFILE, "Coud not remove %s: %s\n", fn, strerror(errno));
break;
}
}
}
g_dir_close(dir);
return ret;
}
int main(int argc, char *argv[])
{
int opt, option_index = 0;
@ -1174,6 +1127,8 @@ int main(int argc, char *argv[])
unsigned long flags = 0;
g_autofree gchar *swtpm_prg = NULL;
g_autofree gchar *tpm_state_path = NULL;
struct swtpm_backend_ops *backend_ops = &swtpm_backend_dir;
void *backend_state = NULL;
g_autofree gchar *config_file = NULL;
g_autofree gchar *ownerpass = NULL;
gboolean got_ownerpass = FALSE;
@ -1201,10 +1156,7 @@ int main(int argc, char *argv[])
const struct passwd *curr_user;
struct group *curr_grp;
char *endptr;
char path[PATH_MAX];
char *p;
gboolean swtpm_has_tpm12, swtpm_has_tpm2;
g_autofree gchar *lockfile = NULL;
int fds_to_pass[1] = { -1 };
unsigned n_fds_to_pass = 0;
char tmpbuffer[200];
@ -1228,7 +1180,12 @@ int main(int argc, char *argv[])
switch (opt) {
case 't': /* --tpmstate, --tpm-state */
g_free(tpm_state_path);
tpm_state_path = g_strdup(optarg);
if (strncmp(optarg, "dir://", 6) == 0) {
tpm_state_path = g_strdup(optarg);
} else {
/* always prefix with dir:// so we can pass verbatim to swtpm */
tpm_state_path = g_strconcat("dir://", optarg, NULL);
}
break;
case 'T': /* --tpm */
g_free(swtpm_prg);
@ -1446,7 +1403,12 @@ int main(int argc, char *argv[])
logerr(gl_LOGFILE, "--tpm-state must be provided\n");
goto error;
}
if (check_directory_access(tpm_state_path, R_OK|W_OK, curr_user) != 0)
backend_state = backend_ops->parse_backend(tpm_state_path);
if (!backend_state)
goto error;
if (backend_ops->check_access(backend_state, R_OK|W_OK, curr_user) != 0)
goto error;
if ((flags & SETUP_WRITE_EK_CERT_FILES_F)) {
@ -1478,20 +1440,10 @@ int main(int argc, char *argv[])
goto out;
}
ret = delete_swtpm_statefiles(tpm_state_path);
ret = backend_ops->delete_state(backend_state);
if (ret != 0)
goto error;
p = pathjoin(path, sizeof(path), tpm_state_path, ".lock", NULL);
if (!p)
goto error;
lockfile = g_strdup(p);
if (stat(lockfile, &statbuf) == 0 && access(lockfile, R_OK|W_OK) != 0) {
logerr(gl_LOGFILE, "User %s cannot read/write lockfile %s.\n",
curr_user ? curr_user->pw_name : "<unknown>", lockfile);
goto error;
}
if (access(config_file, R_OK) != 0) {
logerr(gl_LOGFILE, "User %s cannot read config file %s.\n",
curr_user ? curr_user->pw_name : "<unknown>", config_file);
@ -1608,7 +1560,7 @@ int main(int argc, char *argv[])
logit(gl_LOGFILE, "Successfully authored TPM state.\n");
} else {
logerr(gl_LOGFILE, "An error occurred. Authoring the TPM state failed.\n");
delete_swtpm_statefiles(tpm_state_path);
backend_ops->delete_state(backend_state);
}
now = time(NULL);
@ -1625,6 +1577,8 @@ out:
logerr(gl_LOGFILE, "Could not remove temporary directory for certs: %s\n",
strerror(errno));
if (backend_ops && backend_state)
backend_ops->free_backend(backend_state);
g_strfreev(swtpm_prg_l);
g_free(gl_LOGFILE);