summaryrefslogtreecommitdiff
path: root/source3/smbd/vfs.c
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2011-04-15 12:21:39 -0700
committerJeremy Allison <jra@samba.org>2011-04-15 12:21:39 -0700
commit525ccd589ede79e97e83699629a1d98538855803 (patch)
tree45c17974eceda805e7a5ec26b20a40dc46535bc1 /source3/smbd/vfs.c
parente4c4dcf102f61bf967b16c792ae15a90e9c27320 (diff)
downloadsamba-525ccd589ede79e97e83699629a1d98538855803.tar.gz
samba-525ccd589ede79e97e83699629a1d98538855803.tar.bz2
samba-525ccd589ede79e97e83699629a1d98538855803.zip
Ensure vfs_chown_fsp() is safe against races.
Diffstat (limited to 'source3/smbd/vfs.c')
-rw-r--r--source3/smbd/vfs.c76
1 files changed, 71 insertions, 5 deletions
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index d8c57a6a86..0438f7934e 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -1455,6 +1455,11 @@ int smb_vfs_call_lchown(struct vfs_handle_struct *handle, const char *path,
NTSTATUS vfs_chown_fsp(files_struct *fsp, uid_t uid, gid_t gid)
{
int ret;
+ bool as_root = false;
+ const char *path;
+ char *saved_dir = NULL;
+ char *parent_dir = NULL;
+ NTSTATUS status;
if (fsp->fh->fd != -1) {
/* Try fchown. */
@@ -1467,19 +1472,80 @@ NTSTATUS vfs_chown_fsp(files_struct *fsp, uid_t uid, gid_t gid)
}
}
- if (fsp->posix_open) {
+ as_root = (geteuid() == 0);
+
+ if (as_root) {
+ /*
+ * We are being asked to chown as root. Make
+ * sure we chdir() into the path to pin it,
+ * and always act using lchown to ensure we
+ * don't deref any symbolic links.
+ */
+ const char *final_component = NULL;
+ struct smb_filename local_fname;
+
+ saved_dir = vfs_GetWd(talloc_tos(),fsp->conn);
+ if (!saved_dir) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(0,("vfs_chown_fsp: failed to get "
+ "current working directory. Error was %s\n",
+ strerror(errno)));
+ return status;
+ }
+
+ if (!parent_dirname(talloc_tos(),
+ fsp->fsp_name->base_name,
+ &parent_dir,
+ &final_component)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* cd into the parent dir to pin it. */
+ ret = SMB_VFS_CHDIR(fsp->conn, parent_dir);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ ZERO_STRUCT(local_fname);
+ local_fname.base_name = CONST_DISCARD(char *,final_component);
+
+ /* Must use lstat here. */
+ ret = SMB_VFS_LSTAT(fsp->conn, &local_fname);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ /* Ensure it matches the fsp stat. */
+ if (!check_same_stat(&local_fname.st, &fsp->fsp_name->st)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ path = final_component;
+ } else {
+ path = fsp->fsp_name->base_name;
+ }
+
+ if (fsp->posix_open || as_root) {
ret = SMB_VFS_LCHOWN(fsp->conn,
- fsp->fsp_name->base_name,
+ path,
uid, gid);
} else {
ret = SMB_VFS_CHOWN(fsp->conn,
- fsp->fsp_name->base_name,
+ path,
uid, gid);
}
+
if (ret == 0) {
- return NT_STATUS_OK;
+ status = NT_STATUS_OK;
+ } else {
+ status = map_nt_error_from_unix(errno);
+ }
+
+ if (as_root) {
+ vfs_ChDir(fsp->conn,saved_dir);
+ TALLOC_FREE(saved_dir);
+ TALLOC_FREE(parent_dir);
}
- return map_nt_error_from_unix(errno);
+ return status;
}
int smb_vfs_call_chdir(struct vfs_handle_struct *handle, const char *path)