diff mbox

[16/19] cifs: move sectype to the cifs_ses instead of TCP_Server_Info

Message ID 1369321563-16893-17-git-send-email-jlayton@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jeff Layton May 23, 2013, 3:06 p.m. UTC
Now that we track what sort of NEGOTIATE response was received, stop
mandating that every session on a socket use the same type of auth.

Push that decision out into the session setup code, and make the sectype
a per-session property. This should allow us to mix multiple sectypes on
a socket as long as they are compatible with the NEGOTIATE response.

With this too, we can now eliminate the ses->secFlg field since that
info is redundant and harder to work with than a securityEnum.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/cifs/cifsencrypt.c |   4 +-
 fs/cifs/cifsglob.h    |   2 -
 fs/cifs/cifsproto.h   |   2 +
 fs/cifs/cifssmb.c     |  89 ++++++++------------------------------
 fs/cifs/connect.c     | 117 ++++++++++++++------------------------------------
 fs/cifs/sess.c        |  57 +++++++++++++++++++++++-
 fs/cifs/smb2pdu.c     |  21 +--------
 7 files changed, 114 insertions(+), 178 deletions(-)

Comments

Pavel Shilovsky May 28, 2013, 6:32 a.m. UTC | #1
2013/5/23 Jeff Layton <jlayton@redhat.com>:
> Now that we track what sort of NEGOTIATE response was received, stop
> mandating that every session on a socket use the same type of auth.
>
> Push that decision out into the session setup code, and make the sectype
> a per-session property. This should allow us to mix multiple sectypes on
> a socket as long as they are compatible with the NEGOTIATE response.
>
> With this too, we can now eliminate the ses->secFlg field since that
> info is redundant and harder to work with than a securityEnum.
>
> Signed-off-by: Jeff Layton <jlayton@redhat.com>
> ---
>  fs/cifs/cifsencrypt.c |   4 +-
>  fs/cifs/cifsglob.h    |   2 -
>  fs/cifs/cifsproto.h   |   2 +
>  fs/cifs/cifssmb.c     |  89 ++++++++------------------------------
>  fs/cifs/connect.c     | 117 ++++++++++++++------------------------------------
>  fs/cifs/sess.c        |  57 +++++++++++++++++++++++-
>  fs/cifs/smb2pdu.c     |  21 +--------
>  7 files changed, 114 insertions(+), 178 deletions(-)
>
> diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
> index a85a83d..30bea6b 100644
> --- a/fs/cifs/cifsencrypt.c
> +++ b/fs/cifs/cifsencrypt.c
> @@ -535,7 +535,7 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
>                 return rc;
>         }
>
> -       if (ses->server->secType == RawNTLMSSP)
> +       if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED)
>                 memcpy(ses->auth_key.response + offset,
>                         ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
>         else
> @@ -567,7 +567,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
>         char ntlmv2_hash[16];
>         unsigned char *tiblob = NULL; /* target info blob */
>
> -       if (ses->server->secType == RawNTLMSSP) {
> +       if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {
>                 if (!ses->domainName) {
>                         rc = find_domain_name(ses, nls_cp);
>                         if (rc) {
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index f392ae4..65fd9dd 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -401,7 +401,6 @@ struct smb_vol {
>         kgid_t backupgid;
>         umode_t file_mode;
>         umode_t dir_mode;
> -       unsigned secFlg;
>         enum securityEnum sectype; /* sectype requested via mnt opts */
>         bool sign; /* was signing requested via mnt opts? */
>         bool retry:1;
> @@ -518,7 +517,6 @@ struct TCP_Server_Info {
>         bool echoes:1; /* enable echoes */
>  #endif
>         u16 dialect; /* dialect index that server chose */
> -       enum securityEnum secType;
>         bool oplocks:1; /* enable oplocks */
>         unsigned int maxReq;    /* Clients should submit no more */
>         /* than maxReq distinct unanswered SMBs to the server when using  */
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index ede010f..a82b3c0 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -118,6 +118,8 @@ extern void header_assemble(struct smb_hdr *, char /* command */ ,
>  extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
>                                 struct cifs_ses *ses,
>                                 void **request_buf);
> +extern enum securityEnum select_sectype(struct TCP_Server_Info *server,
> +                               enum securityEnum requested);
>  extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
>                           const struct nls_table *nls_cp);
>  extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index c118533..febe12c 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -368,11 +368,12 @@ vt2_err:
>  }
>
>  static int
> -decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
> +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)
> @@ -391,27 +392,13 @@ decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
>         }
>
>         if (count == SMB1_CLIENT_GUID_SIZE) {
> -               server->secType = RawNTLMSSP;
> +               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;
> -
> -               /* Make sure server supports what we want to use */
> -               switch(server->secType) {
> -               case Kerberos:
> -                       if (!server->sec_kerberos && !server->sec_mskerberos)
> -                               return -EOPNOTSUPP;
> -                       break;
> -               case RawNTLMSSP:
> -                       if (!server->sec_ntlmssp)
> -                               return -EOPNOTSUPP;
> -                       break;
> -               default:
> -                       return -EOPNOTSUPP;
> -               }
>         }
>
>         return 0;
> @@ -446,8 +433,7 @@ cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
>
>  #ifdef CONFIG_CIFS_WEAK_PW_HASH
>  static int
> -decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr,
> -                         unsigned int secFlags, bool sign)
> +decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, bool sign)
>  {
>         __s16 tmp;
>         struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
> @@ -455,12 +441,6 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr,
>         if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT)
>                 return -EOPNOTSUPP;
>
> -       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");
> -               return -EOPNOTSUPP;
> -       }
>         server->sec_mode = le16_to_cpu(rsp->SecurityMode);
>         server->maxReq = min_t(unsigned int,
>                                le16_to_cpu(rsp->MaxMpxCount),
> @@ -535,17 +515,20 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr,
>  #endif
>
>  static bool
> -should_set_ext_sec_flag(unsigned int secFlags)
> +should_set_ext_sec_flag(enum securityEnum sectype)
>  {
> -       if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
> -               return true;
> -       else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5)
> -               return true;
> -       else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
> +       switch (sectype) {
> +       case RawNTLMSSP:
> +       case Kerberos:
>                 return true;
> -       else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP)
> -               return true;
> -       return false;
> +       case Unspecified:
> +               if (global_secflags &
> +                   (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP))
> +                       return true;
> +               /* Fallthrough */
> +       default:
> +               return false;
> +       }
>  }
>
>  int
> @@ -558,7 +541,6 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
>         int i;
>         struct TCP_Server_Info *server = ses->server;
>         u16 count;
> -       unsigned int secFlags;
>
>         if (!server) {
>                 WARN(1, "%s: server is NULL!\n", __func__);
> @@ -570,18 +552,10 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
>         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 (should_set_ext_sec_flag(secFlags)) {
> +       if (should_set_ext_sec_flag(ses->sectype)) {
>                 cifs_dbg(FYI, "Requesting extended security.");
>                 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
>         }
> @@ -610,7 +584,7 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
>                 rc = -EOPNOTSUPP;
>                 goto neg_err_exit;
>         } else if (pSMBr->hdr.WordCount == 13) {
> -               rc = decode_lanman_negprot_rsp(server, pSMBr, secFlags, ses->sign);
> +               rc = decode_lanman_negprot_rsp(server, pSMBr, ses->sign);
>                 server->negflavor = CIFS_NEGFLAVOR_LANMAN;
>                 goto neg_err_exit;
>         } else if (pSMBr->hdr.WordCount != 17) {
> @@ -624,31 +598,6 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
>         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),
> @@ -670,7 +619,7 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
>                         server->capabilities & CAP_EXTENDED_SECURITY) &&
>                                 (pSMBr->EncryptionKeyLength == 0)) {
>                 server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
> -               rc = decode_ext_sec_blob(server, pSMBr);
> +               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 */
>         } else {
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index f5ea895..a57715b 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -1032,56 +1032,40 @@ static int cifs_parse_security_flavors(char *value,
>         vol->sign = false;
>
>         switch (match_token(value, cifs_secflavor_tokens, args)) {
> +       case Opt_sec_krb5p:
> +               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;
> -               vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_SIGN;
>                 break;
> -       case Opt_sec_krb5i:
> -               vol->sectype = Kerberos;
> +       case Opt_sec_ntlmsspi:
>                 vol->sign = true;
> -               vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN;
> -               break;
> -       case Opt_sec_krb5p:
> -               /* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */
> -               cifs_dbg(VFS, "sec=krb5p is not supported!\n");
> -               return 1;
> +               /* Fallthrough */
>         case Opt_sec_ntlmssp:
>                 vol->sectype = RawNTLMSSP;
> -               vol->secFlg |= CIFSSEC_MAY_NTLMSSP;
>                 break;
> -       case Opt_sec_ntlmsspi:
> -               vol->sectype = RawNTLMSSP;
> +       case Opt_sec_ntlmi:
>                 vol->sign = true;
> -               vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN;
> -               break;
> +               /* Fallthrough */
>         case Opt_ntlm:
> -               /* ntlm is default so can be turned off too */
>                 vol->sectype = NTLM;
> -               vol->secFlg |= CIFSSEC_MAY_NTLM;
>                 break;
> -       case Opt_sec_ntlmi:
> -               vol->sectype = NTLM;
> +       case Opt_sec_ntlmv2i:
>                 vol->sign = true;
> -               vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN;
> -               break;
> +               /* Fallthrough */
>         case Opt_sec_ntlmv2:
>                 vol->sectype = NTLMv2;
> -               vol->secFlg |= CIFSSEC_MAY_NTLMV2;
> -               break;
> -       case Opt_sec_ntlmv2i:
> -               vol->sectype = NTLMv2;
> -               vol->sign = true;
> -               vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN;
>                 break;
>  #ifdef CONFIG_CIFS_WEAK_PW_HASH
>         case Opt_sec_lanman:
>                 vol->sectype = LANMAN;
> -               vol->secFlg |= CIFSSEC_MAY_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);
> @@ -1444,7 +1428,6 @@ 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_noac:
> @@ -1994,40 +1977,19 @@ 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 */
> +       /*
> +        * 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;
>
> @@ -2230,7 +2192,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;
> @@ -2507,7 +2473,6 @@ 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 ? volume_info->sign :
>                                 (global_secflags & CIFSSEC_MUST_SIGN);
> @@ -3670,7 +3635,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,
> @@ -3882,27 +3847,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;
> -       default:
> -               /* should never happen */
> -               vol->secFlg = 0;
> -               break;
> -       }
>
>         return cifs_set_cifscreds(vol, ses);
>  }
> diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
> index 82b784a..79358e3 100644
> --- a/fs/cifs/sess.c
> +++ b/fs/cifs/sess.c
> @@ -550,6 +550,56 @@ setup_ntlmv2_ret:
>         return rc;
>  }
>
> +enum securityEnum
> +select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
> +{
> +       switch (server->negflavor) {
> +       case CIFS_NEGFLAVOR_EXTENDED:
> +               switch (requested) {
> +               case Kerberos:
> +               case RawNTLMSSP:
> +                       return requested;
> +               case Unspecified:
> +                       if (server->sec_ntlmssp &&
> +                           (global_secflags & CIFSSEC_MAY_NTLMSSP))
> +                               return RawNTLMSSP;
> +                       if ((server->sec_kerberos || server->sec_mskerberos) &&
> +                           (global_secflags & CIFSSEC_MAY_KRB5))
> +                               return Kerberos;
> +                       /* Fallthrough */
> +               default:
> +                       return Unspecified;
> +               }
> +       case CIFS_NEGFLAVOR_UNENCAP:
> +               switch (requested) {
> +               case NTLM:
> +               case NTLMv2:
> +                       return requested;
> +               case Unspecified:
> +                       if (global_secflags & CIFSSEC_MAY_NTLMV2)
> +                               return NTLMv2;
> +                       if (global_secflags & CIFSSEC_MAY_NTLM)
> +                               return NTLM;
> +                       /* Fallthrough */
> +               default:
> +                       return Unspecified;
> +               }
> +       case CIFS_NEGFLAVOR_LANMAN:
> +               switch (requested) {
> +               case LANMAN:
> +                       return requested;
> +               case Unspecified:
> +                       if (global_secflags & CIFSSEC_MAY_LANMAN)
> +                               return LANMAN;
> +                       /* Fallthrough */
> +               default:
> +                       return Unspecified;
> +               }
> +       default:
> +               return Unspecified;
> +       }
> +}
> +
>  int
>  CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
>                const struct nls_table *nls_cp)
> @@ -576,8 +626,13 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
>                 return -EINVAL;
>         }
>
> -       type = ses->server->secType;
> +       type = select_sectype(ses->server, ses->sectype);
>         cifs_dbg(FYI, "sess setup type %d\n", type);
> +       if (type == Unspecified) {
> +               cifs_dbg(VFS, "Unable to select appropriate authentication method!");
> +               return -EINVAL;
> +       }
> +
>         if (type == RawNTLMSSP) {
>                 /* if memory allocation is successful, caller of this function
>                  * frees it.
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index c753508..d5f2bf0 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -328,7 +328,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
>         int rc = 0;
>         int resp_buftype;
>         struct TCP_Server_Info *server = ses->server;
> -       unsigned int sec_flags;
>         int blob_offset, blob_length;
>         char *security_blob;
>         int flags = CIFS_NEG_OP;
> @@ -344,14 +343,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
>         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);
> @@ -453,7 +444,6 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
>         int resp_buftype;
>         __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
>         struct TCP_Server_Info *server = ses->server;
> -       unsigned int sec_flags;
>         u16 blob_length = 0;
>         char *security_blob;
>         char *ntlmssp_blob = NULL;
> @@ -474,7 +464,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)
> @@ -484,14 +475,6 @@ 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 */
> --
> 1.8.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Acked-by: Pavel Shilovsky <piastry@etersoft.ru>

--
Best regards,
Pavel Shilovsky.
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index a85a83d..30bea6b 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -535,7 +535,7 @@  CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
 		return rc;
 	}
 
