From 3979877e5606ecc58c5a31bd0078c6d80ba9cbe7 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 31 May 2006 22:40:51 +0000 Subject: [CIFS] Support for setting up SMB sessions to legacy lanman servers --- fs/Kconfig | 40 +++- fs/cifs/CHANGES | 5 + fs/cifs/Makefile | 2 +- fs/cifs/cifs_debug.c | 84 ++++++-- fs/cifs/cifs_debug.h | 4 + fs/cifs/cifs_unicode.c | 1 + fs/cifs/cifsencrypt.c | 2 + fs/cifs/cifsfs.c | 4 +- fs/cifs/cifsglob.h | 51 +++-- fs/cifs/cifspdu.h | 37 +++- fs/cifs/cifsproto.h | 2 +- fs/cifs/cifssmb.c | 95 ++++++++- fs/cifs/connect.c | 39 ++-- fs/cifs/dir.c | 2 +- fs/cifs/fcntl.c | 4 +- fs/cifs/inode.c | 3 +- fs/cifs/misc.c | 10 +- fs/cifs/readdir.c | 16 +- fs/cifs/sess.c | 511 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smbencrypt.c | 1 + 20 files changed, 837 insertions(+), 76 deletions(-) create mode 100644 fs/cifs/sess.c diff --git a/fs/Kconfig b/fs/Kconfig index f9b5842c8d2de..c4eaacb86071f 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -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 diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 953d2f7f88b05..b878dfcff0f02 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -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 diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 58c77254a23b3..a26f26ed5a17e 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -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 diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index f4124a32bef89..7f4013a8607f0 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -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 diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index 4304d9dcfb6c6..c26cd0d2c6d52 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -24,6 +24,10 @@ #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 diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index d2b1282559442..d2a8b2941fc26 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -22,6 +22,7 @@ #include "cifs_unicode.h" #include "cifs_uniupr.h" #include "cifspdu.h" +#include "cifsglob.h" #include "cifs_debug.h" /* diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index e7d63737e6511..08781a4698b4c 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -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; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index c262d8874ce97..700570522b232 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -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; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 006eb33bff5ff..7a042041a16ad 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -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 */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index b2233ac05bd27..e0ff9b56cc4ca 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -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 @@ -24,8 +24,14 @@ #include +#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; /******************************************************************************/ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 310ea2f0e0bfd..ff78cf7b0d1b9 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -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); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f00369277c061..da3154fa9c8a6 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -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;iDialectsArray+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; } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index bae1479318d10..7ffb8f244f6a1 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -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 diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 57bdf7f734bf7..e6ed64e94b7bc 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -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, diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c index 633a938113287..d91a3d44e9e30 100644 --- a/fs/cifs/fcntl.c +++ b/fs/cifs/fcntl.c @@ -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) { diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 77a9e2f912fde..a609d26680326 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -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; diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index fafd056426e4b..22c937e5884f3 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -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) { diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 6b36c43d38f54..53903a27f7861 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -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 index 0000000000000..34998416eb730 --- /dev/null +++ b/fs/cifs/sess.c @@ -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 + +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 */ diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index 6103bcdfb16d9..f518c5e45035c 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -30,6 +30,7 @@ #include #include "cifs_unicode.h" #include "cifspdu.h" +#include "cifsglob.h" #include "md5.h" #include "cifs_debug.h" #include "cifsencrypt.h" -- cgit 1.2.3-korg