Commit 0d994cd4 authored by Marios Makassikis's avatar Marios Makassikis Committed by Steve French
Browse files

ksmbd: add buffer validation in session setup



Make sure the security buffer's length/offset are valid with regards to
the packet length.

Acked-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarMarios Makassikis <mmakassikis@freebox.fr>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 621be84a
Loading
Loading
Loading
Loading
+9 −7
Original line number Diff line number Diff line
@@ -298,8 +298,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
				   int blob_len, struct ksmbd_session *sess)
{
	char *domain_name;
	unsigned int lm_off, nt_off;
	unsigned short nt_len;
	unsigned int nt_off, dn_off;
	unsigned short nt_len, dn_len;
	int ret;

	if (blob_len < sizeof(struct authenticate_message)) {
@@ -314,15 +314,17 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
		return -EINVAL;
	}

	lm_off = le32_to_cpu(authblob->LmChallengeResponse.BufferOffset);
	nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset);
	nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length);
	dn_off = le32_to_cpu(authblob->DomainName.BufferOffset);
	dn_len = le16_to_cpu(authblob->DomainName.Length);

	if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len)
		return -EINVAL;

	/* TODO : use domain name that imported from configuration file */
	domain_name = smb_strndup_from_utf16((const char *)authblob +
			le32_to_cpu(authblob->DomainName.BufferOffset),
			le16_to_cpu(authblob->DomainName.Length), true,
			sess->conn->local_nls);
	domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off,
					     dn_len, true, sess->conn->local_nls);
	if (IS_ERR(domain_name))
		return PTR_ERR(domain_name);

+31 −20
Original line number Diff line number Diff line
@@ -1257,19 +1257,13 @@ static int generate_preauth_hash(struct ksmbd_work *work)
	return 0;
}

static int decode_negotiation_token(struct ksmbd_work *work,
				    struct negotiate_message *negblob)
static int decode_negotiation_token(struct ksmbd_conn *conn,
				    struct negotiate_message *negblob,
				    size_t sz)
{
	struct ksmbd_conn *conn = work->conn;
	struct smb2_sess_setup_req *req;
	int sz;

	if (!conn->use_spnego)
		return -EINVAL;

	req = work->request_buf;
	sz = le16_to_cpu(req->SecurityBufferLength);

	if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) {
		if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) {
			conn->auth_mechs |= KSMBD_AUTH_NTLMSSP;
@@ -1281,9 +1275,9 @@ static int decode_negotiation_token(struct ksmbd_work *work,
}

static int ntlm_negotiate(struct ksmbd_work *work,
			  struct negotiate_message *negblob)
			  struct negotiate_message *negblob,
			  size_t negblob_len)
{
	struct smb2_sess_setup_req *req = work->request_buf;
	struct smb2_sess_setup_rsp *rsp = work->response_buf;
	struct challenge_message *chgblob;
	unsigned char *spnego_blob = NULL;
@@ -1292,8 +1286,7 @@ static int ntlm_negotiate(struct ksmbd_work *work,
	int sz, rc;

	ksmbd_debug(SMB, "negotiate phase\n");
	sz = le16_to_cpu(req->SecurityBufferLength);
	rc = ksmbd_decode_ntlmssp_neg_blob(negblob, sz, work->sess);
	rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->sess);
	if (rc)
		return rc;

@@ -1361,12 +1354,23 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn,
	struct authenticate_message *authblob;
	struct ksmbd_user *user;
	char *name;
	int sz;
	unsigned int auth_msg_len, name_off, name_len, secbuf_len;

	secbuf_len = le16_to_cpu(req->SecurityBufferLength);
	if (secbuf_len < sizeof(struct authenticate_message)) {
		ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len);
		return NULL;
	}
	authblob = user_authblob(conn, req);
	sz = le32_to_cpu(authblob->UserName.BufferOffset);
	name = smb_strndup_from_utf16((const char *)authblob + sz,
				      le16_to_cpu(authblob->UserName.Length),
	name_off = le32_to_cpu(authblob->UserName.BufferOffset);
	name_len = le16_to_cpu(authblob->UserName.Length);
	auth_msg_len = le16_to_cpu(req->SecurityBufferOffset) + secbuf_len;

	if (auth_msg_len < (u64)name_off + name_len)
		return NULL;

	name = smb_strndup_from_utf16((const char *)authblob + name_off,
				      name_len,
				      true,
				      conn->local_nls);
	if (IS_ERR(name)) {
@@ -1612,6 +1616,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
	struct smb2_sess_setup_rsp *rsp = work->response_buf;
	struct ksmbd_session *sess;
	struct negotiate_message *negblob;
	unsigned int negblob_len, negblob_off;
	int rc = 0;

	ksmbd_debug(SMB, "Received request for session setup\n");
@@ -1692,10 +1697,16 @@ int smb2_sess_setup(struct ksmbd_work *work)
	if (sess->state == SMB2_SESSION_EXPIRED)
		sess->state = SMB2_SESSION_IN_PROGRESS;

	negblob_off = le16_to_cpu(req->SecurityBufferOffset);
	negblob_len = le16_to_cpu(req->SecurityBufferLength);
	if (negblob_off < (offsetof(struct smb2_sess_setup_req, Buffer) - 4) ||
	    negblob_len < offsetof(struct negotiate_message, NegotiateFlags))
		return -EINVAL;

	negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId +
			le16_to_cpu(req->SecurityBufferOffset));
			negblob_off);

	if (decode_negotiation_token(work, negblob) == 0) {
	if (decode_negotiation_token(conn, negblob, negblob_len) == 0) {
		if (conn->mechToken)
			negblob = (struct negotiate_message *)conn->mechToken;
	}
@@ -1719,7 +1730,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
			sess->Preauth_HashValue = NULL;
		} else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) {
			if (negblob->MessageType == NtLmNegotiate) {
				rc = ntlm_negotiate(work, negblob);
				rc = ntlm_negotiate(work, negblob, negblob_len);
				if (rc)
					goto out_err;
				rsp->hdr.Status =