Commit 442ff9eb authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

ksmbd: add validation in smb2 negotiate

This patch add validation to check request buffer check in smb2
negotiate and fix null pointer deferencing oops in smb3_preauth_hash_rsp()
that found from manual test.

Cc: Tom Talpey <tom@talpey.com>
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Ralph Böhme <slow@samba.org>
Cc: Hyunchul Lee <hyc.lee@gmail.com>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
Reviewed-by: default avatarRalph Boehme <slow@samba.org>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 9496e268
...@@ -1067,6 +1067,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) ...@@ -1067,6 +1067,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
struct smb2_negotiate_req *req = work->request_buf; struct smb2_negotiate_req *req = work->request_buf;
struct smb2_negotiate_rsp *rsp = work->response_buf; struct smb2_negotiate_rsp *rsp = work->response_buf;
int rc = 0; int rc = 0;
unsigned int smb2_buf_len, smb2_neg_size;
__le32 status; __le32 status;
ksmbd_debug(SMB, "Received negotiate request\n"); ksmbd_debug(SMB, "Received negotiate request\n");
...@@ -1084,6 +1085,44 @@ int smb2_handle_negotiate(struct ksmbd_work *work) ...@@ -1084,6 +1085,44 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
goto err_out; 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); conn->cli_cap = le32_to_cpu(req->Capabilities);
switch (conn->dialect) { switch (conn->dialect) {
case SMB311_PROT_ID: case SMB311_PROT_ID:
...@@ -8244,7 +8283,8 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) ...@@ -8244,7 +8283,8 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work)
WORK_BUFFERS(work, req, rsp); 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, ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp,
conn->preauth_info->Preauth_HashValue); conn->preauth_info->Preauth_HashValue);
......
...@@ -169,10 +169,12 @@ static bool supported_protocol(int idx) ...@@ -169,10 +169,12 @@ static bool supported_protocol(int idx)
idx <= server_conf.max_protocol); 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; dialect = dialect + *next_off;
*next_off = strlen(dialect); *next_off = strnlen(dialect, bcount);
if (dialect[*next_off] != '\0')
return NULL;
return dialect; return dialect;
} }
...@@ -187,7 +189,9 @@ static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) ...@@ -187,7 +189,9 @@ static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count)
dialect = cli_dialects; dialect = cli_dialects;
bcount = le16_to_cpu(byte_count); bcount = le16_to_cpu(byte_count);
do { do {
dialect = next_dialect(dialect, &next); dialect = next_dialect(dialect, &next, bcount);
if (!dialect)
break;
ksmbd_debug(SMB, "client requested dialect %s\n", ksmbd_debug(SMB, "client requested dialect %s\n",
dialect); dialect);
if (!strcmp(dialect, smb1_protos[i].name)) { if (!strcmp(dialect, smb1_protos[i].name)) {
...@@ -235,13 +239,22 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) ...@@ -235,13 +239,22 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count)
static int ksmbd_negotiate_smb_dialect(void *buf) 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) { if (proto == SMB2_PROTO_NUMBER) {
struct smb2_negotiate_req *req; struct smb2_negotiate_req *req;
int smb2_neg_size =
offsetof(struct smb2_negotiate_req, Dialects) - 4;
req = (struct smb2_negotiate_req *)buf; 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, return ksmbd_lookup_dialect_by_id(req->Dialects,
req->DialectCount); req->DialectCount);
} }
...@@ -251,10 +264,19 @@ static int ksmbd_negotiate_smb_dialect(void *buf) ...@@ -251,10 +264,19 @@ static int ksmbd_negotiate_smb_dialect(void *buf)
struct smb_negotiate_req *req; struct smb_negotiate_req *req;
req = (struct smb_negotiate_req *)buf; 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, return ksmbd_lookup_dialect_by_name(req->DialectsArray,
req->ByteCount); req->ByteCount);
} }
err_out:
return BAD_PROT_ID; return BAD_PROT_ID;
} }
......
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