]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Disclaim support for FTP EPRT and EPSV commands. Exclude them from FEAT
authorAlex Rousskov <rousskov@measurement-factory.com>
Thu, 29 Aug 2013 16:38:39 +0000 (10:38 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Thu, 29 Aug 2013 16:38:39 +0000 (10:38 -0600)
responses.

Squid still assumes that it can support all other FTP commands.

TODO: We probably want to add ftp_support or a similar ACL-driven option
to give admin more control over which commands are supported. This is
different from access control because we want to filter unsupported
commands from FEAT responses as well.

src/FtpGatewayServer.cc
src/client_side.cc
src/client_side.h

index 1a055bc7af063dc9e096cb7774052cf975178b65..d570c611674f90fef2b03f009716fd12b9f0a911 100644 (file)
@@ -54,6 +54,7 @@ protected:
     enum {
         BEGIN,
         SENT_COMMAND,
+        SENT_FEAT,
         SENT_PASV,
         SENT_PORT,
         SENT_DATA_REQUEST,
@@ -66,6 +67,7 @@ protected:
     void readGreeting();
     void sendCommand();
     void readReply();
+    void readFeatReply();
     void readPasvReply();
     void readPortReply();
     void readDataReply();
@@ -84,6 +86,7 @@ CBDATA_CLASS_INIT(ServerStateData);
 const ServerStateData::SM_FUNC ServerStateData::SM_FUNCS[] = {
     &ServerStateData::readGreeting, // BEGIN
     &ServerStateData::readReply, // SENT_COMMAND
+    &ServerStateData::readFeatReply, // SENT_FEAT
     &ServerStateData::readPasvReply, // SENT_PASV
     &ServerStateData::readPortReply, // SENT_PORT
     &ServerStateData::readDataReply, // SENT_DATA_REQUEST
@@ -429,6 +432,7 @@ ServerStateData::sendCommand()
     writeCommand(mb.content());
 
     state =
+        clientState() == ConnStateData::FTP_HANDLE_FEAT ? SENT_FEAT :
         clientState() == ConnStateData::FTP_HANDLE_PASV ? SENT_PASV :
         clientState() == ConnStateData::FTP_HANDLE_PORT ? SENT_PORT :
         clientState() == ConnStateData::FTP_HANDLE_DATA_REQUEST ? SENT_DATA_REQUEST :
@@ -448,6 +452,17 @@ ServerStateData::readReply()
         forwardReply();
 }
 
+void
+ServerStateData::readFeatReply()
+{
+    assert(clientState() == ConnStateData::FTP_HANDLE_FEAT);
+
+    if (100 <= ctrl.replycode && ctrl.replycode < 200)
+        return; // ignore preliminary replies
+
+    forwardReply();
+}
+
 void
 ServerStateData::readPasvReply()
 {
index 0bbde9ee150eca44c0fc5013a475525aac71f688..c2d6cd52f3532a134c71e91a2d32533c1064fb78 100644 (file)
@@ -241,6 +241,7 @@ static CNCB FtpHandleConnectDone;
 
 static void FtpHandleReply(ClientSocketContext *context, HttpReply *reply, StoreIOBuffer data);
 typedef void FtpReplyHandler(ClientSocketContext *context, const HttpReply *reply, StoreIOBuffer data);
+static FtpReplyHandler FtpHandleFeatReply;
 static FtpReplyHandler FtpHandlePasvReply;
 static FtpReplyHandler FtpHandlePortReply;
 static FtpReplyHandler FtpHandleErrorReply;
@@ -261,6 +262,7 @@ static IOCB FtpWroteReplyData;
 
 typedef bool FtpRequestHandler(ClientSocketContext *context, String &cmd, String &params);
 static FtpRequestHandler FtpHandleRequest;
+static FtpRequestHandler FtpHandleFeatRequest;
 static FtpRequestHandler FtpHandlePasvRequest;
 static FtpRequestHandler FtpHandlePortRequest;
 static FtpRequestHandler FtpHandleDataRequest;
@@ -268,6 +270,8 @@ static FtpRequestHandler FtpHandleUploadRequest;
 
 static bool FtpCheckDataConnection(ClientSocketContext *context);
 static void FtpSetReply(ClientSocketContext *context, const int code, const char *msg);
+static bool FtpSupportedCommand(const String &name);
+
 
 clientStreamNode *
 ClientSocketContext::getTail() const
@@ -5065,9 +5069,6 @@ FtpParseRequest(ConnStateData *connState, HttpRequestMethod *method_p, Http::Pro
     const String cmd = boc;
     String params = bop;
 
-    *method_p = !cmd.caseCmp("APPE") || !cmd.caseCmp("STOR") ||
-        !cmd.caseCmp("STOU") ? Http::METHOD_PUT : Http::METHOD_GET;
-
     if (connState->ftp.uri.size() == 0) {
         // the first command must be USER
         if (cmd.caseCmp("USER") != 0) {
@@ -5086,6 +5087,14 @@ FtpParseRequest(ConnStateData *connState, HttpRequestMethod *method_p, Http::Pro
         !FtpHandleUserRequest(connState, cmd, params))
         return NULL;
 
+    if (!FtpSupportedCommand(cmd)) {
+        FtpWriteEarlyReply(connState, 502, "Unknown or unsupported command");
+        return NULL;
+    }
+
+    *method_p = !cmd.caseCmp("APPE") || !cmd.caseCmp("STOR") ||
+        !cmd.caseCmp("STOU") ? Http::METHOD_PUT : Http::METHOD_GET;
+
     assert(connState->ftp.uri.size() > 0);
     char *uri = xstrdup(connState->ftp.uri.termedBuf());
     HttpRequest *const request =
@@ -5149,6 +5158,7 @@ FtpHandleReply(ClientSocketContext *context, HttpReply *reply, StoreIOBuffer dat
     static FtpReplyHandler *handlers[] = {
         NULL, // FTP_BEGIN
         NULL, // FTP_CONNECTED
+        FtpHandleFeatReply, // FTP_HANDLE_FEAT
         FtpHandlePasvReply, // FTP_HANDLE_PASV
         FtpHandlePortReply, // FTP_HANDLE_PORT
         FtpHandleDataReply, // FTP_HANDLE_DATA_REQUEST
@@ -5163,6 +5173,47 @@ FtpHandleReply(ClientSocketContext *context, HttpReply *reply, StoreIOBuffer dat
         FtpWriteForwardedReply(context, reply);
 }
 
+static void
+FtpHandleFeatReply(ClientSocketContext *context, const HttpReply *reply, StoreIOBuffer data)
+{
+    if (context->http->request->errType != ERR_NONE) {
+        FtpWriteCustomReply(context, 502, "Server does not support FEAT", reply);
+        return;
+    }
+
+    HttpReply *filteredReply = reply->clone();
+    HttpHeader &filteredHeader = filteredReply->header;
+
+    // Remove all unsupported commands from the response wrapper.
+    int deletedCount = 0;
+    HttpHeaderPos pos = HttpHeaderInitPos;
+    while (const HttpHeaderEntry *e = filteredHeader.getEntry(&pos)) {
+        if (e->id == HDR_FTP_PRE) {
+            // assume RFC 2389 FEAT response format, quoted by Squid:
+            // <"> SP NAME [SP PARAMS] <">
+            if (e->value.size() < 4)
+                continue;
+            const char *raw = e->value.termedBuf();
+            if (raw[0] != '"' && raw[1] != ' ')
+                continue;
+            const char *beg = raw + 2; // after quote and space
+            // command name ends with (SP parameter) or quote
+            const char *end = beg + strcspn(beg, " \"");
+            const String cmd = e->value.substr(beg-raw, end-raw);
+
+            if (!FtpSupportedCommand(cmd))
+                filteredHeader.delAt(pos, deletedCount);
+        }
+    }
+
+    if (deletedCount) {
+        filteredHeader.refreshMask();
+        debugs(33, 5, "deleted " << deletedCount);
+    }
+
+    FtpWriteForwardedReply(context, filteredReply);
+}
+
 static void
 FtpHandlePasvReply(ClientSocketContext *context, const HttpReply *reply, StoreIOBuffer data)
 {
@@ -5543,6 +5594,7 @@ FtpHandleRequest(ClientSocketContext *context, String &cmd, String &params) {
     static std::pair<const char *, FtpRequestHandler *> handlers[] = {
         std::make_pair("LIST", FtpHandleDataRequest),
         std::make_pair("NLST", FtpHandleDataRequest),
+        std::make_pair("FEAT", FtpHandleFeatRequest),
         std::make_pair("PASV", FtpHandlePasvRequest),
         std::make_pair("PORT", FtpHandlePortRequest),
         std::make_pair("RETR", FtpHandleDataRequest)
@@ -5601,6 +5653,14 @@ FtpHandleUserRequest(ConnStateData *connState, const String &cmd, String &params
     return true;
 }
 
+bool
+FtpHandleFeatRequest(ClientSocketContext *context, String &cmd, String &params)
+{
+    FtpChangeState(context->getConn(), ConnStateData::FTP_HANDLE_FEAT, "FtpHandleFeatRequest");
+
+    return true;
+}
+
 bool
 FtpHandlePasvRequest(ClientSocketContext *context, String &cmd, String &params)
 {
@@ -5768,3 +5828,17 @@ FtpSetReply(ClientSocketContext *context, const int code, const char *msg)
     repContext->createStoreEntry(http->request->method, flags);
     http->storeEntry()->replaceHttpReply(reply);
 }
+
+static bool
+FtpSupportedCommand(const String &name)
+{
+    static std::set<std::string> BlackList;
+    if (BlackList.empty()) {
+        // FTP commands that Squid cannot gateway correctly:
+        BlackList.insert("EPRT");
+        BlackList.insert("EPSV");
+    }
+
+    // we claim support for all commands that we do not know about
+    return BlackList.find(name.termedBuf()) == BlackList.end();
+}
index 88a0524007d1946f78c8c853535680b84abc800a..c771d1428d2f6e3c4d21b1562028d820c6b694c1 100644 (file)
@@ -340,6 +340,7 @@ public:
     enum FtpState {
         FTP_BEGIN,
         FTP_CONNECTED,
+        FTP_HANDLE_FEAT,
         FTP_HANDLE_PASV,
         FTP_HANDLE_PORT,
         FTP_HANDLE_DATA_REQUEST,