X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fsmbd%2Fopen.c;h=17d9f6f8fb69cce4bf69abe3a6880365d4a71857;hb=ce30a7ae7be70eb4cfac5454d453c32c71e4a5ed;hp=b6a75bb16f73f8142ebfa4a22d0dec5af233b926;hpb=4ccb7e5bddd16c2b1cc7c057d8219c271ff5fe8c;p=mat%2Fsamba.git diff --git a/source3/smbd/open.c b/source3/smbd/open.c index b6a75bb16f..17d9f6f8fb 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -4,28 +4,32 @@ Copyright (C) Andrew Tridgell 1992-1998 Copyright (C) Jeremy Allison 2001-2004 Copyright (C) Volker Lendecke 2005 - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" +#include "system/filesys.h" #include "printing.h" +#include "smbd/smbd.h" #include "smbd/globals.h" #include "fake_file.h" -#include "librpc/gen_ndr/messaging.h" #include "../libcli/security/security.h" #include "../librpc/gen_ndr/ndr_security.h" +#include "../librpc/gen_ndr/open_files.h" +#include "auth.h" +#include "messages.h" extern const struct generic_mapping file_generic_mapping; @@ -35,44 +39,73 @@ struct deferred_open_record { }; /**************************************************************************** - SMB1 file varient of se_access_check. Never test FILE_READ_ATTRIBUTES. + If the requester wanted DELETE_ACCESS and was rejected because + the file ACL didn't include DELETE_ACCESS, see if the parent ACL + ovverrides this. ****************************************************************************/ -NTSTATUS smb1_file_se_access_check(struct connection_struct *conn, - const struct security_descriptor *sd, - const struct security_token *token, - uint32_t access_desired, - uint32_t *access_granted) +static bool parent_override_delete(connection_struct *conn, + const struct smb_filename *smb_fname, + uint32_t access_mask, + uint32_t rejected_mask) { - *access_granted = 0; - - if (get_current_uid(conn) == (uid_t)0) { - /* I'm sorry sir, I didn't know you were root... */ - *access_granted = access_desired; - if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) { - *access_granted |= FILE_GENERIC_ALL; - } - return NT_STATUS_OK; + if ((access_mask & DELETE_ACCESS) && + (rejected_mask & DELETE_ACCESS) && + can_delete_file_in_directory(conn, smb_fname)) { + return true; } - - return se_access_check(sd, - token, - (access_desired & ~FILE_READ_ATTRIBUTES), - access_granted); + return false; } /**************************************************************************** Check if we have open rights. ****************************************************************************/ -NTSTATUS smbd_check_open_rights(struct connection_struct *conn, +NTSTATUS smbd_check_access_rights(struct connection_struct *conn, const struct smb_filename *smb_fname, - uint32_t access_mask, - uint32_t *access_granted) + uint32_t access_mask) { /* Check if we have rights to open. */ NTSTATUS status; struct security_descriptor *sd = NULL; + uint32_t rejected_share_access; + uint32_t rejected_mask = 0; + + rejected_share_access = access_mask & ~(conn->share_access); + + if (rejected_share_access) { + DEBUG(10, ("smbd_check_access_rights: rejected share access 0x%x " + "on %s (0x%x)\n", + (unsigned int)access_mask, + smb_fname_str_dbg(smb_fname), + (unsigned int)rejected_share_access )); + return NT_STATUS_ACCESS_DENIED; + } + + if (get_current_uid(conn) == (uid_t)0) { + /* I'm sorry sir, I didn't know you were root... */ + DEBUG(10,("smbd_check_access_rights: root override " + "on %s. Granting 0x%x\n", + smb_fname_str_dbg(smb_fname), + (unsigned int)access_mask )); + return NT_STATUS_OK; + } + + if ((access_mask & DELETE_ACCESS) && !lp_acl_check_permissions(SNUM(conn))) { + DEBUG(10,("smbd_check_access_rights: not checking ACL " + "on DELETE_ACCESS on file %s. Granting 0x%x\n", + smb_fname_str_dbg(smb_fname), + (unsigned int)access_mask )); + return NT_STATUS_OK; + } + + if (access_mask == DELETE_ACCESS && S_ISLNK(smb_fname->st.st_ex_mode)) { + /* We can always delete a symlink. */ + DEBUG(10,("smbd_check_access_rights: not checking ACL " + "on DELETE_ACCESS on symlink %s.\n", + smb_fname_str_dbg(smb_fname) )); + return NT_STATUS_OK; + } status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name, (SECINFO_OWNER | @@ -80,29 +113,32 @@ NTSTATUS smbd_check_open_rights(struct connection_struct *conn, SECINFO_DACL),&sd); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("smbd_check_open_rights: Could not get acl " + DEBUG(10, ("smbd_check_access_rights: Could not get acl " "on %s: %s\n", smb_fname_str_dbg(smb_fname), nt_errstr(status))); return status; } - status = smb1_file_se_access_check(conn, - sd, + /* + * Never test FILE_READ_ATTRIBUTES. se_access_check() also takes care of + * owner WRITE_DAC and READ_CONTROL. + */ + status = se_access_check(sd, get_current_nttok(conn), - access_mask, - access_granted); + (access_mask & ~FILE_READ_ATTRIBUTES), + &rejected_mask); - DEBUG(10,("smbd_check_open_rights: file %s requesting " + DEBUG(10,("smbd_check_access_rights: file %s requesting " "0x%x returning 0x%x (%s)\n", smb_fname_str_dbg(smb_fname), (unsigned int)access_mask, - (unsigned int)*access_granted, + (unsigned int)rejected_mask, nt_errstr(status) )); if (!NT_STATUS_IS_OK(status)) { if (DEBUGLEVEL >= 10) { - DEBUG(10,("smbd_check_open_rights: acl for %s is:\n", + DEBUG(10,("smbd_check_access_rights: acl for %s is:\n", smb_fname_str_dbg(smb_fname) )); NDR_PRINT_DEBUG(security_descriptor, sd); } @@ -110,7 +146,119 @@ NTSTATUS smbd_check_open_rights(struct connection_struct *conn, TALLOC_FREE(sd); - return status; + if (NT_STATUS_IS_OK(status) || + !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + return status; + } + + /* Here we know status == NT_STATUS_ACCESS_DENIED. */ + if ((access_mask & FILE_WRITE_ATTRIBUTES) && + (rejected_mask & FILE_WRITE_ATTRIBUTES) && + (lp_map_readonly(SNUM(conn)) || + lp_map_archive(SNUM(conn)) || + lp_map_hidden(SNUM(conn)) || + lp_map_system(SNUM(conn)))) { + rejected_mask &= ~FILE_WRITE_ATTRIBUTES; + + DEBUG(10,("smbd_check_access_rights: " + "overrode " + "FILE_WRITE_ATTRIBUTES " + "on file %s\n", + smb_fname_str_dbg(smb_fname))); + } + + if (parent_override_delete(conn, + smb_fname, + access_mask, + rejected_mask)) { + /* Were we trying to do an open + * for delete and didn't get DELETE + * access (only) ? Check if the + * directory allows DELETE_CHILD. + * See here: + * http://blogs.msdn.com/oldnewthing/archive/2004/06/04/148426.aspx + * for details. */ + + rejected_mask &= ~DELETE_ACCESS; + + DEBUG(10,("smbd_check_access_rights: " + "overrode " + "DELETE_ACCESS on " + "file %s\n", + smb_fname_str_dbg(smb_fname))); + } + + if (rejected_mask != 0) { + return NT_STATUS_ACCESS_DENIED; + } else { + return NT_STATUS_OK; + } +} + +static NTSTATUS check_parent_access(struct connection_struct *conn, + struct smb_filename *smb_fname, + uint32_t access_mask, + char **pp_parent_dir) +{ + NTSTATUS status; + char *parent_dir = NULL; + struct security_descriptor *parent_sd = NULL; + uint32_t access_granted = 0; + + if (!parent_dirname(talloc_tos(), + smb_fname->base_name, + &parent_dir, + NULL)) { + return NT_STATUS_NO_MEMORY; + } + + if (pp_parent_dir) { + *pp_parent_dir = parent_dir; + } + + if (get_current_uid(conn) == (uid_t)0) { + /* I'm sorry sir, I didn't know you were root... */ + DEBUG(10,("check_parent_access: root override " + "on %s. Granting 0x%x\n", + smb_fname_str_dbg(smb_fname), + (unsigned int)access_mask )); + return NT_STATUS_OK; + } + + status = SMB_VFS_GET_NT_ACL(conn, + parent_dir, + SECINFO_DACL, + &parent_sd); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5,("check_parent_access: SMB_VFS_GET_NT_ACL failed for " + "%s with error %s\n", + parent_dir, + nt_errstr(status))); + return status; + } + + /* + * Never test FILE_READ_ATTRIBUTES. se_access_check() also takes care of + * owner WRITE_DAC and READ_CONTROL. + */ + status = se_access_check(parent_sd, + get_current_nttok(conn), + (access_mask & ~FILE_READ_ATTRIBUTES), + &access_granted); + if(!NT_STATUS_IS_OK(status)) { + DEBUG(5,("check_parent_access: access check " + "on directory %s for " + "path %s for mask 0x%x returned (0x%x) %s\n", + parent_dir, + smb_fname->base_name, + access_mask, + access_granted, + nt_errstr(status) )); + return status; + } + + return NT_STATUS_OK; } /**************************************************************************** @@ -211,6 +359,17 @@ void change_file_owner_to_parent(connection_struct *conn, "directory %s. Error was %s\n", smb_fname_str_dbg(smb_fname_parent), strerror(errno))); + TALLOC_FREE(smb_fname_parent); + return; + } + + if (smb_fname_parent->st.st_ex_uid == fsp->fsp_name->st.st_ex_uid) { + /* Already this uid - no need to change. */ + DEBUG(10,("change_file_owner_to_parent: file %s " + "is already owned by uid %d\n", + fsp_str_dbg(fsp), + (int)fsp->fsp_name->st.st_ex_uid )); + TALLOC_FREE(smb_fname_parent); return; } @@ -223,12 +382,14 @@ void change_file_owner_to_parent(connection_struct *conn, "was %s\n", fsp_str_dbg(fsp), (unsigned int)smb_fname_parent->st.st_ex_uid, strerror(errno) )); + } else { + DEBUG(10,("change_file_owner_to_parent: changed new file %s to " + "parent directory uid %u.\n", fsp_str_dbg(fsp), + (unsigned int)smb_fname_parent->st.st_ex_uid)); + /* Ensure the uid entry is updated. */ + fsp->fsp_name->st.st_ex_uid = smb_fname_parent->st.st_ex_uid; } - DEBUG(10,("change_file_owner_to_parent: changed new file %s to " - "parent directory uid %u.\n", fsp_str_dbg(fsp), - (unsigned int)smb_fname_parent->st.st_ex_uid)); - TALLOC_FREE(smb_fname_parent); } @@ -302,17 +463,26 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn, /* Ensure we're pointing at the same place. */ if (smb_fname_cwd->st.st_ex_dev != psbuf->st_ex_dev || - smb_fname_cwd->st.st_ex_ino != psbuf->st_ex_ino || - smb_fname_cwd->st.st_ex_mode != psbuf->st_ex_mode ) { + smb_fname_cwd->st.st_ex_ino != psbuf->st_ex_ino) { DEBUG(0,("change_dir_owner_to_parent: " - "device/inode/mode on directory %s changed. " + "device/inode on directory %s changed. " "Refusing to chown !\n", fname )); status = NT_STATUS_ACCESS_DENIED; goto chdir; } + if (smb_fname_parent->st.st_ex_uid == smb_fname_cwd->st.st_ex_uid) { + /* Already this uid - no need to change. */ + DEBUG(10,("change_dir_owner_to_parent: directory %s " + "is already owned by uid %d\n", + fname, + (int)smb_fname_cwd->st.st_ex_uid )); + status = NT_STATUS_OK; + goto chdir; + } + become_root(); - ret = SMB_VFS_CHOWN(conn, ".", smb_fname_parent->st.st_ex_uid, + ret = SMB_VFS_LCHOWN(conn, ".", smb_fname_parent->st.st_ex_uid, (gid_t)-1); unbecome_root(); if (ret == -1) { @@ -322,13 +492,14 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn, "Error was %s\n", fname, (unsigned int)smb_fname_parent->st.st_ex_uid, strerror(errno) )); - goto chdir; + } else { + DEBUG(10,("change_dir_owner_to_parent: changed ownership of new " + "directory %s to parent directory uid %u.\n", + fname, (unsigned int)smb_fname_parent->st.st_ex_uid )); + /* Ensure the uid entry is updated. */ + psbuf->st_ex_uid = smb_fname_parent->st.st_ex_uid; } - DEBUG(10,("change_dir_owner_to_parent: changed ownership of new " - "directory %s to parent directory uid %u.\n", - fname, (unsigned int)smb_fname_parent->st.st_ex_uid )); - chdir: vfs_ChDir(conn,saved_dir); out: @@ -355,6 +526,7 @@ static NTSTATUS open_file(files_struct *fsp, int accmode = (flags & O_ACCMODE); int local_flags = flags; bool file_existed = VALID_STAT(fsp->fsp_name->st); + bool file_created = false; fsp->fh->fd = -1; errno = EPERM; @@ -444,6 +616,35 @@ static NTSTATUS open_file(files_struct *fsp, return NT_STATUS_OBJECT_NAME_INVALID; } + /* Can we access this file ? */ + if (!fsp->base_fsp) { + /* Only do this check on non-stream open. */ + if (file_existed) { + status = smbd_check_access_rights(conn, + smb_fname, + access_mask); + } else if (local_flags & O_CREAT){ + status = check_parent_access(conn, + smb_fname, + SEC_DIR_ADD_FILE, + NULL); + } else { + /* File didn't exist and no O_CREAT. */ + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("open_file: " + "%s on file " + "%s returned %s\n", + file_existed ? + "smbd_check_access_rights" : + "check_parent_access", + smb_fname_str_dbg(smb_fname), + nt_errstr(status) )); + return status; + } + } + /* Actually do the open */ status = fd_open(conn, fsp, local_flags, unx_mode); if (!NT_STATUS_IS_OK(status)) { @@ -454,107 +655,39 @@ static NTSTATUS open_file(files_struct *fsp, } if ((local_flags & O_CREAT) && !file_existed) { - - /* Inherit the ACL if required */ - if (lp_inherit_perms(SNUM(conn))) { - inherit_access_posix_acl(conn, parent_dir, - smb_fname->base_name, - unx_mode); - } - - /* Change the owner if required. */ - if (lp_inherit_owner(SNUM(conn))) { - change_file_owner_to_parent(conn, parent_dir, - fsp); - } - - notify_fname(conn, NOTIFY_ACTION_ADDED, - FILE_NOTIFY_CHANGE_FILE_NAME, - smb_fname->base_name); + file_created = true; } } else { fsp->fh->fd = -1; /* What we used to call a stat open. */ - if (file_existed) { - uint32_t access_granted = 0; + if (!file_existed) { + /* File must exist for a stat open. */ + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } - status = smbd_check_open_rights(conn, - smb_fname, - access_mask, - &access_granted); - if (!NT_STATUS_IS_OK(status)) { - if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { - /* - * On NT_STATUS_ACCESS_DENIED, access_granted - * contains the denied bits. - */ - - if ((access_mask & FILE_WRITE_ATTRIBUTES) && - (access_granted & FILE_WRITE_ATTRIBUTES) && - (lp_map_readonly(SNUM(conn)) || - lp_map_archive(SNUM(conn)) || - lp_map_hidden(SNUM(conn)) || - lp_map_system(SNUM(conn)))) { - access_granted &= ~FILE_WRITE_ATTRIBUTES; - - DEBUG(10,("open_file: " - "overrode " - "FILE_WRITE_" - "ATTRIBUTES " - "on file %s\n", - smb_fname_str_dbg( - smb_fname))); - } + status = smbd_check_access_rights(conn, + smb_fname, + access_mask); - if ((access_mask & DELETE_ACCESS) && - (access_granted & DELETE_ACCESS) && - can_delete_file_in_directory(conn, - smb_fname)) { - /* Were we trying to do a stat open - * for delete and didn't get DELETE - * access (only) ? Check if the - * directory allows DELETE_CHILD. - * See here: - * http://blogs.msdn.com/oldnewthing/archive/2004/06/04/148426.aspx - * for details. */ - - access_granted &= ~DELETE_ACCESS; - - DEBUG(10,("open_file: " - "overrode " - "DELETE_ACCESS on " - "file %s\n", - smb_fname_str_dbg( - smb_fname))); - } + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && + fsp->posix_open && + S_ISLNK(smb_fname->st.st_ex_mode)) { + /* This is a POSIX stat open for delete + * or rename on a symlink that points + * nowhere. Allow. */ + DEBUG(10,("open_file: allowing POSIX " + "open on bad symlink %s\n", + smb_fname_str_dbg(smb_fname))); + status = NT_STATUS_OK; + } - if (access_granted != 0) { - DEBUG(10,("open_file: Access " - "denied on file " - "%s\n", - smb_fname_str_dbg( - smb_fname))); - return status; - } - } else if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && - fsp->posix_open && - S_ISLNK(smb_fname->st.st_ex_mode)) { - /* This is a POSIX stat open for delete - * or rename on a symlink that points - * nowhere. Allow. */ - DEBUG(10,("open_file: allowing POSIX " - "open on bad symlink %s\n", - smb_fname_str_dbg( - smb_fname))); - } else { - DEBUG(10,("open_file: " - "smbd_check_open_rights on file " - "%s returned %s\n", - smb_fname_str_dbg(smb_fname), - nt_errstr(status) )); - return status; - } - } + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("open_file: " + "smbd_check_access_rights on file " + "%s returned %s\n", + smb_fname_str_dbg(smb_fname), + nt_errstr(status) )); + return status; } } @@ -580,6 +713,47 @@ static NTSTATUS open_file(files_struct *fsp, fd_close(fsp); return status; } + + if (file_created) { + bool need_re_stat = false; + /* Do all inheritance work after we've + done a successful stat call and filled + in the stat struct in fsp->fsp_name. */ + + /* Inherit the ACL if required */ + if (lp_inherit_perms(SNUM(conn))) { + inherit_access_posix_acl(conn, parent_dir, + smb_fname->base_name, + unx_mode); + need_re_stat = true; + } + + /* Change the owner if required. */ + if (lp_inherit_owner(SNUM(conn))) { + change_file_owner_to_parent(conn, parent_dir, + fsp); + need_re_stat = true; + } + + if (need_re_stat) { + if (fsp->fh->fd == -1) { + ret = SMB_VFS_STAT(conn, smb_fname); + } else { + ret = SMB_VFS_FSTAT(fsp, &smb_fname->st); + /* If we have an fd, this stat should succeed. */ + if (ret == -1) { + DEBUG(0,("Error doing fstat on open file %s " + "(%s)\n", + smb_fname_str_dbg(smb_fname), + strerror(errno) )); + } + } + } + + notify_fname(conn, NOTIFY_ACTION_ADDED, + FILE_NOTIFY_CHANGE_FILE_NAME, + smb_fname->base_name); + } } /* @@ -619,7 +793,7 @@ static NTSTATUS open_file(files_struct *fsp, fsp->wcp = NULL; /* Write cache pointer. */ DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n", - conn->server_info->unix_name, + conn->session_info->unix_info->unix_name, smb_fname_str_dbg(smb_fname), BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write), conn->num_files_open)); @@ -628,23 +802,6 @@ static NTSTATUS open_file(files_struct *fsp, return NT_STATUS_OK; } -/******************************************************************* - Return True if the filename is one of the special executable types. -********************************************************************/ - -bool is_executable(const char *fname) -{ - if ((fname = strrchr_m(fname,'.'))) { - if (strequal(fname,".com") || - strequal(fname,".dll") || - strequal(fname,".exe") || - strequal(fname,".sym")) { - return True; - } - } - return False; -} - /**************************************************************************** Check if we can open a file with a share mode. Returns True if conflict, False if not. @@ -714,7 +871,7 @@ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (u share_access, FILE_SHARE_WRITE); CHECK_MASK(2, access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA, entry->share_access, FILE_SHARE_WRITE); - + CHECK_MASK(3, entry->access_mask, FILE_READ_DATA | FILE_EXECUTE, share_access, FILE_SHARE_READ); CHECK_MASK(4, access_mask, FILE_READ_DATA | FILE_EXECUTE, @@ -741,7 +898,7 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn, } if (is_deferred_open_entry(share_entry) && - !open_was_deferred(share_entry->op_mid)) { + !open_was_deferred(sconn, share_entry->op_mid)) { char *str = talloc_asprintf(talloc_tos(), "Got a deferred entry without a request: " "PANIC: %s\n", @@ -762,8 +919,7 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn, "share entry with an open file\n"); } - if (is_deferred_open_entry(share_entry) || - is_unused_share_mode_entry(share_entry)) { + if (is_deferred_open_entry(share_entry)) { goto panic; } @@ -842,7 +998,7 @@ static NTSTATUS open_mode_check(connection_struct *conn, /* * Check if the share modes will give us access. */ - + #if defined(DEVELOPER) for(i = 0; i < lck->num_share_modes; i++) { validate_my_share_entries(conn->sconn, i, @@ -868,7 +1024,7 @@ static NTSTATUS open_mode_check(connection_struct *conn, return NT_STATUS_SHARING_VIOLATION; } } - + return NT_STATUS_OK; } @@ -926,7 +1082,9 @@ static NTSTATUS send_break_message(files_struct *fsp, * Do internal consistency checks on the share mode for a file. */ -static void find_oplock_types(struct share_mode_lock *lck, +static void find_oplock_types(files_struct *fsp, + int oplock_request, + struct share_mode_lock *lck, struct share_mode_entry **pp_batch, struct share_mode_entry **pp_ex_or_batch, bool *got_level2, @@ -939,14 +1097,30 @@ static void find_oplock_types(struct share_mode_lock *lck, *got_level2 = false; *got_no_oplock = false; + /* Ignore stat or internal opens, as is done in + delay_for_batch_oplocks() and + delay_for_exclusive_oplocks(). + */ + if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) { + return; + } + for (i=0; inum_share_modes; i++) { if (!is_valid_share_mode_entry(&lck->share_modes[i])) { continue; } + if (lck->share_modes[i].op_type == NO_OPLOCK && + is_stat_open(lck->share_modes[i].access_mask)) { + /* We ignore stat opens in the table - they + always have NO_OPLOCK and never get or + cause breaks. JRA. */ + continue; + } + if (BATCH_OPLOCK_TYPE(lck->share_modes[i].op_type)) { /* batch - can only be one. */ - if (*pp_batch || *got_level2 || *got_no_oplock) { + if (*pp_ex_or_batch || *pp_batch || *got_level2 || *got_no_oplock) { smb_panic("Bad batch oplock entry."); } *pp_batch = &lck->share_modes[i]; @@ -984,13 +1158,13 @@ static bool delay_for_batch_oplocks(files_struct *fsp, if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) { return false; } - - if (batch_entry != NULL) { - /* Found a batch oplock */ - send_break_message(fsp, batch_entry, mid, oplock_request); - return true; + if (batch_entry == NULL) { + return false; } - return false; + + /* Found a batch oplock */ + send_break_message(fsp, batch_entry, mid, oplock_request); + return true; } static bool delay_for_exclusive_oplocks(files_struct *fsp, @@ -998,34 +1172,30 @@ static bool delay_for_exclusive_oplocks(files_struct *fsp, int oplock_request, struct share_mode_entry *ex_entry) { + bool delay_it; + if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) { return false; } - - if (ex_entry != NULL) { - /* Found an exclusive or batch oplock */ - bool delay_it = is_delete_request(fsp) ? - BATCH_OPLOCK_TYPE(ex_entry->op_type) : true; - if (delay_it) { - send_break_message(fsp, ex_entry, mid, oplock_request); - return true; - } + if (ex_entry == NULL) { + return false; } - return false; -} -static bool file_has_brlocks(files_struct *fsp) -{ - struct byte_range_lock *br_lck; + /* Found an exclusive or batch oplock */ - br_lck = brl_get_locks_readonly(fsp); - if (!br_lck) + delay_it = is_delete_request(fsp) ? + BATCH_OPLOCK_TYPE(ex_entry->op_type) : true; + + if (!delay_it) { return false; + } - return br_lck->num_locks > 0 ? true : false; + send_break_message(fsp, ex_entry, mid, oplock_request); + return true; } static void grant_fsp_oplock_type(files_struct *fsp, + const struct byte_range_lock *br_lck, int oplock_request, bool got_level2_oplock, bool got_a_none_oplock) @@ -1043,7 +1213,7 @@ static void grant_fsp_oplock_type(files_struct *fsp, DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n", fsp->oplock_type, fsp_str_dbg(fsp))); return; - } else if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) { + } else if (br_lck && br_lck->num_locks > 0) { DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n", fsp_str_dbg(fsp))); fsp->oplock_type = NO_OPLOCK; @@ -1119,11 +1289,9 @@ static void defer_open(struct share_mode_lock *lck, for (i=0; inum_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; - if (!is_deferred_open_entry(e)) { - continue; - } - - if (procid_is_me(&e->pid) && (e->op_mid == req->mid)) { + if (is_deferred_open_entry(e) && + procid_is_me(&e->pid) && + (e->op_mid == req->mid)) { DEBUG(0, ("Trying to defer an already deferred " "request: mid=%llu, exiting\n", (unsigned long long)req->mid)); @@ -1257,153 +1425,6 @@ NTSTATUS fcb_or_dos_open(struct smb_request *req, create_options, fsp_to_dup_into); } -/**************************************************************************** - Open a file with a share mode - old openX method - map into NTCreate. -****************************************************************************/ - -bool map_open_params_to_ntcreate(const struct smb_filename *smb_fname, - int deny_mode, int open_func, - uint32 *paccess_mask, - uint32 *pshare_mode, - uint32 *pcreate_disposition, - uint32 *pcreate_options, - uint32_t *pprivate_flags) -{ - uint32 access_mask; - uint32 share_mode; - uint32 create_disposition; - uint32 create_options = FILE_NON_DIRECTORY_FILE; - uint32_t private_flags = 0; - - DEBUG(10,("map_open_params_to_ntcreate: fname = %s, deny_mode = 0x%x, " - "open_func = 0x%x\n", - smb_fname_str_dbg(smb_fname), (unsigned int)deny_mode, - (unsigned int)open_func )); - - /* Create the NT compatible access_mask. */ - switch (GET_OPENX_MODE(deny_mode)) { - case DOS_OPEN_EXEC: /* Implies read-only - used to be FILE_READ_DATA */ - case DOS_OPEN_RDONLY: - access_mask = FILE_GENERIC_READ; - break; - case DOS_OPEN_WRONLY: - access_mask = FILE_GENERIC_WRITE; - break; - case DOS_OPEN_RDWR: - case DOS_OPEN_FCB: - access_mask = FILE_GENERIC_READ|FILE_GENERIC_WRITE; - break; - default: - DEBUG(10,("map_open_params_to_ntcreate: bad open mode = 0x%x\n", - (unsigned int)GET_OPENX_MODE(deny_mode))); - return False; - } - - /* Create the NT compatible create_disposition. */ - switch (open_func) { - case OPENX_FILE_EXISTS_FAIL|OPENX_FILE_CREATE_IF_NOT_EXIST: - create_disposition = FILE_CREATE; - break; - - case OPENX_FILE_EXISTS_OPEN: - create_disposition = FILE_OPEN; - break; - - case OPENX_FILE_EXISTS_OPEN|OPENX_FILE_CREATE_IF_NOT_EXIST: - create_disposition = FILE_OPEN_IF; - break; - - case OPENX_FILE_EXISTS_TRUNCATE: - create_disposition = FILE_OVERWRITE; - break; - - case OPENX_FILE_EXISTS_TRUNCATE|OPENX_FILE_CREATE_IF_NOT_EXIST: - create_disposition = FILE_OVERWRITE_IF; - break; - - default: - /* From samba4 - to be confirmed. */ - if (GET_OPENX_MODE(deny_mode) == DOS_OPEN_EXEC) { - create_disposition = FILE_CREATE; - break; - } - DEBUG(10,("map_open_params_to_ntcreate: bad " - "open_func 0x%x\n", (unsigned int)open_func)); - return False; - } - - /* Create the NT compatible share modes. */ - switch (GET_DENY_MODE(deny_mode)) { - case DENY_ALL: - share_mode = FILE_SHARE_NONE; - break; - - case DENY_WRITE: - share_mode = FILE_SHARE_READ; - break; - - case DENY_READ: - share_mode = FILE_SHARE_WRITE; - break; - - case DENY_NONE: - share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; - break; - - case DENY_DOS: - private_flags |= NTCREATEX_OPTIONS_PRIVATE_DENY_DOS; - if (is_executable(smb_fname->base_name)) { - share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; - } else { - if (GET_OPENX_MODE(deny_mode) == DOS_OPEN_RDONLY) { - share_mode = FILE_SHARE_READ; - } else { - share_mode = FILE_SHARE_NONE; - } - } - break; - - case DENY_FCB: - private_flags |= NTCREATEX_OPTIONS_PRIVATE_DENY_FCB; - share_mode = FILE_SHARE_NONE; - break; - - default: - DEBUG(10,("map_open_params_to_ntcreate: bad deny_mode 0x%x\n", - (unsigned int)GET_DENY_MODE(deny_mode) )); - return False; - } - - DEBUG(10,("map_open_params_to_ntcreate: file %s, access_mask = 0x%x, " - "share_mode = 0x%x, create_disposition = 0x%x, " - "create_options = 0x%x private_flags = 0x%x\n", - smb_fname_str_dbg(smb_fname), - (unsigned int)access_mask, - (unsigned int)share_mode, - (unsigned int)create_disposition, - (unsigned int)create_options, - (unsigned int)private_flags)); - - if (paccess_mask) { - *paccess_mask = access_mask; - } - if (pshare_mode) { - *pshare_mode = share_mode; - } - if (pcreate_disposition) { - *pcreate_disposition = create_disposition; - } - if (pcreate_options) { - *pcreate_options = create_options; - } - if (pprivate_flags) { - *pprivate_flags = private_flags; - } - - return True; - -} - static void schedule_defer_open(struct share_mode_lock *lck, struct timeval request_time, struct smb_request *req) @@ -1445,13 +1466,15 @@ static void schedule_defer_open(struct share_mode_lock *lck, Work out what access_mask to use from what the client sent us. ****************************************************************************/ -static NTSTATUS calculate_access_mask(connection_struct *conn, - const struct smb_filename *smb_fname, - bool file_existed, - uint32_t access_mask, - uint32_t *access_mask_out) +NTSTATUS smbd_calculate_access_mask(connection_struct *conn, + const struct smb_filename *smb_fname, + bool file_existed, + uint32_t access_mask, + uint32_t *access_mask_out) { NTSTATUS status; + uint32_t orig_access_mask = access_mask; + uint32_t rejected_share_access; /* * Convert GENERIC bits to specific bits. @@ -1461,7 +1484,9 @@ static NTSTATUS calculate_access_mask(connection_struct *conn, /* Calculate MAXIMUM_ALLOWED_ACCESS if requested. */ if (access_mask & MAXIMUM_ALLOWED_ACCESS) { - if (file_existed) { + if (get_current_uid(conn) == (uid_t)0) { + access_mask |= FILE_GENERIC_ALL; + } else if (file_existed) { struct security_descriptor *sd; uint32_t access_granted = 0; @@ -1472,32 +1497,51 @@ static NTSTATUS calculate_access_mask(connection_struct *conn, SECINFO_DACL),&sd); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("calculate_access_mask: Could not get acl " - "on file %s: %s\n", + DEBUG(10,("smbd_calculate_access_mask: " + "Could not get acl on file %s: %s\n", smb_fname_str_dbg(smb_fname), nt_errstr(status))); return NT_STATUS_ACCESS_DENIED; } - status = smb1_file_se_access_check(conn, - sd, + /* + * Never test FILE_READ_ATTRIBUTES. se_access_check() + * also takes care of owner WRITE_DAC and READ_CONTROL. + */ + status = se_access_check(sd, get_current_nttok(conn), - access_mask, + (access_mask & ~FILE_READ_ATTRIBUTES), &access_granted); TALLOC_FREE(sd); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("calculate_access_mask: Access denied on " - "file %s: when calculating maximum access\n", + DEBUG(10, ("smbd_calculate_access_mask: " + "Access denied on file %s: " + "when calculating maximum access\n", smb_fname_str_dbg(smb_fname))); return NT_STATUS_ACCESS_DENIED; } - access_mask = access_granted; + access_mask = (access_granted | FILE_READ_ATTRIBUTES); } else { access_mask = FILE_GENERIC_ALL; } + + access_mask &= conn->share_access; + } + + rejected_share_access = access_mask & ~(conn->share_access); + + if (rejected_share_access) { + DEBUG(10, ("smbd_calculate_access_mask: Access denied on " + "file %s: rejected by share access mask[0x%08X] " + "orig[0x%08X] mapped[0x%08X] reject[0x%08X]\n", + smb_fname_str_dbg(smb_fname), + conn->share_access, + orig_access_mask, access_mask, + rejected_share_access)); + return NT_STATUS_ACCESS_DENIED; } *access_mask_out = access_mask; @@ -1515,10 +1559,59 @@ void remove_deferred_open_entry(struct file_id id, uint64_t mid, NULL, NULL, NULL); if (lck == NULL) { DEBUG(0, ("could not get share mode lock\n")); - } else { - del_deferred_open_entry(lck, mid, pid); - TALLOC_FREE(lck); + return; + } + del_deferred_open_entry(lck, mid, pid); + TALLOC_FREE(lck); +} + +/**************************************************************** + Ensure we get the brlock lock followed by the share mode lock + in the correct order to prevent deadlocks if other smbd's are + using the brlock database on this file simultaneously with this open + (that code also gets the locks in brlock -> share mode lock order). +****************************************************************/ + +static bool acquire_ordered_locks(TALLOC_CTX *mem_ctx, + files_struct *fsp, + const struct file_id id, + const char *connectpath, + const struct smb_filename *smb_fname, + const struct timespec *p_old_write_time, + struct share_mode_lock **p_lck, + struct byte_range_lock **p_br_lck) +{ + /* Ordering - we must get the br_lck for this + file before the share mode. */ + if (lp_locking(fsp->conn->params)) { + *p_br_lck = brl_get_locks_readonly(fsp); + if (*p_br_lck == NULL) { + DEBUG(0, ("Could not get br_lock\n")); + return false; + } + /* Note - we don't need to free the returned + br_lck explicitly as it was allocated on talloc_tos() + and so will be autofreed (and release the lock) + once the frame context disappears. + + If it was set to fsp->brlock_rec then it was + talloc_move'd to hang off the fsp pointer and + in this case is guarenteed to not be holding the + lock on the brlock database. */ + } + + *p_lck = get_share_mode_lock(mem_ctx, + id, + connectpath, + smb_fname, + p_old_write_time); + + if (*p_lck == NULL) { + DEBUG(0, ("Could not get share mode lock\n")); + TALLOC_FREE(*p_br_lck); + return false; } + return true; } /**************************************************************************** @@ -1560,12 +1653,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, ZERO_STRUCT(id); - /* Windows allows a new file to be created and - silently removes a FILE_ATTRIBUTE_DIRECTORY - sent by the client. Do the same. */ - - new_dos_attributes &= ~FILE_ATTRIBUTE_DIRECTORY; - if (conn->printer) { /* * Printers are handled completely differently. @@ -1599,9 +1686,15 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, unx_mode = (mode_t)(new_dos_attributes & ~FILE_FLAG_POSIX_SEMANTICS); new_dos_attributes = 0; } else { - /* We add aARCH to this as this mode is only used if the file is + /* Windows allows a new file to be created and + silently removes a FILE_ATTRIBUTE_DIRECTORY + sent by the client. Do the same. */ + + new_dos_attributes &= ~FILE_ATTRIBUTE_DIRECTORY; + + /* We add FILE_ATTRIBUTE_ARCHIVE to this as this mode is only used if the file is * created new. */ - unx_mode = unix_mode(conn, new_dos_attributes | aARCH, + unx_mode = unix_mode(conn, new_dos_attributes | FILE_ATTRIBUTE_ARCHIVE, smb_fname, parent_dir); } @@ -1640,15 +1733,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, sconn_server_id(req->sconn)); /* Ensure we don't reprocess this message. */ - remove_deferred_open_message_smb(req->mid); + remove_deferred_open_message_smb(req->sconn, req->mid); } } - status = check_name(conn, smb_fname->base_name); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - if (!posix_open) { new_dos_attributes &= SAMBA_ATTRIBUTES_MASK; if (file_existed) { @@ -1772,11 +1860,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } } - status = calculate_access_mask(conn, smb_fname, file_existed, + status = smbd_calculate_access_mask(conn, smb_fname, file_existed, access_mask, &access_mask); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("open_file_ntcreate: calculate_access_mask " + DEBUG(10, ("open_file_ntcreate: smbd_calculate_access_mask " "on file %s returned %s\n", smb_fname_str_dbg(smb_fname), nt_errstr(status))); return status; @@ -1865,6 +1953,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } if (file_existed) { + struct byte_range_lock *br_lck = NULL; struct share_mode_entry *batch_entry = NULL; struct share_mode_entry *exclusive_entry = NULL; bool got_level2_oplock = false; @@ -1873,17 +1962,21 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, struct timespec old_write_time = smb_fname->st.st_ex_mtime; id = vfs_file_id_from_sbuf(conn, &smb_fname->st); - lck = get_share_mode_lock(talloc_tos(), id, - conn->connectpath, - smb_fname, &old_write_time); - - if (lck == NULL) { - DEBUG(0, ("Could not get share mode lock\n")); + if (!acquire_ordered_locks(talloc_tos(), + fsp, + id, + conn->connectpath, + smb_fname, + &old_write_time, + &lck, + &br_lck)) { return NT_STATUS_SHARING_VIOLATION; } /* Get the types we need to examine. */ - find_oplock_types(lck, + find_oplock_types(fsp, + oplock_request, + lck, &batch_entry, &exclusive_entry, &got_level2_oplock, @@ -1930,6 +2023,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } grant_fsp_oplock_type(fsp, + br_lck, oplock_request, got_level2_oplock, got_a_none_oplock); @@ -1994,8 +2088,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, if (((can_access_mask & FILE_WRITE_DATA) && !CAN_WRITE(conn)) || - !can_access_file_data(conn, smb_fname, - can_access_mask)) { + !NT_STATUS_IS_OK(smbd_check_access_rights(conn, + smb_fname, can_access_mask))) { can_access = False; } @@ -2086,13 +2180,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, open_access_mask); if (!NT_STATUS_IS_OK(fsp_open)) { - if (lck != NULL) { - TALLOC_FREE(lck); - } + TALLOC_FREE(lck); return fsp_open; } if (!file_existed) { + struct byte_range_lock *br_lck = NULL; struct share_mode_entry *batch_entry = NULL; struct share_mode_entry *exclusive_entry = NULL; bool got_level2_oplock = false; @@ -2115,20 +2208,21 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, id = fsp->file_id; - lck = get_share_mode_lock(talloc_tos(), id, - conn->connectpath, - smb_fname, &old_write_time); - - if (lck == NULL) { - DEBUG(0, ("open_file_ntcreate: Could not get share " - "mode lock for %s\n", - smb_fname_str_dbg(smb_fname))); - fd_close(fsp); + if (!acquire_ordered_locks(talloc_tos(), + fsp, + id, + conn->connectpath, + smb_fname, + &old_write_time, + &lck, + &br_lck)) { return NT_STATUS_SHARING_VIOLATION; } /* Get the types we need to examine. */ - find_oplock_types(lck, + find_oplock_types(fsp, + oplock_request, + lck, &batch_entry, &exclusive_entry, &got_level2_oplock, @@ -2171,8 +2265,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, if (!NT_STATUS_IS_OK(status)) { struct deferred_open_record state; - fd_close(fsp); - state.delayed_for_oplocks = False; state.id = id; @@ -2188,10 +2280,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, req, &state); } TALLOC_FREE(lck); + fd_close(fsp); return status; } grant_fsp_oplock_type(fsp, + br_lck, oplock_request, got_level2_oplock, got_a_none_oplock); @@ -2261,8 +2355,20 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, /* * According to Samba4, SEC_FILE_READ_ATTRIBUTE is always granted, + * but we don't have to store this - just ignore it on access check. */ - fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES; + if (conn->sconn->using_smb2) { + /* + * SMB2 doesn't return it (according to Microsoft tests). + * Test Case: TestSuite_ScenarioNo009GrantedAccessTestS0 + * File created with access = 0x7 (Read, Write, Delete) + * Query Info on file returns 0x87 (Read, Write, Delete, Read Attributes) + */ + fsp->access_mask = access_mask; + } else { + /* But SMB1 does. */ + fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES; + } if (file_existed) { /* stat opens on existing files don't get oplocks. */ @@ -2270,10 +2376,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, fsp->oplock_type = NO_OPLOCK; } - if (!(flags2 & O_TRUNC)) { - info = FILE_WAS_OPENED; - } else { + if (flags2 & O_TRUNC) { info = FILE_WAS_OVERWRITTEN; + } else { + info = FILE_WAS_OPENED; } } else { info = FILE_WAS_CREATED; @@ -2296,11 +2402,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, fsp->oplock_type = NO_OPLOCK; } - if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED || info == FILE_WAS_SUPERSEDED) { - new_file_created = True; - } - - set_share_mode(lck, fsp, get_current_uid(conn), 0, + set_share_mode(lck, fsp, get_current_uid(conn), + req ? req->mid : 0, fsp->oplock_type); /* Handle strange delete on close create semantics. */ @@ -2320,13 +2423,19 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, fsp->initial_delete_on_close = True; } + if (info == FILE_WAS_OVERWRITTEN + || info == FILE_WAS_CREATED + || info == FILE_WAS_SUPERSEDED) { + new_file_created = True; + } + if (new_file_created) { /* Files should be initially set as archive */ if (lp_map_archive(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) { if (!posix_open) { if (file_set_dosmode(conn, smb_fname, - new_dos_attributes | aARCH, + new_dos_attributes | FILE_ATTRIBUTE_ARCHIVE, parent_dir, true) == 0) { unx_mode = smb_fname->st.st_ex_mode; } @@ -2437,12 +2546,14 @@ static NTSTATUS mkdir_internal(connection_struct *conn, uint32 file_attributes) { mode_t mode; - char *parent_dir; + char *parent_dir = NULL; NTSTATUS status; bool posix_open = false; + bool need_re_stat = false; + uint32_t access_mask = SEC_DIR_ADD_SUBDIR; - if(!CAN_WRITE(conn)) { - DEBUG(5,("mkdir_internal: failing create on read-only share " + if(access_mask & ~(conn->share_access)) { + DEBUG(5,("mkdir_internal: failing share access " "%s\n", lp_servicename(SNUM(conn)))); return NT_STATUS_ACCESS_DENIED; } @@ -2461,7 +2572,20 @@ static NTSTATUS mkdir_internal(connection_struct *conn, posix_open = true; mode = (mode_t)(file_attributes & ~FILE_FLAG_POSIX_SEMANTICS); } else { - mode = unix_mode(conn, aDIR, smb_dname, parent_dir); + mode = unix_mode(conn, FILE_ATTRIBUTE_DIRECTORY, smb_dname, parent_dir); + } + + status = check_parent_access(conn, + smb_dname, + access_mask, + &parent_dir); + if(!NT_STATUS_IS_OK(status)) { + DEBUG(5,("mkdir_internal: check_parent_access " + "on directory %s for path %s returned %s\n", + parent_dir, + smb_dname->base_name, + nt_errstr(status) )); + return status; } if (SMB_VFS_MKDIR(conn, smb_dname->base_name, mode) != 0) { @@ -2478,15 +2602,15 @@ static NTSTATUS mkdir_internal(connection_struct *conn, } if (!S_ISDIR(smb_dname->st.st_ex_mode)) { - DEBUG(0, ("Directory just '%s' created is not a directory\n", + DEBUG(0, ("Directory '%s' just created is not a directory !\n", smb_fname_str_dbg(smb_dname))); - return NT_STATUS_ACCESS_DENIED; + return NT_STATUS_NOT_A_DIRECTORY; } if (lp_store_dos_attributes(SNUM(conn))) { if (!posix_open) { file_set_dosmode(conn, smb_dname, - file_attributes | aDIR, + file_attributes | FILE_ATTRIBUTE_DIRECTORY, parent_dir, true); } } @@ -2494,6 +2618,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn, if (lp_inherit_perms(SNUM(conn))) { inherit_access_posix_acl(conn, parent_dir, smb_dname->base_name, mode); + need_re_stat = true; } if (!posix_open) { @@ -2508,6 +2633,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn, SMB_VFS_CHMOD(conn, smb_dname->base_name, (smb_dname->st.st_ex_mode | (mode & ~smb_dname->st.st_ex_mode))); + need_re_stat = true; } } @@ -2516,6 +2642,15 @@ static NTSTATUS mkdir_internal(connection_struct *conn, change_dir_owner_to_parent(conn, parent_dir, smb_dname->base_name, &smb_dname->st); + need_re_stat = true; + } + + if (need_re_stat) { + if (SMB_VFS_LSTAT(conn, smb_dname) == -1) { + DEBUG(2, ("Could not stat directory '%s' just created: %s\n", + smb_fname_str_dbg(smb_dname), strerror(errno))); + return map_nt_error_from_unix(errno); + } } notify_fname(conn, NOTIFY_ACTION_ADDED, FILE_NOTIFY_CHANGE_DIR_NAME, @@ -2528,7 +2663,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn, Ensure we didn't get symlink raced on opening a directory. ****************************************************************************/ -static bool check_same_stat(const SMB_STRUCT_STAT *sbuf1, +bool check_same_stat(const SMB_STRUCT_STAT *sbuf1, const SMB_STRUCT_STAT *sbuf2) { if (sbuf1->st_ex_uid != sbuf2->st_ex_uid || @@ -2585,10 +2720,10 @@ static NTSTATUS open_directory(connection_struct *conn, return NT_STATUS_NOT_A_DIRECTORY; } - status = calculate_access_mask(conn, smb_dname, dir_existed, - access_mask, &access_mask); + status = smbd_calculate_access_mask(conn, smb_dname, dir_existed, + access_mask, &access_mask); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("open_directory: calculate_access_mask " + DEBUG(10, ("open_directory: smbd_calculate_access_mask " "on file %s returned %s\n", smb_fname_str_dbg(smb_dname), nt_errstr(status))); @@ -2619,6 +2754,15 @@ static NTSTATUS open_directory(connection_struct *conn, /* If directory exists error. If directory doesn't * exist create. */ + if (dir_existed) { + status = NT_STATUS_OBJECT_NAME_COLLISION; + DEBUG(2, ("open_directory: unable to create " + "%s. Error was %s\n", + smb_fname_str_dbg(smb_dname), + nt_errstr(status))); + return status; + } + status = mkdir_internal(conn, smb_dname, file_attributes); @@ -2639,19 +2783,29 @@ static NTSTATUS open_directory(connection_struct *conn, * exist create. */ - status = mkdir_internal(conn, smb_dname, + if (dir_existed) { + status = NT_STATUS_OK; + info = FILE_WAS_OPENED; + } else { + status = mkdir_internal(conn, smb_dname, file_attributes); - if (NT_STATUS_IS_OK(status)) { - info = FILE_WAS_CREATED; + if (NT_STATUS_IS_OK(status)) { + info = FILE_WAS_CREATED; + } else { + /* Cope with create race. */ + if (!NT_STATUS_EQUAL(status, + NT_STATUS_OBJECT_NAME_COLLISION)) { + DEBUG(2, ("open_directory: unable to create " + "%s. Error was %s\n", + smb_fname_str_dbg(smb_dname), + nt_errstr(status))); + return status; + } + info = FILE_WAS_OPENED; + } } - if (NT_STATUS_EQUAL(status, - NT_STATUS_OBJECT_NAME_COLLISION)) { - info = FILE_WAS_OPENED; - status = NT_STATUS_OK; - } - break; case FILE_SUPERSEDE: @@ -2672,30 +2826,9 @@ static NTSTATUS open_directory(connection_struct *conn, } if (info == FILE_WAS_OPENED) { - uint32_t access_granted = 0; - status = smbd_check_open_rights(conn, smb_dname, access_mask, - &access_granted); - - /* Were we trying to do a directory open - * for delete and didn't get DELETE - * access (only) ? Check if the - * directory allows DELETE_CHILD. - * See here: - * http://blogs.msdn.com/oldnewthing/archive/2004/06/04/148426.aspx - * for details. */ - - if ((NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) && - (access_mask & DELETE_ACCESS) && - (access_granted == DELETE_ACCESS) && - can_delete_file_in_directory(conn, smb_dname))) { - DEBUG(10,("open_directory: overrode ACCESS_DENIED " - "on directory %s\n", - smb_fname_str_dbg(smb_dname))); - status = NT_STATUS_OK; - } - + status = smbd_check_access_rights(conn, smb_dname, access_mask); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("open_directory: smbd_check_open_rights on " + DEBUG(10, ("open_directory: smbd_check_access_rights on " "file %s failed with %s\n", smb_fname_str_dbg(smb_dname), nt_errstr(status))); @@ -2711,7 +2844,7 @@ static NTSTATUS open_directory(connection_struct *conn, /* * Setup the files_struct for it. */ - + fsp->mode = smb_dname->st.st_ex_mode; fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_dname->st); fsp->vuid = req ? req->vuid : UID_FIELD_INVALID; @@ -2794,7 +2927,8 @@ static NTSTATUS open_directory(connection_struct *conn, return status; } - set_share_mode(lck, fsp, get_current_uid(conn), 0, NO_OPLOCK); + set_share_mode(lck, fsp, get_current_uid(conn), + req ? req->mid : 0, NO_OPLOCK); /* For directories the delete on close bit at open time seems always to be honored on close... See test 19 in Samba4 BASE-DELETE. */ @@ -2866,7 +3000,6 @@ void msg_file_was_renamed(struct messaging_context *msg, struct server_id server_id, DATA_BLOB *data) { - struct smbd_server_connection *sconn; files_struct *fsp; char *frm = (char *)data->data; struct file_id id; @@ -2876,12 +3009,9 @@ void msg_file_was_renamed(struct messaging_context *msg, struct smb_filename *smb_fname = NULL; size_t sp_len, bn_len; NTSTATUS status; - - sconn = msg_ctx_to_sconn(msg); - if (sconn == NULL) { - DEBUG(1, ("could not find sconn\n")); - return; - } + struct smbd_server_connection *sconn = + talloc_get_type_abort(private_data, + struct smbd_server_connection); if (data->data == NULL || data->length < MSG_FILE_RENAMED_MIN_SIZE + 2) { @@ -2953,15 +3083,15 @@ void msg_file_was_renamed(struct messaging_context *msg, NTSTATUS open_streams_for_delete(connection_struct *conn, const char *fname) { - struct stream_struct *stream_info; - files_struct **streams; + struct stream_struct *stream_info = NULL; + files_struct **streams = NULL; int i; - unsigned int num_streams; + unsigned int num_streams = 0; TALLOC_CTX *frame = talloc_stackframe(); NTSTATUS status; - status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(), - &num_streams, &stream_info); + status = vfs_streaminfo(conn, NULL, fname, talloc_tos(), + &num_streams, &stream_info); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) || NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { @@ -2971,7 +3101,7 @@ NTSTATUS open_streams_for_delete(connection_struct *conn, } if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n", + DEBUG(10, ("vfs_streaminfo failed: %s\n", nt_errstr(status))); goto fail; } @@ -2984,7 +3114,7 @@ NTSTATUS open_streams_for_delete(connection_struct *conn, return NT_STATUS_OK; } - streams = TALLOC_ARRAY(talloc_tos(), files_struct *, num_streams); + streams = talloc_array(talloc_tos(), files_struct *, num_streams); if (streams == NULL) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; @@ -3060,6 +3190,109 @@ NTSTATUS open_streams_for_delete(connection_struct *conn, return status; } +/********************************************************************* + Create a default ACL by inheriting from the parent. If no inheritance + from the parent available, don't set anything. This will leave the actual + permissions the new file or directory already got from the filesystem + as the NT ACL when read. +*********************************************************************/ + +static NTSTATUS inherit_new_acl(files_struct *fsp) +{ + TALLOC_CTX *ctx = talloc_tos(); + char *parent_name = NULL; + struct security_descriptor *parent_desc = NULL; + NTSTATUS status = NT_STATUS_OK; + struct security_descriptor *psd = NULL; + struct dom_sid *owner_sid = NULL; + struct dom_sid *group_sid = NULL; + uint32_t security_info_sent = (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL); + bool inherit_owner = lp_inherit_owner(SNUM(fsp->conn)); + bool inheritable_components = false; + size_t size = 0; + + if (!parent_dirname(ctx, fsp->fsp_name->base_name, &parent_name, NULL)) { + return NT_STATUS_NO_MEMORY; + } + + status = SMB_VFS_GET_NT_ACL(fsp->conn, + parent_name, + (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL), + &parent_desc); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + inheritable_components = sd_has_inheritable_components(parent_desc, + fsp->is_directory); + + if (!inheritable_components && !inherit_owner) { + /* Nothing to inherit and not setting owner. */ + return NT_STATUS_OK; + } + + /* Create an inherited descriptor from the parent. */ + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("inherit_new_acl: parent acl for %s is:\n", + fsp_str_dbg(fsp) )); + NDR_PRINT_DEBUG(security_descriptor, parent_desc); + } + + /* Inherit from parent descriptor if "inherit owner" set. */ + if (inherit_owner) { + owner_sid = parent_desc->owner_sid; + group_sid = parent_desc->group_sid; + } + + if (owner_sid == NULL) { + owner_sid = &fsp->conn->session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; + } + if (group_sid == NULL) { + group_sid = &fsp->conn->session_info->security_token->sids[PRIMARY_GROUP_SID_INDEX]; + } + + status = se_create_child_secdesc(ctx, + &psd, + &size, + parent_desc, + owner_sid, + group_sid, + fsp->is_directory); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* If inheritable_components == false, + se_create_child_secdesc() + creates a security desriptor with a NULL dacl + entry, but with SEC_DESC_DACL_PRESENT. We need + to remove that flag. */ + + if (!inheritable_components) { + security_info_sent &= ~SECINFO_DACL; + psd->type &= ~SEC_DESC_DACL_PRESENT; + } + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("inherit_new_acl: child acl for %s is:\n", + fsp_str_dbg(fsp) )); + NDR_PRINT_DEBUG(security_descriptor, psd); + } + + if (inherit_owner) { + /* We need to be root to force this. */ + become_root(); + } + status = SMB_VFS_FSET_NT_ACL(fsp, + security_info_sent, + psd); + if (inherit_owner) { + unbecome_root(); + } + return status; +} + /* * Wrapper around open_file_ntcreate and open_directory */ @@ -3129,28 +3362,6 @@ static NTSTATUS create_file_unixpath(connection_struct *conn, } } - /* This is the correct thing to do (check every time) but can_delete - * is expensive (it may have to read the parent directory - * permissions). So for now we're not doing it unless we have a strong - * hint the client is really going to delete this file. If the client - * is forcing FILE_CREATE let the filesystem take care of the - * permissions. */ - - /* Setting FILE_SHARE_DELETE is the hint. */ - - if (lp_acl_check_permissions(SNUM(conn)) - && (create_disposition != FILE_CREATE) - && (share_access & FILE_SHARE_DELETE) - && (access_mask & DELETE_ACCESS) - && (!(can_delete_file_in_directory(conn, smb_fname) || - can_access_file_acl(conn, smb_fname, DELETE_ACCESS)))) { - status = NT_STATUS_ACCESS_DENIED; - DEBUG(10,("create_file_unixpath: open file %s " - "for delete ACCESS_DENIED\n", - smb_fname_str_dbg(smb_fname))); - goto fail; - } - if ((access_mask & SEC_FLAG_SYSTEM_SECURITY) && !security_token_has_privilege(get_current_nttok(conn), SEC_PRIV_SECURITY)) { @@ -3319,45 +3530,6 @@ static NTSTATUS create_file_unixpath(connection_struct *conn, fsp->base_fsp = base_fsp; - /* - * According to the MS documentation, the only time the security - * descriptor is applied to the opened file is iff we *created* the - * file; an existing file stays the same. - * - * Also, it seems (from observation) that you can open the file with - * any access mask but you can still write the sd. We need to override - * the granted access before we call set_sd - * Patch for bug #2242 from Tom Lackemann . - */ - - if ((sd != NULL) && (info == FILE_WAS_CREATED) - && lp_nt_acl_support(SNUM(conn))) { - - uint32_t sec_info_sent; - uint32_t saved_access_mask = fsp->access_mask; - - sec_info_sent = get_sec_info(sd); - - fsp->access_mask = FILE_GENERIC_ALL; - - /* Convert all the generic bits. */ - security_acl_map_generic(sd->dacl, &file_generic_mapping); - security_acl_map_generic(sd->sacl, &file_generic_mapping); - - if (sec_info_sent & (SECINFO_OWNER| - SECINFO_GROUP| - SECINFO_DACL| - SECINFO_SACL)) { - status = SMB_VFS_FSET_NT_ACL(fsp, sec_info_sent, sd); - } - - fsp->access_mask = saved_access_mask; - - if (!NT_STATUS_IS_OK(status)) { - goto fail; - } - } - if ((ea_list != NULL) && ((info == FILE_WAS_CREATED) || (info == FILE_WAS_OVERWRITTEN))) { status = set_ea(conn, fsp, fsp->fsp_name, ea_list); @@ -3393,6 +3565,54 @@ static NTSTATUS create_file_unixpath(connection_struct *conn, } } + if ((info == FILE_WAS_CREATED) && lp_nt_acl_support(SNUM(conn)) && + fsp->base_fsp == NULL) { + if (sd != NULL) { + /* + * According to the MS documentation, the only time the security + * descriptor is applied to the opened file is iff we *created* the + * file; an existing file stays the same. + * + * Also, it seems (from observation) that you can open the file with + * any access mask but you can still write the sd. We need to override + * the granted access before we call set_sd + * Patch for bug #2242 from Tom Lackemann . + */ + + uint32_t sec_info_sent; + uint32_t saved_access_mask = fsp->access_mask; + + sec_info_sent = get_sec_info(sd); + + fsp->access_mask = FILE_GENERIC_ALL; + + /* Convert all the generic bits. */ + security_acl_map_generic(sd->dacl, &file_generic_mapping); + security_acl_map_generic(sd->sacl, &file_generic_mapping); + + if (sec_info_sent & (SECINFO_OWNER| + SECINFO_GROUP| + SECINFO_DACL| + SECINFO_SACL)) { + status = SMB_VFS_FSET_NT_ACL(fsp, sec_info_sent, sd); + } + + fsp->access_mask = saved_access_mask; + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + } else if (lp_inherit_acls(SNUM(conn))) { + /* Inherit from parent. Errors here are not fatal. */ + status = inherit_new_acl(fsp); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("inherit_new_acl: failed for %s with %s\n", + fsp_str_dbg(fsp), + nt_errstr(status) )); + } + } + } + DEBUG(10, ("create_file_unixpath: info=%d\n", info)); *result = fsp; @@ -3431,7 +3651,8 @@ static NTSTATUS create_file_unixpath(connection_struct *conn, NTSTATUS get_relative_fid_filename(connection_struct *conn, struct smb_request *req, uint16_t root_dir_fid, - struct smb_filename *smb_fname) + const struct smb_filename *smb_fname, + struct smb_filename **smb_fname_out) { files_struct *dir_fsp; char *parent_fname = NULL; @@ -3497,7 +3718,7 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn, * Copy in the base directory name. */ - parent_fname = TALLOC_ARRAY(talloc_tos(), char, + parent_fname = talloc_array(talloc_tos(), char, dir_name_len+2); if (parent_fname == NULL) { status = NT_STATUS_NO_MEMORY; @@ -3519,19 +3740,27 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn, } } - new_base_name = talloc_asprintf(smb_fname, "%s%s", parent_fname, + new_base_name = talloc_asprintf(talloc_tos(), "%s%s", parent_fname, smb_fname->base_name); if (new_base_name == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } - TALLOC_FREE(smb_fname->base_name); - smb_fname->base_name = new_base_name; - status = NT_STATUS_OK; + status = filename_convert(req, + conn, + req->flags2 & FLAGS2_DFS_PATHNAMES, + new_base_name, + 0, + NULL, + smb_fname_out); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } out: TALLOC_FREE(parent_fname); + TALLOC_FREE(new_base_name); return status; } @@ -3579,11 +3808,13 @@ NTSTATUS create_file_default(connection_struct *conn, */ if (root_dir_fid != 0) { + struct smb_filename *smb_fname_out = NULL; status = get_relative_fid_filename(conn, req, root_dir_fid, - smb_fname); + smb_fname, &smb_fname_out); if (!NT_STATUS_IS_OK(status)) { goto fail; } + smb_fname = smb_fname_out; } /* @@ -3626,13 +3857,6 @@ NTSTATUS create_file_default(connection_struct *conn, } } - /* All file access must go through check_name() */ - - status = check_name(conn, smb_fname->base_name); - if (!NT_STATUS_IS_OK(status)) { - goto fail; - } - if (stream_name && is_ntfs_default_stream_smb_fname(smb_fname)) { int ret; smb_fname->stream_name = NULL;