Commit e2f34481 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

cifsd: add server-side procedures for SMB3

This adds smb3 engine, NTLM/NTLMv2/Kerberos authentication, oplock/lease
cache mechanism for cifsd.
Signed-off-by: default avatarNamjae Jeon <namjae.jeon@samsung.com>
Signed-off-by: default avatarSergey Senozhatsky <sergey.senozhatsky@gmail.com>
Signed-off-by: default avatarHyunchul Lee <hyc.lee@gmail.com>
Acked-by: default avatarRonnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 0626e664
This diff is collapsed.
/* 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__ */
This diff is collapsed.
/* 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
// 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;
}
/* 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__ */
// 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);
}
/* 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__ */
// 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);
}
/* 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__ */
// 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;
}
/* 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__ */
// 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;
}
/* 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__ */
// 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);
}
/* 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__ */
// 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;
}
/* 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__ */
// 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;
}
/* 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);
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* fs/ksmbd/netmisc.c
*
* Copyright (c) International Business Machines Corp., 2002,2008
* Author(s): Steve French (sfrench@us.ibm.com)
*
* Error mapping routines from Samba libsmb/errormap.c
* Copyright (C) Andrew Tridgell 2001
*/
#include "glob.h"
#include "smberr.h"
#include "nterr.h"
#include "time_wrappers.h"
/*
* Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
* into Unix UTC (based 1970-01-01, in seconds).
*/
struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc)
{
struct timespec64 ts;
/* Subtract the NTFS time offset, then convert to 1s intervals. */
s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
u64 abs_t;
/*
* Unfortunately can not use normal 64 bit division on 32 bit arch, but
* the alternative, do_div, does not work with negative numbers so have
* to special case them
*/
if (t < 0) {
abs_t = -t;
ts.tv_nsec = do_div(abs_t, 10000000) * 100;
ts.tv_nsec = -ts.tv_nsec;
ts.tv_sec = -abs_t;
} else {
abs_t = t;
ts.tv_nsec = do_div(abs_t, 10000000) * 100;
ts.tv_sec = abs_t;
}
return ts;
}
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (c) International Business Machines Corp., 2002,2007
* Author(s): Steve French (sfrench@us.ibm.com)
*/
#ifndef __KSMBD_NTLMSSP_H
#define __KSMBD_NTLMSSP_H
#define NTLMSSP_SIGNATURE "NTLMSSP"
/* Security blob target info data */
#define TGT_Name "KSMBD"
/*
* Size of the crypto key returned on the negotiate SMB in bytes
*/
#define CIFS_CRYPTO_KEY_SIZE (8)
#define CIFS_KEY_SIZE (40)
/*
* Size of encrypted user password in bytes
*/
#define CIFS_ENCPWD_SIZE (16)
#define CIFS_CPHTXT_SIZE (16)
/* Message Types */
#define NtLmNegotiate cpu_to_le32(1)
#define NtLmChallenge cpu_to_le32(2)
#define NtLmAuthenticate cpu_to_le32(3)
#define UnknownMessage cpu_to_le32(8)
/* Negotiate Flags */
#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */
#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */
#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */
/* define reserved9 0x08 */
#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */
#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */
#define NTLMSSP_NEGOTIATE_DGRAM 0x0040
#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */
/* defined reserved 8 0x0100 */
#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */
#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */
#define NTLMSSP_ANONYMOUS 0x0800
#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */
#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000
#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */
#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */
#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
#define NTLMSSP_TARGET_TYPE_SHARE 0x40000
#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/
/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */
#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000
#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */
#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000
#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000
/* #define reserved4 0x1000000 */
#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */
/* #define reserved3 0x4000000 */
/* #define reserved2 0x8000000 */
/* #define reserved1 0x10000000 */
#define NTLMSSP_NEGOTIATE_128 0x20000000
#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000
#define NTLMSSP_NEGOTIATE_56 0x80000000
/* Define AV Pair Field IDs */
enum av_field_type {
NTLMSSP_AV_EOL = 0,
NTLMSSP_AV_NB_COMPUTER_NAME,
NTLMSSP_AV_NB_DOMAIN_NAME,
NTLMSSP_AV_DNS_COMPUTER_NAME,
NTLMSSP_AV_DNS_DOMAIN_NAME,
NTLMSSP_AV_DNS_TREE_NAME,
NTLMSSP_AV_FLAGS,
NTLMSSP_AV_TIMESTAMP,
NTLMSSP_AV_RESTRICTION,
NTLMSSP_AV_TARGET_NAME,
NTLMSSP_AV_CHANNEL_BINDINGS
};
/* Although typedefs are not commonly used for structure definitions */
/* in the Linux kernel, in this particular case they are useful */
/* to more closely match the standards document for NTLMSSP from */
/* OpenGroup and to make the code more closely match the standard in */
/* appearance */
struct security_buffer {
__le16 Length;
__le16 MaximumLength;
__le32 BufferOffset; /* offset to buffer */
} __packed;
struct target_info {
__le16 Type;
__le16 Length;
__u8 Content[0];
} __packed;
struct negotiate_message {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
__le32 MessageType; /* NtLmNegotiate = 1 */
__le32 NegotiateFlags;
struct security_buffer DomainName; /* RFC 1001 style and ASCII */
struct security_buffer WorkstationName; /* RFC 1001 and ASCII */
/*
* struct security_buffer for version info not present since we
* do not set the version is present flag
*/
char DomainString[0];
/* followed by WorkstationString */
} __packed;
struct challenge_message {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
__le32 MessageType; /* NtLmChallenge = 2 */
struct security_buffer TargetName;
__le32 NegotiateFlags;
__u8 Challenge[CIFS_CRYPTO_KEY_SIZE];
__u8 Reserved[8];
struct security_buffer TargetInfoArray;
/*
* struct security_buffer for version info not present since we
* do not set the version is present flag
*/
} __packed;
struct authenticate_message {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
__le32 MessageType; /* NtLmsAuthenticate = 3 */
struct security_buffer LmChallengeResponse;
struct security_buffer NtChallengeResponse;
struct security_buffer DomainName;
struct security_buffer UserName;
struct security_buffer WorkstationName;
struct security_buffer SessionKey;
__le32 NegotiateFlags;
/*
* struct security_buffer for version info not present since we
* do not set the version is present flag
*/
char UserString[0];
} __packed;
struct ntlmv2_resp {
char ntlmv2_hash[CIFS_ENCPWD_SIZE];
__le32 blob_signature;
__u32 reserved;
__le64 time;
__u64 client_chal; /* random */
__u32 reserved2;
/* array of name entries could follow ending in minimum 4 byte struct */
} __packed;
/* per smb session structure/fields */
struct ntlmssp_auth {
/* whether session key is per smb session */
bool sesskey_per_smbsess;
/* sent by client in type 1 ntlmsssp exchange */
__u32 client_flags;
/* sent by server in type 2 ntlmssp exchange */
__u32 conn_flags;
/* sent to server */
unsigned char ciphertext[CIFS_CPHTXT_SIZE];
/* used by ntlmssp */
char cryptkey[CIFS_CRYPTO_KEY_SIZE];
};
#endif /* __KSMBD_NTLMSSP_H */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
#ifndef __KSMBD_OPLOCK_H
#define __KSMBD_OPLOCK_H
#include "smb_common.h"
#define OPLOCK_WAIT_TIME (35*HZ)
/* SMB Oplock levels */
#define OPLOCK_NONE 0
#define OPLOCK_EXCLUSIVE 1
#define OPLOCK_BATCH 2
#define OPLOCK_READ 3 /* level 2 oplock */
/* SMB2 Oplock levels */
#define SMB2_OPLOCK_LEVEL_NONE 0x00
#define SMB2_OPLOCK_LEVEL_II 0x01
#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
#define SMB2_OPLOCK_LEVEL_BATCH 0x09
#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
/* Oplock states */
#define OPLOCK_STATE_NONE 0x00
#define OPLOCK_ACK_WAIT 0x01
#define OPLOCK_CLOSING 0x02
#define OPLOCK_WRITE_TO_READ 0x01
#define OPLOCK_READ_HANDLE_TO_READ 0x02
#define OPLOCK_WRITE_TO_NONE 0x04
#define OPLOCK_READ_TO_NONE 0x08
#define SMB2_LEASE_KEY_SIZE 16
struct lease_ctx_info {
__u8 lease_key[SMB2_LEASE_KEY_SIZE];
__le32 req_state;
__le32 flags;
__le64 duration;
int dlease;
};
struct lease_table {
char client_guid[SMB2_CLIENT_GUID_SIZE];
struct list_head lease_list;
struct list_head l_entry;
spinlock_t lb_lock;
};
struct lease {
__u8 lease_key[SMB2_LEASE_KEY_SIZE];
__le32 state;
__le32 new_state;
__le32 flags;
__le64 duration;
struct lease_table *l_lb;
};
struct oplock_info {
struct ksmbd_conn *conn;
struct ksmbd_session *sess;
struct ksmbd_work *work;
struct ksmbd_file *o_fp;
int level;
int op_state;
unsigned long pending_break;
uint64_t fid;
atomic_t breaking_cnt;
atomic_t refcount;
__u16 Tid;
bool is_lease;
bool open_trunc; /* truncate on open */
struct lease *o_lease;
struct list_head interim_list;
struct list_head op_entry;
struct list_head lease_entry;
wait_queue_head_t oplock_q; /* Other server threads */
wait_queue_head_t oplock_brk; /* oplock breaking wait */
struct rcu_head rcu_head;
};
struct lease_break_info {
__le32 curr_state;
__le32 new_state;
char lease_key[SMB2_LEASE_KEY_SIZE];
};
struct oplock_break_info {
int level;
int open_trunc;
int fid;
};
extern int smb_grant_oplock(struct ksmbd_work *work, int req_op_level,
uint64_t pid, struct ksmbd_file *fp, __u16 tid,
struct lease_ctx_info *lctx, int share_ret);
extern void smb_break_all_levII_oplock(struct ksmbd_work *work,
struct ksmbd_file *fp, int is_trunc);
int opinfo_write_to_read(struct oplock_info *opinfo);
int opinfo_read_handle_to_read(struct oplock_info *opinfo);
int opinfo_write_to_none(struct oplock_info *opinfo);
int opinfo_read_to_none(struct oplock_info *opinfo);
void close_id_del_oplock(struct ksmbd_file *fp);
void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp);
struct oplock_info *opinfo_get(struct ksmbd_file *fp);
void opinfo_put(struct oplock_info *opinfo);
/* Lease related functions */
void create_lease_buf(u8 *rbuf, struct lease *lease);
struct lease_ctx_info *parse_lease_state(void *open_req);
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
int lease_read_to_write(struct oplock_info *opinfo);
/* Durable related functions */
void create_durable_rsp_buf(char *cc);
void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp);
void create_mxac_rsp_buf(char *cc, int maximal_access);
void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id);
void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp);
struct create_context *smb2_find_context_vals(void *open_req, const char *str);
int ksmbd_durable_verify_and_del_oplock(struct ksmbd_session *curr_sess,
struct ksmbd_session *prev_sess,
int fid, struct file **filp,
uint64_t sess_id);
struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
char *lease_key);
int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
struct lease_ctx_info *lctx);
void destroy_lease_table(struct ksmbd_conn *conn);
int smb2_check_durable_oplock(struct ksmbd_file *fp,
struct lease_ctx_info *lctx, char *name);
#endif /* __KSMBD_OPLOCK_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (c) International Business Machines Corp., 2007
* Author(s): Steve French (sfrench@us.ibm.com)
* Modified by Namjae Jeon (linkinjeon@kernel.org)
*/
#ifndef _SMBACL_H
#define _SMBACL_H
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/posix_acl.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 */
#define ACCESS_ALLOWED 0
#define ACCESS_DENIED 1
#define SIDOWNER 1
#define SIDGROUP 2
#define SIDCREATOR_OWNER 3
#define SIDCREATOR_GROUP 4
#define SIDUNIX_USER 5
#define SIDUNIX_GROUP 6
#define SIDNFS_USER 7
#define SIDNFS_GROUP 8
#define SIDNFS_MODE 9
/* Revision for ACLs */
#define SD_REVISION 1
/* Control flags for Security Descriptor */
#define OWNER_DEFAULTED 0x0001
#define GROUP_DEFAULTED 0x0002
#define DACL_PRESENT 0x0004
#define DACL_DEFAULTED 0x0008
#define SACL_PRESENT 0x0010
#define SACL_DEFAULTED 0x0020
#define DACL_TRUSTED 0x0040
#define SERVER_SECURITY 0x0080
#define DACL_AUTO_INHERIT_REQ 0x0100
#define SACL_AUTO_INHERIT_REQ 0x0200
#define DACL_AUTO_INHERITED 0x0400
#define SACL_AUTO_INHERITED 0x0800
#define DACL_PROTECTED 0x1000
#define SACL_PROTECTED 0x2000
#define RM_CONTROL_VALID 0x4000
#define SELF_RELATIVE 0x8000
/* ACE types - see MS-DTYP 2.4.4.1 */
#define ACCESS_ALLOWED_ACE_TYPE 0x00
#define ACCESS_DENIED_ACE_TYPE 0x01
#define SYSTEM_AUDIT_ACE_TYPE 0x02
#define SYSTEM_ALARM_ACE_TYPE 0x03
#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04
#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05
#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06
#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07
#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08
#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09
#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A
#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B
#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C
#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D
#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */
#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F
#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */
#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11
#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12
#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13
/* ACE flags */
#define OBJECT_INHERIT_ACE 0x01
#define CONTAINER_INHERIT_ACE 0x02
#define NO_PROPAGATE_INHERIT_ACE 0x04
#define INHERIT_ONLY_ACE 0x08
#define INHERITED_ACE 0x10
#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40
#define FAILED_ACCESS_ACE_FLAG 0x80
/*
* Maximum size of a string representation of a SID:
*
* The fields are unsigned values in decimal. So:
*
* u8: max 3 bytes in decimal
* u32: max 10 bytes in decimal
*
* "S-" + 3 bytes for version field + 15 for authority field + NULL terminator
*
* For authority field, max is when all 6 values are non-zero and it must be
* represented in hex. So "-0x" + 12 hex digits.
*
* Add 11 bytes for each subauthority field (10 bytes each + 1 for '-')
*/
#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;
__le32 osidoffset;
__le32 gsidoffset;
__le32 sacloffset;
__le32 dacloffset;
} __packed;
struct smb_sid {
__u8 revision; /* revision level */
__u8 num_subauth;
__u8 authority[NUM_AUTHS];
__le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */
} __packed;
/* size of a struct cifs_sid, sans sub_auth array */
#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS)
struct smb_acl {
__le16 revision; /* revision level */
__le16 size;
__le32 num_aces;
} __packed;
struct smb_ace {
__u8 type;
__u8 flags;
__le16 size;
__le32 access_req;
struct smb_sid sid; /* ie UUID of user or group who gets these perms */
} __packed;
struct smb_fattr {
kuid_t cf_uid;
kgid_t cf_gid;
umode_t cf_mode;
__le32 daccess;
struct posix_acl *cf_acls;
struct posix_acl *cf_dacls;
};
struct posix_ace_state {
u32 allow;
u32 deny;
};
struct posix_user_ace_state {
union {
kuid_t uid;
kgid_t gid;
};
struct posix_ace_state perms;
};
struct posix_ace_state_array {
int n;
struct posix_user_ace_state aces[];
};
/*
* while processing the nfsv4 ace, this maintains the partial permissions
* calculated so far:
*/
struct posix_acl_state {
struct posix_ace_state owner;
struct posix_ace_state group;
struct posix_ace_state other;
struct posix_ace_state everyone;
struct posix_ace_state mask; /* deny unused in this case */
struct posix_ace_state_array *users;
struct posix_ace_state_array *groups;
};
int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len,
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 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 */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment