cifs: fix possible memory corruption in CIFSFindNext, CVE-2011-3191
[linux-flexiantxendom0-natty.git] / fs / cifs / cifssmb.c
index 980c38c..e9abcf9 100644 (file)
@@ -91,13 +91,13 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
        struct list_head *tmp1;
 
 /* list all files open on tree connection and mark them invalid */
-       write_lock(&GlobalSMBSeslock);
+       spin_lock(&cifs_file_list_lock);
        list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
                open_file = list_entry(tmp, struct cifsFileInfo, tlist);
                open_file->invalidHandle = true;
                open_file->oplock_break_cancelled = true;
        }
-       write_unlock(&GlobalSMBSeslock);
+       spin_unlock(&cifs_file_list_lock);
        /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
           to this tcon */
 }
@@ -136,9 +136,6 @@ cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)
                }
        }
 
-       if (ses->status == CifsExiting)
-               return -EIO;
-
        /*
         * Give demultiplex thread up to 10 seconds to reconnect, should be
         * greater than cifs socket timeout which is 7 seconds
@@ -156,7 +153,7 @@ cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)
                 * retrying until process is killed or server comes
                 * back on-line
                 */
-               if (!tcon->retry || ses->status == CifsExiting) {
+               if (!tcon->retry) {
                        cFYI(1, "gave up waiting on reconnect in smb_init");
                        return -EHOSTDOWN;
                }
@@ -172,7 +169,8 @@ cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)
         * reconnect the same SMB session
         */
        mutex_lock(&ses->session_mutex);
-       if (ses->need_reconnect)
+       rc = cifs_negotiate_protocol(0, ses);
+       if (rc == 0 && ses->need_reconnect)
                rc = cifs_setup_session(0, ses, nls_codepage);
 
        /* do we need to reconnect tcon? */
@@ -231,7 +229,7 @@ static int
 small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
                void **request_buf)
 {
-       int rc = 0;
+       int rc;
 
        rc = cifs_reconnect_tcon(tcon, smb_command);
        if (rc)
@@ -249,7 +247,7 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
        if (tcon != NULL)
                cifs_stats_inc(&tcon->num_smbs_sent);
 
-       return rc;
+       return 0;
 }
 
 int
@@ -280,16 +278,9 @@ small_smb_init_no_tc(const int smb_command, const int wct,
 
 /* If the return code is zero, this function must fill in request_buf pointer */
 static int
-smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
-        void **request_buf /* returned */ ,
-        void **response_buf /* returned */ )
+__smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
+                       void **request_buf, void **response_buf)
 {
-       int rc = 0;
-
-       rc = cifs_reconnect_tcon(tcon, smb_command);
-       if (rc)
-               return rc;
-
        *request_buf = cifs_buf_get();
        if (*request_buf == NULL) {
                /* BB should we add a retry in here if not a writepage? */
@@ -308,42 +299,64 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
        if (tcon != NULL)
                cifs_stats_inc(&tcon->num_smbs_sent);
 
-       return rc;
+       return 0;
+}
+
+/* If the return code is zero, this function must fill in request_buf pointer */
+static int
+smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
+        void **request_buf, void **response_buf)
+{
+       int rc;
+
+       rc = cifs_reconnect_tcon(tcon, smb_command);
+       if (rc)
+               return rc;
+
+       return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
+}
+
+static int
+smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon,
+                       void **request_buf, void **response_buf)
+{
+       if (tcon->ses->need_reconnect || tcon->need_reconnect)
+               return -EHOSTDOWN;
+
+       return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
 }
 
 static int validate_t2(struct smb_t2_rsp *pSMB)
 {
-       int rc = -EINVAL;
-       int total_size;
-       char *pBCC;
+       unsigned int total_size;
+
+       /* check for plausible wct */
+       if (pSMB->hdr.WordCount < 10)
+               goto vt2_err;
 
-       /* check for plausible wct, bcc and t2 data and parm sizes */
        /* check for parm and data offset going beyond end of smb */
-       if (pSMB->hdr.WordCount >= 10) {
-               if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
-                  (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
-                       /* check that bcc is at least as big as parms + data */
-                       /* check that bcc is less than negotiated smb buffer */
-                       total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
-                       if (total_size < 512) {
-                               total_size +=
-                                       le16_to_cpu(pSMB->t2_rsp.DataCount);
-                               /* BCC le converted in SendReceive */
-                               pBCC = (pSMB->hdr.WordCount * 2) +
-                                       sizeof(struct smb_hdr) +
-                                       (char *)pSMB;
-                               if ((total_size <= (*(u16 *)pBCC)) &&
-                                  (total_size <
-                                       CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
-                                       return 0;
-                               }
-                       }
-               }
-       }
+       if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 ||
+           get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024)
+               goto vt2_err;
+
+       /* check that bcc is at least as big as parms + data */
+       /* check that bcc is less than negotiated smb buffer */
+       total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount);
+       if (total_size >= 512)
+               goto vt2_err;
+
+       total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount);
+       if (total_size > get_bcc(&pSMB->hdr) ||
+           total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE)
+               goto vt2_err;
+
+       return 0;
+vt2_err:
        cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
                sizeof(struct smb_t2_rsp) + 16);
