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