[CIFS] Support for setting up SMB sessions to legacy lanman servers
Steve French [Wed, 31 May 2006 22:40:51 +0000 (22:40 +0000)]
20 files changed:
fs/Kconfig
fs/cifs/CHANGES
fs/cifs/Makefile
fs/cifs/cifs_debug.c
fs/cifs/cifs_debug.h
fs/cifs/cifs_unicode.c
fs/cifs/cifsencrypt.c
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/dir.c
fs/cifs/fcntl.c
fs/cifs/inode.c
fs/cifs/misc.c
fs/cifs/readdir.c
fs/cifs/sess.c [new file with mode: 0644]
fs/cifs/smbencrypt.c

index f9b5842..c4eaacb 100644 (file)
@@ -1663,7 +1663,7 @@ config CIFS_STATS
          mounted by the cifs client to be displayed in /proc/fs/cifs/Stats
 
 config CIFS_STATS2
-       bool "CIFS extended statistics"
+       bool "Extended statistics"
        depends on CIFS_STATS
        help
          Enabling this option will allow more detailed statistics on SMB
@@ -1676,6 +1676,32 @@ config CIFS_STATS2
          Unless you are a developer or are doing network performance analysis
          or tuning, say N.
 
+config CIFS_WEAK_PW_HASH
+       bool "Support legacy servers which use weaker LANMAN security"
+       depends on CIFS
+       help
+         Modern CIFS servers including Samba and most Windows versions
+         (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos)
+         security mechanisms. These hash the password more securely
+         than the mechanisms used in the older LANMAN version of the
+          SMB protocol needed to establish sessions with old SMB servers.
+
+         Enabling this option allows the cifs module to mount to older
+         LANMAN based servers such as OS/2 and Windows 95, but such
+         mounts may be less secure than mounts using NTLM or more recent
+         security mechanisms if you are on a public network.  Unless you
+         have a need to access old SMB servers (and are on a private 
+         network) you probably want to say N.  Even if this support
+         is enabled in the kernel build, they will not be used
+         automatically. At runtime LANMAN mounts are disabled but
+         can be set to required (or optional) either in
+         /proc/fs/cifs (see fs/cifs/README for more detail) or via an
+         option on the mount command. This support is disabled by 
+         default in order to reduce the possibility of a downgrade
+         attack.
+         If unsure, say N.
+
 config CIFS_XATTR
         bool "CIFS extended attributes"
         depends on CIFS
@@ -1704,6 +1730,16 @@ config CIFS_POSIX
          (such as Samba 3.10 and later) which can negotiate
          CIFS POSIX ACL support.  If unsure, say N.
 
+config CIFS_DEBUG2
+       bool "Enable additional CIFS debugging routines
+       help
+          Enabling this option adds a few more debugging routines
+          to the cifs code which slightly increases the size of
+          the cifs module and can cause additional logging of debug
+          messages in some error paths, slowing performance. This
+          option can be turned off unless you are debugging
+          cifs problems.  If unsure, say N.
+          
 config CIFS_EXPERIMENTAL
          bool "CIFS Experimental Features (EXPERIMENTAL)"
          depends on CIFS && EXPERIMENTAL
@@ -1719,7 +1755,7 @@ config CIFS_EXPERIMENTAL
            If unsure, say N.
 
 config CIFS_UPCALL
-         bool "CIFS Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)"
+         bool "Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)"
          depends on CIFS_EXPERIMENTAL
          select CONNECTOR
          help
index 953d2f7..b878dfc 100644 (file)
@@ -1,3 +1,8 @@
+Version 1.44
+------------
+Rewritten sessionsetup support, including support for legacy SMB
+session setup needed for OS/2 and older servers such as Windows 95 and 98.
+
 Version 1.43
 ------------
 POSIX locking to servers which support CIFS POSIX Extensions
index 58c7725..a26f26e 100644 (file)
@@ -3,4 +3,4 @@
 #
 obj-$(CONFIG_CIFS) += cifs.o
 
-cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o readdir.o ioctl.o ntlmssp.o
+cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o readdir.o ioctl.o sess.o
index f4124a3..7f4013a 100644 (file)
@@ -39,7 +39,7 @@ cifs_dump_mem(char *label, void *data, int length)
        char *charptr = data;
        char buf[10], line[80];
 
-       printk(KERN_DEBUG "%s: dump of %d bytes of data at 0x%p\n\n", 
+       printk(KERN_DEBUG "%s: dump of %d bytes of data at 0x%p\n", 
                label, length, data);
        for (i = 0; i < length; i += 16) {
                line[0] = 0;
@@ -57,6 +57,57 @@ cifs_dump_mem(char *label, void *data, int length)
        }
 }
 
+#ifdef CONFIG_CIFS_DEBUG2
+void cifs_dump_detail(struct smb_hdr * smb)
+{
+       cERROR(1,("Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d",
+                 smb->Command, smb->Status.CifsError,
+                 smb->Flags, smb->Flags2, smb->Mid, smb->Pid));
+       cERROR(1,("smb buf %p len %d", smb, smbCalcSize_LE(smb)));
+}
+
+
+void cifs_dump_mids(struct TCP_Server_Info * server)
+{
+       struct list_head *tmp;
+       struct mid_q_entry * mid_entry;
+
+       if(server == NULL)
+               return;
+
+       cERROR(1,("Dump pending requests:"));
+       spin_lock(&GlobalMid_Lock);
+       list_for_each(tmp, &server->pending_mid_q) {
+               mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+               if(mid_entry) {
+                       cERROR(1,("State: %d Cmd: %d Pid: %d Tsk: %p Mid %d",
+                               mid_entry->midState,
+                               (int)mid_entry->command,
+                               mid_entry->pid,
+                               mid_entry->tsk,
+                               mid_entry->mid));
+#ifdef CONFIG_CIFS_STATS2
+                       cERROR(1,("IsLarge: %d buf: %p time rcv: %ld now: %ld",
+                               mid_entry->largeBuf,
+                               mid_entry->resp_buf,
+                               mid_entry->when_received,
+                               jiffies));
+#endif /* STATS2 */
+                       cERROR(1,("IsMult: %d IsEnd: %d", mid_entry->multiRsp,
+                                 mid_entry->multiEnd));
+                       if(mid_entry->resp_buf) {
+                               cifs_dump_detail(mid_entry->resp_buf);
+                               cifs_dump_mem("existing buf: ",
+                                       mid_entry->resp_buf,
+                                       62 /* fixme */);
+                       }
+                       
+               }
+       }
+       spin_unlock(&GlobalMid_Lock);
+}
+#endif /* CONFIG_CIFS_DEBUG2 */
+
 #ifdef CONFIG_PROC_FS
 static int
 cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
