Commit 9aa3ba91 authored by Pavel Shilovsky's avatar Pavel Shilovsky

Update 3.12 sources from stable (v3.12.26)

parent 184bd032
...@@ -290,7 +290,8 @@ int ...@@ -290,7 +290,8 @@ int
cifsConvertToUTF16(__le16 *target, const char *source, int srclen, cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
const struct nls_table *cp, int mapChars) const struct nls_table *cp, int mapChars)
{ {
int i, j, charlen; int i, charlen;
int j = 0;
char src_char; char src_char;
__le16 dst_char; __le16 dst_char;
wchar_t tmp; wchar_t tmp;
...@@ -298,12 +299,11 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen, ...@@ -298,12 +299,11 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
if (!mapChars) if (!mapChars)
return cifs_strtoUTF16(target, source, PATH_MAX, cp); return cifs_strtoUTF16(target, source, PATH_MAX, cp);
for (i = 0, j = 0; i < srclen; j++) { for (i = 0; i < srclen; j++) {
src_char = source[i]; src_char = source[i];
charlen = 1; charlen = 1;
switch (src_char) { switch (src_char) {
case 0: case 0:
put_unaligned(0, &target[j]);
goto ctoUTF16_out; goto ctoUTF16_out;
case ':': case ':':
dst_char = cpu_to_le16(UNI_COLON); dst_char = cpu_to_le16(UNI_COLON);
...@@ -350,6 +350,7 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen, ...@@ -350,6 +350,7 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
} }
ctoUTF16_out: ctoUTF16_out:
put_unaligned(0, &target[j]); /* Null terminate target unicode string */
return j; return j;
} }
......
...@@ -87,10 +87,6 @@ extern mempool_t *cifs_mid_poolp; ...@@ -87,10 +87,6 @@ extern mempool_t *cifs_mid_poolp;
struct workqueue_struct *cifsiod_wq; struct workqueue_struct *cifsiod_wq;
#ifdef CONFIG_CIFS_SMB2
__u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
#endif
/* /*
* Bumps refcount for cifs super block. * Bumps refcount for cifs super block.
* Note that it should be only called if a referece to VFS super block is * Note that it should be only called if a referece to VFS super block is
...@@ -253,6 +249,11 @@ cifs_alloc_inode(struct super_block *sb) ...@@ -253,6 +249,11 @@ cifs_alloc_inode(struct super_block *sb)
cifs_set_oplock_level(cifs_inode, 0); cifs_set_oplock_level(cifs_inode, 0);
cifs_inode->delete_pending = false; cifs_inode->delete_pending = false;
cifs_inode->invalid_mapping = false; cifs_inode->invalid_mapping = false;
clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cifs_inode->flags);
clear_bit(CIFS_INODE_PENDING_WRITERS, &cifs_inode->flags);
clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cifs_inode->flags);
spin_lock_init(&cifs_inode->writers_lock);
cifs_inode->writers = 0;
cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */
cifs_inode->server_eof = 0; cifs_inode->server_eof = 0;
cifs_inode->uniqueid = 0; cifs_inode->uniqueid = 0;
...@@ -734,19 +735,26 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -734,19 +735,26 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos) unsigned long nr_segs, loff_t pos)
{ {
struct inode *inode = file_inode(iocb->ki_filp); struct inode *inode = file_inode(iocb->ki_filp);
struct cifsInodeInfo *cinode = CIFS_I(inode);
ssize_t written; ssize_t written;
int rc; int rc;
written = cifs_get_writer(cinode);
if (written)
return written;
written = generic_file_aio_write(iocb, iov, nr_segs, pos); written = generic_file_aio_write(iocb, iov, nr_segs, pos);
if (CIFS_CACHE_WRITE(CIFS_I(inode))) if (CIFS_CACHE_WRITE(CIFS_I(inode)))
return written; goto out;
rc = filemap_fdatawrite(inode->i_mapping); rc = filemap_fdatawrite(inode->i_mapping);
if (rc) if (rc)
cifs_dbg(FYI, "cifs_file_aio_write: %d rc on %p inode\n", cifs_dbg(FYI, "cifs_file_aio_write: %d rc on %p inode\n",
rc, inode); rc, inode);
out:
cifs_put_writer(cinode);
return written; return written;
} }
...@@ -1183,10 +1191,6 @@ init_cifs(void) ...@@ -1183,10 +1191,6 @@ init_cifs(void)
spin_lock_init(&cifs_file_list_lock); spin_lock_init(&cifs_file_list_lock);
spin_lock_init(&GlobalMid_Lock); spin_lock_init(&GlobalMid_Lock);
#ifdef CONFIG_CIFS_SMB2
get_random_bytes(cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
#endif
if (cifs_max_pending < 2) { if (cifs_max_pending < 2) {
cifs_max_pending = 2; cifs_max_pending = 2;
cifs_dbg(FYI, "cifs_max_pending set to min of 2\n"); cifs_dbg(FYI, "cifs_max_pending set to min of 2\n");
......
...@@ -228,6 +228,8 @@ struct smb_version_operations { ...@@ -228,6 +228,8 @@ struct smb_version_operations {
/* verify the message */ /* verify the message */
int (*check_message)(char *, unsigned int); int (*check_message)(char *, unsigned int);
bool (*is_oplock_break)(char *, struct TCP_Server_Info *); bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
void (*downgrade_oplock)(struct TCP_Server_Info *,
struct cifsInodeInfo *, bool);
/* process transaction2 response */ /* process transaction2 response */
bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *, bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *,
char *, int); char *, int);
...@@ -389,6 +391,7 @@ struct smb_version_operations { ...@@ -389,6 +391,7 @@ struct smb_version_operations {
const char *, u32 *); const char *, u32 *);
int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *, int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *,
int); int);
int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);
}; };
struct smb_version_values { struct smb_version_values {
...@@ -544,6 +547,7 @@ struct TCP_Server_Info { ...@@ -544,6 +547,7 @@ struct TCP_Server_Info {
int echo_credits; /* echo reserved slots */ int echo_credits; /* echo reserved slots */
int oplock_credits; /* oplock break reserved slots */ int oplock_credits; /* oplock break reserved slots */
bool echoes:1; /* enable echoes */ bool echoes:1; /* enable echoes */
__u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* Client GUID */
#endif #endif
u16 dialect; /* dialect index that server chose */ u16 dialect; /* dialect index that server chose */
bool oplocks:1; /* enable oplocks */ bool oplocks:1; /* enable oplocks */
...@@ -1080,6 +1084,12 @@ struct cifsInodeInfo { ...@@ -1080,6 +1084,12 @@ struct cifsInodeInfo {
unsigned int epoch; /* used to track lease state changes */ unsigned int epoch; /* used to track lease state changes */
bool delete_pending; /* DELETE_ON_CLOSE is set */ bool delete_pending; /* DELETE_ON_CLOSE is set */
bool invalid_mapping; /* pagecache is invalid */ bool invalid_mapping; /* pagecache is invalid */
unsigned long flags;
#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */
#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */
#define CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2 (2) /* Downgrade oplock to L2 */
spinlock_t writers_lock;
unsigned int writers; /* Number of writers on this inode */
unsigned long time; /* jiffies of last update of inode */ unsigned long time; /* jiffies of last update of inode */
u64 server_eof; /* current file size on server -- protected by i_lock */ u64 server_eof; /* current file size on server -- protected by i_lock */
u64 uniqueid; /* server inode number */ u64 uniqueid; /* server inode number */
......
...@@ -127,6 +127,9 @@ extern u64 cifs_UnixTimeToNT(struct timespec); ...@@ -127,6 +127,9 @@ extern u64 cifs_UnixTimeToNT(struct timespec);
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
int offset); int offset);
extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
extern int cifs_get_writer(struct cifsInodeInfo *cinode);
extern void cifs_put_writer(struct cifsInodeInfo *cinode);
extern void cifs_done_oplock_break(struct cifsInodeInfo *cinode);
extern int cifs_unlock_range(struct cifsFileInfo *cfile, extern int cifs_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid); struct file_lock *flock, const unsigned int xid);
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
......
...@@ -2149,6 +2149,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) ...@@ -2149,6 +2149,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
sizeof(tcp_ses->srcaddr)); sizeof(tcp_ses->srcaddr));
memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr, memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
sizeof(tcp_ses->dstaddr)); sizeof(tcp_ses->dstaddr));
#ifdef CONFIG_CIFS_SMB2
get_random_bytes(tcp_ses->client_guid, SMB2_CLIENT_GUID_SIZE);
#endif
/* /*
* at this point we are the only ones with the pointer * at this point we are the only ones with the pointer
* to the struct since the kernel thread not created yet * to the struct since the kernel thread not created yet
......
...@@ -2619,12 +2619,20 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, ...@@ -2619,12 +2619,20 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
ssize_t written; ssize_t written;
written = cifs_get_writer(cinode);
if (written)
return written;
if (CIFS_CACHE_WRITE(cinode)) { if (CIFS_CACHE_WRITE(cinode)) {
if (cap_unix(tcon->ses) && if (cap_unix(tcon->ses) &&
(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))
&& ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) {
return generic_file_aio_write(iocb, iov, nr_segs, pos); written = generic_file_aio_write(
return cifs_writev(iocb, iov, nr_segs, pos); iocb, iov, nr_segs, pos);
goto out;
}
written = cifs_writev(iocb, iov, nr_segs, pos);
goto out;
} }
/* /*
* For non-oplocked files in strict cache mode we need to write the data * For non-oplocked files in strict cache mode we need to write the data
...@@ -2644,6 +2652,8 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, ...@@ -2644,6 +2652,8 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
inode); inode);
cinode->oplock = 0; cinode->oplock = 0;
} }
out:
cifs_put_writer(cinode);
return written; return written;
} }
...@@ -3655,6 +3665,13 @@ static int cifs_launder_page(struct page *page) ...@@ -3655,6 +3665,13 @@ static int cifs_launder_page(struct page *page)
return rc; return rc;
} }
static int
cifs_pending_writers_wait(void *unused)
{
schedule();
return 0;
}
void cifs_oplock_break(struct work_struct *work) void cifs_oplock_break(struct work_struct *work)
{ {
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
...@@ -3662,8 +3679,15 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -3662,8 +3679,15 @@ void cifs_oplock_break(struct work_struct *work)
struct inode *inode = cfile->dentry->d_inode; struct inode *inode = cfile->dentry->d_inode;
struct cifsInodeInfo *cinode = CIFS_I(inode); struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
int rc = 0; int rc = 0;
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
cifs_pending_writers_wait, TASK_UNINTERRUPTIBLE);
server->ops->downgrade_oplock(server, cinode,
test_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cinode->flags));
if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) && if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) &&
cifs_has_mand_locks(cinode)) { cifs_has_mand_locks(cinode)) {
cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n", cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n",
...@@ -3700,6 +3724,7 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -3700,6 +3724,7 @@ void cifs_oplock_break(struct work_struct *work)
cinode); cinode);
cifs_dbg(FYI, "Oplock release rc = %d\n", rc); cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
} }
cifs_done_oplock_break(cinode);
} }
const struct address_space_operations cifs_addr_ops = { const struct address_space_operations cifs_addr_ops = {
......
...@@ -472,8 +472,22 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) ...@@ -472,8 +472,22 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
cifs_dbg(FYI, "file id match, oplock break\n"); cifs_dbg(FYI, "file id match, oplock break\n");
pCifsInode = CIFS_I(netfile->dentry->d_inode); pCifsInode = CIFS_I(netfile->dentry->d_inode);
cifs_set_oplock_level(pCifsInode, set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
pSMB->OplockLevel ? OPLOCK_READ : 0); &pCifsInode->flags);
/*
* Set flag if the server downgrades the oplock
* to L2 else clear.
*/
if (pSMB->OplockLevel)
set_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&pCifsInode->flags);
else
clear_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&pCifsInode->flags);
queue_work(cifsiod_wq, queue_work(cifsiod_wq,
&netfile->oplock_break); &netfile->oplock_break);
netfile->oplock_break_cancelled = false; netfile->oplock_break_cancelled = false;
...@@ -557,6 +571,62 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) ...@@ -557,6 +571,62 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
cinode->oplock = 0; cinode->oplock = 0;
} }
static int
cifs_oplock_break_wait(void *unused)
{
schedule();
return signal_pending(current) ? -ERESTARTSYS : 0;
}
/*
* We wait for oplock breaks to be processed before we attempt to perform
* writes.
*/
int cifs_get_writer(struct cifsInodeInfo *cinode)
{
int rc;
start:
rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK,
cifs_oplock_break_wait, TASK_KILLABLE);
if (rc)
return rc;
spin_lock(&cinode->writers_lock);
if (!cinode->writers)
set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags);
cinode->writers++;
/* Check to see if we have started servicing an oplock break */
if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) {
cinode->writers--;
if (cinode->writers == 0) {
clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags);
wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS);
}
spin_unlock(&cinode->writers_lock);
goto start;
}
spin_unlock(&cinode->writers_lock);
return 0;
}
void cifs_put_writer(struct cifsInodeInfo *cinode)
{
spin_lock(&cinode->writers_lock);
cinode->writers--;
if (cinode->writers == 0) {
clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags);
wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS);
}
spin_unlock(&cinode->writers_lock);
}
void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
{
clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK);
}
bool bool
backup_cred(struct cifs_sb_info *cifs_sb) backup_cred(struct cifs_sb_info *cifs_sb)
{ {
......
...@@ -372,6 +372,16 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) ...@@ -372,6 +372,16 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
return 0; return 0;
} }
static void
cifs_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, bool set_level2)
{
if (set_level2)
cifs_set_oplock_level(cinode, OPLOCK_READ);
else
cifs_set_oplock_level(cinode, 0);
}
static bool static bool
cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
char *buf, int malformed) char *buf, int malformed)
...@@ -960,6 +970,7 @@ struct smb_version_operations smb1_operations = { ...@@ -960,6 +970,7 @@ struct smb_version_operations smb1_operations = {
.clear_stats = cifs_clear_stats, .clear_stats = cifs_clear_stats,
.print_stats = cifs_print_stats, .print_stats = cifs_print_stats,
.is_oplock_break = is_valid_oplock_break, .is_oplock_break = is_valid_oplock_break,
.downgrade_oplock = cifs_downgrade_oplock,
.check_trans2 = cifs_check_trans2, .check_trans2 = cifs_check_trans2,
.need_neg = cifs_need_neg, .need_neg = cifs_need_neg,
.negotiate = cifs_negotiate, .negotiate = cifs_negotiate,
......
...@@ -575,9 +575,21 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -575,9 +575,21 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
else else
cfile->oplock_break_cancelled = false; cfile->oplock_break_cancelled = false;
server->ops->set_oplock_level(cinode, set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0, &cinode->flags);
0, NULL);
/*
* Set flag if the server downgrades the oplock
* to L2 else clear.
*/
if (rsp->OplockLevel)
set_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
else
clear_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
queue_work(cifsiod_wq, &cfile->oplock_break); queue_work(cifsiod_wq, &cfile->oplock_break);
......
...@@ -650,6 +650,17 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -650,6 +650,17 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
} }
static void static void
smb2_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, bool set_level2)
{
if (set_level2)
server->ops->set_oplock_level(cinode, SMB2_OPLOCK_LEVEL_II,
0, NULL);
else
server->ops->set_oplock_level(cinode, 0, 0, NULL);
}
static void
smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
unsigned int epoch, bool *purge_cache) unsigned int epoch, bool *purge_cache)
{ {
...@@ -855,6 +866,7 @@ struct smb_version_operations smb20_operations = { ...@@ -855,6 +866,7 @@ struct smb_version_operations smb20_operations = {
.clear_stats = smb2_clear_stats, .clear_stats = smb2_clear_stats,
.print_stats = smb2_print_stats, .print_stats = smb2_print_stats,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
.negotiate_wsize = smb2_negotiate_wsize, .negotiate_wsize = smb2_negotiate_wsize,
...@@ -926,6 +938,7 @@ struct smb_version_operations smb21_operations = { ...@@ -926,6 +938,7 @@ struct smb_version_operations smb21_operations = {
.clear_stats = smb2_clear_stats, .clear_stats = smb2_clear_stats,
.print_stats = smb2_print_stats, .print_stats = smb2_print_stats,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
.negotiate_wsize = smb2_negotiate_wsize, .negotiate_wsize = smb2_negotiate_wsize,
...@@ -998,6 +1011,7 @@ struct smb_version_operations smb30_operations = { ...@@ -998,6 +1011,7 @@ struct smb_version_operations smb30_operations = {
.print_stats = smb2_print_stats, .print_stats = smb2_print_stats,
.dump_share_caps = smb2_dump_share_caps, .dump_share_caps = smb2_dump_share_caps,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
.negotiate_wsize = smb2_negotiate_wsize, .negotiate_wsize = smb2_negotiate_wsize,
...@@ -1049,6 +1063,7 @@ struct smb_version_operations smb30_operations = { ...@@ -1049,6 +1063,7 @@ struct smb_version_operations smb30_operations = {
.set_oplock_level = smb3_set_oplock_level, .set_oplock_level = smb3_set_oplock_level,
.create_lease_buf = smb3_create_lease_buf, .create_lease_buf = smb3_create_lease_buf,
.parse_lease_buf = smb3_parse_lease_buf, .parse_lease_buf = smb3_parse_lease_buf,
.validate_negotiate = smb3_validate_negotiate,
}; };
struct smb_version_values smb20_values = { struct smb_version_values smb20_values = {
......
...@@ -375,7 +375,12 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) ...@@ -375,7 +375,12 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities); req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities);
memcpy(req->ClientGUID, cifs_client_guid, SMB2_CLIENT_GUID_SIZE); /* ClientGUID must be zero for SMB2.02 dialect */
if (ses->server->vals->protocol_id == SMB20_PROT_ID)
memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE);
else
memcpy(req->ClientGUID, server->client_guid,
SMB2_CLIENT_GUID_SIZE);
iov[0].iov_base = (char *)req; iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */ /* 4 for rfc1002 length field */
...@@ -456,6 +461,82 @@ neg_exit: ...@@ -456,6 +461,82 @@ neg_exit:
return rc; return rc;
} }
int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
{
int rc = 0;
struct validate_negotiate_info_req vneg_inbuf;
struct validate_negotiate_info_rsp *pneg_rsp;
u32 rsplen;
cifs_dbg(FYI, "validate negotiate\n");
/*
* validation ioctl must be signed, so no point sending this if we
* can not sign it. We could eventually change this to selectively
* sign just this, the first and only signed request on a connection.
* This is good enough for now since a user who wants better security
* would also enable signing on the mount. Having validation of
* negotiate info for signed connections helps reduce attack vectors
*/
if (tcon->ses->server->sign == false)
return 0; /* validation requires signing */
vneg_inbuf.Capabilities =
cpu_to_le32(tcon->ses->server->vals->req_capabilities);
memcpy(vneg_inbuf.Guid, tcon->ses->server->client_guid,
SMB2_CLIENT_GUID_SIZE);
if (tcon->ses->sign)
vneg_inbuf.SecurityMode =
cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
else if (global_secflags & CIFSSEC_MAY_SIGN)
vneg_inbuf.SecurityMode =
cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
else
vneg_inbuf.SecurityMode = 0;
vneg_inbuf.DialectCount = cpu_to_le16(1);
vneg_inbuf.Dialects[0] =
cpu_to_le16(tcon->ses->server->vals->protocol_id);
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
(char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
(char **)&pneg_rsp, &rsplen);
if (rc != 0) {
cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
return -EIO;
}
if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
cifs_dbg(VFS, "invalid size of protocol negotiate response\n");
return -EIO;
}
/* check validate negotiate info response matches what we got earlier */
if (pneg_rsp->Dialect !=
cpu_to_le16(tcon->ses->server->vals->protocol_id))
goto vneg_out;
if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode))
goto vneg_out;
/* do not validate server guid because not saved at negprot time yet */
if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND |
SMB2_LARGE_FILES) != tcon->ses->server->capabilities)
goto vneg_out;
/* validate negotiate successful */
cifs_dbg(FYI, "validate negotiate info successful\n");
return 0;
vneg_out:
cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n");
return -EIO;
}
int int
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp) const struct nls_table *nls_cp)
...@@ -821,6 +902,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, ...@@ -821,6 +902,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); cifs_dbg(VFS, "DFS capability contradicts DFS flag\n");
if (tcon->ses->server->ops->validate_negotiate)
rc = tcon->ses->server->ops->validate_negotiate(xid, tcon);
tcon_exit: tcon_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
kfree(unc_path); kfree(unc_path);
......
...@@ -166,8 +166,6 @@ struct smb2_symlink_err_rsp { ...@@ -166,8 +166,6 @@ struct smb2_symlink_err_rsp {
#define SMB2_CLIENT_GUID_SIZE 16 #define SMB2_CLIENT_GUID_SIZE 16
extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
struct smb2_negotiate_req { struct smb2_negotiate_req {
struct smb2_hdr hdr; struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 36 */ __le16 StructureSize; /* Must be 36 */
...@@ -546,13 +544,19 @@ struct copychunk_ioctl { ...@@ -546,13 +544,19 @@ struct copychunk_ioctl {
__u32 Reserved2; __u32 Reserved2;
} __packed; } __packed;
/* Response and Request are the same format */ struct validate_negotiate_info_req {
struct validate_negotiate_info {
__le32 Capabilities; __le32 Capabilities;
__u8 Guid[SMB2_CLIENT_GUID_SIZE]; __u8 Guid[SMB2_CLIENT_GUID_SIZE];
__le16 SecurityMode; __le16 SecurityMode;
__le16 DialectCount; __le16 DialectCount;
__le16 Dialect[1]; __le16 Dialects[1]; /* dialect (someday maybe list) client asked for */
} __packed;
struct validate_negotiate_info_rsp {
__le32 Capabilities;
__u8 Guid[SMB2_CLIENT_GUID_SIZE];
__le16 SecurityMode;
__le16 Dialect; /* Dialect in use for the connection */
} __packed; } __packed;
#define RSS_CAPABLE 0x00000001 #define RSS_CAPABLE 0x00000001
......
...@@ -158,5 +158,6 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -158,5 +158,6 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
struct smb2_lock_element *buf); struct smb2_lock_element *buf);
extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
__u8 *lease_key, const __le32 lease_state); __u8 *lease_key, const __le32 lease_state);
extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
#endif /* _SMB2PROTO_H */ #endif /* _SMB2PROTO_H */
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */ #define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */
#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */
#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ #define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */
#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 /* BB add struct */ #define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204
/* Perform server-side data movement */ /* Perform server-side data movement */
#define FSCTL_SRV_COPYCHUNK 0x001440F2 #define FSCTL_SRV_COPYCHUNK 0x001440F2
#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 #define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2
......
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