]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/cifs/sess.c
pageflags: get rid of FLAGS_RESERVED
[linux-2.6-omap-h63xx.git] / fs / cifs / sess.c
index 899dc6078d9ab7848b3ffda2547161c212c2e9e5..ed150efbe27c93fbec1834767262453c47619e14 100644 (file)
@@ -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);
@@ -340,11 +341,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
        SESSION_SETUP_ANDX *pSMB;
        __u32 capabilities;
        int count;
-       int resp_buf_type = 0;
-       struct kvec iov[2];
+       int resp_buf_type;
+       struct kvec iov[3];
        enum securityEnum type;
        __u16 action;
        int bytes_remaining;
+       struct key *spnego_key = NULL;
 
        if (ses == NULL)
                return -EINVAL;
@@ -377,24 +379,32 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
 
        capabilities = cifs_ssetup_hdr(ses, pSMB);
 
-       /* we will send the SMB in two pieces,
-       a fixed length beginning part, and a
-       second part which will include the strings
-       and rest of bcc area, in order to avoid having
-       to do a large buffer 17K allocation */
+       /* we will send the SMB in three pieces:
+       a fixed length beginning part, an optional
+       SPNEGO blob (which can be zero length), and a
+       last part which will include the strings
+       and rest of bcc area. This allows us to avoid
+       a large buffer 17K allocation */
        iov[0].iov_base = (char *)pSMB;
        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. */
        str_area = kmalloc(2000, GFP_KERNEL);
        if (str_area == NULL) {
-               cifs_small_buf_release(smb_buf);
-               return -ENOMEM;
+               rc = -ENOMEM;
+               goto ssetup_exit;
        }
        bcc_ptr = str_area;
 
        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];
@@ -407,10 +417,6 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
 
                calc_lanman_hash(ses, lnm_session_key);
                ses->flags |= CIFS_SES_LANMAN;
-/* #ifdef CONFIG_CIFS_DEBUG2
-               cifs_dump_mem("cryptkey: ",ses->server->cryptKey,
-                       CIFS_SESS_KEY_SIZE);
-#endif */
                memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE);
                bcc_ptr += CIFS_SESS_KEY_SIZE;
 
@@ -463,8 +469,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
                   struct ntlmv2_resp */
 
                if (v2_sess_key == NULL) {
-                       cifs_small_buf_release(smb_buf);
-                       return -ENOMEM;
+                       rc = -ENOMEM;
+                       goto ssetup_exit;
                }
 
                pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
@@ -499,22 +505,69 @@ 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;
+               }
+               if (first_time) {
+                       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 + iov[1].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,
-                         0 /* not long op */, 1 /* log NT STATUS if any */ );
+       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 */
 
        cFYI(1, ("ssetup rc from sendrecv2 is %d", rc));
@@ -560,6 +613,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));