-	if (ses->server->secType == RawNTLMSSP)
+	if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED)
 		memcpy(ses->auth_key.response + offset,
 			ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
 	else
@@ -567,7 +567,7 @@  setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
 	char ntlmv2_hash[16];
 	unsigned char *tiblob = NULL; /* target info blob */
 
-	if (ses->server->secType == RawNTLMSSP) {
+	if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {
 		if (!ses->domainName) {
 			rc = find_domain_name(ses, nls_cp);
 			if (rc) {
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index f392ae4..65fd9dd 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -401,7 +401,6 @@  struct smb_vol {
 	kgid_t backupgid;
 	umode_t file_mode;
 	umode_t dir_mode;
-	unsigned secFlg;
 	enum securityEnum sectype; /* sectype requested via mnt opts */
 	bool sign; /* was signing requested via mnt opts? */
 	bool retry:1;
@@ -518,7 +517,6 @@  struct TCP_Server_Info {
 	bool echoes:1; /* enable echoes */
 #endif
 	u16 dialect; /* dialect index that server chose */
-	enum securityEnum secType;
 	bool oplocks:1; /* enable oplocks */
 	unsigned int maxReq;	/* Clients should submit no more */
 	/* than maxReq distinct unanswered SMBs to the server when using  */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index ede010f..a82b3c0 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -118,6 +118,8 @@  extern void header_assemble(struct smb_hdr *, char /* command */ ,
 extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
 				struct cifs_ses *ses,
 				void **request_buf);
+extern enum securityEnum select_sectype(struct TCP_Server_Info *server,
+				enum securityEnum requested);
 extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
 			  const struct nls_table *nls_cp);
 extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index c118533..febe12c 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -368,11 +368,12 @@  vt2_err:
 }
 
 static int
-decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
+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)
@@ -391,27 +392,13 @@  decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
 	}
 
 	if (count == SMB1_CLIENT_GUID_SIZE) {
-		server->secType = RawNTLMSSP;
+		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;
-
-		/* Make sure server supports what we want to use */
-		switch(server->secType) {
-		case Kerberos:
-			if (!server->sec_kerberos && !server->sec_mskerberos)
-				return -EOPNOTSUPP;
-			break;
-		case RawNTLMSSP:
-			if (!server->sec_ntlmssp)
-				return -EOPNOTSUPP;
-			break;
-		default:
-			return -EOPNOTSUPP;
-		}
 	}
 
 	return 0;
@@ -446,8 +433,7 @@  cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
 
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
 static int
-decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr,
-			  unsigned int secFlags, bool sign)
+decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, bool sign)
 {
 	__s16 tmp;
 	struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
@@ -455,12 +441,6 @@  decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr,
 	if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT)
 		return -EOPNOTSUPP;
 
-	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");
-		return -EOPNOTSUPP;
-	}
 	server->sec_mode = le16_to_cpu(rsp->SecurityMode);
 	server->maxReq = min_t(unsigned int,
 			       le16_to_cpu(rsp->MaxMpxCount),
@@ -535,17 +515,20 @@  decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr,
 #endif
 
 static bool
-should_set_ext_sec_flag(unsigned int secFlags)
+should_set_ext_sec_flag(enum securityEnum sectype)
 {
-	if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
-		return true;
-	else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5)
-		return true;
-	else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
+	switch (sectype) {
+	case RawNTLMSSP:
+	case Kerberos:
 		return true;
-	else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP)
-		return true;
-	return false;
+	case Unspecified:
+		if (global_secflags &
+		    (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP))
+			return true;
+		/* Fallthrough */
+	default:
+		return false;
+	}
 }
 
 int
