Commit 772950ed authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6

Pull SMB3 fixes from Steve French:
 "A collection of CIFS/SMB3 fixes.

  It includes a couple bug fixes, a few for improved debugging of
  cifs.ko and some improvements to the way cifs does key generation.

  I do have some additional bug fixes I expect in the next week or two
  (to address a problem found by xfstest, and some fixes for SMB3.11
  dialect, and a couple patches that just came in yesterday that I am
  reviewing)"

* 'for-next' of git://git.samba.org/sfrench/cifs-2.6:
  cifs_dbg() outputs an uninitialized buffer in cifs_readdir()
  cifs: fix race between call_async() and reconnect()
  Prepare for encryption support (first part). Add decryption and encryption key generation. Thanks to Metze for helping with this.
  cifs: Allow using O_DIRECT with cache=loose
  cifs: Make echo interval tunable
  cifs: Check uniqueid for SMB2+ and return -ESTALE if necessary
  Print IP address of unresponsive server
  cifs: Ratelimit kernel log messages
parents 587198ba 01b9b0b2
...@@ -50,7 +50,7 @@ void cifs_vfs_err(const char *fmt, ...) ...@@ -50,7 +50,7 @@ void cifs_vfs_err(const char *fmt, ...)
vaf.fmt = fmt; vaf.fmt = fmt;
vaf.va = &args; vaf.va = &args;
pr_err("CIFS VFS: %pV", &vaf); pr_err_ratelimited("CIFS VFS: %pV", &vaf);
va_end(args); va_end(args);
} }
......
...@@ -51,14 +51,13 @@ __printf(1, 2) void cifs_vfs_err(const char *fmt, ...); ...@@ -51,14 +51,13 @@ __printf(1, 2) void cifs_vfs_err(const char *fmt, ...);
/* information message: e.g., configuration, major event */ /* information message: e.g., configuration, major event */
#define cifs_dbg(type, fmt, ...) \ #define cifs_dbg(type, fmt, ...) \
do { \ do { \
if (type == FYI) { \ if (type == FYI && cifsFYI & CIFS_INFO) { \
if (cifsFYI & CIFS_INFO) { \ pr_debug_ratelimited("%s: " \
pr_debug("%s: " fmt, __FILE__, ##__VA_ARGS__); \ fmt, __FILE__, ##__VA_ARGS__); \
} \
} else if (type == VFS) { \ } else if (type == VFS) { \
cifs_vfs_err(fmt, ##__VA_ARGS__); \ cifs_vfs_err(fmt, ##__VA_ARGS__); \
} else if (type == NOISY && type != 0) { \ } else if (type == NOISY && type != 0) { \
pr_debug(fmt, ##__VA_ARGS__); \ pr_debug_ratelimited(fmt, ##__VA_ARGS__); \
} \ } \
} while (0) } while (0)
......
...@@ -507,6 +507,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) ...@@ -507,6 +507,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
seq_printf(s, ",rsize=%u", cifs_sb->rsize); seq_printf(s, ",rsize=%u", cifs_sb->rsize);
seq_printf(s, ",wsize=%u", cifs_sb->wsize); seq_printf(s, ",wsize=%u", cifs_sb->wsize);
seq_printf(s, ",echo_interval=%lu",
tcon->ses->server->echo_interval / HZ);
/* convert actimeo and display it in seconds */ /* convert actimeo and display it in seconds */
seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);
...@@ -752,6 +754,9 @@ cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter) ...@@ -752,6 +754,9 @@ cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter)
ssize_t rc; ssize_t rc;
struct inode *inode = file_inode(iocb->ki_filp); struct inode *inode = file_inode(iocb->ki_filp);
if (iocb->ki_filp->f_flags & O_DIRECT)
return cifs_user_readv(iocb, iter);
rc = cifs_revalidate_mapping(inode); rc = cifs_revalidate_mapping(inode);
if (rc) if (rc)
return rc; return rc;
...@@ -766,6 +771,18 @@ static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -766,6 +771,18 @@ static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
ssize_t written; ssize_t written;
int rc; int rc;
if (iocb->ki_filp->f_flags & O_DIRECT) {
written = cifs_user_writev(iocb, from);
if (written > 0 && CIFS_CACHE_READ(cinode)) {
cifs_zap_mapping(inode);
cifs_dbg(FYI,
"Set no oplock for inode=%p after a write operation\n",
inode);
cinode->oplock = 0;
}
return written;
}
written = cifs_get_writer(cinode); written = cifs_get_writer(cinode);
if (written) if (written)
return written; return written;
......
...@@ -70,8 +70,10 @@ ...@@ -70,8 +70,10 @@
#define SERVER_NAME_LENGTH 40 #define SERVER_NAME_LENGTH 40
#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1) #define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1)
/* SMB echo "timeout" -- FIXME: tunable? */ /* echo interval in seconds */
#define SMB_ECHO_INTERVAL (60 * HZ) #define SMB_ECHO_INTERVAL_MIN 1
#define SMB_ECHO_INTERVAL_MAX 600
#define SMB_ECHO_INTERVAL_DEFAULT 60
#include "cifspdu.h" #include "cifspdu.h"
...@@ -225,7 +227,7 @@ struct smb_version_operations { ...@@ -225,7 +227,7 @@ struct smb_version_operations {
void (*print_stats)(struct seq_file *m, struct cifs_tcon *); void (*print_stats)(struct seq_file *m, struct cifs_tcon *);
void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *); void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *);
/* verify the message */ /* verify the message */
int (*check_message)(char *, unsigned int); int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
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 *, void (*downgrade_oplock)(struct TCP_Server_Info *,
struct cifsInodeInfo *, bool); struct cifsInodeInfo *, bool);
...@@ -507,6 +509,7 @@ struct smb_vol { ...@@ -507,6 +509,7 @@ struct smb_vol {
struct sockaddr_storage dstaddr; /* destination address */ struct sockaddr_storage dstaddr; /* destination address */
struct sockaddr_storage srcaddr; /* allow binding to a local IP */ struct sockaddr_storage srcaddr; /* allow binding to a local IP */
struct nls_table *local_nls; struct nls_table *local_nls;
unsigned int echo_interval; /* echo interval in secs */
}; };
#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \ #define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \
...@@ -627,7 +630,9 @@ struct TCP_Server_Info { ...@@ -627,7 +630,9 @@ struct TCP_Server_Info {
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
unsigned int max_read; unsigned int max_read;
unsigned int max_write; unsigned int max_write;
__u8 preauth_hash[512];
#endif /* CONFIG_CIFS_SMB2 */ #endif /* CONFIG_CIFS_SMB2 */
unsigned long echo_interval;
}; };
static inline unsigned int static inline unsigned int
...@@ -809,7 +814,10 @@ struct cifs_ses { ...@@ -809,7 +814,10 @@ struct cifs_ses {
bool need_reconnect:1; /* connection reset, uid now invalid */ bool need_reconnect:1; /* connection reset, uid now invalid */
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
__u16 session_flags; __u16 session_flags;
char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */ __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
__u8 preauth_hash[512];
#endif /* CONFIG_CIFS_SMB2 */ #endif /* CONFIG_CIFS_SMB2 */
}; };
......
...@@ -102,7 +102,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid, ...@@ -102,7 +102,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
struct smb_hdr *out_buf, struct smb_hdr *out_buf,
int *bytes_returned); int *bytes_returned);
extern int cifs_reconnect(struct TCP_Server_Info *server); extern int cifs_reconnect(struct TCP_Server_Info *server);
extern int checkSMB(char *buf, unsigned int length); extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
extern bool backup_cred(struct cifs_sb_info *); extern bool backup_cred(struct cifs_sb_info *);
extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
...@@ -439,7 +439,8 @@ extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *); ...@@ -439,7 +439,8 @@ extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *);
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
extern void cifs_crypto_shash_release(struct TCP_Server_Info *); extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
extern int calc_seckey(struct cifs_ses *); extern int calc_seckey(struct cifs_ses *);
extern int generate_smb3signingkey(struct cifs_ses *); extern int generate_smb30signingkey(struct cifs_ses *);
extern int generate_smb311signingkey(struct cifs_ses *);
#ifdef CONFIG_CIFS_WEAK_PW_HASH #ifdef CONFIG_CIFS_WEAK_PW_HASH
extern int calc_lanman_hash(const char *password, const char *cryptkey, extern int calc_lanman_hash(const char *password, const char *cryptkey,
......
...@@ -95,6 +95,7 @@ enum { ...@@ -95,6 +95,7 @@ enum {
Opt_cruid, Opt_gid, Opt_file_mode, Opt_cruid, Opt_gid, Opt_file_mode,
Opt_dirmode, Opt_port, Opt_dirmode, Opt_port,
Opt_rsize, Opt_wsize, Opt_actimeo, Opt_rsize, Opt_wsize, Opt_actimeo,
Opt_echo_interval,
/* Mount options which take string value */ /* Mount options which take string value */
Opt_user, Opt_pass, Opt_ip, Opt_user, Opt_pass, Opt_ip,
...@@ -188,6 +189,7 @@ static const match_table_t cifs_mount_option_tokens = { ...@@ -188,6 +189,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_rsize, "rsize=%s" }, { Opt_rsize, "rsize=%s" },
{ Opt_wsize, "wsize=%s" }, { Opt_wsize, "wsize=%s" },
{ Opt_actimeo, "actimeo=%s" }, { Opt_actimeo, "actimeo=%s" },
{ Opt_echo_interval, "echo_interval=%s" },
{ Opt_blank_user, "user=" }, { Opt_blank_user, "user=" },
{ Opt_blank_user, "username=" }, { Opt_blank_user, "username=" },
...@@ -368,7 +370,6 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -368,7 +370,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
server->session_key.response = NULL; server->session_key.response = NULL;
server->session_key.len = 0; server->session_key.len = 0;
server->lstrp = jiffies; server->lstrp = jiffies;
mutex_unlock(&server->srv_mutex);
/* mark submitted MIDs for retry and issue callback */ /* mark submitted MIDs for retry and issue callback */
INIT_LIST_HEAD(&retry_list); INIT_LIST_HEAD(&retry_list);
...@@ -381,6 +382,7 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -381,6 +382,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
list_move(&mid_entry->qhead, &retry_list); list_move(&mid_entry->qhead, &retry_list);
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
list_for_each_safe(tmp, tmp2, &retry_list) { list_for_each_safe(tmp, tmp2, &retry_list) {
...@@ -418,6 +420,7 @@ cifs_echo_request(struct work_struct *work) ...@@ -418,6 +420,7 @@ cifs_echo_request(struct work_struct *work)
int rc; int rc;
struct TCP_Server_Info *server = container_of(work, struct TCP_Server_Info *server = container_of(work,
struct TCP_Server_Info, echo.work); struct TCP_Server_Info, echo.work);
unsigned long echo_interval = server->echo_interval;
/* /*
* We cannot send an echo if it is disabled or until the * We cannot send an echo if it is disabled or until the
...@@ -427,7 +430,7 @@ cifs_echo_request(struct work_struct *work) ...@@ -427,7 +430,7 @@ cifs_echo_request(struct work_struct *work)
*/ */
if (!server->ops->need_neg || server->ops->need_neg(server) || if (!server->ops->need_neg || server->ops->need_neg(server) ||
(server->ops->can_echo && !server->ops->can_echo(server)) || (server->ops->can_echo && !server->ops->can_echo(server)) ||
time_before(jiffies, server->lstrp + SMB_ECHO_INTERVAL - HZ)) time_before(jiffies, server->lstrp + echo_interval - HZ))
goto requeue_echo; goto requeue_echo;
rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS; rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
...@@ -436,7 +439,7 @@ cifs_echo_request(struct work_struct *work) ...@@ -436,7 +439,7 @@ cifs_echo_request(struct work_struct *work)
server->hostname); server->hostname);
requeue_echo: requeue_echo:
queue_delayed_work(cifsiod_wq, &server->echo, SMB_ECHO_INTERVAL); queue_delayed_work(cifsiod_wq, &server->echo, echo_interval);
} }
static bool static bool
...@@ -487,9 +490,9 @@ server_unresponsive(struct TCP_Server_Info *server) ...@@ -487,9 +490,9 @@ server_unresponsive(struct TCP_Server_Info *server)
* a response in >60s. * a response in >60s.
*/ */
if (server->tcpStatus == CifsGood && if (server->tcpStatus == CifsGood &&
time_after(jiffies, server->lstrp + 2 * SMB_ECHO_INTERVAL)) { time_after(jiffies, server->lstrp + 2 * server->echo_interval)) {
cifs_dbg(VFS, "Server %s has not responded in %d seconds. Reconnecting...\n", cifs_dbg(VFS, "Server %s has not responded in %lu seconds. Reconnecting...\n",
server->hostname, (2 * SMB_ECHO_INTERVAL) / HZ); server->hostname, (2 * server->echo_interval) / HZ);
cifs_reconnect(server); cifs_reconnect(server);
wake_up(&server->response_q); wake_up(&server->response_q);
return true; return true;
...@@ -828,7 +831,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -828,7 +831,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
* 48 bytes is enough to display the header and a little bit * 48 bytes is enough to display the header and a little bit
* into the payload for debugging purposes. * into the payload for debugging purposes.
*/ */
length = server->ops->check_message(buf, server->total_read); length = server->ops->check_message(buf, server->total_read, server);
if (length != 0) if (length != 0)
cifs_dump_mem("Bad SMB: ", buf, cifs_dump_mem("Bad SMB: ", buf,
min_t(unsigned int, server->total_read, 48)); min_t(unsigned int, server->total_read, 48));
...@@ -1624,6 +1627,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -1624,6 +1627,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
break; break;
case Opt_echo_interval:
if (get_option_ul(args, &option)) {
cifs_dbg(VFS, "%s: Invalid echo interval value\n",
__func__);
goto cifs_parse_mount_err;
}
vol->echo_interval = option;
break;
/* String Arguments */ /* String Arguments */
...@@ -2089,6 +2100,9 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol) ...@@ -2089,6 +2100,9 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol)
if (!match_security(server, vol)) if (!match_security(server, vol))
return 0; return 0;
if (server->echo_interval != vol->echo_interval)
return 0;
return 1; return 1;
} }
...@@ -2208,6 +2222,12 @@ cifs_get_tcp_session(struct smb_vol *volume_info) ...@@ -2208,6 +2222,12 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
tcp_ses->tcpStatus = CifsNew; tcp_ses->tcpStatus = CifsNew;
++tcp_ses->srv_count; ++tcp_ses->srv_count;
if (volume_info->echo_interval >= SMB_ECHO_INTERVAL_MIN &&
volume_info->echo_interval <= SMB_ECHO_INTERVAL_MAX)
tcp_ses->echo_interval = volume_info->echo_interval * HZ;
else
tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
rc = ip_connect(tcp_ses); rc = ip_connect(tcp_ses);
if (rc < 0) { if (rc < 0) {
cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n"); cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n");
...@@ -2237,7 +2257,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) ...@@ -2237,7 +2257,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
cifs_fscache_get_client_cookie(tcp_ses); cifs_fscache_get_client_cookie(tcp_ses);
/* queue echo request delayed work */ /* queue echo request delayed work */
queue_delayed_work(cifsiod_wq, &tcp_ses->echo, SMB_ECHO_INTERVAL); queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
return tcp_ses; return tcp_ses;
......
...@@ -814,8 +814,21 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, ...@@ -814,8 +814,21 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
} }
} else } else
fattr.cf_uniqueid = iunique(sb, ROOT_I); fattr.cf_uniqueid = iunique(sb, ROOT_I);
} else } else {
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid; if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&
validinum == false && server->ops->get_srv_inum) {
/*
* Pass a NULL tcon to ensure we don't make a round
* trip to the server. This only works for SMB2+.
*/
tmprc = server->ops->get_srv_inum(xid,
NULL, cifs_sb, full_path,
&fattr.cf_uniqueid, data);
if (tmprc)
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
} else
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
}
/* query for SFU type info if supported and needed */ /* query for SFU type info if supported and needed */
if (fattr.cf_cifsattrs & ATTR_SYSTEM && if (fattr.cf_cifsattrs & ATTR_SYSTEM &&
...@@ -856,6 +869,13 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, ...@@ -856,6 +869,13 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
} else { } else {
/* we already have inode, update it */ /* we already have inode, update it */
/* if uniqueid is different, return error */
if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM &&
CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) {
rc = -ESTALE;
goto cgii_exit;
}
/* if filetype is different, return error */ /* if filetype is different, return error */
if (unlikely(((*inode)->i_mode & S_IFMT) != if (unlikely(((*inode)->i_mode & S_IFMT) !=
(fattr.cf_mode & S_IFMT))) { (fattr.cf_mode & S_IFMT))) {
......
...@@ -310,7 +310,7 @@ check_smb_hdr(struct smb_hdr *smb) ...@@ -310,7 +310,7 @@ check_smb_hdr(struct smb_hdr *smb)
} }
int int
checkSMB(char *buf, unsigned int total_read) checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)
{ {
struct smb_hdr *smb = (struct smb_hdr *)buf; struct smb_hdr *smb = (struct smb_hdr *)buf;
__u32 rfclen = be32_to_cpu(smb->smb_buf_length); __u32 rfclen = be32_to_cpu(smb->smb_buf_length);
......
...@@ -847,6 +847,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) ...@@ -847,6 +847,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
* if buggy server returns . and .. late do we want to * if buggy server returns . and .. late do we want to
* check for that here? * check for that here?
*/ */
*tmp_buf = 0;
rc = cifs_filldir(current_entry, file, ctx, rc = cifs_filldir(current_entry, file, ctx,
tmp_buf, max_len); tmp_buf, max_len);
if (rc) { if (rc) {
......
...@@ -38,7 +38,7 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid) ...@@ -38,7 +38,7 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid)
* Make sure that this really is an SMB, that it is a response, * Make sure that this really is an SMB, that it is a response,
* and that the message ids match. * and that the message ids match.
*/ */
if ((*(__le32 *)hdr->ProtocolId == SMB2_PROTO_NUMBER) && if ((hdr->ProtocolId == SMB2_PROTO_NUMBER) &&
(mid == wire_mid)) { (mid == wire_mid)) {
if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
return 0; return 0;
...@@ -50,9 +50,9 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid) ...@@ -50,9 +50,9 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid)
cifs_dbg(VFS, "Received Request not response\n"); cifs_dbg(VFS, "Received Request not response\n");
} }
} else { /* bad signature or mid */ } else { /* bad signature or mid */
if (*(__le32 *)hdr->ProtocolId != SMB2_PROTO_NUMBER) if (hdr->ProtocolId != SMB2_PROTO_NUMBER)
cifs_dbg(VFS, "Bad protocol string signature header %x\n", cifs_dbg(VFS, "Bad protocol string signature header %x\n",
*(unsigned int *) hdr->ProtocolId); le32_to_cpu(hdr->ProtocolId));
if (mid != wire_mid) if (mid != wire_mid)
cifs_dbg(VFS, "Mids do not match: %llu and %llu\n", cifs_dbg(VFS, "Mids do not match: %llu and %llu\n",
mid, wire_mid); mid, wire_mid);
...@@ -93,11 +93,11 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { ...@@ -93,11 +93,11 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
}; };
int int
smb2_check_message(char *buf, unsigned int length) smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
{ {
struct smb2_hdr *hdr = (struct smb2_hdr *)buf; struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
struct smb2_pdu *pdu = (struct smb2_pdu *)hdr; struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
__u64 mid = le64_to_cpu(hdr->MessageId); __u64 mid;
__u32 len = get_rfc1002_length(buf); __u32 len = get_rfc1002_length(buf);
__u32 clc_len; /* calculated length */ __u32 clc_len; /* calculated length */
int command; int command;
...@@ -111,6 +111,30 @@ smb2_check_message(char *buf, unsigned int length) ...@@ -111,6 +111,30 @@ smb2_check_message(char *buf, unsigned int length)
* ie Validate the wct via smb2_struct_sizes table above * ie Validate the wct via smb2_struct_sizes table above
*/ */
if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
struct smb2_transform_hdr *thdr =
(struct smb2_transform_hdr *)buf;
struct cifs_ses *ses = NULL;
struct list_head *tmp;
/* decrypt frame now that it is completely read in */
spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &srvr->smb_ses_list) {
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
if (ses->Suid == thdr->SessionId)
break;
ses = NULL;
}
spin_unlock(&cifs_tcp_ses_lock);
if (ses == NULL) {
cifs_dbg(VFS, "no decryption - session id not found\n");
return 1;
}
}
mid = le64_to_cpu(hdr->MessageId);
if (length < sizeof(struct smb2_pdu)) { if (length < sizeof(struct smb2_pdu)) {
if ((length >= sizeof(struct smb2_hdr)) && (hdr->Status != 0)) { if ((length >= sizeof(struct smb2_hdr)) && (hdr->Status != 0)) {
pdu->StructureSize2 = 0; pdu->StructureSize2 = 0;
...@@ -322,7 +346,7 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) ...@@ -322,7 +346,7 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
/* return pointer to beginning of data area, ie offset from SMB start */ /* return pointer to beginning of data area, ie offset from SMB start */
if ((*off != 0) && (*len != 0)) if ((*off != 0) && (*len != 0))
return (char *)(&hdr->ProtocolId[0]) + *off; return (char *)(&hdr->ProtocolId) + *off;
else else
return NULL; return NULL;
} }
......
...@@ -182,6 +182,11 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf) ...@@ -182,6 +182,11 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf)
struct smb2_hdr *hdr = (struct smb2_hdr *)buf; struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
__u64 wire_mid = le64_to_cpu(hdr->MessageId); __u64 wire_mid = le64_to_cpu(hdr->MessageId);
if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
cifs_dbg(VFS, "encrypted frame parsing not supported yet");
return NULL;
}
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_for_each_entry(mid, &server->pending_mid_q, qhead) { list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if ((mid->mid == wire_mid) && if ((mid->mid == wire_mid) &&
...@@ -1692,7 +1697,7 @@ struct smb_version_operations smb30_operations = { ...@@ -1692,7 +1697,7 @@ struct smb_version_operations smb30_operations = {
.get_lease_key = smb2_get_lease_key, .get_lease_key = smb2_get_lease_key,
.set_lease_key = smb2_set_lease_key, .set_lease_key = smb2_set_lease_key,
.new_lease_key = smb2_new_lease_key, .new_lease_key = smb2_new_lease_key,
.generate_signingkey = generate_smb3signingkey, .generate_signingkey = generate_smb30signingkey,
.calc_signature = smb3_calc_signature, .calc_signature = smb3_calc_signature,
.set_integrity = smb3_set_integrity, .set_integrity = smb3_set_integrity,
.is_read_op = smb21_is_read_op, .is_read_op = smb21_is_read_op,
...@@ -1779,7 +1784,7 @@ struct smb_version_operations smb311_operations = { ...@@ -1779,7 +1784,7 @@ struct smb_version_operations smb311_operations = {
.get_lease_key = smb2_get_lease_key, .get_lease_key = smb2_get_lease_key,
.set_lease_key = smb2_set_lease_key, .set_lease_key = smb2_set_lease_key,
.new_lease_key = smb2_new_lease_key, .new_lease_key = smb2_new_lease_key,
.generate_signingkey = generate_smb3signingkey, .generate_signingkey = generate_smb311signingkey,
.calc_signature = smb3_calc_signature, .calc_signature = smb3_calc_signature,
.set_integrity = smb3_set_integrity, .set_integrity = smb3_set_integrity,
.is_read_op = smb21_is_read_op, .is_read_op = smb21_is_read_op,
...@@ -1838,7 +1843,7 @@ struct smb_version_values smb21_values = { ...@@ -1838,7 +1843,7 @@ struct smb_version_values smb21_values = {
struct smb_version_values smb30_values = { struct smb_version_values smb30_values = {
.version_string = SMB30_VERSION_STRING, .version_string = SMB30_VERSION_STRING,
.protocol_id = SMB30_PROT_ID, .protocol_id = SMB30_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,
...@@ -1858,7 +1863,7 @@ struct smb_version_values smb30_values = { ...@@ -1858,7 +1863,7 @@ struct smb_version_values smb30_values = {
struct smb_version_values smb302_values = { struct smb_version_values smb302_values = {
.version_string = SMB302_VERSION_STRING, .version_string = SMB302_VERSION_STRING,
.protocol_id = SMB302_PROT_ID, .protocol_id = SMB302_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,
......
...@@ -97,10 +97,7 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , ...@@ -97,10 +97,7 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
hdr->smb2_buf_length = cpu_to_be32(parmsize + sizeof(struct smb2_hdr) hdr->smb2_buf_length = cpu_to_be32(parmsize + sizeof(struct smb2_hdr)
- 4 /* RFC 1001 length field itself not counted */); - 4 /* RFC 1001 length field itself not counted */);
hdr->ProtocolId[0] = 0xFE; hdr->ProtocolId = SMB2_PROTO_NUMBER;
hdr->ProtocolId[1] = 'S';
hdr->ProtocolId[2] = 'M';
hdr->ProtocolId[3] = 'B';
hdr->StructureSize = cpu_to_le16(64); hdr->StructureSize = cpu_to_le16(64);
hdr->Command = smb2_cmd; hdr->Command = smb2_cmd;
hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */ hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */
...@@ -1573,7 +1570,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, ...@@ -1573,7 +1570,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
goto ioctl_exit; goto ioctl_exit;
} }
memcpy(*out_data, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset), memcpy(*out_data,
(char *)&rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
*plen); *plen);
ioctl_exit: ioctl_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
...@@ -2093,7 +2091,7 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, ...@@ -2093,7 +2091,7 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
} }
if (*buf) { if (*buf) {
memcpy(*buf, (char *)rsp->hdr.ProtocolId + rsp->DataOffset, memcpy(*buf, (char *)&rsp->hdr.ProtocolId + rsp->DataOffset,
*nbytes); *nbytes);
free_rsp_buf(resp_buftype, iov[0].iov_base); free_rsp_buf(resp_buftype, iov[0].iov_base);
} else if (resp_buftype != CIFS_NO_BUFFER) { } else if (resp_buftype != CIFS_NO_BUFFER) {
......
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ #define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */
#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) #define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe)
#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd)
/* /*
* SMB2 Header Definition * SMB2 Header Definition
...@@ -102,7 +103,7 @@ struct smb2_hdr { ...@@ -102,7 +103,7 @@ struct smb2_hdr {
__be32 smb2_buf_length; /* big endian on wire */ __be32 smb2_buf_length; /* big endian on wire */
/* length is only two or three bytes - with /* length is only two or three bytes - with
one or two byte type preceding it that MBZ */ one or two byte type preceding it that MBZ */
__u8 ProtocolId[4]; /* 0xFE 'S' 'M' 'B' */ __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */
__le16 StructureSize; /* 64 */ __le16 StructureSize; /* 64 */
__le16 CreditCharge; /* MBZ */ __le16 CreditCharge; /* MBZ */
__le32 Status; /* Error from server */ __le32 Status; /* Error from server */
...@@ -128,11 +129,10 @@ struct smb2_transform_hdr { ...@@ -128,11 +129,10 @@ struct smb2_transform_hdr {
one or two byte type preceding it that MBZ */ one or two byte type preceding it that MBZ */
__u8 ProtocolId[4]; /* 0xFD 'S' 'M' 'B' */ __u8 ProtocolId[4]; /* 0xFD 'S' 'M' 'B' */
__u8 Signature[16]; __u8 Signature[16];
__u8 Nonce[11]; __u8 Nonce[16];
__u8 Reserved[5];
__le32 OriginalMessageSize; __le32 OriginalMessageSize;
__u16 Reserved1; __u16 Reserved1;
__le16 EncryptionAlgorithm; __le16 Flags; /* EncryptionAlgorithm */
__u64 SessionId; __u64 SessionId;
} __packed; } __packed;
......
...@@ -34,7 +34,8 @@ struct smb_rqst; ...@@ -34,7 +34,8 @@ struct smb_rqst;
***************************************************************** *****************************************************************
*/ */
extern int map_smb2_to_linux_error(char *buf, bool log_err); extern int map_smb2_to_linux_error(char *buf, bool log_err);
extern int smb2_check_message(char *buf, unsigned int length); extern int smb2_check_message(char *buf, unsigned int length,
struct TCP_Server_Info *server);
extern unsigned int smb2_calc_size(void *buf); extern unsigned int smb2_calc_size(void *buf);
extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr); extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
extern __le16 *cifs_convert_path_to_utf16(const char *from, extern __le16 *cifs_convert_path_to_utf16(const char *from,
......
...@@ -222,8 +222,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -222,8 +222,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc; return rc;
} }
int static int generate_key(struct cifs_ses *ses, struct kvec label,
generate_smb3signingkey(struct cifs_ses *ses) struct kvec context, __u8 *key, unsigned int key_size)
{ {
unsigned char zero = 0x0; unsigned char zero = 0x0;
__u8 i[4] = {0, 0, 0, 1}; __u8 i[4] = {0, 0, 0, 1};
...@@ -233,7 +233,7 @@ generate_smb3signingkey(struct cifs_ses *ses) ...@@ -233,7 +233,7 @@ generate_smb3signingkey(struct cifs_ses *ses)
unsigned char *hashptr = prfhash; unsigned char *hashptr = prfhash;
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
memset(ses->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE); memset(key, 0x0, key_size);
rc = smb3_crypto_shash_allocate(ses->server); rc = smb3_crypto_shash_allocate(ses->server);
if (rc) { if (rc) {
...@@ -262,7 +262,7 @@ generate_smb3signingkey(struct cifs_ses *ses) ...@@ -262,7 +262,7 @@ generate_smb3signingkey(struct cifs_ses *ses)
} }
rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
"SMB2AESCMAC", 12); label.iov_base, label.iov_len);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with label\n", __func__); cifs_dbg(VFS, "%s: Could not update with label\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
...@@ -276,7 +276,7 @@ generate_smb3signingkey(struct cifs_ses *ses) ...@@ -276,7 +276,7 @@ generate_smb3signingkey(struct cifs_ses *ses)
} }
rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
"SmbSign", 8); context.iov_base, context.iov_len);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with context\n", __func__); cifs_dbg(VFS, "%s: Could not update with context\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
...@@ -296,12 +296,102 @@ generate_smb3signingkey(struct cifs_ses *ses) ...@@ -296,12 +296,102 @@ generate_smb3signingkey(struct cifs_ses *ses)
goto smb3signkey_ret; goto smb3signkey_ret;
} }
memcpy(ses->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE); memcpy(key, hashptr, key_size);
smb3signkey_ret: smb3signkey_ret:
return rc; return rc;
} }
struct derivation {
struct kvec label;
struct kvec context;
};
struct derivation_triplet {
struct derivation signing;
struct derivation encryption;
struct derivation decryption;
};
static int
generate_smb3signingkey(struct cifs_ses *ses,
const struct derivation_triplet *ptriplet)
{
int rc;
rc = generate_key(ses, ptriplet->signing.label,
ptriplet->signing.context, ses->smb3signingkey,
SMB3_SIGN_KEY_SIZE);
if (rc)
return rc;
rc = generate_key(ses, ptriplet->encryption.label,
ptriplet->encryption.context, ses->smb3encryptionkey,
SMB3_SIGN_KEY_SIZE);
if (rc)
return rc;
return generate_key(ses, ptriplet->decryption.label,
ptriplet->decryption.context,
ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
}
int
generate_smb30signingkey(struct cifs_ses *ses)
{
struct derivation_triplet triplet;
struct derivation *d;
d = &triplet.signing;
d->label.iov_base = "SMB2AESCMAC";
d->label.iov_len = 12;
d->context.iov_base = "SmbSign";
d->context.iov_len = 8;
d = &triplet.encryption;
d->label.iov_base = "SMB2AESCCM";
d->label.iov_len = 11;
d->context.iov_base = "ServerIn ";
d->context.iov_len = 10;
d = &triplet.decryption;
d->label.iov_base = "SMB2AESCCM";
d->label.iov_len = 11;
d->context.iov_base = "ServerOut";
d->context.iov_len = 10;
return generate_smb3signingkey(ses, &triplet);
}
int
generate_smb311signingkey(struct cifs_ses *ses)
{
struct derivation_triplet triplet;
struct derivation *d;
d = &triplet.signing;
d->label.iov_base = "SMB2AESCMAC";
d->label.iov_len = 12;
d->context.iov_base = "SmbSign";
d->context.iov_len = 8;
d = &triplet.encryption;
d->label.iov_base = "SMB2AESCCM";
d->label.iov_len = 11;
d->context.iov_base = "ServerIn ";
d->context.iov_len = 10;
d = &triplet.decryption;
d->label.iov_base = "SMB2AESCCM";
d->label.iov_len = 11;
d->context.iov_base = "ServerOut";
d->context.iov_len = 10;
return generate_smb3signingkey(ses, &triplet);
}
int int
smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{ {
......
...@@ -576,14 +576,16 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, ...@@ -576,14 +576,16 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
cifs_in_send_dec(server); cifs_in_send_dec(server);
cifs_save_when_sent(mid); cifs_save_when_sent(mid);
if (rc < 0) if (rc < 0) {
server->sequence_number -= 2; server->sequence_number -= 2;
cifs_delete_mid(mid);
}
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
if (rc == 0) if (rc == 0)
return 0; return 0;
cifs_delete_mid(mid);
add_credits_and_wake_if(server, credits, optype); add_credits_and_wake_if(server, credits, optype);
return rc; return rc;
} }
......
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