Print this page
NEX-19378 Access problem with SMB server
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-19152 MacOS HighSierra Finder crashes...
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-13644 File access audit logging
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-17779 Creating named streams on existing files is not quite right
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-17289 Minimal SMB 3.0.2 support
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-2807 Restoring previous versions from snapshots doesn't work with nested folders.
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-13653 Obsolete SMB server work-around for ZFS read-only
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-6276 SMB sparse file support
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5844 want SMB2 ioctl FSCTL_SRV_COPYCHUNK
NEX-6124 smb_fsop_read/write should allow file != sr->fid_ofile
NEX-6125 smbtorture invalid response with smb2.ioctl
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-6041 Should pass the smbtorture lock tests
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-4474 SMB open with access=MAXIMUM_ALLOWED fails after NEX-3232
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-58 smbsrv should be immune to its own FEM hooks
SMB-50 User-mode SMB server
Includes work by these authors:
Thomas Keiser <thomas.keiser@nexenta.com>
Albert Lee <trisk@nexenta.com>
SMB-65 SMB server in non-global zones (use zone_kcred())
SMB-63 taskq_create_proc ... TQ_DYNAMIC puts tasks in p0
re #11974 CIFS Share - Tree connect fails from Windows 7 Clients
SUS-172 Excel 2003 warning dialog when re-saving a file
SUS-173 Open fails if the client does not ask for read_attribute permission
re #7815 SMB server delivers old modification time...
re #11215 rb3676 sesctl to SGI JBOD hangs in biowait() with a command stuck in mptsas driver
re #10734 NT Trans. Notify returning too quickly
@@ -18,26 +18,27 @@
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/sid.h>
#include <sys/nbmlock.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smb_kproto.h>
#include <acl/acl_common.h>
#include <sys/fcntl.h>
+#include <sys/filio.h>
#include <sys/flock.h>
#include <fs/fs_subr.h>
extern caller_context_t smb_ct;
-static int smb_fsop_create_stream(smb_request_t *, cred_t *, smb_node_t *,
- char *, char *, int, smb_attr_t *, smb_node_t **);
+static int smb_fsop_create_file_with_stream(smb_request_t *, cred_t *,
+ smb_node_t *, char *, char *, int, smb_attr_t *, smb_node_t **);
static int smb_fsop_create_file(smb_request_t *, cred_t *, smb_node_t *,
char *, int, smb_attr_t *, smb_node_t **);
#ifdef _KERNEL
@@ -131,12 +132,14 @@
cred_t *kcr = zone_kcred();
int aclbsize = 0; /* size of acl list in bytes */
int flags = 0;
int rc;
boolean_t is_dir;
+ boolean_t do_audit;
ASSERT(fs_sd);
+ ASSERT(ret_snode != NULL);
if (SMB_TREE_IS_CASEINSENSITIVE(sr))
flags = SMB_IGNORE_CASE;
if (SMB_TREE_SUPPORTS_CATIA(sr))
flags |= SMB_CATIA;
@@ -143,10 +146,11 @@
ASSERT(cr);
is_dir = ((fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR) != 0);
+ do_audit = smb_audit_init(sr);
if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_ACLONCREATE)) {
if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) {
dacl = fs_sd->sd_zdacl;
sacl = fs_sd->sd_zsacl;
ASSERT(dacl || sacl);
@@ -181,10 +185,16 @@
if (SMB_TREE_HAS_ACCESS(sr, ACE_ADD_FILE) != 0)
rc = smb_vop_create(dnode->vp, name, attr,
&vp, flags, cr, vsap);
}
+ if (do_audit) {
+ smb_audit_fini(sr,
+ is_dir ? ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE,
+ dnode, rc == 0);
+ }
+
if (vsap != NULL)
kmem_free(vsap->vsa_aclentp, aclbsize);
if (rc != 0)
return (rc);
@@ -235,10 +245,16 @@
} else {
rc = smb_vop_create(dnode->vp, name, attr, &vp,
flags, cr, NULL);
}
+ if (do_audit) {
+ smb_audit_fini(sr,
+ is_dir ? ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE,
+ dnode, rc == 0);
+ }
+
if (rc != 0)
return (rc);
*ret_snode = smb_node_lookup(sr, &sr->arg.open, cr, vp,
name, dnode, NULL);
@@ -317,11 +333,11 @@
if (smb_is_stream_name(name)) {
fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
smb_stream_parse_name(name, fname, sname);
- rc = smb_fsop_create_stream(sr, cr, dnode,
+ rc = smb_fsop_create_file_with_stream(sr, cr, dnode,
fname, sname, flags, attr, ret_snode);
kmem_free(fname, MAXNAMELEN);
kmem_free(sname, MAXNAMELEN);
return (rc);
@@ -346,40 +362,38 @@
}
/*
- * smb_fsop_create_stream
+ * smb_fsop_create_file_with_stream
*
- * Create NTFS named stream file (sname) on unnamed stream
- * file (fname), creating the unnamed stream file if it
+ * Create named stream (sname) on file (fname), creating the file if it
* doesn't exist.
- * If we created the unnamed stream file and then creation
- * of the named stream file fails, we delete the unnamed stream.
+ * If we created the file and then creation of the named stream fails,
+ * we delete the file.
* Since we use the real file name for the smb_vop_remove we
* clear the SMB_IGNORE_CASE flag to ensure a case sensitive
* match.
*
- * The second parameter of smb_vop_setattr() is set to
- * NULL, even though an unnamed stream exists. This is
- * because we want to set the UID and GID on the named
- * stream in this case for consistency with the (unnamed
- * stream) file (see comments for smb_vop_setattr()).
+ * Note that some stream "types" are "restricted" and only
+ * internal callers (cr == kcred) can create those.
*/
static int
-smb_fsop_create_stream(smb_request_t *sr, cred_t *cr,
+smb_fsop_create_file_with_stream(smb_request_t *sr, cred_t *cr,
smb_node_t *dnode, char *fname, char *sname, int flags,
smb_attr_t *attr, smb_node_t **ret_snode)
{
- smb_attr_t fattr;
smb_node_t *fnode;
- vnode_t *xattrdvp;
- vnode_t *vp;
cred_t *kcr = zone_kcred();
int rc = 0;
boolean_t fcreate = B_FALSE;
+ ASSERT(ret_snode != NULL);
+
+ if (cr != kcr && smb_strname_restricted(sname))
+ return (EACCES);
+
/* Look up / create the unnamed stream, fname */
rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
sr->tid_tree->t_snode, dnode, fname, &fnode);
if (rc == ENOENT) {
fcreate = B_TRUE;
@@ -387,52 +401,91 @@
attr, &fnode);
}
if (rc != 0)
return (rc);
- fattr.sa_mask = SMB_AT_UID | SMB_AT_GID;
- rc = smb_vop_getattr(fnode->vp, NULL, &fattr, 0, kcr);
+ rc = smb_fsop_create_stream(sr, cr, dnode, fnode, sname, flags, attr,
+ ret_snode);
- if (rc == 0) {
- /* create the named stream, sname */
- rc = smb_vop_stream_create(fnode->vp, sname, attr,
- &vp, &xattrdvp, flags, cr);
- }
if (rc != 0) {
if (fcreate) {
flags &= ~SMB_IGNORE_CASE;
(void) smb_vop_remove(dnode->vp,
fnode->od_name, flags, cr);
}
+ }
+
smb_node_release(fnode);
return (rc);
+}
+
+/*
+ * smb_fsop_create_stream
+ *
+ * Create named stream (sname) on existing file (fnode).
+ *
+ * The second parameter of smb_vop_setattr() is set to
+ * NULL, even though an unnamed stream exists. This is
+ * because we want to set the UID and GID on the named
+ * stream in this case for consistency with the (unnamed
+ * stream) file (see comments for smb_vop_setattr()).
+ *
+ * Note that some stream "types" are "restricted" and only
+ * internal callers (cr == kcred) can create those.
+ */
+int
+smb_fsop_create_stream(smb_request_t *sr, cred_t *cr,
+ smb_node_t *dnode, smb_node_t *fnode, char *sname, int flags,
+ smb_attr_t *attr, smb_node_t **ret_snode)
+{
+ smb_attr_t fattr;
+ vnode_t *xattrdvp;
+ vnode_t *vp;
+ cred_t *kcr = zone_kcred();
+ int rc = 0;
+
+ ASSERT(ret_snode != NULL);
+
+ if (cr != kcr && smb_strname_restricted(sname))
+ return (EACCES);
+
+ bzero(&fattr, sizeof (fattr));
+ fattr.sa_mask = SMB_AT_UID | SMB_AT_GID;
+ rc = smb_vop_getattr(fnode->vp, NULL, &fattr, 0, kcr);
+
+ if (rc == 0) {
+ /* create the named stream, sname */
+ rc = smb_vop_stream_create(fnode->vp, sname,
+ attr, &vp, &xattrdvp, flags, cr);
}
+ if (rc != 0)
+ return (rc);
attr->sa_vattr.va_uid = fattr.sa_vattr.va_uid;
attr->sa_vattr.va_gid = fattr.sa_vattr.va_gid;
attr->sa_mask = SMB_AT_UID | SMB_AT_GID;
rc = smb_vop_setattr(vp, NULL, attr, 0, kcr);
if (rc != 0) {
- smb_node_release(fnode);
+ VN_RELE(xattrdvp);
+ VN_RELE(vp);
return (rc);
}
*ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdvp,
vp, sname);
- smb_node_release(fnode);
VN_RELE(xattrdvp);
VN_RELE(vp);
if (*ret_snode == NULL)
rc = ENOMEM;
/* notify change to the unnamed stream */
if (rc == 0)
smb_node_notify_change(dnode,
- FILE_ACTION_ADDED_STREAM, fname);
+ FILE_ACTION_ADDED_STREAM, fnode->od_name);
return (rc);
}
/*
@@ -445,10 +498,12 @@
{
smb_arg_open_t *op = &sr->sr_open;
vnode_t *vp;
int rc;
+ ASSERT(ret_snode != NULL);
+
#ifdef _KERNEL
smb_fssd_t fs_sd;
uint32_t secinfo;
uint32_t status;
@@ -486,14 +541,23 @@
#endif /* _KERNEL */
{
/*
* No incoming SD and filesystem is not ZFS
* let the filesystem handles the inheritance.
+ *
+ * fsop_create_with_sd handles auditing in the other cases.
+ * Handle it explicitly here.
*/
+ boolean_t do_audit = smb_audit_init(sr);
+
rc = smb_vop_create(dnode->vp, name, attr, &vp,
flags, cr, NULL);
+ if (do_audit) {
+ smb_audit_fini(sr, ACE_ADD_FILE, dnode, rc == 0);
+ }
+
if (rc == 0) {
*ret_snode = smb_node_lookup(sr, op, cr, vp,
name, dnode, NULL);
if (*ret_snode == NULL)
@@ -625,13 +689,24 @@
smb_fssd_term(&fs_sd);
} else
#endif /* _KERNEL */
{
+ /*
+ * fsop_create_with_sd handles auditing in the other cases.
+ * Handle it explicitly here.
+ */
+ boolean_t do_audit = smb_audit_init(sr);
+
rc = smb_vop_mkdir(dnode->vp, name, attr, &vp, flags, cr,
NULL);
+ if (do_audit) {
+ smb_audit_fini(sr, ACE_ADD_SUBDIRECTORY, dnode,
+ rc == 0);
+ }
+
if (rc == 0) {
*ret_snode = smb_node_lookup(sr, op, cr, vp, name,
dnode, NULL);
if (*ret_snode == NULL)
@@ -656,10 +731,13 @@
* for avoiding this wrapper.
*
* It is assumed that a reference exists on snode coming into this routine.
*
* A null smb_request might be passed to this function.
+ *
+ * Note that some stream "types" are "restricted" and only
+ * internal callers (cr == kcred) can remove those.
*/
int
smb_fsop_remove(
smb_request_t *sr,
cred_t *cr,
@@ -691,10 +769,15 @@
fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
if (dnode->flags & NODE_XATTR_DIR) {
+ if (cr != zone_kcred() && smb_strname_restricted(name)) {
+ rc = EACCES;
+ goto out;
+ }
+
fnode = dnode->n_dnode;
rc = smb_vop_stream_remove(fnode->vp, name, flags, cr);
/* notify change to the unnamed stream */
if ((rc == 0) && fnode->n_dnode) {
@@ -702,10 +785,15 @@
FILE_ACTION_REMOVED_STREAM, fnode->od_name);
}
} else if (smb_is_stream_name(name)) {
smb_stream_parse_name(name, fname, sname);
+ if (cr != zone_kcred() && smb_strname_restricted(sname)) {
+ rc = EACCES;
+ goto out;
+ }
+
/*
* Look up the unnamed stream (i.e. fname).
* Unmangle processing will be done on fname
* as well as any link target.
*/
@@ -712,13 +800,11 @@
rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
sr->tid_tree->t_snode, dnode, fname, &fnode);
if (rc != 0) {
- kmem_free(fname, MAXNAMELEN);
- kmem_free(sname, MAXNAMELEN);
- return (rc);
+ goto out;
}
/*
* XXX
* Need to find out what permission is required by NTFS
@@ -737,13 +823,11 @@
rc = smb_vop_remove(dnode->vp, name, flags, cr);
if (rc == ENOENT) {
if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
!smb_maybe_mangled(name)) {
- kmem_free(fname, MAXNAMELEN);
- kmem_free(sname, MAXNAMELEN);
- return (rc);
+ goto out;
}
longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
if (SMB_TREE_SUPPORTS_ABE(sr))
flags |= SMB_ABE;
@@ -769,10 +853,11 @@
smb_node_notify_change(dnode,
FILE_ACTION_REMOVED, name);
}
}
+out:
kmem_free(fname, MAXNAMELEN);
kmem_free(sname, MAXNAMELEN);
return (rc);
}
@@ -814,10 +899,11 @@
status = smb_odir_openat(sr, fnode, &od);
switch (status) {
case 0:
break;
+ case NT_STATUS_OBJECT_NAME_NOT_FOUND:
case NT_STATUS_NO_SUCH_FILE:
case NT_STATUS_NOT_SUPPORTED:
/* No streams to remove. */
return (0);
default:
@@ -1112,12 +1198,17 @@
/*
* XXX: Lock required through smb_node_release() below?
*/
+ /*
+ * Don't audit the lookup
+ */
+ smb_audit_save();
rc = smb_vop_lookup(from_dnode->vp, from_name, &from_vp, NULL,
flags, &ret_flags, NULL, &from_attr, cr);
+ smb_audit_load();
if (rc != 0)
return (rc);
if (from_attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) {
@@ -1153,10 +1244,11 @@
if (rc != NT_STATUS_SUCCESS) {
VN_RELE(from_vp);
return (EACCES);
}
+ /* TODO: rename drops ATTR_NOACLCHECK, so this is a no-op. */
if (smb_tree_has_feature(sr->tid_tree,
SMB_TREE_ACEMASKONACCESS))
flags = ATTR_NOACLCHECK;
}
@@ -1237,29 +1329,10 @@
if (SMB_TREE_HAS_ACCESS(sr,
ACE_WRITE_ATTRIBUTES | ACE_WRITE_NAMED_ATTRS) == 0)
return (EACCES);
/*
- * The file system cannot detect pending READDONLY
- * (i.e. if the file has been opened readonly but
- * not yet closed) so we need to test READONLY here.
- *
- * Note that file handle that were opened before the
- * READONLY flag was set in the node (or the FS) are
- * immune to that change, and remain writable.
- */
- if (sr && (set_attr->sa_mask & SMB_AT_SIZE)) {
- if (sr->fid_ofile) {
- if (SMB_OFILE_IS_READONLY(sr->fid_ofile))
- return (EACCES);
- } else {
- if (SMB_PATHFILE_IS_READONLY(sr, snode))
- return (EACCES);
- }
- }
-
- /*
* SMB checks access on open and retains an access granted
* mask for use while the file is open. ACL changes should
* not affect access to an open file.
*
* If the setattr is being performed on an ofile:
@@ -1311,25 +1384,22 @@
return (rc);
}
/*
* Support for SMB2 setinfo FileValidDataLengthInformation.
- * Free data from the specified offset to EoF.
- *
- * This can effectively truncate data. It truncates the data
- * leaving the file size as it was, leaving zeros after the
- * offset specified here. That is effectively modifying the
- * file content, so for access control this is a write.
+ * Free (zero out) data in the range off, off+len
*/
int
-smb_fsop_set_data_length(
+smb_fsop_freesp(
smb_request_t *sr,
cred_t *cr,
- smb_node_t *node,
- offset_t end_of_data)
+ smb_ofile_t *ofile,
+ off64_t off,
+ off64_t len)
{
flock64_t flk;
+ smb_node_t *node = ofile->f_node;
uint32_t status;
uint32_t access = FILE_WRITE_DATA;
int rc;
ASSERT(cr);
@@ -1345,27 +1415,10 @@
if (SMB_TREE_HAS_ACCESS(sr, access) == 0)
return (EACCES);
/*
- * The file system cannot detect pending READDONLY
- * (i.e. if the file has been opened readonly but
- * not yet closed) so we need to test READONLY here.
- *
- * Note that file handle that were opened before the
- * READONLY flag was set in the node (or the FS) are
- * immune to that change, and remain writable.
- */
- if (sr->fid_ofile) {
- if (SMB_OFILE_IS_READONLY(sr->fid_ofile))
- return (EACCES);
- } else {
- /* This requires an open file. */
- return (EACCES);
- }
-
- /*
* SMB checks access on open and retains an access granted
* mask for use while the file is open. ACL changes should
* not affect access to an open file.
*
* If the setattr is being performed on an ofile:
@@ -1375,11 +1428,12 @@
status = smb_ofile_access(sr->fid_ofile, cr, access);
if (status != NT_STATUS_SUCCESS)
return (EACCES);
bzero(&flk, sizeof (flk));
- flk.l_start = end_of_data;
+ flk.l_start = off;
+ flk.l_len = len;
rc = smb_vop_space(node->vp, F_FREESP, &flk, FWRITE, 0LL, cr);
return (rc);
}
@@ -1390,37 +1444,48 @@
* the the calls are performed with the appropriate credentials.
* Please document any direct call to explain the reason
* for avoiding this wrapper.
*
* It is assumed that a reference exists on snode coming into this routine.
+ * Note that ofile may be different from sr->fid_ofile, or may be NULL.
*/
int
-smb_fsop_read(smb_request_t *sr, cred_t *cr, smb_node_t *snode, uio_t *uio)
+smb_fsop_read(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
+ smb_ofile_t *ofile, uio_t *uio, int ioflag)
{
caller_context_t ct;
cred_t *kcr = zone_kcred();
+ uint32_t amask;
int svmand;
int rc;
ASSERT(cr);
ASSERT(snode);
ASSERT(snode->n_magic == SMB_NODE_MAGIC);
ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
ASSERT(sr);
- ASSERT(sr->fid_ofile);
- if (SMB_TREE_HAS_ACCESS(sr, ACE_READ_DATA) == 0)
+ if (ofile != NULL) {
+ /*
+ * Check tree access. Not SMB_TREE_HAS_ACCESS
+ * because we need to use ofile->f_tree
+ */
+ if ((ofile->f_tree->t_access & ACE_READ_DATA) == 0)
return (EACCES);
- rc = smb_ofile_access(sr->fid_ofile, cr, FILE_READ_DATA);
- if ((rc != NT_STATUS_SUCCESS) &&
- (sr->smb_flg2 & SMB_FLAGS2_READ_IF_EXECUTE))
- rc = smb_ofile_access(sr->fid_ofile, cr, FILE_EXECUTE);
-
- if (rc != NT_STATUS_SUCCESS)
+ /*
+ * Check ofile access. Use in-line smb_ofile_access
+ * so we can check both amask bits at the same time.
+ * If any bit in amask is granted, allow this read.
+ */
+ amask = FILE_READ_DATA;
+ if (sr->smb_flg2 & SMB_FLAGS2_READ_IF_EXECUTE)
+ amask |= FILE_EXECUTE;
+ if (cr != kcr && (ofile->f_granted_access & amask) == 0)
return (EACCES);
+ }
/*
* Streams permission are checked against the unnamed stream,
* but in FS level they have their own permissions. To avoid
* rejection by FS due to lack of permission on the actual
@@ -1434,74 +1499,82 @@
if (rc) {
smb_node_end_crit(snode);
return (rc);
}
+ /*
+ * Note: SMB allows a zero-byte read, which should not
+ * conflict with any locks. However nbl_lock_conflict
+ * takes a zero-byte length as lock to EOF, so we must
+ * special case that here.
+ */
+ if (uio->uio_resid > 0) {
ct = smb_ct;
- ct.cc_pid = sr->fid_ofile->f_uniqid;
+ if (ofile != NULL)
+ ct.cc_pid = ofile->f_uniqid;
rc = nbl_lock_conflict(snode->vp, NBL_READ, uio->uio_loffset,
- uio->uio_iov->iov_len, svmand, &ct);
-
- if (rc) {
+ uio->uio_resid, svmand, &ct);
+ if (rc != 0) {
smb_node_end_crit(snode);
return (ERANGE);
}
+ }
- rc = smb_vop_read(snode->vp, uio, cr);
+ rc = smb_vop_read(snode->vp, uio, ioflag, cr);
smb_node_end_crit(snode);
return (rc);
}
/*
* smb_fsop_write
*
- * This is a wrapper function used for smb_write and smb_write_raw operations.
- *
* It is assumed that a reference exists on snode coming into this routine.
+ * Note that ofile may be different from sr->fid_ofile, or may be NULL.
*/
int
smb_fsop_write(
smb_request_t *sr,
cred_t *cr,
smb_node_t *snode,
+ smb_ofile_t *ofile,
uio_t *uio,
uint32_t *lcount,
int ioflag)
{
caller_context_t ct;
smb_attr_t attr;
+ cred_t *kcr = zone_kcred();
smb_node_t *u_node;
vnode_t *u_vp = NULL;
- smb_ofile_t *of;
vnode_t *vp;
- cred_t *kcr = zone_kcred();
+ uint32_t amask;
int svmand;
int rc;
ASSERT(cr);
ASSERT(snode);
ASSERT(snode->n_magic == SMB_NODE_MAGIC);
ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
ASSERT(sr);
- ASSERT(sr->tid_tree);
- of = sr->fid_ofile;
vp = snode->vp;
- if (SMB_TREE_IS_READONLY(sr))
+ if (ofile != NULL) {
+ amask = FILE_WRITE_DATA | FILE_APPEND_DATA;
+
+ /* Check tree access. */
+ if ((ofile->f_tree->t_access & amask) == 0)
return (EROFS);
- if (SMB_OFILE_IS_READONLY(of) ||
- SMB_TREE_HAS_ACCESS(sr, ACE_WRITE_DATA | ACE_APPEND_DATA) == 0)
+ /*
+ * Check ofile access. Use in-line smb_ofile_access
+ * so we can check both amask bits at the same time.
+ * If any bit in amask is granted, allow this write.
+ */
+ if (cr != kcr && (ofile->f_granted_access & amask) == 0)
return (EACCES);
-
- rc = smb_ofile_access(of, cr, FILE_WRITE_DATA);
- if (rc != NT_STATUS_SUCCESS) {
- rc = smb_ofile_access(of, cr, FILE_APPEND_DATA);
- if (rc != NT_STATUS_SUCCESS)
- return (EACCES);
}
/*
* Streams permission are checked against the unnamed stream,
* but in FS level they have their own permissions. To avoid
@@ -1521,19 +1594,27 @@
if (rc) {
smb_node_end_crit(snode);
return (rc);
}
+ /*
+ * Note: SMB allows a zero-byte write, which should not
+ * conflict with any locks. However nbl_lock_conflict
+ * takes a zero-byte length as lock to EOF, so we must
+ * special case that here.
+ */
+ if (uio->uio_resid > 0) {
ct = smb_ct;
- ct.cc_pid = of->f_uniqid;
+ if (ofile != NULL)
+ ct.cc_pid = ofile->f_uniqid;
rc = nbl_lock_conflict(vp, NBL_WRITE, uio->uio_loffset,
- uio->uio_iov->iov_len, svmand, &ct);
-
- if (rc) {
+ uio->uio_resid, svmand, &ct);
+ if (rc != 0) {
smb_node_end_crit(snode);
return (ERANGE);
}
+ }
rc = smb_vop_write(vp, uio, ioflag, lcount, cr);
/*
* Once the mtime has been set via this ofile, the
@@ -1544,12 +1625,13 @@
* The VFS interface does not offer a way to ask it to
* skip the mtime updates, so we simulate the desired
* behavior by re-setting the mtime after writes on a
* handle where the mtime has been set.
*/
- if (of->f_pending_attr.sa_mask & SMB_AT_MTIME) {
- bcopy(&of->f_pending_attr, &attr, sizeof (attr));
+ if (ofile != NULL &&
+ (ofile->f_pending_attr.sa_mask & SMB_AT_MTIME) != 0) {
+ bcopy(&ofile->f_pending_attr, &attr, sizeof (attr));
attr.sa_mask = SMB_AT_MTIME;
(void) smb_vop_setattr(vp, u_vp, &attr, 0, kcr);
}
smb_node_end_crit(snode);
@@ -1556,10 +1638,34 @@
return (rc);
}
/*
+ * Find the next allocated range starting at or after
+ * the offset (*datap), returning the start/end of
+ * that range in (*datap, *holep)
+ */
+int
+smb_fsop_next_alloc_range(
+ cred_t *cr,
+ smb_node_t *node,
+ off64_t *datap,
+ off64_t *holep)
+{
+ int err;
+
+ err = smb_vop_ioctl(node->vp, _FIO_SEEK_DATA, datap, cr);
+ if (err != 0)
+ return (err);
+
+ *holep = *datap;
+ err = smb_vop_ioctl(node->vp, _FIO_SEEK_HOLE, holep, cr);
+
+ return (err);
+}
+
+/*
* smb_fsop_statfs
*
* This is a wrapper function used for stat operations.
*/
int
@@ -1587,10 +1693,13 @@
* separate from that on the unnamed stream. If READ or EXECUTE
* access has been requested on a named stream, an additional access
* check is performed on the named stream in case it has been
* quarantined. kcred is used to avoid issues with the permissions
* set on the extended attribute file representing the named stream.
+ *
+ * Note that some stream "types" are "restricted" and only
+ * internal callers (cr == kcred) can access those.
*/
int
smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
uint32_t faccess)
{
@@ -1617,21 +1726,26 @@
if (smb_node_is_reparse(snode) && (faccess & DELETE))
return (NT_STATUS_ACCESS_DENIED);
unnamed_node = SMB_IS_STREAM(snode);
if (unnamed_node) {
+ cred_t *kcr = zone_kcred();
+
ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
+ if (cr != kcr && smb_strname_restricted(snode->od_name))
+ return (NT_STATUS_ACCESS_DENIED);
+
/*
* Perform VREAD access check on the named stream in case it
* is quarantined. kcred is passed to smb_vop_access so it
* doesn't fail due to lack of permission.
*/
if (faccess & (FILE_READ_DATA | FILE_EXECUTE)) {
error = smb_vop_access(snode->vp, VREAD,
- 0, NULL, zone_kcred());
+ 0, NULL, kcr);
if (error)
return (NT_STATUS_ACCESS_DENIED);
}
/*
@@ -1690,13 +1804,17 @@
}
/*
* smb_fsop_lookup_name()
*
+ * Lookup both the file and stream specified in 'name'.
* If name indicates that the file is a stream file, perform
* stream specific lookup, otherwise call smb_fsop_lookup.
*
+ * On success, returns the found node in *ret_snode. This will be either a named
+ * or unnamed stream node, depending on the name specified.
+ *
* Return an error if the looked-up file is in outside the tree.
* (Required when invoked from open path.)
*
* Case sensitivity flags (SMB_IGNORE_CASE, SMB_CASE_SENSITIVE):
* if SMB_CASE_SENSITIVE is set, the SMB_IGNORE_CASE flag will NOT be set
@@ -1714,22 +1832,68 @@
smb_node_t *root_node,
smb_node_t *dnode,
char *name,
smb_node_t **ret_snode)
{
- smb_node_t *fnode;
- vnode_t *xattrdirvp;
- vnode_t *vp;
- char *od_name;
+ char *sname = NULL;
+ int rc;
+ smb_node_t *tmp_node;
+
+ ASSERT(ret_snode != NULL);
+
+ rc = smb_fsop_lookup_file(sr, cr, flags, root_node, dnode, name,
+ &sname, ret_snode);
+
+ if (rc != 0 || sname == NULL)
+ return (rc);
+
+ tmp_node = *ret_snode;
+ rc = smb_fsop_lookup_stream(sr, cr, flags, root_node, tmp_node, sname,
+ ret_snode);
+ kmem_free(sname, MAXNAMELEN);
+ smb_node_release(tmp_node);
+
+ return (rc);
+}
+
+/*
+ * smb_fsop_lookup_file()
+ *
+ * Look up of the file portion of 'name'. If a Stream is specified,
+ * return the stream name in 'sname', which this allocates.
+ * The caller must free 'sname'.
+ *
+ * Return an error if the looked-up file is outside the tree.
+ * (Required when invoked from open path.)
+ *
+ * Case sensitivity flags (SMB_IGNORE_CASE, SMB_CASE_SENSITIVE):
+ * if SMB_CASE_SENSITIVE is set, the SMB_IGNORE_CASE flag will NOT be set
+ * based on the tree's case sensitivity. However, if the SMB_IGNORE_CASE
+ * flag is set in the flags value passed as a parameter, a case insensitive
+ * lookup WILL be done (regardless of whether SMB_CASE_SENSITIVE is set
+ * or not).
+ */
+
+int
+smb_fsop_lookup_file(
+ smb_request_t *sr,
+ cred_t *cr,
+ int flags,
+ smb_node_t *root_node,
+ smb_node_t *dnode,
+ char *name,
+ char **sname,
+ smb_node_t **ret_snode)
+{
char *fname;
- char *sname;
int rc;
ASSERT(cr);
ASSERT(dnode);
ASSERT(dnode->n_magic == SMB_NODE_MAGIC);
ASSERT(dnode->n_state != SMB_NODE_STATE_DESTROYING);
+ ASSERT(ret_snode != NULL);
/*
* The following check is required for streams processing, below
*/
@@ -1736,82 +1900,99 @@
if (!(flags & SMB_CASE_SENSITIVE)) {
if (SMB_TREE_IS_CASEINSENSITIVE(sr))
flags |= SMB_IGNORE_CASE;
}
+ *sname = NULL;
+ if (smb_is_stream_name(name)) {
+ *sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
- sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+ smb_stream_parse_name(name, fname, *sname);
- if (smb_is_stream_name(name)) {
- smb_stream_parse_name(name, fname, sname);
-
/*
* Look up the unnamed stream (i.e. fname).
* Unmangle processing will be done on fname
* as well as any link target.
*/
rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode,
- fname, &fnode);
-
- if (rc != 0) {
+ fname, ret_snode);
kmem_free(fname, MAXNAMELEN);
- kmem_free(sname, MAXNAMELEN);
+ } else {
+ rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode, name,
+ ret_snode);
+ }
+
+ if (rc == 0) {
+ ASSERT(ret_snode);
+ if (SMB_TREE_CONTAINS_NODE(sr, *ret_snode) == 0) {
+ smb_node_release(*ret_snode);
+ *ret_snode = NULL;
+ rc = EACCES;
+ }
+ }
+
+ if (rc != 0 && *sname != NULL) {
+ kmem_free(*sname, MAXNAMELEN);
+ *sname = NULL;
+ }
return (rc);
+}
+
+/*
+ * smb_fsop_lookup_stream
+ *
+ * The file exists, see if the stream exists.
+ */
+int
+smb_fsop_lookup_stream(
+ smb_request_t *sr,
+ cred_t *cr,
+ int flags,
+ smb_node_t *root_node,
+ smb_node_t *fnode,
+ char *sname,
+ smb_node_t **ret_snode)
+{
+ char *od_name;
+ vnode_t *xattrdirvp;
+ vnode_t *vp;
+ int rc;
+
+ /*
+ * The following check is required for streams processing, below
+ */
+
+ if (!(flags & SMB_CASE_SENSITIVE)) {
+ if (SMB_TREE_IS_CASEINSENSITIVE(sr))
+ flags |= SMB_IGNORE_CASE;
}
od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
/*
* od_name is the on-disk name of the stream, except
* without the prepended stream prefix (SMB_STREAM_PREFIX)
*/
- /*
- * XXX
- * What permissions NTFS requires for stream lookup if any?
- */
rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name,
&xattrdirvp, flags, root_node->vp, cr);
if (rc != 0) {
- smb_node_release(fnode);
- kmem_free(fname, MAXNAMELEN);
- kmem_free(sname, MAXNAMELEN);
kmem_free(od_name, MAXNAMELEN);
return (rc);
}
*ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
vp, od_name);
kmem_free(od_name, MAXNAMELEN);
- smb_node_release(fnode);
VN_RELE(xattrdirvp);
VN_RELE(vp);
- if (*ret_snode == NULL) {
- kmem_free(fname, MAXNAMELEN);
- kmem_free(sname, MAXNAMELEN);
+ if (*ret_snode == NULL)
return (ENOMEM);
- }
- } else {
- rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode, name,
- ret_snode);
- }
- if (rc == 0) {
- ASSERT(ret_snode);
- if (SMB_TREE_CONTAINS_NODE(sr, *ret_snode) == 0) {
- smb_node_release(*ret_snode);
- *ret_snode = NULL;
- rc = EACCES;
- }
- }
-
- kmem_free(fname, MAXNAMELEN);
- kmem_free(sname, MAXNAMELEN);
-
return (rc);
}
/*
* smb_fsop_lookup
@@ -1879,12 +2060,21 @@
if (SMB_TREE_SUPPORTS_CATIA(sr))
flags |= SMB_CATIA;
if (SMB_TREE_SUPPORTS_ABE(sr))
flags |= SMB_ABE;
- od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+ /*
+ * Can have "" or "." when opening named streams on a directory.
+ */
+ if (name[0] == '\0' || (name[0] == '.' && name[1] == '\0')) {
+ smb_node_ref(dnode);
+ *ret_snode = dnode;
+ return (0);
+ }
+ od_name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
+
rc = smb_vop_lookup(dnode->vp, name, &vp, od_name, flags,
&ret_flags, root_node ? root_node->vp : NULL, &attr, cr);
if (rc != 0) {
if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
@@ -1925,11 +2115,11 @@
}
if ((flags & SMB_FOLLOW_LINKS) && (vp->v_type == VLNK) &&
((attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)) {
rc = smb_pathname(sr, od_name, FOLLOW, root_node, dnode,
- &lnk_dnode, &lnk_target_node, cr);
+ &lnk_dnode, &lnk_target_node, cr, NULL);
if (rc != 0) {
/*
* The link is assumed to be for the last component
* of a path. Hence any ENOTDIR error will be returned
@@ -2034,14 +2224,17 @@
{
int error = 0;
int flags = 0;
int access = 0;
acl_t *acl;
- smb_node_t *unnamed_node;
ASSERT(cr);
+ /* Can't query security on named streams */
+ if (SMB_IS_STREAM(snode) != NULL)
+ return (EINVAL);
+
if (SMB_TREE_HAS_ACCESS(sr, ACE_READ_ACL) == 0)
return (EACCES);
if (sr->fid_ofile) {
if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
@@ -2054,20 +2247,10 @@
if (error != NT_STATUS_SUCCESS) {
return (EACCES);
}
}
- unnamed_node = SMB_IS_STREAM(snode);
- if (unnamed_node) {
- ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
- ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
- /*
- * Streams don't have ACL, any read ACL attempt on a stream
- * should be performed on the unnamed stream.
- */
- snode = unnamed_node;
- }
if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_ACEMASKONACCESS))
flags = ATTR_NOACLCHECK;
error = smb_vop_acl_read(snode->vp, &acl, flags,
@@ -2100,19 +2283,22 @@
int target_flavor;
int error = 0;
int flags = 0;
int access = 0;
acl_t *acl, *dacl, *sacl;
- smb_node_t *unnamed_node;
ASSERT(cr);
ASSERT(sr);
ASSERT(sr->tid_tree);
if (SMB_TREE_IS_READONLY(sr))
return (EROFS);
+ /* Can't set security on named streams */
+ if (SMB_IS_STREAM(snode) != NULL)
+ return (EINVAL);
+
if (SMB_TREE_HAS_ACCESS(sr, ACE_WRITE_ACL) == 0)
return (EACCES);
if (sr->fid_ofile) {
if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
@@ -2136,21 +2322,10 @@
break;
default:
return (EINVAL);
}
- unnamed_node = SMB_IS_STREAM(snode);
- if (unnamed_node) {
- ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
- ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
- /*
- * Streams don't have ACL, any write ACL attempt on a stream
- * should be performed on the unnamed stream.
- */
- snode = unnamed_node;
- }
-
dacl = fs_sd->sd_zdacl;
sacl = fs_sd->sd_zsacl;
ASSERT(dacl || sacl);
if ((dacl == NULL) && (sacl == NULL))
@@ -2201,10 +2376,14 @@
smb_attr_t attr;
ASSERT(cr);
ASSERT(fs_sd);
+ /* Can't query security on named streams */
+ if (SMB_IS_STREAM(snode) != NULL)
+ return (EINVAL);
+
/*
* File's uid/gid is fetched in two cases:
*
* 1. it's explicitly requested
*
@@ -2366,10 +2545,14 @@
ASSERT(sr);
ASSERT(sr->tid_tree);
if (SMB_TREE_IS_READONLY(sr))
return (EROFS);
+ /* Can't set security on named streams */
+ if (SMB_IS_STREAM(snode) != NULL)
+ return (EINVAL);
+
bzero(&set_attr, sizeof (smb_attr_t));
if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) {
set_attr.sa_vattr.va_uid = fs_sd->sd_uid;
set_attr.sa_mask |= SMB_AT_UID;
@@ -2556,10 +2739,19 @@
*eaccess |= FILE_EXECUTE;
if (access & VWRITE)
*eaccess |= FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD;
+
+ if (access & (VREAD | VWRITE))
+ *eaccess |= SYNCHRONIZE;
+
+#ifdef _FAKE_KERNEL
+ /* Should be: if (we are the owner)... */
+ if (access & VWRITE)
+ *eaccess |= DELETE | WRITE_DAC | WRITE_OWNER;
+#endif
}
/*
* smb_fsop_shrlock
*
@@ -2636,10 +2828,18 @@
* incompatibilities between POSIX and Windows. In the Windows world,
* if a client submits such a lock, the server will not lock any
* bytes. Interestingly if the same lock (same offset and length) is
* resubmitted Windows will consider that there is an overlap and
* the granting rules will then apply.
+ *
+ * 3) The SMB-level process IDs (smb_pid) are not passed down to the
+ * POSIX level in l_pid because (a) the rules about lock PIDs are
+ * different in SMB, and (b) we're putting our ofile f_uniqid in
+ * the POSIX l_pid field to segregate locks per SMB ofile.
+ * (We're also using a "remote" system ID in l_sysid.)
+ * All SMB locking PIDs are handled at the SMB level and
+ * not exposed in POSIX locking.
*/
if ((lock->l_length == 0) ||
((lock->l_start + lock->l_length - 1) < lock->l_start))
return (0);