@@ -558,7 +541,6 @@  CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
 	int i;
 	struct TCP_Server_Info *server = ses->server;
 	u16 count;
-	unsigned int secFlags;
 
 	if (!server) {
 		WARN(1, "%s: server is NULL!\n", __func__);
@@ -570,18 +552,10 @@  CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
 	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 (should_set_ext_sec_flag(secFlags)) {
+	if (should_set_ext_sec_flag(ses->sectype)) {
 		cifs_dbg(FYI, "Requesting extended security.");
 		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
 	}
@@ -610,7 +584,7 @@  CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
 		rc = -EOPNOTSUPP;
 		goto neg_err_exit;
 	} else if (pSMBr->hdr.WordCount == 13) {
-		rc = decode_lanman_negprot_rsp(server, pSMBr, secFlags, ses->sign);
+		rc = decode_lanman_negprot_rsp(server, pSMBr, ses->sign);
 		server->negflavor = CIFS_NEGFLAVOR_LANMAN;
 		goto neg_err_exit;
 	} else if (pSMBr->hdr.WordCount != 17) {
@@ -624,31 +598,6 @@  CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
 	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),
@@ -670,7 +619,7 @@  CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
 			server->capabilities & CAP_EXTENDED_SECURITY) &&
 				(pSMBr->EncryptionKeyLength == 0)) {
 		server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
-		rc = decode_ext_sec_blob(server, pSMBr);
+		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 */
 	} else {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index f5ea895..a57715b 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1032,56 +1032,40 @@  static int cifs_parse_security_flavors(char *value,
 	vol->sign = false;
 
 	switch (match_token(value, cifs_secflavor_tokens, args)) {
+	case Opt_sec_krb5p:
+		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;
-		vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_SIGN;
 		break;
-	case Opt_sec_krb5i:
-		vol->sectype = Kerberos;
+	case Opt_sec_ntlmsspi:
 		vol->sign = true;
-		vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN;
-		break;
-	case Opt_sec_krb5p:
-		/* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */
-		cifs_dbg(VFS, "sec=krb5p is not supported!\n");
-		return 1;
+		/* Fallthrough */
 	case Opt_sec_ntlmssp:
 		vol->sectype = RawNTLMSSP;
-		vol->secFlg |= CIFSSEC_MAY_NTLMSSP;
 		break;
-	case Opt_sec_ntlmsspi:
-		vol->sectype = RawNTLMSSP;
+	case Opt_sec_ntlmi:
 		vol->sign = true;
-		vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN;
-		break;
+		/* Fallthrough */
 	case Opt_ntlm:
-		/* ntlm is default so can be turned off too */
 		vol->sectype = NTLM;
-		vol->secFlg |= CIFSSEC_MAY_NTLM;
 		break;
-	case Opt_sec_ntlmi:
-		vol->sectype = NTLM;
+	case Opt_sec_ntlmv2i:
 		vol->sign = true;
-		vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN;
-		break;
+		/* Fallthrough */
 	case Opt_sec_ntlmv2:
 		vol->sectype = NTLMv2;
-		vol->secFlg |= CIFSSEC_MAY_NTLMV2;
-		break;
-	case Opt_sec_ntlmv2i:
-		vol->sectype = NTLMv2;
-		vol->sign = true;
-		vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN;
 		break;
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
 	case Opt_sec_lanman:
 		vol->sectype = LANMAN;
-		vol->secFlg |= CIFSSEC_MAY_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);
@@ -1444,7 +1428,6 @@  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_noac:
@@ -1994,40 +1977,19 @@  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 */
+	/*
+	 * 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;
 
@@ -2230,7 +2192,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;
@@ -2507,7 +2473,6 @@  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 ? volume_info->sign :
 				(global_secflags & CIFSSEC_MUST_SIGN);
@@ -3670,7 +3635,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,
@@ -3882,27 +3847,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;
-	default:
-		/* should never happen */
-		vol->secFlg = 0;
-		break;
-	}
 
 	return cifs_set_cifscreds(vol, ses);
 }
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 82b784a..79358e3 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -550,6 +550,56 @@  setup_ntlmv2_ret:
 	return rc;
 }
 
