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