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 3 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(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); 106 if (!p) 107 return -ENOMEM; 108 109 p->pattern = kstrdup(veto_list, GFP_KERNEL); 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(sizeof(struct ksmbd_share_config), GFP_KERNEL); 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, GFP_KERNEL); 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 GFP_KERNEL); 170 if (share->path) { 171 share->path_sz = strlen(share->path); 172 while (share->path_sz > 1 && 173 share->path[share->path_sz - 1] == '/') 174 share->path[--share->path_sz] = '\0'; 175 } 176 share->create_mask = resp->create_mask; 177 share->directory_mask = resp->directory_mask; 178 share->force_create_mode = resp->force_create_mode; 179 share->force_directory_mode = resp->force_directory_mode; 180 share->force_uid = resp->force_uid; 181 share->force_gid = resp->force_gid; 182 ret = parse_veto_list(share, 183 KSMBD_SHARE_CONFIG_VETO_LIST(resp), 184 resp->veto_list_sz); 185 if (!ret && share->path) { 186 if (__ksmbd_override_fsids(work, share)) { 187 kill_share(share); 188 share = NULL; 189 goto out; 190 } 191 192 ret = kern_path(share->path, 0, &share->vfs_path); 193 ksmbd_revert_fsids(work); 194 if (ret) { 195 ksmbd_debug(SMB, "failed to access '%s'\n", 196 share->path); 197 /* Avoid put_path() */ 198 kfree(share->path); 199 share->path = NULL; 200 } 201 } 202 if (ret || !share->name) { 203 kill_share(share); 204 share = NULL; 205 goto out; 206 } 207 } 208 209 down_write(&shares_table_lock); 210 lookup = __share_lookup(name); 211 if (lookup) 212 lookup = __get_share_config(lookup); 213 if (!lookup) { 214 hash_add(shares_table, &share->hlist, share_name_hash(name)); 215 } else { 216 kill_share(share); 217 share = lookup; 218 } 219 up_write(&shares_table_lock); 220 221 out: 222 kvfree(resp); 223 return share; 224 } 225 226 struct ksmbd_share_config *ksmbd_share_config_get(struct ksmbd_work *work, 227 const char *name) 228 { 229 struct ksmbd_share_config *share; 230 231 down_read(&shares_table_lock); 232 share = __share_lookup(name); 233 if (share) 234 share = __get_share_config(share); 235 up_read(&shares_table_lock); 236 237 if (share) 238 return share; 239 return share_config_request(work, name); 240 } 241 242 bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, 243 const char *filename) 244 { 245 struct ksmbd_veto_pattern *p; 246 247 list_for_each_entry(p, &share->veto_list, list) { 248 if (match_wildcard(p->pattern, filename)) 249 return true; 250 } 251 return false; 252 } 253