CIFS: Fix lease context buffer parsing
[platform/adaptation/renesas_rcar/renesas_kernel.git] / fs / cifs / smb2pdu.c
index 2b95ce2..19fafeb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/smb2pdu.c
  *
- *   Copyright (C) International Business Machines  Corp., 2009, 2012
+ *   Copyright (C) International Business Machines  Corp., 2009, 2013
  *                 Etersoft, 2012
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *              Pavel Shilovsky (pshilovsky@samba.org) 2012
@@ -108,19 +108,33 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
        if (!tcon)
                goto out;
 
+       /* BB FIXME when we do write > 64K add +1 for every 64K in req or rsp */
+       /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */
+       /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */
+       if ((tcon->ses) &&
+           (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
+               hdr->CreditCharge = cpu_to_le16(1);
+       /* else CreditCharge MBZ */
+
        hdr->TreeId = tcon->tid;
        /* Uid is not converted */
        if (tcon->ses)
                hdr->SessionId = tcon->ses->Suid;
-       /* BB check following DFS flags BB */
-       /* BB do we have to add check for SHI1005_FLAGS_DFS_ROOT too? */
-       if (tcon->share_flags & SHI1005_FLAGS_DFS)
-               hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS;
-       /* BB how does SMB2 do case sensitive? */
-       /* if (tcon->nocase)
-               hdr->Flags |= SMBFLG_CASELESS; */
-       if (tcon->ses && tcon->ses->server &&
-           (tcon->ses->server->sec_mode & SECMODE_SIGN_REQUIRED))
+
+       /*
+        * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have
+        * to pass the path on the Open SMB prefixed by \\server\share.
+        * Not sure when we would need to do the augmented path (if ever) and
+        * setting this flag breaks the SMB2 open operation since it is
+        * illegal to send an empty path name (without \\server\share prefix)
+        * when the DFS flag is set in the SMB open header. We could
+        * consider setting the flag on all operations other than open
+        * but it is safer to net set it for now.
+        */
+/*     if (tcon->share_flags & SHI1005_FLAGS_DFS)
+               hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
+
+       if (tcon->ses && tcon->ses->server && tcon->ses->server->sign)
                hdr->Flags |= SMB2_FLAGS_SIGNED;
 out:
        pdu->StructureSize2 = cpu_to_le16(parmsize);
@@ -328,34 +342,22 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
        struct kvec iov[1];
        int rc = 0;
        int resp_buftype;
-       struct TCP_Server_Info *server;
-       unsigned int sec_flags;
-       u16 temp = 0;
+       struct TCP_Server_Info *server = ses->server;
        int blob_offset, blob_length;
        char *security_blob;
        int flags = CIFS_NEG_OP;
 
        cifs_dbg(FYI, "Negotiate protocol\n");
 
-       if (ses->server)
-               server = ses->server;
-       else {
-               rc = -EIO;
-               return rc;
+       if (!server) {
+               WARN(1, "%s: server is NULL!\n", __func__);
+               return -EIO;
        }
 
        rc = small_smb2_init(SMB2_NEGOTIATE, NULL, (void **) &req);
        if (rc)
                return rc;
 
-       /* if any of auth flags (ie not sign or seal) are overriden use them */
-       if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
-               sec_flags = ses->overrideSecFlg;  /* BB FIXME fix sign flags?*/
-       else /* if override flags set only sign/seal OR them with global auth */
-               sec_flags = global_secflags | ses->overrideSecFlg;
-
-       cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags);
-
        req->hdr.SessionId = 0;
 
        req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
@@ -364,12 +366,12 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
        inc_rfc1001_len(req, 2);
 
        /* only one of SMB2 signing flags may be set in SMB2 request */
-       if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN)
-               temp = SMB2_NEGOTIATE_SIGNING_REQUIRED;
-       else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */
-               temp = SMB2_NEGOTIATE_SIGNING_ENABLED;
-
-       req->SecurityMode = cpu_to_le16(temp);
+       if (ses->sign)
+               req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
+       else if (global_secflags & CIFSSEC_MAY_SIGN)
+               req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
+       else
+               req->SecurityMode = 0;
 
        req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities);
 
