Merge tag 'fcoe' into for-linus
[platform/adaptation/renesas_rcar/renesas_kernel.git] / fs / cifs / cifssmb.c
index a58dc77..a89c4cb 100644 (file)
@@ -367,6 +367,185 @@ vt2_err:
        return -EINVAL;
 }
 
+static int
+decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr)
+{
+       int     rc = 0;
+       u16     count;
+       char    *guid = pSMBr->u.extended_response.GUID;
+       struct TCP_Server_Info *server = ses->server;
+
+       count = get_bcc(&pSMBr->hdr);
+       if (count < SMB1_CLIENT_GUID_SIZE)
+               return -EIO;
+
+       spin_lock(&cifs_tcp_ses_lock);
+       if (server->srv_count > 1) {
+               spin_unlock(&cifs_tcp_ses_lock);
+               if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) {
+                       cifs_dbg(FYI, "server UID changed\n");
+                       memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE);
+               }
+       } else {
+               spin_unlock(&cifs_tcp_ses_lock);
+               memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE);
+       }
+
+       if (count == SMB1_CLIENT_GUID_SIZE) {
+               server->sec_ntlmssp = true;
+       } else {
+               count -= SMB1_CLIENT_GUID_SIZE;
+               rc = decode_negTokenInit(
+                       pSMBr->u.extended_response.SecurityBlob, count, server);
+               if (rc != 1)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+int
+cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
+{
+       bool srv_sign_required = server->sec_mode & server->vals->signing_required;
+       bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled;
+       bool mnt_sign_enabled = global_secflags & CIFSSEC_MAY_SIGN;
+
+       /*
+        * Is signing required by mnt options? If not then check
+        * global_secflags to see if it is there.
+        */
+       if (!mnt_sign_required)
+               mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) ==
+                                               CIFSSEC_MUST_SIGN);
+
+       /*
+        * If signing is required then it's automatically enabled too,
+        * otherwise, check to see if the secflags allow it.
+        */
+       mnt_sign_enabled = mnt_sign_required ? mnt_sign_required :
+                               (global_secflags & CIFSSEC_MAY_SIGN);
+
+       /* If server requires signing, does client allow it? */
+       if (srv_sign_required) {
+               if (!mnt_sign_enabled) {
+                       cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!");
+                       return -ENOTSUPP;
+               }
+               server->sign = true;
+       }
+
+       /* If client requires signing, does server allow it? */
+       if (mnt_sign_required) {
+               if (!srv_sign_enabled) {
+                       cifs_dbg(VFS, "Server does not support signing!");
+                       return -ENOTSUPP;
+               }
+               server->sign = true;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+static int
+decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
+{
+       __s16 tmp;
+       struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
+
+       if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT)
+               return -EOPNOTSUPP;
+
+       server->sec_mode = le16_to_cpu(rsp->SecurityMode);
+       server->maxReq = min_t(unsigned int,
+                              le16_to_cpu(rsp->MaxMpxCount),
+                              cifs_max_pending);
+       set_credits(server, server->maxReq);
+       server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
+       server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
+       /* 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) {
+               server->max_rw = 0xFF00;
+               server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
+       } else {
+               server->max_rw = 0;/* do not need to use raw anyway */
+               server->capabilities = CAP_MPX_MODE;
+       }
+       tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
+       if (tmp == -1) {
+               /* OS/2 often does not set timezone therefore
+                * we must use server time to calc time zone.
+                * Could deviate slightly from the right zone.
+                * Smallest defined timezone difference is 15 minutes
+                * (i.e. Nepal).  Rounding up/down is done to match
+                * this requirement.
+                */
+               int val, seconds, remain, result;
+               struct timespec ts, utc;
+               utc = CURRENT_TIME;
+               ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
+                                   rsp->SrvTime.Time, 0);
+               cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n",
+                        (int)ts.tv_sec, (int)utc.tv_sec,
+                        (int)(utc.tv_sec - ts.tv_sec));
+               val = (int)(utc.tv_sec - ts.tv_sec);
+               seconds = abs(val);
+               result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
+               remain = seconds % MIN_TZ_ADJ;
+               if (remain >= (MIN_TZ_ADJ / 2))
+                       result += MIN_TZ_ADJ;
+               if (val < 0)
+                       result = -result;
+               server->timeAdj = result;
+       } else {
+               server->timeAdj = (int)tmp;
+               server->timeAdj *= 60; /* also in seconds */
+       }
+       cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj);
+
+
+       /* BB get server time for time conversions and add
+       code to use it and timezone since this is not UTC */
+
+       if (rsp->EncryptionKeyLength ==
+                       cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
+               memcpy(server->cryptkey, rsp->EncryptionKey,
+                       CIFS_CRYPTO_KEY_SIZE);
+       } else if (server->sec_mode & SECMODE_PW_ENCRYPT) {
+               return -EIO; /* need cryptkey unless plain text */
+       }
+
+       cifs_dbg(FYI, "LANMAN negotiated\n");
+       return 0;
+}
+#else
+static inline int
+decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
+{
+       cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n");
+       return -EOPNOTSUPP;
+}
+#endif
+
+static bool
+should_set_ext_sec_flag(enum securityEnum sectype)
+{
+       switch (sectype) {
+       case RawNTLMSSP:
+       case Kerberos:
+               return true;
+       case Unspecified:
+               if (global_secflags &
+                   (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP))
+                       return true;
+               /* Fallthrough */
+       default:
+               return false;
+       }
+}
+
 int
 CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
 {
@@ -375,41 +554,24 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
        int rc = 0;
        int bytes_returned;
        int i;
-       struct TCP_Server_Info *server;
+       struct TCP_Server_Info *server = ses->server;
        u16 count;
-       unsigned int secFlags;
 
-       if (ses->server)
-               server = ses->server;
-       else {
-               rc = -EIO;
-               return rc;
+       if (!server) {
+               WARN(1, "%s: server is NULL!\n", __func__);
+               return -EIO;
        }
+
        rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
                      (void **) &pSMB, (void **) &pSMBr);
        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)))
