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
share_name_hash(const char * name)31 static unsigned int share_name_hash(const char *name)
32 {
33 return jhash(name, strlen(name), 0);
34 }
35
kill_share(struct ksmbd_share_config * share)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
ksmbd_share_config_del(struct ksmbd_share_config * share)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
__ksmbd_share_config_put(struct ksmbd_share_config * share)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 *
__get_share_config(struct ksmbd_share_config * share)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
__share_lookup(const char * name)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
parse_veto_list(struct ksmbd_share_config * share,char * veto_list,int veto_list_sz)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
share_config_request(struct ksmbd_work * work,const char * name)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
ksmbd_share_config_get(struct ksmbd_work * work,const char * name)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
ksmbd_share_veto_filename(struct ksmbd_share_config * share,const char * filename)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