@@ -399,6 +401,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
                cifs_dbg(FYI, "negotiated smb2.1 dialect\n");
        else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID))
                cifs_dbg(FYI, "negotiated smb3.0 dialect\n");
+       else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID))
+               cifs_dbg(FYI, "negotiated smb3.02 dialect\n");
        else {
                cifs_dbg(VFS, "Illegal dialect returned by server %d\n",
                         le16_to_cpu(rsp->DialectRevision));
@@ -407,6 +411,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
        }
        server->dialect = le16_to_cpu(rsp->DialectRevision);
 
+       /* SMB2 only has an extended negflavor */
+       server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
        server->maxBuf = le32_to_cpu(rsp->MaxTransactSize);
        server->max_read = le32_to_cpu(rsp->MaxReadSize);
        server->max_write = le32_to_cpu(rsp->MaxWriteSize);
@@ -418,44 +424,22 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 
        security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
                                               &rsp->hdr);
-       if (blob_length == 0) {
-               cifs_dbg(VFS, "missing security blob on negprot\n");
-               rc = -EIO;
-               goto neg_exit;
-       }
-
-       cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags);
-       if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
-               cifs_dbg(FYI, "Signing required\n");
-               if (!(server->sec_mode & (SMB2_NEGOTIATE_SIGNING_REQUIRED |
-                     SMB2_NEGOTIATE_SIGNING_ENABLED))) {
-                       cifs_dbg(VFS, "signing required but server lacks support\n");
-                       rc = -EOPNOTSUPP;
-                       goto neg_exit;
-               }
-               server->sec_mode |= SECMODE_SIGN_REQUIRED;
-       } else if (sec_flags & CIFSSEC_MAY_SIGN) {
-               cifs_dbg(FYI, "Signing optional\n");
-               if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
-                       cifs_dbg(FYI, "Server requires signing\n");
-                       server->sec_mode |= SECMODE_SIGN_REQUIRED;
-               } else {
-                       server->sec_mode &=
-                               ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
-               }
-       } else {
-               cifs_dbg(FYI, "Signing disabled\n");
-               if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
-                       cifs_dbg(VFS, "Server requires packet signing to be enabled in /proc/fs/cifs/SecurityFlags\n");
-                       rc = -EOPNOTSUPP;
-                       goto neg_exit;
-               }
-               server->sec_mode &=
-                       ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
-       }
+       /*
+        * See MS-SMB2 section 2.2.4: if no blob, client picks default which
+        * for us will be
+        *      ses->sectype = RawNTLMSSP;
+        * but for time being this is our only auth choice so doesn't matter.
+        * We just found a server which sets blob length to zero expecting raw.
+        */
+       if (blob_length == 0)
+               cifs_dbg(FYI, "missing security blob on negprot\n");
 
+       rc = cifs_enable_signing(server, ses->sign);
 #ifdef CONFIG_SMB2_ASN1  /* BB REMOVEME when updated asn1.c ready */