-               secFlags = ses->overrideSecFlg;  /* BB FIXME fix sign flags? */
-       else /* if override flags set only sign/seal OR them with global auth */
-               secFlags = global_secflags | ses->overrideSecFlg;
-
-       cifs_dbg(FYI, "secFlags 0x%x\n", secFlags);
-
        pSMB->hdr.Mid = get_next_mid(server);
        pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
 
-       if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
-               pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
-       else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
-               cifs_dbg(FYI, "Kerberos only mechanism, enable extended security\n");
-               pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
-       } else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
-               pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
-       else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
-               cifs_dbg(FYI, "NTLMSSP only mechanism, enable extended security\n");
+       if (should_set_ext_sec_flag(ses->sectype)) {
+               cifs_dbg(FYI, "Requesting extended security.");
                pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
        }
 
@@ -436,127 +598,21 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
                could not negotiate a common dialect */
                rc = -EOPNOTSUPP;
                goto neg_err_exit;
-#ifdef CONFIG_CIFS_WEAK_PW_HASH
-       } else if ((pSMBr->hdr.WordCount == 13)
-                       && ((server->dialect == LANMAN_PROT)
-                               || (server->dialect == LANMAN2_PROT))) {
-               __s16 tmp;
-               struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
-
-               if ((secFlags & CIFSSEC_MAY_LANMAN) ||
-                       (secFlags & CIFSSEC_MAY_PLNTXT))
-                       server->secType = LANMAN;
-               else {
-                       cifs_dbg(VFS, "mount failed weak security disabled in /proc/fs/cifs/SecurityFlags\n");
-                       rc = -EOPNOTSUPP;
-                       goto neg_err_exit;
-               }
-               server->sec_mode = le16_to_cpu(rsp->SecurityMode);
-               server->maxReq = min_t(unsigned int,
-                                      le16_to_cpu(rsp->MaxMpxCount),
-                                      cifs_max_pending);
-               set_credits(server, server->maxReq);
-               server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
-               server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
-               /* 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) {
-                       server->max_rw = 0xFF00;
-                       server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
-               } else {
-                       server->max_rw = 0;/* do not need to use raw anyway */
-                       server->capabilities = CAP_MPX_MODE;
-               }
-               tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
-               if (tmp == -1) {
-                       /* OS/2 often does not set timezone therefore
-                        * we must use server time to calc time zone.
-                        * Could deviate slightly from the right zone.
-                        * Smallest defined timezone difference is 15 minutes
-                        * (i.e. Nepal).  Rounding up/down is done to match
-                        * this requirement.
-                        */
-                       int val, seconds, remain, result;
-                       struct timespec ts, utc;
-                       utc = CURRENT_TIME;
-                       ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
-                                           rsp->SrvTime.Time, 0);
-                       cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n",
-                                (int)ts.tv_sec, (int)utc.tv_sec,
-                                (int)(utc.tv_sec - ts.tv_sec));
-                       val = (int)(utc.tv_sec - ts.tv_sec);
-                       seconds = abs(val);
-                       result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
-                       remain = seconds % MIN_TZ_ADJ;
-                       if (remain >= (MIN_TZ_ADJ / 2))
-                               result += MIN_TZ_ADJ;
-                       if (val < 0)
-                               result = -result;
-                       server->timeAdj = result;
-               } else {
-                       server->timeAdj = (int)tmp;
-                       server->timeAdj *= 60; /* also in seconds */
-               }
-               cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj);
-
-
-               /* BB get server time for time conversions and add
-               code to use it and timezone since this is not UTC */
-
-               if (rsp->EncryptionKeyLength ==
-                               cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
-                       memcpy(ses->server->cryptkey, rsp->EncryptionKey,
-                               CIFS_CRYPTO_KEY_SIZE);
-               } else if (server->sec_mode & SECMODE_PW_ENCRYPT) {
-                       rc = -EIO; /* need cryptkey unless plain text */
-                       goto neg_err_exit;
-               }
-
-               cifs_dbg(FYI, "LANMAN negotiated\n");
-               /* we will not end up setting signing flags - as no signing
-               was in LANMAN and server did not return the flags on */
-               goto signing_check;
-#else /* weak security disabled */
        } else if (pSMBr->hdr.WordCount == 13) {
-               cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n");
-               rc = -EOPNOTSUPP;
-#endif /* WEAK_PW_HASH */
-               goto neg_err_exit;
+               server->negflavor = CIFS_NEGFLAVOR_LANMAN;
+               rc = decode_lanman_negprot_rsp(server, pSMBr);
+               goto signing_check;
        } else if (pSMBr->hdr.WordCount != 17) {
                /* unknown wct */
                rc = -EOPNOTSUPP;
                goto neg_err_exit;
        }
