15331Samw /* 25331Samw * CDDL HEADER START 35331Samw * 45331Samw * The contents of this file are subject to the terms of the 55331Samw * Common Development and Distribution License (the "License"). 65331Samw * You may not use this file except in compliance with the License. 75331Samw * 85331Samw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95331Samw * or http://www.opensolaris.org/os/licensing. 105331Samw * See the License for the specific language governing permissions 115331Samw * and limitations under the License. 125331Samw * 135331Samw * When distributing Covered Code, include this CDDL HEADER in each 145331Samw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155331Samw * If applicable, add the following below this CDDL HEADER, with the 165331Samw * fields enclosed by brackets "[]" replaced with your own identifying 175331Samw * information: Portions Copyright [yyyy] [name of copyright owner] 185331Samw * 195331Samw * CDDL HEADER END 205331Samw */ 215331Samw /* 225331Samw * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 235331Samw * Use is subject to license terms. 245331Samw */ 255331Samw 265331Samw #pragma ident "%Z%%M% %I% %E% SMI" 275331Samw 285331Samw #include <sys/sid.h> 295331Samw #include <smbsrv/smb_fsops.h> 30*5521Sas200622 #include <smbsrv/smb_kproto.h> 31*5521Sas200622 #include <smbsrv/smbvar.h> 32*5521Sas200622 #include <smbsrv/ntstatus.h> 33*5521Sas200622 #include <smbsrv/ntaccess.h> 345331Samw #include <acl/acl_common.h> 355331Samw 365331Samw u_longlong_t smb_caller_id; 375331Samw 385331Samw static int smb_fsop_amask_to_omode(uint32_t granted_access); 395331Samw static int smb_fsop_sdinherit(smb_request_t *sr, smb_node_t *dnode, 405331Samw smb_fssd_t *fs_sd); 415331Samw 425331Samw /* 435331Samw * The smb_fsop_* functions have knowledge of CIFS semantics. 445331Samw * 455331Samw * The smb_vop_* functions have minimal knowledge of CIFS semantics and 465331Samw * serve as an interface to the VFS layer. 475331Samw * 485331Samw * Hence, smb_request_t and smb_node_t structures should not be passed 495331Samw * from the smb_fsop_* layer to the smb_vop_* layer. 505331Samw * 515331Samw * In general, CIFS service code should only ever call smb_fsop_* 525331Samw * functions directly, and never smb_vop_* functions directly. 535331Samw * 545331Samw * smb_fsop_* functions should call smb_vop_* functions where possible, instead 555331Samw * of their smb_fsop_* counterparts. However, there are times when 565331Samw * this cannot be avoided. 575331Samw */ 585331Samw 595331Samw /* 605331Samw * Note: Stream names cannot be mangled. 615331Samw */ 625331Samw 635331Samw int 645331Samw smb_fsop_start() 655331Samw { 665331Samw int error; 675331Samw 685331Samw smb_caller_id = fs_new_caller_id(); 695331Samw error = smb_node_root_init(); 705331Samw 715331Samw if (error == 0) 725331Samw error = smb_fem_init(); 735331Samw 745331Samw return (error); 755331Samw } 765331Samw 775331Samw void 785331Samw smb_fsop_stop() 795331Samw { 805331Samw smb_fem_shutdown(); 815331Samw smb_vfs_rele_all(); 825331Samw smb_node_root_fini(); 835331Samw } 845331Samw 855331Samw int 865331Samw smb_fsop_open(smb_ofile_t *of) 875331Samw { 885331Samw caller_context_t ct; 895331Samw int mode; 905331Samw 915331Samw mode = smb_fsop_amask_to_omode(of->f_granted_access); 925331Samw 935331Samw smb_get_caller_context(NULL, &ct); 945331Samw 955331Samw /* 965331Samw * Assuming that same vnode is returned as we had before 975331Samw * (i.e. no special vnodes) 985331Samw */ 995331Samw 1005331Samw return (smb_vop_open(&of->f_node->vp, mode, of->f_cr, &ct)); 1015331Samw } 1025331Samw 1035331Samw int 1045331Samw smb_fsop_close(smb_ofile_t *of) 1055331Samw { 1065331Samw caller_context_t ct; 1075331Samw int mode; 1085331Samw 1095331Samw mode = smb_fsop_amask_to_omode(of->f_granted_access); 1105331Samw 1115331Samw smb_get_caller_context(NULL, &ct); 1125331Samw 1135331Samw return (smb_vop_close(of->f_node->vp, mode, of->f_cr, &ct)); 1145331Samw } 1155331Samw 1165331Samw static int 1175331Samw smb_fsop_amask_to_omode(uint32_t granted_access) 1185331Samw { 1195331Samw int mode = 0; 1205331Samw 1215331Samw if (granted_access & (ACE_READ_DATA | ACE_EXECUTE)) 1225331Samw mode |= FREAD; 1235331Samw 1245331Samw if (granted_access & (ACE_WRITE_DATA | ACE_APPEND_DATA)) 1255331Samw mode |= FWRITE; 1265331Samw 1275331Samw if (granted_access & ACE_APPEND_DATA) 1285331Samw mode |= FAPPEND; 1295331Samw 1305331Samw return (mode); 1315331Samw } 1325331Samw 1335331Samw static int 1345331Samw smb_fsop_create_with_sd( 1355331Samw struct smb_request *sr, 1365331Samw cred_t *cr, 1375331Samw smb_node_t *snode, 1385331Samw char *name, 1395331Samw smb_attr_t *attr, 1405331Samw smb_node_t **ret_snode, 1415331Samw smb_attr_t *ret_attr, 1425331Samw smb_fssd_t *fs_sd) 1435331Samw { 1445331Samw caller_context_t ct; 1455331Samw vsecattr_t *vsap; 1465331Samw vsecattr_t vsecattr; 1475331Samw acl_t *acl, *dacl, *sacl; 1485331Samw smb_attr_t set_attr; 1495331Samw vnode_t *vp; 1505331Samw int aclbsize = 0; /* size of acl list in bytes */ 1515331Samw int flags = 0; 1525331Samw int is_dir; 1535331Samw int rc; 154*5521Sas200622 boolean_t no_xvattr = B_FALSE; 1555331Samw 1565331Samw ASSERT(fs_sd); 1575331Samw 1585331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 1595331Samw flags = SMB_IGNORE_CASE; 1605331Samw 1615331Samw ASSERT(cr); 1625331Samw smb_get_caller_context(sr, &ct); 1635331Samw 1645331Samw is_dir = ((fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR) != 0); 1655331Samw 1665331Samw if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACLONCREATE) { 1675331Samw if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) { 1685331Samw dacl = fs_sd->sd_zdacl; 1695331Samw sacl = fs_sd->sd_zsacl; 1705331Samw ASSERT(dacl || sacl); 1715331Samw if (dacl && sacl) { 172*5521Sas200622 acl = smb_fsacl_merge(dacl, sacl); 1735331Samw } else if (dacl) { 1745331Samw acl = dacl; 1755331Samw } else { 1765331Samw acl = sacl; 1775331Samw } 1785331Samw 179*5521Sas200622 rc = smb_fsacl_to_vsa(acl, &vsecattr, &aclbsize); 1805331Samw 1815331Samw if (dacl && sacl) 1825331Samw acl_free(acl); 1835331Samw 1845331Samw if (rc) 1855331Samw return (rc); 1865331Samw 1875331Samw vsap = &vsecattr; 1885331Samw } 1895331Samw else 1905331Samw vsap = NULL; 1915331Samw 1925331Samw if (is_dir) { 1935331Samw rc = smb_vop_mkdir(snode->vp, name, attr, &vp, flags, 1945331Samw cr, &ct, vsap); 1955331Samw } else { 1965331Samw rc = smb_vop_create(snode->vp, name, attr, &vp, flags, 1975331Samw cr, &ct, vsap); 1985331Samw } 1995331Samw 2005331Samw if (vsap != NULL) 2015331Samw kmem_free(vsap->vsa_aclentp, aclbsize); 2025331Samw 2035331Samw if (rc != 0) 2045331Samw return (rc); 2055331Samw 2065331Samw set_attr.sa_mask = 0; 2075331Samw 2085331Samw /* 2095331Samw * Ideally we should be able to specify the owner and owning 2105331Samw * group at create time along with the ACL. Since we cannot 2115331Samw * do that right now, kcred is passed to smb_vop_setattr so it 2125331Samw * doesn't fail due to lack of permission. 2135331Samw */ 2145331Samw if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) { 2155331Samw set_attr.sa_vattr.va_uid = fs_sd->sd_uid; 2165331Samw set_attr.sa_mask |= SMB_AT_UID; 2175331Samw } 2185331Samw 2195331Samw if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) { 2205331Samw set_attr.sa_vattr.va_gid = fs_sd->sd_gid; 2215331Samw set_attr.sa_mask |= SMB_AT_GID; 2225331Samw } 2235331Samw 2245331Samw if (set_attr.sa_mask) { 225*5521Sas200622 if (sr && sr->tid_tree) 226*5521Sas200622 if (sr->tid_tree->t_flags & SMB_TREE_FLAG_UFS) 227*5521Sas200622 no_xvattr = B_TRUE; 2285331Samw rc = smb_vop_setattr(snode->vp, NULL, &set_attr, 229*5521Sas200622 0, kcred, no_xvattr, &ct); 2305331Samw } 2315331Samw 2325331Samw } else { 2335331Samw /* 2345331Samw * For filesystems that don't support ACL-on-create, try 2355331Samw * to set the specified SD after create, which could actually 2365331Samw * fail because of conflicts between inherited security 2375331Samw * attributes upon creation and the specified SD. 2385331Samw * 2395331Samw * Passing kcred to smb_fsop_sdwrite() to overcome this issue. 2405331Samw */ 2415331Samw 2425331Samw if (is_dir) { 2435331Samw rc = smb_vop_mkdir(snode->vp, name, attr, &vp, flags, 2445331Samw cr, &ct, NULL); 2455331Samw } else { 2465331Samw rc = smb_vop_create(snode->vp, name, attr, &vp, flags, 2475331Samw cr, &ct, NULL); 2485331Samw } 2495331Samw 250*5521Sas200622 if (rc != 0) 251*5521Sas200622 return (rc); 252*5521Sas200622 253*5521Sas200622 if ((sr->tid_tree->t_flags & SMB_TREE_FLAG_NFS_MOUNTED) == 0) 2545331Samw rc = smb_fsop_sdwrite(sr, kcred, snode, fs_sd, 1); 2555331Samw } 2565331Samw 2575331Samw if (rc == 0) { 2585331Samw *ret_snode = smb_node_lookup(sr, &sr->arg.open, cr, vp, name, 2595331Samw snode, NULL, ret_attr); 2605331Samw 2615331Samw if (*ret_snode == NULL) { 2625331Samw VN_RELE(vp); 2635331Samw rc = ENOMEM; 2645331Samw } 2655331Samw } 2665331Samw 267*5521Sas200622 if (rc != 0) { 268*5521Sas200622 if (is_dir) { 269*5521Sas200622 (void) smb_vop_rmdir(snode->vp, name, flags, cr, &ct); 270*5521Sas200622 } else { 271*5521Sas200622 (void) smb_vop_remove(snode->vp, name, flags, cr, &ct); 272*5521Sas200622 } 273*5521Sas200622 } 274*5521Sas200622 2755331Samw return (rc); 2765331Samw } 2775331Samw 2785331Samw 2795331Samw /* 2805331Samw * smb_fsop_create 2815331Samw * 2825331Samw * All SMB functions should use this wrapper to ensure that 2835331Samw * all the smb_vop_creates are performed with the appropriate credentials. 2845331Samw * Please document any direct calls to explain the reason 2855331Samw * for avoiding this wrapper. 2865331Samw * 2875331Samw * It is assumed that a reference exists on snode coming into this routine. 2885331Samw * 2895331Samw * *ret_snode is returned with a reference upon success. No reference is 2905331Samw * taken if an error is returned. 2915331Samw */ 2925331Samw 2935331Samw int 2945331Samw smb_fsop_create( 2955331Samw struct smb_request *sr, 2965331Samw cred_t *cr, 2975331Samw smb_node_t *dir_snode, 2985331Samw char *name, 2995331Samw smb_attr_t *attr, 3005331Samw smb_node_t **ret_snode, 3015331Samw smb_attr_t *ret_attr) 3025331Samw { 3035331Samw struct open_param *op = &sr->arg.open; 3045331Samw smb_node_t *fnode; 3055331Samw smb_attr_t file_attr; 3065331Samw caller_context_t ct; 3075331Samw vnode_t *xattrdirvp; 3085331Samw vnode_t *vp; 3095331Samw char *longname = NULL; 3105331Samw char *namep; 3115331Samw char *fname; 3125331Samw char *sname; 3135331Samw int is_stream; 3145331Samw int flags = 0; 3155331Samw int rc = 0; 3165331Samw smb_fssd_t fs_sd; 3175331Samw uint32_t secinfo; 3185331Samw uint32_t status; 3195331Samw 3205331Samw ASSERT(cr); 3215331Samw ASSERT(dir_snode); 3225331Samw ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); 3235331Samw ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); 3245331Samw 3255331Samw ASSERT(ret_snode); 3265331Samw *ret_snode = 0; 3275331Samw 3285331Samw ASSERT(name); 3295331Samw if (*name == 0) 3305331Samw return (EINVAL); 3315331Samw 3325331Samw if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) 3335331Samw return (EACCES); 3345331Samw 3355331Samw ASSERT(sr); 3365331Samw ASSERT(sr->tid_tree); 3375331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 3385331Samw return (EROFS); 3395331Samw 3405331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 3415331Samw flags = SMB_IGNORE_CASE; 3425331Samw 3435331Samw fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 3445331Samw sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 3455331Samw 3465331Samw is_stream = smb_stream_parse_name(name, fname, sname); 3475331Samw 3485331Samw if (is_stream) 3495331Samw namep = fname; 3505331Samw else 3515331Samw namep = name; 3525331Samw 3535331Samw if (smb_maybe_mangled_name(namep)) { 3545331Samw longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 3555331Samw 3565331Samw rc = smb_unmangle_name(sr, cr, dir_snode, namep, longname, 3575331Samw MAXNAMELEN, NULL, NULL, 1); 3585331Samw 3595331Samw if ((is_stream == 0) && (rc == 0)) 3605331Samw rc = EEXIST; 3615331Samw 3625331Samw if ((is_stream && rc) || 3635331Samw ((is_stream == 0) && (rc != ENOENT))) { 3645331Samw kmem_free(longname, MAXNAMELEN); 3655331Samw kmem_free(fname, MAXNAMELEN); 3665331Samw kmem_free(sname, MAXNAMELEN); 3675331Samw return (rc); 3685331Samw } 3695331Samw 3705331Samw if (is_stream) 3715331Samw namep = longname; 3725331Samw else 3735331Samw kmem_free(longname, MAXNAMELEN); 3745331Samw } 3755331Samw 3765331Samw if (is_stream) { 3775331Samw /* 3785331Samw * Look up the unnamed stream. 3795331Samw * 3805331Samw * Mangle processing in smb_fsop_lookup() for the unnamed 3815331Samw * stream won't be needed (as it was done above), but 3825331Samw * it may be needed on any link target (which 3835331Samw * smb_fsop_lookup() will provide). 3845331Samw */ 3855331Samw rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS, 3865331Samw sr->tid_tree->t_snode, dir_snode, namep, &fnode, &file_attr, 3875331Samw 0, 0); 3885331Samw 3895331Samw if (longname) { 3905331Samw kmem_free(longname, MAXNAMELEN); 3915331Samw namep = NULL; 3925331Samw } 3935331Samw 3945331Samw if (rc != 0) { 3955331Samw kmem_free(fname, MAXNAMELEN); 3965331Samw kmem_free(sname, MAXNAMELEN); 3975331Samw return (rc); 3985331Samw } 3995331Samw 4005331Samw smb_get_caller_context(sr, &ct); 4015331Samw 4025331Samw rc = smb_vop_stream_create(fnode->vp, sname, attr, &vp, 4035331Samw &xattrdirvp, flags, cr, &ct); 4045331Samw 4055331Samw if (rc != 0) { 4065331Samw smb_node_release(fnode); 4075331Samw kmem_free(fname, MAXNAMELEN); 4085331Samw kmem_free(sname, MAXNAMELEN); 4095331Samw return (rc); 4105331Samw } 4115331Samw 4125331Samw *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, 4135331Samw vp, sname, ret_attr); 4145331Samw 4155331Samw smb_node_release(fnode); 4165331Samw 4175331Samw if (*ret_snode == NULL) { 4185331Samw VN_RELE(xattrdirvp); 4195331Samw VN_RELE(vp); 4205331Samw kmem_free(fname, MAXNAMELEN); 4215331Samw kmem_free(sname, MAXNAMELEN); 4225331Samw return (ENOMEM); 4235331Samw } 4245331Samw } else { 425*5521Sas200622 if (op->sd) { 4265331Samw /* 4275331Samw * SD sent by client in Windows format. Needs to be 4285331Samw * converted to FS format. No inheritance. 4295331Samw */ 430*5521Sas200622 secinfo = smb_sd_get_secinfo(op->sd); 431*5521Sas200622 smb_fssd_init(&fs_sd, secinfo, 0); 432*5521Sas200622 433*5521Sas200622 status = smb_sd_tofs(op->sd, &fs_sd); 4345331Samw if (status == NT_STATUS_SUCCESS) { 4355331Samw rc = smb_fsop_create_with_sd(sr, cr, dir_snode, 4365331Samw name, attr, ret_snode, ret_attr, &fs_sd); 4375331Samw } 4385331Samw else 4395331Samw rc = EINVAL; 440*5521Sas200622 smb_fssd_term(&fs_sd); 4415331Samw } else if (sr->tid_tree->t_acltype == ACE_T) { 4425331Samw /* 4435331Samw * No incoming SD and filesystem is ZFS 4445331Samw * Server applies Windows inheritance rules, 4455331Samw * see smb_fsop_sdinherit() comments as to why. 4465331Samw */ 447*5521Sas200622 smb_fssd_init(&fs_sd, SMB_ACL_SECINFO, 0); 4485331Samw rc = smb_fsop_sdinherit(sr, dir_snode, &fs_sd); 4495331Samw if (rc == 0) { 4505331Samw rc = smb_fsop_create_with_sd(sr, cr, dir_snode, 4515331Samw name, attr, ret_snode, ret_attr, &fs_sd); 4525331Samw } 4535331Samw 454*5521Sas200622 smb_fssd_term(&fs_sd); 4555331Samw } else { 4565331Samw /* 4575331Samw * No incoming SD and filesystem is not ZFS 4585331Samw * let the filesystem handles the inheritance. 4595331Samw */ 4605331Samw smb_get_caller_context(sr, &ct); 4615331Samw rc = smb_vop_create(dir_snode->vp, name, attr, &vp, 4625331Samw flags, cr, &ct, NULL); 4635331Samw 4645331Samw if (rc == 0) { 4655331Samw *ret_snode = smb_node_lookup(sr, op, cr, vp, 4665331Samw name, dir_snode, NULL, ret_attr); 4675331Samw 4685331Samw if (*ret_snode == NULL) { 4695331Samw VN_RELE(vp); 4705331Samw rc = ENOMEM; 4715331Samw } 4725331Samw } 4735331Samw 4745331Samw } 4755331Samw } 4765331Samw 4775331Samw kmem_free(fname, MAXNAMELEN); 4785331Samw kmem_free(sname, MAXNAMELEN); 4795331Samw return (rc); 4805331Samw } 4815331Samw 4825331Samw /* 4835331Samw * smb_fsop_mkdir 4845331Samw * 4855331Samw * All SMB functions should use this wrapper to ensure that 4865331Samw * the the calls are performed with the appropriate credentials. 4875331Samw * Please document any direct call to explain the reason 4885331Samw * for avoiding this wrapper. 4895331Samw * 4905331Samw * It is assumed that a reference exists on snode coming into this routine. 4915331Samw * 4925331Samw * *ret_snode is returned with a reference upon success. No reference is 4935331Samw * taken if an error is returned. 4945331Samw */ 4955331Samw int 4965331Samw smb_fsop_mkdir( 4975331Samw struct smb_request *sr, 4985331Samw cred_t *cr, 4995331Samw smb_node_t *dir_snode, 5005331Samw char *name, 5015331Samw smb_attr_t *attr, 5025331Samw smb_node_t **ret_snode, 5035331Samw smb_attr_t *ret_attr) 5045331Samw { 5055331Samw struct open_param *op = &sr->arg.open; 5065331Samw caller_context_t ct; 5075331Samw char *longname; 5085331Samw vnode_t *vp; 5095331Samw int flags = 0; 5105331Samw smb_fssd_t fs_sd; 5115331Samw uint32_t secinfo; 5125331Samw uint32_t status; 5135331Samw int rc; 5145331Samw 5155331Samw ASSERT(cr); 5165331Samw ASSERT(dir_snode); 5175331Samw ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); 5185331Samw ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); 5195331Samw 5205331Samw ASSERT(ret_snode); 5215331Samw *ret_snode = 0; 5225331Samw 5235331Samw ASSERT(name); 5245331Samw if (*name == 0) 5255331Samw return (EINVAL); 5265331Samw 5275331Samw if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) 5285331Samw return (EACCES); 5295331Samw 5305331Samw ASSERT(sr); 5315331Samw ASSERT(sr->tid_tree); 5325331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 5335331Samw return (EROFS); 5345331Samw 5355331Samw if (smb_maybe_mangled_name(name)) { 5365331Samw longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 5375331Samw rc = smb_unmangle_name(sr, cr, dir_snode, name, longname, 5385331Samw MAXNAMELEN, NULL, NULL, 1); 5395331Samw 5405331Samw kmem_free(longname, MAXNAMELEN); 5415331Samw 5425331Samw /* 5435331Samw * If the name passed in by the client has an unmangled 5445331Samw * equivalent that is found in the specified directory, 5455331Samw * then the mkdir cannot succeed. Return EEXIST. 5465331Samw * 5475331Samw * Only if ENOENT is returned will a mkdir be attempted. 5485331Samw */ 5495331Samw 5505331Samw if (rc == 0) 5515331Samw rc = EEXIST; 5525331Samw 5535331Samw if (rc != ENOENT) 5545331Samw return (rc); 5555331Samw } 5565331Samw 5575331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 5585331Samw flags = SMB_IGNORE_CASE; 5595331Samw 5605331Samw smb_get_caller_context(sr, &ct); 5615331Samw 562*5521Sas200622 if (op->sd) { 5635331Samw /* 5645331Samw * SD sent by client in Windows format. Needs to be 5655331Samw * converted to FS format. No inheritance. 5665331Samw */ 567*5521Sas200622 secinfo = smb_sd_get_secinfo(op->sd); 568*5521Sas200622 smb_fssd_init(&fs_sd, secinfo, SMB_FSSD_FLAGS_DIR); 569*5521Sas200622 570*5521Sas200622 status = smb_sd_tofs(op->sd, &fs_sd); 5715331Samw if (status == NT_STATUS_SUCCESS) { 5725331Samw rc = smb_fsop_create_with_sd(sr, cr, dir_snode, 5735331Samw name, attr, ret_snode, ret_attr, &fs_sd); 5745331Samw } 5755331Samw else 5765331Samw rc = EINVAL; 577*5521Sas200622 smb_fssd_term(&fs_sd); 5785331Samw } else if (sr->tid_tree->t_acltype == ACE_T) { 5795331Samw /* 5805331Samw * No incoming SD and filesystem is ZFS 5815331Samw * Server applies Windows inheritance rules, 5825331Samw * see smb_fsop_sdinherit() comments as to why. 5835331Samw */ 584*5521Sas200622 smb_fssd_init(&fs_sd, SMB_ACL_SECINFO, SMB_FSSD_FLAGS_DIR); 5855331Samw rc = smb_fsop_sdinherit(sr, dir_snode, &fs_sd); 5865331Samw if (rc == 0) { 5875331Samw rc = smb_fsop_create_with_sd(sr, cr, dir_snode, 5885331Samw name, attr, ret_snode, ret_attr, &fs_sd); 5895331Samw } 5905331Samw 591*5521Sas200622 smb_fssd_term(&fs_sd); 5925331Samw 5935331Samw } else { 5945331Samw rc = smb_vop_mkdir(dir_snode->vp, name, attr, &vp, flags, cr, 5955331Samw &ct, NULL); 5965331Samw 5975331Samw if (rc == 0) { 5985331Samw *ret_snode = smb_node_lookup(sr, op, cr, vp, name, 5995331Samw dir_snode, NULL, ret_attr); 6005331Samw 6015331Samw if (*ret_snode == NULL) { 6025331Samw VN_RELE(vp); 6035331Samw rc = ENOMEM; 6045331Samw } 6055331Samw } 6065331Samw } 6075331Samw 6085331Samw return (rc); 6095331Samw } 6105331Samw 6115331Samw /* 6125331Samw * smb_fsop_remove 6135331Samw * 6145331Samw * All SMB functions should use this wrapper to ensure that 6155331Samw * the the calls are performed with the appropriate credentials. 6165331Samw * Please document any direct call to explain the reason 6175331Samw * for avoiding this wrapper. 6185331Samw * 6195331Samw * It is assumed that a reference exists on snode coming into this routine. 6205331Samw * 6215331Samw * od: This means that the name passed in is an on-disk name. 6225331Samw * A null smb_request might be passed to this function. 6235331Samw */ 6245331Samw 6255331Samw int 6265331Samw smb_fsop_remove( 6275331Samw struct smb_request *sr, 6285331Samw cred_t *cr, 6295331Samw smb_node_t *dir_snode, 6305331Samw char *name, 6315331Samw int od) 6325331Samw { 6335331Samw smb_node_t *fnode; 6345331Samw smb_attr_t file_attr; 6355331Samw caller_context_t ct; 6365331Samw char *longname; 6375331Samw char *fname; 6385331Samw char *sname; 6395331Samw int flags = 0; 6405331Samw int rc; 6415331Samw 6425331Samw ASSERT(cr); 6435331Samw /* 6445331Samw * The state of the node could be SMB_NODE_STATE_DESTROYING if this 6455331Samw * function is called during the deletion of the node (because of 6465331Samw * DELETE_ON_CLOSE). 6475331Samw */ 6485331Samw ASSERT(dir_snode); 6495331Samw ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); 6505331Samw 6515331Samw if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) 6525331Samw return (EACCES); 6535331Samw 6545331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 6555331Samw return (EROFS); 6565331Samw 6575331Samw smb_get_caller_context(sr, &ct); 6585331Samw 6595331Samw fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 6605331Samw sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 6615331Samw 6625331Samw if (smb_stream_parse_name(name, fname, sname)) { 6635331Samw 6645331Samw ASSERT(od == 0); 6655331Samw 6665331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 6675331Samw flags = SMB_IGNORE_CASE; 6685331Samw 6695331Samw /* 6705331Samw * Look up the unnamed stream (i.e. fname). 6715331Samw * Unmangle processing will be done on fname 6725331Samw * as well as any link target. 6735331Samw */ 6745331Samw 6755331Samw rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS, 6765331Samw sr->tid_tree->t_snode, dir_snode, fname, &fnode, &file_attr, 6775331Samw 0, 0); 6785331Samw 6795331Samw if (rc != 0) { 6805331Samw kmem_free(fname, MAXNAMELEN); 6815331Samw kmem_free(sname, MAXNAMELEN); 6825331Samw return (rc); 6835331Samw } 6845331Samw 6855331Samw /* 6865331Samw * XXX 6875331Samw * Need to find out what permission is required by NTFS 6885331Samw * to remove a stream. 6895331Samw */ 6905331Samw rc = smb_vop_stream_remove(fnode->vp, sname, flags, cr, &ct); 6915331Samw 6925331Samw smb_node_release(fnode); 6935331Samw } else { 6945331Samw /* 6955331Samw * If the passed-in name is an on-disk name, 6965331Samw * then we need to do a case-sensitive remove. 6975331Samw * This is important if the on-disk name 6985331Samw * corresponds to a mangled name passed in by 6995331Samw * the client. We want to make sure to remove 7005331Samw * the exact file specified by the client, 7015331Samw * instead of letting the underlying file system 7025331Samw * do a remove on the "first match." 7035331Samw */ 7045331Samw 7055331Samw if ((od == 0) && SMB_TREE_CASE_INSENSITIVE(sr)) 7065331Samw flags = SMB_IGNORE_CASE; 7075331Samw 7085331Samw rc = smb_vop_remove(dir_snode->vp, name, flags, cr, &ct); 7095331Samw 7105331Samw if (rc == ENOENT) { 7115331Samw if (smb_maybe_mangled_name(name) == 0) { 7125331Samw kmem_free(fname, MAXNAMELEN); 7135331Samw kmem_free(sname, MAXNAMELEN); 7145331Samw return (rc); 7155331Samw } 7165331Samw longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 7175331Samw 7185331Samw rc = smb_unmangle_name(sr, cr, dir_snode, name, 7195331Samw longname, MAXNAMELEN, NULL, NULL, 1); 7205331Samw 7215331Samw if (rc == 0) { 7225331Samw /* 7235331Samw * We passed "1" as the "od" parameter 7245331Samw * to smb_unmangle_name(), such that longname 7255331Samw * is the real (case-sensitive) on-disk name. 7265331Samw * We make sure we do a remove on this exact 7275331Samw * name, as the name was mangled and denotes 7285331Samw * a unique file. 7295331Samw */ 7305331Samw flags &= ~SMB_IGNORE_CASE; 7315331Samw rc = smb_vop_remove(dir_snode->vp, longname, 7325331Samw flags, cr, &ct); 7335331Samw } 7345331Samw 7355331Samw kmem_free(longname, MAXNAMELEN); 7365331Samw } 7375331Samw } 7385331Samw 7395331Samw kmem_free(fname, MAXNAMELEN); 7405331Samw kmem_free(sname, MAXNAMELEN); 7415331Samw return (rc); 7425331Samw } 7435331Samw 7445331Samw /* 7455331Samw * smb_fsop_remove_streams 7465331Samw * 7475331Samw * This function removes a file's streams without removing the 7485331Samw * file itself. 7495331Samw * 7505331Samw * It is assumed that snode is not a link. 7515331Samw */ 7525331Samw int 7535331Samw smb_fsop_remove_streams(struct smb_request *sr, cred_t *cr, 7545331Samw smb_node_t *fnode) 7555331Samw { 7565331Samw struct fs_stream_info stream_info; 7575331Samw caller_context_t ct; 7585331Samw uint32_t cookie = 0; 7595331Samw int flags = 0; 7605331Samw int rc; 7615331Samw 7625331Samw ASSERT(cr); 7635331Samw ASSERT(fnode); 7645331Samw ASSERT(fnode->n_magic == SMB_NODE_MAGIC); 7655331Samw ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING); 7665331Samw 7675331Samw if (SMB_TREE_ROOT_FS(sr, fnode) == 0) 7685331Samw return (EACCES); 7695331Samw 7705331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 7715331Samw return (EROFS); 7725331Samw 7735331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 7745331Samw flags = SMB_IGNORE_CASE; 7755331Samw 7765331Samw smb_get_caller_context(sr, &ct); 7775331Samw 7785331Samw for (;;) { 7795331Samw rc = smb_vop_stream_readdir(fnode->vp, &cookie, &stream_info, 7805331Samw NULL, NULL, flags, cr, &ct); 7815331Samw 7825331Samw if ((rc != 0) || (cookie == SMB_EOF)) 7835331Samw break; 7845331Samw 7855331Samw (void) smb_vop_stream_remove(fnode->vp, stream_info.name, flags, 7865331Samw cr, &ct); 7875331Samw } 7885331Samw return (rc); 7895331Samw } 7905331Samw 7915331Samw /* 7925331Samw * smb_fsop_rmdir 7935331Samw * 7945331Samw * All SMB functions should use this wrapper to ensure that 7955331Samw * the the calls are performed with the appropriate credentials. 7965331Samw * Please document any direct call to explain the reason 7975331Samw * for avoiding this wrapper. 7985331Samw * 7995331Samw * It is assumed that a reference exists on snode coming into this routine. 8005331Samw * 8015331Samw * od: This means that the name passed in is an on-disk name. 8025331Samw */ 8035331Samw 8045331Samw int 8055331Samw smb_fsop_rmdir( 8065331Samw struct smb_request *sr, 8075331Samw cred_t *cr, 8085331Samw smb_node_t *dir_snode, 8095331Samw char *name, 8105331Samw int od) 8115331Samw { 8125331Samw caller_context_t ct; 8135331Samw int rc; 8145331Samw int flags = 0; 8155331Samw char *longname; 8165331Samw 8175331Samw ASSERT(cr); 8185331Samw /* 8195331Samw * The state of the node could be SMB_NODE_STATE_DESTROYING if this 8205331Samw * function is called during the deletion of the node (because of 8215331Samw * DELETE_ON_CLOSE). 8225331Samw */ 8235331Samw ASSERT(dir_snode); 8245331Samw ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); 8255331Samw 8265331Samw if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) 8275331Samw return (EACCES); 8285331Samw 8295331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 8305331Samw return (EROFS); 8315331Samw 8325331Samw /* 8335331Samw * If the passed-in name is an on-disk name, 8345331Samw * then we need to do a case-sensitive rmdir. 8355331Samw * This is important if the on-disk name 8365331Samw * corresponds to a mangled name passed in by 8375331Samw * the client. We want to make sure to remove 8385331Samw * the exact directory specified by the client, 8395331Samw * instead of letting the underlying file system 8405331Samw * do a rmdir on the "first match." 8415331Samw */ 8425331Samw 8435331Samw if ((od == 0) && SMB_TREE_CASE_INSENSITIVE(sr)) 8445331Samw flags = SMB_IGNORE_CASE; 8455331Samw 8465331Samw smb_get_caller_context(sr, &ct); 8475331Samw 8485331Samw rc = smb_vop_rmdir(dir_snode->vp, name, flags, cr, &ct); 8495331Samw 8505331Samw if (rc == ENOENT) { 8515331Samw if (smb_maybe_mangled_name(name) == 0) 8525331Samw return (rc); 8535331Samw 8545331Samw longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 8555331Samw 8565331Samw rc = smb_unmangle_name(sr, cr, dir_snode, 8575331Samw name, longname, MAXNAMELEN, NULL, 8585331Samw NULL, 1); 8595331Samw 8605331Samw if (rc == 0) { 8615331Samw /* 8625331Samw * We passed "1" as the "od" parameter 8635331Samw * to smb_unmangle_name(), such that longname 8645331Samw * is the real (case-sensitive) on-disk name. 8655331Samw * We make sure we do a rmdir on this exact 8665331Samw * name, as the name was mangled and denotes 8675331Samw * a unique directory. 8685331Samw */ 8695331Samw flags &= ~SMB_IGNORE_CASE; 8705331Samw rc = smb_vop_rmdir(dir_snode->vp, longname, flags, cr, 8715331Samw &ct); 8725331Samw } 8735331Samw 8745331Samw kmem_free(longname, MAXNAMELEN); 8755331Samw } 8765331Samw 8775331Samw return (rc); 8785331Samw } 8795331Samw 8805331Samw /* 8815331Samw * smb_fsop_getattr 8825331Samw * 8835331Samw * All SMB functions should use this wrapper to ensure that 8845331Samw * the the calls are performed with the appropriate credentials. 8855331Samw * Please document any direct call to explain the reason 8865331Samw * for avoiding this wrapper. 8875331Samw * 8885331Samw * It is assumed that a reference exists on snode coming into this routine. 8895331Samw */ 8905331Samw int 8915331Samw smb_fsop_getattr(struct smb_request *sr, cred_t *cr, smb_node_t *snode, 8925331Samw smb_attr_t *attr) 8935331Samw { 8945331Samw smb_node_t *unnamed_node; 8955331Samw vnode_t *unnamed_vp = NULL; 8965331Samw caller_context_t ct; 8975331Samw uint32_t status; 8985331Samw uint32_t access = 0; 8995331Samw int flags = 0; 9005331Samw 9015331Samw ASSERT(cr); 9025331Samw ASSERT(snode); 9035331Samw ASSERT(snode->n_magic == SMB_NODE_MAGIC); 9045331Samw ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); 9055331Samw 9065331Samw if (SMB_TREE_ROOT_FS(sr, snode) == 0) 9075331Samw return (EACCES); 9085331Samw 9095331Samw if (sr->fid_ofile) { 9105331Samw /* if uid and/or gid is requested */ 9115331Samw if (attr->sa_mask & (SMB_AT_UID|SMB_AT_GID)) 9125331Samw access |= READ_CONTROL; 9135331Samw 9145331Samw /* if anything else is also requested */ 9155331Samw if (attr->sa_mask & ~(SMB_AT_UID|SMB_AT_GID)) 9165331Samw access |= FILE_READ_ATTRIBUTES; 9175331Samw 9185331Samw status = smb_ofile_access(sr->fid_ofile, cr, access); 9195331Samw if (status != NT_STATUS_SUCCESS) 9205331Samw return (EACCES); 9215331Samw 9225331Samw if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) 9235331Samw flags = ATTR_NOACLCHECK; 9245331Samw } 9255331Samw 9265331Samw smb_get_caller_context(sr, &ct); 9275331Samw 9285331Samw unnamed_node = SMB_IS_STREAM(snode); 9295331Samw 9305331Samw if (unnamed_node) { 9315331Samw ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); 9325331Samw ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); 9335331Samw unnamed_vp = unnamed_node->vp; 9345331Samw } 9355331Samw 9365331Samw return (smb_vop_getattr(snode->vp, unnamed_vp, attr, flags, cr, &ct)); 9375331Samw } 9385331Samw 9395331Samw /* 9405331Samw * smb_fsop_readdir 9415331Samw * 9425331Samw * All SMB functions should use this smb_fsop_readdir wrapper to ensure that 9435331Samw * the smb_vop_readdir is performed with the appropriate credentials. 9445331Samw * Please document any direct call to smb_vop_readdir to explain the reason 9455331Samw * for avoiding this wrapper. 9465331Samw * 9475331Samw * It is assumed that a reference exists on snode coming into this routine. 9485331Samw */ 9495331Samw int 9505331Samw smb_fsop_readdir( 9515331Samw struct smb_request *sr, 9525331Samw cred_t *cr, 9535331Samw smb_node_t *dir_snode, 9545331Samw uint32_t *cookie, 9555331Samw char *name, 9565331Samw int *namelen, 9575331Samw ino64_t *fileid, 9585331Samw struct fs_stream_info *stream_info, 9595331Samw smb_node_t **ret_snode, 9605331Samw smb_attr_t *ret_attr) 9615331Samw { 9625331Samw caller_context_t ct; 9635331Samw smb_node_t *ret_snodep; 9645331Samw smb_node_t *fnode; 9655331Samw smb_attr_t tmp_attr; 9665331Samw vnode_t *xattrdirvp; 9675331Samw vnode_t *fvp; 9685331Samw vnode_t *vp = NULL; 9695331Samw char *od_name; 9705331Samw int rc; 9715331Samw int flags = 0; 9725331Samw 9735331Samw ASSERT(cr); 9745331Samw ASSERT(dir_snode); 9755331Samw ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); 9765331Samw ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); 9775331Samw 9785331Samw if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) 9795331Samw return (EACCES); 9805331Samw 9815331Samw if (*cookie == SMB_EOF) { 9825331Samw *namelen = 0; 9835331Samw return (0); 9845331Samw } 9855331Samw 9865331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 9875331Samw flags = SMB_IGNORE_CASE; 9885331Samw 9895331Samw smb_get_caller_context(sr, &ct); 9905331Samw 9915331Samw od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 9925331Samw 9935331Samw if (stream_info) { 9945331Samw rc = smb_vop_lookup(dir_snode->vp, name, &fvp, od_name, 9955331Samw SMB_FOLLOW_LINKS, sr->tid_tree->t_snode->vp, cr, &ct); 9965331Samw 9975331Samw if (rc != 0) { 9985331Samw kmem_free(od_name, MAXNAMELEN); 9995331Samw return (rc); 10005331Samw } 10015331Samw 10025331Samw fnode = smb_node_lookup(sr, NULL, cr, fvp, od_name, dir_snode, 10035331Samw NULL, ret_attr); 10045331Samw 10055331Samw kmem_free(od_name, MAXNAMELEN); 10065331Samw 10075331Samw if (fnode == NULL) { 10085331Samw VN_RELE(fvp); 10095331Samw return (ENOMEM); 10105331Samw } 10115331Samw 10125331Samw /* 10135331Samw * XXX 10145331Samw * Need to find out what permission(s) NTFS requires for getting 10155331Samw * a file's streams list. 10165331Samw * 10175331Samw * Might have to use kcred. 10185331Samw */ 10195331Samw rc = smb_vop_stream_readdir(fvp, cookie, stream_info, &vp, 10205331Samw &xattrdirvp, flags, cr, &ct); 10215331Samw 10225331Samw if ((rc != 0) || (*cookie == SMB_EOF)) { 10235331Samw smb_node_release(fnode); 10245331Samw return (rc); 10255331Samw } 10265331Samw 10275331Samw ret_snodep = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, 10285331Samw vp, stream_info->name, &tmp_attr); 10295331Samw 10305331Samw smb_node_release(fnode); 10315331Samw 10325331Samw if (ret_snodep == NULL) { 10335331Samw VN_RELE(xattrdirvp); 10345331Samw VN_RELE(vp); 10355331Samw return (ENOMEM); 10365331Samw } 10375331Samw 10385331Samw stream_info->size = tmp_attr.sa_vattr.va_size; 10395331Samw 10405331Samw if (ret_attr) 10415331Samw *ret_attr = tmp_attr; 10425331Samw 10435331Samw if (ret_snode) 10445331Samw *ret_snode = ret_snodep; 10455331Samw else 10465331Samw smb_node_release(ret_snodep); 10475331Samw 10485331Samw } else { 10495331Samw rc = smb_vop_readdir(dir_snode->vp, cookie, name, namelen, 10505331Samw fileid, &vp, od_name, flags, cr, &ct); 10515331Samw 10525331Samw if (rc != 0) { 10535331Samw kmem_free(od_name, MAXNAMELEN); 10545331Samw return (rc); 10555331Samw } 10565331Samw 10575331Samw if (*namelen) { 10585331Samw ASSERT(vp); 10595331Samw if (ret_attr || ret_snode) { 10605331Samw ret_snodep = smb_node_lookup(sr, NULL, cr, vp, 10615331Samw od_name, dir_snode, NULL, &tmp_attr); 10625331Samw 10635331Samw if (ret_snodep == NULL) { 10645331Samw kmem_free(od_name, MAXNAMELEN); 10655331Samw VN_RELE(vp); 10665331Samw return (ENOMEM); 10675331Samw } 10685331Samw 10695331Samw if (ret_attr) 10705331Samw *ret_attr = tmp_attr; 10715331Samw 10725331Samw if (ret_snode) 10735331Samw *ret_snode = ret_snodep; 10745331Samw else 10755331Samw smb_node_release(ret_snodep); 10765331Samw } 10775331Samw } 10785331Samw 10795331Samw kmem_free(od_name, MAXNAMELEN); 10805331Samw } 10815331Samw 10825331Samw return (rc); 10835331Samw } 10845331Samw 10855331Samw /* 10865331Samw * smb_fsop_getdents 10875331Samw * 10885331Samw * All SMB functions should use this smb_vop_getdents wrapper to ensure that 10895331Samw * the smb_vop_getdents is performed with the appropriate credentials. 10905331Samw * Please document any direct call to smb_vop_getdents to explain the reason 10915331Samw * for avoiding this wrapper. 10925331Samw * 10935331Samw * It is assumed that a reference exists on snode coming into this routine. 10945331Samw */ 10955331Samw /*ARGSUSED*/ 10965331Samw int 10975331Samw smb_fsop_getdents( 10985331Samw struct smb_request *sr, 10995331Samw cred_t *cr, 11005331Samw smb_node_t *dir_snode, 11015331Samw uint32_t *cookie, 11025331Samw uint64_t *verifierp, 11035331Samw int32_t *maxcnt, 11045331Samw char *args, 11055331Samw char *pattern) 11065331Samw { 11075331Samw caller_context_t ct; 11085331Samw int flags = 0; 11095331Samw 11105331Samw ASSERT(cr); 11115331Samw ASSERT(dir_snode); 11125331Samw ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); 11135331Samw ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); 11145331Samw 11155331Samw if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) 11165331Samw return (EACCES); 11175331Samw 11185331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 11195331Samw flags = SMB_IGNORE_CASE; 11205331Samw 11215331Samw smb_get_caller_context(sr, &ct); 11225331Samw 11235331Samw return (smb_vop_getdents(dir_snode, cookie, 0, maxcnt, args, pattern, 11245331Samw flags, sr, cr, &ct)); 11255331Samw } 11265331Samw 11275331Samw /* 11285331Samw * smb_fsop_rename 11295331Samw * 11305331Samw * All SMB functions should use this smb_vop_rename wrapper to ensure that 11315331Samw * the smb_vop_rename is performed with the appropriate credentials. 11325331Samw * Please document any direct call to smb_vop_rename to explain the reason 11335331Samw * for avoiding this wrapper. 11345331Samw * 11355331Samw * It is assumed that references exist on from_dir_snode and to_dir_snode coming 11365331Samw * into this routine. 11375331Samw */ 11385331Samw int 11395331Samw smb_fsop_rename( 11405331Samw struct smb_request *sr, 11415331Samw cred_t *cr, 11425331Samw smb_node_t *from_dir_snode, 11435331Samw char *from_name, 11445331Samw smb_node_t *to_dir_snode, 11455331Samw char *to_name) 11465331Samw { 11475331Samw smb_node_t *from_snode; 11485331Samw caller_context_t ct; 11495331Samw smb_attr_t tmp_attr; 11505331Samw vnode_t *from_vp; 11515331Samw int flags = 0; 11525331Samw int rc; 11535331Samw 11545331Samw ASSERT(cr); 11555331Samw ASSERT(from_dir_snode); 11565331Samw ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC); 11575331Samw ASSERT(from_dir_snode->n_state != SMB_NODE_STATE_DESTROYING); 11585331Samw 11595331Samw ASSERT(to_dir_snode); 11605331Samw ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC); 11615331Samw ASSERT(to_dir_snode->n_state != SMB_NODE_STATE_DESTROYING); 11625331Samw 11635331Samw if (SMB_TREE_ROOT_FS(sr, from_dir_snode) == 0) 11645331Samw return (EACCES); 11655331Samw 11665331Samw if (SMB_TREE_ROOT_FS(sr, to_dir_snode) == 0) 11675331Samw return (EACCES); 11685331Samw 11695331Samw ASSERT(sr); 11705331Samw ASSERT(sr->tid_tree); 11715331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 11725331Samw return (EROFS); 11735331Samw 11745331Samw /* 11755331Samw * Note: There is no need to check SMB_TREE_CASE_INSENSITIVE(sr) 11765331Samw * here. 11775331Samw * 11785331Samw * A case-sensitive rename is always done in this routine 11795331Samw * because we are using the on-disk name from an earlier lookup. 11805331Samw * If a mangled name was passed in by the caller (denoting a 11815331Samw * deterministic lookup), then the exact file must be renamed 11825331Samw * (i.e. SMB_IGNORE_CASE must not be passed to VOP_RENAME, or 11835331Samw * else the underlying file system might return a "first-match" 11845331Samw * on this on-disk name, possibly resulting in the wrong file). 11855331Samw */ 11865331Samw 11875331Samw /* 11885331Samw * XXX: Lock required through smb_node_release() below? 11895331Samw */ 11905331Samw 11915331Samw smb_get_caller_context(sr, &ct); 11925331Samw 11935331Samw rc = smb_vop_lookup(from_dir_snode->vp, from_name, &from_vp, NULL, 0, 11945331Samw NULL, cr, &ct); 11955331Samw 11965331Samw if (rc != 0) 11975331Samw return (rc); 11985331Samw 11995331Samw rc = smb_vop_rename(from_dir_snode->vp, from_name, to_dir_snode->vp, 12005331Samw to_name, flags, cr, &ct); 12015331Samw 12025331Samw if (rc == 0) { 12035331Samw from_snode = smb_node_lookup(sr, NULL, cr, from_vp, from_name, 12045331Samw from_dir_snode, NULL, &tmp_attr); 12055331Samw 12065331Samw if (from_snode == NULL) { 12075331Samw VN_RELE(from_vp); 12085331Samw return (ENOMEM); 12095331Samw } 12105331Samw 12115331Samw (void) smb_node_rename(from_dir_snode, from_snode, to_dir_snode, 12125331Samw to_name); 12135331Samw 12145331Samw smb_node_release(from_snode); 12155331Samw } else { 12165331Samw VN_RELE(from_vp); 12175331Samw } 12185331Samw 12195331Samw /* XXX: unlock */ 12205331Samw 12215331Samw return (rc); 12225331Samw } 12235331Samw 12245331Samw /* 12255331Samw * smb_fsop_setattr 12265331Samw * 12275331Samw * All SMB functions should use this wrapper to ensure that 12285331Samw * the the calls are performed with the appropriate credentials. 12295331Samw * Please document any direct call to explain the reason 12305331Samw * for avoiding this wrapper. 12315331Samw * 12325331Samw * It is assumed that a reference exists on snode coming into this routine. 12335331Samw * A null smb_request might be passed to this function. 12345331Samw */ 12355331Samw int 12365331Samw smb_fsop_setattr( 12375331Samw smb_request_t *sr, 12385331Samw cred_t *cr, 12395331Samw smb_node_t *snode, 12405331Samw smb_attr_t *set_attr, 12415331Samw smb_attr_t *ret_attr) 12425331Samw { 12435331Samw smb_node_t *unnamed_node; 12445331Samw vnode_t *unnamed_vp = NULL; 12455331Samw caller_context_t ct; 12465331Samw uint32_t status; 12475331Samw uint32_t access = 0; 12485331Samw int rc = 0; 12495331Samw int flags = 0; 1250*5521Sas200622 boolean_t no_xvattr = B_FALSE; 12515331Samw 12525331Samw ASSERT(cr); 12535331Samw ASSERT(snode); 12545331Samw ASSERT(snode->n_magic == SMB_NODE_MAGIC); 12555331Samw ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); 12565331Samw 12575331Samw if (SMB_TREE_ROOT_FS(sr, snode) == 0) 12585331Samw return (EACCES); 12595331Samw 12605331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 12615331Samw return (EROFS); 12625331Samw 12635331Samw /* sr could be NULL in some cases */ 12645331Samw if (sr && sr->fid_ofile) { 12655331Samw /* if uid and/or gid is requested */ 12665331Samw if (set_attr->sa_mask & (SMB_AT_UID|SMB_AT_GID)) 12675331Samw access |= WRITE_OWNER; 12685331Samw 12695331Samw /* if anything else is also requested */ 12705331Samw if (set_attr->sa_mask & ~(SMB_AT_UID|SMB_AT_GID)) 12715331Samw access |= FILE_WRITE_ATTRIBUTES; 12725331Samw 12735331Samw status = smb_ofile_access(sr->fid_ofile, cr, access); 12745331Samw if (status != NT_STATUS_SUCCESS) 12755331Samw return (EACCES); 12765331Samw 12775331Samw if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) 12785331Samw flags = ATTR_NOACLCHECK; 12795331Samw } 12805331Samw 12815331Samw smb_get_caller_context(sr, &ct); 12825331Samw 12835331Samw unnamed_node = SMB_IS_STREAM(snode); 12845331Samw 12855331Samw if (unnamed_node) { 12865331Samw ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); 12875331Samw ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); 12885331Samw unnamed_vp = unnamed_node->vp; 12895331Samw } 1290*5521Sas200622 if (sr && sr->tid_tree) 1291*5521Sas200622 if (sr->tid_tree->t_flags & SMB_TREE_FLAG_UFS) 1292*5521Sas200622 no_xvattr = B_TRUE; 1293*5521Sas200622 1294*5521Sas200622 rc = smb_vop_setattr(snode->vp, unnamed_vp, 1295*5521Sas200622 set_attr, flags, cr, no_xvattr, &ct); 12965331Samw 12975331Samw if ((rc == 0) && ret_attr) { 12985331Samw /* 12995331Samw * This is an operation on behalf of CIFS service (to update 13005331Samw * smb node's attr) not on behalf of the user so it's done 13015331Samw * using kcred and the return value is intentionally ignored. 13025331Samw */ 13035331Samw ret_attr->sa_mask = SMB_AT_ALL; 13045331Samw (void) smb_vop_getattr(snode->vp, unnamed_vp, ret_attr, 0, 13055331Samw kcred, &ct); 13065331Samw } 13075331Samw 13085331Samw return (rc); 13095331Samw } 13105331Samw 13115331Samw /* 13125331Samw * smb_fsop_read 13135331Samw * 13145331Samw * All SMB functions should use this wrapper to ensure that 13155331Samw * the the calls are performed with the appropriate credentials. 13165331Samw * Please document any direct call to explain the reason 13175331Samw * for avoiding this wrapper. 13185331Samw * 13195331Samw * It is assumed that a reference exists on snode coming into this routine. 13205331Samw */ 13215331Samw int 13225331Samw smb_fsop_read( 13235331Samw struct smb_request *sr, 13245331Samw cred_t *cr, 13255331Samw smb_node_t *snode, 13265331Samw uio_t *uio, 13275331Samw smb_attr_t *ret_attr) 13285331Samw { 13295331Samw smb_node_t *unnamed_node; 13305331Samw vnode_t *unnamed_vp = NULL; 13315331Samw caller_context_t ct; 13325331Samw int rc; 13335331Samw 13345331Samw ASSERT(cr); 13355331Samw ASSERT(snode); 13365331Samw ASSERT(snode->n_magic == SMB_NODE_MAGIC); 13375331Samw ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); 13385331Samw 13395331Samw ASSERT(sr); 13405331Samw ASSERT(sr->fid_ofile); 13415331Samw 13425331Samw rc = smb_ofile_access(sr->fid_ofile, cr, FILE_READ_DATA); 13435331Samw if (rc != NT_STATUS_SUCCESS) { 13445331Samw rc = smb_ofile_access(sr->fid_ofile, cr, FILE_EXECUTE); 13455331Samw if (rc != NT_STATUS_SUCCESS) 13465331Samw return (EACCES); 13475331Samw } 13485331Samw 13495331Samw unnamed_node = SMB_IS_STREAM(snode); 13505331Samw if (unnamed_node) { 13515331Samw ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); 13525331Samw ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); 13535331Samw unnamed_vp = unnamed_node->vp; 13545331Samw /* 13555331Samw * Streams permission are checked against the unnamed stream, 13565331Samw * but in FS level they have their own permissions. To avoid 13575331Samw * rejection by FS due to lack of permission on the actual 13585331Samw * extended attr kcred is passed for streams. 13595331Samw */ 13605331Samw cr = kcred; 13615331Samw } 13625331Samw 13635331Samw smb_get_caller_context(sr, &ct); 13645331Samw rc = smb_vop_read(snode->vp, uio, cr, &ct); 13655331Samw 13665331Samw if (rc == 0) { 13675331Samw /* 13685331Samw * This is an operation on behalf of CIFS service (to update 13695331Samw * smb node's attr) not on behalf of the user so it's done 13705331Samw * using kcred and the return value is intentionally ignored. 13715331Samw */ 13725331Samw ret_attr->sa_mask = SMB_AT_ALL; 13735331Samw (void) smb_vop_getattr(snode->vp, unnamed_vp, ret_attr, 0, 13745331Samw kcred, &ct); 13755331Samw } 13765331Samw 13775331Samw return (rc); 13785331Samw } 13795331Samw 13805331Samw /* 13815331Samw * smb_fsop_write 13825331Samw * 13835331Samw * This is a wrapper function used for smb_write and smb_write_raw operations. 13845331Samw * 13855331Samw * It is assumed that a reference exists on snode coming into this routine. 13865331Samw */ 13875331Samw int 13885331Samw smb_fsop_write( 13895331Samw struct smb_request *sr, 13905331Samw cred_t *cr, 13915331Samw smb_node_t *snode, 13925331Samw uio_t *uio, 13935331Samw uint32_t *lcount, 13945331Samw smb_attr_t *ret_attr, 13955331Samw uint32_t *flag) 13965331Samw { 13975331Samw smb_node_t *unnamed_node; 13985331Samw vnode_t *unnamed_vp = NULL; 13995331Samw caller_context_t ct; 14005331Samw int rc; 14015331Samw 14025331Samw ASSERT(cr); 14035331Samw ASSERT(snode); 14045331Samw ASSERT(snode->n_magic == SMB_NODE_MAGIC); 14055331Samw ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); 14065331Samw 14075331Samw ASSERT(sr); 14085331Samw ASSERT(sr->tid_tree); 14095331Samw ASSERT(sr->fid_ofile); 14105331Samw 14115331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 14125331Samw return (EROFS); 14135331Samw /* 14145331Samw * XXX what if the file has been opened only with 14155331Samw * FILE_APPEND_DATA? 14165331Samw */ 14175331Samw rc = smb_ofile_access(sr->fid_ofile, cr, FILE_WRITE_DATA); 14185331Samw if (rc != NT_STATUS_SUCCESS) 14195331Samw return (EACCES); 14205331Samw 14215331Samw smb_get_caller_context(sr, &ct); 14225331Samw 14235331Samw unnamed_node = SMB_IS_STREAM(snode); 14245331Samw 14255331Samw if (unnamed_node) { 14265331Samw ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); 14275331Samw ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); 14285331Samw unnamed_vp = unnamed_node->vp; 14295331Samw /* 14305331Samw * Streams permission are checked against the unnamed stream, 14315331Samw * but in FS level they have their own permissions. To avoid 14325331Samw * rejection by FS due to lack of permission on the actual 14335331Samw * extended attr kcred is passed for streams. 14345331Samw */ 14355331Samw cr = kcred; 14365331Samw } 14375331Samw 14385331Samw rc = smb_vop_write(snode->vp, uio, flag, lcount, cr, &ct); 14395331Samw 14405331Samw if (rc == 0) { 14415331Samw /* 14425331Samw * This is an operation on behalf of CIFS service (to update 14435331Samw * smb node's attr) not on behalf of the user so it's done 14445331Samw * using kcred and the return value is intentionally ignored. 14455331Samw */ 14465331Samw ret_attr->sa_mask = SMB_AT_ALL; 14475331Samw (void) smb_vop_getattr(snode->vp, unnamed_vp, ret_attr, 0, 14485331Samw kcred, &ct); 14495331Samw } 14505331Samw 14515331Samw return (rc); 14525331Samw } 14535331Samw 14545331Samw /* 14555331Samw * smb_fsop_statfs 14565331Samw * 14575331Samw * This is a wrapper function used for stat operations. 14585331Samw */ 14595331Samw int 14605331Samw smb_fsop_statfs( 14615331Samw cred_t *cr, 14625331Samw smb_node_t *snode, 14635331Samw struct statvfs64 *statp) 14645331Samw { 14655331Samw ASSERT(cr); 14665331Samw ASSERT(snode); 14675331Samw ASSERT(snode->n_magic == SMB_NODE_MAGIC); 14685331Samw ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); 14695331Samw 14705331Samw return (smb_vop_statfs(snode->vp, statp, cr)); 14715331Samw } 14725331Samw 14735331Samw /* 14745331Samw * smb_fsop_access 14755331Samw */ 14765331Samw int 14775331Samw smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode, 14785331Samw uint32_t faccess) 14795331Samw { 14805331Samw int access = 0; 14815331Samw int error; 14825331Samw vnode_t *dir_vp; 14835331Samw boolean_t acl_check = B_TRUE; 14845331Samw smb_node_t *unnamed_node; 14855331Samw 14865331Samw ASSERT(cr); 14875331Samw ASSERT(snode); 14885331Samw ASSERT(snode->n_magic == SMB_NODE_MAGIC); 14895331Samw ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); 14905331Samw 14915331Samw if (faccess == 0) 14925331Samw return (NT_STATUS_SUCCESS); 14935331Samw 14945331Samw if (SMB_TREE_IS_READ_ONLY(sr)) { 14955331Samw if (faccess & (FILE_WRITE_DATA|FILE_APPEND_DATA| 14965331Samw FILE_WRITE_EA|FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES| 14975331Samw DELETE|WRITE_DAC|WRITE_OWNER)) { 14985331Samw return (NT_STATUS_ACCESS_DENIED); 14995331Samw } 15005331Samw } 15015331Samw 15025331Samw unnamed_node = SMB_IS_STREAM(snode); 15035331Samw if (unnamed_node) { 15045331Samw ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); 15055331Samw ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); 15065331Samw /* 15075331Samw * Streams authorization should be performed against the 15085331Samw * unnamed stream. 15095331Samw */ 15105331Samw snode = unnamed_node; 15115331Samw } 15125331Samw 15135331Samw if (faccess & ACCESS_SYSTEM_SECURITY) { 15145331Samw /* 15155331Samw * This permission is required for reading/writing SACL and 15165331Samw * it's not part of DACL. It's only granted via proper 15175331Samw * privileges. 15185331Samw */ 15195331Samw if ((sr->uid_user->u_privileges & 15205331Samw (SMB_USER_PRIV_BACKUP | 15215331Samw SMB_USER_PRIV_RESTORE | 15225331Samw SMB_USER_PRIV_SECURITY)) == 0) 15235331Samw return (NT_STATUS_PRIVILEGE_NOT_HELD); 15245331Samw 15255331Samw faccess &= ~ACCESS_SYSTEM_SECURITY; 15265331Samw } 15275331Samw 15285331Samw /* Links don't have ACL */ 15295331Samw if (((sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) == 0) || 15305331Samw (snode->attr.sa_vattr.va_type == VLNK)) 15315331Samw acl_check = B_FALSE; 15325331Samw 15335331Samw if (acl_check) { 15345331Samw dir_vp = (snode->dir_snode) ? snode->dir_snode->vp : NULL; 15355331Samw error = smb_vop_access(snode->vp, faccess, V_ACE_MASK, dir_vp, 15365331Samw cr); 15375331Samw } else { 15385331Samw /* 15395331Samw * FS doesn't understand 32-bit mask, need to map 15405331Samw */ 15415331Samw if (faccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) 15425331Samw access |= VWRITE; 15435331Samw 15445331Samw if (faccess & FILE_READ_DATA) 15455331Samw access |= VREAD; 15465331Samw 15475331Samw if (faccess & FILE_EXECUTE) 15485331Samw access |= VEXEC; 15495331Samw 15505331Samw error = smb_vop_access(snode->vp, access, 0, NULL, cr); 15515331Samw } 15525331Samw 15535331Samw return ((error) ? NT_STATUS_ACCESS_DENIED : NT_STATUS_SUCCESS); 15545331Samw } 15555331Samw 15565331Samw /* 15575331Samw * smb_fsop_lookup_name() 15585331Samw * 15595331Samw * Sanity checks on dir_snode done in smb_fsop_lookup(). 15605331Samw * 15615331Samw * Note: This function is called only from the open path. 15625331Samw * It will check if the file is a stream. 15635331Samw * It will also return an error if the looked-up file is in 15645331Samw * a child mount. 15655331Samw */ 15665331Samw 15675331Samw int 15685331Samw smb_fsop_lookup_name( 15695331Samw struct smb_request *sr, 15705331Samw cred_t *cr, 15715331Samw int flags, 15725331Samw smb_node_t *root_node, 15735331Samw smb_node_t *dir_snode, 15745331Samw char *name, 15755331Samw smb_node_t **ret_snode, 15765331Samw smb_attr_t *ret_attr) 15775331Samw { 15785331Samw smb_node_t *fnode; 15795331Samw smb_attr_t file_attr; 15805331Samw caller_context_t ct; 15815331Samw vnode_t *xattrdirvp; 15825331Samw vnode_t *vp; 15835331Samw char *od_name; 15845331Samw char *fname; 15855331Samw char *sname; 15865331Samw int rc; 15875331Samw 15885331Samw ASSERT(cr); 15895331Samw ASSERT(dir_snode); 15905331Samw ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); 15915331Samw ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); 15925331Samw 15935331Samw /* 15945331Samw * The following check is required for streams processing, below 15955331Samw */ 15965331Samw 15975331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 15985331Samw flags |= SMB_IGNORE_CASE; 15995331Samw 16005331Samw fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 16015331Samw sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 16025331Samw 16035331Samw if (smb_stream_parse_name(name, fname, sname)) { 16045331Samw /* 16055331Samw * Look up the unnamed stream (i.e. fname). 16065331Samw * Unmangle processing will be done on fname 16075331Samw * as well as any link target. 16085331Samw */ 16095331Samw rc = smb_fsop_lookup(sr, cr, flags, root_node, dir_snode, fname, 16105331Samw &fnode, &file_attr, NULL, NULL); 16115331Samw 16125331Samw if (rc != 0) { 16135331Samw kmem_free(fname, MAXNAMELEN); 16145331Samw kmem_free(sname, MAXNAMELEN); 16155331Samw return (rc); 16165331Samw } 16175331Samw 16185331Samw od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 16195331Samw 16205331Samw /* 16215331Samw * od_name is the on-disk name of the stream, except 16225331Samw * without the prepended stream prefix (SMB_STREAM_PREFIX) 16235331Samw */ 16245331Samw 16255331Samw /* 16265331Samw * XXX 16275331Samw * What permissions NTFS requires for stream lookup if any? 16285331Samw */ 16295331Samw rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name, 16305331Samw &xattrdirvp, flags, root_node->vp, cr, &ct); 16315331Samw 16325331Samw if (rc != 0) { 16335331Samw smb_node_release(fnode); 16345331Samw kmem_free(fname, MAXNAMELEN); 16355331Samw kmem_free(sname, MAXNAMELEN); 16365331Samw kmem_free(od_name, MAXNAMELEN); 16375331Samw return (rc); 16385331Samw } 16395331Samw 16405331Samw *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, 16415331Samw vp, od_name, ret_attr); 16425331Samw 16435331Samw kmem_free(od_name, MAXNAMELEN); 16445331Samw smb_node_release(fnode); 16455331Samw 16465331Samw if (*ret_snode == NULL) { 16475331Samw VN_RELE(xattrdirvp); 16485331Samw VN_RELE(vp); 16495331Samw kmem_free(fname, MAXNAMELEN); 16505331Samw kmem_free(sname, MAXNAMELEN); 16515331Samw return (ENOMEM); 16525331Samw } 16535331Samw } else { 16545331Samw rc = smb_fsop_lookup(sr, cr, flags, root_node, dir_snode, name, 16555331Samw ret_snode, ret_attr, NULL, NULL); 16565331Samw } 16575331Samw 16585331Samw if (rc == 0) { 16595331Samw ASSERT(ret_snode); 16605331Samw if (SMB_TREE_ROOT_FS(sr, *ret_snode) == 0) { 16615331Samw smb_node_release(*ret_snode); 16625331Samw *ret_snode = NULL; 16635331Samw rc = EACCES; 16645331Samw } 16655331Samw } 16665331Samw 16675331Samw kmem_free(fname, MAXNAMELEN); 16685331Samw kmem_free(sname, MAXNAMELEN); 16695331Samw 16705331Samw return (rc); 16715331Samw } 16725331Samw 16735331Samw /* 16745331Samw * smb_fsop_lookup 16755331Samw * 16765331Samw * All SMB functions should use this smb_vop_lookup wrapper to ensure that 16775331Samw * the smb_vop_lookup is performed with the appropriate credentials and using 16785331Samw * case insensitive compares. Please document any direct call to smb_vop_lookup 16795331Samw * to explain the reason for avoiding this wrapper. 16805331Samw * 16815331Samw * It is assumed that a reference exists on dir_snode coming into this routine 16825331Samw * (and that it is safe from deallocation). 16835331Samw * 16845331Samw * Same with the root_node. 16855331Samw * 16865331Samw * *ret_snode is returned with a reference upon success. No reference is 16875331Samw * taken if an error is returned. 16885331Samw * 16895331Samw * Note: The returned ret_snode may be in a child mount. This is ok for 16905331Samw * readdir and getdents. 16915331Samw * 16925331Samw * Other smb_fsop_* routines will call SMB_TREE_ROOT_FS() to prevent 16935331Samw * operations on files not in the parent mount. 16945331Samw */ 16955331Samw int 16965331Samw smb_fsop_lookup( 16975331Samw struct smb_request *sr, 16985331Samw cred_t *cr, 16995331Samw int flags, 17005331Samw smb_node_t *root_node, 17015331Samw smb_node_t *dir_snode, 17025331Samw char *name, 17035331Samw smb_node_t **ret_snode, 17045331Samw smb_attr_t *ret_attr, 17055331Samw char *ret_shortname, /* Must be at least MANGLE_NAMELEN chars */ 17065331Samw char *ret_name83) /* Must be at least MANGLE_NAMELEN chars */ 17075331Samw { 17085331Samw smb_node_t *lnk_target_node; 17095331Samw smb_node_t *lnk_dnode; 17105331Samw caller_context_t ct; 17115331Samw char *longname; 17125331Samw char *od_name; 17135331Samw vnode_t *vp; 17145331Samw int rc; 17155331Samw 17165331Samw ASSERT(cr); 17175331Samw ASSERT(dir_snode); 17185331Samw ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); 17195331Samw ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); 17205331Samw 17215331Samw if (name == NULL) 17225331Samw return (EINVAL); 17235331Samw 17245331Samw if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) 17255331Samw return (EACCES); 17265331Samw 17275331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 17285331Samw flags |= SMB_IGNORE_CASE; 17295331Samw 17305331Samw smb_get_caller_context(sr, &ct); 17315331Samw 17325331Samw od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 17335331Samw 17345331Samw rc = smb_vop_lookup(dir_snode->vp, name, &vp, od_name, flags, 17355331Samw root_node ? root_node->vp : NULL, cr, &ct); 17365331Samw 17375331Samw if (rc != 0) { 17385331Samw if (smb_maybe_mangled_name(name) == 0) { 17395331Samw kmem_free(od_name, MAXNAMELEN); 17405331Samw return (rc); 17415331Samw } 17425331Samw 17435331Samw longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 17445331Samw 17455331Samw rc = smb_unmangle_name(sr, cr, dir_snode, name, longname, 17465331Samw MAXNAMELEN, ret_shortname, ret_name83, 1); 17475331Samw 17485331Samw if (rc != 0) { 17495331Samw kmem_free(od_name, MAXNAMELEN); 17505331Samw kmem_free(longname, MAXNAMELEN); 17515331Samw return (rc); 17525331Samw } 17535331Samw 17545331Samw /* 17555331Samw * We passed "1" as the "od" parameter 17565331Samw * to smb_unmangle_name(), such that longname 17575331Samw * is the real (case-sensitive) on-disk name. 17585331Samw * We make sure we do a lookup on this exact 17595331Samw * name, as the name was mangled and denotes 17605331Samw * a unique file. 17615331Samw */ 17625331Samw 17635331Samw if (flags & SMB_IGNORE_CASE) 17645331Samw flags &= ~SMB_IGNORE_CASE; 17655331Samw 17665331Samw rc = smb_vop_lookup(dir_snode->vp, longname, &vp, od_name, 17675331Samw flags, root_node ? root_node->vp : NULL, cr, &ct); 17685331Samw 17695331Samw kmem_free(longname, MAXNAMELEN); 17705331Samw 17715331Samw if (rc != 0) { 17725331Samw kmem_free(od_name, MAXNAMELEN); 17735331Samw return (rc); 17745331Samw } 17755331Samw } 17765331Samw 17775331Samw if ((flags & SMB_FOLLOW_LINKS) && (vp->v_type == VLNK)) { 17785331Samw 17795331Samw rc = smb_pathname(sr, od_name, FOLLOW, root_node, dir_snode, 17805331Samw &lnk_dnode, &lnk_target_node, cr); 17815331Samw 17825331Samw if (rc != 0) { 17835331Samw /* 17845331Samw * The link is assumed to be for the last component 17855331Samw * of a path. Hence any ENOTDIR error will be returned 17865331Samw * as ENOENT. 17875331Samw */ 17885331Samw if (rc == ENOTDIR) 17895331Samw rc = ENOENT; 17905331Samw 17915331Samw VN_RELE(vp); 17925331Samw kmem_free(od_name, MAXNAMELEN); 17935331Samw return (rc); 17945331Samw } 17955331Samw 17965331Samw /* 17975331Samw * Release the original VLNK vnode 17985331Samw */ 17995331Samw 18005331Samw VN_RELE(vp); 18015331Samw vp = lnk_target_node->vp; 18025331Samw 18035331Samw rc = smb_vop_traverse_check(&vp); 18045331Samw 18055331Samw if (rc != 0) { 18065331Samw smb_node_release(lnk_dnode); 18075331Samw smb_node_release(lnk_target_node); 18085331Samw kmem_free(od_name, MAXNAMELEN); 18095331Samw return (rc); 18105331Samw } 18115331Samw 18125331Samw /* 18135331Samw * smb_vop_traverse_check() may have returned a different vnode 18145331Samw */ 18155331Samw 18165331Samw if (lnk_target_node->vp == vp) { 18175331Samw *ret_snode = lnk_target_node; 18185331Samw *ret_attr = (*ret_snode)->attr; 18195331Samw } else { 18205331Samw *ret_snode = smb_node_lookup(sr, NULL, cr, vp, 18215331Samw lnk_target_node->od_name, lnk_dnode, NULL, 18225331Samw ret_attr); 18235331Samw 18245331Samw if (*ret_snode == NULL) { 18255331Samw VN_RELE(vp); 18265331Samw rc = ENOMEM; 18275331Samw } 18285331Samw smb_node_release(lnk_target_node); 18295331Samw } 18305331Samw 18315331Samw smb_node_release(lnk_dnode); 18325331Samw 18335331Samw } else { 18345331Samw 18355331Samw rc = smb_vop_traverse_check(&vp); 18365331Samw if (rc) { 18375331Samw VN_RELE(vp); 18385331Samw kmem_free(od_name, MAXNAMELEN); 18395331Samw return (rc); 18405331Samw } 18415331Samw 18425331Samw *ret_snode = smb_node_lookup(sr, NULL, cr, vp, od_name, 18435331Samw dir_snode, NULL, ret_attr); 18445331Samw 18455331Samw if (*ret_snode == NULL) { 18465331Samw VN_RELE(vp); 18475331Samw rc = ENOMEM; 18485331Samw } 18495331Samw } 18505331Samw 18515331Samw kmem_free(od_name, MAXNAMELEN); 18525331Samw return (rc); 18535331Samw } 18545331Samw 18555331Samw /* 18565331Samw * smb_fsop_stream_readdir() 18575331Samw * 18585331Samw * ret_snode and ret_attr are optional parameters (i.e. NULL may be passed in) 18595331Samw * 18605331Samw * This routine will return only NTFS streams. If an NTFS stream is not 18615331Samw * found at the offset specified, the directory will be read until an NTFS 18625331Samw * stream is found or until EOF. 18635331Samw * 18645331Samw * Note: Sanity checks done in caller 18655331Samw * (smb_fsop_readdir(), smb_fsop_remove_streams()) 18665331Samw */ 18675331Samw 18685331Samw int 18695331Samw smb_fsop_stream_readdir(struct smb_request *sr, cred_t *cr, smb_node_t *fnode, 18705331Samw uint32_t *cookiep, struct fs_stream_info *stream_info, 18715331Samw smb_node_t **ret_snode, smb_attr_t *ret_attr) 18725331Samw { 18735331Samw smb_node_t *ret_snodep = NULL; 18745331Samw caller_context_t ct; 18755331Samw smb_attr_t tmp_attr; 18765331Samw vnode_t *xattrdirvp; 18775331Samw vnode_t *vp; 18785331Samw int rc = 0; 18795331Samw int flags = 0; 18805331Samw 18815331Samw /* 18825331Samw * XXX NTFS permission requirements if any? 18835331Samw */ 18845331Samw ASSERT(cr); 18855331Samw ASSERT(fnode); 18865331Samw ASSERT(fnode->n_magic == SMB_NODE_MAGIC); 18875331Samw ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING); 18885331Samw 18895331Samw if (SMB_TREE_CASE_INSENSITIVE(sr)) 18905331Samw flags = SMB_IGNORE_CASE; 18915331Samw 18925331Samw smb_get_caller_context(sr, &ct); 18935331Samw 18945331Samw rc = smb_vop_stream_readdir(fnode->vp, cookiep, stream_info, &vp, 18955331Samw &xattrdirvp, flags, cr, &ct); 18965331Samw 18975331Samw if ((rc != 0) || *cookiep == SMB_EOF) 18985331Samw return (rc); 18995331Samw 19005331Samw ret_snodep = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, vp, 19015331Samw stream_info->name, &tmp_attr); 19025331Samw 19035331Samw if (ret_snodep == NULL) { 19045331Samw VN_RELE(xattrdirvp); 19055331Samw VN_RELE(vp); 19065331Samw return (ENOMEM); 19075331Samw } 19085331Samw 19095331Samw stream_info->size = tmp_attr.sa_vattr.va_size; 19105331Samw 19115331Samw if (ret_attr) 19125331Samw *ret_attr = tmp_attr; 19135331Samw 19145331Samw if (ret_snode) 19155331Samw *ret_snode = ret_snodep; 19165331Samw else 19175331Samw smb_node_release(ret_snodep); 19185331Samw 19195331Samw return (rc); 19205331Samw } 19215331Samw 19225331Samw int /*ARGSUSED*/ 19235331Samw smb_fsop_commit(smb_request_t *sr, cred_t *cr, smb_node_t *snode) 19245331Samw { 19255331Samw caller_context_t ct; 19265331Samw 19275331Samw ASSERT(cr); 19285331Samw ASSERT(snode); 19295331Samw ASSERT(snode->n_magic == SMB_NODE_MAGIC); 19305331Samw ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); 19315331Samw 19325331Samw ASSERT(sr); 19335331Samw ASSERT(sr->tid_tree); 19345331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 19355331Samw return (EROFS); 19365331Samw 19375331Samw smb_get_caller_context(sr, &ct); 19385331Samw 19395331Samw return (smb_vop_commit(snode->vp, cr, &ct)); 19405331Samw } 19415331Samw 19425331Samw /* 19435331Samw * smb_fsop_aclread 19445331Samw * 19455331Samw * Retrieve filesystem ACL. Depends on requested ACLs in 19465331Samw * fs_sd->sd_secinfo, it'll set DACL and SACL pointers in 19475331Samw * fs_sd. Note that requesting a DACL/SACL doesn't mean that 19485331Samw * the corresponding field in fs_sd should be non-NULL upon 19495331Samw * return, since the target ACL might not contain that type of 19505331Samw * entries. 19515331Samw * 19525331Samw * Returned ACL is always in ACE_T (aka ZFS) format. 19535331Samw * If successful the allocated memory for the ACL should be freed 1954*5521Sas200622 * using smb_fsacl_free() or smb_fssd_term() 19555331Samw */ 19565331Samw int 19575331Samw smb_fsop_aclread(smb_request_t *sr, cred_t *cr, smb_node_t *snode, 19585331Samw smb_fssd_t *fs_sd) 19595331Samw { 19605331Samw int error = 0; 19615331Samw int flags = 0; 19625331Samw int access = 0; 19635331Samw acl_t *acl; 19645331Samw caller_context_t ct; 19655331Samw smb_node_t *unnamed_node; 19665331Samw 19675331Samw ASSERT(cr); 19685331Samw 19695331Samw if (sr->fid_ofile) { 19705331Samw if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) 19715331Samw access = READ_CONTROL; 19725331Samw 19735331Samw if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) 19745331Samw access |= ACCESS_SYSTEM_SECURITY; 19755331Samw 19765331Samw error = smb_ofile_access(sr->fid_ofile, cr, access); 19775331Samw if (error != NT_STATUS_SUCCESS) { 19785331Samw return (EACCES); 19795331Samw } 19805331Samw } 19815331Samw 19825331Samw unnamed_node = SMB_IS_STREAM(snode); 19835331Samw if (unnamed_node) { 19845331Samw ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); 19855331Samw ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); 19865331Samw /* 19875331Samw * Streams don't have ACL, any read ACL attempt on a stream 19885331Samw * should be performed on the unnamed stream. 19895331Samw */ 19905331Samw snode = unnamed_node; 19915331Samw } 19925331Samw 19935331Samw if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) 19945331Samw flags = ATTR_NOACLCHECK; 19955331Samw 19965331Samw smb_get_caller_context(sr, &ct); 19975331Samw error = smb_vop_acl_read(snode->vp, &acl, flags, 19985331Samw sr->tid_tree->t_acltype, cr, &ct); 19995331Samw if (error != 0) { 20005331Samw return (error); 20015331Samw } 20025331Samw 20035331Samw error = acl_translate(acl, _ACL_ACE_ENABLED, 20045331Samw (snode->vp->v_type == VDIR), fs_sd->sd_uid, fs_sd->sd_gid); 20055331Samw 20065331Samw if (error == 0) { 2007*5521Sas200622 smb_fsacl_split(acl, &fs_sd->sd_zdacl, &fs_sd->sd_zsacl, 20085331Samw fs_sd->sd_secinfo); 20095331Samw } 20105331Samw 20115331Samw acl_free(acl); 20125331Samw return (error); 20135331Samw } 20145331Samw 20155331Samw /* 20165331Samw * smb_fsop_aclwrite 20175331Samw * 20185331Samw * Stores the filesystem ACL provided in fs_sd->sd_acl. 20195331Samw */ 20205331Samw int 20215331Samw smb_fsop_aclwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode, 20225331Samw smb_fssd_t *fs_sd) 20235331Samw { 20245331Samw int target_flavor; 20255331Samw int error = 0; 20265331Samw int flags = 0; 20275331Samw int access = 0; 20285331Samw caller_context_t ct; 20295331Samw acl_t *acl, *dacl, *sacl; 20305331Samw smb_node_t *unnamed_node; 20315331Samw 20325331Samw ASSERT(cr); 20335331Samw 20345331Samw ASSERT(sr); 20355331Samw ASSERT(sr->tid_tree); 20365331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 20375331Samw return (EROFS); 20385331Samw 20395331Samw if (sr->fid_ofile) { 20405331Samw if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) 20415331Samw access = WRITE_DAC; 20425331Samw 20435331Samw if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) 20445331Samw access |= ACCESS_SYSTEM_SECURITY; 20455331Samw 20465331Samw error = smb_ofile_access(sr->fid_ofile, cr, access); 20475331Samw if (error != NT_STATUS_SUCCESS) 20485331Samw return (EACCES); 20495331Samw } 20505331Samw 20515331Samw switch (sr->tid_tree->t_acltype) { 20525331Samw case ACLENT_T: 20535331Samw target_flavor = _ACL_ACLENT_ENABLED; 20545331Samw break; 20555331Samw 20565331Samw case ACE_T: 20575331Samw target_flavor = _ACL_ACE_ENABLED; 20585331Samw break; 20595331Samw default: 20605331Samw return (EINVAL); 20615331Samw } 20625331Samw 20635331Samw unnamed_node = SMB_IS_STREAM(snode); 20645331Samw if (unnamed_node) { 20655331Samw ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); 20665331Samw ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); 20675331Samw /* 20685331Samw * Streams don't have ACL, any write ACL attempt on a stream 20695331Samw * should be performed on the unnamed stream. 20705331Samw */ 20715331Samw snode = unnamed_node; 20725331Samw } 20735331Samw 20745331Samw dacl = fs_sd->sd_zdacl; 20755331Samw sacl = fs_sd->sd_zsacl; 20765331Samw 20775331Samw ASSERT(dacl || sacl); 20785331Samw if ((dacl == NULL) && (sacl == NULL)) 20795331Samw return (EINVAL); 20805331Samw 20815331Samw if (dacl && sacl) 2082*5521Sas200622 acl = smb_fsacl_merge(dacl, sacl); 20835331Samw else if (dacl) 20845331Samw acl = dacl; 20855331Samw else 20865331Samw acl = sacl; 20875331Samw 20885331Samw error = acl_translate(acl, target_flavor, (snode->vp->v_type == VDIR), 20895331Samw fs_sd->sd_uid, fs_sd->sd_gid); 20905331Samw if (error == 0) { 20915331Samw smb_get_caller_context(sr, &ct); 20925331Samw if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) 20935331Samw flags = ATTR_NOACLCHECK; 20945331Samw 20955331Samw error = smb_vop_acl_write(snode->vp, acl, flags, cr, &ct); 20965331Samw } 20975331Samw 20985331Samw if (dacl && sacl) 20995331Samw acl_free(acl); 21005331Samw 21015331Samw return (error); 21025331Samw } 21035331Samw 21045331Samw acl_type_t 21055331Samw smb_fsop_acltype(smb_node_t *snode) 21065331Samw { 21075331Samw return (smb_vop_acl_type(snode->vp)); 21085331Samw } 21095331Samw 21105331Samw /* 21115331Samw * smb_fsop_sdread 21125331Samw * 21135331Samw * Read the requested security descriptor items from filesystem. 21145331Samw * The items are specified in fs_sd->sd_secinfo. 21155331Samw */ 21165331Samw int 21175331Samw smb_fsop_sdread(smb_request_t *sr, cred_t *cr, smb_node_t *snode, 21185331Samw smb_fssd_t *fs_sd) 21195331Samw { 21205331Samw int error = 0; 21215331Samw int getowner = 0; 21225331Samw cred_t *ga_cred; 21235331Samw smb_attr_t attr; 21245331Samw 21255331Samw ASSERT(cr); 21265331Samw ASSERT(fs_sd); 21275331Samw 21285331Samw /* 21295331Samw * File's uid/gid is fetched in two cases: 21305331Samw * 21315331Samw * 1. it's explicitly requested 21325331Samw * 21335331Samw * 2. target ACL is ACE_T (ZFS ACL). They're needed for 21345331Samw * owner@/group@ entries. In this case kcred should be used 21355331Samw * because uid/gid are fetched on behalf of smb server. 21365331Samw */ 21375331Samw if (fs_sd->sd_secinfo & (SMB_OWNER_SECINFO | SMB_GROUP_SECINFO)) { 21385331Samw getowner = 1; 21395331Samw ga_cred = cr; 21405331Samw } else if (sr->tid_tree->t_acltype == ACE_T) { 21415331Samw getowner = 1; 21425331Samw ga_cred = kcred; 21435331Samw } 21445331Samw 21455331Samw if (getowner) { 21465331Samw /* 21475331Samw * Windows require READ_CONTROL to read owner/group SID since 21485331Samw * they're part of Security Descriptor. 21495331Samw * ZFS only requires read_attribute. Need to have a explicit 21505331Samw * access check here. 21515331Samw */ 21525331Samw if (sr->fid_ofile == NULL) { 21535331Samw error = smb_fsop_access(sr, ga_cred, snode, 21545331Samw READ_CONTROL); 21555331Samw if (error) 21565331Samw return (error); 21575331Samw } 21585331Samw 21595331Samw attr.sa_mask = SMB_AT_UID | SMB_AT_GID; 21605331Samw error = smb_fsop_getattr(sr, ga_cred, snode, &attr); 21615331Samw if (error == 0) { 21625331Samw fs_sd->sd_uid = attr.sa_vattr.va_uid; 21635331Samw fs_sd->sd_gid = attr.sa_vattr.va_gid; 21645331Samw } else { 21655331Samw return (error); 21665331Samw } 21675331Samw } 21685331Samw 21695331Samw if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) { 21705331Samw error = smb_fsop_aclread(sr, cr, snode, fs_sd); 21715331Samw } 21725331Samw 21735331Samw return (error); 21745331Samw } 21755331Samw 21765331Samw /* 21775331Samw * smb_fsop_sdmerge 21785331Samw * 21795331Samw * From SMB point of view DACL and SACL are two separate list 21805331Samw * which can be manipulated independently without one affecting 21815331Samw * the other, but entries for both DACL and SACL will end up 21825331Samw * in the same ACL if target filesystem supports ACE_T ACLs. 21835331Samw * 21845331Samw * So, if either DACL or SACL is present in the client set request 21855331Samw * the entries corresponding to the non-present ACL shouldn't 21865331Samw * be touched in the FS ACL. 21875331Samw * 21885331Samw * fs_sd parameter contains DACL and SACL specified by SMB 21895331Samw * client to be set on a file/directory. The client could 21905331Samw * specify both or one of these ACLs (if none is specified 21915331Samw * we don't get this far). When both DACL and SACL are given 21925331Samw * by client the existing ACL should be overwritten. If only 21935331Samw * one of them is specified the entries corresponding to the other 21945331Samw * ACL should not be touched. For example, if only DACL 21955331Samw * is specified in input fs_sd, the function reads audit entries 21965331Samw * of the existing ACL of the file and point fs_sd->sd_zsdacl 21975331Samw * pointer to the fetched SACL, this way when smb_fsop_sdwrite() 21985331Samw * function is called the passed fs_sd would point to the specified 21995331Samw * DACL by client and fetched SACL from filesystem, so the file 22005331Samw * will end up with correct ACL. 22015331Samw */ 22025331Samw static int 22035331Samw smb_fsop_sdmerge(smb_request_t *sr, smb_node_t *snode, smb_fssd_t *fs_sd) 22045331Samw { 22055331Samw smb_fssd_t cur_sd; 22065331Samw int error = 0; 22075331Samw 22085331Samw if (sr->tid_tree->t_acltype != ACE_T) 22095331Samw /* Don't bother if target FS doesn't support ACE_T */ 22105331Samw return (0); 22115331Samw 22125331Samw if ((fs_sd->sd_secinfo & SMB_ACL_SECINFO) != SMB_ACL_SECINFO) { 22135331Samw if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) { 22145331Samw /* 22155331Samw * Don't overwrite existing audit entries 22165331Samw */ 2217*5521Sas200622 smb_fssd_init(&cur_sd, SMB_SACL_SECINFO, 22185331Samw fs_sd->sd_flags); 22195331Samw 22205331Samw error = smb_fsop_sdread(sr, kcred, snode, &cur_sd); 22215331Samw if (error == 0) { 22225331Samw ASSERT(fs_sd->sd_zsacl == NULL); 22235331Samw fs_sd->sd_zsacl = cur_sd.sd_zsacl; 22245331Samw if (fs_sd->sd_zsacl && fs_sd->sd_zdacl) 22255331Samw fs_sd->sd_zsacl->acl_flags = 22265331Samw fs_sd->sd_zdacl->acl_flags; 22275331Samw } 22285331Samw } else { 22295331Samw /* 22305331Samw * Don't overwrite existing access entries 22315331Samw */ 2232*5521Sas200622 smb_fssd_init(&cur_sd, SMB_DACL_SECINFO, 22335331Samw fs_sd->sd_flags); 22345331Samw 22355331Samw error = smb_fsop_sdread(sr, kcred, snode, &cur_sd); 22365331Samw if (error == 0) { 22375331Samw ASSERT(fs_sd->sd_zdacl == NULL); 22385331Samw fs_sd->sd_zdacl = cur_sd.sd_zdacl; 22395331Samw if (fs_sd->sd_zdacl && fs_sd->sd_zsacl) 22405331Samw fs_sd->sd_zdacl->acl_flags = 22415331Samw fs_sd->sd_zsacl->acl_flags; 22425331Samw } 22435331Samw } 22445331Samw 22455331Samw if (error) 2246*5521Sas200622 smb_fssd_term(&cur_sd); 22475331Samw } 22485331Samw 22495331Samw return (error); 22505331Samw } 22515331Samw 22525331Samw /* 22535331Samw * smb_fsop_sdwrite 22545331Samw * 22555331Samw * Stores the given uid, gid and acl in filesystem. 22565331Samw * Provided items in fs_sd are specified by fs_sd->sd_secinfo. 22575331Samw * 22585331Samw * A SMB security descriptor could contain owner, primary group, 22595331Samw * DACL and SACL. Setting an SD should be atomic but here it has to 22605331Samw * be done via two separate FS operations: VOP_SETATTR and 22615331Samw * VOP_SETSECATTR. Therefore, this function has to simulate the 22625331Samw * atomicity as well as it can. 22635331Samw */ 22645331Samw int 22655331Samw smb_fsop_sdwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode, 22665331Samw smb_fssd_t *fs_sd, int overwrite) 22675331Samw { 22685331Samw int error = 0; 22695331Samw int access = 0; 22705331Samw smb_attr_t set_attr; 22715331Samw smb_attr_t orig_attr; 22725331Samw 22735331Samw ASSERT(cr); 22745331Samw ASSERT(fs_sd); 22755331Samw 22765331Samw ASSERT(sr); 22775331Samw ASSERT(sr->tid_tree); 22785331Samw if (SMB_TREE_IS_READ_ONLY(sr)) 22795331Samw return (EROFS); 22805331Samw 22815331Samw bzero(&set_attr, sizeof (smb_attr_t)); 22825331Samw 22835331Samw if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) { 22845331Samw set_attr.sa_vattr.va_uid = fs_sd->sd_uid; 22855331Samw set_attr.sa_mask |= SMB_AT_UID; 22865331Samw } 22875331Samw 22885331Samw if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) { 22895331Samw set_attr.sa_vattr.va_gid = fs_sd->sd_gid; 22905331Samw set_attr.sa_mask |= SMB_AT_GID; 22915331Samw } 22925331Samw 22935331Samw if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) 22945331Samw access |= WRITE_DAC; 22955331Samw 22965331Samw if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) 22975331Samw access |= ACCESS_SYSTEM_SECURITY; 22985331Samw 22995331Samw if (sr->fid_ofile) 23005331Samw error = smb_ofile_access(sr->fid_ofile, cr, access); 23015331Samw else 23025331Samw error = smb_fsop_access(sr, cr, snode, access); 23035331Samw 23045331Samw if (error) 23055331Samw return (EACCES); 23065331Samw 23075331Samw if (set_attr.sa_mask) { 23085331Samw /* 23095331Samw * Get the current uid, gid so if smb_fsop_aclwrite fails 23105331Samw * we can revert uid, gid changes. 23115331Samw * 23125331Samw * We use root cred here so the operation doesn't fail 23135331Samw * due to lack of permission for the user to read the attrs 23145331Samw */ 23155331Samw 23165331Samw orig_attr.sa_mask = SMB_AT_UID | SMB_AT_GID; 23175331Samw error = smb_fsop_getattr(sr, kcred, snode, &orig_attr); 23185331Samw if (error == 0) 23195331Samw error = smb_fsop_setattr(sr, cr, snode, &set_attr, 23205331Samw NULL); 23215331Samw 23225331Samw if (error) 23235331Samw return (error); 23245331Samw } 23255331Samw 23265331Samw if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) { 23275331Samw if (overwrite == 0) { 23285331Samw error = smb_fsop_sdmerge(sr, snode, fs_sd); 23295331Samw if (error) 23305331Samw return (error); 23315331Samw } 23325331Samw 23335331Samw error = smb_fsop_aclwrite(sr, cr, snode, fs_sd); 23345331Samw if (error) { 23355331Samw /* 23365331Samw * Revert uid/gid changes if required. 23375331Samw */ 23385331Samw if (set_attr.sa_mask) { 23395331Samw orig_attr.sa_mask = set_attr.sa_mask; 23405331Samw (void) smb_fsop_setattr(sr, kcred, snode, 23415331Samw &orig_attr, NULL); 23425331Samw } 23435331Samw } 23445331Samw } 23455331Samw 23465331Samw return (error); 23475331Samw } 23485331Samw 23495331Samw /* 23505331Samw * smb_fsop_sdinherit 23515331Samw * 23525331Samw * Inherit the security descriptor from the parent container. 23535331Samw * This function is called after FS has created the file/folder 23545331Samw * so if this doesn't do anything it means FS inheritance is 23555331Samw * in place. 23565331Samw * 23575331Samw * Do inheritance for ZFS internally. 23585331Samw * 23595331Samw * If we want to let ZFS does the inheritance the 23605331Samw * following setting should be true: 23615331Samw * 23625331Samw * - aclinherit = passthrough 23635331Samw * - aclmode = passthrough 23645331Samw * - smbd umask = 0777 23655331Samw * 23665331Samw * This will result in right effective permissions but 23675331Samw * ZFS will always add 6 ACEs for owner, owning group 23685331Samw * and others to be POSIX compliant. This is not what 23695331Samw * Windows clients/users expect, so we decided that CIFS 23705331Samw * implements Windows rules and overwrite whatever ZFS 23715331Samw * comes up with. This way we also don't have to care 23725331Samw * about ZFS aclinherit and aclmode settings. 23735331Samw */ 23745331Samw static int 23755331Samw smb_fsop_sdinherit(smb_request_t *sr, smb_node_t *dnode, smb_fssd_t *fs_sd) 23765331Samw { 23775331Samw int is_dir; 2378*5521Sas200622 acl_t *dacl = NULL; 2379*5521Sas200622 acl_t *sacl = NULL; 23805331Samw ksid_t *owner_sid; 23815331Samw int error; 23825331Samw 23835331Samw ASSERT(fs_sd); 23845331Samw 23855331Samw if (sr->tid_tree->t_acltype != ACE_T) { 23865331Samw /* 23875331Samw * No forced inheritance for non-ZFS filesystems. 23885331Samw */ 23895331Samw fs_sd->sd_secinfo = 0; 23905331Samw return (0); 23915331Samw } 23925331Samw 23935331Samw 23945331Samw /* Fetch parent directory's ACL */ 23955331Samw error = smb_fsop_sdread(sr, kcred, dnode, fs_sd); 23965331Samw if (error) { 23975331Samw return (error); 23985331Samw } 23995331Samw 24005331Samw is_dir = (fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR); 24015331Samw owner_sid = crgetsid(sr->user_cr, KSID_OWNER); 24025331Samw ASSERT(owner_sid); 2403*5521Sas200622 dacl = smb_fsacl_inherit(fs_sd->sd_zdacl, is_dir, SMB_DACL_SECINFO, 24045331Samw owner_sid->ks_id); 2405*5521Sas200622 sacl = smb_fsacl_inherit(fs_sd->sd_zsacl, is_dir, SMB_SACL_SECINFO, 24065331Samw (uid_t)-1); 24075331Samw 2408*5521Sas200622 if (sacl == NULL) 2409*5521Sas200622 fs_sd->sd_secinfo &= ~SMB_SACL_SECINFO; 2410*5521Sas200622 2411*5521Sas200622 smb_fsacl_free(fs_sd->sd_zdacl); 2412*5521Sas200622 smb_fsacl_free(fs_sd->sd_zsacl); 24135331Samw 24145331Samw fs_sd->sd_zdacl = dacl; 24155331Samw fs_sd->sd_zsacl = sacl; 24165331Samw 24175331Samw return (0); 24185331Samw } 24195331Samw 24205331Samw /* 24215331Samw * smb_fsop_eaccess 24225331Samw * 24235331Samw * Returns the effective permission of the given credential for the 24245331Samw * specified object. 24255331Samw * 24265331Samw * This is just a workaround. We need VFS/FS support for this. 24275331Samw */ 24285331Samw void 24295331Samw smb_fsop_eaccess(smb_request_t *sr, cred_t *cr, smb_node_t *snode, 24305331Samw uint32_t *eaccess) 24315331Samw { 24325331Samw int access = 0; 24335331Samw vnode_t *dir_vp; 24345331Samw smb_node_t *unnamed_node; 24355331Samw 24365331Samw ASSERT(cr); 24375331Samw ASSERT(snode); 24385331Samw ASSERT(snode->n_magic == SMB_NODE_MAGIC); 24395331Samw ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); 24405331Samw 24415331Samw unnamed_node = SMB_IS_STREAM(snode); 24425331Samw if (unnamed_node) { 24435331Samw ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); 24445331Samw ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); 24455331Samw /* 24465331Samw * Streams authorization should be performed against the 24475331Samw * unnamed stream. 24485331Samw */ 24495331Samw snode = unnamed_node; 24505331Samw } 24515331Samw 24525331Samw if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) { 24535331Samw dir_vp = (snode->dir_snode) ? snode->dir_snode->vp : NULL; 24545331Samw smb_vop_eaccess(snode->vp, (int *)eaccess, V_ACE_MASK, dir_vp, 24555331Samw cr); 24565331Samw return; 24575331Samw } 24585331Samw 24595331Samw /* 24605331Samw * FS doesn't understand 32-bit mask 24615331Samw */ 24625331Samw smb_vop_eaccess(snode->vp, &access, 0, NULL, cr); 24635331Samw 24645331Samw *eaccess = READ_CONTROL | FILE_READ_EA | FILE_READ_ATTRIBUTES; 24655331Samw 24665331Samw if (access & VREAD) 24675331Samw *eaccess |= FILE_READ_DATA; 24685331Samw 24695331Samw if (access & VEXEC) 24705331Samw *eaccess |= FILE_EXECUTE; 24715331Samw 24725331Samw if (access & VWRITE) 24735331Samw *eaccess |= FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | 24745331Samw FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD; 24755331Samw } 2476*5521Sas200622 2477*5521Sas200622 /*ARGSUSED*/ 2478*5521Sas200622 void 2479*5521Sas200622 smb_get_caller_context(smb_request_t *sr, caller_context_t *ct) 2480*5521Sas200622 { 2481*5521Sas200622 ct->cc_caller_id = smb_caller_id; 2482*5521Sas200622 ct->cc_pid = 0; /* TBD */ 2483*5521Sas200622 ct->cc_sysid = 0; /* TBD */ 2484*5521Sas200622 } 2485