[CIFS] Fix allocation of buffers for new session setup routine to allow
longer user and domain names and allow passing sec options on mount

Signed-off-by: Steve French <sfrench@us.ibm.com>
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index 79a202b..a61d17e 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -7,7 +7,8 @@
 or recent levels of FindFirst unless server says it supports NT SMBs
 (instead use legacy equivalents from LANMAN dialect). Fix to allow
 NTLMv2 authentication support (now can use stronger password hashing
-on mount if corresponding /proc/fs/cifs/SecurityFlags is set (0x4004)
+on mount if corresponding /proc/fs/cifs/SecurityFlags is set (0x4004).
+Allow override of global cifs security flags on mount via "sec=" option(s).
 
 Version 1.43
 ------------
diff --git a/fs/cifs/README b/fs/cifs/README
index 46c2cfa..7986d0d 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -443,7 +443,10 @@
 		SFU does).  In the future the bottom 9 bits of the mode
 		mode also will be emulated using queries of the security
 		descriptor (ACL).
-sec		Security mode.  Allowed values are:
+ sign           Must use packet signing (helps avoid unwanted data modification
+		by intermediate systems in the route).  Note that signing
+		does not work with lanman or plaintext authentication.
+ sec            Security mode.  Allowed values are:
 			none	attempt to connection as a null user (no name)
 			krb5    Use Kerberos version 5 authentication
 			krb5i   Use Kerberos authentication and packet signing
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 87453a6b..6d7cf5f 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -186,6 +186,7 @@
 	struct TCP_Server_Info *server;	/* pointer to server info */
 	atomic_t inUse; /* # of mounts (tree connections) on this ses */
 	enum statusEnum status;
+	unsigned overrideSecFlg;  /* if non-zero override global sec flags */
 	__u16 ipc_tid;		/* special tid for connection to IPC share */
 	__u16 flags;
 	char *serverOS;		/* name of operating system underlying server */
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 38f83db2..de405bf 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -396,6 +396,7 @@
 	int i;
 	struct TCP_Server_Info * server;
 	u16 count;
+	unsigned int secFlags;
 
 	if(ses->server)
 		server = ses->server;
@@ -407,9 +408,16 @@
 		      (void **) &pSMB, (void **) &pSMBr);
 	if (rc)
 		return rc;
