diff options
Diffstat (limited to 'fs/cifs/cifssmb.c')
-rw-r--r-- | fs/cifs/cifssmb.c | 102 |
1 files changed, 87 insertions, 15 deletions
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 075d8fb3d37..098790eb2aa 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -46,6 +46,7 @@ static struct { } protocols[] = { #ifdef CONFIG_CIFS_WEAK_PW_HASH {LANMAN_PROT, "\2LM1.2X002"}, + {LANMAN2_PROT, "\2LANMAN2.1"}, #endif /* weak password hashing for legacy clients */ {CIFS_PROT, "\2NT LM 0.12"}, {POSIX_PROT, "\2POSIX 2"}, @@ -58,6 +59,7 @@ static struct { } protocols[] = { #ifdef CONFIG_CIFS_WEAK_PW_HASH {LANMAN_PROT, "\2LM1.2X002"}, + {LANMAN2_PROT, "\2LANMAN2.1"}, #endif /* weak password hashing for legacy clients */ {CIFS_PROT, "\2NT LM 0.12"}, {BAD_PROT, "\2"} @@ -67,13 +69,13 @@ static struct { /* 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 +#define CIFS_NUM_PROT 4 #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 +#define CIFS_NUM_PROT 3 #else #define CIFS_NUM_PROT 1 #endif /* CONFIG_CIFS_WEAK_PW_HASH */ @@ -397,6 +399,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) struct TCP_Server_Info * server; u16 count; unsigned int secFlags; + u16 dialect; if(ses->server) server = ses->server; @@ -436,9 +439,10 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) if (rc != 0) goto neg_err_exit; - cFYI(1,("Dialect: %d", pSMBr->DialectIndex)); + dialect = le16_to_cpu(pSMBr->DialectIndex); + cFYI(1,("Dialect: %d", dialect)); /* Check wct = 1 error case */ - if((pSMBr->hdr.WordCount < 13) || (pSMBr->DialectIndex == BAD_PROT)) { + if((pSMBr->hdr.WordCount < 13) || (dialect == 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 */ @@ -446,7 +450,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) goto neg_err_exit; #ifdef CONFIG_CIFS_WEAK_PW_HASH } else if((pSMBr->hdr.WordCount == 13) - && (pSMBr->DialectIndex == LANMAN_PROT)) { + && ((dialect == LANMAN_PROT) + || (dialect == LANMAN2_PROT))) { + __s16 tmp; struct lanman_neg_rsp * rsp = (struct lanman_neg_rsp *)pSMBr; if((secFlags & CIFSSEC_MAY_LANMAN) || @@ -472,12 +478,44 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) server->maxRw = 0;/* we do not need to use raw anyway */ server->capabilities = CAP_MPX_MODE; } - server->timeZone = le16_to_cpu(rsp->ServerTimeZone); + tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); + if (tmp == -1) { + /* OS/2 often does not set timezone therefore + * we must use server time to calc time zone. + * Could deviate slightly from the right zone. + * Smallest defined timezone difference is 15 minutes + * (i.e. Nepal). Rounding up/down is done to match + * this requirement. + */ + int val, seconds, remain, result; + struct timespec ts, utc; + utc = CURRENT_TIME; + ts = cnvrtDosUnixTm(le16_to_cpu(rsp->SrvTime.Date), + le16_to_cpu(rsp->SrvTime.Time)); + cFYI(1,("SrvTime: %d sec since 1970 (utc: %d) diff: %d", + (int)ts.tv_sec, (int)utc.tv_sec, + (int)(utc.tv_sec - ts.tv_sec))); + val = (int)(utc.tv_sec - ts.tv_sec); + seconds = val < 0 ? -val : val; + result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; + remain = seconds % MIN_TZ_ADJ; + if(remain >= (MIN_TZ_ADJ / 2)) + result += MIN_TZ_ADJ; + if(val < 0) + result = - result; + server->timeAdj = result; + } else { + server->timeAdj = (int)tmp; + server->timeAdj *= 60; /* also in seconds */ + } + cFYI(1,("server->timeAdj: %d seconds", server->timeAdj)); + /* BB get server time for time conversions and add code to use it and timezone since this is not UTC */ - if (rsp->EncryptionKeyLength == cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { + if (rsp->EncryptionKeyLength == + cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { memcpy(server->cryptKey, rsp->EncryptionKey, CIFS_CRYPTO_KEY_SIZE); } else if (server->secMode & SECMODE_PW_ENCRYPT) { @@ -531,7 +569,8 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) 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); + server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); + server->timeAdj *= 60; if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { memcpy(server->cryptKey, pSMBr->u.EncryptionKey, CIFS_CRYPTO_KEY_SIZE); @@ -1617,7 +1656,7 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) pSMBr = (CLOSE_RSP *)pSMB; /* BB removeme BB */ pSMB->FileID = (__u16) smb_file_id; - pSMB->LastWriteTime = 0; + pSMB->LastWriteTime = 0xFFFFFFFF; pSMB->ByteCount = 0; rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); @@ -2773,9 +2812,11 @@ GetExtAttrOut: /* security id for everyone */ -const struct cifs_sid sid_everyone = {1, 1, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0}}; +const static struct cifs_sid sid_everyone = + {1, 1, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0}}; /* group users */ -const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {32, 545, 0, 0}}; +const static struct cifs_sid sid_user = + {1, 2 , {0, 0, 0, 0, 0, 5}, {32, 545, 0, 0}}; /* Convert CIFS ACL to POSIX form */ static int parse_sec_desc(struct cifs_sid * psec_desc, int acl_len) @@ -2856,7 +2897,6 @@ qsec_out: return rc; } - /* Legacy Query Path Information call for lookup to old servers such as Win9x/WinME */ int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, @@ -2898,7 +2938,16 @@ QInfRetry: if (rc) { cFYI(1, ("Send error in QueryInfo = %d", rc)); } else if (pFinfo) { /* decode response */ + struct timespec ts; + __u32 time = le32_to_cpu(pSMBr->last_write_time); + /* BB FIXME - add time zone adjustment BB */ memset(pFinfo, 0, sizeof(FILE_ALL_INFO)); + ts.tv_nsec = 0; + ts.tv_sec = time; + /* decode time fields */ + pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts)); + pFinfo->LastWriteTime = pFinfo->ChangeTime; + pFinfo->LastAccessTime = 0; pFinfo->AllocationSize = cpu_to_le64(le32_to_cpu(pSMBr->size)); pFinfo->EndOfFile = pFinfo->AllocationSize; @@ -2922,6 +2971,7 @@ int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_ALL_INFO * pFindData, + int legacy /* old style infolevel */, const struct nls_table *nls_codepage, int remap) { /* level 263 SMB_QUERY_FILE_ALL_INFO */ @@ -2970,7 +3020,10 @@ QPathInfoRetry: byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); + if(legacy) + pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); + else + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); @@ -2982,13 +3035,24 @@ QPathInfoRetry: } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || (pSMBr->ByteCount < 40)) + if (rc) /* BB add auto retry on EOPNOTSUPP? */ + rc = -EIO; + else if (!legacy && (pSMBr->ByteCount < 40)) rc = -EIO; /* bad smb */ + else if(legacy && (pSMBr->ByteCount < 24)) + rc = -EIO; /* 24 or 26 expected but we do not read last field */ else if (pFindData){ + int size; __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + if(legacy) /* we do not read the last field, EAsize, fortunately + since it varies by subdialect and on Set vs. Get, is + two bytes or 4 bytes depending but we don't care here */ + size = sizeof(FILE_INFO_STANDARD); + else + size = sizeof(FILE_ALL_INFO); memcpy((char *) pFindData, (char *) &pSMBr->hdr.Protocol + - data_offset, sizeof (FILE_ALL_INFO)); + data_offset, size); } else rc = -ENOMEM; } @@ -3613,6 +3677,14 @@ getDFSRetry: strncpy(pSMB->RequestFileName, searchName, name_len); } + if(ses->server) { + if(ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + } + + pSMB->hdr.Uid = ses->Suid; + params = 2 /* level */ + name_len /*includes null */ ; pSMB->TotalDataCount = 0; pSMB->DataCount = 0; |