All of lore.kernel.org
 help / color / mirror / Atom feed
From: shngmao@gmail.com
To: linux-btrfs@vger.kernel.org
Cc: Sheng Mao <shngmao@gmail.com>
Subject: [PATCH 3/3] btrfs-progs: add TLS arguments to send/receive
Date: Thu, 24 Dec 2020 21:50:37 -0700	[thread overview]
Message-ID: <20201225045037.185537-3-shngmao@gmail.com> (raw)
In-Reply-To: <20201225045037.185537-1-shngmao@gmail.com>

From: Sheng Mao <shngmao@gmail.com>

If TLS is enabled, btrfs send/receive can accept four more
arguments:

- address to connect to/listen on
- port to connect to/listen on
- keyfile name (optional), PEM format is preferred
- TLS mode: TLS 1.2/1.3 and 128/256 GCM

In TLS mode, btrfs receive assumes -e and btrfs receive
stops after receiving an end cmd marker in the stream.

Issue: #326
Signed-off-by: Sheng Mao <shngmao@gmail.com>
---
 Documentation/btrfs-receive.asciidoc |  13 +++
 Documentation/btrfs-send.asciidoc    |   9 ++
 cmds/receive.c                       | 153 ++++++++++++++++++++++----
 cmds/send.c                          | 155 +++++++++++++++++++++++----
 4 files changed, 292 insertions(+), 38 deletions(-)

diff --git a/Documentation/btrfs-receive.asciidoc b/Documentation/btrfs-receive.asciidoc
index e4c4d2c0..0bc70165 100644
--- a/Documentation/btrfs-receive.asciidoc
+++ b/Documentation/btrfs-receive.asciidoc
@@ -38,6 +38,19 @@ A subvolume is made read-only after the receiving process finishes successfully
 -f <FILE>::
 read the stream from <FILE> instead of stdin,
 
+--tls-addr <url>::
+Address to listen on. It can be an IP address or a domain name.
+
+--tls-port <port>::
+The local port of the TLS connection.
+
+--tls-key <file>::
+Use the key from file; otherwise read key from stdin. Key file is first parsed
+as PEM format; if parsing fails, file content is treated as binary key.
+
+--tls-mode <mode>::
+Use tls_12_128_gcm, tls_13_128_gcm, tls_12_256_gcm.
+
 -C|--chroot::
 confine the process to 'path' using `chroot`(1)
 
diff --git a/Documentation/btrfs-send.asciidoc b/Documentation/btrfs-send.asciidoc
index c4a05672..313451d5 100644
--- a/Documentation/btrfs-send.asciidoc
+++ b/Documentation/btrfs-send.asciidoc
@@ -49,6 +49,15 @@ use this snapshot as a clone source for an incremental send (multiple allowed)
 -f <outfile>::
 output is normally written to standard output so it can be, for example, piped
 to btrfs receive. Use this option to write it to a file instead.
+--tls-addr <url>::
+Address of remote receiver. It can be an IP address or a domain name.
+--tls-port <port>::
+The remote port of the TLS connection.
+--tls-key <file>::
+Use the key from file; otherwise read key from stdin. Key file is first parsed
+as PEM format; if parsing fails, file content is treated as binary key.
+--tls-mode <mode>::
+Use tls_12_128_gcm, tls_13_128_gcm, tls_12_256_gcm.
 --no-data::
 send in 'NO_FILE_DATA' mode
 +
diff --git a/cmds/receive.c b/cmds/receive.c
index 2aaba3ff..e1dc5415 100644
--- a/cmds/receive.c
+++ b/cmds/receive.c
@@ -53,8 +53,11 @@
 #include "common/help.h"
 #include "common/path-utils.h"
 
