[SCM] proftpd-dfsg branch, squeeze, updated. debian/1.3.3a-6-11-gd3ad62c

Francesco Paolo Lovergine frankie at debian.org
Tue Jan 8 12:55:33 UTC 2013


The following commit has been merged in the squeeze branch:
commit ff2d7b0deb4d50c0a72d575259133664ca162454
Author: Francesco Paolo Lovergine <frankie at debian.org>
Date:   Tue Jan 8 11:39:58 2013 +0100

    Fixed CVE-2012-6095

diff --git a/debian/changelog b/debian/changelog
index 181928e..f92e065 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+proftpd-dfsg (1.3.3a-6squeeze5) stable-security; urgency=low
+
+  * [SECURITY] 3841.dpatch. This patch fixes a possible race condition in the
+  handling of the MKD/XMKD FTP commands, when the UserOwner directive is 
+  involved, and the attacker is on the same physical machine as a running
+  proftpd. This is CVE-2012-6095.
+
+ -- Francesco Paolo Lovergine <frankie at debian.org>  Tue, 08 Jan 2013 10:27:27 +0100
+
 proftpd-dfsg (1.3.3a-6squeeze4) stable-security; urgency=low
 
   * [SECURITY] 3711.dpatch. This patch fixes a response pool use-after-free
