forked from Minki/linux
Eleven fixes for the ksmbd kernel server, including an important fix disabling weak NTLMv1 authentication, and seven security (improved buffer overflow checks) fixes
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmFXwIYACgkQiiy9cAdy T1EWfQv/fSSoymQpFZrnzd0ELS7J14IvPbjpL5wWLWUdtrHLIk5Fcg1rxNXDZVsY o932TGAo/X3qEgMXWbD812Q4MoB+Rupj0NmHReLL+UxwrgCHexFVnzr0SH0YQfWA 59xa+2BVzInqnejika1H4HewJqGKt6npGiAg0Rzx+nJiFlX0CAPupW8oC90UM5Co 3vJNG4orZILlGLRIdMpSashW8Z5dbXY95k/VqF/vYqHgfy37L1m+pDCjRjEbXtFY fuqFGeAcsnRWnu6ECvuujTyh+hQMSdwb/5F6uovVUrdChdvbfWi+rjtHDx0HpD2t UKMnRQPk/BWD1/6zFriObCt4QDpufZdvLlVNyir4BdIT2OhzkwkZ4qXz/du+IWKm 4/4nYEaD2lFN4pEAy73NGGt9eJrAjbnaswNDPTZIDpJ7IyZiakDFjOD8iLFncBS7 xL6hUcMvc4njaqxcB9LHFZ8w67cjwR5aw0+wr8DKfCh13lJgSvxoXEP/D+4fxINv QULcIhF/ =X733 -----END PGP SIGNATURE----- Merge tag '5.15-rc3-ksmbd-fixes' of git://git.samba.org/ksmbd Pull ksmbd server fixes from Steve French: "Eleven fixes for the ksmbd kernel server, mostly security related: - an important fix for disabling weak NTLMv1 authentication - seven security (improved buffer overflow checks) fixes - fix for wrong infolevel struct used in some getattr/setattr paths - two small documentation fixes" * tag '5.15-rc3-ksmbd-fixes' of git://git.samba.org/ksmbd: ksmbd: missing check for NULL in convert_to_nt_pathname() ksmbd: fix transform header validation ksmbd: add buffer validation for SMB2_CREATE_CONTEXT ksmbd: add validation in smb2 negotiate ksmbd: add request buffer validation in smb2_set_info ksmbd: use correct basic info level in set_file_basic_info() ksmbd: remove NTLMv1 authentication ksmbd: fix documentation for 2 functions MAINTAINERS: rename cifs_common to smbfs_common in cifs and ksmbd entry ksmbd: fix invalid request buffer access in compound ksmbd: remove RFC1002 check in smb2 request
This commit is contained in:
commit
e25ca045c3
@ -4657,7 +4657,7 @@ W: http://linux-cifs.samba.org/
|
||||
T: git git://git.samba.org/sfrench/cifs-2.6.git
|
||||
F: Documentation/admin-guide/cifs/
|
||||
F: fs/cifs/
|
||||
F: fs/cifs_common/
|
||||
F: fs/smbfs_common/
|
||||
|
||||
COMPACTPCI HOTPLUG CORE
|
||||
M: Scott Murray <scott@spiteful.org>
|
||||
@ -10195,8 +10195,8 @@ M: Hyunchul Lee <hyc.lee@gmail.com>
|
||||
L: linux-cifs@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.samba.org/ksmbd.git
|
||||
F: fs/cifs_common/
|
||||
F: fs/ksmbd/
|
||||
F: fs/smbfs_common/
|
||||
|
||||
KERNEL UNIT TESTING FRAMEWORK (KUnit)
|
||||
M: Brendan Higgins <brendanhiggins@google.com>
|
||||
|
205
fs/ksmbd/auth.c
205
fs/ksmbd/auth.c
@ -68,125 +68,6 @@ void ksmbd_copy_gss_neg_header(void *buf)
|
||||
memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH);
|
||||
}
|
||||
|
||||
static void
|
||||
str_to_key(unsigned char *str, unsigned char *key)
|
||||
{
|
||||
int i;
|
||||
|
||||
key[0] = str[0] >> 1;
|
||||
key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2);
|
||||
key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3);
|
||||
key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4);
|
||||
key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5);
|
||||
key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6);
|
||||
key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7);
|
||||
key[7] = str[6] & 0x7F;
|
||||
for (i = 0; i < 8; i++)
|
||||
key[i] = (key[i] << 1);
|
||||
}
|
||||
|
||||
static int
|
||||
smbhash(unsigned char *out, const unsigned char *in, unsigned char *key)
|
||||
{
|
||||
unsigned char key2[8];
|
||||
struct des_ctx ctx;
|
||||
|
||||
if (fips_enabled) {
|
||||
ksmbd_debug(AUTH, "FIPS compliance enabled: DES not permitted\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
str_to_key(key, key2);
|
||||
des_expand_key(&ctx, key2, DES_KEY_SIZE);
|
||||
des_encrypt(&ctx, out, in);
|
||||
memzero_explicit(&ctx, sizeof(ctx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksmbd_enc_p24(unsigned char *p21, const unsigned char *c8, unsigned char *p24)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = smbhash(p24, c8, p21);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = smbhash(p24 + 8, c8, p21 + 7);
|
||||
if (rc)
|
||||
return rc;
|
||||
return smbhash(p24 + 16, c8, p21 + 14);
|
||||
}
|
||||
|
||||
/* produce a md4 message digest from data of length n bytes */
|
||||
static int ksmbd_enc_md4(unsigned char *md4_hash, unsigned char *link_str,
|
||||
int link_len)
|
||||
{
|
||||
int rc;
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
ctx = ksmbd_crypto_ctx_find_md4();
|
||||
if (!ctx) {
|
||||
ksmbd_debug(AUTH, "Crypto md4 allocation error\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rc = crypto_shash_init(CRYPTO_MD4(ctx));
|
||||
if (rc) {
|
||||
ksmbd_debug(AUTH, "Could not init md4 shash\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = crypto_shash_update(CRYPTO_MD4(ctx), link_str, link_len);
|
||||
if (rc) {
|
||||
ksmbd_debug(AUTH, "Could not update with link_str\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = crypto_shash_final(CRYPTO_MD4(ctx), md4_hash);
|
||||
if (rc)
|
||||
ksmbd_debug(AUTH, "Could not generate md4 hash\n");
|
||||
out:
|
||||
ksmbd_release_crypto_ctx(ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ksmbd_enc_update_sess_key(unsigned char *md5_hash, char *nonce,
|
||||
char *server_challenge, int len)
|
||||
{
|
||||
int rc;
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
ctx = ksmbd_crypto_ctx_find_md5();
|
||||
if (!ctx) {
|
||||
ksmbd_debug(AUTH, "Crypto md5 allocation error\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rc = crypto_shash_init(CRYPTO_MD5(ctx));
|
||||
if (rc) {
|
||||
ksmbd_debug(AUTH, "Could not init md5 shash\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = crypto_shash_update(CRYPTO_MD5(ctx), server_challenge, len);
|
||||
if (rc) {
|
||||
ksmbd_debug(AUTH, "Could not update with challenge\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = crypto_shash_update(CRYPTO_MD5(ctx), nonce, len);
|
||||
if (rc) {
|
||||
ksmbd_debug(AUTH, "Could not update with nonce\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = crypto_shash_final(CRYPTO_MD5(ctx), md5_hash);
|
||||
if (rc)
|
||||
ksmbd_debug(AUTH, "Could not generate md5 hash\n");
|
||||
out:
|
||||
ksmbd_release_crypto_ctx(ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_gen_sess_key() - function to generate session key
|
||||
* @sess: session of connection
|
||||
@ -324,43 +205,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_auth_ntlm() - NTLM authentication handler
|
||||
* @sess: session of connection
|
||||
* @pw_buf: NTLM challenge response
|
||||
* @passkey: user password
|
||||
*
|
||||
* Return: 0 on success, error number on error
|
||||
*/
|
||||
int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf)
|
||||
{
|
||||
int rc;
|
||||
unsigned char p21[21];
|
||||
char key[CIFS_AUTH_RESP_SIZE];
|
||||
|
||||
memset(p21, '\0', 21);
|
||||
memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE);
|
||||
rc = ksmbd_enc_p24(p21, sess->ntlmssp.cryptkey, key);
|
||||
if (rc) {
|
||||
pr_err("password processing failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
ksmbd_enc_md4(sess->sess_key, user_passkey(sess->user),
|
||||
CIFS_SMB1_SESSKEY_SIZE);
|
||||
memcpy(sess->sess_key + CIFS_SMB1_SESSKEY_SIZE, key,
|
||||
CIFS_AUTH_RESP_SIZE);
|
||||
sess->sequence_number = 1;
|
||||
|
||||
if (strncmp(pw_buf, key, CIFS_AUTH_RESP_SIZE) != 0) {
|
||||
ksmbd_debug(AUTH, "ntlmv1 authentication failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ksmbd_debug(AUTH, "ntlmv1 authentication pass\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_auth_ntlmv2() - NTLMv2 authentication handler
|
||||
* @sess: session of connection
|
||||
@ -441,44 +285,6 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* __ksmbd_auth_ntlmv2() - NTLM2(extended security) authentication handler
|
||||
* @sess: session of connection
|
||||
* @client_nonce: client nonce from LM response.
|
||||
* @ntlm_resp: ntlm response data from client.
|
||||
*
|
||||
* Return: 0 on success, error number on error
|
||||
*/
|
||||
static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, char *client_nonce,
|
||||
char *ntlm_resp)
|
||||
{
|
||||
char sess_key[CIFS_SMB1_SESSKEY_SIZE] = {0};
|
||||
int rc;
|
||||
unsigned char p21[21];
|
||||
char key[CIFS_AUTH_RESP_SIZE];
|
||||
|
||||
rc = ksmbd_enc_update_sess_key(sess_key,
|
||||
client_nonce,
|
||||
(char *)sess->ntlmssp.cryptkey, 8);
|
||||
if (rc) {
|
||||
pr_err("password processing failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(p21, '\0', 21);
|
||||
memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE);
|
||||
rc = ksmbd_enc_p24(p21, sess_key, key);
|
||||
if (rc) {
|
||||
pr_err("password processing failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memcmp(ntlm_resp, key, CIFS_AUTH_RESP_SIZE) != 0)
|
||||
rc = -EINVAL;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_decode_ntlmssp_auth_blob() - helper function to construct
|
||||
* authenticate blob
|
||||
@ -512,17 +318,6 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
|
||||
nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset);
|
||||
nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length);
|
||||
|
||||
/* process NTLM authentication */
|
||||
if (nt_len == CIFS_AUTH_RESP_SIZE) {
|
||||
if (le32_to_cpu(authblob->NegotiateFlags) &
|
||||
NTLMSSP_NEGOTIATE_EXTENDED_SEC)
|
||||
return __ksmbd_auth_ntlmv2(sess, (char *)authblob +
|
||||
lm_off, (char *)authblob + nt_off);
|
||||
else
|
||||
return ksmbd_auth_ntlm(sess, (char *)authblob +
|
||||
nt_off);
|
||||
}
|
||||
|
||||
/* TODO : use domain name that imported from configuration file */
|
||||
domain_name = smb_strndup_from_utf16((const char *)authblob +
|
||||
le32_to_cpu(authblob->DomainName.BufferOffset),
|
||||
|
@ -81,12 +81,6 @@ static struct shash_desc *alloc_shash_desc(int id)
|
||||
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;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@ -214,16 +208,6 @@ 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;
|
||||
|
@ -15,8 +15,6 @@ enum {
|
||||
CRYPTO_SHASH_CMACAES,
|
||||
CRYPTO_SHASH_SHA256,
|
||||
CRYPTO_SHASH_SHA512,
|
||||
CRYPTO_SHASH_MD4,
|
||||
CRYPTO_SHASH_MD5,
|
||||
CRYPTO_SHASH_MAX,
|
||||
};
|
||||
|
||||
@ -43,8 +41,6 @@ struct ksmbd_crypto_ctx {
|
||||
#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)\
|
||||
@ -52,8 +48,6 @@ struct ksmbd_crypto_ctx {
|
||||
#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_AES_GCM])
|
||||
#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM])
|
||||
@ -64,8 +58,6 @@ 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);
|
||||
|
@ -162,17 +162,14 @@ char *convert_to_nt_pathname(char *filename)
|
||||
{
|
||||
char *ab_pathname;
|
||||
|
||||
if (strlen(filename) == 0) {
|
||||
ab_pathname = kmalloc(2, GFP_KERNEL);
|
||||
ab_pathname[0] = '\\';
|
||||
ab_pathname[1] = '\0';
|
||||
} else {
|
||||
ab_pathname = kstrdup(filename, GFP_KERNEL);
|
||||
if (!ab_pathname)
|
||||
return NULL;
|
||||
if (strlen(filename) == 0)
|
||||
filename = "\\";
|
||||
|
||||
ksmbd_conv_path_to_windows(ab_pathname);
|
||||
}
|
||||
ab_pathname = kstrdup(filename, GFP_KERNEL);
|
||||
if (!ab_pathname)
|
||||
return NULL;
|
||||
|
||||
ksmbd_conv_path_to_windows(ab_pathname);
|
||||
return ab_pathname;
|
||||
}
|
||||
|
||||
|
@ -1451,26 +1451,47 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
|
||||
*/
|
||||
struct create_context *smb2_find_context_vals(void *open_req, const char *tag)
|
||||
{
|
||||
char *data_offset;
|
||||
struct create_context *cc;
|
||||
unsigned int next = 0;
|
||||
char *name;
|
||||
struct smb2_create_req *req = (struct smb2_create_req *)open_req;
|
||||
unsigned int remain_len, name_off, name_len, value_off, value_len,
|
||||
cc_len;
|
||||
|
||||
data_offset = (char *)req + 4 + le32_to_cpu(req->CreateContextsOffset);
|
||||
cc = (struct create_context *)data_offset;
|
||||
/*
|
||||
* CreateContextsOffset and CreateContextsLength are guaranteed to
|
||||
* be valid because of ksmbd_smb2_check_message().
|
||||
*/
|
||||
cc = (struct create_context *)((char *)req + 4 +
|
||||
le32_to_cpu(req->CreateContextsOffset));
|
||||
remain_len = le32_to_cpu(req->CreateContextsLength);
|
||||
do {
|
||||
int val;
|
||||
|
||||
cc = (struct create_context *)((char *)cc + next);
|
||||
name = le16_to_cpu(cc->NameOffset) + (char *)cc;
|
||||
val = le16_to_cpu(cc->NameLength);
|
||||
if (val < 4)
|
||||
if (remain_len < offsetof(struct create_context, Buffer))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (memcmp(name, tag, val) == 0)
|
||||
return cc;
|
||||
next = le32_to_cpu(cc->Next);
|
||||
name_off = le16_to_cpu(cc->NameOffset);
|
||||
name_len = le16_to_cpu(cc->NameLength);
|
||||
value_off = le16_to_cpu(cc->DataOffset);
|
||||
value_len = le32_to_cpu(cc->DataLength);
|
||||
cc_len = next ? next : remain_len;
|
||||
|
||||
if ((next & 0x7) != 0 ||
|
||||
next > remain_len ||
|
||||
name_off != offsetof(struct create_context, Buffer) ||
|
||||
name_len < 4 ||
|
||||
name_off + name_len > cc_len ||
|
||||
(value_off & 0x7) != 0 ||
|
||||
(value_off && (value_off < name_off + name_len)) ||
|
||||
((u64)value_off + value_len > cc_len))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
name = (char *)cc + name_off;
|
||||
if (memcmp(name, tag, name_len) == 0)
|
||||
return cc;
|
||||
|
||||
remain_len -= next;
|
||||
} while (next != 0);
|
||||
|
||||
return NULL;
|
||||
|
@ -459,13 +459,22 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work)
|
||||
bool is_chained_smb2_message(struct ksmbd_work *work)
|
||||
{
|
||||
struct smb2_hdr *hdr = work->request_buf;
|
||||
unsigned int len;
|
||||
unsigned int len, next_cmd;
|
||||
|
||||
if (hdr->ProtocolId != SMB2_PROTO_NUMBER)
|
||||
return false;
|
||||
|
||||
hdr = ksmbd_req_buf_next(work);
|
||||
if (le32_to_cpu(hdr->NextCommand) > 0) {
|
||||
next_cmd = le32_to_cpu(hdr->NextCommand);
|
||||
if (next_cmd > 0) {
|
||||
if ((u64)work->next_smb2_rcv_hdr_off + next_cmd +
|
||||
__SMB2_HEADER_STRUCTURE_SIZE >
|
||||
get_rfc1002_len(work->request_buf)) {
|
||||
pr_err("next command(%u) offset exceeds smb msg size\n",
|
||||
next_cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
ksmbd_debug(SMB, "got SMB2 chained command\n");
|
||||
init_chained_smb2_rsp(work);
|
||||
return true;
|
||||
@ -1058,6 +1067,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
|
||||
struct smb2_negotiate_req *req = work->request_buf;
|
||||
struct smb2_negotiate_rsp *rsp = work->response_buf;
|
||||
int rc = 0;
|
||||
unsigned int smb2_buf_len, smb2_neg_size;
|
||||
__le32 status;
|
||||
|
||||
ksmbd_debug(SMB, "Received negotiate request\n");
|
||||
@ -1075,6 +1085,44 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
smb2_buf_len = get_rfc1002_len(work->request_buf);
|
||||
smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects) - 4;
|
||||
if (smb2_neg_size > smb2_buf_len) {
|
||||
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
||||
rc = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (conn->dialect == SMB311_PROT_ID) {
|
||||
unsigned int nego_ctxt_off = le32_to_cpu(req->NegotiateContextOffset);
|
||||
|
||||
if (smb2_buf_len < nego_ctxt_off) {
|
||||
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
||||
rc = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (smb2_neg_size > nego_ctxt_off) {
|
||||
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
||||
rc = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) >
|
||||
nego_ctxt_off) {
|
||||
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
||||
rc = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
} else {
|
||||
if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) >
|
||||
smb2_buf_len) {
|
||||
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
||||
rc = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
conn->cli_cap = le32_to_cpu(req->Capabilities);
|
||||
switch (conn->dialect) {
|
||||
case SMB311_PROT_ID:
|
||||
@ -2093,16 +2141,22 @@ out:
|
||||
* smb2_set_ea() - handler for setting extended attributes using set
|
||||
* info command
|
||||
* @eabuf: set info command buffer
|
||||
* @buf_len: set info command buffer length
|
||||
* @path: dentry path for get ea
|
||||
*
|
||||
* Return: 0 on success, otherwise error
|
||||
*/
|
||||
static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path)
|
||||
static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
|
||||
struct path *path)
|
||||
{
|
||||
struct user_namespace *user_ns = mnt_user_ns(path->mnt);
|
||||
char *attr_name = NULL, *value;
|
||||
int rc = 0;
|
||||
int next = 0;
|
||||
unsigned int next = 0;
|
||||
|
||||
if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength +
|
||||
le16_to_cpu(eabuf->EaValueLength))
|
||||
return -EINVAL;
|
||||
|
||||
attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL);
|
||||
if (!attr_name)
|
||||
@ -2167,7 +2221,13 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path)
|
||||
|
||||
next:
|
||||
next = le32_to_cpu(eabuf->NextEntryOffset);
|
||||
if (next == 0 || buf_len < next)
|
||||
break;
|
||||
buf_len -= next;
|
||||
eabuf = (struct smb2_ea_info *)((char *)eabuf + next);
|
||||
if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength))
|
||||
break;
|
||||
|
||||
} while (next != 0);
|
||||
|
||||
kfree(attr_name);
|
||||
@ -2367,6 +2427,10 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work,
|
||||
ksmbd_debug(SMB,
|
||||
"Set ACLs using SMB2_CREATE_SD_BUFFER context\n");
|
||||
sd_buf = (struct create_sd_buf_req *)context;
|
||||
if (le16_to_cpu(context->DataOffset) +
|
||||
le32_to_cpu(context->DataLength) <
|
||||
sizeof(struct create_sd_buf_req))
|
||||
return -EINVAL;
|
||||
return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd,
|
||||
le32_to_cpu(sd_buf->ccontext.DataLength), true);
|
||||
}
|
||||
@ -2561,6 +2625,12 @@ int smb2_open(struct ksmbd_work *work)
|
||||
goto err_out1;
|
||||
} else if (context) {
|
||||
ea_buf = (struct create_ea_buf_req *)context;
|
||||
if (le16_to_cpu(context->DataOffset) +
|
||||
le32_to_cpu(context->DataLength) <
|
||||
sizeof(struct create_ea_buf_req)) {
|
||||
rc = -EINVAL;
|
||||
goto err_out1;
|
||||
}
|
||||
if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) {
|
||||
rsp->hdr.Status = STATUS_ACCESS_DENIED;
|
||||
rc = -EACCES;
|
||||
@ -2599,6 +2669,12 @@ int smb2_open(struct ksmbd_work *work)
|
||||
} else if (context) {
|
||||
struct create_posix *posix =
|
||||
(struct create_posix *)context;
|
||||
if (le16_to_cpu(context->DataOffset) +
|
||||
le32_to_cpu(context->DataLength) <
|
||||
sizeof(struct create_posix)) {
|
||||
rc = -EINVAL;
|
||||
goto err_out1;
|
||||
}
|
||||
ksmbd_debug(SMB, "get posix context\n");
|
||||
|
||||
posix_mode = le32_to_cpu(posix->Mode);
|
||||
@ -2748,7 +2824,15 @@ int smb2_open(struct ksmbd_work *work)
|
||||
created = true;
|
||||
user_ns = mnt_user_ns(path.mnt);
|
||||
if (ea_buf) {
|
||||
rc = smb2_set_ea(&ea_buf->ea, &path);
|
||||
if (le32_to_cpu(ea_buf->ccontext.DataLength) <
|
||||
sizeof(struct smb2_ea_info)) {
|
||||
rc = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
rc = smb2_set_ea(&ea_buf->ea,
|
||||
le32_to_cpu(ea_buf->ccontext.DataLength),
|
||||
&path);
|
||||
if (rc == -EOPNOTSUPP)
|
||||
rc = 0;
|
||||
else if (rc)
|
||||
@ -2981,9 +3065,16 @@ int smb2_open(struct ksmbd_work *work)
|
||||
rc = PTR_ERR(az_req);
|
||||
goto err_out;
|
||||
} else if (az_req) {
|
||||
loff_t alloc_size = le64_to_cpu(az_req->AllocationSize);
|
||||
loff_t alloc_size;
|
||||
int err;
|
||||
|
||||
if (le16_to_cpu(az_req->ccontext.DataOffset) +
|
||||
le32_to_cpu(az_req->ccontext.DataLength) <
|
||||
sizeof(struct create_alloc_size_req)) {
|
||||
rc = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
alloc_size = le64_to_cpu(az_req->AllocationSize);
|
||||
ksmbd_debug(SMB,
|
||||
"request smb2 create allocate size : %llu\n",
|
||||
alloc_size);
|
||||
@ -4152,7 +4243,7 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp,
|
||||
static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
|
||||
struct ksmbd_file *fp, void *rsp_org)
|
||||
{
|
||||
struct smb2_file_all_info *basic_info;
|
||||
struct smb2_file_basic_info *basic_info;
|
||||
struct kstat stat;
|
||||
u64 time;
|
||||
|
||||
@ -4162,7 +4253,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
basic_info = (struct smb2_file_all_info *)rsp->Buffer;
|
||||
basic_info = (struct smb2_file_basic_info *)rsp->Buffer;
|
||||
generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp),
|
||||
&stat);
|
||||
basic_info->CreationTime = cpu_to_le64(fp->create_time);
|
||||
@ -4175,9 +4266,8 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
|
||||
basic_info->Attributes = fp->f_ci->m_fattr;
|
||||
basic_info->Pad1 = 0;
|
||||
rsp->OutputBufferLength =
|
||||
cpu_to_le32(offsetof(struct smb2_file_all_info, AllocationSize));
|
||||
inc_rfc1001_len(rsp_org, offsetof(struct smb2_file_all_info,
|
||||
AllocationSize));
|
||||
cpu_to_le32(sizeof(struct smb2_file_basic_info));
|
||||
inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -5333,7 +5423,7 @@ out:
|
||||
static int smb2_create_link(struct ksmbd_work *work,
|
||||
struct ksmbd_share_config *share,
|
||||
struct smb2_file_link_info *file_info,
|
||||
struct file *filp,
|
||||
unsigned int buf_len, struct file *filp,
|
||||
struct nls_table *local_nls)
|
||||
{
|
||||
char *link_name = NULL, *target_name = NULL, *pathname = NULL;
|
||||
@ -5341,6 +5431,10 @@ static int smb2_create_link(struct ksmbd_work *work,
|
||||
bool file_present = true;
|
||||
int rc;
|
||||
|
||||
if (buf_len < (u64)sizeof(struct smb2_file_link_info) +
|
||||
le32_to_cpu(file_info->FileNameLength))
|
||||
return -EINVAL;
|
||||
|
||||
ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n");
|
||||
pathname = kmalloc(PATH_MAX, GFP_KERNEL);
|
||||
if (!pathname)
|
||||
@ -5400,10 +5494,10 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int set_file_basic_info(struct ksmbd_file *fp, char *buf,
|
||||
static int set_file_basic_info(struct ksmbd_file *fp,
|
||||
struct smb2_file_basic_info *file_info,
|
||||
struct ksmbd_share_config *share)
|
||||
{
|
||||
struct smb2_file_all_info *file_info;
|
||||
struct iattr attrs;
|
||||
struct timespec64 ctime;
|
||||
struct file *filp;
|
||||
@ -5414,7 +5508,6 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf,
|
||||
if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE))
|
||||
return -EACCES;
|
||||
|
||||
file_info = (struct smb2_file_all_info *)buf;
|
||||
attrs.ia_valid = 0;
|
||||
filp = fp->filp;
|
||||
inode = file_inode(filp);
|
||||
@ -5491,7 +5584,8 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf,
|
||||
}
|
||||
|
||||
static int set_file_allocation_info(struct ksmbd_work *work,
|
||||
struct ksmbd_file *fp, char *buf)
|
||||
struct ksmbd_file *fp,
|
||||
struct smb2_file_alloc_info *file_alloc_info)
|
||||
{
|
||||
/*
|
||||
* TODO : It's working fine only when store dos attributes
|
||||
@ -5499,7 +5593,6 @@ static int set_file_allocation_info(struct ksmbd_work *work,
|
||||
* properly with any smb.conf option
|
||||
*/
|
||||
|
||||
struct smb2_file_alloc_info *file_alloc_info;
|
||||
loff_t alloc_blks;
|
||||
struct inode *inode;
|
||||
int rc;
|
||||
@ -5507,7 +5600,6 @@ static int set_file_allocation_info(struct ksmbd_work *work,
|
||||
if (!(fp->daccess & FILE_WRITE_DATA_LE))
|
||||
return -EACCES;
|
||||
|
||||
file_alloc_info = (struct smb2_file_alloc_info *)buf;
|
||||
alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;
|
||||
inode = file_inode(fp->filp);
|
||||
|
||||
@ -5543,9 +5635,8 @@ static int set_file_allocation_info(struct ksmbd_work *work,
|
||||
}
|
||||
|
||||
static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
char *buf)
|
||||
struct smb2_file_eof_info *file_eof_info)
|
||||
{
|
||||
struct smb2_file_eof_info *file_eof_info;
|
||||
loff_t newsize;
|
||||
struct inode *inode;
|
||||
int rc;
|
||||
@ -5553,7 +5644,6 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
if (!(fp->daccess & FILE_WRITE_DATA_LE))
|
||||
return -EACCES;
|
||||
|
||||
file_eof_info = (struct smb2_file_eof_info *)buf;
|
||||
newsize = le64_to_cpu(file_eof_info->EndOfFile);
|
||||
inode = file_inode(fp->filp);
|
||||
|
||||
@ -5580,7 +5670,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
}
|
||||
|
||||
static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
char *buf)
|
||||
struct smb2_file_rename_info *rename_info,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
struct user_namespace *user_ns;
|
||||
struct ksmbd_file *parent_fp;
|
||||
@ -5593,6 +5684,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (buf_len < (u64)sizeof(struct smb2_file_rename_info) +
|
||||
le32_to_cpu(rename_info->FileNameLength))
|
||||
return -EINVAL;
|
||||
|
||||
user_ns = file_mnt_user_ns(fp->filp);
|
||||
if (ksmbd_stream_fd(fp))
|
||||
goto next;
|
||||
@ -5615,14 +5710,13 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
}
|
||||
}
|
||||
next:
|
||||
return smb2_rename(work, fp, user_ns,
|
||||
(struct smb2_file_rename_info *)buf,
|
||||
return smb2_rename(work, fp, user_ns, rename_info,
|
||||
work->sess->conn->local_nls);
|
||||
}
|
||||
|
||||
static int set_file_disposition_info(struct ksmbd_file *fp, char *buf)
|
||||
static int set_file_disposition_info(struct ksmbd_file *fp,
|
||||
struct smb2_file_disposition_info *file_info)
|
||||
{
|
||||
struct smb2_file_disposition_info *file_info;
|
||||
struct inode *inode;
|
||||
|
||||
if (!(fp->daccess & FILE_DELETE_LE)) {
|
||||
@ -5631,7 +5725,6 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf)
|
||||
}
|
||||
|
||||
inode = file_inode(fp->filp);
|
||||
file_info = (struct smb2_file_disposition_info *)buf;
|
||||
if (file_info->DeletePending) {
|
||||
if (S_ISDIR(inode->i_mode) &&
|
||||
ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY)
|
||||
@ -5643,15 +5736,14 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_file_position_info(struct ksmbd_file *fp, char *buf)
|
||||
static int set_file_position_info(struct ksmbd_file *fp,
|
||||
struct smb2_file_pos_info *file_info)
|
||||
{
|
||||
struct smb2_file_pos_info *file_info;
|
||||
loff_t current_byte_offset;
|
||||
unsigned long sector_size;
|
||||
struct inode *inode;
|
||||
|
||||
inode = file_inode(fp->filp);
|
||||
file_info = (struct smb2_file_pos_info *)buf;
|
||||
current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset);
|
||||
sector_size = inode->i_sb->s_blocksize;
|
||||
|
||||
@ -5667,12 +5759,11 @@ static int set_file_position_info(struct ksmbd_file *fp, char *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_file_mode_info(struct ksmbd_file *fp, char *buf)
|
||||
static int set_file_mode_info(struct ksmbd_file *fp,
|
||||
struct smb2_file_mode_info *file_info)
|
||||
{
|
||||
struct smb2_file_mode_info *file_info;
|
||||
__le32 mode;
|
||||
|
||||
file_info = (struct smb2_file_mode_info *)buf;
|
||||
mode = file_info->Mode;
|
||||
|
||||
if ((mode & ~FILE_MODE_INFO_MASK) ||
|
||||
@ -5702,40 +5793,74 @@ static int set_file_mode_info(struct ksmbd_file *fp, char *buf)
|
||||
* TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH
|
||||
*/
|
||||
static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
int info_class, char *buf,
|
||||
struct smb2_set_info_req *req,
|
||||
struct ksmbd_share_config *share)
|
||||
{
|
||||
switch (info_class) {
|
||||
unsigned int buf_len = le32_to_cpu(req->BufferLength);
|
||||
|
||||
switch (req->FileInfoClass) {
|
||||
case FILE_BASIC_INFORMATION:
|
||||
return set_file_basic_info(fp, buf, share);
|
||||
{
|
||||
if (buf_len < sizeof(struct smb2_file_basic_info))
|
||||
return -EINVAL;
|
||||
|
||||
return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share);
|
||||
}
|
||||
case FILE_ALLOCATION_INFORMATION:
|
||||
return set_file_allocation_info(work, fp, buf);
|
||||
{
|
||||
if (buf_len < sizeof(struct smb2_file_alloc_info))
|
||||
return -EINVAL;
|
||||
|
||||
return set_file_allocation_info(work, fp,
|
||||
(struct smb2_file_alloc_info *)req->Buffer);
|
||||
}
|
||||
case FILE_END_OF_FILE_INFORMATION:
|
||||
return set_end_of_file_info(work, fp, buf);
|
||||
{
|
||||
if (buf_len < sizeof(struct smb2_file_eof_info))
|
||||
return -EINVAL;
|
||||
|
||||
return set_end_of_file_info(work, fp,
|
||||
(struct smb2_file_eof_info *)req->Buffer);
|
||||
}
|
||||
case FILE_RENAME_INFORMATION:
|
||||
{
|
||||
if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) {
|
||||
ksmbd_debug(SMB,
|
||||
"User does not have write permission\n");
|
||||
return -EACCES;
|
||||
}
|
||||
return set_rename_info(work, fp, buf);
|
||||
|
||||
if (buf_len < sizeof(struct smb2_file_rename_info))
|
||||
return -EINVAL;
|
||||
|
||||
return set_rename_info(work, fp,
|
||||
(struct smb2_file_rename_info *)req->Buffer,
|
||||
buf_len);
|
||||
}
|
||||
case FILE_LINK_INFORMATION:
|
||||
return smb2_create_link(work, work->tcon->share_conf,
|
||||
(struct smb2_file_link_info *)buf, fp->filp,
|
||||
work->sess->conn->local_nls);
|
||||
{
|
||||
if (buf_len < sizeof(struct smb2_file_link_info))
|
||||
return -EINVAL;
|
||||
|
||||
return smb2_create_link(work, work->tcon->share_conf,
|
||||
(struct smb2_file_link_info *)req->Buffer,
|
||||
buf_len, fp->filp,
|
||||
work->sess->conn->local_nls);
|
||||
}
|
||||
case FILE_DISPOSITION_INFORMATION:
|
||||
{
|
||||
if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) {
|
||||
ksmbd_debug(SMB,
|
||||
"User does not have write permission\n");
|
||||
return -EACCES;
|
||||
}
|
||||
return set_file_disposition_info(fp, buf);
|
||||
|
||||
if (buf_len < sizeof(struct smb2_file_disposition_info))
|
||||
return -EINVAL;
|
||||
|
||||
return set_file_disposition_info(fp,
|
||||
(struct smb2_file_disposition_info *)req->Buffer);
|
||||
}
|
||||
case FILE_FULL_EA_INFORMATION:
|
||||
{
|
||||
if (!(fp->daccess & FILE_WRITE_EA_LE)) {
|
||||
@ -5744,18 +5869,29 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return smb2_set_ea((struct smb2_ea_info *)buf,
|
||||
&fp->filp->f_path);
|
||||
}
|
||||
if (buf_len < sizeof(struct smb2_ea_info))
|
||||
return -EINVAL;
|
||||
|
||||
return smb2_set_ea((struct smb2_ea_info *)req->Buffer,
|
||||
buf_len, &fp->filp->f_path);
|
||||
}
|
||||
case FILE_POSITION_INFORMATION:
|
||||
return set_file_position_info(fp, buf);
|
||||
{
|
||||
if (buf_len < sizeof(struct smb2_file_pos_info))
|
||||
return -EINVAL;
|
||||
|
||||
return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer);
|
||||
}
|
||||
case FILE_MODE_INFORMATION:
|
||||
return set_file_mode_info(fp, buf);
|
||||
{
|
||||
if (buf_len < sizeof(struct smb2_file_mode_info))
|
||||
return -EINVAL;
|
||||
|
||||
return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("Unimplemented Fileinfoclass :%d\n", info_class);
|
||||
pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -5816,8 +5952,7 @@ int smb2_set_info(struct ksmbd_work *work)
|
||||
switch (req->InfoType) {
|
||||
case SMB2_O_INFO_FILE:
|
||||
ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n");
|
||||
rc = smb2_set_info_file(work, fp, req->FileInfoClass,
|
||||
req->Buffer, work->tcon->share_conf);
|
||||
rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf);
|
||||
break;
|
||||
case SMB2_O_INFO_SECURITY:
|
||||
ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n");
|
||||
@ -8171,7 +8306,8 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work)
|
||||
|
||||
WORK_BUFFERS(work, req, rsp);
|
||||
|
||||
if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE)
|
||||
if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE &&
|
||||
conn->preauth_info)
|
||||
ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp,
|
||||
conn->preauth_info->Preauth_HashValue);
|
||||
|
||||
@ -8278,16 +8414,8 @@ int smb3_decrypt_req(struct ksmbd_work *work)
|
||||
unsigned int buf_data_size = pdu_length + 4 -
|
||||
sizeof(struct smb2_transform_hdr);
|
||||
struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
|
||||
unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
|
||||
int rc = 0;
|
||||
|
||||
sess = ksmbd_session_lookup_all(conn, le64_to_cpu(tr_hdr->SessionId));
|
||||
if (!sess) {
|
||||
pr_err("invalid session id(%llx) in transform header\n",
|
||||
le64_to_cpu(tr_hdr->SessionId));
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
if (pdu_length + 4 <
|
||||
sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr)) {
|
||||
pr_err("Transform message is too small (%u)\n",
|
||||
@ -8295,11 +8423,19 @@ int smb3_decrypt_req(struct ksmbd_work *work)
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
if (pdu_length + 4 < orig_len + sizeof(struct smb2_transform_hdr)) {
|
||||
if (pdu_length + 4 <
|
||||
le32_to_cpu(tr_hdr->OriginalMessageSize) + sizeof(struct smb2_transform_hdr)) {
|
||||
pr_err("Transform message is broken\n");
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
sess = ksmbd_session_lookup_all(conn, le64_to_cpu(tr_hdr->SessionId));
|
||||
if (!sess) {
|
||||
pr_err("invalid session id(%llx) in transform header\n",
|
||||
le64_to_cpu(tr_hdr->SessionId));
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
iov[0].iov_base = buf;
|
||||
iov[0].iov_len = sizeof(struct smb2_transform_hdr);
|
||||
iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr);
|
||||
|
@ -1464,6 +1464,15 @@ struct smb2_file_all_info { /* data block encoding of response to level 18 */
|
||||
char FileName[1];
|
||||
} __packed; /* level 18 Query */
|
||||
|
||||
struct smb2_file_basic_info { /* data block encoding of response to level 18 */
|
||||
__le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */
|
||||
__le64 LastAccessTime;
|
||||
__le64 LastWriteTime;
|
||||
__le64 ChangeTime;
|
||||
__le32 Attributes;
|
||||
__u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */
|
||||
} __packed;
|
||||
|
||||
struct smb2_file_alt_name_info {
|
||||
__le32 FileNameLength;
|
||||
char FileName[0];
|
||||
|
@ -155,20 +155,7 @@ int ksmbd_verify_smb_message(struct ksmbd_work *work)
|
||||
*/
|
||||
bool ksmbd_smb_request(struct ksmbd_conn *conn)
|
||||
{
|
||||
int type = *(char *)conn->request_buf;
|
||||
|
||||
switch (type) {
|
||||
case RFC1002_SESSION_MESSAGE:
|
||||
/* Regular SMB request */
|
||||
return true;
|
||||
case RFC1002_SESSION_KEEP_ALIVE:
|
||||
ksmbd_debug(SMB, "RFC 1002 session keep alive\n");
|
||||
break;
|
||||
default:
|
||||
ksmbd_debug(SMB, "RFC 1002 unknown request type 0x%x\n", type);
|
||||
}
|
||||
|
||||
return false;
|
||||
return conn->request_buf[0] == 0;
|
||||
}
|
||||
|
||||
static bool supported_protocol(int idx)
|
||||
@ -182,10 +169,12 @@ static bool supported_protocol(int idx)
|
||||
idx <= server_conf.max_protocol);
|
||||
}
|
||||
|
||||
static char *next_dialect(char *dialect, int *next_off)
|
||||
static char *next_dialect(char *dialect, int *next_off, int bcount)
|
||||
{
|
||||
dialect = dialect + *next_off;
|
||||
*next_off = strlen(dialect);
|
||||
*next_off = strnlen(dialect, bcount);
|
||||
if (dialect[*next_off] != '\0')
|
||||
return NULL;
|
||||
return dialect;
|
||||
}
|
||||
|
||||
@ -200,7 +189,9 @@ static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count)
|
||||
dialect = cli_dialects;
|
||||
bcount = le16_to_cpu(byte_count);
|
||||
do {
|
||||
dialect = next_dialect(dialect, &next);
|
||||
dialect = next_dialect(dialect, &next, bcount);
|
||||
if (!dialect)
|
||||
break;
|
||||
ksmbd_debug(SMB, "client requested dialect %s\n",
|
||||
dialect);
|
||||
if (!strcmp(dialect, smb1_protos[i].name)) {
|
||||
@ -248,13 +239,22 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count)
|
||||
|
||||
static int ksmbd_negotiate_smb_dialect(void *buf)
|
||||
{
|
||||
__le32 proto;
|
||||
int smb_buf_length = get_rfc1002_len(buf);
|
||||
__le32 proto = ((struct smb2_hdr *)buf)->ProtocolId;
|
||||
|
||||
proto = ((struct smb2_hdr *)buf)->ProtocolId;
|
||||
if (proto == SMB2_PROTO_NUMBER) {
|
||||
struct smb2_negotiate_req *req;
|
||||
int smb2_neg_size =
|
||||
offsetof(struct smb2_negotiate_req, Dialects) - 4;
|
||||
|
||||
req = (struct smb2_negotiate_req *)buf;
|
||||
if (smb2_neg_size > smb_buf_length)
|
||||
goto err_out;
|
||||
|
||||
if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) >
|
||||
smb_buf_length)
|
||||
goto err_out;
|
||||
|
||||
return ksmbd_lookup_dialect_by_id(req->Dialects,
|
||||
req->DialectCount);
|
||||
}
|
||||
@ -264,10 +264,19 @@ static int ksmbd_negotiate_smb_dialect(void *buf)
|
||||
struct smb_negotiate_req *req;
|
||||
|
||||
req = (struct smb_negotiate_req *)buf;
|
||||
if (le16_to_cpu(req->ByteCount) < 2)
|
||||
goto err_out;
|
||||
|
||||
if (offsetof(struct smb_negotiate_req, DialectsArray) - 4 +
|
||||
le16_to_cpu(req->ByteCount) > smb_buf_length) {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return ksmbd_lookup_dialect_by_name(req->DialectsArray,
|
||||
req->ByteCount);
|
||||
}
|
||||
|
||||
err_out:
|
||||
return BAD_PROT_ID;
|
||||
}
|
||||
|
||||
|
@ -48,14 +48,6 @@
|
||||
#define CIFS_DEFAULT_IOSIZE (64 * 1024)
|
||||
#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
|
||||
|
||||
/* RFC 1002 session packet types */
|
||||
#define RFC1002_SESSION_MESSAGE 0x00
|
||||
#define RFC1002_SESSION_REQUEST 0x81
|
||||
#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82
|
||||
#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83
|
||||
#define RFC1002_RETARGET_SESSION_RESPONSE 0x84
|
||||
#define RFC1002_SESSION_KEEP_ALIVE 0x85
|
||||
|
||||
/* Responses when opening a file. */
|
||||
#define F_SUPERSEDED 0
|
||||
#define F_OPENED 1
|
||||
|
@ -380,7 +380,7 @@ static void parse_dacl(struct user_namespace *user_ns,
|
||||
{
|
||||
int i, ret;
|
||||
int num_aces = 0;
|
||||
int acl_size;
|
||||
unsigned int acl_size;
|
||||
char *acl_base;
|
||||
struct smb_ace **ppace;
|
||||
struct posix_acl_entry *cf_pace, *cf_pdace;
|
||||
@ -392,7 +392,7 @@ static void parse_dacl(struct user_namespace *user_ns,
|
||||
return;
|
||||
|
||||
/* validate that we do not go past end of acl */
|
||||
if (end_of_acl <= (char *)pdacl ||
|
||||
if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) ||
|
||||
end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
|
||||
pr_err("ACL too small to parse DACL\n");
|
||||
return;
|
||||
@ -431,8 +431,22 @@ static void parse_dacl(struct user_namespace *user_ns,
|
||||
* user/group/other have no permissions
|
||||
*/
|
||||
for (i = 0; i < num_aces; ++i) {
|
||||
if (end_of_acl - acl_base < acl_size)
|
||||
break;
|
||||
|
||||
ppace[i] = (struct smb_ace *)(acl_base + acl_size);
|
||||
acl_base = (char *)ppace[i];
|
||||
acl_size = offsetof(struct smb_ace, sid) +
|
||||
offsetof(struct smb_sid, sub_auth);
|
||||
|
||||
if (end_of_acl - acl_base < acl_size ||
|
||||
ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
|
||||
(end_of_acl - acl_base <
|
||||
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
|
||||
(le16_to_cpu(ppace[i]->size) <
|
||||
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth))
|
||||
break;
|
||||
|
||||
acl_size = le16_to_cpu(ppace[i]->size);
|
||||
ppace[i]->access_req =
|
||||
smb_map_generic_desired_access(ppace[i]->access_req);
|
||||
@ -807,6 +821,9 @@ int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
|
||||
if (!pntsd)
|
||||
return -EIO;
|
||||
|
||||
if (acl_len < sizeof(struct smb_ntsd))
|
||||
return -EINVAL;
|
||||
|
||||
owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
|
||||
le32_to_cpu(pntsd->osidoffset));
|
||||
group_sid_ptr = (struct smb_sid *)((char *)pntsd +
|
||||
|
@ -215,7 +215,7 @@ out_error:
|
||||
* ksmbd_kthread_fn() - listen to new SMB connections and callback server
|
||||
* @p: arguments to forker thread
|
||||
*
|
||||
* Return: Returns a task_struct or ERR_PTR
|
||||
* Return: 0 on success, error number otherwise
|
||||
*/
|
||||
static int ksmbd_kthread_fn(void *p)
|
||||
{
|
||||
@ -387,7 +387,7 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket)
|
||||
/**
|
||||
* create_socket - create socket for ksmbd/0
|
||||
*
|
||||
* Return: Returns a task_struct or ERR_PTR
|
||||
* Return: 0 on success, error number otherwise
|
||||
*/
|
||||
static int create_socket(struct interface *iface)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user