summaryrefslogtreecommitdiff
path: root/fs/ksmbd/smb2pdu.c
diff options
context:
space:
mode:
authorNamjae Jeon <namjae.jeon@samsung.com>2021-07-21 10:03:19 +0900
committerNamjae Jeon <namjae.jeon@samsung.com>2021-07-22 09:56:00 +0900
commitaf320a739029f6f8c5c05e769fadaf88e9b7d34f (patch)
treebbc75384d94f3beca06698a40e00be2900a2825b /fs/ksmbd/smb2pdu.c
parent9223958816f9df133ae936c9371378ba1203e0da (diff)
ksmbd: add negotiate context verification
This patch add negotiate context verification code to check bounds. Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/ksmbd/smb2pdu.c')
-rw-r--r--fs/ksmbd/smb2pdu.c118
1 files changed, 62 insertions, 56 deletions
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c
index 77e42a572825..64a4d66997a3 100644
--- a/fs/ksmbd/smb2pdu.c
+++ b/fs/ksmbd/smb2pdu.c
@@ -835,10 +835,10 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt,
conn->cipher_type);
rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt);
- ctxt_size += sizeof(struct smb2_encryption_neg_context);
+ ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2;
/* Round to 8 byte boundary */
pneg_ctxt +=
- round_up(sizeof(struct smb2_encryption_neg_context),
+ round_up(sizeof(struct smb2_encryption_neg_context) + 2,
8);
}
@@ -850,9 +850,10 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
build_compression_ctxt((struct smb2_compression_ctx *)pneg_ctxt,
conn->compress_algorithm);
rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt);
- ctxt_size += sizeof(struct smb2_compression_ctx);
+ ctxt_size += sizeof(struct smb2_compression_ctx) + 2;
/* Round to 8 byte boundary */
- pneg_ctxt += round_up(sizeof(struct smb2_compression_ctx), 8);
+ pneg_ctxt += round_up(sizeof(struct smb2_compression_ctx) + 2,
+ 8);
}
if (conn->posix_ext_supported) {
@@ -881,16 +882,23 @@ static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn,
return err;
}
-static int decode_encrypt_ctxt(struct ksmbd_conn *conn,
- struct smb2_encryption_neg_context *pneg_ctxt)
+static void decode_encrypt_ctxt(struct ksmbd_conn *conn,
+ struct smb2_encryption_neg_context *pneg_ctxt,
+ int len_of_ctxts)
{
- int i;
int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount);
+ int i, cphs_size = cph_cnt * sizeof(__le16);
conn->cipher_type = 0;
+ if (sizeof(struct smb2_encryption_neg_context) + cphs_size >
+ len_of_ctxts) {
+ pr_err("Invalid cipher count(%d)\n", cph_cnt);
+ return;
+ }
+
if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION))
- goto out;
+ return;
for (i = 0; i < cph_cnt; i++) {
if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM ||
@@ -903,90 +911,88 @@ static int decode_encrypt_ctxt(struct ksmbd_conn *conn,
break;
}
}
-
-out:
- /*
- * Return encrypt context size in request.
- * So need to plus extra number of ciphers size.
- */
- return sizeof(struct smb2_encryption_neg_context) +
- ((cph_cnt - 1) * 2);
}
-static int decode_compress_ctxt(struct ksmbd_conn *conn,
- struct smb2_compression_ctx *pneg_ctxt)
+static void decode_compress_ctxt(struct ksmbd_conn *conn,
+ struct smb2_compression_ctx *pneg_ctxt)
{
- int algo_cnt = le16_to_cpu(pneg_ctxt->CompressionAlgorithmCount);
-
conn->compress_algorithm = SMB3_COMPRESS_NONE;
-
- /*
- * Return compression context size in request.
- * So need to plus extra number of CompressionAlgorithms size.
- */
- return sizeof(struct smb2_compression_ctx) +
- ((algo_cnt - 1) * 2);
}
static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn,
struct smb2_negotiate_req *req)
{
- int i = 0;
- __le32 status = 0;
/* +4 is to account for the RFC1001 len field */
- char *pneg_ctxt = (char *)req +
- le32_to_cpu(req->NegotiateContextOffset) + 4;
- __le16 *ContextType = (__le16 *)pneg_ctxt;
+ struct smb2_neg_context *pctx = (struct smb2_neg_context *)((char *)req + 4);
+ int i = 0, len_of_ctxts;
+ int offset = le32_to_cpu(req->NegotiateContextOffset);
int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount);
- int ctxt_size;
+ int len_of_smb = be32_to_cpu(req->hdr.smb2_buf_length);
+ __le32 status = STATUS_INVALID_PARAMETER;
+
+ ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt);
+ if (len_of_smb <= offset) {
+ ksmbd_debug(SMB, "Invalid response: negotiate context offset\n");
+ return status;
+ }
+
+ len_of_ctxts = len_of_smb - offset;
- ksmbd_debug(SMB, "negotiate context count = %d\n", neg_ctxt_cnt);
- status = STATUS_INVALID_PARAMETER;
while (i++ < neg_ctxt_cnt) {
- if (*ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) {
+ int clen;
+
+ /* check that offset is not beyond end of SMB */
+ if (len_of_ctxts == 0)
+ break;
+
+ if (len_of_ctxts < sizeof(struct smb2_neg_context))
+ break;
+
+ pctx = (struct smb2_neg_context *)((char *)pctx + offset);
+ clen = le16_to_cpu(pctx->DataLength);
+ if (clen + sizeof(struct smb2_neg_context) > len_of_ctxts)
+ break;
+
+ if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) {
ksmbd_debug(SMB,
"deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n");
if (conn->preauth_info->Preauth_HashId)
break;
status = decode_preauth_ctxt(conn,
- (struct smb2_preauth_neg_context *)pneg_ctxt);
- pneg_ctxt += DIV_ROUND_UP(sizeof(struct smb2_preauth_neg_context), 8) * 8;
- } else if (*ContextType == SMB2_ENCRYPTION_CAPABILITIES) {
+ (struct smb2_preauth_neg_context *)pctx);
+ if (status != STATUS_SUCCESS)
+ break;
+ } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) {
ksmbd_debug(SMB,
"deassemble SMB2_ENCRYPTION_CAPABILITIES context\n");
if (conn->cipher_type)
break;
- ctxt_size = decode_encrypt_ctxt(conn,
- (struct smb2_encryption_neg_context *)pneg_ctxt);
- pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8;
- } else if (*ContextType == SMB2_COMPRESSION_CAPABILITIES) {
+ decode_encrypt_ctxt(conn,
+ (struct smb2_encryption_neg_context *)pctx,
+ len_of_ctxts);
+ } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) {
ksmbd_debug(SMB,
"deassemble SMB2_COMPRESSION_CAPABILITIES context\n");
if (conn->compress_algorithm)
break;
- ctxt_size = decode_compress_ctxt(conn,
- (struct smb2_compression_ctx *)pneg_ctxt);
- pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8;
- } else if (*ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) {
+ decode_compress_ctxt(conn,
+ (struct smb2_compression_ctx *)pctx);
+ } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) {
ksmbd_debug(SMB,
"deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n");
- ctxt_size = sizeof(struct smb2_netname_neg_context);
- ctxt_size += DIV_ROUND_UP(le16_to_cpu(((struct smb2_netname_neg_context *)
- pneg_ctxt)->DataLength), 8) * 8;
- pneg_ctxt += ctxt_size;
- } else if (*ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) {
+ } else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) {
ksmbd_debug(SMB,
"deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n");
conn->posix_ext_supported = true;
- pneg_ctxt += DIV_ROUND_UP(sizeof(struct smb2_posix_neg_context), 8) * 8;
}
- ContextType = (__le16 *)pneg_ctxt;
- if (status != STATUS_SUCCESS)
- break;
+ /* offsets must be 8 byte aligned */
+ clen = (clen + 7) & ~0x7;
+ offset = clen + sizeof(struct smb2_neg_context);
+ len_of_ctxts -= clen + sizeof(struct smb2_neg_context);
}
return status;
}