-struct btrfs_receive
-{
+#if KTLS_SEND_RECV == 1
+#include "common/ktls.h"
+#endif
+
+struct btrfs_receive {
 	int mnt_fd;
 	int dest_dir_fd;
 
@@ -1216,7 +1219,7 @@ out:
 	return ret;
 }
 
-static const char * const cmd_receive_usage[] = {
+static const char *const cmd_receive_usage[] = {
 	"btrfs receive [options] <mount>\n"
 	"btrfs receive --dump [options]",
 	"Receive subvolumes from a stream",
@@ -1229,22 +1232,28 @@ static const char * const cmd_receive_usage[] = {
 	"After receiving a subvolume, it is immediately set to",
 	"read-only.",
 	"",
-	"-q|--quiet       suppress all messages, except errors",
-	"-f FILE          read the stream from FILE instead of stdin",
-	"-e               terminate after receiving an <end cmd> marker in the stream.",
-	"                 Without this option the receiver side terminates only in case",
-	"                 of an error on end of file.",
-	"-C|--chroot      confine the process to <mount> using chroot",
+	"-q|--quiet        suppress all messages, except errors",
+	"-f FILE           read the stream from FILE instead of stdin",
+#if KTLS_SEND_RECV == 1
+	"--tls-addr <url>      Address to listen on for incoming TLS connection.",
+	"--tls-port <port>     The remote port of the TLS connection",
+	"--tls-key <file>      Use the key from file; otherwise read key from stdin.",
+	"--tls-mode <mode> Use tls_12_128_gcm, tls_13_128_gcm, tls_12_256_gcm."
+#endif
+	"-e                terminate after receiving an <end cmd> marker in the stream.",
+	"                  Without this option the receiver side terminates only in case",
+	"                  of an error on end of file.",
+	"-C|--chroot       confine the process to <mount> using chroot",
 	"-E|--max-errors NERR",
-	"                 terminate as soon as NERR errors occur while",
-	"                 stream processing commands from the stream.",
-	"                 Default value is 1. A value of 0 means no limit.",
-	"-m ROOTMOUNT     the root mount point of the destination filesystem.",
-	"                 If /proc is not accessible, use this to tell us where",
-	"                 this file system is mounted.",
-	"--dump           dump stream metadata, one line per operation,",
-	"                 does not require the MOUNT parameter",
-	"-v               deprecated, alias for global -v option",
+	"                  terminate as soon as NERR errors occur while",
+	"                  stream processing commands from the stream.",
+	"                  Default value is 1. A value of 0 means no limit.",
+	"-m ROOTMOUNT      the root mount point of the destination filesystem.",
+	"                  If /proc is not accessible, use this to tell us where",
+	"                  this file system is mounted.",
+	"--dump            dump stream metadata, one line per operation,",
+	"                  does not require the MOUNT parameter",
+	"-v                deprecated, alias for global -v option",
 	HELPINFO_INSERT_GLOBALS,
 	HELPINFO_INSERT_VERBOSE,
 	HELPINFO_INSERT_QUIET,
@@ -1261,6 +1270,23 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
 	u64 max_errors = 1;
 	int dump = 0;
 	int ret = 0;
+#if KTLS_SEND_RECV == 1
+	enum {
+		KTLS_IDX_ADDR = 0,
+		KTLS_IDX_PORT = 1,
+		KTLS_IDX_KEY = 2,
+		KTLS_IDX_TLS_MODE = 3,
+		KTLS_IDX_SIZE
+	};
+	char *ktls_args[KTLS_IDX_SIZE];
+	struct ktls_session *ktls_session = NULL;
+	char ktls_username[LOGIN_NAME_MAX] = "btrfs";
+	u32 i = 0;
+	size_t arg_len = 0;
+	int arg_idx = 0;
+
+	explicit_bzero(ktls_args, sizeof(ktls_args));
+#endif
 
 	memset(&rctx, 0, sizeof(rctx));
 	rctx.mnt_fd = -1;
@@ -1285,10 +1311,25 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
 	optind = 0;
 	while (1) {
 		int c;
-		enum { GETOPT_VAL_DUMP = 257 };
+		enum {
+			GETOPT_VAL_DUMP = 257,
+			GETOPT_VAL_ADDR = 300,
+			GETOPT_VAL_PORT = 301,
+			GETOPT_VAL_KEY = 302,
+			GETOPT_VAL_TLS_MODE = 303,
+		};
 		static const struct option long_opts[] = {
 			{ "max-errors", required_argument, NULL, 'E' },
 			{ "chroot", no_argument, NULL, 'C' },
+#if KTLS_SEND_RECV == 1
+			{ "tls-addr", required_argument, NULL,
+			  GETOPT_VAL_ADDR },
+			{ "tls-port", required_argument, NULL,
+			  GETOPT_VAL_PORT },
+			{ "tls-key", required_argument, NULL, GETOPT_VAL_KEY },
+			{ "tls-mode", required_argument, NULL,
+			  GETOPT_VAL_TLS_MODE },
+#endif
 			{ "dump", no_argument, NULL, GETOPT_VAL_DUMP },
 			{ "quiet", no_argument, NULL, 'q' },
 			{ NULL, 0, NULL, 0 }
@@ -1330,6 +1371,27 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
 				goto out;
 			}
 			break;
+#if KTLS_SEND_RECV == 1
+		case GETOPT_VAL_ADDR:
+		case GETOPT_VAL_PORT:
+		case GETOPT_VAL_KEY:
+		case GETOPT_VAL_TLS_MODE:
+			arg_len = strlen(optarg);
+			arg_idx = c - GETOPT_VAL_ADDR;
+			ktls_args[arg_idx] = (char *)malloc(arg_len + 1);
+			if (!ktls_args[arg_idx]) {
+				error("fail to allocate buffer (%zu)", arg_len);
+				ret = 1;
+				goto out;
+			}
+			if (arg_copy_path(ktls_args[arg_idx], optarg,
+					  arg_len + 1)) {
+				error("argument too long (%zu)", arg_len);
+				ret = 1;
+				goto out;
+			}
+			break;
+#endif
 		case GETOPT_VAL_DUMP:
 			dump = 1;
 			break;
@@ -1353,6 +1415,50 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
 		}
 	}
 
+#if KTLS_SEND_RECV == 1
+	if (ktls_args[KTLS_IDX_ADDR]) {
+		if (fromfile[0]) {
+			error("cannot send to both ktls and file");
+			ret = 1;
+			goto out;
+		}
+
+		if (!ktls_args[KTLS_IDX_PORT]) {
+			error("no remote ktls port");
+			ret = 1;
+			goto out;
+		}
+
+		ktls_session = ktls_create_session(false);
+
+		if (ktls_args[KTLS_IDX_TLS_MODE]) {
+			if (ktls_set_tls_mode(ktls_session,
+					      ktls_args[KTLS_IDX_TLS_MODE]))
+				goto out;
+		}
+
+		if (ktls_args[KTLS_IDX_KEY]) {
+			if (ktls_set_psk_session_from_keyfile(
+				    ktls_session, ktls_username,
+				    ktls_args[KTLS_IDX_KEY])) {
+				goto out;
+			};
+		} else {
+			if (ktls_set_psk_session_from_password_prompt(
+				    ktls_session, ktls_username)) {
+				goto out;
+			}
+		}
+
+		receive_fd = ktls_create_sock_oneshot(ktls_session,
+						      ktls_args[KTLS_IDX_ADDR],
+						      ktls_args[KTLS_IDX_PORT]);
+
+		/* socket implies honor end cmd*/
+		rctx.honor_end_cmd = 1;
+	}
+#endif
+
 	if (dump) {
 		struct btrfs_dump_send_args dump_args;
 
@@ -1370,9 +1476,16 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
 		ret = do_receive(&rctx, tomnt, realmnt, receive_fd, max_errors);
 	}
 
+out:
+#if KTLS_SEND_RECV == 1
+	for (i = KTLS_IDX_ADDR; i < KTLS_IDX_SIZE; i++) {
+		if (ktls_args[i])
+			free(ktls_args[i]);
+	}
+	ktls_destroy_session(ktls_session);
+#endif
 	if (receive_fd != fileno(stdin))
 		close(receive_fd);
-out:
 
 	return !!ret;
 }
diff --git a/cmds/send.c b/cmds/send.c
index b8e3ba12..2b58b0c2 100644
--- a/cmds/send.c
+++ b/cmds/send.c
@@ -46,6 +46,10 @@
 #include "common/help.h"
 #include "common/path-utils.h"
 
+#if KTLS_SEND_RECV == 1
+#include "common/ktls.h"
+#endif
+
 #define SEND_BUFFER_SIZE	SZ_64K
 
 
@@ -424,7 +428,7 @@ static void free_send_info(struct btrfs_send *sctx)
 	subvol_uuid_search_finit(&sctx->sus);
 }
 
-static const char * const cmd_send_usage[] = {
+static const char *const cmd_send_usage[] = {
 	"btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
 	"Send the subvolume(s) to stdout.",
 	"Sends the subvolume(s) specified by <subvol> to stdout.",
@@ -439,21 +443,27 @@ static const char * const cmd_send_usage[] = {
 	"which case 'btrfs send' will determine a suitable parent among the",
 	"clone sources itself.",
 	"",
-	"-e               If sending multiple subvols at once, use the new",
-	"                 format and omit the end-cmd between the subvols.",
-	"-p <parent>      Send an incremental stream from <parent> to",
-	"                 <subvol>.",
-	"-c <clone-src>   Use this snapshot as a clone source for an ",
-	"                 incremental send (multiple allowed)",
-	"-f <outfile>     Output is normally written to stdout. To write to",
-	"                 a file, use this option. An alternative would be to",
-	"                 use pipes.",
-	"--no-data        send in NO_FILE_DATA mode, Note: the output stream",
-	"                 does not contain any file data and thus cannot be used",
-	"                 to transfer changes. This mode is faster and useful to",
-	"                 show the differences in metadata.",
-	"-v|--verbose     deprecated, alias for global -v option",
-	"-q|--quiet       deprecated, alias for global -q option",
+	"-e                If sending multiple subvols at once, use the new",
+	"                  format and omit the end-cmd between the subvols.",
+	"-p <parent>       Send an incremental stream from <parent> to",
+	"                  <subvol>.",
+	"-c <clone-src>    Use this snapshot as a clone source for an ",
+	"                  incremental send (multiple allowed)",
+	"-f <outfile>      Output is normally written to stdout. To write to",
+	"                  a file, use this option. An alternative would be to",
+	"                  use pipes.",
+#if KTLS_SEND_RECV == 1
+	"--tls-addr <url>      Address of remote receiver.",
+	"--tls-port <port>     The remote port of the TLS connection",
+	"--tls-key <file>      Use the key from file; otherwise read key from stdin.",
+	"--tls-mode <mode> Use tls_12_128_gcm, tls_13_128_gcm, tls_12_256_gcm."
+#endif
+	"--no-data         send in NO_FILE_DATA mode, Note: the output stream",
+	"                  does not contain any file data and thus cannot be used",
+	"                  to transfer changes. This mode is faster and useful to",
+	"                  show the differences in metadata.",
+	"-v|--verbose      deprecated, alias for global -v option",
+	"-q|--quiet        deprecated, alias for global -q option",
 	HELPINFO_INSERT_GLOBALS,
 	HELPINFO_INSERT_VERBOSE,
 	HELPINFO_INSERT_QUIET,
@@ -474,6 +484,22 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
 	int full_send = 1;
 	int new_end_cmd_semantic = 0;
 	u64 send_flags = 0;
+#if KTLS_SEND_RECV == 1
+	enum {
+		KTLS_IDX_ADDR = 0,
+		KTLS_IDX_PORT = 1,
+		KTLS_IDX_KEY = 2,
+		KTLS_IDX_TLS_MODE = 3,
+		KTLS_IDX_SIZE
+	};
+	char *ktls_args[KTLS_IDX_SIZE];
+	struct ktls_session *ktls_session = NULL;
+	char ktls_username[LOGIN_NAME_MAX] = "btrfs";
+	size_t arg_len = 0;
+	int arg_idx = 0;
+
+	explicit_bzero(ktls_args, sizeof(ktls_args));
+#endif
 
 	memset(&send, 0, sizeof(send));
 	send.dump_fd = fileno(stdout);
@@ -492,11 +518,26 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
 
 	optind = 0;
 	while (1) {
-		enum { GETOPT_VAL_SEND_NO_DATA = 256 };
+		enum {
+			GETOPT_VAL_SEND_NO_DATA = 256,
+			GETOPT_VAL_ADDR = 300,
+			GETOPT_VAL_PORT = 301,
+			GETOPT_VAL_KEY = 302,
+			GETOPT_VAL_TLS_MODE = 303,
+		};
 		static const struct option long_options[] = {
 			{ "verbose", no_argument, NULL, 'v' },
 			{ "quiet", no_argument, NULL, 'q' },
-			{ "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
+			{ "no-data", no_argument, NULL,
+			  GETOPT_VAL_SEND_NO_DATA },
+			{ "tls-addr", required_argument, NULL,
+			  GETOPT_VAL_ADDR },
+			{ "tls-port", required_argument, NULL,
+			  GETOPT_VAL_PORT },
+			{ "tls-key", required_argument, NULL, GETOPT_VAL_KEY },
+			{ "tls-mode", required_argument, NULL,
+			  GETOPT_VAL_TLS_MODE },
+			{ NULL, 0, NULL, 0  }
 		};
 		int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
 
@@ -581,6 +622,27 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
 			error("option -i was removed, use -c instead");
 			ret = 1;
 			goto out;
+#if KTLS_SEND_RECV == 1
+		case GETOPT_VAL_ADDR:
+		case GETOPT_VAL_PORT:
+		case GETOPT_VAL_KEY:
+		case GETOPT_VAL_TLS_MODE:
+			arg_len = strlen(optarg);
+			arg_idx = c - GETOPT_VAL_ADDR;
+			ktls_args[arg_idx] = (char *)malloc(arg_len + 1);
+			if (!ktls_args[arg_idx]) {
+				error("fail to allocate buffer (%zu)", arg_len);
+				ret = 1;
+				goto out;
+			}
+			if (arg_copy_path(ktls_args[arg_idx], optarg,
+					  arg_len + 1)) {
+				error("argument too long (%zu)", arg_len);
+				ret = 1;
+				goto out;
+			}
+			break;
+#endif
 		case GETOPT_VAL_SEND_NO_DATA:
 			send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
 			break;
@@ -613,6 +675,53 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
 			goto out;
 		}
 	}
+#if KTLS_SEND_RECV == 1
+	if (ktls_args[KTLS_IDX_ADDR]) {
+		if (outname[0]) {
+			error("cannot send to both ktls and file");
+			ret = 1;
+			goto out;
+		}
+
+		if (!ktls_args[KTLS_IDX_PORT]) {
+			error("no remote ktls port");
+			ret = 1;
+			goto out;
+		}
+
+		if (!ktls_args[KTLS_IDX_PORT]) {
+			error("fail to create ktls session");
+			ret = 1;
+			goto out;
+		}
+
+		ktls_session = ktls_create_session(true);
+
+		if (ktls_args[KTLS_IDX_TLS_MODE]) {
+			if (ktls_set_tls_mode(ktls_session,
+					      ktls_args[KTLS_IDX_TLS_MODE]))
+				goto out;
+		}
+
+		if (ktls_args[KTLS_IDX_KEY]) {
+			if (ktls_set_psk_session_from_keyfile(
+				    ktls_session, ktls_username,
+				    ktls_args[KTLS_IDX_KEY])) {
+				goto out;
+			};
+		} else {
+			if (ktls_set_psk_session_from_password_prompt(
+				    ktls_session, ktls_username)) {
+				goto out;
+			}
+		}
+
+		send.dump_fd =
+			ktls_create_sock_oneshot(ktls_session,
+						 ktls_args[KTLS_IDX_ADDR],
+						 ktls_args[KTLS_IDX_PORT]);
+	}
+#endif
 
 	if (isatty(send.dump_fd)) {
 		error(
@@ -755,6 +864,16 @@ out:
 	free(snapshot_parent);
 	free(send.clone_sources);
 	free_send_info(&send);
+#if KTLS_SEND_RECV == 1
+	for (i = KTLS_IDX_ADDR; i < KTLS_IDX_SIZE; i++) {
+		if (ktls_args[i])
+			free(ktls_args[i]);
+	}
+	ktls_destroy_session(ktls_session);
+#endif
+	if (send.dump_fd != fileno(stdin))
+		close(send.dump_fd);
+
 	return !!ret;
 }
 DEFINE_SIMPLE_COMMAND(send, "send");
-- 
2.29.2


  parent reply	other threads:[~2020-12-25  4:53 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-25  4:50 [PATCH 1/3] btrfs-progs: add Kernel TLS to btrfs send/receive shngmao
2020-12-25  4:50 ` [PATCH 2/3] btrfs-progs: add build support for ktls feature shngmao
2020-12-25  4:50 ` shngmao [this message]
2020-12-31 11:16   ` [PATCH 3/3] btrfs-progs: add TLS arguments to send/receive Wang Yugui
2020-12-31 18:33     ` Sheng Mao
2021-01-01  5:53       ` Wang Yugui
2021-01-02  3:49         ` [PATCH v2 1/3] btrfs-progs: add Kernel TLS to btrfs send/receive shngmao
2021-01-02  3:49           ` [PATCH v2 2/3] btrfs-progs: add build support for ktls feature shngmao
2021-01-02  3:49           ` [PATCH v2 3/3] btrfs-progs: add TLS arguments to send/receive shngmao
2021-01-02 10:45           ` [PATCH v2 1/3] btrfs-progs: add Kernel TLS to btrfs send/receive Wang Yugui
2021-01-02 15:47             ` Sheng Mao
2021-01-03  4:45               ` Wang Yugui
2021-01-03  5:57                 ` Sheng Mao
2021-01-03 11:19                 ` Wang Yugui
2021-01-04  3:52                   ` Sheng Mao
2021-01-04  4:59                     ` Wang Yugui
2021-01-04  6:25                       ` Sheng Mao
2021-01-07  3:06                         ` Sheng Mao
2021-01-02  4:08         ` [PATCH 3/3] btrfs-progs: add TLS arguments to send/receive Sheng Mao

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201225045037.185537-3-shngmao@gmail.com \
    --to=shngmao@gmail.com \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.