You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@apache.org on 2007/10/03 07:03:58 UTC

svn commit: r581495 - /httpd/mod_ftp/trunk/modules/ftp/ftp_commands.c

Author: wrowe
Date: Tue Oct  2 22:03:58 2007
New Revision: 581495

URL: http://svn.apache.org/viewvc?rev=581495&view=rev
Log:
Implement the initial attempt at EPRT.  Note that this is foo-weak
with respect to understanding IPV6_IS_IPV4_MAPPED, and needs some
real professional love with respect to that gray area.

Modified:
    httpd/mod_ftp/trunk/modules/ftp/ftp_commands.c

Modified: httpd/mod_ftp/trunk/modules/ftp/ftp_commands.c
URL: http://svn.apache.org/viewvc/httpd/mod_ftp/trunk/modules/ftp/ftp_commands.c?rev=581495&r1=581494&r2=581495&view=diff
==============================================================================
--- httpd/mod_ftp/trunk/modules/ftp/ftp_commands.c (original)
+++ httpd/mod_ftp/trunk/modules/ftp/ftp_commands.c Tue Oct  2 22:03:58 2007
@@ -1701,6 +1701,141 @@
     return FTP_REPLY_COMMAND_OK;
 }
 
+
+static int ftp_cmd_eprt(request_rec *r, const char *arg)
+{
+    ftp_server_config *fsc = ftp_get_module_config(r->server->module_config);
+    ftp_connection *fc = ftp_get_module_config(r->request_config);
+    conn_rec *c = r->connection;
+    apr_sockaddr_t *sa;
+    apr_socket_t *s;
+    apr_status_t rv;
+    char *arg_tok, *ip_addr;
+    apr_int32_t family;
+    apr_port_t port;
+#if !defined(WIN32) && !defined(HPUX)
+    uid_t user = 0;
+#endif
+    int res;
+
+    if (fc->all_epsv) {
+        fc->response_notes = apr_pstrdup(r->pool, "Restricted by EPSV ALL ");
+        return FTP_REPLY_BAD_SEQUENCE;
+    }
+
+    if (fc->csock) {
+        apr_socket_close(fc->csock);
+        fc->csock = NULL;
+        fc->passive_created = -1;
+    }
+
+    arg_tok = apr_pstrdup(c->pool, arg);
+    if ((res = ftp_eprt_decode(&family, &ip_addr, &port, arg_tok))
+            != FTP_REPLY_COMMAND_OK) {
+        fc->response_notes = apr_pstrdup(r->pool, "Invalid EPRT request");
+        return res;
+    }
+
+    /* Open data connection only if the PORT connection is to the client's 
+     * IP address.
+     * All other PORT connection requests are denied, unless disabled using:
+     *
+     *   FTPOptions AllowProxyPORT 
+     */
+    if (!(fsc->options & FTP_OPT_ALLOWPROXYPORT)) {
+        /* XXX: Match IPv6 ip_addr strings where c->remote_ip is IPv4 mapped */
+        if (strcasecmp(ip_addr, c->remote_ip) != 0) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server,
+                         "PORT data connection request to %s "
+                         "doesn't match the client IP %s",
+                         ip_addr, c->remote_ip);
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server,
+                         "PORT data connection request denied, "
+                         "not configured to AllowProxyPORT");
+            fc->response_notes = apr_pstrdup(r->pool, 
+                         "Invalid PORT command, proxy PORT is not permitted");
+            return FTP_REPLY_SYNTAX_ERROR;   
+        }
+    }
+    
+#if !defined(WIN32) && !defined(HPUX)
+    user = geteuid();
+    if ((fsc->active_min != -1) && (fsc->active_min < 1024)) {
+        if (seteuid(0) != 0) {
+            /* For safety, switch back to user, ignore result */
+            seteuid(user);
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
+                          "Unable to switch to root priviledges.");
+        }
+    }
+#endif
+
+#if APR_MAJOR_VERSION < 1
+    rv = apr_socket_create_ex(&s, family, SOCK_STREAM, APR_PROTO_TCP, c->pool);
+#else
+    rv = apr_socket_create(&s, family, SOCK_STREAM, APR_PROTO_TCP, c->pool);
+#endif
+
+    if (rv != APR_SUCCESS) {
+#if !defined(WIN32) && !defined(HPUX)
+        if ((fsc->active_min != -1) && (fsc->active_min < 1024)) {
+            seteuid(user);
+        }
+#endif
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                     "Couldn't create socket");
+        return FTP_REPLY_CANNOT_OPEN_DATACONN;
+    }
+
+    apr_socket_addr_get(&sa, APR_LOCAL, s);
+
+    if (fsc->active_min != -1) {
+        short port_num = fsc->active_min + 
+                   ((int) (rand() % (fsc->active_max - fsc->active_min + 1)));
+        sa->port = port_num;
+    }
+    else {
+        sa->port = 0;
+    }
+
+    /* XXX: Handle IPv6 ip_addr string where c->remote_ip is IPv4 mapped */
+    if ((c->local_addr->family == APR_INET) && (family == APR_INET)) {
+        apr_sockaddr_info_get(&sa, c->local_ip, family,
+                              sa->port, 0, c->pool);
+    }
+    else if ((c->local_addr->family == APR_INET6) && (family == APR_INET6)) {
+        apr_sockaddr_info_get(&sa, c->local_ip, family,
+                              sa->port, 0, c->pool);
+    }
+    else {
+        apr_sockaddr_info_get(&sa, NULL, family,
+                              sa->port, 0, c->pool);
+    }
+    apr_socket_opt_set(s, APR_SO_REUSEADDR, 1);
+    rv = apr_socket_bind(s, sa);
+
+#if !defined(WIN32) && !defined(HPUX)
+    if ((fsc->active_min != -1) && (fsc->active_min < 1024)) {
+        seteuid(user);
+    }
+#endif
+
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                     "Couldn't bind to socket");
+        return FTP_REPLY_CANNOT_OPEN_DATACONN;
+    }
+
+    apr_socket_addr_get(&fc->clientsa, APR_LOCAL, s);
+    apr_sockaddr_info_get(&fc->clientsa, ip_addr, family,
+                          port, 0, c->pool);
+
+    fc->response_notes = apr_psprintf(r->pool, FTP_MSG_SUCCESS, r->method);
+    fc->passive_created = -1; /* Redundant but just for clarity */
+    fc->csock = s;
+    return FTP_REPLY_COMMAND_OK;
+}
+
 static int ftp_cmd_port(request_rec *r, const char *arg)
 {
     ftp_server_config *fsc = ftp_get_module_config(r->server->module_config);
@@ -2650,7 +2785,7 @@
                  FTP_NEED_LOGIN|FTP_TAKE1,
                  "<sp> file-name");
 
-    ftp_hook_cmd("EPRT", NULL, FTP_HOOK_LAST,
+    ftp_hook_cmd("EPRT", ftp_cmd_eprt, FTP_HOOK_LAST,
                  FTP_NEED_LOGIN|FTP_TAKE1,
                  "<sp> <d>af<d>addr<d>port<d>");