page-xfer: Add TLS support with X509 certificates

This commit adds Transport Layer Security (TLS) support for remote
page-server connections.

The following command-line options are introduced with this commit:

--tls-cacert  FILE    Trust certificates signed only by this CA
--tls-cacrl   FILE    CA certificate revocation list
--tls-cert    FILE    TLS certificate
--tls-key     FILE    TLS private key
--tls                   Use TLS to secure remote connections

The default PKI locations are:

CA certificate              /etc/pki/CA/cacert.pem
CA revocation list          /etc/pki/CA/cacrl.pem
Client/server certificate   /etc/pki/criu/cert.pem
Client/server private key   /etc/pki/criu/private/key.pem

The files cacert.pem and cacrl.pem are optional. If they are not
present, and not explicitly specified with a command-line option,
CRIU will use only the system's trusted CAs to verify the remote
peer's identity. This implies that if a CA certificate is specified
using "--tls-cacert" only this CA will be used for verification.
If CA certificate (cacert.pem) is not present, certificate revocation
list (cacrl.pem) will be ignored.

Both (client and server) sides require a private key and certificate.

When the "--tls" option is specified, a TLS handshake (key exchange)
will be performed immediately after the remote TCP connection has been
accepted.

X.509 certificates can be generated as follows:
-------------------------%<-------------------------
	# Generate CA key and certificate
	echo -ne "ca\ncert_signing_key" > temp
	certtool --generate-privkey > cakey.pem
	certtool --generate-self-signed \
	    --template temp \
	    --load-privkey cakey.pem \
	    --outfile cacert.pem

	# Generate server key and certificate
	echo -ne "cn=$HOSTNAME\nencryption_key\nsigning_key" > temp
	certtool --generate-privkey > key.pem
	certtool --generate-certificate \
	    --template temp \
	    --load-privkey key.pem \
	    --load-ca-certificate cacert.pem \
	    --load-ca-privkey cakey.pem \
	    --outfile cert.pem
	rm temp

	mkdir -p /etc/pki/CA
	mkdir -p /etc/pki/criu/private

	mv cacert.pem /etc/pki/CA/
	mv cert.pem /etc/pki/criu/
	mv key.pem /etc/pki/criu/private
-------------------------%<-------------------------

Usage Example:

Page-server:

 [src]# criu page-server -D <PATH> --port <PORT> --tls

 [dst]# criu dump --page-server --address <SRC> --port <PORT> \
	-t <PID> -D <PATH> --tls

Lazy migration:

 [src]# criu dump --lazy-pages --port <PORT> -t <PID> -D <PATH> --tls

 [dst]# criu lazy-pages --page-server --address <SRC> --port <PORT> \
	-D <PATH> --tls

 [dst]# criu restore -D <PATH> --lazy-pages

Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
This commit is contained in:
Radostin Stoyanov 2019-03-31 12:05:22 +01:00 committed by Andrei Vagin
parent b7230b6132
commit 76a41209b0
11 changed files with 546 additions and 34 deletions

View file

@ -594,6 +594,33 @@ Launches *criu* in page server mode.
remote *lazy-pages* daemon to request memory pages in random
order.
*--tls-cacert* 'file'::
Specifies the path to a trusted Certificate Authority (CA) certificate
file to be used for verification of a client or server certificate.
The 'file' must be in PEM format. When this option is used only the
specified CA is used for verification. Otherwise, the system's trusted CAs
and, if present, '/etc/pki/CA/cacert.pem' will be used.
*--tls-cacrl* 'file'::
Specifies a path to a Certificate Revocation List (CRL) 'file' which
contains a list of revoked certificates that should no longer be trusted.
The 'file' must be in PEM format. When this option is not specified, the
file, if present, '/etc/pki/CA/cacrl.pem' will be used.
*--tls-cert* 'file'::
Specifies a path to a file that contains a X.509 certificate to present
to the remote entity. The 'file' must be in PEM format. When this option
is not specified, the default location ('/etc/pki/criu/cert.pem') will be
used.
*--tls-key* 'file'::
Specifies a path to a file that contains TLS private key. The 'file' must
be in PEM format. When this option is not the default location
('/etc/pki/criu/private/key.pem') will be used.
*--tls*::
Use TLS to secure remote connections.
*lazy-pages*
~~~~~~~~~~~~
Launches *criu* in lazy-pages daemon mode.

