Merge remote-tracking branch 'asoc/fix/omap' into asoc-linus
[cascardo/linux.git] / fs / cifs / connect.c
index e3bc39b..fa68813 100644 (file)
@@ -85,7 +85,7 @@ enum {
        Opt_acl, Opt_noacl, Opt_locallease,
        Opt_sign, Opt_seal, Opt_noac,
        Opt_fsc, Opt_mfsymlinks,
-       Opt_multiuser, Opt_sloppy,
+       Opt_multiuser, Opt_sloppy, Opt_nosharesock,
 
        /* Mount options which take numeric value */
        Opt_backupuid, Opt_backupgid, Opt_uid,
@@ -165,6 +165,7 @@ static const match_table_t cifs_mount_option_tokens = {
        { Opt_mfsymlinks, "mfsymlinks" },
        { Opt_multiuser, "multiuser" },
        { Opt_sloppy, "sloppy" },
+       { Opt_nosharesock, "nosharesock" },
 
        { Opt_backupuid, "backupuid=%s" },
        { Opt_backupgid, "backupgid=%s" },
@@ -275,6 +276,7 @@ static const match_table_t cifs_smb_version_tokens = {
        { Smb_20, SMB20_VERSION_STRING},
        { Smb_21, SMB21_VERSION_STRING },
        { Smb_30, SMB30_VERSION_STRING },
+       { Smb_302, SMB302_VERSION_STRING },
 };
 
 static int ip_connect(struct TCP_Server_Info *server);
@@ -1024,44 +1026,48 @@ static int cifs_parse_security_flavors(char *value,
 
        substring_t args[MAX_OPT_ARGS];
 
+       /*
+        * With mount options, the last one should win. Reset any existing
+        * settings back to default.
+        */
+       vol->sectype = Unspecified;
+       vol->sign = false;
+
        switch (match_token(value, cifs_secflavor_tokens, args)) {
-       case Opt_sec_krb5:
-               vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_SIGN;
-               break;
-       case Opt_sec_krb5i:
-               vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN;
-               break;
        case Opt_sec_krb5p:
-               /* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */
-               cifs_dbg(VFS, "Krb5 cifs privacy not supported\n");
-               break;
-       case Opt_sec_ntlmssp:
-               vol->secFlg |= CIFSSEC_MAY_NTLMSSP;
+               cifs_dbg(VFS, "sec=krb5p is not supported!\n");
+               return 1;
+       case Opt_sec_krb5i:
+               vol->sign = true;
+               /* Fallthrough */
+       case Opt_sec_krb5:
+               vol->sectype = Kerberos;
                break;
        case Opt_sec_ntlmsspi:
-               vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN;
-               break;
-       case Opt_ntlm:
-               /* ntlm is default so can be turned off too */
-               vol->secFlg |= CIFSSEC_MAY_NTLM;
+               vol->sign = true;
+               /* Fallthrough */
+       case Opt_sec_ntlmssp:
+               vol->sectype = RawNTLMSSP;
                break;
        case Opt_sec_ntlmi:
-               vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN;
-               break;
-       case Opt_sec_ntlmv2:
-               vol->secFlg |= CIFSSEC_MAY_NTLMV2;
+               vol->sign = true;
+               /* Fallthrough */
+       case Opt_ntlm:
+               vol->sectype = NTLM;
                break;
        case Opt_sec_ntlmv2i:
-               vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN;
+               vol->sign = true;
+               /* Fallthrough */
+       case Opt_sec_ntlmv2:
+               vol->sectype = NTLMv2;
                break;
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
        case Opt_sec_lanman:
-               vol->secFlg |= CIFSSEC_MAY_LANMAN;
+               vol->sectype = LANMAN;
                break;
 #endif
        case Opt_sec_none:
                vol->nullauth = 1;
-               vol->secFlg |= CIFSSEC_MAY_NTLM;
                break;
        default:
                cifs_dbg(VFS, "bad security option: %s\n", value);
@@ -1119,6 +1125,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
                vol->ops = &smb30_operations;
                vol->vals = &smb30_values;
                break;
+       case Smb_302:
+               vol->ops = &smb30_operations; /* currently identical with 3.0 */
+               vol->vals = &smb302_values;
+               break;
 #endif
        default:
                cifs_dbg(VFS, "Unknown vers= option specified: %s\n", value);
@@ -1424,7 +1434,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        vol->local_lease = 1;
                        break;
                case Opt_sign:
-                       vol->secFlg |= CIFSSEC_MUST_SIGN;
+                       vol->sign = true;
                        break;
                case Opt_seal:
                        /* we do not do the following in secFlags because seal
@@ -1455,6 +1465,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                case Opt_sloppy:
                        sloppy = true;
                        break;
+               case Opt_nosharesock:
+                       vol->nosharesock = true;
+                       break;
 
                /* Numeric Values */
                case Opt_backupuid:
@@ -1978,47 +1991,21 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr,
 static bool
 match_security(struct TCP_Server_Info *server, struct smb_vol *vol)
 {
-       unsigned int secFlags;
-
-       if (vol->secFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
-               secFlags = vol->secFlg;
-       else
-               secFlags = global_secflags | vol->secFlg;
-
-       switch (server->secType) {
-       case LANMAN:
-               if (!(secFlags & (CIFSSEC_MAY_LANMAN|CIFSSEC_MAY_PLNTXT)))
-                       return false;
-               break;
-       case NTLMv2:
-               if (!(secFlags & CIFSSEC_MAY_NTLMV2))
-                       return false;
-               break;
-       case NTLM:
-               if (!(secFlags & CIFSSEC_MAY_NTLM))
-                       return false;
-               break;
-       case Kerberos:
-               if (!(secFlags & CIFSSEC_MAY_KRB5))
-                       return false;
-               break;
-       case RawNTLMSSP:
-               if (!(secFlags & CIFSSEC_MAY_NTLMSSP))
-                       return false;
-               break;
-       default:
-               /* shouldn't happen */
+       /*
+        * The select_sectype function should either return the vol->sectype
+        * that was specified, or "Unspecified" if that sectype was not
+        * compatible with the given NEGOTIATE request.
+        */
+       if (select_sectype(server, vol->sectype) == Unspecified)
                return false;
-       }
 
-       /* now check if signing mode is acceptable */
-       if ((secFlags & CIFSSEC_MAY_SIGN) == 0 &&
-           (server->sec_mode & SECMODE_SIGN_REQUIRED))
-                       return false;
-       else if (((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) &&
-                (server->sec_mode &
-                 (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED)) == 0)
-                       return false;
+       /*
+        * Now check if signing mode is acceptable. No need to check
+        * global_secflags at this point since if MUST_SIGN is set then
+        * the server->sign had better be too.
+        */
+       if (vol->sign && !server->sign)
+               return false;
 
        return true;
 }
@@ -2027,6 +2014,9 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol)
 {
        struct sockaddr *addr = (struct sockaddr *)&vol->dstaddr;
 
+       if (vol->nosharesock)
+               return 0;
+
        if ((server->vals != vol->vals) || (server->ops != vol->ops))
                return 0;
 
@@ -2118,12 +2108,6 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
                goto out_err;
        }
 
-       rc = cifs_crypto_shash_allocate(tcp_ses);
-       if (rc) {
-               cifs_dbg(VFS, "could not setup hash structures rc %d\n", rc);
-               goto out_err;
-       }
-
        tcp_ses->ops = volume_info->ops;
        tcp_ses->vals = volume_info->vals;
        cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
@@ -2216,7 +2200,11 @@ out_err:
 
 static int match_session(struct cifs_ses *ses, struct smb_vol *vol)
 {
-       switch (ses->server->secType) {
+       if (vol->sectype != Unspecified &&
+           vol->sectype != ses->sectype)
+               return 0;
+
+       switch (ses->sectype) {
        case Kerberos:
                if (!uid_eq(vol->cred_uid, ses->cred_uid))
                        return 0;
@@ -2493,7 +2481,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
        ses->cred_uid = volume_info->cred_uid;
        ses->linux_uid = volume_info->linux_uid;
 
-       ses->overrideSecFlg = volume_info->secFlg;
+       ses->sectype = volume_info->sectype;
+       ses->sign = volume_info->sign;
 
        mutex_lock(&ses->session_mutex);
        rc = cifs_negotiate_protocol(xid, ses);
@@ -3656,7 +3645,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
                   NTLMv2 password here) */
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
                if ((global_secflags & CIFSSEC_MAY_LANMAN) &&
-                   (ses->server->secType == LANMAN))
+                   (ses->sectype == LANMAN))
                        calc_lanman_hash(tcon->password, ses->server->cryptkey,
                                         ses->server->sec_mode &
                                            SECMODE_PW_ENCRYPT ? true : false,
@@ -3674,8 +3663,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
                }
        }
 
-       if (ses->server->sec_mode &
-                       (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+       if (ses->server->sign)
                smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
 
        if (ses->capabilities & CAP_STATUS32) {
@@ -3738,7 +3726,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
                }
                bcc_ptr += length + 1;
                bytes_left -= (length + 1);
-               strncpy(tcon->treeName, tree, MAX_TREE_SIZE);
+               strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
 
                /* mostly informational -- no need to fail on error here */
                kfree(tcon->nativeFileSystem);
@@ -3827,7 +3815,6 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
        int rc = -ENOSYS;
        struct TCP_Server_Info *server = ses->server;
 
-       ses->flags = 0;
        ses->capabilities = server->capabilities;
        if (linuxExtEnabled == 0)
                ses->capabilities &= (~server->vals->cap_unix);
@@ -3848,6 +3835,8 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
                        server->sequence_number = 0x2;
                        server->session_estab = true;
                        ses->auth_key.response = NULL;
+                       if (server->ops->generate_signingkey)
+                               server->ops->generate_signingkey(server);
                }
                mutex_unlock(&server->srv_mutex);
 
@@ -3870,23 +3859,11 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
 static int
 cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses)
 {
-       switch (ses->server->secType) {
-       case Kerberos:
-               vol->secFlg = CIFSSEC_MUST_KRB5;
+       vol->sectype = ses->sectype;
+
+       /* krb5 is special, since we don't need username or pw */
+       if (vol->sectype == Kerberos)
                return 0;
-       case NTLMv2:
-               vol->secFlg = CIFSSEC_MUST_NTLMV2;
-               break;
-       case NTLM:
-               vol->secFlg = CIFSSEC_MUST_NTLM;
-               break;
-       case RawNTLMSSP:
-               vol->secFlg = CIFSSEC_MUST_NTLMSSP;
-               break;
-       case LANMAN:
-               vol->secFlg = CIFSSEC_MUST_LANMAN;
-               break;
-       }
 
        return cifs_set_cifscreds(vol, ses);
 }
@@ -3912,6 +3889,8 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
        vol_info->nocase = master_tcon->nocase;
        vol_info->local_lease = master_tcon->local_lease;
        vol_info->no_linux_ext = !master_tcon->unix_ext;
+       vol_info->sectype = master_tcon->ses->sectype;
+       vol_info->sign = master_tcon->ses->sign;
 
        rc = cifs_set_vol_auth(vol_info, master_tcon->ses);
        if (rc) {