1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2018 Samsung Electronics Co., Ltd. 4 */ 5 6 #include <linux/list.h> 7 #include <linux/jhash.h> 8 #include <linux/slab.h> 9 #include <linux/rwsem.h> 10 #include <linux/parser.h> 11 #include <linux/namei.h> 12 #include <linux/sched.h> 13 #include <linux/mm.h> 14 15 #include "share_config.h" 16 #include "user_config.h" 17 #include "user_session.h" 18 #include "../connection.h" 19 #include "../transport_ipc.h" 20 #include "../misc.h" 21 22 #define SHARE_HASH_BITS 12 23 static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); 24 static DECLARE_RWSEM(shares_table_lock); 25 26 struct ksmbd_veto_pattern { 27 char *pattern; 28 struct list_head list; 29 }; 30 31 static unsigned int share_name_hash(const char *name) 32 { 33 return jhash(name, strlen(name), 0); 34 } 35 36 static void kill_share(struct ksmbd_share_config *share) 37 { 38 while (!list_empty(&share->veto_list)) { 39 struct ksmbd_veto_pattern *p; 40 41 p = list_entry(share->veto_list.next, 42 struct ksmbd_veto_pattern, 43 list); 44 list_del(&p->list); 45 kfree(p->pattern); 46 kfree(p); 47 } 48 49 if (share->path) 50 path_put(&share->vfs_path); 51 kfree(share->name); 52 kfree(share->path); 53 kfree(share); 54 } 55 56 void ksmbd_share_config_del(struct ksmbd_share_config *share) 57 { 58 down_write(&shares_table_lock); 59 hash_del(&share->hlist); 60 up_write(&shares_table_lock); 61 } 62 63 void __ksmbd_share_config_put(struct ksmbd_share_config *share) 64 { 65 ksmbd_share_config_del(share); 66 kill_share(share); 67 } 68 69 static struct ksmbd_share_config * 70 __get_share_config(struct ksmbd_share_config *share) 71 { 72 if (!atomic_inc_not_zero(&share->refcount)) 73 return NULL; 74 return share; 75 } 76 77 static struct ksmbd_share_config *__share_lookup(const char *name) 78 { 79 struct ksmbd_share_config *share; 80 unsigned int key = share_name_hash(name); 81 82 hash_for_each_possible(shares_table, share, hlist, key) { 83 if (!strcmp(name, share->name)) 84 return share; 85 } 86 return NULL; 87 } 88 89 static int parse_veto_list(struct ksmbd_share_config *share, 90 char *veto_list, 91 int veto_list_sz) 92 { 93 int sz = 0; 94 95 if (!veto_list_sz) 96 return 0; 97 98 while (veto_list_sz > 0) { 99 struct ksmbd_veto_pattern *p; 100 101 sz = strlen(veto_list); 102 if (!sz) 103 break; 104 105 p = kzalloc_obj(struct ksmbd_veto_pattern, KSMBD_DEFAULT_GFP); 106 if (!p) 107 return -ENOMEM; 108 109 p->pattern = kstrdup(veto_list, KSMBD_DEFAULT_GFP); 110 if (!p->pattern) { 111 kfree(p); 112 return -ENOMEM; 113 } 114 115 list_add(&p->list, &share->veto_list); 116 117 veto_list += sz + 1; 118 veto_list_sz -= (sz + 1); 119 } 120 121 return 0; 122 } 123 124 static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, 125 const char *name) 126 { 127 struct ksmbd_share_config_response *resp; 128 struct ksmbd_share_config *share = NULL; 129 struct ksmbd_share_config *lookup; 130 struct unicode_map *um = work->conn->um; 131 int ret; 132 133 resp = ksmbd_ipc_share_config_request(name); 134 if (!resp) 135 return NULL; 136 137 if (resp->flags == KSMBD_SHARE_FLAG_INVALID) 138 goto out; 139 140 if (*resp->share_name) { 141 char *cf_resp_name; 142 bool equal; 143 144 cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name); 145 if (IS_ERR(cf_resp_name)) 146 goto out; 147 equal = !strcmp(cf_resp_name, name); 148 kfree(cf_resp_name); 149 if (!equal) 150 goto out; 151 } 152 153 share = kzalloc_obj(struct ksmbd_share_config, KSMBD_DEFAULT_GFP); 154 if (!share) 155 goto out; 156 157 share->flags = resp->flags; 158 atomic_set(&share->refcount, 1); 159 INIT_LIST_HEAD(&share->veto_list); 160 share->name = kstrdup(name, KSMBD_DEFAULT_GFP); 161 162 if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { 163 int path_len = PATH_MAX; 164 165 if (resp->payload_sz) 166 path_len = resp->payload_sz - resp->veto_list_sz; 167 168 share->path = kstrndup(ksmbd_share_config_path(resp), path_len, 169 KSMBD_DEFAULT_GFP); 170 if (!share->path) { 171 ret = -ENOMEM; 172 } else { 173 ret = 0; 174 share->path_sz = strlen(share->path); 175 while (share->path_sz > 1 && 176 share->path[share->path_sz - 1] == '/') 177 share->path[--share->path_sz] = '\0'; 178 } 179 share->create_mask = resp->create_mask; 180 share->directory_mask = resp->directory_mask; 181 share->force_create_mode = resp->force_create_mode; 182 share->force_directory_mode = resp->force_directory_mode; 183 share->force_uid = resp->force_uid; 184 share->force_gid = resp->force_gid; 185 if (!ret) 186 ret = parse_veto_list(share, 187 KSMBD_SHARE_CONFIG_VETO_LIST(resp), 188 resp->veto_list_sz); 189 if (!ret && share->path) { 190 if (__ksmbd_override_fsids(work, share)) { 191 kill_share(share); 192 share = NULL; 193 goto out; 194 } 195 196 ret = kern_path(share->path, 0, &share->vfs_path); 197 ksmbd_revert_fsids(work); 198 if (ret) { 199 ksmbd_debug(SMB, "failed to access '%s'\n", 200 share->path); 201 /* Avoid put_path() */ 202 kfree(share->path); 203 share->path = NULL; 204 } 205 } 206 if (ret || !share->name) { 207 kill_share(share); 208 share = NULL; 209 goto out; 210 } 211 } 212 213 down_write(&shares_table_lock); 214 lookup = __share_lookup(name); 215 if (lookup) 216 lookup = __get_share_config(lookup); 217 if (!lookup) { 218 hash_add(shares_table, &share->hlist, share_name_hash(name)); 219 } else { 220 kill_share(share); 221 share = lookup; 222 } 223 up_write(&shares_table_lock); 224 225 out: 226 kvfree(resp); 227 return share; 228 } 229 230 struct ksmbd_share_config *ksmbd_share_config_get(struct ksmbd_work *work, 231 const char *name) 232 { 233 struct ksmbd_share_config *share; 234 235 down_read(&shares_table_lock); 236 share = __share_lookup(name); 237 if (share) 238 share = __get_share_config(share); 239 up_read(&shares_table_lock); 240 241 if (share) 242 return share; 243 return share_config_request(work, name); 244 } 245 246 bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, 247 const char *filename) 248 { 249 struct ksmbd_veto_pattern *p; 250 251 list_for_each_entry(p, &share->veto_list, list) { 252 if (match_wildcard(p->pattern, filename)) 253 return true; 254 } 255 return false; 256 } 257