]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
r5792: Added new parameter "inherit owner". If set on a share, the created file/directory
authorJeremy Allison <jra@samba.org>
Tue, 15 Mar 2005 01:19:58 +0000 (01:19 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:56:01 +0000 (10:56 -0500)
will be owned by the same uid as the containing directory. Doing this for directories
in a race-free mannor has only been tested on Linux (it depends on being able to open
a directory and then do a fchown on that file descriptor). If this functionality is
not available then the code silently downgrades to not changing the ownership of a
new directory. This new parameter (docs to follow) finally makes it possible to create
"drop boxes" on Samba, which requires all files within a directory to be commonly owned.
A HOWTO on how to use this will follow.
Jeremy.

source/param/loadparm.c
source/printing/nt_printing.c
source/smbd/filename.c
source/smbd/open.c
source/smbd/reply.c

index d86f4b391a2a0621a2f02f55b684bfef0a205f60..a75a19f85caa4a1b40472a2f5b86f1ba37fd92f6 100644 (file)
@@ -413,6 +413,7 @@ typedef struct
        BOOL bBlockingLocks;
        BOOL bInheritPerms;
        BOOL bInheritACLS;
+       BOOL bInheritOwner;
        BOOL bMSDfsRoot;
        BOOL bUseClientDriver;
        BOOL bDefaultDevmode;
@@ -539,6 +540,7 @@ static service sDefault = {
        True,                   /* bBlockingLocks */
        False,                  /* bInheritPerms */
        False,                  /* bInheritACLS */
+       False,                  /* bInheritOwner */
        False,                  /* bMSDfsRoot */
        False,                  /* bUseClientDriver */
        False,                  /* bDefaultDevmode */
@@ -864,6 +866,7 @@ static struct parm_struct parm_table[] = {
        {"force unknown acl user", P_BOOL, P_LOCAL, &sDefault.bForceUnknownAclUser, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
        {"inherit permissions", P_BOOL, P_LOCAL, &sDefault.bInheritPerms, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE}, 
        {"inherit acls", P_BOOL, P_LOCAL, &sDefault.bInheritACLS, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE}, 
+       {"inherit owner", P_BOOL, P_LOCAL, &sDefault.bInheritOwner, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE}, 
        {"guest only", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE}, 
        {"only guest", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL, NULL, FLAG_HIDE}, 
 
@@ -1907,6 +1910,7 @@ FN_LOCAL_BOOL(lp_fake_dir_create_times, bFakeDirCreateTimes)
 FN_LOCAL_BOOL(lp_blocking_locks, bBlockingLocks)
 FN_LOCAL_BOOL(lp_inherit_perms, bInheritPerms)
 FN_LOCAL_BOOL(lp_inherit_acls, bInheritACLS)
+FN_LOCAL_BOOL(lp_inherit_owner, bInheritOwner)
 FN_LOCAL_BOOL(lp_use_client_driver, bUseClientDriver)
 FN_LOCAL_BOOL(lp_default_devmode, bDefaultDevmode)
 FN_LOCAL_BOOL(lp_force_printername, bForcePrintername)
index ad911c3b05b05a506bfdeb077abd227f097777c8..7faeb60efa257a174655087418acae8b45fc7799 100644 (file)
@@ -1494,7 +1494,7 @@ BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
        DEBUG(5,("Creating first directory\n"));
        slprintf(new_dir, sizeof(new_dir)-1, "%s/%d", architecture, driver->cversion);
        driver_unix_convert(new_dir, conn, NULL, &bad_path, &st);
-       mkdir_internal(conn, new_dir);
+       mkdir_internal(conn, new_dir, bad_path);
 
        /* For each driver file, archi\filexxx.yyy, if there is a duplicate file
         * listed for this driver which has already been moved, skip it (note:
index 8c484dd232af19279198e55be77703ccdbddbe36..9ca2c0efae9ed6aaa1cce6c4c76ef5eeab0b346b 100644 (file)
@@ -397,7 +397,7 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen
  a valid one for the user to access.
 ****************************************************************************/
 
-BOOL check_name(pstring name,connection_struct *conn)
+BOOL check_name(const pstring name,connection_struct *conn)
 {
        BOOL ret = True;
 
index a552dc5b610174ff8296d876128fe07b683a70d5..3124526c7efd973afe43bd55a14ed5371f0b0671 100644 (file)
@@ -82,6 +82,103 @@ static void check_for_pipe(const char *fname)
        }
 }
 
+/****************************************************************************
+ Change the ownership of a file to that of the parent directory.
+ Do this by fd if possible.
+****************************************************************************/
+
+void change_owner_to_parent(connection_struct *conn, files_struct *fsp, const char *fname, SMB_STRUCT_STAT *psbuf)
+{
+       const char *parent_path = parent_dirname(fname);
+       SMB_STRUCT_STAT parent_st;
+       int ret;
+
+       ret = SMB_VFS_STAT(conn, parent_path, &parent_st);
+       if (ret == -1) {
+               DEBUG(0,("change_owner_to_parent: failed to stat parent directory %s. Error was %s\n",
+                       parent_path, strerror(errno) ));
+               return;
+       }
+
+       if (fsp && fsp->fd != -1) {
+               become_root();
+               ret = SMB_VFS_FCHOWN(fsp, fsp->fd, parent_st.st_uid, (gid_t)-1);
+               unbecome_root();
+               if (ret == -1) {
+                       DEBUG(0,("change_owner_to_parent: failed to fchown file %s to parent directory uid %u. \
+Error was %s\n",
+                               fname, (unsigned int)parent_st.st_uid, strerror(errno) ));
+               }
+
+               DEBUG(10,("change_owner_to_parent: changed new file %s to parent directory uid %u.\n",
+                       fname, (unsigned int)parent_st.st_uid ));
+
+       } else {
+               /* We've already done an lstat into psbuf, and we know it's a directory. If
+                  we can do an open/fstat and the dev/ino are the same then we can safely
+                  fchown without races. This works under Linux - but should just fail gracefully
+                  if any step on the way fails. JRA */
+
+               BOOL need_close_fsp = False;
+               SMB_STRUCT_STAT sbuf;
+               int fd = -1;
+
+               if (!fsp) {
+                       int action;
+                       fsp = open_directory(conn, fname, psbuf, FILE_GENERIC_READ,
+                                       SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
+                                       FILE_EXISTS_OPEN, &action);
+                       if (!fsp) {
+                               DEBUG(10,("change_owner_to_parent: open_directory on %s failed. Error was %s\n",
+                                       fname, strerror(errno) ));
+                               return;
+                       }
+                       need_close_fsp = True;
+               } 
+               fd = SMB_VFS_OPEN(conn,fname,O_RDONLY,0);
+               if (fd == -1) {
+                       DEBUG(10,("change_owner_to_parent: failed to VFS_OPEN directory %s. Error was %s\n",
+                               fname, strerror(errno) ));
+                       goto out;
+               }
+               ret = SMB_VFS_FSTAT(fsp,fd,&sbuf);
+               if (ret == -1) {
+                       DEBUG(10,("change_owner_to_parent: failed to VFS_STAT directory %s. Error was %s\n",
+                               fname, strerror(errno) ));
+                       goto out;
+               }
+
+               /* Ensure we're pointing at the same place. */
+               if (sbuf.st_dev != psbuf->st_dev || sbuf.st_ino != psbuf->st_ino || !S_ISDIR(sbuf.st_mode)) {
+                       DEBUG(0,("change_owner_to_parent: device/inode/mode on director %s changed. Refusing to fchown !\n",
+                               fname ));
+                       goto out;
+               }
+
+               become_root();
+               ret = SMB_VFS_FCHOWN(fsp, fd, parent_st.st_uid, (gid_t)-1);
+               unbecome_root();
+               if (ret == -1) {
+                       DEBUG(10,("change_owner_to_parent: failed to fchown directory %s to parent directory uid %u. \
+Error was %s\n",
+                               fname, (unsigned int)parent_st.st_uid, strerror(errno) ));
+                       goto out;
+               }
+
+               DEBUG(10,("change_owner_to_parent: changed new directory %s to parent directory uid %u.\n",
+                       fname, (unsigned int)parent_st.st_uid ));
+
+  out:
+
+               if (fd != -1) {
+                       SMB_VFS_CLOSE(fsp,fd);
+               }
+               if (need_close_fsp) {
+                       close_file(fsp, False);
+               }
+       }
+}
+
 /****************************************************************************
  Open a file.
 ****************************************************************************/
@@ -1391,8 +1488,13 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
                action = FILE_WAS_OPENED;
        if (file_existed && (flags2 & O_TRUNC))
                action = FILE_WAS_OVERWRITTEN;
-       if (!file_existed) 
+       if (!file_existed) {
                action = FILE_WAS_CREATED;
+               /* Change the owner if required. */
+               if (lp_inherit_owner(SNUM(conn))) {
+                       change_owner_to_parent(conn, fsp, fsp->fsp_name, psbuf);
+               }
+       }
 
        if (paction) {
                *paction = action;
@@ -1547,7 +1649,7 @@ int close_file_fchmod(files_struct *fsp)
  Open a directory from an NT SMB call.
 ****************************************************************************/
 
-files_struct *open_directory(connection_struct *conn, char *fname, SMB_STRUCT_STAT *psbuf,
+files_struct *open_directory(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf,
                        uint32 desired_access, int share_mode, int smb_ofun, int *action)
 {
        extern struct current_user current_user;
@@ -1585,39 +1687,29 @@ files_struct *open_directory(connection_struct *conn, char *fname, SMB_STRUCT_ST
                         * Try and create the directory.
                         */
 
-                       if(!CAN_WRITE(conn)) {
-                               DEBUG(2,("open_directory: failing create on read-only share\n"));
-                               file_free(fsp);
-                               errno = EACCES;
-                               return NULL;
-                       }
+                       /* We know bad_path is false as it's caught earlier. */
 
-                       if (ms_has_wild(fname))  {
-                               file_free(fsp);
-                               DEBUG(5,("open_directory: failing create on filename %s with wildcards\n", fname));
-                               unix_ERR_class = ERRDOS;
-                               unix_ERR_code = ERRinvalidname;
-                               unix_ERR_ntstatus = NT_STATUS_OBJECT_NAME_INVALID;
-                               return NULL;
-                       }
+                       NTSTATUS status = mkdir_internal(conn, fname, False);
 
-                       if( strchr_m(fname, ':')) {
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(2,("open_directory: unable to create %s. Error was %s\n",
+                                        fname, strerror(errno) ));
                                file_free(fsp);
-                               DEBUG(5,("open_directory: failing create on filename %s with colon in name\n", fname));
-                               unix_ERR_class = ERRDOS;
-                               unix_ERR_code = ERRinvalidname;
-                               unix_ERR_ntstatus = NT_STATUS_NOT_A_DIRECTORY;
+                               /* Ensure we return the correct NT status to the client. */
+                               unix_ERR_ntstatus = status;
                                return NULL;
                        }
 
-                       if(vfs_MkDir(conn,fname, unix_mode(conn,aDIR, fname, True)) < 0) {
-                               DEBUG(2,("open_directory: unable to create %s. Error was %s\n",
-                                        fname, strerror(errno) ));
+                       /* Ensure we're checking for a symlink here.... */
+                       /* We don't want to get caught by a symlink racer. */
+
+                       if(SMB_VFS_LSTAT(conn,fname, psbuf) != 0) {
                                file_free(fsp);
                                return NULL;
                        }
 
-                       if(SMB_VFS_STAT(conn,fname, psbuf) != 0) {
+                       if(!S_ISDIR(psbuf->st_mode)) {
+                               DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
                                file_free(fsp);
                                return NULL;
                        }
@@ -1674,13 +1766,19 @@ files_struct *open_directory(connection_struct *conn, char *fname, SMB_STRUCT_ST
        string_set(&fsp->fsp_name,fname);
 
        if (delete_on_close) {
-               NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close, 0);
+               NTSTATUS status = set_delete_on_close_internal(fsp, delete_on_close, 0);
 
-               if (NT_STATUS_V(result) !=  NT_STATUS_V(NT_STATUS_OK)) {
+               if (!NT_STATUS_IS_OK(status)) {
                        file_free(fsp);
                        return NULL;
                }
        }
+
+       /* Change the owner if required. */
+       if ((*action == FILE_WAS_CREATED) && lp_inherit_owner(SNUM(conn))) {
+               change_owner_to_parent(conn, fsp, fsp->fsp_name, psbuf);
+       }
+
        conn->num_files_open++;
 
        return fsp;
index f149b79f793bd7a5a476c9199cd6556a972f58dd..376b42e5fd717559d1929d5f968194c2c81766f5 100644 (file)
@@ -3303,29 +3303,47 @@ int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_
  code. 
 ****************************************************************************/
 
-NTSTATUS mkdir_internal(connection_struct *conn, pstring directory)
+NTSTATUS mkdir_internal(connection_struct *conn, const pstring directory, BOOL bad_path)
 {
-       BOOL bad_path = False;
-       SMB_STRUCT_STAT sbuf;
        int ret= -1;
        
-       unix_convert(directory,conn,0,&bad_path,&sbuf);
-
-       if( strchr_m(directory, ':')) {
-               return NT_STATUS_NOT_A_DIRECTORY;
+       if(!CAN_WRITE(conn)) {
+               DEBUG(5,("mkdir_internal: failing create on read-only share %s\n", lp_servicename(SNUM(conn))));
+               errno = EACCES;
+               return map_nt_error_from_unix(errno);
        }
 
+       /* The following 2 clauses set explicit DOS error codes. JRA. */
        if (ms_has_wild(directory)) {
+               DEBUG(5,("mkdir_internal: failing create on filename %s with wildcards\n", directory));
+               unix_ERR_class = ERRDOS;
+               unix_ERR_code = ERRinvalidname;
                return NT_STATUS_OBJECT_NAME_INVALID;
        }
 
+       if( strchr_m(directory, ':')) {
+               DEBUG(5,("mkdir_internal: failing create on filename %s with colon in name\n", directory));
+               unix_ERR_class = ERRDOS;
+               unix_ERR_code = ERRinvalidname;
+               return NT_STATUS_NOT_A_DIRECTORY;
+       }
+
        if (bad_path) {
                return NT_STATUS_OBJECT_PATH_NOT_FOUND;
        }
 
-       if (check_name(directory, conn))
-               ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory,True));
-       
+       if (!check_name(directory, conn)) {
+               if(errno == ENOENT) {
+                       if (bad_path) {
+                               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                       } else {
+                               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+                       }
+               }
+               return map_nt_error_from_unix(errno);
+       }
+
+       ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory,True));
        if (ret == -1) {
                if(errno == ENOENT) {
                        return NT_STATUS_OBJECT_NAME_NOT_FOUND;
@@ -3345,6 +3363,9 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
        pstring directory;
        int outsize;
        NTSTATUS status;
+       BOOL bad_path = False;
+       SMB_STRUCT_STAT sbuf;
+
        START_PROFILE(SMBmkdir);
  
        srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status, False);
@@ -3355,12 +3376,32 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
 
        RESOLVE_DFSPATH(directory, conn, inbuf, outbuf);
 
-       status = mkdir_internal(conn, directory);
+       unix_convert(directory,conn,0,&bad_path,&sbuf);
+
+       status = mkdir_internal(conn, directory,bad_path);
        if (!NT_STATUS_IS_OK(status)) {
                END_PROFILE(SMBmkdir);
                return ERROR_NT(status);
        }
 
+       if (lp_inherit_owner(SNUM(conn))) {
+               /* Ensure we're checking for a symlink here.... */
+               /* We don't want to get caught by a symlink racer. */
+                                                                                                                                                   
+               if(SMB_VFS_LSTAT(conn,directory, &sbuf) != 0) {
+                       END_PROFILE(SMBmkdir);
+                       return(UNIXERROR(ERRDOS,ERRnoaccess));
+               }
+                                                                                                                                                   
+               if(!S_ISDIR(sbuf.st_mode)) {
+                       DEBUG(0,("reply_mkdir: %s is not a directory !\n", directory ));
+                       END_PROFILE(SMBmkdir);
+                       return(UNIXERROR(ERRDOS,ERRnoaccess));
+               }
+
+               change_owner_to_parent(conn, NULL, directory, &sbuf);
+       }
+
        outsize = set_message(outbuf,0,0,True);
 
        DEBUG( 3, ( "mkdir %s ret=%d\n", directory, outsize ) );