xref: /openbmc/linux/fs/smb/server/misc.c (revision 75417833)
138c8a9a5SSteve French // SPDX-License-Identifier: GPL-2.0-or-later
238c8a9a5SSteve French /*
338c8a9a5SSteve French  *   Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
438c8a9a5SSteve French  *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
538c8a9a5SSteve French  */
638c8a9a5SSteve French 
738c8a9a5SSteve French #include <linux/kernel.h>
838c8a9a5SSteve French #include <linux/xattr.h>
938c8a9a5SSteve French #include <linux/fs.h>
1038c8a9a5SSteve French #include <linux/unicode.h>
1138c8a9a5SSteve French 
1238c8a9a5SSteve French #include "misc.h"
1338c8a9a5SSteve French #include "smb_common.h"
1438c8a9a5SSteve French #include "connection.h"
1538c8a9a5SSteve French #include "vfs.h"
1638c8a9a5SSteve French 
1738c8a9a5SSteve French #include "mgmt/share_config.h"
1838c8a9a5SSteve French 
1938c8a9a5SSteve French /**
2038c8a9a5SSteve French  * match_pattern() - compare a string with a pattern which might include
2138c8a9a5SSteve French  * wildcard '*' and '?'
2238c8a9a5SSteve French  * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR
2338c8a9a5SSteve French  *
2438c8a9a5SSteve French  * @str:	string to compare with a pattern
2538c8a9a5SSteve French  * @len:	string length
2638c8a9a5SSteve French  * @pattern:	pattern string which might include wildcard '*' and '?'
2738c8a9a5SSteve French  *
2838c8a9a5SSteve French  * Return:	0 if pattern matched with the string, otherwise non zero value
2938c8a9a5SSteve French  */
match_pattern(const char * str,size_t len,const char * pattern)3038c8a9a5SSteve French int match_pattern(const char *str, size_t len, const char *pattern)
3138c8a9a5SSteve French {
3238c8a9a5SSteve French 	const char *s = str;
3338c8a9a5SSteve French 	const char *p = pattern;
3438c8a9a5SSteve French 	bool star = false;
3538c8a9a5SSteve French 
3638c8a9a5SSteve French 	while (*s && len) {
3738c8a9a5SSteve French 		switch (*p) {
3838c8a9a5SSteve French 		case '?':
3938c8a9a5SSteve French 			s++;
4038c8a9a5SSteve French 			len--;
4138c8a9a5SSteve French 			p++;
4238c8a9a5SSteve French 			break;
4338c8a9a5SSteve French 		case '*':
4438c8a9a5SSteve French 			star = true;
4538c8a9a5SSteve French 			str = s;
4638c8a9a5SSteve French 			if (!*++p)
4738c8a9a5SSteve French 				return true;
4838c8a9a5SSteve French 			pattern = p;
4938c8a9a5SSteve French 			break;
5038c8a9a5SSteve French 		default:
5138c8a9a5SSteve French 			if (tolower(*s) == tolower(*p)) {
5238c8a9a5SSteve French 				s++;
5338c8a9a5SSteve French 				len--;
5438c8a9a5SSteve French 				p++;
5538c8a9a5SSteve French 			} else {
5638c8a9a5SSteve French 				if (!star)
5738c8a9a5SSteve French 					return false;
5838c8a9a5SSteve French 				str++;
5938c8a9a5SSteve French 				s = str;
6038c8a9a5SSteve French 				p = pattern;
6138c8a9a5SSteve French 			}
6238c8a9a5SSteve French 			break;
6338c8a9a5SSteve French 		}
6438c8a9a5SSteve French 	}
6538c8a9a5SSteve French 
6638c8a9a5SSteve French 	if (*p == '*')
6738c8a9a5SSteve French 		++p;
6838c8a9a5SSteve French 	return !*p;
6938c8a9a5SSteve French }
7038c8a9a5SSteve French 
7138c8a9a5SSteve French /*
7238c8a9a5SSteve French  * is_char_allowed() - check for valid character
7338c8a9a5SSteve French  * @ch:		input character to be checked
7438c8a9a5SSteve French  *
7538c8a9a5SSteve French  * Return:	1 if char is allowed, otherwise 0
7638c8a9a5SSteve French  */
is_char_allowed(char ch)7738c8a9a5SSteve French static inline int is_char_allowed(char ch)
7838c8a9a5SSteve French {
7938c8a9a5SSteve French 	/* check for control chars, wildcards etc. */
8038c8a9a5SSteve French 	if (!(ch & 0x80) &&
8138c8a9a5SSteve French 	    (ch <= 0x1f ||
8238c8a9a5SSteve French 	     ch == '?' || ch == '"' || ch == '<' ||
8338c8a9a5SSteve French 	     ch == '>' || ch == '|' || ch == '*'))
8438c8a9a5SSteve French 		return 0;
8538c8a9a5SSteve French 
8638c8a9a5SSteve French 	return 1;
8738c8a9a5SSteve French }
8838c8a9a5SSteve French 
ksmbd_validate_filename(char * filename)8938c8a9a5SSteve French int ksmbd_validate_filename(char *filename)
9038c8a9a5SSteve French {
9138c8a9a5SSteve French 	while (*filename) {
9238c8a9a5SSteve French 		char c = *filename;
9338c8a9a5SSteve French 
9438c8a9a5SSteve French 		filename++;
9538c8a9a5SSteve French 		if (!is_char_allowed(c)) {
9638c8a9a5SSteve French 			ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c);
9738c8a9a5SSteve French 			return -ENOENT;
9838c8a9a5SSteve French 		}
9938c8a9a5SSteve French 	}
10038c8a9a5SSteve French 
10138c8a9a5SSteve French 	return 0;
10238c8a9a5SSteve French }
10338c8a9a5SSteve French 
ksmbd_validate_stream_name(char * stream_name)10438c8a9a5SSteve French static int ksmbd_validate_stream_name(char *stream_name)
10538c8a9a5SSteve French {
10638c8a9a5SSteve French 	while (*stream_name) {
10738c8a9a5SSteve French 		char c = *stream_name;
10838c8a9a5SSteve French 
10938c8a9a5SSteve French 		stream_name++;
11038c8a9a5SSteve French 		if (c == '/' || c == ':' || c == '\\') {
11138c8a9a5SSteve French 			pr_err("Stream name validation failed: %c\n", c);
11238c8a9a5SSteve French 			return -ENOENT;
11338c8a9a5SSteve French 		}
11438c8a9a5SSteve French 	}
11538c8a9a5SSteve French 
11638c8a9a5SSteve French 	return 0;
11738c8a9a5SSteve French }
11838c8a9a5SSteve French 
parse_stream_name(char * filename,char ** stream_name,int * s_type)11938c8a9a5SSteve French int parse_stream_name(char *filename, char **stream_name, int *s_type)
12038c8a9a5SSteve French {
12138c8a9a5SSteve French 	char *stream_type;
12238c8a9a5SSteve French 	char *s_name;
12338c8a9a5SSteve French 	int rc = 0;
12438c8a9a5SSteve French 
12538c8a9a5SSteve French 	s_name = filename;
12638c8a9a5SSteve French 	filename = strsep(&s_name, ":");
12738c8a9a5SSteve French 	ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name);
12838c8a9a5SSteve French 	if (strchr(s_name, ':')) {
12938c8a9a5SSteve French 		stream_type = s_name;
13038c8a9a5SSteve French 		s_name = strsep(&stream_type, ":");
13138c8a9a5SSteve French 
13238c8a9a5SSteve French 		rc = ksmbd_validate_stream_name(s_name);
13338c8a9a5SSteve French 		if (rc < 0) {
13438c8a9a5SSteve French 			rc = -ENOENT;
13538c8a9a5SSteve French 			goto out;
13638c8a9a5SSteve French 		}
13738c8a9a5SSteve French 
13838c8a9a5SSteve French 		ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name,
13938c8a9a5SSteve French 			    stream_type);
14038c8a9a5SSteve French 		if (!strncasecmp("$data", stream_type, 5))
14138c8a9a5SSteve French 			*s_type = DATA_STREAM;
14238c8a9a5SSteve French 		else if (!strncasecmp("$index_allocation", stream_type, 17))
14338c8a9a5SSteve French 			*s_type = DIR_STREAM;
14438c8a9a5SSteve French 		else
14538c8a9a5SSteve French 			rc = -ENOENT;
14638c8a9a5SSteve French 	}
14738c8a9a5SSteve French 
14838c8a9a5SSteve French 	*stream_name = s_name;
14938c8a9a5SSteve French out:
15038c8a9a5SSteve French 	return rc;
15138c8a9a5SSteve French }
15238c8a9a5SSteve French 
15338c8a9a5SSteve French /**
15438c8a9a5SSteve French  * convert_to_nt_pathname() - extract and return windows path string
15538c8a9a5SSteve French  *      whose share directory prefix was removed from file path
15638c8a9a5SSteve French  * @share: ksmbd_share_config pointer
15738c8a9a5SSteve French  * @path: path to report
15838c8a9a5SSteve French  *
15938c8a9a5SSteve French  * Return : windows path string or error
16038c8a9a5SSteve French  */
16138c8a9a5SSteve French 
convert_to_nt_pathname(struct ksmbd_share_config * share,const struct path * path)16238c8a9a5SSteve French char *convert_to_nt_pathname(struct ksmbd_share_config *share,
16338c8a9a5SSteve French 			     const struct path *path)
16438c8a9a5SSteve French {
16538c8a9a5SSteve French 	char *pathname, *ab_pathname, *nt_pathname;
16638c8a9a5SSteve French 	int share_path_len = share->path_sz;
16738c8a9a5SSteve French 
16838c8a9a5SSteve French 	pathname = kmalloc(PATH_MAX, GFP_KERNEL);
16938c8a9a5SSteve French 	if (!pathname)
17038c8a9a5SSteve French 		return ERR_PTR(-EACCES);
17138c8a9a5SSteve French 
17238c8a9a5SSteve French 	ab_pathname = d_path(path, pathname, PATH_MAX);
17338c8a9a5SSteve French 	if (IS_ERR(ab_pathname)) {
17438c8a9a5SSteve French 		nt_pathname = ERR_PTR(-EACCES);
17538c8a9a5SSteve French 		goto free_pathname;
17638c8a9a5SSteve French 	}
17738c8a9a5SSteve French 
17838c8a9a5SSteve French 	if (strncmp(ab_pathname, share->path, share_path_len)) {
17938c8a9a5SSteve French 		nt_pathname = ERR_PTR(-EACCES);
18038c8a9a5SSteve French 		goto free_pathname;
18138c8a9a5SSteve French 	}
18238c8a9a5SSteve French 
18338c8a9a5SSteve French 	nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL);
18438c8a9a5SSteve French 	if (!nt_pathname) {
18538c8a9a5SSteve French 		nt_pathname = ERR_PTR(-ENOMEM);
18638c8a9a5SSteve French 		goto free_pathname;
18738c8a9a5SSteve French 	}
18838c8a9a5SSteve French 	if (ab_pathname[share_path_len] == '\0')
18938c8a9a5SSteve French 		strcpy(nt_pathname, "/");
19038c8a9a5SSteve French 	strcat(nt_pathname, &ab_pathname[share_path_len]);
19138c8a9a5SSteve French 
19238c8a9a5SSteve French 	ksmbd_conv_path_to_windows(nt_pathname);
19338c8a9a5SSteve French 
19438c8a9a5SSteve French free_pathname:
19538c8a9a5SSteve French 	kfree(pathname);
19638c8a9a5SSteve French 	return nt_pathname;
19738c8a9a5SSteve French }
19838c8a9a5SSteve French 
get_nlink(struct kstat * st)19938c8a9a5SSteve French int get_nlink(struct kstat *st)
20038c8a9a5SSteve French {
20138c8a9a5SSteve French 	int nlink;
20238c8a9a5SSteve French 
20338c8a9a5SSteve French 	nlink = st->nlink;
20438c8a9a5SSteve French 	if (S_ISDIR(st->mode))
20538c8a9a5SSteve French 		nlink--;
20638c8a9a5SSteve French 
20738c8a9a5SSteve French 	return nlink;
20838c8a9a5SSteve French }
20938c8a9a5SSteve French 
ksmbd_conv_path_to_unix(char * path)21038c8a9a5SSteve French void ksmbd_conv_path_to_unix(char *path)
21138c8a9a5SSteve French {
21238c8a9a5SSteve French 	strreplace(path, '\\', '/');
21338c8a9a5SSteve French }
21438c8a9a5SSteve French 
ksmbd_strip_last_slash(char * path)21538c8a9a5SSteve French void ksmbd_strip_last_slash(char *path)
21638c8a9a5SSteve French {
21738c8a9a5SSteve French 	int len = strlen(path);
21838c8a9a5SSteve French 
21938c8a9a5SSteve French 	while (len && path[len - 1] == '/') {
22038c8a9a5SSteve French 		path[len - 1] = '\0';
22138c8a9a5SSteve French 		len--;
22238c8a9a5SSteve French 	}
22338c8a9a5SSteve French }
22438c8a9a5SSteve French 
ksmbd_conv_path_to_windows(char * path)22538c8a9a5SSteve French void ksmbd_conv_path_to_windows(char *path)
22638c8a9a5SSteve French {
22738c8a9a5SSteve French 	strreplace(path, '/', '\\');
22838c8a9a5SSteve French }
22938c8a9a5SSteve French 
ksmbd_casefold_sharename(struct unicode_map * um,const char * name)23038c8a9a5SSteve French char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name)
23138c8a9a5SSteve French {
23238c8a9a5SSteve French 	char *cf_name;
23338c8a9a5SSteve French 	int cf_len;
23438c8a9a5SSteve French 
23538c8a9a5SSteve French 	cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL);
23638c8a9a5SSteve French 	if (!cf_name)
23738c8a9a5SSteve French 		return ERR_PTR(-ENOMEM);
23838c8a9a5SSteve French 
23938c8a9a5SSteve French 	if (IS_ENABLED(CONFIG_UNICODE) && um) {
24038c8a9a5SSteve French 		const struct qstr q_name = {.name = name, .len = strlen(name)};
24138c8a9a5SSteve French 
24238c8a9a5SSteve French 		cf_len = utf8_casefold(um, &q_name, cf_name,
24338c8a9a5SSteve French 				       KSMBD_REQ_MAX_SHARE_NAME);
24438c8a9a5SSteve French 		if (cf_len < 0)
24538c8a9a5SSteve French 			goto out_ascii;
24638c8a9a5SSteve French 
24738c8a9a5SSteve French 		return cf_name;
24838c8a9a5SSteve French 	}
24938c8a9a5SSteve French 
25038c8a9a5SSteve French out_ascii:
25138c8a9a5SSteve French 	cf_len = strscpy(cf_name, name, KSMBD_REQ_MAX_SHARE_NAME);
25238c8a9a5SSteve French 	if (cf_len < 0) {
25338c8a9a5SSteve French 		kfree(cf_name);
25438c8a9a5SSteve French 		return ERR_PTR(-E2BIG);
25538c8a9a5SSteve French 	}
25638c8a9a5SSteve French 
25738c8a9a5SSteve French 	for (; *cf_name; ++cf_name)
25838c8a9a5SSteve French 		*cf_name = isascii(*cf_name) ? tolower(*cf_name) : *cf_name;
25938c8a9a5SSteve French 	return cf_name - cf_len;
26038c8a9a5SSteve French }
26138c8a9a5SSteve French 
26238c8a9a5SSteve French /**
26338c8a9a5SSteve French  * ksmbd_extract_sharename() - get share name from tree connect request
26475417833SYang Li  * @um: pointer to a unicode_map structure for character encoding handling
26538c8a9a5SSteve French  * @treename:	buffer containing tree name and share name
26638c8a9a5SSteve French  *
26738c8a9a5SSteve French  * Return:      share name on success, otherwise error
26838c8a9a5SSteve French  */
ksmbd_extract_sharename(struct unicode_map * um,const char * treename)26938c8a9a5SSteve French char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename)
27038c8a9a5SSteve French {
27138c8a9a5SSteve French 	const char *name = treename, *pos = strrchr(name, '\\');
27238c8a9a5SSteve French 
27338c8a9a5SSteve French 	if (pos)
27438c8a9a5SSteve French 		name = (pos + 1);
27538c8a9a5SSteve French 
27638c8a9a5SSteve French 	/* caller has to free the memory */
27738c8a9a5SSteve French 	return ksmbd_casefold_sharename(um, name);
27838c8a9a5SSteve French }
27938c8a9a5SSteve French 
28038c8a9a5SSteve French /**
28138c8a9a5SSteve French  * convert_to_unix_name() - convert windows name to unix format
28238c8a9a5SSteve French  * @share:	ksmbd_share_config pointer
28338c8a9a5SSteve French  * @name:	file name that is relative to share
28438c8a9a5SSteve French  *
28538c8a9a5SSteve French  * Return:	converted name on success, otherwise NULL
28638c8a9a5SSteve French  */
convert_to_unix_name(struct ksmbd_share_config * share,const char * name)28738c8a9a5SSteve French char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
28838c8a9a5SSteve French {
28938c8a9a5SSteve French 	int no_slash = 0, name_len, path_len;
29038c8a9a5SSteve French 	char *new_name;
29138c8a9a5SSteve French 
29238c8a9a5SSteve French 	if (name[0] == '/')
29338c8a9a5SSteve French 		name++;
29438c8a9a5SSteve French 
29538c8a9a5SSteve French 	path_len = share->path_sz;
29638c8a9a5SSteve French 	name_len = strlen(name);
29738c8a9a5SSteve French 	new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL);
29838c8a9a5SSteve French 	if (!new_name)
29938c8a9a5SSteve French 		return new_name;
30038c8a9a5SSteve French 
30138c8a9a5SSteve French 	memcpy(new_name, share->path, path_len);
30238c8a9a5SSteve French 	if (new_name[path_len - 1] != '/') {
30338c8a9a5SSteve French 		new_name[path_len] = '/';
30438c8a9a5SSteve French 		no_slash = 1;
30538c8a9a5SSteve French 	}
30638c8a9a5SSteve French 
30738c8a9a5SSteve French 	memcpy(new_name + path_len + no_slash, name, name_len);
30838c8a9a5SSteve French 	path_len += name_len + no_slash;
30938c8a9a5SSteve French 	new_name[path_len] = 0x00;
31038c8a9a5SSteve French 	return new_name;
31138c8a9a5SSteve French }
31238c8a9a5SSteve French 
ksmbd_convert_dir_info_name(struct ksmbd_dir_info * d_info,const struct nls_table * local_nls,int * conv_len)31338c8a9a5SSteve French char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
31438c8a9a5SSteve French 				  const struct nls_table *local_nls,
31538c8a9a5SSteve French 				  int *conv_len)
31638c8a9a5SSteve French {
31738c8a9a5SSteve French 	char *conv;
31838c8a9a5SSteve French 	int  sz = min(4 * d_info->name_len, PATH_MAX);
31938c8a9a5SSteve French 
32038c8a9a5SSteve French 	if (!sz)
32138c8a9a5SSteve French 		return NULL;
32238c8a9a5SSteve French 
32338c8a9a5SSteve French 	conv = kmalloc(sz, GFP_KERNEL);
32438c8a9a5SSteve French 	if (!conv)
32538c8a9a5SSteve French 		return NULL;
32638c8a9a5SSteve French 
32738c8a9a5SSteve French 	/* XXX */
32838c8a9a5SSteve French 	*conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name,
32938c8a9a5SSteve French 				      d_info->name_len, local_nls, 0);
33038c8a9a5SSteve French 	*conv_len *= 2;
33138c8a9a5SSteve French 
33238c8a9a5SSteve French 	/* We allocate buffer twice bigger than needed. */
33338c8a9a5SSteve French 	conv[*conv_len] = 0x00;
33438c8a9a5SSteve French 	conv[*conv_len + 1] = 0x00;
33538c8a9a5SSteve French 	return conv;
33638c8a9a5SSteve French }
33738c8a9a5SSteve French 
33838c8a9a5SSteve French /*
33938c8a9a5SSteve French  * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
34038c8a9a5SSteve French  * into Unix UTC (based 1970-01-01, in seconds).
34138c8a9a5SSteve French  */
ksmbd_NTtimeToUnix(__le64 ntutc)34238c8a9a5SSteve French struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc)
34338c8a9a5SSteve French {
34438c8a9a5SSteve French 	struct timespec64 ts;
34538c8a9a5SSteve French 
34638c8a9a5SSteve French 	/* Subtract the NTFS time offset, then convert to 1s intervals. */
34738c8a9a5SSteve French 	s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
34838c8a9a5SSteve French 	u64 abs_t;
34938c8a9a5SSteve French 
35038c8a9a5SSteve French 	/*
35138c8a9a5SSteve French 	 * Unfortunately can not use normal 64 bit division on 32 bit arch, but
35238c8a9a5SSteve French 	 * the alternative, do_div, does not work with negative numbers so have
35338c8a9a5SSteve French 	 * to special case them
35438c8a9a5SSteve French 	 */
35538c8a9a5SSteve French 	if (t < 0) {
35638c8a9a5SSteve French 		abs_t = -t;
35738c8a9a5SSteve French 		ts.tv_nsec = do_div(abs_t, 10000000) * 100;
35838c8a9a5SSteve French 		ts.tv_nsec = -ts.tv_nsec;
35938c8a9a5SSteve French 		ts.tv_sec = -abs_t;
36038c8a9a5SSteve French 	} else {
36138c8a9a5SSteve French 		abs_t = t;
36238c8a9a5SSteve French 		ts.tv_nsec = do_div(abs_t, 10000000) * 100;
36338c8a9a5SSteve French 		ts.tv_sec = abs_t;
36438c8a9a5SSteve French 	}
36538c8a9a5SSteve French 
36638c8a9a5SSteve French 	return ts;
36738c8a9a5SSteve French }
36838c8a9a5SSteve French 
36938c8a9a5SSteve French /* Convert the Unix UTC into NT UTC. */
ksmbd_UnixTimeToNT(struct timespec64 t)37038c8a9a5SSteve French inline u64 ksmbd_UnixTimeToNT(struct timespec64 t)
37138c8a9a5SSteve French {
37238c8a9a5SSteve French 	/* Convert to 100ns intervals and then add the NTFS time offset. */
37338c8a9a5SSteve French 	return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET;
37438c8a9a5SSteve French }
37538c8a9a5SSteve French 
ksmbd_systime(void)37638c8a9a5SSteve French inline long long ksmbd_systime(void)
37738c8a9a5SSteve French {
37838c8a9a5SSteve French 	struct timespec64	ts;
37938c8a9a5SSteve French 
38038c8a9a5SSteve French 	ktime_get_real_ts64(&ts);
38138c8a9a5SSteve French 	return ksmbd_UnixTimeToNT(ts);
38238c8a9a5SSteve French }
383