From: Jeff Mahoney <jeffm@suse.com>
Subject: NAT/FTP: Fix broken conntrack
References: bnc#681639 bnc#466279
Patch-mainline: Submitted 14 Apr 2011

 patches.fixes/netfilter-implement-rfc-1123-for-ftp-conntrack contains
 a bug where the string is correctly found but the parameters describing
 its location aren't properly passed to the caller.

 This results in bad NAT transformations like the following:
 Using: 227 Entering Passive Mode (10,237,125,21,61,95)
 BROKEN: ***Passive Mode (10,23***
 FIXED:  ***10,237,125,21,61,95***

 Using: 227 Data transfer will passively listen to 67,218,99,134,50,144
 BROKEN: ***transfer will passiv***
 FIXED:  ***67,218,99,134,50,144***

 The important part is that the string in ***'s is overwritten by the NAT
 code, so the FTP response is completely mangled.

 This patch properly returns the updated offset.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 net/netfilter/nf_conntrack_ftp.c |   47 +++++++++++++++++++++++----------------
 1 file changed, 28 insertions(+), 19 deletions(-)

--- a/net/netfilter/nf_conntrack_ftp.c
+++ b/net/netfilter/nf_conntrack_ftp.c
@@ -52,11 +52,14 @@ unsigned int (*nf_nat_ftp_hook)(struct s
 				struct nf_conntrack_expect *exp);
 EXPORT_SYMBOL_GPL(nf_nat_ftp_hook);
 
-static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, char);
-static int try_rfc1123(const char *, size_t, struct nf_conntrack_man *, char);
-static int try_eprt(const char *, size_t, struct nf_conntrack_man *, char);
+static int try_rfc959(const char *, size_t, struct nf_conntrack_man *,
+		      char, unsigned int *);
+static int try_rfc1123(const char *, size_t, struct nf_conntrack_man *,
+		       char, unsigned int *);
+static int try_eprt(const char *, size_t, struct nf_conntrack_man *,
+		    char, unsigned int *);
 static int try_epsv_response(const char *, size_t, struct nf_conntrack_man *,
-			     char);
+			     char, unsigned int *);
 
 static struct ftp_search {
 	const char *pattern;
@@ -64,7 +67,7 @@ static struct ftp_search {
 	char skip;
 	char term;
 	enum nf_ct_ftp_type ftptype;
-	int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char);
+	int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char, unsigned int *);
 } search[IP_CT_DIR_MAX][2] = {
 	[IP_CT_DIR_ORIGINAL] = {
 		{
@@ -88,8 +91,6 @@ static struct ftp_search {
 		{
 			.pattern	= "227 ",
 			.plen		= sizeof("227 ") - 1,
-			.skip		= ' ',
-			.term		= '\0',
 			.ftptype	= NF_CT_FTP_PASV,
 			.getnum		= try_rfc1123,
 		},
@@ -147,7 +148,8 @@ static int try_number(const char *data,
 
 /* Returns 0, or length of numbers: 192,168,1,1,5,6 */
 static int try_rfc959(const char *data, size_t dlen,
-		      struct nf_conntrack_man *cmd, char term)
+		      struct nf_conntrack_man *cmd, char term,
+		      unsigned int *offset)
 {
 	int length;
 	u_int32_t array[6];
@@ -173,7 +175,8 @@ static int try_rfc959(const char *data,
  * of the host and port numbers.
  */
 static int try_rfc1123(const char *data, size_t dlen,
-		       struct nf_conntrack_man *cmd, char term)
+		       struct nf_conntrack_man *cmd, char term,
+		       unsigned int *offset)
 {
 	int i;
 	for (i = 0; i < dlen; i++)
@@ -183,7 +186,9 @@ static int try_rfc1123(const char *data,
 	if (i == dlen)
 		return 0;
 
-	return try_rfc959(data + i, dlen - i, cmd, 0);
+	*offset += i;
+
+	return try_rfc959(data + i, dlen - i, cmd, 0, offset);
 }
 
 /* Grab port: number up to delimiter */
@@ -214,7 +219,7 @@ static int get_port(const char *data, in
 
 /* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */
 static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd,
-		    char term)
+		    char term, unsigned int *offset)
 {
 	char delim;
 	int length;
@@ -262,7 +267,8 @@ static int try_eprt(const char *data, si
 
 /* Returns 0, or length of numbers: |||6446| */
 static int try_epsv_response(const char *data, size_t dlen,
-			     struct nf_conntrack_man *cmd, char term)
+			     struct nf_conntrack_man *cmd, char term,
+			     unsigned int *offset)
 {
 	char delim;
 
@@ -284,9 +290,10 @@ static int find_pattern(const char *data
 			unsigned int *numlen,
 			struct nf_conntrack_man *cmd,
 			int (*getnum)(const char *, size_t,
-				      struct nf_conntrack_man *, char))
+				      struct nf_conntrack_man *, char,
+				      unsigned int *))
 {
-	size_t i;
+	size_t i = plen;
 
 	pr_debug("find_pattern `%s': dlen = %Zu\n", pattern, dlen);
 	if (dlen == 0)
@@ -316,16 +323,18 @@ static int find_pattern(const char *data
 	pr_debug("Pattern matches!\n");
 	/* Now we've found the constant string, try to skip
 	   to the 'skip' character */
-	for (i = plen; data[i] != skip; i++)
-		if (i == dlen - 1) return -1;
+	if (skip) {
+		for (i = plen; data[i] != skip; i++)
+			if (i == dlen - 1) return -1;
 
-	/* Skip over the last character */
-	i++;
+		/* Skip over the last character */
+		i++;
+	}
 
 	pr_debug("Skipped up to `%c'!\n", skip);
 
 	*numoff = i;
-	*numlen = getnum(data + i, dlen - i, cmd, term);
+	*numlen = getnum(data + i, dlen - i, cmd, term, numoff);
 	if (!*numlen)
 		return -1;
 
