Commit cc87c47d authored by Sachin Prabhu's avatar Sachin Prabhu Committed by Steve French

cifs: Separate rawntlmssp auth from CIFS_SessSetup()

Separate rawntlmssp authentication from CIFS_SessSetup(). Also cleanup
CIFS_SessSetup() since we no longer do any auth within it.
Signed-off-by: default avatarSachin Prabhu <sprabhu@redhat.com>
Reviewed-by: default avatarShirish Pargaonkar <spargaonkar@suse.com>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent ee03c646
...@@ -1113,241 +1113,225 @@ sess_auth_kerberos(struct sess_data *sess_data) ...@@ -1113,241 +1113,225 @@ sess_auth_kerberos(struct sess_data *sess_data)
} }
#endif /* ! CONFIG_CIFS_UPCALL */ #endif /* ! CONFIG_CIFS_UPCALL */
int /*
CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, * The required kvec buffers have to be allocated before calling this
const struct nls_table *nls_cp) * function.
*/
static int
_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
{ {
int rc = 0;
int wct;
struct smb_hdr *smb_buf; struct smb_hdr *smb_buf;
char *bcc_ptr;
char *str_area;
SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMB;
struct cifs_ses *ses = sess_data->ses;
__u32 capabilities; __u32 capabilities;
__u16 count; char *bcc_ptr;
int resp_buf_type;
struct kvec iov[3];
enum securityEnum type;
__u16 action, bytes_remaining;
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
u16 blob_len;
char *ntlmsspblob = NULL;
struct sess_data *sess_data;
if (ses == NULL) { pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
WARN(1, "%s: ses == NULL!", __func__); smb_buf = (struct smb_hdr *)pSMB;
return -EINVAL;
capabilities = cifs_ssetup_hdr(ses, pSMB);
if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
return -ENOSYS;
} }
sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
if (!sess_data) capabilities |= CAP_EXTENDED_SECURITY;
return -ENOMEM; pSMB->req.Capabilities |= cpu_to_le32(capabilities);
sess_data->xid = xid;
sess_data->ses = ses;
sess_data->buf0_type = CIFS_NO_BUFFER;
sess_data->nls_cp = (struct nls_table *) nls_cp;
type = select_sectype(ses->server, ses->sectype); bcc_ptr = sess_data->iov[2].iov_base;
cifs_dbg(FYI, "sess setup type %d\n", type); /* unicode strings must be word aligned */
if (type == Unspecified) { if ((sess_data->iov[0].iov_len + sess_data->iov[1].iov_len) % 2) {
cifs_dbg(VFS, *bcc_ptr = 0;
"Unable to select appropriate authentication method!"); bcc_ptr++;
return -EINVAL;
} }
unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
switch (type) { sess_data->iov[2].iov_len = (long) bcc_ptr -
case LANMAN: (long) sess_data->iov[2].iov_base;
sess_auth_lanman(sess_data);
goto out;
case NTLM:
sess_auth_ntlm(sess_data);
goto out;
case NTLMv2:
sess_auth_ntlmv2(sess_data);
goto out;
case Kerberos:
sess_auth_kerberos(sess_data);
goto out;
default:
cifs_dbg(FYI, "Continuing with CIFS_SessSetup\n");
}
if (type == RawNTLMSSP) { return 0;
/* if memory allocation is successful, caller of this function }
* frees it.
*/ static void
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data);
if (!ses->ntlmssp)
return -ENOMEM; static void
ses->ntlmssp->sesskey_per_smbsess = false; sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
{
int rc;
struct smb_hdr *smb_buf;
SESSION_SETUP_ANDX *pSMB;
struct cifs_ses *ses = sess_data->ses;
__u16 bytes_remaining;
char *bcc_ptr;
u16 blob_len;
cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n");
/*
* if memory allocation is successful, caller of this function
* frees it.
*/
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
if (!ses->ntlmssp) {
rc = -ENOMEM;
goto out;
} }
ses->ntlmssp->sesskey_per_smbsess = false;
ssetup_ntlmssp_authenticate: /* wct = 12 */
if (phase == NtLmChallenge) rc = sess_alloc_buffer(sess_data, 12);
phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ if (rc)
goto out;
/* same size: negotiate or auth, NTLMSSP or extended security */ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
wct = 12;
rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses, /* Build security blob before we assemble the request */
(void **)&smb_buf); build_ntlmssp_negotiate_blob(pSMB->req.SecurityBlob, ses);
sess_data->iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
sess_data->iov[1].iov_base = pSMB->req.SecurityBlob;
pSMB->req.SecurityBlobLength = cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
if (rc) if (rc)
return rc; goto out;
pSMB = (SESSION_SETUP_ANDX *)smb_buf; rc = sess_sendreceive(sess_data);
capabilities = cifs_ssetup_hdr(ses, pSMB); pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
/* we will send the SMB in three pieces: /* If true, rc here is expected and not an error */
a fixed length beginning part, an optional if (sess_data->buf0_type != CIFS_NO_BUFFER &&
SPNEGO blob (which can be zero length), and a smb_buf->Status.CifsError ==
last part which will include the strings cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
and rest of bcc area. This allows us to avoid rc = 0;
a large buffer 17K allocation */
iov[0].iov_base = (char *)pSMB;
iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
/* setting this here allows the code at the end of the function
to free the request buffer if there's an error */
resp_buf_type = CIFS_SMALL_BUFFER;
/* 2000 big enough to fit max user, domain, NOS name etc. */ if (rc)
str_area = kmalloc(2000, GFP_KERNEL); goto out;
if (str_area == NULL) {
rc = -ENOMEM; cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
goto ssetup_exit;
if (smb_buf->WordCount != 4) {
rc = -EIO;
cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
goto out;
} }
bcc_ptr = str_area;
iov[1].iov_base = NULL; ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
iov[1].iov_len = 0; cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
if (type == RawNTLMSSP) { bytes_remaining = get_bcc(smb_buf);
if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { bcc_ptr = pByteArea(smb_buf);
cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
rc = -ENOSYS;
goto ssetup_exit;
}
cifs_dbg(FYI, "ntlmssp session setup phase %d\n", phase); blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; if (blob_len > bytes_remaining) {
capabilities |= CAP_EXTENDED_SECURITY; cifs_dbg(VFS, "bad security blob length %d\n",
pSMB->req.Capabilities |= cpu_to_le32(capabilities); blob_len);
switch(phase) { rc = -EINVAL;
case NtLmNegotiate: goto out;
build_ntlmssp_negotiate_blob( }
pSMB->req.SecurityBlob, ses);
iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
iov[1].iov_base = pSMB->req.SecurityBlob;
pSMB->req.SecurityBlobLength =
cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
break;
case NtLmAuthenticate:
/*
* 5 is an empirical value, large enough to hold
* authenticate message plus max 10 of av paris,
* domain, user, workstation names, flags, etc.
*/
ntlmsspblob = kzalloc(
5*sizeof(struct _AUTHENTICATE_MESSAGE),
GFP_KERNEL);
if (!ntlmsspblob) {
rc = -ENOMEM;
goto ssetup_exit;
}
rc = build_ntlmssp_auth_blob(ntlmsspblob, rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
&blob_len, ses, nls_cp); out:
if (rc) sess_free_buffer(sess_data);
goto ssetup_exit;
iov[1].iov_len = blob_len; if (!rc) {
iov[1].iov_base = ntlmsspblob; sess_data->func = sess_auth_rawntlmssp_authenticate;
pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); return;
/*
* Make sure that we tell the server that we are using
* the uid that it just gave us back on the response
* (challenge)
*/
smb_buf->Uid = ses->Suid;
break;
default:
cifs_dbg(VFS, "invalid phase %d\n", phase);
rc = -ENOSYS;
goto ssetup_exit;
}
/* unicode strings must be word aligned */
if ((iov[0].iov_len + iov[1].iov_len) % 2) {
*bcc_ptr = 0;
bcc_ptr++;
}
unicode_oslm_strings(&bcc_ptr, nls_cp);
} else {
cifs_dbg(VFS, "secType %d not supported!\n", type);
rc = -ENOSYS;
goto ssetup_exit;
} }
iov[2].iov_base = str_area; /* Else error. Cleanup */
iov[2].iov_len = (long) bcc_ptr - (long) str_area; kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
kfree(ses->ntlmssp);
ses->ntlmssp = NULL;
count = iov[1].iov_len + iov[2].iov_len; sess_data->func = NULL;
smb_buf->smb_buf_length = sess_data->result = rc;
cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count); }
put_bcc(count, smb_buf); static void
sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
{
int rc;
struct smb_hdr *smb_buf;
SESSION_SETUP_ANDX *pSMB;
struct cifs_ses *ses = sess_data->ses;
__u16 bytes_remaining;
char *bcc_ptr;
char *ntlmsspblob = NULL;
u16 blob_len;
rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type, cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
CIFS_LOG_ERROR);
/* SMB request buf freed in SendReceive2 */
pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base; /* wct = 12 */
smb_buf = (struct smb_hdr *)iov[0].iov_base; rc = sess_alloc_buffer(sess_data, 12);
if (rc)
goto out;
if ((type == RawNTLMSSP) && (resp_buf_type != CIFS_NO_BUFFER) && /* Build security blob before we assemble the request */
(smb_buf->Status.CifsError == pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))) { smb_buf = (struct smb_hdr *)pSMB;
if (phase != NtLmNegotiate) { /*
cifs_dbg(VFS, "Unexpected more processing error\n"); * 5 is an empirical value, large enough to hold
goto ssetup_exit; * authenticate message plus max 10 of av paris,
} * domain, user, workstation names, flags, etc.
/* NTLMSSP Negotiate sent now processing challenge (response) */ */
phase = NtLmChallenge; /* process ntlmssp challenge */ ntlmsspblob = kzalloc(5*sizeof(struct _AUTHENTICATE_MESSAGE),
rc = 0; /* MORE_PROC rc is not an error here, but expected */ GFP_KERNEL);
if (!ntlmsspblob) {
rc = -ENOMEM;
goto out;
} }
rc = build_ntlmssp_auth_blob(ntlmsspblob,
&blob_len, ses, sess_data->nls_cp);
if (rc)
goto out_free_ntlmsspblob;
sess_data->iov[1].iov_len = blob_len;
sess_data->iov[1].iov_base = ntlmsspblob;
pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
/*
* Make sure that we tell the server that we are using
* the uid that it just gave us back on the response
* (challenge)
*/
smb_buf->Uid = ses->Suid;
rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
if (rc) if (rc)
goto ssetup_exit; goto out_free_ntlmsspblob;
rc = sess_sendreceive(sess_data);
if (rc)
goto out_free_ntlmsspblob;
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
if (smb_buf->WordCount != 4) { if (smb_buf->WordCount != 4) {
rc = -EIO; rc = -EIO;
cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
goto ssetup_exit; goto out_free_ntlmsspblob;
} }
action = le16_to_cpu(pSMB->resp.Action);
if (action & GUEST_LOGIN) if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
/* response can have either 3 or 4 word count - Samba sends 3 */
/* and lanman response is 3 */
bytes_remaining = get_bcc(smb_buf); bytes_remaining = get_bcc(smb_buf);
bcc_ptr = pByteArea(smb_buf); bcc_ptr = pByteArea(smb_buf);
blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
if (blob_len > bytes_remaining) { if (blob_len > bytes_remaining) {
cifs_dbg(VFS, "bad security blob length %d\n", cifs_dbg(VFS, "bad security blob length %d\n",
blob_len); blob_len);
rc = -EINVAL; rc = -EINVAL;
goto ssetup_exit; goto out_free_ntlmsspblob;
}
if (phase == NtLmChallenge) {
rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
if (rc)
goto ssetup_exit;
} }
bcc_ptr += blob_len; bcc_ptr += blob_len;
bytes_remaining -= blob_len; bytes_remaining -= blob_len;
/* BB check if Unicode and decode strings */ /* BB check if Unicode and decode strings */
if (bytes_remaining == 0) { if (bytes_remaining == 0) {
/* no string area to decode, do nothing */ /* no string area to decode, do nothing */
...@@ -1357,61 +1341,113 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, ...@@ -1357,61 +1341,113 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
++bcc_ptr; ++bcc_ptr;
--bytes_remaining; --bytes_remaining;
} }
decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
sess_data->nls_cp);
} else { } else {
decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
sess_data->nls_cp);
} }
ssetup_exit: out_free_ntlmsspblob:
kfree(str_area);
kfree(ntlmsspblob); kfree(ntlmsspblob);
ntlmsspblob = NULL; out:
if (resp_buf_type == CIFS_SMALL_BUFFER) { sess_free_buffer(sess_data);
cifs_dbg(FYI, "ssetup freeing small buf %p\n", iov[0].iov_base);
cifs_small_buf_release(iov[0].iov_base);
} else if (resp_buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(iov[0].iov_base);
/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
if ((phase == NtLmChallenge) && (rc == 0))
goto ssetup_ntlmssp_authenticate;
if (!rc) {
mutex_lock(&ses->server->srv_mutex);
if (!ses->server->session_estab) {
if (ses->server->sign) {
ses->server->session_key.response =
kmemdup(ses->auth_key.response,
ses->auth_key.len, GFP_KERNEL);
if (!ses->server->session_key.response) {
rc = -ENOMEM;
mutex_unlock(&ses->server->srv_mutex);
goto keycp_exit;
}
ses->server->session_key.len =
ses->auth_key.len;
}
ses->server->sequence_number = 0x2;
ses->server->session_estab = true;
}
mutex_unlock(&ses->server->srv_mutex);
cifs_dbg(FYI, "CIFS session established successfully\n"); if (!rc)
spin_lock(&GlobalMid_Lock); rc = sess_establish_session(sess_data);
ses->status = CifsGood;
ses->need_reconnect = false;
spin_unlock(&GlobalMid_Lock);
}
keycp_exit: /* Cleanup */
kfree(ses->auth_key.response); kfree(ses->auth_key.response);
ses->auth_key.response = NULL; ses->auth_key.response = NULL;
kfree(ses->ntlmssp); kfree(ses->ntlmssp);
ses->ntlmssp = NULL;
return rc; sess_data->func = NULL;
sess_data->result = rc;
}
out: int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
{
int type;
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;
}
switch (type) {
case LANMAN:
/* LANMAN and plaintext are less secure and off by default.
* So we make this explicitly be turned on in kconfig (in the
* build) and turned on at runtime (changed from the default)
* in proc/fs/cifs or via mount parm. Unfortunately this is
* needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
#ifdef CONFIG_CIFS_WEAK_PW_HASH
sess_data->func = sess_auth_lanman;
break;
#else
return -EOPNOTSUPP;
#endif
case NTLM:
sess_data->func = sess_auth_ntlm;
break;
case NTLMv2:
sess_data->func = sess_auth_ntlmv2;
break;
case Kerberos:
#ifdef CONFIG_CIFS_UPCALL
sess_data->func = sess_auth_kerberos;
break;
#else
cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
return -ENOSYS;
break;
#endif /* CONFIG_CIFS_UPCALL */
case RawNTLMSSP:
sess_data->func = sess_auth_rawntlmssp_negotiate;
break;
default:
cifs_dbg(VFS, "secType %d not supported!\n", type);
return -ENOSYS;
}
return 0;
}
int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp)
{
int rc = 0;
struct sess_data *sess_data;
if (ses == NULL) {
WARN(1, "%s: ses == NULL!", __func__);
return -EINVAL;
}
sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL);
if (!sess_data)
return -ENOMEM;
rc = 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);
/* Store result before we free 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