diff --git a/fs/cifsd/Makefile b/fs/cifsd/Makefile index 85bb672575479..a6c03c4ba51ed 100644 --- a/fs/cifsd/Makefile +++ b/fs/cifsd/Makefile @@ -9,5 +9,5 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ - smb2ops.o smb2misc.o asn1.o netmisc.o + smb2ops.o smb2misc.o asn1.o netmisc.o ndr.o ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 119b070ba82c0..0a49c67a69d68 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -1110,6 +1110,40 @@ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, return rc; } +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash) +{ + int rc = -1; + struct ksmbd_crypto_ctx *ctx = NULL; + + ctx = ksmbd_crypto_ctx_find_sha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha256 rc %d\n", rc); + goto out; + } + + rc = crypto_shash_init(CRYPTO_SHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, __u64 ses_id, int enc, diff --git a/fs/cifsd/auth.h b/fs/cifsd/auth.h index a1bdc159f1798..6fcfad5e7e1f1 100644 --- a/fs/cifsd/auth.h +++ b/fs/cifsd/auth.h @@ -85,4 +85,6 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess); int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash); +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash); #endif diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c index 581dd78ffb71f..15d7e2f7c3d71 100644 --- a/fs/cifsd/crypto_ctx.c +++ b/fs/cifsd/crypto_ctx.c @@ -77,6 +77,9 @@ static struct shash_desc *alloc_shash_desc(int id) case CRYPTO_SHASH_CMACAES: tfm = crypto_alloc_shash("cmac(aes)", 0, 0); break; + case CRYPTO_SHASH_SHA256: + tfm = crypto_alloc_shash("sha256", 0, 0); + break; case CRYPTO_SHASH_SHA512: tfm = crypto_alloc_shash("sha512", 0, 0); break; @@ -206,6 +209,11 @@ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); } +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256); +} + struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) { return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); diff --git a/fs/cifsd/crypto_ctx.h b/fs/cifsd/crypto_ctx.h index a17e0f0166e97..64a11dfd6c83c 100644 --- a/fs/cifsd/crypto_ctx.h +++ b/fs/cifsd/crypto_ctx.h @@ -13,6 +13,7 @@ enum { CRYPTO_SHASH_HMACMD5 = 0, CRYPTO_SHASH_HMACSHA256, CRYPTO_SHASH_CMACAES, + CRYPTO_SHASH_SHA256, CRYPTO_SHASH_SHA512, CRYPTO_SHASH_MD4, CRYPTO_SHASH_MD5, @@ -40,6 +41,7 @@ struct ksmbd_crypto_ctx { #define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) #define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) #define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) +#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) #define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) #define CRYPTO_MD4(c) ((c)->desc[CRYPTO_SHASH_MD4]) #define CRYPTO_MD5(c) ((c)->desc[CRYPTO_SHASH_MD5]) @@ -48,6 +50,7 @@ struct ksmbd_crypto_ctx { #define CRYPTO_HMACSHA256_TFM(c)\ ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) #define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) +#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) #define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) #define CRYPTO_MD4_TFM(c) ((c)->desc[CRYPTO_SHASH_MD4]->tfm) #define CRYPTO_MD5_TFM(c) ((c)->desc[CRYPTO_SHASH_MD5]->tfm) @@ -61,6 +64,7 @@ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void); diff --git a/fs/cifsd/ksmbd_server.h b/fs/cifsd/ksmbd_server.h index e0508b3f2fc3b..01eaf9ec4fdee 100644 --- a/fs/cifsd/ksmbd_server.h +++ b/fs/cifsd/ksmbd_server.h @@ -228,6 +228,7 @@ enum KSMBD_TREE_CONN_STATUS { #define KSMBD_SHARE_FLAG_INHERIT_OWNER (1 << 11) #define KSMBD_SHARE_FLAG_STREAMS (1 << 12) #define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS (1 << 13) +#define KSMBD_SHARE_FLAG_ACL_XATTR (1 << 14) /* * Tree connect request flags. @@ -274,6 +275,7 @@ enum KSMBD_TREE_CONN_STATUS { #define KSMBD_RPC_EINVALID_PARAMETER 0x00000057 #define KSMBD_RPC_EMORE_DATA 0x000000EA #define KSMBD_RPC_EINVALID_LEVEL 0x0000007C +#define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107 #define KSMBD_CONFIG_OPT_DISABLED 0 #define KSMBD_CONFIG_OPT_ENABLED 1 diff --git a/fs/cifsd/ksmbd_work.c b/fs/cifsd/ksmbd_work.c index 1fa76c1a6a8ae..8cd5dff0762d5 100644 --- a/fs/cifsd/ksmbd_work.c +++ b/fs/cifsd/ksmbd_work.c @@ -37,7 +37,7 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void) void ksmbd_free_work_struct(struct ksmbd_work *work) { - WARN_ON(work->saved_cred_level != 0); + WARN_ON(work->saved_cred != NULL); if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_TBUF && work->set_trans_buf) ksmbd_release_buffer(RESPONSE_BUF(work)); diff --git a/fs/cifsd/ksmbd_work.h b/fs/cifsd/ksmbd_work.h index c9c4fb02f6828..405434d4c8aba 100644 --- a/fs/cifsd/ksmbd_work.h +++ b/fs/cifsd/ksmbd_work.h @@ -48,7 +48,6 @@ struct ksmbd_work { unsigned int compound_sid; const struct cred *saved_cred; - int saved_cred_level; /* Number of granted credits */ unsigned int credits_granted; @@ -105,12 +104,12 @@ struct ksmbd_work { #define INIT_AUX_PAYLOAD(w) ((w)->aux_payload_buf = NULL) #define HAS_AUX_PAYLOAD(w) ((w)->aux_payload_sz != 0) -#define AUX_PAYLOAD(w) (void *)((w)->aux_payload_buf) +#define AUX_PAYLOAD(w) ((void *)((w)->aux_payload_buf)) #define AUX_PAYLOAD_SIZE(w) ((w)->aux_payload_sz) #define RESP_HDR_SIZE(w) ((w)->resp_hdr_sz) #define HAS_TRANSFORM_BUF(w) ((w)->tr_buf != NULL) -#define TRANSFORM_BUF(w) (void *)((w)->tr_buf) +#define TRANSFORM_BUF(w) ((void *)((w)->tr_buf)) struct ksmbd_work *ksmbd_alloc_work_struct(void); void ksmbd_free_work_struct(struct ksmbd_work *work); diff --git a/fs/cifsd/ndr.c b/fs/cifsd/ndr.c new file mode 100644 index 0000000000000..0a6fb916189f6 --- /dev/null +++ b/fs/cifsd/ndr.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include + +#include "glob.h" +#include "ndr.h" + +#define PAYLOAD_HEAD(d) ((d)->data + (d)->offset) + +#define KSMBD_ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) + +#define KSMBD_ALIGN(x, a) \ + ({ \ + typeof(x) ret = (x); \ + if (((x) & ((typeof(x))(a) - 1)) != 0) \ + ret = KSMBD_ALIGN_MASK(x, (typeof(x))(a) - 1); \ + ret; \ + }) + +enum { + XATTR_DOSINFO_ATTRIB = 0x00000001, + XATTR_DOSINFO_EA_SIZE = 0x00000002, + XATTR_DOSINFO_SIZE = 0x00000004, + XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, + XATTR_DOSINFO_CREATE_TIME = 0x00000010, + XATTR_DOSINFO_CHANGE_TIME = 0x00000020 +}; + +static void align_offset(struct ndr *ndr, int n) +{ + ndr->offset = KSMBD_ALIGN(ndr->offset, n); +} + +static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) +{ + char *data; + + data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); + if (!data) + return -ENOMEM; + + n->data = data; + n->length += 1024; + memset(n->data + n->offset, 0, 1024); + return 0; +} + +static void ndr_write_int16(struct ndr *n, __u16 value) +{ + if (n->length <= n->offset + sizeof(value)) + try_to_realloc_ndr_blob(n, sizeof(value)); + + *(__le16 *)PAYLOAD_HEAD(n) = cpu_to_le16(value); + n->offset += sizeof(value); +} + +static void ndr_write_int32(struct ndr *n, __u32 value) +{ + if (n->length <= n->offset + sizeof(value)) + try_to_realloc_ndr_blob(n, sizeof(value)); + + *(__le32 *)PAYLOAD_HEAD(n) = cpu_to_le32(value); + n->offset += sizeof(value); +} + +static void ndr_write_int64(struct ndr *n, __u64 value) +{ + if (n->length <= n->offset + sizeof(value)) + try_to_realloc_ndr_blob(n, sizeof(value)); + + *(__le64 *)PAYLOAD_HEAD(n) = cpu_to_le64(value); + n->offset += sizeof(value); +} + +static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->length <= n->offset + sz) + try_to_realloc_ndr_blob(n, sz); + + memcpy(PAYLOAD_HEAD(n), value, sz); + n->offset += sz; + return 0; +} + +static int ndr_write_string(struct ndr *n, void *value, size_t sz) +{ + if (n->length <= n->offset + sz) + try_to_realloc_ndr_blob(n, sz); + + strncpy(PAYLOAD_HEAD(n), value, sz); + sz++; + n->offset += sz; + align_offset(n, 2); + return 0; +} + +static int ndr_read_string(struct ndr *n, void *value, size_t sz) +{ + int len = strnlen(PAYLOAD_HEAD(n), sz); + + memcpy(value, PAYLOAD_HEAD(n), len); + len++; + n->offset += len; + align_offset(n, 2); + return 0; +} + +static int ndr_read_bytes(struct ndr *n, void *value, size_t sz) +{ + memcpy(value, PAYLOAD_HEAD(n), sz); + n->offset += sz; + return 0; +} + +static __u16 ndr_read_int16(struct ndr *n) +{ + __u16 ret; + + ret = le16_to_cpu(*(__le16 *)PAYLOAD_HEAD(n)); + n->offset += sizeof(__u16); + return ret; +} + +static __u32 ndr_read_int32(struct ndr *n) +{ + __u32 ret; + + ret = le32_to_cpu(*(__le32 *)PAYLOAD_HEAD(n)); + n->offset += sizeof(__u32); + return ret; +} + +static __u64 ndr_read_int64(struct ndr *n) +{ + __u64 ret; + + ret = le64_to_cpu(*(__le64 *)PAYLOAD_HEAD(n)); + n->offset += sizeof(__u64); + return ret; +} + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12] = {0}; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + snprintf(hex_attr, 10, "0x%x", da->attr); + ndr_write_string(n, hex_attr, strlen(hex_attr)); + ndr_write_int16(n, da->version); + ndr_write_int32(n, da->version); + ndr_write_int32(n, XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME); + ndr_write_int32(n, da->attr); + ndr_write_int32(n, da->ea_size); + ndr_write_int64(n, da->size); + ndr_write_int64(n, da->alloc_size); + ndr_write_int64(n, da->create_time); + ndr_write_int64(n, da->change_time); + return 0; +} + +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12] = {0}; + int version2; + + n->offset = 0; + ndr_read_string(n, hex_attr, n->length - n->offset); + da->version = ndr_read_int16(n); + if (da->version != 3) { + ksmbd_err("v%d version is not supported\n", da->version); + return -EINVAL; + } + + version2 = ndr_read_int32(n); + if (da->version != version2) { + ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", + da->version, version2); + return -EINVAL; + } + + ndr_read_int32(n); + da->attr = ndr_read_int32(n); + ndr_read_int32(n); + ndr_read_int64(n); + ndr_read_int64(n); + da->create_time = ndr_read_int64(n); + ndr_read_int64(n); + return 0; +} + +static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) +{ + int i; + + ndr_write_int32(n, acl->count); + align_offset(n, 8); + ndr_write_int32(n, acl->count); + ndr_write_int32(n, 0); + + for (i = 0; i < acl->count; i++) { + align_offset(n, 8); + ndr_write_int16(n, acl->entries[i].type); + ndr_write_int16(n, acl->entries[i].type); + + if (acl->entries[i].type == SMB_ACL_USER) { + align_offset(n, 8); + ndr_write_int64(n, acl->entries[i].uid); + } else if (acl->entries[i].type == SMB_ACL_GROUP) { + align_offset(n, 8); + ndr_write_int64(n, acl->entries[i].gid); + } + + /* push permission */ + ndr_write_int32(n, acl->entries[i].perm); + } + + return 0; +} + +int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, + struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl) +{ + int ref_id = 0x00020000; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (acl) { + /* ACL ACCESS */ + ndr_write_int32(n, ref_id); + ref_id += 4; + } else + ndr_write_int32(n, 0); + + if (def_acl) { + /* DEFAULT ACL ACCESS */ + ndr_write_int32(n, ref_id); + ref_id += 4; + } else + ndr_write_int32(n, 0); + + ndr_write_int64(n, from_kuid(&init_user_ns, inode->i_uid)); + ndr_write_int64(n, from_kgid(&init_user_ns, inode->i_gid)); + ndr_write_int32(n, inode->i_mode); + + if (acl) { + ndr_encode_posix_acl_entry(n, acl); + if (def_acl) + ndr_encode_posix_acl_entry(n, def_acl); + } + return 0; +} + +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + int ref_id = 0x00020004; + + n->offset = 0; + n->length = 2048; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + ndr_write_int16(n, acl->version); + ndr_write_int32(n, acl->version); + ndr_write_int16(n, 2); + ndr_write_int32(n, ref_id); + + /* push hash type and hash 64bytes */ + ndr_write_int16(n, acl->hash_type); + ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + ndr_write_bytes(n, acl->desc, acl->desc_len); + ndr_write_int64(n, acl->current_time); + ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + + /* push ndr for security descriptor */ + ndr_write_bytes(n, acl->sd_buf, acl->sd_size); + + return 0; +} + +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + int version2; + + n->offset = 0; + acl->version = ndr_read_int16(n); + if (acl->version != 4) { + ksmbd_err("v%d version is not supported\n", acl->version); + return -EINVAL; + } + + version2 = ndr_read_int32(n); + if (acl->version != version2) { + ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", + acl->version, version2); + return -EINVAL; + } + + /* Read Level */ + ndr_read_int16(n); + /* Read Ref Id */ + ndr_read_int32(n); + acl->hash_type = ndr_read_int16(n); + ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + + ndr_read_bytes(n, acl->desc, 10); + if (strncmp(acl->desc, "posix_acl", 9)) { + ksmbd_err("Invalid acl desciption : %s\n", acl->desc); + return -EINVAL; + } + + /* Read Time */ + ndr_read_int64(n); + /* Read Posix ACL hash */ + ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + acl->sd_size = n->length - n->offset; + acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); + if (!acl->sd_buf) + return -ENOMEM; + + ndr_read_bytes(n, acl->sd_buf, acl->sd_size); + + return 0; +} diff --git a/fs/cifsd/ndr.h b/fs/cifsd/ndr.h new file mode 100644 index 0000000000000..a9db968b78aca --- /dev/null +++ b/fs/cifsd/ndr.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +struct ndr { + char *data; + int offset; + int length; +}; + +#define NDR_NTSD_OFFSETOF 0xA0 + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, + struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl); +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index d38257d3ac8f2..e90d68446d000 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "glob.h" #include "smb2pdu.h" @@ -35,6 +36,7 @@ #include "mgmt/tree_connect.h" #include "mgmt/user_session.h" #include "mgmt/ksmbd_ida.h" +#include "ndr.h" static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) { @@ -2259,12 +2261,12 @@ static noinline int smb2_set_stream_name_xattr(struct path *path, rc = ksmbd_vfs_xattr_stream_name(stream_name, &xattr_stream_name, - &xattr_stream_size); + &xattr_stream_size, + s_type); if (rc) return rc; fp->stream.name = xattr_stream_name; - fp->stream.type = s_type; fp->stream.size = xattr_stream_size; /* Check if there is stream prefix in xattr space */ @@ -2305,11 +2307,8 @@ static int smb2_remove_smb_xattrs(struct dentry *dentry) if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && strncmp(&name[XATTR_USER_PREFIX_LEN], - CREATION_TIME_PREFIX, - CREATION_TIME_PREFIX_LEN) && - strncmp(&name[XATTR_USER_PREFIX_LEN], - FILE_ATTRIBUTE_PREFIX, - FILE_ATTRIBUTE_PREFIX_LEN) && + DOS_ATTRIBUTE_PREFIX, + DOS_ATTRIBUTE_PREFIX_LEN) && strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, STREAM_PREFIX_LEN)) @@ -2347,59 +2346,41 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, struct path *path, struct ksmbd_file *fp) { + struct xattr_dos_attrib da = {0}; int rc; if (!test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) return; - rc = ksmbd_vfs_setxattr(path->dentry, - XATTR_NAME_FILE_ATTRIBUTE, - (void *)&fp->f_ci->m_fattr, - FILE_ATTRIBUTE_LEN, - 0); - if (rc) - ksmbd_debug(SMB, "failed to store file attribute in EA\n"); + da.version = 3; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.create_time = fp->create_time; - rc = ksmbd_vfs_setxattr(path->dentry, - XATTR_NAME_CREATION_TIME, - (void *)&fp->create_time, - CREATIOM_TIME_LEN, - 0); + rc = ksmbd_vfs_set_dos_attrib_xattr(path->dentry, &da); if (rc) - ksmbd_debug(SMB, "failed to store creation time in EA\n"); + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); } static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, struct path *path, struct ksmbd_file *fp) { - char *attr = NULL; + struct xattr_dos_attrib da; int rc; fp->f_ci->m_fattr &= ~(ATTR_HIDDEN_LE | ATTR_SYSTEM_LE); - /* get FileAttributes from XATTR_NAME_FILE_ATTRIBUTE */ + /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ if (!test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) return; - rc = ksmbd_vfs_getxattr(path->dentry, - XATTR_NAME_FILE_ATTRIBUTE, - &attr); - if (rc > 0) - fp->f_ci->m_fattr = *((__le32 *)attr); - - ksmbd_free(attr); - - rc = ksmbd_vfs_getxattr(path->dentry, - XATTR_NAME_CREATION_TIME, - &attr); - - if (rc > 0) - fp->create_time = *((__u64 *)attr); - - ksmbd_free(attr); + rc = ksmbd_vfs_get_dos_attrib_xattr(path->dentry, &da); + if (rc > 0) { + fp->f_ci->m_fattr = cpu_to_le32(da.attr); + fp->create_time = da.create_time; + } } static int smb2_creat(struct ksmbd_work *work, @@ -2443,8 +2424,8 @@ static int smb2_creat(struct ksmbd_work *work, return 0; } -static int smb2_create_sd_buffer(struct smb2_create_req *req, - struct dentry *dentry) +static int smb2_create_sd_buffer(struct ksmbd_work *work, + struct smb2_create_req *req, struct dentry *dentry) { struct create_context *context; int rc = -ENOENT; @@ -2460,7 +2441,7 @@ static int smb2_create_sd_buffer(struct smb2_create_req *req, ksmbd_debug(SMB, "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); sd_buf = (struct create_sd_buf_req *)context; - rc = set_info_sec(dentry, &sd_buf->ntsd, + rc = set_info_sec(work->conn, work->tcon, dentry, &sd_buf->ntsd, le32_to_cpu(sd_buf->ccontext.DataLength), true); } @@ -2846,7 +2827,7 @@ int smb2_open(struct ksmbd_work *work) daccess = smb_map_generic_desired_access(req->DesiredAccess); if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - rc = smb_check_perm_dacl(path.dentry, &daccess, + rc = smb_check_perm_dacl(conn, path.dentry, &daccess, sess->user->uid); if (rc) goto err_out; @@ -2952,20 +2933,65 @@ int smb2_open(struct ksmbd_work *work) /* Set default windows and posix acls if creating new file */ if (created) { - rc = smb_inherit_dacl(path.dentry, sess->user->uid, - sess->user->gid); + int posix_acl_rc; + struct inode *inode = path.dentry->d_inode; + + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(inode, path.dentry->d_parent->d_inode); + if (posix_acl_rc) + ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + rc = smb_inherit_dacl(conn, path.dentry, sess->user->uid, + sess->user->gid); + } + if (rc) { - rc = smb2_create_sd_buffer(req, path.dentry); + rc = smb2_create_sd_buffer(work, req, path.dentry); if (rc) { - store_init_posix_acl(path.dentry->d_inode); - if (daccess & FILE_WRITE_DAC_LE) - store_init_ntacl(path.dentry); + if (posix_acl_rc) + ksmbd_vfs_set_init_posix_acl(inode); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + struct smb_fattr fattr; + struct smb_ntsd *pntsd; + int pntsd_size, ace_num; + + fattr.cf_uid = inode->i_uid; + fattr.cf_gid = inode->i_gid; + fattr.cf_mode = inode->i_mode; + fattr.cf_dacls = NULL; + + fattr.cf_acls = ksmbd_vfs_get_acl(inode, ACL_TYPE_ACCESS); + ace_num = fattr.cf_acls->a_count; + if (S_ISDIR(inode->i_mode)) { + fattr.cf_dacls = + ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); + ace_num += fattr.cf_dacls->a_count; + } + + pntsd = kmalloc(sizeof(struct smb_ntsd) + + sizeof(struct smb_sid)*3 + + sizeof(struct smb_acl) + + sizeof(struct smb_ace)*ace_num*2, + GFP_KERNEL); + if (!pntsd) + goto err_out; + + rc = build_sec_desc(pntsd, NULL, + OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO, + &pntsd_size, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + + rc = ksmbd_vfs_set_sd_xattr(conn, + path.dentry, pntsd, pntsd_size); + if (rc) + ksmbd_err("failed to store ntacl in xattr : %d\n", + rc); + } } - } else { - rc = smb_inherit_posix_acl(path.dentry->d_inode, - path.dentry->d_parent->d_inode); - if (rc) - ksmbd_debug(SMB, "inherit posix acl failed : %d\n", rc); } rc = 0; } @@ -2996,7 +3022,11 @@ int smb2_open(struct ksmbd_work *work) list_add(&fp->node, &fp->f_ci->m_fp_list); write_unlock(&fp->f_ci->m_lock); - generic_fillattr(d_inode(path.dentry), &stat); + rc = ksmbd_vfs_getattr(&path, &stat); + if (rc) { + generic_fillattr(d_inode(path.dentry), &stat); + rc = 0; + } /* Check delete pending among previous fp before oplock break */ if (ksmbd_inode_pending_delete(fp)) { @@ -3082,16 +3112,10 @@ int smb2_open(struct ksmbd_work *work) } } - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STREAMS) && - (file_info != FILE_OPENED) && - !S_ISDIR(file_inode(filp)->i_mode)) { - /* Create default data stream in xattr */ - ksmbd_vfs_setxattr(path.dentry, XATTR_NAME_STREAM, - NULL, 0, 0); - } - - fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); if (req->FileAttributes || fp->f_ci->m_fattr == 0) fp->f_ci->m_fattr = cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); @@ -4180,10 +4204,6 @@ static int smb2_get_ea(struct ksmbd_work *work, if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) continue; - if (!strncmp(&name[XATTR_USER_PREFIX_LEN], CREATION_TIME_PREFIX, - CREATION_TIME_PREFIX_LEN)) - continue; - if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, STREAM_PREFIX_LEN)) continue; @@ -4194,7 +4214,7 @@ static int smb2_get_ea(struct ksmbd_work *work, continue; if (!strncmp(&name[XATTR_USER_PREFIX_LEN], - FILE_ATTRIBUTE_PREFIX, FILE_ATTRIBUTE_PREFIX_LEN)) + DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) continue; if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) @@ -4468,7 +4488,6 @@ static void get_file_stream_info(struct ksmbd_work *work, struct ksmbd_conn *conn = work->conn; struct smb2_file_stream_info *file_info; char *stream_name, *xattr_list = NULL, *stream_buf; - char *stream_type; struct kstat stat; struct path *path = &fp->filp->f_path; ssize_t xattr_list_len; @@ -4477,26 +4496,6 @@ static void get_file_stream_info(struct ksmbd_work *work, generic_fillattr(FP_INODE(fp), &stat); file_info = (struct smb2_file_stream_info *)rsp->Buffer; - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STREAMS)) { - file_info->NextEntryOffset = 0; - streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, - "::$DATA", - 7, - conn->local_nls, - 0); - - streamlen *= 2; - file_info->StreamNameLength = cpu_to_le32(streamlen); - - file_info->StreamSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.size); - file_info->StreamAllocationSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.size); - nbytes = sizeof(struct smb2_file_stream_info) + streamlen; - goto out; - } - xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); if (xattr_list_len < 0) { goto out; @@ -4520,23 +4519,14 @@ static void get_file_stream_info(struct ksmbd_work *work, STREAM_PREFIX_LEN); streamlen = stream_name_len; - if (fp->stream.type == 2) { - streamlen += 17; - stream_type = "$INDEX_ALLOCATION"; - } else { - streamlen += 5; - stream_type = "$DATA"; - } - - /* plus :: size */ - streamlen += 2; + /* plus : size */ + streamlen += 1; stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); if (!stream_buf) break; streamlen = snprintf(stream_buf, streamlen + 1, - ":%s:%s", &stream_name[XATTR_NAME_STREAM_LEN], - stream_type); + ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); file_info = (struct smb2_file_stream_info *) &rsp->Buffer[nbytes]; @@ -4556,6 +4546,20 @@ static void get_file_stream_info(struct ksmbd_work *work, file_info->NextEntryOffset = cpu_to_le32(next); } + if (nbytes) { + file_info = (struct smb2_file_stream_info *) + &rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + "::$DATA", 7, conn->local_nls, 0); + streamlen *= 2; + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.size); + file_info->StreamAllocationSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.size); + nbytes += sizeof(struct smb2_file_stream_info) + streamlen; + } + /* last entry offset should be 0 */ file_info->NextEntryOffset = 0; out: @@ -5116,7 +5120,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, void *rsp_org) { struct ksmbd_file *fp; - struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer; + struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; struct smb_fattr fattr = {{0}}; struct inode *inode; __u32 secdesclen; @@ -5152,12 +5156,14 @@ static int smb2_get_info_sec(struct ksmbd_work *work, if (S_ISDIR(inode->i_mode)) fattr.cf_dacls = ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); - fattr.ntacl = ksmbd_vfs_get_sd_xattr(fp->filp->f_path.dentry); + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) + ksmbd_vfs_get_sd_xattr(work->conn, fp->filp->f_path.dentry, &ppntsd); - rc = build_sec_desc(pntsd, addition_info, &secdesclen, &fattr); + rc = build_sec_desc(pntsd, ppntsd, addition_info, &secdesclen, &fattr); posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); - kfree(fattr.ntacl); + kfree(ppntsd); ksmbd_fd_put(work, fp); if (rc) return rc; @@ -5450,7 +5456,8 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, rc = ksmbd_vfs_xattr_stream_name(stream_name, &xattr_stream_name, - &xattr_stream_size); + &xattr_stream_size, + s_type); if (rc) goto out; @@ -5609,21 +5616,8 @@ static int set_file_basic_info(struct ksmbd_file *fp, filp = fp->filp; inode = file_inode(filp); - if (file_info->CreationTime) { + if (file_info->CreationTime) fp->create_time = le64_to_cpu(file_info->CreationTime); - if (test_share_config_flag(share, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { - rc = ksmbd_vfs_setxattr(filp->f_path.dentry, - XATTR_NAME_CREATION_TIME, - (void *)&fp->create_time, - CREATIOM_TIME_LEN, 0); - if (rc) { - ksmbd_debug(SMB, - "failed to set creation time\n"); - return -EINVAL; - } - } - } if (file_info->LastAccessTime) { attrs.ia_atime = ksmbd_NTtimeToUnix(file_info->LastAccessTime); @@ -5643,27 +5637,27 @@ static int set_file_basic_info(struct ksmbd_file *fp, } if (file_info->Attributes) { - struct kstat stat; - if (!S_ISDIR(inode->i_mode) && file_info->Attributes == ATTR_DIRECTORY_LE) { ksmbd_err("can't change a file to a directory\n"); return -EINVAL; } - - generic_fillattr(inode, &stat); fp->f_ci->m_fattr = file_info->Attributes; - if (test_share_config_flag(share, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { - rc = ksmbd_vfs_setxattr(filp->f_path.dentry, - XATTR_NAME_FILE_ATTRIBUTE, - (void *)&fp->f_ci->m_fattr, - FILE_ATTRIBUTE_LEN, 0); - if (rc) - ksmbd_debug(SMB, - "failed to store file attribute in EA\n"); - rc = 0; - } + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && + (file_info->CreationTime || file_info->Attributes)) { + struct xattr_dos_attrib da = {0}; + + da.version = 3; + da.create_time = fp->create_time; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + + rc = ksmbd_vfs_set_dos_attrib_xattr(filp->f_path.dentry, &da); + if (rc) + ksmbd_debug(SMB, + "failed to restore file attribute in EA\n"); + rc = 0; } /* @@ -5959,7 +5953,8 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, fp->saccess |= FILE_SHARE_DELETE_LE; - return set_info_sec(fp->filp->f_path.dentry, pntsd, buf_len, false); + return set_info_sec(fp->conn, fp->tcon, fp->filp->f_path.dentry, pntsd, + buf_len, false); } /** @@ -7403,12 +7398,16 @@ static int fsctl_pipe_transceive(struct ksmbd_work *work, uint64_t id, data_buf, le32_to_cpu(req->InputCount)); if (rpc_resp) { - if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { + /* + * set STATUS_SOME_NOT_MAPPED response + * for unknown domain sid. + */ + rsp->hdr.Status = STATUS_SOME_NOT_MAPPED; + } else if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { rsp->hdr.Status = STATUS_NOT_SUPPORTED; goto out; - } - - if (rpc_resp->flags != KSMBD_RPC_OK) { + } else if (rpc_resp->flags != KSMBD_RPC_OK) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; goto out; } @@ -7452,14 +7451,19 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, uint64_t id, if (fp->f_ci->m_fattr != old_fattr && test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { - ret = ksmbd_vfs_setxattr(fp->filp->f_path.dentry, - XATTR_NAME_FILE_ATTRIBUTE, - (void *)&fp->f_ci->m_fattr, - FILE_ATTRIBUTE_LEN, 0); + struct xattr_dos_attrib da; + + ret = ksmbd_vfs_get_dos_attrib_xattr(fp->filp->f_path.dentry, &da); + if (ret <= 0) + goto out; + + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + ret = ksmbd_vfs_set_dos_attrib_xattr(fp->filp->f_path.dentry, &da); if (ret) fp->f_ci->m_fattr = old_fattr; } +out: ksmbd_fd_put(work, fp); return ret; } diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index 1d596b8122a39..3f1152ccffe4f 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -595,16 +595,10 @@ int ksmbd_override_fsids(struct ksmbd_work *work) struct ksmbd_session *sess = work->sess; struct ksmbd_share_config *share = work->tcon->share_conf; struct cred *cred; + struct group_info *gi; unsigned int uid; unsigned int gid; - if (work->saved_cred_level) { - WARN_ON(work->saved_cred == NULL); - work->saved_cred_level++; - validate_process_creds(); - return 0; - } - uid = user_uid(sess->user); gid = user_gid(sess->user); if (share->force_uid != KSMBD_SHARE_INVALID_UID) @@ -618,31 +612,35 @@ int ksmbd_override_fsids(struct ksmbd_work *work) cred->fsuid = make_kuid(current_user_ns(), uid); cred->fsgid = make_kgid(current_user_ns(), gid); + + gi = groups_alloc(0); + if (!gi) { + abort_creds(cred); + return -ENOMEM; + } + set_groups(cred, gi); + put_group_info(gi); + if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) cred->cap_effective = cap_drop_fs_set(cred->cap_effective); + WARN_ON(work->saved_cred != NULL); work->saved_cred = override_creds(cred); if (!work->saved_cred) { abort_creds(cred); return -EINVAL; } - - work->saved_cred_level = 1; return 0; } void ksmbd_revert_fsids(struct ksmbd_work *work) { - work->saved_cred_level--; - WARN_ON(work->saved_cred_level < 0); - if (!work->saved_cred_level) { - const struct cred *cred; - - cred = current_cred(); - revert_creds(work->saved_cred); - put_cred(cred); - work->saved_cred = NULL; - } + const struct cred *cred; + + cred = current_cred(); + revert_creds(work->saved_cred); + put_cred(cred); + work->saved_cred = NULL; } __le32 smb_map_generic_desired_access(__le32 daccess) diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c index cb51c93117f14..4f867a5f44f62 100644 --- a/fs/cifsd/smbacl.c +++ b/fs/cifsd/smbacl.c @@ -9,10 +9,13 @@ #include #include #include + #include "smbacl.h" #include "smb_common.h" #include "server.h" #include "misc.h" +#include "ksmbd_server.h" +#include "mgmt/share_config.h" static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3), @@ -380,7 +383,6 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, struct posix_acl_entry *cf_pace, *cf_pdace; struct posix_acl_state acl_state, default_acl_state; umode_t mode = 0, acl_mode; - struct smb_ace *ace; bool owner_found = false, group_found = false, others_found = false; if (!pdacl) @@ -398,7 +400,6 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, acl_base = (char *)pdacl; acl_size = sizeof(struct smb_acl); - fattr->ntacl->size = le16_to_cpu(pdacl->size); num_aces = le32_to_cpu(pdacl->num_aces); if (num_aces <= 0) @@ -407,7 +408,6 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) return; - fattr->ntacl->num_aces = num_aces; ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); if (!ppace) @@ -427,21 +427,16 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, * Also, if num_aces is 0 i.e. DACL has no ACEs, * user/group/other have no permissions */ - ace = fattr->ntacl->ace; for (i = 0; i < num_aces; ++i) { ppace[i] = (struct smb_ace *) (acl_base + acl_size); acl_base = (char *)ppace[i]; acl_size = le16_to_cpu(ppace[i]->size); - - memcpy(ace, ppace[i], acl_size); - ace->access_req = + ppace[i]->access_req = smb_map_generic_desired_access(ppace[i]->access_req); - ace = (struct smb_ace *)((char *)ace + acl_size); if (!(compare_sids(&(ppace[i]->sid), &sid_unix_NFS_mode))) { fattr->cf_mode = le32_to_cpu(ppace[i]->sid.sub_auth[2]); - fattr->ntacl->type &= ~DACL_PRESENT; break; } else if (!compare_sids(&(ppace[i]->sid), pownersid)) { acl_mode = access_flags_to_mode(fattr, @@ -454,8 +449,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, } owner_found = true; } else if (!compare_sids(&(ppace[i]->sid), pgrpsid) || - le32_to_cpu(ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1]) == - 513) { + ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == + DOMAIN_USER_RID_LE) { acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, ppace[i]->type); acl_mode &= 0070; @@ -507,9 +502,6 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, kfree(ppace); if (owner_found) { - fattr->cf_mode &= ~(0700); - fattr->cf_mode |= mode & 0700; - /* The owner must be set to at least read-only. */ acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid; @@ -523,9 +515,6 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, } if (group_found) { - fattr->cf_mode &= ~(0070); - fattr->cf_mode |= mode & 0070; - acl_state.group.allow = (mode & 0070) >> 3; acl_state.groups->aces[acl_state.groups->n].gid = fattr->cf_gid; @@ -570,19 +559,21 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, free_acl_state(&default_acl_state); } -static void set_posix_acl_entries_dacl(struct smb_acl *pndacl, +static void set_posix_acl_entries_dacl(struct smb_ace *pndace, struct smb_fattr *fattr, u32 *num_aces, u16 *size, u32 nt_aces_num) { struct posix_acl_entry *pace; - struct smb_ace *ntace; struct smb_sid *sid; - int i, j, flags = 0; + struct smb_ace *ntace; + int i, j; - if (!fattr->cf_acls || IS_ERR(fattr->cf_acls)) + if (!fattr->cf_acls) goto posix_default_acl; pace = fattr->cf_acls->a_entries; for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { + int flags = 0; + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); if (!sid) break; @@ -595,7 +586,7 @@ static void set_posix_acl_entries_dacl(struct smb_acl *pndacl, if (!uid) sid_type = SIDUNIX_USER; id_to_sid(uid, sid_type, sid); - } else if (pace->e_tag == ACL_GROUP && !nt_aces_num) { + } else if (pace->e_tag == ACL_GROUP) { gid_t gid; gid = from_kgid(&init_user_ns, pace->e_gid); @@ -606,22 +597,37 @@ static void set_posix_acl_entries_dacl(struct smb_acl *pndacl, kfree(sid); continue; } - - ntace = (struct smb_ace *)pndacl; - for (j = 0; j < *num_aces; j++) { - if (!compare_sids((const struct smb_sid *)sid, &ntace->sid)) + ntace = pndace; + for (j = 0; j < nt_aces_num; j++) { + if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == + sid->sub_auth[sid->num_subauth - 1]) goto pass_same_sid; ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size)); } - if (fattr->cf_dacls) + if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) flags = 0x03; - *size += fill_ace_for_sid( - (struct smb_ace *) ((char *)pndacl + *size), - sid, ACCESS_ALLOWED, flags, pace->e_perm, 0777); + ntace = (struct smb_ace *) ((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, + pace->e_perm, 0777); (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + + if (S_ISDIR(fattr->cf_mode) && + (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { + ntace = (struct smb_ace *) ((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, + 0x03, pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + } + pass_same_sid: kfree(sid); } @@ -630,10 +636,9 @@ static void set_posix_acl_entries_dacl(struct smb_acl *pndacl, return; posix_default_acl: - if (!fattr->cf_dacls || IS_ERR(fattr->cf_dacls)) + if (!fattr->cf_dacls) return; - flags = 0x0b; pace = fattr->cf_dacls->a_entries; for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); @@ -655,107 +660,102 @@ static void set_posix_acl_entries_dacl(struct smb_acl *pndacl, continue; } - ntace = (struct smb_ace *)pndacl; - for (j = 0; j < *num_aces; j++) { - if (!compare_sids((const struct smb_sid *)sid, &ntace->sid)) - goto pass_same_sid; - ntace = (struct smb_ace *)((char *)ntace + - le16_to_cpu(ntace->size)); - } - - *size += fill_ace_for_sid( - (struct smb_ace *) ((char *)pndacl + *size), - sid, ACCESS_ALLOWED, flags, pace->e_perm, 0777); + ntace = (struct smb_ace *) ((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, + pace->e_perm, 0777); (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; kfree(sid); } } -static void set_ntacl_dacl(struct smb_acl *pndacl, const struct smb_sid *pownersid, - const struct smb_sid *pgrpsid, struct smb_fattr *fattr) +static void set_ntacl_dacl(struct smb_acl *pndacl, struct smb_acl *nt_dacl, + const struct smb_sid *pownersid, const struct smb_sid *pgrpsid, + struct smb_fattr *fattr) { - u16 size = 0; - u32 num_aces = 0; - struct smb_acl *pnndacl; - struct smb_ace *ntace; + struct smb_ace *ntace, *pndace; + int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; + unsigned short size = 0; int i; - pnndacl = (struct smb_acl *)((char *)pndacl + sizeof(struct smb_acl)); - - if (fattr->ntacl->num_aces) { - ntace = fattr->ntacl->ace; - for (i = 0; i < fattr->ntacl->num_aces; i++) { - memcpy((char *)pnndacl + size, ntace, le16_to_cpu(ntace->size)); + pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + if (nt_num_aces) { + ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); + for (i = 0; i < nt_num_aces; i++) { + memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size)); size += le16_to_cpu(ntace->size); ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size)); num_aces++; } } - set_posix_acl_entries_dacl(pnndacl, fattr, &num_aces, &size, fattr->ntacl->num_aces); + set_posix_acl_entries_dacl(pndace, fattr, &num_aces, &size, nt_num_aces); pndacl->num_aces = cpu_to_le32(num_aces); pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); } static void set_mode_dacl(struct smb_acl *pndacl, struct smb_fattr *fattr) { - struct smb_acl *pnndacl; - struct smb_ace *pace; - u32 num_aces = 3; + struct smb_ace *pace, *pndace; + u32 num_aces = 0; u16 size = 0, ace_size = 0; uid_t uid; + const struct smb_sid *sid; + + pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); - pnndacl = (struct smb_acl *)((char *)pndacl + sizeof(struct smb_acl)); - pace = (struct smb_ace *)pnndacl; + if (fattr->cf_acls) { + set_posix_acl_entries_dacl(pndace, fattr, &num_aces, &size, num_aces); + goto out; + } /* owner RID */ uid = from_kuid(&init_user_ns, fattr->cf_uid); if (uid) - ace_size = fill_ace_for_sid(pace, &server_conf.domain_sid, - ACCESS_ALLOWED, 0, fattr->cf_mode, 0700); + sid = &server_conf.domain_sid; else - ace_size = fill_ace_for_sid(pace, &sid_unix_users, - ACCESS_ALLOWED, 0, fattr->cf_mode, 0700); + sid = &sid_unix_users; + ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0700); pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; pace->size = cpu_to_le16(ace_size + 4); size += le16_to_cpu(pace->size); - pace = (struct smb_ace *)((char *)pnndacl + size); + pace = (struct smb_ace *)((char *)pndace + size); - /* Unix Group */ + /* Group RID */ ace_size = fill_ace_for_sid(pace, &sid_unix_groups, ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); pace->size = cpu_to_le16(ace_size + 4); size += le16_to_cpu(pace->size); - pace = (struct smb_ace *)((char *)pnndacl + size); - - /* other */ - size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, - fattr->cf_mode, 0007); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 3; if (S_ISDIR(fattr->cf_mode)) { - pace = (struct smb_ace *)((char *)pnndacl + size); + pace = (struct smb_ace *)((char *)pndace + size); /* creator owner */ size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, 0x0b, fattr->cf_mode, 0700); pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; - pace = (struct smb_ace *)((char *)pnndacl + size); + pace = (struct smb_ace *)((char *)pndace + size); /* creator group */ size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, 0x0b, fattr->cf_mode, 0070); - pace = (struct smb_ace *)((char *)pnndacl + size); - - /* other */ - size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, - 0x0b, fattr->cf_mode, 0070); - num_aces = 6; + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 5; } - set_posix_acl_entries_dacl(pnndacl, fattr, &num_aces, &size, num_aces); + /* other */ + size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0007); + +out: pndacl->num_aces = cpu_to_le32(num_aces); pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); } @@ -783,7 +783,7 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, struct smb_acl *dacl_ptr; /* no need for SACL ptr */ char *end_of_acl = ((char *)pntsd) + acl_len; __u32 dacloffset; - int total_ace_size = 0; + int total_ace_size = 0, pntsd_type; if (pntsd == NULL) return -EIO; @@ -803,17 +803,15 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, if (dacloffset && dacl_ptr) total_ace_size = le16_to_cpu(dacl_ptr->size) - sizeof(struct smb_acl); - fattr->ntacl = kzalloc(sizeof(struct smb_ntacl) + - total_ace_size, GFP_KERNEL); - if (!fattr->ntacl) - return -ENOMEM; - if (!(le16_to_cpu(pntsd->type) & DACL_PRESENT)) { + pntsd_type = le16_to_cpu(pntsd->type); + + if (!(pntsd_type & DACL_PRESENT)) { ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); return rc; } - fattr->ntacl->type = DACL_PRESENT; + pntsd->type = cpu_to_le16(DACL_PRESENT); if (pntsd->osidoffset) { rc = parse_sid(owner_sid_ptr, end_of_acl); @@ -845,11 +843,12 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, } } - if ((le16_to_cpu(pntsd->type) & + if ((pntsd_type & (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) - fattr->ntacl->type |= DACL_AUTO_INHERITED; - fattr->ntacl->type |= le16_to_cpu(pntsd->type) & DACL_PROTECTED; + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + if (pntsd_type & DACL_PROTECTED) + pntsd->type |= cpu_to_le16(DACL_PROTECTED); if (dacloffset) { parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, @@ -860,8 +859,8 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, } /* Convert permission bits from mode to equivalent CIFS ACL */ -int build_sec_desc(struct smb_ntsd *pntsd, int addition_info, __u32 *secdesclen, - struct smb_fattr *fattr) +int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, + int addition_info, __u32 *secdesclen, struct smb_fattr *fattr) { int rc = 0; __u32 offset; @@ -894,8 +893,8 @@ int build_sec_desc(struct smb_ntsd *pntsd, int addition_info, __u32 *secdesclen, pntsd->sacloffset = 0; pntsd->revision = cpu_to_le16(1); pntsd->type = cpu_to_le16(SELF_RELATIVE); - if (fattr->ntacl) - pntsd->type |= cpu_to_le16(fattr->ntacl->type); + if (ppntsd) + pntsd->type |= ppntsd->type; if (addition_info & OWNER_SECINFO) { pntsd->osidoffset = cpu_to_le32(offset); @@ -918,14 +917,18 @@ int build_sec_desc(struct smb_ntsd *pntsd, int addition_info, __u32 *secdesclen, dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); dacl_ptr->num_aces = 0; - if (!fattr->ntacl) + if (!ppntsd) set_mode_dacl(dacl_ptr, fattr); - else if (!fattr->ntacl->size) + else if (!ppntsd->dacloffset) goto out; - else - set_ntacl_dacl(dacl_ptr, nowner_sid_ptr, ngroup_sid_ptr, - fattr); + else { + struct smb_acl *ppdacl_ptr; + ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + + le32_to_cpu(ppntsd->dacloffset)); + set_ntacl_dacl(dacl_ptr, ppdacl_ptr, nowner_sid_ptr, + ngroup_sid_ptr, fattr); + } pntsd->dacloffset = cpu_to_le32(offset); offset += le16_to_cpu(dacl_ptr->size); } @@ -947,39 +950,45 @@ static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); } -int smb_inherit_dacl(struct dentry *dentry, unsigned int uid, unsigned int gid) +int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, + unsigned int uid, unsigned int gid) { const struct smb_sid *psid, *creator = NULL; - struct smb_ace *paces, *aces; - struct smb_ntacl *pntacl; + struct smb_ace *parent_aces, *aces; + struct smb_acl *parent_pdacl; + struct smb_ntsd *parent_pntsd = NULL; struct smb_sid owner_sid, group_sid; + struct dentry *parent = dentry->d_parent; int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0; + int rc = -ENOENT, num_aces, dacloffset, pntsd_type, acl_len; char *aces_base; - int rc = -ENOENT; bool is_dir = S_ISDIR(dentry->d_inode->i_mode); - struct dentry *parent = dentry->d_parent; - pntacl = ksmbd_vfs_get_sd_xattr(parent); - if (!pntacl) + acl_len = ksmbd_vfs_get_sd_xattr(conn, parent, &parent_pntsd); + if (acl_len <= 0) return rc; + dacloffset = le32_to_cpu(parent_pntsd->dacloffset); + if (!dacloffset) + goto out; - if (!pntacl->num_aces) - goto free_pntacl; + parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); + num_aces = le32_to_cpu(parent_pdacl->num_aces); + pntsd_type = le16_to_cpu(parent_pntsd->type); - aces_base = kmalloc(sizeof(struct smb_ace) * pntacl->num_aces * 2, - GFP_KERNEL); + aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); if (!aces_base) - goto free_pntacl; + goto out; aces = (struct smb_ace *)aces_base; - paces = pntacl->ace; + parent_aces = (struct smb_ace *)((char *)parent_pdacl + + sizeof(struct smb_acl)); - if (pntacl->type & DACL_AUTO_INHERITED) + if (pntsd_type & DACL_AUTO_INHERITED) inherited_flags = INHERITED_ACE; - for (i = 0; i < pntacl->num_aces; i++) { - flags = paces->flags; - if (!smb_inherit_flags(paces->flags, is_dir)) + for (i = 0; i < num_aces; i++) { + flags = parent_aces->flags; + if (!smb_inherit_flags(flags, is_dir)) goto pass; if (is_dir) { flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE); @@ -990,96 +999,107 @@ int smb_inherit_dacl(struct dentry *dentry, unsigned int uid, unsigned int gid) } else flags = 0; - if (paces->type & DACL_AUTO_INHERITED) - flags |= INHERITED_ACE; - - if (!compare_sids(&creator_owner, &paces->sid)) { + if (!compare_sids(&creator_owner, &parent_aces->sid)) { creator = &creator_owner; id_to_sid(uid, SIDOWNER, &owner_sid); psid = &owner_sid; - } else if (!compare_sids(&creator_group, &paces->sid)) { + } else if (!compare_sids(&creator_group, &parent_aces->sid)) { creator = &creator_group; id_to_sid(gid, SIDUNIX_GROUP, &group_sid); psid = &group_sid; } else { creator = NULL; - psid = &paces->sid; + psid = &parent_aces->sid; } if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { - smb_set_ace(aces, psid, paces->type, inherited_flags, - paces->access_req); + smb_set_ace(aces, psid, parent_aces->type, inherited_flags, + parent_aces->access_req); nt_size += le16_to_cpu(aces->size); ace_cnt++; aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); flags |= INHERIT_ONLY_ACE; psid = creator; - } else if (is_dir && !(paces->flags & NO_PROPAGATE_INHERIT_ACE)) - psid = &paces->sid; + } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) + psid = &parent_aces->sid; - smb_set_ace(aces, psid, paces->type, flags | inherited_flags, - paces->access_req); + smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, + parent_aces->access_req); nt_size += le16_to_cpu(aces->size); aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); ace_cnt++; pass: - paces = (struct smb_ace *)((char *)paces + le16_to_cpu(paces->size)); + parent_aces = + (struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size)); } if (nt_size > 0) { - struct smb_ntacl *ntacl; - - ntacl = kmalloc(sizeof(struct smb_ntacl) + nt_size, - GFP_KERNEL); - if (!ntacl) - return -ENOMEM; - ntacl->num_aces = ace_cnt; - ntacl->type = SELF_RELATIVE | DACL_PRESENT; - if (pntacl->type & DACL_AUTO_INHERITED) - ntacl->type |= DACL_AUTO_INHERITED; - ntacl->size = nt_size; - memcpy(ntacl->ace, aces_base, nt_size); - ksmbd_vfs_set_sd_xattr(dentry, (char *)ntacl); - kfree(ntacl); - rc = 0; - } + struct smb_ntsd *pntsd; + struct smb_acl *pdacl; + struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; + int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; + + if (parent_pntsd->osidoffset) { + powner_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->osidoffset)); + powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); + } + if (parent_pntsd->gsidoffset) { + pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->gsidoffset)); + pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); + } - kfree(aces_base); -free_pntacl: - kfree(pntacl); + pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + + pgroup_sid_size + sizeof(struct smb_acl) + + nt_size, GFP_KERNEL); + if (!pntsd) { + rc = -ENOMEM; + goto out; + } - return rc; -} + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT); + if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + pntsd_size = sizeof(struct smb_ntsd); + pntsd->osidoffset = parent_pntsd->osidoffset; + pntsd->gsidoffset = parent_pntsd->gsidoffset; + pntsd->dacloffset = parent_pntsd->dacloffset; + + if (pntsd->osidoffset) { + struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + memcpy(owner_sid, powner_sid, powner_sid_size); + pntsd_size += powner_sid_size; + } -int smb_inherit_posix_acl(struct inode *inode, struct inode *parent_inode) -{ - struct posix_acl *acls; - struct posix_acl_entry *pace; - int rc, i; + if (pntsd->gsidoffset) { + struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + memcpy(group_sid, pgroup_sid, pgroup_sid_size); + pntsd_size += pgroup_sid_size; + } - acls = ksmbd_vfs_get_acl(parent_inode, ACL_TYPE_DEFAULT); - if (!acls) - return -ENOENT; - pace = acls->a_entries; + if (pntsd->dacloffset) { + struct smb_ace *pace; - for (i = 0; i < acls->a_count; i++, pace++) { - if (pace->e_tag == ACL_MASK) { - pace->e_perm = 0x07; - break; + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + pdacl->revision = cpu_to_le16(2); + pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); + pdacl->num_aces = cpu_to_le32(ace_cnt); + pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + memcpy(pace, aces_base, nt_size); + pntsd_size += sizeof(struct smb_acl) + nt_size; } - } - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); - if (rc < 0) - ksmbd_err("Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); - else if (S_ISDIR(inode->i_mode)) { - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); - if (rc < 0) - ksmbd_err("Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); + ksmbd_vfs_set_sd_xattr(conn, dentry, pntsd, pntsd_size); + kfree(pntsd); + rc = 0; } - posix_acl_release(acls); + + kfree(aces_base); +out: return rc; } @@ -1096,11 +1116,13 @@ bool smb_inherit_flags(int flags, bool is_dir) return false; } -int smb_check_perm_dacl(struct dentry *dentry, __le32 *pdaccess, int uid) +int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, + __le32 *pdaccess, int uid) { - struct smb_ntacl *ntacl; + struct smb_ntsd *pntsd = NULL; + struct smb_acl *pdacl; struct posix_acl *posix_acls; - int rc = 0; + int rc = 0, acl_size; struct smb_sid sid; int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); struct smb_ace *ace; @@ -1111,20 +1133,21 @@ int smb_check_perm_dacl(struct dentry *dentry, __le32 *pdaccess, int uid) unsigned int sid_type = SIDOWNER; ksmbd_debug(SMB, "check permission using windows acl\n"); - ntacl = ksmbd_vfs_get_sd_xattr(dentry); - if (!ntacl) { + acl_size = ksmbd_vfs_get_sd_xattr(conn, dentry, &pntsd); + if (acl_size <= 0 || (pntsd && !pntsd->dacloffset)) { if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) *pdaccess = cpu_to_le32(GENERIC_ALL_FLAGS); return 0; } - if (!ntacl->num_aces) { - if (ntacl->size > 0 && + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + if (!pdacl->num_aces) { + if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) && *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { rc = -EACCES; goto err_out; } - kfree(ntacl); + kfree(pntsd); return 0; } @@ -1132,10 +1155,13 @@ int smb_check_perm_dacl(struct dentry *dentry, __le32 *pdaccess, int uid) granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | DELETE; - for (i = 0; i < ntacl->num_aces; i++) - granted |= le32_to_cpu(ntacl->ace[i].access_req); + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + granted |= le32_to_cpu(ace->access_req); + ace = (struct smb_ace *) ((char *)ace + le16_to_cpu(ace->size)); + } - if (!ntacl->num_aces) + if (!pdacl->num_aces) granted = GENERIC_ALL_FLAGS; } @@ -1143,8 +1169,8 @@ int smb_check_perm_dacl(struct dentry *dentry, __le32 *pdaccess, int uid) sid_type = SIDUNIX_USER; id_to_sid(uid, sid_type, &sid); - ace = ntacl->ace; - for (i = 0; i < ntacl->num_aces; i++) { + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { if (!compare_sids(&sid, &ace->sid) || !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { found = 1; @@ -1162,7 +1188,7 @@ int smb_check_perm_dacl(struct dentry *dentry, __le32 *pdaccess, int uid) granted |= le32_to_cpu(ace->access_req); - if (!ntacl->num_aces) + if (!pdacl->num_aces) granted = GENERIC_ALL_FLAGS; } @@ -1221,134 +1247,13 @@ int smb_check_perm_dacl(struct dentry *dentry, __le32 *pdaccess, int uid) *pdaccess = cpu_to_le32(granted); err_out: - kfree(ntacl); + kfree(pntsd); return rc; } -int store_init_ntacl(struct dentry *dentry) -{ - struct smb_ntacl *ntacl; - struct inode *inode = dentry->d_inode; - struct smb_ace *pace; - char *pace_base; - int num_aces = 3, ace_size; - uid_t uid; - - if (S_ISDIR(inode->i_mode)) - num_aces = 6; - - ntacl = kmalloc(sizeof(struct smb_ntacl) + - num_aces * sizeof(struct smb_ace), GFP_KERNEL); - if (!ntacl) - return -ENOMEM; - - ntacl->num_aces = num_aces; - - pace_base = kmalloc_array(num_aces, sizeof(struct smb_ace), - GFP_KERNEL); - if (!pace_base) - return -ENOMEM; - - /* owner RID */ - pace = (struct smb_ace *)pace_base; - uid = from_kuid(&init_user_ns, inode->i_uid); - if (uid) - ace_size = fill_ace_for_sid(pace, &server_conf.domain_sid, - ACCESS_ALLOWED, 0, inode->i_mode, 0700); - else - ace_size = fill_ace_for_sid(pace, &sid_unix_users, - ACCESS_ALLOWED, 0, inode->i_mode, 0700); - pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); - pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; - /* Increase RID size */ - pace->size = cpu_to_le16(ace_size + 4); - ntacl->size = le16_to_cpu(pace->size); - - /* Unix Groups */ - pace = (struct smb_ace *)((char *)pace + le16_to_cpu(pace->size)); - ace_size = fill_ace_for_sid(pace, &server_conf.domain_sid, - ACCESS_ALLOWED, 0, inode->i_mode, 0070); - pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(513); - /* Increase RID size */ - pace->size = cpu_to_le16(ace_size + 4); - ntacl->size += le16_to_cpu(pace->size); - - /* other */ - pace = (struct smb_ace *)((char *)pace + le16_to_cpu(pace->size)); - ntacl->size += fill_ace_for_sid(pace, &sid_everyone, - ACCESS_ALLOWED, 0, inode->i_mode, 0007); - ntacl->size += le16_to_cpu(pace->size); - - if (S_ISDIR(inode->i_mode)) { - /* creator owner */ - pace = (struct smb_ace *)((char *)pace + le16_to_cpu(pace->size)); - ntacl->size += fill_ace_for_sid(pace, &creator_owner, - ACCESS_ALLOWED, 0x03, inode->i_mode, 0700); - pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; - - /* creator group */ - pace = (struct smb_ace *)((char *)pace + le16_to_cpu(pace->size)); - ntacl->size += fill_ace_for_sid(pace, &creator_group, - ACCESS_ALLOWED, 0x03, inode->i_mode, 0070); - - /* other */ - pace = (struct smb_ace *)((char *)pace + le16_to_cpu(pace->size)); - ntacl->size += fill_ace_for_sid(pace, &sid_everyone, - ACCESS_ALLOWED, 0x03, inode->i_mode, 0007); - } - - memcpy(ntacl->ace, pace_base, ntacl->size); - kfree(pace_base); - ntacl->type = SELF_RELATIVE | DACL_PRESENT; - - ksmbd_vfs_set_sd_xattr(dentry, (char *)ntacl); - kfree(ntacl); - return 0; -} - -int store_init_posix_acl(struct inode *inode) -{ - struct posix_acl_state acl_state; - struct posix_acl *acls; - int rc; - - ksmbd_debug(SMB, "Set posix acls\n"); - init_acl_state(&acl_state, 1); - - /* Set default owner group */ - acl_state.owner.allow = (inode->i_mode & 0700) >> 6; - acl_state.group.allow = (inode->i_mode & 0070) >> 3; - acl_state.other.allow = inode->i_mode & 0007; - acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; - acl_state.users->aces[acl_state.users->n++].perms.allow = - acl_state.owner.allow; - acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; - acl_state.groups->aces[acl_state.groups->n++].perms.allow = - acl_state.group.allow; - acl_state.mask.allow = 0x07; - - acls = ksmbd_vfs_posix_acl_alloc(6, GFP_KERNEL); - if (!acls) - return -ENOMEM; - posix_state_to_acl(&acl_state, acls->a_entries); - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); - if (rc < 0) - ksmbd_err("Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); - else if (S_ISDIR(inode->i_mode)) { - posix_state_to_acl(&acl_state, acls->a_entries); - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); - if (rc < 0) - ksmbd_err("Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); - } - free_acl_state(&acl_state); - posix_acl_release(acls); - return rc; -} - -int set_info_sec(struct dentry *dentry, struct smb_ntsd *pntsd, - int ntsd_len, bool type_check) +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check) { int rc; struct smb_fattr fattr = {{0}}; @@ -1380,17 +1285,19 @@ int set_info_sec(struct dentry *dentry, struct smb_ntsd *pntsd, } /* Check it only calling from SD BUFFER context */ - if (type_check && !(fattr.ntacl->type & DACL_PRESENT)) + if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) goto out; - /* Update WinACL in xattr */ - ksmbd_vfs_remove_sd_xattrs(dentry); - ksmbd_vfs_set_sd_xattr(dentry, (char *)fattr.ntacl); + if (test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + /* Update WinACL in xattr */ + ksmbd_vfs_remove_sd_xattrs(dentry); + ksmbd_vfs_set_sd_xattr(conn, dentry, pntsd, ntsd_len); + } out: posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); - kfree(fattr.ntacl); mark_inode_dirty(inode); return rc; } diff --git a/fs/cifsd/smbacl.h b/fs/cifsd/smbacl.h index 92fbd511fdca7..9b22bff4191f3 100644 --- a/fs/cifsd/smbacl.h +++ b/fs/cifsd/smbacl.h @@ -12,8 +12,7 @@ #include #include -#include "glob.h" -#include "vfs_cache.h" +#include "mgmt/tree_connect.h" #define NUM_AUTHS (6) /* number of authority fields */ #define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ @@ -101,6 +100,10 @@ #define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) #define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ +#define DOMAIN_USER_RID_LE cpu_to_le32(513) + +struct ksmbd_conn; + struct smb_ntsd { __le16 revision; /* revision level */ __le16 type; @@ -134,14 +137,6 @@ struct smb_ace { struct smb_sid sid; /* ie UUID of user or group who gets these perms */ } __packed; -struct smb_ntacl { - unsigned int crc32; - int size; - int type; - int num_aces; - struct smb_ace ace[]; -}; - struct smb_fattr { kuid_t cf_uid; kgid_t cf_gid; @@ -149,7 +144,6 @@ struct smb_fattr { __le32 daccess; struct posix_acl *cf_acls; struct posix_acl *cf_dacls; - struct smb_ntacl *ntacl; }; struct posix_ace_state { @@ -187,21 +181,22 @@ struct posix_acl_state { int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, struct smb_fattr *fattr); -int build_sec_desc(struct smb_ntsd *pntsd, int addition_info, __u32 *secdesclen, - struct smb_fattr *fattr); +int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, + int addition_info, __u32 *secdesclen, struct smb_fattr *fattr); int init_acl_state(struct posix_acl_state *state, int cnt); void free_acl_state(struct posix_acl_state *state); void posix_state_to_acl(struct posix_acl_state *state, struct posix_acl_entry *pace); int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); bool smb_inherit_flags(int flags, bool is_dir); -int smb_inherit_dacl(struct dentry *dentry, unsigned int uid, unsigned int gid); -int smb_inherit_posix_acl(struct inode *inode, struct inode *parent_inode); -int smb_check_perm_dacl(struct dentry *dentry, __le32 *pdaccess, int uid); -int store_init_ntacl(struct dentry *dentry); -int store_init_posix_acl(struct inode *inode); -int set_info_sec(struct dentry *dentry, struct smb_ntsd *pntsd, - int ntsd_len, bool type_check); +int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, + unsigned int uid, unsigned int gid); +int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, + __le32 *pdaccess, int uid); +int store_init_posix_acl(struct inode *inode, umode_t perm); +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check); void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); void ksmbd_init_domain(u32 *sub_auth); #endif /* _SMBACL_H */ diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c index 5f24a1ed5c34d..b91fa265f85d6 100644 --- a/fs/cifsd/transport_ipc.c +++ b/fs/cifsd/transport_ipc.c @@ -14,11 +14,11 @@ #include #include +#include "vfs_cache.h" #include "transport_ipc.h" #include "buffer_pool.h" #include "server.h" #include "smb_common.h" -#include "vfs_cache.h" #include "mgmt/user_config.h" #include "mgmt/share_config.h" @@ -64,7 +64,7 @@ struct ksmbd_ipc_msg { }; #define KSMBD_IPC_MSG_PAYLOAD(m) \ - (void *)(((struct ksmbd_ipc_msg *)(m))->____payload) + ((void *)(((struct ksmbd_ipc_msg *)(m))->____payload)) struct ipc_msg_table_entry { unsigned int handle; diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c index 6a8d2bf442e7e..60ec9b2e0370d 100644 --- a/fs/cifsd/transport_tcp.c +++ b/fs/cifsd/transport_tcp.c @@ -13,16 +13,22 @@ #include "connection.h" #include "transport_tcp.h" +#define IFACE_STATE_DOWN (1 << 0) +#define IFACE_STATE_CONFIGURED (1 << 1) + struct interface { struct task_struct *ksmbd_kthread; struct socket *ksmbd_socket; struct list_head entry; char *name; struct mutex sock_release_lock; + int state; }; static LIST_HEAD(iface_list); +static int bind_additional_ifaces; + struct tcp_transport { struct ksmbd_transport transport; struct socket *sock; @@ -32,6 +38,9 @@ struct tcp_transport { static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; +static void tcp_stop_kthread(struct task_struct *kthread); +static struct interface *alloc_iface(char *ifname); + #define KSMBD_TRANS(t) (&(t)->transport) #define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ struct tcp_transport, transport)) @@ -452,6 +461,7 @@ static int create_socket(struct interface *iface) ksmbd_err("Can't start ksmbd main kthread: %d\n", ret); goto out_error; } + iface->state = IFACE_STATE_CONFIGURED; return 0; @@ -461,23 +471,69 @@ static int create_socket(struct interface *iface) return ret; } -int ksmbd_tcp_init(void) +static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, + void *ptr) { + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct interface *iface; - struct list_head *tmp; - int ret; + int ret, found = 0; + + switch (event) { + case NETDEV_UP: + if (netdev->priv_flags & IFF_BRIDGE_PORT) + return NOTIFY_OK; + + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name)) { + found = 1; + if (iface->state != IFACE_STATE_DOWN) + break; + ret = create_socket(iface); + if (ret) + return NOTIFY_OK; + break; + } + } + if (!found && bind_additional_ifaces) { + iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); + if (!iface) + return NOTIFY_OK; + ret = create_socket(iface); + if (ret) + break; + } + break; + case NETDEV_DOWN: + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name) && + iface->state == IFACE_STATE_CONFIGURED) { + tcp_stop_kthread(iface->ksmbd_kthread); + iface->ksmbd_kthread = NULL; + mutex_lock(&iface->sock_release_lock); + tcp_destroy_socket(iface->ksmbd_socket); + iface->ksmbd_socket = NULL; + mutex_unlock(&iface->sock_release_lock); + + iface->state = IFACE_STATE_DOWN; + break; + } + } + break; + } - if (list_empty(&iface_list)) - return 0; + return NOTIFY_DONE; - list_for_each(tmp, &iface_list) { - iface = list_entry(tmp, struct interface, entry); - ret = create_socket(iface); - if (ret) - break; - } +} - return ret; +static struct notifier_block ksmbd_netdev_notifier = { + .notifier_call = ksmbd_netdev_event, +}; + +int ksmbd_tcp_init(void) +{ + register_netdevice_notifier(&ksmbd_netdev_notifier); + + return 0; } static void tcp_stop_kthread(struct task_struct *kthread) @@ -496,52 +552,33 @@ void ksmbd_tcp_destroy(void) { struct interface *iface, *tmp; + unregister_netdevice_notifier(&ksmbd_netdev_notifier); + list_for_each_entry_safe(iface, tmp, &iface_list, entry) { list_del(&iface->entry); - tcp_stop_kthread(iface->ksmbd_kthread); - mutex_lock(&iface->sock_release_lock); - tcp_destroy_socket(iface->ksmbd_socket); - iface->ksmbd_socket = NULL; - mutex_unlock(&iface->sock_release_lock); kfree(iface->name); ksmbd_free(iface); } } -static bool iface_exists(const char *ifname) -{ - struct net_device *netdev; - bool ret = false; - - rcu_read_lock(); - netdev = dev_get_by_name_rcu(&init_net, ifname); - if (netdev) { - if (!(netdev->flags & IFF_UP)) - ksmbd_err("Device %s is down\n", ifname); - else - ret = true; - } - rcu_read_unlock(); - return ret; -} - -static int alloc_iface(char *ifname) +static struct interface *alloc_iface(char *ifname) { struct interface *iface; if (!ifname) - return -ENOMEM; + return NULL; iface = ksmbd_alloc(sizeof(struct interface)); if (!iface) { kfree(ifname); - return -ENOMEM; + return NULL; } iface->name = ifname; + iface->state = IFACE_STATE_DOWN; list_add(&iface->entry, &iface_list); mutex_init(&iface->sock_release_lock); - return 0; + return iface; } int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) @@ -555,20 +592,17 @@ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) for_each_netdev(&init_net, netdev) { if (netdev->priv_flags & IFF_BRIDGE_PORT) continue; - if (alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) + if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) return -ENOMEM; } rtnl_unlock(); + bind_additional_ifaces = 1; return 0; } while (ifc_list_sz > 0) { - if (iface_exists(ifc_list)) { - if (alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) - return -ENOMEM; - } else { - ksmbd_err("Unknown interface: %s\n", ifc_list); - } + if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) + return -ENOMEM; sz = strlen(ifc_list); if (!sz) @@ -578,6 +612,8 @@ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) ifc_list_sz -= (sz + 1); } + bind_additional_ifaces = 0; + return 0; } diff --git a/fs/cifsd/unicode.c b/fs/cifsd/unicode.c index 1dc7bd141794f..22a4d10a20005 100644 --- a/fs/cifsd/unicode.c +++ b/fs/cifsd/unicode.c @@ -9,9 +9,9 @@ #include #include #include +#include "glob.h" #include "unicode.h" #include "uniupr.h" -#include "glob.h" #include "smb_common.h" /* diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 1dc075a9ba144..38728f10d48e1 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -28,6 +28,8 @@ #include "vfs.h" #include "vfs_cache.h" #include "smbacl.h" +#include "ndr.h" +#include "auth.h" #include "time_wrappers.h" #include "smb_common.h" @@ -196,9 +198,6 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, struct dentry *dentry; int err; - if (ksmbd_override_fsids(work)) - return -ENOMEM; - dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); if (IS_ERR(dentry)) { ksmbd_revert_fsids(work); @@ -219,7 +218,6 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, ksmbd_err("mkdir(%s): creation failed (err:%d)\n", name, err); done_path_create(&path, dentry); - ksmbd_revert_fsids(work); return err; } @@ -507,60 +505,22 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, return err; } -static void __fill_dentry_attributes(struct ksmbd_work *work, - struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat) -{ - /* - * set default value for the case that store dos attributes is not yes - * or that acl is disable in server's filesystem and the config is yes. - */ - if (S_ISDIR(ksmbd_kstat->kstat->mode)) - ksmbd_kstat->file_attributes = ATTR_DIRECTORY_LE; - else - ksmbd_kstat->file_attributes = ATTR_ARCHIVE_LE; - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { - char *file_attribute = NULL; - int rc; - - rc = ksmbd_vfs_getxattr(dentry, - XATTR_NAME_FILE_ATTRIBUTE, - &file_attribute); - if (rc > 0) - ksmbd_kstat->file_attributes = - *((__le32 *)file_attribute); - else - ksmbd_debug(VFS, "fail to fill file attributes.\n"); - ksmbd_free(file_attribute); - } -} - -static void __file_dentry_ctime(struct ksmbd_work *work, - struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat) +/** + * ksmbd_vfs_getattr() - vfs helper for smb getattr + * @work: work + * @fid: file id of open file + * @attrs: inode attributes + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_getattr(struct path *path, struct kstat *stat) { - char *create_time = NULL; - int xattr_len; - u64 time; - - /* - * if "store dos attributes" conf is not yes, - * create time = change time - */ - time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); - ksmbd_kstat->create_time = time; + int err; - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { - xattr_len = ksmbd_vfs_getxattr(dentry, - XATTR_NAME_CREATION_TIME, - &create_time); - if (xattr_len > 0) - ksmbd_kstat->create_time = *((u64 *)create_time); - ksmbd_free(create_time); - } + err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); + if (err) + ksmbd_err("getattr failed, err %d\n", err); + return err; } /** @@ -1388,45 +1348,239 @@ int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry) return err; } -int ksmbd_vfs_set_sd_xattr(struct dentry *dentry, char *sd_data) +static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct inode *inode, + int acl_type) +{ + struct xattr_smb_acl *smb_acl = NULL; + struct posix_acl *posix_acls; + struct posix_acl_entry *pa_entry; + struct xattr_acl_entry *xa_entry; + int i; + + posix_acls = ksmbd_vfs_get_acl(inode, acl_type); + if (!posix_acls) + return NULL; + + smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + + sizeof(struct xattr_acl_entry) * posix_acls->a_count, + GFP_KERNEL); + if (!smb_acl) + goto out; + + smb_acl->count = posix_acls->a_count; + pa_entry = posix_acls->a_entries; + xa_entry = smb_acl->entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++, xa_entry++) { + switch (pa_entry->e_tag) { + case ACL_USER: + xa_entry->type = SMB_ACL_USER; + xa_entry->uid = from_kuid(&init_user_ns, pa_entry->e_uid); + break; + case ACL_USER_OBJ: + xa_entry->type = SMB_ACL_USER_OBJ; + break; + case ACL_GROUP: + xa_entry->type = SMB_ACL_GROUP; + xa_entry->gid = from_kgid(&init_user_ns, pa_entry->e_gid); + break; + case ACL_GROUP_OBJ: + xa_entry->type = SMB_ACL_GROUP_OBJ; + break; + case ACL_OTHER: + xa_entry->type = SMB_ACL_OTHER; + break; + case ACL_MASK: + xa_entry->type = SMB_ACL_MASK; + break; + default: + ksmbd_err("unknown type : 0x%x\n", pa_entry->e_tag); + goto out; + } + + if (pa_entry->e_perm & ACL_READ) + xa_entry->perm |= SMB_ACL_READ; + if (pa_entry->e_perm & ACL_WRITE) + xa_entry->perm |= SMB_ACL_WRITE; + if (pa_entry->e_perm & ACL_EXECUTE) + xa_entry->perm |= SMB_ACL_EXECUTE; + } +out: + posix_acl_release(posix_acls); + return smb_acl; +} + +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, + struct smb_ntsd *pntsd, int len) { - struct smb_ntacl *ntacl = (struct smb_ntacl *)sd_data; int rc; + struct ndr sd_ndr = {0}, acl_ndr = {0}; + struct xattr_ntacl acl = {0}; + struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; + struct inode *inode = dentry->d_inode; + + acl.version = 4; + acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; + acl.current_time = ksmbd_UnixTimeToNT(current_time(dentry->d_inode)); + + memcpy(acl.desc, "posix_acl", 9); + acl.desc_len = 10; + + pntsd->osidoffset = + cpu_to_le32(le32_to_cpu(pntsd->osidoffset) + NDR_NTSD_OFFSETOF); + pntsd->gsidoffset = + cpu_to_le32(le32_to_cpu(pntsd->gsidoffset) + NDR_NTSD_OFFSETOF); + pntsd->dacloffset = + cpu_to_le32(le32_to_cpu(pntsd->dacloffset) + NDR_NTSD_OFFSETOF); + + acl.sd_buf = (char *)pntsd; + acl.sd_size = len; + + rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); + if (rc) { + ksmbd_err("failed to generate hash for ndr acl\n"); + return rc; + } - if (!ntacl) - return 0; + smb_acl = ksmbd_vfs_make_xattr_posix_acl(dentry->d_inode, ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(dentry->d_inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); + if (rc) { + ksmbd_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, + acl.posix_acl_hash); + if (rc) { + ksmbd_err("failed to generate hash for ndr acl\n"); + goto out; + } + + rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); + if (rc) { + ksmbd_err("failed to encode ndr to posix acl\n"); + goto out; + } - ntacl->crc32 = crc32c(0, ntacl->ace, ntacl->size); - rc = ksmbd_vfs_setxattr(dentry, XATTR_NAME_SD, sd_data, - ntacl->size + sizeof(struct smb_ntacl), 0); + rc = ksmbd_vfs_setxattr(dentry, XATTR_NAME_SD, sd_ndr.data, + sd_ndr.offset, 0); if (rc < 0) - ksmbd_err("Failed to store XATTR sd :%d\n", rc); - return 0; + ksmbd_err("Failed to store XATTR ntacl :%d\n", rc); + + kfree(sd_ndr.data); +out: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + return rc; } -struct smb_ntacl *ksmbd_vfs_get_sd_xattr(struct dentry *dentry) +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, + struct smb_ntsd **pntsd) { - struct smb_ntacl *ntacl = NULL; - char *attr = NULL, *sd_data = NULL; int rc; + struct ndr n; - rc = ksmbd_vfs_getxattr(dentry, XATTR_NAME_SD, &attr); + rc = ksmbd_vfs_getxattr(dentry, XATTR_NAME_SD, &n.data); if (rc > 0) { - sd_data = kzalloc(rc, GFP_KERNEL); - if (!sd_data) - return NULL; - - memcpy(sd_data, attr, rc); - ntacl = (struct smb_ntacl *)sd_data; - if (ntacl->crc32 != crc32c(0, ntacl->ace, ntacl->size)) { - ksmbd_vfs_remove_sd_xattrs(dentry); - kfree(sd_data); - ntacl = NULL; + struct inode *inode = dentry->d_inode; + struct ndr acl_ndr = {0}; + struct xattr_ntacl acl; + struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; + __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; + + n.length = rc; + rc = ndr_decode_v4_ntacl(&n, &acl); + if (rc) + return rc; + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); + if (rc) { + ksmbd_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, + cmp_hash); + if (rc) { + ksmbd_err("failed to generate hash for ndr acl\n"); + goto out; + } + + if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { + ksmbd_err("hash value diff\n"); + rc = -EINVAL; + goto out; } + + *pntsd = acl.sd_buf; + (*pntsd)->osidoffset = + cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - NDR_NTSD_OFFSETOF); + (*pntsd)->gsidoffset = + cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - NDR_NTSD_OFFSETOF); + (*pntsd)->dacloffset = + cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - NDR_NTSD_OFFSETOF); + + rc = acl.sd_size; +out: + kfree(n.data); + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); } - ksmbd_free(attr); - return ntacl; + return rc; +} + +int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ndr_encode_dos_attr(&n, da); + if (err) + return err; + + err = ksmbd_vfs_setxattr(dentry, + XATTR_NAME_DOS_ATTRIBUTE, + (void *)n.data, + n.offset, + 0); + if (err) + ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); + kfree(n.data); + + return err; +} + +int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ksmbd_vfs_getxattr(dentry, + XATTR_NAME_DOS_ATTRIBUTE, + (char **)&n.data); + if (err > 0) { + n.length = err; + if (ndr_decode_dos_attr(&n, da)) + err = -EINVAL; + ksmbd_free(n.data); + } else + ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); + + return err; } struct posix_acl *ksmbd_vfs_posix_acl_alloc(int count, gfp_t flags) @@ -1493,9 +1647,35 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, struct ksmbd_kstat *ksmbd_kstat) { + u64 time; + int rc; + generic_fillattr(d_inode(dentry), ksmbd_kstat->kstat); - __file_dentry_ctime(work, dentry, ksmbd_kstat); - __fill_dentry_attributes(work, dentry, ksmbd_kstat); + + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + ksmbd_kstat->create_time = time; + + /* + * set default value for the case that store dos attributes is not yes + * or that acl is disable in server's filesystem and the config is yes. + */ + if (S_ISDIR(ksmbd_kstat->kstat->mode)) + ksmbd_kstat->file_attributes = ATTR_DIRECTORY_LE; + else + ksmbd_kstat->file_attributes = ATTR_ARCHIVE_LE; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + rc = ksmbd_vfs_get_dos_attrib_xattr(dentry, &da); + if (rc > 0) { + ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); + ksmbd_kstat->create_time = da.create_time; + } else + ksmbd_debug(VFS, "fail to load dos attribute.\n"); + } + return 0; } @@ -1527,14 +1707,24 @@ ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, - size_t *xattr_stream_name_size) + size_t *xattr_stream_name_size, + int s_type) { int stream_name_size; char *xattr_stream_name_buf; + char *type; + int type_len; + + if (s_type == DIR_STREAM) + type = ":$INDEX_ALLOCATION"; + else + type = ":$DATA"; + type_len = strlen(type); stream_name_size = strlen(stream_name); *xattr_stream_name_size = stream_name_size + XATTR_NAME_STREAM_LEN + 1; - xattr_stream_name_buf = kmalloc(*xattr_stream_name_size, GFP_KERNEL); + xattr_stream_name_buf = kmalloc(*xattr_stream_name_size + type_len, + GFP_KERNEL); if (!xattr_stream_name_buf) return -ENOMEM; @@ -1542,10 +1732,13 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, XATTR_NAME_STREAM, XATTR_NAME_STREAM_LEN); - if (stream_name_size) + if (stream_name_size) { memcpy(&xattr_stream_name_buf[XATTR_NAME_STREAM_LEN], stream_name, stream_name_size); + } + memcpy(&xattr_stream_name_buf[*xattr_stream_name_size - 1], type, type_len); + *xattr_stream_name_size += type_len; xattr_stream_name_buf[*xattr_stream_name_size - 1] = '\0'; *xattr_stream_name = xattr_stream_name_buf; @@ -1553,30 +1746,6 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, return 0; } -int ksmbd_vfs_xattr_sd(char *sd_data, char **xattr_sd, size_t *xattr_sd_size) -{ - int sd_size; - char *xattr_sd_buf; - struct smb_ntacl *acl = (struct smb_ntacl *)sd_data; - - sd_size = sizeof(struct smb_ace)*acl->num_aces + 4; - *xattr_sd_size = sd_size + XATTR_NAME_SD_LEN + 1; - xattr_sd_buf = kmalloc(*xattr_sd_size, GFP_KERNEL); - if (!xattr_sd_buf) - return -ENOMEM; - - memcpy(xattr_sd_buf, XATTR_NAME_SD, XATTR_NAME_SD_LEN); - - if (sd_size) - memcpy(&xattr_sd_buf[XATTR_NAME_SD_LEN], sd_data, - sd_size); - - xattr_sd_buf[*xattr_sd_size - 1] = '\0'; - *xattr_sd = xattr_sd_buf; - - return 0; -} - static int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, size_t len) @@ -1706,3 +1875,76 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) { locks_delete_block(flock); } + +int ksmbd_vfs_set_init_posix_acl(struct inode *inode) +{ + struct posix_acl_state acl_state; + struct posix_acl *acls; + int rc; + + ksmbd_debug(SMB, "Set posix acls\n"); + init_acl_state(&acl_state, 1); + + /* Set default owner group */ + acl_state.owner.allow = (inode->i_mode & 0700) >> 6; + acl_state.group.allow = (inode->i_mode & 0070) >> 3; + acl_state.other.allow = inode->i_mode & 0007; + acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + acl_state.owner.allow; + acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + acl_state.group.allow; + acl_state.mask.allow = 0x07; + + acls = ksmbd_vfs_posix_acl_alloc(6, GFP_KERNEL); + if (!acls) + return -ENOMEM; + posix_state_to_acl(&acl_state, acls->a_entries); + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + else if (S_ISDIR(inode->i_mode)) { + posix_state_to_acl(&acl_state, acls->a_entries); + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + free_acl_state(&acl_state); + posix_acl_release(acls); + return rc; +} + +int ksmbd_vfs_inherit_posix_acl(struct inode *inode, struct inode *parent_inode) +{ + struct posix_acl *acls; + struct posix_acl_entry *pace; + int rc, i; + + acls = ksmbd_vfs_get_acl(parent_inode, ACL_TYPE_DEFAULT); + if (!acls) + return -ENOENT; + pace = acls->a_entries; + + for (i = 0; i < acls->a_count; i++, pace++) { + if (pace->e_tag == ACL_MASK) { + pace->e_perm = 0x07; + break; + } + } + + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode)) { + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + posix_acl_release(acls); + return rc; +} diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index 6b3371a589596..72682e5a516a7 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -13,31 +13,77 @@ #include #include -/* CREATION TIME XATTR PREFIX */ -#define CREATION_TIME_PREFIX "creation.time." -#define CREATION_TIME_PREFIX_LEN (sizeof(CREATION_TIME_PREFIX) - 1) -#define CREATIOM_TIME_LEN (sizeof(__u64)) -#define XATTR_NAME_CREATION_TIME \ - (XATTR_USER_PREFIX CREATION_TIME_PREFIX) -#define XATTR_NAME_CREATION_TIME_LEN (sizeof(XATTR_NAME_CREATION_TIME) - 1) +#include "smbacl.h" /* STREAM XATTR PREFIX */ -#define STREAM_PREFIX "stream." +#define STREAM_PREFIX "DosStream." #define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) #define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) #define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) -/* FILE ATTRIBUITE XATTR PREFIX */ -#define FILE_ATTRIBUTE_PREFIX "file.attribute." -#define FILE_ATTRIBUTE_PREFIX_LEN (sizeof(FILE_ATTRIBUTE_PREFIX) - 1) -#define FILE_ATTRIBUTE_LEN (sizeof(__u32)) -#define XATTR_NAME_FILE_ATTRIBUTE \ - (XATTR_USER_PREFIX FILE_ATTRIBUTE_PREFIX) -#define XATTR_NAME_FILE_ATTRIBUTE_LEN \ - (sizeof(XATTR_USER_PREFIX FILE_ATTRIBUTE_PREFIX) - 1) +struct xattr_dos_attrib { + __u16 version; + __u32 flags; + __u32 attr; + __u32 ea_size; + __u64 size; + __u64 alloc_size; + __u64 create_time; + __u64 change_time; +}; + +/* DOS ATTRIBUITE XATTR PREFIX */ +#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" +#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) +#define XATTR_NAME_DOS_ATTRIBUTE \ + (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) +#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ + (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) + +#define XATTR_SD_HASH_TYPE_SHA256 0x1 +#define XATTR_SD_HASH_SIZE 64 + +#define SMB_ACL_READ 4 +#define SMB_ACL_WRITE 2 +#define SMB_ACL_EXECUTE 1 + +enum { + SMB_ACL_TAG_INVALID = 0, + SMB_ACL_USER, + SMB_ACL_USER_OBJ, + SMB_ACL_GROUP, + SMB_ACL_GROUP_OBJ, + SMB_ACL_OTHER, + SMB_ACL_MASK +}; + +struct xattr_acl_entry { + int type; + uid_t uid; + gid_t gid; + mode_t perm; +}; + +struct xattr_smb_acl { + int count; + int next; + struct xattr_acl_entry entries[0]; +}; + +struct xattr_ntacl { + __u16 version; + void *sd_buf; + __u32 sd_size; + __u16 hash_type; + __u8 desc[10]; + __u16 desc_len; + __u64 current_time; + __u8 hash[XATTR_SD_HASH_SIZE]; + __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; +}; /* SECURITY DESCRIPTOR XATTR PREFIX */ -#define SD_PREFIX "sd." +#define SD_PREFIX "NTACL" #define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) #define XATTR_NAME_SD \ (XATTR_SECURITY_PREFIX SD_PREFIX) @@ -94,6 +140,7 @@ struct ksmbd_work; struct ksmbd_file; +struct ksmbd_conn; struct ksmbd_dir_info { const char *name; @@ -145,6 +192,7 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, uint64_t fid, uint64_t p_id); int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, const char *newname); +int ksmbd_vfs_getattr(struct path *path, struct kstat *stat); int ksmbd_vfs_symlink(const char *name, const char *symname); int ksmbd_vfs_readlink(struct path *path, char *buf, int lenp); @@ -195,8 +243,8 @@ int ksmbd_vfs_fsetxattr(const char *filename, int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, - size_t *xattr_stream_name_size); -int ksmbd_vfs_xattr_sd(char *sd_data, char **xattr_sd, size_t *xattr_sd_size); + size_t *xattr_stream_name_size, + int s_type); int ksmbd_vfs_truncate_xattr(struct dentry *dentry, int wo_streams); int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name); @@ -236,11 +284,19 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry); int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry); -int ksmbd_vfs_set_sd_xattr(struct dentry *dentry, char *sd_data); -struct smb_ntacl *ksmbd_vfs_get_sd_xattr(struct dentry *dentry); +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, + struct smb_ntsd *pntsd, int len); +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, + struct smb_ntsd **pntsd); +int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, + struct xattr_dos_attrib *da); struct posix_acl *ksmbd_vfs_posix_acl_alloc(int count, gfp_t flags); struct posix_acl *ksmbd_vfs_get_acl(struct inode *inode, int type); int ksmbd_vfs_set_posix_acl(struct inode *inode, int type, struct posix_acl *acl); - +int ksmbd_vfs_set_init_posix_acl(struct inode *inode); +int ksmbd_vfs_inherit_posix_acl(struct inode *inode, + struct inode *parent_inode); #endif /* __KSMBD_VFS_H__ */ diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h index c7e5decceb8a8..ba82ccf13c2e7 100644 --- a/fs/cifsd/vfs_cache.h +++ b/fs/cifsd/vfs_cache.h @@ -6,6 +6,7 @@ #ifndef __VFS_CACHE_H__ #define __VFS_CACHE_H__ +#include #include #include #include @@ -50,7 +51,6 @@ struct ksmbd_lock { struct stream { char *name; - int type; ssize_t size; };