aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/cifspdu.h16
-rw-r--r--fs/cifs/cifssmb.c205
2 files changed, 139 insertions, 82 deletions
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index c43bf4b7a556..93d5ee02a25b 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -1906,17 +1906,15 @@ typedef struct smb_com_transaction2_get_dfs_refer_req {
1906 1906
1907typedef struct dfs_referral_level_3 { 1907typedef struct dfs_referral_level_3 {
1908 __le16 VersionNumber; 1908 __le16 VersionNumber;
1909 __le16 ReferralSize; 1909 __le16 Size;
1910 __le16 ServerType; /* 0x0001 = CIFS server */ 1910 __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */
1911 __le16 ReferralFlags; /* or proximity - not clear which since it is 1911 __le16 ReferralEntryFlags; /* 0x0200 bit set only for domain
1912 always set to zero - SNIA spec says 0x01 1912 or DC referral responce */
1913 means strip off PathConsumed chars before 1913 __le32 TimeToLive;
1914 submitting RequestFileName to remote node */
1915 __le16 TimeToLive;
1916 __le16 Proximity;
1917 __le16 DfsPathOffset; 1914 __le16 DfsPathOffset;
1918 __le16 DfsAlternatePathOffset; 1915 __le16 DfsAlternatePathOffset;
1919 __le16 NetworkAddressOffset; 1916 __le16 NetworkAddressOffset; /* offset of the link target */
1917 __le16 ServiceSiteGuid;
1920} __attribute__((packed)) REFERRAL3; 1918} __attribute__((packed)) REFERRAL3;
1921 1919
1922typedef struct smb_com_transaction_get_dfs_refer_rsp { 1920typedef struct smb_com_transaction_get_dfs_refer_rsp {
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index fc297383cb0e..6f8ed93a4ae8 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -81,6 +81,39 @@ static struct {
81#endif /* CONFIG_CIFS_WEAK_PW_HASH */ 81#endif /* CONFIG_CIFS_WEAK_PW_HASH */
82#endif /* CIFS_POSIX */ 82#endif /* CIFS_POSIX */
83 83
84/* Allocates buffer into dst and copies smb string from src to it.
85 * caller is responsible for freeing dst if function returned 0.
86 * returns:
87 * on success - 0
88 * on failure - errno
89 */
90static int
91cifs_strncpy_to_host(char **dst, const char *src, const int maxlen,
92 const bool is_unicode, const struct nls_table *nls_codepage)
93{
94 int plen;
95
96 if (is_unicode) {
97 plen = UniStrnlen((wchar_t *)src, maxlen);
98 *dst = kmalloc(plen + 2, GFP_KERNEL);
99 if (!*dst)
100 goto cifs_strncpy_to_host_ErrExit;
101 cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage);
102 } else {
103 plen = strnlen(src, maxlen);
104 *dst = kmalloc(plen + 2, GFP_KERNEL);
105 if (!*dst)
106 goto cifs_strncpy_to_host_ErrExit;
107 strncpy(*dst, src, plen);
108 }
109 (*dst)[plen] = 0;
110 return 0;
111
112cifs_strncpy_to_host_ErrExit:
113 cERROR(1, ("Failed to allocate buffer for string\n"));
114 return -ENOMEM;
115}
116
84 117
85/* Mark as invalid, all open files on tree connections since they 118/* Mark as invalid, all open files on tree connections since they
86 were closed when session to server was lost */ 119 were closed when session to server was lost */
@@ -3867,6 +3900,96 @@ GetInodeNumOut:
3867 return rc; 3900 return rc;
3868} 3901}
3869 3902
3903/* parses DFS refferal V3 structure
3904 * caller is responsible for freeing target_nodes
3905 * returns:
3906 * on success - 0
3907 * on failure - errno
3908 */
3909static int
3910parse_DFS_REFERRALS(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
3911 unsigned int *num_of_nodes,
3912 struct dfs_info3_param **target_nodes,
3913 const struct nls_table *nls_codepage)
3914{
3915 int i, rc = 0;
3916 char *data_end;
3917 bool is_unicode;
3918 struct dfs_referral_level_3 *ref;
3919
3920 is_unicode = pSMBr->hdr.Flags2 & SMBFLG2_UNICODE;
3921 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
3922
3923 if (*num_of_nodes < 1) {
3924 cERROR(1, ("num_referrals: must be at least > 0,"
3925 "but we get num_referrals = %d\n", *num_of_nodes));
3926 rc = -EINVAL;
3927 goto parse_DFS_REFERRALS_exit;
3928 }
3929
3930 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
3931 if (ref->VersionNumber != 3) {
3932 cERROR(1, ("Referrals of V%d version are not supported,"
3933 "should be V3", ref->VersionNumber));
3934 rc = -EINVAL;
3935 goto parse_DFS_REFERRALS_exit;
3936 }
3937
3938 /* get the upper boundary of the resp buffer */
3939 data_end = (char *)(&(pSMBr->PathConsumed)) +
3940 le16_to_cpu(pSMBr->t2.DataCount);
3941
3942 cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n",
3943 *num_of_nodes,
3944 le16_to_cpu(pSMBr->DFSFlags)));
3945
3946 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
3947 *num_of_nodes, GFP_KERNEL);
3948 if (*target_nodes == NULL) {
3949 cERROR(1, ("Failed to allocate buffer for target_nodes\n"));
3950 rc = -ENOMEM;
3951 goto parse_DFS_REFERRALS_exit;
3952 }
3953
3954 /* collect neccessary data from referrals */
3955 for (i = 0; i < *num_of_nodes; i++) {
3956 char *temp;
3957 int max_len;
3958 struct dfs_info3_param *node = (*target_nodes)+i;
3959
3960 node->flags = le16_to_cpu(pSMBr->DFSFlags);
3961 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
3962 node->server_type = le16_to_cpu(ref->ServerType);
3963 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
3964
3965 /* copy DfsPath */
3966 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
3967 max_len = data_end - temp;
3968 rc = cifs_strncpy_to_host(&(node->path_name), temp,
3969 max_len, is_unicode, nls_codepage);
3970 if (rc)
3971 goto parse_DFS_REFERRALS_exit;
3972
3973 /* copy link target UNC */
3974 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
3975 max_len = data_end - temp;
3976 rc = cifs_strncpy_to_host(&(node->node_name), temp,
3977 max_len, is_unicode, nls_codepage);
3978 if (rc)
3979 goto parse_DFS_REFERRALS_exit;
3980
3981 ref += ref->Size;
3982 }
3983
3984parse_DFS_REFERRALS_exit:
3985 if (rc) {
3986 free_dfs_info_array(*target_nodes, *num_of_nodes);
3987 *target_nodes = NULL;
3988 *num_of_nodes = 0;
3989 }
3990 return rc;
3991}
3992
3870int 3993int
3871CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, 3994CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
3872 const unsigned char *searchName, 3995 const unsigned char *searchName,
@@ -3877,12 +4000,9 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
3877/* TRANS2_GET_DFS_REFERRAL */ 4000/* TRANS2_GET_DFS_REFERRAL */
3878 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; 4001 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
3879 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; 4002 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
3880 struct dfs_referral_level_3 *referrals = NULL;
3881 int rc = 0; 4003 int rc = 0;
3882 int bytes_returned; 4004 int bytes_returned;
3883 int name_len; 4005 int name_len;
3884 unsigned int i;
3885 char *temp;
3886 __u16 params, byte_count; 4006 __u16 params, byte_count;
3887 *num_of_nodes = 0; 4007 *num_of_nodes = 0;
3888 *target_nodes = NULL; 4008 *target_nodes = NULL;
@@ -3960,80 +4080,19 @@ getDFSRetry:
3960 rc = validate_t2((struct smb_t2_rsp *)pSMBr); 4080 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3961 4081
3962 /* BB Also check if enough total bytes returned? */ 4082 /* BB Also check if enough total bytes returned? */
3963 if (rc || (pSMBr->ByteCount < 17)) 4083 if (rc || (pSMBr->ByteCount < 17)) {
3964 rc = -EIO; /* bad smb */ 4084 rc = -EIO; /* bad smb */
3965 else { 4085 goto GetDFSRefExit;
3966 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); 4086 }
3967 __u16 data_count = le16_to_cpu(pSMBr->t2.DataCount);
3968
3969 cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d",
3970 pSMBr->ByteCount, data_offset));
3971 referrals =
3972 (struct dfs_referral_level_3 *)
3973 (8 /* sizeof start of data block */ +
3974 data_offset +
3975 (char *) &pSMBr->hdr.Protocol);
3976 cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n"
3977 "for referral one refer size: 0x%x srv "
3978 "type: 0x%x refer flags: 0x%x ttl: 0x%x",
3979 le16_to_cpu(pSMBr->NumberOfReferrals),
3980 le16_to_cpu(pSMBr->DFSFlags),
3981 le16_to_cpu(referrals->ReferralSize),
3982 le16_to_cpu(referrals->ServerType),
3983 le16_to_cpu(referrals->ReferralFlags),
3984 le16_to_cpu(referrals->TimeToLive)));
3985 /* BB This field is actually two bytes in from start of
3986 data block so we could do safety check that DataBlock
3987 begins at address of pSMBr->NumberOfReferrals */
3988 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
3989
3990 /* BB Fix below so can return more than one referral */
3991 if (*num_of_nodes > 1)
3992 *num_of_nodes = 1;
3993
3994 /* get the length of the strings describing refs */
3995 name_len = 0;
3996 for (i = 0; i < *num_of_nodes; i++) {
3997 /* make sure that DfsPathOffset not past end */
3998 __u16 offset = le16_to_cpu(referrals->DfsPathOffset);
3999 if (offset > data_count) {
4000 /* if invalid referral, stop here and do
4001 not try to copy any more */
4002 *num_of_nodes = i;
4003 break;
4004 }
4005 temp = ((char *)referrals) + offset;
4006 4087
4007 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { 4088 cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d",
4008 name_len += UniStrnlen((wchar_t *)temp, 4089 pSMBr->ByteCount,
4009 data_count); 4090 le16_to_cpu(pSMBr->t2.DataOffset)));
4010 } else {
4011 name_len += strnlen(temp, data_count);
4012 }
4013 referrals++;
4014 /* BB add check that referral pointer does
4015 not fall off end PDU */
4016 }
4017 /* BB add check for name_len bigger than bcc */
4018 *target_nodes =
4019 kmalloc(name_len+1+(*num_of_nodes),
4020 GFP_KERNEL);
4021 if (*target_nodes == NULL) {
4022 rc = -ENOMEM;
4023 goto GetDFSRefExit;
4024 }
4025 4091
4026 referrals = (struct dfs_referral_level_3 *) 4092 /* parse returned result into more usable form */
4027 (8 /* sizeof data hdr */ + data_offset + 4093 rc = parse_DFS_REFERRALS(pSMBr, num_of_nodes,
4028 (char *) &pSMBr->hdr.Protocol); 4094 target_nodes, nls_codepage);
4029 4095
4030 for (i = 0; i < *num_of_nodes; i++) {
4031 temp = ((char *)referrals) +
4032 le16_to_cpu(referrals->DfsPathOffset);
4033 /* BB update target_uncs pointers */
4034 referrals++;
4035 }
4036 }
4037GetDFSRefExit: 4096GetDFSRefExit:
4038 if (pSMB) 4097 if (pSMB)
4039 cifs_buf_release(pSMB); 4098 cifs_buf_release(pSMB);