Merge ../linus
[pandora-kernel.git] / fs / cifs / sess.c
index 3499841..d1705ab 100644 (file)
 #include "cifs_debug.h"
 #include "ntlmssp.h"
 #include "nterr.h"
-#include <linux/ctype.h>
-
-extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
-                       unsigned char *p24);
+#include <linux/utsname.h>
 
 extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
                          unsigned char *p24);
 
-#ifdef CONFIG_CIFS_EXPERIMENTAL
-
 static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
 {
        __u32 capabilities = 0;
@@ -79,7 +74,7 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
        return capabilities;
 }
 
-void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
+static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
                            const struct nls_table * nls_cp)
 {
        char * bcc_ptr = *pbcc_area;
@@ -88,11 +83,11 @@ void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
        /* BB FIXME add check that strings total less
        than 335 or will need to send them as arrays */
 
-       /* align unicode strings, must be word aligned */
-       if ((long) bcc_ptr % 2) {
+       /* unicode strings, must be word aligned before the call */
+/*     if ((long) bcc_ptr % 2) {
                *bcc_ptr = 0;
                bcc_ptr++;
-       }
+       } */
        /* copy user */
        if(ses->userName == NULL) {
                /* BB what about null user mounts - check that we do this BB */
@@ -129,7 +124,7 @@ void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
        *pbcc_area = bcc_ptr;
 }
 
-void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
+static void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
                          const struct nls_table * nls_cp)
 {
        char * bcc_ptr = *pbcc_area;
@@ -143,7 +138,7 @@ void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
                 strncpy(bcc_ptr, ses->userName, 300);
         }
        /* BB improve check for overflow */
-        bcc_ptr += strnlen(ses->userName, 200);
+        bcc_ptr += strnlen(ses->userName, 300);
        *bcc_ptr = 0;
         bcc_ptr++; /* account for null termination */
 
@@ -172,7 +167,7 @@ void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
         *pbcc_area = bcc_ptr;
 }
 
-int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
+static int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
                             const struct nls_table * nls_cp)
 {
        int rc = 0;
@@ -254,7 +249,7 @@ int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
        return rc;
 }
 
