diff options
-rw-r--r-- | source3/param/loadparm.c | 4 | ||||
-rw-r--r-- | source3/printing/nt_printing.c | 2 | ||||
-rw-r--r-- | source3/smbd/filename.c | 2 | ||||
-rw-r--r-- | source3/smbd/open.c | 152 | ||||
-rw-r--r-- | source3/smbd/reply.c | 63 |
5 files changed, 183 insertions, 40 deletions
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index d86f4b391a..a75a19f85c 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -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) diff --git a/source3/printing/nt_printing.c b/source3/printing/nt_printing.c index ad911c3b05..7faeb60efa 100644 --- a/source3/printing/nt_printing.c +++ b/source3/printing/nt_printing.c @@ -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: diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 8c484dd232..9ca2c0efae 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -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; diff --git a/source3/smbd/open.c b/source3/smbd/open.c index a552dc5b61..3124526c7e 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -83,6 +83,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; diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index f149b79f79..376b42e5fd 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -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 ) ); |