Updated patches to work with the current trunk.
[rsync/rsync-patches.git] / openssl-support.diff
CommitLineData
03019e41 1Casey Marshall wrote:
ce06af28
WD
2
3I've been hacking together a way to use rsync with OpenSSL, and have
4attached my current patch against a recent CVS tree. The details of
5this implementation are:
6
7 1. The SSL code is added as a "layer" that is forked into its own
8 process.
9
10 2. An SSL connection is established by the client issuing the
11 command:
12
13 #starttls
14
37d262c5 15 And, if the daemon allows SSL, it replies with
ce06af28
WD
16
17 @RSYNCD: starttls
18
19 At which point both sides begin negotiating the SSL connection.
20 Servers that can't or don't want to use SSL just treat it as a
21 normal unknown command.
22
23 3. The SSL code is meant to be unobtrusive, and when this patch is
24 applied the program may still be built with no SSL code.
25
26 4. There are a number of details not implemented.
27
28All warnings apply; I don't do C programming all that often, so I
29can't say if I've left any cleanup/compatibility errors in the code.
30
03019e41
WD
31To use this patch, run these commands for a successful build:
32
33 patch -p1 <patches/openssl-support.diff
34 ./prepare-source
35 ./configure
36 make
ce06af28 37
cc3e685d 38diff --git a/Makefile.in b/Makefile.in
fc557362 39index feacb90..3016643 100644
cc3e685d
WD
40--- a/Makefile.in
41+++ b/Makefile.in
fc557362 42@@ -41,7 +41,7 @@ OBJS3=progress.o pipe.o
ce06af28
WD
43 DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
44 popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
45 popt/popthelp.o popt/poptparse.o
46-OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@
47+OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@ @SSL_OBJS@
48
58b399b9 49 TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
ce06af28 50
cc3e685d 51diff --git a/cleanup.c b/cleanup.c
fc557362 52index 19ef072..506f575 100644
cc3e685d
WD
53--- a/cleanup.c
54+++ b/cleanup.c
ffc18846 55@@ -25,6 +25,9 @@
1898c899
WD
56 extern int am_server;
57 extern int am_daemon;
8a529471 58 extern int io_error;
22585581 59+#ifdef HAVE_OPENSSL
8a529471 60+extern int use_ssl;
ce06af28 61+#endif
9c015a83 62 extern int keep_partial;
cc3e685d 63 extern int got_xfer_error;
fc557362
WD
64 extern int output_needs_newline;
65@@ -127,6 +130,14 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
22585581
WD
66 code, file, line);
67 }
78114162 68
22585581
WD
69+#ifdef HAVE_OPENSSL
70+ /* FALLTHROUGH */
71+#include "case_N.h"
72+
b3ea4757
WD
73+ if (use_ssl)
74+ end_tls();
ce06af28 75+#endif
78114162 76+
22585581
WD
77 /* FALLTHROUGH */
78 #include "case_N.h"
79
cc3e685d 80diff --git a/clientserver.c b/clientserver.c
fc557362 81index b6afe00..5467c72 100644
cc3e685d
WD
82--- a/clientserver.c
83+++ b/clientserver.c
fc557362 84@@ -30,6 +30,9 @@ extern int am_sender;
d9a67109
WD
85 extern int am_server;
86 extern int am_daemon;
87 extern int am_root;
22585581 88+#ifdef HAVE_OPENSSL
ce06af28
WD
89+extern int use_ssl;
90+#endif
d9a67109 91 extern int rsync_port;
c0c7984e 92 extern int protect_args;
f2376a08 93 extern int ignore_errors;
fc557362 94@@ -134,8 +137,18 @@ int start_socket_client(char *host, int remote_argc, char *remote_argv[],
f9df736a 95 #endif
ce06af28 96
790ba11a 97 ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv);
9d3fe73a 98+ if (ret)
ce06af28 99+ return ret;
cc3e685d 100+
22585581 101+#ifdef HAVE_OPENSSL
ce06af28
WD
102+ if (use_ssl) {
103+ int f_in = get_tls_rfd();
104+ int f_out = get_tls_wfd();
105+ return client_run(f_in, f_out, -1, argc, argv);
106+ }
107+#endif
cc3e685d
WD
108
109- return ret ? ret : client_run(fd, fd, -1, argc, argv);
ce06af28
WD
110+ return client_run(fd, fd, -1, argc, argv);
111 }
112
790ba11a 113 static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client)
fc557362
WD
114@@ -278,6 +291,32 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
115 if (DEBUG_GTE(CMD, 1))
c8a8b4a7 116 print_child_argv("sending daemon args:", sargs);
ce06af28 117
22585581 118+#ifdef HAVE_OPENSSL
ce06af28
WD
119+ if (use_ssl) {
120+ io_printf(f_out, "#starttls\n");
121+ while (1) {
790ba11a 122+ if (!read_line_old(f_in, line, sizeof line)) {
ce06af28
WD
123+ rprintf(FERROR, "rsync: did not receive reply to #starttls\n");
124+ return -1;
125+ }
126+ if (strncmp(line, "@ERROR", 6) == 0) {
2fd4a7f7 127+ rprintf(FERROR, "%s\n", line);
ce06af28
WD
128+ return -1;
129+ }
790ba11a 130+ if (strcmp(line, "@RSYNCD: starttls") == 0)
ce06af28 131+ break;
ce06af28
WD
132+ rprintf(FINFO, "%s\n", line);
133+ }
134+ if (start_tls(f_in, f_out)) {
135+ rprintf(FERROR, "rsync: error during SSL handshake: %s\n",
136+ get_ssl_error());
137+ return -1;
138+ }
139+ f_in = get_tls_rfd();
140+ f_out = get_tls_wfd();
141+ }
142+#endif
143+
5bf6d6c5
WD
144 io_printf(f_out, "%.*s\n", modlen, modname);
145
146 /* Old servers may just drop the connection here,
fc557362 147@@ -303,6 +342,10 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
ce06af28
WD
148 * server to terminate the listing of modules.
149 * We don't want to go on and transfer
150 * anything; just exit. */
22585581 151+#ifdef HAVE_OPENSSL
ce06af28
WD
152+ if (use_ssl)
153+ end_tls();
154+#endif
155 exit(0);
156 }
157
fc557362 158@@ -310,6 +353,10 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
7628f156 159 rprintf(FERROR, "%s\n", line);
ce06af28
WD
160 /* This is always fatal; the server will now
161 * close the socket. */
22585581 162+#ifdef HAVE_OPENSSL
ce06af28
WD
163+ if (use_ssl)
164+ end_tls();
165+#endif
9d3fe73a
WD
166 return -1;
167 }
168
fc557362 169@@ -1022,6 +1069,9 @@ int start_daemon(int f_in, int f_out)
790ba11a
WD
170 if (exchange_protocols(f_in, f_out, line, sizeof line, 0) < 0)
171 return -1;
2fd4a7f7 172
22585581 173+#ifdef HAVE_OPENSSL
2fd4a7f7
WD
174+retry:
175+#endif
176 line[0] = 0;
790ba11a 177 if (!read_line_old(f_in, line, sizeof line))
2fd4a7f7 178 return -1;
fc557362 179@@ -1033,6 +1083,20 @@ int start_daemon(int f_in, int f_out)
5e929029
WD
180 return -1;
181 }
78114162 182
22585581 183+#ifdef HAVE_OPENSSL
5e929029
WD
184+ if (use_ssl && strcmp(line, "#starttls") == 0) {
185+ io_printf(f_out, "@RSYNCD: starttls\n");
186+ if (start_tls(f_in, f_out)) {
187+ rprintf(FLOG, "SSL connection failed: %s\n",
188+ get_ssl_error());
189+ return -1;
ce06af28 190+ }
5e929029
WD
191+ f_in = get_tls_rfd();
192+ f_out = get_tls_wfd();
2fd4a7f7 193+ goto retry;
5e929029 194+ }
ce06af28 195+#endif
78114162 196+
5e929029
WD
197 if (*line == '#') {
198 /* it's some sort of command that I don't understand */
199 io_printf(f_out, "@ERROR: Unknown command '%s'\n", line);
cc3e685d 200diff --git a/configure.in b/configure.in
fc557362 201index bc7d4a7..73ca6c5 100644
cc3e685d
WD
202--- a/configure.in
203+++ b/configure.in
fc557362 204@@ -293,6 +293,21 @@ if test x"$enable_locale" != x"no"; then
0ca6aebe 205 AC_DEFINE(CONFIG_LOCALE)
ce06af28
WD
206 fi
207
208+AC_ARG_ENABLE(openssl,
209+ AC_HELP_STRING([--enable-openssl], [compile SSL support with OpenSSL.]))
210+
211+if test "x$enable_openssl" != xno
212+then
213+ have_ssl=yes
214+ AC_CHECK_LIB(ssl, SSL_library_init, , [have_ssl=no])
215+ if test "x$have_ssl" = xyes
216+ then
217+ AC_DEFINE(HAVE_OPENSSL, 1, [true if you want to use SSL.])
218+ SSL_OBJS=ssl.o
219+ AC_SUBST(SSL_OBJS)
220+ fi
221+fi
222+
223 AC_MSG_CHECKING([whether to call shutdown on all sockets])
224 case $host_os in
225 *cygwin* ) AC_MSG_RESULT(yes)
cc3e685d 226diff --git a/options.c b/options.c
fc557362 227index e7c6c61..634b89e 100644
cc3e685d
WD
228--- a/options.c
229+++ b/options.c
fc557362 230@@ -191,6 +191,14 @@ int logfile_format_has_o_or_i = 0;
ce06af28
WD
231 int always_checksum = 0;
232 int list_only = 0;
233
22585581 234+#ifdef HAVE_OPENSSL
ce06af28
WD
235+int use_ssl = 0;
236+char *ssl_cert_path = NULL;
237+char *ssl_key_path = NULL;
238+char *ssl_key_passwd = NULL;
239+char *ssl_ca_path = NULL;
240+#endif
241+
9be39c35
WD
242 #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
243 char *batch_name = NULL;
244
fc557362 245@@ -566,6 +574,7 @@ static void print_rsync_version(enum logcode f)
ce06af28 246 char const *links = "no ";
58b399b9 247 char const *iconv = "no ";
ce06af28
WD
248 char const *ipv6 = "no ";
249+ char const *ssl = "no ";
250 STRUCT_STAT *dumstat;
251
ac2da598 252 #if SUBPROTOCOL_VERSION != 0
fc557362 253@@ -599,6 +608,9 @@ static void print_rsync_version(enum logcode f)
85096e5e
WD
254 #if defined HAVE_LUTIMES && defined HAVE_UTIMES
255 symtimes = "";
ce06af28 256 #endif
22585581 257+#ifdef HAVE_OPENSSL
ce06af28
WD
258+ ssl = "";
259+#endif
ac2da598
WD
260
261 rprintf(f, "%s version %s protocol version %d%s\n",
262 RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
fc557362 263@@ -612,8 +624,8 @@ static void print_rsync_version(enum logcode f)
5e3c6c93
WD
264 (int)(sizeof (int64) * 8));
265 rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
266 got_socketpair, hardlinks, links, ipv6, have_inplace);
85096e5e
WD
267- rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes\n",
268- have_inplace, acls, xattrs, iconv, symtimes);
269+ rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sSSL\n",
270+ have_inplace, acls, xattrs, iconv, symtimes, ssl);
5e3c6c93 271
ce06af28 272 #ifdef MAINTAINER_MODE
5e3c6c93 273 rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
fc557362 274@@ -784,6 +796,13 @@ void usage(enum logcode F)
d705d4dd 275 #endif
d4e89c6a
WD
276 rprintf(F," -4, --ipv4 prefer IPv4\n");
277 rprintf(F," -6, --ipv6 prefer IPv6\n");
22585581 278+#ifdef HAVE_OPENSSL
ce06af28 279+ rprintf(F," --ssl allow socket connections to use SSL\n");
37d262c5
WD
280+ rprintf(F," --ssl-cert=FILE path to daemon's SSL certificate\n");
281+ rprintf(F," --ssl-key=FILE path to daemon's SSL private key\n");
ce06af28
WD
282+ rprintf(F," --ssl-key-passwd=PASS password for PEM-encoded private key\n");
283+ rprintf(F," --ssl-ca-certs=FILE path to trusted CA certificates\n");
284+#endif
896871f8 285 rprintf(F," --version print version number\n");
d9a67109 286 rprintf(F,"(-h) --help show this help (-h works with no other options)\n");
ce06af28 287
fc557362 288@@ -798,7 +817,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
0ca6aebe 289 OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
5398d042 290 OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
fc557362
WD
291 OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG,
292- OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN,
293+ OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_USE_SSL,
0ca6aebe 294 OPT_SERVER, OPT_REFUSED_BASE = 9000};
ce06af28 295
70c5d149 296 static struct poptOption long_options[] = {
fc557362 297@@ -1012,6 +1031,13 @@ static struct poptOption long_options[] = {
489b0a72 298 {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 },
0ca6aebe 299 {"server", 0, POPT_ARG_NONE, 0, OPT_SERVER, 0, 0 },
489b0a72 300 {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 },
22585581 301+#ifdef HAVE_OPENSSL
5388f859
WD
302+ {"ssl", 0, POPT_ARG_NONE, 0, OPT_USE_SSL, 0, 0},
303+ {"ssl-cert", 0, POPT_ARG_STRING, &ssl_cert_path, OPT_USE_SSL, 0, 0},
304+ {"ssl-key", 0, POPT_ARG_STRING, &ssl_key_path, OPT_USE_SSL, 0, 0},
ce06af28 305+ {"ssl-key-passwd", 0, POPT_ARG_STRING, &ssl_key_passwd, OPT_USE_SSL, 0, 0},
5388f859 306+ {"ssl-ca-certs", 0, POPT_ARG_STRING, &ssl_ca_path, OPT_USE_SSL, 0, 0},
ce06af28 307+#endif
489b0a72 308 /* All the following options switch us into daemon-mode option-parsing. */
5388f859 309 {"config", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
2b06a19d 310 {"daemon", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
fc557362 311@@ -1039,6 +1065,13 @@ static void daemon_usage(enum logcode F)
d705d4dd 312 rprintf(F," -v, --verbose increase verbosity\n");
144adbf3
WD
313 rprintf(F," -4, --ipv4 prefer IPv4\n");
314 rprintf(F," -6, --ipv6 prefer IPv6\n");
22585581 315+#ifdef HAVE_OPENSSL
144adbf3
WD
316+ rprintf(F," --ssl allow socket connections to use SSL\n");
317+ rprintf(F," --ssl-cert=FILE path to daemon's SSL certificate\n");
318+ rprintf(F," --ssl-key=FILE path to daemon's SSL private key\n");
319+ rprintf(F," --ssl-key-passwd=PASS password for PEM-encoded private key\n");
320+ rprintf(F," --ssl-ca-certs=FILE path to trusted CA certificates\n");
321+#endif
322 rprintf(F," --help show this help screen\n");
323
324 rprintf(F,"\n");
fc557362 325@@ -1064,6 +1097,13 @@ static struct poptOption long_daemon_options[] = {
144adbf3
WD
326 {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 },
327 {"server", 0, POPT_ARG_NONE, &am_server, 0, 0, 0 },
328 {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
22585581 329+#ifdef HAVE_OPENSSL
144adbf3
WD
330+ {"ssl", 0, POPT_ARG_NONE, 0, OPT_USE_SSL, 0, 0},
331+ {"ssl-cert", 0, POPT_ARG_STRING, &ssl_cert_path, OPT_USE_SSL, 0, 0},
332+ {"ssl-key", 0, POPT_ARG_STRING, &ssl_key_path, OPT_USE_SSL, 0, 0},
333+ {"ssl-key-passwd", 0, POPT_ARG_STRING, &ssl_key_passwd, OPT_USE_SSL, 0, 0},
334+ {"ssl-ca-certs", 0, POPT_ARG_STRING, &ssl_ca_path, OPT_USE_SSL, 0, 0},
335+#endif
336 {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 },
337 {"no-verbose", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
338 {"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 },
fc557362 339@@ -1358,6 +1398,12 @@ int parse_arguments(int *argc_p, const char ***argv_p)
144adbf3
WD
340 verbose++;
341 break;
342
343+#ifdef HAVE_OPENSSL
344+ case OPT_USE_SSL:
345+ use_ssl = 1;
346+ break;
347+#endif
348+
349 default:
350 rprintf(FERROR,
351 "rsync: %s: %s (in daemon mode)\n",
fc557362 352@@ -1384,6 +1430,17 @@ int parse_arguments(int *argc_p, const char ***argv_p)
144adbf3
WD
353 exit_cleanup(RERR_SYNTAX);
354 }
355
356+#ifdef HAVE_OPENSSL
357+ if (use_ssl) {
358+ if (init_tls()) {
359+ snprintf(err_buf, sizeof(err_buf),
360+ "Openssl error: %s\n",
361+ get_ssl_error());
362+ return 0;
363+ }
364+ }
365+#endif
366+
790ba11a
WD
367 *argv_p = argv = poptGetArgs(pc);
368 *argc_p = argc = count_args(argv);
144adbf3 369 am_starting_up = 0;
fc557362 370@@ -1742,6 +1799,12 @@ int parse_arguments(int *argc_p, const char ***argv_p)
5795bf59 371 return 0;
ffc18846
WD
372 #endif
373
22585581 374+#ifdef HAVE_OPENSSL
144adbf3 375+ case OPT_USE_SSL:
ce06af28 376+ use_ssl = 1;
ce06af28 377+ break;
144adbf3 378+#endif
ce06af28
WD
379+
380 default:
381 /* A large opt value means that set_refuse_options()
27a7053c 382 * turned this option off. */
fc557362 383@@ -2108,6 +2171,17 @@ int parse_arguments(int *argc_p, const char ***argv_p)
a7219d20 384 if (delay_updates && !partial_dir)
4a65fe72 385 partial_dir = tmp_partialdir;
78114162 386
22585581 387+#ifdef HAVE_OPENSSL
ce06af28
WD
388+ if (use_ssl) {
389+ if (init_tls()) {
390+ snprintf(err_buf, sizeof(err_buf),
391+ "Openssl error: %s\n",
392+ get_ssl_error());
393+ return 0;
394+ }
395+ }
396+#endif
78114162 397+
4c1f2ca5 398 if (inplace) {
3d1facaa 399 #ifdef HAVE_FTRUNCATE
4c1f2ca5 400 if (partial_dir) {
fc557362
WD
401@@ -2698,9 +2772,18 @@ char *check_for_hostspec(char *s, char **host_ptr, int *port_ptr)
402 {
403 char *path;
404
def2ace9 405- if (port_ptr && strncasecmp(URL_PREFIX, s, strlen(URL_PREFIX)) == 0) {
fc557362
WD
406- *host_ptr = parse_hostspec(s + strlen(URL_PREFIX), &path, port_ptr);
407- if (*host_ptr) {
408+ if (port_ptr) {
409+ int url_prefix_len;
410+ if (strncasecmp(URL_PREFIX, s, sizeof URL_PREFIX - 1) == 0)
411+ url_prefix_len = sizeof URL_PREFIX - 1;
412+ else if (strncasecmp(SSL_URL_PREFIX, s, sizeof SSL_URL_PREFIX - 1) == 0) {
def2ace9
WD
413+ if (!use_ssl)
414+ init_tls();
415+ use_ssl = 1;
fc557362
WD
416+ url_prefix_len = sizeof SSL_URL_PREFIX - 1;
417+ } else
418+ url_prefix_len = 0;
419+ if (url_prefix_len && (*host_ptr = parse_hostspec(s + url_prefix_len, &path, port_ptr))) {
420 if (!*port_ptr)
421 *port_ptr = RSYNC_PORT;
422 return path;
cc3e685d 423diff --git a/rsync.h b/rsync.h
fc557362 424index be7cf8a..7b7cc88 100644
cc3e685d
WD
425--- a/rsync.h
426+++ b/rsync.h
ffc18846 427@@ -31,6 +31,7 @@
ce06af28
WD
428
429 #define DEFAULT_LOCK_FILE "/var/run/rsyncd.lock"
430 #define URL_PREFIX "rsync://"
431+#define SSL_URL_PREFIX "rsyncs://"
432
fc557362 433 #define SYMLINK_PREFIX "/rsyncd-munged/" /* This MUST have a trailing slash! */
cc3e685d 434 #define SYMLINK_PREFIX_LEN ((int)sizeof SYMLINK_PREFIX - 1)
fc557362 435@@ -569,6 +570,11 @@ typedef unsigned int size_t;
a7219d20 436 # define SIZEOF_INT64 SIZEOF_OFF_T
78114162
WD
437 #endif
438
22585581 439+#ifdef HAVE_OPENSSL
ce06af28
WD
440+#include <openssl/ssl.h>
441+#include <openssl/err.h>
78114162
WD
442+#endif
443+
9c25eef5
WD
444 struct hashtable {
445 void *nodes;
446 int32 size, entries;
cc3e685d
WD
447diff --git a/ssl.c b/ssl.c
448new file mode 100644
fc557362 449index 0000000..f0d4d9f
cc3e685d
WD
450--- /dev/null
451+++ b/ssl.c
fc557362 452@@ -0,0 +1,369 @@
ce06af28 453+/* -*- c-file-style: "linux" -*-
e2e42a01 454+ * ssl.c: operations for negotiating SSL rsync connections.
ce06af28
WD
455+ *
456+ * Copyright (C) 2003 Casey Marshall <rsdio@metastatic.org>
457+ *
458+ * This program is free software; you can redistribute it and/or modify
459+ * it under the terms of the GNU General Public License as published by
460+ * the Free Software Foundation; either version 2 of the License, or
461+ * (at your option) any later version.
e2e42a01 462+ *
ce06af28
WD
463+ * This program is distributed in the hope that it will be useful,
464+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
465+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
466+ * GNU General Public License for more details.
e2e42a01 467+ *
ce06af28
WD
468+ * You should have received a copy of the GNU General Public License
469+ * along with this program; if not, write to the Free Software
470+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
471+ */
472+
473+#include "rsync.h"
474+
22585581 475+#ifdef HAVE_SYS_SELECT_H
ce06af28
WD
476+#include <sys/select.h>
477+#else
478+#include <sys/time.h>
479+#include <sys/types.h>
480+#include <unistd.h>
481+#endif
482+#include <string.h>
483+
484+#define BUF_SIZE 1024
485+
ce06af28
WD
486+extern int am_daemon;
487+extern int am_server;
488+
489+extern char *ssl_cert_path;
490+extern char *ssl_key_path;
491+extern char *ssl_key_passwd;
492+extern char *ssl_ca_path;
493+
494+static SSL_CTX *ssl_ctx;
495+static SSL *ssl;
496+static int tls_read[2] = { -1, -1 };
497+static int tls_write[2] = { -1, -1 };
498+static int ssl_running;
499+static int ssl_pid = -1;
500+
144adbf3
WD
501+#ifdef HAVE_SIGACTION
502+static struct sigaction sigact;
503+#endif
504+
ce06af28
WD
505+/**
506+ * A non-interactive callback to be passed to SSL_CTX_set_default_password_cb,
507+ * which merely copies the value of ssl_key_passwd into buf. This is
508+ * used for when the private key password is supplied via an option.
509+ */
510+static int default_password_cb(char *buf, int n, UNUSED(int f), UNUSED(void *u))
511+{
2fd4a7f7 512+ if (ssl_key_passwd == NULL || n < (int)strlen(ssl_key_passwd))
ce06af28
WD
513+ return 0;
514+ strncpy(buf, ssl_key_passwd, n-1);
515+ return strlen(ssl_key_passwd);
516+}
517+
518+/**
519+ * If verbose, this method traces the status of the SSL handshake.
520+ */
2fd4a7f7 521+static void info_callback(const SSL *ssl, int cb, int val)
ce06af28
WD
522+{
523+ char buf[128];
524+ char *cbs;
525+
526+ switch (cb) {
527+ case SSL_CB_LOOP:
528+ cbs = "SSL_CB_LOOP";
529+ break;
530+ case SSL_CB_EXIT:
531+ cbs = "SSL_CB_EXIT";
532+ break;
533+ case SSL_CB_READ:
534+ cbs = "SSL_CB_READ";
535+ break;
536+ case SSL_CB_WRITE:
537+ cbs = "SSL_CB_WRITE";
538+ break;
539+ case SSL_CB_ALERT:
540+ cbs = "SSL_CB_ALERT";
541+ break;
542+ case SSL_CB_READ_ALERT:
543+ cbs = "SSL_CB_READ_ALERT";
544+ break;
545+ case SSL_CB_WRITE_ALERT:
546+ cbs = "SSL_CB_WRITE_ALERT";
547+ break;
548+ case SSL_CB_ACCEPT_LOOP:
549+ cbs = "SSL_CB_ACCEPT_LOOP";
550+ break;
551+ case SSL_CB_ACCEPT_EXIT:
552+ cbs = "SSL_CB_ACCEPT_EXIT";
553+ break;
554+ case SSL_CB_CONNECT_LOOP:
555+ cbs = "SSL_CB_CONNECT_LOOP";
556+ break;
557+ case SSL_CB_CONNECT_EXIT:
558+ cbs = "SSL_CB_CONNECT_EXIT";
559+ break;
560+ case SSL_CB_HANDSHAKE_START:
561+ cbs = "SSL_CB_HANDSHAKE_START";
562+ break;
563+ case SSL_CB_HANDSHAKE_DONE:
564+ cbs = "SSL_CB_HANDSHAKE_DONE";
565+ break;
566+ default:
567+ snprintf(buf, sizeof buf, "??? (%d)", cb);
568+ cbs = buf;
569+ break;
570+ }
fc557362 571+ if (DEBUG_GTE(CONNECT, 1)) {
ce06af28
WD
572+ rprintf(FLOG, "SSL: info_callback(%p,%s,%d)\n", ssl, cbs, val);
573+ if (cb == SSL_CB_HANDSHAKE_DONE) {
2fd4a7f7 574+ SSL_CIPHER_description(SSL_get_current_cipher((SSL*)ssl),
ce06af28
WD
575+ buf, sizeof buf);
576+ rprintf(FLOG, "SSL: cipher: %s", buf);
577+ }
578+ }
579+}
580+
581+/**
582+ * Initializes the SSL context for TLSv1 connections; returns zero on
583+ * success.
584+ */
585+int init_tls(void)
586+{
587+ if (ssl_ctx)
588+ return 0;
589+ SSL_library_init();
590+ SSL_load_error_strings();
591+ ssl_ctx = SSL_CTX_new(TLSv1_method());
592+ if (!ssl_ctx)
593+ return 1;
594+ SSL_CTX_set_info_callback(ssl_ctx, info_callback);
595+
596+ /* Sets the certificate sent to the other party. */
597+ if (ssl_cert_path != NULL
598+ && SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert_path,
599+ SSL_FILETYPE_PEM) != 1)
600+ return 1;
601+ /* Set up the simple non-interactive callback if the password
602+ * was supplied on the command line. */
603+ if (ssl_key_passwd != NULL)
604+ SSL_CTX_set_default_passwd_cb(ssl_ctx, default_password_cb);
605+ /* Sets the private key that matches the public certificate. */
606+ if (ssl_key_path != NULL) {
607+ if (SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_key_path,
608+ SSL_FILETYPE_PEM) != 1)
609+ return 1;
610+ if (SSL_CTX_check_private_key(ssl_ctx) != 1)
611+ return 1;
612+ }
613+ if (ssl_ca_path != NULL
614+ && !SSL_CTX_load_verify_locations(ssl_ctx, ssl_ca_path, NULL))
615+ return 1;
616+
617+ return 0;
618+}
619+
620+/**
621+ * Returns the error string for the current SSL error, if any.
622+ */
623+char *get_ssl_error(void)
624+{
625+ return ERR_error_string(ERR_get_error(), NULL);
626+}
627+
628+/**
629+ * Returns the input file descriptor for the SSL connection.
630+ */
631+int get_tls_rfd(void)
632+{
633+ return tls_read[0];
634+}
635+
636+/**
637+ * Returns the output file descriptor for the SSL connection.
638+ */
639+int get_tls_wfd(void)
640+{
641+ return tls_write[1];
642+}
643+
644+/**
645+ * Signal handler that ends the SSL connection.
646+ */
647+static RETSIGTYPE tls_sigusr1(int UNUSED(val))
648+{
649+ if (ssl) {
650+ SSL_shutdown(ssl);
651+ SSL_free(ssl);
652+ ssl = NULL;
653+ }
654+ ssl_running = 0;
655+}
656+
657+/**
658+ * Negotiates the TLS connection, creates a socket pair for communicating
659+ * with the rsync process, then forks into a new process that will handle
660+ * the communication.
661+ *
662+ * 0 is returned on success.
663+ */
664+int start_tls(int f_in, int f_out)
665+{
666+ int tls_fd;
667+ int n = 0, r;
668+ unsigned char buf1[BUF_SIZE], buf2[BUF_SIZE];
669+ int avail1 = 0, avail2 = 0, write1 = 0, write2 = 0;
670+ fd_set rd, wd;
671+
672+ if (fd_pair(tls_read))
673+ return 1;
674+ if (fd_pair(tls_write))
675+ return 1;
676+
677+ set_blocking(tls_read[0]);
678+ set_blocking(tls_read[1]);
679+ set_blocking(tls_write[0]);
680+ set_blocking(tls_write[1]);
681+ set_blocking(f_in);
682+ set_blocking(f_out);
683+
684+ ssl_pid = do_fork();
685+ if (ssl_pid < 0)
686+ return -1;
687+ if (ssl_pid != 0) {
688+ close(tls_write[0]);
689+ close(tls_read[1]);
690+ return 0;
691+ }
692+
144adbf3 693+ SIGACTION(SIGUSR1, tls_sigusr1);
ce06af28
WD
694+ ssl = SSL_new(ssl_ctx);
695+ if (!ssl)
696+ goto closed;
697+ if (am_daemon || am_server)
698+ SSL_set_accept_state(ssl);
699+ else
700+ SSL_set_connect_state(ssl);
701+ SSL_set_rfd(ssl, f_in);
702+ SSL_set_wfd(ssl, f_out);
703+
704+ tls_fd = SSL_get_fd(ssl);
705+ n = tls_write[0];
706+ n = MAX(tls_read[1], n);
707+ n = MAX(tls_fd, n) + 1;
708+
709+ ssl_running = 1;
710+ while (ssl_running) {
711+ FD_ZERO(&rd);
712+ FD_ZERO(&wd);
713+ FD_SET(tls_write[0], &rd);
714+ FD_SET(tls_read[1], &wd);
715+ FD_SET(tls_fd, &rd);
716+ FD_SET(tls_fd, &wd);
717+
718+ r = select(n, &rd, &wd, NULL, NULL);
719+
720+ if (r == -1 && errno == EINTR)
721+ continue;
722+ if (FD_ISSET(tls_write[0], &rd)) {
723+ r = read(tls_write[0], buf1+avail1, BUF_SIZE-avail1);
724+ if (r >= 0)
725+ avail1 += r;
726+ else {
727+ rprintf(FERROR, "pipe read error: %s\n",
728+ strerror(errno));
729+ break;
730+ }
731+ }
732+ if (FD_ISSET(tls_fd, &rd)) {
733+ r = SSL_read(ssl, buf2+avail2, BUF_SIZE-avail2);
734+ if (r > 0)
735+ avail2 += r;
736+ else {
737+ switch (SSL_get_error(ssl, r)) {
738+ case SSL_ERROR_ZERO_RETURN:
739+ goto closed;
740+ case SSL_ERROR_WANT_READ:
741+ case SSL_ERROR_WANT_WRITE:
742+ break;
743+ case SSL_ERROR_SYSCALL:
744+ if (r == 0)
745+ rprintf(FERROR, "SSL spurious EOF\n");
746+ else
747+ rprintf(FERROR, "SSL I/O error: %s\n",
748+ strerror(errno));
749+ goto closed;
750+ case SSL_ERROR_SSL:
751+ rprintf(FERROR, "SSL: %s\n",
752+ ERR_error_string(ERR_get_error(), NULL));
753+ goto closed;
754+ default:
755+ rprintf(FERROR, "unexpected ssl error %d\n", r);
756+ goto closed;
757+ }
758+ }
759+ }
760+ if (FD_ISSET(tls_read[1], &wd) && write2 < avail2) {
761+ r = write(tls_read[1], buf2+write2, avail2-write2);
762+ if (r >= 0)
763+ write2 += r;
764+ else {
765+ rprintf(FERROR, "pipe write error: %s\n",
766+ strerror(errno));
767+ break;
768+ }
769+ }
770+ if (FD_ISSET(tls_fd, &wd) && write1 < avail1) {
771+ r = SSL_write(ssl, buf1+write1, avail1-write1);
772+ if (r > 0)
773+ write1 += r;
774+ else {
775+ switch (SSL_get_error(ssl, r)) {
776+ case SSL_ERROR_ZERO_RETURN:
777+ goto closed;
778+ case SSL_ERROR_WANT_READ:
779+ case SSL_ERROR_WANT_WRITE:
780+ break;
781+ case SSL_ERROR_SYSCALL:
782+ if (r == 0)
783+ rprintf(FERROR, "SSL: spurious EOF\n");
784+ else
785+ rprintf(FERROR, "SSL: I/O error: %s\n",
786+ strerror(errno));
787+ goto closed;
788+ case SSL_ERROR_SSL:
789+ rprintf(FERROR, "SSL: %s\n",
790+ ERR_error_string(ERR_get_error(), NULL));
791+ goto closed;
792+ default:
793+ rprintf(FERROR, "unexpected ssl error %d\n", r);
794+ goto closed;
795+ }
796+ }
797+ }
798+ if (avail1 == write1)
799+ avail1 = write1 = 0;
800+ if (avail2 == write2)
801+ avail2 = write2 = 0;
802+ }
803+
804+ /* XXX I'm pretty sure that there is a lot that I am not considering
805+ here. Bugs? Yes, probably. */
806+
807+ /* We're finished. */
808+ closed:
809+ close(tls_read[1]);
810+ close(tls_write[0]);
811+ exit(0);
812+}
813+
814+/**
815+ * Ends the TLS connection.
816+ */
817+void end_tls(void)
818+{
819+ if (ssl_pid > 0)
820+ kill(ssl_pid, SIGUSR1);
821+}