xref: /openbmc/linux/fs/smb/server/mgmt/share_config.c (revision 769218209d8f00a4003dc9ee351198a473043eaf)
138c8a9a5SSteve French // SPDX-License-Identifier: GPL-2.0-or-later
238c8a9a5SSteve French /*
338c8a9a5SSteve French  *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
438c8a9a5SSteve French  */
538c8a9a5SSteve French 
638c8a9a5SSteve French #include <linux/list.h>
738c8a9a5SSteve French #include <linux/jhash.h>
838c8a9a5SSteve French #include <linux/slab.h>
938c8a9a5SSteve French #include <linux/rwsem.h>
1038c8a9a5SSteve French #include <linux/parser.h>
1138c8a9a5SSteve French #include <linux/namei.h>
1238c8a9a5SSteve French #include <linux/sched.h>
1338c8a9a5SSteve French #include <linux/mm.h>
1438c8a9a5SSteve French 
1538c8a9a5SSteve French #include "share_config.h"
1638c8a9a5SSteve French #include "user_config.h"
1738c8a9a5SSteve French #include "user_session.h"
18*5a199eedSNamjae Jeon #include "../connection.h"
1938c8a9a5SSteve French #include "../transport_ipc.h"
2038c8a9a5SSteve French #include "../misc.h"
2138c8a9a5SSteve French 
2238c8a9a5SSteve French #define SHARE_HASH_BITS		3
2338c8a9a5SSteve French static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
2438c8a9a5SSteve French static DECLARE_RWSEM(shares_table_lock);
2538c8a9a5SSteve French 
2638c8a9a5SSteve French struct ksmbd_veto_pattern {
2738c8a9a5SSteve French 	char			*pattern;
2838c8a9a5SSteve French 	struct list_head	list;
2938c8a9a5SSteve French };
3038c8a9a5SSteve French 
share_name_hash(const char * name)3138c8a9a5SSteve French static unsigned int share_name_hash(const char *name)
3238c8a9a5SSteve French {
3338c8a9a5SSteve French 	return jhash(name, strlen(name), 0);
3438c8a9a5SSteve French }
3538c8a9a5SSteve French 
kill_share(struct ksmbd_share_config * share)3638c8a9a5SSteve French static void kill_share(struct ksmbd_share_config *share)
3738c8a9a5SSteve French {
3838c8a9a5SSteve French 	while (!list_empty(&share->veto_list)) {
3938c8a9a5SSteve French 		struct ksmbd_veto_pattern *p;
4038c8a9a5SSteve French 
4138c8a9a5SSteve French 		p = list_entry(share->veto_list.next,
4238c8a9a5SSteve French 			       struct ksmbd_veto_pattern,
4338c8a9a5SSteve French 			       list);
4438c8a9a5SSteve French 		list_del(&p->list);
4538c8a9a5SSteve French 		kfree(p->pattern);
4638c8a9a5SSteve French 		kfree(p);
4738c8a9a5SSteve French 	}
4838c8a9a5SSteve French 
4938c8a9a5SSteve French 	if (share->path)
5038c8a9a5SSteve French 		path_put(&share->vfs_path);
5138c8a9a5SSteve French 	kfree(share->name);
5238c8a9a5SSteve French 	kfree(share->path);
5338c8a9a5SSteve French 	kfree(share);
5438c8a9a5SSteve French }
5538c8a9a5SSteve French 
ksmbd_share_config_del(struct ksmbd_share_config * share)5638c8a9a5SSteve French void ksmbd_share_config_del(struct ksmbd_share_config *share)
5738c8a9a5SSteve French {
5838c8a9a5SSteve French 	down_write(&shares_table_lock);
5938c8a9a5SSteve French 	hash_del(&share->hlist);
6038c8a9a5SSteve French 	up_write(&shares_table_lock);
6138c8a9a5SSteve French }
6238c8a9a5SSteve French 
__ksmbd_share_config_put(struct ksmbd_share_config * share)6338c8a9a5SSteve French void __ksmbd_share_config_put(struct ksmbd_share_config *share)
6438c8a9a5SSteve French {
6538c8a9a5SSteve French 	ksmbd_share_config_del(share);
6638c8a9a5SSteve French 	kill_share(share);
6738c8a9a5SSteve French }
6838c8a9a5SSteve French 
6938c8a9a5SSteve French static struct ksmbd_share_config *
__get_share_config(struct ksmbd_share_config * share)7038c8a9a5SSteve French __get_share_config(struct ksmbd_share_config *share)
7138c8a9a5SSteve French {
7238c8a9a5SSteve French 	if (!atomic_inc_not_zero(&share->refcount))
7338c8a9a5SSteve French 		return NULL;
7438c8a9a5SSteve French 	return share;
7538c8a9a5SSteve French }
7638c8a9a5SSteve French 
__share_lookup(const char * name)7738c8a9a5SSteve French static struct ksmbd_share_config *__share_lookup(const char *name)
7838c8a9a5SSteve French {
7938c8a9a5SSteve French 	struct ksmbd_share_config *share;
8038c8a9a5SSteve French 	unsigned int key = share_name_hash(name);
8138c8a9a5SSteve French 
8238c8a9a5SSteve French 	hash_for_each_possible(shares_table, share, hlist, key) {
8338c8a9a5SSteve French 		if (!strcmp(name, share->name))
8438c8a9a5SSteve French 			return share;
8538c8a9a5SSteve French 	}
8638c8a9a5SSteve French 	return NULL;
8738c8a9a5SSteve French }
8838c8a9a5SSteve French 
parse_veto_list(struct ksmbd_share_config * share,char * veto_list,int veto_list_sz)8938c8a9a5SSteve French static int parse_veto_list(struct ksmbd_share_config *share,
9038c8a9a5SSteve French 			   char *veto_list,
9138c8a9a5SSteve French 			   int veto_list_sz)
9238c8a9a5SSteve French {
9338c8a9a5SSteve French 	int sz = 0;
9438c8a9a5SSteve French 
9538c8a9a5SSteve French 	if (!veto_list_sz)
9638c8a9a5SSteve French 		return 0;
9738c8a9a5SSteve French 
9838c8a9a5SSteve French 	while (veto_list_sz > 0) {
9938c8a9a5SSteve French 		struct ksmbd_veto_pattern *p;
10038c8a9a5SSteve French 
10138c8a9a5SSteve French 		sz = strlen(veto_list);
10238c8a9a5SSteve French 		if (!sz)
10338c8a9a5SSteve French 			break;
10438c8a9a5SSteve French 
10538c8a9a5SSteve French 		p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL);
10638c8a9a5SSteve French 		if (!p)
10738c8a9a5SSteve French 			return -ENOMEM;
10838c8a9a5SSteve French 
10938c8a9a5SSteve French 		p->pattern = kstrdup(veto_list, GFP_KERNEL);
11038c8a9a5SSteve French 		if (!p->pattern) {
11138c8a9a5SSteve French 			kfree(p);
11238c8a9a5SSteve French 			return -ENOMEM;
11338c8a9a5SSteve French 		}
11438c8a9a5SSteve French 
11538c8a9a5SSteve French 		list_add(&p->list, &share->veto_list);
11638c8a9a5SSteve French 
11738c8a9a5SSteve French 		veto_list += sz + 1;
11838c8a9a5SSteve French 		veto_list_sz -= (sz + 1);
11938c8a9a5SSteve French 	}
12038c8a9a5SSteve French 
12138c8a9a5SSteve French 	return 0;
12238c8a9a5SSteve French }
12338c8a9a5SSteve French 
share_config_request(struct ksmbd_work * work,const char * name)124*5a199eedSNamjae Jeon static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
12538c8a9a5SSteve French 						       const char *name)
12638c8a9a5SSteve French {
12738c8a9a5SSteve French 	struct ksmbd_share_config_response *resp;
12838c8a9a5SSteve French 	struct ksmbd_share_config *share = NULL;
12938c8a9a5SSteve French 	struct ksmbd_share_config *lookup;
130*5a199eedSNamjae Jeon 	struct unicode_map *um = work->conn->um;
13138c8a9a5SSteve French 	int ret;
13238c8a9a5SSteve French 
13338c8a9a5SSteve French 	resp = ksmbd_ipc_share_config_request(name);
13438c8a9a5SSteve French 	if (!resp)
13538c8a9a5SSteve French 		return NULL;
13638c8a9a5SSteve French 
13738c8a9a5SSteve French 	if (resp->flags == KSMBD_SHARE_FLAG_INVALID)
13838c8a9a5SSteve French 		goto out;
13938c8a9a5SSteve French 
14038c8a9a5SSteve French 	if (*resp->share_name) {
14138c8a9a5SSteve French 		char *cf_resp_name;
14238c8a9a5SSteve French 		bool equal;
14338c8a9a5SSteve French 
14438c8a9a5SSteve French 		cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name);
14538c8a9a5SSteve French 		if (IS_ERR(cf_resp_name))
14638c8a9a5SSteve French 			goto out;
14738c8a9a5SSteve French 		equal = !strcmp(cf_resp_name, name);
14838c8a9a5SSteve French 		kfree(cf_resp_name);
14938c8a9a5SSteve French 		if (!equal)
15038c8a9a5SSteve French 			goto out;
15138c8a9a5SSteve French 	}
15238c8a9a5SSteve French 
15338c8a9a5SSteve French 	share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL);
15438c8a9a5SSteve French 	if (!share)
15538c8a9a5SSteve French 		goto out;
15638c8a9a5SSteve French 
15738c8a9a5SSteve French 	share->flags = resp->flags;
15838c8a9a5SSteve French 	atomic_set(&share->refcount, 1);
15938c8a9a5SSteve French 	INIT_LIST_HEAD(&share->veto_list);
16038c8a9a5SSteve French 	share->name = kstrdup(name, GFP_KERNEL);
16138c8a9a5SSteve French 
16238c8a9a5SSteve French 	if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
163a637fabaSNamjae Jeon 		int path_len = PATH_MAX;
164a637fabaSNamjae Jeon 
165a637fabaSNamjae Jeon 		if (resp->payload_sz)
166a637fabaSNamjae Jeon 			path_len = resp->payload_sz - resp->veto_list_sz;
167a637fabaSNamjae Jeon 
168a637fabaSNamjae Jeon 		share->path = kstrndup(ksmbd_share_config_path(resp), path_len,
16938c8a9a5SSteve French 				      GFP_KERNEL);
1705698ba69SNandor Kracser 		if (share->path) {
17138c8a9a5SSteve French 			share->path_sz = strlen(share->path);
1725698ba69SNandor Kracser 			while (share->path_sz > 1 &&
1735698ba69SNandor Kracser 			       share->path[share->path_sz - 1] == '/')
1745698ba69SNandor Kracser 				share->path[--share->path_sz] = '\0';
1755698ba69SNandor Kracser 		}
17638c8a9a5SSteve French 		share->create_mask = resp->create_mask;
17738c8a9a5SSteve French 		share->directory_mask = resp->directory_mask;
17838c8a9a5SSteve French 		share->force_create_mode = resp->force_create_mode;
17938c8a9a5SSteve French 		share->force_directory_mode = resp->force_directory_mode;
18038c8a9a5SSteve French 		share->force_uid = resp->force_uid;
18138c8a9a5SSteve French 		share->force_gid = resp->force_gid;
18238c8a9a5SSteve French 		ret = parse_veto_list(share,
18338c8a9a5SSteve French 				      KSMBD_SHARE_CONFIG_VETO_LIST(resp),
18438c8a9a5SSteve French 				      resp->veto_list_sz);
18538c8a9a5SSteve French 		if (!ret && share->path) {
186*5a199eedSNamjae Jeon 			if (__ksmbd_override_fsids(work, share)) {
187*5a199eedSNamjae Jeon 				kill_share(share);
188*5a199eedSNamjae Jeon 				share = NULL;
189*5a199eedSNamjae Jeon 				goto out;
190*5a199eedSNamjae Jeon 			}
191*5a199eedSNamjae Jeon 
19238c8a9a5SSteve French 			ret = kern_path(share->path, 0, &share->vfs_path);
193*5a199eedSNamjae Jeon 			ksmbd_revert_fsids(work);
19438c8a9a5SSteve French 			if (ret) {
19538c8a9a5SSteve French 				ksmbd_debug(SMB, "failed to access '%s'\n",
19638c8a9a5SSteve French 					    share->path);
19738c8a9a5SSteve French 				/* Avoid put_path() */
19838c8a9a5SSteve French 				kfree(share->path);
19938c8a9a5SSteve French 				share->path = NULL;
20038c8a9a5SSteve French 			}
20138c8a9a5SSteve French 		}
20238c8a9a5SSteve French 		if (ret || !share->name) {
20338c8a9a5SSteve French 			kill_share(share);
20438c8a9a5SSteve French 			share = NULL;
20538c8a9a5SSteve French 			goto out;
20638c8a9a5SSteve French 		}
20738c8a9a5SSteve French 	}
20838c8a9a5SSteve French 
20938c8a9a5SSteve French 	down_write(&shares_table_lock);
21038c8a9a5SSteve French 	lookup = __share_lookup(name);
21138c8a9a5SSteve French 	if (lookup)
21238c8a9a5SSteve French 		lookup = __get_share_config(lookup);
21338c8a9a5SSteve French 	if (!lookup) {
21438c8a9a5SSteve French 		hash_add(shares_table, &share->hlist, share_name_hash(name));
21538c8a9a5SSteve French 	} else {
21638c8a9a5SSteve French 		kill_share(share);
21738c8a9a5SSteve French 		share = lookup;
21838c8a9a5SSteve French 	}
21938c8a9a5SSteve French 	up_write(&shares_table_lock);
22038c8a9a5SSteve French 
22138c8a9a5SSteve French out:
22238c8a9a5SSteve French 	kvfree(resp);
22338c8a9a5SSteve French 	return share;
22438c8a9a5SSteve French }
22538c8a9a5SSteve French 
ksmbd_share_config_get(struct ksmbd_work * work,const char * name)226*5a199eedSNamjae Jeon struct ksmbd_share_config *ksmbd_share_config_get(struct ksmbd_work *work,
22738c8a9a5SSteve French 						  const char *name)
22838c8a9a5SSteve French {
22938c8a9a5SSteve French 	struct ksmbd_share_config *share;
23038c8a9a5SSteve French 
23138c8a9a5SSteve French 	down_read(&shares_table_lock);
23238c8a9a5SSteve French 	share = __share_lookup(name);
23338c8a9a5SSteve French 	if (share)
23438c8a9a5SSteve French 		share = __get_share_config(share);
23538c8a9a5SSteve French 	up_read(&shares_table_lock);
23638c8a9a5SSteve French 
23738c8a9a5SSteve French 	if (share)
23838c8a9a5SSteve French 		return share;
239*5a199eedSNamjae Jeon 	return share_config_request(work, name);
24038c8a9a5SSteve French }
24138c8a9a5SSteve French 
ksmbd_share_veto_filename(struct ksmbd_share_config * share,const char * filename)24238c8a9a5SSteve French bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
24338c8a9a5SSteve French 			       const char *filename)
24438c8a9a5SSteve French {
24538c8a9a5SSteve French 	struct ksmbd_veto_pattern *p;
24638c8a9a5SSteve French 
24738c8a9a5SSteve French 	list_for_each_entry(p, &share->veto_list, list) {
24838c8a9a5SSteve French 		if (match_wildcard(p->pattern, filename))
24938c8a9a5SSteve French 			return true;
25038c8a9a5SSteve French 	}
25138c8a9a5SSteve French 	return false;
25238c8a9a5SSteve French }
253