Commit 3ea4fcc5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'cifs-bug-fixes-for-4.13' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French:
 "First set of CIFS/SMB3 fixes for the merge window. Also improves POSIX
  character mapping for SMB3"

* tag 'cifs-bug-fixes-for-4.13' of git://git.samba.org/sfrench/cifs-2.6:
  CIFS: fix circular locking dependency
  cifs: set oparms.create_options rather than or'ing in CREATE_OPEN_BACKUP_INTENT
  cifs: Do not modify mid entry after submitting I/O in cifs_call_async
  CIFS: add SFM mapping for 0x01-0x1F
  cifs: hide unused functions
  cifs: Use smb 2 - 3 and cifsacl mount options getacl functions
  cifs: prototype declaration and definition for smb 2 - 3 and cifsacl mount options
  CIFS: add CONFIG_CIFS_DEBUG_KEYS to dump encryption keys
  cifs: set mapping error when page writeback fails in writepage or launder_pages
  SMB3: Enable encryption for SMB3.1.1
parents 1a86fc75 966681c9
...@@ -146,6 +146,15 @@ config CIFS_DEBUG2 ...@@ -146,6 +146,15 @@ config CIFS_DEBUG2
option can be turned off unless you are debugging option can be turned off unless you are debugging
cifs problems. If unsure, say N. cifs problems. If unsure, say N.
config CIFS_DEBUG_DUMP_KEYS
bool "Dump encryption keys for offline decryption (Unsafe)"
depends on CIFS_DEBUG && CIFS_SMB2
help
Enabling this will dump the encryption and decryption keys
used to communicate on an encrypted share connection on the
console. This allows Wireshark to decrypt and dissect
encrypted network captures. Enable this carefully.
config CIFS_DFS_UPCALL config CIFS_DFS_UPCALL
bool "DFS feature support" bool "DFS feature support"
depends on CIFS && KEYS depends on CIFS && KEYS
......
...@@ -79,6 +79,10 @@ convert_sfu_char(const __u16 src_char, char *target) ...@@ -79,6 +79,10 @@ convert_sfu_char(const __u16 src_char, char *target)
static bool static bool
convert_sfm_char(const __u16 src_char, char *target) convert_sfm_char(const __u16 src_char, char *target)
{ {
if (src_char >= 0xF001 && src_char <= 0xF01F) {
*target = src_char - 0xF000;
return true;
}
switch (src_char) { switch (src_char) {
case SFM_COLON: case SFM_COLON:
*target = ':'; *target = ':';
...@@ -417,6 +421,10 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string) ...@@ -417,6 +421,10 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
{ {
__le16 dest_char; __le16 dest_char;
if (src_char >= 0x01 && src_char <= 0x1F) {
dest_char = cpu_to_le16(src_char + 0xF000);
return dest_char;
}
switch (src_char) { switch (src_char) {
case ':': case ':':
dest_char = cpu_to_le16(SFM_COLON); dest_char = cpu_to_le16(SFM_COLON);
......
...@@ -2234,14 +2234,16 @@ cifs_writepage_locked(struct page *page, struct writeback_control *wbc) ...@@ -2234,14 +2234,16 @@ cifs_writepage_locked(struct page *page, struct writeback_control *wbc)
set_page_writeback(page); set_page_writeback(page);
retry_write: retry_write:
rc = cifs_partialpagewrite(page, 0, PAGE_SIZE); rc = cifs_partialpagewrite(page, 0, PAGE_SIZE);
if (rc == -EAGAIN && wbc->sync_mode == WB_SYNC_ALL) if (rc == -EAGAIN) {
if (wbc->sync_mode == WB_SYNC_ALL)
goto retry_write; goto retry_write;
else if (rc == -EAGAIN)
redirty_page_for_writepage(wbc, page); redirty_page_for_writepage(wbc, page);
else if (rc != 0) } else if (rc != 0) {
SetPageError(page); SetPageError(page);
else mapping_set_error(page->mapping, rc);
} else {
SetPageUptodate(page); SetPageUptodate(page);
}
end_page_writeback(page); end_page_writeback(page);
put_page(page); put_page(page);
free_xid(xid); free_xid(xid);
...@@ -2810,12 +2812,12 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) ...@@ -2810,12 +2812,12 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from)
struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
ssize_t rc; ssize_t rc;
inode_lock(inode);
/* /*
* We need to hold the sem to be sure nobody modifies lock list * We need to hold the sem to be sure nobody modifies lock list
* with a brlock that prevents writing. * with a brlock that prevents writing.
*/ */
down_read(&cinode->lock_sem); down_read(&cinode->lock_sem);
inode_lock(inode);
rc = generic_write_checks(iocb, from); rc = generic_write_checks(iocb, from);
if (rc <= 0) if (rc <= 0)
...@@ -2828,11 +2830,11 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) ...@@ -2828,11 +2830,11 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from)
else else
rc = -EACCES; rc = -EACCES;
out: out:
up_read(&cinode->lock_sem);
inode_unlock(inode); inode_unlock(inode);
if (rc > 0) if (rc > 0)
rc = generic_write_sync(iocb, rc); rc = generic_write_sync(iocb, rc);
up_read(&cinode->lock_sem);
return rc; return rc;
} }
......
...@@ -1288,6 +1288,108 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1288,6 +1288,108 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
#ifdef CONFIG_CIFS_ACL
static struct cifs_ntsd *
get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
const struct cifs_fid *cifsfid, u32 *pacllen)
{
struct cifs_ntsd *pntsd = NULL;
unsigned int xid;
int rc = -EOPNOTSUPP;
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return ERR_CAST(tlink);
xid = get_xid();
cifs_dbg(FYI, "trying to get acl\n");
rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid,
cifsfid->volatile_fid, (void **)&pntsd, pacllen);
free_xid(xid);
cifs_put_tlink(tlink);
cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
if (rc)
return ERR_PTR(rc);
return pntsd;
}
static struct cifs_ntsd *
get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
const char *path, u32 *pacllen)
{
struct cifs_ntsd *pntsd = NULL;
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
unsigned int xid;
int rc;
struct cifs_tcon *tcon;
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
struct cifs_fid fid;
struct cifs_open_parms oparms;
__le16 *utf16_path;
cifs_dbg(FYI, "get smb3 acl for path %s\n", path);
if (IS_ERR(tlink))
return ERR_CAST(tlink);
tcon = tlink_tcon(tlink);
xid = get_xid();
if (backup_cred(cifs_sb))
oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
else
oparms.create_options = 0;
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
if (!utf16_path)
return ERR_PTR(-ENOMEM);
oparms.tcon = tcon;
oparms.desired_access = READ_CONTROL;
oparms.disposition = FILE_OPEN;
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
kfree(utf16_path);
if (!rc) {
rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
fid.volatile_fid, (void **)&pntsd, pacllen);
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
}
cifs_put_tlink(tlink);
free_xid(xid);
cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
if (rc)
return ERR_PTR(rc);
return pntsd;
}
/* Retrieve an ACL from the server */
static struct cifs_ntsd *
get_smb2_acl(struct cifs_sb_info *cifs_sb,
struct inode *inode, const char *path,
u32 *pacllen)
{
struct cifs_ntsd *pntsd = NULL;
struct cifsFileInfo *open_file = NULL;
if (inode)
open_file = find_readable_file(CIFS_I(inode), true);
if (!open_file)
return get_smb2_acl_by_path(cifs_sb, path, pacllen);
pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen);
cifsFileInfo_put(open_file);
return pntsd;
}
#endif
static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
loff_t offset, loff_t len, bool keep_size) loff_t offset, loff_t len, bool keep_size)
{ {
...@@ -2393,6 +2495,11 @@ struct smb_version_operations smb20_operations = { ...@@ -2393,6 +2495,11 @@ struct smb_version_operations smb20_operations = {
.dir_needs_close = smb2_dir_needs_close, .dir_needs_close = smb2_dir_needs_close,
.get_dfs_refer = smb2_get_dfs_refer, .get_dfs_refer = smb2_get_dfs_refer,
.select_sectype = smb2_select_sectype, .select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_ACL
.get_acl = get_smb2_acl,
.get_acl_by_fid = get_smb2_acl_by_fid,
/* .set_acl = set_smb3_acl, */
#endif /* CIFS_ACL */
}; };
struct smb_version_operations smb21_operations = { struct smb_version_operations smb21_operations = {
...@@ -2477,6 +2584,11 @@ struct smb_version_operations smb21_operations = { ...@@ -2477,6 +2584,11 @@ struct smb_version_operations smb21_operations = {
.enum_snapshots = smb3_enum_snapshots, .enum_snapshots = smb3_enum_snapshots,
.get_dfs_refer = smb2_get_dfs_refer, .get_dfs_refer = smb2_get_dfs_refer,
.select_sectype = smb2_select_sectype, .select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_ACL
.get_acl = get_smb2_acl,
.get_acl_by_fid = get_smb2_acl_by_fid,
/* .set_acl = set_smb3_acl, */
#endif /* CIFS_ACL */
}; };
struct smb_version_operations smb30_operations = { struct smb_version_operations smb30_operations = {
...@@ -2571,6 +2683,11 @@ struct smb_version_operations smb30_operations = { ...@@ -2571,6 +2683,11 @@ struct smb_version_operations smb30_operations = {
.receive_transform = smb3_receive_transform, .receive_transform = smb3_receive_transform,
.get_dfs_refer = smb2_get_dfs_refer, .get_dfs_refer = smb2_get_dfs_refer,
.select_sectype = smb2_select_sectype, .select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_ACL
.get_acl = get_smb2_acl,
.get_acl_by_fid = get_smb2_acl_by_fid,
/* .set_acl = set_smb3_acl, */
#endif /* CIFS_ACL */
}; };
#ifdef CONFIG_CIFS_SMB311 #ifdef CONFIG_CIFS_SMB311
...@@ -2753,7 +2870,7 @@ struct smb_version_values smb302_values = { ...@@ -2753,7 +2870,7 @@ struct smb_version_values smb302_values = {
struct smb_version_values smb311_values = { struct smb_version_values smb311_values = {
.version_string = SMB311_VERSION_STRING, .version_string = SMB311_VERSION_STRING,
.protocol_id = SMB311_PROT_ID, .protocol_id = SMB311_PROT_ID,
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES, .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION,
.large_lock_type = 0, .large_lock_type = 0,
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
......
...@@ -2081,8 +2081,9 @@ validate_and_copy_buf(unsigned int offset, unsigned int buffer_length, ...@@ -2081,8 +2081,9 @@ validate_and_copy_buf(unsigned int offset, unsigned int buffer_length,
static int static int
query_info(const unsigned int xid, struct cifs_tcon *tcon, query_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u8 info_class, u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
size_t output_len, size_t min_len, void *data) u32 additional_info, size_t output_len, size_t min_len, void **data,
u32 *dlen)
{ {
struct smb2_query_info_req *req; struct smb2_query_info_req *req;
struct smb2_query_info_rsp *rsp = NULL; struct smb2_query_info_rsp *rsp = NULL;
...@@ -2108,10 +2109,11 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2108,10 +2109,11 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
if (encryption_required(tcon)) if (encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ; flags |= CIFS_TRANSFORM_REQ;
req->InfoType = SMB2_O_INFO_FILE; req->InfoType = info_type;
req->FileInfoClass = info_class; req->FileInfoClass = info_class;
req->PersistentFileId = persistent_fid; req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid; req->VolatileFileId = volatile_fid;
req->AdditionalInformation = cpu_to_le32(additional_info);
/* 4 for rfc1002 length field and 1 for Buffer */ /* 4 for rfc1002 length field and 1 for Buffer */
req->InputBufferOffset = req->InputBufferOffset =
cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4); cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4);
...@@ -2130,24 +2132,51 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2130,24 +2132,51 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
goto qinf_exit; goto qinf_exit;
} }
if (dlen) {
*dlen = le32_to_cpu(rsp->OutputBufferLength);
if (!*data) {
*data = kmalloc(*dlen, GFP_KERNEL);
if (!*data) {
cifs_dbg(VFS,
"Error %d allocating memory for acl\n",
rc);
*dlen = 0;
goto qinf_exit;
}
}
}
rc = validate_and_copy_buf(le16_to_cpu(rsp->OutputBufferOffset), rc = validate_and_copy_buf(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), le32_to_cpu(rsp->OutputBufferLength),
&rsp->hdr, min_len, data); &rsp->hdr, min_len, *data);
qinf_exit: qinf_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
return rc; return rc;
} }
int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
{
return query_info(xid, tcon, persistent_fid, volatile_fid,
FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0,
sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
sizeof(struct smb2_file_all_info), (void **)&data,
NULL);
}
int int
SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u64 persistent_fid, u64 volatile_fid,
struct smb2_file_all_info *data) void **data, u32 *plen)
{ {
__u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO;
*plen = 0;
return query_info(xid, tcon, persistent_fid, volatile_fid, return query_info(xid, tcon, persistent_fid, volatile_fid,
FILE_ALL_INFORMATION, 0, SMB2_O_INFO_SECURITY, additional_info,
sizeof(struct smb2_file_all_info) + PATH_MAX * 2, SMB2_MAX_BUFFER_SIZE,
sizeof(struct smb2_file_all_info), data); sizeof(struct smb2_file_all_info), data, plen);
} }
int int
...@@ -2155,9 +2184,10 @@ SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2155,9 +2184,10 @@ SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid) u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid)
{ {
return query_info(xid, tcon, persistent_fid, volatile_fid, return query_info(xid, tcon, persistent_fid, volatile_fid,
FILE_INTERNAL_INFORMATION, FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0,
sizeof(struct smb2_file_internal_info),
sizeof(struct smb2_file_internal_info), sizeof(struct smb2_file_internal_info),
sizeof(struct smb2_file_internal_info), uniqueid); (void **)&uniqueid, NULL);
} }
/* /*
......
...@@ -135,6 +135,9 @@ extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -135,6 +135,9 @@ extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id, u64 persistent_file_id, u64 volatile_file_id,
struct smb2_file_all_info *data); struct smb2_file_all_info *data);
extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id,
void **data, unsigned int *plen);
extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u64 persistent_fid, u64 volatile_fid,
__le64 *uniqueid); __le64 *uniqueid);
......
...@@ -335,9 +335,31 @@ generate_smb3signingkey(struct cifs_ses *ses, ...@@ -335,9 +335,31 @@ generate_smb3signingkey(struct cifs_ses *ses,
if (rc) if (rc)
return rc; return rc;
return generate_key(ses, ptriplet->decryption.label, rc = generate_key(ses, ptriplet->decryption.label,
ptriplet->decryption.context, ptriplet->decryption.context,
ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
if (rc)
return rc;
#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__);
/*
* The session id is opaque in terms of endianness, so we can't
* print it as a long long. we dump it as we got it on the wire
*/
cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid),
&ses->Suid);
cifs_dbg(VFS, "Session Key %*ph\n",
SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response);
cifs_dbg(VFS, "Signing Key %*ph\n",
SMB3_SIGN_KEY_SIZE, ses->smb3signingkey);
cifs_dbg(VFS, "ServerIn Key %*ph\n",
SMB3_SIGN_KEY_SIZE, ses->smb3encryptionkey);
cifs_dbg(VFS, "ServerOut Key %*ph\n",
SMB3_SIGN_KEY_SIZE, ses->smb3decryptionkey);
#endif
return rc;
} }
int int
......
...@@ -536,11 +536,14 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, ...@@ -536,11 +536,14 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
list_add_tail(&mid->qhead, &server->pending_mid_q); list_add_tail(&mid->qhead, &server->pending_mid_q);
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
/*
* Need to store the time in mid before calling I/O. For call_async,
* I/O response may come back and free the mid entry on another thread.
*/
cifs_save_when_sent(mid);
cifs_in_send_inc(server); cifs_in_send_inc(server);
rc = smb_send_rqst(server, rqst, flags); rc = smb_send_rqst(server, rqst, flags);
cifs_in_send_dec(server); cifs_in_send_dec(server);
cifs_save_when_sent(mid);
if (rc < 0) { if (rc < 0) {
server->sequence_number -= 2; server->sequence_number -= 2;
......
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