Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ceb1b0b9 authored by Steve French's avatar Steve French
Browse files

[SMB3] Fix sec=krb5 on smb3 mounts

Kerberos, which is very important for security, was only enabled for
CIFS not SMB2/SMB3 mounts (e.g. vers=3.0)

Patch based on the information detailed in
http://thread.gmane.org/gmane.linux.kernel.cifs/10081/focus=10307


to enable Kerberized SMB2/SMB3

a) SMB2_negotiate: enable/use decode_negTokenInit in SMB2_negotiate
b) SMB2_sess_setup: handle Kerberos sectype and replicate Kerberos
   SMB1 processing done in sess_auth_kerberos

Signed-off-by: default avatarNoel Power <noel.power@suse.com>
Signed-off-by: default avatarJim McDonough <jmcd@samba.org>
CC: Stable <stable@vger.kernel.org>
Signed-off-by: default avatarSteve French <steve.french@primarydata.com>
parent 98ce94c8
Loading
Loading
Loading
Loading
+61 −15
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@
#include "smb2status.h"
#include "smb2glob.h"
#include "cifspdu.h"
#include "cifs_spnego.h"

/*
 *  The following table defines the expected "StructureSize" of SMB2 requests
@@ -486,19 +487,15 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
		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 */
	if (rc)
		goto neg_exit;
	if (blob_length)
	if (blob_length) {
		rc = decode_negTokenInit(security_blob, blob_length, server);
		if (rc == 1)
			rc = 0;
	else if (rc == 0) {
		else if (rc == 0)
			rc = -EIO;
		goto neg_exit;
	}
#endif

neg_exit:
	free_rsp_buf(resp_buftype, rsp);
	return rc;
@@ -592,7 +589,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
	__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
	struct TCP_Server_Info *server = ses->server;
	u16 blob_length = 0;
	char *security_blob;
	struct key *spnego_key = NULL;
	char *security_blob = NULL;
	char *ntlmssp_blob = NULL;
	bool use_spnego = false; /* else use raw ntlmssp */

@@ -620,6 +618,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
	ses->ntlmssp->sesskey_per_smbsess = true;

	/* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
	if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
		ses->sectype = RawNTLMSSP;

ssetup_ntlmssp_authenticate:
@@ -649,7 +648,48 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
	iov[0].iov_base = (char *)req;
	/* 4 for rfc1002 length field and 1 for pad */
	iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
	if (phase == NtLmNegotiate) {

	if (ses->sectype == Kerberos) {
#ifdef CONFIG_CIFS_UPCALL
		struct cifs_spnego_msg *msg;

		spnego_key = cifs_get_spnego_key(ses);
		if (IS_ERR(spnego_key)) {
			rc = PTR_ERR(spnego_key);
			spnego_key = NULL;
			goto ssetup_exit;
		}

		msg = spnego_key->payload.data;
		/*
		 * check version field to make sure that cifs.upcall is
		 * sending us a response in an expected form
		 */
		if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
			cifs_dbg(VFS,
				  "bad cifs.upcall version. Expected %d got %d",
				  CIFS_SPNEGO_UPCALL_VERSION, msg->version);
			rc = -EKEYREJECTED;
			goto ssetup_exit;
		}
		ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
						 GFP_KERNEL);
		if (!ses->auth_key.response) {
			cifs_dbg(VFS,
				"Kerberos can't allocate (%u bytes) memory",
				msg->sesskey_len);
			rc = -ENOMEM;
			goto ssetup_exit;
		}
		ses->auth_key.len = msg->sesskey_len;
		blob_length = msg->secblob_len;
		iov[1].iov_base = msg->data + msg->sesskey_len;
		iov[1].iov_len = blob_length;
#else
		rc = -EOPNOTSUPP;
		goto ssetup_exit;
#endif /* CONFIG_CIFS_UPCALL */
	} else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */
		ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
				       GFP_KERNEL);
		if (ntlmssp_blob == NULL) {
@@ -672,6 +712,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
			/* with raw NTLMSSP we don't encapsulate in SPNEGO */
			security_blob = ntlmssp_blob;
		}
		iov[1].iov_base = security_blob;
		iov[1].iov_len = blob_length;
	} else if (phase == NtLmAuthenticate) {
		req->hdr.SessionId = ses->Suid;
		ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500,
@@ -699,6 +741,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
		} else {
			security_blob = ntlmssp_blob;
		}
		iov[1].iov_base = security_blob;
		iov[1].iov_len = blob_length;
	} else {
		cifs_dbg(VFS, "illegal ntlmssp phase\n");
		rc = -EIO;
@@ -710,8 +754,6 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
				cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
					    1 /* pad */ - 4 /* rfc1001 len */);
	req->SecurityBufferLength = cpu_to_le16(blob_length);
	iov[1].iov_base = security_blob;
	iov[1].iov_len = blob_length;

	inc_rfc1001_len(req, blob_length - 1 /* pad */);

@@ -722,6 +764,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,

	kfree(security_blob);
	rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
	ses->Suid = rsp->hdr.SessionId;
	if (resp_buftype != CIFS_NO_BUFFER &&
	    rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
		if (phase != NtLmNegotiate) {
@@ -739,7 +782,6 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
		/* NTLMSSP Negotiate sent now processing challenge (response) */
		phase = NtLmChallenge; /* process ntlmssp challenge */
		rc = 0; /* MORE_PROCESSING is not an error here but expected */
		ses->Suid = rsp->hdr.SessionId;
		rc = decode_ntlmssp_challenge(rsp->Buffer,
				le16_to_cpu(rsp->SecurityBufferLength), ses);
	}
@@ -796,6 +838,10 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
		kfree(ses->auth_key.response);
		ses->auth_key.response = NULL;
	}
	if (spnego_key) {
		key_invalidate(spnego_key);
		key_put(spnego_key);
	}
	kfree(ses->ntlmssp);

	return rc;