-       return rc;
+       return -EINVAL;
 }
+
 int
 CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
 {
@@ -355,7 +368,6 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
        struct TCP_Server_Info *server;
        u16 count;
        unsigned int secFlags;
-       u16 dialect;
 
        if (ses->server)
                server = ses->server;
@@ -372,7 +384,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
        if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
                secFlags = ses->overrideSecFlg;  /* BB FIXME fix sign flags? */
        else /* if override flags set only sign/seal OR them with global auth */
-               secFlags = extended_security | ses->overrideSecFlg;
+               secFlags = global_secflags | ses->overrideSecFlg;
 
        cFYI(1, "secFlags 0x%x", secFlags);
 
@@ -384,15 +396,12 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
        else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
                cFYI(1, "Kerberos only mechanism, enable extended security");
                pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
-       }
-#ifdef CONFIG_CIFS_EXPERIMENTAL
-       else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
+       } else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
                pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
        else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
                cFYI(1, "NTLMSSP only mechanism, enable extended security");
                pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
        }
-#endif
 
        count = 0;
        for (i = 0; i < CIFS_NUM_PROT; i++) {
@@ -408,10 +417,10 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
        if (rc != 0)
                goto neg_err_exit;
 
-       dialect = le16_to_cpu(pSMBr->DialectIndex);
-       cFYI(1, "Dialect: %d", dialect);
+       server->dialect = le16_to_cpu(pSMBr->DialectIndex);
+       cFYI(1, "Dialect: %d", server->dialect);
        /* Check wct = 1 error case */
-       if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) {
+       if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {
                /* core returns wct = 1, but we do not ask for core - otherwise
                small wct just comes when dialect index is -1 indicating we
                could not negotiate a common dialect */
@@ -419,8 +428,8 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                goto neg_err_exit;
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
        } else if ((pSMBr->hdr.WordCount == 13)
-                       && ((dialect == LANMAN_PROT)
-                               || (dialect == LANMAN2_PROT))) {
+                       && ((server->dialect == LANMAN_PROT)
+                               || (server->dialect == LANMAN2_PROT))) {
                __s16 tmp;
                struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
 
@@ -438,7 +447,6 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
                                (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
                server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
-               GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
                /* even though we do not use raw we might as well set this
                accurately, in case we ever find a need for it */
                if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
@@ -486,7 +494,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
 
                if (rsp->EncryptionKeyLength ==
                                cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
-                       memcpy(server->cryptKey, rsp->EncryptionKey,
+                       memcpy(ses->server->cryptkey, rsp->EncryptionKey,
                                CIFS_CRYPTO_KEY_SIZE);
                } else if (server->secMode & SECMODE_PW_ENCRYPT) {
                        rc = -EIO; /* need cryptkey unless plain text */
@@ -552,12 +560,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                        (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
        server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
        cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
-       GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
        server->capabilities = le32_to_cpu(pSMBr->Capabilities);
        server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
        server->timeAdj *= 60;
        if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
-               memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
+               memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
                       CIFS_CRYPTO_KEY_SIZE);
        } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
                        && (pSMBr->EncryptionKeyLength == 0)) {
@@ -576,9 +583,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                        rc = -EIO;
                        goto neg_err_exit;
                }
-               read_lock(&cifs_tcp_ses_lock);
+               spin_lock(&cifs_tcp_ses_lock);
                if (server->srv_count > 1) {
-                       read_unlock(&cifs_tcp_ses_lock);
+                       spin_unlock(&cifs_tcp_ses_lock);
                        if (memcmp(server->server_GUID,
                                   pSMBr->u.extended_response.
                                   GUID, 16) != 0) {
@@ -588,7 +595,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                                        16);
                        }
                } else {
-                       read_unlock(&cifs_tcp_ses_lock);
+                       spin_unlock(&cifs_tcp_ses_lock);
                        memcpy(server->server_GUID,
                               pSMBr->u.extended_response.GUID, 16);
                }
@@ -597,13 +604,21 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                        server->secType = RawNTLMSSP;
                } else {
                        rc = decode_negTokenInit(pSMBr->u.extended_response.
-                                                SecurityBlob,
-                                                count - 16,
-                                                &server->secType);
+                                                SecurityBlob, count - 16,
+                                                server);
                        if (rc == 1)
                                rc = 0;
                        else
                                rc = -EINVAL;
+                       if (server->secType == Kerberos) {
+                               if (!server->sec_kerberos &&
+                                               !server->sec_mskerberos)
+                                       rc = -EOPNOTSUPP;
+                       } else if (server->secType == RawNTLMSSP) {
+                               if (!server->sec_ntlmssp)
+                                       rc = -EOPNOTSUPP;
+                       } else
+                                       rc = -EOPNOTSUPP;
                }
        } else
                server->capabilities &= ~CAP_EXTENDED_SECURITY;
