[CIFS] Have CIFS_SessSetup build correct SPNEGO SessionSetup request
Have CIFS_SessSetup call cifs_get_spnego_key when Kerberos is negotiated. Use the info in the key payload to build a session setup request packet. Also clean up how the request buffer in the function is freed on error. With appropriate user space helper (in samba/source/client). Kerberos support (secure session establishment can be done now via Kerberos, previously users would have to use NTLMv2 instead for more secure session setup). Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
parent
8840dee9dc
commit
2442421b17
4 changed files with 77 additions and 18 deletions
|
@ -1,6 +1,7 @@
|
||||||
Version 1.52
|
Version 1.52
|
||||||
------------
|
------------
|
||||||
Fix oops on second mount to server when null auth is used.
|
Fix oops on second mount to server when null auth is used.
|
||||||
|
Enable experimental Kerberos support
|
||||||
|
|
||||||
Version 1.51
|
Version 1.51
|
||||||
------------
|
------------
|
||||||
|
|
|
@ -16,7 +16,7 @@ SecurityDescriptors
|
||||||
c) Better pam/winbind integration (e.g. to handle uid mapping
|
c) Better pam/winbind integration (e.g. to handle uid mapping
|
||||||
better)
|
better)
|
||||||
|
|
||||||
d) Kerberos/SPNEGO session setup support - (started)
|
d) Verify that Kerberos signing works
|
||||||
|
|
||||||
e) Cleanup now unneeded SessSetup code in
|
e) Cleanup now unneeded SessSetup code in
|
||||||
fs/cifs/connect.c and add back in NTLMSSP code if any servers
|
fs/cifs/connect.c and add back in NTLMSSP code if any servers
|
||||||
|
|
|
@ -110,6 +110,7 @@ struct mac_key {
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
union {
|
union {
|
||||||
char ntlm[CIFS_SESS_KEY_SIZE + 16];
|
char ntlm[CIFS_SESS_KEY_SIZE + 16];
|
||||||
|
char krb5[CIFS_SESS_KEY_SIZE + 16]; /* BB: length correct? */
|
||||||
struct {
|
struct {
|
||||||
char key[16];
|
char key[16];
|
||||||
struct ntlmv2_resp resp;
|
struct ntlmv2_resp resp;
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "ntlmssp.h"
|
#include "ntlmssp.h"
|
||||||
#include "nterr.h"
|
#include "nterr.h"
|
||||||
#include <linux/utsname.h>
|
#include <linux/utsname.h>
|
||||||
|
#include "cifs_spnego.h"
|
||||||
|
|
||||||
extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
|
extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
|
||||||
unsigned char *p24);
|
unsigned char *p24);
|
||||||
|
@ -340,11 +341,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
|
||||||
SESSION_SETUP_ANDX *pSMB;
|
SESSION_SETUP_ANDX *pSMB;
|
||||||
__u32 capabilities;
|
__u32 capabilities;
|
||||||
int count;
|
int count;
|
||||||
int resp_buf_type = 0;
|
int resp_buf_type;
|
||||||
struct kvec iov[2];
|
struct kvec iov[3];
|
||||||
enum securityEnum type;
|
enum securityEnum type;
|
||||||
__u16 action;
|
__u16 action;
|
||||||
int bytes_remaining;
|
int bytes_remaining;
|
||||||
|
struct key *spnego_key = NULL;
|
||||||
|
|
||||||
if (ses == NULL)
|
if (ses == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -377,24 +379,32 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
|
||||||
|
|
||||||
capabilities = cifs_ssetup_hdr(ses, pSMB);
|
capabilities = cifs_ssetup_hdr(ses, pSMB);
|
||||||
|
|
||||||
/* we will send the SMB in two pieces,
|
/* we will send the SMB in three pieces:
|
||||||
a fixed length beginning part, and a
|
a fixed length beginning part, an optional
|
||||||
second part which will include the strings
|
SPNEGO blob (which can be zero length), and a
|
||||||
and rest of bcc area, in order to avoid having
|
last part which will include the strings
|
||||||
to do a large buffer 17K allocation */
|
and rest of bcc area. This allows us to avoid
|
||||||
|
a large buffer 17K allocation */
|
||||||
iov[0].iov_base = (char *)pSMB;
|
iov[0].iov_base = (char *)pSMB;
|
||||||
iov[0].iov_len = smb_buf->smb_buf_length + 4;
|
iov[0].iov_len = 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. */
|
/* 2000 big enough to fit max user, domain, NOS name etc. */
|
||||||
str_area = kmalloc(2000, GFP_KERNEL);
|
str_area = kmalloc(2000, GFP_KERNEL);
|
||||||
if (str_area == NULL) {
|
if (str_area == NULL) {
|
||||||
cifs_small_buf_release(smb_buf);
|
rc = -ENOMEM;
|
||||||
return -ENOMEM;
|
goto ssetup_exit;
|
||||||
}
|
}
|
||||||
bcc_ptr = str_area;
|
bcc_ptr = str_area;
|
||||||
|
|
||||||
ses->flags &= ~CIFS_SES_LANMAN;
|
ses->flags &= ~CIFS_SES_LANMAN;
|
||||||
|
|
||||||
|
iov[1].iov_base = NULL;
|
||||||
|
iov[1].iov_len = 0;
|
||||||
|
|
||||||
if (type == LANMAN) {
|
if (type == LANMAN) {
|
||||||
#ifdef CONFIG_CIFS_WEAK_PW_HASH
|
#ifdef CONFIG_CIFS_WEAK_PW_HASH
|
||||||
char lnm_session_key[CIFS_SESS_KEY_SIZE];
|
char lnm_session_key[CIFS_SESS_KEY_SIZE];
|
||||||
|
@ -463,8 +473,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
|
||||||
struct ntlmv2_resp */
|
struct ntlmv2_resp */
|
||||||
|
|
||||||
if (v2_sess_key == NULL) {
|
if (v2_sess_key == NULL) {
|
||||||
cifs_small_buf_release(smb_buf);
|
rc = -ENOMEM;
|
||||||
return -ENOMEM;
|
goto ssetup_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
|
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
|
||||||
|
@ -499,21 +509,66 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
|
||||||
unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
|
unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
|
||||||
} else
|
} else
|
||||||
ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
|
ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
|
||||||
} else /* NTLMSSP or SPNEGO */ {
|
} else if (type == 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;
|
||||||
|
/* bail out if key is too long */
|
||||||
|
if (msg->sesskey_len >
|
||||||
|
sizeof(ses->server->mac_signing_key.data.krb5)) {
|
||||||
|
cERROR(1, ("Kerberos signing key too long (%u bytes)",
|
||||||
|
msg->sesskey_len));
|
||||||
|
rc = -EOVERFLOW;
|
||||||
|
goto ssetup_exit;
|
||||||
|
}
|
||||||
|
ses->server->mac_signing_key.len = msg->sesskey_len;
|
||||||
|
memcpy(ses->server->mac_signing_key.data.krb5, msg->data,
|
||||||
|
msg->sesskey_len);
|
||||||
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
||||||
capabilities |= CAP_EXTENDED_SECURITY;
|
capabilities |= CAP_EXTENDED_SECURITY;
|
||||||
pSMB->req.Capabilities = cpu_to_le32(capabilities);
|
pSMB->req.Capabilities = cpu_to_le32(capabilities);
|
||||||
/* BB set password lengths */
|
iov[1].iov_base = msg->data + msg->sesskey_len;
|
||||||
|
iov[1].iov_len = msg->secblob_len;
|
||||||
|
pSMB->req.SecurityBlobLength = cpu_to_le16(iov[1].iov_len);
|
||||||
|
|
||||||
|
if (ses->capabilities & CAP_UNICODE) {
|
||||||
|
/* unicode strings must be word aligned */
|
||||||
|
if (iov[0].iov_len % 2) {
|
||||||
|
*bcc_ptr = 0;
|
||||||
|
bcc_ptr++;
|
||||||
|
}
|
||||||
|
unicode_oslm_strings(&bcc_ptr, nls_cp);
|
||||||
|
unicode_domain_string(&bcc_ptr, ses, nls_cp);
|
||||||
|
} else
|
||||||
|
/* BB: is this right? */
|
||||||
|
ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
|
||||||
|
#else /* ! CONFIG_CIFS_UPCALL */
|
||||||
|
cERROR(1, ("Kerberos negotiated but upcall support disabled!"));
|
||||||
|
rc = -ENOSYS;
|
||||||
|
goto ssetup_exit;
|
||||||
|
#endif /* CONFIG_CIFS_UPCALL */
|
||||||
|
} else {
|
||||||
|
cERROR(1, ("secType %d not supported!", type));
|
||||||
|
rc = -ENOSYS;
|
||||||
|
goto ssetup_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = (long) bcc_ptr - (long) str_area;
|
iov[2].iov_base = str_area;
|
||||||
|
iov[2].iov_len = (long) bcc_ptr - (long) str_area;
|
||||||
|
|
||||||
|
count = iov[1].iov_len + iov[2].iov_len;
|
||||||
smb_buf->smb_buf_length += count;
|
smb_buf->smb_buf_length += count;
|
||||||
|
|
||||||
BCC_LE(smb_buf) = cpu_to_le16(count);
|
BCC_LE(smb_buf) = cpu_to_le16(count);
|
||||||
|
|
||||||
iov[1].iov_base = str_area;
|
rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type,
|
||||||
iov[1].iov_len = count;
|
|
||||||
rc = SendReceive2(xid, ses, iov, 2 /* num_iovecs */, &resp_buf_type,
|
|
||||||
CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR);
|
CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR);
|
||||||
/* SMB request buf freed in SendReceive2 */
|
/* SMB request buf freed in SendReceive2 */
|
||||||
|
|
||||||
|
@ -560,6 +615,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
|
||||||
ses, nls_cp);
|
ses, nls_cp);
|
||||||
|
|
||||||
ssetup_exit:
|
ssetup_exit:
|
||||||
|
if (spnego_key)
|
||||||
|
key_put(spnego_key);
|
||||||
kfree(str_area);
|
kfree(str_area);
|
||||||
if (resp_buf_type == CIFS_SMALL_BUFFER) {
|
if (resp_buf_type == CIFS_SMALL_BUFFER) {
|
||||||
cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base));
|
cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base));
|
||||||
|
|
Loading…
Add table
Reference in a new issue