+
+	/* if any of auth flags (ie not sign or seal) are overriden use them */
+	if(ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
+		secFlags = ses->overrideSecFlg;
+	else /* if override flags set only sign/seal OR them with global auth */
+		secFlags = extended_security | ses->overrideSecFlg;
+
 	pSMB->hdr.Mid = GetNextMid(server);
 	pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
-	if((extended_security & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
+	if((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
 		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
 	
 	count = 0;
@@ -439,8 +447,8 @@
 			&& (pSMBr->DialectIndex == LANMAN_PROT)) {
 		struct lanman_neg_rsp * rsp = (struct lanman_neg_rsp *)pSMBr;
 
-		if((extended_security & CIFSSEC_MAY_LANMAN) || 
-			(extended_security & CIFSSEC_MAY_PLNTXT))
+		if((secFlags & CIFSSEC_MAY_LANMAN) || 
+			(secFlags & CIFSSEC_MAY_PLNTXT))
 			server->secType = LANMAN;
 		else {
 			cERROR(1, ("mount failed weak security disabled"
@@ -498,12 +506,12 @@
 
 	if((server->secMode & SECMODE_PW_ENCRYPT) == 0)
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
-		if ((extended_security & CIFSSEC_MAY_PLNTXT) == 0)
+		if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
 #endif /* CIFS_WEAK_PW_HASH */
 			cERROR(1,("Server requests plain text password"
 				  " but client support disabled"));
 
-	if(extended_security & CIFSSEC_MUST_NTLMV2)
+	if(secFlags & CIFSSEC_MUST_NTLMV2)
 		server->secType = NTLMv2;
 	else
 		server->secType = NTLM;
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index c0f98dd..876eb9e 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -915,32 +915,32 @@
 				cERROR(1,("no security value specified"));
                                 continue;
                         } else if (strnicmp(value, "krb5i", 5) == 0) {
-				vol->secFlg = CIFSSEC_MAY_KRB5 | 
+				vol->secFlg |= CIFSSEC_MAY_KRB5 | 
 					CIFSSEC_MUST_SIGN;
 			} else if (strnicmp(value, "krb5p", 5) == 0) {
-				/* vol->secFlg = CIFSSEC_MUST_SEAL | 
+				/* vol->secFlg |= CIFSSEC_MUST_SEAL | 
 					CIFSSEC_MAY_KRB5; */ 
 				cERROR(1,("Krb5 cifs privacy not supported"));
 				return 1;
 			} else if (strnicmp(value, "krb5", 4) == 0) {
-				vol->secFlg = CIFSSEC_MAY_KRB5;
+				vol->secFlg |= CIFSSEC_MAY_KRB5;
 			} else if (strnicmp(value, "ntlmv2i", 7) == 0) {
-				vol->secFlg = CIFSSEC_MAY_NTLMV2 |
+				vol->secFlg |= CIFSSEC_MAY_NTLMV2 |
 					CIFSSEC_MUST_SIGN;
 			} else if (strnicmp(value, "ntlmv2", 6) == 0) {
-				vol->secFlg = CIFSSEC_MAY_NTLMV2;
+				vol->secFlg |= CIFSSEC_MAY_NTLMV2;
 			} else if (strnicmp(value, "ntlmi", 5) == 0) {
-				vol->secFlg = CIFSSEC_MAY_NTLM |
+				vol->secFlg |= CIFSSEC_MAY_NTLM |
 					CIFSSEC_MUST_SIGN;
 			} else if (strnicmp(value, "ntlm", 4) == 0) {
 				/* ntlm is default so can be turned off too */
-				vol->secFlg = CIFSSEC_MAY_NTLM;
+				vol->secFlg |= CIFSSEC_MAY_NTLM;
 			} else if (strnicmp(value, "nontlm", 6) == 0) {
 				/* BB is there a better way to do this? */
-				vol->secFlg = CIFSSEC_MAY_NTLMV2;
+				vol->secFlg |= CIFSSEC_MAY_NTLMV2;
 #ifdef CONFIG_CIFS_WEAK_PW_HASH
 			} else if (strnicmp(value, "lanman", 6) == 0) {
-                                vol->secFlg = CIFSSEC_MAY_LANMAN;
+                                vol->secFlg |= CIFSSEC_MAY_LANMAN;
 #endif
 			} else if (strnicmp(value, "none", 4) == 0) {
 				vol->nullauth = 1;
@@ -1173,6 +1173,10 @@
 			vol->no_psx_acl = 0;
 		} else if (strnicmp(data, "noacl",5) == 0) {
 			vol->no_psx_acl = 1;
+		} else if (strnicmp(data, "sign",4) == 0) {
+			vol->secFlg |= CIFSSEC_MUST_SIGN;
+/*		} else if (strnicmp(data, "seal",4) == 0) {
+			vol->secFlg |= CIFSSEC_MUST_SEAL; */
 		} else if (strnicmp(data, "direct",6) == 0) {
 			vol->direct_io = 1;
 		} else if (strnicmp(data, "forcedirectio",13) == 0) {
@@ -1776,6 +1780,7 @@
 						volume_info.domainname);
 			}
 			pSesInfo->linux_uid = volume_info.linux_uid;
+			pSesInfo->overrideSecFlg = volume_info.secFlg;
 			down(&pSesInfo->sesSem);
 			/* BB FIXME need to pass vol->secFlgs BB */
 			rc = cifs_setup_session(xid,pSesInfo, cifs_sb->local_nls);
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 70e32a8..7737edd 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -138,7 +138,7 @@
                 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 */
 
@@ -313,11 +313,12 @@
 	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[2];  /* BB split variable length info into 2nd iovec */
+	struct kvec iov[2];
 	enum securityEnum type;
 	__u16 action;
 	int bytes_remaining;
@@ -351,7 +352,18 @@
 	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
@@ -365,10 +377,10 @@
 
 		calc_lanman_hash(ses, lnm_session_key);
 
-#ifdef CONFIG_CIFS_DEBUG2
+/* #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 +389,7 @@
 		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 +408,7 @@
 
 		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 */
 
@@ -454,23 +466,14 @@
 		/* 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));
@@ -515,6 +518,7 @@
 		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);