@@ -684,6 +699,53 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
        return rc;
 }
 
+/*
+ * This is a no-op for now. We're not really interested in the reply, but
+ * rather in the fact that the server sent one and that server->lstrp
+ * gets updated.
+ *
+ * FIXME: maybe we should consider checking that the reply matches request?
+ */
+static void
+cifs_echo_callback(struct mid_q_entry *mid)
+{
+       struct TCP_Server_Info *server = mid->callback_data;
+
+       DeleteMidQEntry(mid);
+       atomic_dec(&server->inFlight);
+       wake_up(&server->request_q);
+}
+
+int
+CIFSSMBEcho(struct TCP_Server_Info *server)
+{
+       ECHO_REQ *smb;
+       int rc = 0;
+
+       cFYI(1, "In echo request");
+
+       rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb);
+       if (rc)
+               return rc;
+
+       /* set up echo request */
+       smb->hdr.Tid = cpu_to_le16(0xffff);
+       smb->hdr.WordCount = 1;
+       put_unaligned_le16(1, &smb->EchoCount);
+       put_bcc_le(1, &smb->hdr);
+       smb->Data[0] = 'a';
+       smb->hdr.smb_buf_length += 3;
+
+       rc = cifs_call_async(server, (struct smb_hdr *)smb,
+                               cifs_echo_callback, server);
+       if (rc)
+               cFYI(1, "Echo request failed: %d", rc);
+
+       cifs_small_buf_release(smb);
+
+       return rc;
+}
+
 int
 CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
 {
@@ -1171,7 +1233,7 @@ OldOpenRetry:
        pSMB->ByteCount = cpu_to_le16(count);
        /* long_op set to 1 to allow for oplock break timeouts */
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
-                       (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
+                       (struct smb_hdr *)pSMBr, &bytes_returned, 0);
        cifs_stats_inc(&tcon->num_opens);
        if (rc) {
                cFYI(1, "Error in Open = %d", rc);
@@ -1284,7 +1346,7 @@ openRetry:
        pSMB->ByteCount = cpu_to_le16(count);
        /* long_op set to 1 to allow for oplock break timeouts */
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
-                       (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
+                       (struct smb_hdr *)pSMBr, &bytes_returned, 0);
        cifs_stats_inc(&tcon->num_opens);
        if (rc) {
                cFYI(1, "Error in Open = %d", rc);
@@ -1366,7 +1428,7 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
        iov[0].iov_base = (char *)pSMB;
        iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
        rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
-                        &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
+                        &resp_buf_type, CIFS_LOG_ERROR);
        cifs_stats_inc(&tcon->num_reads);
        pSMBr = (READ_RSP *)iov[0].iov_base;
        if (rc) {
@@ -1641,7 +1703,8 @@ int
 CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
            const __u16 smb_file_id, const __u64 len,
            const __u64 offset, const __u32 numUnlock,
-           const __u32 numLock, const __u8 lockType, const bool waitFlag)
+           const __u32 numLock, const __u8 lockType,
+           const bool waitFlag, const __u8 oplock_level)
 {
        int rc = 0;
        LOCK_REQ *pSMB = NULL;
@@ -1669,6 +1732,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
        pSMB->NumberOfLocks = cpu_to_le16(numLock);
        pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
        pSMB->LockType = lockType;
+       pSMB->OplockLevel = oplock_level;
        pSMB->AndXCommand = 0xFF;       /* none */
        pSMB->Fid = smb_file_id; /* netfid stays le */
 
@@ -2453,95 +2517,6 @@ querySymLinkRetry:
 }
 
 #ifdef CONFIG_CIFS_EXPERIMENTAL
-/* Initialize NT TRANSACT SMB into small smb request buffer.
-   This assumes that all NT TRANSACTS that we init here have
-   total parm and data under about 400 bytes (to fit in small cifs
-   buffer size), which is the case so far, it easily fits. NB:
-       Setup words themselves and ByteCount
-       MaxSetupCount (size of returned setup area) and
-       MaxParameterCount (returned parms size) must be set by caller */
-static int
-smb_init_nttransact(const __u16 sub_command, const int setup_count,
-                  const int parm_len, struct cifsTconInfo *tcon,
-                  void **ret_buf)
-{
-       int rc;
-       __u32 temp_offset;
-       struct smb_com_ntransact_req *pSMB;
-
-       rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
-                               (void **)&pSMB);
-       if (rc)
-               return rc;
-       *ret_buf = (void *)pSMB;
-       pSMB->Reserved = 0;
-       pSMB->TotalParameterCount = cpu_to_le32(parm_len);
-       pSMB->TotalDataCount  = 0;
-       pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
-                                         MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
-       pSMB->ParameterCount = pSMB->TotalParameterCount;
-       pSMB->DataCount  = pSMB->TotalDataCount;
-       temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
-                       (setup_count * 2) - 4 /* for rfc1001 length itself */;
-       pSMB->ParameterOffset = cpu_to_le32(temp_offset);
-       pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
-       pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
-       pSMB->SubCommand = cpu_to_le16(sub_command);
-       return 0;
-}
-
-static int
-validate_ntransact(char *buf, char **ppparm, char **ppdata,
-                  __u32 *pparmlen, __u32 *pdatalen)
-{
-       char *end_of_smb;
-       __u32 data_count, data_offset, parm_count, parm_offset;
-       struct smb_com_ntransact_rsp *pSMBr;
-
-       *pdatalen = 0;
-       *pparmlen = 0;
-
-       if (buf == NULL)
-               return -EINVAL;
-
-       pSMBr = (struct smb_com_ntransact_rsp *)buf;
-
-       /* ByteCount was converted from little endian in SendReceive */
-       end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
-                       (char *)&pSMBr->ByteCount;
-
-       data_offset = le32_to_cpu(pSMBr->DataOffset);
-       data_count = le32_to_cpu(pSMBr->DataCount);
-       parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
-       parm_count = le32_to_cpu(pSMBr->ParameterCount);
-
-       *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
-       *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
-
-       /* should we also check that parm and data areas do not overlap? */
-       if (*ppparm > end_of_smb) {
-               cFYI(1, "parms start after end of smb");
-               return -EINVAL;
-       } else if (parm_count + *ppparm > end_of_smb) {
-               cFYI(1, "parm end after end of smb");
-               return -EINVAL;
-       } else if (*ppdata > end_of_smb) {
-               cFYI(1, "data starts after end of smb");
-               return -EINVAL;
-       } else if (data_count + *ppdata > end_of_smb) {
-               cFYI(1, "data %p + count %d (%p) past smb end %p start %p",
-                       *ppdata, data_count, (data_count + *ppdata),
-                       end_of_smb, pSMBr);
-               return -EINVAL;
-       } else if (parm_count + data_count > pSMBr->ByteCount) {
-               cFYI(1, "parm count and data count larger than SMB");
-               return -EINVAL;
-       }
-       *pdatalen = data_count;
-       *pparmlen = parm_count;
-       return 0;
-}
-
 int
 CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
                        const unsigned char *searchName,