@@ -73,7 +124,6 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
 
        *beginBuffer = buf + offset;
 
-       
        length =
            sprintf(buf,
                    "Display Internal CIFS Data Structures for Debugging\n"
@@ -397,10 +447,10 @@ static read_proc_t multiuser_mount_read;
 static write_proc_t multiuser_mount_write;
 static read_proc_t extended_security_read;
 static write_proc_t extended_security_write;
-static read_proc_t ntlmv2_enabled_read;
+/* static read_proc_t ntlmv2_enabled_read;
 static write_proc_t ntlmv2_enabled_write;
 static read_proc_t packet_signing_enabled_read;
-static write_proc_t packet_signing_enabled_write;
+static write_proc_t packet_signing_enabled_write;*/
 static read_proc_t experimEnabled_read;
 static write_proc_t experimEnabled_write;
 static read_proc_t linuxExtensionsEnabled_read;
@@ -469,7 +519,7 @@ cifs_proc_init(void)
        if (pde)
                pde->write_proc = lookupFlag_write;
 
-       pde =
+/*     pde =
            create_proc_read_entry("NTLMV2Enabled", 0, proc_fs_cifs,
                                ntlmv2_enabled_read, NULL);
        if (pde)
@@ -479,7 +529,7 @@ cifs_proc_init(void)
            create_proc_read_entry("PacketSigningEnabled", 0, proc_fs_cifs,
                                packet_signing_enabled_read, NULL);
        if (pde)
-               pde->write_proc = packet_signing_enabled_write;
+               pde->write_proc = packet_signing_enabled_write;*/
 }
 
 void
@@ -496,9 +546,9 @@ cifs_proc_clean(void)
 #endif
        remove_proc_entry("MultiuserMount", proc_fs_cifs);
        remove_proc_entry("OplockEnabled", proc_fs_cifs);
-       remove_proc_entry("NTLMV2Enabled",proc_fs_cifs);
+/*     remove_proc_entry("NTLMV2Enabled",proc_fs_cifs); */
        remove_proc_entry("ExtendedSecurity",proc_fs_cifs);
-       remove_proc_entry("PacketSigningEnabled",proc_fs_cifs);
+/*     remove_proc_entry("PacketSigningEnabled",proc_fs_cifs); */
        remove_proc_entry("LinuxExtensionsEnabled",proc_fs_cifs);
        remove_proc_entry("Experimental",proc_fs_cifs);
        remove_proc_entry("LookupCacheEnabled",proc_fs_cifs);