-       rc = decode_neg_token_init(security_blob, blob_length,
+       if (rc)
+               goto neg_exit;
+       if (blob_length)
+               rc = decode_neg_token_init(security_blob, blob_length,
                                   &server->sec_type);
        if (rc == 1)
                rc = 0;
@@ -480,9 +464,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
        int rc = 0;
        int resp_buftype;
        __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
-       struct TCP_Server_Info *server;
-       unsigned int sec_flags;
-       u8 temp = 0;
+       struct TCP_Server_Info *server = ses->server;
        u16 blob_length = 0;
        char *security_blob;
        char *ntlmssp_blob = NULL;
@@ -490,11 +472,9 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
 
        cifs_dbg(FYI, "Session Setup\n");
 
-       if (ses->server)
-               server = ses->server;
-       else {
-               rc = -EIO;
-               return rc;
+       if (!server) {
+               WARN(1, "%s: server is NULL!\n", __func__);
+               return -EIO;
        }
 
        /*
@@ -505,7 +485,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
        if (!ses->ntlmssp)
                return -ENOMEM;
 
-       ses->server->secType = RawNTLMSSP;
+       /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
+       ses->sectype = RawNTLMSSP;
 
 ssetup_ntlmssp_authenticate:
        if (phase == NtLmChallenge)
@@ -515,28 +496,19 @@ ssetup_ntlmssp_authenticate:
        if (rc)
                return rc;
 
-       /* if any of auth flags (ie not sign or seal) are overriden use them */
-       if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
-               sec_flags = ses->overrideSecFlg;  /* BB FIXME fix sign flags?*/
-       else /* if override flags set only sign/seal OR them with global auth */
-               sec_flags = global_secflags | ses->overrideSecFlg;
-
-       cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags);
-
        req->hdr.SessionId = 0; /* First session, not a reauthenticate */
        req->VcNumber = 0; /* MBZ */
        /* to enable echos and oplocks */
        req->hdr.CreditRequest = cpu_to_le16(3);
 
        /* only one of SMB2 signing flags may be set in SMB2 request */
-       if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN)
-               temp = SMB2_NEGOTIATE_SIGNING_REQUIRED;
-       else if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
-               temp = SMB2_NEGOTIATE_SIGNING_REQUIRED;
-       else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */
-               temp = SMB2_NEGOTIATE_SIGNING_ENABLED;
-
-       req->SecurityMode = temp;
+       if (server->sign)
+               req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
+       else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */
+               req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+       else
+               req->SecurityMode = 0;
+
        req->Capabilities = 0;
        req->Channel = 0; /* MBZ */
 
@@ -679,7 +651,7 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
 
         /* since no tcon, smb2_init can not do this, so do here */
        req->hdr.SessionId = ses->Suid;
-       if (server->sec_mode & SECMODE_SIGN_REQUIRED)
+       if (server->sign)
                req->hdr.Flags |= SMB2_FLAGS_SIGNED;
 
        rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0);
@@ -788,11 +760,12 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
        }
 
        tcon->share_flags = le32_to_cpu(rsp->ShareFlags);
+       tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */
        tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess);
        tcon->tidStatus = CifsGood;
        tcon->need_reconnect = false;
        tcon->tid = rsp->hdr.TreeId;
-       strncpy(tcon->treeName, tree, MAX_TREE_SIZE);
+       strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
 
        if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
            ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
@@ -880,23 +853,24 @@ parse_lease_state(struct smb2_create_rsp *rsp)
        char *data_offset;
        struct create_lease *lc;
        bool found = false;
+       unsigned int next = 0;
+       char *name;
 
-       data_offset = (char *)rsp;
-       data_offset += 4 + le32_to_cpu(rsp->CreateContextsOffset);
+       data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset);
        lc = (struct create_lease *)data_offset;
        do {
-               char *name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc;
+               lc = (struct create_lease *)((char *)lc + next);
+               name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc;
                if (le16_to_cpu(lc->ccontext.NameLength) != 4 ||
                    strncmp(name, "RqLs", 4)) {
-                       lc = (struct create_lease *)((char *)lc
-                                       + le32_to_cpu(lc->ccontext.Next));
+                       next = le32_to_cpu(lc->ccontext.Next);
                        continue;
                }
                if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
                        return SMB2_OPLOCK_LEVEL_NOCHANGE;
                found = true;
                break;
-       } while (le32_to_cpu(lc->ccontext.Next) != 0);
+       } while (next != 0);
 
        if (!found)
                return 0;
@@ -1036,6 +1010,122 @@ creat_exit:
        return rc;
 }
 