-       /* else wct == 17 NTLM */
+       /* else wct == 17, NTLM or better */
+
        server->sec_mode = pSMBr->SecurityMode;
        if ((server->sec_mode & SECMODE_USER) == 0)
                cifs_dbg(FYI, "share mode security\n");
 
-       if ((server->sec_mode & SECMODE_PW_ENCRYPT) == 0)
-#ifdef CONFIG_CIFS_WEAK_PW_HASH
-               if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
-#endif /* CIFS_WEAK_PW_HASH */
-                       cifs_dbg(VFS, "Server requests plain text password but client support disabled\n");
-
-       if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
-               server->secType = NTLMv2;
-       else if (secFlags & CIFSSEC_MAY_NTLM)
-               server->secType = NTLM;
-       else if (secFlags & CIFSSEC_MAY_NTLMV2)
-               server->secType = NTLMv2;
-       else if (secFlags & CIFSSEC_MAY_KRB5)
-               server->secType = Kerberos;
-       else if (secFlags & CIFSSEC_MAY_NTLMSSP)
-               server->secType = RawNTLMSSP;
-       else if (secFlags & CIFSSEC_MAY_LANMAN)
-               server->secType = LANMAN;
-       else {
-               rc = -EOPNOTSUPP;
-               cifs_dbg(VFS, "Invalid security type\n");
-               goto neg_err_exit;
-       }
-       /* else ... any others ...? */
-
        /* one byte, so no need to convert this or EncryptionKeyLen from
           little endian */
        server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount),
