From 8d3f77b70d3c49bd14eb05c423788ec5df65bdad Mon Sep 17 00:00:00 2001 From: Joe Orton Date: Mon, 14 Sep 2009 14:16:14 +0000 Subject: [PATCH] Security fix - this is presumed to fix CVE-2009-3094 (the disclosed information was limited so this has not been confirmed): * modules/proxy/mod_proxy_ftp.c (parse_epsv_reply): New function. (proxy_ftp_handler): Fix possible NULL pointer deference in apr_socket_close(NULL) on error paths. Fix possible buffer overread in EPSV response parser; use parse_epsv_reply instead. Thanks to Jeff Trawick and Stefan Fritsch for analysis of this issue. Submitted by: Stefan Fritsch , jorton git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@814652 13f79535-47bb-0310-9956-ffa450edef68 --- modules/proxy/mod_proxy_ftp.c | 58 +++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/modules/proxy/mod_proxy_ftp.c b/modules/proxy/mod_proxy_ftp.c index 1caa604dcb..dfe3cade5b 100644 --- a/modules/proxy/mod_proxy_ftp.c +++ b/modules/proxy/mod_proxy_ftp.c @@ -683,6 +683,31 @@ static apr_status_t proxy_send_dir_filter(ap_filter_t *f, return APR_SUCCESS; } +/* Parse EPSV reply and return port, or zero on error. Modifies + * 'reply'. */ +static apr_port_t parse_epsv_reply(char *reply) +{ + char *p, *ep; + long port; + + /* Reply syntax per RFC 2428: "229 blah blah (|||port|)" where '|' + * can be any character in ASCII from 33-126, obscurely. Verify + * the syntax. */ + p = ap_strchr(reply, '('); + if (p == NULL || !p[0] || !p[1] || p[1] != p[2] || p[1] != p[3] + || p[4] == p[1]) { + return 0; + } + + errno = 0; + port = strtol(p + 4, &ep, 10); + if (errno || port < 1 || port > 65535 || ep[0] != p[1] || ep[1] != ')') { + return 0; + } + + return (apr_port_t)port; +} + /* * Generic "send FTP command to server" routine, using the control socket. * Returns the FTP returncode (3 digit code) @@ -1296,26 +1321,11 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); } else if (rc == 229) { - char *pstr; - char *tok_cntx; + /* Parse the port out of the EPSV reply. */ + data_port = parse_epsv_reply(ftpmessage); - pstr = ftpmessage; - pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */ - if (pstr != NULL) { - if (*(pstr + strlen(pstr) + 1) == '=') { - pstr += strlen(pstr) + 2; - } - else { - pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address & - * port params */ - if (pstr != NULL) - pstr = apr_strtok(NULL, ")", &tok_cntx); - } - } - - if (pstr) { + if (data_port) { apr_sockaddr_t *epsv_addr; - data_port = atoi(pstr + 3); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: FTP: EPSV contacting remote host on port %d", @@ -1356,10 +1366,6 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, connect = 1; } } - else { - /* and try the regular way */ - apr_socket_close(data_sock); - } } } @@ -1446,10 +1452,6 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, connect = 1; } } - else { - /* and try the regular way */ - apr_socket_close(data_sock); - } } } /*bypass:*/ @@ -1929,7 +1931,9 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, * for a slow client to eat these bytes */ ap_flush_conn(data); - apr_socket_close(data_sock); + if (data_sock) { + apr_socket_close(data_sock); + } data_sock = NULL; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: FTP: data connection closed"); -- 2.50.1