diff --git a/debian/patches/3841.dpatch b/debian/patches/3841.dpatch
new file mode 100755
index 0000000..40bb24f
--- /dev/null
+++ b/debian/patches/3841.dpatch
@@ -0,0 +1,318 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 3841.dpatch by Francesco Paolo Lovergine <frankie at debian.org>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: No description.
+
+ at DPATCH@
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' proftpd-dfsg~/contrib/mod_sftp/fxp.c proftpd-dfsg/contrib/mod_sftp/fxp.c
+--- proftpd-dfsg~/contrib/mod_sftp/fxp.c	2013-01-08 10:30:08.000000000 +0100
++++ proftpd-dfsg/contrib/mod_sftp/fxp.c	2013-01-08 10:38:43.000000000 +0100
+@@ -5341,6 +5341,7 @@
+   uint32_t attr_flags, buflen, bufsz, status_code;
+   struct fxp_packet *resp;
+   cmd_rec *cmd, *cmd2;
++  int res;
+ 
+   path = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz);
+   if (fxp_session->client_version >= fxp_utf8_protocol_version) {
+@@ -5488,7 +5489,8 @@
+   (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
+     "creating directory '%s' with mode 0%o", path, (unsigned int) dir_mode);
+ 
+-  if (pr_fsio_mkdir(path, dir_mode) < 0) {
++  res = pr_fsio_smkdir(fxp->pool, path, dir_mode, (uid_t) -1, (gid_t) -1);
++  if (res < 0) {
+     const char *reason;
+     int xerrno = errno;
+ 
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' proftpd-dfsg~/contrib/mod_sftp/scp.c proftpd-dfsg/contrib/mod_sftp/scp.c
+--- proftpd-dfsg~/contrib/mod_sftp/scp.c	2013-01-08 10:26:35.000000000 +0100
++++ proftpd-dfsg/contrib/mod_sftp/scp.c	2013-01-08 10:31:24.000000000 +0100
+@@ -711,7 +711,7 @@
+       if (xerrno == ENOENT) {
+         pr_trace_msg(trace_channel, 5, "creating directory '%s'", sp->filename);
+ 
+-        if (pr_fsio_mkdir(sp->filename, 0777) < 0) {
++        if (pr_fsio_smkdir(p, sp->filename, 0777, (uid_t) -1, (gid_t) -1) < 0) {
+           xerrno = errno;
+ 
+           (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' proftpd-dfsg~/include/fsio.h proftpd-dfsg/include/fsio.h
+--- proftpd-dfsg~/include/fsio.h	2013-01-08 10:26:35.000000000 +0100
++++ proftpd-dfsg/include/fsio.h	2013-01-08 10:31:24.000000000 +0100
+@@ -249,6 +249,7 @@
+ int pr_fsio_rmdir(const char *);
+ int pr_fsio_rename(const char *, const char *);
+ int pr_fsio_rename_canon(const char *, const char *);
++int pr_fsio_smkdir(pool *, const char *, mode_t, uid_t, gid_t);
+ int pr_fsio_unlink(const char *);
+ int pr_fsio_unlink_canon(const char *);
+ pr_fh_t *pr_fsio_open(const char *, int);
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' proftpd-dfsg~/modules/mod_core.c proftpd-dfsg/modules/mod_core.c
+--- proftpd-dfsg~/modules/mod_core.c	2013-01-08 10:26:36.000000000 +0100
++++ proftpd-dfsg/modules/mod_core.c	2013-01-08 10:40:34.000000000 +0100
+@@ -4048,7 +4048,8 @@
+     return PR_ERROR(cmd);
+   }
+ 
+-  if (pr_fsio_mkdir(dir, 0777) < 0) {
++  if (pr_fsio_smkdir(cmd->tmp_pool, dir, 0777, session.fsuid,
++      session.fsgid) < 0) {
+     int xerrno = errno;
+ 
+     (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
+@@ -4060,71 +4061,6 @@
+     return PR_ERROR(cmd);
+   }
+ 
+-  /* Check to see if we need to change the ownership (user and/or group) of
+-   * the newly created directory.
+-   */
+-  if (session.fsuid != (uid_t) -1) {
+-    int err = 0, iserr = 0;
+-
+-    pr_fsio_stat(dir, &st);
+-
+-    PRIVS_ROOT
+-    if (pr_fsio_chown(dir, session.fsuid, session.fsgid) == -1) {
+-      iserr++;
+-      err = errno;
+-    }
+-    PRIVS_RELINQUISH
+-
+-    if (iserr) {
+-      pr_log_pri(PR_LOG_WARNING, "chown() as root failed: %s", strerror(err));
+-
+-    } else {
+-      if (session.fsgid != (gid_t) -1) {
+-        pr_log_debug(DEBUG2, "root chown(%s) to uid %lu, gid %lu successful",
+-          dir, (unsigned long) session.fsuid, (unsigned long) session.fsgid);
+-
+-      } else {
+-        pr_log_debug(DEBUG2, "root chown(%s) to uid %lu successful", dir,
+-          (unsigned long) session.fsuid);
+-      }
+-    }
+-
+-  } else if (session.fsgid != (gid_t) -1) {
+-    register unsigned int i;
+-    int use_root_privs = TRUE;
+-
+-    pr_fsio_stat(dir, &st);
+-
+-    /* Check if session.fsgid is in session.gids.  If not, use root privs.  */
+-    for (i = 0; i < session.gids->nelts; i++) {
+-      gid_t *group_ids = session.gids->elts;
+-
+-      if (group_ids[i] == session.fsgid) {
+-        use_root_privs = FALSE;
+-        break;
+-      }
+-    }
+-
+-    if (use_root_privs) {
+-      PRIVS_ROOT
+-    }
+-
+-    res = pr_fsio_chown(dir, (uid_t) -1, session.fsgid);
+-
+-    if (use_root_privs) {
+-      PRIVS_RELINQUISH
+-    }
+-
+-    if (res == -1) {
+-      pr_log_pri(PR_LOG_WARNING, "%schown() failed: %s",
+-        use_root_privs ? "root " : "", strerror(errno));
+-
+-    } else { 
+-      pr_log_debug(DEBUG2, "%schown(%s) to gid %lu successful",
+-        use_root_privs ? "root " : "", dir, (unsigned long) session.fsgid);
+-    }
+-  }
+-
+   pr_response_add(R_257, _("\"%s\" - Directory successfully created"),
+     quote_dir(cmd, dir));
+ 
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' proftpd-dfsg~/src/fsio.c proftpd-dfsg/src/fsio.c
+--- proftpd-dfsg~/src/fsio.c	2013-01-08 10:26:36.000000000 +0100
++++ proftpd-dfsg/src/fsio.c	2013-01-08 10:31:24.000000000 +0100
+@@ -29,6 +29,7 @@
+  */
+ 
+ #include "conf.h"
++#include "privs.h"
+ 
+ #ifdef HAVE_REGEX_H
+ # include <regex.h>
+@@ -2477,6 +2478,170 @@
+   return res;
+ }
+ 
++/* "secure mkdir" variant of mkdir(2), uses mkdtemp(3), lchown(2), and
++ * rename(2) to create a directory which cannot be hijacked by a symlink
++ * race (hopefully) before the UserOwner/GroupOwner ownership changes are
++ * applied.
++ */
++int pr_fsio_smkdir(pool *p, const char *path, mode_t mode, uid_t uid,
++    gid_t gid) {
++  int res;
++  char *tmpl_path;
++#ifdef HAVE_MKDTEMP
++  mode_t mask, *dir_umask;
++  char *dst_dir, *tmpl, *ptr;
++  size_t tmpl_len;
++  struct stat st;
++#endif /* HAVE_MKDTEMP */
++
++  if (path == NULL) {
++    errno = EINVAL;
++    return -1;
++  }
++
++#ifdef HAVE_MKDTEMP
++  ptr = strrchr(path, '/');
++  if (ptr == NULL) {
++    errno = EINVAL;
++    return -1;
++  }
++
++  dst_dir = pstrndup(p, path, (ptr - path));
++  res = lstat(dst_dir, &st);
++  if (res < 0) {
++    return -1;
++  }
++
++  if (!S_ISDIR(st.st_mode)) {
++    errno = EPERM;
++    return -1;
++  }
++
++  /* Allocate enough space for the temporary name: the length of the
++   * destination directory, a slash, 9 X's, 3 for the prefix, and 1 for the
++   * trailing NUL.
++   */
++  tmpl_len = strlen(path) + 14;
++  tmpl = pcalloc(p, tmpl_len);
++  snprintf(tmpl, tmpl_len-1, "%s/dstXXXXXXXXX", dst_dir);
++
++  /* Use mkdtemp(3) to create the temporary directory (in the same destination
++   * directory as the target path).
++   */
++  tmpl_path = mkdtemp(tmpl);
++  if (tmpl_path == NULL) {
++    return -1;
++  }
++#else
++
++  res = pr_fsio_mkdir(path, mode);
++  if (res < 0) {
++    return -1;
++  }
++
++  tmpl_path = pstrdup(p, path);
++
++#endif /* HAVE_MKDTEMP */
++
++  if (uid != (uid_t) -1) {
++    int xerrno;
++
++    PRIVS_ROOT
++    res = pr_fsio_lchown(tmpl_path, uid, gid);
++    xerrno = errno;
++    PRIVS_RELINQUISH
++
++    if (res < 0) {
++      pr_log_pri(PR_LOG_WARNING, "lchown(%s) as root failed: %s", tmpl_path,
++        strerror(xerrno));
++
++    } else {
++      if (gid != (gid_t) -1) {
++        pr_log_debug(DEBUG2, "root lchown(%s) to UID %lu, GID %lu successful",
++          tmpl_path, (unsigned long) uid, (unsigned long) gid);
++
++      } else {
++        pr_log_debug(DEBUG2, "root lchown(%s) to UID %lu successful",
++          tmpl_path, (unsigned long) uid);
++      }
++    }
++
++  } else if (gid != (gid_t) -1) {
++    register unsigned int i;
++    int use_root_privs = TRUE, xerrno;
++
++    /* Check if session.fsgid is in session.gids.  If not, use root privs.  */
++    for (i = 0; i < session.gids->nelts; i++) {
++      gid_t *group_ids = session.gids->elts;
++
++      if (group_ids[i] == gid) {
++        use_root_privs = FALSE;
++        break;
++      }
++    }
++
++    if (use_root_privs) {
++      PRIVS_ROOT
++    }
++
++    res = pr_fsio_lchown(tmpl_path, (uid_t) -1, gid);
++    xerrno = errno;
++
++    if (use_root_privs) {
++      PRIVS_RELINQUISH
++    }
++
++    if (res < 0) {
++      pr_log_pri(PR_LOG_WARNING, "%slchown(%s) failed: %s",
++        use_root_privs ? "root " : "", tmpl_path, strerror(xerrno));
++
++    } else {
++      pr_log_debug(DEBUG2, "%slchown(%s) to GID %lu successful",
++        use_root_privs ? "root " : "", tmpl_path, (unsigned long) gid);
++    }
++  }
++
++#ifdef HAVE_MKDTEMP
++  /* Use chmod(2) to set the permission that we want.
++   *
++   * mkdtemp(3) creates a directory with 0700 perms; we are given the
++   * target mode (modulo the configured Umask).
++   */
++  dir_umask = get_param_ptr(CURRENT_CONF, "DirUmask", FALSE);
++  if (dir_umask) {
++    mask = *dir_umask;
++
++  } else {
++    mask = (mode_t) 0022;
++  }
++
++  res = chmod(tmpl_path, mode & ~mask);
++  if (res < 0) {
++    int xerrno = errno;
++
++    (void) rmdir(tmpl_path);
++
++    errno = xerrno;
++    return -1;
++  }
++
++  /* Use rename(2) to move the temporary directory into place at the
++   * target path.
++   */
++  res = rename(tmpl_path, path);
++  if (res < 0) {
++    int xerrno = errno;
++    
++    (void) rmdir(tmpl_path);
++    
++    errno = xerrno;
++    return -1;
++  }
++#endif /* HAVE_MKDTEMP */
++
++  return 0;
++}
++
+ int pr_fsio_rmdir(const char *path) {
+   int res;
+   pr_fs_t *fs;

-- 
ProFTPD core package



More information about the Pkg-proftpd-maintainers mailing list