Merge tag '5.15-rc1-ksmbd' of git://git.samba.org/ksmbd

Pull ksmbd server fixes from Steve French:
"Three ksmbd fixes, including an important security fix for path
processing, and a buffer overflow check, and a trivial fix for
incorrect header inclusion"

* tag '5.15-rc1-ksmbd' of git://git.samba.org/ksmbd:
ksmbd: add validation for FILE_FULL_EA_INFORMATION of smb2_get_info
ksmbd: prevent out of share access
ksmbd: transport_rdma: Don't include rwlock.h directly

+81 -17
+67 -9
fs/ksmbd/misc.c
··· 191 191 return nlink; 192 192 } 193 193 194 - void ksmbd_conv_path_to_unix(char *path) 194 + char *ksmbd_conv_path_to_unix(char *path) 195 195 { 196 + size_t path_len, remain_path_len, out_path_len; 197 + char *out_path, *out_next; 198 + int i, pre_dotdot_cnt = 0, slash_cnt = 0; 199 + bool is_last; 200 + 196 201 strreplace(path, '\\', '/'); 197 - } 202 + path_len = strlen(path); 203 + remain_path_len = path_len; 204 + if (path_len == 0) 205 + return ERR_PTR(-EINVAL); 198 206 199 - void ksmbd_strip_last_slash(char *path) 200 - { 201 - int len = strlen(path); 207 + out_path = kzalloc(path_len + 2, GFP_KERNEL); 208 + if (!out_path) 209 + return ERR_PTR(-ENOMEM); 210 + out_path_len = 0; 211 + out_next = out_path; 202 212 203 - while (len && path[len - 1] == '/') { 204 - path[len - 1] = '\0'; 205 - len--; 206 - } 213 + do { 214 + char *name = path + path_len - remain_path_len; 215 + char *next = strchrnul(name, '/'); 216 + size_t name_len = next - name; 217 + 218 + is_last = !next[0]; 219 + if (name_len == 2 && name[0] == '.' && name[1] == '.') { 220 + pre_dotdot_cnt++; 221 + /* handle the case that path ends with "/.." */ 222 + if (is_last) 223 + goto follow_dotdot; 224 + } else { 225 + if (pre_dotdot_cnt) { 226 + follow_dotdot: 227 + slash_cnt = 0; 228 + for (i = out_path_len - 1; i >= 0; i--) { 229 + if (out_path[i] == '/' && 230 + ++slash_cnt == pre_dotdot_cnt + 1) 231 + break; 232 + } 233 + 234 + if (i < 0 && 235 + slash_cnt != pre_dotdot_cnt) { 236 + kfree(out_path); 237 + return ERR_PTR(-EINVAL); 238 + } 239 + 240 + out_next = &out_path[i+1]; 241 + *out_next = '\0'; 242 + out_path_len = i + 1; 243 + 244 + } 245 + 246 + if (name_len != 0 && 247 + !(name_len == 1 && name[0] == '.') && 248 + !(name_len == 2 && name[0] == '.' && name[1] == '.')) { 249 + next[0] = '\0'; 250 + sprintf(out_next, "%s/", name); 251 + out_next += name_len + 1; 252 + out_path_len += name_len + 1; 253 + next[0] = '/'; 254 + } 255 + pre_dotdot_cnt = 0; 256 + } 257 + 258 + remain_path_len -= name_len + 1; 259 + } while (!is_last); 260 + 261 + if (out_path_len > 0) 262 + out_path[out_path_len-1] = '\0'; 263 + path[path_len] = '\0'; 264 + return out_path; 207 265 } 208 266 209 267 void ksmbd_conv_path_to_windows(char *path)
+1 -2
fs/ksmbd/misc.h
··· 16 16 int parse_stream_name(char *filename, char **stream_name, int *s_type); 17 17 char *convert_to_nt_pathname(char *filename, char *sharepath); 18 18 int get_nlink(struct kstat *st); 19 - void ksmbd_conv_path_to_unix(char *path); 20 - void ksmbd_strip_last_slash(char *path); 19 + char *ksmbd_conv_path_to_unix(char *path); 21 20 void ksmbd_conv_path_to_windows(char *path); 22 21 char *ksmbd_extract_sharename(char *treename); 23 22 char *convert_to_unix_name(struct ksmbd_share_config *share, char *name);
+13 -5
fs/ksmbd/smb2pdu.c
··· 634 634 smb2_get_name(struct ksmbd_share_config *share, const char *src, 635 635 const int maxlen, struct nls_table *local_nls) 636 636 { 637 - char *name, *unixname; 637 + char *name, *norm_name, *unixname; 638 638 639 639 name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); 640 640 if (IS_ERR(name)) { ··· 643 643 } 644 644 645 645 /* change it to absolute unix name */ 646 - ksmbd_conv_path_to_unix(name); 647 - ksmbd_strip_last_slash(name); 648 - 649 - unixname = convert_to_unix_name(share, name); 646 + norm_name = ksmbd_conv_path_to_unix(name); 647 + if (IS_ERR(norm_name)) { 648 + kfree(name); 649 + return norm_name; 650 + } 650 651 kfree(name); 652 + 653 + unixname = convert_to_unix_name(share, norm_name); 654 + kfree(norm_name); 651 655 if (!unixname) { 652 656 pr_err("can not convert absolute name\n"); 653 657 return ERR_PTR(-ENOMEM); ··· 4045 4041 path = &fp->filp->f_path; 4046 4042 /* single EA entry is requested with given user.* name */ 4047 4043 if (req->InputBufferLength) { 4044 + if (le32_to_cpu(req->InputBufferLength) < 4045 + sizeof(struct smb2_ea_info_req)) 4046 + return -EINVAL; 4047 + 4048 4048 ea_req = (struct smb2_ea_info_req *)req->Buffer; 4049 4049 } else { 4050 4050 /* need to send all EAs, if no specific EA is requested*/
-1
fs/ksmbd/transport_rdma.c
··· 20 20 #define SUBMOD_NAME "smb_direct" 21 21 22 22 #include <linux/kthread.h> 23 - #include <linux/rwlock.h> 24 23 #include <linux/list.h> 25 24 #include <linux/mempool.h> 26 25 #include <linux/highmem.h>