On 8/1/25 2:38 PM, Mike Pattrick via dev wrote: > Recent versions of Curl combine the EPSV and EPRT commands with IPv4 > connections instead of the PASV and PORT commands. This patch adds > support for these extended commands. > > Reported-at: https://issues.redhat.com/browse/FDP-907 > Reported-by: Eelco Chaudron <echau...@redhat.com> > Signed-off-by: Mike Pattrick <m...@redhat.com> > --- > lib/conntrack.c | 189 +++++++++++++++++++++++----------- > tests/system-common-macros.at | 4 +- > 2 files changed, 133 insertions(+), 60 deletions(-)
Thanks, Mike! I'll leave the core of the patch for Aaron or Eelco to review, but see a few comments below. First, we probably need to add a NEWS entry for this. And maybe expand the commit message a bit explaining what these commands are. > > diff --git a/lib/conntrack.c b/lib/conntrack.c > index 00346a0be..cb8bda306 100644 > --- a/lib/conntrack.c > +++ b/lib/conntrack.c > @@ -192,6 +192,8 @@ static alg_helper alg_helpers[] = { > #define FTP_PORT_CMD "PORT" > /* FTP pasv string used in passive mode. */ > #define FTP_PASV_REPLY_CODE "227" > +/* FTP epsv string used in passive mode. */ > +#define FTP_EPSV_REPLY_CODE "229" > /* Maximum decimal digits for port in FTP command. > * The port is represented as two 3 digit numbers with the > * high part a multiple of 256. */ > @@ -203,6 +205,8 @@ static alg_helper alg_helpers[] = { > #define FTP_EPSV_REPLY "EXTENDED PASSIVE" > /* Maximum decimal digits for port in FTP extended command. */ > #define MAX_EXT_FTP_PORT_DGTS 5 > +/* FTP extended command code for IPv4. */ > +#define FTP_AF_V4 '1' > /* FTP extended command code for IPv6. */ > #define FTP_AF_V6 '2' > /* Used to indicate a wildcard L4 source port number for ALGs. > @@ -3269,10 +3273,16 @@ static int > repl_ftp_v4_addr(struct dp_packet *pkt, ovs_be32 v4_addr_rep, > char *ftp_data_start, > size_t addr_offset_from_ftp_data_start, > - size_t addr_size OVS_UNUSED) > + size_t addr_size) > { > enum { MAX_FTP_V4_NAT_DELTA = 8 }; > > + /* EPSV mode */ nit: Period at the end. > + if (addr_offset_from_ftp_data_start == 0 && > + addr_size == 0) { > + return 0; > + } > + > /* Do conservative check for pathological MTU usage. */ > uint32_t orig_used_size = dp_packet_size(pkt); > if (orig_used_size + MAX_FTP_V4_NAT_DELTA > > @@ -3345,8 +3355,11 @@ detect_ftp_ctl_type(const struct conn_lookup_ctx *ctx, > } > } else { > if (strncasecmp(ftp_msg, FTP_PORT_CMD, strlen(FTP_PORT_CMD)) && > + strncasecmp(ftp_msg, FTP_EPRT_CMD, strlen(FTP_EPRT_CMD)) && > strncasecmp(ftp_msg, FTP_PASV_REPLY_CODE, > - strlen(FTP_PASV_REPLY_CODE))) { > + strlen(FTP_PASV_REPLY_CODE)) && > + strncasecmp(ftp_msg, FTP_EPSV_REPLY_CODE, > + strlen(FTP_EPSV_REPLY_CODE))) { > return CT_FTP_CTL_OTHER; > } > } > @@ -3370,11 +3383,23 @@ process_ftp_ctl_v4(struct conntrack *ct, > char ftp_msg[LARGEST_FTP_MSG_OF_INTEREST + 1] = {0}; > get_ftp_ctl_msg(pkt, ftp_msg); > char *ftp = ftp_msg; > + struct in_addr ip_addr; > enum ct_alg_mode mode; > + bool extended = false; > + char delim = 0; > > if (!strncasecmp(ftp, FTP_PORT_CMD, strlen(FTP_PORT_CMD))) { > ftp = ftp_msg + strlen(FTP_PORT_CMD); > mode = CT_FTP_MODE_ACTIVE; > + } else if (!strncasecmp(ftp, FTP_EPRT_CMD, strlen(FTP_EPRT_CMD))) { > + ftp = ftp_msg + strlen(FTP_EPRT_CMD); > + mode = CT_FTP_MODE_ACTIVE; > + extended = true; > + } else if (!strncasecmp(ftp, FTP_EPSV_REPLY_CODE, > + strlen(FTP_EPSV_REPLY_CODE))) { > + ftp = ftp_msg + strlen(FTP_EPSV_REPLY_CODE); > + mode = CT_FTP_MODE_PASSIVE; > + extended = true; > } else { > ftp = ftp_msg + strlen(FTP_PASV_REPLY_CODE); > mode = CT_FTP_MODE_PASSIVE; > @@ -3392,71 +3417,117 @@ process_ftp_ctl_v4(struct conntrack *ct, > return CT_FTP_CTL_INVALID; > } > > - char *ip_addr_start = ftp; > - *addr_offset_from_ftp_data_start = ip_addr_start - ftp_msg; > + /* EPRT, skip address family. */ > + if (extended && mode == CT_FTP_MODE_ACTIVE) { > + if (*ftp != FTP_AF_V4) { > + return CT_FTP_CTL_INVALID; > + } > + delim = ftp[1]; > + ftp = skip_non_digits(ftp + 1); > + if (*ftp == 0) { > + return CT_FTP_CTL_INVALID; > + } > + } > + > + if (!extended || mode == CT_FTP_MODE_ACTIVE) { > + char *ip_addr_start = ftp; > + *addr_offset_from_ftp_data_start = ip_addr_start - ftp_msg; > > - uint8_t comma_count = 0; > - while (comma_count < 4 && *ftp) { > - if (*ftp == ',') { > - comma_count++; > - if (comma_count == 4) { > - *ftp = 0; > + uint8_t delim_count = 0; > + while (delim_count < 4 && *ftp) { > + if (extended) { > + if (*ftp == '.') { > + delim_count++; > + } else if (*ftp == delim) { > + delim_count++; > + *ftp = 0; > + ftp++; > + break; > + } > } else { > - *ftp = '.'; > + if (*ftp == ',') { > + delim_count++; > + if (delim_count == 4) { > + *ftp = 0; > + } else { > + *ftp = '.'; > + } > + } > } > + ftp++; > + } > + if (delim_count != 4) { > + return CT_FTP_CTL_INVALID; > } > - ftp++; > - } > - if (comma_count != 4) { > - return CT_FTP_CTL_INVALID; > - } > > - struct in_addr ip_addr; > - int rc2 = inet_pton(AF_INET, ip_addr_start, &ip_addr); > - if (rc2 != 1) { > - return CT_FTP_CTL_INVALID; > + int rc2 = inet_pton(AF_INET, ip_addr_start, &ip_addr); > + if (rc2 != 1) { > + return CT_FTP_CTL_INVALID; > + } > + > + *addr_size = ftp - ip_addr_start - 1; > + } else { > + *addr_size = 0; > + *addr_offset_from_ftp_data_start = 0; > } > > - *addr_size = ftp - ip_addr_start - 1; > char *save_ftp = ftp; > - ftp = terminate_number_str(ftp, MAX_FTP_PORT_DGTS); > - if (!ftp) { > - return CT_FTP_CTL_INVALID; > - } > - int value; > - if (!str_to_int(save_ftp, 10, &value)) { > - return CT_FTP_CTL_INVALID; > - } > + uint16_t port_hs; > > - /* This is derived from the L4 port maximum is 65535. */ > - if (value > 255) { > - return CT_FTP_CTL_INVALID; > - } > + if (!extended) { > + ftp = terminate_number_str(ftp, MAX_FTP_PORT_DGTS); > + if (!ftp) { > + return CT_FTP_CTL_INVALID; > + } > + int value; > + if (!str_to_int(save_ftp, 10, &value)) { > + return CT_FTP_CTL_INVALID; > + } > > - uint16_t port_hs = value; > - port_hs <<= 8; > + /* This is derived from the L4 port maximum is 65535. */ > + if (value > 255) { > + return CT_FTP_CTL_INVALID; > + } > > - /* Skip over comma. */ > - ftp++; > - save_ftp = ftp; > - bool digit_found = false; > - while (isdigit(*ftp)) { > + port_hs = value; > + port_hs <<= 8; > + > + /* Skip over comma. */ > ftp++; > - digit_found = true; > - } > - if (!digit_found) { > - return CT_FTP_CTL_INVALID; > - } > - *ftp = 0; > - if (!str_to_int(save_ftp, 10, &value)) { > - return CT_FTP_CTL_INVALID; > - } > + save_ftp = ftp; > + bool digit_found = false; > + while (isdigit(*ftp)) { > + ftp++; > + digit_found = true; > + } > + if (!digit_found) { > + return CT_FTP_CTL_INVALID; > + } > + *ftp = 0; > + if (!str_to_int(save_ftp, 10, &value)) { > + return CT_FTP_CTL_INVALID; > + } > > - if (value > 255) { > - return CT_FTP_CTL_INVALID; > + if (value > 255) { > + return CT_FTP_CTL_INVALID; > + } > + > + port_hs |= value; > + } else { > + ftp = terminate_number_str(ftp, MAX_EXT_FTP_PORT_DGTS); > + if (!ftp) { > + return CT_FTP_CTL_INVALID; > + } > + int value; > + if (!str_to_int(save_ftp, 10, &value)) { > + return CT_FTP_CTL_INVALID; > + } > + if (value > UINT16_MAX) { > + return CT_FTP_CTL_INVALID; > + } > + port_hs = (uint16_t) value; > } > > - port_hs |= value; > ovs_be16 port = htons(port_hs); > ovs_be32 conn_ipv4_addr; > > @@ -3478,12 +3549,14 @@ process_ftp_ctl_v4(struct conntrack *ct, > OVS_NOT_REACHED(); > } > > - ovs_be32 ftp_ipv4_addr; > - ftp_ipv4_addr = ip_addr.s_addr; > - /* Although most servers will block this exploit, there may be some > - * less well managed. */ > - if (ftp_ipv4_addr != conn_ipv4_addr && ftp_ipv4_addr != *v4_addr_rep) { > - return CT_FTP_CTL_INVALID; > + if (!extended || mode == CT_FTP_MODE_ACTIVE) { > + ovs_be32 ftp_ipv4_addr; > + ftp_ipv4_addr = ip_addr.s_addr; > + /* Although most servers will block this exploit, there may be some > + * less well managed. */ > + if (ftp_ipv4_addr != conn_ipv4_addr && ftp_ipv4_addr != > *v4_addr_rep) { > + return CT_FTP_CTL_INVALID; > + } > } > > expectation_create(ct, port, conn_for_expectation, > diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at > index 7f029dbb0..e4862231a 100644 > --- a/tests/system-common-macros.at > +++ b/tests/system-common-macros.at > @@ -280,7 +280,7 @@ m4_define([OVS_GET_HTTP], > # > m4_define([OVS_GET_FTP], > [curl ftp://$1 --retry 3 --max-time 1 --retry-connrefused \ > - --disable-epsv -v $2] > + -v $2] > ) > > # OVS_GET_FTP_ACTIVE([url], [optional_curl_arguments]) > @@ -289,7 +289,7 @@ m4_define([OVS_GET_FTP], > # > m4_define([OVS_GET_FTP_ACTIVE], > [curl ftp://$1 --retry 3 --max-time 1 --retry-connrefused -v \ > - --ftp-port - --disable-eprt $2] > + --ftp-port - $2] > ) Looks like this removes coverage for packets with the other set of commands. We should probably add a test with these options disabled to avoid regressions. Best regards, Ilya Maximets. _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev