Subject: import directories

From: Sean H Davies <[email-masked]>
Date: Wed, 29 Apr 1998 15:51:50 +0100

I have been able to setup an import directory where anonymous users are
able to drop off files. The directory is unlistable but writable by
anonymous.
Files are written ( due to the upload directive in ftpaccess ) with an
owner of unknown but group of ftp.
However, if anonymous users create directories in the import hierarchy,
then the owner and group is ftp:staff. I have used the -u option in
inetd to ensure that the permissions are correct but have been warned
about directories belonging to ftp.

How do I ensure that directories created in the import hierarchy by
anonymous are of unknown:ftp ??

-- 
Sean Davies
Hursley Midrange & Dist Systems   +-----------------------------------+ 
IBM UK Laboratories Ltd.          | VNET:     sean@hursley            | 
Hursley Park,                     | Internet: [email-masked]    | 
Winchester, Hants,                +-----------------------------------+ 
England SO21 2JN
Internal: 7-248974                International: +44 1962-818974

---

From: [email-masked] (Andy Church)
Date: Wed, 29 Apr 1998 22:21:37 EDT

>then the owner and group is ftp:staff.

     This is a bug in wu-ftpd.  Try the following patch (also available as
ftp://achurch.dragonfire.net/wu-ftpd/mkdir.patch).  Note that this patch is
untested, but looks like it should work.

*** src/extensions.c.old	Tue Apr 28 01:09:03 1998
--- src/extensions.c	Wed Apr 29 22:17:34 1998
***************
*** 949,960 ****
  
  int
  #ifdef __STDC__
! dir_check(char *name, uid_t *uid, gid_t *gid, int *valid)
  #else
! dir_check(name,uid,gid,valid)
  char *name;
  uid_t *uid;
  gid_t *gid;
  int *valid;
  #endif
  {
--- 949,961 ----
  
  int
  #ifdef __STDC__
! dir_check(char *name, uid_t *uid, gid_t *gid, int *d_mode, int *valid)
  #else
! dir_check(name,uid,gid,d_mode,valid)
  char *name;
  uid_t *uid;
  gid_t *gid;
+ int *d_mode;
  int *valid;
  #endif
  {
***************
*** 965,971 ****
    char *ap2 = NULL,
         *ap3 = NULL,
         *ap4 = NULL,
!        *ap6 = NULL;
    char cwdir[BUFSIZ];
    char path[BUFSIZ];
    char *sp;
--- 966,974 ----
    char *ap2 = NULL,
         *ap3 = NULL,
         *ap4 = NULL,
!        *ap5 = NULL,
!        *ap6 = NULL,
!        *ap7 = NULL;
    char cwdir[BUFSIZ];
    char path[BUFSIZ];
    char *sp;
***************
*** 983,989 ****
      return(-1);
    }
    while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
!       if ( (!strcmp(ARG0, pw->pw_dir)) &&
             ((i = path_compare(ARG1, cwdir)) >= match_value) ) {
            match_value = i;
            ap2 = ARG2;
--- 986,992 ----
      return(-1);
    }
    while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
!       if ( (!strcmp(ARG0, "*") || !strcmp(ARG0, pw->pw_dir)) &&
             ((i = path_compare(ARG1, cwdir)) >= match_value) ) {
            match_value = i;
            ap2 = ARG2;
***************
*** 991,998 ****
--- 994,1005 ----
            else       ap3 = NULL;
            if (ARG4)  ap4 = ARG4;
            else       ap4 = NULL;
+           if (ARG5)  ap5 = ARG5;
+           else       ap5 = NULL;
            if (ARG6)  ap6 = ARG6;
            else       ap6 = NULL;
+           if (ARG7)  ap7 = ARG7;
+           else       ap7 = NULL;
        }
    }
    if ( (ap3 && !strcasecmp(ap3, "nodirs")) ||
***************
*** 1004,1011 ****
       *uid = atoi(ap3);    /* the uid  */
    if (ap4) {
       *gid = atoi(ap4);    /* the gid */
-      *valid = 1;
     }
    return(1);
  }
  
--- 1011,1030 ----
       *uid = atoi(ap3);    /* the uid  */
    if (ap4) {
       *gid = atoi(ap4);    /* the gid */
     }
+   if (ap7) {
+      sscanf(ap7, "%o", d_mode); /* the mode */
+      *valid = 1;
+   } else if (ap5) {
+      sscanf(ap7, "%o", d_mode); /* take file mode, and add search permission */
+      if (*d_mode & 0600)
+         *d_mode |= 0100;
+      if (*d_mode & 0060)
+         *d_mode |= 0010;
+      if (*d_mode & 0006)
+         *d_mode |= 0001;
+      *valid = 1;
+   }
    return(1);
  }
  
*** src/ftpd.c.old	Tue Apr 28 01:09:04 1998
--- src/ftpd.c	Wed Apr 29 21:53:43 1998
***************
*** 3107,3118 ****
  {
  	uid_t uid;
  	gid_t gid;
  	int   valid;
  
      /*
       * check the directory, can we mkdir here?
       */
!     if ( (dir_check(name, &uid, &gid, &valid)) <= 0 )
          return;
  
      /*
--- 3107,3119 ----
  {
  	uid_t uid;
  	gid_t gid;
+ 	int   mode;
  	int   valid;
  
      /*
       * check the directory, can we mkdir here?
       */
!     if ( (dir_check(name, &uid, &gid, &mode, &valid)) <= 0 )
          return;
  
      /*
***************
*** 3121,3133 ****
      if ( (fn_check(name)) <= 0 )
          return;
  
!     if (mkdir(name, 0777) < 0) {
          if (errno == EEXIST)
              perror_reply(553, name);
          else
              perror_reply(550, name);
  	return;
      }
      reply(257, "MKD command successful.");
  }
  
--- 3122,3160 ----
      if ( (fn_check(name)) <= 0 )
          return;
  
!     if (valid <= 0)
!         mode = 0777;
! 
!     if (mkdir(name, mode) < 0) {
          if (errno == EEXIST)
              perror_reply(553, name);
          else
              perror_reply(550, name);
  	return;
      }
+ 
+     if (valid > 0) {
+         uid_t oldid = geteuid();
+         delay_signaling();
+         (void) seteuid((uid_t) 0);
+         /*
+          * There's a small file race here: if someone deletes the directory
+          * we just created and creates another file or directory in its
+          * place before we get a chance to chown() it, that new file or
+          * directory gets the appropriate modes.  I don't think you could
+          * actually do anything useful with this, though (remember, set-id
+          * bits are cleared on chown())...
+          */
+         if ((chown(name, uid, gid)) < 0) {
+             (void) seteuid(oldid);
+             enable_signaling();
+             perror_reply(550, "fchown");
+             return;
+         }
+         (void) seteuid(oldid);
+         enable_signaling();
+     }
+ 
      reply(257, "MKD command successful.");
  }

  --Andy Church                  | If Bell Atlantic really is the heart
    [email-masked]       | of communication, then it desperately
    www.dragonfire.net/~achurch/ | needs a quadruple bypass.