Commit 16b5f54e authored by Atte Heikkilä's avatar Atte Heikkilä Committed by Steve French

ksmbd: casefold utf-8 share names and fix ascii lowercase conversion

strtolower() corrupts all UTF-8 share names that have a byte in the C0
(À ISO8859-1) to DE (Þ ISO8859-1) range, since the non-ASCII part of
ISO8859-1 is incompatible with UTF-8. Prevent this by checking that a
byte is in the ASCII range with isascii(), before the conversion to
lowercase with tolower(). Properly handle case-insensitivity of UTF-8
share names by casefolding them, but fallback to ASCII lowercase
conversion on failure or if CONFIG_UNICODE is not set. Refactor to move
the share name casefolding immediately after the share name extraction.
Also, make the associated constness corrections.
Signed-off-by: default avatarAtte Heikkilä <atteh.mailbox@gmail.com>
Acked-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 276a3f7c
...@@ -60,6 +60,12 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) ...@@ -60,6 +60,12 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
conn->local_nls = load_nls("utf8"); conn->local_nls = load_nls("utf8");
if (!conn->local_nls) if (!conn->local_nls)
conn->local_nls = load_nls_default(); conn->local_nls = load_nls_default();
if (IS_ENABLED(CONFIG_UNICODE))
conn->um = utf8_load(UNICODE_AGE(12, 1, 0));
else
conn->um = ERR_PTR(-EOPNOTSUPP);
if (IS_ERR(conn->um))
conn->um = NULL;
atomic_set(&conn->req_running, 0); atomic_set(&conn->req_running, 0);
atomic_set(&conn->r_count, 0); atomic_set(&conn->r_count, 0);
conn->total_credits = 1; conn->total_credits = 1;
...@@ -350,6 +356,8 @@ int ksmbd_conn_handler_loop(void *p) ...@@ -350,6 +356,8 @@ int ksmbd_conn_handler_loop(void *p)
wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0);
if (IS_ENABLED(CONFIG_UNICODE))
utf8_unload(conn->um);
unload_nls(conn->local_nls); unload_nls(conn->local_nls);
if (default_conn_ops.terminate_fn) if (default_conn_ops.terminate_fn)
default_conn_ops.terminate_fn(conn); default_conn_ops.terminate_fn(conn);
......
...@@ -46,6 +46,7 @@ struct ksmbd_conn { ...@@ -46,6 +46,7 @@ struct ksmbd_conn {
char *request_buf; char *request_buf;
struct ksmbd_transport *transport; struct ksmbd_transport *transport;
struct nls_table *local_nls; struct nls_table *local_nls;
struct unicode_map *um;
struct list_head conns_list; struct list_head conns_list;
/* smb session 1 per user */ /* smb session 1 per user */
struct xarray sessions; struct xarray sessions;
......
...@@ -26,7 +26,7 @@ struct ksmbd_veto_pattern { ...@@ -26,7 +26,7 @@ struct ksmbd_veto_pattern {
struct list_head list; struct list_head list;
}; };
static unsigned int share_name_hash(char *name) static unsigned int share_name_hash(const char *name)
{ {
return jhash(name, strlen(name), 0); return jhash(name, strlen(name), 0);
} }
...@@ -72,7 +72,7 @@ __get_share_config(struct ksmbd_share_config *share) ...@@ -72,7 +72,7 @@ __get_share_config(struct ksmbd_share_config *share)
return share; return share;
} }
static struct ksmbd_share_config *__share_lookup(char *name) static struct ksmbd_share_config *__share_lookup(const char *name)
{ {
struct ksmbd_share_config *share; struct ksmbd_share_config *share;
unsigned int key = share_name_hash(name); unsigned int key = share_name_hash(name);
...@@ -119,7 +119,7 @@ static int parse_veto_list(struct ksmbd_share_config *share, ...@@ -119,7 +119,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,
return 0; return 0;
} }
static struct ksmbd_share_config *share_config_request(char *name) static struct ksmbd_share_config *share_config_request(const char *name)
{ {
struct ksmbd_share_config_response *resp; struct ksmbd_share_config_response *resp;
struct ksmbd_share_config *share = NULL; struct ksmbd_share_config *share = NULL;
...@@ -190,20 +190,10 @@ static struct ksmbd_share_config *share_config_request(char *name) ...@@ -190,20 +190,10 @@ static struct ksmbd_share_config *share_config_request(char *name)
return share; return share;
} }
static void strtolower(char *share_name) struct ksmbd_share_config *ksmbd_share_config_get(const char *name)
{
while (*share_name) {
*share_name = tolower(*share_name);
share_name++;
}
}
struct ksmbd_share_config *ksmbd_share_config_get(char *name)
{ {
struct ksmbd_share_config *share; struct ksmbd_share_config *share;
strtolower(name);
down_read(&shares_table_lock); down_read(&shares_table_lock);
share = __share_lookup(name); share = __share_lookup(name);
if (share) if (share)
......
...@@ -74,7 +74,7 @@ static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) ...@@ -74,7 +74,7 @@ static inline void ksmbd_share_config_put(struct ksmbd_share_config *share)
__ksmbd_share_config_put(share); __ksmbd_share_config_put(share);
} }
struct ksmbd_share_config *ksmbd_share_config_get(char *name); struct ksmbd_share_config *ksmbd_share_config_get(const char *name);
bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
const char *filename); const char *filename);
#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */ #endif /* __SHARE_CONFIG_MANAGEMENT_H__ */
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
struct ksmbd_tree_conn_status struct ksmbd_tree_conn_status
ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
char *share_name) const char *share_name)
{ {
struct ksmbd_tree_conn_status status = {-ENOENT, NULL}; struct ksmbd_tree_conn_status status = {-ENOENT, NULL};
struct ksmbd_tree_connect_response *resp = NULL; struct ksmbd_tree_connect_response *resp = NULL;
......
...@@ -42,7 +42,7 @@ struct ksmbd_session; ...@@ -42,7 +42,7 @@ struct ksmbd_session;
struct ksmbd_tree_conn_status struct ksmbd_tree_conn_status
ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
char *share_name); const char *share_name);
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn); struct ksmbd_tree_connect *tree_conn);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/unicode.h>
#include "misc.h" #include "misc.h"
#include "smb_common.h" #include "smb_common.h"
...@@ -226,26 +227,53 @@ void ksmbd_conv_path_to_windows(char *path) ...@@ -226,26 +227,53 @@ void ksmbd_conv_path_to_windows(char *path)
strreplace(path, '/', '\\'); strreplace(path, '/', '\\');
} }
static char *casefold_sharename(struct unicode_map *um, const char *name)
{
char *cf_name;
int cf_len;
cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL);
if (!cf_name)
return ERR_PTR(-ENOMEM);
if (IS_ENABLED(CONFIG_UNICODE) && um) {
const struct qstr q_name = {.name = name, .len = strlen(name)};
cf_len = utf8_casefold(um, &q_name, cf_name,
KSMBD_REQ_MAX_SHARE_NAME);
if (cf_len < 0)
goto out_ascii;
return cf_name;
}
out_ascii:
cf_len = strscpy(cf_name, name, KSMBD_REQ_MAX_SHARE_NAME);
if (cf_len < 0) {
kfree(cf_name);
return ERR_PTR(-E2BIG);
}
for (; *cf_name; ++cf_name)
*cf_name = isascii(*cf_name) ? tolower(*cf_name) : *cf_name;
return cf_name - cf_len;
}
/** /**
* ksmbd_extract_sharename() - get share name from tree connect request * ksmbd_extract_sharename() - get share name from tree connect request
* @treename: buffer containing tree name and share name * @treename: buffer containing tree name and share name
* *
* Return: share name on success, otherwise error * Return: share name on success, otherwise error
*/ */
char *ksmbd_extract_sharename(char *treename) char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename)
{ {
char *name = treename; const char *name = treename, *pos = strrchr(name, '\\');
char *dst;
char *pos = strrchr(name, '\\');
if (pos) if (pos)
name = (pos + 1); name = (pos + 1);
/* caller has to free the memory */ /* caller has to free the memory */
dst = kstrdup(name, GFP_KERNEL); return casefold_sharename(um, name);
if (!dst)
return ERR_PTR(-ENOMEM);
return dst;
} }
/** /**
......
...@@ -20,7 +20,7 @@ int get_nlink(struct kstat *st); ...@@ -20,7 +20,7 @@ int get_nlink(struct kstat *st);
void ksmbd_conv_path_to_unix(char *path); void ksmbd_conv_path_to_unix(char *path);
void ksmbd_strip_last_slash(char *path); void ksmbd_strip_last_slash(char *path);
void ksmbd_conv_path_to_windows(char *path); void ksmbd_conv_path_to_windows(char *path);
char *ksmbd_extract_sharename(char *treename); char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename);
char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name);
#define KSMBD_DIR_INFO_ALIGNMENT 8 #define KSMBD_DIR_INFO_ALIGNMENT 8
......
...@@ -1883,7 +1883,7 @@ int smb2_tree_connect(struct ksmbd_work *work) ...@@ -1883,7 +1883,7 @@ int smb2_tree_connect(struct ksmbd_work *work)
goto out_err1; goto out_err1;
} }
name = ksmbd_extract_sharename(treename); name = ksmbd_extract_sharename(conn->um, treename);
if (IS_ERR(name)) { if (IS_ERR(name)) {
status.ret = KSMBD_TREE_CONN_STATUS_ERROR; status.ret = KSMBD_TREE_CONN_STATUS_ERROR;
goto out_err1; goto out_err1;
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/nls.h> #include <linux/nls.h>
#include <linux/unicode.h>
#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ #define UNIUPR_NOLOWER /* Example to not expand lower case tables */
...@@ -69,7 +70,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen, ...@@ -69,7 +70,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen,
const struct nls_table *codepage); const struct nls_table *codepage);
int smbConvertToUTF16(__le16 *target, const char *source, int srclen, int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
const struct nls_table *cp, int mapchars); const struct nls_table *cp, int mapchars);
char *ksmbd_extract_sharename(char *treename); char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename);
#endif #endif
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment