X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fsmbd%2Fopen.c;h=8b7b47b7ebc46d9fa251bbdd1babdb50f41c031b;hb=7000bb69332f12d35c41cb08792d29c08736937b;hp=61d1a2e3ccc6346a800e3e75dff3423c2654848a;hpb=f9d183f931f021c7b76047766838388cb7686c37;p=kai%2Fsamba.git diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 61d1a2e3ccc..8b7b47b7ebc 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -27,6 +27,7 @@ #include "fake_file.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" @@ -38,60 +39,71 @@ 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) { - *access_granted = 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 ((access_mask & DELETE_ACCESS) && !lp_acl_check_permissions(SNUM(conn))) { - *access_granted = access_mask; + 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; + } - DEBUG(10,("smbd_check_open_rights: not checking ACL " + 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_granted )); + (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; } @@ -101,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); } @@ -131,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; } /**************************************************************************** @@ -489,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)) { @@ -504,86 +660,34 @@ static NTSTATUS open_file(files_struct *fsp, } else { fsp->fh->fd = -1; /* What we used to call a stat open. */ - if (file_existed) { - uint32_t access_granted = 0; - - 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))); - } + if (!file_existed) { + /* File must exist for a stat open. */ + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } - 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))); - } + status = smbd_check_access_rights(conn, + smb_fname, + access_mask); + + 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; } } @@ -815,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; } @@ -981,7 +1084,7 @@ static NTSTATUS send_break_message(files_struct *fsp, static void find_oplock_types(files_struct *fsp, int oplock_request, - struct share_mode_lock *lck, + const struct share_mode_lock *lck, struct share_mode_entry **pp_batch, struct share_mode_entry **pp_ex_or_batch, bool *got_level2, @@ -1055,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, @@ -1069,20 +1172,26 @@ 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) { + 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; - } + /* Found an exclusive or batch oplock */ + + delay_it = is_delete_request(fsp) ? + BATCH_OPLOCK_TYPE(ex_entry->op_type) : true; + + if (!delay_it) { + return false; } - return false; + + send_break_message(fsp, ex_entry, mid, oplock_request); + return true; } static void grant_fsp_oplock_type(files_struct *fsp, @@ -1180,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)); @@ -1377,7 +1484,9 @@ NTSTATUS smbd_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; @@ -1395,10 +1504,13 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn, 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); @@ -1411,7 +1523,7 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn, return NT_STATUS_ACCESS_DENIED; } - access_mask = access_granted; + access_mask = (access_granted | FILE_READ_ATTRIBUTES); } else { access_mask = FILE_GENERIC_ALL; } @@ -1447,10 +1559,10 @@ 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); } /**************************************************************** @@ -1527,7 +1639,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, bool posix_open = False; bool new_file_created = False; bool clear_ads = false; - struct file_id id; NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED; mode_t new_unx_mode = (mode_t)0; mode_t unx_mode = (mode_t)0; @@ -1539,14 +1650,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, NTSTATUS status; char *parent_dir; - 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. @@ -1580,6 +1683,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, unx_mode = (mode_t)(new_dos_attributes & ~FILE_FLAG_POSIX_SEMANTICS); new_dos_attributes = 0; } else { + /* 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 | FILE_ATTRIBUTE_ARCHIVE, @@ -1625,11 +1734,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } } - 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) { @@ -1851,6 +1955,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, struct share_mode_entry *exclusive_entry = NULL; bool got_level2_oplock = false; bool got_a_none_oplock = false; + struct file_id id; struct timespec old_write_time = smb_fname->st.st_ex_mtime; id = vfs_file_id_from_sbuf(conn, &smb_fname->st); @@ -1981,8 +2086,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; } @@ -2084,6 +2189,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, bool got_level2_oplock = false; bool got_a_none_oplock = false; struct timespec old_write_time = smb_fname->st.st_ex_mtime; + struct file_id id; /* * Deal with the race condition where two smbd's detect the * file doesn't exist and do the create at the same time. One @@ -2269,10 +2375,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; @@ -2295,10 +2401,6 @@ 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), req ? req->mid : 0, fsp->oplock_type); @@ -2320,6 +2422,12 @@ 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)) || @@ -2437,13 +2545,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; } @@ -2465,6 +2574,19 @@ static NTSTATUS mkdir_internal(connection_struct *conn, 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) { return map_nt_error_from_unix(errno); } @@ -2479,9 +2601,9 @@ 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))) { @@ -2631,6 +2753,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); @@ -2651,18 +2782,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: @@ -2683,30 +2825,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))); @@ -2878,7 +2999,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; @@ -2888,12 +3008,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) { @@ -2965,15 +3082,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)) { @@ -2983,7 +3100,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; } @@ -3072,6 +3189,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 */ @@ -3141,26 +3361,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 ((create_disposition != FILE_CREATE) - && (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)) { @@ -3329,45 +3529,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); @@ -3403,6 +3564,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; @@ -3647,13 +3856,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;