xref: /openbmc/linux/fs/smb/server/mgmt/share_config.c (revision 8d81cd1a)
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