-int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
+static int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
                             const struct nls_table * nls_cp)
 {
        int rc = 0;
@@ -316,22 +311,24 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
 {
        int rc = 0;
        int wct;
-       int i;
        struct smb_hdr *smb_buf;
        char *bcc_ptr;
+       char *str_area;
        SESSION_SETUP_ANDX *pSMB;
        __u32 capabilities;
        int count;
        int resp_buf_type = 0;
-       struct kvec iov[1];
+       struct kvec iov[2];
        enum securityEnum type;
        __u16 action;
        int bytes_remaining;
-       
+
        if(ses == NULL)
                return -EINVAL;
 
        type = ses->server->secType;
+
+       cFYI(1,("sess setup type %d",type));
        if(type == LANMAN) {
 #ifndef CONFIG_CIFS_WEAK_PW_HASH
                /* LANMAN and plaintext are less secure and off by default.
@@ -342,9 +339,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
                return -EOPNOTSUPP;
 #endif
                wct = 10; /* lanman 2 style sessionsetup */
-       } else if(type == NTLM) /* NTLMv2 may retry NTLM */
+       } else if((type == NTLM) || (type == NTLMv2)) { 
+               /* For NTLMv2 failures eventually may need to retry NTLM */
                wct = 13; /* old style NTLM sessionsetup */
-       else /* same size for negotiate or auth, NTLMSSP or extended security */
+       else /* same size for negotiate or auth, NTLMSSP or extended security */
                wct = 12;
 
        rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
@@ -355,63 +353,55 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
        pSMB = (SESSION_SETUP_ANDX *)smb_buf;
 
        capabilities = cifs_ssetup_hdr(ses, pSMB);
-       bcc_ptr = pByteArea(smb_buf);
+
+       /* 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 */
+        iov[0].iov_base = (char *)pSMB;
+        iov[0].iov_len = smb_buf->smb_buf_length + 4;
+
+       /* 2000 big enough to fit max user, domain, NOS name etc. */
+       str_area = kmalloc(2000, GFP_KERNEL);
+       bcc_ptr = str_area;
 
        if(type == LANMAN) {
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
-               char lnm_session_key[CIFS_SESSION_KEY_SIZE];
-               char password_with_pad[CIFS_ENCPWD_SIZE];
+               char lnm_session_key[CIFS_SESS_KEY_SIZE];
 
                /* no capabilities flags in old lanman negotiation */
 
-               pSMB->old_req.PasswordLength = CIFS_SESSION_KEY_SIZE
+               pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE)
                /* BB calculate hash with password */
                /* and copy into bcc */
 
-               memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
-               strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
-
-               /* calculate old style session key */
-               /* toupper may be less broken then repeatedly calling
-               nls_toupper would be, but neither handles multibyte code pages
-               but the only alternative would be converting to UCS-16 (Unicode)
-               uppercasing and converting back which is only worth doing if
-               we knew it were utf8. utf8 code page needs its own
-               toupper and tolower and strnicmp functions */
-               
-               for(i = 0; i< CIFS_ENCPWD_SIZE; i++) {
-                       password_with_pad[i] = toupper(password_with_pad[i]);
-               }
-
-               SMBencrypt(password_with_pad, ses->server->cryptKey,
-                          lnm_session_key);
+               calc_lanman_hash(ses, lnm_session_key);
 
-#ifdef CONFIG_CIFS_DEBUG2
+/* #ifdef CONFIG_CIFS_DEBUG2
                cifs_dump_mem("cryptkey: ",ses->server->cryptKey,
-                       CIFS_SESSION_KEY_SIZE);
-#endif
-               /* clear password before we return/free memory */
-               memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
-               memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESSION_KEY_SIZE);
-               bcc_ptr += CIFS_SESSION_KEY_SIZE;
+                       CIFS_SESS_KEY_SIZE);
+#endif */
+               memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE);
+               bcc_ptr += CIFS_SESS_KEY_SIZE;
 
                /* can not sign if LANMAN negotiated so no need
                to calculate signing key? but what if server
                changed to do higher than lanman dialect and
                we reconnected would we ever calc signing_key? */
 
