# SECURITY: CVE-2009-3094 (cve.mitre.org) # mod_proxy_ftp: NULL pointer dereference on error paths. # [Stefan Fritsch , Joe Orton] # Reviewed by: pgollucci, poirier, trawick # Backports: trunk revs 814652 and 814785 # # /httpd/httpd/branches/2.0.x/modules/proxy/proxy_ftp.c # Index: modules/proxy/proxy_ftp.c =================================================================== --- modules/proxy/proxy_ftp.c (revision 943924) +++ modules/proxy/proxy_ftp.c (revision 943925) @@ -588,6 +588,31 @@ return APR_SUCCESS; } +/* Parse EPSV reply and return port, or zero on error. */ +static apr_port_t parse_epsv_reply(const char *reply) +{ + const char *p; + char *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_c(reply, '('); + if (p == NULL || !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) @@ -1232,26 +1257,11 @@ return ap_proxyerror(r, 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", @@ -1287,10 +1297,6 @@ connect = 1; } } - else { - /* and try the regular way */ - apr_socket_close(data_sock); - } } } @@ -1372,10 +1378,6 @@ connect = 1; } } - else { - /* and try the regular way */ - apr_socket_close(data_sock); - } } } /*bypass:*/ @@ -1840,7 +1842,9 @@ * 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");