Index: Makefile.in =================================================================== RCS file: /cvsroot/rsync/Makefile.in,v retrieving revision 1.96 diff -u -r1.96 Makefile.in --- Makefile.in 30 Jul 2003 06:12:25 -0000 1.96 +++ Makefile.in 21 Sep 2003 13:51:46 -0000 @@ -39,7 +39,7 @@ DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \ popt/popthelp.o popt/poptparse.o -OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@ +OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@ @SSL_OBJS@ TLS_OBJ = tls.o syscall.o lib/permstring.o Index: cleanup.c =================================================================== RCS file: /cvsroot/rsync/cleanup.c,v retrieving revision 1.18 diff -u -r1.18 cleanup.c --- cleanup.c 21 Mar 2003 23:43:50 -0000 1.18 +++ cleanup.c 21 Sep 2003 13:51:46 -0000 @@ -87,6 +87,9 @@ int ocode = code; extern int keep_partial; extern int log_got_error; +#ifdef HAVE_OPENSSL + extern int use_ssl; +#endif static int inside_cleanup = 0; if (inside_cleanup > 10) { @@ -97,6 +100,12 @@ signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); + +#ifdef HAVE_OPENSSL + if (use_ssl) { + end_tls(); + } +#endif if (verbose > 3) rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): entered\n", Index: clientserver.c =================================================================== RCS file: /cvsroot/rsync/clientserver.c,v retrieving revision 1.110 diff -u -r1.110 clientserver.c --- clientserver.c 11 Sep 2003 04:00:19 -0000 1.110 +++ clientserver.c 21 Sep 2003 13:51:47 -0000 @@ -39,6 +39,10 @@ extern struct exclude_struct **server_exclude_list; extern char *exclude_path_prefix; +#ifdef HAVE_OPENSSL +extern int use_ssl; +#endif + /** * Run a client connected to an rsyncd. The alternative to this * function for remote-shell connections is do_cmd(). @@ -56,6 +60,7 @@ int start_socket_client(char *host, char *path, int argc, char *argv[]) { int fd, ret; + int f_in, f_out; char *p, *user=NULL; extern char *bind_address; extern int default_af_hint; @@ -90,7 +95,16 @@ ret = start_inband_exchange(user, path, fd, fd, argc); - return ret < 0? ret : client_run(fd, fd, -1, argc, argv); + f_in = fd; + f_out = fd; +#ifdef HAVE_OPENSSL + if (use_ssl) { + f_in = get_tls_rfd(); + f_out = get_tls_wfd(); + } +#endif + + return ret < 0? ret : client_run(f_in, f_out, -1, argc, argv); } int start_inband_exchange(char *user, char *path, int f_in, int f_out, int argc) @@ -145,6 +159,33 @@ if (protocol_version > remote_protocol) protocol_version = remote_protocol; +#ifdef HAVE_OPENSSL + if (use_ssl) { + io_printf(f_out, "#starttls\n"); + while (1) { + if (!read_line(f_in, line, sizeof(line)-1)) { + rprintf(FERROR, "rsync: did not receive reply to #starttls\n"); + return -1; + } + if (strncmp(line, "@ERROR", 6) == 0) { + rprintf(FERROR, "rsync: ssl connection denied\n"); + return -1; + } + if (strcmp(line, "@RSYNCD: starttls") == 0) { + break; + } + rprintf(FINFO, "%s\n", line); + } + if (start_tls(f_in, f_out)) { + rprintf(FERROR, "rsync: error during SSL handshake: %s\n", + get_ssl_error()); + return -1; + } + f_in = get_tls_rfd(); + f_out = get_tls_wfd(); + } +#endif + p = strchr(path,'/'); if (p) *p = 0; io_printf(f_out, "%s\n", path); @@ -172,6 +213,9 @@ * server to terminate the listing of modules. * We don't want to go on and transfer * anything; just exit. */ +#ifdef HAVE_OPENSSL + if (use_ssl) end_tls(); +#endif exit(0); } @@ -179,6 +223,9 @@ rprintf(FERROR,"%s\n", line); /* This is always fatal; the server will now * close the socket. */ +#ifdef HAVE_OPENSSL + if (use_ssl) end_tls(); +#endif return RERR_STARTCLIENT; } else { rprintf(FINFO,"%s\n", line); @@ -499,6 +546,7 @@ 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 */ @@ -559,6 +607,20 @@ send_listing(f_out); return -1; } + +#if HAVE_OPENSSL + if (use_ssl && strcmp(line, "#starttls") == 0) { + io_printf(f_out, "@RSYNCD: starttls\n"); + if (start_tls(f_in, f_out)) { + rprintf(FLOG, "SSL connection failed: %s\n", + get_ssl_error()); + return -1; + } + f_in = get_tls_rfd(); + f_out = get_tls_wfd(); + continue; + } +#endif if (*line == '#') { /* it's some sort of command that I don't understand */ Index: config.h.in =================================================================== RCS file: /cvsroot/rsync/config.h.in,v retrieving revision 1.81 diff -u -r1.81 config.h.in --- config.h.in 30 Jul 2003 06:12:38 -0000 1.81 +++ config.h.in 21 Sep 2003 13:51:48 -0000 @@ -169,6 +169,9 @@ /* */ #undef HAVE_OFF64_T +/* true if you want to use SSL. */ +#undef HAVE_OPENSSL + /* Define to 1 if you have the `readlink' function. */ #undef HAVE_READLINK Index: configure =================================================================== RCS file: /cvsroot/rsync/configure,v retrieving revision 1.163 diff -u -r1.163 configure --- configure 30 Jul 2003 06:12:38 -0000 1.163 +++ configure 21 Sep 2003 13:52:19 -0000 @@ -308,7 +308,7 @@ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS RSYNC_VERSION build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA HAVE_REMSH EGREP LIBOBJS ALLOCA OBJ_SAVE OBJ_RESTORE CC_SHOBJ_FLAG BUILD_POPT LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS RSYNC_VERSION build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA HAVE_REMSH EGREP SSL_OBJS LIBOBJS ALLOCA OBJ_SAVE OBJ_RESTORE CC_SHOBJ_FLAG BUILD_POPT LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -851,6 +851,7 @@ turn on extra debug features --disable-largefile omit support for large files --disable-ipv6 don't even try to use IPv6 + --enable-openssl compile SSL support with OpenSSL. Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -3456,6 +3457,93 @@ fi +# Check whether --enable-openssl or --disable-openssl was given. +if test "${enable_openssl+set}" = set; then + enableval="$enable_openssl" + +fi; + +if test "x$enable_openssl" != xno +then + have_ssl=yes + +echo "$as_me:$LINENO: checking for SSL_library_init in -lssl" >&5 +echo $ECHO_N "checking for SSL_library_init in -lssl... $ECHO_C" >&6 +if test "${ac_cv_lib_ssl_SSL_library_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lssl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char SSL_library_init (); +int +main () +{ +SSL_library_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ssl_SSL_library_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_ssl_SSL_library_init=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_ssl_SSL_library_init" >&5 +echo "${ECHO_T}$ac_cv_lib_ssl_SSL_library_init" >&6 +if test $ac_cv_lib_ssl_SSL_library_init = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSSL 1 +_ACEOF + + LIBS="-lssl $LIBS" + +else + have_ssl=no +fi + + if test "x$have_ssl" = xyes + then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_OPENSSL 1 +_ACEOF + + SSL_OBJS=ssl.o + + fi +fi + echo "$as_me:$LINENO: checking whether to call shutdown on all sockets" >&5 echo $ECHO_N "checking whether to call shutdown on all sockets... $ECHO_C" >&6 case $host_os in @@ -10691,6 +10779,7 @@ s,@INSTALL_DATA@,$INSTALL_DATA,;t t s,@HAVE_REMSH@,$HAVE_REMSH,;t t s,@EGREP@,$EGREP,;t t +s,@SSL_OBJS@,$SSL_OBJS,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@ALLOCA@,$ALLOCA,;t t s,@OBJ_SAVE@,$OBJ_SAVE,;t t Index: configure.in =================================================================== RCS file: /cvsroot/rsync/configure.in,v retrieving revision 1.170 diff -u -r1.170 configure.in --- configure.in 30 Jul 2003 06:12:29 -0000 1.170 +++ configure.in 21 Sep 2003 13:52:21 -0000 @@ -247,6 +247,21 @@ AC_SEARCH_LIBS(getaddrinfo, inet6) fi +AC_ARG_ENABLE(openssl, + AC_HELP_STRING([--enable-openssl], [compile SSL support with OpenSSL.])) + +if test "x$enable_openssl" != xno +then + have_ssl=yes + AC_CHECK_LIB(ssl, SSL_library_init, , [have_ssl=no]) + if test "x$have_ssl" = xyes + then + AC_DEFINE(HAVE_OPENSSL, 1, [true if you want to use SSL.]) + SSL_OBJS=ssl.o + AC_SUBST(SSL_OBJS) + fi +fi + AC_MSG_CHECKING([whether to call shutdown on all sockets]) case $host_os in *cygwin* ) AC_MSG_RESULT(yes) Index: main.c =================================================================== RCS file: /cvsroot/rsync/main.c,v retrieving revision 1.172 diff -u -r1.172 main.c --- main.c 11 Sep 2003 04:53:05 -0000 1.172 +++ main.c 21 Sep 2003 13:52:23 -0000 @@ -698,18 +698,33 @@ extern int rsync_port; extern int daemon_over_rsh; extern int read_batch; +#ifdef HAVE_OPENSSL + extern int use_ssl; +#endif int rc; + int url_prefix = 0; /* Don't clobber argv[] so that ps(1) can still show the right * command line. */ if ((rc = copy_argv(argv))) return rc; - /* rsync:// always uses rsync server over direct socket connection */ if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) { + url_prefix = strlen(URL_PREFIX); + } +#ifdef HAVE_OPENSSL + if (strncasecmp(SSL_URL_PREFIX, argv[0], strlen(SSL_URL_PREFIX)) == 0) { + if (!use_ssl) init_tls(); + use_ssl = 1; + url_prefix = strlen(SSL_URL_PREFIX); + } +#endif + + /* rsync:// always uses rsync server over direct socket connection */ + if (url_prefix) { char *host, *path; - host = argv[0] + strlen(URL_PREFIX); + host = argv[0] + url_prefix; p = strchr(host,'/'); if (p) { *p = 0; @@ -758,12 +773,22 @@ argv++; } else { am_sender = 1; + if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) { + url_prefix = strlen(URL_PREFIX); + } +#ifdef HAVE_OPENSSL + if (strncasecmp(SSL_URL_PREFIX, argv[0], strlen(SSL_URL_PREFIX)) == 0) { + if (!use_ssl) init_tls(); + use_ssl = 1; + url_prefix = strlen(SSL_URL_PREFIX); + } +#endif /* rsync:// destination uses rsync server over direct socket */ - if (strncasecmp(URL_PREFIX, argv[argc-1], strlen(URL_PREFIX)) == 0) { + if (url_prefix) { char *host, *path; - host = argv[argc-1] + strlen(URL_PREFIX); + host = argv[argc-1] + url_prefix; p = strchr(host,'/'); if (p) { *p = 0; Index: options.c =================================================================== RCS file: /cvsroot/rsync/options.c,v retrieving revision 1.116 diff -u -r1.116 options.c --- options.c 11 Sep 2003 04:53:01 -0000 1.116 +++ options.c 21 Sep 2003 13:52:25 -0000 @@ -134,6 +134,13 @@ * address, or a hostname. **/ char *bind_address; +#ifdef HAVE_OPENSSL +int use_ssl = 0; +extern char *ssl_cert_path; +extern char *ssl_key_path; +extern char *ssl_key_passwd; +extern char *ssl_ca_path; +#endif static void print_rsync_version(enum logcode f) { @@ -141,6 +148,7 @@ char const *hardlinks = "no "; char const *links = "no "; char const *ipv6 = "no "; + char const *ssl = "no "; STRUCT_STAT *dumstat; #ifdef HAVE_SOCKETPAIR @@ -159,6 +167,10 @@ ipv6 = ""; #endif +#ifdef HAVE_OPENSSL + ssl = ""; +#endif + rprintf(f, "%s version %s protocol version %d\n", RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION); rprintf(f, @@ -172,10 +184,10 @@ /* Note that this field may not have type ino_t. It depends * on the complicated interaction between largefile feature * macros. */ - rprintf(f, " %sIPv6, %d-bit system inums, %d-bit internal inums\n", + rprintf(f, " %sIPv6, %d-bit system inums, %d-bit internal inums, %sssl\n", ipv6, (int) (sizeof(dumstat->st_ino) * 8), - (int) (sizeof(INO64_T) * 8)); + (int) (sizeof(INO64_T) * 8), ssl); #ifdef MAINTAINER_MODE rprintf(f, " panic action: \"%s\"\n", get_panic_action()); @@ -287,6 +299,13 @@ rprintf(F," -4 prefer IPv4\n"); rprintf(F," -6 prefer IPv6\n"); #endif +#ifdef HAVE_OPENSSL + rprintf(F," --ssl allow socket connections to use SSL\n"); + rprintf(F," --ssl-cert=FILE path to server's SSL certificate\n"); + rprintf(F," --ssl-key=FILE path to server'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,"\n"); @@ -297,7 +316,7 @@ enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED, OPT_LINK_DEST, OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, - OPT_READ_BATCH, OPT_WRITE_BATCH}; + OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_USE_SSL}; static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ @@ -381,6 +400,13 @@ {0, '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 }, {0, '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 }, #endif +#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 {0,0,0,0, 0, 0, 0} }; @@ -585,6 +611,12 @@ return 0; #endif + case OPT_USE_SSL: +#ifdef HAVE_OPENSSL + use_ssl = 1; +#endif + break; + default: /* FIXME: If --daemon is specified, then errors for later @@ -644,6 +676,17 @@ if (do_progress && !verbose) verbose = 1; + +#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 = poptGetArgs(pc); if (*argv) Index: rsync.h =================================================================== RCS file: /cvsroot/rsync/rsync.h,v retrieving revision 1.155 diff -u -r1.155 rsync.h --- rsync.h 16 Sep 2003 02:49:59 -0000 1.155 +++ rsync.h 21 Sep 2003 13:52:27 -0000 @@ -31,6 +31,7 @@ #define DEFAULT_LOCK_FILE "/var/run/rsyncd.lock" #define URL_PREFIX "rsync://" +#define SSL_URL_PREFIX "rsyncs://" #define BACKUP_SUFFIX "~" @@ -290,6 +291,10 @@ #else /* As long as it gets... */ #define uint64 unsigned off_t +#endif + +#if HAVE_OPENSSL +#include "ssl.h" #endif /* Starting from protocol version 26, we always use 64-bit Index: ssl.c =================================================================== RCS file: ssl.c diff -N ssl.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ssl.c 21 Sep 2003 13:52:28 -0000 @@ -0,0 +1,353 @@ +/* -*- c-file-style: "linux" -*- + * ssl.c: operations for negotiating SSL rsync connections. + * + * Copyright (C) 2003 Casey Marshall + * + * This program is free software; you can redistribute it and/or modify + * 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. + */ + +#include "rsync.h" + +#ifdef HAVE_SYS_SELECT_H +#include +#else +#include +#include +#include +#endif +#include + +#define BUF_SIZE 1024 + +char *ssl_cert_path = NULL; +char *ssl_key_path = NULL; +char *ssl_key_passwd = NULL; +char *ssl_ca_path = NULL; +static SSL_CTX *ssl_ctx; +static SSL *ssl; +static int tls_read[2] = { -1, -1 }; +static int tls_write[2] = { -1, -1 }; +static int ssl_running; +static int ssl_pid = -1; + +/** + * 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 + * used for when the private key password is supplied via an option. + */ +int default_password_cb(char *buf, int n, UNUSED(int f), UNUSED(void *u)) +{ + if (ssl_key_passwd == NULL || n < strlen(ssl_key_passwd)) { + return 0; + } + strncpy(buf, ssl_key_passwd, n-1); + return strlen(ssl_key_passwd); +} + +/** + * If verbose, this method traces the status of the SSL handshake. + */ +static void info_callback(SSL *ssl, int cb, int val) +{ + extern int verbose; + char buf[128]; + char *cbs; + + switch (cb) { + case SSL_CB_LOOP: cbs = "SSL_CB_LOOP"; break; + case SSL_CB_EXIT: cbs = "SSL_CB_EXIT"; break; + case SSL_CB_READ: cbs = "SSL_CB_READ"; break; + case SSL_CB_WRITE: cbs = "SSL_CB_WRITE"; break; + case SSL_CB_ALERT: cbs = "SSL_CB_ALERT"; break; + case SSL_CB_READ_ALERT: cbs = "SSL_CB_READ_ALERT"; break; + case SSL_CB_WRITE_ALERT: cbs = "SSL_CB_WRITE_ALERT"; break; + case SSL_CB_ACCEPT_LOOP: cbs = "SSL_CB_ACCEPT_LOOP"; break; + case SSL_CB_ACCEPT_EXIT: cbs = "SSL_CB_ACCEPT_EXIT"; break; + case SSL_CB_CONNECT_LOOP: cbs = "SSL_CB_CONNECT_LOOP"; break; + case SSL_CB_CONNECT_EXIT: cbs = "SSL_CB_CONNECT_EXIT"; break; + case SSL_CB_HANDSHAKE_START: cbs = "SSL_CB_HANDSHAKE_START"; break; + case SSL_CB_HANDSHAKE_DONE: cbs = "SSL_CB_HANDSHAKE_DONE"; break; + default: + snprintf(buf, sizeof(buf), "??? (%d)", cb); + cbs = buf; + break; + } + if (verbose > 2) { + 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), + buf, sizeof(buf)); + rprintf(FLOG, "SSL: cipher: %s", buf); + } + } +} + +/** + * Initializes the SSL context for TLSv1 connections; returns zero on + * success. + */ +int init_tls(void) +{ + if (ssl_ctx) return 0; + SSL_library_init(); + SSL_load_error_strings(); + ssl_ctx = SSL_CTX_new(TLSv1_method()); + if (!ssl_ctx) { + return 1; + } + SSL_CTX_set_info_callback(ssl_ctx, info_callback); + + /* Sets the certificate sent to the other party. */ + if (ssl_cert_path != NULL) { + if (SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert_path, + SSL_FILETYPE_PEM) != 1) + { + return 1; + } + } + /* Set up the simple non-interactive callback if the password + was supplied on the command line. */ + if (ssl_key_passwd != NULL) { + SSL_CTX_set_default_passwd_cb(ssl_ctx, default_password_cb); + } + /* Sets the private key that matches the public certificate. */ + if (ssl_key_path != NULL) { + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_key_path, + SSL_FILETYPE_PEM) != 1) + { + return 1; + } + if (SSL_CTX_check_private_key(ssl_ctx) != 1) { + return 1; + } + } + if (ssl_ca_path != NULL) { + if (!SSL_CTX_load_verify_locations(ssl_ctx, ssl_ca_path, NULL)) { + return 1; + } + } + + return 0; +} + +/** + * Returns the error string for the current SSL error, if any. + */ +char *get_ssl_error(void) +{ + return ERR_error_string(ERR_get_error(), NULL); +} + +/** + * Returns the input file descriptor for the SSL connection. + */ +int get_tls_rfd(void) +{ + return tls_read[0]; +} + +/** + * Returns the output file descriptor for the SSL connection. + */ +int get_tls_wfd(void) +{ + return tls_write[1]; +} + +/** + * Signal handler that ends the SSL connection. + */ +static RETSIGTYPE tls_sigusr1(int UNUSED(val)) +{ + if (ssl) { + SSL_shutdown(ssl); + SSL_free(ssl); + ssl = NULL; + } + ssl_running = 0; +} + +/** + * 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. + */ +int start_tls(int f_in, int f_out) +{ + extern int am_daemon; + extern int am_server; + int tls_fd; + int n = 0, r; + unsigned char buf1[BUF_SIZE], buf2[BUF_SIZE]; + int avail1 = 0, avail2 = 0, write1 = 0, write2 = 0; + fd_set rd, wd; + + if (fd_pair(tls_read)) { + return 1; + } + if (fd_pair(tls_write)) { + return 1; + } + + set_blocking(tls_read[0]); + set_blocking(tls_read[1]); + set_blocking(tls_write[0]); + set_blocking(tls_write[1]); + set_blocking(f_in); + set_blocking(f_out); + + ssl_pid = do_fork(); + if (ssl_pid < 0) + return -1; + if (ssl_pid != 0) { + close(tls_write[0]); + close(tls_read[1]); + return 0; + } + + signal(SIGUSR1, tls_sigusr1); + ssl = SSL_new(ssl_ctx); + if (!ssl) { + goto closed; + } + if (am_daemon || am_server) { + SSL_set_accept_state(ssl); + } else { + SSL_set_connect_state(ssl); + } + 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; + + ssl_running = 1; + 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 { + rprintf(FERROR, "pipe read error: %s\n", + strerror(errno)); + break; + } + } + if (FD_ISSET(tls_fd, &rd)) { + r = SSL_read(ssl, buf2+avail2, BUF_SIZE-avail2); + if (r > 0) + avail2 += r; + else { + switch (SSL_get_error(ssl, r)) { + case SSL_ERROR_ZERO_RETURN: + goto closed; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + 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)); + goto closed; + case SSL_ERROR_SSL: + rprintf(FERROR, "SSL: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + goto closed; + default: + rprintf(FERROR, "unexpected ssl error %d\n", r); + goto closed; + } + } + } + if (FD_ISSET(tls_read[1], &wd) && write2 < avail2) { + r = write(tls_read[1], buf2+write2, avail2-write2); + if (r >= 0) + write2 += r; + else { + 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 { + switch (SSL_get_error(ssl, r)) { + case SSL_ERROR_ZERO_RETURN: + goto closed; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + 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)); + goto closed; + case SSL_ERROR_SSL: + rprintf(FERROR, "SSL: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + goto closed; + default: + rprintf(FERROR, "unexpected ssl error %d\n", r); + goto closed; + } + } + } + if (avail1 == write1) + avail1 = write1 = 0; + if (avail2 == write2) + avail2 = write2 = 0; + } + + /* XXX I'm pretty sure that there is a lot that I am not considering + here. Bugs? Yes, probably. */ + + /* We're finished. */ + closed: + close(tls_read[1]); + close(tls_write[0]); + exit(0); +} + +/** + * Ends the TLS connection. + */ +void end_tls(void) +{ + if (ssl_pid > 0) { + kill(ssl_pid, SIGUSR1); + } +} Index: ssl.h =================================================================== RCS file: ssl.h diff -N ssl.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ssl.h 21 Sep 2003 13:52:28 -0000 @@ -0,0 +1,34 @@ +/* -*- c-file-style: "linux" -*- + * ssl.h: header for SSL functions. + * + * Copyright (C) 2003 Casey Marshall + * + * This program is free software; you can redistribute it and/or modify + * 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. + */ + +#ifndef __SSL_H__ +#define __SSL_H__ + +#include +#include + +int init_tls(void); +int get_tls_rfd(void); +int get_tls_wfd(void); +char *get_ssl_error(void); +int start_tls(int, int); +void end_tls(void); + +#endif /* __SSL_H__ */