From c077300a22806bab94d11a87c8ef96b6b541ada5 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 8 Nov 2004 11:35:49 +0000 Subject: r3618: - this adds the special case for DENY_DOS semantics, as shown by the BASE-DENYDOS test. - pvfs now passes BASE-DENY1 and BASE-DENYDOS. (This used to be commit aa09df22ee729c02552638859236d9068e9748ae) --- source4/include/smb.h | 29 ++++--- source4/ntvfs/ntvfs_generic.c | 10 ++- source4/ntvfs/posix/pvfs_open.c | 154 +++++++++++++++++++++++++-------- source4/ntvfs/posix/pvfs_qfileinfo.c | 2 +- source4/ntvfs/posix/pvfs_read.c | 2 +- source4/ntvfs/posix/pvfs_setfileinfo.c | 4 +- source4/ntvfs/posix/pvfs_write.c | 2 +- source4/ntvfs/posix/vfs_posix.h | 6 +- source4/script/tests/test_posix.sh | 6 +- 9 files changed, 156 insertions(+), 59 deletions(-) diff --git a/source4/include/smb.h b/source4/include/smb.h index 940af75671..1d13b06e25 100644 --- a/source4/include/smb.h +++ b/source4/include/smb.h @@ -152,17 +152,24 @@ enum smb_signing_state {SMB_SIGNING_OFF, SMB_SIGNING_SUPPORTED, #define NTCREATEX_DISP_OVERWRITE_IF 5 /* if exists overwrite, else create */ /* ntcreatex create_options field */ -#define NTCREATEX_OPTIONS_DIRECTORY 0x0001 -#define NTCREATEX_OPTIONS_WRITE_THROUGH 0x0002 -#define NTCREATEX_OPTIONS_SEQUENTIAL_ONLY 0x0004 -#define NTCREATEX_OPTIONS_SYNC_ALERT 0x0010 -#define NTCREATEX_OPTIONS_ASYNC_ALERT 0x0020 -#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE 0x0040 -#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE 0x0200 -#define NTCREATEX_OPTIONS_EIGHT_DOT_THREE_ONLY 0x0400 -#define NTCREATEX_OPTIONS_RANDOM_ACCESS 0x0800 -#define NTCREATEX_OPTIONS_DELETE_ON_CLOSE 0x1000 -#define NTCREATEX_OPTIONS_OPEN_BY_FILE_ID 0x2000 +#define NTCREATEX_OPTIONS_DIRECTORY 0x0001 +#define NTCREATEX_OPTIONS_WRITE_THROUGH 0x0002 +#define NTCREATEX_OPTIONS_SEQUENTIAL_ONLY 0x0004 +#define NTCREATEX_OPTIONS_SYNC_ALERT 0x0010 +#define NTCREATEX_OPTIONS_ASYNC_ALERT 0x0020 +#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE 0x0040 +#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE 0x0200 +#define NTCREATEX_OPTIONS_EIGHT_DOT_THREE_ONLY 0x0400 +#define NTCREATEX_OPTIONS_RANDOM_ACCESS 0x0800 +#define NTCREATEX_OPTIONS_DELETE_ON_CLOSE 0x1000 +#define NTCREATEX_OPTIONS_OPEN_BY_FILE_ID 0x2000 + +/* create options these bits are for private use by backends, they are + not valid on the wire */ +#define NTCREATEX_OPTIONS_PRIVATE_MASK 0xFF000000 +#define NTCREATEX_OPTIONS_PRIVATE_DENY_DOS 0x01000000 +#define NTCREATEX_OPTIONS_PRIVATE_DENY_FCB 0x02000000 + /* ntcreatex impersonation field */ #define NTCREATEX_IMPERSONATION_ANONYMOUS 0 diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c index 9ef2481d26..a9bc8120c8 100644 --- a/source4/ntvfs/ntvfs_generic.c +++ b/source4/ntvfs/ntvfs_generic.c @@ -117,7 +117,7 @@ static NTSTATUS ntvfs_map_async_finish(struct smbsrv_request *req, NTSTATUS stat see if a filename ends in EXE COM DLL or SYM. This is needed for the DENY_DOS mapping for OpenX */ -static BOOL is_exe_file(const char *fname) +BOOL is_exe_filename(const char *fname) { char *p; p = strrchr(fname, '.'); @@ -315,7 +315,9 @@ NTSTATUS ntvfs_map_open(struct smbsrv_request *req, union smb_open *io, break; case OPENX_MODE_DENY_DOS: /* DENY_DOS is quite strange - it depends on the filename! */ - if (is_exe_file(io->openx.in.fname)) { + io2->generic.in.create_options |= + NTCREATEX_OPTIONS_PRIVATE_DENY_DOS; + if (is_exe_filename(io->openx.in.fname)) { io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; @@ -329,6 +331,8 @@ NTSTATUS ntvfs_map_open(struct smbsrv_request *req, union smb_open *io, } break; case OPENX_MODE_DENY_FCB: + io2->generic.in.create_options |= + NTCREATEX_OPTIONS_PRIVATE_DENY_FCB; io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; break; default: @@ -400,7 +404,7 @@ NTSTATUS ntvfs_map_open(struct smbsrv_request *req, union smb_open *io, switch (io->openold.in.flags & OPEN_FLAGS_DENY_MASK) { case OPEN_FLAGS_DENY_DOS: /* DENY_DOS is quite strange - it depends on the filename! */ - if (is_exe_file(io->openold.in.fname)) { + if (is_exe_filename(io->openold.in.fname)) { io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; } else { if ((io->openold.in.flags & OPEN_FLAGS_MODE_MASK) == diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index 5411f83e8d..cfd2b0f159 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -147,19 +147,20 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, return NT_STATUS_TOO_MANY_OPENED_FILES; } - f->fnum = fnum; - f->session = req->session; - f->smbpid = req->smbpid; - f->pvfs = pvfs; - f->pending_list = NULL; - f->lock_count = 0; + f->fnum = fnum; + f->session = req->session; + f->smbpid = req->smbpid; + f->pvfs = pvfs; + f->pending_list = NULL; + f->lock_count = 0; + f->share_access = io->generic.in.share_access; + f->impersonation = io->generic.in.impersonation; f->handle->pvfs = pvfs; f->handle->name = talloc_steal(f->handle, name); f->handle->fd = -1; f->handle->locking_key = data_blob(NULL, 0); f->handle->create_options = io->generic.in.create_options; - f->handle->share_access = io->generic.in.share_access; f->handle->seek_offset = 0; f->handle->position = 0; f->handle->mode = 0; @@ -326,11 +327,8 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE; } - if ((access_mask & SA_RIGHT_FILE_READ_EXEC) && - (access_mask & SA_RIGHT_FILE_WRITE_APPEND)) { + if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) { flags = O_RDWR; - } else if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) { - flags = O_WRONLY; } else { flags = O_RDONLY; } @@ -407,19 +405,20 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, return status; } - f->fnum = fnum; - f->session = req->session; - f->smbpid = req->smbpid; - f->pvfs = pvfs; - f->pending_list = NULL; - f->lock_count = 0; + f->fnum = fnum; + f->session = req->session; + f->smbpid = req->smbpid; + f->pvfs = pvfs; + f->pending_list = NULL; + f->lock_count = 0; + f->share_access = io->generic.in.share_access; + f->access_mask = access_mask; + f->impersonation = io->generic.in.impersonation; f->handle->pvfs = pvfs; f->handle->name = talloc_steal(f->handle, name); f->handle->fd = fd; f->handle->create_options = io->generic.in.create_options; - f->handle->share_access = io->generic.in.share_access; - f->handle->access_mask = access_mask; f->handle->seek_offset = 0; f->handle->position = 0; f->handle->mode = 0; @@ -535,6 +534,86 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason) } +/* + special handling for openx DENY_DOS semantics + + This function attempts a reference open using an existing handle. If its allowed, + then it returns NT_STATUS_OK, otherwise it returns any other code and normal + open processing continues. +*/ +static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs, + struct smbsrv_request *req, union smb_open *io, + struct pvfs_file *f, struct odb_lock *lck) +{ + struct pvfs_state *pvfs = ntvfs->private_data; + struct pvfs_file *f2; + struct pvfs_filename *name; + + /* search for an existing open with the right parameters. Note + the magic ntcreatex options flag, which is set in the + generic mapping code. This might look ugly, but its + actually pretty much now w2k does it internally as well. + + If you look at the BASE-DENYDOS test you will see that a + DENY_DOS is a very special case, and in the right + circumstances you actually get the _same_ handle back + twice, rather than a new handle. + */ + for (f2=pvfs->open_files;f2;f2=f2->next) { + if (f2 != f && + f2->session == req->session && + f2->smbpid == req->smbpid && + (f2->handle->create_options & + (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | + NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) && + (f2->access_mask & SA_RIGHT_FILE_WRITE_DATA) && + StrCaseCmp(f2->handle->name->original_name, + io->generic.in.fname)==0) { + break; + } + } + + if (!f2) { + return NT_STATUS_SHARING_VIOLATION; + } + + /* quite an insane set of semantics ... */ + if (is_exe_filename(io->generic.in.fname) && + (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) { + return NT_STATUS_SHARING_VIOLATION; + } + + /* + setup a reference to the existing handle + */ + talloc_free(f->handle); + f->handle = talloc_reference(f, f2->handle); + + talloc_free(lck); + + name = f->handle->name; + + io->generic.out.oplock_level = NO_OPLOCK; + io->generic.out.fnum = f->fnum; + io->generic.out.create_action = NTCREATEX_ACTION_EXISTED; + io->generic.out.create_time = name->dos.create_time; + io->generic.out.access_time = name->dos.access_time; + io->generic.out.write_time = name->dos.write_time; + io->generic.out.change_time = name->dos.change_time; + io->generic.out.attrib = name->dos.attrib; + io->generic.out.alloc_size = name->dos.alloc_size; + io->generic.out.size = name->st.st_size; + io->generic.out.file_type = FILE_TYPE_DISK; + io->generic.out.ipc_state = 0; + io->generic.out.is_directory = 0; + + talloc_steal(f->pvfs, f); + + return NT_STATUS_OK; +} + + + /* setup for a open retry after a sharing violation */ @@ -549,6 +628,16 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs, NTSTATUS status; struct timeval end_time; + if (io->generic.in.create_options & + (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { + /* see if we can satisfy the request using the special DENY_DOS + code */ + status = pvfs_open_deny_dos(ntvfs, req, io, f, lck); + if (NT_STATUS_IS_OK(status)) { + return status; + } + } + r = talloc_p(req, struct pvfs_open_retry); if (r == NULL) { return NT_STATUS_NO_MEMORY; @@ -638,10 +727,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, } /* use the generic mapping code to avoid implementing all the - different open calls. This won't allow openx to work - perfectly as the mapping code has no way of knowing if two - opens are on the same connection, so this will need to - change eventually */ + different open calls. */ if (io->generic.level != RAW_OPEN_GENERIC) { return ntvfs_map_open(req, io, ntvfs); } @@ -715,11 +801,8 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return NT_STATUS_INVALID_PARAMETER; } - if ((access_mask & SA_RIGHT_FILE_READ_EXEC) && - (access_mask & SA_RIGHT_FILE_WRITE_APPEND)) { + if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) { flags |= O_RDWR; - } else if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) { - flags |= O_WRONLY; } else { flags |= O_RDONLY; } @@ -766,19 +849,20 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return NT_STATUS_TOO_MANY_OPENED_FILES; } - f->fnum = fnum; - f->session = req->session; - f->smbpid = req->smbpid; - f->pvfs = pvfs; - f->pending_list = NULL; - f->lock_count = 0; + f->fnum = fnum; + f->session = req->session; + f->smbpid = req->smbpid; + f->pvfs = pvfs; + f->pending_list = NULL; + f->lock_count = 0; + f->share_access = io->generic.in.share_access; + f->access_mask = access_mask; + f->impersonation = io->generic.in.impersonation; f->handle->pvfs = pvfs; f->handle->fd = -1; f->handle->name = talloc_steal(f->handle, name); f->handle->create_options = io->generic.in.create_options; - f->handle->share_access = io->generic.in.share_access; - f->handle->access_mask = access_mask; f->handle->seek_offset = 0; f->handle->position = 0; f->handle->have_opendb_entry = False; diff --git a/source4/ntvfs/posix/pvfs_qfileinfo.c b/source4/ntvfs/posix/pvfs_qfileinfo.c index fee3b19c9f..c8134939a3 100644 --- a/source4/ntvfs/posix/pvfs_qfileinfo.c +++ b/source4/ntvfs/posix/pvfs_qfileinfo.c @@ -283,7 +283,7 @@ NTSTATUS pvfs_qfileinfo(struct ntvfs_module_context *ntvfs, break; case RAW_FILEINFO_ACCESS_INFORMATION: - info->access_information.out.access_flags = h->access_mask; + info->access_information.out.access_flags = f->access_mask; break; case RAW_FILEINFO_MODE_INFORMATION: diff --git a/source4/ntvfs/posix/pvfs_read.c b/source4/ntvfs/posix/pvfs_read.c index b14a1e601c..5598ccfb08 100644 --- a/source4/ntvfs/posix/pvfs_read.c +++ b/source4/ntvfs/posix/pvfs_read.c @@ -54,7 +54,7 @@ NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs, if (req->flags2 & FLAGS2_READ_PERMIT_EXECUTE) { mask |= SA_RIGHT_FILE_EXECUTE; } - if (!(f->handle->access_mask & mask)) { + if (!(f->access_mask & mask)) { return NT_STATUS_ACCESS_DENIED; } diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c index cad51c751c..a550fb03e4 100644 --- a/source4/ntvfs/posix/pvfs_setfileinfo.c +++ b/source4/ntvfs/posix/pvfs_setfileinfo.c @@ -202,7 +202,7 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, case RAW_SFILEINFO_DISPOSITION_INFO: case RAW_SFILEINFO_DISPOSITION_INFORMATION: - if (!(h->access_mask & STD_RIGHT_DELETE_ACCESS)) { + if (!(f->access_mask & STD_RIGHT_DELETE_ACCESS)) { return NT_STATUS_ACCESS_DENIED; } create_options = h->create_options; @@ -255,7 +255,7 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) { return NT_STATUS_FILE_IS_A_DIRECTORY; } - if (h->access_mask & SA_RIGHT_FILE_WRITE_APPEND) { + if (f->access_mask & SA_RIGHT_FILE_WRITE_APPEND) { ret = ftruncate(h->fd, newstats.st.st_size); } else { ret = truncate(h->name->full_name, newstats.st.st_size); diff --git a/source4/ntvfs/posix/pvfs_write.c b/source4/ntvfs/posix/pvfs_write.c index 7b38123971..6ebd9b70cf 100644 --- a/source4/ntvfs/posix/pvfs_write.c +++ b/source4/ntvfs/posix/pvfs_write.c @@ -48,7 +48,7 @@ NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs, return NT_STATUS_FILE_IS_A_DIRECTORY; } - if (!(f->handle->access_mask & SA_RIGHT_FILE_WRITE_APPEND)) { + if (!(f->access_mask & SA_RIGHT_FILE_WRITE_APPEND)) { return NT_STATUS_ACCESS_VIOLATION; } diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index 3f74de4b63..46cc38c1f8 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -100,8 +100,6 @@ struct pvfs_file_handle { DATA_BLOB locking_key; uint32_t create_options; - uint32_t share_access; - uint32_t access_mask; /* this is set by the mode_information level. What does it do? */ uint32_t mode; @@ -124,6 +122,10 @@ struct pvfs_file { struct pvfs_state *pvfs; + uint32_t impersonation; + uint32_t share_access; + uint32_t access_mask; + /* we need to remember the session it was opened on, as it is illegal to operate on someone elses fnum */ struct smbsrv_session *session; diff --git a/source4/script/tests/test_posix.sh b/source4/script/tests/test_posix.sh index 92ed4fe52a..e1d6201a72 100755 --- a/source4/script/tests/test_posix.sh +++ b/source4/script/tests/test_posix.sh @@ -33,8 +33,8 @@ testit() { tests="BASE-FDPASS BASE-LOCK1 BASE-LOCK2 BASE-LOCK3 BASE-LOCK4" tests="$tests BASE-LOCK5 BASE-LOCK6 BASE-LOCK7 BASE-UNLINK BASE-ATTR" tests="$tests BASE-NEGNOWAIT BASE-DIR1 BASE-DIR2 BASE-VUID" -tests="$tests BASE-DENY2 BASE-TCON BASE-TCONDEV BASE-RW1" -tests="$tests BASE-DENY3 BASE-XCOPY BASE-OPEN" +tests="$tests BASE-DENY1 BASE-DENY2 BASE-TCON BASE-TCONDEV BASE-RW1" +tests="$tests BASE-DENY3 BASE-XCOPY BASE-OPEN BASE-DENYDOS" tests="$tests BASE-DELETE BASE-PROPERTIES BASE-MANGLE" tests="$tests BASE-CHKPATH BASE-SECLEAK BASE-TRANS2" tests="$tests BASE-NTDENY1 BASE-NTDENY2 BASE-RENAME BASE-OPENATTR" @@ -43,7 +43,7 @@ tests="$tests RAW-LOCK RAW-MKDIR RAW-SEEK RAW-CONTEXT RAW-MUX RAW-OPEN" tests="$tests RAW-UNLINK RAW-READ RAW-CLOSE RAW-IOCTL RAW-SEARCH RAW-CHKPATH" tests="$tests LOCAL-ICONV LOCAL-TALLOC LOCAL-MESSAGING LOCAL-BINDING LOCAL-IDTREE" -soon="BASE-DENY1 BASE-CHARSET" +soon="BASE-CHARSET" soon="$soon RAW-SFILEINFO RAW-OPLOCK RAW-NOTIFY" soon="$soon RAW-WRITE RAW-RENAME" -- cgit