Browse Source
Pre-Shared Keys (PSK) is a simpler mechanism for enabling TLS
connections than using certificates. It requires only a simple secret
key:
$ mkdir -m 0700 /tmp/keys
$ psktool -u rjones -p /tmp/keys/keys.psk
$ cat /tmp/keys/keys.psk
rjones:d543770c15ad93d76443fb56f501a31969235f47e999720ae8d2336f6a13fcbc
The key can be secretly shared between clients and servers. Clients
must specify the directory containing the "keys.psk" file and a
username (defaults to "qemu"). Servers must specify only the
directory.
Example NBD client:
$ qemu-img info \
--object tls-creds-psk,id=tls0,dir=/tmp/keys,username=rjones,endpoint=client \
--image-opts \
file.driver=nbd,file.host=localhost,file.port=10809,file.tls-creds=tls0,file.export=/
Example NBD server using qemu-nbd:
$ qemu-nbd -t -x / \
--object tls-creds-psk,id=tls0,endpoint=server,dir=/tmp/keys \
--tls-creds tls0 \
image.qcow2
Example NBD server using nbdkit:
$ nbdkit -n -e / -fv \
--tls=on --tls-psk=/tmp/keys/keys.psk \
file file=disk.img
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
pull/73/head
committed by
Daniel P. Berrangé
11 changed files with 774 additions and 23 deletions
@ -0,0 +1,308 @@ |
|||
/*
|
|||
* QEMU crypto TLS Pre-Shared Keys (PSK) support |
|||
* |
|||
* Copyright (c) 2018 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|||
* |
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "crypto/tlscredspsk.h" |
|||
#include "tlscredspriv.h" |
|||
#include "qapi/error.h" |
|||
#include "qom/object_interfaces.h" |
|||
#include "trace.h" |
|||
|
|||
|
|||
#ifdef CONFIG_GNUTLS |
|||
|
|||
static int |
|||
lookup_key(const char *pskfile, const char *username, gnutls_datum_t *key, |
|||
Error **errp) |
|||
{ |
|||
const size_t ulen = strlen(username); |
|||
GError *gerr = NULL; |
|||
char *content = NULL; |
|||
char **lines = NULL; |
|||
size_t clen = 0, i; |
|||
int ret = -1; |
|||
|
|||
if (!g_file_get_contents(pskfile, &content, &clen, &gerr)) { |
|||
error_setg(errp, "Cannot read PSK file %s: %s", |
|||
pskfile, gerr->message); |
|||
g_error_free(gerr); |
|||
return -1; |
|||
} |
|||
|
|||
lines = g_strsplit(content, "\n", -1); |
|||
for (i = 0; lines[i] != NULL; ++i) { |
|||
if (strncmp(lines[i], username, ulen) == 0 && lines[i][ulen] == ':') { |
|||
key->data = (unsigned char *) g_strdup(&lines[i][ulen + 1]); |
|||
key->size = strlen(lines[i]) - ulen - 1; |
|||
ret = 0; |
|||
goto out; |
|||
} |
|||
} |
|||
error_setg(errp, "Username %s not found in PSK file %s", |
|||
username, pskfile); |
|||
|
|||
out: |
|||
free(content); |
|||
g_strfreev(lines); |
|||
return ret; |
|||
} |
|||
|
|||
static int |
|||
qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds, |
|||
Error **errp) |
|||
{ |
|||
char *pskfile = NULL, *dhparams = NULL; |
|||
const char *username; |
|||
int ret; |
|||
int rv = -1; |
|||
gnutls_datum_t key = { .data = NULL }; |
|||
|
|||
trace_qcrypto_tls_creds_psk_load(creds, |
|||
creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>"); |
|||
|
|||
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { |
|||
if (creds->username) { |
|||
error_setg(errp, "username should not be set when endpoint=server"); |
|||
goto cleanup; |
|||
} |
|||
|
|||
if (qcrypto_tls_creds_get_path(&creds->parent_obj, |
|||
QCRYPTO_TLS_CREDS_DH_PARAMS, |
|||
false, &dhparams, errp) < 0 || |
|||
qcrypto_tls_creds_get_path(&creds->parent_obj, |
|||
QCRYPTO_TLS_CREDS_PSKFILE, |
|||
true, &pskfile, errp) < 0) { |
|||
goto cleanup; |
|||
} |
|||
|
|||
ret = gnutls_psk_allocate_server_credentials(&creds->data.server); |
|||
if (ret < 0) { |
|||
error_setg(errp, "Cannot allocate credentials: %s", |
|||
gnutls_strerror(ret)); |
|||
goto cleanup; |
|||
} |
|||
|
|||
if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, |
|||
&creds->parent_obj.dh_params, |
|||
errp) < 0) { |
|||
goto cleanup; |
|||
} |
|||
|
|||
gnutls_psk_set_server_credentials_file(creds->data.server, pskfile); |
|||
gnutls_psk_set_server_dh_params(creds->data.server, |
|||
creds->parent_obj.dh_params); |
|||
} else { |
|||
if (qcrypto_tls_creds_get_path(&creds->parent_obj, |
|||
QCRYPTO_TLS_CREDS_PSKFILE, |
|||
true, &pskfile, errp) < 0) { |
|||
goto cleanup; |
|||
} |
|||
|
|||
if (creds->username) { |
|||
username = creds->username; |
|||
} else { |
|||
username = "qemu"; |
|||
} |
|||
if (lookup_key(pskfile, username, &key, errp) != 0) { |
|||
goto cleanup; |
|||
} |
|||
|
|||
ret = gnutls_psk_allocate_client_credentials(&creds->data.client); |
|||
if (ret < 0) { |
|||
error_setg(errp, "Cannot allocate credentials: %s", |
|||
gnutls_strerror(ret)); |
|||
goto cleanup; |
|||
} |
|||
|
|||
gnutls_psk_set_client_credentials(creds->data.client, |
|||
username, &key, GNUTLS_PSK_KEY_HEX); |
|||
} |
|||
|
|||
rv = 0; |
|||
cleanup: |
|||
g_free(key.data); |
|||
g_free(pskfile); |
|||
g_free(dhparams); |
|||
return rv; |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds) |
|||
{ |
|||
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { |
|||
if (creds->data.client) { |
|||
gnutls_psk_free_client_credentials(creds->data.client); |
|||
creds->data.client = NULL; |
|||
} |
|||
} else { |
|||
if (creds->data.server) { |
|||
gnutls_psk_free_server_credentials(creds->data.server); |
|||
creds->data.server = NULL; |
|||
} |
|||
} |
|||
if (creds->parent_obj.dh_params) { |
|||
gnutls_dh_params_deinit(creds->parent_obj.dh_params); |
|||
creds->parent_obj.dh_params = NULL; |
|||
} |
|||
} |
|||
|
|||
#else /* ! CONFIG_GNUTLS */ |
|||
|
|||
|
|||
static void |
|||
qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED, |
|||
Error **errp) |
|||
{ |
|||
error_setg(errp, "TLS credentials support requires GNUTLS"); |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED) |
|||
{ |
|||
/* nada */ |
|||
} |
|||
|
|||
|
|||
#endif /* ! CONFIG_GNUTLS */ |
|||
|
|||
|
|||
static void |
|||
qcrypto_tls_creds_psk_prop_set_loaded(Object *obj, |
|||
bool value, |
|||
Error **errp) |
|||
{ |
|||
QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); |
|||
|
|||
if (value) { |
|||
qcrypto_tls_creds_psk_load(creds, errp); |
|||
} else { |
|||
qcrypto_tls_creds_psk_unload(creds); |
|||
} |
|||
} |
|||
|
|||
|
|||
#ifdef CONFIG_GNUTLS |
|||
|
|||
|
|||
static bool |
|||
qcrypto_tls_creds_psk_prop_get_loaded(Object *obj, |
|||
Error **errp G_GNUC_UNUSED) |
|||
{ |
|||
QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); |
|||
|
|||
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { |
|||
return creds->data.server != NULL; |
|||
} else { |
|||
return creds->data.client != NULL; |
|||
} |
|||
} |
|||
|
|||
|
|||
#else /* ! CONFIG_GNUTLS */ |
|||
|
|||
|
|||
static bool |
|||
qcrypto_tls_creds_psk_prop_get_loaded(Object *obj G_GNUC_UNUSED, |
|||
Error **errp G_GNUC_UNUSED) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
|
|||
#endif /* ! CONFIG_GNUTLS */ |
|||
|
|||
|
|||
static void |
|||
qcrypto_tls_creds_psk_complete(UserCreatable *uc, Error **errp) |
|||
{ |
|||
object_property_set_bool(OBJECT(uc), true, "loaded", errp); |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_tls_creds_psk_finalize(Object *obj) |
|||
{ |
|||
QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); |
|||
|
|||
qcrypto_tls_creds_psk_unload(creds); |
|||
} |
|||
|
|||
static void |
|||
qcrypto_tls_creds_psk_prop_set_username(Object *obj, |
|||
const char *value, |
|||
Error **errp G_GNUC_UNUSED) |
|||
{ |
|||
QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); |
|||
|
|||
creds->username = g_strdup(value); |
|||
} |
|||
|
|||
|
|||
static char * |
|||
qcrypto_tls_creds_psk_prop_get_username(Object *obj, |
|||
Error **errp G_GNUC_UNUSED) |
|||
{ |
|||
QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); |
|||
|
|||
return g_strdup(creds->username); |
|||
} |
|||
|
|||
static void |
|||
qcrypto_tls_creds_psk_class_init(ObjectClass *oc, void *data) |
|||
{ |
|||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); |
|||
|
|||
ucc->complete = qcrypto_tls_creds_psk_complete; |
|||
|
|||
object_class_property_add_bool(oc, "loaded", |
|||
qcrypto_tls_creds_psk_prop_get_loaded, |
|||
qcrypto_tls_creds_psk_prop_set_loaded, |
|||
NULL); |
|||
object_class_property_add_str(oc, "username", |
|||
qcrypto_tls_creds_psk_prop_get_username, |
|||
qcrypto_tls_creds_psk_prop_set_username, |
|||
NULL); |
|||
} |
|||
|
|||
|
|||
static const TypeInfo qcrypto_tls_creds_psk_info = { |
|||
.parent = TYPE_QCRYPTO_TLS_CREDS, |
|||
.name = TYPE_QCRYPTO_TLS_CREDS_PSK, |
|||
.instance_size = sizeof(QCryptoTLSCredsPSK), |
|||
.instance_finalize = qcrypto_tls_creds_psk_finalize, |
|||
.class_size = sizeof(QCryptoTLSCredsPSKClass), |
|||
.class_init = qcrypto_tls_creds_psk_class_init, |
|||
.interfaces = (InterfaceInfo[]) { |
|||
{ TYPE_USER_CREATABLE }, |
|||
{ } |
|||
} |
|||
}; |
|||
|
|||
|
|||
static void |
|||
qcrypto_tls_creds_psk_register_types(void) |
|||
{ |
|||
type_register_static(&qcrypto_tls_creds_psk_info); |
|||
} |
|||
|
|||
|
|||
type_init(qcrypto_tls_creds_psk_register_types); |
|||
@ -0,0 +1,106 @@ |
|||
/*
|
|||
* QEMU crypto TLS Pre-Shared Key (PSK) support |
|||
* |
|||
* Copyright (c) 2018 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|||
* |
|||
*/ |
|||
|
|||
#ifndef QCRYPTO_TLSCREDSPSK_H |
|||
#define QCRYPTO_TLSCREDSPSK_H |
|||
|
|||
#include "crypto/tlscreds.h" |
|||
|
|||
#define TYPE_QCRYPTO_TLS_CREDS_PSK "tls-creds-psk" |
|||
#define QCRYPTO_TLS_CREDS_PSK(obj) \ |
|||
OBJECT_CHECK(QCryptoTLSCredsPSK, (obj), TYPE_QCRYPTO_TLS_CREDS_PSK) |
|||
|
|||
typedef struct QCryptoTLSCredsPSK QCryptoTLSCredsPSK; |
|||
typedef struct QCryptoTLSCredsPSKClass QCryptoTLSCredsPSKClass; |
|||
|
|||
#define QCRYPTO_TLS_CREDS_PSKFILE "keys.psk" |
|||
|
|||
/**
|
|||
* QCryptoTLSCredsPSK: |
|||
* |
|||
* The QCryptoTLSCredsPSK object provides a representation |
|||
* of the Pre-Shared Key credential used to perform a TLS handshake. |
|||
* |
|||
* This is a user creatable object, which can be instantiated |
|||
* via object_new_propv(): |
|||
* |
|||
* <example> |
|||
* <title>Creating TLS-PSK credential objects in code</title> |
|||
* <programlisting> |
|||
* Object *obj; |
|||
* Error *err = NULL; |
|||
* obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_PSK, |
|||
* "tlscreds0", |
|||
* &err, |
|||
* "dir", "/path/to/dir", |
|||
* "endpoint", "client", |
|||
* NULL); |
|||
* </programlisting> |
|||
* </example> |
|||
* |
|||
* Or via QMP: |
|||
* |
|||
* <example> |
|||
* <title>Creating TLS-PSK credential objects via QMP</title> |
|||
* <programlisting> |
|||
* { |
|||
* "execute": "object-add", "arguments": { |
|||
* "id": "tlscreds0", |
|||
* "qom-type": "tls-creds-psk", |
|||
* "props": { |
|||
* "dir": "/path/to/dir", |
|||
* "endpoint": "client" |
|||
* } |
|||
* } |
|||
* } |
|||
* </programlisting> |
|||
* </example> |
|||
* |
|||
* Or via the CLI: |
|||
* |
|||
* <example> |
|||
* <title>Creating TLS-PSK credential objects via CLI</title> |
|||
* <programlisting> |
|||
* qemu-system-x86_64 --object tls-creds-psk,id=tlscreds0,\ |
|||
* endpoint=client,dir=/path/to/dir[,username=qemu] |
|||
* </programlisting> |
|||
* </example> |
|||
* |
|||
* The PSK file can be created and managed using psktool. |
|||
*/ |
|||
|
|||
struct QCryptoTLSCredsPSK { |
|||
QCryptoTLSCreds parent_obj; |
|||
char *username; |
|||
#ifdef CONFIG_GNUTLS |
|||
union { |
|||
gnutls_psk_server_credentials_t server; |
|||
gnutls_psk_client_credentials_t client; |
|||
} data; |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
struct QCryptoTLSCredsPSKClass { |
|||
QCryptoTLSCredsClass parent_class; |
|||
}; |
|||
|
|||
|
|||
#endif /* QCRYPTO_TLSCREDSPSK_H */ |
|||
@ -0,0 +1,50 @@ |
|||
/*
|
|||
* Copyright (C) 2015-2018 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2.1 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library. If not, see |
|||
* <http://www.gnu.org/licenses/>.
|
|||
* |
|||
* Author: Richard W.M. Jones <rjones@redhat.com> |
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
|
|||
/* Include this first because it defines QCRYPTO_HAVE_TLS_TEST_SUPPORT */ |
|||
#include "crypto-tls-x509-helpers.h" |
|||
|
|||
#include "crypto-tls-psk-helpers.h" |
|||
#include "qemu/sockets.h" |
|||
|
|||
#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT |
|||
|
|||
void test_tls_psk_init(const char *pskfile) |
|||
{ |
|||
FILE *fp; |
|||
|
|||
fp = fopen(pskfile, "w"); |
|||
if (fp == NULL) { |
|||
g_critical("Failed to create pskfile %s", pskfile); |
|||
abort(); |
|||
} |
|||
/* Don't do this in real applications! Use psktool. */ |
|||
fprintf(fp, "qemu:009d5638c40fde0c\n"); |
|||
fclose(fp); |
|||
} |
|||
|
|||
void test_tls_psk_cleanup(const char *pskfile) |
|||
{ |
|||
unlink(pskfile); |
|||
} |
|||
|
|||
#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ |
|||
@ -0,0 +1,29 @@ |
|||
/*
|
|||
* Copyright (C) 2015-2018 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2.1 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library. If not, see |
|||
* <http://www.gnu.org/licenses/>.
|
|||
* |
|||
* Author: Richard W.M. Jones <rjones@redhat.com> |
|||
*/ |
|||
|
|||
#include <gnutls/gnutls.h> |
|||
|
|||
#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT |
|||
# include "qemu-common.h" |
|||
|
|||
void test_tls_psk_init(const char *keyfile); |
|||
void test_tls_psk_cleanup(const char *keyfile); |
|||
|
|||
#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ |
|||
Loading…
Reference in new issue