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

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
Loading
Loading
Loading
Loading
+41 −1
Original line number Diff line number Diff line
@@ -1067,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");
@@ -1084,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:
@@ -8244,7 +8283,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);

+27 −5
Original line number Diff line number Diff line
@@ -169,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;
}

@@ -187,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)) {
@@ -235,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);
	}
@@ -251,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;
}