From 102a0743326a03cd1a1202ceda21e175b7d3575c Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Tue, 20 Feb 2024 01:20:52 +0000 Subject: [PATCH] add new system file --- kernel/fs/cifs/smb2misc.c | 153 +++++++++++++++++++++++++++++++++++---------------- 1 files changed, 105 insertions(+), 48 deletions(-) diff --git a/kernel/fs/cifs/smb2misc.c b/kernel/fs/cifs/smb2misc.c index 7177720..be3df90 100644 --- a/kernel/fs/cifs/smb2misc.c +++ b/kernel/fs/cifs/smb2misc.c @@ -29,6 +29,7 @@ #include "cifs_unicode.h" #include "smb2status.h" #include "smb2glob.h" +#include "nterr.h" static int check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid) @@ -93,6 +94,8 @@ /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) }; +#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_sync_hdr) + sizeof(struct smb2_negotiate_rsp)) + static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len, __u32 non_ctxlen) { @@ -108,15 +111,21 @@ /* Make sure that negotiate contexts start after gss security blob */ nc_offset = le32_to_cpu(pneg_rsp->NegotiateContextOffset); - if (nc_offset < non_ctxlen) { - printk_once(KERN_WARNING "invalid negotiate context offset\n"); + if (nc_offset + 1 < non_ctxlen) { + pr_warn_once("Invalid negotiate context offset %d\n", nc_offset); return 0; - } - size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen; + } else if (nc_offset + 1 == non_ctxlen) { + cifs_dbg(FYI, "no SPNEGO security blob in negprot rsp\n"); + size_of_pad_before_neg_ctxts = 0; + } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE) + /* has padding, but no SPNEGO blob */ + size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen + 1; + else + size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen; /* Verify that at least minimal negotiate contexts fit within frame */ if (len < nc_offset + (neg_count * sizeof(struct smb2_neg_context))) { - printk_once(KERN_WARNING "negotiate context goes beyond end\n"); + pr_warn_once("negotiate context goes beyond end\n"); return 0; } @@ -189,14 +198,14 @@ return 1; if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { - cifs_dbg(VFS, "Illegal structure size %u\n", + cifs_dbg(VFS, "Invalid structure size %u\n", le16_to_cpu(shdr->StructureSize)); return 1; } command = le16_to_cpu(shdr->Command); if (command >= NUMBER_OF_SMB2_COMMANDS) { - cifs_dbg(VFS, "Illegal SMB2 command %d\n", command); + cifs_dbg(VFS, "Invalid SMB2 command %d\n", command); return 1; } @@ -204,7 +213,7 @@ if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 || pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2)) { /* error packets have 9 byte structure size */ - cifs_dbg(VFS, "Illegal response size %u for command %d\n", + cifs_dbg(VFS, "Invalid response size %u for command %d\n", le16_to_cpu(pdu->StructureSize2), command); return 1; } else if (command == SMB2_OPLOCK_BREAK_HE @@ -212,7 +221,7 @@ && (le16_to_cpu(pdu->StructureSize2) != 44) && (le16_to_cpu(pdu->StructureSize2) != 36)) { /* special case for SMB2.1 lease break message */ - cifs_dbg(VFS, "Illegal response size %d for oplock break\n", + cifs_dbg(VFS, "Invalid response size %d for oplock break\n", le16_to_cpu(pdu->StructureSize2)); return 1; } @@ -249,16 +258,10 @@ * of junk. Other servers match RFC1001 len to actual * SMB2/SMB3 frame length (header + smb2 response specific data) * Some windows servers also pad up to 8 bytes when compounding. - * If pad is longer than eight bytes, log the server behavior - * (once), since may indicate a problem but allow it and continue - * since the frame is parseable. */ - if (clc_len < len) { - pr_warn_once( - "srv rsp padded more than expected. Length %d not %d for cmd:%d mid:%llu\n", - len, clc_len, command, mid); + if (clc_len < len) return 0; - } + pr_warn_once( "srv rsp too short, len %d not %d. cmd:%d mid:%llu\n", len, clc_len, command, mid); @@ -359,9 +362,13 @@ ((struct smb2_ioctl_rsp *)shdr)->OutputCount); break; case SMB2_CHANGE_NOTIFY: + *off = le16_to_cpu( + ((struct smb2_change_notify_rsp *)shdr)->OutputBufferOffset); + *len = le32_to_cpu( + ((struct smb2_change_notify_rsp *)shdr)->OutputBufferLength); + break; default: - /* BB FIXME for unimplemented cases above */ - cifs_dbg(VFS, "no length check for command\n"); + cifs_dbg(VFS, "no length check for command %d\n", le16_to_cpu(shdr->Command)); break; } @@ -747,19 +754,35 @@ { struct close_cancelled_open *cancelled = container_of(work, struct close_cancelled_open, work); + struct cifs_tcon *tcon = cancelled->tcon; + int rc; - cifs_dbg(VFS, "Close unmatched open\n"); + if (cancelled->mid) + cifs_tcon_dbg(VFS, "Close unmatched open for MID:%llx\n", + cancelled->mid); + else + cifs_tcon_dbg(VFS, "Close interrupted close\n"); - SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid, - cancelled->fid.volatile_fid); - cifs_put_tcon(cancelled->tcon); + rc = SMB2_close(0, tcon, cancelled->fid.persistent_fid, + cancelled->fid.volatile_fid); + if (rc) + cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc); + + cifs_put_tcon(tcon); kfree(cancelled); } -/* Caller should already has an extra reference to @tcon */ +/* + * Caller should already has an extra reference to @tcon + * This function is used to queue work to close a handle to prevent leaks + * on the server. + * We handle two cases. If an open was interrupted after we sent the + * SMB2_CREATE to the server but before we processed the reply, and second + * if a close was interrupted before we sent the SMB2_CLOSE to the server. + */ static int -__smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, - __u64 volatile_fid) +__smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, + __u64 persistent_fid, __u64 volatile_fid) { struct close_cancelled_open *cancelled; @@ -770,6 +793,8 @@ cancelled->fid.persistent_fid = persistent_fid; cancelled->fid.volatile_fid = volatile_fid; cancelled->tcon = tcon; + cancelled->cmd = cmd; + cancelled->mid = mid; INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false); @@ -784,10 +809,25 @@ cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); spin_lock(&cifs_tcp_ses_lock); + if (tcon->tc_count <= 0) { + struct TCP_Server_Info *server = NULL; + + WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); + spin_unlock(&cifs_tcp_ses_lock); + + if (tcon->ses) + server = tcon->ses->server; + + cifs_server_dbg(FYI, "tid=%u: tcon is closing, skipping async close retry of fid %llu %llu\n", + tcon->tid, persistent_fid, volatile_fid); + + return 0; + } tcon->tc_count++; spin_unlock(&cifs_tcp_ses_lock); - rc = __smb2_handle_cancelled_close(tcon, persistent_fid, volatile_fid); + rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, + persistent_fid, volatile_fid); if (rc) cifs_put_tcon(tcon); @@ -795,14 +835,14 @@ } int -smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server) +smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server) { - struct smb2_sync_hdr *sync_hdr = (struct smb2_sync_hdr *)buffer; - struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer; + struct smb2_sync_hdr *sync_hdr = mid->resp_buf; + struct smb2_create_rsp *rsp = mid->resp_buf; struct cifs_tcon *tcon; int rc; - if (sync_hdr->Command != SMB2_CREATE || + if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || sync_hdr->Command != SMB2_CREATE || sync_hdr->Status != STATUS_SUCCESS) return 0; @@ -811,8 +851,11 @@ if (!tcon) return -ENOENT; - rc = __smb2_handle_cancelled_close(tcon, rsp->PersistentFileId, - rsp->VolatileFileId); + rc = __smb2_handle_cancelled_cmd(tcon, + le16_to_cpu(sync_hdr->Command), + le64_to_cpu(sync_hdr->MessageId), + rsp->PersistentFileId, + rsp->VolatileFileId); if (rc) cifs_put_tcon(tcon); @@ -831,33 +874,47 @@ int i, rc; struct sdesc *d; struct smb2_sync_hdr *hdr; + struct TCP_Server_Info *server = cifs_ses_server(ses); - if (ses->server->tcpStatus == CifsGood) { - /* skip non smb311 connections */ - if (ses->server->dialect != SMB311_PROT_ID) - return 0; + hdr = (struct smb2_sync_hdr *)iov[0].iov_base; + /* neg prot are always taken */ + if (hdr->Command == SMB2_NEGOTIATE) + goto ok; - /* skip last sess setup response */ - hdr = (struct smb2_sync_hdr *)iov[0].iov_base; - if (hdr->Flags & SMB2_FLAGS_SIGNED) - return 0; - } + /* + * If we process a command which wasn't a negprot it means the + * neg prot was already done, so the server dialect was set + * and we can test it. Preauth requires 3.1.1 for now. + */ + if (server->dialect != SMB311_PROT_ID) + return 0; - rc = smb311_crypto_shash_allocate(ses->server); + if (hdr->Command != SMB2_SESSION_SETUP) + return 0; + + /* skip last sess setup response */ + if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + && (hdr->Status == NT_STATUS_OK + || (hdr->Status != + cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)))) + return 0; + +ok: + rc = smb311_crypto_shash_allocate(server); if (rc) return rc; - d = ses->server->secmech.sdescsha512; + d = server->secmech.sdescsha512; rc = crypto_shash_init(&d->shash); if (rc) { - cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__); + cifs_dbg(VFS, "%s: Could not init sha512 shash\n", __func__); return rc; } rc = crypto_shash_update(&d->shash, ses->preauth_sha_hash, SMB2_PREAUTH_HASH_SIZE); if (rc) { - cifs_dbg(VFS, "%s: could not update sha512 shash\n", __func__); + cifs_dbg(VFS, "%s: Could not update sha512 shash\n", __func__); return rc; } @@ -865,7 +922,7 @@ rc = crypto_shash_update(&d->shash, iov[i].iov_base, iov[i].iov_len); if (rc) { - cifs_dbg(VFS, "%s: could not update sha512 shash\n", + cifs_dbg(VFS, "%s: Could not update sha512 shash\n", __func__); return rc; } @@ -873,7 +930,7 @@ rc = crypto_shash_final(&d->shash, ses->preauth_sha_hash); if (rc) { - cifs_dbg(VFS, "%s: could not finalize sha512 shash\n", + cifs_dbg(VFS, "%s: Could not finalize sha512 shash\n", __func__); return rc; } -- Gitblit v1.6.2