BOOL bBlockingLocks;
BOOL bInheritPerms;
BOOL bInheritACLS;
+ BOOL bInheritOwner;
BOOL bMSDfsRoot;
BOOL bUseClientDriver;
BOOL bDefaultDevmode;
True, /* bBlockingLocks */
False, /* bInheritPerms */
False, /* bInheritACLS */
+ False, /* bInheritOwner */
False, /* bMSDfsRoot */
False, /* bUseClientDriver */
False, /* bDefaultDevmode */
{"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},
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)
}
}
+/****************************************************************************
+ 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.
****************************************************************************/
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;
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;
* 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;
}
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;
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;
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);
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 ) );