Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
[powerpc.git] / fs / cifs / sess.c
index a52aacb..bbdda99 100644 (file)
@@ -33,8 +33,6 @@
 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;
@@ -85,14 +83,16 @@ static 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 */
+               /* null user mount */
+               *bcc_ptr = 0;
+               *(bcc_ptr+1) = 0;
        } else { /* 300 should be long enough for any conceivable user name */
                bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->userName,
                                          300, nls_cp);
@@ -100,10 +100,13 @@ static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
        bcc_ptr += 2 * bytes_ret;
        bcc_ptr += 2; /* account for null termination */
        /* copy domain */
-       if(ses->domainName == NULL)
-               bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr,
-                                         "CIFS_LINUX_DOM", 32, nls_cp);
-       else
+       if(ses->domainName == NULL) {
+               /* Sending null domain better than using a bogus domain name (as
+               we did briefly in 2.6.18) since server will use its default */
+               *bcc_ptr = 0;
+               *(bcc_ptr+1) = 0;
+               bytes_ret = 0;
+       } else
                bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName, 
                                          256, nls_cp);
        bcc_ptr += 2 * bytes_ret;
@@ -113,7 +116,7 @@ static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
        bytes_ret = cifs_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32,
                                  nls_cp);
        bcc_ptr += 2 * bytes_ret;
-       bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, system_utsname.release,
+       bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, init_utsname()->release,
                                  32, nls_cp);
        bcc_ptr += 2 * bytes_ret;
        bcc_ptr += 2; /* trailing null */
@@ -140,19 +143,17 @@ static 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 */
 
         /* copy domain */
        
-        if(ses->domainName == NULL) {
-                strcpy(bcc_ptr, "CIFS_LINUX_DOM");
-               bcc_ptr += 14;  /* strlen(CIFS_LINUX_DOM) */
-       } else {
+        if(ses->domainName != NULL) {
                 strncpy(bcc_ptr, ses->domainName, 256); 
                bcc_ptr += strnlen(ses->domainName, 256);
-       }
+       } /* else we will send a null domain name 
+            so the server will default to its own domain */
        *bcc_ptr = 0;
        bcc_ptr++;
 
@@ -160,8 +161,8 @@ static void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
 
        strcpy(bcc_ptr, "Linux version ");
        bcc_ptr += strlen("Linux version ");
-       strcpy(bcc_ptr, system_utsname.release);
-       bcc_ptr += strlen(system_utsname.release) + 1;
+       strcpy(bcc_ptr, init_utsname()->release);
+       bcc_ptr += strlen(init_utsname()->release) + 1;
 
        strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
        bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
@@ -270,6 +271,10 @@ static int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo
        ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
        if(ses->serverOS)
                strncpy(ses->serverOS, bcc_ptr, len);
+       if(strncmp(ses->serverOS, "OS/2",4) == 0) {
+                       cFYI(1,("OS/2 server"));
+                       ses->flags |= CIFS_SES_OS2;
+       }
 
        bcc_ptr += len + 1;
        bleft -= len + 1;
@@ -292,16 +297,11 @@ static int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo
         if(len > bleft)
                 return rc;
 
-        if(ses->serverDomain)
-                kfree(ses->serverDomain);
-
-        ses->serverDomain = kzalloc(len + 1, GFP_KERNEL);
-        if(ses->serverOS)
-                strncpy(ses->serverOS, bcc_ptr, len);
-
-        bcc_ptr += len + 1;
-       bleft -= len + 1;
-
+       /* No domain field in LANMAN case. Domain is
+          returned by old servers in the SMB negprot response */
+       /* BB For newer servers which do not support Unicode,
+          but thus do return domain here we could add parsing
+          for it later, but it is not very important */
        cFYI(1,("ascii: bytes left %d",bleft));
 
        return rc;
@@ -315,19 +315,22 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
        int wct;
        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.
@@ -338,9 +341,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) || (type == NTLMv2)) /* 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,
@@ -351,7 +355,20 @@ 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;
+
+       ses->flags &= ~CIFS_SES_LANMAN;
 
        if(type == LANMAN) {
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
@@ -359,16 +376,16 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
 
                /* no capabilities flags in old lanman negotiation */
 
-               pSMB->old_req.PasswordLength = CIFS_SESS_KEY_SIZE
+               pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE)
                /* BB calculate hash with password */
                /* and copy into bcc */
 
                calc_lanman_hash(ses, lnm_session_key);
-
-#ifdef CONFIG_CIFS_DEBUG2
+               ses->flags |= CIFS_SES_LANMAN; 
+/* #ifdef CONFIG_CIFS_DEBUG2
                cifs_dump_mem("cryptkey: ",ses->server->cryptKey,
                        CIFS_SESS_KEY_SIZE);
-#endif
+#endif */
                memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE);
                bcc_ptr += CIFS_SESS_KEY_SIZE;
 
@@ -377,7 +394,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
                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    
@@ -396,7 +413,7 @@ 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 */
 
@@ -404,12 +421,21 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
                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)
+               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
+               else
                        ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
        } else if (type == NTLMv2) {
-               char * v2_sess_key = kmalloc(V2_SESS_KEY_SIZE, GFP_KERNEL);
+               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);
@@ -423,10 +449,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
                /*      cpu_to_le16(LM2_SESS_KEY_SIZE); */
 
                pSMB->req_no_secext.CaseSensitivePasswordLength =
-                       cpu_to_le16(V2_SESS_KEY_SIZE);
+                       cpu_to_le16(sizeof(struct ntlmv2_resp));
 
                /* calculate session key */
-               CalcNTLMv2_response(ses, v2_sess_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,
@@ -436,11 +462,15 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
 
        /*      memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE);
                bcc_ptr += LM2_SESS_KEY_SIZE; */
-               memcpy(bcc_ptr, (char *)v2_sess_key, V2_SESS_KEY_SIZE);
-               bcc_ptr += V2_SESS_KEY_SIZE;
-               if(ses->capabilities & CAP_UNICODE)
+               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;
@@ -449,23 +479,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));
@@ -482,7 +503,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 */
@@ -510,6 +531,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);
@@ -518,4 +540,3 @@ ssetup_exit:
 
        return rc;
 }
-#endif /* CONFIG_CIFS_EXPERIMENTAL */