X-Git-Url: http://git.samba.org/samba.git/?p=rsync-patches.git;a=blobdiff_plain;f=openssl-support.diff;h=d3818868dc2632c2e83cdcf9d1aab23f9f98861f;hp=390c1ff5f27b7192d55a75c7bf4d7edbbb3a97c0;hb=462996ef20406b735ea816e9d0fb3cc670b63398;hpb=8330f4aa54de109b396734702a205c5d8ea3ded8 diff --git a/openssl-support.diff b/openssl-support.diff index 390c1ff..d381886 100644 --- a/openssl-support.diff +++ b/openssl-support.diff @@ -1,10 +1,4 @@ -After applying this patch, run these commands for a successful build: - - ./prepare-source - ./configure - make - -Casey Marshall writes: +Casey Marshall wrote: I've been hacking together a way to use rsync with OpenSSL, and have attached my current patch against a recent CVS tree. The details of @@ -34,62 +28,75 @@ this implementation are: All warnings apply; I don't do C programming all that often, so I can't say if I've left any cleanup/compatibility errors in the code. +To use this patch, run these commands for a successful build: ---- old/Makefile.in -+++ new/Makefile.in -@@ -39,7 +39,7 @@ OBJS3=progress.o pipe.o + patch -p1 3) { - rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): entered\n", - code, file, line); ---- old/clientserver.c -+++ new/clientserver.c -@@ -29,6 +29,9 @@ extern int am_sender; + /* FALLTHROUGH */ + #include "case_N.h" + switch_step++; +diff --git a/clientserver.c b/clientserver.c +--- a/clientserver.c ++++ b/clientserver.c +@@ -30,6 +30,9 @@ extern int am_sender; extern int am_server; extern int am_daemon; extern int am_root; -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL +extern int use_ssl; +#endif extern int rsync_port; - extern int kluge_around_eof; - extern int daemon_over_rsh; -@@ -105,8 +108,18 @@ int start_socket_client(char *host, char - set_socket_options(fd, sockopts); + extern int protect_args; + extern int ignore_errors; +@@ -132,8 +135,18 @@ int start_socket_client(char *host, int remote_argc, char *remote_argv[], + #endif - ret = start_inband_exchange(user, path, fd, fd, argc); + ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv); + if (ret) + return ret; + -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL + if (use_ssl) { + int f_in = get_tls_rfd(); + int f_out = get_tls_wfd(); @@ -101,16 +108,16 @@ can't say if I've left any cleanup/compatibility errors in the code. + return client_run(fd, fd, -1, argc, argv); } - int start_inband_exchange(char *user, char *path, int f_in, int f_out, -@@ -167,6 +180,33 @@ int start_inband_exchange(char *user, ch - if (verbose > 1) - print_child_argv(sargs); + static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client) +@@ -279,6 +292,32 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char + if (DEBUG_GTE(CMD, 1)) + print_child_argv("sending daemon args:", sargs); -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL + if (use_ssl) { + io_printf(f_out, "#starttls\n"); + while (1) { -+ if (!read_line(f_in, line, sizeof(line)-1)) { ++ if (!read_line_old(f_in, line, sizeof line)) { + rprintf(FERROR, "rsync: did not receive reply to #starttls\n"); + return -1; + } @@ -118,9 +125,8 @@ can't say if I've left any cleanup/compatibility errors in the code. + rprintf(FERROR, "%s\n", line); + return -1; + } -+ if (strcmp(line, "@RSYNCD: starttls") == 0) { ++ if (strcmp(line, "@RSYNCD: starttls") == 0) + break; -+ } + rprintf(FINFO, "%s\n", line); + } + if (start_tls(f_in, f_out)) { @@ -133,54 +139,57 @@ can't say if I've left any cleanup/compatibility errors in the code. + } +#endif + - p = strchr(path,'/'); - if (p) *p = 0; - io_printf(f_out, "%s\n", path); -@@ -195,6 +235,10 @@ int start_inband_exchange(char *user, ch + io_printf(f_out, "%.*s\n", modlen, modname); + + /* Old servers may just drop the connection here, +@@ -304,6 +343,10 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char * server to terminate the listing of modules. * We don't want to go on and transfer * anything; just exit. */ -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL + if (use_ssl) + end_tls(); +#endif exit(0); } -@@ -202,6 +246,10 @@ int start_inband_exchange(char *user, ch +@@ -311,6 +354,10 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char rprintf(FERROR, "%s\n", line); /* This is always fatal; the server will now * close the socket. */ -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL + if (use_ssl) + end_tls(); +#endif return -1; } -@@ -718,6 +766,7 @@ static void send_listing(int fd) - io_printf(fd,"@RSYNCD: EXIT\n"); - } - -+ - /* this is called when a connection is established to a client - and we want to start talking. The setup of the system is done from - here */ -@@ -776,6 +825,9 @@ int start_daemon(int f_in, int f_out) - if (protocol_version > remote_protocol) - protocol_version = remote_protocol; +@@ -716,6 +763,10 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char + set_env_num("RSYNC_PID", (long)pid); + if (wait_process(pid, &status, 0) < 0) + status = -1; ++#ifdef HAVE_OPENSSL ++ if (use_ssl) ++ end_tls(); ++#endif + set_env_num("RSYNC_RAW_STATUS", status); + if (WIFEXITED(status)) + status = WEXITSTATUS(status); +@@ -1071,6 +1122,9 @@ int start_daemon(int f_in, int f_out) + if (exchange_protocols(f_in, f_out, line, sizeof line, 0) < 0) + return -1; -+#if HAVE_OPENSSL -+retry: ++#ifdef HAVE_OPENSSL ++ retry: +#endif line[0] = 0; - if (!read_line(f_in, line, sizeof line - 1)) + if (!read_line_old(f_in, line, sizeof line, 0)) return -1; -@@ -787,6 +839,20 @@ int start_daemon(int f_in, int f_out) +@@ -1082,6 +1136,20 @@ int start_daemon(int f_in, int f_out) return -1; } -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL + if (use_ssl && strcmp(line, "#starttls") == 0) { + io_printf(f_out, "@RSYNCD: starttls\n"); + if (start_tls(f_in, f_out)) { @@ -197,9 +206,10 @@ can't say if I've left any cleanup/compatibility errors in the code. if (*line == '#') { /* it's some sort of command that I don't understand */ io_printf(f_out, "@ERROR: Unknown command '%s'\n", line); ---- old/configure.in -+++ new/configure.in -@@ -290,6 +290,21 @@ if test x"$enable_locale" != x"no"; then +diff --git a/configure.ac b/configure.ac +--- a/configure.ac ++++ b/configure.ac +@@ -320,6 +320,25 @@ if test x"$enable_locale" != x"no"; then AC_DEFINE(CONFIG_LOCALE) fi @@ -208,6 +218,8 @@ can't say if I've left any cleanup/compatibility errors in the code. + +if test "x$enable_openssl" != xno +then ++ save_LIBS=$LIBS ++ LIBS="$LIBS -lcrypto" + have_ssl=yes + AC_CHECK_LIB(ssl, SSL_library_init, , [have_ssl=no]) + if test "x$have_ssl" = xyes @@ -215,19 +227,122 @@ can't say if I've left any cleanup/compatibility errors in the code. + AC_DEFINE(HAVE_OPENSSL, 1, [true if you want to use SSL.]) + SSL_OBJS=ssl.o + AC_SUBST(SSL_OBJS) ++ else ++ LIBS=$save_LIBS + fi +fi + AC_MSG_CHECKING([whether to call shutdown on all sockets]) case $host_os in *cygwin* ) AC_MSG_RESULT(yes) ---- old/options.c -+++ new/options.c -@@ -172,6 +172,14 @@ int logfile_format_has_o_or_i = 0; +diff --git a/main.c b/main.c +--- a/main.c ++++ b/main.c +@@ -92,6 +92,9 @@ extern char backup_dir_buf[MAXPATHLEN]; + extern char *basis_dir[MAX_BASIS_DIRS+1]; + extern struct file_list *first_flist; + extern filter_rule_list daemon_filter_list; ++#ifdef HAVE_OPENSSL ++extern int use_ssl; ++#endif + + uid_t our_uid; + gid_t our_gid; +@@ -152,6 +155,52 @@ pid_t wait_process(pid_t pid, int *status_ptr, int flags) + return waited_pid; + } + ++/* Sends signal "signo", waits for the process to die, and if it doesn't, sends ++ * a SIGKILL. If "graceful" is set, the initial "signo" signal is delayed by a ++ * second to try to let the process exit on its own first. */ ++pid_t terminate_process(pid_t pid, int *status_ptr, int signo, int graceful) ++{ ++ pid_t waited_pid; ++ int timeout = graceful ? 1000 : 3000; ++ if (!graceful) ++ kill(pid, signo); ++ while (1) { ++ waited_pid = wait_process(pid, status_ptr, timeout >= 0 ? WNOHANG : 0); ++ if (waited_pid) ++ break; ++ if (timeout == 0) { ++ if (graceful) { ++ graceful = 0; ++ timeout = 3000; ++ } else { ++ signo = SIGKILL; ++ timeout = -1; ++ } ++ rprintf(FINFO, "%s:%s shutdown didn't work - sending signal %d\n", ++ __FUNCTION__, graceful ? " graceful" : "", signo); ++ kill(pid, signo); ++ } ++ ++ if (timeout > 0) { ++ /* interruptible wait and calculate the time left for waiting */ ++ struct timeval tval, t1, t2; ++ ++ gettimeofday(&t1, NULL); ++ ++ tval.tv_sec = timeout/1000; ++ tval.tv_usec = (timeout%1000)*1000; ++ select(0, NULL, NULL, NULL, &tval); ++ gettimeofday(&t2, NULL); ++ ++ timeout -= (t2.tv_sec-t1.tv_sec)*1000 + (t2.tv_usec-t1.tv_usec)/1000; ++ if (timeout < 0) ++ timeout = 0; ++ } ++ } ++ ++ return waited_pid; ++} ++ + /* Wait for a process to exit, calling io_flush while waiting. */ + static void wait_process_with_flush(pid_t pid, int *exit_code_ptr) + { +@@ -808,6 +857,11 @@ static void do_server_sender(int f_in, int f_out, int argc, char *argv[]) + argv[0] = "."; + } + ++#ifdef HAVE_OPENSSL ++ if (use_ssl) ++ start_tls_buffering(); ++#endif ++ + flist = send_file_list(f_out,argc,argv); + if (!flist || flist->used == 0) + exit_cleanup(0); +@@ -928,6 +982,10 @@ static int do_recv(int f_in, int f_out, char *local_name) + + io_start_buffering_out(f_out); + io_start_multiplex_in(f_in); ++#ifdef HAVE_OPENSSL ++ if (use_ssl) ++ start_tls_buffering(); ++#endif + + #ifdef SUPPORT_HARD_LINKS + if (preserve_hard_links && inc_recurse) { +@@ -1132,6 +1190,10 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) + io_start_multiplex_in(f_in); + else + io_start_buffering_in(f_in); ++#ifdef HAVE_OPENSSL ++ if (use_ssl) ++ start_tls_buffering(); ++#endif + send_filter_list(f_out); + if (filesfrom_host) + filesfrom_fd = f_in; +diff --git a/options.c b/options.c +--- a/options.c ++++ b/options.c +@@ -192,6 +192,14 @@ int logfile_format_has_o_or_i = 0; int always_checksum = 0; int list_only = 0; -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL +int use_ssl = 0; +char *ssl_cert_path = NULL; +char *ssl_key_path = NULL; @@ -238,42 +353,40 @@ can't say if I've left any cleanup/compatibility errors in the code. #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */ char *batch_name = NULL; -@@ -200,6 +208,7 @@ static void print_rsync_version(enum log - char const *hardlinks = "no "; +@@ -570,6 +578,7 @@ static void print_rsync_version(enum logcode f) char const *links = "no "; + char const *iconv = "no "; char const *ipv6 = "no "; + char const *ssl = "no "; STRUCT_STAT *dumstat; - #ifdef HAVE_SOCKETPAIR -@@ -222,6 +231,10 @@ static void print_rsync_version(enum log - ipv6 = ""; + #if SUBPROTOCOL_VERSION != 0 +@@ -606,6 +615,9 @@ static void print_rsync_version(enum logcode f) + #ifdef CAN_SET_SYMLINK_TIMES + symtimes = ""; #endif - -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL + ssl = ""; +#endif -+ - rprintf(f, "%s version %s protocol version %d\n", - RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION); - rprintf(f, "Copyright (C) 1996-2006 by Andrew Tridgell, Wayne Davison, and others.\n"); -@@ -234,9 +247,9 @@ static void print_rsync_version(enum log - /* Note that this field may not have type ino_t. It depends - * on the complicated interaction between largefile feature - * macros. */ -- rprintf(f, " %sinplace, %sIPv6, " -+ rprintf(f, " %sinplace, %sIPv6, %sSSL, " - "%d-bit system inums, %d-bit internal inums\n", -- have_inplace, ipv6, -+ have_inplace, ipv6, ssl, - (int) (sizeof dumstat->st_ino * 8), - (int) (sizeof (int64) * 8)); + + rprintf(f, "%s version %s protocol version %d%s\n", + RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol); +@@ -619,8 +631,8 @@ static void print_rsync_version(enum logcode f) + (int)(sizeof (int64) * 8)); + rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n", + got_socketpair, hardlinks, links, ipv6, have_inplace); +- rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc\n", +- have_inplace, acls, xattrs, iconv, symtimes, prealloc); ++ rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc, %sSSL\n", ++ have_inplace, acls, xattrs, iconv, symtimes, prealloc, ssl); + #ifdef MAINTAINER_MODE -@@ -383,6 +396,13 @@ void usage(enum logcode F) + rprintf(f, "Panic Action: \"%s\"\n", get_panic_action()); +@@ -797,6 +809,13 @@ void usage(enum logcode F) + #endif rprintf(F," -4, --ipv4 prefer IPv4\n"); rprintf(F," -6, --ipv6 prefer IPv6\n"); - #endif -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL + rprintf(F," --ssl allow socket connections to use SSL\n"); + rprintf(F," --ssl-cert=FILE path to daemon's SSL certificate\n"); + rprintf(F," --ssl-key=FILE path to daemon's SSL private key\n"); @@ -283,20 +396,20 @@ can't say if I've left any cleanup/compatibility errors in the code. rprintf(F," --version print version number\n"); rprintf(F,"(-h) --help show this help (-h works with no other options)\n"); -@@ -396,7 +416,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OP +@@ -810,7 +829,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP, OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD, OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE, -- OPT_NO_D, -+ OPT_NO_D, OPT_USE_SSL, +- OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, ++ OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_USE_SSL, + OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_SERVER, OPT_REFUSED_BASE = 9000}; - static struct poptOption long_options[] = { -@@ -541,6 +561,13 @@ static struct poptOption long_options[] +@@ -1029,6 +1048,13 @@ static struct poptOption long_options[] = { {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 }, {"server", 0, POPT_ARG_NONE, 0, OPT_SERVER, 0, 0 }, {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 }, -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL + {"ssl", 0, POPT_ARG_NONE, 0, OPT_USE_SSL, 0, 0}, + {"ssl-cert", 0, POPT_ARG_STRING, &ssl_cert_path, OPT_USE_SSL, 0, 0}, + {"ssl-key", 0, POPT_ARG_STRING, &ssl_key_path, OPT_USE_SSL, 0, 0}, @@ -306,24 +419,83 @@ can't say if I've left any cleanup/compatibility errors in the code. /* All the following options switch us into daemon-mode option-parsing. */ {"config", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 }, {"daemon", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 }, -@@ -1084,6 +1111,12 @@ int parse_arguments(int *argc, const cha - usage(FINFO); - exit_cleanup(0); +@@ -1056,6 +1082,13 @@ static void daemon_usage(enum logcode F) + rprintf(F," -v, --verbose increase verbosity\n"); + rprintf(F," -4, --ipv4 prefer IPv4\n"); + rprintf(F," -6, --ipv6 prefer IPv6\n"); ++#ifdef HAVE_OPENSSL ++ rprintf(F," --ssl allow socket connections to use SSL\n"); ++ rprintf(F," --ssl-cert=FILE path to daemon's SSL certificate\n"); ++ rprintf(F," --ssl-key=FILE path to daemon's SSL private key\n"); ++ rprintf(F," --ssl-key-passwd=PASS password for PEM-encoded private key\n"); ++ rprintf(F," --ssl-ca-certs=FILE path to trusted CA certificates\n"); ++#endif + rprintf(F," --help show this help screen\n"); + rprintf(F,"\n"); +@@ -1081,6 +1114,13 @@ static struct poptOption long_daemon_options[] = { + {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, + {"server", 0, POPT_ARG_NONE, &am_server, 0, 0, 0 }, + {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, ++#ifdef HAVE_OPENSSL ++ {"ssl", 0, POPT_ARG_NONE, 0, OPT_USE_SSL, 0, 0}, ++ {"ssl-cert", 0, POPT_ARG_STRING, &ssl_cert_path, OPT_USE_SSL, 0, 0}, ++ {"ssl-key", 0, POPT_ARG_STRING, &ssl_key_path, OPT_USE_SSL, 0, 0}, ++ {"ssl-key-passwd", 0, POPT_ARG_STRING, &ssl_key_passwd, OPT_USE_SSL, 0, 0}, ++ {"ssl-ca-certs", 0, POPT_ARG_STRING, &ssl_ca_path, OPT_USE_SSL, 0, 0}, ++#endif + {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 }, + {"no-verbose", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 }, + {"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 }, +@@ -1376,6 +1416,12 @@ int parse_arguments(int *argc_p, const char ***argv_p) + verbose++; + break; + ++#ifdef HAVE_OPENSSL ++ case OPT_USE_SSL: ++ use_ssl = 1; ++ break; ++#endif ++ + default: + rprintf(FERROR, + "rsync: %s: %s (in daemon mode)\n", +@@ -1402,6 +1448,17 @@ int parse_arguments(int *argc_p, const char ***argv_p) + exit_cleanup(RERR_SYNTAX); + } + ++#ifdef HAVE_OPENSSL ++ if (use_ssl) { ++ if (init_tls()) { ++ snprintf(err_buf, sizeof(err_buf), ++ "Openssl error: %s\n", ++ get_ssl_error()); ++ return 0; ++ } ++ } ++#endif ++ + *argv_p = argv = poptGetArgs(pc); + *argc_p = argc = count_args(argv); + am_starting_up = 0; +@@ -1780,6 +1837,12 @@ int parse_arguments(int *argc_p, const char ***argv_p) + return 0; + #endif + ++#ifdef HAVE_OPENSSL + case OPT_USE_SSL: -+#if HAVE_OPENSSL + use_ssl = 1; -+#endif + break; ++#endif + default: /* A large opt value means that set_refuse_options() * turned this option off. */ -@@ -1364,6 +1397,17 @@ int parse_arguments(int *argc, const cha +@@ -2184,6 +2247,17 @@ int parse_arguments(int *argc_p, const char ***argv_p) if (delay_updates && !partial_dir) partial_dir = tmp_partialdir; -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL + if (use_ssl) { + if (init_tls()) { + snprintf(err_buf, sizeof(err_buf), @@ -337,63 +509,58 @@ can't say if I've left any cleanup/compatibility errors in the code. if (inplace) { #ifdef HAVE_FTRUNCATE if (partial_dir) { -@@ -1781,10 +1825,27 @@ char *check_for_hostspec(char *s, char * - char *p; - int not_host; - int hostlen; -+ int url_prefix_len = sizeof URL_PREFIX - 1; +@@ -2779,9 +2853,18 @@ char *check_for_hostspec(char *s, char **host_ptr, int *port_ptr) + { + char *path; - if (port_ptr && strncasecmp(URL_PREFIX, s, strlen(URL_PREFIX)) == 0) { -+ if (!port_ptr) -+ url_prefix_len = 0; -+ else if (strncasecmp(URL_PREFIX, s, url_prefix_len) != 0) { -+#if HAVE_OPENSSL -+ url_prefix_len = sizeof SSL_URL_PREFIX - 1; -+ if (strncasecmp(SSL_URL_PREFIX, s, url_prefix_len) != 0) -+ url_prefix_len = 0; -+ else { +- *host_ptr = parse_hostspec(s + strlen(URL_PREFIX), &path, port_ptr); +- if (*host_ptr) { ++ if (port_ptr) { ++ int url_prefix_len; ++ if (strncasecmp(URL_PREFIX, s, sizeof URL_PREFIX - 1) == 0) ++ url_prefix_len = sizeof URL_PREFIX - 1; ++ else if (strncasecmp(SSL_URL_PREFIX, s, sizeof SSL_URL_PREFIX - 1) == 0) { + if (!use_ssl) + init_tls(); + use_ssl = 1; -+ } -+#else -+ url_prefix_len = 0; -+#endif -+ } -+ if (url_prefix_len) { - char *path; -- s += strlen(URL_PREFIX); -+ s += url_prefix_len; - if ((p = strchr(s, '/')) != NULL) { - hostlen = p - s; - path = p + 1; ---- old/rsync.h -+++ new/rsync.h -@@ -32,6 +32,7 @@ ++ url_prefix_len = sizeof SSL_URL_PREFIX - 1; ++ } else ++ url_prefix_len = 0; ++ if (url_prefix_len && (*host_ptr = parse_hostspec(s + url_prefix_len, &path, port_ptr))) { + if (!*port_ptr) + *port_ptr = RSYNC_PORT; + return path; +diff --git a/rsync.h b/rsync.h +--- a/rsync.h ++++ b/rsync.h +@@ -31,6 +31,7 @@ #define DEFAULT_LOCK_FILE "/var/run/rsyncd.lock" #define URL_PREFIX "rsync://" +#define SSL_URL_PREFIX "rsyncs://" - #define BACKUP_SUFFIX "~" - -@@ -419,6 +420,11 @@ enum msgcode { + #define SYMLINK_PREFIX "/rsyncd-munged/" /* This MUST have a trailing slash! */ + #define SYMLINK_PREFIX_LEN ((int)sizeof SYMLINK_PREFIX - 1) +@@ -608,6 +609,11 @@ typedef unsigned int size_t; # define SIZEOF_INT64 SIZEOF_OFF_T #endif -+#if HAVE_OPENSSL ++#ifdef HAVE_OPENSSL +#include +#include +#endif + - /* Starting from protocol version 26, we always use 64-bit - * ino_t and dev_t internally, even if this platform does not - * allow files to have 64-bit inums. That's because the ---- old/ssl.c -+++ new/ssl.c -@@ -0,0 +1,366 @@ + struct hashtable { + void *nodes; + int32 size, entries; +diff --git a/ssl.c b/ssl.c +new file mode 100644 +--- /dev/null ++++ b/ssl.c +@@ -0,0 +1,603 @@ +/* -*- c-file-style: "linux" -*- -+ * ssl.c: operations for negotiating SSL rsync connections. ++ * ssl.c: operations for negotiating SSL rsync connections. + * + * Copyright (C) 2003 Casey Marshall + * @@ -401,12 +568,12 @@ can't say if I've left any cleanup/compatibility errors in the code. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. -+ * ++ * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. -+ * ++ * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -414,7 +581,7 @@ can't say if I've left any cleanup/compatibility errors in the code. + +#include "rsync.h" + -+#if HAVE_SYS_SELECT_H ++#ifdef HAVE_SYS_SELECT_H +#include +#else +#include @@ -423,9 +590,9 @@ can't say if I've left any cleanup/compatibility errors in the code. +#endif +#include + -+#define BUF_SIZE 1024 ++#define SSL_MAX_RECORD_SIZE (1024*16) /* TLS record size */ ++#define MAX_BUFFERING_TIME_MS 100 + -+extern int verbose; +extern int am_daemon; +extern int am_server; + @@ -439,8 +606,20 @@ can't say if I've left any cleanup/compatibility errors in the code. +static int tls_read[2] = { -1, -1 }; +static int tls_write[2] = { -1, -1 }; +static int ssl_running; ++static int ssl_min_send_size; +static int ssl_pid = -1; + ++#ifdef HAVE_SIGACTION ++static struct sigaction sigact; ++#endif ++ ++/* copied from progress.c */ ++static unsigned long msdiff(struct timeval *t1, struct timeval *t2) ++{ ++ return (t2->tv_sec - t1->tv_sec) * 1000L ++ + (t2->tv_usec - t1->tv_usec) / 1000; ++} ++ +/** + * A non-interactive callback to be passed to SSL_CTX_set_default_password_cb, + * which merely copies the value of ssl_key_passwd into buf. This is @@ -507,7 +686,7 @@ can't say if I've left any cleanup/compatibility errors in the code. + cbs = buf; + break; + } -+ if (verbose > 2) { ++ if (DEBUG_GTE(CONNECT, 1)) { + rprintf(FLOG, "SSL: info_callback(%p,%s,%d)\n", ssl, cbs, val); + if (cb == SSL_CB_HANDSHAKE_DONE) { + SSL_CIPHER_description(SSL_get_current_cipher((SSL*)ssl), @@ -532,6 +711,8 @@ can't say if I've left any cleanup/compatibility errors in the code. + return 1; + SSL_CTX_set_info_callback(ssl_ctx, info_callback); + ++ SSL_CTX_set_options(ssl_ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); ++ + /* Sets the certificate sent to the other party. */ + if (ssl_cert_path != NULL + && SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert_path, @@ -585,28 +766,59 @@ can't say if I've left any cleanup/compatibility errors in the code. + */ +static RETSIGTYPE tls_sigusr1(int UNUSED(val)) +{ -+ if (ssl) { -+ SSL_shutdown(ssl); -+ SSL_free(ssl); -+ ssl = NULL; -+ } + ssl_running = 0; +} + ++/* Signal handler that starts record-size buffering. */ ++static RETSIGTYPE tls_sigusr2(int UNUSED(val)) ++{ ++ ssl_min_send_size = SSL_MAX_RECORD_SIZE; ++} ++ +/** + * Negotiates the TLS connection, creates a socket pair for communicating + * with the rsync process, then forks into a new process that will handle + * the communication. + * + * 0 is returned on success. ++ * rsync to network algorithm: ++ * A buffer ring of two buffers is maintained. Data is read from the local ++ * socket into the current buffer. The buffer is eligible for sending if ++ * enough data has been buffered, or enough time has passed. Once the buffer ++ * becomes eligible for sending it is marked "ready" and buffering continues ++ * on the other buffer (assuming it's "unready"). This algorithm tries to ++ * maximize SSL record size. ++ * ++ * network to rsync algorithm: ++ * One receive buffer twice the size of the SSL max record size. Reading ++ * from the network is attempted only if there's enough space for a whole ++ * record (this is done to optimize OpenSSL per-call overhead). ++ * As bytes enter the buffer sending to rsync begins, when the buffer becomes ++ * full we wait for sending to rsync to finish. ++ * ++ * A graceful stop is attempted on both directions. So if the pipe from rsync ++ * is gracefully shut down, we try to send all data to the network before closing ++ * the connection and if the SSL connection is gracefully shut down we try to ++ * send everything to rsync before closing the rsync pipe. + */ +int start_tls(int f_in, int f_out) +{ -+ int tls_fd; + int n = 0, r; -+ unsigned char buf1[BUF_SIZE], buf2[BUF_SIZE]; -+ int avail1 = 0, avail2 = 0, write1 = 0, write2 = 0; ++ unsigned char buf_tonetwork[2][SSL_MAX_RECORD_SIZE], buf_fromnetwork[2*SSL_MAX_RECORD_SIZE]; ++ int tonet_ready[2] = {0}, tonet_size[2] = {0}, write_tonet = 0, tonet_fill_idx = 0, tonet_send_idx = 0; ++ struct timeval buffering_start, now, timeout, *tp; ++ int avail_fromnet = 0, write_fromnet = 0; ++ int want_read_fromnet = 1, want_write_tonet = 0; ++ int want_read_fromrsync = 1, want_write_torsync = 0; ++ int write_tonet_blocks = 0, read_fromnet_blocks = 0; ++ int write_torsync_blocks = 0, read_fromrsync_blocks = 0; ++ int ssl_readfd_blocks = 0, ssl_writefd_blocks = 0; ++ int ssl_write_count = 0; ++ int want_more_io; ++ int net_closed = 0, rsync_closed = 0; ++ int loopcount; + fd_set rd, wd; ++ const char *ssl_ciphers; + + if (fd_pair(tls_read)) + return 1; @@ -614,11 +826,11 @@ can't say if I've left any cleanup/compatibility errors in the code. + return 1; + + set_blocking(tls_read[0]); -+ set_blocking(tls_read[1]); -+ set_blocking(tls_write[0]); ++ set_nonblocking(tls_read[1]); ++ set_nonblocking(tls_write[0]); + set_blocking(tls_write[1]); -+ set_blocking(f_in); -+ set_blocking(f_out); ++ set_nonblocking(f_in); ++ set_nonblocking(f_out); + + ssl_pid = do_fork(); + if (ssl_pid < 0) @@ -626,58 +838,114 @@ can't say if I've left any cleanup/compatibility errors in the code. + if (ssl_pid != 0) { + close(tls_write[0]); + close(tls_read[1]); ++ close(f_in); ++ close(f_out); + return 0; + } + -+ signal(SIGUSR1, tls_sigusr1); ++ close(tls_write[1]); ++ close(tls_read[0]); ++ ++ SIGACTION(SIGUSR1, tls_sigusr1); ++ SIGACTION(SIGUSR2, tls_sigusr2); + ssl = SSL_new(ssl_ctx); + if (!ssl) -+ goto closed; ++ goto out; + if (am_daemon || am_server) + SSL_set_accept_state(ssl); -+ else ++ else { + SSL_set_connect_state(ssl); ++ ssl_ciphers = getenv("RSYNC_SSL_CIPHERS"); ++ if (ssl_ciphers) ++ SSL_set_cipher_list(ssl, ssl_ciphers); ++ } ++ + SSL_set_rfd(ssl, f_in); + SSL_set_wfd(ssl, f_out); + -+ tls_fd = SSL_get_fd(ssl); + n = tls_write[0]; + n = MAX(tls_read[1], n); -+ n = MAX(tls_fd, n) + 1; ++ n = MAX(f_in, n); ++ n = MAX(f_out, n) + 1; + + ssl_running = 1; ++ loopcount = 0; + while (ssl_running) { -+ FD_ZERO(&rd); -+ FD_ZERO(&wd); -+ FD_SET(tls_write[0], &rd); -+ FD_SET(tls_read[1], &wd); -+ FD_SET(tls_fd, &rd); -+ FD_SET(tls_fd, &wd); -+ -+ r = select(n, &rd, &wd, NULL, NULL); -+ -+ if (r == -1 && errno == EINTR) -+ continue; -+ if (FD_ISSET(tls_write[0], &rd)) { -+ r = read(tls_write[0], buf1+avail1, BUF_SIZE-avail1); -+ if (r >= 0) -+ avail1 += r; -+ else { ++ ++loopcount; /* the loopcount prevents starvation of one transfer direction by the other */ ++ want_more_io = 0; ++ ++ if (want_read_fromrsync && !read_fromrsync_blocks) { ++ int cur_buf_size = tonet_size[tonet_fill_idx]; ++ r = read(tls_write[0], buf_tonetwork[tonet_fill_idx]+cur_buf_size, sizeof(buf_tonetwork[0])-cur_buf_size); ++ if (r > 0) { ++ want_more_io = 1; ++ if (!net_closed) { ++ if (cur_buf_size == 0) ++ gettimeofday(&buffering_start, NULL); ++ cur_buf_size += r; ++ tonet_size[tonet_fill_idx] = cur_buf_size; ++ if (cur_buf_size >= ssl_min_send_size) { ++ want_write_tonet = 1; ++ tonet_ready[tonet_fill_idx] = 1; ++ tonet_fill_idx = !tonet_fill_idx; ++ if (tonet_ready[tonet_fill_idx]) ++ want_read_fromrsync = 0; ++ } ++ } ++ } else if (r < 0 && errno == EWOULDBLOCK) ++ read_fromrsync_blocks = 1; ++ else if (r < 0) { + rprintf(FERROR, "pipe read error: %s\n", + strerror(errno)); + break; ++ } else { ++ rsync_closed = 1; ++ if (cur_buf_size) { ++ want_write_tonet = 1; ++ tonet_ready[tonet_fill_idx] = 1; /* close current buffer */ ++ } ++ want_read_fromrsync = 0; /* don't read */ ++ want_write_torsync = 0; /* don't write */ ++ read_fromrsync_blocks = 0; /* don't select */ ++ write_torsync_blocks = 0; ++ want_read_fromnet = 1; /* purge incoming data from network */ ++ avail_fromnet = 0; ++ if (net_closed || !want_write_tonet) ++ break; + } + } -+ if (FD_ISSET(tls_fd, &rd)) { -+ r = SSL_read(ssl, buf2+avail2, BUF_SIZE-avail2); -+ if (r > 0) -+ avail2 += r; -+ else { ++ ++ if (want_read_fromnet && !read_fromnet_blocks) { ++ r = SSL_read(ssl, buf_fromnetwork+avail_fromnet, SSL_MAX_RECORD_SIZE); ++ if (r > 0) { ++ want_more_io = 1; ++ if (!rsync_closed) { ++ avail_fromnet += r; ++ want_write_torsync = 1; ++ if (avail_fromnet >= (int)(sizeof(buf_fromnetwork)-SSL_MAX_RECORD_SIZE)) ++ want_read_fromnet = 0; ++ } ++ } else { + switch (SSL_get_error(ssl, r)) { + case SSL_ERROR_ZERO_RETURN: -+ goto closed; ++ net_closed = 1; ++ want_read_fromnet = 0; /* don't read */ ++ want_write_tonet = 0; /* don't write */ ++ ssl_readfd_blocks = 0; /* and for heaven's sake - don't select() */ ++ ssl_writefd_blocks = 0; ++ want_read_fromrsync = 1; /* and purge stuff from rsync side */ ++ tonet_size[tonet_fill_idx] = 0; ++ tonet_ready[tonet_fill_idx] = 0; ++ if (rsync_closed || !want_write_torsync) ++ goto graceful_stop; ++ break; + case SSL_ERROR_WANT_READ: ++ ssl_readfd_blocks = 1; ++ read_fromnet_blocks = 1; ++ break; + case SSL_ERROR_WANT_WRITE: ++ ssl_writefd_blocks = 1; ++ read_fromnet_blocks = 1; + break; + case SSL_ERROR_SYSCALL: + if (r == 0) @@ -687,7 +955,7 @@ can't say if I've left any cleanup/compatibility errors in the code. + strerror(errno)); + goto closed; + case SSL_ERROR_SSL: -+ rprintf(FERROR, "SSL: %s\n", ++ rprintf(FERROR, "SSL %s\n", + ERR_error_string(ERR_get_error(), NULL)); + goto closed; + default: @@ -696,33 +964,70 @@ can't say if I've left any cleanup/compatibility errors in the code. + } + } + } -+ if (FD_ISSET(tls_read[1], &wd) && write2 < avail2) { -+ r = write(tls_read[1], buf2+write2, avail2-write2); -+ if (r >= 0) -+ write2 += r; ++ ++ if (want_write_torsync && !write_torsync_blocks) { ++ r = write(tls_read[1], buf_fromnetwork+write_fromnet, avail_fromnet-write_fromnet); ++ if (r > 0) { ++ want_more_io = 1; ++ write_fromnet += r; ++ if (write_fromnet >= avail_fromnet) { ++ write_fromnet = 0; ++ avail_fromnet = 0; ++ if (net_closed) ++ break; ++ want_read_fromnet = 1; ++ want_write_torsync = 0; ++ } ++ } ++ else if (errno == EWOULDBLOCK) ++ write_torsync_blocks = 1; + else { -+ rprintf(FERROR, "pipe write error: %s\n", -+ strerror(errno)); ++ if (errno != EPIPE) ++ rprintf(FERROR, "pipe write error: %s\n", strerror(errno)); + break; + } + } -+ if (FD_ISSET(tls_fd, &wd) && write1 < avail1) { -+ r = SSL_write(ssl, buf1+write1, avail1-write1); -+ if (r > 0) -+ write1 += r; -+ else { ++ ++ if (want_write_tonet && !write_tonet_blocks) { ++ /* lock the write count, 'cause if SSL_write fails on non-blocking IO, it ++ * must be repeated with same parameters. */ ++ if (ssl_write_count == 0) ++ ssl_write_count = tonet_size[tonet_send_idx] - write_tonet; ++ r = SSL_write(ssl, buf_tonetwork[tonet_send_idx]+write_tonet, ssl_write_count); ++ if (r > 0) { ++ want_more_io = 1; ++ write_tonet += r; ++ ssl_write_count = 0; ++ if (write_tonet >= tonet_size[tonet_send_idx]) { ++ write_tonet = 0; ++ tonet_size[tonet_send_idx] = 0; ++ tonet_ready[tonet_send_idx] = 0; ++ if (!rsync_closed) ++ want_read_fromrsync = 1; ++ tonet_send_idx = !tonet_send_idx; ++ if (!tonet_ready[tonet_send_idx]) { ++ want_write_tonet = 0; ++ if (rsync_closed) ++ break; ++ } ++ } ++ } else { + switch (SSL_get_error(ssl, r)) { + case SSL_ERROR_ZERO_RETURN: -+ goto closed; ++ goto graceful_stop; + case SSL_ERROR_WANT_READ: ++ ssl_readfd_blocks = 1; ++ write_tonet_blocks = 1; ++ break; + case SSL_ERROR_WANT_WRITE: ++ ssl_writefd_blocks = 1; ++ write_tonet_blocks = 1; + break; + case SSL_ERROR_SYSCALL: + if (r == 0) + rprintf(FERROR, "SSL: spurious EOF\n"); + else -+ rprintf(FERROR, "SSL: I/O error: %s\n", -+ strerror(errno)); ++ rprintf(FERROR, "SSL: I/O error: %s\n", strerror(errno)); + goto closed; + case SSL_ERROR_SSL: + rprintf(FERROR, "SSL: %s\n", @@ -734,20 +1039,110 @@ can't say if I've left any cleanup/compatibility errors in the code. + } + } + } -+ if (avail1 == write1) -+ avail1 = write1 = 0; -+ if (avail2 == write2) -+ avail2 = write2 = 0; ++ ++ if (!want_more_io || (loopcount&31) == 0) { ++ int has_buffered = 0; ++ if (!net_closed && !tonet_ready[tonet_fill_idx] && tonet_size[tonet_fill_idx]) { ++ has_buffered = 1; ++ unsigned long buffering_time; ++ gettimeofday(&now,NULL); ++ buffering_time = msdiff(&buffering_start,&now); ++ if (buffering_time >= MAX_BUFFERING_TIME_MS) { ++ want_write_tonet = 1; ++ want_more_io = 1; ++ tonet_ready[tonet_fill_idx] = 1; ++ tonet_fill_idx = !tonet_fill_idx; ++ if (tonet_ready[tonet_fill_idx]) ++ want_read_fromrsync = 0; ++ } ++ } ++ ++ int waiting_on_something = 0; ++ FD_ZERO(&rd); ++ FD_ZERO(&wd); ++ if (read_fromrsync_blocks) { ++ waiting_on_something = 1; ++ FD_SET(tls_write[0], &rd); ++ } ++ if (write_torsync_blocks) { ++ waiting_on_something = 1; ++ FD_SET(tls_read[1], &wd); ++ } ++ if (ssl_readfd_blocks) { ++ waiting_on_something = 1; ++ FD_SET(f_in, &rd); ++ } ++ if (ssl_writefd_blocks) { ++ waiting_on_something = 1; ++ FD_SET(f_out, &wd); ++ } ++ ++ if (want_more_io) { ++ /* just poll to see if some sockets became non-blocking*/ ++ timeout.tv_sec = 0; ++ timeout.tv_usec = 0; ++ tp = &timeout; ++ } else if (waiting_on_something && !has_buffered) ++ tp = NULL; /*infinite wait until a socket becomes available*/ ++ else { ++ timeout.tv_sec = MAX_BUFFERING_TIME_MS/1000; ++ timeout.tv_usec = (MAX_BUFFERING_TIME_MS%1000)*1000; ++ tp = &timeout; ++ } ++ ++ if (waiting_on_something || !want_more_io) { ++ r = select(n, &rd, &wd, NULL, tp); ++ ++ if (r == -1) { ++ if (errno != EINTR) { ++ rprintf(FERROR, "select error: %s\n", ++ strerror(errno)); ++ break; ++ } ++ } ++ ++ if (r > 0) { ++ if (FD_ISSET(tls_write[0], &rd)) ++ read_fromrsync_blocks = 0; ++ if (FD_ISSET(f_in,&rd)) { ++ read_fromnet_blocks = 0; ++ write_tonet_blocks = 0; ++ ssl_readfd_blocks = 0; ++ } ++ if (FD_ISSET(tls_read[1],&wd)) ++ write_torsync_blocks = 0; ++ if (FD_ISSET(f_out,&wd)) { ++ read_fromnet_blocks = 0; ++ write_tonet_blocks = 0; ++ ssl_writefd_blocks = 0; ++ } ++ } ++ } ++ } + } + -+ /* XXX I'm pretty sure that there is a lot that I am not considering -+ here. Bugs? Yes, probably. */ ++ graceful_stop: ++ /* The SSL shutdown API looks pretty broken with respect to corner cases ++ * where the underlying socket is blocking. This is a "best effort". */ ++ if (SSL_shutdown(ssl) != 1) ++ SSL_shutdown(ssl); ++ closed: ++ SSL_free(ssl); + + /* We're finished. */ -+ closed: ++ out: + close(tls_read[1]); + close(tls_write[0]); -+ exit(0); ++ close(f_in); ++ if (f_out != f_in) ++ close(f_out); ++ _exit(0); ++} ++ ++void start_tls_buffering(void) ++{ ++ if (ssl_pid > 0) ++ kill(ssl_pid, SIGUSR2); +} + +/** @@ -755,6 +1150,15 @@ can't say if I've left any cleanup/compatibility errors in the code. + */ +void end_tls(void) +{ -+ if (ssl_pid > 0) -+ kill(ssl_pid, SIGUSR1); ++ if (ssl_pid > 0) { ++ int status; ++ /* try a graceful stop - this makes sure all data is sent, and ++ * also causes our side to send a close_notify alert, as required ++ * by TLS specs. */ ++ close(get_tls_rfd()); ++ if (get_tls_wfd() != get_tls_rfd()) ++ close(get_tls_wfd()); ++ terminate_process(ssl_pid, &status, SIGUSR1, 1); ++ ssl_pid = -1; ++ } +}