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: Atte Heikkilä <atteh.mailbox@gmail.com>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Atte Heikkilä 2022-09-15 22:49:11 +09:00 committed by Steve French
parent 276a3f7cf1
commit 16b5f54e30
10 changed files with 56 additions and 28 deletions

View file

@ -60,6 +60,12 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
conn->local_nls = load_nls("utf8");
if (!conn->local_nls)
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->r_count, 0);
conn->total_credits = 1;
@ -350,6 +356,8 @@ int ksmbd_conn_handler_loop(void *p)
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);
if (default_conn_ops.terminate_fn)
default_conn_ops.terminate_fn(conn);

View file

@ -46,6 +46,7 @@ struct ksmbd_conn {
char *request_buf;
struct ksmbd_transport *transport;
struct nls_table *local_nls;
struct unicode_map *um;
struct list_head conns_list;
/* smb session 1 per user */
struct xarray sessions;

View file

@ -26,7 +26,7 @@ struct ksmbd_veto_pattern {
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);
}
@ -72,7 +72,7 @@ __get_share_config(struct ksmbd_share_config *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;
unsigned int key = share_name_hash(name);
@ -119,7 +119,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,
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 *share = NULL;
@ -190,20 +190,10 @@ static struct ksmbd_share_config *share_config_request(char *name)
return share;
}
static void strtolower(char *share_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 *ksmbd_share_config_get(const char *name)
{
struct ksmbd_share_config *share;
strtolower(name);
down_read(&shares_table_lock);
share = __share_lookup(name);
if (share)

View file

@ -74,7 +74,7 @@ static inline void ksmbd_share_config_put(struct ksmbd_share_config *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,
const char *filename);
#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */

View file

@ -17,7 +17,7 @@
struct ksmbd_tree_conn_status
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_connect_response *resp = NULL;

View file

@ -42,7 +42,7 @@ struct ksmbd_session;
struct ksmbd_tree_conn_status
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,
struct ksmbd_tree_connect *tree_conn);

View file

@ -7,6 +7,7 @@
#include <linux/kernel.h>
#include <linux/xattr.h>
#include <linux/fs.h>
#include <linux/unicode.h>
#include "misc.h"
#include "smb_common.h"
@ -226,26 +227,53 @@ void ksmbd_conv_path_to_windows(char *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
* @treename: buffer containing tree name and share name
*
* 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;
char *dst;
char *pos = strrchr(name, '\\');
const char *name = treename, *pos = strrchr(name, '\\');
if (pos)
name = (pos + 1);
/* caller has to free the memory */
dst = kstrdup(name, GFP_KERNEL);
if (!dst)
return ERR_PTR(-ENOMEM);
return dst;
return casefold_sharename(um, name);
}
/**

View file

@ -20,7 +20,7 @@ int get_nlink(struct kstat *st);
void ksmbd_conv_path_to_unix(char *path);
void ksmbd_strip_last_slash(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);
#define KSMBD_DIR_INFO_ALIGNMENT 8

View file

@ -1883,7 +1883,7 @@ int smb2_tree_connect(struct ksmbd_work *work)
goto out_err1;
}
name = ksmbd_extract_sharename(treename);
name = ksmbd_extract_sharename(conn->um, treename);
if (IS_ERR(name)) {
status.ret = KSMBD_TREE_CONN_STATUS_ERROR;
goto out_err1;

View file

@ -24,6 +24,7 @@
#include <asm/byteorder.h>
#include <linux/types.h>
#include <linux/nls.h>
#include <linux/unicode.h>
#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,
const struct nls_table *codepage);
int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
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
/*