[linux-cifs-client] [PATCH 4/5] [CIFS] have CIFSSMBNegotiate handle SPNEGO via upcall

Jeff Layton jlayton at poochiereds.net
Tue Oct 30 14:56:19 GMT 2007


On Thu, 25 Oct 2007 13:43:47 -0400
Jeff Layton <jlayton at redhat.com> wrote:

> Change args for CIFSSMBNegotiate to take key pointer and a hostname
> string. Have it call cifs_get_spnego_key when the NegotiateProtocol
> response contains a SPNEGO blob.
> 
> Signed-off-by: Jeff Layton <jlayton at redhat.com>
> ---
>  fs/cifs/cifsproto.h |    3 ++-
>  fs/cifs/cifssmb.c   |   41 ++++++++++++++++++++++++++++++-----------
>  fs/cifs/connect.c   |    7 +++++--
>  3 files changed, 37 insertions(+), 14 deletions(-)
>

I just realized that this patch has a significant (and rather
boneheaded) bug. If we call cifs_setup_session on a reconnect, then
we'll end up calling CIFS_SessSetup with a NULL spnego_key. I'm still
looking at the right way to fix it, but anyone testing this should be
aware of this. I'll repost once I have a solution and have tested it.

> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index 0f8a99d..41c29c5 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -104,7 +104,8 @@ void cifs_proc_clean(void);
>  
>  extern int cifs_setup_session(unsigned int xid, struct cifsSesInfo
> *pSesInfo, struct nls_table *nls_info, const char *unc);
> -extern int CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo
> *ses); +extern int CIFSSMBNegotiate(unsigned int xid, struct
> cifsSesInfo *ses,
> +			struct key **spnego_key, const char
> *hostname); 
>  extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
>  			const char *tree, struct cifsTconInfo *tcon,
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index 31686e9..68ac988 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -38,6 +38,7 @@
>  #include "cifsproto.h"
>  #include "cifs_unicode.h"
>  #include "cifs_debug.h"
> +#include "cifs_spnego.h"
>  
>  #ifdef CONFIG_CIFS_POSIX
>  static struct {
> @@ -407,7 +408,8 @@ static int validate_t2(struct smb_t2_rsp *pSMB)
>  	return rc;
>  }
>  int
> -CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
> +CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses,
> +		 struct key **spnego_key, const char *hostname)
>  {
>  	NEGOTIATE_REQ *pSMB;
>  	NEGOTIATE_RSP *pSMBr;
> @@ -641,19 +643,36 @@ CIFSSMBNegotiate(unsigned int xid, struct
> cifsSesInfo *ses) memcpy(server->server_GUID,
>  			       pSMBr->u.extended_response.GUID, 16);
>  
> -		if (count == 16) {
> +		if (count == 16)
>  			server->secType = RawNTLMSSP;
> -		} else {
> -			rc =
> decode_negTokenInit(pSMBr->u.extended_response.
> -						 SecurityBlob,
> -						 count - 16,
> -						 &server->secType);
> -			if (rc == 1) {
> -			/* BB Need to fill struct for sessetup here
> */
> -				rc = -EOPNOTSUPP;
> -			} else {
> +		else {
> +#ifdef CONFIG_CIFS_UPCALL
> +			struct cifs_spnego_msg *msg;
> +			*spnego_key = cifs_get_spnego_key(ses,
> pSMBr->u.
> +
> extended_response.SecurityBlob,
> +						count - 16,
> hostname);
> +			if (IS_ERR(*spnego_key)) {
> +				rc = PTR_ERR(*spnego_key);
> +				*spnego_key = NULL;
> +				goto neg_err_exit;
> +			}
> +
> +			msg = (struct cifs_spnego_msg *)
> +
> (*spnego_key)->payload.data;
> +			if (msg->flags & CIFS_SPNEGO_KRB5)
> +				server->secType = Kerberos;
> +			else if (msg->flags & CIFS_SPNEGO_NTLMSSP)
> +				server->secType = NTLMSSP;
> +			else {
>  				rc = -EINVAL;
> +				goto neg_err_exit;
>  			}
> +#else /* CONFIG_CIFS_UPCALL */
> +			cERROR(1, ("SPNEGO response received, but "
> +				   "CONFIG_CIFS_UPCALL not
> enabled!"));
> +			rc = -EINVAL;
> +			goto neg_err_exit;
> +#endif /* CONFIG_CIFS_UPCALL */
>  		}
>  	} else
>  		server->capabilities &= ~CAP_EXTENDED_SECURITY;
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 057d55c..37e9474 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -3546,6 +3546,7 @@ int cifs_setup_session(unsigned int xid, struct
> cifsSesInfo *pSesInfo, char ntlm_session_key[CIFS_SESS_KEY_SIZE];
>  	int ntlmv2_flag = FALSE;
>  	int first_time = 0;
> +	struct key *spnego_key = NULL;
>  	char *hostname;
>  
>  	hostname = extract_hostname_from_unc(unc);
> @@ -3557,9 +3558,9 @@ int cifs_setup_session(unsigned int xid, struct
> cifsSesInfo *pSesInfo, 
>  	/* what if server changes its buffer size after dropping the
> session? */ if (pSesInfo->server->maxBuf == 0) /* no need to send on
> reconnect */ {
> -		rc = CIFSSMBNegotiate(xid, pSesInfo);
> +		rc = CIFSSMBNegotiate(xid, pSesInfo, &spnego_key,
> hostname); if (rc == -EAGAIN) /* retry only once on 1st time
> connection */ {
> -			rc = CIFSSMBNegotiate(xid, pSesInfo);
> +			rc = CIFSSMBNegotiate(xid, pSesInfo,
> &spnego_key, hostname); if (rc == -EAGAIN)
>  				rc = -EHOSTDOWN;
>  		}
> @@ -3666,6 +3667,8 @@ int cifs_setup_session(unsigned int xid, struct
> cifsSesInfo *pSesInfo, }
>  	}
>  ss_err_exit:
> +	if (spnego_key)
> +		key_put(spnego_key);
>  	if (hostname)
>  		kfree(hostname);
>  

-- 
Jeff Layton <jlayton at poochiereds.net>


More information about the linux-cifs-client mailing list