@@ -3031,7 +3006,97 @@ GetExtAttrOut:
 
 #endif /* CONFIG_POSIX */
 
-#ifdef CONFIG_CIFS_EXPERIMENTAL
+#ifdef CONFIG_CIFS_ACL
+/*
+ * Initialize NT TRANSACT SMB into small smb request buffer.  This assumes that
+ * all NT TRANSACTS that we init here have total parm and data under about 400
+ * bytes (to fit in small cifs buffer size), which is the case so far, it
+ * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of
+ * returned setup area) and MaxParameterCount (returned parms size) must be set
+ * by caller
+ */
+static int
+smb_init_nttransact(const __u16 sub_command, const int setup_count,
+                  const int parm_len, struct cifsTconInfo *tcon,
+                  void **ret_buf)
+{
+       int rc;
+       __u32 temp_offset;
+       struct smb_com_ntransact_req *pSMB;
+
+       rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
+                               (void **)&pSMB);
+       if (rc)
+               return rc;
+       *ret_buf = (void *)pSMB;
+       pSMB->Reserved = 0;
+       pSMB->TotalParameterCount = cpu_to_le32(parm_len);
+       pSMB->TotalDataCount  = 0;
+       pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
+                                         MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+       pSMB->ParameterCount = pSMB->TotalParameterCount;
+       pSMB->DataCount  = pSMB->TotalDataCount;
+       temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
+                       (setup_count * 2) - 4 /* for rfc1001 length itself */;
+       pSMB->ParameterOffset = cpu_to_le32(temp_offset);
+       pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
+       pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
+       pSMB->SubCommand = cpu_to_le16(sub_command);
+       return 0;
+}
+
+static int
+validate_ntransact(char *buf, char **ppparm, char **ppdata,
+                  __u32 *pparmlen, __u32 *pdatalen)
+{
+       char *end_of_smb;
+       __u32 data_count, data_offset, parm_count, parm_offset;
+       struct smb_com_ntransact_rsp *pSMBr;
+
+       *pdatalen = 0;
+       *pparmlen = 0;
+
+       if (buf == NULL)
+               return -EINVAL;
+
+       pSMBr = (struct smb_com_ntransact_rsp *)buf;
+
+       /* ByteCount was converted from little endian in SendReceive */
+       end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
+                       (char *)&pSMBr->ByteCount;
+
+       data_offset = le32_to_cpu(pSMBr->DataOffset);
+       data_count = le32_to_cpu(pSMBr->DataCount);
+       parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
+       parm_count = le32_to_cpu(pSMBr->ParameterCount);
+
+       *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
+       *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
+
+       /* should we also check that parm and data areas do not overlap? */
+       if (*ppparm > end_of_smb) {
+               cFYI(1, "parms start after end of smb");
+               return -EINVAL;
+       } else if (parm_count + *ppparm > end_of_smb) {
+               cFYI(1, "parm end after end of smb");
+               return -EINVAL;
+       } else if (*ppdata > end_of_smb) {
+               cFYI(1, "data starts after end of smb");
+               return -EINVAL;
+       } else if (data_count + *ppdata > end_of_smb) {
+               cFYI(1, "data %p + count %d (%p) past smb end %p start %p",
+                       *ppdata, data_count, (data_count + *ppdata),
+                       end_of_smb, pSMBr);
+               return -EINVAL;
+       } else if (parm_count + data_count > pSMBr->ByteCount) {
+               cFYI(1, "parm count and data count larger than SMB");
+               return -EINVAL;
+       }
+       *pdatalen = data_count;
+       *pparmlen = parm_count;
+       return 0;
+}
+
 /* Get Security Descriptor (by handle) from remote server for a file or dir */
 int
 CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
