Improve SQLite support by unifying DB updating.
[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 based-on: 63f91976112b8b2118cc17eb5fc8142175566f4f
39 diff --git a/Makefile.in b/Makefile.in
40 --- a/Makefile.in
41 +++ b/Makefile.in
42 @@ -43,7 +43,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) @BUILD_ZLIB@ @BUILD_POPT@
47 +OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @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 --- a/cleanup.c
53 +++ b/cleanup.c
54 @@ -26,6 +26,9 @@ extern int am_server;
55  extern int am_daemon;
56  extern int am_receiver;
57  extern int io_error;
58 +#ifdef HAVE_OPENSSL
59 +extern int use_ssl;
60 +#endif
61  extern int keep_partial;
62  extern int got_xfer_error;
63  extern int protocol_version;
64 @@ -137,6 +140,14 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
65                                 who_am_i(), code, file, line);
66                 }
67  
68 +#ifdef HAVE_OPENSSL
69 +               /* FALLTHROUGH */
70 +#include "case_N.h"
71 +
72 +               if (use_ssl)
73 +                       end_tls();
74 +#endif
75 +
76                 /* FALLTHROUGH */
77  #include "case_N.h"
78                 switch_step++;
79 diff --git a/clientserver.c b/clientserver.c
80 --- a/clientserver.c
81 +++ b/clientserver.c
82 @@ -30,6 +30,9 @@ extern int am_sender;
83  extern int am_server;
84  extern int am_daemon;
85  extern int am_root;
86 +#ifdef HAVE_OPENSSL
87 +extern int use_ssl;
88 +#endif
89  extern int rsync_port;
90  extern int protect_args;
91  extern int ignore_errors;
92 @@ -132,8 +135,18 @@ int start_socket_client(char *host, int remote_argc, char *remote_argv[],
93  #endif
94  
95         ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv);
96 +       if (ret)
97 +               return ret;
98 +
99 +#ifdef HAVE_OPENSSL
100 +       if (use_ssl) {
101 +               int f_in = get_tls_rfd();
102 +               int f_out = get_tls_wfd();
103 +               return client_run(f_in, f_out, -1, argc, argv);
104 +       }
105 +#endif
106  
107 -       return ret ? ret : client_run(fd, fd, -1, argc, argv);
108 +       return client_run(fd, fd, -1, argc, argv);
109  }
110  
111  static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client)
112 @@ -279,6 +292,32 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
113         if (DEBUG_GTE(CMD, 1))
114                 print_child_argv("sending daemon args:", sargs);
115  
116 +#ifdef HAVE_OPENSSL
117 +       if (use_ssl) {
118 +               io_printf(f_out, "#starttls\n");
119 +               while (1) {
120 +                       if (!read_line_old(f_in, line, sizeof line)) {
121 +                               rprintf(FERROR, "rsync: did not receive reply to #starttls\n");
122 +                               return -1;
123 +                       }
124 +                       if (strncmp(line, "@ERROR", 6) == 0) {
125 +                               rprintf(FERROR, "%s\n", line);
126 +                               return -1;
127 +                       }
128 +                       if (strcmp(line, "@RSYNCD: starttls") == 0)
129 +                               break;
130 +                       rprintf(FINFO, "%s\n", line);
131 +               }
132 +               if (start_tls(f_in, f_out)) {
133 +                       rprintf(FERROR, "rsync: error during SSL handshake: %s\n",
134 +                               get_ssl_error());
135 +                       return -1;
136 +               }
137 +               f_in = get_tls_rfd();
138 +               f_out = get_tls_wfd();
139 +       }
140 +#endif
141 +
142         io_printf(f_out, "%.*s\n", modlen, modname);
143  
144         /* Old servers may just drop the connection here,
145 @@ -304,6 +343,10 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
146                          * server to terminate the listing of modules.
147                          * We don't want to go on and transfer
148                          * anything; just exit. */
149 +#ifdef HAVE_OPENSSL
150 +                       if (use_ssl)
151 +                               end_tls();
152 +#endif
153                         exit(0);
154                 }
155  
156 @@ -311,6 +354,10 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
157                         rprintf(FERROR, "%s\n", line);
158                         /* This is always fatal; the server will now
159                          * close the socket. */
160 +#ifdef HAVE_OPENSSL
161 +                       if (use_ssl)
162 +                               end_tls();
163 +#endif
164                         return -1;
165                 }
166  
167 @@ -713,6 +760,10 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
168                                 set_env_num("RSYNC_PID", (long)pid);
169                                 if (wait_process(pid, &status, 0) < 0)
170                                         status = -1;
171 +#ifdef HAVE_OPENSSL
172 +                               if (use_ssl)
173 +                                       end_tls();
174 +#endif
175                                 set_env_num("RSYNC_RAW_STATUS", status);
176                                 if (WIFEXITED(status))
177                                         status = WEXITSTATUS(status);
178 @@ -1068,6 +1119,9 @@ int start_daemon(int f_in, int f_out)
179         if (exchange_protocols(f_in, f_out, line, sizeof line, 0) < 0)
180                 return -1;
181  
182 +#ifdef HAVE_OPENSSL
183 +  retry:
184 +#endif
185         line[0] = 0;
186         if (!read_line_old(f_in, line, sizeof line, 0))
187                 return -1;
188 @@ -1079,6 +1133,20 @@ int start_daemon(int f_in, int f_out)
189                 return -1;
190         }
191  
192 +#ifdef HAVE_OPENSSL
193 +       if (use_ssl && strcmp(line, "#starttls") == 0) {
194 +               io_printf(f_out, "@RSYNCD: starttls\n");
195 +               if (start_tls(f_in, f_out)) {
196 +                       rprintf(FLOG, "SSL connection failed: %s\n",
197 +                               get_ssl_error());
198 +                       return -1;
199 +               }
200 +               f_in = get_tls_rfd();
201 +               f_out = get_tls_wfd();
202 +               goto retry;
203 +       }
204 +#endif
205 +
206         if (*line == '#') {
207                 /* it's some sort of command that I don't understand */
208                 io_printf(f_out, "@ERROR: Unknown command '%s'\n", line);
209 diff --git a/configure.ac b/configure.ac
210 --- a/configure.ac
211 +++ b/configure.ac
212 @@ -328,6 +328,25 @@ if test x"$enable_locale" != x"no"; then
213         AC_DEFINE(CONFIG_LOCALE)
214  fi
215  
216 +AC_ARG_ENABLE(openssl,
217 +              AC_HELP_STRING([--enable-openssl], [compile SSL support with OpenSSL.]))
218 +
219 +if test "x$enable_openssl" != xno
220 +then
221 +       save_LIBS=$LIBS
222 +       LIBS="$LIBS -lcrypto"
223 +       have_ssl=yes
224 +       AC_CHECK_LIB(ssl, SSL_library_init, , [have_ssl=no])
225 +       if test "x$have_ssl" = xyes
226 +       then
227 +               AC_DEFINE(HAVE_OPENSSL, 1, [true if you want to use SSL.])
228 +               SSL_OBJS=ssl.o
229 +               AC_SUBST(SSL_OBJS)
230 +       else
231 +               LIBS=$save_LIBS
232 +       fi
233 +fi
234 +
235  AC_MSG_CHECKING([whether to call shutdown on all sockets])
236  case $host_os in
237         *cygwin* ) AC_MSG_RESULT(yes)
238 diff --git a/main.c b/main.c
239 --- a/main.c
240 +++ b/main.c
241 @@ -93,6 +93,9 @@ extern char backup_dir_buf[MAXPATHLEN];
242  extern char *basis_dir[MAX_BASIS_DIRS+1];
243  extern struct file_list *first_flist;
244  extern filter_rule_list daemon_filter_list;
245 +#ifdef HAVE_OPENSSL
246 +extern int use_ssl;
247 +#endif
248  
249  uid_t our_uid;
250  gid_t our_gid;
251 @@ -153,6 +156,52 @@ pid_t wait_process(pid_t pid, int *status_ptr, int flags)
252         return waited_pid;
253  }
254  
255 +/* Sends signal "signo", waits for the process to die, and if it doesn't, sends
256 + * a SIGKILL.  If "graceful" is set, the initial "signo" signal is delayed by a
257 + * second to try to let the process exit on its own first. */
258 +pid_t terminate_process(pid_t pid, int *status_ptr, int signo, int graceful)
259 +{
260 +       pid_t waited_pid;
261 +       int timeout = graceful ? 1000 : 3000;
262 +       if (!graceful)
263 +               kill(pid, signo);
264 +       while (1) {
265 +               waited_pid = wait_process(pid, status_ptr, timeout >= 0 ? WNOHANG : 0);
266 +               if (waited_pid)
267 +                       break;
268 +               if (timeout == 0) {
269 +                       if (graceful) {
270 +                               graceful = 0;
271 +                               timeout = 3000;
272 +                       } else {
273 +                               signo = SIGKILL;
274 +                               timeout = -1;
275 +                       }
276 +                       rprintf(FINFO, "%s:%s shutdown didn't work - sending signal %d\n",
277 +                               __FUNCTION__, graceful ? " graceful" : "", signo);
278 +                       kill(pid, signo);
279 +               }
280 +
281 +               if (timeout > 0) {
282 +                       /* interruptible wait and calculate the time left for waiting */
283 +                       struct timeval tval, t1, t2;
284 +
285 +                       gettimeofday(&t1, NULL);
286 +
287 +                       tval.tv_sec = timeout/1000;
288 +                       tval.tv_usec = (timeout%1000)*1000;
289 +                       select(0, NULL, NULL, NULL, &tval);
290 +                       gettimeofday(&t2, NULL);
291 +
292 +                       timeout -= (t2.tv_sec-t1.tv_sec)*1000 + (t2.tv_usec-t1.tv_usec)/1000;
293 +                       if (timeout < 0)
294 +                               timeout = 0;
295 +               }
296 +       }
297 +
298 +       return waited_pid;
299 +}
300 +
301  /* Wait for a process to exit, calling io_flush while waiting. */
302  static void wait_process_with_flush(pid_t pid, int *exit_code_ptr)
303  {
304 @@ -809,6 +858,11 @@ static void do_server_sender(int f_in, int f_out, int argc, char *argv[])
305                 argv[0] = ".";
306         }
307  
308 +#ifdef HAVE_OPENSSL
309 +       if (use_ssl)
310 +               start_tls_buffering();
311 +#endif
312 +
313         flist = send_file_list(f_out,argc,argv);
314         if (!flist || flist->used == 0) {
315                 /* Make sure input buffering is off so we can't hang in noop_io_until_death(). */
316 @@ -933,6 +987,10 @@ static int do_recv(int f_in, int f_out, char *local_name)
317  
318         io_start_buffering_out(f_out);
319         io_start_multiplex_in(f_in);
320 +#ifdef HAVE_OPENSSL
321 +       if (use_ssl)
322 +               start_tls_buffering();
323 +#endif
324  
325  #ifdef SUPPORT_HARD_LINKS
326         if (preserve_hard_links && inc_recurse) {
327 @@ -1135,6 +1193,10 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
328                         io_start_multiplex_in(f_in);
329                 else
330                         io_start_buffering_in(f_in);
331 +#ifdef HAVE_OPENSSL
332 +               if (use_ssl)
333 +                       start_tls_buffering();
334 +#endif
335                 send_filter_list(f_out);
336                 if (filesfrom_host)
337                         filesfrom_fd = f_in;
338 diff --git a/options.c b/options.c
339 --- a/options.c
340 +++ b/options.c
341 @@ -191,6 +191,14 @@ int logfile_format_has_o_or_i = 0;
342  int always_checksum = 0;
343  int list_only = 0;
344  
345 +#ifdef HAVE_OPENSSL
346 +int use_ssl = 0;
347 +char *ssl_cert_path = NULL;
348 +char *ssl_key_path = NULL;
349 +char *ssl_key_passwd = NULL;
350 +char *ssl_ca_path = NULL;
351 +#endif
352 +
353  #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
354  char *batch_name = NULL;
355  
356 @@ -572,6 +580,7 @@ static void print_rsync_version(enum logcode f)
357         char const *links = "no ";
358         char const *iconv = "no ";
359         char const *ipv6 = "no ";
360 +       char const *ssl = "no ";
361         STRUCT_STAT *dumstat;
362  
363  #if SUBPROTOCOL_VERSION != 0
364 @@ -608,6 +617,9 @@ static void print_rsync_version(enum logcode f)
365  #ifdef CAN_SET_SYMLINK_TIMES
366         symtimes = "";
367  #endif
368 +#ifdef HAVE_OPENSSL
369 +       ssl = "";
370 +#endif
371  
372         rprintf(f, "%s  version %s  protocol version %d%s\n",
373                 RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
374 @@ -621,8 +633,8 @@ static void print_rsync_version(enum logcode f)
375                 (int)(sizeof (int64) * 8));
376         rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
377                 got_socketpair, hardlinks, links, ipv6, have_inplace);
378 -       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc\n",
379 -               have_inplace, acls, xattrs, iconv, symtimes, prealloc);
380 +       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc, %sSSL\n",
381 +               have_inplace, acls, xattrs, iconv, symtimes, prealloc, ssl);
382  
383  #ifdef MAINTAINER_MODE
384         rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
385 @@ -804,6 +816,13 @@ void usage(enum logcode F)
386    rprintf(F,"     --checksum-seed=NUM     set block/file checksum seed (advanced)\n");
387    rprintf(F," -4, --ipv4                  prefer IPv4\n");
388    rprintf(F," -6, --ipv6                  prefer IPv6\n");
389 +#ifdef HAVE_OPENSSL
390 +  rprintf(F,"     --ssl                   allow socket connections to use SSL\n");
391 +  rprintf(F,"     --ssl-cert=FILE         path to daemon's SSL certificate\n");
392 +  rprintf(F,"     --ssl-key=FILE          path to daemon's SSL private key\n");
393 +  rprintf(F,"     --ssl-key-passwd=PASS   password for PEM-encoded private key\n");
394 +  rprintf(F,"     --ssl-ca-certs=FILE     path to trusted CA certificates\n");
395 +#endif
396    rprintf(F,"     --version               print version number\n");
397    rprintf(F,"(-h) --help                  show this help (-h is --help only if used alone)\n");
398  
399 @@ -817,7 +836,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
400        OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP,
401        OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
402        OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
403 -      OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG,
404 +      OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_USE_SSL,
405        OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT,
406        OPT_SERVER, OPT_REFUSED_BASE = 9000};
407  
408 @@ -1039,6 +1058,13 @@ static struct poptOption long_options[] = {
409    {"checksum-seed",    0,  POPT_ARG_INT,    &checksum_seed, 0, 0, 0 },
410    {"server",           0,  POPT_ARG_NONE,   0, OPT_SERVER, 0, 0 },
411    {"sender",           0,  POPT_ARG_NONE,   0, OPT_SENDER, 0, 0 },
412 +#ifdef HAVE_OPENSSL
413 +  {"ssl",              0,  POPT_ARG_NONE,   0, OPT_USE_SSL, 0, 0},
414 +  {"ssl-cert",         0,  POPT_ARG_STRING, &ssl_cert_path, OPT_USE_SSL, 0, 0},
415 +  {"ssl-key",          0,  POPT_ARG_STRING, &ssl_key_path, OPT_USE_SSL, 0, 0},
416 +  {"ssl-key-passwd",   0,  POPT_ARG_STRING, &ssl_key_passwd, OPT_USE_SSL, 0, 0},
417 +  {"ssl-ca-certs",     0,  POPT_ARG_STRING, &ssl_ca_path, OPT_USE_SSL, 0, 0},
418 +#endif
419    /* All the following options switch us into daemon-mode option-parsing. */
420    {"config",           0,  POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
421    {"daemon",           0,  POPT_ARG_NONE,   0, OPT_DAEMON, 0, 0 },
422 @@ -1066,6 +1092,13 @@ static void daemon_usage(enum logcode F)
423    rprintf(F," -v, --verbose               increase verbosity\n");
424    rprintf(F," -4, --ipv4                  prefer IPv4\n");
425    rprintf(F," -6, --ipv6                  prefer IPv6\n");
426 +#ifdef HAVE_OPENSSL
427 +  rprintf(F,"     --ssl                   allow socket connections to use SSL\n");
428 +  rprintf(F,"     --ssl-cert=FILE         path to daemon's SSL certificate\n");
429 +  rprintf(F,"     --ssl-key=FILE          path to daemon's SSL private key\n");
430 +  rprintf(F,"     --ssl-key-passwd=PASS   password for PEM-encoded private key\n");
431 +  rprintf(F,"     --ssl-ca-certs=FILE     path to trusted CA certificates\n");
432 +#endif
433    rprintf(F,"     --help                  show this help screen\n");
434  
435    rprintf(F,"\n");
436 @@ -1091,6 +1124,13 @@ static struct poptOption long_daemon_options[] = {
437    {"protocol",         0,  POPT_ARG_INT,    &protocol_version, 0, 0, 0 },
438    {"server",           0,  POPT_ARG_NONE,   &am_server, 0, 0, 0 },
439    {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
440 +#ifdef HAVE_OPENSSL
441 +  {"ssl",              0,  POPT_ARG_NONE,   0, OPT_USE_SSL, 0, 0},
442 +  {"ssl-cert",         0,  POPT_ARG_STRING, &ssl_cert_path, OPT_USE_SSL, 0, 0},
443 +  {"ssl-key",          0,  POPT_ARG_STRING, &ssl_key_path, OPT_USE_SSL, 0, 0},
444 +  {"ssl-key-passwd",   0,  POPT_ARG_STRING, &ssl_key_passwd, OPT_USE_SSL, 0, 0},
445 +  {"ssl-ca-certs",     0,  POPT_ARG_STRING, &ssl_ca_path, OPT_USE_SSL, 0, 0},
446 +#endif
447    {"verbose",         'v', POPT_ARG_NONE,   0, 'v', 0, 0 },
448    {"no-verbose",       0,  POPT_ARG_VAL,    &verbose, 0, 0, 0 },
449    {"no-v",             0,  POPT_ARG_VAL,    &verbose, 0, 0, 0 },
450 @@ -1386,6 +1426,12 @@ int parse_arguments(int *argc_p, const char ***argv_p)
451                                         verbose++;
452                                         break;
453  
454 +#ifdef HAVE_OPENSSL
455 +                               case OPT_USE_SSL:
456 +                                       use_ssl = 1;
457 +                                       break;
458 +#endif
459 +
460                                 default:
461                                         rprintf(FERROR,
462                                             "rsync: %s: %s (in daemon mode)\n",
463 @@ -1412,6 +1458,17 @@ int parse_arguments(int *argc_p, const char ***argv_p)
464                                 exit_cleanup(RERR_SYNTAX);
465                         }
466  
467 +#ifdef HAVE_OPENSSL
468 +                       if (use_ssl) {
469 +                               if (init_tls()) {
470 +                                       snprintf(err_buf, sizeof(err_buf),
471 +                                                "Openssl error: %s\n",
472 +                                                get_ssl_error());
473 +                                       return 0;
474 +                               }
475 +                       }
476 +#endif
477 +
478                         *argv_p = argv = poptGetArgs(pc);
479                         *argc_p = argc = count_args(argv);
480                         am_starting_up = 0;
481 @@ -1794,6 +1851,12 @@ int parse_arguments(int *argc_p, const char ***argv_p)
482                         return 0;
483  #endif
484  
485 +#ifdef HAVE_OPENSSL
486 +               case OPT_USE_SSL:
487 +                       use_ssl = 1;
488 +                       break;
489 +#endif
490 +
491                 default:
492                         /* A large opt value means that set_refuse_options()
493                          * turned this option off. */
494 @@ -2231,6 +2294,17 @@ int parse_arguments(int *argc_p, const char ***argv_p)
495         if (delay_updates && !partial_dir)
496                 partial_dir = tmp_partialdir;
497  
498 +#ifdef HAVE_OPENSSL
499 +       if (use_ssl) {
500 +               if (init_tls()) {
501 +                       snprintf(err_buf, sizeof(err_buf),
502 +                                "Openssl error: %s\n",
503 +                                get_ssl_error());
504 +                       return 0;
505 +               }
506 +       }
507 +#endif
508 +
509         if (inplace) {
510  #ifdef HAVE_FTRUNCATE
511                 if (partial_dir) {
512 @@ -2831,9 +2905,21 @@ char *check_for_hostspec(char *s, char **host_ptr, int *port_ptr)
513  {
514         char *path;
515  
516 -       if (port_ptr && strncasecmp(URL_PREFIX, s, strlen(URL_PREFIX)) == 0) {
517 -               *host_ptr = parse_hostspec(s + strlen(URL_PREFIX), &path, port_ptr);
518 -               if (*host_ptr) {
519 +       if (port_ptr) {
520 +               int url_prefix_len;
521 +               if (strncasecmp(URL_PREFIX, s, sizeof URL_PREFIX - 1) == 0)
522 +                       url_prefix_len = sizeof URL_PREFIX - 1;
523 +#ifdef HAVE_OPENSSL
524 +               else if (strncasecmp(SSL_URL_PREFIX, s, sizeof SSL_URL_PREFIX - 1) == 0) {
525 +                       if (!use_ssl)
526 +                               init_tls();
527 +                       use_ssl = 1;
528 +                       url_prefix_len = sizeof SSL_URL_PREFIX - 1;
529 +               }
530 +#endif
531 +               else
532 +                       url_prefix_len = 0;
533 +               if (url_prefix_len && (*host_ptr = parse_hostspec(s + url_prefix_len, &path, port_ptr))) {
534                         if (!*port_ptr)
535                                 *port_ptr = RSYNC_PORT;
536                         return path;
537 diff --git a/rsync.h b/rsync.h
538 --- a/rsync.h
539 +++ b/rsync.h
540 @@ -31,6 +31,7 @@
541  
542  #define DEFAULT_LOCK_FILE "/var/run/rsyncd.lock"
543  #define URL_PREFIX "rsync://"
544 +#define SSL_URL_PREFIX "rsyncs://"
545  
546  #define SYMLINK_PREFIX "/rsyncd-munged/"  /* This MUST have a trailing slash! */
547  #define SYMLINK_PREFIX_LEN ((int)sizeof SYMLINK_PREFIX - 1)
548 @@ -608,6 +609,11 @@ typedef unsigned int size_t;
549  # define SIZEOF_INT64 SIZEOF_OFF_T
550  #endif
551  
552 +#ifdef HAVE_OPENSSL
553 +#include <openssl/ssl.h>
554 +#include <openssl/err.h>
555 +#endif
556 +
557  struct hashtable {
558         void *nodes;
559         int32 size, entries;
560 diff --git a/ssl.c b/ssl.c
561 new file mode 100644
562 --- /dev/null
563 +++ b/ssl.c
564 @@ -0,0 +1,603 @@
565 +/* -*- c-file-style: "linux" -*-
566 + * ssl.c: operations for negotiating SSL rsync connections.
567 + *
568 + * Copyright (C) 2003  Casey Marshall <rsdio@metastatic.org>
569 + *
570 + * This program is free software; you can redistribute it and/or modify
571 + * it under the terms of the GNU General Public License as published by
572 + * the Free Software Foundation; either version 2 of the License, or
573 + * (at your option) any later version.
574 + *
575 + * This program is distributed in the hope that it will be useful,
576 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
577 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
578 + * GNU General Public License for more details.
579 + *
580 + * You should have received a copy of the GNU General Public License
581 + * along with this program; if not, write to the Free Software
582 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
583 + */
584 +
585 +#include "rsync.h"
586 +
587 +#ifdef HAVE_SYS_SELECT_H
588 +#include <sys/select.h>
589 +#else
590 +#include <sys/time.h>
591 +#include <sys/types.h>
592 +#include <unistd.h>
593 +#endif
594 +#include <string.h>
595 +
596 +#define SSL_MAX_RECORD_SIZE (1024*16) /* TLS record size */
597 +#define MAX_BUFFERING_TIME_MS  100
598 +
599 +extern int am_daemon;
600 +extern int am_server;
601 +
602 +extern char *ssl_cert_path;
603 +extern char *ssl_key_path;
604 +extern char *ssl_key_passwd;
605 +extern char *ssl_ca_path;
606 +
607 +static SSL_CTX *ssl_ctx;
608 +static SSL *ssl;
609 +static int tls_read[2] = { -1, -1 };
610 +static int tls_write[2] = { -1, -1 };
611 +static int ssl_running;
612 +static int ssl_min_send_size;
613 +static int ssl_pid = -1;
614 +
615 +#ifdef HAVE_SIGACTION
616 +static struct sigaction sigact;
617 +#endif
618 +
619 +/* copied from progress.c */
620 +static unsigned long msdiff(struct timeval *t1, struct timeval *t2)
621 +{
622 +       return (t2->tv_sec - t1->tv_sec) * 1000L
623 +               + (t2->tv_usec - t1->tv_usec) / 1000;
624 +}
625 +
626 +/**
627 + * A non-interactive callback to be passed to SSL_CTX_set_default_password_cb,
628 + * which merely copies the value of ssl_key_passwd into buf. This is
629 + * used for when the private key password is supplied via an option.
630 + */
631 +static int default_password_cb(char *buf, int n, UNUSED(int f), UNUSED(void *u))
632 +{
633 +       if (ssl_key_passwd == NULL || n < (int)strlen(ssl_key_passwd))
634 +               return 0;
635 +       strncpy(buf, ssl_key_passwd, n-1);
636 +       return strlen(ssl_key_passwd);
637 +}
638 +
639 +/**
640 + * If verbose, this method traces the status of the SSL handshake.
641 + */
642 +static void info_callback(const SSL *ssl, int cb, int val)
643 +{
644 +       char buf[128];
645 +       char *cbs;
646 +
647 +       switch (cb) {
648 +       case SSL_CB_LOOP:
649 +               cbs = "SSL_CB_LOOP";
650 +               break;
651 +       case SSL_CB_EXIT:
652 +               cbs = "SSL_CB_EXIT";
653 +               break;
654 +       case SSL_CB_READ:
655 +               cbs = "SSL_CB_READ";
656 +               break;
657 +       case SSL_CB_WRITE:
658 +               cbs = "SSL_CB_WRITE";
659 +               break;
660 +       case SSL_CB_ALERT:
661 +               cbs = "SSL_CB_ALERT";
662 +               break;
663 +       case SSL_CB_READ_ALERT:
664 +               cbs = "SSL_CB_READ_ALERT";
665 +               break;
666 +       case SSL_CB_WRITE_ALERT:
667 +               cbs = "SSL_CB_WRITE_ALERT";
668 +               break;
669 +       case SSL_CB_ACCEPT_LOOP:
670 +               cbs = "SSL_CB_ACCEPT_LOOP";
671 +               break;
672 +       case SSL_CB_ACCEPT_EXIT:
673 +               cbs = "SSL_CB_ACCEPT_EXIT";
674 +               break;
675 +       case SSL_CB_CONNECT_LOOP:
676 +               cbs = "SSL_CB_CONNECT_LOOP";
677 +               break;
678 +       case SSL_CB_CONNECT_EXIT:
679 +               cbs = "SSL_CB_CONNECT_EXIT";
680 +               break;
681 +       case SSL_CB_HANDSHAKE_START:
682 +               cbs = "SSL_CB_HANDSHAKE_START";
683 +               break;
684 +       case SSL_CB_HANDSHAKE_DONE:
685 +               cbs = "SSL_CB_HANDSHAKE_DONE";
686 +               break;
687 +       default:
688 +               snprintf(buf, sizeof buf, "??? (%d)", cb);
689 +               cbs = buf;
690 +               break;
691 +       }
692 +       if (DEBUG_GTE(CONNECT, 1)) {
693 +               rprintf(FLOG, "SSL: info_callback(%p,%s,%d)\n", ssl, cbs, val);
694 +               if (cb == SSL_CB_HANDSHAKE_DONE) {
695 +                       SSL_CIPHER_description(SSL_get_current_cipher((SSL*)ssl),
696 +                                              buf, sizeof buf);
697 +                       rprintf(FLOG, "SSL: cipher: %s", buf);
698 +               }
699 +       }
700 +}
701 +
702 +/**
703 + * Initializes the SSL context for TLSv1 connections; returns zero on
704 + * success.
705 + */
706 +int init_tls(void)
707 +{
708 +       if (ssl_ctx)
709 +               return 0;
710 +       SSL_library_init();
711 +       SSL_load_error_strings();
712 +       ssl_ctx = SSL_CTX_new(TLSv1_method());
713 +       if (!ssl_ctx)
714 +               return 1;
715 +       SSL_CTX_set_info_callback(ssl_ctx, info_callback);
716 +
717 +       SSL_CTX_set_options(ssl_ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
718 +
719 +       /* Sets the certificate sent to the other party. */
720 +       if (ssl_cert_path != NULL
721 +           && SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert_path,
722 +                                           SSL_FILETYPE_PEM) != 1)
723 +               return 1;
724 +       /* Set up the simple non-interactive callback if the password
725 +        * was supplied on the command line. */
726 +       if (ssl_key_passwd != NULL)
727 +               SSL_CTX_set_default_passwd_cb(ssl_ctx, default_password_cb);
728 +       /* Sets the private key that matches the public certificate. */
729 +       if (ssl_key_path != NULL) {
730 +               if (SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_key_path,
731 +                                               SSL_FILETYPE_PEM) != 1)
732 +                       return 1;
733 +               if (SSL_CTX_check_private_key(ssl_ctx) != 1)
734 +                       return 1;
735 +       }
736 +       if (ssl_ca_path != NULL
737 +           && !SSL_CTX_load_verify_locations(ssl_ctx, ssl_ca_path, NULL))
738 +               return 1;
739 +
740 +       return 0;
741 +}
742 +
743 +/**
744 + * Returns the error string for the current SSL error, if any.
745 + */
746 +char *get_ssl_error(void)
747 +{
748 +       return ERR_error_string(ERR_get_error(), NULL);
749 +}
750 +
751 +/**
752 + * Returns the input file descriptor for the SSL connection.
753 + */
754 +int get_tls_rfd(void)
755 +{
756 +       return tls_read[0];
757 +}
758 +
759 +/**
760 + * Returns the output file descriptor for the SSL connection.
761 + */
762 +int get_tls_wfd(void)
763 +{
764 +       return tls_write[1];
765 +}
766 +
767 +/**
768 + * Signal handler that ends the SSL connection.
769 + */
770 +static RETSIGTYPE tls_sigusr1(int UNUSED(val))
771 +{
772 +       ssl_running = 0;
773 +}
774 +
775 +/* Signal handler that starts record-size buffering. */
776 +static RETSIGTYPE tls_sigusr2(int UNUSED(val))
777 +{
778 +       ssl_min_send_size = SSL_MAX_RECORD_SIZE;
779 +}
780 +
781 +/**
782 + * Negotiates the TLS connection, creates a socket pair for communicating
783 + * with the rsync process, then forks into a new process that will handle
784 + * the communication.
785 + *
786 + * 0 is returned on success.
787 + * rsync to network algorithm:
788 + * A buffer ring of two buffers is maintained. Data is read from the local
789 + * socket into the current buffer. The buffer is eligible for sending if
790 + * enough data has been buffered, or enough time has passed. Once the buffer
791 + * becomes eligible for sending it is marked "ready" and buffering continues
792 + * on the other buffer (assuming it's "unready"). This algorithm tries to
793 + * maximize SSL record size.
794 + *
795 + * network to rsync algorithm:
796 + * One receive buffer twice the size of the SSL max record size. Reading
797 + * from the network is attempted only if there's enough space for a whole
798 + * record (this is done to optimize OpenSSL per-call overhead).
799 + * As bytes enter the buffer sending to rsync begins, when the buffer becomes
800 + * full we wait for sending to rsync to finish.
801 + *
802 + * A graceful stop is attempted on both directions. So if the pipe from rsync
803 + * is gracefully shut down, we try to send all data to the network before closing
804 + * the connection and if the SSL connection is gracefully shut down we try to
805 + * send everything to rsync before closing the rsync pipe.
806 + */
807 +int start_tls(int f_in, int f_out)
808 +{
809 +       int n = 0, r;
810 +       unsigned char buf_tonetwork[2][SSL_MAX_RECORD_SIZE], buf_fromnetwork[2*SSL_MAX_RECORD_SIZE];
811 +       int tonet_ready[2] = {0}, tonet_size[2] = {0}, write_tonet = 0, tonet_fill_idx = 0, tonet_send_idx = 0;
812 +       struct timeval buffering_start, now, timeout, *tp;
813 +       int avail_fromnet = 0, write_fromnet = 0;
814 +       int want_read_fromnet = 1, want_write_tonet = 0;
815 +       int want_read_fromrsync = 1, want_write_torsync = 0;
816 +       int write_tonet_blocks = 0, read_fromnet_blocks = 0;
817 +       int write_torsync_blocks = 0, read_fromrsync_blocks = 0;
818 +       int ssl_readfd_blocks = 0, ssl_writefd_blocks = 0;
819 +       int ssl_write_count = 0;
820 +       int want_more_io;
821 +       int net_closed = 0, rsync_closed = 0;
822 +       int loopcount;
823 +       fd_set rd, wd;
824 +       const char *ssl_ciphers;
825 +
826 +       if (fd_pair(tls_read))
827 +               return 1;
828 +       if (fd_pair(tls_write))
829 +               return 1;
830 +
831 +       set_blocking(tls_read[0]);
832 +       set_nonblocking(tls_read[1]);
833 +       set_nonblocking(tls_write[0]);
834 +       set_blocking(tls_write[1]);
835 +       set_nonblocking(f_in);
836 +       set_nonblocking(f_out);
837 +
838 +       ssl_pid = do_fork();
839 +       if (ssl_pid < 0)
840 +               return -1;
841 +       if (ssl_pid != 0) {
842 +               close(tls_write[0]);
843 +               close(tls_read[1]);
844 +               close(f_in);
845 +               close(f_out);
846 +               return 0;
847 +       }
848 +
849 +       close(tls_write[1]);
850 +       close(tls_read[0]);
851 +
852 +       SIGACTION(SIGUSR1, tls_sigusr1);
853 +       SIGACTION(SIGUSR2, tls_sigusr2);
854 +       ssl = SSL_new(ssl_ctx);
855 +       if (!ssl)
856 +               goto out;
857 +       if (am_daemon || am_server)
858 +               SSL_set_accept_state(ssl);
859 +       else {
860 +               SSL_set_connect_state(ssl);
861 +               ssl_ciphers = getenv("RSYNC_SSL_CIPHERS");
862 +               if (ssl_ciphers)
863 +                       SSL_set_cipher_list(ssl, ssl_ciphers);
864 +       }
865 +
866 +       SSL_set_rfd(ssl, f_in);
867 +       SSL_set_wfd(ssl, f_out);
868 +
869 +       n = tls_write[0];
870 +       n = MAX(tls_read[1], n);
871 +       n = MAX(f_in, n);
872 +       n = MAX(f_out, n) + 1;
873 +
874 +       ssl_running = 1;
875 +       loopcount = 0;
876 +       while (ssl_running) {
877 +               ++loopcount; /* the loopcount prevents starvation of one transfer direction by the other */
878 +               want_more_io = 0;
879 +
880 +               if (want_read_fromrsync && !read_fromrsync_blocks) {
881 +                       int cur_buf_size = tonet_size[tonet_fill_idx];
882 +                       r = read(tls_write[0], buf_tonetwork[tonet_fill_idx]+cur_buf_size, sizeof(buf_tonetwork[0])-cur_buf_size);
883 +                       if (r > 0) {
884 +                               want_more_io = 1;
885 +                               if (!net_closed) {
886 +                                       if (cur_buf_size == 0)
887 +                                               gettimeofday(&buffering_start, NULL);
888 +                                       cur_buf_size += r;
889 +                                       tonet_size[tonet_fill_idx] = cur_buf_size;
890 +                                       if (cur_buf_size >= ssl_min_send_size) {
891 +                                               want_write_tonet = 1;
892 +                                               tonet_ready[tonet_fill_idx] = 1;
893 +                                               tonet_fill_idx = !tonet_fill_idx;
894 +                                               if (tonet_ready[tonet_fill_idx])
895 +                                                       want_read_fromrsync = 0;
896 +                                       }
897 +                               }
898 +                       } else if (r < 0 && errno == EWOULDBLOCK)
899 +                               read_fromrsync_blocks = 1;
900 +                       else if (r < 0) {
901 +                               rprintf(FERROR, "pipe read error: %s\n",
902 +                                       strerror(errno));
903 +                               break;
904 +                       } else {
905 +                               rsync_closed = 1;
906 +                               if (cur_buf_size) {
907 +                                       want_write_tonet = 1;
908 +                                       tonet_ready[tonet_fill_idx] = 1; /* close current buffer */
909 +                               }
910 +                               want_read_fromrsync = 0; /* don't read */
911 +                               want_write_torsync = 0; /* don't write */
912 +                               read_fromrsync_blocks = 0; /* don't select */
913 +                               write_torsync_blocks = 0;
914 +                               want_read_fromnet = 1; /* purge incoming data from network */
915 +                               avail_fromnet = 0;
916 +                               if (net_closed || !want_write_tonet)
917 +                                       break;
918 +                       }
919 +               }
920 +
921 +               if (want_read_fromnet && !read_fromnet_blocks) {
922 +                       r = SSL_read(ssl, buf_fromnetwork+avail_fromnet, SSL_MAX_RECORD_SIZE);
923 +                       if (r > 0) {
924 +                               want_more_io = 1;
925 +                               if (!rsync_closed) {
926 +                                       avail_fromnet += r;
927 +                                       want_write_torsync = 1;
928 +                                       if (avail_fromnet >= (int)(sizeof(buf_fromnetwork)-SSL_MAX_RECORD_SIZE))
929 +                                               want_read_fromnet = 0;
930 +                               }
931 +                       } else {
932 +                               switch (SSL_get_error(ssl, r)) {
933 +                               case SSL_ERROR_ZERO_RETURN:
934 +                                       net_closed = 1;
935 +                                       want_read_fromnet = 0; /* don't read */
936 +                                       want_write_tonet = 0; /* don't write */
937 +                                       ssl_readfd_blocks = 0; /* and for heaven's sake - don't select() */
938 +                                       ssl_writefd_blocks = 0;
939 +                                       want_read_fromrsync = 1; /* and purge stuff from rsync side */
940 +                                       tonet_size[tonet_fill_idx] = 0;
941 +                                       tonet_ready[tonet_fill_idx] = 0;
942 +                                       if (rsync_closed || !want_write_torsync)
943 +                                               goto graceful_stop;
944 +                                       break;
945 +                               case SSL_ERROR_WANT_READ:
946 +                                       ssl_readfd_blocks = 1;
947 +                                       read_fromnet_blocks = 1;
948 +                                       break;
949 +                               case SSL_ERROR_WANT_WRITE:
950 +                                       ssl_writefd_blocks = 1;
951 +                                       read_fromnet_blocks = 1;
952 +                                       break;
953 +                               case SSL_ERROR_SYSCALL:
954 +                                       if (r == 0)
955 +                                               rprintf(FERROR, "SSL spurious EOF\n");
956 +                                       else
957 +                                               rprintf(FERROR, "SSL I/O error: %s\n",
958 +                                                       strerror(errno));
959 +                                       goto closed;
960 +                               case SSL_ERROR_SSL:
961 +                                       rprintf(FERROR, "SSL %s\n",
962 +                                               ERR_error_string(ERR_get_error(), NULL));
963 +                                       goto closed;
964 +                               default:
965 +                                       rprintf(FERROR, "unexpected ssl error %d\n", r);
966 +                                       goto closed;
967 +                               }
968 +                       }
969 +               }
970 +
971 +               if (want_write_torsync && !write_torsync_blocks) {
972 +                       r = write(tls_read[1], buf_fromnetwork+write_fromnet, avail_fromnet-write_fromnet);
973 +                       if (r > 0) {
974 +                               want_more_io = 1;
975 +                               write_fromnet += r;
976 +                               if (write_fromnet >= avail_fromnet) {
977 +                                       write_fromnet = 0;
978 +                                       avail_fromnet = 0;
979 +                                       if (net_closed)
980 +                                               break;
981 +                                       want_read_fromnet = 1;
982 +                                       want_write_torsync = 0;
983 +                               }
984 +                       }
985 +                       else if (errno == EWOULDBLOCK)
986 +                               write_torsync_blocks = 1;
987 +                       else {
988 +                               if (errno != EPIPE)
989 +                                       rprintf(FERROR, "pipe write error: %s\n", strerror(errno));
990 +                               break;
991 +                       }
992 +               }
993 +
994 +               if (want_write_tonet && !write_tonet_blocks) {
995 +                       /* lock the write count, 'cause if SSL_write fails on non-blocking IO, it
996 +                        * must be repeated with same parameters. */
997 +                       if (ssl_write_count == 0)
998 +                               ssl_write_count = tonet_size[tonet_send_idx] - write_tonet;
999 +                       r = SSL_write(ssl, buf_tonetwork[tonet_send_idx]+write_tonet, ssl_write_count);
1000 +                       if (r > 0) {
1001 +                               want_more_io = 1;
1002 +                               write_tonet += r;
1003 +                               ssl_write_count = 0;
1004 +                               if (write_tonet >= tonet_size[tonet_send_idx]) {
1005 +                                       write_tonet = 0;
1006 +                                       tonet_size[tonet_send_idx] = 0;
1007 +                                       tonet_ready[tonet_send_idx] = 0;
1008 +                                       if (!rsync_closed)
1009 +                                               want_read_fromrsync = 1;
1010 +                                       tonet_send_idx = !tonet_send_idx;
1011 +                                       if (!tonet_ready[tonet_send_idx]) {
1012 +                                               want_write_tonet = 0;
1013 +                                               if (rsync_closed)
1014 +                                                       break;
1015 +                                       }
1016 +                               }
1017 +                       } else {
1018 +                               switch (SSL_get_error(ssl, r)) {
1019 +                               case SSL_ERROR_ZERO_RETURN:
1020 +                                       goto graceful_stop;
1021 +                               case SSL_ERROR_WANT_READ:
1022 +                                       ssl_readfd_blocks = 1;
1023 +                                       write_tonet_blocks = 1;
1024 +                                       break;
1025 +                               case SSL_ERROR_WANT_WRITE:
1026 +                                       ssl_writefd_blocks = 1;
1027 +                                       write_tonet_blocks = 1;
1028 +                                       break;
1029 +                               case SSL_ERROR_SYSCALL:
1030 +                                       if (r == 0)
1031 +                                               rprintf(FERROR, "SSL: spurious EOF\n");
1032 +                                       else
1033 +                                               rprintf(FERROR, "SSL: I/O error: %s\n", strerror(errno));
1034 +                                       goto closed;
1035 +                               case SSL_ERROR_SSL:
1036 +                                       rprintf(FERROR, "SSL: %s\n",
1037 +                                               ERR_error_string(ERR_get_error(), NULL));
1038 +                                       goto closed;
1039 +                               default:
1040 +                                       rprintf(FERROR, "unexpected ssl error %d\n", r);
1041 +                                       goto closed;
1042 +                               }
1043 +                       }
1044 +               }
1045 +
1046 +               if (!want_more_io || (loopcount&31) == 0) {
1047 +                       int has_buffered = 0;
1048 +                       if (!net_closed && !tonet_ready[tonet_fill_idx] && tonet_size[tonet_fill_idx]) {
1049 +                               has_buffered = 1;
1050 +                               unsigned long buffering_time;
1051 +                               gettimeofday(&now,NULL);
1052 +                               buffering_time = msdiff(&buffering_start,&now);
1053 +                               if (buffering_time >= MAX_BUFFERING_TIME_MS) {
1054 +                                       want_write_tonet = 1;
1055 +                                       want_more_io = 1;
1056 +                                       tonet_ready[tonet_fill_idx] = 1;
1057 +                                       tonet_fill_idx = !tonet_fill_idx;
1058 +                                       if (tonet_ready[tonet_fill_idx])
1059 +                                               want_read_fromrsync = 0;
1060 +                               }
1061 +                       }
1062 +
1063 +                       int waiting_on_something = 0;
1064 +                       FD_ZERO(&rd);
1065 +                       FD_ZERO(&wd);
1066 +                       if (read_fromrsync_blocks) {
1067 +                               waiting_on_something = 1;
1068 +                               FD_SET(tls_write[0], &rd);
1069 +                       }
1070 +                       if (write_torsync_blocks) {
1071 +                               waiting_on_something = 1;
1072 +                               FD_SET(tls_read[1], &wd);
1073 +                       }
1074 +                       if (ssl_readfd_blocks) {
1075 +                               waiting_on_something = 1;
1076 +                               FD_SET(f_in, &rd);
1077 +                       }
1078 +                       if (ssl_writefd_blocks) {
1079 +                               waiting_on_something = 1;
1080 +                               FD_SET(f_out, &wd);
1081 +                       }
1082 +
1083 +                       if (want_more_io) {
1084 +                               /* just poll to see if some sockets became non-blocking*/
1085 +                               timeout.tv_sec = 0;
1086 +                               timeout.tv_usec = 0;
1087 +                               tp = &timeout;
1088 +                       } else if (waiting_on_something && !has_buffered)
1089 +                               tp = NULL; /*infinite wait until a socket becomes available*/
1090 +                       else {
1091 +                               timeout.tv_sec = MAX_BUFFERING_TIME_MS/1000;
1092 +                               timeout.tv_usec = (MAX_BUFFERING_TIME_MS%1000)*1000;
1093 +                               tp = &timeout;
1094 +                       }
1095 +
1096 +                       if (waiting_on_something || !want_more_io) {
1097 +                               r = select(n, &rd, &wd, NULL, tp);
1098 +
1099 +                               if (r == -1) {
1100 +                                       if (errno != EINTR) {
1101 +                                               rprintf(FERROR, "select error: %s\n",
1102 +                                                       strerror(errno));
1103 +                                               break;
1104 +                                       }
1105 +                               }
1106 +
1107 +                               if (r > 0) {
1108 +                                       if (FD_ISSET(tls_write[0], &rd))
1109 +                                               read_fromrsync_blocks = 0;
1110 +                                       if (FD_ISSET(f_in,&rd)) {
1111 +                                               read_fromnet_blocks = 0;
1112 +                                               write_tonet_blocks = 0;
1113 +                                               ssl_readfd_blocks = 0;
1114 +                                       }
1115 +                                       if (FD_ISSET(tls_read[1],&wd))
1116 +                                               write_torsync_blocks = 0;
1117 +                                       if (FD_ISSET(f_out,&wd)) {
1118 +                                               read_fromnet_blocks = 0;
1119 +                                               write_tonet_blocks = 0;
1120 +                                               ssl_writefd_blocks = 0;
1121 +                                       }
1122 +                               }
1123 +                       }
1124 +               }
1125 +       }
1126 +
1127 +  graceful_stop:
1128 +       /* The SSL shutdown API looks pretty broken with respect to corner cases
1129 +        * where the underlying socket is blocking. This is a "best effort". */
1130 +       if (SSL_shutdown(ssl) != 1)
1131 +               SSL_shutdown(ssl);
1132 +  closed:
1133 +       SSL_free(ssl);
1134 +
1135 +       /* We're finished. */
1136 +  out:
1137 +       close(tls_read[1]);
1138 +       close(tls_write[0]);
1139 +       close(f_in);
1140 +       if (f_out != f_in)
1141 +               close(f_out);
1142 +       _exit(0);
1143 +}
1144 +
1145 +void start_tls_buffering(void)
1146 +{
1147 +       if (ssl_pid > 0)
1148 +               kill(ssl_pid, SIGUSR2);
1149 +}
1150 +
1151 +/**
1152 + * Ends the TLS connection.
1153 + */
1154 +void end_tls(void)
1155 +{
1156 +       if (ssl_pid > 0) {
1157 +               int status;
1158 +               /* try a graceful stop - this makes sure all data is sent, and
1159 +                * also causes our side to send a close_notify alert, as required
1160 +                * by TLS specs. */
1161 +               close(get_tls_rfd());
1162 +               if (get_tls_wfd() != get_tls_rfd())
1163 +                       close(get_tls_wfd());
1164 +               terminate_process(ssl_pid, &status, SIGUSR1, 1);
1165 +               ssl_pid = -1;
1166 +       }
1167 +}