summarylogtreecommitdiffstats
path: root/0008-rsync-protocol.patch
blob: e1694ee0adc33851d2544e738e38a060f35f1c42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
From: Russ Allbery <rra@stanford.edu>
Subject: [PATCH] Handle the rsync v3 -e option for protocol information

As of rsync 3, rsync reused the -e option to pass protocol information
from the client to the server.  We therefore cannot reject all -e
options to rsync, only ones not sent with --server or containing
something other than protocol information as an argument.

Also scan the rsync command line for any --rsh option and reject it as
well.  This replaces and improves the upstream strategy for rejecting
that command-line option, taking advantage of the parsing added to
check the -e option.

Based on work by Robert Hardy.

Debian Bug#471803

Signed-off-by: Russ Allbery <rra@stanford.edu>

---
 util.c |   80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 72 insertions(+), 8 deletions(-)

diff --git a/util.c b/util.c
index f98d2bc..a257b06 100644
--- a/util.c
+++ b/util.c
@@ -56,6 +56,7 @@
 #ifdef HAVE_LIBGEN_H
 #include <libgen.h>
 #endif /* HAVE_LIBGEN_H */
+#include <regex.h>
 
 /* LOCAL INCLUDES */
 #include "pathnames.h"
@@ -198,6 +199,73 @@ bool check_command( char *cl, ShellOptions_t *opts, char *cmd, int cmdflag )
 
 
 /*
+ * rsync_e_okay() - take the command line passed to rssh and look for an -e
+ *		    option.  If one is found, make sure --server is provided
+ *		    and the option contains only the protocol information.
+ *		    Also check for and reject any --rsh option.	 Returns FALSE
+ *		    if the command line should not be allowed, TRUE if it is
+ *		    okay.
+ */
+static int rsync_e_okay( char **vec )
+{
+	regex_t	re;
+	int	server = FALSE;
+	int	e_found = FALSE;
+
+	/*
+	 * rsync will send -e, followed by either just "." (meaning no special
+	 * protocol) or "N.N" (meaning a pre-release protocol version),
+	 * followed by some number of alphabetic flags indicating various
+	 * supported options.  There may be other options between - and the e,
+	 * but -e will always be the last option in the string.	 A typical
+	 * option passed by the client is "-ltpre.iL".
+	 *
+	 * Note that if --server is given, this should never be parsed as a
+	 * shell, but we'll tightly verify it anyway, just in case.
+	 *
+	 * This regex matches the acceptable flags containing -e, so if it
+	 * does not match, the command line should be rejected.
+	 */
+	static const char pattern[]
+	    = "^-[a-df-zA-Z]*e[0-9]*\\.[0-9]*[a-zA-Z]*$";
+
+	/*
+	 * Only recognize --server if it's the first option.  rsync itself
+	 * always passes it that way, and if it's not the first argument, it
+	 * could be hidden from the server as an argument to some other
+	 * option.
+	 */
+	if ( vec && vec[0] && vec[1] && strcmp(vec[1], "--server") == 0 ){
+		server = TRUE;
+	}
+
+	/* Check the remaining options for -e or --rsh. */
+	if ( regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB) != 0 ){
+		return FALSE;
+	}
+	while (vec && *vec){
+		if ( strcmp(*vec, "--") == 0 ) break;
+		if ( strcmp(*vec, "--rsh") == 0
+		     || strncmp(*vec, "--rsh=", strlen("--rsh=")) == 0 ){
+			regfree(&re);
+			return FALSE;
+		}
+		if ( strncmp(*vec, "--", 2) != 0 && opt_exist(*vec, 'e') ){
+			e_found = TRUE;
+			if ( regexec(&re, *vec, 0, NULL, 0) != 0 ){
+				regfree(&re);
+				return FALSE;
+			}
+		}
+		vec++;
+	}
+	regfree(&re);
+	if ( e_found && !server ) return FALSE;
+	return TRUE;
+}
+
+
+/*
  * check_command_line() - take the command line passed to rssh, and verify
  *			  that the specified command is one the user is
  *			  allowed to run and validate the arguments.  Return the
@@ -230,14 +298,10 @@ char *check_command_line( char **cl, ShellOptions_t *opts )
 
 	if ( check_command(*cl, opts, PATH_RSYNC, RSSH_ALLOW_RSYNC) ){
 		/* filter -e option */
-		if ( opt_filter(cl, 'e') ) return NULL;
-		while (cl && *cl){
-			if ( strstr(*cl, "--rsh" ) ){
-				fprintf(stderr, "\ninsecure --rsh= not allowed.");
-				log_msg("insecure --rsh option in rsync command line!");
-				return NULL;
-			}
-			cl++;
+		if ( !rsync_e_okay(cl) ){
+			fprintf(stderr, "\ninsecure -e or --rsh option not allowed.");
+			log_msg("insecure -e or --rsh option in rsync command line!");
+			return NULL;
 		}
 		return PATH_RSYNC;
 	}
-- 
tg: (f8b36e2..) fixes/rsync-protocol (depends on: upstream)