components/proftpd/patches/013-21514375-4143-tls-xss.patch
changeset 4730 72b018b2d0fc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/proftpd/patches/013-21514375-4143-tls-xss.patch	Mon Aug 03 02:40:05 2015 -0700
@@ -0,0 +1,207 @@
+http://bugs.proftpd.org/show_bug.cgi?id=4143#c0
+https://github.com/proftpd/proftpd/pull/81.patch
+
+diff --git a/include/cmd.h b/include/cmd.h
+index a95cac3..814dc62 100644
+--- a/include/cmd.h
++++ b/include/cmd.h
+@@ -106,6 +106,16 @@ int pr_cmd_get_id(const char *name_name);
+ #define PR_CMD_MIN_NAMELEN	3
+ #define PR_CMD_MAX_NAMELEN	4
+ 
++/* Returns TRUE if the given command is a known HTTP method, FALSE if not
++ * a known HTTP method, and -1 if there is an error.
++ */
++int pr_cmd_is_http(cmd_rec *c);
++
++/* Returns TRUE if the given command is a known SMTP method, FALSE if not
++ * a known SMTP method, and -1 if there is an error.
++ */
++int pr_cmd_is_smtp(cmd_rec *c);
++
+ int pr_cmd_set_name(cmd_rec *, const char *);
+ 
+ /* Implemented in main.c */
+diff --git a/include/dirtree.h b/include/dirtree.h
+index fe7b14b..ddb31a8 100644
+--- a/include/dirtree.h
++++ b/include/dirtree.h
+@@ -130,6 +130,13 @@ typedef struct cmd_struc {
+   int error_code;               /* Stores errno of failed file transfer
+                                  * commands. Required for Solaris auditing.
+                                  */
++
++  /* If we detect that the client sent commands for a protocol OTHER than
++   * FTP, then this field will be FALSE; the protocol field will identify
++   * the detected protocol.
++   */
++  int is_ftp;
++  const char *protocol;
+ } cmd_rec;
+ 
+ struct config_struc {
+diff --git a/include/session.h b/include/session.h
+index a0ccd1a..d47ea83 100644
+--- a/include/session.h
++++ b/include/session.h
+@@ -72,6 +72,9 @@
+ /* Disconnected due to snprintf(3) buffer truncation. */
+ #define PR_SESS_DISCONNECT_SNPRINTF_TRUNCATED	13
+ 
++/* Disconnected due to wrong protocol used (e.g. HTTP/SMTP). */
++#define PR_SESS_DISCONNECT_BAD_PROTOCOL                14
++
+ /* Returns a string describing the reason the client was disconnected or
+  * the session ended.  If a pointer to a char * was provided, any extra
+  * disconnect details will be provided.
+diff --git a/src/cmd.c b/src/cmd.c
+index b441c54..4688ff3 100644
+--- a/src/cmd.c
++++ b/src/cmd.c
+@@ -112,6 +112,38 @@ static struct cmd_entry cmd_ids[] = {
+   { NULL,	0 }
+ };
+ 
++/* Due to potential XSS issues (see Bug#4143), we want to explicitly
++ * check for commands from other text-based protocols (e.g. HTTP and SMTP);
++ * if we see these, we want to close the connection with extreme prejudice.
++ */
++
++static struct cmd_entry http_ids[] = {
++  { " ",       1 },    /* Index 0 is intentionally filled with a sentinel */
++  { "CONNECT", 7 },
++  { "DELETE",  6 },
++  { "GET",     3 },
++  { "HEAD",    4 },
++  { "OPTIONS", 7 },
++  { "PATCH",   5 },
++  { "POST",    4 },
++  { "PUT",     3 },
++
++  { NULL,      0 }
++};
++
++static struct cmd_entry smtp_ids[] = {
++  { " ",       1 },    /* Index 0 is intentionally filled with a sentinel */
++  { "DATA",    4 },
++  { "EHLO",    4 },
++  { "HELO",    4 },
++  { "MAIL",    4 },
++  { "RCPT",    4 },
++  { "RSET",    4 },
++  { "VRFY",    4 },
++
++  { NULL,      0 }
++};
++
+ cmd_rec *pr_cmd_alloc(pool *p, int argc, ...) { 
+   pool *newpool = NULL;
+   cmd_rec *cmd = NULL;
+@@ -340,3 +372,59 @@ int pr_cmd_get_id(const char *cmd_name) {
+   errno = ENOENT;
+   return -1;
+ }
++
++static int is_known_cmd(struct cmd_entry *known_cmds, const char *cmd_name,
++    size_t cmd_namelen) {
++  register unsigned int i;
++  int known = FALSE;
++
++  for (i = 0; known_cmds[i].cmd_name != NULL; i++) {
++    if (cmd_namelen == known_cmds[i].cmd_namelen) {
++      if (strncmp(cmd_name, known_cmds[i].cmd_name, cmd_namelen + 1) == 0) {
++        known = TRUE;
++        break;
++      }
++    }
++  }
++
++  return known;
++}
++
++int pr_cmd_is_http(cmd_rec *cmd) {
++  const char *cmd_name;
++  size_t cmd_namelen;
++
++  if (cmd == NULL) {
++    errno = EINVAL;
++    return -1;
++  }
++
++  cmd_name = cmd->argv[0];
++  if (cmd_name == NULL) {
++    errno = EINVAL;
++    return -1;
++  }
++
++  cmd_namelen = strlen(cmd_name);
++  return is_known_cmd(http_ids, cmd_name, cmd_namelen);
++}
++
++int pr_cmd_is_smtp(cmd_rec *cmd) {
++  const char *cmd_name;
++  size_t cmd_namelen;
++
++  if (cmd == NULL) {
++    errno = EINVAL;
++    return -1;
++  }
++
++  cmd_name = cmd->argv[0];
++  if (cmd_name == NULL) {
++    errno = EINVAL;
++    return -1;
++  }
++
++  cmd_namelen = strlen(cmd_name);
++  return is_known_cmd(smtp_ids, cmd_name, cmd_namelen);
++}
++
+diff --git a/src/main.c b/src/main.c
+index b951436..b0a8a2a 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -572,7 +572,21 @@ int pr_cmd_read(cmd_rec **res) {
+     cmd = make_ftp_cmd(session.pool, cp, flags);
+     if (cmd) {
+       *res = cmd;
+-    } 
++
++      if (pr_cmd_is_http(cmd) == TRUE) {
++        cmd->is_ftp = FALSE;
++        cmd->protocol = "HTTP";
++
++      } else if (pr_cmd_is_smtp(cmd) == TRUE) {
++        cmd->is_ftp = FALSE;
++        cmd->protocol = "SMTP";
++
++      } else {
++        /* Assume that the client is sending valid FTP commands. */
++        cmd->is_ftp = TRUE;
++        cmd->protocol = "FTP";
++      }
++    }
+   }
+ 
+   return 0;
+@@ -827,6 +841,20 @@ static void cmd_loop(server_rec *server, conn_t *c) {
+     }
+ 
+     if (cmd) {
++
++      /* Detect known commands for other protocols; if found, drop the
++       * connection, lest we be used as part of an attack on a different
++       * protocol server (Bug#4143).
++       */
++      if (cmd->is_ftp == FALSE) {
++        pr_log_pri(PR_LOG_WARNING,
++          "client sent %s command '%s', disconnecting", cmd->protocol,
++          cmd->argv[0]);
++        pr_event_generate("core.bad-protocol", cmd);
++        pr_session_disconnect(NULL, PR_SESS_DISCONNECT_BAD_PROTOCOL,
++          cmd->protocol);
++      }
++
+       pr_cmd_dispatch(cmd);
+       destroy_pool(cmd->pool);
+