You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
cifsd: add server-side procedures for SMB3
This adds smb3 engine, NTLM/NTLMv2/Kerberos authentication, oplock/lease cache mechanism for cifsd. Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com> Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Acked-by: Ronnie Sahlberg <lsahlber@redhat.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
committed by
Steve French
parent
0626e6641f
commit
e2f34481b2
702
fs/cifsd/asn1.c
Normal file
702
fs/cifsd/asn1.c
Normal file
File diff suppressed because it is too large
Load Diff
29
fs/cifsd/asn1.h
Normal file
29
fs/cifsd/asn1.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
|
||||
* turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
|
||||
*
|
||||
* Copyright (c) 2000 RP Internet (www.rpi.net.au).
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __ASN1_H__
|
||||
#define __ASN1_H__
|
||||
|
||||
int ksmbd_decode_negTokenInit(unsigned char *security_blob,
|
||||
int length,
|
||||
struct ksmbd_conn *conn);
|
||||
|
||||
int ksmbd_decode_negTokenTarg(unsigned char *security_blob,
|
||||
int length,
|
||||
struct ksmbd_conn *conn);
|
||||
|
||||
int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer,
|
||||
u16 *buflen,
|
||||
char *ntlm_blob,
|
||||
int ntlm_blob_len);
|
||||
|
||||
int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer,
|
||||
u16 *buflen,
|
||||
int neg_result);
|
||||
#endif /* __ASN1_H__ */
|
||||
1348
fs/cifsd/auth.c
Normal file
1348
fs/cifsd/auth.c
Normal file
File diff suppressed because it is too large
Load Diff
90
fs/cifsd/auth.h
Normal file
90
fs/cifsd/auth.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __AUTH_H__
|
||||
#define __AUTH_H__
|
||||
|
||||
#include "ntlmssp.h"
|
||||
|
||||
#ifdef CONFIG_SMB_SERVER_KERBEROS5
|
||||
#define AUTH_GSS_LENGTH 96
|
||||
#define AUTH_GSS_PADDING 0
|
||||
#else
|
||||
#define AUTH_GSS_LENGTH 74
|
||||
#define AUTH_GSS_PADDING 6
|
||||
#endif
|
||||
|
||||
#define CIFS_HMAC_MD5_HASH_SIZE (16)
|
||||
#define CIFS_NTHASH_SIZE (16)
|
||||
|
||||
/*
|
||||
* Size of the ntlm client response
|
||||
*/
|
||||
#define CIFS_AUTH_RESP_SIZE 24
|
||||
#define CIFS_SMB1_SIGNATURE_SIZE 8
|
||||
#define CIFS_SMB1_SESSKEY_SIZE 16
|
||||
|
||||
#define KSMBD_AUTH_NTLMSSP 0x0001
|
||||
#define KSMBD_AUTH_KRB5 0x0002
|
||||
#define KSMBD_AUTH_MSKRB5 0x0004
|
||||
#define KSMBD_AUTH_KRB5U2U 0x0008
|
||||
|
||||
struct ksmbd_session;
|
||||
struct ksmbd_conn;
|
||||
struct kvec;
|
||||
|
||||
int ksmbd_crypt_message(struct ksmbd_conn *conn,
|
||||
struct kvec *iov,
|
||||
unsigned int nvec,
|
||||
int enc);
|
||||
|
||||
void ksmbd_copy_gss_neg_header(void *buf);
|
||||
|
||||
int ksmbd_auth_ntlm(struct ksmbd_session *sess,
|
||||
char *pw_buf);
|
||||
|
||||
int ksmbd_auth_ntlmv2(struct ksmbd_session *sess,
|
||||
struct ntlmv2_resp *ntlmv2,
|
||||
int blen,
|
||||
char *domain_name);
|
||||
|
||||
int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
|
||||
int blob_len,
|
||||
struct ksmbd_session *sess);
|
||||
|
||||
int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob,
|
||||
int blob_len,
|
||||
struct ksmbd_session *sess);
|
||||
|
||||
unsigned int
|
||||
ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
|
||||
struct ksmbd_session *sess);
|
||||
|
||||
int ksmbd_krb5_authenticate(struct ksmbd_session *sess,
|
||||
char *in_blob, int in_len,
|
||||
char *out_blob, int *out_len);
|
||||
|
||||
int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn,
|
||||
char *key,
|
||||
struct kvec *iov,
|
||||
int n_vec,
|
||||
char *sig);
|
||||
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn,
|
||||
char *key,
|
||||
struct kvec *iov,
|
||||
int n_vec,
|
||||
char *sig);
|
||||
|
||||
int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess);
|
||||
int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess);
|
||||
int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess);
|
||||
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
|
||||
287
fs/cifsd/crypto_ctx.c
Normal file
287
fs/cifsd/crypto_ctx.c
Normal file
@@ -0,0 +1,287 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "glob.h"
|
||||
#include "crypto_ctx.h"
|
||||
#include "buffer_pool.h"
|
||||
|
||||
struct crypto_ctx_list {
|
||||
spinlock_t ctx_lock;
|
||||
int avail_ctx;
|
||||
struct list_head idle_ctx;
|
||||
wait_queue_head_t ctx_wait;
|
||||
};
|
||||
|
||||
static struct crypto_ctx_list ctx_list;
|
||||
|
||||
static inline void free_aead(struct crypto_aead *aead)
|
||||
{
|
||||
if (aead)
|
||||
crypto_free_aead(aead);
|
||||
}
|
||||
|
||||
static void free_shash(struct shash_desc *shash)
|
||||
{
|
||||
if (shash) {
|
||||
crypto_free_shash(shash->tfm);
|
||||
kfree(shash);
|
||||
}
|
||||
}
|
||||
|
||||
static struct crypto_aead *alloc_aead(int id)
|
||||
{
|
||||
struct crypto_aead *tfm = NULL;
|
||||
|
||||
switch (id) {
|
||||
case CRYPTO_AEAD_AES128_GCM:
|
||||
tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
|
||||
break;
|
||||
case CRYPTO_AEAD_AES128_CCM:
|
||||
tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
|
||||
break;
|
||||
default:
|
||||
ksmbd_err("Does not support encrypt ahead(id : %d)\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (IS_ERR(tfm)) {
|
||||
ksmbd_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tfm;
|
||||
}
|
||||
|
||||
static struct shash_desc *alloc_shash_desc(int id)
|
||||
{
|
||||
struct crypto_shash *tfm = NULL;
|
||||
struct shash_desc *shash;
|
||||
|
||||
switch (id) {
|
||||
case CRYPTO_SHASH_HMACMD5:
|
||||
tfm = crypto_alloc_shash("hmac(md5)", 0, 0);
|
||||
break;
|
||||
case CRYPTO_SHASH_HMACSHA256:
|
||||
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
|
||||
break;
|
||||
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;
|
||||
case CRYPTO_SHASH_MD4:
|
||||
tfm = crypto_alloc_shash("md4", 0, 0);
|
||||
break;
|
||||
case CRYPTO_SHASH_MD5:
|
||||
tfm = crypto_alloc_shash("md5", 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_ERR(tfm))
|
||||
return NULL;
|
||||
|
||||
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
|
||||
GFP_KERNEL);
|
||||
if (!shash)
|
||||
crypto_free_shash(tfm);
|
||||
else
|
||||
shash->tfm = tfm;
|
||||
return shash;
|
||||
}
|
||||
|
||||
static struct ksmbd_crypto_ctx *ctx_alloc(void)
|
||||
{
|
||||
return ksmbd_alloc(sizeof(struct ksmbd_crypto_ctx));
|
||||
}
|
||||
|
||||
static void ctx_free(struct ksmbd_crypto_ctx *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CRYPTO_SHASH_MAX; i++)
|
||||
free_shash(ctx->desc[i]);
|
||||
for (i = 0; i < CRYPTO_AEAD_MAX; i++)
|
||||
free_aead(ctx->ccmaes[i]);
|
||||
ksmbd_free(ctx);
|
||||
}
|
||||
|
||||
static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
while (1) {
|
||||
spin_lock(&ctx_list.ctx_lock);
|
||||
if (!list_empty(&ctx_list.idle_ctx)) {
|
||||
ctx = list_entry(ctx_list.idle_ctx.next,
|
||||
struct ksmbd_crypto_ctx,
|
||||
list);
|
||||
list_del(&ctx->list);
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
if (ctx_list.avail_ctx > num_online_cpus()) {
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
wait_event(ctx_list.ctx_wait,
|
||||
!list_empty(&ctx_list.idle_ctx));
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx_list.avail_ctx++;
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
|
||||
ctx = ctx_alloc();
|
||||
if (!ctx) {
|
||||
spin_lock(&ctx_list.ctx_lock);
|
||||
ctx_list.avail_ctx--;
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
wait_event(ctx_list.ctx_wait,
|
||||
!list_empty(&ctx_list.idle_ctx));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
spin_lock(&ctx_list.ctx_lock);
|
||||
if (ctx_list.avail_ctx <= num_online_cpus()) {
|
||||
list_add(&ctx->list, &ctx_list.idle_ctx);
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
wake_up(&ctx_list.ctx_wait);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx_list.avail_ctx--;
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
ctx_free(ctx);
|
||||
}
|
||||
|
||||
static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
if (id >= CRYPTO_SHASH_MAX)
|
||||
return NULL;
|
||||
|
||||
ctx = ksmbd_find_crypto_ctx();
|
||||
if (ctx->desc[id])
|
||||
return ctx;
|
||||
|
||||
ctx->desc[id] = alloc_shash_desc(id);
|
||||
if (ctx->desc[id])
|
||||
return ctx;
|
||||
ksmbd_release_crypto_ctx(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD4);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD5);
|
||||
}
|
||||
|
||||
static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
if (id >= CRYPTO_AEAD_MAX)
|
||||
return NULL;
|
||||
|
||||
ctx = ksmbd_find_crypto_ctx();
|
||||
if (ctx->ccmaes[id])
|
||||
return ctx;
|
||||
|
||||
ctx->ccmaes[id] = alloc_aead(id);
|
||||
if (ctx->ccmaes[id])
|
||||
return ctx;
|
||||
ksmbd_release_crypto_ctx(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void)
|
||||
{
|
||||
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES128_GCM);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void)
|
||||
{
|
||||
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES128_CCM);
|
||||
}
|
||||
|
||||
void ksmbd_crypto_destroy(void)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
while (!list_empty(&ctx_list.idle_ctx)) {
|
||||
ctx = list_entry(ctx_list.idle_ctx.next,
|
||||
struct ksmbd_crypto_ctx,
|
||||
list);
|
||||
list_del(&ctx->list);
|
||||
ctx_free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int ksmbd_crypto_create(void)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
spin_lock_init(&ctx_list.ctx_lock);
|
||||
INIT_LIST_HEAD(&ctx_list.idle_ctx);
|
||||
init_waitqueue_head(&ctx_list.ctx_wait);
|
||||
ctx_list.avail_ctx = 1;
|
||||
|
||||
ctx = ctx_alloc();
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
list_add(&ctx->list, &ctx_list.idle_ctx);
|
||||
return 0;
|
||||
}
|
||||
77
fs/cifsd/crypto_ctx.h
Normal file
77
fs/cifsd/crypto_ctx.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __CRYPTO_CTX_H__
|
||||
#define __CRYPTO_CTX_H__
|
||||
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/aead.h>
|
||||
|
||||
enum {
|
||||
CRYPTO_SHASH_HMACMD5 = 0,
|
||||
CRYPTO_SHASH_HMACSHA256,
|
||||
CRYPTO_SHASH_CMACAES,
|
||||
CRYPTO_SHASH_SHA256,
|
||||
CRYPTO_SHASH_SHA512,
|
||||
CRYPTO_SHASH_MD4,
|
||||
CRYPTO_SHASH_MD5,
|
||||
CRYPTO_SHASH_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
CRYPTO_AEAD_AES128_GCM = 16,
|
||||
CRYPTO_AEAD_AES128_CCM,
|
||||
CRYPTO_AEAD_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
CRYPTO_BLK_ECBDES = 32,
|
||||
CRYPTO_BLK_MAX,
|
||||
};
|
||||
|
||||
struct ksmbd_crypto_ctx {
|
||||
struct list_head list;
|
||||
|
||||
struct shash_desc *desc[CRYPTO_SHASH_MAX];
|
||||
struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX];
|
||||
};
|
||||
|
||||
#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])
|
||||
|
||||
#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm)
|
||||
#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)
|
||||
|
||||
#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES128_GCM])
|
||||
#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES128_CCM])
|
||||
|
||||
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx);
|
||||
|
||||
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);
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
|
||||
|
||||
void ksmbd_crypto_destroy(void);
|
||||
int ksmbd_crypto_create(void);
|
||||
|
||||
#endif /* __CRYPTO_CTX_H__ */
|
||||
69
fs/cifsd/mgmt/ksmbd_ida.c
Normal file
69
fs/cifsd/mgmt/ksmbd_ida.c
Normal file
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include "ksmbd_ida.h"
|
||||
|
||||
struct ksmbd_ida *ksmbd_ida_alloc(void)
|
||||
{
|
||||
struct ksmbd_ida *ida;
|
||||
|
||||
ida = kmalloc(sizeof(struct ksmbd_ida), GFP_KERNEL);
|
||||
if (!ida)
|
||||
return NULL;
|
||||
|
||||
ida_init(&ida->map);
|
||||
return ida;
|
||||
}
|
||||
|
||||
void ksmbd_ida_free(struct ksmbd_ida *ida)
|
||||
{
|
||||
if (!ida)
|
||||
return;
|
||||
|
||||
ida_destroy(&ida->map);
|
||||
kfree(ida);
|
||||
}
|
||||
|
||||
static inline int __acquire_id(struct ksmbd_ida *ida, int from, int to)
|
||||
{
|
||||
return ida_simple_get(&ida->map, from, to, GFP_KERNEL);
|
||||
}
|
||||
|
||||
int ksmbd_acquire_smb2_tid(struct ksmbd_ida *ida)
|
||||
{
|
||||
int id;
|
||||
|
||||
do {
|
||||
id = __acquire_id(ida, 0, 0);
|
||||
} while (id == 0xFFFF);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int ksmbd_acquire_smb2_uid(struct ksmbd_ida *ida)
|
||||
{
|
||||
int id;
|
||||
|
||||
do {
|
||||
id = __acquire_id(ida, 1, 0);
|
||||
} while (id == 0xFFFE);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int ksmbd_acquire_async_msg_id(struct ksmbd_ida *ida)
|
||||
{
|
||||
return __acquire_id(ida, 1, 0);
|
||||
}
|
||||
|
||||
int ksmbd_acquire_id(struct ksmbd_ida *ida)
|
||||
{
|
||||
return __acquire_id(ida, 0, 0);
|
||||
}
|
||||
|
||||
void ksmbd_release_id(struct ksmbd_ida *ida, int id)
|
||||
{
|
||||
ida_simple_remove(&ida->map, id);
|
||||
}
|
||||
41
fs/cifsd/mgmt/ksmbd_ida.h
Normal file
41
fs/cifsd/mgmt/ksmbd_ida.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_IDA_MANAGEMENT_H__
|
||||
#define __KSMBD_IDA_MANAGEMENT_H__
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
struct ksmbd_ida {
|
||||
struct ida map;
|
||||
};
|
||||
|
||||
struct ksmbd_ida *ksmbd_ida_alloc(void);
|
||||
void ksmbd_ida_free(struct ksmbd_ida *ida);
|
||||
|
||||
/*
|
||||
* 2.2.1.6.7 TID Generation
|
||||
* The value 0xFFFF MUST NOT be used as a valid TID. All other
|
||||
* possible values for TID, including zero (0x0000), are valid.
|
||||
* The value 0xFFFF is used to specify all TIDs or no TID,
|
||||
* depending upon the context in which it is used.
|
||||
*/
|
||||
int ksmbd_acquire_smb2_tid(struct ksmbd_ida *ida);
|
||||
|
||||
/*
|
||||
* 2.2.1.6.8 UID Generation
|
||||
* The value 0xFFFE was declared reserved in the LAN Manager 1.0
|
||||
* documentation, so a value of 0xFFFE SHOULD NOT be used as a
|
||||
* valid UID.<21> All other possible values for a UID, excluding
|
||||
* zero (0x0000), are valid.
|
||||
*/
|
||||
int ksmbd_acquire_smb2_uid(struct ksmbd_ida *ida);
|
||||
int ksmbd_acquire_async_msg_id(struct ksmbd_ida *ida);
|
||||
|
||||
int ksmbd_acquire_id(struct ksmbd_ida *ida);
|
||||
|
||||
void ksmbd_release_id(struct ksmbd_ida *ida, int id);
|
||||
#endif /* __KSMBD_IDA_MANAGEMENT_H__ */
|
||||
239
fs/cifsd/mgmt/share_config.c
Normal file
239
fs/cifsd/mgmt/share_config.c
Normal file
@@ -0,0 +1,239 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "share_config.h"
|
||||
#include "user_config.h"
|
||||
#include "user_session.h"
|
||||
#include "../buffer_pool.h"
|
||||
#include "../transport_ipc.h"
|
||||
#include "../ksmbd_server.h" /* FIXME */
|
||||
|
||||
#define SHARE_HASH_BITS 3
|
||||
static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
|
||||
static DECLARE_RWSEM(shares_table_lock);
|
||||
|
||||
struct ksmbd_veto_pattern {
|
||||
char *pattern;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static unsigned int share_name_hash(char *name)
|
||||
{
|
||||
return jhash(name, strlen(name), 0);
|
||||
}
|
||||
|
||||
static void kill_share(struct ksmbd_share_config *share)
|
||||
{
|
||||
while (!list_empty(&share->veto_list)) {
|
||||
struct ksmbd_veto_pattern *p;
|
||||
|
||||
p = list_entry(share->veto_list.next,
|
||||
struct ksmbd_veto_pattern,
|
||||
list);
|
||||
list_del(&p->list);
|
||||
kfree(p->pattern);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
if (share->path)
|
||||
path_put(&share->vfs_path);
|
||||
kfree(share->name);
|
||||
kfree(share->path);
|
||||
kfree(share);
|
||||
}
|
||||
|
||||
void __ksmbd_share_config_put(struct ksmbd_share_config *share)
|
||||
{
|
||||
down_write(&shares_table_lock);
|
||||
hash_del(&share->hlist);
|
||||
up_write(&shares_table_lock);
|
||||
|
||||
kill_share(share);
|
||||
}
|
||||
|
||||
static struct ksmbd_share_config *
|
||||
__get_share_config(struct ksmbd_share_config *share)
|
||||
{
|
||||
if (!atomic_inc_not_zero(&share->refcount))
|
||||
return NULL;
|
||||
return share;
|
||||
}
|
||||
|
||||
static struct ksmbd_share_config *__share_lookup(char *name)
|
||||
{
|
||||
struct ksmbd_share_config *share;
|
||||
unsigned int key = share_name_hash(name);
|
||||
|
||||
hash_for_each_possible(shares_table, share, hlist, key) {
|
||||
if (!strcmp(name, share->name))
|
||||
return share;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int parse_veto_list(struct ksmbd_share_config *share,
|
||||
char *veto_list,
|
||||
int veto_list_sz)
|
||||
{
|
||||
int sz = 0;
|
||||
|
||||
if (!veto_list_sz)
|
||||
return 0;
|
||||
|
||||
while (veto_list_sz > 0) {
|
||||
struct ksmbd_veto_pattern *p;
|
||||
|
||||
p = ksmbd_alloc(sizeof(struct ksmbd_veto_pattern));
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
sz = strlen(veto_list);
|
||||
if (!sz)
|
||||
break;
|
||||
|
||||
p->pattern = kstrdup(veto_list, GFP_KERNEL);
|
||||
if (!p->pattern) {
|
||||
ksmbd_free(p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
list_add(&p->list, &share->veto_list);
|
||||
|
||||
veto_list += sz + 1;
|
||||
veto_list_sz -= (sz + 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ksmbd_share_config *share_config_request(char *name)
|
||||
{
|
||||
struct ksmbd_share_config_response *resp;
|
||||
struct ksmbd_share_config *share = NULL;
|
||||
struct ksmbd_share_config *lookup;
|
||||
int ret;
|
||||
|
||||
resp = ksmbd_ipc_share_config_request(name);
|
||||
if (!resp)
|
||||
return NULL;
|
||||
|
||||
if (resp->flags == KSMBD_SHARE_FLAG_INVALID)
|
||||
goto out;
|
||||
|
||||
share = ksmbd_alloc(sizeof(struct ksmbd_share_config));
|
||||
if (!share)
|
||||
goto out;
|
||||
|
||||
share->flags = resp->flags;
|
||||
atomic_set(&share->refcount, 1);
|
||||
INIT_LIST_HEAD(&share->veto_list);
|
||||
share->name = kstrdup(name, GFP_KERNEL);
|
||||
|
||||
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
|
||||
share->path = kstrdup(KSMBD_SHARE_CONFIG_PATH(resp),
|
||||
GFP_KERNEL);
|
||||
if (share->path)
|
||||
share->path_sz = strlen(share->path);
|
||||
share->create_mask = resp->create_mask;
|
||||
share->directory_mask = resp->directory_mask;
|
||||
share->force_create_mode = resp->force_create_mode;
|
||||
share->force_directory_mode = resp->force_directory_mode;
|
||||
share->force_uid = resp->force_uid;
|
||||
share->force_gid = resp->force_gid;
|
||||
ret = parse_veto_list(share,
|
||||
KSMBD_SHARE_CONFIG_VETO_LIST(resp),
|
||||
resp->veto_list_sz);
|
||||
if (!ret && share->path) {
|
||||
ret = kern_path(share->path, 0, &share->vfs_path);
|
||||
if (ret) {
|
||||
ksmbd_debug(SMB, "failed to access '%s'\n",
|
||||
share->path);
|
||||
/* Avoid put_path() */
|
||||
kfree(share->path);
|
||||
share->path = NULL;
|
||||
}
|
||||
}
|
||||
if (ret || !share->name) {
|
||||
kill_share(share);
|
||||
share = NULL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
down_write(&shares_table_lock);
|
||||
lookup = __share_lookup(name);
|
||||
if (lookup)
|
||||
lookup = __get_share_config(lookup);
|
||||
if (!lookup) {
|
||||
hash_add(shares_table, &share->hlist, share_name_hash(name));
|
||||
} else {
|
||||
kill_share(share);
|
||||
share = lookup;
|
||||
}
|
||||
up_write(&shares_table_lock);
|
||||
|
||||
out:
|
||||
ksmbd_free(resp);
|
||||
return share;
|
||||
}
|
||||
|
||||
static void strtolower(char *share_name)
|
||||
{
|
||||
while (*share_name) {
|
||||
*share_name = tolower(*share_name);
|
||||
share_name++;
|
||||
}
|
||||
}
|
||||
|
||||
struct ksmbd_share_config *ksmbd_share_config_get(char *name)
|
||||
{
|
||||
struct ksmbd_share_config *share;
|
||||
|
||||
strtolower(name);
|
||||
|
||||
down_read(&shares_table_lock);
|
||||
share = __share_lookup(name);
|
||||
if (share)
|
||||
share = __get_share_config(share);
|
||||
up_read(&shares_table_lock);
|
||||
|
||||
if (share)
|
||||
return share;
|
||||
return share_config_request(name);
|
||||
}
|
||||
|
||||
bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
|
||||
const char *filename)
|
||||
{
|
||||
struct ksmbd_veto_pattern *p;
|
||||
|
||||
list_for_each_entry(p, &share->veto_list, list) {
|
||||
if (match_wildcard(p->pattern, filename))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ksmbd_share_configs_cleanup(void)
|
||||
{
|
||||
struct ksmbd_share_config *share;
|
||||
struct hlist_node *tmp;
|
||||
int i;
|
||||
|
||||
down_write(&shares_table_lock);
|
||||
hash_for_each_safe(shares_table, i, tmp, share, hlist) {
|
||||
hash_del(&share->hlist);
|
||||
kill_share(share);
|
||||
}
|
||||
up_write(&shares_table_lock);
|
||||
}
|
||||
83
fs/cifsd/mgmt/share_config.h
Normal file
83
fs/cifsd/mgmt/share_config.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __SHARE_CONFIG_MANAGEMENT_H__
|
||||
#define __SHARE_CONFIG_MANAGEMENT_H__
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/path.h>
|
||||
|
||||
#include "../glob.h" /* FIXME */
|
||||
|
||||
struct ksmbd_share_config {
|
||||
char *name;
|
||||
char *path;
|
||||
|
||||
unsigned int path_sz;
|
||||
unsigned int flags;
|
||||
struct list_head veto_list;
|
||||
|
||||
struct path vfs_path;
|
||||
|
||||
atomic_t refcount;
|
||||
struct hlist_node hlist;
|
||||
unsigned short create_mask;
|
||||
unsigned short directory_mask;
|
||||
unsigned short force_create_mode;
|
||||
unsigned short force_directory_mode;
|
||||
unsigned short force_uid;
|
||||
unsigned short force_gid;
|
||||
};
|
||||
|
||||
#define KSMBD_SHARE_INVALID_UID ((__u16)-1)
|
||||
#define KSMBD_SHARE_INVALID_GID ((__u16)-1)
|
||||
|
||||
static inline int share_config_create_mode(struct ksmbd_share_config *share,
|
||||
umode_t posix_mode)
|
||||
{
|
||||
if (!share->force_create_mode) {
|
||||
if (!posix_mode)
|
||||
return share->create_mask;
|
||||
else
|
||||
return posix_mode & share->create_mask;
|
||||
}
|
||||
return share->force_create_mode & share->create_mask;
|
||||
}
|
||||
|
||||
static inline int share_config_directory_mode(struct ksmbd_share_config *share,
|
||||
umode_t posix_mode)
|
||||
{
|
||||
if (!share->force_directory_mode) {
|
||||
if (!posix_mode)
|
||||
return share->directory_mask;
|
||||
else
|
||||
return posix_mode & share->directory_mask;
|
||||
}
|
||||
|
||||
return share->force_directory_mode & share->directory_mask;
|
||||
}
|
||||
|
||||
static inline int test_share_config_flag(struct ksmbd_share_config *share,
|
||||
int flag)
|
||||
{
|
||||
return share->flags & flag;
|
||||
}
|
||||
|
||||
extern void __ksmbd_share_config_put(struct ksmbd_share_config *share);
|
||||
|
||||
static inline void ksmbd_share_config_put(struct ksmbd_share_config *share)
|
||||
{
|
||||
if (!atomic_dec_and_test(&share->refcount))
|
||||
return;
|
||||
__ksmbd_share_config_put(share);
|
||||
}
|
||||
|
||||
struct ksmbd_share_config *ksmbd_share_config_get(char *name);
|
||||
bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
|
||||
const char *filename);
|
||||
void ksmbd_share_configs_cleanup(void);
|
||||
|
||||
#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */
|
||||
129
fs/cifsd/mgmt/tree_connect.c
Normal file
129
fs/cifsd/mgmt/tree_connect.c
Normal file
@@ -0,0 +1,129 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "../ksmbd_server.h" /* FIXME */
|
||||
#include "../buffer_pool.h"
|
||||
#include "../transport_ipc.h"
|
||||
#include "../connection.h"
|
||||
|
||||
#include "tree_connect.h"
|
||||
#include "user_config.h"
|
||||
#include "share_config.h"
|
||||
#include "user_session.h"
|
||||
|
||||
struct ksmbd_tree_conn_status
|
||||
ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name)
|
||||
{
|
||||
struct ksmbd_tree_conn_status status = {-EINVAL, NULL};
|
||||
struct ksmbd_tree_connect_response *resp = NULL;
|
||||
struct ksmbd_share_config *sc;
|
||||
struct ksmbd_tree_connect *tree_conn = NULL;
|
||||
struct sockaddr *peer_addr;
|
||||
|
||||
sc = ksmbd_share_config_get(share_name);
|
||||
if (!sc)
|
||||
return status;
|
||||
|
||||
tree_conn = ksmbd_alloc(sizeof(struct ksmbd_tree_connect));
|
||||
if (!tree_conn) {
|
||||
status.ret = -ENOMEM;
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
tree_conn->id = ksmbd_acquire_tree_conn_id(sess);
|
||||
if (tree_conn->id < 0) {
|
||||
status.ret = -EINVAL;
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
peer_addr = KSMBD_TCP_PEER_SOCKADDR(sess->conn);
|
||||
resp = ksmbd_ipc_tree_connect_request(sess,
|
||||
sc,
|
||||
tree_conn,
|
||||
peer_addr);
|
||||
if (!resp) {
|
||||
status.ret = -EINVAL;
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
status.ret = resp->status;
|
||||
if (status.ret != KSMBD_TREE_CONN_STATUS_OK)
|
||||
goto out_error;
|
||||
|
||||
tree_conn->flags = resp->connection_flags;
|
||||
tree_conn->user = sess->user;
|
||||
tree_conn->share_conf = sc;
|
||||
status.tree_conn = tree_conn;
|
||||
|
||||
list_add(&tree_conn->list, &sess->tree_conn_list);
|
||||
|
||||
ksmbd_free(resp);
|
||||
return status;
|
||||
|
||||
out_error:
|
||||
if (tree_conn)
|
||||
ksmbd_release_tree_conn_id(sess, tree_conn->id);
|
||||
ksmbd_share_config_put(sc);
|
||||
ksmbd_free(tree_conn);
|
||||
ksmbd_free(resp);
|
||||
return status;
|
||||
}
|
||||
|
||||
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
|
||||
struct ksmbd_tree_connect *tree_conn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
|
||||
ksmbd_release_tree_conn_id(sess, tree_conn->id);
|
||||
list_del(&tree_conn->list);
|
||||
ksmbd_share_config_put(tree_conn->share_conf);
|
||||
ksmbd_free(tree_conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
|
||||
unsigned int id)
|
||||
{
|
||||
struct ksmbd_tree_connect *tree_conn;
|
||||
struct list_head *tmp;
|
||||
|
||||
list_for_each(tmp, &sess->tree_conn_list) {
|
||||
tree_conn = list_entry(tmp, struct ksmbd_tree_connect, list);
|
||||
if (tree_conn->id == id)
|
||||
return tree_conn;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
|
||||
unsigned int id)
|
||||
{
|
||||
struct ksmbd_tree_connect *tc;
|
||||
|
||||
tc = ksmbd_tree_conn_lookup(sess, id);
|
||||
if (tc)
|
||||
return tc->share_conf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (!list_empty(&sess->tree_conn_list)) {
|
||||
struct ksmbd_tree_connect *tc;
|
||||
|
||||
tc = list_entry(sess->tree_conn_list.next,
|
||||
struct ksmbd_tree_connect,
|
||||
list);
|
||||
ret |= ksmbd_tree_conn_disconnect(sess, tc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
56
fs/cifsd/mgmt/tree_connect.h
Normal file
56
fs/cifsd/mgmt/tree_connect.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __TREE_CONNECT_MANAGEMENT_H__
|
||||
#define __TREE_CONNECT_MANAGEMENT_H__
|
||||
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "../ksmbd_server.h" /* FIXME */
|
||||
|
||||
struct ksmbd_share_config;
|
||||
struct ksmbd_user;
|
||||
|
||||
struct ksmbd_tree_connect {
|
||||
int id;
|
||||
|
||||
unsigned int flags;
|
||||
struct ksmbd_share_config *share_conf;
|
||||
struct ksmbd_user *user;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
int maximal_access;
|
||||
bool posix_extensions;
|
||||
};
|
||||
|
||||
struct ksmbd_tree_conn_status {
|
||||
unsigned int ret;
|
||||
struct ksmbd_tree_connect *tree_conn;
|
||||
};
|
||||
|
||||
static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn,
|
||||
int flag)
|
||||
{
|
||||
return tree_conn->flags & flag;
|
||||
}
|
||||
|
||||
struct ksmbd_session;
|
||||
|
||||
struct ksmbd_tree_conn_status
|
||||
ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name);
|
||||
|
||||
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
|
||||
struct ksmbd_tree_connect *tree_conn);
|
||||
|
||||
struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
|
||||
unsigned int id);
|
||||
|
||||
struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
|
||||
unsigned int id);
|
||||
|
||||
int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess);
|
||||
|
||||
#endif /* __TREE_CONNECT_MANAGEMENT_H__ */
|
||||
70
fs/cifsd/mgmt/user_config.c
Normal file
70
fs/cifsd/mgmt/user_config.c
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "user_config.h"
|
||||
#include "../buffer_pool.h"
|
||||
#include "../transport_ipc.h"
|
||||
#include "../ksmbd_server.h" /* FIXME */
|
||||
|
||||
struct ksmbd_user *ksmbd_login_user(const char *account)
|
||||
{
|
||||
struct ksmbd_login_response *resp;
|
||||
struct ksmbd_user *user = NULL;
|
||||
|
||||
resp = ksmbd_ipc_login_request(account);
|
||||
if (!resp)
|
||||
return NULL;
|
||||
|
||||
if (!(resp->status & KSMBD_USER_FLAG_OK))
|
||||
goto out;
|
||||
|
||||
user = ksmbd_alloc_user(resp);
|
||||
out:
|
||||
ksmbd_free(resp);
|
||||
return user;
|
||||
}
|
||||
|
||||
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp)
|
||||
{
|
||||
struct ksmbd_user *user = NULL;
|
||||
|
||||
user = ksmbd_alloc(sizeof(struct ksmbd_user));
|
||||
if (!user)
|
||||
return NULL;
|
||||
|
||||
user->name = kstrdup(resp->account, GFP_KERNEL);
|
||||
user->flags = resp->status;
|
||||
user->gid = resp->gid;
|
||||
user->uid = resp->uid;
|
||||
user->passkey_sz = resp->hash_sz;
|
||||
user->passkey = ksmbd_alloc(resp->hash_sz);
|
||||
if (user->passkey)
|
||||
memcpy(user->passkey, resp->hash, resp->hash_sz);
|
||||
|
||||
if (!user->name || !user->passkey) {
|
||||
kfree(user->name);
|
||||
ksmbd_free(user->passkey);
|
||||
ksmbd_free(user);
|
||||
user = NULL;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
void ksmbd_free_user(struct ksmbd_user *user)
|
||||
{
|
||||
ksmbd_ipc_logout_request(user->name);
|
||||
kfree(user->name);
|
||||
ksmbd_free(user->passkey);
|
||||
ksmbd_free(user);
|
||||
}
|
||||
|
||||
int ksmbd_anonymous_user(struct ksmbd_user *user)
|
||||
{
|
||||
if (user->name[0] == '\0')
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
67
fs/cifsd/mgmt/user_config.h
Normal file
67
fs/cifsd/mgmt/user_config.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __USER_CONFIG_MANAGEMENT_H__
|
||||
#define __USER_CONFIG_MANAGEMENT_H__
|
||||
|
||||
#include "../glob.h" /* FIXME */
|
||||
#include "../ksmbd_server.h" /* FIXME */
|
||||
|
||||
struct ksmbd_user {
|
||||
unsigned short flags;
|
||||
|
||||
unsigned int uid;
|
||||
unsigned int gid;
|
||||
|
||||
char *name;
|
||||
|
||||
size_t passkey_sz;
|
||||
char *passkey;
|
||||
};
|
||||
|
||||
static inline bool user_guest(struct ksmbd_user *user)
|
||||
{
|
||||
return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT;
|
||||
}
|
||||
|
||||
static inline void set_user_flag(struct ksmbd_user *user, int flag)
|
||||
{
|
||||
user->flags |= flag;
|
||||
}
|
||||
|
||||
static inline int test_user_flag(struct ksmbd_user *user, int flag)
|
||||
{
|
||||
return user->flags & flag;
|
||||
}
|
||||
|
||||
static inline void set_user_guest(struct ksmbd_user *user)
|
||||
{
|
||||
}
|
||||
|
||||
static inline char *user_passkey(struct ksmbd_user *user)
|
||||
{
|
||||
return user->passkey;
|
||||
}
|
||||
|
||||
static inline char *user_name(struct ksmbd_user *user)
|
||||
{
|
||||
return user->name;
|
||||
}
|
||||
|
||||
static inline unsigned int user_uid(struct ksmbd_user *user)
|
||||
{
|
||||
return user->uid;
|
||||
}
|
||||
|
||||
static inline unsigned int user_gid(struct ksmbd_user *user)
|
||||
{
|
||||
return user->gid;
|
||||
}
|
||||
|
||||
struct ksmbd_user *ksmbd_login_user(const char *account);
|
||||
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp);
|
||||
void ksmbd_free_user(struct ksmbd_user *user);
|
||||
int ksmbd_anonymous_user(struct ksmbd_user *user);
|
||||
#endif /* __USER_CONFIG_MANAGEMENT_H__ */
|
||||
345
fs/cifsd/mgmt/user_session.c
Normal file
345
fs/cifsd/mgmt/user_session.c
Normal file
@@ -0,0 +1,345 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
#include "ksmbd_ida.h"
|
||||
#include "user_session.h"
|
||||
#include "user_config.h"
|
||||
#include "tree_connect.h"
|
||||
#include "../transport_ipc.h"
|
||||
#include "../connection.h"
|
||||
#include "../buffer_pool.h"
|
||||
#include "../ksmbd_server.h" /* FIXME */
|
||||
#include "../vfs_cache.h"
|
||||
|
||||
static struct ksmbd_ida *session_ida;
|
||||
|
||||
#define SESSION_HASH_BITS 3
|
||||
static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS);
|
||||
static DECLARE_RWSEM(sessions_table_lock);
|
||||
|
||||
struct ksmbd_session_rpc {
|
||||
int id;
|
||||
unsigned int method;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static void free_channel_list(struct ksmbd_session *sess)
|
||||
{
|
||||
struct channel *chann;
|
||||
struct list_head *tmp, *t;
|
||||
|
||||
list_for_each_safe(tmp, t, &sess->ksmbd_chann_list) {
|
||||
chann = list_entry(tmp, struct channel, chann_list);
|
||||
if (chann) {
|
||||
list_del(&chann->chann_list);
|
||||
kfree(chann);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __session_rpc_close(struct ksmbd_session *sess,
|
||||
struct ksmbd_session_rpc *entry)
|
||||
{
|
||||
struct ksmbd_rpc_command *resp;
|
||||
|
||||
resp = ksmbd_rpc_close(sess, entry->id);
|
||||
if (!resp)
|
||||
pr_err("Unable to close RPC pipe %d\n", entry->id);
|
||||
|
||||
ksmbd_free(resp);
|
||||
ksmbd_rpc_id_free(entry->id);
|
||||
ksmbd_free(entry);
|
||||
}
|
||||
|
||||
static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
|
||||
while (!list_empty(&sess->rpc_handle_list)) {
|
||||
entry = list_entry(sess->rpc_handle_list.next,
|
||||
struct ksmbd_session_rpc,
|
||||
list);
|
||||
|
||||
list_del(&entry->list);
|
||||
__session_rpc_close(sess, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static int __rpc_method(char *rpc_name)
|
||||
{
|
||||
if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc"))
|
||||
return KSMBD_RPC_SRVSVC_METHOD_INVOKE;
|
||||
|
||||
if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc"))
|
||||
return KSMBD_RPC_WKSSVC_METHOD_INVOKE;
|
||||
|
||||
if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman"))
|
||||
return KSMBD_RPC_RAP_METHOD;
|
||||
|
||||
if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr"))
|
||||
return KSMBD_RPC_SAMR_METHOD_INVOKE;
|
||||
|
||||
if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc"))
|
||||
return KSMBD_RPC_LSARPC_METHOD_INVOKE;
|
||||
|
||||
ksmbd_err("Unsupported RPC: %s\n", rpc_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
struct ksmbd_rpc_command *resp;
|
||||
int method;
|
||||
|
||||
method = __rpc_method(rpc_name);
|
||||
if (!method)
|
||||
return -EINVAL;
|
||||
|
||||
entry = ksmbd_alloc(sizeof(struct ksmbd_session_rpc));
|
||||
if (!entry)
|
||||
return -EINVAL;
|
||||
|
||||
list_add(&entry->list, &sess->rpc_handle_list);
|
||||
entry->method = method;
|
||||
entry->id = ksmbd_ipc_id_alloc();
|
||||
if (entry->id < 0)
|
||||
goto error;
|
||||
|
||||
resp = ksmbd_rpc_open(sess, entry->id);
|
||||
if (!resp)
|
||||
goto error;
|
||||
|
||||
ksmbd_free(resp);
|
||||
return entry->id;
|
||||
error:
|
||||
list_del(&entry->list);
|
||||
ksmbd_free(entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
|
||||
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
|
||||
if (entry->id == id) {
|
||||
list_del(&entry->list);
|
||||
__session_rpc_close(sess, entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
|
||||
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
|
||||
if (entry->id == id)
|
||||
return entry->method;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksmbd_session_destroy(struct ksmbd_session *sess)
|
||||
{
|
||||
if (!sess)
|
||||
return;
|
||||
|
||||
if (!atomic_dec_and_test(&sess->refcnt))
|
||||
return;
|
||||
|
||||
list_del(&sess->sessions_entry);
|
||||
|
||||
if (IS_SMB2(sess->conn)) {
|
||||
down_write(&sessions_table_lock);
|
||||
hash_del(&sess->hlist);
|
||||
up_write(&sessions_table_lock);
|
||||
}
|
||||
|
||||
if (sess->user)
|
||||
ksmbd_free_user(sess->user);
|
||||
|
||||
ksmbd_tree_conn_session_logoff(sess);
|
||||
ksmbd_destroy_file_table(&sess->file_table);
|
||||
ksmbd_session_rpc_clear_list(sess);
|
||||
free_channel_list(sess);
|
||||
kfree(sess->Preauth_HashValue);
|
||||
ksmbd_release_id(session_ida, sess->id);
|
||||
|
||||
ksmbd_ida_free(sess->tree_conn_ida);
|
||||
ksmbd_free(sess);
|
||||
}
|
||||
|
||||
static struct ksmbd_session *__session_lookup(unsigned long long id)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
hash_for_each_possible(sessions_table, sess, hlist, id) {
|
||||
if (id == sess->id)
|
||||
return sess;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ksmbd_session_register(struct ksmbd_conn *conn,
|
||||
struct ksmbd_session *sess)
|
||||
{
|
||||
sess->conn = conn;
|
||||
list_add(&sess->sessions_entry, &conn->sessions);
|
||||
}
|
||||
|
||||
void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
while (!list_empty(&conn->sessions)) {
|
||||
sess = list_entry(conn->sessions.next,
|
||||
struct ksmbd_session,
|
||||
sessions_entry);
|
||||
|
||||
ksmbd_session_destroy(sess);
|
||||
}
|
||||
}
|
||||
|
||||
bool ksmbd_session_id_match(struct ksmbd_session *sess, unsigned long long id)
|
||||
{
|
||||
return sess->id == id;
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
|
||||
unsigned long long id)
|
||||
{
|
||||
struct ksmbd_session *sess = NULL;
|
||||
|
||||
list_for_each_entry(sess, &conn->sessions, sessions_entry) {
|
||||
if (ksmbd_session_id_match(sess, id))
|
||||
return sess;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int get_session(struct ksmbd_session *sess)
|
||||
{
|
||||
return atomic_inc_not_zero(&sess->refcnt);
|
||||
}
|
||||
|
||||
void put_session(struct ksmbd_session *sess)
|
||||
{
|
||||
if (atomic_dec_and_test(&sess->refcnt))
|
||||
ksmbd_err("get/%s seems to be mismatched.", __func__);
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
down_read(&sessions_table_lock);
|
||||
sess = __session_lookup(id);
|
||||
if (sess) {
|
||||
if (!get_session(sess))
|
||||
sess = NULL;
|
||||
}
|
||||
up_read(&sessions_table_lock);
|
||||
|
||||
return sess;
|
||||
}
|
||||
|
||||
static int __init_smb2_session(struct ksmbd_session *sess)
|
||||
{
|
||||
int id = ksmbd_acquire_smb2_uid(session_ida);
|
||||
|
||||
if (id < 0)
|
||||
return -EINVAL;
|
||||
sess->id = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ksmbd_session *__session_create(int protocol)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
int ret;
|
||||
|
||||
sess = ksmbd_alloc(sizeof(struct ksmbd_session));
|
||||
if (!sess)
|
||||
return NULL;
|
||||
|
||||
if (ksmbd_init_file_table(&sess->file_table))
|
||||
goto error;
|
||||
|
||||
set_session_flag(sess, protocol);
|
||||
INIT_LIST_HEAD(&sess->sessions_entry);
|
||||
INIT_LIST_HEAD(&sess->tree_conn_list);
|
||||
INIT_LIST_HEAD(&sess->ksmbd_chann_list);
|
||||
INIT_LIST_HEAD(&sess->rpc_handle_list);
|
||||
sess->sequence_number = 1;
|
||||
atomic_set(&sess->refcnt, 1);
|
||||
|
||||
switch (protocol) {
|
||||
case CIFDS_SESSION_FLAG_SMB2:
|
||||
ret = __init_smb2_session(sess);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
sess->tree_conn_ida = ksmbd_ida_alloc();
|
||||
if (!sess->tree_conn_ida)
|
||||
goto error;
|
||||
|
||||
if (protocol == CIFDS_SESSION_FLAG_SMB2) {
|
||||
down_read(&sessions_table_lock);
|
||||
hash_add(sessions_table, &sess->hlist, sess->id);
|
||||
up_read(&sessions_table_lock);
|
||||
}
|
||||
return sess;
|
||||
|
||||
error:
|
||||
ksmbd_session_destroy(sess);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_smb2_session_create(void)
|
||||
{
|
||||
return __session_create(CIFDS_SESSION_FLAG_SMB2);
|
||||
}
|
||||
|
||||
int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess)
|
||||
{
|
||||
int id = -EINVAL;
|
||||
|
||||
if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))
|
||||
id = ksmbd_acquire_smb2_tid(sess->tree_conn_ida);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id)
|
||||
{
|
||||
if (id >= 0)
|
||||
ksmbd_release_id(sess->tree_conn_ida, id);
|
||||
}
|
||||
|
||||
int ksmbd_init_session_table(void)
|
||||
{
|
||||
session_ida = ksmbd_ida_alloc();
|
||||
if (!session_ida)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksmbd_free_session_table(void)
|
||||
{
|
||||
ksmbd_ida_free(session_ida);
|
||||
}
|
||||
105
fs/cifsd/mgmt/user_session.h
Normal file
105
fs/cifsd/mgmt/user_session.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __USER_SESSION_MANAGEMENT_H__
|
||||
#define __USER_SESSION_MANAGEMENT_H__
|
||||
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "../smb_common.h"
|
||||
#include "../ntlmssp.h"
|
||||
|
||||
#define CIFDS_SESSION_FLAG_SMB2 (1 << 1)
|
||||
|
||||
#define PREAUTH_HASHVALUE_SIZE 64
|
||||
|
||||
struct ksmbd_ida;
|
||||
struct ksmbd_file_table;
|
||||
|
||||
struct channel {
|
||||
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
|
||||
struct ksmbd_conn *conn;
|
||||
struct list_head chann_list;
|
||||
};
|
||||
|
||||
struct preauth_session {
|
||||
__u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE];
|
||||
uint64_t sess_id;
|
||||
struct list_head list_entry;
|
||||
};
|
||||
|
||||
struct ksmbd_session {
|
||||
uint64_t id;
|
||||
|
||||
struct ksmbd_user *user;
|
||||
struct ksmbd_conn *conn;
|
||||
unsigned int sequence_number;
|
||||
unsigned int flags;
|
||||
|
||||
bool sign;
|
||||
bool enc;
|
||||
bool is_anonymous;
|
||||
|
||||
int state;
|
||||
__u8 *Preauth_HashValue;
|
||||
|
||||
struct ntlmssp_auth ntlmssp;
|
||||
char sess_key[CIFS_KEY_SIZE];
|
||||
|
||||
struct hlist_node hlist;
|
||||
struct list_head ksmbd_chann_list;
|
||||
struct list_head tree_conn_list;
|
||||
struct ksmbd_ida *tree_conn_ida;
|
||||
struct list_head rpc_handle_list;
|
||||
|
||||
__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
|
||||
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
|
||||
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
|
||||
|
||||
struct list_head sessions_entry;
|
||||
struct ksmbd_file_table file_table;
|
||||
atomic_t refcnt;
|
||||
};
|
||||
|
||||
static inline int test_session_flag(struct ksmbd_session *sess, int bit)
|
||||
{
|
||||
return sess->flags & bit;
|
||||
}
|
||||
|
||||
static inline void set_session_flag(struct ksmbd_session *sess, int bit)
|
||||
{
|
||||
sess->flags |= bit;
|
||||
}
|
||||
|
||||
static inline void clear_session_flag(struct ksmbd_session *sess, int bit)
|
||||
{
|
||||
sess->flags &= ~bit;
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_smb2_session_create(void);
|
||||
|
||||
void ksmbd_session_destroy(struct ksmbd_session *sess);
|
||||
|
||||
bool ksmbd_session_id_match(struct ksmbd_session *sess, unsigned long long id);
|
||||
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id);
|
||||
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
|
||||
unsigned long long id);
|
||||
void ksmbd_session_register(struct ksmbd_conn *conn,
|
||||
struct ksmbd_session *sess);
|
||||
void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
|
||||
|
||||
int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess);
|
||||
void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id);
|
||||
|
||||
int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name);
|
||||
void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id);
|
||||
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id);
|
||||
int get_session(struct ksmbd_session *sess);
|
||||
void put_session(struct ksmbd_session *sess);
|
||||
|
||||
int ksmbd_init_session_table(void);
|
||||
void ksmbd_free_session_table(void);
|
||||
|
||||
#endif /* __USER_SESSION_MANAGEMENT_H__ */
|
||||
293
fs/cifsd/misc.c
Normal file
293
fs/cifsd/misc.c
Normal file
@@ -0,0 +1,293 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "smb_common.h"
|
||||
#include "connection.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#include "mgmt/share_config.h"
|
||||
|
||||
/**
|
||||
* match_pattern() - compare a string with a pattern which might include
|
||||
* wildcard '*' and '?'
|
||||
* TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR
|
||||
*
|
||||
* @string: string to compare with a pattern
|
||||
* @pattern: pattern string which might include wildcard '*' and '?'
|
||||
*
|
||||
* Return: 0 if pattern matched with the string, otherwise non zero value
|
||||
*/
|
||||
int match_pattern(const char *str, const char *pattern)
|
||||
{
|
||||
const char *s = str;
|
||||
const char *p = pattern;
|
||||
bool star = false;
|
||||
|
||||
while (*s) {
|
||||
switch (*p) {
|
||||
case '?':
|
||||
s++;
|
||||
p++;
|
||||
break;
|
||||
case '*':
|
||||
star = true;
|
||||
str = s;
|
||||
if (!*++p)
|
||||
return true;
|
||||
pattern = p;
|
||||
break;
|
||||
default:
|
||||
if (tolower(*s) == tolower(*p)) {
|
||||
s++;
|
||||
p++;
|
||||
} else {
|
||||
if (!star)
|
||||
return false;
|
||||
str++;
|
||||
s = str;
|
||||
p = pattern;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == '*')
|
||||
++p;
|
||||
return !*p;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_char_allowed() - check for valid character
|
||||
* @ch: input character to be checked
|
||||
*
|
||||
* Return: 1 if char is allowed, otherwise 0
|
||||
*/
|
||||
static inline int is_char_allowed(char ch)
|
||||
{
|
||||
/* check for control chars, wildcards etc. */
|
||||
if (!(ch & 0x80) &&
|
||||
(ch <= 0x1f ||
|
||||
ch == '?' || ch == '"' || ch == '<' ||
|
||||
ch == '>' || ch == '|' || ch == '*'))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ksmbd_validate_filename(char *filename)
|
||||
{
|
||||
while (*filename) {
|
||||
char c = *filename;
|
||||
|
||||
filename++;
|
||||
if (!is_char_allowed(c)) {
|
||||
ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksmbd_validate_stream_name(char *stream_name)
|
||||
{
|
||||
while (*stream_name) {
|
||||
char c = *stream_name;
|
||||
|
||||
stream_name++;
|
||||
if (c == '/' || c == ':' || c == '\\') {
|
||||
ksmbd_err("Stream name validation failed: %c\n", c);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_stream_name(char *filename, char **stream_name, int *s_type)
|
||||
{
|
||||
char *stream_type;
|
||||
char *s_name;
|
||||
int rc = 0;
|
||||
|
||||
s_name = filename;
|
||||
filename = strsep(&s_name, ":");
|
||||
ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name);
|
||||
if (strchr(s_name, ':')) {
|
||||
stream_type = s_name;
|
||||
s_name = strsep(&stream_type, ":");
|
||||
|
||||
rc = ksmbd_validate_stream_name(s_name);
|
||||
if (rc < 0) {
|
||||
rc = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name,
|
||||
stream_type);
|
||||
if (!strncasecmp("$data", stream_type, 5))
|
||||
*s_type = DATA_STREAM;
|
||||
else if (!strncasecmp("$index_allocation", stream_type, 17))
|
||||
*s_type = DIR_STREAM;
|
||||
else
|
||||
rc = -ENOENT;
|
||||
}
|
||||
|
||||
*stream_name = s_name;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert_to_nt_pathname() - extract and return windows path string
|
||||
* whose share directory prefix was removed from file path
|
||||
* @filename : unix filename
|
||||
* @sharepath: share path string
|
||||
*
|
||||
* Return : windows path string or error
|
||||
*/
|
||||
|
||||
char *convert_to_nt_pathname(char *filename, char *sharepath)
|
||||
{
|
||||
char *ab_pathname;
|
||||
int len, name_len;
|
||||
|
||||
name_len = strlen(filename);
|
||||
ab_pathname = kmalloc(name_len, GFP_KERNEL);
|
||||
if (!ab_pathname)
|
||||
return NULL;
|
||||
|
||||
ab_pathname[0] = '\\';
|
||||
ab_pathname[1] = '\0';
|
||||
|
||||
len = strlen(sharepath);
|
||||
if (!strncmp(filename, sharepath, len) && name_len != len) {
|
||||
strscpy(ab_pathname, &filename[len], name_len);
|
||||
ksmbd_conv_path_to_windows(ab_pathname);
|
||||
}
|
||||
|
||||
return ab_pathname;
|
||||
}
|
||||
|
||||
int get_nlink(struct kstat *st)
|
||||
{
|
||||
int nlink;
|
||||
|
||||
nlink = st->nlink;
|
||||
if (S_ISDIR(st->mode))
|
||||
nlink--;
|
||||
|
||||
return nlink;
|
||||
}
|
||||
|
||||
void ksmbd_conv_path_to_unix(char *path)
|
||||
{
|
||||
strreplace(path, '\\', '/');
|
||||
}
|
||||
|
||||
void ksmbd_strip_last_slash(char *path)
|
||||
{
|
||||
int len = strlen(path);
|
||||
|
||||
while (len && path[len - 1] == '/') {
|
||||
path[len - 1] = '\0';
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
void ksmbd_conv_path_to_windows(char *path)
|
||||
{
|
||||
strreplace(path, '/', '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* extract_sharename() - get share name from tree connect request
|
||||
* @treename: buffer containing tree name and share name
|
||||
*
|
||||
* Return: share name on success, otherwise error
|
||||
*/
|
||||
char *extract_sharename(char *treename)
|
||||
{
|
||||
char *name = treename;
|
||||
char *dst;
|
||||
char *pos = strrchr(name, '\\');
|
||||
|
||||
if (pos)
|
||||
name = (pos + 1);
|
||||
|
||||
/* caller has to free the memory */
|
||||
dst = kstrdup(name, GFP_KERNEL);
|
||||
if (!dst)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert_to_unix_name() - convert windows name to unix format
|
||||
* @path: name to be converted
|
||||
* @tid: tree id of mathing share
|
||||
*
|
||||
* Return: converted name on success, otherwise NULL
|
||||
*/
|
||||
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name)
|
||||
{
|
||||
int no_slash = 0, name_len, path_len;
|
||||
char *new_name;
|
||||
|
||||
if (name[0] == '/')
|
||||
name++;
|
||||
|
||||
path_len = share->path_sz;
|
||||
name_len = strlen(name);
|
||||
new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL);
|
||||
if (!new_name)
|
||||
return new_name;
|
||||
|
||||
memcpy(new_name, share->path, path_len);
|
||||
if (new_name[path_len - 1] != '/') {
|
||||
new_name[path_len] = '/';
|
||||
no_slash = 1;
|
||||
}
|
||||
|
||||
memcpy(new_name + path_len + no_slash, name, name_len);
|
||||
path_len += name_len + no_slash;
|
||||
new_name[path_len] = 0x00;
|
||||
return new_name;
|
||||
}
|
||||
|
||||
char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
|
||||
const struct nls_table *local_nls,
|
||||
int *conv_len)
|
||||
{
|
||||
char *conv;
|
||||
int sz = min(4 * d_info->name_len, PATH_MAX);
|
||||
|
||||
if (!sz)
|
||||
return NULL;
|
||||
|
||||
conv = kmalloc(sz, GFP_KERNEL);
|
||||
if (!conv)
|
||||
return NULL;
|
||||
|
||||
/* XXX */
|
||||
*conv_len = smbConvertToUTF16((__le16 *)conv,
|
||||
d_info->name,
|
||||
d_info->name_len,
|
||||
local_nls,
|
||||
0);
|
||||
*conv_len *= 2;
|
||||
|
||||
/* We allocate buffer twice bigger than needed. */
|
||||
conv[*conv_len] = 0x00;
|
||||
conv[*conv_len + 1] = 0x00;
|
||||
return conv;
|
||||
}
|
||||
38
fs/cifsd/misc.h
Normal file
38
fs/cifsd/misc.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_MISC_H__
|
||||
#define __KSMBD_MISC_H__
|
||||
|
||||
struct ksmbd_share_config;
|
||||
struct nls_table;
|
||||
struct kstat;
|
||||
struct ksmbd_file;
|
||||
|
||||
int match_pattern(const char *str, const char *pattern);
|
||||
|
||||
int ksmbd_validate_filename(char *filename);
|
||||
|
||||
int parse_stream_name(char *filename, char **stream_name, int *s_type);
|
||||
|
||||
char *convert_to_nt_pathname(char *filename, char *sharepath);
|
||||
|
||||
int get_nlink(struct kstat *st);
|
||||
|
||||
void ksmbd_conv_path_to_unix(char *path);
|
||||
void ksmbd_strip_last_slash(char *path);
|
||||
void ksmbd_conv_path_to_windows(char *path);
|
||||
|
||||
char *extract_sharename(char *treename);
|
||||
|
||||
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name);
|
||||
|
||||
#define KSMBD_DIR_INFO_ALIGNMENT 8
|
||||
|
||||
struct ksmbd_dir_info;
|
||||
char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
|
||||
const struct nls_table *local_nls,
|
||||
int *conv_len);
|
||||
#endif /* __KSMBD_MISC_H__ */
|
||||
344
fs/cifsd/ndr.c
Normal file
344
fs/cifsd/ndr.c
Normal file
@@ -0,0 +1,344 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2021 Samsung Electronics Co., Ltd.
|
||||
* Author(s): Namjae Jeon <linkinjeon@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#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; \
|
||||
})
|
||||
|
||||
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;
|
||||
|
||||
if (da->version == 3) {
|
||||
snprintf(hex_attr, 10, "0x%x", da->attr);
|
||||
ndr_write_string(n, hex_attr, strlen(hex_attr));
|
||||
} else {
|
||||
ndr_write_string(n, "", strlen(""));
|
||||
}
|
||||
ndr_write_int16(n, da->version);
|
||||
ndr_write_int32(n, da->version);
|
||||
|
||||
ndr_write_int32(n, da->flags);
|
||||
ndr_write_int32(n, da->attr);
|
||||
if (da->version == 3) {
|
||||
ndr_write_int32(n, da->ea_size);
|
||||
ndr_write_int64(n, da->size);
|
||||
ndr_write_int64(n, da->alloc_size);
|
||||
} else
|
||||
ndr_write_int64(n, da->itime);
|
||||
ndr_write_int64(n, da->create_time);
|
||||
if (da->version == 3)
|
||||
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 && da->version != 4) {
|
||||
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);
|
||||
if (da->version == 4) {
|
||||
da->itime = ndr_read_int64(n);
|
||||
da->create_time = ndr_read_int64(n);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
21
fs/cifsd/ndr.h
Normal file
21
fs/cifsd/ndr.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics Co., Ltd.
|
||||
* Author(s): Namjae Jeon <linkinjeon@kernel.org>
|
||||
*/
|
||||
|
||||
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);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user