Commit 166cea4d authored by Sachin Prabhu's avatar Sachin Prabhu Committed by Steve French

SMB2: Separate RawNTLMSSP authentication from SMB2_sess_setup

We split the rawntlmssp authentication into negotiate and
authencate parts. We also clean up the code and add helpers.
Signed-off-by: default avatarSachin Prabhu <sprabhu@redhat.com>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
Reviewed-by: default avatarPavel Shilovsky <pshilov@microsoft.com>
parent 3baf1a7b
...@@ -803,245 +803,208 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) ...@@ -803,245 +803,208 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
} }
#endif #endif
int static void
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data);
const struct nls_table *nls_cp)
static void
SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
{ {
struct smb2_sess_setup_req *req; int rc;
struct cifs_ses *ses = sess_data->ses;
struct smb2_sess_setup_rsp *rsp = NULL; struct smb2_sess_setup_rsp *rsp = NULL;
struct kvec iov[2]; char *ntlmssp_blob = NULL;
int rc = 0;
int resp_buftype = CIFS_NO_BUFFER;
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
struct TCP_Server_Info *server = ses->server;
u16 blob_length = 0;
char *security_blob = NULL;
unsigned char *ntlmssp_blob = NULL;
bool use_spnego = false; /* else use raw ntlmssp */ bool use_spnego = false; /* else use raw ntlmssp */
u64 previous_session = ses->Suid; u16 blob_length = 0;
struct SMB2_sess_data *sess_data;
cifs_dbg(FYI, "Session Setup\n");
if (!server) {
WARN(1, "%s: server is NULL!\n", __func__);
return -EIO;
}
sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL);
if (!sess_data)
return -ENOMEM;
sess_data->xid = xid;
sess_data->ses = ses;
sess_data->buf0_type = CIFS_NO_BUFFER;
sess_data->nls_cp = (struct nls_table *) nls_cp;
sess_data->previous_session = ses->Suid;
if (ses->sectype == Kerberos) {
SMB2_auth_kerberos(sess_data);
goto out;
}
/*
* If we are here due to reconnect, free per-smb session key
* in case signing was required.
*/
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
/* /*
* If memory allocation is successful, caller of this function * If memory allocation is successful, caller of this function
* frees it. * frees it.
*/ */
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
if (!ses->ntlmssp) if (!ses->ntlmssp) {
return -ENOMEM; rc = -ENOMEM;
goto out_err;
}
ses->ntlmssp->sesskey_per_smbsess = true; ses->ntlmssp->sesskey_per_smbsess = true;
/* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */ rc = SMB2_sess_alloc_buffer(sess_data);
if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
ses->sectype = RawNTLMSSP;
ssetup_ntlmssp_authenticate:
if (phase == NtLmChallenge)
phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req);
if (rc) if (rc)
return rc; goto out_err;
req->hdr.SessionId = 0; /* First session, not a reauthenticate */
/* if reconnect, we need to send previous sess id, otherwise it is 0 */ ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
req->PreviousSessionId = previous_session; GFP_KERNEL);
if (ntlmssp_blob == NULL) {
rc = -ENOMEM;
goto out;
}
req->Flags = 0; /* MBZ */ build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
/* to enable echos and oplocks */ if (use_spnego) {
req->hdr.CreditRequest = cpu_to_le16(3); /* BB eventually need to add this */
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
rc = -EOPNOTSUPP;
goto out;
} else {
blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
/* with raw NTLMSSP we don't encapsulate in SPNEGO */
}
sess_data->iov[1].iov_base = ntlmssp_blob;
sess_data->iov[1].iov_len = blob_length;
/* only one of SMB2 signing flags may be set in SMB2 request */ rc = SMB2_sess_sendreceive(sess_data);
if (server->sign) rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */
req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED;
else
req->SecurityMode = 0;
req->Capabilities = 0; /* If true, rc here is expected and not an error */
req->Channel = 0; /* MBZ */ if (sess_data->buf0_type != CIFS_NO_BUFFER &&
rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
rc = 0;
iov[0].iov_base = (char *)req; if (rc)
/* 4 for rfc1002 length field and 1 for pad */ goto out;
iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
if (phase == NtLmNegotiate) { if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), le16_to_cpu(rsp->SecurityBufferOffset)) {
GFP_KERNEL); cifs_dbg(VFS, "Invalid security buffer offset %d\n",
if (ntlmssp_blob == NULL) { le16_to_cpu(rsp->SecurityBufferOffset));
rc = -ENOMEM;
goto ssetup_exit;
}
build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
if (use_spnego) {
/* blob_length = build_spnego_ntlmssp_blob(
&security_blob,
sizeof(struct _NEGOTIATE_MESSAGE),
ntlmssp_blob); */
/* BB eventually need to add this */
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
rc = -EOPNOTSUPP;
kfree(ntlmssp_blob);
goto ssetup_exit;
} else {
blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
/* 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;
rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
nls_cp);
if (rc) {
cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
rc);
goto ssetup_exit; /* BB double check error handling */
}
if (use_spnego) {
/* blob_length = build_spnego_ntlmssp_blob(
&security_blob,
blob_length,
ntlmssp_blob); */
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
rc = -EOPNOTSUPP;
kfree(ntlmssp_blob);
goto ssetup_exit;
} 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; rc = -EIO;
goto ssetup_exit; goto out;
} }
rc = decode_ntlmssp_challenge(rsp->Buffer,
le16_to_cpu(rsp->SecurityBufferLength), ses);
if (rc)
goto out;
/* Testing shows that buffer offset must be at location of Buffer[0] */ cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
req->SecurityBufferOffset =
cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
1 /* pad */ - 4 /* rfc1001 len */);
req->SecurityBufferLength = cpu_to_le16(blob_length);
inc_rfc1001_len(req, blob_length - 1 /* pad */);
/* BB add code to build os and lm fields */ ses->Suid = rsp->hdr.SessionId;
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, out:
CIFS_LOG_ERROR | CIFS_NEG_OP); kfree(ntlmssp_blob);
SMB2_sess_free_buffer(sess_data);
if (!rc) {
sess_data->result = 0;
sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate;
return;
}
out_err:
kfree(ses->ntlmssp);
ses->ntlmssp = NULL;
sess_data->result = rc;
sess_data->func = NULL;
}
kfree(security_blob); static void
rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base; SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
ses->Suid = rsp->hdr.SessionId; {
if (resp_buftype != CIFS_NO_BUFFER && int rc;
rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) { struct cifs_ses *ses = sess_data->ses;
if (phase != NtLmNegotiate) { struct smb2_sess_setup_req *req;
cifs_dbg(VFS, "Unexpected more processing error\n"); struct smb2_sess_setup_rsp *rsp = NULL;
goto ssetup_exit; unsigned char *ntlmssp_blob = NULL;
} bool use_spnego = false; /* else use raw ntlmssp */
if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 != u16 blob_length = 0;
le16_to_cpu(rsp->SecurityBufferOffset)) {
cifs_dbg(VFS, "Invalid security buffer offset %d\n", rc = SMB2_sess_alloc_buffer(sess_data);
le16_to_cpu(rsp->SecurityBufferOffset)); if (rc)
rc = -EIO; goto out;
goto ssetup_exit;
} req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
req->hdr.SessionId = ses->Suid;
/* NTLMSSP Negotiate sent now processing challenge (response) */ rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
phase = NtLmChallenge; /* process ntlmssp challenge */ sess_data->nls_cp);
rc = 0; /* MORE_PROCESSING is not an error here but expected */ if (rc) {
rc = decode_ntlmssp_challenge(rsp->Buffer, cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc);
le16_to_cpu(rsp->SecurityBufferLength), ses); goto out;
} }
/* if (use_spnego) {
* BB eventually add code for SPNEGO decoding of NtlmChallenge blob, /* BB eventually need to add this */
* but at least the raw NTLMSSP case works. cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
*/ rc = -EOPNOTSUPP;
/* goto out;
* No tcon so can't do }
* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); sess_data->iov[1].iov_base = ntlmssp_blob;
*/ sess_data->iov[1].iov_len = blob_length;
if (rc != 0)
goto ssetup_exit; rc = SMB2_sess_sendreceive(sess_data);
if (rc)
goto out;
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
ses->Suid = rsp->hdr.SessionId;
ses->session_flags = le16_to_cpu(rsp->SessionFlags); ses->session_flags = le16_to_cpu(rsp->SessionFlags);
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
cifs_dbg(VFS, "SMB3 encryption not supported yet\n"); cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
ssetup_exit:
free_rsp_buf(resp_buftype, rsp);
/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */ rc = SMB2_sess_establish_session(sess_data);
if ((phase == NtLmChallenge) && (rc == 0)) out:
goto ssetup_ntlmssp_authenticate; kfree(ntlmssp_blob);
SMB2_sess_free_buffer(sess_data);
kfree(ses->ntlmssp);
ses->ntlmssp = NULL;
sess_data->result = rc;
sess_data->func = NULL;
}
if (!rc) { static int
mutex_lock(&server->srv_mutex); SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
if (server->sign && server->ops->generate_signingkey) { {
rc = server->ops->generate_signingkey(ses); if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
kfree(ses->auth_key.response); ses->sectype = RawNTLMSSP;
ses->auth_key.response = NULL;
if (rc) {
cifs_dbg(FYI,
"SMB3 session key generation failed\n");
mutex_unlock(&server->srv_mutex);
goto keygen_exit;
}
}
if (!server->session_estab) {
server->sequence_number = 0x2;
server->session_estab = true;
}
mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "SMB2/3 session established successfully\n"); switch (ses->sectype) {
spin_lock(&GlobalMid_Lock); case Kerberos:
ses->status = CifsGood; sess_data->func = SMB2_auth_kerberos;
ses->need_reconnect = false; break;
spin_unlock(&GlobalMid_Lock); case RawNTLMSSP:
sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate;
break;
default:
cifs_dbg(VFS, "secType %d not supported!\n", ses->sectype);
return -EOPNOTSUPP;
} }
keygen_exit: return 0;
if (!server->sign) { }
kfree(ses->auth_key.response);
ses->auth_key.response = NULL; int
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp)
{
int rc = 0;
struct TCP_Server_Info *server = ses->server;
struct SMB2_sess_data *sess_data;
cifs_dbg(FYI, "Session Setup\n");
if (!server) {
WARN(1, "%s: server is NULL!\n", __func__);
return -EIO;
} }
kfree(ses->ntlmssp);
return rc; sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL);
out: if (!sess_data)
return -ENOMEM;
rc = SMB2_select_sec(ses, sess_data);
if (rc)
goto out;
sess_data->xid = xid;
sess_data->ses = ses;
sess_data->buf0_type = CIFS_NO_BUFFER;
sess_data->nls_cp = (struct nls_table *) nls_cp;
while (sess_data->func)
sess_data->func(sess_data);
rc = sess_data->result; rc = sess_data->result;
out:
kfree(sess_data); kfree(sess_data);
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment