Updated patches to work with the current trunk.
[rsync-patches.git] / openssl-support.diff
1 Casey Marshall wrote:
2
3 I've been hacking together a way to use rsync with OpenSSL, and have
4 attached my current patch against a recent CVS tree. The details of
5 this 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
15      And, if the daemon allows SSL, it replies with
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
28 All warnings apply; I don't do C programming all that often, so I
29 can't say if I've left any cleanup/compatibility errors in the code.
30
31 To 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
37
38 diff --git a/Makefile.in b/Makefile.in
39 index feacb90..3016643 100644
40 --- a/Makefile.in
41 +++ b/Makefile.in
42 @@ -41,7 +41,7 @@ OBJS3=progress.o pipe.o
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  
49  TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
50  
51 diff --git a/cleanup.c b/cleanup.c
52 index 19ef072..506f575 100644
53 --- a/cleanup.c
54 +++ b/cleanup.c
55 @@ -25,6 +25,9 @@
56  extern int am_server;
57  extern int am_daemon;
58  extern int io_error;
59 +#ifdef HAVE_OPENSSL
60 +extern int use_ssl;
61 +#endif
62  extern int keep_partial;
63  extern int got_xfer_error;
64  extern int output_needs_newline;
65 @@ -127,6 +130,14 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
66                                 code, file, line);
67                 }
68  
69 +#ifdef HAVE_OPENSSL
70 +               /* FALLTHROUGH */
71 +#include "case_N.h"
72 +
73 +               if (use_ssl)
74 +                       end_tls();
75 +#endif
76 +
77                 /* FALLTHROUGH */
78  #include "case_N.h"
79  
80 diff --git a/clientserver.c b/clientserver.c
81 index b6afe00..5467c72 100644
82 --- a/clientserver.c
83 +++ b/clientserver.c
84 @@ -30,6 +30,9 @@ extern int am_sender;
85  extern int am_server;
86  extern int am_daemon;
87  extern int am_root;
88 +#ifdef HAVE_OPENSSL
89 +extern int use_ssl;
90 +#endif
91  extern int rsync_port;
92  extern int protect_args;
93  extern int ignore_errors;
94 @@ -134,8 +137,18 @@ int start_socket_client(char *host, int remote_argc, char *remote_argv[],
95  #endif
96  
97         ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv);
98 +       if (ret)
99 +               return ret;
100 +
101 +#ifdef HAVE_OPENSSL
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
108  
109 -       return ret ? ret : client_run(fd, fd, -1, argc, argv);
110 +       return client_run(fd, fd, -1, argc, argv);
111  }
112  
113  static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client)
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))
116                 print_child_argv("sending daemon args:", sargs);
117  
118 +#ifdef HAVE_OPENSSL
119 +       if (use_ssl) {
120 +               io_printf(f_out, "#starttls\n");
121 +               while (1) {
122 +                       if (!read_line_old(f_in, line, sizeof line)) {
123 +                               rprintf(FERROR, "rsync: did not receive reply to #starttls\n");
124 +                               return -1;
125 +                       }
126 +                       if (strncmp(line, "@ERROR", 6) == 0) {
127 +                               rprintf(FERROR, "%s\n", line);
128 +                               return -1;
129 +                       }
130 +                       if (strcmp(line, "@RSYNCD: starttls") == 0)
131 +                               break;
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 +
144         io_printf(f_out, "%.*s\n", modlen, modname);
145  
146         /* Old servers may just drop the connection here,
147 @@ -303,6 +342,10 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
148                          * server to terminate the listing of modules.
149                          * We don't want to go on and transfer
150                          * anything; just exit. */
151 +#ifdef HAVE_OPENSSL
152 +                       if (use_ssl)
153 +                               end_tls();
154 +#endif
155                         exit(0);
156                 }
157  
158 @@ -310,6 +353,10 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
159                         rprintf(FERROR, "%s\n", line);
160                         /* This is always fatal; the server will now
161                          * close the socket. */
162 +#ifdef HAVE_OPENSSL
163 +                       if (use_ssl)
164 +                               end_tls();
165 +#endif
166                         return -1;
167                 }
168  
169 @@ -1022,6 +1069,9 @@ int start_daemon(int f_in, int f_out)
170         if (exchange_protocols(f_in, f_out, line, sizeof line, 0) < 0)
171                 return -1;
172  
173 +#ifdef HAVE_OPENSSL
174 +retry:
175 +#endif
176         line[0] = 0;
177         if (!read_line_old(f_in, line, sizeof line))
178                 return -1;
179 @@ -1033,6 +1083,20 @@ int start_daemon(int f_in, int f_out)
180                 return -1;
181         }
182  
183 +#ifdef HAVE_OPENSSL
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;
190 +               }
191 +               f_in = get_tls_rfd();
192 +               f_out = get_tls_wfd();
193 +               goto retry;
194 +       }
195 +#endif
196 +
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);
200 diff --git a/configure.in b/configure.in
201 index bc7d4a7..73ca6c5 100644
202 --- a/configure.in
203 +++ b/configure.in
204 @@ -293,6 +293,21 @@ if test x"$enable_locale" != x"no"; then
205         AC_DEFINE(CONFIG_LOCALE)
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)
226 diff --git a/options.c b/options.c
227 index e7c6c61..634b89e 100644
228 --- a/options.c
229 +++ b/options.c
230 @@ -191,6 +191,14 @@ int logfile_format_has_o_or_i = 0;
231  int always_checksum = 0;
232  int list_only = 0;
233  
234 +#ifdef HAVE_OPENSSL
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 +
242  #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
243  char *batch_name = NULL;
244  
245 @@ -566,6 +574,7 @@ static void print_rsync_version(enum logcode f)
246         char const *links = "no ";
247         char const *iconv = "no ";
248         char const *ipv6 = "no ";
249 +       char const *ssl = "no ";
250         STRUCT_STAT *dumstat;
251  
252  #if SUBPROTOCOL_VERSION != 0
253 @@ -599,6 +608,9 @@ static void print_rsync_version(enum logcode f)
254  #if defined HAVE_LUTIMES && defined HAVE_UTIMES
255         symtimes = "";
256  #endif
257 +#ifdef HAVE_OPENSSL
258 +       ssl = "";
259 +#endif
260  
261         rprintf(f, "%s  version %s  protocol version %d%s\n",
262                 RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
263 @@ -612,8 +624,8 @@ static void print_rsync_version(enum logcode f)
264                 (int)(sizeof (int64) * 8));
265         rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
266                 got_socketpair, hardlinks, links, ipv6, have_inplace);
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);
271  
272  #ifdef MAINTAINER_MODE
273         rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
274 @@ -784,6 +796,13 @@ void usage(enum logcode F)
275  #endif
276    rprintf(F," -4, --ipv4                  prefer IPv4\n");
277    rprintf(F," -6, --ipv6                  prefer IPv6\n");
278 +#ifdef HAVE_OPENSSL
279 +  rprintf(F,"     --ssl                   allow socket connections to use SSL\n");
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");
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
285    rprintf(F,"     --version               print version number\n");
286    rprintf(F,"(-h) --help                  show this help (-h works with no other options)\n");
287  
288 @@ -798,7 +817,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
289        OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
290        OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
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,
294        OPT_SERVER, OPT_REFUSED_BASE = 9000};
295  
296  static struct poptOption long_options[] = {
297 @@ -1012,6 +1031,13 @@ static struct poptOption long_options[] = {
298    {"checksum-seed",    0,  POPT_ARG_INT,    &checksum_seed, 0, 0, 0 },
299    {"server",           0,  POPT_ARG_NONE,   0, OPT_SERVER, 0, 0 },
300    {"sender",           0,  POPT_ARG_NONE,   0, OPT_SENDER, 0, 0 },
301 +#ifdef HAVE_OPENSSL
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},
305 +  {"ssl-key-passwd",   0,  POPT_ARG_STRING, &ssl_key_passwd, OPT_USE_SSL, 0, 0},
306 +  {"ssl-ca-certs",     0,  POPT_ARG_STRING, &ssl_ca_path, OPT_USE_SSL, 0, 0},
307 +#endif
308    /* All the following options switch us into daemon-mode option-parsing. */
309    {"config",           0,  POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
310    {"daemon",           0,  POPT_ARG_NONE,   0, OPT_DAEMON, 0, 0 },
311 @@ -1039,6 +1065,13 @@ static void daemon_usage(enum logcode F)
312    rprintf(F," -v, --verbose               increase verbosity\n");
313    rprintf(F," -4, --ipv4                  prefer IPv4\n");
314    rprintf(F," -6, --ipv6                  prefer IPv6\n");
315 +#ifdef HAVE_OPENSSL
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");
325 @@ -1064,6 +1097,13 @@ static struct poptOption long_daemon_options[] = {
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 },
329 +#ifdef HAVE_OPENSSL
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 },
339 @@ -1358,6 +1398,12 @@ int parse_arguments(int *argc_p, const char ***argv_p)
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",
352 @@ -1384,6 +1430,17 @@ int parse_arguments(int *argc_p, const char ***argv_p)
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 +
367                         *argv_p = argv = poptGetArgs(pc);
368                         *argc_p = argc = count_args(argv);
369                         am_starting_up = 0;
370 @@ -1742,6 +1799,12 @@ int parse_arguments(int *argc_p, const char ***argv_p)
371                         return 0;
372  #endif
373  
374 +#ifdef HAVE_OPENSSL
375 +               case OPT_USE_SSL:
376 +                       use_ssl = 1;
377 +                       break;
378 +#endif
379 +
380                 default:
381                         /* A large opt value means that set_refuse_options()
382                          * turned this option off. */
383 @@ -2108,6 +2171,17 @@ int parse_arguments(int *argc_p, const char ***argv_p)
384         if (delay_updates && !partial_dir)
385                 partial_dir = tmp_partialdir;
386  
387 +#ifdef HAVE_OPENSSL
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
397 +
398         if (inplace) {
399  #ifdef HAVE_FTRUNCATE
400                 if (partial_dir) {
401 @@ -2698,9 +2772,18 @@ char *check_for_hostspec(char *s, char **host_ptr, int *port_ptr)
402  {
403         char *path;
404  
405 -       if (port_ptr && strncasecmp(URL_PREFIX, s, strlen(URL_PREFIX)) == 0) {
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) {
413 +                       if (!use_ssl)
414 +                               init_tls();
415 +                       use_ssl = 1;
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;
423 diff --git a/rsync.h b/rsync.h
424 index be7cf8a..7b7cc88 100644
425 --- a/rsync.h
426 +++ b/rsync.h
427 @@ -31,6 +31,7 @@
428  
429  #define DEFAULT_LOCK_FILE "/var/run/rsyncd.lock"
430  #define URL_PREFIX "rsync://"
431 +#define SSL_URL_PREFIX "rsyncs://"
432  
433  #define SYMLINK_PREFIX "/rsyncd-munged/"  /* This MUST have a trailing slash! */
434  #define SYMLINK_PREFIX_LEN ((int)sizeof SYMLINK_PREFIX - 1)
435 @@ -569,6 +570,11 @@ typedef unsigned int size_t;
436  # define SIZEOF_INT64 SIZEOF_OFF_T
437  #endif
438  
439 +#ifdef HAVE_OPENSSL
440 +#include <openssl/ssl.h>
441 +#include <openssl/err.h>
442 +#endif
443 +
444  struct hashtable {
445         void *nodes;
446         int32 size, entries;
447 diff --git a/ssl.c b/ssl.c
448 new file mode 100644
449 index 0000000..f0d4d9f
450 --- /dev/null
451 +++ b/ssl.c
452 @@ -0,0 +1,369 @@
453 +/* -*- c-file-style: "linux" -*-
454 + * ssl.c: operations for negotiating SSL rsync connections.
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.
462 + *
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.
467 + *
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 +
475 +#ifdef HAVE_SYS_SELECT_H
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 +
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 +
501 +#ifdef HAVE_SIGACTION
502 +static struct sigaction sigact;
503 +#endif
504 +
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 +{
512 +       if (ssl_key_passwd == NULL || n < (int)strlen(ssl_key_passwd))
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 + */
521 +static void info_callback(const SSL *ssl, int cb, int val)
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 +       }
571 +       if (DEBUG_GTE(CONNECT, 1)) {
572 +               rprintf(FLOG, "SSL: info_callback(%p,%s,%d)\n", ssl, cbs, val);
573 +               if (cb == SSL_CB_HANDSHAKE_DONE) {
574 +                       SSL_CIPHER_description(SSL_get_current_cipher((SSL*)ssl),
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 +
693 +       SIGACTION(SIGUSR1, tls_sigusr1);
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 +}