+/*
+ *     SMB2 IOCTL is used for both IOCTLs and FSCTLs
+ */
+int
+SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+          u64 volatile_fid, u32 opcode, bool is_fsctl, char *in_data,
+          u32 indatalen, char **out_data, u32 *plen /* returned data len */)
+{
+       struct smb2_ioctl_req *req;
+       struct smb2_ioctl_rsp *rsp;
+       struct TCP_Server_Info *server;
+       struct cifs_ses *ses = tcon->ses;
+       struct kvec iov[2];
+       int resp_buftype;
+       int num_iovecs;
+       int rc = 0;
+
+       cifs_dbg(FYI, "SMB2 IOCTL\n");
+
+       /* zero out returned data len, in case of error */
+       if (plen)
+               *plen = 0;
+
+       if (ses && (ses->server))
+               server = ses->server;
+       else
+               return -EIO;
+
+       rc = small_smb2_init(SMB2_IOCTL, tcon, (void **) &req);
+       if (rc)
+               return rc;
+
+       req->CtlCode = cpu_to_le32(opcode);
+       req->PersistentFileId = persistent_fid;
+       req->VolatileFileId = volatile_fid;
+
+       if (indatalen) {
+               req->InputCount = cpu_to_le32(indatalen);
+               /* do not set InputOffset if no input data */
+               req->InputOffset =
+                      cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer) - 4);
+               iov[1].iov_base = in_data;
+               iov[1].iov_len = indatalen;
+               num_iovecs = 2;
+       } else
+               num_iovecs = 1;
+
+       req->OutputOffset = 0;
+       req->OutputCount = 0; /* MBZ */
+
+       /*
+        * Could increase MaxOutputResponse, but that would require more
+        * than one credit. Windows typically sets this smaller, but for some
+        * ioctls it may be useful to allow server to send more. No point
+        * limiting what the server can send as long as fits in one credit
+        */
+       req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */
+
+       if (is_fsctl)
+               req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
+       else
+               req->Flags = 0;
+
+       iov[0].iov_base = (char *)req;
+       /* 4 for rfc1002 length field */
+       iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+       if (indatalen)
+               inc_rfc1001_len(req, indatalen);
+
+       rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
+       rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
+
+       if (rc != 0) {
+               if (tcon)
+                       cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+               goto ioctl_exit;
+       }
+
+       /* check if caller wants to look at return data or just return rc */
+       if ((plen == NULL) || (out_data == NULL))
+               goto ioctl_exit;
+
+       *plen = le32_to_cpu(rsp->OutputCount);
+
+       /* We check for obvious errors in the output buffer length and offset */
+       if (*plen == 0)
+               goto ioctl_exit; /* server returned no data */
+       else if (*plen > 0xFF00) {
+               cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen);
+               *plen = 0;
+               rc = -EIO;
+               goto ioctl_exit;
+       }
+
+       if (get_rfc1002_length(rsp) < le32_to_cpu(rsp->OutputOffset) + *plen) {
+               cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen,
+                       le32_to_cpu(rsp->OutputOffset));
+               *plen = 0;
+               rc = -EIO;
+               goto ioctl_exit;
+       }
+
+       *out_data = kmalloc(*plen, GFP_KERNEL);
+       if (*out_data == NULL) {
+               rc = -ENOMEM;
+               goto ioctl_exit;
+       }
+
+       memcpy(*out_data, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
+              *plen);
+ioctl_exit:
+       free_rsp_buf(resp_buftype, rsp);
+       return rc;
+}
+
 int
 SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
           u64 persistent_fid, u64 volatile_fid)
@@ -1384,8 +1474,7 @@ smb2_readv_callback(struct mid_q_entry *mid)
        case MID_RESPONSE_RECEIVED:
                credits_received = le16_to_cpu(buf->CreditRequest);
                /* result already set, check signature */
-               if (server->sec_mode &
-                   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
+               if (server->sign) {
                        int rc;
 
                        rc = smb2_verify_signature(&rqst, server);