View file

@ -193,6 +193,7 @@ include Makefile.config
else
# To clean all files, enable make/build options here
export CONFIG_COMPAT := y
export CONFIG_GNUTLS := y
endif
#

View file

@ -1,5 +1,5 @@
# here is a workaround for a bug in libnl-3:
# 6a8d90f5fec4 "attr: Allow attribute type 0"
# 6a8d90f5fec4 "attr: Allow attribute type 0"
WRAPFLAGS += -Wl,--wrap=nla_parse,--wrap=nlmsg_parse
ARCH_DIR := criu/arch/$(SRCARCH)
@ -14,6 +14,7 @@ endif
#
# Configuration file paths
CONFIG-DEFINES += -DSYSCONFDIR='"/etc"'
CONFIG-DEFINES += -DGLOBAL_CONFIG_DIR='"/etc/criu/"'
CONFIG-DEFINES += -DDEFAULT_CONFIG_FILENAME='"default.conf"'
CONFIG-DEFINES += -DUSER_CONFIG_DIR='".criu/"'

View file

@ -73,6 +73,7 @@ obj-y += string.o
obj-y += sysctl.o
obj-y += sysfs_parse.o
obj-y += timerfd.o
obj-$(CONFIG_GNUTLS) += tls.o
obj-y += tty.o
obj-y += tun.o
obj-y += util.o

View file

