diff --git a/proftpd-1.3.2a-mlsd.patch b/proftpd-1.3.2a-mlsd.patch new file mode 100644 index 0000000..9cf3ec5 --- /dev/null +++ b/proftpd-1.3.2a-mlsd.patch @@ -0,0 +1,245 @@ +--- proftpd-1.3.2a/modules/mod_facts.c.mlsd 2009-04-28 22:17:45.000000000 +0100 ++++ proftpd-1.3.2a/modules/mod_facts.c 2009-09-07 14:17:39.000000000 +0100 +@@ -22,7 +22,7 @@ + * resulting executable, without including the source code for OpenSSL in the + * source distribution. + * +- * $Id: mod_facts.c,v 1.13.2.1 2009/04/28 21:17:45 castaglia Exp $ ++ * $Id: mod_facts.c,v 1.22 2009/04/09 15:59:38 castaglia Exp $ + */ + + #include "conf.h" +@@ -227,7 +227,17 @@ + ptr = buf + buflen; + } + +- snprintf(ptr, bufsz - buflen, " %s\n", info->path); ++ /* MLST entries are not sent via pr_data_xfer(), and thus we do not need ++ * to include an LF at the end; it is appended by pr_response_send_raw(). ++ * But MLSD entries DO need the trailing LF, so that it can be converted ++ * into a CRLF sequence by pr_data_xfer(). ++ */ ++ if (strcmp(session.curr_cmd, C_MLSD) == 0) { ++ snprintf(ptr, bufsz - buflen, " %s\n", info->path); ++ ++ } else { ++ snprintf(ptr, bufsz - buflen, " %s", info->path); ++ } + + buf[bufsz-1] = '\0'; + buflen = strlen(buf); +@@ -244,11 +254,17 @@ + * channel, wherease MLSD's output is sent via a data transfer, much like + * LIST or NLST. + */ +-static char mlinfo_buf[PR_TUNABLE_BUFFER_SIZE]; ++static char *mlinfo_buf = NULL; ++static size_t mlinfo_bufsz = 0; + static size_t mlinfo_buflen = 0; + + static void facts_mlinfobuf_init(void) { +- memset(mlinfo_buf, '\0', sizeof(mlinfo_buf)); ++ if (mlinfo_buf == NULL) { ++ mlinfo_bufsz = pr_config_get_xfer_bufsz(); ++ mlinfo_buf = palloc(session.pool, mlinfo_bufsz); ++ } ++ ++ memset(mlinfo_buf, '\0', mlinfo_bufsz); + mlinfo_buflen = 0; + } + +@@ -261,11 +277,11 @@ + /* If this buffer will exceed the capacity of mlinfo_buf, then flush + * mlinfo_buf. + */ +- if (buflen >= (sizeof(mlinfo_buf) - mlinfo_buflen)) { ++ if (buflen >= (mlinfo_bufsz - mlinfo_buflen)) { + (void) facts_mlinfobuf_flush(); + } + +- sstrcat(mlinfo_buf, buf, sizeof(mlinfo_buf)); ++ sstrcat(mlinfo_buf, buf, mlinfo_bufsz); + mlinfo_buflen += buflen; + } + +@@ -358,6 +374,8 @@ + char buf[PR_TUNABLE_BUFFER_SIZE]; + + (void) facts_mlinfo_fmt(info, buf, sizeof(buf)); ++ ++ /* The trailing CRLF will be added by pr_response_send_raw(). */ + pr_response_send_raw("%s", buf); + } + +@@ -595,7 +613,6 @@ + */ + + MODRET facts_mff(cmd_rec *cmd) { +- register unsigned int i; + const char *path, *decoded_path; + char *facts, *ptr; + +@@ -606,18 +623,16 @@ + + facts = cmd->argv[1]; + +- /* The path can contain spaces; it is thus the concatenation of all of the +- * arguments after the timestamp. ++ /* The path can contain spaces. Thus we need to use cmd->arg, not cmd->argv, ++ * to find the path. But cmd->arg contains the facts as well. Thus we ++ * find the FIRST space in cmd->arg; the path is everything past that space. + */ +- path = pstrdup(cmd->tmp_pool, cmd->argv[2]); +- for (i = 3; i < cmd->argc; i++) { +- path = pstrcat(cmd->tmp_pool, path, " ", cmd->argv[i], NULL); +- } ++ ptr = strchr(cmd->arg, ' '); ++ path = pstrdup(cmd->tmp_pool, ptr + 1); + + decoded_path = pr_fs_decode_path(cmd->tmp_pool, path); + +- if (!dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, (char *) decoded_path, +- NULL)) { ++ if (!dir_check(cmd->tmp_pool, cmd, cmd->group, (char *) decoded_path, NULL)) { + pr_log_debug(DEBUG4, MOD_FACTS_VERSION ": %s command denied by ", + cmd->argv[0]); + pr_response_add_err(R_550, _("Unable to handle command")); +@@ -739,7 +754,6 @@ + } + + MODRET facts_mfmt(cmd_rec *cmd) { +- register unsigned int i; + const char *path, *decoded_path; + char *timestamp, *ptr; + int res; +@@ -751,18 +765,16 @@ + + timestamp = cmd->argv[1]; + +- /* The path can contain spaces; it is thus the concatenation of all of the +- * arguments after the timestamp. ++ /* The path can contain spaces. Thus we need to use cmd->arg, not cmd->argv, ++ * to find the path. But cmd->arg contains the facts as well. Thus we ++ * find the FIRST space in cmd->arg; the path is everything past that space. + */ +- path = pstrdup(cmd->tmp_pool, cmd->argv[2]); +- for (i = 3; i < cmd->argc; i++) { +- path = pstrcat(cmd->tmp_pool, path, " ", cmd->argv[i], NULL); +- } ++ ptr = strchr(cmd->arg, ' '); ++ path = pstrdup(cmd->tmp_pool, ptr + 1); + + decoded_path = pr_fs_decode_path(cmd->tmp_pool, path); + +- if (!dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, (char *) decoded_path, +- NULL)) { ++ if (!dir_check(cmd->tmp_pool, cmd, cmd->group, (char *) decoded_path, NULL)) { + pr_log_debug(DEBUG4, MOD_FACTS_VERSION ": %s command denied by ", + cmd->argv[0]); + pr_response_add_err(R_550, _("Unable to handle command")); +@@ -820,33 +832,28 @@ + DIR *dirh; + struct dirent *dent; + +- if (cmd->argc > 2) { +- pr_response_add_err(R_501, _("Invalid number of arguments")); +- return PR_ERROR(cmd); +- } +- + if (cmd->argc != 1) { +- path = cmd->argv[1]; ++ path = pstrdup(cmd->tmp_pool, cmd->arg); + decoded_path = pr_fs_decode_path(cmd->tmp_pool, path); + + } else { + decoded_path = path = pr_fs_getcwd(); + } + +- if (!dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, (char *) decoded_path, +- NULL)) { ++ if (!dir_check(cmd->tmp_pool, cmd, cmd->group, (char *) decoded_path, NULL)) { + pr_log_debug(DEBUG4, MOD_FACTS_VERSION ": %s command denied by ", + cmd->argv[0]); + pr_response_add_err(R_550, _("Unable to handle command")); + return PR_ERROR(cmd); + } + +- /* RFC3659 explicitly does NOT support glob characters. */ ++ /* RFC3659 explicitly does NOT support glob characters. So warn about ++ * this, but let the command continue as is. We don't actually call ++ * glob(3) here, so no expansion will occur. ++ */ + if (strpbrk(decoded_path, "{[*?") != NULL) { +- pr_log_debug(DEBUG2, MOD_FACTS_VERSION ": unable to handle MLSD command: " +- "target '%s' contains glob characters", decoded_path); +- pr_response_add_err(R_550, _("Unable to handle command")); +- return PR_ERROR(cmd); ++ pr_log_debug(DEBUG9, MOD_FACTS_VERSION ": glob characters in MLSD ('%s') " ++ "ignored", decoded_path); + } + + /* Make sure that the given path is actually a directory. */ +@@ -870,11 +877,11 @@ + int xerrno = errno; + + pr_trace_msg("fileperms", 1, "MLSD, user '%s' (UID %lu, GID %lu): " +- "error reading directory '%s': %s", session.user, ++ "error opening directory '%s': %s", session.user, + (unsigned long) session.uid, (unsigned long) session.gid, + decoded_path, strerror(xerrno)); + +- pr_response_add_err(R_550, _("'%s' cannot be listed"), path); ++ pr_response_add_err(R_550, "%s: %s", path, strerror(xerrno)); + return PR_ERROR(cmd); + } + +@@ -899,16 +906,14 @@ + /* Check that the file can be listed. */ + abs_path = dir_realpath(cmd->tmp_pool, rel_path); + if (abs_path) { +- res = dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, abs_path, +- &hidden); ++ res = dir_check(cmd->tmp_pool, cmd, cmd->group, abs_path, &hidden); + + } else { + abs_path = dir_canonical_path(cmd->tmp_pool, rel_path); + if (abs_path == NULL) + abs_path = rel_path; + +- res = dir_check_canon(cmd->tmp_pool, cmd->argv[0], cmd->group, abs_path, +- &hidden); ++ res = dir_check_canon(cmd->tmp_pool, cmd, cmd->group, abs_path, &hidden); + } + + if (!res || hidden) { +@@ -955,20 +960,15 @@ + const char *path, *decoded_path; + struct mlinfo info; + +- if (cmd->argc > 2) { +- pr_response_add_err(R_501, _("Invalid number of arguments")); +- return PR_ERROR(cmd); +- } +- + if (cmd->argc != 1) { +- path = cmd->argv[1]; ++ path = pstrdup(cmd->tmp_pool, cmd->arg); + decoded_path = pr_fs_decode_path(cmd->tmp_pool, path); + + } else { + decoded_path = path = pr_fs_getcwd(); + } + +- if (!dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, (char *) decoded_path, ++ if (!dir_check(cmd->tmp_pool, cmd, cmd->group, (char *) decoded_path, + &hidden)) { + pr_log_debug(DEBUG4, MOD_FACTS_VERSION ": %s command denied by ", + cmd->argv[0]); +@@ -1155,6 +1155,7 @@ + + pr_feat_add("MFF modify;UNIX.group;UNIX.mode;"); + pr_feat_add("MFMT"); ++ pr_feat_add("TVFS"); + + facts_mlst_feat_add(session.pool); + diff --git a/proftpd.spec b/proftpd.spec index 5e6f61d..849049c 100644 --- a/proftpd.spec +++ b/proftpd.spec @@ -7,7 +7,7 @@ %endif #global prever rc3 -%global rpmrel 4 +%global rpmrel 5 Summary: Flexible, stable and highly-configurable FTP server Name: proftpd @@ -31,6 +31,7 @@ Source11: http://www.castaglia.org/proftpd/modules/proftpd-mod-exec-0.9.6.tar.g Patch0: proftpd-1.3.2rc3-nostrip.patch Patch1: proftpd-1.3.2a-defines.patch Patch2: proftpd-1.3.2-parallel-build.patch +Patch3: proftpd-1.3.2a-mlsd.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root Requires(post): /sbin/chkconfig Requires(preun): /sbin/service, /sbin/chkconfig, coreutils, findutils @@ -98,6 +99,10 @@ Module to add PostgreSQL support to the ProFTPD FTP server. # Fix parallel build (http://bugs.proftpd.org/3189) %patch2 -p1 -b .parallel +# Fix MLSD for directories containing shell metacharacters (#521634) +# (upstream patch) +%patch3 -p1 -b .mlsd + # Avoid documentation name conflicts %{__mv} contrib/README contrib/README.contrib @@ -285,6 +290,9 @@ fi %changelog +* Mon Sep 7 2009 Paul Howarth 1.3.2a-5 +- Add upstream patch for MLSD with dirnames containing glob chars (#521634) + * Wed Sep 2 2009 Paul Howarth 1.3.2a-4 - New DSO module: mod_exec (#520214)