-               cERROR(1,("Negotiating LANMAN setting up strings"));
+               cFYI(1,("Negotiating LANMAN setting up strings"));
                /* Unicode not allowed for LANMAN dialects */
                ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
 #endif    
        } else if (type == NTLM) {
-               char ntlm_session_key[CIFS_SESSION_KEY_SIZE];
+               char ntlm_session_key[CIFS_SESS_KEY_SIZE];
 
                pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
                pSMB->req_no_secext.CaseInsensitivePasswordLength =
-                       cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+                       cpu_to_le16(CIFS_SESS_KEY_SIZE);
                pSMB->req_no_secext.CaseSensitivePasswordLength =
-                       cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+                       cpu_to_le16(CIFS_SESS_KEY_SIZE);
        
                /* calculate session key */
                SMBNTencrypt(ses->password, ses->server->cryptKey,
@@ -419,18 +409,64 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
 
                if(first_time) /* should this be moved into common code 
                                  with similar ntlmv2 path? */
-                       cifs_calculate_mac_key(
-                               ses->server->mac_signing_key,
+                       cifs_calculate_mac_key(ses->server->mac_signing_key,
                                ntlm_session_key, ses->password);
                /* copy session key */
 
-               memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESSION_KEY_SIZE);
-               bcc_ptr += CIFS_SESSION_KEY_SIZE;
-               memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESSION_KEY_SIZE);
-               bcc_ptr += CIFS_SESSION_KEY_SIZE;
-               if(ses->capabilities & CAP_UNICODE)
+               memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE);
+               bcc_ptr += CIFS_SESS_KEY_SIZE;
+               memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE);
+               bcc_ptr += CIFS_SESS_KEY_SIZE;
+               if(ses->capabilities & CAP_UNICODE) {
+                       /* unicode strings must be word aligned */
+                       if (iov[0].iov_len % 2) {
+                               *bcc_ptr = 0;
+                               bcc_ptr++;              
+                       }       
+                       unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
+               } else
+                       ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
+       } else if (type == NTLMv2) {
+               char * v2_sess_key = 
+                       kmalloc(sizeof(struct ntlmv2_resp), GFP_KERNEL);
+
+               /* BB FIXME change all users of v2_sess_key to
+                  struct ntlmv2_resp */
+
+               if(v2_sess_key == NULL) {
+                       cifs_small_buf_release(smb_buf);
+                       return -ENOMEM;
+               }
+
+               pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+
+               /* LM2 password would be here if we supported it */
+               pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
+               /*      cpu_to_le16(LM2_SESS_KEY_SIZE); */
+
+               pSMB->req_no_secext.CaseSensitivePasswordLength =
+                       cpu_to_le16(sizeof(struct ntlmv2_resp));
+
+               /* calculate session key */
+               setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp);
+               if(first_time) /* should this be moved into common code
+                                 with similar ntlmv2 path? */
+               /*   cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key,
+                               response BB FIXME, v2_sess_key); */
+
+               /* copy session key */
+
+       /*      memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE);
+               bcc_ptr += LM2_SESS_KEY_SIZE; */
+               memcpy(bcc_ptr, (char *)v2_sess_key, sizeof(struct ntlmv2_resp));
+               bcc_ptr += sizeof(struct ntlmv2_resp);
+               kfree(v2_sess_key);
+               if(ses->capabilities & CAP_UNICODE) {
+                       if(iov[0].iov_len % 2) {
+                               *bcc_ptr = 0;
+                       }       bcc_ptr++;
                        unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
-               else
+               else
                        ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
        } else /* NTLMSSP or SPNEGO */ {
                pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
@@ -439,23 +475,14 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
                /* BB set password lengths */
        }
 
-       count = (long) bcc_ptr - (long) pByteArea(smb_buf);
+       count = (long) bcc_ptr - (long) str_area;
        smb_buf->smb_buf_length += count;
 
-       /* if we switch to small buffers, count will need to be fewer
-          than 383 (strings less than 335 bytes) */
-
        BCC_LE(smb_buf) = cpu_to_le16(count);
 
-
-       /* BB FIXME check for other non ntlm code paths */
-
-       /* BB check is this too big for a small smb? */
-
-       iov[0].iov_base = (char *)pSMB;
-       iov[0].iov_len = smb_buf->smb_buf_length + 4;
-
-       rc = SendReceive2(xid, ses, iov, 1 /* num_iovecs */, &resp_buf_type, 0);
+       iov[1].iov_base = str_area;
+       iov[1].iov_len = count; 
+       rc = SendReceive2(xid, ses, iov, 2 /* num_iovecs */, &resp_buf_type, 0);
        /* SMB request buf freed in SendReceive2 */
 
        cFYI(1,("ssetup rc from sendrecv2 is %d",rc));
@@ -472,7 +499,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
        }
        action = le16_to_cpu(pSMB->resp.Action);
        if (action & GUEST_LOGIN)
-               cFYI(1, (" Guest login")); /* BB mark SesInfo struct? */
+               cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */
        ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
        cFYI(1, ("UID = %d ", ses->Suid));
        /* response can have either 3 or 4 word count - Samba sends 3 */
@@ -500,6 +527,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
                rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,nls_cp);
        
 ssetup_exit:
+       kfree(str_area);
        if(resp_buf_type == CIFS_SMALL_BUFFER) {
                cFYI(1,("ssetup freeing small buf %p", iov[0].iov_base));
                cifs_small_buf_release(iov[0].iov_base);
@@ -508,4 +536,3 @@ ssetup_exit:
 
        return rc;
 }
-#endif /* CONFIG_CIFS_EXPERIMENTAL */