diff options
Diffstat (limited to 'fs/cifs/cifssmb.c')
-rw-r--r-- | fs/cifs/cifssmb.c | 179 |
1 files changed, 176 insertions, 3 deletions
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 1620991cd4d2..20300bc9ae77 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -1926,6 +1926,90 @@ querySymLinkRetry: | |||
1926 | return rc; | 1926 | return rc; |
1927 | } | 1927 | } |
1928 | 1928 | ||
1929 | /* Initialize NT TRANSACT SMB into small smb request buffer. | ||
1930 | This assumes that all NT TRANSACTS that we init here have | ||
1931 | total parm and data under about 400 bytes (to fit in small cifs | ||
1932 | buffer size), which is the case so far, it easily fits. NB: | ||
1933 | Setup words themselves and ByteCount | ||
1934 | MaxSetupCount (size of returned setup area) and | ||
1935 | MaxParameterCount (returned parms size) must be set by caller */ | ||
1936 | static int | ||
1937 | smb_init_ntransact(const __u16 sub_command, const int setup_count, | ||
1938 | const int parm_len, struct cifsTconInfo *tcon, | ||
1939 | void ** ret_buf) | ||
1940 | { | ||
1941 | int rc; | ||
1942 | __u32 temp_offset; | ||
1943 | struct smb_com_ntransact_req * pSMB; | ||
1944 | |||
1945 | rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon, | ||
1946 | (void **)&pSMB); | ||
1947 | if (rc) | ||
1948 | return rc; | ||
1949 | *ret_buf = (void *)pSMB; | ||
1950 | pSMB->Reserved = 0; | ||
1951 | pSMB->TotalParameterCount = cpu_to_le32(parm_len); | ||
1952 | pSMB->TotalDataCount = 0; | ||
1953 | pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf - | ||
1954 | MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); | ||
1955 | pSMB->ParameterCount = pSMB->TotalParameterCount; | ||
1956 | pSMB->DataCount = pSMB->TotalDataCount; | ||
1957 | temp_offset = offsetof(struct smb_com_ntransact_req, Parms) + | ||
1958 | (setup_count * 2) - 4 /* for rfc1001 length itself */; | ||
1959 | pSMB->ParameterOffset = cpu_to_le32(temp_offset); | ||
1960 | pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len); | ||
1961 | pSMB->SetupCount = setup_count; /* no need to le convert byte fields */ | ||
1962 | pSMB->SubCommand = cpu_to_le16(sub_command); | ||
1963 | return 0; | ||
1964 | } | ||
1965 | |||
1966 | static int | ||
1967 | validate_ntransact(char * buf, char ** ppparm, char ** ppdata, | ||
1968 | int * pdatalen, int * pparmlen) | ||
1969 | { | ||
1970 | char * end_of_smb; | ||
1971 | __u32 data_count, data_offset, parm_count, parm_offset; | ||
1972 | struct smb_com_ntransact_rsp * pSMBr; | ||
1973 | |||
1974 | if(buf == NULL) | ||
1975 | return -EINVAL; | ||
1976 | |||
1977 | pSMBr = (struct smb_com_ntransact_rsp *)buf; | ||
1978 | |||
1979 | /* ByteCount was converted from little endian in SendReceive */ | ||
1980 | end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount + | ||
1981 | (char *)&pSMBr->ByteCount; | ||
1982 | |||
1983 | |||
1984 | data_offset = le32_to_cpu(pSMBr->DataOffset); | ||
1985 | data_count = le32_to_cpu(pSMBr->DataCount); | ||
1986 | parm_offset = le32_to_cpu(pSMBr->ParameterOffset); | ||
1987 | parm_count = le32_to_cpu(pSMBr->ParameterCount); | ||
1988 | |||
1989 | *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset; | ||
1990 | *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset; | ||
1991 | |||
1992 | /* should we also check that parm and data areas do not overlap? */ | ||
1993 | if(*ppparm > end_of_smb) { | ||
1994 | cFYI(1,("parms start after end of smb")); | ||
1995 | return -EINVAL; | ||
1996 | } else if(parm_count + *ppparm > end_of_smb) { | ||
1997 | cFYI(1,("parm end after end of smb")); | ||
1998 | return -EINVAL; | ||
1999 | } else if(*ppdata > end_of_smb) { | ||
2000 | cFYI(1,("data starts after end of smb")); | ||
2001 | return -EINVAL; | ||
2002 | } else if(data_count + *ppdata > end_of_smb) { | ||
2003 | cFYI(1,("data %p + count %d (%p) ends after end of smb %p start %p", | ||
2004 | *ppdata, data_count, (data_count + *ppdata), end_of_smb, pSMBr)); /* BB FIXME */ | ||
2005 | return -EINVAL; | ||
2006 | } else if(parm_count + data_count > pSMBr->ByteCount) { | ||
2007 | cFYI(1,("parm count and data count larger than SMB")); | ||
2008 | return -EINVAL; | ||
2009 | } | ||
2010 | return 0; | ||
2011 | } | ||
2012 | |||
1929 | int | 2013 | int |
1930 | CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, | 2014 | CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, |
1931 | const unsigned char *searchName, | 2015 | const unsigned char *searchName, |
@@ -1948,7 +2032,8 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, | |||
1948 | pSMB->TotalDataCount = 0; | 2032 | pSMB->TotalDataCount = 0; |
1949 | pSMB->MaxParameterCount = cpu_to_le32(2); | 2033 | pSMB->MaxParameterCount = cpu_to_le32(2); |
1950 | /* BB find exact data count max from sess structure BB */ | 2034 | /* BB find exact data count max from sess structure BB */ |
1951 | pSMB->MaxDataCount = cpu_to_le32(4000); | 2035 | pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf - |
2036 | MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); | ||
1952 | pSMB->MaxSetupCount = 4; | 2037 | pSMB->MaxSetupCount = 4; |
1953 | pSMB->Reserved = 0; | 2038 | pSMB->Reserved = 0; |
1954 | pSMB->ParameterOffset = 0; | 2039 | pSMB->ParameterOffset = 0; |
@@ -1975,7 +2060,9 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, | |||
1975 | rc = -EIO; /* bad smb */ | 2060 | rc = -EIO; /* bad smb */ |
1976 | else { | 2061 | else { |
1977 | if(data_count && (data_count < 2048)) { | 2062 | if(data_count && (data_count < 2048)) { |
1978 | char * end_of_smb = pSMBr->ByteCount + (char *)&pSMBr->ByteCount; | 2063 | char * end_of_smb = 2 /* sizeof byte count */ + |
2064 | pSMBr->ByteCount + | ||
2065 | (char *)&pSMBr->ByteCount; | ||
1979 | 2066 | ||
1980 | struct reparse_data * reparse_buf = (struct reparse_data *) | 2067 | struct reparse_data * reparse_buf = (struct reparse_data *) |
1981 | ((char *)&pSMBr->hdr.Protocol + data_offset); | 2068 | ((char *)&pSMBr->hdr.Protocol + data_offset); |
@@ -2219,6 +2306,7 @@ queryAclRetry: | |||
2219 | 2306 | ||
2220 | rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, | 2307 | rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, |
2221 | (struct smb_hdr *) pSMBr, &bytes_returned, 0); | 2308 | (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
2309 | cifs_stats_inc(&tcon->num_acl_get); | ||
2222 | if (rc) { | 2310 | if (rc) { |
2223 | cFYI(1, ("Send error in Query POSIX ACL = %d", rc)); | 2311 | cFYI(1, ("Send error in Query POSIX ACL = %d", rc)); |
2224 | } else { | 2312 | } else { |
@@ -2406,6 +2494,87 @@ GetExtAttrOut: | |||
2406 | 2494 | ||
2407 | #endif /* CONFIG_POSIX */ | 2495 | #endif /* CONFIG_POSIX */ |
2408 | 2496 | ||
2497 | /* Convert CIFS ACL to POSIX form */ | ||
2498 | static int parse_sec_desc(struct sec_desc * psec_desc, int acl_len) | ||
2499 | { | ||
2500 | CHECK ON OTHER COMPUTER TO SEE IF FORMAT ENCODED IN CIFSPDU.H ALREADY | ||
2501 | return 0; | ||
2502 | } | ||
2503 | |||
2504 | /* Get Security Descriptor (by handle) from remote server for a file or dir */ | ||
2505 | int | ||
2506 | CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, | ||
2507 | /* BB fix up return info */ char *acl_inf, const int buflen, | ||
2508 | const int acl_type /* ACCESS/DEFAULT not sure implication */) | ||
2509 | { | ||
2510 | int rc = 0; | ||
2511 | int buf_type = 0; | ||
2512 | QUERY_SEC_DESC_REQ * pSMB; | ||
2513 | struct kvec iov[1]; | ||
2514 | |||
2515 | cFYI(1, ("GetCifsACL")); | ||
2516 | |||
2517 | rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, | ||
2518 | 8 /* parm len */, tcon, (void **) &pSMB); | ||
2519 | if (rc) | ||
2520 | return rc; | ||
2521 | |||
2522 | pSMB->MaxParameterCount = cpu_to_le32(4); | ||
2523 | /* BB TEST with big acls that might need to be e.g. larger than 16K */ | ||
2524 | pSMB->MaxSetupCount = 0; | ||
2525 | pSMB->Fid = fid; /* file handle always le */ | ||
2526 | pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP | | ||
2527 | CIFS_ACL_DACL); | ||
2528 | pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */ | ||
2529 | pSMB->hdr.smb_buf_length += 11; | ||
2530 | iov[0].iov_base = (char *)pSMB; | ||
2531 | iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; | ||
2532 | |||
2533 | rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, 0); | ||
2534 | cifs_stats_inc(&tcon->num_acl_get); | ||
2535 | if (rc) { | ||
2536 | cFYI(1, ("Send error in QuerySecDesc = %d", rc)); | ||
2537 | } else { /* decode response */ | ||
2538 | struct sec_desc * psec_desc; | ||
2539 | __le32 * parm; | ||
2540 | int parm_len; | ||
2541 | int data_len; | ||
2542 | int acl_len; | ||
2543 | struct smb_com_ntransact_rsp * pSMBr; | ||
2544 | |||
2545 | /* validate_nttransact */ | ||
2546 | rc = validate_ntransact(iov[0].iov_base, (char **)&parm, | ||
2547 | (char **)&psec_desc, | ||
2548 | &parm_len, &data_len); | ||
2549 | |||
2550 | if(rc) | ||
2551 | goto qsec_out; | ||
2552 | pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base; | ||
2553 | |||
2554 | cERROR(1,("smb %p parm %p data %p",pSMBr,parm,psec_desc)); /* BB removeme BB */ | ||
2555 | |||
2556 | if (le32_to_cpu(pSMBr->ParameterCount) != 4) { | ||
2557 | rc = -EIO; /* bad smb */ | ||
2558 | goto qsec_out; | ||
2559 | } | ||
2560 | |||
2561 | /* BB check that data area is minimum length and as big as acl_len */ | ||
2562 | |||
2563 | acl_len = le32_to_cpu(*(__le32 *)parm); | ||
2564 | /* BB check if(acl_len > bufsize) */ | ||
2565 | |||
2566 | parse_sec_desc(psec_desc, acl_len); | ||
2567 | } | ||
2568 | qsec_out: | ||
2569 | if(buf_type == CIFS_SMALL_BUFFER) | ||
2570 | cifs_small_buf_release(iov[0].iov_base); | ||
2571 | else if(buf_type == CIFS_LARGE_BUFFER) | ||
2572 | cifs_buf_release(iov[0].iov_base); | ||
2573 | cifs_small_buf_release(pSMB); | ||
2574 | return rc; | ||
2575 | } | ||
2576 | |||
2577 | |||
2409 | /* Legacy Query Path Information call for lookup to old servers such | 2578 | /* Legacy Query Path Information call for lookup to old servers such |
2410 | as Win9x/WinME */ | 2579 | as Win9x/WinME */ |
2411 | int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, | 2580 | int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, |
@@ -4304,7 +4473,7 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, | |||
4304 | { | 4473 | { |
4305 | int rc = 0; | 4474 | int rc = 0; |
4306 | struct smb_com_transaction_change_notify_req * pSMB = NULL; | 4475 | struct smb_com_transaction_change_notify_req * pSMB = NULL; |
4307 | struct smb_com_transaction_change_notify_rsp * pSMBr = NULL; | 4476 | struct smb_com_ntransaction_change_notify_rsp * pSMBr = NULL; |
4308 | struct dir_notify_req *dnotify_req; | 4477 | struct dir_notify_req *dnotify_req; |
4309 | int bytes_returned; | 4478 | int bytes_returned; |
4310 | 4479 | ||
@@ -4319,6 +4488,10 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, | |||
4319 | pSMB->MaxParameterCount = cpu_to_le32(2); | 4488 | pSMB->MaxParameterCount = cpu_to_le32(2); |
4320 | /* BB find exact data count max from sess structure BB */ | 4489 | /* BB find exact data count max from sess structure BB */ |
4321 | pSMB->MaxDataCount = 0; /* same in little endian or be */ | 4490 | pSMB->MaxDataCount = 0; /* same in little endian or be */ |
4491 | /* BB VERIFY verify which is correct for above BB */ | ||
4492 | pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf - | ||
4493 | MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); | ||
4494 | |||
4322 | pSMB->MaxSetupCount = 4; | 4495 | pSMB->MaxSetupCount = 4; |
4323 | pSMB->Reserved = 0; | 4496 | pSMB->Reserved = 0; |
4324 | pSMB->ParameterOffset = 0; | 4497 | pSMB->ParameterOffset = 0; |