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