@@ -787,7 +837,7 @@ extended_security_read(char *page, char **start, off_t off,
 {
        int len;
 
-       len = sprintf(page, "%d\n", extended_security);
+       len = sprintf(page, "0x%x\n", extended_security);
 
        len -= off;
        *start = page + off;
@@ -808,19 +858,25 @@ extended_security_write(struct file *file, const char __user *buffer,
 {
        char c;
        int rc;
+       cERROR(1,("size %ld",count)); /* BB removeme BB */
+       if((count < 2) || (count > 8))
+               return -EINVAL;
 
        rc = get_user(c, buffer);
+
+/* BB fixme need to parse more characters in order to handle CIFSSEC flags */ 
+
        if (rc)
                return rc;
        if (c == '0' || c == 'n' || c == 'N')
-               extended_security = 0;
+               extended_security = CIFSSEC_DEF; /* default */
        else if (c == '1' || c == 'y' || c == 'Y')
-               extended_security = 1;
+               extended_security = CIFSSEC_MAX;
 
        return count;
 }
 
-static int
+/* static int
 ntlmv2_enabled_read(char *page, char **start, off_t off,
                       int count, int *eof, void *data)
 {
@@ -855,6 +911,8 @@ ntlmv2_enabled_write(struct file *file, const char __user *buffer,
                ntlmv2_support = 0;
        else if (c == '1' || c == 'y' || c == 'Y')
                ntlmv2_support = 1;
+       else if (c == '2')
+               ntlmv2_support = 2;
 
        return count;
 }
@@ -898,7 +956,7 @@ packet_signing_enabled_write(struct file *file, const char __user *buffer,
                sign_CIFS_PDUs = 2;
 
        return count;
-}
+} */
 
 
 #endif
index 4304d9d..c26cd0d 100644 (file)
 #define _H_CIFS_DEBUG
 
 void cifs_dump_mem(char *label, void *data, int length);
+#ifdef CONFIG_CIFS_DEBUG2
+void cifs_dump_detail(struct smb_hdr *);
+void cifs_dump_mids(struct TCP_Server_Info *);
+#endif
 extern int traceSMB;           /* flag which enables the function below */
 void dump_smb(struct smb_hdr *, int);
 #define CIFS_INFO      0x01
index d2b1282..d2a8b29 100644 (file)
@@ -22,6 +22,7 @@
 #include "cifs_unicode.h"
 #include "cifs_uniupr.h"
 #include "cifspdu.h"
+#include "cifsglob.h"
 #include "cifs_debug.h"
 
 /*
index e7d6373..08781a4 100644 (file)
@@ -225,6 +225,8 @@ int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, struct nls_table * nls_
        user_name_len = strlen(ses->userName);
        if(user_name_len > MAX_USERNAME_SIZE)
                return -EINVAL;
+       if(ses->domainName == NULL)
+               return -EINVAL; /* BB should we use CIFS_LINUX_DOM */
        dom_name_len = strlen(ses->domainName);
        if(dom_name_len > MAX_USERNAME_SIZE)
                return -EINVAL;
index c262d88..7005705 100644 (file)
@@ -56,8 +56,8 @@ unsigned int experimEnabled = 0;
 unsigned int linuxExtEnabled = 1;
 unsigned int lookupCacheEnabled = 1;
 unsigned int multiuser_mount = 0;
-unsigned int extended_security = 0;
-unsigned int ntlmv2_support = 0;
+unsigned int extended_security = CIFSSEC_DEF;
+/* unsigned int ntlmv2_support = 0; */
 unsigned int sign_CIFS_PDUs = 1;
 extern struct task_struct * oplockThread; /* remove sparse warning */
 struct task_struct * oplockThread = NULL;
index 006eb33..7a04204 100644 (file)
@@ -88,7 +88,8 @@ enum statusEnum {
 };
 
 enum securityEnum {
-       NTLM = 0,               /* Legacy NTLM012 auth with NTLM hash */
+       LANMAN = 0,             /* Legacy LANMAN auth */
+       NTLM,                   /* Legacy NTLM012 auth with NTLM hash */
        NTLMv2,                 /* Legacy NTLM auth with NTLMv2 hash */
        RawNTLMSSP,             /* NTLMSSP without SPNEGO */
        NTLMSSP,                /* NTLMSSP via SPNEGO */
@@ -179,7 +180,9 @@ struct cifsUidInfo {
 struct cifsSesInfo {
        struct list_head cifsSessionList;
        struct semaphore sesSem;
+#if 0
        struct cifsUidInfo *uidInfo;    /* pointer to user info */
+#endif
        struct TCP_Server_Info *server; /* pointer to server info */
        atomic_t inUse; /* # of mounts (tree connections) on this ses */
        enum statusEnum status;
@@ -194,7 +197,7 @@ struct cifsSesInfo {
        char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for 
                                TCP names - will ipv6 and sctp addresses fit? */
        char userName[MAX_USERNAME_SIZE + 1];
-       char domainName[MAX_USERNAME_SIZE + 1];
+       char * domainName;
        char * password;
 };
 /* session flags */
@@ -391,9 +394,9 @@ struct mid_q_entry {
        struct smb_hdr *resp_buf;       /* response buffer */
        int midState;   /* wish this were enum but can not pass to wait_event */
        __u8 command;   /* smb command code */
-       unsigned multiPart:1;   /* multiple responses to one SMB request */
        unsigned largeBuf:1;    /* if valid response, is pointer to large buf */
-       unsigned multiResp:1;   /* multiple trans2 responses for one request  */
+       unsigned multiRsp:1;   /* multiple trans2 responses for one request  */
+       unsigned multiEnd:1; /* both received */
 };
 
 struct oplock_q_entry {
@@ -430,15 +433,35 @@ struct dir_notify_req {
 #define   CIFS_LARGE_BUFFER     2
 #define   CIFS_IOVEC            4    /* array of response buffers */
 
-/* Type of session setup needed */
-#define   CIFS_PLAINTEXT       0
-#define   CIFS_LANMAN          1
-#define   CIFS_NTLM            2
-#define   CIFS_NTLMSSP_NEG     3
-#define   CIFS_NTLMSSP_AUTH    4
-#define   CIFS_SPNEGO_INIT     5
-#define   CIFS_SPNEGO_TARG     6
-
+/* Security Flags: indicate type of session setup needed */
+#define   CIFSSEC_MAY_SIGN     0x00001
+#define   CIFSSEC_MAY_NTLM     0x00002
+#define   CIFSSEC_MAY_NTLMV2   0x00004
+#define   CIFSSEC_MAY_KRB5     0x00008
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define   CIFSSEC_MAY_LANMAN   0x00010
+#define   CIFSSEC_MAY_PLNTXT   0x00020
+#endif /* weak passwords */
+#define   CIFSSEC_MAY_SEAL     0x00040 /* not supported yet */
+
+#define   CIFSSEC_MUST_SIGN    0x01001
+/* note that only one of the following can be set so the
+result of setting MUST flags more than once will be to
+require use of the stronger protocol */
+#define   CIFSSEC_MUST_NTLM    0x02002
+#define   CIFSSEC_MUST_NTLMV2  0x04004
+#define   CIFSSEC_MUST_KRB5    0x08008
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define   CIFSSEC_MUST_LANMAN  0x10010
+#define   CIFSSEC_MUST_PLNTXT  0x20020
+#define   CIFSSEC_MASK          0x37037 /* current flags supported if weak */
+#else    
+#define          CIFSSEC_MASK          0x07007 /* flags supported if no weak config */
+#endif /* WEAK_PW_HASH */
+#define   CIFSSEC_MUST_SEAL    0x40040 /* not supported yet */
+
+#define   CIFSSEC_DEF  CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2
+#define   CIFSSEC_MAX  CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2
 /*
  *****************************************************************
  * All constants go here
@@ -540,8 +563,8 @@ GLOBAL_EXTERN unsigned int experimEnabled;
 GLOBAL_EXTERN unsigned int lookupCacheEnabled;
 GLOBAL_EXTERN unsigned int extended_security;  /* if on, session setup sent 
                                with more secure ntlmssp2 challenge/resp */
-GLOBAL_EXTERN unsigned int ntlmv2_support;  /* better optional password hash */
 GLOBAL_EXTERN unsigned int sign_CIFS_PDUs;  /* enable smb packet signing */
+GLOBAL_EXTERN unsigned int secFlags;
 GLOBAL_EXTERN unsigned int linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/
 GLOBAL_EXTERN unsigned int CIFSMaxBufSize;  /* max size not including hdr */
 GLOBAL_EXTERN unsigned int cifs_min_rcv;    /* min size of big ntwrk buf pool */
index b2233ac..e0ff9b5 100644 (file)
@@ -16,7 +16,7 @@
  *
  *   You should have received a copy of the GNU Lesser General Public License
  *   along with this library; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
 #ifndef _CIFSPDU_H
 
 #include <net/sock.h>
 
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define LANMAN_PROT 0
+#define CIFS_PROT   1
+#else
 #define CIFS_PROT   0
-#define BAD_PROT    CIFS_PROT+1
+#endif
+#define POSIX_PROT  CIFS_PROT+1
+#define BAD_PROT 0xFFFF
 
 /* SMB command codes */
 /* Some commands have minimal (wct=0,bcc=0), or uninteresting, responses
@@ -400,6 +406,25 @@ typedef struct negotiate_req {
        unsigned char DialectsArray[1];
 } __attribute__((packed)) NEGOTIATE_REQ;
 
+/* Dialect index is 13 for LANMAN */
+
+typedef struct lanman_neg_rsp {
+       struct smb_hdr hdr;     /* wct = 13 */
+       __le16 DialectIndex;
+       __le16 SecurityMode;
+       __le16 MaxBufSize;
+       __le16 MaxMpxCount;
+       __le16 MaxNumberVcs;
+       __le16 RawMode;
+       __le32 SessionKey;
+       __le32 ServerTime;
+       __le16 ServerTimeZone;
+       __le16 EncryptionKeyLength;
+       __le16 Reserved;
+       __u16  ByteCount;
+       unsigned char EncryptionKey[1];
+} __attribute__((packed)) LANMAN_NEG_RSP;
+
 typedef struct negotiate_rsp {
        struct smb_hdr hdr;     /* wct = 17 */
        __le16 DialectIndex;
@@ -520,8 +545,8 @@ typedef union smb_com_session_setup_andx {
                __le16 MaxMpxCount;
                __le16 VcNumber;
                __u32 SessionKey;
-               __le16 PassswordLength;
-               __u32 Reserved;
+               __le16 PasswordLength;
+               __u32 Reserved; /* encrypt key len and offset */
                __le16 ByteCount;
                unsigned char AccountPassword[1];       /* followed by */
                /* STRING AccountName */
@@ -1844,13 +1869,13 @@ typedef struct {
 typedef struct {
        __le32 DeviceType;
        __le32 DeviceCharacteristics;
-} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO;     /* device info, level 0x104 */
+} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
 
 typedef struct {
        __le32 Attributes;
        __le32 MaxPathNameComponentLength;
        __le32 FileSystemNameLen;
-       char FileSystemName[52]; /* do not really need to save this - so potentially get only subset of name */
+       char FileSystemName[52]; /* do not have to save this - get subset? */
 } __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO;
 
 /******************************************************************************/
index 310ea2f..ff78cf7 100644 (file)
@@ -69,7 +69,7 @@ extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
                                struct cifsSesInfo *ses,
                                void ** request_buf);
 extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
-                            const int stage, int * pNTLMv2_flg,
+                            const int stage, 
                             const struct nls_table *nls_cp);
 #endif
 extern __u16 GetNextMid(struct TCP_Server_Info *server);
index f003692..da3154f 100644 (file)
@@ -44,8 +44,11 @@ static struct {
        int index;
        char *name;
 } protocols[] = {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+       {LANMAN_PROT, "\2LM1.2X002"},
+#endif /* weak password hashing for legacy clients */
        {CIFS_PROT, "\2NT LM 0.12"}, 
-       {CIFS_PROT, "\2POSIX 2"},
+       {POSIX_PROT, "\2POSIX 2"},
        {BAD_PROT, "\2"}
 };
 #else
@@ -53,11 +56,29 @@ static struct {
        int index;
        char *name;
 } protocols[] = {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+       {LANMAN_PROT, "\2LM1.2X002"},
+#endif /* weak password hashing for legacy clients */
        {CIFS_PROT, "\2NT LM 0.12"}, 
        {BAD_PROT, "\2"}
 };
 #endif
 
+/* define the number of elements in the cifs dialect array */
+#ifdef CONFIG_CIFS_POSIX
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFS_NUM_PROT 3
+#else
+#define CIFS_NUM_PROT 2
+#endif /* CIFS_WEAK_PW_HASH */
+#else /* not posix */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFS_NUM_PROT 2
+#else
+#define CIFS_NUM_PROT 1
+#endif /* CONFIG_CIFS_WEAK_PW_HASH */
+#endif /* CIFS_POSIX */
+
 
 /* Mark as invalid, all open files on tree connections since they
    were closed when session to server was lost */
@@ -322,7 +343,8 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
     /* potential retries of smb operations it turns out we can determine */
     /* from the mid flags when the request buffer can be resent without  */
     /* having to use a second distinct buffer for the response */
-       *response_buf = *request_buf; 
+       if(response_buf)
+               *response_buf = *request_buf; 
 
        header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
                        wct /*wct */ );
@@ -373,6 +395,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
        NEGOTIATE_RSP *pSMBr;
        int rc = 0;
        int bytes_returned;
+       int i;
        struct TCP_Server_Info * server;
        u16 count;
 
@@ -388,19 +411,71 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                return rc;
        pSMB->hdr.Mid = GetNextMid(server);
        pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
-       if (extended_security)
-               pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
-
-       count = strlen(protocols[0].name) + 1;
-       strncpy(pSMB->DialectsArray, protocols[0].name, 30);    
-    /* null guaranteed to be at end of source and target buffers anyway */
-
+/*     if (extended_security)
+               pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;*/
+       
+       count = 0;
+       for(i=0;i<CIFS_NUM_PROT;i++) {
+               strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
+               count += strlen(protocols[i].name) + 1;
+               /* null at end of source and target buffers anyway */
+       }
        pSMB->hdr.smb_buf_length += count;
        pSMB->ByteCount = cpu_to_le16(count);
 
        rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
                         (struct smb_hdr *) pSMBr, &bytes_returned, 0);
        if (rc == 0) {
+               cFYI(1,("Dialect: %d", pSMBr->DialectIndex));
+               /* Check wct = 1 error case */
+               if((pSMBr->hdr.WordCount < 13)  
+                       || (pSMBr->DialectIndex == BAD_PROT)) {
+                       /* core returns wct = 1, but we do not ask for
+                       core - otherwise it just comes when dialect
+                       index is -1 indicating we could not negotiate
+                       a common dialect */
+                       rc = -EOPNOTSUPP;
+                       goto neg_err_exit;
+               } else if((pSMBr->hdr.WordCount == 13) && 
+                       (pSMBr->DialectIndex == LANMAN_PROT)) {
+                       struct lanman_neg_rsp * rsp = 
+                               (struct lanman_neg_rsp *)pSMBr;
+
+
+                       /* BB Mark ses struct as negotiated lanman level BB */
+                       server->secType = LANMAN;
+                       server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
+                       server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
+                       server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
+                               (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
+
+                       /* BB what do we do with raw mode? BB */
+                       server->timeZone = le16_to_cpu(rsp->ServerTimeZone);
+                       /* Do we have to set signing flags? no signing
+                       was available LANMAN - default should be ok */
+
+                       /* BB FIXME set default dummy capabilities since
+                       they are not returned by the server in this dialect */
+
+                       /* get server time for time conversions and add
+                       code to use it and timezone since this is not UTC */    
+
+                       if (rsp->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
+                               memcpy(server->cryptKey, rsp->EncryptionKey,
+                                       CIFS_CRYPTO_KEY_SIZE);
+                       } else {
+                               rc = -EIO;
+                               goto neg_err_exit;
+                       }
+
+                       cFYI(1,("LANMAN negotiated")); /* BB removeme BB */
+                       goto neg_err_exit;
+               } else if(pSMBr->hdr.WordCount != 17) {
+                       /* unknown wct */
+                       rc = -EOPNOTSUPP;
+                       goto neg_err_exit;
+               }
+
                server->secMode = pSMBr->SecurityMode;
                if((server->secMode & SECMODE_USER) == 0)
                        cFYI(1,("share mode security"));
@@ -479,7 +554,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                }
                                
        }
-       
+neg_err_exit:  
        cifs_buf_release(pSMB);
        return rc;
 }
index bae1479..7ffb8f2 100644 (file)
@@ -49,8 +49,6 @@
 
 static DECLARE_COMPLETION(cifsd_complete);
 
-extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
-                      unsigned char *p24);
 extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
                         unsigned char *p24);
 
@@ -585,9 +583,11 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                                                /* merge response - fix up 1st*/
                                                if(coalesce_t2(smb_buffer, 
                                                        mid_entry->resp_buf)) {
+                                                       mid_entry->multiRsp = 1;
                                                        break;
                                                } else {
                                                        /* all parts received */
+                                                       mid_entry->multiEnd = 1;
                                                        goto multi_t2_fnd; 
                                                }
                                        } else {
@@ -632,9 +632,14 @@ multi_t2_fnd:
                        wake_up_process(task_to_wake);
                } else if ((is_valid_oplock_break(smb_buffer, server) == FALSE)
                    && (isMultiRsp == FALSE)) {                          
-                       cERROR(1, ("No task to wake, unknown frame rcvd!"));
+                       cERROR(1, ("No task to wake, unknown frame rcvd! NumMids %d", midCount.counter));
                        cifs_dump_mem("Received Data is: ",(char *)smb_buffer,
                                      sizeof(struct smb_hdr));
+#ifdef CONFIG_CIFS_DEBUG2
+                       cifs_dump_detail(smb_buffer);
+                       cifs_dump_mids(server);
+#endif /* CIFS_DEBUG2 */
+                       
                }
        } /* end while !EXITING */
 
@@ -976,7 +981,7 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
                        }
                        /* BB are there cases in which a comma can be valid in
                        a domain name and need special handling? */
