[linux-cifs-client] [PATCH 5/5] [CIFS] have CIFS_SessSetup
build correct SPNEGO SessionSetup request
Jeff Layton
jlayton at redhat.com
Fri Nov 16 13:53:51 GMT 2007
On Fri, 16 Nov 2007 16:08:52 +0300
"Q (Igor Mammedov)" <qwerty0987654321 at mail.ru> wrote:
> Jeff Layton wrote:
> > + ses->server->mac_signing_key.len =
> > msg->sesskey_len;
> > + memcpy(ses->server->mac_signing_key.data.krb5,
> > msg->data,
> > + msg->sesskey_len);
>
> Possible memory corruption here. Strictly speaking we don't have
> control over key length, it depends on krb server and client
> implementation/settings. Taking into account that session setup
> doesn't happen very often it wouldn't hurt to check if the size of
> mac_signing_key.data.krb5 is suitable or bail out in case of overflow.
>
Good catch. How about this patch instead?
From 3fd419aee87260b2f349fa092766fcb535cf7f53 Mon Sep 17 00:00:00 2001
From: Jeff Layton <jlayton at redhat.com>
Date: Fri, 16 Nov 2007 08:44:45 -0500
Subject: [PATCH 5/5] [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.
Signed-off-by: Jeff Layton <jlayton at redhat.com>
---
fs/cifs/cifsglob.h | 1 +
fs/cifs/sess.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 60 insertions(+), 7 deletions(-)
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 3525082..1fde219 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -110,6 +110,7 @@ struct mac_key {
unsigned int len;
union {
char ntlm[CIFS_SESS_KEY_SIZE + 16];
+ char krb5[CIFS_SESS_KEY_SIZE + 16]; /* BB: length correct? */
struct {
char key[16];
struct ntlmv2_resp resp;
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index ed01ef3..192b154 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -29,6 +29,7 @@
#include "ntlmssp.h"
#include "nterr.h"
#include <linux/utsname.h>
+#include "cifs_spnego.h"
extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
unsigned char *p24);
@@ -341,10 +342,11 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
__u32 capabilities;
int count;
int resp_buf_type = 0;
- struct kvec iov[2];
+ struct kvec iov[3];
enum securityEnum type;
__u16 action;
int bytes_remaining;
+ struct key *spnego_key = NULL;
if (ses == NULL)
return -EINVAL;
@@ -395,6 +397,9 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
ses->flags &= ~CIFS_SES_LANMAN;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+
if (type == LANMAN) {
#ifdef CONFIG_CIFS_WEAK_PW_HASH
char lnm_session_key[CIFS_SESS_KEY_SIZE];
@@ -499,21 +504,66 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
} else
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;
capabilities |= CAP_EXTENDED_SECURITY;
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;
BCC_LE(smb_buf) = cpu_to_le16(count);
- iov[1].iov_base = str_area;
- iov[1].iov_len = count;
- rc = SendReceive2(xid, ses, iov, 2 /* num_iovecs */, &resp_buf_type,
+ rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type,
CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR);
/* SMB request buf freed in SendReceive2 */
@@ -560,6 +610,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
ses, nls_cp);
ssetup_exit:
+ if (spnego_key)
+ key_put(spnego_key);
kfree(str_area);
if (resp_buf_type == CIFS_SMALL_BUFFER) {
cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base));
--
1.5.3.3
More information about the linux-cifs-client
mailing list