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/cifs/cifspdu.h | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'fs/cifs/cifspdu.h') diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index b2233ac05bd2..e0ff9b56cc4c 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; /******************************************************************************/ -- cgit v1.2.3 From 7c7b25bc8e392aea781324efa771bc191377b876 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 1 Jun 2006 19:20:10 +0000 Subject: [CIFS] Support for setting up SMB sessions to legacy lanman servers part 2 --- fs/cifs/cifs_debug.c | 4 +-- fs/cifs/cifsencrypt.c | 40 +++++++++++++++++++-- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifspdu.h | 3 +- fs/cifs/cifsproto.h | 3 ++ fs/cifs/cifssmb.c | 27 +++++++++++--- fs/cifs/connect.c | 47 +++++++++++++++---------- fs/cifs/netmisc.c | 2 +- fs/cifs/sess.c | 97 ++++++++++++++++++++++++++++----------------------- 9 files changed, 150 insertions(+), 75 deletions(-) (limited to 'fs/cifs/cifspdu.h') diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 7f4013a8607f..4e10e21c54fd 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -508,7 +508,7 @@ cifs_proc_init(void) pde->write_proc = multiuser_mount_write; pde = - create_proc_read_entry("ExtendedSecurity", 0, proc_fs_cifs, + create_proc_read_entry("SecurityFlags", 0, proc_fs_cifs, extended_security_read, NULL); if (pde) pde->write_proc = extended_security_write; @@ -547,7 +547,7 @@ cifs_proc_clean(void) remove_proc_entry("MultiuserMount", proc_fs_cifs); remove_proc_entry("OplockEnabled", proc_fs_cifs); /* remove_proc_entry("NTLMV2Enabled",proc_fs_cifs); */ - remove_proc_entry("ExtendedSecurity",proc_fs_cifs); + remove_proc_entry("SecurityFlags",proc_fs_cifs); /* remove_proc_entry("PacketSigningEnabled",proc_fs_cifs); */ remove_proc_entry("LinuxExtensionsEnabled",proc_fs_cifs); remove_proc_entry("Experimental",proc_fs_cifs); diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 08781a4698b4..e11d8c6bb227 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -26,6 +26,7 @@ #include "md5.h" #include "cifs_unicode.h" #include "cifsproto.h" +#include /* Calculate and return the CIFS signature based on the mac key and the smb pdu */ /* the 16 byte signature must be allocated by the caller */ @@ -35,6 +36,8 @@ extern void mdfour(unsigned char *out, unsigned char *in, int n); extern void E_md4hash(const unsigned char *passwd, unsigned char *p16); +extern void SMBencrypt(unsigned char *passwd, unsigned char *c8, + unsigned char *p24); static int cifs_calculate_signature(const struct smb_hdr * cifs_pdu, const char * key, char * signature) @@ -45,7 +48,7 @@ static int cifs_calculate_signature(const struct smb_hdr * cifs_pdu, return -EINVAL; MD5Init(&context); - MD5Update(&context,key,CIFS_SESSION_KEY_SIZE+16); + MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16); MD5Update(&context,cifs_pdu->Protocol,cifs_pdu->smb_buf_length); MD5Final(signature,&context); return 0; @@ -90,7 +93,7 @@ static int cifs_calc_signature2(const struct kvec * iov, int n_vec, return -EINVAL; MD5Init(&context); - MD5Update(&context,key,CIFS_SESSION_KEY_SIZE+16); + MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16); for(i=0;ipassword, CIFS_ENCPWD_SIZE); + + /* calculate old style session key */ + /* calling toupper is less broken than repeatedly + calling nls_toupper would be since that will never + work for UTF8, but neither handles multibyte code pages + but the only alternative would be converting to UCS-16 (Unicode) + (using a routine something like UniStrupr) then + uppercasing and then converting back from Unicode - which + would only worth doing it if we knew it were utf8. Basically + utf8 and other multibyte codepages each need their own strupper + function since a byte at a time will ont work. */ + + 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); + /* clear password before we return/free memory */ + memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); +} +#endif /* CIFS_WEAK_PW_HASH */ + void CalcNTLMv2_response(const struct cifsSesInfo * ses,char * v2_session_response) { struct HMACMD5Context context; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 7a042041a16a..975e69a2e1c4 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -158,7 +158,7 @@ struct TCP_Server_Info { /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* needed for CIFS PDU signature */ - char mac_signing_key[CIFS_SESSION_KEY_SIZE + 16]; + char mac_signing_key[CIFS_SESS_KEY_SIZE + 16]; }; /* diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index e0ff9b56cc4c..135941738d9d 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -116,7 +116,8 @@ /* * Size of the session key (crypto key encrypted with the password */ -#define CIFS_SESSION_KEY_SIZE (24) +#define CIFS_SESS_KEY_SIZE (24) +#define V2_SESS_KEY_SIZE (86) /* * Maximum user name length diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ff78cf7b0d1b..59b037f14511 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -287,6 +287,9 @@ extern int cifs_verify_signature(struct smb_hdr *, const char * mac_key, extern int cifs_calculate_mac_key(char * key,const char * rn,const char * pass); extern int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *, struct nls_table *); extern void CalcNTLMv2_response(const struct cifsSesInfo *,char * ); +#ifdef CONFIG_CIFS_WEAK_PW_HASH +extern void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key); +#endif /* CIFS_WEAK_PW_HASH */ extern int CIFSSMBCopy(int xid, struct cifsTconInfo *source_tcon, const char *fromName, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index da3154fa9c8a..6b5be6d59f07 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -438,12 +438,19 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) goto neg_err_exit; } else if((pSMBr->hdr.WordCount == 13) && (pSMBr->DialectIndex == LANMAN_PROT)) { +#ifdef CONFIG_CIFS_WEAK_PW_HASH struct lanman_neg_rsp * rsp = (struct lanman_neg_rsp *)pSMBr; - - /* BB Mark ses struct as negotiated lanman level BB */ - server->secType = LANMAN; + if((extended_security & CIFSSEC_MAY_LANMAN) || + (extended_security & CIFSSEC_MAY_PLNTXT)) + server->secType = LANMAN; + else { + cERROR(1, ("mount failed weak security disabled" + " in /proc/fs/cifs/SecurityFlags")); + rc = -EOPNOTSUPP; + goto neg_err_exit; + } server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode); server->maxReq = le16_to_cpu(rsp->MaxMpxCount); server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize), @@ -469,6 +476,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) } cFYI(1,("LANMAN negotiated")); /* BB removeme BB */ +#else /* weak security disabled */ + cERROR(1,("mount failed, cifs module not built with " + "CIFS_WEAK_PW_HASH support")); + rc = -EOPNOTSUPP; +#endif /* WEAK_PW_HASH */ goto neg_err_exit; } else if(pSMBr->hdr.WordCount != 17) { /* unknown wct */ @@ -479,8 +491,13 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) server->secMode = pSMBr->SecurityMode; if((server->secMode & SECMODE_USER) == 0) cFYI(1,("share mode security")); - server->secType = NTLM; /* BB override default for - NTLMv2 or kerberos v5 */ + + if(extended_security & CIFSSEC_MUST_NTLMV2) + server->secType = NTLMv2; + else + server->secType = NTLM; + /* else krb5 ... */ + /* one byte - no need to convert this or EncryptionKeyLen from little endian */ server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 7ffb8f244f6a..e6f3d2fff6c0 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1990,7 +1990,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, static int CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, - char session_key[CIFS_SESSION_KEY_SIZE], + char session_key[CIFS_SESS_KEY_SIZE], const struct nls_table *nls_codepage) { struct smb_hdr *smb_buffer; @@ -2048,15 +2048,15 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); pSMB->req_no_secext.CaseInsensitivePasswordLength = - cpu_to_le16(CIFS_SESSION_KEY_SIZE); + cpu_to_le16(CIFS_SESS_KEY_SIZE); pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(CIFS_SESSION_KEY_SIZE); + cpu_to_le16(CIFS_SESS_KEY_SIZE); bcc_ptr = pByteArea(smb_buffer); - memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE); - bcc_ptr += CIFS_SESSION_KEY_SIZE; - memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE); - bcc_ptr += CIFS_SESSION_KEY_SIZE; + memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE); + bcc_ptr += CIFS_SESS_KEY_SIZE; + memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE); + bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode */ @@ -3004,14 +3004,14 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, SecurityBlob->LmChallengeResponse.Buffer = 0; SecurityBlob->NtChallengeResponse.Length = - cpu_to_le16(CIFS_SESSION_KEY_SIZE); + cpu_to_le16(CIFS_SESS_KEY_SIZE); SecurityBlob->NtChallengeResponse.MaximumLength = - cpu_to_le16(CIFS_SESSION_KEY_SIZE); - memcpy(bcc_ptr, ntlm_session_key, CIFS_SESSION_KEY_SIZE); + cpu_to_le16(CIFS_SESS_KEY_SIZE); + memcpy(bcc_ptr, ntlm_session_key, CIFS_SESS_KEY_SIZE); SecurityBlob->NtChallengeResponse.Buffer = cpu_to_le32(SecurityBlobLength); - SecurityBlobLength += CIFS_SESSION_KEY_SIZE; - bcc_ptr += CIFS_SESSION_KEY_SIZE; + SecurityBlobLength += CIFS_SESS_KEY_SIZE; + bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { if (domain == NULL) { @@ -3350,22 +3350,33 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, bcc_ptr = &pSMB->Password[0]; if((ses->server->secMode) & SECMODE_USER) { pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ + *bcc_ptr = 0; /* password is null byte */ bcc_ptr++; /* skip password */ + /* already aligned so no need to do it below */ } else { - pSMB->PasswordLength = cpu_to_le16(CIFS_SESSION_KEY_SIZE); + pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); /* BB FIXME add code to fail this if NTLMv2 or Kerberos specified as required (when that support is added to the vfs in the future) as only NTLM or the much - weaker LANMAN (which we do not send) is accepted + weaker LANMAN (which we do not send by default) is accepted by Samba (not sure whether other servers allow NTLMv2 password here) */ +#ifdef CONFIG_CIFS_WEAK_PW_HASH + if((extended_security & CIFSSEC_MAY_LANMAN) && + (ses->server->secType == LANMAN)) + calc_lanman_hash(ses, bcc_ptr); + else +#endif /* CIFS_WEAK_PW_HASH */ SMBNTencrypt(ses->password, ses->server->cryptKey, bcc_ptr); - bcc_ptr += CIFS_SESSION_KEY_SIZE; - *bcc_ptr = 0; - bcc_ptr++; /* align */ + bcc_ptr += CIFS_SESS_KEY_SIZE; + if(ses->capabilities & CAP_UNICODE) { + /* must align unicode strings */ + *bcc_ptr = 0; /* null byte password */ + bcc_ptr++; + } } if(ses->server->secMode & @@ -3507,7 +3518,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, struct nls_table * nls_info) { int rc = 0; - char ntlm_session_key[CIFS_SESSION_KEY_SIZE]; + char ntlm_session_key[CIFS_SESS_KEY_SIZE]; int ntlmv2_flag = FALSE; int first_time = 0; diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 5de74d216fdd..a0bcdd6f4a67 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -84,7 +84,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = { static const struct smb_to_posix_error mapping_table_ERRSRV[] = { {ERRerror, -EIO}, - {ERRbadpw, -EPERM}, + {ERRbadpw, -EACCES}, /* was EPERM */ {ERRbadtype, -EREMOTE}, {ERRaccess, -EACCES}, {ERRinvtid, -ENXIO}, diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index fdc248fffa0a..a52aacb3feff 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -28,12 +28,8 @@ #include "cifs_debug.h" #include "ntlmssp.h" #include "nterr.h" -#include #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); @@ -80,7 +76,7 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB) return capabilities; } -void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, +static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, const struct nls_table * nls_cp) { char * bcc_ptr = *pbcc_area; @@ -130,7 +126,7 @@ void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, *pbcc_area = bcc_ptr; } -void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, +static void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, const struct nls_table * nls_cp) { char * bcc_ptr = *pbcc_area; @@ -173,7 +169,7 @@ void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, *pbcc_area = bcc_ptr; } -int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses, +static int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses, const struct nls_table * nls_cp) { int rc = 0; @@ -255,7 +251,7 @@ int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses, return rc; } -int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses, +static int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses, const struct nls_table * nls_cp) { int rc = 0; @@ -317,7 +313,6 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, { int rc = 0; int wct; - int i; struct smb_hdr *smb_buf; char *bcc_ptr; SESSION_SETUP_ANDX *pSMB; @@ -343,7 +338,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, return -EOPNOTSUPP; #endif wct = 10; /* lanman 2 style sessionsetup */ - } else if(type == NTLM) /* NTLMv2 may retry NTLM */ + } else if((type == NTLM) || (type == NTLMv2)) /* NTLMv2 may retry NTLM */ wct = 13; /* old style NTLM sessionsetup */ else /* same size for negotiate or auth, NTLMSSP or extended security */ wct = 12; @@ -360,41 +355,22 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, if(type == LANMAN) { #ifdef CONFIG_CIFS_WEAK_PW_HASH - char lnm_session_key[CIFS_SESSION_KEY_SIZE]; - char password_with_pad[CIFS_ENCPWD_SIZE]; + char lnm_session_key[CIFS_SESS_KEY_SIZE]; /* no capabilities flags in old lanman negotiation */ - pSMB->old_req.PasswordLength = CIFS_SESSION_KEY_SIZE; + pSMB->old_req.PasswordLength = CIFS_SESS_KEY_SIZE; /* BB calculate hash with password */ /* and copy into bcc */ - memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); - strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE); - - /* calculate old style session key */ - /* toupper may be less broken then repeatedly calling - nls_toupper would be, but neither handles multibyte code pages - but the only alternative would be converting to UCS-16 (Unicode) - uppercasing and converting back which is only worth doing if - we knew it were utf8. utf8 code page needs its own - toupper and tolower and strnicmp functions */ - - for(i = 0; i< CIFS_ENCPWD_SIZE; i++) { - password_with_pad[i] = toupper(password_with_pad[i]); - } - - SMBencrypt(password_with_pad, ses->server->cryptKey, - lnm_session_key); + calc_lanman_hash(ses, lnm_session_key); #ifdef CONFIG_CIFS_DEBUG2 cifs_dump_mem("cryptkey: ",ses->server->cryptKey, - CIFS_SESSION_KEY_SIZE); + CIFS_SESS_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; + memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE); + bcc_ptr += CIFS_SESS_KEY_SIZE; /* can not sign if LANMAN negotiated so no need to calculate signing key? but what if server @@ -406,13 +382,13 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); #endif } else if (type == NTLM) { - char ntlm_session_key[CIFS_SESSION_KEY_SIZE]; + char ntlm_session_key[CIFS_SESS_KEY_SIZE]; pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); pSMB->req_no_secext.CaseInsensitivePasswordLength = - cpu_to_le16(CIFS_SESSION_KEY_SIZE); + cpu_to_le16(CIFS_SESS_KEY_SIZE); pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(CIFS_SESSION_KEY_SIZE); + cpu_to_le16(CIFS_SESS_KEY_SIZE); /* calculate session key */ SMBNTencrypt(ses->password, ses->server->cryptKey, @@ -420,15 +396,48 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, if(first_time) /* should this be moved into common code with similar ntlmv2 path? */ - cifs_calculate_mac_key( - ses->server->mac_signing_key, + cifs_calculate_mac_key( ses->server->mac_signing_key, ntlm_session_key, ses->password); /* copy session key */ - memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESSION_KEY_SIZE); - bcc_ptr += CIFS_SESSION_KEY_SIZE; - memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESSION_KEY_SIZE); - bcc_ptr += CIFS_SESSION_KEY_SIZE; + memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE); + bcc_ptr += CIFS_SESS_KEY_SIZE; + memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE); + bcc_ptr += CIFS_SESS_KEY_SIZE; + if(ses->capabilities & CAP_UNICODE) + unicode_ssetup_strings(&bcc_ptr, ses, nls_cp); + else + ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); + } else if (type == NTLMv2) { + char * v2_sess_key = kmalloc(V2_SESS_KEY_SIZE, GFP_KERNEL); + + if(v2_sess_key == NULL) { + cifs_small_buf_release(smb_buf); + return -ENOMEM; + } + + pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); + + /* LM2 password would be here if we supported it */ + pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; + /* cpu_to_le16(LM2_SESS_KEY_SIZE); */ + + pSMB->req_no_secext.CaseSensitivePasswordLength = + cpu_to_le16(V2_SESS_KEY_SIZE); + + /* calculate session key */ + CalcNTLMv2_response(ses, v2_sess_key); + if(first_time) /* should this be moved into common code + with similar ntlmv2 path? */ + /* cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key, + response BB FIXME, v2_sess_key); */ + + /* copy session key */ + + /* memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE); + bcc_ptr += LM2_SESS_KEY_SIZE; */ + memcpy(bcc_ptr, (char *)v2_sess_key, V2_SESS_KEY_SIZE); + bcc_ptr += V2_SESS_KEY_SIZE; if(ses->capabilities & CAP_UNICODE) unicode_ssetup_strings(&bcc_ptr, ses, nls_cp); else -- cgit v1.2.3 From 254e55ed03e2e8d23089b4a468eec2fd2e1ead9b Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 4 Jun 2006 05:53:15 +0000 Subject: CIFS] Support for older servers which require plaintext passwords - part 2 Signed-off-by: Steve French --- fs/cifs/README | 32 ++++-- fs/cifs/cifsglob.h | 17 ++-- fs/cifs/cifspdu.h | 4 + fs/cifs/cifssmb.c | 279 +++++++++++++++++++++++++++-------------------------- fs/cifs/sess.c | 3 +- 5 files changed, 184 insertions(+), 151 deletions(-) (limited to 'fs/cifs/cifspdu.h') diff --git a/fs/cifs/README b/fs/cifs/README index 0355003f4f0a..a68f8e3db2d2 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -485,14 +485,34 @@ PacketSigningEnabled If set to one, cifs packet signing is enabled it. If set to two, cifs packet signing is required even if the server considers packet signing optional. (default 1) +SecurityFlags Flags which control security negotiation and + also packet signing. Authentication (may/must) + flags (e.g. for NTLM and/or NTLMv2) may be combined with + the signing flags. Specifying two different password + hashing mechanisms (as "must use") on the other hand + does not make much sense. Default flags are + 0x07007 + (NTLM, NTLMv2 and packet signing allowed). Maximum + allowable flags if you want to allow mounts to servers + using weaker password hashes is 0x37037 (lanman, + plaintext, ntlm, ntlmv2, signing allowed): + + may use packet signing 0x00001 + must use packet signing 0x01001 + may use NTLM (most common password hash) 0x00002 + must use NTLM 0x02002 + may use NTLMv2 0x00004 + must use NTLMv2 0x04004 + may use Kerberos security (not implemented yet) 0x00008 + must use Kerberos (not implemented yet) 0x08008 + may use lanman (weak) password hash 0x00010 + must use lanman password hash 0x10010 + may use plaintext passwords 0x00020 + must use plaintext passwords 0x20020 + (reserved for future packet encryption) 0x00040 + cifsFYI If set to one, additional debug information is logged to the system error log. (default 0) -ExtendedSecurity If set to one, SPNEGO session establishment - is allowed which enables more advanced - secure CIFS session establishment (default 0) -NTLMV2Enabled If set to one, more secure password hashes - are used when the server supports them and - when kerberos is not negotiated (default 0) traceSMB If set to one, debug information is logged to the system error log with the start of smb requests and responses (default 0) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 975e69a2e1c4..87453a6bcaf8 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -212,12 +212,12 @@ struct cifsTconInfo { struct list_head openFileList; struct semaphore tconSem; struct cifsSesInfo *ses; /* pointer to session associated with */ - char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource (in ASCII not UTF) */ + char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ char *nativeFileSystem; __u16 tid; /* The 2 byte tree id */ __u16 Flags; /* optional support bits */ enum statusEnum tidStatus; - atomic_t useCount; /* how many mounts (explicit or implicit) to this share */ + atomic_t useCount; /* how many explicit/implicit mounts to share */ #ifdef CONFIG_CIFS_STATS atomic_t num_smbs_sent; atomic_t num_writes; @@ -257,7 +257,7 @@ struct cifsTconInfo { spinlock_t stat_lock; #endif /* CONFIG_CIFS_STATS */ FILE_SYSTEM_DEVICE_INFO fsDevInfo; - FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if file system name truncated */ + FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ FILE_SYSTEM_UNIX_INFO fsUnixInfo; unsigned retry:1; unsigned nocase:1; @@ -308,7 +308,6 @@ struct cifsFileInfo { atomic_t wrtPending; /* handle in use - defer close */ struct semaphore fh_sem; /* prevents reopen race after dead ses*/ char * search_resume_name; /* BB removeme BB */ - unsigned int resume_name_length; /* BB removeme - field renamed and moved BB */ struct cifs_search_info srch_inf; }; @@ -523,16 +522,16 @@ GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ GLOBAL_EXTERN struct list_head GlobalOplock_Q; GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; /* Outstanding dir notify requests */ -GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q; /* Dir notify response queue */ +GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q;/* DirNotify response queue */ /* * Global transaction id (XID) information */ GLOBAL_EXTERN unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ -GLOBAL_EXTERN unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ +GLOBAL_EXTERN unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ GLOBAL_EXTERN unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ -GLOBAL_EXTERN spinlock_t GlobalMid_Lock; /* protects above and list operations */ - /* on midQ entries */ +GLOBAL_EXTERN spinlock_t GlobalMid_Lock; /* protects above & list operations */ + /* on midQ entries */ GLOBAL_EXTERN char Local_System_Name[15]; /* @@ -554,7 +553,7 @@ GLOBAL_EXTERN atomic_t smBufAllocCount; GLOBAL_EXTERN atomic_t midCount; /* Misc globals */ -GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions +GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions to be established on existing mount if we have the uid/password or Kerberos credential or equivalent for current user */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 135941738d9d..e714803a52dc 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -426,6 +426,10 @@ typedef struct lanman_neg_rsp { unsigned char EncryptionKey[1]; } __attribute__((packed)) LANMAN_NEG_RSP; +#define READ_RAW_ENABLE 1 +#define WRITE_RAW_ENABLE 2 +#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) + typedef struct negotiate_rsp { struct smb_hdr hdr; /* wct = 17 */ __le16 DialectIndex; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 77cca3809467..0442c3b36799 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -411,8 +411,8 @@ 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;*/ + if((extended_security & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) + pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; count = 0; for(i=0;iDialectIndex)); - /* 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 */ + if (rc != 0) + goto neg_err_exit; + + 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 + small wct just comes when dialect index is -1 indicating we + could not negotiate a common dialect */ + rc = -EOPNOTSUPP; + goto neg_err_exit; +#ifdef CONFIG_CIFS_WEAK_PW_HASH + } else if((pSMBr->hdr.WordCount == 13) + && (pSMBr->DialectIndex == LANMAN_PROT)) { + struct lanman_neg_rsp * rsp = (struct lanman_neg_rsp *)pSMBr; + + if((extended_security & CIFSSEC_MAY_LANMAN) || + (extended_security & CIFSSEC_MAY_PLNTXT)) + server->secType = LANMAN; + else { + cERROR(1, ("mount failed weak security disabled" + " in /proc/fs/cifs/SecurityFlags")); rc = -EOPNOTSUPP; goto neg_err_exit; -#ifdef CONFIG_CIFS_WEAK_PW_HASH - } else if((pSMBr->hdr.WordCount == 13) - && (pSMBr->DialectIndex == LANMAN_PROT)) { - struct lanman_neg_rsp * rsp = - (struct lanman_neg_rsp *)pSMBr; - - if((extended_security & CIFSSEC_MAY_LANMAN) || - (extended_security & CIFSSEC_MAY_PLNTXT)) - server->secType = LANMAN; - else { - cERROR(1, ("mount failed weak security disabled" - " in /proc/fs/cifs/SecurityFlags")); - rc = -EOPNOTSUPP; - goto neg_err_exit; - } - server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode); - server->maxReq = le16_to_cpu(rsp->MaxMpxCount); - server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize), + } + 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); + GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey); + /* even though we do not use raw we might as well set this + accurately, in case we ever find a need for it */ + if((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { + server->maxRw = 0xFF00; + server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; + } else { + server->maxRw = 0;/* we do not need to use raw anyway */ + server->capabilities = CAP_MPX_MODE; + } + server->timeZone = le16_to_cpu(rsp->ServerTimeZone); - /* 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 */ + /* BB 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; - } + if (rsp->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { + memcpy(server->cryptKey, rsp->EncryptionKey, + CIFS_CRYPTO_KEY_SIZE); + } else if (server->secMode & SECMODE_PW_ENCRYPT) { + rc = -EIO; /* need cryptkey unless plain text */ + goto neg_err_exit; + } - cFYI(1,("LANMAN negotiated")); /* BB removeme BB */ + cFYI(1,("LANMAN negotiated")); + /* we will not end up setting signing flags - as no signing + was in LANMAN and server did not return the flags on */ + goto signing_check; #else /* weak security disabled */ - } else if(pSMBr->hdr.WordCount == 13) { - cERROR(1,("mount failed, cifs module not built " - "with CIFS_WEAK_PW_HASH support")); + } else if(pSMBr->hdr.WordCount == 13) { + cERROR(1,("mount failed, cifs module not built " + "with CIFS_WEAK_PW_HASH support")); rc = -EOPNOTSUPP; #endif /* WEAK_PW_HASH */ - 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")); + goto neg_err_exit; + } else if(pSMBr->hdr.WordCount != 17) { + /* unknown wct */ + rc = -EOPNOTSUPP; + goto neg_err_exit; + } + /* else wct == 17 NTLM */ + server->secMode = pSMBr->SecurityMode; + if((server->secMode & SECMODE_USER) == 0) + cFYI(1,("share mode security")); - if((server->secMode & SECMODE_PW_ENCRYPT) == 0) + if((server->secMode & SECMODE_PW_ENCRYPT) == 0) #ifdef CONFIG_CIFS_WEAK_PW_HASH - if ((extended_security & CIFSSEC_MAY_PLNTXT) == 0) + if ((extended_security & CIFSSEC_MAY_PLNTXT) == 0) #endif /* CIFS_WEAK_PW_HASH */ - cERROR(1,("Server requests plain text password" - " but client support disabled")); + cERROR(1,("Server requests plain text password" + " but client support disabled")); - if(extended_security & CIFSSEC_MUST_NTLMV2) - server->secType = NTLMv2; - else - server->secType = NTLM; - /* else krb5 ... */ - - /* one byte - no need to convert this or EncryptionKeyLen - from little endian */ - server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount); - /* probably no need to store and check maxvcs */ - server->maxBuf = - min(le32_to_cpu(pSMBr->MaxBufferSize), + if(extended_security & CIFSSEC_MUST_NTLMV2) + server->secType = NTLMv2; + else + server->secType = NTLM; + /* else krb5 ... */ + + /* one byte, so no need to convert this or EncryptionKeyLen from + little endian */ + server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount); + /* probably no need to store and check maxvcs */ + server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize), (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); - server->maxRw = le32_to_cpu(pSMBr->MaxRawSize); - cFYI(0, ("Max buf = %d", ses->server->maxBuf)); - GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); - server->capabilities = le32_to_cpu(pSMBr->Capabilities); - server->timeZone = le16_to_cpu(pSMBr->ServerTimeZone); - /* BB with UTC do we ever need to be using srvr timezone? */ - if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { - memcpy(server->cryptKey, pSMBr->u.EncryptionKey, - CIFS_CRYPTO_KEY_SIZE); - } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) - && (pSMBr->EncryptionKeyLength == 0)) { - /* decode security blob */ - } else - rc = -EIO; + server->maxRw = le32_to_cpu(pSMBr->MaxRawSize); + cFYI(0, ("Max buf = %d", ses->server->maxBuf)); + GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); + server->capabilities = le32_to_cpu(pSMBr->Capabilities); + server->timeZone = le16_to_cpu(pSMBr->ServerTimeZone); + if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { + memcpy(server->cryptKey, pSMBr->u.EncryptionKey, + CIFS_CRYPTO_KEY_SIZE); + } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) + && (pSMBr->EncryptionKeyLength == 0)) { + /* decode security blob */ + } else if (server->secMode & SECMODE_PW_ENCRYPT) { + rc = -EIO; /* no crypt key only if plain text pwd */ + goto neg_err_exit; + } - /* BB might be helpful to save off the domain of server here */ + /* BB might be helpful to save off the domain of server here */ - if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && - (server->capabilities & CAP_EXTENDED_SECURITY)) { - count = pSMBr->ByteCount; - if (count < 16) - rc = -EIO; - else if (count == 16) { - server->secType = RawNTLMSSP; - if (server->socketUseCount.counter > 1) { - if (memcmp - (server->server_GUID, - pSMBr->u.extended_response. - GUID, 16) != 0) { - cFYI(1, ("server UID changed")); - memcpy(server-> - server_GUID, - pSMBr->u. - extended_response. - GUID, 16); - } - } else + if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && + (server->capabilities & CAP_EXTENDED_SECURITY)) { + count = pSMBr->ByteCount; + if (count < 16) + rc = -EIO; + else if (count == 16) { + server->secType = RawNTLMSSP; + if (server->socketUseCount.counter > 1) { + if (memcmp(server->server_GUID, + pSMBr->u.extended_response. + GUID, 16) != 0) { + cFYI(1, ("server UID changed")); memcpy(server->server_GUID, - pSMBr->u.extended_response. - GUID, 16); - } else { - rc = decode_negTokenInit(pSMBr->u. - extended_response. - SecurityBlob, - count - 16, - &server->secType); - if(rc == 1) { - /* BB Need to fill struct for sessetup here */ - rc = -EOPNOTSUPP; - } else { - rc = -EINVAL; + pSMBr->u.extended_response.GUID, + 16); } + } else + memcpy(server->server_GUID, + pSMBr->u.extended_response.GUID, 16); + } else { + rc = decode_negTokenInit(pSMBr->u.extended_response. + SecurityBlob, + count - 16, + &server->secType); + if(rc == 1) { + /* BB Need to fill struct for sessetup here */ + rc = -EOPNOTSUPP; + } else { + rc = -EINVAL; } - } else - server->capabilities &= ~CAP_EXTENDED_SECURITY; - if(sign_CIFS_PDUs == FALSE) { - if(server->secMode & SECMODE_SIGN_REQUIRED) - cERROR(1, - ("Server requires /proc/fs/cifs/PacketSigningEnabled")); - server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } else if(sign_CIFS_PDUs == 1) { - if((server->secMode & SECMODE_SIGN_REQUIRED) == 0) - server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); } - + } else + server->capabilities &= ~CAP_EXTENDED_SECURITY; + +signing_check: + if(sign_CIFS_PDUs == FALSE) { + if(server->secMode & SECMODE_SIGN_REQUIRED) + cERROR(1,("Server requires " + "/proc/fs/cifs/PacketSigningEnabled to be on")); + server->secMode &= + ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); + } else if(sign_CIFS_PDUs == 1) { + if((server->secMode & SECMODE_SIGN_REQUIRED) == 0) + server->secMode &= + ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); + } else if(sign_CIFS_PDUs == 2) { + if((server->secMode & + (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) { + cERROR(1,("signing required but server lacks support")); + } } neg_err_exit: cifs_buf_release(pSMB); + + cFYI(1,("negprot rc %d",rc)); return rc; } diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index a52aacb3feff..76a09f5f804f 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -323,7 +323,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, enum securityEnum type; __u16 action; int bytes_remaining; - + + cFYI(1,("new sess setup")); if(ses == NULL) return -EINVAL; -- cgit v1.2.3 From f64b23ae4aef9f69d71ea41529a188acd5ab4930 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 5 Jun 2006 05:27:37 +0000 Subject: [CIFS] NTLMv2 support part 2 Still need to fill in response structure and check that hash works Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 2 ++ fs/cifs/cifspdu.h | 27 ++++++++++++++++++++++++--- fs/cifs/sess.c | 13 +++++++++---- 3 files changed, 35 insertions(+), 7 deletions(-) (limited to 'fs/cifs/cifspdu.h') diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 3ae964bbfdc3..24ab770f60dc 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -306,6 +306,8 @@ void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key) void CalcNTLMv2_response(const struct cifsSesInfo * ses,char * v2_session_response) { + /* BB FIXME - update struct ntlmv2_response and change calling convention + of this function */ struct HMACMD5Context context; memcpy(v2_session_response + 8, ses->server->cryptKey,8); /* gen_blob(v2_session_response + 16); */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index e714803a52dc..503b7e32ede3 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -117,7 +117,6 @@ * Size of the session key (crypto key encrypted with the password */ #define CIFS_SESS_KEY_SIZE (24) -#define V2_SESS_KEY_SIZE (86) /* * Maximum user name length @@ -539,7 +538,7 @@ typedef union smb_com_session_setup_andx { /* unsigned char * NativeOS; */ /* unsigned char * NativeLanMan; */ /* unsigned char * PrimaryDomain; */ - } __attribute__((packed)) resp; /* NTLM response format (with or without extended security */ + } __attribute__((packed)) resp; /* NTLM response with or without extended sec*/ struct { /* request format */ struct smb_hdr hdr; /* wct = 10 */ @@ -573,6 +572,26 @@ typedef union smb_com_session_setup_andx { } __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */ } __attribute__((packed)) SESSION_SETUP_ANDX; +/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */ + +struct ntlmssp2_name { + __le16 type; + __le16 length; +/* char name[length]; */ +} __attribute__((packed)); + +struct ntlmv2_resp { + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + __le32 blob_sign; + __u32 reserved; + __le64 time; + __u64 client_chal; /* random */ + __u32 reserved2; + struct ntlmssp2_name names[1]; + /* array of name entries could follow ending in minimum 4 byte struct */ +} __attribute__((packed)); + + #define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux" /* Capabilities bits (for NTLM SessSetup request) */ @@ -603,7 +622,9 @@ typedef struct smb_com_tconx_req { } __attribute__((packed)) TCONX_REQ; typedef struct smb_com_tconx_rsp { - struct smb_hdr hdr; /* wct = 3 *//* note that Win2000 has sent wct=7 in some cases on responses. Four unspecified words followed OptionalSupport */ + struct smb_hdr hdr; /* wct = 3 note that Win2000 has sent wct = 7 + in some cases on responses. Four unspecified + words followed OptionalSupport */ __u8 AndXCommand; __u8 AndXReserved; __le16 AndXOffset; diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 1fe9461c6dca..9ce628df29b4 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -411,7 +411,11 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, else ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); } else if (type == NTLMv2) { - char * v2_sess_key = kmalloc(V2_SESS_KEY_SIZE, GFP_KERNEL); + char * v2_sess_key = kmalloc(sizeof(struct ntlmv2_resp), + GFP_KERNEL); + + /* BB FIXME change all users of v2_sess_key to + struct ntlmv2_resp */ if(v2_sess_key == NULL) { cifs_small_buf_release(smb_buf); @@ -425,7 +429,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, /* cpu_to_le16(LM2_SESS_KEY_SIZE); */ pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(V2_SESS_KEY_SIZE); + cpu_to_le16(sizeof(struct ntlmv2_resp)); /* calculate session key */ CalcNTLMv2_response(ses, v2_sess_key); @@ -438,8 +442,9 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, /* memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE); bcc_ptr += LM2_SESS_KEY_SIZE; */ - memcpy(bcc_ptr, (char *)v2_sess_key, V2_SESS_KEY_SIZE); - bcc_ptr += V2_SESS_KEY_SIZE; + memcpy(bcc_ptr, (char *)v2_sess_key, sizeof(struct ntlmv2_resp)); + bcc_ptr += sizeof(struct ntlmv2_resp); + kfree(v2_sess_key); if(ses->capabilities & CAP_UNICODE) unicode_ssetup_strings(&bcc_ptr, ses, nls_cp); else -- cgit v1.2.3 From 6d027cfdb19c26df3151a519ed55acfe2c4cb7c3 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 5 Jun 2006 16:26:05 +0000 Subject: [CIFS] NTLMv2 support part 3 Response struct filled in exacty for 16 byte hash which we need to check more to make sure it works. Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 17 +++++++++++++++-- fs/cifs/cifspdu.h | 2 +- fs/cifs/cifsproto.h | 1 + fs/cifs/sess.c | 6 +++--- 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'fs/cifs/cifspdu.h') diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 24ab770f60dc..09f94617e534 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -27,6 +27,7 @@ #include "cifs_unicode.h" #include "cifsproto.h" #include +#include /* Calculate and return the CIFS signature based on the mac key and the smb pdu */ /* the 16 byte signature must be allocated by the caller */ @@ -304,10 +305,22 @@ void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key) } #endif /* CIFS_WEAK_PW_HASH */ +void setup_ntlmv2_rsp(const struct cifsSesInfo * ses, char * resp_buf) +{ + struct ntlmv2_resp * buf = (struct ntlmv2_resp *)resp_buf; + + buf->blob_signature = cpu_to_le32(0x00000101); + buf->reserved = 0; + buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); + get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); + buf->reserved2 = 0; + buf->names[0].type = 0; + buf->names[0].length = 0; + /* calculate buf->ntlmv2_hash */ +} + void CalcNTLMv2_response(const struct cifsSesInfo * ses,char * v2_session_response) { - /* BB FIXME - update struct ntlmv2_response and change calling convention - of this function */ struct HMACMD5Context context; memcpy(v2_session_response + 8, ses->server->cryptKey,8); /* gen_blob(v2_session_response + 16); */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 503b7e32ede3..5250b93d3098 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -582,7 +582,7 @@ struct ntlmssp2_name { struct ntlmv2_resp { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; - __le32 blob_sign; + __le32 blob_signature; __u32 reserved; __le64 time; __u64 client_chal; /* random */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 59b037f14511..824afb937a6f 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -287,6 +287,7 @@ extern int cifs_verify_signature(struct smb_hdr *, const char * mac_key, extern int cifs_calculate_mac_key(char * key,const char * rn,const char * pass); extern int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *, struct nls_table *); extern void CalcNTLMv2_response(const struct cifsSesInfo *,char * ); +extern void setup_ntlmv2_rsp(const struct cifsSesInfo *, char *); #ifdef CONFIG_CIFS_WEAK_PW_HASH extern void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key); #endif /* CIFS_WEAK_PW_HASH */ diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 9ce628df29b4..c6fd01f55e90 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -411,8 +411,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, else ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); } else if (type == NTLMv2) { - char * v2_sess_key = kmalloc(sizeof(struct ntlmv2_resp), - GFP_KERNEL); + char * v2_sess_key = + kmalloc(sizeof(struct ntlmv2_resp), GFP_KERNEL); /* BB FIXME change all users of v2_sess_key to struct ntlmv2_resp */ @@ -432,7 +432,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, cpu_to_le16(sizeof(struct ntlmv2_resp)); /* calculate session key */ - CalcNTLMv2_response(ses, v2_sess_key); + setup_ntlmv2_rsp(ses, v2_sess_key); if(first_time) /* should this be moved into common code with similar ntlmv2 path? */ /* cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key, -- cgit v1.2.3 From 5bafd76593f060540acbea3b61e3087e009aa269 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 7 Jun 2006 00:18:43 +0000 Subject: [CIFS] Add support for readdir to legacy servers Fixes oops to OS/2 on ls and removes redundant NTCreateX calls to servers which do not support NT SMBs. Key operations to OS/2 work. Signed-off-by: Steve French --- fs/cifs/CHANGES | 4 ++ fs/cifs/cifspdu.h | 29 +++++++++-- fs/cifs/dir.c | 11 +++- fs/cifs/file.c | 9 +++- fs/cifs/readdir.c | 150 +++++++++++++++++++++++++++++++++++++++--------------- 5 files changed, 153 insertions(+), 50 deletions(-) (limited to 'fs/cifs/cifspdu.h') diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index b878dfcff0f0..7e0058bc3dd9 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -2,6 +2,10 @@ 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. +Fix oops on ls to OS/2 servers. Add support for level 1 FindFirst +so we can do search (ls etc.) to OS/2. Do not send NTCreateX +or recent levels of FindFirst unless server says it supports NT SMBs +(instead use legacy equivalents from LANMAN dialect). Version 1.43 ------------ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 5250b93d3098..86239023545b 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -1374,6 +1374,9 @@ struct smb_t2_rsp { #define SMB_FILE_MAXIMUM_INFO 0x40d /* Find File infolevels */ +#define SMB_FIND_FILE_INFO_STANDARD 0x001 +#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002 +#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003 #define SMB_FIND_FILE_DIRECTORY_INFO 0x101 #define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 #define SMB_FIND_FILE_NAMES_INFO 0x103 @@ -1998,7 +2001,8 @@ typedef struct { struct file_allocation_info { __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ -} __attribute__((packed)); /* size used on disk, level 0x103 for set, 0x105 for query */ +} __attribute__((packed)); /* size used on disk, for level 0x103 for set, + 0x105 for query */ struct file_end_of_file_info { __le64 FileSize; /* offset to end of file */ @@ -2105,7 +2109,7 @@ typedef struct { __le32 ExtFileAttributes; __le32 FileNameLength; char FileName[1]; -} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF response data area */ +} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */ typedef struct { __le32 NextEntryOffset; @@ -2120,7 +2124,7 @@ typedef struct { __le32 FileNameLength; __le32 EaSize; /* length of the xattrs */ char FileName[1]; -} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 FF response data area */ +} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */ typedef struct { __le32 NextEntryOffset; @@ -2137,7 +2141,7 @@ typedef struct { __le32 Reserved; __u64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ char FileName[1]; -} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF response data area */ +} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */ typedef struct { __le32 NextEntryOffset; @@ -2155,7 +2159,22 @@ typedef struct { __u8 Reserved; __u8 ShortName[12]; char FileName[1]; -} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FF response data area */ +} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */ + +typedef struct { + __u32 ResumeKey; + __le16 CreationDate; /* SMB Date */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __u8 FileNameLength; + char FileName[1]; +} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */ struct win_dev { diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index e6ed64e94b7b..ba4cbe9b0684 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -178,11 +178,14 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, FreeXid(xid); return -ENOMEM; } - - rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, + if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) + rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &fileHandle, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + else + rc = -EIO; /* no NT SMB support fall into legacy open below */ + if(rc == -EIO) { /* old server, retry the open legacy style */ rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, @@ -369,6 +372,10 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + /* BB FIXME - add handling for backlevel servers + which need legacy open and check for all + calls to SMBOpen for fallback to + SMBLeagcyOpen */ if(!rc) { /* BB Do not bother to decode buf since no local inode yet to put timestamps in, diff --git a/fs/cifs/file.c b/fs/cifs/file.c index d62e29fe91f2..fafdcdffba62 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -260,10 +260,15 @@ int cifs_open(struct inode *inode, struct file *file) rc = -ENOMEM; goto out; } - rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, - CREATE_NOT_DIR, &netfid, &oplock, buf, + + if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) + rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, + desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + else + rc = -EIO; /* no NT SMB support fall into legacy open below */ + if (rc == -EIO) { /* Old server, try legacy style OpenX */ rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index e3e762d774df..03bbcb377913 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -109,32 +109,52 @@ static int construct_dentry(struct qstr *qstring, struct file *file, return rc; } -static void fill_in_inode(struct inode *tmp_inode, - FILE_DIRECTORY_INFO *pfindData, int *pobject_type, int isNewInode) +static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, + char * buf, int *pobject_type, int isNewInode) { loff_t local_size; struct timespec local_mtime; struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode); struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb); - __u32 attr = le32_to_cpu(pfindData->ExtFileAttributes); - __u64 allocation_size = le64_to_cpu(pfindData->AllocationSize); - __u64 end_of_file = le64_to_cpu(pfindData->EndOfFile); - - cifsInfo->cifsAttrs = attr; - cifsInfo->time = jiffies; + __u32 attr; + __u64 allocation_size; + __u64 end_of_file; /* save mtime and size */ local_mtime = tmp_inode->i_mtime; local_size = tmp_inode->i_size; + if(new_buf_type) { + FILE_DIRECTORY_INFO *pfindData = (FILE_DIRECTORY_INFO *)buf; + + attr = le32_to_cpu(pfindData->ExtFileAttributes); + allocation_size = le64_to_cpu(pfindData->AllocationSize); + end_of_file = le64_to_cpu(pfindData->EndOfFile); + tmp_inode->i_atime = + cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime)); + tmp_inode->i_mtime = + cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); + tmp_inode->i_ctime = + cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); + } else { /* legacy, OS2 and DOS style */ + FIND_FILE_STANDARD_INFO * pfindData = + (FIND_FILE_STANDARD_INFO *)buf; + + attr = le16_to_cpu(pfindData->Attributes); + allocation_size = le32_to_cpu(pfindData->AllocationSize); + end_of_file = le32_to_cpu(pfindData->DataSize); + tmp_inode->i_atime = CURRENT_TIME; + /* tmp_inode->i_mtime = BB FIXME - add dos time handling + tmp_inode->i_ctime = 0; BB FIXME */ + + } + /* Linux can not store file creation time unfortunately so ignore it */ - tmp_inode->i_atime = - cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime)); - tmp_inode->i_mtime = - cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); - tmp_inode->i_ctime = - cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); + + cifsInfo->cifsAttrs = attr; + cifsInfo->time = jiffies; + /* treat dos attribute of read-only as read-only mode bit e.g. 555? */ /* 2767 perms - indicate mandatory locking */ /* BB fill in uid and gid here? with help from winbind? @@ -420,7 +440,10 @@ static int initiate_cifs_search(const int xid, struct file *file) ffirst_retry: /* test for Unix extensions */ if (pTcon->ses->capabilities & CAP_UNIX) { - cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; + cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; + } else if ((pTcon->ses->capabilities & + (CAP_NT_SMBS | CAP_NT_FIND)) == 0) { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; } else /* not srvinos - BB fixme add check for backlevel? */ { @@ -456,12 +479,19 @@ static int cifs_unicode_bytelen(char *str) return len << 1; } -static char *nxt_dir_entry(char *old_entry, char *end_of_smb) +static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level) { char * new_entry; FILE_DIRECTORY_INFO * pDirInfo = (FILE_DIRECTORY_INFO *)old_entry; - new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset); + if(level == SMB_FIND_FILE_INFO_STANDARD) { + FIND_FILE_STANDARD_INFO * pfData; + pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo; + + new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) + + pfData->FileNameLength; + } else + new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset); cFYI(1,("new entry %p old entry %p",new_entry,old_entry)); /* validate that new_entry is not past end of SMB */ if(new_entry >= end_of_smb) { @@ -469,7 +499,10 @@ static char *nxt_dir_entry(char *old_entry, char *end_of_smb) ("search entry %p began after end of SMB %p old entry %p", new_entry, end_of_smb, old_entry)); return NULL; - } else if (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb) { + } else if(((level == SMB_FIND_FILE_INFO_STANDARD) && + (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) || + ((level != SMB_FIND_FILE_INFO_STANDARD) && + (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) { cERROR(1,("search entry %p extends after end of SMB %p", new_entry, end_of_smb)); return NULL; @@ -487,7 +520,7 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) char * filename = NULL; int len = 0; - if(cfile->srch_inf.info_level == 0x202) { + if(cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) { FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry; filename = &pFindData->FileName[0]; if(cfile->srch_inf.unicode) { @@ -496,26 +529,34 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) /* BB should we make this strnlen of PATH_MAX? */ len = strnlen(filename, 5); } - } else if(cfile->srch_inf.info_level == 0x101) { + } else if(cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) { FILE_DIRECTORY_INFO * pFindData = (FILE_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); - } else if(cfile->srch_inf.info_level == 0x102) { + } else if(cfile->srch_inf.info_level == + SMB_FIND_FILE_FULL_DIRECTORY_INFO) { FILE_FULL_DIRECTORY_INFO * pFindData = (FILE_FULL_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); - } else if(cfile->srch_inf.info_level == 0x105) { + } else if(cfile->srch_inf.info_level == + SMB_FIND_FILE_ID_FULL_DIR_INFO) { SEARCH_ID_FULL_DIR_INFO * pFindData = (SEARCH_ID_FULL_DIR_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); - } else if(cfile->srch_inf.info_level == 0x104) { + } else if(cfile->srch_inf.info_level == + SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { FILE_BOTH_DIRECTORY_INFO * pFindData = (FILE_BOTH_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); + } else if(cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) { + FIND_FILE_STANDARD_INFO * pFindData = + (FIND_FILE_STANDARD_INFO *)current_entry; + filename = &pFindData->FileName[0]; + len = le32_to_cpu(pFindData->FileNameLength); } else { cFYI(1,("Unknown findfirst level %d",cfile->srch_inf.info_level)); } @@ -651,10 +692,12 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry - cifsFile->srch_inf.entries_in_buffer; pos_in_buf = index_to_find - first_entry_in_buffer; - cFYI(1,("found entry - pos_in_buf %d",pos_in_buf)); + cFYI(1,("found entry - pos_in_buf %d",pos_in_buf)); + for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) { /* go entry by entry figuring out which is first */ - current_entry = nxt_dir_entry(current_entry,end_of_smb); + current_entry = nxt_dir_entry(current_entry,end_of_smb, + cifsFile->srch_inf.info_level); } if((current_entry == NULL) && (i < pos_in_buf)) { /* BB fixme - check if we should flag this error */ @@ -681,7 +724,7 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, /* inode num, inode type and filename returned */ static int cifs_get_name_from_search_buf(struct qstr *pqst, char *current_entry, __u16 level, unsigned int unicode, - struct cifs_sb_info * cifs_sb, ino_t *pinum) + struct cifs_sb_info * cifs_sb, int max_len, ino_t *pinum) { int rc = 0; unsigned int len = 0; @@ -725,10 +768,22 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst, (FILE_BOTH_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); + } else if(level == SMB_FIND_FILE_INFO_STANDARD) { + FIND_FILE_STANDARD_INFO * pFindData = + (FIND_FILE_STANDARD_INFO *)current_entry; + filename = &pFindData->FileName[0]; + /* one byte length, no name conversion */ + len = (unsigned int)pFindData->FileNameLength; } else { cFYI(1,("Unknown findfirst level %d",level)); return -EINVAL; } + + if(len > max_len) { + cERROR(1,("bad search response length %d past smb end", len)); + return -EINVAL; + } + if(unicode) { /* BB fixme - test with long names */ /* Note converted filename can be longer than in unicode */ @@ -748,7 +803,7 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst, } static int cifs_filldir(char *pfindEntry, struct file *file, - filldir_t filldir, void *direntry, char *scratch_buf) + filldir_t filldir, void *direntry, char *scratch_buf, int max_len) { int rc = 0; struct qstr qstring; @@ -784,6 +839,7 @@ static int cifs_filldir(char *pfindEntry, struct file *file, rc = cifs_get_name_from_search_buf(&qstring,pfindEntry, pCifsF->srch_inf.info_level, pCifsF->srch_inf.unicode,cifs_sb, + max_len, &inum /* returned */); if(rc) @@ -805,13 +861,16 @@ static int cifs_filldir(char *pfindEntry, struct file *file, /* we pass in rc below, indicating whether it is a new inode, so we can figure out whether to invalidate the inode cached data if the file has changed */ - if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) { + if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) unix_fill_in_inode(tmp_inode, - (FILE_UNIX_INFO *)pfindEntry,&obj_type, rc); - } else { - fill_in_inode(tmp_inode, - (FILE_DIRECTORY_INFO *)pfindEntry,&obj_type, rc); - } + (FILE_UNIX_INFO *)pfindEntry, + &obj_type, rc); + else if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) + fill_in_inode(tmp_inode, 0 /* old level 1 buffer type */, + pfindEntry, &obj_type, rc); + else + fill_in_inode(tmp_inode, 1 /* NT */, pfindEntry, &obj_type, rc); + rc = filldir(direntry,qstring.name,qstring.len,file->f_pos, tmp_inode->i_ino,obj_type); @@ -871,6 +930,12 @@ static int cifs_save_resume_key(const char *current_entry, filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); cifsFile->srch_inf.resume_key = pFindData->FileIndex; + } else if(level == SMB_FIND_FILE_INFO_STANDARD) { + FIND_FILE_STANDARD_INFO * pFindData = + (FIND_FILE_STANDARD_INFO *)current_entry; + filename = &pFindData->FileName[0]; + /* one byte length, no name conversion */ + len = (unsigned int)pFindData->FileNameLength; } else { cFYI(1,("Unknown findfirst level %d",level)); return -EINVAL; @@ -891,6 +956,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) int num_to_fill = 0; char * tmp_buf = NULL; char * end_of_smb; + int max_len; xid = GetXid(); @@ -966,10 +1032,11 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) goto rddir2_exit; } cFYI(1,("loop through %d times filling dir for net buf %p", - num_to_fill,cifsFile->srch_inf.ntwrk_buf_start)); - end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + - smbCalcSize((struct smb_hdr *) - cifsFile->srch_inf.ntwrk_buf_start); + num_to_fill,cifsFile->srch_inf.ntwrk_buf_start)); + max_len = smbCalcSize((struct smb_hdr *) + cifsFile->srch_inf.ntwrk_buf_start); + end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; + /* To be safe - for UCS to UTF-8 with strings loaded with the rare long characters alloc more to account for such multibyte target UTF-8 characters. cifs_unicode.c, @@ -984,8 +1051,8 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) } /* if buggy server returns . and .. late do we want to check for that here? */ - rc = cifs_filldir(current_entry, file, - filldir, direntry,tmp_buf); + rc = cifs_filldir(current_entry, file, + filldir, direntry, tmp_buf, max_len); file->f_pos++; if(file->f_pos == cifsFile->srch_inf.index_of_last_entry) { @@ -994,8 +1061,9 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) cifs_save_resume_key(current_entry,cifsFile); break; } else - current_entry = nxt_dir_entry(current_entry, - end_of_smb); + current_entry = + nxt_dir_entry(current_entry, end_of_smb, + cifsFile->srch_inf.info_level); } kfree(tmp_buf); break; -- cgit v1.2.3