-                       if (strnlen(value, 65) < 65) {
+                       if (strnlen(value, 256) < 256) {
                                vol->domainname = value;
                                cFYI(1, ("Domain name set"));
                        } else {
@@ -1762,9 +1767,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        if (volume_info.username)
                                strncpy(pSesInfo->userName,
                                        volume_info.username,MAX_USERNAME_SIZE);
-                       if (volume_info.domainname)
-                               strncpy(pSesInfo->domainName,
-                                       volume_info.domainname,MAX_USERNAME_SIZE);
+                       if (volume_info.domainname) {
+                               int len = strlen(volume_info.domainname);
+                               pSesInfo->domainName = 
+                                       kmalloc(len + 1, GFP_KERNEL);
+                               if(pSesInfo->domainName)
+                                       strcpy(pSesInfo->domainName,
+                                               volume_info.domainname);
+                       }
                        pSesInfo->linux_uid = volume_info.linux_uid;
                        down(&pSesInfo->sesSem);
                        rc = cifs_setup_session(xid,pSesInfo, cifs_sb->local_nls);
@@ -2054,7 +2064,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
                        bcc_ptr++;
                }
                if(user == NULL)
-                       bytes_returned = 0; /* skill null user */
+                       bytes_returned = 0; /* skip null user */
                else
                        bytes_returned =
                                cifs_strtoUCS((__le16 *) bcc_ptr, user, 100,
@@ -2635,8 +2645,8 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
            /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128;
        if(sign_CIFS_PDUs)
                negotiate_flags |= NTLMSSP_NEGOTIATE_SIGN;
-       if(ntlmv2_support)
-               negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;
+/*     if(ntlmv2_support)
+               negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;*/
        /* setup pointers to domain name and workstation name */
        bcc_ptr += SecurityBlobLength;
 
@@ -3429,7 +3439,10 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
                        }
                        /* else do not bother copying these informational fields */
                }