@@ -3064,7 +3129,7 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
        iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
 
        rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
-                        CIFS_STD_OP);
+                        0);
        cifs_stats_inc(&tcon->num_acl_get);
        if (rc) {
                cFYI(1, "Send error in QuerySecDesc = %d", rc);
@@ -3189,7 +3254,7 @@ setCifsAclRetry:
        return (rc);
 }
 
-#endif /* CONFIG_CIFS_EXPERIMENTAL */
+#endif /* CONFIG_CIFS_ACL */
 
 /* Legacy Query Path Information call for lookup to old servers such
    as Win9x/WinME */
@@ -3740,7 +3805,8 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
        T2_FNEXT_RSP_PARMS *parms;
        char *response_data;
        int rc = 0;
-       int bytes_returned, name_len;
+       int bytes_returned;
+       unsigned int name_len;
        __u16 params, byte_count;
 
        cFYI(1, "In FindNext");
@@ -4528,8 +4594,8 @@ CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
 
        cFYI(1, "In QFSUnixInfo");
 QFSUnixRetry:
-       rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
-                     (void **) &pSMBr);
+       rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
+                                  (void **) &pSMB, (void **) &pSMBr);
        if (rc)
                return rc;
 
@@ -4598,8 +4664,8 @@ CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
        cFYI(1, "In SETFSUnixInfo");
 SETFSUnixRetry:
        /* BB switch to small buf init to save memory */
-       rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
-                     (void **) &pSMBr);
+       rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
+                                       (void **) &pSMB, (void **) &pSMBr);
        if (rc)
                return rc;
 
@@ -4846,7 +4912,6 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
                   __u16 fid, __u32 pid_of_opener, bool SetAllocation)
 {
        struct smb_com_transaction2_sfi_req *pSMB  = NULL;
-       char *data_offset;
        struct file_end_of_file_info *parm_data;
        int rc = 0;
        __u16 params, param_offset, offset, byte_count, count;
@@ -4870,8 +4935,6 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
        param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
        offset = param_offset + params;
 
-       data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
-
        count = sizeof(struct file_end_of_file_info);
        pSMB->MaxParameterCount = cpu_to_le16(2);
        /* BB find exact max SMB PDU from sess structure BB */
@@ -5539,7 +5602,7 @@ QAllEAsRetry:
        }
 
        /* make sure list_len doesn't go past end of SMB */
-       end_of_smb = (char *)pByteArea(&pSMBr->hdr) + BCC(&pSMBr->hdr);
+       end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);
        if ((char *)ea_response_data + list_len > end_of_smb) {
                cFYI(1, "EA list appears to go beyond SMB");
                rc = -EIO;