Commit 5e5d7597 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '5.15-rc2-ksmbd-fixes' of git://git.samba.org/ksmbd

Pull ksmbd fixes from Steve French:
 "Five fixes for the ksmbd kernel server, including three security
  fixes:

   - remove follow symlinks support

   - use LOOKUP_BENEATH to prevent out of share access

   - SMB3 compounding security fix

   - fix for returning the default streams correctly, fixing a bug when
     writing ppt or doc files from some clients

   - logging more clearly that ksmbd is experimental (at module load
     time)"

* tag '5.15-rc2-ksmbd-fixes' of git://git.samba.org/ksmbd:
  ksmbd: use LOOKUP_BENEATH to prevent the out of share access
  ksmbd: remove follow symlinks support
  ksmbd: check protocol id in ksmbd_verify_smb_message()
  ksmbd: add default data stream name in FILE_STREAM_INFORMATION
  ksmbd: log that server is experimental at module load
parents 996148ee 265fd199
...@@ -158,25 +158,21 @@ int parse_stream_name(char *filename, char **stream_name, int *s_type) ...@@ -158,25 +158,21 @@ int parse_stream_name(char *filename, char **stream_name, int *s_type)
* Return : windows path string or error * Return : windows path string or error
*/ */
char *convert_to_nt_pathname(char *filename, char *sharepath) char *convert_to_nt_pathname(char *filename)
{ {
char *ab_pathname; char *ab_pathname;
int len, name_len;
name_len = strlen(filename); if (strlen(filename) == 0) {
ab_pathname = kmalloc(name_len, GFP_KERNEL); ab_pathname = kmalloc(2, GFP_KERNEL);
if (!ab_pathname) ab_pathname[0] = '\\';
return NULL; ab_pathname[1] = '\0';
} else {
ab_pathname[0] = '\\'; ab_pathname = kstrdup(filename, GFP_KERNEL);
ab_pathname[1] = '\0'; if (!ab_pathname)
return NULL;
len = strlen(sharepath);
if (!strncmp(filename, sharepath, len) && name_len != len) {
strscpy(ab_pathname, &filename[len], name_len);
ksmbd_conv_path_to_windows(ab_pathname); ksmbd_conv_path_to_windows(ab_pathname);
} }
return ab_pathname; return ab_pathname;
} }
...@@ -191,77 +187,19 @@ int get_nlink(struct kstat *st) ...@@ -191,77 +187,19 @@ int get_nlink(struct kstat *st)
return nlink; return nlink;
} }
char *ksmbd_conv_path_to_unix(char *path) void ksmbd_conv_path_to_unix(char *path)
{ {
size_t path_len, remain_path_len, out_path_len;
char *out_path, *out_next;
int i, pre_dotdot_cnt = 0, slash_cnt = 0;
bool is_last;
strreplace(path, '\\', '/'); strreplace(path, '\\', '/');
path_len = strlen(path); }
remain_path_len = path_len;
if (path_len == 0)
return ERR_PTR(-EINVAL);
out_path = kzalloc(path_len + 2, GFP_KERNEL);
if (!out_path)
return ERR_PTR(-ENOMEM);
out_path_len = 0;
out_next = out_path;
do {
char *name = path + path_len - remain_path_len;
char *next = strchrnul(name, '/');
size_t name_len = next - name;
is_last = !next[0];
if (name_len == 2 && name[0] == '.' && name[1] == '.') {
pre_dotdot_cnt++;
/* handle the case that path ends with "/.." */
if (is_last)
goto follow_dotdot;
} else {
if (pre_dotdot_cnt) {
follow_dotdot:
slash_cnt = 0;
for (i = out_path_len - 1; i >= 0; i--) {
if (out_path[i] == '/' &&
++slash_cnt == pre_dotdot_cnt + 1)
break;
}
if (i < 0 &&
slash_cnt != pre_dotdot_cnt) {
kfree(out_path);
return ERR_PTR(-EINVAL);
}
out_next = &out_path[i+1];
*out_next = '\0';
out_path_len = i + 1;
}
if (name_len != 0 &&
!(name_len == 1 && name[0] == '.') &&
!(name_len == 2 && name[0] == '.' && name[1] == '.')) {
next[0] = '\0';
sprintf(out_next, "%s/", name);
out_next += name_len + 1;
out_path_len += name_len + 1;
next[0] = '/';
}
pre_dotdot_cnt = 0;
}
remain_path_len -= name_len + 1; void ksmbd_strip_last_slash(char *path)
} while (!is_last); {
int len = strlen(path);
if (out_path_len > 0) while (len && path[len - 1] == '/') {
out_path[out_path_len-1] = '\0'; path[len - 1] = '\0';
path[path_len] = '\0'; len--;
return out_path; }
} }
void ksmbd_conv_path_to_windows(char *path) void ksmbd_conv_path_to_windows(char *path)
...@@ -298,7 +236,7 @@ char *ksmbd_extract_sharename(char *treename) ...@@ -298,7 +236,7 @@ char *ksmbd_extract_sharename(char *treename)
* *
* Return: converted name on success, otherwise NULL * Return: converted name on success, otherwise NULL
*/ */
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name) char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
{ {
int no_slash = 0, name_len, path_len; int no_slash = 0, name_len, path_len;
char *new_name; char *new_name;
......
...@@ -14,12 +14,13 @@ struct ksmbd_file; ...@@ -14,12 +14,13 @@ struct ksmbd_file;
int match_pattern(const char *str, size_t len, const char *pattern); int match_pattern(const char *str, size_t len, const char *pattern);
int ksmbd_validate_filename(char *filename); int ksmbd_validate_filename(char *filename);
int parse_stream_name(char *filename, char **stream_name, int *s_type); int parse_stream_name(char *filename, char **stream_name, int *s_type);
char *convert_to_nt_pathname(char *filename, char *sharepath); char *convert_to_nt_pathname(char *filename);
int get_nlink(struct kstat *st); int get_nlink(struct kstat *st);
char *ksmbd_conv_path_to_unix(char *path); void ksmbd_conv_path_to_unix(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(char *treename);
char *convert_to_unix_name(struct ksmbd_share_config *share, 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
struct ksmbd_dir_info; struct ksmbd_dir_info;
......
...@@ -584,6 +584,9 @@ static int __init ksmbd_server_init(void) ...@@ -584,6 +584,9 @@ static int __init ksmbd_server_init(void)
ret = ksmbd_workqueue_init(); ret = ksmbd_workqueue_init();
if (ret) if (ret)
goto err_crypto_destroy; goto err_crypto_destroy;
pr_warn_once("The ksmbd server is experimental, use at your own risk.\n");
return 0; return 0;
err_crypto_destroy: err_crypto_destroy:
......
...@@ -433,7 +433,7 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) ...@@ -433,7 +433,7 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work)
work->compound_pfid = KSMBD_NO_FID; work->compound_pfid = KSMBD_NO_FID;
} }
memset((char *)rsp_hdr + 4, 0, sizeof(struct smb2_hdr) + 2); memset((char *)rsp_hdr + 4, 0, sizeof(struct smb2_hdr) + 2);
rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
rsp_hdr->Command = rcv_hdr->Command; rsp_hdr->Command = rcv_hdr->Command;
...@@ -634,7 +634,7 @@ static char * ...@@ -634,7 +634,7 @@ static char *
smb2_get_name(struct ksmbd_share_config *share, const char *src, smb2_get_name(struct ksmbd_share_config *share, const char *src,
const int maxlen, struct nls_table *local_nls) const int maxlen, struct nls_table *local_nls)
{ {
char *name, *norm_name, *unixname; char *name;
name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); name = smb_strndup_from_utf16(src, maxlen, 1, local_nls);
if (IS_ERR(name)) { if (IS_ERR(name)) {
...@@ -642,23 +642,9 @@ smb2_get_name(struct ksmbd_share_config *share, const char *src, ...@@ -642,23 +642,9 @@ smb2_get_name(struct ksmbd_share_config *share, const char *src,
return name; return name;
} }
/* change it to absolute unix name */ ksmbd_conv_path_to_unix(name);
norm_name = ksmbd_conv_path_to_unix(name); ksmbd_strip_last_slash(name);
if (IS_ERR(norm_name)) { return name;
kfree(name);
return norm_name;
}
kfree(name);
unixname = convert_to_unix_name(share, norm_name);
kfree(norm_name);
if (!unixname) {
pr_err("can not convert absolute name\n");
return ERR_PTR(-ENOMEM);
}
ksmbd_debug(SMB, "absolute name = %s\n", unixname);
return unixname;
} }
int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
...@@ -2352,7 +2338,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, ...@@ -2352,7 +2338,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
return rc; return rc;
} }
rc = ksmbd_vfs_kern_path(name, 0, path, 0); rc = ksmbd_vfs_kern_path(work, name, 0, path, 0);
if (rc) { if (rc) {
pr_err("cannot get linux path (%s), err = %d\n", pr_err("cannot get linux path (%s), err = %d\n",
name, rc); name, rc);
...@@ -2427,7 +2413,7 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2427,7 +2413,7 @@ int smb2_open(struct ksmbd_work *work)
struct oplock_info *opinfo; struct oplock_info *opinfo;
__le32 *next_ptr = NULL; __le32 *next_ptr = NULL;
int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
int rc = 0, len = 0; int rc = 0;
int contxt_cnt = 0, query_disk_id = 0; int contxt_cnt = 0, query_disk_id = 0;
int maximal_access_ctxt = 0, posix_ctxt = 0; int maximal_access_ctxt = 0, posix_ctxt = 0;
int s_type = 0; int s_type = 0;
...@@ -2499,17 +2485,11 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2499,17 +2485,11 @@ int smb2_open(struct ksmbd_work *work)
goto err_out1; goto err_out1;
} }
} else { } else {
len = strlen(share->path); name = kstrdup("", GFP_KERNEL);
ksmbd_debug(SMB, "share path len %d\n", len);
name = kmalloc(len + 1, GFP_KERNEL);
if (!name) { if (!name) {
rsp->hdr.Status = STATUS_NO_MEMORY;
rc = -ENOMEM; rc = -ENOMEM;
goto err_out1; goto err_out1;
} }
memcpy(name, share->path, len);
*(name + len) = '\0';
} }
req_op_level = req->RequestedOplockLevel; req_op_level = req->RequestedOplockLevel;
...@@ -2632,13 +2612,9 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2632,13 +2612,9 @@ int smb2_open(struct ksmbd_work *work)
goto err_out1; goto err_out1;
} }
if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
/* if (!rc) {
* On delete request, instead of following up, need to if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) {
* look the current entity
*/
rc = ksmbd_vfs_kern_path(name, 0, &path, 1);
if (!rc) {
/* /*
* If file exists with under flags, return access * If file exists with under flags, return access
* denied error. * denied error.
...@@ -2657,34 +2633,16 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2657,34 +2633,16 @@ int smb2_open(struct ksmbd_work *work)
path_put(&path); path_put(&path);
goto err_out; goto err_out;
} }
} } else if (d_is_symlink(path.dentry)) {
} else { rc = -EACCES;
if (test_share_config_flag(work->tcon->share_conf, path_put(&path);
KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) { goto err_out;
/*
* Use LOOKUP_FOLLOW to follow the path of
* symlink in path buildup
*/
rc = ksmbd_vfs_kern_path(name, LOOKUP_FOLLOW, &path, 1);
if (rc) { /* Case for broken link ?*/
rc = ksmbd_vfs_kern_path(name, 0, &path, 1);
}
} else {
rc = ksmbd_vfs_kern_path(name, 0, &path, 1);
if (!rc && d_is_symlink(path.dentry)) {
rc = -EACCES;
path_put(&path);
goto err_out;
}
} }
} }
if (rc) { if (rc) {
if (rc == -EACCES) { if (rc != -ENOENT)
ksmbd_debug(SMB,
"User does not have right permission\n");
goto err_out; goto err_out;
}
ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n",
name, rc); name, rc);
rc = 0; rc = 0;
...@@ -3180,7 +3138,7 @@ int smb2_open(struct ksmbd_work *work) ...@@ -3180,7 +3138,7 @@ int smb2_open(struct ksmbd_work *work)
rsp->hdr.Status = STATUS_INVALID_PARAMETER; rsp->hdr.Status = STATUS_INVALID_PARAMETER;
else if (rc == -EOPNOTSUPP) else if (rc == -EOPNOTSUPP)
rsp->hdr.Status = STATUS_NOT_SUPPORTED; rsp->hdr.Status = STATUS_NOT_SUPPORTED;
else if (rc == -EACCES || rc == -ESTALE) else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV)
rsp->hdr.Status = STATUS_ACCESS_DENIED; rsp->hdr.Status = STATUS_ACCESS_DENIED;
else if (rc == -ENOENT) else if (rc == -ENOENT)
rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID;
...@@ -4296,8 +4254,7 @@ static int get_file_all_info(struct ksmbd_work *work, ...@@ -4296,8 +4254,7 @@ static int get_file_all_info(struct ksmbd_work *work,
return -EACCES; return -EACCES;
} }
filename = convert_to_nt_pathname(fp->filename, filename = convert_to_nt_pathname(fp->filename);
work->tcon->share_conf->path);
if (!filename) if (!filename)
return -ENOMEM; return -ENOMEM;
...@@ -4428,17 +4385,15 @@ static void get_file_stream_info(struct ksmbd_work *work, ...@@ -4428,17 +4385,15 @@ static void get_file_stream_info(struct ksmbd_work *work,
file_info->NextEntryOffset = cpu_to_le32(next); file_info->NextEntryOffset = cpu_to_le32(next);
} }
if (nbytes) { if (!S_ISDIR(stat.mode)) {
file_info = (struct smb2_file_stream_info *) file_info = (struct smb2_file_stream_info *)
&rsp->Buffer[nbytes]; &rsp->Buffer[nbytes];
streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName,
"::$DATA", 7, conn->local_nls, 0); "::$DATA", 7, conn->local_nls, 0);
streamlen *= 2; streamlen *= 2;
file_info->StreamNameLength = cpu_to_le32(streamlen); file_info->StreamNameLength = cpu_to_le32(streamlen);
file_info->StreamSize = S_ISDIR(stat.mode) ? 0 : file_info->StreamSize = 0;
cpu_to_le64(stat.size); file_info->StreamAllocationSize = 0;
file_info->StreamAllocationSize = S_ISDIR(stat.mode) ? 0 :
cpu_to_le64(stat.size);
nbytes += sizeof(struct smb2_file_stream_info) + streamlen; nbytes += sizeof(struct smb2_file_stream_info) + streamlen;
} }
...@@ -4753,12 +4708,8 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, ...@@ -4753,12 +4708,8 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
struct path path; struct path path;
int rc = 0, len; int rc = 0, len;
int fs_infoclass_size = 0; int fs_infoclass_size = 0;
int lookup_flags = 0;
if (test_share_config_flag(share, KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS))
lookup_flags = LOOKUP_FOLLOW;
rc = ksmbd_vfs_kern_path(share->path, lookup_flags, &path, 0); rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path);
if (rc) { if (rc) {
pr_err("cannot create vfs path\n"); pr_err("cannot create vfs path\n");
return -EIO; return -EIO;
...@@ -5307,7 +5258,7 @@ static int smb2_rename(struct ksmbd_work *work, ...@@ -5307,7 +5258,7 @@ static int smb2_rename(struct ksmbd_work *work,
goto out; goto out;
len = strlen(new_name); len = strlen(new_name);
if (new_name[len - 1] != '/') { if (len > 0 && new_name[len - 1] != '/') {
pr_err("not allow base filename in rename\n"); pr_err("not allow base filename in rename\n");
rc = -ESHARE; rc = -ESHARE;
goto out; goto out;
...@@ -5335,11 +5286,14 @@ static int smb2_rename(struct ksmbd_work *work, ...@@ -5335,11 +5286,14 @@ static int smb2_rename(struct ksmbd_work *work,
} }
ksmbd_debug(SMB, "new name %s\n", new_name); ksmbd_debug(SMB, "new name %s\n", new_name);
rc = ksmbd_vfs_kern_path(new_name, 0, &path, 1); rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1);
if (rc) if (rc) {
if (rc != -ENOENT)
goto out;
file_present = false; file_present = false;
else } else {
path_put(&path); path_put(&path);
}
if (ksmbd_share_veto_filename(share, new_name)) { if (ksmbd_share_veto_filename(share, new_name)) {
rc = -ENOENT; rc = -ENOENT;
...@@ -5409,11 +5363,14 @@ static int smb2_create_link(struct ksmbd_work *work, ...@@ -5409,11 +5363,14 @@ static int smb2_create_link(struct ksmbd_work *work,
} }
ksmbd_debug(SMB, "target name is %s\n", target_name); ksmbd_debug(SMB, "target name is %s\n", target_name);
rc = ksmbd_vfs_kern_path(link_name, 0, &path, 0); rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0);
if (rc) if (rc) {
if (rc != -ENOENT)
goto out;
file_present = false; file_present = false;
else } else {
path_put(&path); path_put(&path);
}
if (file_info->ReplaceIfExists) { if (file_info->ReplaceIfExists) {
if (file_present) { if (file_present) {
...@@ -5573,7 +5530,7 @@ static int set_file_allocation_info(struct ksmbd_work *work, ...@@ -5573,7 +5530,7 @@ static int set_file_allocation_info(struct ksmbd_work *work,
* inode size is retained by backup inode size. * inode size is retained by backup inode size.
*/ */
size = i_size_read(inode); size = i_size_read(inode);
rc = ksmbd_vfs_truncate(work, NULL, fp, alloc_blks * 512); rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512);
if (rc) { if (rc) {
pr_err("truncate failed! filename : %s, err %d\n", pr_err("truncate failed! filename : %s, err %d\n",
fp->filename, rc); fp->filename, rc);
...@@ -5610,7 +5567,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -5610,7 +5567,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) {
ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n", ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n",
fp->filename, newsize); fp->filename, newsize);
rc = ksmbd_vfs_truncate(work, NULL, fp, newsize); rc = ksmbd_vfs_truncate(work, fp, newsize);
if (rc) { if (rc) {
ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n", ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n",
fp->filename, rc); fp->filename, rc);
...@@ -5887,7 +5844,7 @@ int smb2_set_info(struct ksmbd_work *work) ...@@ -5887,7 +5844,7 @@ int smb2_set_info(struct ksmbd_work *work)
return 0; return 0;
err_out: err_out:
if (rc == -EACCES || rc == -EPERM) if (rc == -EACCES || rc == -EPERM || rc == -EXDEV)
rsp->hdr.Status = STATUS_ACCESS_DENIED; rsp->hdr.Status = STATUS_ACCESS_DENIED;
else if (rc == -EINVAL) else if (rc == -EINVAL)
rsp->hdr.Status = STATUS_INVALID_PARAMETER; rsp->hdr.Status = STATUS_INVALID_PARAMETER;
......
...@@ -129,16 +129,22 @@ int ksmbd_lookup_protocol_idx(char *str) ...@@ -129,16 +129,22 @@ int ksmbd_lookup_protocol_idx(char *str)
* *
* check for valid smb signature and packet direction(request/response) * check for valid smb signature and packet direction(request/response)
* *
* Return: 0 on success, otherwise 1 * Return: 0 on success, otherwise -EINVAL
*/ */
int ksmbd_verify_smb_message(struct ksmbd_work *work) int ksmbd_verify_smb_message(struct ksmbd_work *work)
{ {
struct smb2_hdr *smb2_hdr = work->request_buf; struct smb2_hdr *smb2_hdr = work->request_buf + work->next_smb2_rcv_hdr_off;
struct smb_hdr *hdr;
if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER)
return ksmbd_smb2_check_message(work); return ksmbd_smb2_check_message(work);
return 0; hdr = work->request_buf;
if (*(__le32 *)hdr->Protocol == SMB1_PROTO_NUMBER &&
hdr->Command == SMB_COM_NEGOTIATE)
return 0;
return -EINVAL;
} }
/** /**
...@@ -265,7 +271,6 @@ static int ksmbd_negotiate_smb_dialect(void *buf) ...@@ -265,7 +271,6 @@ static int ksmbd_negotiate_smb_dialect(void *buf)
return BAD_PROT_ID; return BAD_PROT_ID;
} }
#define SMB_COM_NEGOTIATE 0x72
int ksmbd_init_smb_server(struct ksmbd_work *work) int ksmbd_init_smb_server(struct ksmbd_work *work)
{ {
struct ksmbd_conn *conn = work->conn; struct ksmbd_conn *conn = work->conn;
......
...@@ -210,6 +210,7 @@ ...@@ -210,6 +210,7 @@
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)
#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) #define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff)
#define SMB_COM_NEGOTIATE 0x72
#define SMB1_CLIENT_GUID_SIZE (16) #define SMB1_CLIENT_GUID_SIZE (16)
struct smb_hdr { struct smb_hdr {
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include <linux/sched/xacct.h> #include <linux/sched/xacct.h>
#include <linux/crc32c.h> #include <linux/crc32c.h>
#include "../internal.h" /* for vfs_path_lookup */
#include "glob.h" #include "glob.h"
#include "oplock.h" #include "oplock.h"
#include "connection.h" #include "connection.h"
...@@ -44,7 +46,6 @@ static char *extract_last_component(char *path) ...@@ -44,7 +46,6 @@ static char *extract_last_component(char *path)
p++; p++;
} else { } else {
p = NULL; p = NULL;
pr_err("Invalid path %s\n", path);
} }
return p; return p;
} }
...@@ -155,7 +156,7 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, ...@@ -155,7 +156,7 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
/** /**
* ksmbd_vfs_create() - vfs helper for smb create file * ksmbd_vfs_create() - vfs helper for smb create file
* @work: work * @work: work
* @name: file name * @name: file name that is relative to share
* @mode: file create mode * @mode: file create mode
* *
* Return: 0 on success, otherwise error * Return: 0 on success, otherwise error
...@@ -166,7 +167,8 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) ...@@ -166,7 +167,8 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
struct dentry *dentry; struct dentry *dentry;
int err; int err;
dentry = kern_path_create(AT_FDCWD, name, &path, 0); dentry = ksmbd_vfs_kern_path_create(work, name,
LOOKUP_NO_SYMLINKS, &path);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
err = PTR_ERR(dentry); err = PTR_ERR(dentry);
if (err != -ENOENT) if (err != -ENOENT)
...@@ -191,7 +193,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) ...@@ -191,7 +193,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
/** /**
* ksmbd_vfs_mkdir() - vfs helper for smb create directory * ksmbd_vfs_mkdir() - vfs helper for smb create directory
* @work: work * @work: work
* @name: directory name * @name: directory name that is relative to share
* @mode: directory create mode * @mode: directory create mode
* *
* Return: 0 on success, otherwise error * Return: 0 on success, otherwise error
...@@ -203,7 +205,9 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) ...@@ -203,7 +205,9 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
struct dentry *dentry; struct dentry *dentry;
int err; int err;
dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); dentry = ksmbd_vfs_kern_path_create(work, name,
LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
&path);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
err = PTR_ERR(dentry); err = PTR_ERR(dentry);
if (err != -EEXIST) if (err != -EEXIST)
...@@ -578,7 +582,7 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) ...@@ -578,7 +582,7 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id)
/** /**
* ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink
* @name: absolute directory or file name * @name: directory or file name that is relative to share
* *
* Return: 0 on success, otherwise error * Return: 0 on success, otherwise error
*/ */
...@@ -588,16 +592,11 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) ...@@ -588,16 +592,11 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name)
struct path path; struct path path;
struct dentry *parent; struct dentry *parent;
int err; int err;
int flags = 0;
if (ksmbd_override_fsids(work)) if (ksmbd_override_fsids(work))
return -ENOMEM; return -ENOMEM;
if (test_share_config_flag(work->tcon->share_conf, err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false);
KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS))
flags = LOOKUP_FOLLOW;
err = kern_path(name, flags, &path);
if (err) { if (err) {
ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); ksmbd_debug(VFS, "can't get %s, err %d\n", name, err);
ksmbd_revert_fsids(work); ksmbd_revert_fsids(work);
...@@ -642,7 +641,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) ...@@ -642,7 +641,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name)
/** /**
* ksmbd_vfs_link() - vfs helper for creating smb hardlink * ksmbd_vfs_link() - vfs helper for creating smb hardlink
* @oldname: source file name * @oldname: source file name
* @newname: hardlink name * @newname: hardlink name that is relative to share
* *
* Return: 0 on success, otherwise error * Return: 0 on success, otherwise error
*/ */
...@@ -652,24 +651,20 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, ...@@ -652,24 +651,20 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
struct path oldpath, newpath; struct path oldpath, newpath;
struct dentry *dentry; struct dentry *dentry;
int err; int err;
int flags = 0;
if (ksmbd_override_fsids(work)) if (ksmbd_override_fsids(work))
return -ENOMEM; return -ENOMEM;
if (test_share_config_flag(work->tcon->share_conf, err = kern_path(oldname, LOOKUP_NO_SYMLINKS, &oldpath);
KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS))
flags = LOOKUP_FOLLOW;
err = kern_path(oldname, flags, &oldpath);
if (err) { if (err) {
pr_err("cannot get linux path for %s, err = %d\n", pr_err("cannot get linux path for %s, err = %d\n",
oldname, err); oldname, err);
goto out1; goto out1;
} }
dentry = kern_path_create(AT_FDCWD, newname, &newpath, dentry = ksmbd_vfs_kern_path_create(work, newname,
flags | LOOKUP_REVAL); LOOKUP_NO_SYMLINKS | LOOKUP_REVAL,
&newpath);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
err = PTR_ERR(dentry); err = PTR_ERR(dentry);
pr_err("path create err for %s, err %d\n", newname, err); pr_err("path create err for %s, err %d\n", newname, err);
...@@ -788,21 +783,19 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -788,21 +783,19 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
struct dentry *src_dent, *trap_dent, *src_child; struct dentry *src_dent, *trap_dent, *src_child;
char *dst_name; char *dst_name;
int err; int err;
int flags;
dst_name = extract_last_component(newname); dst_name = extract_last_component(newname);
if (!dst_name) if (!dst_name) {
return -EINVAL; dst_name = newname;
newname = "";
}
src_dent_parent = dget_parent(fp->filp->f_path.dentry); src_dent_parent = dget_parent(fp->filp->f_path.dentry);
src_dent = fp->filp->f_path.dentry; src_dent = fp->filp->f_path.dentry;
flags = LOOKUP_DIRECTORY; err = ksmbd_vfs_kern_path(work, newname,
if (test_share_config_flag(work->tcon->share_conf, LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) &dst_path, false);
flags |= LOOKUP_FOLLOW;
err = kern_path(newname, flags, &dst_path);
if (err) { if (err) {
ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err);
goto out; goto out;
...@@ -848,61 +841,43 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -848,61 +841,43 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
/** /**
* ksmbd_vfs_truncate() - vfs helper for smb file truncate * ksmbd_vfs_truncate() - vfs helper for smb file truncate
* @work: work * @work: work
* @name: old filename
* @fid: file id of old file * @fid: file id of old file
* @size: truncate to given size * @size: truncate to given size
* *
* Return: 0 on success, otherwise error * Return: 0 on success, otherwise error
*/ */
int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, int ksmbd_vfs_truncate(struct ksmbd_work *work,
struct ksmbd_file *fp, loff_t size) struct ksmbd_file *fp, loff_t size)
{ {
struct path path;
int err = 0; int err = 0;
struct file *filp;
if (name) { filp = fp->filp;
err = kern_path(name, 0, &path);
if (err) {
pr_err("cannot get linux path for %s, err %d\n",
name, err);
return err;
}
err = vfs_truncate(&path, size);
if (err)
pr_err("truncate failed for %s err %d\n",
name, err);
path_put(&path);
} else {
struct file *filp;
filp = fp->filp;
/* Do we need to break any of a levelII oplock? */
smb_break_all_levII_oplock(work, fp, 1);
if (!work->tcon->posix_extensions) { /* Do we need to break any of a levelII oplock? */
struct inode *inode = file_inode(filp); smb_break_all_levII_oplock(work, fp, 1);
if (size < inode->i_size) { if (!work->tcon->posix_extensions) {
err = check_lock_range(filp, size, struct inode *inode = file_inode(filp);
inode->i_size - 1, WRITE);
} else {
err = check_lock_range(filp, inode->i_size,
size - 1, WRITE);
}
if (err) { if (size < inode->i_size) {
pr_err("failed due to lock\n"); err = check_lock_range(filp, size,
return -EAGAIN; inode->i_size - 1, WRITE);
} } else {
err = check_lock_range(filp, inode->i_size,
size - 1, WRITE);
} }
err = vfs_truncate(&filp->f_path, size); if (err) {
if (err) pr_err("failed due to lock\n");
pr_err("truncate failed for filename : %s err %d\n", return -EAGAIN;
fp->filename, err); }
} }
err = vfs_truncate(&filp->f_path, size);
if (err)
pr_err("truncate failed for filename : %s err %d\n",
fp->filename, err);
return err; return err;
} }
...@@ -1220,22 +1195,25 @@ static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen) ...@@ -1220,22 +1195,25 @@ static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen)
/** /**
* ksmbd_vfs_kern_path() - lookup a file and get path info * ksmbd_vfs_kern_path() - lookup a file and get path info
* @name: name of file for lookup * @name: file path that is relative to share
* @flags: lookup flags * @flags: lookup flags
* @path: if lookup succeed, return path info * @path: if lookup succeed, return path info
* @caseless: caseless filename lookup * @caseless: caseless filename lookup
* *
* Return: 0 on success, otherwise error * Return: 0 on success, otherwise error
*/ */
int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
bool caseless) unsigned int flags, struct path *path, bool caseless)
{ {
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
int err; int err;
if (name[0] != '/') flags |= LOOKUP_BENEATH;
return -EINVAL; err = vfs_path_lookup(share_conf->vfs_path.dentry,
share_conf->vfs_path.mnt,
err = kern_path(name, flags, path); name,
flags,
path);
if (!err) if (!err)
return 0; return 0;
...@@ -1249,11 +1227,10 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, ...@@ -1249,11 +1227,10 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
return -ENOMEM; return -ENOMEM;
path_len = strlen(filepath); path_len = strlen(filepath);
remain_len = path_len - 1; remain_len = path_len;
err = kern_path("/", flags, &parent); parent = share_conf->vfs_path;
if (err) path_get(&parent);
goto out;
while (d_can_lookup(parent.dentry)) { while (d_can_lookup(parent.dentry)) {
char *filename = filepath + path_len - remain_len; char *filename = filepath + path_len - remain_len;
...@@ -1266,21 +1243,21 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, ...@@ -1266,21 +1243,21 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
err = ksmbd_vfs_lookup_in_dir(&parent, filename, err = ksmbd_vfs_lookup_in_dir(&parent, filename,
filename_len); filename_len);
if (err) { path_put(&parent);
path_put(&parent); if (err)
goto out; goto out;
}
path_put(&parent);
next[0] = '\0'; next[0] = '\0';
err = kern_path(filepath, flags, &parent); err = vfs_path_lookup(share_conf->vfs_path.dentry,
share_conf->vfs_path.mnt,
filepath,
flags,
&parent);
if (err) if (err)
goto out; goto out;
else if (is_last) {
if (is_last) { *path = parent;
path->mnt = parent.mnt;
path->dentry = parent.dentry;
goto out; goto out;
} }
...@@ -1296,6 +1273,23 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, ...@@ -1296,6 +1273,23 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
return err; return err;
} }
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
const char *name,
unsigned int flags,
struct path *path)
{
char *abs_name;
struct dentry *dent;
abs_name = convert_to_unix_name(work->tcon->share_conf, name);
if (!abs_name)
return ERR_PTR(-ENOMEM);
dent = kern_path_create(AT_FDCWD, abs_name, path, flags);
kfree(abs_name);
return dent;
}
int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
struct dentry *dentry) struct dentry *dentry)
{ {
......
...@@ -126,7 +126,7 @@ int ksmbd_vfs_link(struct ksmbd_work *work, ...@@ -126,7 +126,7 @@ int ksmbd_vfs_link(struct ksmbd_work *work,
int ksmbd_vfs_getattr(struct path *path, struct kstat *stat); int ksmbd_vfs_getattr(struct path *path, struct kstat *stat);
int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
char *newname); char *newname);
int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, int ksmbd_vfs_truncate(struct ksmbd_work *work,
struct ksmbd_file *fp, loff_t size); struct ksmbd_file *fp, loff_t size);
struct srv_copychunk; struct srv_copychunk;
int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
...@@ -152,8 +152,13 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, ...@@ -152,8 +152,13 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
size_t *xattr_stream_name_size, int s_type); size_t *xattr_stream_name_size, int s_type);
int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
struct dentry *dentry, char *attr_name); struct dentry *dentry, char *attr_name);
int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, int ksmbd_vfs_kern_path(struct ksmbd_work *work,
char *name, unsigned int flags, struct path *path,
bool caseless); bool caseless);
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
const char *name,
unsigned int flags,
struct path *path);
int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); int ksmbd_vfs_empty_dir(struct ksmbd_file *fp);
void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option);
int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
......
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