@ -510,6 +510,11 @@ int parse_options(int argc, char **argv, bool *usage_error,
{ "ps-socket", required_argument, 0, 1091},
{ "config", required_argument, 0, 1089},
{ "no-default-config", no_argument, 0, 1090},
{ "tls-cacert", required_argument, 0, 1092},
{ "tls-cacrl", required_argument, 0, 1093},
{ "tls-cert", required_argument, 0, 1094},
{ "tls-key", required_argument, 0, 1095},
BOOL_OPT("tls", &opts.tls),
{ },
};
@ -796,6 +801,18 @@ int parse_options(int argc, char **argv, bool *usage_error,
case 1091:
opts.ps_socket = atoi(optarg);
break;
case 1092:
SET_CHAR_OPTS(tls_cacert, optarg);
break;
case 1093:
SET_CHAR_OPTS(tls_cacrl, optarg);
break;
case 1094:
SET_CHAR_OPTS(tls_cert, optarg);
break;
case 1095:
SET_CHAR_OPTS(tls_key, optarg);
break;
case 'V':
pr_msg("Version: %s\n", CRIU_VERSION);
if (strcmp(CRIU_GITID, "0"))
@ -857,6 +874,13 @@ int check_options()
}
}
#ifndef CONFIG_GNUTLS
if (opts.tls) {
pr_err("CRIU was built without TLS support\n");
return 1;
}
#endif
if (check_namespace_opts()) {
pr_err("Error: namespace flags conflict\n");
return 1;

View file

@ -427,6 +427,11 @@ usage:
" -d|--daemon run in the background after creating socket\n"
" --status-fd FD write \\0 to the FD and close it once process is ready\n"
" to handle requests\n"
" --tls-cacert FILE trust certificates signed only by this CA\n"
" --tls-cacrl FILE path to CA certificate revocation list file\n"
" --tls-cert FILE path to TLS certificate file\n"
" --tls-key FILE path to TLS private key file\n"
" --tls use TLS to secure remote connection\n"
"\n"
"Configuration file options:\n"
" --config FILEPATH pass a specific configuration file\n"

View file

@ -138,6 +138,11 @@ struct cr_options {
pid_t tree_id;
int log_level;
char *imgs_dir;
char *tls_cacert;
char *tls_cacrl;
char *tls_cert;
char *tls_key;
int tls;
};
extern struct cr_options opts;

26
criu/include/tls.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef __CR_TLS_H__
#define __CR_TLS_H__
# ifdef CONFIG_GNUTLS
int tls_x509_init(int sockfd, bool is_server);
void tls_terminate_session();
ssize_t tls_send(const void *buf, size_t len, int flags);
ssize_t tls_recv(void *buf, size_t len, int flags);
int tls_send_data_from_fd(int fd, unsigned long len);
int tls_recv_data_to_fd(int fd, unsigned long len);
# else /* CONFIG_GNUTLS */
#define tls_x509_init(sockfd, is_server) (0)
#define tls_send(buf, len, flags) (-1)
#define tls_recv(buf, len, flags) (-1)
#define tls_send_data_from_fd(fd, len) (-1)
#define tls_recv_data_to_fd(fd, len) (-1)
#define tls_terminate_session()
#endif /* CONFIG_HAS_GNUTLS */
#endif /* __CR_TLS_H__ */

View file

@ -21,6 +21,7 @@
#include "parasite-syscall.h"
#include "rst_info.h"
#include "stats.h"
#include "tls.h"
static int page_server_sk = -1;
@ -128,13 +129,22 @@ static inline u32 decode_ps_flags(u32 cmd)
return cmd >> PS_CMD_BITS;
}
static inline int __send(int sk, const void *buf, size_t sz, int fl)
{
return opts.tls ? tls_send(buf, sz, fl) : send(sk, buf, sz, fl);
}
static inline int __recv(int sk, void *buf, size_t sz, int fl)
{
return opts.tls ? tls_recv(buf, sz, fl) : recv(sk, buf, sz, fl);
}
static inline int send_psi_flags(int sk, struct page_server_iov *pi, int flags)
{
if (send(sk, pi, sizeof(*pi), flags) != sizeof(*pi)) {
if (__send(sk, pi, sizeof(*pi), flags) != sizeof(*pi)) {
pr_perror("Can't send PSI %d to server", pi->cmd);
return -1;
}
return 0;
}
@ -149,17 +159,28 @@ static int write_pages_to_server(struct page_xfer *xfer,
{
ssize_t ret, left = len;
pr_debug("Splicing %lu bytes / %lu pages into socket\n", len, len / PAGE_SIZE);
if (opts.tls) {
pr_debug("Sending %lu bytes / %lu pages\n",
len, len / PAGE_SIZE);
while (left > 0) {
ret = splice(p, NULL, xfer->sk, NULL, left, SPLICE_F_MOVE);
if (ret < 0) {
pr_perror("Can't write pages to socket");
if (tls_send_data_from_fd(p, len))
return -1;
}
} else {
pr_debug("Splicing %lu bytes / %lu pages into socket\n",
len, len / PAGE_SIZE);
pr_debug("\tSpliced: %lu bytes sent\n", (unsigned long)ret);
left -= ret;
while (left > 0) {
ret = splice(p, NULL, xfer->sk, NULL, left,
SPLICE_F_MOVE);
if (ret < 0) {
pr_perror("Can't write pages to socket");
return -1;
}
pr_debug("\tSpliced: %lu bytes sent\n",
(unsigned long)ret);
left -= ret;
}
}
return 0;
@ -205,7 +226,7 @@ static int open_page_server_xfer(struct page_xfer *xfer, int fd_type, unsigned l
/* Push the command NOW */
tcp_nodelay(xfer->sk, true);
if (read(xfer->sk, &has_parent, 1) != 1) {
if (__recv(xfer->sk, &has_parent, 1, 0) != 1) {
pr_perror("The page server doesn't answer");
return -1;
}
@ -539,7 +560,7 @@ static int page_server_check_parent(int sk, struct page_server_iov *pi)
if (ret < 0)
return -1;
if (write(sk, &ret, sizeof(ret)) != sizeof(ret)) {
if (__send(sk, &ret, sizeof(ret), 0) != sizeof(ret)) {
pr_perror("Unable to send response");
return -1;
}
@ -560,7 +581,7 @@ static int check_parent_server_xfer(int fd_type, unsigned long img_id)
tcp_nodelay(page_server_sk, true);
if (read(page_server_sk, &has_parent, sizeof(int)) != sizeof(int)) {
if (__recv(page_server_sk, &has_parent, sizeof(int), 0) != sizeof(int)) {
pr_perror("The page server doesn't answer");
return -1;
}
@ -624,8 +645,7 @@ static int page_server_open(int sk, struct page_server_iov *pi)
if (sk >= 0) {
char has_parent = !!cxfer.loc_xfer.parent;
if (write(sk, &has_parent, 1) != 1) {
if (__send(sk, &has_parent, 1, 0) != 1) {
pr_perror("Unable to send response");
close_page_xfer(&cxfer.loc_xfer);
return -1;
@ -684,14 +704,23 @@ static int page_server_add(int sk, struct page_server_iov *pi, u32 flags)
return -1;
}
chunk = splice(sk, NULL, cxfer.p[1], NULL, chunk, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
if (chunk < 0) {
pr_perror("Can't read from socket");
return -1;
}
if (chunk == 0) {
pr_err("A socket was closed unexpectedly\n");
return -1;
if (opts.tls) {
if(tls_recv_data_to_fd(cxfer.p[1], chunk)) {
pr_err("Can't read from socket\n");
return -1;
}
} else {
chunk = splice(sk, NULL, cxfer.p[1], NULL, chunk,
SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
if (chunk < 0) {
pr_perror("Can't read from socket");
return -1;
}
if (chunk == 0) {
pr_err("A socket was closed unexpectedly\n");
return -1;
}
}
if (lxfer->write_pages(lxfer, cxfer.p[0], chunk))
@ -733,9 +762,16 @@ static int page_server_get_pages(int sk, struct page_server_iov *pi)
return -1;
len = pi->nr_pages * PAGE_SIZE;
ret = splice(pipe_read_dest.p[0], NULL, sk, NULL, len, SPLICE_F_MOVE);
if (ret != len)
return -1;
if (opts.tls) {
if (tls_send_data_from_fd(pipe_read_dest.p[0], len))
return -1;
} else {
ret = splice(pipe_read_dest.p[0], NULL, sk, NULL, len,
SPLICE_F_MOVE);
if (ret != len)
return -1;
}
tcp_nodelay(sk, true);
@ -773,7 +809,7 @@ static int page_server_serve(int sk)
struct page_server_iov pi;
u32 cmd;
ret = recv(sk, &pi, sizeof(pi), MSG_WAITALL);
ret = __recv(sk, &pi, sizeof(pi), MSG_WAITALL);
if (!ret)
break;
@ -823,7 +859,7 @@ static int page_server_serve(int sk)
* An answer must be sent back to inform another side,
* that all data were received
*/
if (write(sk, &status, sizeof(status)) != sizeof(status)) {
if (__send(sk, &status, sizeof(status), 0) != sizeof(status)) {
pr_perror("Can't send the final package");
ret = -1;
}
@ -856,14 +892,15 @@ static int page_server_serve(int sk)
* Wait when a remote side closes the connection
* to avoid TIME_WAIT bucket
*/
if (read(sk, &c, sizeof(c)) != 0) {
pr_perror("Unexpected data");
ret = -1;
}
}
tls_terminate_session();
page_server_close();
pr_info("Session over\n");
close(sk);
@ -1011,6 +1048,11 @@ no_server:
if (ret != 0)
return ret > 0 ? 0 : -1;
if (tls_x509_init(ask, true)) {
close(sk);
return -1;
}
if (ask >= 0)
ret = page_server_serve(ask);
@ -1034,6 +1076,11 @@ static int connect_to_page_server(void)
page_server_sk = setup_tcp_client(opts.addr);
if (page_server_sk == -1)
return -1;
if (tls_x509_init(page_server_sk, false)) {
close(page_server_sk);
return -1;
}
out:
/*
* CORK the socket at the very beginning. As per ANK
@ -1076,14 +1123,16 @@ int disconnect_from_page_server(void)
if (send_psi(page_server_sk, &pi))
goto out;
if (read(page_server_sk, &status, sizeof(status)) != sizeof(status)) {
if (__recv(page_server_sk, &status, sizeof(status), 0) != sizeof(status)) {
pr_perror("The page server doesn't answer");
goto out;
}
ret = 0;
out:
tls_terminate_session();
close_safe(&page_server_sk);
return ret ? : status;
}
@ -1160,10 +1209,14 @@ static int page_server_read(struct ps_async_read *ar, int flags)
need = ar->goal - ar->rb;
}
ret = recv(page_server_sk, buf, need, flags);
ret = __recv(page_server_sk, buf, need, flags);
if (ret < 0) {
pr_perror("Error reading async data from page server");
return -1;
if (flags == MSG_DONTWAIT && (errno == EAGAIN || errno == EINTR)) {
ret = 0;
} else {
pr_perror("Error reading data from page server");
return -1;
}
}
ar->rb += ret;

366
criu/tls.c Normal file
View file

@ -0,0 +1,366 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/limits.h>
#include <gnutls/gnutls.h>
#include "cr_options.h"
#include "xmalloc.h"
/* Compatability with GnuTLS verson <3.5 */
#ifndef GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR
# define GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR GNUTLS_E_CERTIFICATE_ERROR
#endif
#undef LOG_PREFIX
#define LOG_PREFIX "tls: "
#define CRIU_PKI_DIR SYSCONFDIR "/pki"
#define CRIU_CACERT CRIU_PKI_DIR "/CA/cacert.pem"
#define CRIU_CACRL CRIU_PKI_DIR "/CA/cacrl.pem"
#define CRIU_CERT CRIU_PKI_DIR "/criu/cert.pem"
#define CRIU_KEY CRIU_PKI_DIR "/criu/private/key.pem"
#define SPLICE_BUF_SZ_MAX (PIPE_BUF * 100)
#define tls_perror(msg, ret) pr_err("%s: %s\n", msg, gnutls_strerror(ret))
static gnutls_session_t session;
static gnutls_certificate_credentials_t x509_cred;
static int tls_sk = -1;
static int tls_sk_flags = 0;
void tls_terminate_session()
{
int ret;
if (!opts.tls)
return;
if (session) {
do {
/* don't wait for peer to close connection */
ret = gnutls_bye(session, GNUTLS_SHUT_WR);
} while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
gnutls_deinit(session);
}
tls_sk = -1;
if (x509_cred)
gnutls_certificate_free_credentials(x509_cred);
}
ssize_t tls_send(const void *buf, size_t len, int flags)
{
int ret;
tls_sk_flags = flags;
ret = gnutls_record_send(session, buf, len);
tls_sk_flags = 0;
if (ret < 0) {
switch(ret) {
case GNUTLS_E_AGAIN:
errno = EAGAIN;
break;
case GNUTLS_E_INTERRUPTED:
errno = EINTR;
break;
case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
errno = ENOMSG;
break;
default:
tls_perror("Failed to send data", ret);
errno = EIO;
break;
}
}
return ret;
}
/*
* Read data from a file descriptor, then encrypt and send it with GnuTLS.
* This function is used for cases when we would otherwise use splice()
* to transfer data from PIPE to TCP socket.
*/
int tls_send_data_from_fd(int fd, unsigned long len)
{
ssize_t copied;
unsigned long buf_size = min(len, (unsigned long)SPLICE_BUF_SZ_MAX);
void *buf = xmalloc(buf_size);
if (!buf)
return -1;
while (len > 0) {
int ret, sent;
copied = read(fd, buf, min(len, buf_size));
if (copied <= 0) {
pr_perror("Can't read from pipe");
goto err;
}
for(sent = 0; sent < copied; sent += ret) {
ret = tls_send((buf + sent), (copied - sent), 0);
if (ret < 0) {
tls_perror("Failed sending data", ret);
goto err;
}
}
len -= copied;
}
err:
xfree(buf);
return (len > 0);
}
ssize_t tls_recv(void *buf, size_t len, int flags)
{
int ret;
tls_sk_flags = flags;
ret = gnutls_record_recv(session, buf, len);
tls_sk_flags = 0;
/* Check if there are any data to receive in the gnutls buffers. */
if (flags == MSG_DONTWAIT
&& (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)) {
size_t pending = gnutls_record_check_pending(session);
if (pending > 0) {
pr_debug("Receiving pending data (%zu bytes)\n", pending);
ret = gnutls_record_recv(session, buf, len);
}
}
if (ret < 0) {
switch (ret) {
case GNUTLS_E_AGAIN:
errno = EAGAIN;
break;
case GNUTLS_E_INTERRUPTED:
errno = EINTR;
break;
default:
tls_perror("Failed receiving data", ret);
errno = EIO;
break;
}
ret = -1;
}
return ret;
}
/*
* Read and decrypt data with GnuTLS, then write it to a file descriptor.
* This function is used for cases when we would otherwise use splice()
* to transfer data from a TCP socket to a PIPE.
*/
int tls_recv_data_to_fd(int fd, unsigned long len)
{
gnutls_packet_t packet;
while (len > 0) {
int ret, w;
gnutls_datum_t pdata;
ret = gnutls_record_recv_packet(session, &packet);
if (ret == 0) {
pr_info("Connection closed by peer\n");
break;
} else if (ret < 0) {
tls_perror("Received corrupted data", ret);
break;
}
gnutls_packet_get(packet, &pdata, NULL);
for(w = 0; w < pdata.size; w += ret) {
ret = write(fd, (pdata.data + w), (pdata.size - w));
if (ret < 0) {
pr_perror("Failed writing to fd");
goto err;
}
}
len -= pdata.size;
}
err:
gnutls_packet_deinit(packet);
return (len > 0);
}
static inline void tls_handshake_verification_status_print(int ret, unsigned status)
{
gnutls_datum_t out;
int type = gnutls_certificate_type_get(session);
if (!gnutls_certificate_verification_status_print(status, type, &out, 0))
pr_err("%s\n", out.data);
gnutls_free(out.data);
}
static int tls_x509_verify_peer_cert(void)
{
int ret;
unsigned status;
ret = gnutls_certificate_verify_peers3(session, opts.addr, &status);
if (ret != GNUTLS_E_SUCCESS) {
tls_perror("Unable to verify TLS peer", ret);
return -1;
}
if (status != 0) {
pr_err("Invalid certificate\n");
tls_handshake_verification_status_print(
GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR, status);
return -1;
}
return 0;
}
static int tls_handshake()
{
int ret = -1;
while (ret != GNUTLS_E_SUCCESS) {
ret = gnutls_handshake(session);
if (gnutls_error_is_fatal(ret)) {
tls_perror("TLS handshake failed", ret);
return -1;
}
}
pr_info("TLS handshake completed\n");
return 0;
}
static int tls_x509_setup_creds()
{
int ret;
char *cacert = CRIU_CACERT;
char *cacrl = CRIU_CACRL;
char *cert = CRIU_CERT;
char *key = CRIU_KEY;
gnutls_x509_crt_fmt_t pem = GNUTLS_X509_FMT_PEM;
if (opts.tls_cacert)
cacert = opts.tls_cacert;
if (opts.tls_cacrl)
cacrl = opts.tls_cacrl;
if (opts.tls_cert)
cert = opts.tls_cert;
if (opts.tls_key)
key = opts.tls_key;
ret = gnutls_certificate_allocate_credentials(&x509_cred);
if (ret != GNUTLS_E_SUCCESS) {
tls_perror("Failed to allocate x509 credentials", ret);
return -1;
}
if (!opts.tls_cacert) {
ret = gnutls_certificate_set_x509_system_trust(x509_cred);
if (ret < 0) {
tls_perror("Failed to load default trusted CAs", ret);
return -1;
}
}
ret = gnutls_certificate_set_x509_trust_file(x509_cred, cacert, pem);
if (ret == 0) {
pr_info("No trusted CA certificates added (%s)\n", cacert);
if (opts.tls_cacert)
return -1;
}
if (!access(cacrl, R_OK)) {
ret = gnutls_certificate_set_x509_crl_file(x509_cred, cacrl, pem);
if (ret < 0) {
tls_perror("Can't set certificate revocation list", ret);
return -1;
}
} else if (opts.tls_cacrl) {
pr_perror("Can't read certificate revocation list %s", cacrl);
return -1;
}
ret = gnutls_certificate_set_x509_key_file(x509_cred, cert, key, pem);
if (ret != GNUTLS_E_SUCCESS) {
tls_perror("Failed to set certificate/private key pair", ret);
return -1;
}
return 0;
}
static ssize_t _tls_push_cb(void *p, const void* data, size_t sz)
{
int fd = *(int *)(p);
return send(fd, data, sz, tls_sk_flags);
}
static ssize_t _tls_pull_cb(void *p, void* data, size_t sz)
{
int fd = *(int *)(p);
return recv(fd, data, sz, tls_sk_flags);
}
static int tls_x509_setup_session(unsigned int flags)
{
int ret;
ret = gnutls_init(&session, flags);
if (ret != GNUTLS_E_SUCCESS) {
tls_perror("Failed to initialize session", ret);
return -1;
}
ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
if (ret != GNUTLS_E_SUCCESS) {
tls_perror("Failed to set session credentials", ret);
return -1;
}
ret = gnutls_set_default_priority(session);
if (ret != GNUTLS_E_SUCCESS) {
tls_perror("Failed to set priority", ret);
return -1;
}
gnutls_transport_set_ptr(session, &tls_sk);
gnutls_transport_set_push_function(session, _tls_push_cb);
gnutls_transport_set_pull_function(session, _tls_pull_cb);
if (flags == GNUTLS_SERVER) {
/* Require client certificate */
gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE);
/* Do not advertise trusted CAs to the client */
gnutls_certificate_send_x509_rdn_sequence(session, 1);
}
return 0;
}
int tls_x509_init(int sockfd, bool is_server)
{
if (!opts.tls)
return 0;
tls_sk = sockfd;
if (tls_x509_setup_creds())
goto err;
if (tls_x509_setup_session(is_server ? GNUTLS_SERVER : GNUTLS_CLIENT))
goto err;
if (tls_handshake())
goto err;
if (tls_x509_verify_peer_cert())
goto err;
return 0;
err:
tls_terminate_session();
return -1;
}

View file

@ -37,6 +37,7 @@
#include "page-xfer.h"
#include "common/lock.h"
#include "rst-malloc.h"
#include "tls.h"
#include "fdstore.h"
#include "util.h"
@ -1469,5 +1470,7 @@ int cr_lazy_pages(bool daemon)
ret = handle_requests(epollfd, events, nr_fds);
tls_terminate_session();
return ret;
}