[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:
Steve French 2007-11-16 23:37:35 +00:00
parent 8840dee9dc
commit 2442421b17
4 changed files with 77 additions and 18 deletions

View file

@ -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
------------ ------------

View file

@ -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

View file

@ -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;

View file

@ -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));