@@ -569,90 +625,26 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
        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) {
+               server->negflavor = CIFS_NEGFLAVOR_UNENCAP;
                memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
                       CIFS_CRYPTO_KEY_SIZE);
        } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC ||
                        server->capabilities & CAP_EXTENDED_SECURITY) &&
                                (pSMBr->EncryptionKeyLength == 0)) {
-               /* decode security blob */
-               count = get_bcc(&pSMBr->hdr);
-               if (count < 16) {
-                       rc = -EIO;
-                       goto neg_err_exit;
-               }
-               spin_lock(&cifs_tcp_ses_lock);
-               if (server->srv_count > 1) {
-                       spin_unlock(&cifs_tcp_ses_lock);
-                       if (memcmp(server->server_GUID,
-                                  pSMBr->u.extended_response.
-                                  GUID, 16) != 0) {
-                               cifs_dbg(FYI, "server UID changed\n");
-                               memcpy(server->server_GUID,
-                                       pSMBr->u.extended_response.GUID,
-                                       16);
-                       }
-               } else {
-                       spin_unlock(&cifs_tcp_ses_lock);
-                       memcpy(server->server_GUID,
-                              pSMBr->u.extended_response.GUID, 16);
-               }
-
-               if (count == 16) {
-                       server->secType = RawNTLMSSP;
-               } else {
-                       rc = decode_negTokenInit(pSMBr->u.extended_response.
-                                                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;
-               }
+               server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
+               rc = decode_ext_sec_blob(ses, pSMBr);
        } else if (server->sec_mode & SECMODE_PW_ENCRYPT) {
                rc = -EIO; /* no crypt key only if plain text pwd */
-               goto neg_err_exit;
-       } else
-               server->capabilities &= ~CAP_EXTENDED_SECURITY;
-
-#ifdef CONFIG_CIFS_WEAK_PW_HASH
-signing_check:
-#endif
-       if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
-               /* MUST_SIGN already includes the MAY_SIGN FLAG
-                  so if this is zero it means that signing is disabled */
-               cifs_dbg(FYI, "Signing disabled\n");
-               if (server->sec_mode & SECMODE_SIGN_REQUIRED) {
-                       cifs_dbg(VFS, "Server requires packet signing to be enabled in /proc/fs/cifs/SecurityFlags\n");
-                       rc = -EOPNOTSUPP;
-               }
-               server->sec_mode &=
-                       ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
-       } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
-               /* signing required */
-               cifs_dbg(FYI, "Must sign - secFlags 0x%x\n", secFlags);
-               if ((server->sec_mode &
-                       (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
-                       cifs_dbg(VFS, "signing required but server lacks support\n");
-                       rc = -EOPNOTSUPP;
-               } else
-                       server->sec_mode |= SECMODE_SIGN_REQUIRED;
        } else {
-               /* signing optional ie CIFSSEC_MAY_SIGN */
-               if ((server->sec_mode & SECMODE_SIGN_REQUIRED) == 0)
-                       server->sec_mode &=
-                               ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+               server->negflavor = CIFS_NEGFLAVOR_UNENCAP;
+               server->capabilities &= ~CAP_EXTENDED_SECURITY;
        }
 
+signing_check:
+       if (!rc)
+               rc = cifs_enable_signing(server, ses->sign);
 neg_err_exit:
        cifs_buf_release(pSMB);
 
@@ -777,9 +769,8 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
 
        pSMB->hdr.Mid = get_next_mid(ses->server);
 
-       if (ses->server->sec_mode &
-                  (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
-                       pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+       if (ses->server->sign)
+               pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
 
        pSMB->hdr.Uid = ses->Suid;
 
@@ -1540,8 +1531,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
        switch (mid->mid_state) {
        case MID_RESPONSE_RECEIVED:
                /* result already set, check signature */
-               if (server->sec_mode &
-                   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
+               if (server->sign) {
                        int rc = 0;
 
                        rc = cifs_verify_signature(&rqst, server,
@@ -3940,6 +3930,7 @@ QFileInfoRetry:
        pSMB->Pad = 0;
        pSMB->Fid = netfid;
        inc_rfc1001_len(pSMB, byte_count);
+       pSMB->t2.ByteCount = cpu_to_le16(byte_count);
 
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
@@ -4108,6 +4099,7 @@ UnixQFileInfoRetry:
        pSMB->Pad = 0;
        pSMB->Fid = netfid;
        inc_rfc1001_len(pSMB, byte_count);
+       pSMB->t2.ByteCount = cpu_to_le16(byte_count);
 
        rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
@@ -4794,11 +4786,8 @@ getDFSRetry:
                strncpy(pSMB->RequestFileName, search_name, name_len);
        }
 
-       if (ses->server) {
-               if (ses->server->sec_mode &
-                  (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
-                       pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
-       }
+       if (ses->server && ses->server->sign)
+               pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
 
        pSMB->hdr.Uid = ses->Suid;