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
22static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
23static DECLARE_RWSEM(shares_table_lock);
24
25struct ksmbd_veto_pattern {
26	char			*pattern;
27	struct list_head	list;
28};
29
30static unsigned int share_name_hash(const char *name)
31{
32	return jhash(name, strlen(name), 0);
33}
34
35static 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
55void 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
62void __ksmbd_share_config_put(struct ksmbd_share_config *share)
63{
64	ksmbd_share_config_del(share);
65	kill_share(share);
66}
67
68static struct ksmbd_share_config *
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
76static 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
88static 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
123static 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		int path_len = PATH_MAX;
162
163		if (resp->payload_sz)
164			path_len = resp->payload_sz - resp->veto_list_sz;
165
166		share->path = kstrndup(ksmbd_share_config_path(resp), path_len,
167				      GFP_KERNEL);
168		if (share->path)
169			share->path_sz = strlen(share->path);
170		share->create_mask = resp->create_mask;
171		share->directory_mask = resp->directory_mask;
172		share->force_create_mode = resp->force_create_mode;
173		share->force_directory_mode = resp->force_directory_mode;
174		share->force_uid = resp->force_uid;
175		share->force_gid = resp->force_gid;
176		ret = parse_veto_list(share,
177				      KSMBD_SHARE_CONFIG_VETO_LIST(resp),
178				      resp->veto_list_sz);
179		if (!ret && share->path) {
180			ret = kern_path(share->path, 0, &share->vfs_path);
181			if (ret) {
182				ksmbd_debug(SMB, "failed to access '%s'\n",
183					    share->path);
184				/* Avoid put_path() */
185				kfree(share->path);
186				share->path = NULL;
187			}
188		}
189		if (ret || !share->name) {
190			kill_share(share);
191			share = NULL;
192			goto out;
193		}
194	}
195
196	down_write(&shares_table_lock);
197	lookup = __share_lookup(name);
198	if (lookup)
199		lookup = __get_share_config(lookup);
200	if (!lookup) {
201		hash_add(shares_table, &share->hlist, share_name_hash(name));
202	} else {
203		kill_share(share);
204		share = lookup;
205	}
206	up_write(&shares_table_lock);
207
208out:
209	kvfree(resp);
210	return share;
211}
212
213struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um,
214						  const char *name)
215{
216	struct ksmbd_share_config *share;
217
218	down_read(&shares_table_lock);
219	share = __share_lookup(name);
220	if (share)
221		share = __get_share_config(share);
222	up_read(&shares_table_lock);
223
224	if (share)
225		return share;
226	return share_config_request(um, name);
227}
228
229bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
230			       const char *filename)
231{
232	struct ksmbd_veto_pattern *p;
233
234	list_for_each_entry(p, &share->veto_list, list) {
235		if (match_wildcard(p->pattern, filename))
236			return true;
237	}
238	return false;
239}
240