-               tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
+               if(smb_buffer_response->WordCount == 3)
+                       tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
+               else
+                       tcon->Flags = 0;
                cFYI(1, ("Tcon flags: 0x%x ", tcon->Flags));
        } else if ((rc == 0) && tcon == NULL) {
         /* all we need to save for IPC$ connection */
@@ -3528,8 +3541,8 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
                        pSesInfo->server->timeZone));
 #ifdef CONFIG_CIFS_EXPERIMENTAL
                if(experimEnabled > 1)
-                       rc = CIFS_SessSetup(xid, pSesInfo, CIFS_NTLM /* type */,
-                                           &ntlmv2_flag, nls_info);    
+                       rc = CIFS_SessSetup(xid, pSesInfo,
+                                           first_time, nls_info);
                else
 #endif
                if (extended_security
index 57bdf7f..e6ed64e 100644 (file)
@@ -113,7 +113,7 @@ cifs_bp_rename_retry:
        full_path[namelen+2] = 0;
 BB remove above eight lines BB */
 
-/* Inode operations in similar order to how they appear in the Linux file fs.h */
+/* Inode operations in similar order to how they appear in Linux file fs.h */
 
 int
 cifs_create(struct inode *inode, struct dentry *direntry, int mode,
index 633a938..d91a3d4 100644 (file)
@@ -91,14 +91,14 @@ int cifs_dir_notify(struct file * file, unsigned long arg)
        if(full_path == NULL) {
                rc = -ENOMEM;
        } else {
-               cERROR(1,("cifs dir notify on file %s with arg 0x%lx",full_path,arg)); /* BB removeme BB */
+               cFYI(1,("dir notify on file %s Arg 0x%lx",full_path,arg));
                rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, 
                        GENERIC_READ | SYNCHRONIZE, 0 /* create options */,
                        &netfid, &oplock,NULL, cifs_sb->local_nls,
                        cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
                /* BB fixme - add this handle to a notify handle list */
                if(rc) {
-                       cERROR(1,("Could not open directory for notify"));  /* BB remove BB */
+                       cFYI(1,("Could not open directory for notify"));
                } else {
                        filter = convert_to_cifs_notify_flags(arg);
                        if(filter != 0) {
index 77a9e2f..a609d26 100644 (file)
@@ -1121,7 +1121,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
 
        xid = GetXid();
 
-       cFYI(1, ("In cifs_setattr, name = %s attrs->iavalid 0x%x",
+       cFYI(1, ("setattr on file %s attrs->iavalid 0x%x",
                 direntry->d_name.name, attrs->ia_valid));
 
        cifs_sb = CIFS_SB(direntry->d_inode->i_sb);
@@ -1157,6 +1157,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
                   when the local oplock break takes longer to flush
                   writebehind data than the SMB timeout for the SetPathInfo
                   request would allow */
+
                open_file = find_writable_file(cifsInode);
                if (open_file) {
                        __u16 nfid = open_file->netfid;
index fafd056..22c937e 100644 (file)
@@ -101,6 +101,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
        kfree(buf_to_free->serverDomain);
        kfree(buf_to_free->serverNOS);
        kfree(buf_to_free->password);
+       kfree(buf_to_free->domainName);
        kfree(buf_to_free);
 }
 
@@ -499,11 +500,12 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
                if(pSMBr->ByteCount > sizeof(struct file_notify_information)) {
                        data_offset = le32_to_cpu(pSMBr->DataOffset);
 
-                       pnotify = (struct file_notify_information *)((char *)&pSMBr->hdr.Protocol
-                               + data_offset);
-                       cFYI(1,("dnotify on %s with action: 0x%x",pnotify->FileName,
+                       pnotify = (struct file_notify_information *)
+                               ((char *)&pSMBr->hdr.Protocol + data_offset);
+                       cFYI(1,("dnotify on %s Action: 0x%x",pnotify->FileName,
                                pnotify->Action));  /* BB removeme BB */
-                    /*   cifs_dump_mem("Received notify Data is: ",buf,sizeof(struct smb_hdr)+60); */
+                    /*   cifs_dump_mem("Rcvd notify Data: ",buf,
+                               sizeof(struct smb_hdr)+60); */
                        return TRUE;
                }
                if(pSMBr->hdr.Status.CifsError) {
index 6b36c43..53903a2 100644 (file)
@@ -31,8 +31,8 @@
 #include "cifs_fs_sb.h"
 #include "cifsfs.h"
 
-/* BB fixme - add debug wrappers around this function to disable it fixme BB */
-/* static void dump_cifs_file_struct(struct file *file, char *label)
+#ifdef CONFIG_CIFS_DEBUG2
+static void dump_cifs_file_struct(struct file *file, char *label)
 {
        struct cifsFileInfo * cf;
 
@@ -53,7 +53,8 @@
                }
                
        }
-} */
+}
+#endif /* DEBUG2 */
 
 /* Returns one if new inode created (which therefore needs to be hashed) */
 /* Might check in the future if inode number changed so we can rehash inode */
@@ -597,7 +598,9 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
        . and .. for the root of a drive and for those we need
        to start two entries earlier */
 
-/*     dump_cifs_file_struct(file, "In fce ");*/
+#ifdef CONFIG_CIFS_DEBUG2
+       dump_cifs_file_struct(file, "In fce ");
+#endif
        if(((index_to_find < cifsFile->srch_inf.index_of_last_entry) && 
             is_dir_changed(file)) || 
           (index_to_find < first_entry_in_buffer)) {
@@ -980,9 +983,10 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
                        rc = cifs_filldir(current_entry, file, 
                                        filldir, direntry,tmp_buf);
                        file->f_pos++;
-                       if(file->f_pos == cifsFile->srch_inf.index_of_last_entry) {
+                       if(file->f_pos == 
+                               cifsFile->srch_inf.index_of_last_entry) {
                                cFYI(1,("last entry in buf at pos %lld %s",
-                                       file->f_pos,tmp_buf)); /* BB removeme BB */
+                                       file->f_pos,tmp_buf));
                                cifs_save_resume_key(current_entry,cifsFile);
                                break;
                        } else 
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
new file mode 100644 (file)
index 0000000..3499841
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ *   fs/cifs/sess.c
+ *
+ *   SMB/CIFS session setup handling routines
+ *
+ *   Copyright (c) International Business Machines  Corp., 2006
+ *   Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#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);
+
+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;
+
+       /* init fields common to all four types of SessSetup */
+       /* note that header is initialized to zero in header_assemble */
+       pSMB->req.AndXCommand = 0xFF;
+       pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+       pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+       /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
+
+       /* BB verify whether signing required on neg or just on auth frame 
+          (and NTLM case) */
+
+       capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+                       CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
+
+       if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+               pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+       if (ses->capabilities & CAP_UNICODE) {
+               pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE;
+               capabilities |= CAP_UNICODE;
+       }
+       if (ses->capabilities & CAP_STATUS32) {
+               pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS;
+               capabilities |= CAP_STATUS32;
+       }
+       if (ses->capabilities & CAP_DFS) {
+               pSMB->req.hdr.Flags2 |= SMBFLG2_DFS;
+               capabilities |= CAP_DFS;
+       }
+       if (ses->capabilities & CAP_UNIX) {
+               capabilities |= CAP_UNIX;
+       }
+
+       /* BB check whether to init vcnum BB */
+       return capabilities;
+}
+
+void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
+                           const struct nls_table * nls_cp)
+{
+       char * bcc_ptr = *pbcc_area;
+       int bytes_ret = 0;
+
+       /* 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) {
+               *bcc_ptr = 0;
+               bcc_ptr++;
+       }
+       /* copy user */
+       if(ses->userName == NULL) {
+               /* BB what about null user mounts - check that we do this BB */
+       } else { /* 300 should be long enough for any conceivable user name */
+               bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->userName,
+                                         300, nls_cp);
+       }
+       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
+               bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName, 
+                                         256, nls_cp);
+       bcc_ptr += 2 * bytes_ret;
+       bcc_ptr += 2;  /* account for null terminator */
+
+       /* Copy OS version */
+       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,
+                                 32, nls_cp);
+       bcc_ptr += 2 * bytes_ret;
+       bcc_ptr += 2; /* trailing null */
+
+       bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
+                                  32, nls_cp);
+       bcc_ptr += 2 * bytes_ret;
+       bcc_ptr += 2; /* trailing null */
+
+       *pbcc_area = bcc_ptr;
+}
+
+void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
+                         const struct nls_table * nls_cp)
+{
+       char * bcc_ptr = *pbcc_area;
+
+       /* copy user */
+       /* BB what about null user mounts - check that we do this BB */
+        /* copy user */
+        if(ses->userName == NULL) {
+                /* BB what about null user mounts - check that we do this BB */
+        } else { /* 300 should be long enough for any conceivable user name */
+                strncpy(bcc_ptr, ses->userName, 300);
+        }
+       /* BB improve check for overflow */
+        bcc_ptr += strnlen(ses->userName, 200);
+       *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 {
+                strncpy(bcc_ptr, ses->domainName, 256); 
+               bcc_ptr += strnlen(ses->domainName, 256);
+       }
+       *bcc_ptr = 0;
+       bcc_ptr++;
+
+       /* BB check for overflow here */
+
+       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, CIFS_NETWORK_OPSYS);
+       bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+
+        *pbcc_area = bcc_ptr;
+}
+
+int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
+                            const struct nls_table * nls_cp)
+{
+       int rc = 0;
+       int words_left, len;
+       char * data = *pbcc_area;
+
+
+
+       cFYI(1,("bleft %d",bleft));
+
+
+       /* word align, if bytes remaining is not even */
+       if(bleft % 2) {
+               bleft--;
+               data++;
+       }
+       words_left = bleft / 2;
+
+       /* save off server operating system */
+       len = UniStrnlen((wchar_t *) data, words_left);
+
+/* We look for obvious messed up bcc or strings in response so we do not go off
+   the end since (at least) WIN2K and Windows XP have a major bug in not null
+   terminating last Unicode string in response  */
+       if(len >= words_left)
+               return rc;
+
+       if(ses->serverOS)
+               kfree(ses->serverOS);
+       /* UTF-8 string will not grow more than four times as big as UCS-16 */
+       ses->serverOS = kzalloc(4 * len, GFP_KERNEL);
+       if(ses->serverOS != NULL) {
+               cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len,
+                                  nls_cp);
+       }
+       data += 2 * (len + 1);
+       words_left -= len + 1;
+
+       /* save off server network operating system */
+       len = UniStrnlen((wchar_t *) data, words_left);
+
+       if(len >= words_left)
+               return rc;
+
+       if(ses->serverNOS)
+               kfree(ses->serverNOS);
+       ses->serverNOS = kzalloc(4 * len, GFP_KERNEL); /* BB this is wrong length FIXME BB */
+       if(ses->serverNOS != NULL) {
+               cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len,
+                                  nls_cp);
+               if(strncmp(ses->serverNOS, "NT LAN Manager 4",16) == 0) {
+                       cFYI(1,("NT4 server"));
+                       ses->flags |= CIFS_SES_NT4;
+               }
+       }
+       data += 2 * (len + 1);
+       words_left -= len + 1;
+
+        /* save off server domain */
+        len = UniStrnlen((wchar_t *) data, words_left);
+
+        if(len > words_left)
+                return rc;
+
+        if(ses->serverDomain)
+                kfree(ses->serverDomain);
+        ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); /* BB FIXME wrong length */
+        if(ses->serverDomain != NULL) {
+                cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len,
+                                   nls_cp);
+                ses->serverDomain[2*len] = 0;
+                ses->serverDomain[(2*len) + 1] = 0;
+        }
+        data += 2 * (len + 1);
+        words_left -= len + 1;
+       
+       cFYI(1,("words left: %d",words_left));
+
+       return rc;
+}
+
+int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
+                            const struct nls_table * nls_cp)
+{
+       int rc = 0;
+       int len;
+       char * bcc_ptr = *pbcc_area;
+
+       cFYI(1,("decode sessetup ascii. bleft %d", bleft));
+       
+       len = strnlen(bcc_ptr, bleft);
+       if(len >= bleft)
+               return rc;
+       
+       if(ses->serverOS)
+               kfree(ses->serverOS);
+
+       ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
+       if(ses->serverOS)
+               strncpy(ses->serverOS, bcc_ptr, len);
+
+       bcc_ptr += len + 1;
+       bleft -= len + 1;
+
+       len = strnlen(bcc_ptr, bleft);
+       if(len >= bleft)
+               return rc;
+
+       if(ses->serverNOS)
+               kfree(ses->serverNOS);
+
+       ses->serverNOS = kzalloc(len + 1, GFP_KERNEL);
+       if(ses->serverNOS)
+               strncpy(ses->serverNOS, bcc_ptr, len);
+
+       bcc_ptr += len + 1;
+       bleft -= len + 1;
+
+        len = strnlen(bcc_ptr, bleft);
+        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;
+
+       cFYI(1,("ascii: bytes left %d",bleft));
+
+       return rc;
+}
+
+int 
+CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
+               const struct nls_table *nls_cp)
+{
+       int rc = 0;
+       int wct;
+       int i;
+       struct smb_hdr *smb_buf;
+       char *bcc_ptr;
+       SESSION_SETUP_ANDX *pSMB;
+       __u32 capabilities;
+       int count;
+       int resp_buf_type = 0;
+       struct kvec iov[1];
+       enum securityEnum type;
+       __u16 action;
+       int bytes_remaining;
+       
+       if(ses == NULL)
+               return -EINVAL;
+
+       type = ses->server->secType;
+       if(type == LANMAN) {
+#ifndef CONFIG_CIFS_WEAK_PW_HASH
+               /* LANMAN and plaintext are less secure and off by default.
+               So we make this explicitly be turned on in kconfig (in the
+               build) and turned on at runtime (changed from the default)
+               in proc/fs/cifs or via mount parm.  Unfortunately this is
+               needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
+               return -EOPNOTSUPP;
+#endif
+               wct = 10; /* lanman 2 style sessionsetup */
+       } else if(type == NTLM) /* NTLMv2 may retry NTLM */
+               wct = 13; /* old style NTLM sessionsetup */
+       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,
+                           (void **)&smb_buf);
+       if(rc)
+               return rc;
+
+       pSMB = (SESSION_SETUP_ANDX *)smb_buf;
+
+       capabilities = cifs_ssetup_hdr(ses, pSMB);
+       bcc_ptr = pByteArea(smb_buf);
+
+       if(type == LANMAN) {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+               char lnm_session_key[CIFS_SESSION_KEY_SIZE];
+               char password_with_pad[CIFS_ENCPWD_SIZE];
+
+               /* no capabilities flags in old lanman negotiation */
+
+               pSMB->old_req.PasswordLength = CIFS_SESSION_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);
+
+#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;
+
+               /* 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"));
+               /* 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];
+
+               pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+               pSMB->req_no_secext.CaseInsensitivePasswordLength =
+                       cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+               pSMB->req_no_secext.CaseSensitivePasswordLength =
+                       cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+       
+               /* calculate session key */
+               SMBNTencrypt(ses->password, ses->server->cryptKey,
+                            ntlm_session_key);
+
+               if(first_time) /* should this be moved into common code 
+                                 with similar ntlmv2 path? */
+                       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)
+                       unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
+               else
+                       ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
+       } else /* NTLMSSP or SPNEGO */ {
+               pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+               capabilities |= CAP_EXTENDED_SECURITY;
+               pSMB->req.Capabilities = cpu_to_le32(capabilities);
+               /* BB set password lengths */
+       }
+
+       count = (long) bcc_ptr - (long) pByteArea(smb_buf);
+       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);
+       /* SMB request buf freed in SendReceive2 */
+
+       cFYI(1,("ssetup rc from sendrecv2 is %d",rc));
+       if(rc)
+               goto ssetup_exit;
+
+       pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
+       smb_buf = (struct smb_hdr *)iov[0].iov_base;
+
+       if((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) {
+               rc = -EIO;
+               cERROR(1,("bad word count %d", smb_buf->WordCount));
+               goto ssetup_exit;
+       }
+       action = le16_to_cpu(pSMB->resp.Action);
+       if (action & GUEST_LOGIN)
+               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 */
+       /* and lanman response is 3 */
+       bytes_remaining = BCC(smb_buf);
+       bcc_ptr = pByteArea(smb_buf);
+
+       if(smb_buf->WordCount == 4) {
+               __u16 blob_len;
+               blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
+               bcc_ptr += blob_len;
+               if(blob_len > bytes_remaining) {
+                       cERROR(1,("bad security blob length %d", blob_len));
+                       rc = -EINVAL;
+                       goto ssetup_exit;
+               }
+               bytes_remaining -= blob_len;
+       }       
+
+       /* BB check if Unicode and decode strings */
+       if(smb_buf->Flags2 & SMBFLG2_UNICODE)
+               rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining,
+                                                  ses, nls_cp);
+       else
+               rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,nls_cp);
+       
+ssetup_exit:
+       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);
+       } else if(resp_buf_type == CIFS_LARGE_BUFFER)
+               cifs_buf_release(iov[0].iov_base);
+
+       return rc;
+}
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
index 6103bcd..f518c5e 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/random.h>
 #include "cifs_unicode.h"
 #include "cifspdu.h"
+#include "cifsglob.h"
 #include "md5.h"
 #include "cifs_debug.h"
 #include "cifsencrypt.h"