162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) International Business Machines Corp., 2002,2011 562306a36Sopenharmony_ci * Etersoft, 2012 662306a36Sopenharmony_ci * Author(s): Steve French (sfrench@us.ibm.com) 762306a36Sopenharmony_ci * Pavel Shilovsky (pshilovsky@samba.org) 2012 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/ctype.h> 1162306a36Sopenharmony_ci#include "cifsglob.h" 1262306a36Sopenharmony_ci#include "cifsproto.h" 1362306a36Sopenharmony_ci#include "smb2proto.h" 1462306a36Sopenharmony_ci#include "cifs_debug.h" 1562306a36Sopenharmony_ci#include "cifs_unicode.h" 1662306a36Sopenharmony_ci#include "smb2status.h" 1762306a36Sopenharmony_ci#include "smb2glob.h" 1862306a36Sopenharmony_ci#include "nterr.h" 1962306a36Sopenharmony_ci#include "cached_dir.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic int 2262306a36Sopenharmony_cicheck_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci __u64 wire_mid = le64_to_cpu(shdr->MessageId); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* 2762306a36Sopenharmony_ci * Make sure that this really is an SMB, that it is a response, 2862306a36Sopenharmony_ci * and that the message ids match. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci if ((shdr->ProtocolId == SMB2_PROTO_NUMBER) && 3162306a36Sopenharmony_ci (mid == wire_mid)) { 3262306a36Sopenharmony_ci if (shdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) 3362306a36Sopenharmony_ci return 0; 3462306a36Sopenharmony_ci else { 3562306a36Sopenharmony_ci /* only one valid case where server sends us request */ 3662306a36Sopenharmony_ci if (shdr->Command == SMB2_OPLOCK_BREAK) 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci else 3962306a36Sopenharmony_ci cifs_dbg(VFS, "Received Request not response\n"); 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci } else { /* bad signature or mid */ 4262306a36Sopenharmony_ci if (shdr->ProtocolId != SMB2_PROTO_NUMBER) 4362306a36Sopenharmony_ci cifs_dbg(VFS, "Bad protocol string signature header %x\n", 4462306a36Sopenharmony_ci le32_to_cpu(shdr->ProtocolId)); 4562306a36Sopenharmony_ci if (mid != wire_mid) 4662306a36Sopenharmony_ci cifs_dbg(VFS, "Mids do not match: %llu and %llu\n", 4762306a36Sopenharmony_ci mid, wire_mid); 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", wire_mid); 5062306a36Sopenharmony_ci return 1; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * The following table defines the expected "StructureSize" of SMB2 responses 5562306a36Sopenharmony_ci * in order by SMB2 command. This is similar to "wct" in SMB/CIFS responses. 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * Note that commands are defined in smb2pdu.h in le16 but the array below is 5862306a36Sopenharmony_ci * indexed by command in host byte order 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cistatic const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { 6162306a36Sopenharmony_ci /* SMB2_NEGOTIATE */ cpu_to_le16(65), 6262306a36Sopenharmony_ci /* SMB2_SESSION_SETUP */ cpu_to_le16(9), 6362306a36Sopenharmony_ci /* SMB2_LOGOFF */ cpu_to_le16(4), 6462306a36Sopenharmony_ci /* SMB2_TREE_CONNECT */ cpu_to_le16(16), 6562306a36Sopenharmony_ci /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), 6662306a36Sopenharmony_ci /* SMB2_CREATE */ cpu_to_le16(89), 6762306a36Sopenharmony_ci /* SMB2_CLOSE */ cpu_to_le16(60), 6862306a36Sopenharmony_ci /* SMB2_FLUSH */ cpu_to_le16(4), 6962306a36Sopenharmony_ci /* SMB2_READ */ cpu_to_le16(17), 7062306a36Sopenharmony_ci /* SMB2_WRITE */ cpu_to_le16(17), 7162306a36Sopenharmony_ci /* SMB2_LOCK */ cpu_to_le16(4), 7262306a36Sopenharmony_ci /* SMB2_IOCTL */ cpu_to_le16(49), 7362306a36Sopenharmony_ci /* BB CHECK this ... not listed in documentation */ 7462306a36Sopenharmony_ci /* SMB2_CANCEL */ cpu_to_le16(0), 7562306a36Sopenharmony_ci /* SMB2_ECHO */ cpu_to_le16(4), 7662306a36Sopenharmony_ci /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(9), 7762306a36Sopenharmony_ci /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(9), 7862306a36Sopenharmony_ci /* SMB2_QUERY_INFO */ cpu_to_le16(9), 7962306a36Sopenharmony_ci /* SMB2_SET_INFO */ cpu_to_le16(2), 8062306a36Sopenharmony_ci /* BB FIXME can also be 44 for lease break */ 8162306a36Sopenharmony_ci /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_hdr) + sizeof(struct smb2_negotiate_rsp)) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len, 8762306a36Sopenharmony_ci __u32 non_ctxlen) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci __u16 neg_count; 9062306a36Sopenharmony_ci __u32 nc_offset, size_of_pad_before_neg_ctxts; 9162306a36Sopenharmony_ci struct smb2_negotiate_rsp *pneg_rsp = (struct smb2_negotiate_rsp *)hdr; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Negotiate contexts are only valid for latest dialect SMB3.11 */ 9462306a36Sopenharmony_ci neg_count = le16_to_cpu(pneg_rsp->NegotiateContextCount); 9562306a36Sopenharmony_ci if ((neg_count == 0) || 9662306a36Sopenharmony_ci (pneg_rsp->DialectRevision != cpu_to_le16(SMB311_PROT_ID))) 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * if SPNEGO blob present (ie the RFC2478 GSS info which indicates 10162306a36Sopenharmony_ci * which security mechanisms the server supports) make sure that 10262306a36Sopenharmony_ci * the negotiate contexts start after it 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci nc_offset = le32_to_cpu(pneg_rsp->NegotiateContextOffset); 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * non_ctxlen is at least shdr->StructureSize + pdu->StructureSize2 10762306a36Sopenharmony_ci * and the latter is 1 byte bigger than the fix-sized area of the 10862306a36Sopenharmony_ci * NEGOTIATE response 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci if (nc_offset + 1 < non_ctxlen) { 11162306a36Sopenharmony_ci pr_warn_once("Invalid negotiate context offset %d\n", nc_offset); 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci } else if (nc_offset + 1 == non_ctxlen) { 11462306a36Sopenharmony_ci cifs_dbg(FYI, "no SPNEGO security blob in negprot rsp\n"); 11562306a36Sopenharmony_ci size_of_pad_before_neg_ctxts = 0; 11662306a36Sopenharmony_ci } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE + 1) 11762306a36Sopenharmony_ci /* has padding, but no SPNEGO blob */ 11862306a36Sopenharmony_ci size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen + 1; 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Verify that at least minimal negotiate contexts fit within frame */ 12362306a36Sopenharmony_ci if (len < nc_offset + (neg_count * sizeof(struct smb2_neg_context))) { 12462306a36Sopenharmony_ci pr_warn_once("negotiate context goes beyond end\n"); 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci cifs_dbg(FYI, "length of negcontexts %d pad %d\n", 12962306a36Sopenharmony_ci len - nc_offset, size_of_pad_before_neg_ctxts); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* length of negcontexts including pad from end of sec blob to them */ 13262306a36Sopenharmony_ci return (len - nc_offset) + size_of_pad_before_neg_ctxts; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciint 13662306a36Sopenharmony_cismb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct TCP_Server_Info *pserver; 13962306a36Sopenharmony_ci struct smb2_hdr *shdr = (struct smb2_hdr *)buf; 14062306a36Sopenharmony_ci struct smb2_pdu *pdu = (struct smb2_pdu *)shdr; 14162306a36Sopenharmony_ci int hdr_size = sizeof(struct smb2_hdr); 14262306a36Sopenharmony_ci int pdu_size = sizeof(struct smb2_pdu); 14362306a36Sopenharmony_ci int command; 14462306a36Sopenharmony_ci __u32 calc_len; /* calculated length */ 14562306a36Sopenharmony_ci __u64 mid; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* If server is a channel, select the primary channel */ 14862306a36Sopenharmony_ci pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * Add function to do table lookup of StructureSize by command 15262306a36Sopenharmony_ci * ie Validate the wct via smb2_struct_sizes table above 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { 15562306a36Sopenharmony_ci struct smb2_transform_hdr *thdr = 15662306a36Sopenharmony_ci (struct smb2_transform_hdr *)buf; 15762306a36Sopenharmony_ci struct cifs_ses *ses = NULL; 15862306a36Sopenharmony_ci struct cifs_ses *iter; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* decrypt frame now that it is completely read in */ 16162306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 16262306a36Sopenharmony_ci list_for_each_entry(iter, &pserver->smb_ses_list, smb_ses_list) { 16362306a36Sopenharmony_ci if (iter->Suid == le64_to_cpu(thdr->SessionId)) { 16462306a36Sopenharmony_ci ses = iter; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 16962306a36Sopenharmony_ci if (!ses) { 17062306a36Sopenharmony_ci cifs_dbg(VFS, "no decryption - session id not found\n"); 17162306a36Sopenharmony_ci return 1; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci mid = le64_to_cpu(shdr->MessageId); 17662306a36Sopenharmony_ci if (check_smb2_hdr(shdr, mid)) 17762306a36Sopenharmony_ci return 1; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { 18062306a36Sopenharmony_ci cifs_dbg(VFS, "Invalid structure size %u\n", 18162306a36Sopenharmony_ci le16_to_cpu(shdr->StructureSize)); 18262306a36Sopenharmony_ci return 1; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci command = le16_to_cpu(shdr->Command); 18662306a36Sopenharmony_ci if (command >= NUMBER_OF_SMB2_COMMANDS) { 18762306a36Sopenharmony_ci cifs_dbg(VFS, "Invalid SMB2 command %d\n", command); 18862306a36Sopenharmony_ci return 1; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (len < pdu_size) { 19262306a36Sopenharmony_ci if ((len >= hdr_size) 19362306a36Sopenharmony_ci && (shdr->Status != 0)) { 19462306a36Sopenharmony_ci pdu->StructureSize2 = 0; 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * As with SMB/CIFS, on some error cases servers may 19762306a36Sopenharmony_ci * not return wct properly 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci } else { 20162306a36Sopenharmony_ci cifs_dbg(VFS, "Length less than SMB header size\n"); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci return 1; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) { 20662306a36Sopenharmony_ci cifs_dbg(VFS, "SMB length greater than maximum, mid=%llu\n", 20762306a36Sopenharmony_ci mid); 20862306a36Sopenharmony_ci return 1; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { 21262306a36Sopenharmony_ci if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 || 21362306a36Sopenharmony_ci pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { 21462306a36Sopenharmony_ci /* error packets have 9 byte structure size */ 21562306a36Sopenharmony_ci cifs_dbg(VFS, "Invalid response size %u for command %d\n", 21662306a36Sopenharmony_ci le16_to_cpu(pdu->StructureSize2), command); 21762306a36Sopenharmony_ci return 1; 21862306a36Sopenharmony_ci } else if (command == SMB2_OPLOCK_BREAK_HE 21962306a36Sopenharmony_ci && (shdr->Status == 0) 22062306a36Sopenharmony_ci && (le16_to_cpu(pdu->StructureSize2) != 44) 22162306a36Sopenharmony_ci && (le16_to_cpu(pdu->StructureSize2) != 36)) { 22262306a36Sopenharmony_ci /* special case for SMB2.1 lease break message */ 22362306a36Sopenharmony_ci cifs_dbg(VFS, "Invalid response size %d for oplock break\n", 22462306a36Sopenharmony_ci le16_to_cpu(pdu->StructureSize2)); 22562306a36Sopenharmony_ci return 1; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci calc_len = smb2_calc_size(buf); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might 23262306a36Sopenharmony_ci * be 0, and not a real miscalculation */ 23362306a36Sopenharmony_ci if (command == SMB2_IOCTL_HE && calc_len == 0) 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (command == SMB2_NEGOTIATE_HE) 23762306a36Sopenharmony_ci calc_len += get_neg_ctxt_len(shdr, len, calc_len); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (len != calc_len) { 24062306a36Sopenharmony_ci /* create failed on symlink */ 24162306a36Sopenharmony_ci if (command == SMB2_CREATE_HE && 24262306a36Sopenharmony_ci shdr->Status == STATUS_STOPPED_ON_SYMLINK) 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci /* Windows 7 server returns 24 bytes more */ 24562306a36Sopenharmony_ci if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci /* server can return one byte more due to implied bcc[0] */ 24862306a36Sopenharmony_ci if (calc_len == len + 1) 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * Some windows servers (win2016) will pad also the final 25362306a36Sopenharmony_ci * PDU in a compound to 8 bytes. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci if (ALIGN(calc_len, 8) == len) 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * MacOS server pads after SMB2.1 write response with 3 bytes 26062306a36Sopenharmony_ci * of junk. Other servers match RFC1001 len to actual 26162306a36Sopenharmony_ci * SMB2/SMB3 frame length (header + smb2 response specific data) 26262306a36Sopenharmony_ci * Some windows servers also pad up to 8 bytes when compounding. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci if (calc_len < len) 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Only log a message if len was really miscalculated */ 26862306a36Sopenharmony_ci if (unlikely(cifsFYI)) 26962306a36Sopenharmony_ci cifs_dbg(FYI, "Server response too short: calculated " 27062306a36Sopenharmony_ci "length %u doesn't match read length %u (cmd=%d, mid=%llu)\n", 27162306a36Sopenharmony_ci calc_len, len, command, mid); 27262306a36Sopenharmony_ci else 27362306a36Sopenharmony_ci pr_warn("Server response too short: calculated length " 27462306a36Sopenharmony_ci "%u doesn't match read length %u (cmd=%d, mid=%llu)\n", 27562306a36Sopenharmony_ci calc_len, len, command, mid); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 1; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* 28362306a36Sopenharmony_ci * The size of the variable area depends on the offset and length fields 28462306a36Sopenharmony_ci * located in different fields for various SMB2 responses. SMB2 responses 28562306a36Sopenharmony_ci * with no variable length info, show an offset of zero for the offset field. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_cistatic const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { 28862306a36Sopenharmony_ci /* SMB2_NEGOTIATE */ true, 28962306a36Sopenharmony_ci /* SMB2_SESSION_SETUP */ true, 29062306a36Sopenharmony_ci /* SMB2_LOGOFF */ false, 29162306a36Sopenharmony_ci /* SMB2_TREE_CONNECT */ false, 29262306a36Sopenharmony_ci /* SMB2_TREE_DISCONNECT */ false, 29362306a36Sopenharmony_ci /* SMB2_CREATE */ true, 29462306a36Sopenharmony_ci /* SMB2_CLOSE */ false, 29562306a36Sopenharmony_ci /* SMB2_FLUSH */ false, 29662306a36Sopenharmony_ci /* SMB2_READ */ true, 29762306a36Sopenharmony_ci /* SMB2_WRITE */ false, 29862306a36Sopenharmony_ci /* SMB2_LOCK */ false, 29962306a36Sopenharmony_ci /* SMB2_IOCTL */ true, 30062306a36Sopenharmony_ci /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ 30162306a36Sopenharmony_ci /* SMB2_ECHO */ false, 30262306a36Sopenharmony_ci /* SMB2_QUERY_DIRECTORY */ true, 30362306a36Sopenharmony_ci /* SMB2_CHANGE_NOTIFY */ true, 30462306a36Sopenharmony_ci /* SMB2_QUERY_INFO */ true, 30562306a36Sopenharmony_ci /* SMB2_SET_INFO */ false, 30662306a36Sopenharmony_ci /* SMB2_OPLOCK_BREAK */ false 30762306a36Sopenharmony_ci}; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci/* 31062306a36Sopenharmony_ci * Returns the pointer to the beginning of the data area. Length of the data 31162306a36Sopenharmony_ci * area and the offset to it (from the beginning of the smb are also returned. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_cichar * 31462306a36Sopenharmony_cismb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci const int max_off = 4096; 31762306a36Sopenharmony_ci const int max_len = 128 * 1024; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci *off = 0; 32062306a36Sopenharmony_ci *len = 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* error responses do not have data area */ 32362306a36Sopenharmony_ci if (shdr->Status && shdr->Status != STATUS_MORE_PROCESSING_REQUIRED && 32462306a36Sopenharmony_ci (((struct smb2_err_rsp *)shdr)->StructureSize) == 32562306a36Sopenharmony_ci SMB2_ERROR_STRUCTURE_SIZE2_LE) 32662306a36Sopenharmony_ci return NULL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * Following commands have data areas so we have to get the location 33062306a36Sopenharmony_ci * of the data buffer offset and data buffer length for the particular 33162306a36Sopenharmony_ci * command. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci switch (shdr->Command) { 33462306a36Sopenharmony_ci case SMB2_NEGOTIATE: 33562306a36Sopenharmony_ci *off = le16_to_cpu( 33662306a36Sopenharmony_ci ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferOffset); 33762306a36Sopenharmony_ci *len = le16_to_cpu( 33862306a36Sopenharmony_ci ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferLength); 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case SMB2_SESSION_SETUP: 34162306a36Sopenharmony_ci *off = le16_to_cpu( 34262306a36Sopenharmony_ci ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferOffset); 34362306a36Sopenharmony_ci *len = le16_to_cpu( 34462306a36Sopenharmony_ci ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferLength); 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci case SMB2_CREATE: 34762306a36Sopenharmony_ci *off = le32_to_cpu( 34862306a36Sopenharmony_ci ((struct smb2_create_rsp *)shdr)->CreateContextsOffset); 34962306a36Sopenharmony_ci *len = le32_to_cpu( 35062306a36Sopenharmony_ci ((struct smb2_create_rsp *)shdr)->CreateContextsLength); 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci case SMB2_QUERY_INFO: 35362306a36Sopenharmony_ci *off = le16_to_cpu( 35462306a36Sopenharmony_ci ((struct smb2_query_info_rsp *)shdr)->OutputBufferOffset); 35562306a36Sopenharmony_ci *len = le32_to_cpu( 35662306a36Sopenharmony_ci ((struct smb2_query_info_rsp *)shdr)->OutputBufferLength); 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci case SMB2_READ: 35962306a36Sopenharmony_ci /* TODO: is this a bug ? */ 36062306a36Sopenharmony_ci *off = ((struct smb2_read_rsp *)shdr)->DataOffset; 36162306a36Sopenharmony_ci *len = le32_to_cpu(((struct smb2_read_rsp *)shdr)->DataLength); 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci case SMB2_QUERY_DIRECTORY: 36462306a36Sopenharmony_ci *off = le16_to_cpu( 36562306a36Sopenharmony_ci ((struct smb2_query_directory_rsp *)shdr)->OutputBufferOffset); 36662306a36Sopenharmony_ci *len = le32_to_cpu( 36762306a36Sopenharmony_ci ((struct smb2_query_directory_rsp *)shdr)->OutputBufferLength); 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case SMB2_IOCTL: 37062306a36Sopenharmony_ci *off = le32_to_cpu( 37162306a36Sopenharmony_ci ((struct smb2_ioctl_rsp *)shdr)->OutputOffset); 37262306a36Sopenharmony_ci *len = le32_to_cpu( 37362306a36Sopenharmony_ci ((struct smb2_ioctl_rsp *)shdr)->OutputCount); 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci case SMB2_CHANGE_NOTIFY: 37662306a36Sopenharmony_ci *off = le16_to_cpu( 37762306a36Sopenharmony_ci ((struct smb2_change_notify_rsp *)shdr)->OutputBufferOffset); 37862306a36Sopenharmony_ci *len = le32_to_cpu( 37962306a36Sopenharmony_ci ((struct smb2_change_notify_rsp *)shdr)->OutputBufferLength); 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci default: 38262306a36Sopenharmony_ci cifs_dbg(VFS, "no length check for command %d\n", le16_to_cpu(shdr->Command)); 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* 38762306a36Sopenharmony_ci * Invalid length or offset probably means data area is invalid, but 38862306a36Sopenharmony_ci * we have little choice but to ignore the data area in this case. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci if (unlikely(*off < 0 || *off > max_off || 39162306a36Sopenharmony_ci *len < 0 || *len > max_len)) { 39262306a36Sopenharmony_ci cifs_dbg(VFS, "%s: invalid data area (off=%d len=%d)\n", 39362306a36Sopenharmony_ci __func__, *off, *len); 39462306a36Sopenharmony_ci *off = 0; 39562306a36Sopenharmony_ci *len = 0; 39662306a36Sopenharmony_ci } else if (*off == 0) { 39762306a36Sopenharmony_ci *len = 0; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* return pointer to beginning of data area, ie offset from SMB start */ 40162306a36Sopenharmony_ci if (*off > 0 && *len > 0) 40262306a36Sopenharmony_ci return (char *)shdr + *off; 40362306a36Sopenharmony_ci return NULL; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* 40762306a36Sopenharmony_ci * Calculate the size of the SMB message based on the fixed header 40862306a36Sopenharmony_ci * portion, the number of word parameters and the data portion of the message. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ciunsigned int 41162306a36Sopenharmony_cismb2_calc_size(void *buf) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct smb2_pdu *pdu = buf; 41462306a36Sopenharmony_ci struct smb2_hdr *shdr = &pdu->hdr; 41562306a36Sopenharmony_ci int offset; /* the offset from the beginning of SMB to data area */ 41662306a36Sopenharmony_ci int data_length; /* the length of the variable length data area */ 41762306a36Sopenharmony_ci /* Structure Size has already been checked to make sure it is 64 */ 41862306a36Sopenharmony_ci int len = le16_to_cpu(shdr->StructureSize); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* 42162306a36Sopenharmony_ci * StructureSize2, ie length of fixed parameter area has already 42262306a36Sopenharmony_ci * been checked to make sure it is the correct length. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci len += le16_to_cpu(pdu->StructureSize2); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false) 42762306a36Sopenharmony_ci goto calc_size_exit; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci smb2_get_data_area_len(&offset, &data_length, shdr); 43062306a36Sopenharmony_ci cifs_dbg(FYI, "SMB2 data length %d offset %d\n", data_length, offset); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (data_length > 0) { 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * Check to make sure that data area begins after fixed area, 43562306a36Sopenharmony_ci * Note that last byte of the fixed area is part of data area 43662306a36Sopenharmony_ci * for some commands, typically those with odd StructureSize, 43762306a36Sopenharmony_ci * so we must add one to the calculation. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ci if (offset + 1 < len) { 44062306a36Sopenharmony_ci cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", 44162306a36Sopenharmony_ci offset + 1, len); 44262306a36Sopenharmony_ci data_length = 0; 44362306a36Sopenharmony_ci } else { 44462306a36Sopenharmony_ci len = offset + data_length; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_cicalc_size_exit: 44862306a36Sopenharmony_ci cifs_dbg(FYI, "SMB2 len %d\n", len); 44962306a36Sopenharmony_ci return len; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci/* Note: caller must free return buffer */ 45362306a36Sopenharmony_ci__le16 * 45462306a36Sopenharmony_cicifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci int len; 45762306a36Sopenharmony_ci const char *start_of_path; 45862306a36Sopenharmony_ci __le16 *to; 45962306a36Sopenharmony_ci int map_type; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) 46262306a36Sopenharmony_ci map_type = SFM_MAP_UNI_RSVD; 46362306a36Sopenharmony_ci else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) 46462306a36Sopenharmony_ci map_type = SFU_MAP_UNI_RSVD; 46562306a36Sopenharmony_ci else 46662306a36Sopenharmony_ci map_type = NO_MAP_UNI_RSVD; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Windows doesn't allow paths beginning with \ */ 46962306a36Sopenharmony_ci if (from[0] == '\\') 47062306a36Sopenharmony_ci start_of_path = from + 1; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* SMB311 POSIX extensions paths do not include leading slash */ 47362306a36Sopenharmony_ci else if (cifs_sb_master_tlink(cifs_sb) && 47462306a36Sopenharmony_ci cifs_sb_master_tcon(cifs_sb)->posix_extensions && 47562306a36Sopenharmony_ci (from[0] == '/')) { 47662306a36Sopenharmony_ci start_of_path = from + 1; 47762306a36Sopenharmony_ci } else 47862306a36Sopenharmony_ci start_of_path = from; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, 48162306a36Sopenharmony_ci cifs_sb->local_nls, map_type); 48262306a36Sopenharmony_ci return to; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci__le32 48662306a36Sopenharmony_cismb2_get_lease_state(struct cifsInodeInfo *cinode) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci __le32 lease = 0; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (CIFS_CACHE_WRITE(cinode)) 49162306a36Sopenharmony_ci lease |= SMB2_LEASE_WRITE_CACHING_LE; 49262306a36Sopenharmony_ci if (CIFS_CACHE_HANDLE(cinode)) 49362306a36Sopenharmony_ci lease |= SMB2_LEASE_HANDLE_CACHING_LE; 49462306a36Sopenharmony_ci if (CIFS_CACHE_READ(cinode)) 49562306a36Sopenharmony_ci lease |= SMB2_LEASE_READ_CACHING_LE; 49662306a36Sopenharmony_ci return lease; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistruct smb2_lease_break_work { 50062306a36Sopenharmony_ci struct work_struct lease_break; 50162306a36Sopenharmony_ci struct tcon_link *tlink; 50262306a36Sopenharmony_ci __u8 lease_key[16]; 50362306a36Sopenharmony_ci __le32 lease_state; 50462306a36Sopenharmony_ci}; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic void 50762306a36Sopenharmony_cicifs_ses_oplock_break(struct work_struct *work) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct smb2_lease_break_work *lw = container_of(work, 51062306a36Sopenharmony_ci struct smb2_lease_break_work, lease_break); 51162306a36Sopenharmony_ci int rc = 0; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key, 51462306a36Sopenharmony_ci lw->lease_state); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci cifs_dbg(FYI, "Lease release rc %d\n", rc); 51762306a36Sopenharmony_ci cifs_put_tlink(lw->tlink); 51862306a36Sopenharmony_ci kfree(lw); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic void 52262306a36Sopenharmony_cismb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key, 52362306a36Sopenharmony_ci __le32 new_lease_state) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct smb2_lease_break_work *lw; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); 52862306a36Sopenharmony_ci if (!lw) { 52962306a36Sopenharmony_ci cifs_put_tlink(tlink); 53062306a36Sopenharmony_ci return; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); 53462306a36Sopenharmony_ci lw->tlink = tlink; 53562306a36Sopenharmony_ci lw->lease_state = new_lease_state; 53662306a36Sopenharmony_ci memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE); 53762306a36Sopenharmony_ci queue_work(cifsiod_wq, &lw->lease_break); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic bool 54162306a36Sopenharmony_cismb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci __u8 lease_state; 54462306a36Sopenharmony_ci struct cifsFileInfo *cfile; 54562306a36Sopenharmony_ci struct cifsInodeInfo *cinode; 54662306a36Sopenharmony_ci int ack_req = le32_to_cpu(rsp->Flags & 54762306a36Sopenharmony_ci SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci lease_state = le32_to_cpu(rsp->NewLeaseState); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci list_for_each_entry(cfile, &tcon->openFileList, tlist) { 55262306a36Sopenharmony_ci cinode = CIFS_I(d_inode(cfile->dentry)); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (memcmp(cinode->lease_key, rsp->LeaseKey, 55562306a36Sopenharmony_ci SMB2_LEASE_KEY_SIZE)) 55662306a36Sopenharmony_ci continue; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci cifs_dbg(FYI, "found in the open list\n"); 55962306a36Sopenharmony_ci cifs_dbg(FYI, "lease key match, lease break 0x%x\n", 56062306a36Sopenharmony_ci lease_state); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (ack_req) 56362306a36Sopenharmony_ci cfile->oplock_break_cancelled = false; 56462306a36Sopenharmony_ci else 56562306a36Sopenharmony_ci cfile->oplock_break_cancelled = true; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci cfile->oplock_epoch = le16_to_cpu(rsp->Epoch); 57062306a36Sopenharmony_ci cfile->oplock_level = lease_state; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci cifs_queue_oplock_break(cfile); 57362306a36Sopenharmony_ci return true; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return false; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic struct cifs_pending_open * 58062306a36Sopenharmony_cismb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon, 58162306a36Sopenharmony_ci struct smb2_lease_break *rsp) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci __u8 lease_state = le32_to_cpu(rsp->NewLeaseState); 58462306a36Sopenharmony_ci int ack_req = le32_to_cpu(rsp->Flags & 58562306a36Sopenharmony_ci SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); 58662306a36Sopenharmony_ci struct cifs_pending_open *open; 58762306a36Sopenharmony_ci struct cifs_pending_open *found = NULL; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci list_for_each_entry(open, &tcon->pending_opens, olist) { 59062306a36Sopenharmony_ci if (memcmp(open->lease_key, rsp->LeaseKey, 59162306a36Sopenharmony_ci SMB2_LEASE_KEY_SIZE)) 59262306a36Sopenharmony_ci continue; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (!found && ack_req) { 59562306a36Sopenharmony_ci found = open; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci cifs_dbg(FYI, "found in the pending open list\n"); 59962306a36Sopenharmony_ci cifs_dbg(FYI, "lease key match, lease break 0x%x\n", 60062306a36Sopenharmony_ci lease_state); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci open->oplock = lease_state; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return found; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic bool 60962306a36Sopenharmony_cismb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; 61262306a36Sopenharmony_ci struct TCP_Server_Info *pserver; 61362306a36Sopenharmony_ci struct cifs_ses *ses; 61462306a36Sopenharmony_ci struct cifs_tcon *tcon; 61562306a36Sopenharmony_ci struct cifs_pending_open *open; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci cifs_dbg(FYI, "Checking for lease break\n"); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* If server is a channel, select the primary channel */ 62062306a36Sopenharmony_ci pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* look up tcon based on tid & uid */ 62362306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 62462306a36Sopenharmony_ci list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { 62562306a36Sopenharmony_ci list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { 62662306a36Sopenharmony_ci spin_lock(&tcon->open_file_lock); 62762306a36Sopenharmony_ci cifs_stats_inc( 62862306a36Sopenharmony_ci &tcon->stats.cifs_stats.num_oplock_brks); 62962306a36Sopenharmony_ci if (smb2_tcon_has_lease(tcon, rsp)) { 63062306a36Sopenharmony_ci spin_unlock(&tcon->open_file_lock); 63162306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 63262306a36Sopenharmony_ci return true; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci open = smb2_tcon_find_pending_open_lease(tcon, 63562306a36Sopenharmony_ci rsp); 63662306a36Sopenharmony_ci if (open) { 63762306a36Sopenharmony_ci __u8 lease_key[SMB2_LEASE_KEY_SIZE]; 63862306a36Sopenharmony_ci struct tcon_link *tlink; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci tlink = cifs_get_tlink(open->tlink); 64162306a36Sopenharmony_ci memcpy(lease_key, open->lease_key, 64262306a36Sopenharmony_ci SMB2_LEASE_KEY_SIZE); 64362306a36Sopenharmony_ci spin_unlock(&tcon->open_file_lock); 64462306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 64562306a36Sopenharmony_ci smb2_queue_pending_open_break(tlink, 64662306a36Sopenharmony_ci lease_key, 64762306a36Sopenharmony_ci rsp->NewLeaseState); 64862306a36Sopenharmony_ci return true; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci spin_unlock(&tcon->open_file_lock); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (cached_dir_lease_break(tcon, rsp->LeaseKey)) { 65362306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 65462306a36Sopenharmony_ci return true; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 65962306a36Sopenharmony_ci cifs_dbg(FYI, "Can not process lease break - no lease matched\n"); 66062306a36Sopenharmony_ci trace_smb3_lease_not_found(le32_to_cpu(rsp->CurrentLeaseState), 66162306a36Sopenharmony_ci le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), 66262306a36Sopenharmony_ci le64_to_cpu(rsp->hdr.SessionId), 66362306a36Sopenharmony_ci *((u64 *)rsp->LeaseKey), 66462306a36Sopenharmony_ci *((u64 *)&rsp->LeaseKey[8])); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return false; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cibool 67062306a36Sopenharmony_cismb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer; 67362306a36Sopenharmony_ci struct TCP_Server_Info *pserver; 67462306a36Sopenharmony_ci struct cifs_ses *ses; 67562306a36Sopenharmony_ci struct cifs_tcon *tcon; 67662306a36Sopenharmony_ci struct cifsInodeInfo *cinode; 67762306a36Sopenharmony_ci struct cifsFileInfo *cfile; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci cifs_dbg(FYI, "Checking for oplock break\n"); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (rsp->hdr.Command != SMB2_OPLOCK_BREAK) 68262306a36Sopenharmony_ci return false; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (rsp->StructureSize != 68562306a36Sopenharmony_ci smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { 68662306a36Sopenharmony_ci if (le16_to_cpu(rsp->StructureSize) == 44) 68762306a36Sopenharmony_ci return smb2_is_valid_lease_break(buffer, server); 68862306a36Sopenharmony_ci else 68962306a36Sopenharmony_ci return false; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* If server is a channel, select the primary channel */ 69562306a36Sopenharmony_ci pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* look up tcon based on tid & uid */ 69862306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 69962306a36Sopenharmony_ci list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { 70062306a36Sopenharmony_ci list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci spin_lock(&tcon->open_file_lock); 70362306a36Sopenharmony_ci list_for_each_entry(cfile, &tcon->openFileList, tlist) { 70462306a36Sopenharmony_ci if (rsp->PersistentFid != 70562306a36Sopenharmony_ci cfile->fid.persistent_fid || 70662306a36Sopenharmony_ci rsp->VolatileFid != 70762306a36Sopenharmony_ci cfile->fid.volatile_fid) 70862306a36Sopenharmony_ci continue; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci cifs_dbg(FYI, "file id match, oplock break\n"); 71162306a36Sopenharmony_ci cifs_stats_inc( 71262306a36Sopenharmony_ci &tcon->stats.cifs_stats.num_oplock_brks); 71362306a36Sopenharmony_ci cinode = CIFS_I(d_inode(cfile->dentry)); 71462306a36Sopenharmony_ci spin_lock(&cfile->file_info_lock); 71562306a36Sopenharmony_ci if (!CIFS_CACHE_WRITE(cinode) && 71662306a36Sopenharmony_ci rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) 71762306a36Sopenharmony_ci cfile->oplock_break_cancelled = true; 71862306a36Sopenharmony_ci else 71962306a36Sopenharmony_ci cfile->oplock_break_cancelled = false; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, 72262306a36Sopenharmony_ci &cinode->flags); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci cfile->oplock_epoch = 0; 72562306a36Sopenharmony_ci cfile->oplock_level = rsp->OplockLevel; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci spin_unlock(&cfile->file_info_lock); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci cifs_queue_oplock_break(cfile); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci spin_unlock(&tcon->open_file_lock); 73262306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 73362306a36Sopenharmony_ci return true; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci spin_unlock(&tcon->open_file_lock); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 73962306a36Sopenharmony_ci cifs_dbg(FYI, "No file id matched, oplock break ignored\n"); 74062306a36Sopenharmony_ci trace_smb3_oplock_not_found(0 /* no xid */, rsp->PersistentFid, 74162306a36Sopenharmony_ci le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), 74262306a36Sopenharmony_ci le64_to_cpu(rsp->hdr.SessionId)); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return true; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_civoid 74862306a36Sopenharmony_cismb2_cancelled_close_fid(struct work_struct *work) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct close_cancelled_open *cancelled = container_of(work, 75162306a36Sopenharmony_ci struct close_cancelled_open, work); 75262306a36Sopenharmony_ci struct cifs_tcon *tcon = cancelled->tcon; 75362306a36Sopenharmony_ci int rc; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (cancelled->mid) 75662306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Close unmatched open for MID:%llu\n", 75762306a36Sopenharmony_ci cancelled->mid); 75862306a36Sopenharmony_ci else 75962306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Close interrupted close\n"); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci rc = SMB2_close(0, tcon, cancelled->fid.persistent_fid, 76262306a36Sopenharmony_ci cancelled->fid.volatile_fid); 76362306a36Sopenharmony_ci if (rc) 76462306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci cifs_put_tcon(tcon); 76762306a36Sopenharmony_ci kfree(cancelled); 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci/* 77162306a36Sopenharmony_ci * Caller should already has an extra reference to @tcon 77262306a36Sopenharmony_ci * This function is used to queue work to close a handle to prevent leaks 77362306a36Sopenharmony_ci * on the server. 77462306a36Sopenharmony_ci * We handle two cases. If an open was interrupted after we sent the 77562306a36Sopenharmony_ci * SMB2_CREATE to the server but before we processed the reply, and second 77662306a36Sopenharmony_ci * if a close was interrupted before we sent the SMB2_CLOSE to the server. 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_cistatic int 77962306a36Sopenharmony_ci__smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, 78062306a36Sopenharmony_ci __u64 persistent_fid, __u64 volatile_fid) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct close_cancelled_open *cancelled; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL); 78562306a36Sopenharmony_ci if (!cancelled) 78662306a36Sopenharmony_ci return -ENOMEM; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci cancelled->fid.persistent_fid = persistent_fid; 78962306a36Sopenharmony_ci cancelled->fid.volatile_fid = volatile_fid; 79062306a36Sopenharmony_ci cancelled->tcon = tcon; 79162306a36Sopenharmony_ci cancelled->cmd = cmd; 79262306a36Sopenharmony_ci cancelled->mid = mid; 79362306a36Sopenharmony_ci INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); 79462306a36Sopenharmony_ci WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ciint 80062306a36Sopenharmony_cismb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, 80162306a36Sopenharmony_ci __u64 volatile_fid) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci int rc; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); 80662306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 80762306a36Sopenharmony_ci if (tcon->tc_count <= 0) { 80862306a36Sopenharmony_ci struct TCP_Server_Info *server = NULL; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); 81162306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (tcon->ses) 81462306a36Sopenharmony_ci server = tcon->ses->server; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci cifs_server_dbg(FYI, "tid=0x%x: tcon is closing, skipping async close retry of fid %llu %llu\n", 81762306a36Sopenharmony_ci tcon->tid, persistent_fid, volatile_fid); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return 0; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci tcon->tc_count++; 82262306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, 82562306a36Sopenharmony_ci persistent_fid, volatile_fid); 82662306a36Sopenharmony_ci if (rc) 82762306a36Sopenharmony_ci cifs_put_tcon(tcon); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci return rc; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ciint 83362306a36Sopenharmony_cismb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct smb2_hdr *hdr = mid->resp_buf; 83662306a36Sopenharmony_ci struct smb2_create_rsp *rsp = mid->resp_buf; 83762306a36Sopenharmony_ci struct cifs_tcon *tcon; 83862306a36Sopenharmony_ci int rc; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || hdr->Command != SMB2_CREATE || 84162306a36Sopenharmony_ci hdr->Status != STATUS_SUCCESS) 84262306a36Sopenharmony_ci return 0; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci tcon = smb2_find_smb_tcon(server, le64_to_cpu(hdr->SessionId), 84562306a36Sopenharmony_ci le32_to_cpu(hdr->Id.SyncId.TreeId)); 84662306a36Sopenharmony_ci if (!tcon) 84762306a36Sopenharmony_ci return -ENOENT; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci rc = __smb2_handle_cancelled_cmd(tcon, 85062306a36Sopenharmony_ci le16_to_cpu(hdr->Command), 85162306a36Sopenharmony_ci le64_to_cpu(hdr->MessageId), 85262306a36Sopenharmony_ci rsp->PersistentFileId, 85362306a36Sopenharmony_ci rsp->VolatileFileId); 85462306a36Sopenharmony_ci if (rc) 85562306a36Sopenharmony_ci cifs_put_tcon(tcon); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return rc; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci/** 86162306a36Sopenharmony_ci * smb311_update_preauth_hash - update @ses hash with the packet data in @iov 86262306a36Sopenharmony_ci * 86362306a36Sopenharmony_ci * Assumes @iov does not contain the rfc1002 length and iov[0] has the 86462306a36Sopenharmony_ci * SMB2 header. 86562306a36Sopenharmony_ci * 86662306a36Sopenharmony_ci * @ses: server session structure 86762306a36Sopenharmony_ci * @server: pointer to server info 86862306a36Sopenharmony_ci * @iov: array containing the SMB request we will send to the server 86962306a36Sopenharmony_ci * @nvec: number of array entries for the iov 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_ciint 87262306a36Sopenharmony_cismb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server, 87362306a36Sopenharmony_ci struct kvec *iov, int nvec) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci int i, rc; 87662306a36Sopenharmony_ci struct smb2_hdr *hdr; 87762306a36Sopenharmony_ci struct shash_desc *sha512 = NULL; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci hdr = (struct smb2_hdr *)iov[0].iov_base; 88062306a36Sopenharmony_ci /* neg prot are always taken */ 88162306a36Sopenharmony_ci if (hdr->Command == SMB2_NEGOTIATE) 88262306a36Sopenharmony_ci goto ok; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* 88562306a36Sopenharmony_ci * If we process a command which wasn't a negprot it means the 88662306a36Sopenharmony_ci * neg prot was already done, so the server dialect was set 88762306a36Sopenharmony_ci * and we can test it. Preauth requires 3.1.1 for now. 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_ci if (server->dialect != SMB311_PROT_ID) 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (hdr->Command != SMB2_SESSION_SETUP) 89362306a36Sopenharmony_ci return 0; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* skip last sess setup response */ 89662306a36Sopenharmony_ci if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) 89762306a36Sopenharmony_ci && (hdr->Status == NT_STATUS_OK 89862306a36Sopenharmony_ci || (hdr->Status != 89962306a36Sopenharmony_ci cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)))) 90062306a36Sopenharmony_ci return 0; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ciok: 90362306a36Sopenharmony_ci rc = smb311_crypto_shash_allocate(server); 90462306a36Sopenharmony_ci if (rc) 90562306a36Sopenharmony_ci return rc; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci sha512 = server->secmech.sha512; 90862306a36Sopenharmony_ci rc = crypto_shash_init(sha512); 90962306a36Sopenharmony_ci if (rc) { 91062306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Could not init sha512 shash\n", __func__); 91162306a36Sopenharmony_ci return rc; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci rc = crypto_shash_update(sha512, ses->preauth_sha_hash, 91562306a36Sopenharmony_ci SMB2_PREAUTH_HASH_SIZE); 91662306a36Sopenharmony_ci if (rc) { 91762306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Could not update sha512 shash\n", __func__); 91862306a36Sopenharmony_ci return rc; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci for (i = 0; i < nvec; i++) { 92262306a36Sopenharmony_ci rc = crypto_shash_update(sha512, iov[i].iov_base, iov[i].iov_len); 92362306a36Sopenharmony_ci if (rc) { 92462306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Could not update sha512 shash\n", 92562306a36Sopenharmony_ci __func__); 92662306a36Sopenharmony_ci return rc; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci rc = crypto_shash_final(sha512, ses->preauth_sha_hash); 93162306a36Sopenharmony_ci if (rc) { 93262306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Could not finalize sha512 shash\n", 93362306a36Sopenharmony_ci __func__); 93462306a36Sopenharmony_ci return rc; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci return 0; 93862306a36Sopenharmony_ci} 939