+enum securityEnum
+select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
+{
+	switch (server->negflavor) {
+	case CIFS_NEGFLAVOR_EXTENDED:
+		switch (requested) {
+		case Kerberos:
+		case RawNTLMSSP:
+			return requested;
+		case Unspecified:
+			if (server->sec_ntlmssp &&
+			    (global_secflags & CIFSSEC_MAY_NTLMSSP))
+				return RawNTLMSSP;
+			if ((server->sec_kerberos || server->sec_mskerberos) &&
+			    (global_secflags & CIFSSEC_MAY_KRB5))
+				return Kerberos;
+			/* Fallthrough */
+		default:
+			return Unspecified;
+		}
+	case CIFS_NEGFLAVOR_UNENCAP:
+		switch (requested) {
+		case NTLM:
+		case NTLMv2:
+			return requested;
+		case Unspecified:
+			if (global_secflags & CIFSSEC_MAY_NTLMV2)
+				return NTLMv2;
+			if (global_secflags & CIFSSEC_MAY_NTLM)
+				return NTLM;
+			/* Fallthrough */
+		default:
+			return Unspecified;
+		}
+	case CIFS_NEGFLAVOR_LANMAN:
+		switch (requested) {
+		case LANMAN:
+			return requested;
+		case Unspecified:
+			if (global_secflags & CIFSSEC_MAY_LANMAN)
+				return LANMAN;
+			/* Fallthrough */
+		default:
+			return Unspecified;
+		}
+	default:
+		return Unspecified;
+	}
+}
+
 int
 CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
 	       const struct nls_table *nls_cp)
@@ -576,8 +626,13 @@  CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
 		return -EINVAL;
 	}
 
-	type = ses->server->secType;
+	type = select_sectype(ses->server, ses->sectype);
 	cifs_dbg(FYI, "sess setup type %d\n", type);
+	if (type == Unspecified) {
+		cifs_dbg(VFS, "Unable to select appropriate authentication method!");
+		return -EINVAL;
+	}
+
 	if (type == RawNTLMSSP) {
 		/* if memory allocation is successful, caller of this function
 		 * frees it.
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index c753508..d5f2bf0 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -328,7 +328,6 @@  SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 	int rc = 0;
 	int resp_buftype;
 	struct TCP_Server_Info *server = ses->server;
-	unsigned int sec_flags;
 	int blob_offset, blob_length;
 	char *security_blob;
 	int flags = CIFS_NEG_OP;
@@ -344,14 +343,6 @@  SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 	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);
@@ -453,7 +444,6 @@  SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
 	int resp_buftype;
 	__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
 	struct TCP_Server_Info *server = ses->server;
-	unsigned int sec_flags;
 	u16 blob_length = 0;
 	char *security_blob;
 	char *ntlmssp_blob = NULL;
@@ -474,7 +464,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)
@@ -484,14 +475,6 @@  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 */