Sophie

Sophie

distrib > Mandriva > mes5 > x86_64 > by-pkgid > 1a245e4eb1517e9779681a0d0ab35fbe > files > 13

proftpd-1.3.3g-0.2mdvmes5.2.src.rpm

diff -Naurp proftpd-1.3.3g/contrib/mod_sftp/fxp.c proftpd-1.3.3g.oden/contrib/mod_sftp/fxp.c
--- proftpd-1.3.3g/contrib/mod_sftp/fxp.c	2011-03-19 23:02:09.000000000 +0000
+++ proftpd-1.3.3g.oden/contrib/mod_sftp/fxp.c	2013-04-05 13:38:25.000000000 +0000
@@ -5498,7 +5498,7 @@ static int fxp_handle_mkdir(struct fxp_p
   (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
     "creating directory '%s' with mode 0%o", path, (unsigned int) dir_mode);
 
-  res = pr_fsio_mkdir(path, dir_mode);
+  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 -Naurp proftpd-1.3.3g/contrib/mod_sftp/scp.c proftpd-1.3.3g.oden/contrib/mod_sftp/scp.c
--- proftpd-1.3.3g/contrib/mod_sftp/scp.c	2011-02-26 02:46:45.000000000 +0000
+++ proftpd-1.3.3g.oden/contrib/mod_sftp/scp.c	2013-04-05 13:38:25.000000000 +0000
@@ -711,7 +711,7 @@ static int recv_finfo(pool *p, uint32_t 
       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 -Naurp proftpd-1.3.3g/include/fsio.h proftpd-1.3.3g.oden/include/fsio.h
--- proftpd-1.3.3g/include/fsio.h	2009-11-05 17:46:54.000000000 +0000
+++ proftpd-1.3.3g.oden/include/fsio.h	2013-04-05 13:38:25.000000000 +0000
@@ -129,6 +129,7 @@ struct fs_rec {
   int (*fchmod)(pr_fh_t *, int, mode_t);
   int (*chown)(pr_fs_t *, const char *, uid_t, gid_t);
   int (*fchown)(pr_fh_t *, int, uid_t, gid_t);
+  int (*lchown)(pr_fs_t *, const char *, uid_t, gid_t);
   int (*access)(pr_fs_t *, const char *, int, uid_t, gid_t, array_header *);
   int (*faccess)(pr_fh_t *, int, uid_t, gid_t, array_header *);
   int (*utimes)(pr_fs_t *, const char *, struct timeval *);
@@ -249,6 +250,7 @@ int pr_fsio_mkdir(const char *, mode_t);
 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);
@@ -270,6 +272,7 @@ int pr_fsio_fchmod(pr_fh_t *, mode_t);
 int pr_fsio_chmod_canon(const char *, mode_t);
 int pr_fsio_chown(const char *, uid_t, gid_t);
 int pr_fsio_fchown(pr_fh_t *, uid_t, gid_t);
+int pr_fsio_lchown(const char *, uid_t, gid_t);
 int pr_fsio_chown_canon(const char *, uid_t, gid_t);
 int pr_fsio_chroot(const char *);
 int pr_fsio_access(const char *, int, uid_t, gid_t, array_header *);
diff -Naurp proftpd-1.3.3g/modules/mod_core.c proftpd-1.3.3g.oden/modules/mod_core.c
--- proftpd-1.3.3g/modules/mod_core.c	2011-02-21 02:36:38.000000000 +0000
+++ proftpd-1.3.3g.oden/modules/mod_core.c	2013-04-05 13:38:25.000000000 +0000
@@ -4043,7 +4043,8 @@ MODRET core_mkd(cmd_rec *cmd) {
     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): "
@@ -4055,71 +4056,6 @@ MODRET core_mkd(cmd_rec *cmd) {
     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 -Naurp proftpd-1.3.3g/src/fsio.c proftpd-1.3.3g.oden/src/fsio.c
--- proftpd-1.3.3g/src/fsio.c	2010-04-12 19:00:00.000000000 +0000
+++ proftpd-1.3.3g.oden/src/fsio.c	2013-04-05 13:38:25.000000000 +0000
@@ -29,6 +29,7 @@
  */
 
 #include "conf.h"
+#include "privs.h"
 
 #ifdef HAVE_REGEX_H
 # include <regex.h>
@@ -179,6 +180,10 @@ static int sys_fchown(pr_fh_t *fh, int f
   return fchown(fd, uid, gid);
 }
 
+static int sys_lchown(pr_fs_t *fs, const char *path, uid_t uid, gid_t gid) {
+  return lchown(path, uid, gid);
+}
+
 /* We provide our own equivalent of access(2) here, rather than using
  * access(2) directly, because access(2) uses the real IDs, rather than
  * the effective IDs, of the process.
@@ -2477,6 +2482,170 @@ int pr_fsio_mkdir(const char *path, mode
   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;
@@ -3336,6 +3505,33 @@ int pr_fsio_fchown(pr_fh_t *fh, uid_t ui
   return res;
 }
 
+int pr_fsio_lchown(const char *name, uid_t uid, gid_t gid) {
+  int res;
+  pr_fs_t *fs;
+
+  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHOWN);
+  if (fs == NULL) {
+    return -1;
+  }
+
+  /* Find the first non-NULL custom lchown handler.  If there are none,
+   * use the system chown.
+   */
+  while (fs && fs->fs_next && !fs->lchown) {
+    fs = fs->fs_next;
+  }
+
+  pr_trace_msg(trace_channel, 8, "using %s lchown() for path '%s'",
+    fs->fs_name, name);
+  res = (fs->lchown)(fs, name, uid, gid);
+
+  if (res == 0) {
+    pr_fs_clear_cache();
+  }
+
+  return res;
+}
+
 int pr_fsio_access(const char *path, int mode, uid_t uid, gid_t gid,
     array_header *suppl_gids) {
   pr_fs_t *fs;
@@ -3955,6 +4151,7 @@ int init_fs(void) {
   root_fs->fchmod = sys_fchmod;
   root_fs->chown = sys_chown;
   root_fs->fchown = sys_fchown;
+  root_fs->lchown = sys_lchown;
   root_fs->access = sys_access;
   root_fs->faccess = sys_faccess;
   root_fs->utimes = sys_utimes;
@@ -4036,6 +4233,9 @@ static const char *get_fs_hooks_str(pool
   if (fs->chown)
     hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chown(2)", NULL);
 
+  if (fs->lchown)
+    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lchown(2)", NULL);
+
   if (fs->access)
     hooks = pstrcat(p, hooks, *hooks ? ", " : "", "access(2)", NULL);