From a0b3df5cf1fc46ad885bbc5c9f56ff0f4877beb5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 24 May 2013 07:40:59 -0400 Subject: cifs: add a "nosharesock" mount option to force new sockets to server to be created Some servers set max_vcs to 1 and actually do enforce that limit. Add a new mount option to work around this behavior that forces a mount request to open a new socket to the server instead of reusing an existing one. I'd prefer to come up with a solution that doesn't require this, so consider this a debug patch that you can use to determine whether this is the real problem. Cc: Jim McDonough Cc: Steve French Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 + fs/cifs/connect.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 4f07f6fbe49..db9f985b8c2 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -441,6 +441,7 @@ struct smb_vol { bool mfsymlinks:1; /* use Minshall+French Symlinks */ bool multiuser:1; bool rwpidforward:1; /* pid forward for read/write operations */ + bool nosharesock; unsigned int rsize; unsigned int wsize; bool sockopt_tcp_nodelay:1; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e3bc39bb9d1..180d9b95fab 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -85,7 +85,7 @@ enum { Opt_acl, Opt_noacl, Opt_locallease, Opt_sign, Opt_seal, Opt_noac, Opt_fsc, Opt_mfsymlinks, - Opt_multiuser, Opt_sloppy, + Opt_multiuser, Opt_sloppy, Opt_nosharesock, /* Mount options which take numeric value */ Opt_backupuid, Opt_backupgid, Opt_uid, @@ -165,6 +165,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_mfsymlinks, "mfsymlinks" }, { Opt_multiuser, "multiuser" }, { Opt_sloppy, "sloppy" }, + { Opt_nosharesock, "nosharesock" }, { Opt_backupuid, "backupuid=%s" }, { Opt_backupgid, "backupgid=%s" }, @@ -1455,6 +1456,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, case Opt_sloppy: sloppy = true; break; + case Opt_nosharesock: + vol->nosharesock = true; + break; /* Numeric Values */ case Opt_backupuid: @@ -2027,6 +2031,9 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol) { struct sockaddr *addr = (struct sockaddr *)&vol->dstaddr; + if (vol->nosharesock) + return 0; + if ((server->vals != vol->vals) || (server->ops != vol->ops)) return 0; -- cgit v1.2.3-18-g5258 From 6f709494a74938f98769fba76d3a1f8b0f12b606 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 24 May 2013 07:41:00 -0400 Subject: cifs: remove protocolEnum definition The field that held this was removed quite some time ago. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index db9f985b8c2..29dd1113e40 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -109,12 +109,6 @@ enum securityEnum { Kerberos, /* Kerberos via SPNEGO */ }; -enum protocolEnum { - TCP = 0, - SCTP - /* Netbios frames protocol not supported at this time */ -}; - struct session_key { unsigned int len; char *response; -- cgit v1.2.3-18-g5258 From ffa598a5373d072a675f882999f5b46fb5ec2f69 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 24 May 2013 07:41:00 -0400 Subject: cifs: remove useless memset in LANMAN auth code It turns out that CIFS_SESS_KEY_SIZE == CIFS_ENCPWD_SIZE, so this memset doesn't do anything useful. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 71436d1fca1..a85a83d1d00 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -276,7 +276,6 @@ int calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, strncpy(password_with_pad, password, CIFS_ENCPWD_SIZE); if (!encrypt && global_secflags & CIFSSEC_MAY_PLNTXT) { - memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE); memcpy(lnm_session_key, password_with_pad, CIFS_ENCPWD_SIZE); return 0; -- cgit v1.2.3-18-g5258 From 7d066459697610f6e755a7cfe199c3c6b142fb85 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 24 May 2013 07:41:00 -0400 Subject: cifs: make decode_ascii_ssetup void return ...rc is always set to 0. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/sess.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index f230571a7ab..838e2240fc5 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -310,11 +310,10 @@ decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, return; } -static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, - struct cifs_ses *ses, - const struct nls_table *nls_cp) +static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, + struct cifs_ses *ses, + const struct nls_table *nls_cp) { - int rc = 0; int len; char *bcc_ptr = *pbcc_area; @@ -322,7 +321,7 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, len = strnlen(bcc_ptr, bleft); if (len >= bleft) - return rc; + return; kfree(ses->serverOS); @@ -339,7 +338,7 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, len = strnlen(bcc_ptr, bleft); if (len >= bleft) - return rc; + return; kfree(ses->serverNOS); @@ -352,7 +351,7 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, len = strnlen(bcc_ptr, bleft); if (len > bleft) - return rc; + return; /* No domain field in LANMAN case. Domain is returned by old servers in the SMB negprot response */ @@ -360,8 +359,6 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, but thus do return domain here we could add parsing for it later, but it is not very important */ cifs_dbg(FYI, "ascii: bytes left %d\n", bleft); - - return rc; } int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, @@ -938,8 +935,7 @@ ssetup_ntlmssp_authenticate: } decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); } else { - rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, - ses, nls_cp); + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); } ssetup_exit: -- cgit v1.2.3-18-g5258 From 3534b8508e4b21eec0b7b839f7234a9b6fe27d03 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 24 May 2013 07:41:01 -0400 Subject: cifs: throw a warning if negotiate or sess_setup ops are passed NULL server or session pointers These look pretty cargo-culty to me, but let's be certain. Leave them in place for now. Pop a WARN if it ever does happen. Also, move to a more standard idiom for setting the "server" pointer. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 11 +++++------ fs/cifs/sess.c | 4 +++- fs/cifs/smb2pdu.c | 20 ++++++++------------ 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a58dc77cc44..c1c2006376a 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -375,16 +375,15 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) int rc = 0; int bytes_returned; int i; - struct TCP_Server_Info *server; + struct TCP_Server_Info *server = ses->server; u16 count; unsigned int secFlags; - if (ses->server) - server = ses->server; - else { - rc = -EIO; - return rc; + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; } + rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ , (void **) &pSMB, (void **) &pSMBr); if (rc) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 838e2240fc5..e8c5dc9f338 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -576,8 +576,10 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, u16 blob_len; char *ntlmsspblob = NULL; - if (ses == NULL) + if (ses == NULL) { + WARN(1, "%s: ses == NULL!", __func__); return -EINVAL; + } type = ses->server->secType; cifs_dbg(FYI, "sess setup type %d\n", type); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2b95ce2b54e..3af66aa18d3 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -328,7 +328,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) struct kvec iov[1]; int rc = 0; int resp_buftype; - struct TCP_Server_Info *server; + struct TCP_Server_Info *server = ses->server; unsigned int sec_flags; u16 temp = 0; int blob_offset, blob_length; @@ -337,11 +337,9 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) cifs_dbg(FYI, "Negotiate protocol\n"); - if (ses->server) - server = ses->server; - else { - rc = -EIO; - return rc; + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; } rc = small_smb2_init(SMB2_NEGOTIATE, NULL, (void **) &req); @@ -480,7 +478,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, int rc = 0; int resp_buftype; __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ - struct TCP_Server_Info *server; + struct TCP_Server_Info *server = ses->server; unsigned int sec_flags; u8 temp = 0; u16 blob_length = 0; @@ -490,11 +488,9 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, cifs_dbg(FYI, "Session Setup\n"); - if (ses->server) - server = ses->server; - else { - rc = -EIO; - return rc; + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; } /* -- cgit v1.2.3-18-g5258 From 281e2e7d06c42ce8dfd423fa2ae5616af0e0323f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:00:56 -0400 Subject: cifs: remove the cifs_ses->flags field This field is completely unused: CIFS_SES_W9X is completely unused. CIFS_SES_LANMAN and CIFS_SES_OS2 are set but never checked. CIFS_SES_NT4 is checked, but never set. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 10 ---------- fs/cifs/connect.c | 1 - fs/cifs/sess.c | 7 +------ fs/cifs/smb1ops.c | 18 ++++++------------ 4 files changed, 7 insertions(+), 29 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 29dd1113e40..be993ec5895 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -692,7 +692,6 @@ struct cifs_ses { enum statusEnum status; unsigned overrideSecFlg; /* if non-zero override global sec flags */ __u16 ipc_tid; /* special tid for connection to IPC share */ - __u16 flags; __u16 vcnum; char *serverOS; /* name of operating system underlying server */ char *serverNOS; /* name of network operating system of server */ @@ -715,15 +714,6 @@ struct cifs_ses { #endif /* CONFIG_CIFS_SMB2 */ }; -/* no more than one of the following three session flags may be set */ -#define CIFS_SES_NT4 1 -#define CIFS_SES_OS2 2 -#define CIFS_SES_W9X 4 -/* following flag is set for old servers such as OS2 (and Win95?) - which do not negotiate NTLM or POSIX dialects, but instead - negotiate one of the older LANMAN dialects */ -#define CIFS_SES_LANMAN 8 - static inline bool cap_unix(struct cifs_ses *ses) { diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 180d9b95fab..16013490223 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3834,7 +3834,6 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, int rc = -ENOSYS; struct TCP_Server_Info *server = ses->server; - ses->flags = 0; ses->capabilities = server->capabilities; if (linuxExtEnabled == 0) ses->capabilities &= (~server->vals->cap_unix); diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index e8c5dc9f338..0d0fe38f66a 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -328,10 +328,8 @@ static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, ses->serverOS = kzalloc(len + 1, GFP_KERNEL); if (ses->serverOS) strncpy(ses->serverOS, bcc_ptr, len); - if (strncmp(ses->serverOS, "OS/2", 4) == 0) { + if (strncmp(ses->serverOS, "OS/2", 4) == 0) cifs_dbg(FYI, "OS/2 server\n"); - ses->flags |= CIFS_SES_OS2; - } bcc_ptr += len + 1; bleft -= len + 1; @@ -642,8 +640,6 @@ ssetup_ntlmssp_authenticate: } bcc_ptr = str_area; - ses->flags &= ~CIFS_SES_LANMAN; - iov[1].iov_base = NULL; iov[1].iov_len = 0; @@ -667,7 +663,6 @@ ssetup_ntlmssp_authenticate: ses->server->sec_mode & SECMODE_PW_ENCRYPT ? true : false, lnm_session_key); - ses->flags |= CIFS_SES_LANMAN; memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); bcc_ptr += CIFS_AUTH_RESP_SIZE; diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 3efdb9d5c0b..7d1c78bce4a 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -765,20 +765,14 @@ smb_set_file_info(struct inode *inode, const char *full_path, } tcon = tlink_tcon(tlink); - /* - * NT4 apparently returns success on this call, but it doesn't really - * work. - */ - if (!(tcon->ses->flags & CIFS_SES_NT4)) { - rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, - cifs_sb->local_nls, + rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc == 0) { - cinode->cifsAttrs = le32_to_cpu(buf->Attributes); - goto out; - } else if (rc != -EOPNOTSUPP && rc != -EINVAL) - goto out; + if (rc == 0) { + cinode->cifsAttrs = le32_to_cpu(buf->Attributes); + goto out; + } else if (rc != -EOPNOTSUPP && rc != -EINVAL) { + goto out; } cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n"); -- cgit v1.2.3-18-g5258 From 31d9e2bd5f83839408a1de83bfaafbda0d309f2b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:00:57 -0400 Subject: cifs: break out decoding of security blob into separate function ...cleanup. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifspdu.h | 4 +- fs/cifs/cifssmb.c | 109 ++++++++++++++++++++++++++++++------------------------ 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index e996ff6b26d..4e6135a39fd 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -531,7 +531,7 @@ typedef struct lanman_neg_rsp { #define READ_RAW_ENABLE 1 #define WRITE_RAW_ENABLE 2 #define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) - +#define SMB1_CLIENT_GUID_SIZE (16) typedef struct negotiate_rsp { struct smb_hdr hdr; /* wct = 17 */ __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ @@ -553,7 +553,7 @@ typedef struct negotiate_rsp { /* followed by 16 bytes of server GUID */ /* then security blob if cap_extended_security negotiated */ struct { - unsigned char GUID[16]; + unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; unsigned char SecurityBlob[1]; } __attribute__((packed)) extended_response; } __attribute__((packed)) u; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index c1c2006376a..9b4aea85b15 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -367,6 +367,56 @@ vt2_err: return -EINVAL; } +static int +decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) +{ + int rc = 0; + u16 count; + char *guid = pSMBr->u.extended_response.GUID; + + count = get_bcc(&pSMBr->hdr); + if (count < SMB1_CLIENT_GUID_SIZE) + return -EIO; + + spin_lock(&cifs_tcp_ses_lock); + if (server->srv_count > 1) { + spin_unlock(&cifs_tcp_ses_lock); + if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) { + cifs_dbg(FYI, "server UID changed\n"); + memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); + } + } else { + spin_unlock(&cifs_tcp_ses_lock); + memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); + } + + if (count == SMB1_CLIENT_GUID_SIZE) { + server->secType = RawNTLMSSP; + } else { + count -= SMB1_CLIENT_GUID_SIZE; + rc = decode_negTokenInit( + pSMBr->u.extended_response.SecurityBlob, count, server); + if (rc != 1) + return -EINVAL; + + /* Make sure server supports what we want to use */ + switch(server->secType) { + case Kerberos: + if (!server->sec_kerberos && !server->sec_mskerberos) + return -EOPNOTSUPP; + break; + case RawNTLMSSP: + if (!server->sec_ntlmssp) + return -EOPNOTSUPP; + break; + default: + return -EOPNOTSUPP; + } + } + + return 0; +} + int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) { @@ -568,61 +618,22 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) server->capabilities = le32_to_cpu(pSMBr->Capabilities); server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); server->timeAdj *= 60; - if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { + + if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey, CIFS_CRYPTO_KEY_SIZE); - } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || + else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || server->capabilities & CAP_EXTENDED_SECURITY) && - (pSMBr->EncryptionKeyLength == 0)) { - /* decode security blob */ - count = get_bcc(&pSMBr->hdr); - if (count < 16) { - rc = -EIO; - goto neg_err_exit; - } - spin_lock(&cifs_tcp_ses_lock); - if (server->srv_count > 1) { - spin_unlock(&cifs_tcp_ses_lock); - if (memcmp(server->server_GUID, - pSMBr->u.extended_response. - GUID, 16) != 0) { - cifs_dbg(FYI, "server UID changed\n"); - memcpy(server->server_GUID, - pSMBr->u.extended_response.GUID, - 16); - } - } else { - spin_unlock(&cifs_tcp_ses_lock); - memcpy(server->server_GUID, - pSMBr->u.extended_response.GUID, 16); - } - - if (count == 16) { - server->secType = RawNTLMSSP; - } else { - rc = decode_negTokenInit(pSMBr->u.extended_response. - SecurityBlob, count - 16, - server); - if (rc == 1) - rc = 0; - else - rc = -EINVAL; - if (server->secType == Kerberos) { - if (!server->sec_kerberos && - !server->sec_mskerberos) - rc = -EOPNOTSUPP; - } else if (server->secType == RawNTLMSSP) { - if (!server->sec_ntlmssp) - rc = -EOPNOTSUPP; - } else - rc = -EOPNOTSUPP; - } - } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { + (pSMBr->EncryptionKeyLength == 0)) + rc = decode_ext_sec_blob(server, pSMBr); + else if (server->sec_mode & SECMODE_PW_ENCRYPT) rc = -EIO; /* no crypt key only if plain text pwd */ - goto neg_err_exit; - } else + else server->capabilities &= ~CAP_EXTENDED_SECURITY; + if (rc) + goto neg_err_exit; + #ifdef CONFIG_CIFS_WEAK_PW_HASH signing_check: #endif -- cgit v1.2.3-18-g5258 From 2190eca1d07956cf81a9ed974ecd98a427e54817 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:00:57 -0400 Subject: cifs: break out lanman NEGOTIATE handling into separate function ...this also gets rid of some #ifdef ugliness too. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 185 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 97 insertions(+), 88 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 9b4aea85b15..5dd4f8a51e0 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -417,6 +417,96 @@ decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) return 0; } +#ifdef CONFIG_CIFS_WEAK_PW_HASH +static int +decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, + unsigned int secFlags) +{ + __s16 tmp; + struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr; + + if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT) + return -EOPNOTSUPP; + + if ((secFlags & CIFSSEC_MAY_LANMAN) || (secFlags & CIFSSEC_MAY_PLNTXT)) + server->secType = LANMAN; + else { + cifs_dbg(VFS, "mount failed weak security disabled in /proc/fs/cifs/SecurityFlags\n"); + return -EOPNOTSUPP; + } + server->sec_mode = le16_to_cpu(rsp->SecurityMode); + server->maxReq = min_t(unsigned int, + le16_to_cpu(rsp->MaxMpxCount), + cifs_max_pending); + set_credits(server, server->maxReq); + server->maxBuf = le16_to_cpu(rsp->MaxBufSize); + server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); + /* even though we do not use raw we might as well set this + accurately, in case we ever find a need for it */ + if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { + server->max_rw = 0xFF00; + server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; + } else { + server->max_rw = 0;/* do not need to use raw anyway */ + server->capabilities = CAP_MPX_MODE; + } + tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); + if (tmp == -1) { + /* OS/2 often does not set timezone therefore + * we must use server time to calc time zone. + * Could deviate slightly from the right zone. + * Smallest defined timezone difference is 15 minutes + * (i.e. Nepal). Rounding up/down is done to match + * this requirement. + */ + int val, seconds, remain, result; + struct timespec ts, utc; + utc = CURRENT_TIME; + ts = cnvrtDosUnixTm(rsp->SrvTime.Date, + rsp->SrvTime.Time, 0); + cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n", + (int)ts.tv_sec, (int)utc.tv_sec, + (int)(utc.tv_sec - ts.tv_sec)); + val = (int)(utc.tv_sec - ts.tv_sec); + seconds = abs(val); + result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; + remain = seconds % MIN_TZ_ADJ; + if (remain >= (MIN_TZ_ADJ / 2)) + result += MIN_TZ_ADJ; + if (val < 0) + result = -result; + server->timeAdj = result; + } else { + server->timeAdj = (int)tmp; + server->timeAdj *= 60; /* also in seconds */ + } + cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj); + + + /* BB get server time for time conversions and add + code to use it and timezone since this is not UTC */ + + if (rsp->EncryptionKeyLength == + cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { + memcpy(server->cryptkey, rsp->EncryptionKey, + CIFS_CRYPTO_KEY_SIZE); + } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { + return -EIO; /* need cryptkey unless plain text */ + } + + cifs_dbg(FYI, "LANMAN negotiated\n"); + return 0; +} +#else +static inline int +decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, + unsigned int secFlags) +{ + cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n"); + return -EOPNOTSUPP; +} +#endif + int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) { @@ -485,98 +575,19 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) could not negotiate a common dialect */ rc = -EOPNOTSUPP; goto neg_err_exit; -#ifdef CONFIG_CIFS_WEAK_PW_HASH - } else if ((pSMBr->hdr.WordCount == 13) - && ((server->dialect == LANMAN_PROT) - || (server->dialect == LANMAN2_PROT))) { - __s16 tmp; - struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr; - - if ((secFlags & CIFSSEC_MAY_LANMAN) || - (secFlags & CIFSSEC_MAY_PLNTXT)) - server->secType = LANMAN; - else { - cifs_dbg(VFS, "mount failed weak security disabled in /proc/fs/cifs/SecurityFlags\n"); - rc = -EOPNOTSUPP; - goto neg_err_exit; - } - server->sec_mode = le16_to_cpu(rsp->SecurityMode); - server->maxReq = min_t(unsigned int, - le16_to_cpu(rsp->MaxMpxCount), - cifs_max_pending); - set_credits(server, server->maxReq); - server->maxBuf = le16_to_cpu(rsp->MaxBufSize); - server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); - /* even though we do not use raw we might as well set this - accurately, in case we ever find a need for it */ - if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { - server->max_rw = 0xFF00; - server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; - } else { - server->max_rw = 0;/* do not need to use raw anyway */ - server->capabilities = CAP_MPX_MODE; - } - tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); - if (tmp == -1) { - /* OS/2 often does not set timezone therefore - * we must use server time to calc time zone. - * Could deviate slightly from the right zone. - * Smallest defined timezone difference is 15 minutes - * (i.e. Nepal). Rounding up/down is done to match - * this requirement. - */ - int val, seconds, remain, result; - struct timespec ts, utc; - utc = CURRENT_TIME; - ts = cnvrtDosUnixTm(rsp->SrvTime.Date, - rsp->SrvTime.Time, 0); - cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n", - (int)ts.tv_sec, (int)utc.tv_sec, - (int)(utc.tv_sec - ts.tv_sec)); - val = (int)(utc.tv_sec - ts.tv_sec); - seconds = abs(val); - result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; - remain = seconds % MIN_TZ_ADJ; - if (remain >= (MIN_TZ_ADJ / 2)) - result += MIN_TZ_ADJ; - if (val < 0) - result = -result; - server->timeAdj = result; - } else { - server->timeAdj = (int)tmp; - server->timeAdj *= 60; /* also in seconds */ - } - cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj); - - - /* BB get server time for time conversions and add - code to use it and timezone since this is not UTC */ - - if (rsp->EncryptionKeyLength == - cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { - memcpy(ses->server->cryptkey, rsp->EncryptionKey, - CIFS_CRYPTO_KEY_SIZE); - } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { - rc = -EIO; /* need cryptkey unless plain text */ - goto neg_err_exit; - } - - cifs_dbg(FYI, "LANMAN negotiated\n"); - /* we will not end up setting signing flags - as no signing - was in LANMAN and server did not return the flags on */ - goto signing_check; -#else /* weak security disabled */ } else if (pSMBr->hdr.WordCount == 13) { - cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n"); - rc = -EOPNOTSUPP; -#endif /* WEAK_PW_HASH */ - goto neg_err_exit; + rc = decode_lanman_negprot_rsp(server, pSMBr, secFlags); + if (!rc) + goto signing_check; + else + goto neg_err_exit; } else if (pSMBr->hdr.WordCount != 17) { /* unknown wct */ rc = -EOPNOTSUPP; goto neg_err_exit; } - /* else wct == 17 NTLM */ + /* else wct == 17, NTLM or better */ + server->sec_mode = pSMBr->SecurityMode; if ((server->sec_mode & SECMODE_USER) == 0) cifs_dbg(FYI, "share mode security\n"); @@ -634,9 +645,7 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) if (rc) goto neg_err_exit; -#ifdef CONFIG_CIFS_WEAK_PW_HASH signing_check: -#endif if ((secFlags & CIFSSEC_MAY_SIGN) == 0) { /* MUST_SIGN already includes the MAY_SIGN FLAG so if this is zero it means that signing is disabled */ -- cgit v1.2.3-18-g5258 From 9ddec561313b9c73d6f58a1910d37ea9d965d101 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:00:58 -0400 Subject: cifs: move handling of signed connections into separate function Move the sanity checks for signed connections into a separate function. SMB2's was a cut-and-paste job from CIFS code, so we can make them use the same function. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 1 + fs/cifs/cifssmb.c | 68 +++++++++++++++++++++++++++-------------------------- fs/cifs/smb2pdu.c | 33 ++++---------------------- 3 files changed, 40 insertions(+), 62 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index dda188a9433..f0e93ffe654 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -212,6 +212,7 @@ extern int cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses); extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, struct nls_table *nls_info); +extern int cifs_enable_signing(struct TCP_Server_Info *server, unsigned int secFlags); extern int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses); extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 5dd4f8a51e0..1a3776322c7 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -417,6 +417,38 @@ decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) return 0; } +int +cifs_enable_signing(struct TCP_Server_Info *server, unsigned int secFlags) +{ + if ((secFlags & CIFSSEC_MAY_SIGN) == 0) { + /* MUST_SIGN already includes the MAY_SIGN FLAG + so if this is zero it means that signing is disabled */ + cifs_dbg(FYI, "Signing disabled\n"); + if (server->sec_mode & SECMODE_SIGN_REQUIRED) { + cifs_dbg(VFS, "Server requires packet signing to be enabled in /proc/fs/cifs/SecurityFlags\n"); + return -EOPNOTSUPP; + } + server->sec_mode &= + ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); + } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) { + /* signing required */ + cifs_dbg(FYI, "Must sign - secFlags 0x%x\n", secFlags); + if ((server->sec_mode & + (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) { + cifs_dbg(VFS, "signing required but server lacks support\n"); + return -EOPNOTSUPP; + } else + server->sec_mode |= SECMODE_SIGN_REQUIRED; + } else { + /* signing optional ie CIFSSEC_MAY_SIGN */ + if ((server->sec_mode & SECMODE_SIGN_REQUIRED) == 0) + server->sec_mode &= + ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); + } + + return 0; +} + #ifdef CONFIG_CIFS_WEAK_PW_HASH static int decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, @@ -577,10 +609,7 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) goto neg_err_exit; } else if (pSMBr->hdr.WordCount == 13) { rc = decode_lanman_negprot_rsp(server, pSMBr, secFlags); - if (!rc) - goto signing_check; - else - goto neg_err_exit; + goto signing_check; } else if (pSMBr->hdr.WordCount != 17) { /* unknown wct */ rc = -EOPNOTSUPP; @@ -642,36 +671,9 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) else server->capabilities &= ~CAP_EXTENDED_SECURITY; - if (rc) - goto neg_err_exit; - signing_check: - if ((secFlags & CIFSSEC_MAY_SIGN) == 0) { - /* MUST_SIGN already includes the MAY_SIGN FLAG - so if this is zero it means that signing is disabled */ - cifs_dbg(FYI, "Signing disabled\n"); - if (server->sec_mode & SECMODE_SIGN_REQUIRED) { - cifs_dbg(VFS, "Server requires packet signing to be enabled in /proc/fs/cifs/SecurityFlags\n"); - rc = -EOPNOTSUPP; - } - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) { - /* signing required */ - cifs_dbg(FYI, "Must sign - secFlags 0x%x\n", secFlags); - if ((server->sec_mode & - (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) { - cifs_dbg(VFS, "signing required but server lacks support\n"); - rc = -EOPNOTSUPP; - } else - server->sec_mode |= SECMODE_SIGN_REQUIRED; - } else { - /* signing optional ie CIFSSEC_MAY_SIGN */ - if ((server->sec_mode & SECMODE_SIGN_REQUIRED) == 0) - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } - + if (!rc) + rc = cifs_enable_signing(server, secFlags); neg_err_exit: cifs_buf_release(pSMB); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 3af66aa18d3..ebb97b484ab 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -423,36 +423,11 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) } cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags); - if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) { - cifs_dbg(FYI, "Signing required\n"); - if (!(server->sec_mode & (SMB2_NEGOTIATE_SIGNING_REQUIRED | - SMB2_NEGOTIATE_SIGNING_ENABLED))) { - cifs_dbg(VFS, "signing required but server lacks support\n"); - rc = -EOPNOTSUPP; - goto neg_exit; - } - server->sec_mode |= SECMODE_SIGN_REQUIRED; - } else if (sec_flags & CIFSSEC_MAY_SIGN) { - cifs_dbg(FYI, "Signing optional\n"); - if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { - cifs_dbg(FYI, "Server requires signing\n"); - server->sec_mode |= SECMODE_SIGN_REQUIRED; - } else { - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } - } else { - cifs_dbg(FYI, "Signing disabled\n"); - if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { - cifs_dbg(VFS, "Server requires packet signing to be enabled in /proc/fs/cifs/SecurityFlags\n"); - rc = -EOPNOTSUPP; - goto neg_exit; - } - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } - + rc = cifs_enable_signing(server, sec_flags); #ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */ + if (rc) + goto neg_exit; + rc = decode_neg_token_init(security_blob, blob_length, &server->sec_type); if (rc == 1) -- cgit v1.2.3-18-g5258 From 9193400b69eaf8a409bd1c3d40ecd15445e6e08b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:00:58 -0400 Subject: cifs: factor out check for extended security bit into separate function Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 1a3776322c7..e6396108675 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -539,6 +539,20 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, } #endif +static bool +should_set_ext_sec_flag(unsigned int secFlags) +{ + if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) + return true; + else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) + return true; + else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) + return true; + else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) + return true; + return false; +} + int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) { @@ -572,15 +586,8 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) pSMB->hdr.Mid = get_next_mid(server); pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS); - if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) - pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; - else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) { - cifs_dbg(FYI, "Kerberos only mechanism, enable extended security\n"); - pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; - } else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) - pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; - else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) { - cifs_dbg(FYI, "NTLMSSP only mechanism, enable extended security\n"); + if (should_set_ext_sec_flag(secFlags)) { + cifs_dbg(FYI, "Requesting extended security."); pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; } -- cgit v1.2.3-18-g5258 From 515d82ffd0fe4a87d872c655a6e19a318770ea0c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:00:58 -0400 Subject: cifs: add new "Unspecified" securityEnum value Add a new securityEnum value to cover the case where a sec= option was not explicitly set. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index be993ec5895..9f8dc3da5f3 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -101,11 +101,11 @@ enum statusEnum { }; enum securityEnum { - LANMAN = 0, /* Legacy LANMAN auth */ + Unspecified = 0, /* not specified */ + LANMAN, /* Legacy LANMAN auth */ NTLM, /* Legacy NTLM012 auth with NTLM hash */ NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */ -/* NTLMSSP, */ /* can use rawNTLMSSP instead of NTLMSSP via SPNEGO */ Kerberos, /* Kerberos via SPNEGO */ }; -- cgit v1.2.3-18-g5258 From e598d1d8fb512c7a4d86c729cdca30e87fe7cfc9 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:00:59 -0400 Subject: cifs: track the flavor of the NEGOTIATE reponse Track what sort of NEGOTIATE response we get from the server, as that will govern what sort of authentication types this socket will support. There are three possibilities: LANMAN: server sent legacy LANMAN-type response UNENCAP: server sent a newer-style response, but extended security bit wasn't set. This socket will only support unencapsulated auth types. EXTENDED: server sent a newer-style response with the extended security bit set. This is necessary to support krb5 and ntlmssp auth types. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 4 ++++ fs/cifs/cifssmb.c | 15 ++++++++++----- fs/cifs/smb2pdu.c | 2 ++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9f8dc3da5f3..82ba4b97489 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -541,6 +541,10 @@ struct TCP_Server_Info { struct session_key session_key; unsigned long lstrp; /* when we got last response from this server */ struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ +#define CIFS_NEGFLAVOR_LANMAN 0 /* wct == 13, LANMAN */ +#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */ +#define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */ + char negflavor; /* NEGOTIATE response flavor */ /* extended security flavors that server supports */ bool sec_ntlmssp; /* supports NTLMSSP */ bool sec_kerberosu2u; /* supports U2U Kerberos */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index e6396108675..80ca6886a81 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -615,6 +615,7 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) rc = -EOPNOTSUPP; goto neg_err_exit; } else if (pSMBr->hdr.WordCount == 13) { + server->negflavor = CIFS_NEGFLAVOR_LANMAN; rc = decode_lanman_negprot_rsp(server, pSMBr, secFlags); goto signing_check; } else if (pSMBr->hdr.WordCount != 17) { @@ -666,17 +667,21 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); server->timeAdj *= 60; - if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) + if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { + server->negflavor = CIFS_NEGFLAVOR_UNENCAP; memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey, CIFS_CRYPTO_KEY_SIZE); - else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || + } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || server->capabilities & CAP_EXTENDED_SECURITY) && - (pSMBr->EncryptionKeyLength == 0)) + (pSMBr->EncryptionKeyLength == 0)) { + server->negflavor = CIFS_NEGFLAVOR_EXTENDED; rc = decode_ext_sec_blob(server, pSMBr); - else if (server->sec_mode & SECMODE_PW_ENCRYPT) + } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { rc = -EIO; /* no crypt key only if plain text pwd */ - else + } else { + server->negflavor = CIFS_NEGFLAVOR_UNENCAP; server->capabilities &= ~CAP_EXTENDED_SECURITY; + } signing_check: if (!rc) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index ebb97b484ab..1609699e7be 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -405,6 +405,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) } server->dialect = le16_to_cpu(rsp->DialectRevision); + /* SMB2 only has an extended negflavor */ + server->negflavor = CIFS_NEGFLAVOR_EXTENDED; server->maxBuf = le32_to_cpu(rsp->MaxTransactSize); server->max_read = le32_to_cpu(rsp->MaxReadSize); server->max_write = le32_to_cpu(rsp->MaxWriteSize); -- cgit v1.2.3-18-g5258 From 28e11bd86d63ce18b481cd9f90bd5fa1b5ba746b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:01:00 -0400 Subject: cifs: add new fields to cifs_ses to track requested security flavor Currently we have the overrideSecFlg field, but it's quite cumbersome to work with. Add some new fields that will eventually supercede it. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 11 +++++++---- fs/cifs/cifsglob.h | 2 ++ fs/cifs/connect.c | 4 ++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 3752b9f6d9e..0f36654f22d 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -312,11 +312,14 @@ cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server) } static void -cifs_show_security(struct seq_file *s, struct TCP_Server_Info *server) +cifs_show_security(struct seq_file *s, struct cifs_ses *ses) { + if (ses->sectype == Unspecified) + return; + seq_printf(s, ",sec="); - switch (server->secType) { + switch (ses->sectype) { case LANMAN: seq_printf(s, "lanman"); break; @@ -338,7 +341,7 @@ cifs_show_security(struct seq_file *s, struct TCP_Server_Info *server) break; } - if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->sign) seq_printf(s, "i"); } @@ -369,7 +372,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root) srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; seq_printf(s, ",vers=%s", tcon->ses->server->vals->version_string); - cifs_show_security(s, tcon->ses->server); + cifs_show_security(s, tcon->ses); cifs_show_cache_flavor(s, cifs_sb); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 82ba4b97489..87d92e35e99 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -712,6 +712,8 @@ struct cifs_ses { char *password; struct session_key auth_key; struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ + enum securityEnum sectype; /* what security flavor was specified? */ + bool sign; /* is signing required? */ bool need_reconnect:1; /* connection reset, uid now invalid */ #ifdef CONFIG_CIFS_SMB2 __u16 session_flags; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 16013490223..2a8b2107ad5 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2501,6 +2501,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) ses->linux_uid = volume_info->linux_uid; ses->overrideSecFlg = volume_info->secFlg; + ses->sectype = volume_info->sectype; + ses->sign = volume_info->sign; mutex_lock(&ses->session_mutex); rc = cifs_negotiate_protocol(xid, ses); @@ -3918,6 +3920,8 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) vol_info->nocase = master_tcon->nocase; vol_info->local_lease = master_tcon->local_lease; vol_info->no_linux_ext = !master_tcon->unix_ext; + vol_info->sectype = master_tcon->ses->sectype; + vol_info->sign = master_tcon->ses->sign; rc = cifs_set_vol_auth(vol_info, master_tcon->ses); if (rc) { -- cgit v1.2.3-18-g5258 From 1e3cc57e474867771aba2bdf23d0c7d8fb5e4822 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 10 Jun 2013 17:12:23 -0500 Subject: add new fields to smb_vol to track the requested security flavor We have this to some degree already in secFlgs, but those get "or'ed" so there's no way to know what the last option requested was. Add new fields that will eventually supercede the secFlgs field in the cifs_ses. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/connect.c | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 87d92e35e99..2f3a89a2c49 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -402,6 +402,8 @@ struct smb_vol { umode_t file_mode; umode_t dir_mode; unsigned secFlg; + enum securityEnum sectype; /* sectype requested via mnt opts */ + bool sign; /* was signing requested via mnt opts? */ bool retry:1; bool intr:1; bool setuids:1; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2a8b2107ad5..f638b5e1a2d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1025,11 +1025,21 @@ static int cifs_parse_security_flavors(char *value, substring_t args[MAX_OPT_ARGS]; + /* + * With mount options, the last one should win. Reset any existing + * settings back to default. + */ + vol->sectype = Unspecified; + vol->sign = false; + switch (match_token(value, cifs_secflavor_tokens, args)) { case Opt_sec_krb5: + vol->sectype = Kerberos; vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_SIGN; break; case Opt_sec_krb5i: + vol->sectype = Kerberos; + vol->sign = true; vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN; break; case Opt_sec_krb5p: @@ -1037,26 +1047,36 @@ static int cifs_parse_security_flavors(char *value, cifs_dbg(VFS, "Krb5 cifs privacy not supported\n"); break; case Opt_sec_ntlmssp: + vol->sectype = RawNTLMSSP; vol->secFlg |= CIFSSEC_MAY_NTLMSSP; break; case Opt_sec_ntlmsspi: + vol->sectype = RawNTLMSSP; + vol->sign = true; vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN; break; case Opt_ntlm: /* ntlm is default so can be turned off too */ + vol->sectype = NTLM; vol->secFlg |= CIFSSEC_MAY_NTLM; break; case Opt_sec_ntlmi: + vol->sectype = NTLM; + vol->sign = true; vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN; break; case Opt_sec_ntlmv2: + vol->sectype = NTLMv2; vol->secFlg |= CIFSSEC_MAY_NTLMV2; break; case Opt_sec_ntlmv2i: + vol->sectype = NTLMv2; + vol->sign = true; vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN; break; #ifdef CONFIG_CIFS_WEAK_PW_HASH case Opt_sec_lanman: + vol->sectype = LANMAN; vol->secFlg |= CIFSSEC_MAY_LANMAN; break; #endif @@ -1426,6 +1446,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, break; case Opt_sign: vol->secFlg |= CIFSSEC_MUST_SIGN; + vol->sign = true; break; case Opt_seal: /* we do not do the following in secFlags because seal @@ -3894,6 +3915,10 @@ cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) case LANMAN: vol->secFlg = CIFSSEC_MUST_LANMAN; break; + default: + /* should never happen */ + vol->secFlg = 0; + break; } return cifs_set_cifscreds(vol, ses); -- cgit v1.2.3-18-g5258 From 38d77c50b4f4e3ea1687e119871364f1c8d2f531 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:01:00 -0400 Subject: cifs: track the enablement of signing in the TCP_Server_Info Currently, we determine this according to flags in the sec_mode, flags in the global_secflags and via other methods. That makes the semantics very hard to follow and there are corner cases where we don't handle this correctly. Add a new bool to the TCP_Server_Info that acts as a simple flag to tell us whether signing is enabled on this connection or not, and fix up the places that need to determine this to use that flag. This is a bit weird for the SMB2 case, where signing is per-session. SMB2 needs work in this area already though. The existing SMB2 code has similar logic to what we're using here, so there should be no real change in behavior. These changes should make it easier to implement per-session signing in the future though. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 + fs/cifs/cifsproto.h | 2 +- fs/cifs/cifssmb.c | 76 ++++++++++++++++++++++++++----------------------- fs/cifs/connect.c | 12 ++------ fs/cifs/misc.c | 3 +- fs/cifs/sess.c | 9 ++---- fs/cifs/smb1ops.c | 3 +- fs/cifs/smb2pdu.c | 40 +++++++++++--------------- fs/cifs/smb2transport.c | 3 +- fs/cifs/transport.c | 4 +-- 10 files changed, 71 insertions(+), 82 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 2f3a89a2c49..49020ae460c 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -511,6 +511,7 @@ struct TCP_Server_Info { struct task_struct *tsk; char server_GUID[16]; __u16 sec_mode; + bool sign; /* is signing enabled on this connection? */ bool session_estab; /* mark when very first sess is established */ #ifdef CONFIG_CIFS_SMB2 int echo_credits; /* echo reserved slots */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index f0e93ffe654..ede010fd046 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -212,7 +212,7 @@ extern int cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses); extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, struct nls_table *nls_info); -extern int cifs_enable_signing(struct TCP_Server_Info *server, unsigned int secFlags); +extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); extern int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses); extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 80ca6886a81..dd7e2f61f60 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -418,32 +418,43 @@ decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) } int -cifs_enable_signing(struct TCP_Server_Info *server, unsigned int secFlags) +cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) { - if ((secFlags & CIFSSEC_MAY_SIGN) == 0) { - /* MUST_SIGN already includes the MAY_SIGN FLAG - so if this is zero it means that signing is disabled */ - cifs_dbg(FYI, "Signing disabled\n"); - if (server->sec_mode & SECMODE_SIGN_REQUIRED) { - cifs_dbg(VFS, "Server requires packet signing to be enabled in /proc/fs/cifs/SecurityFlags\n"); - return -EOPNOTSUPP; + bool srv_sign_required = server->sec_mode & SECMODE_SIGN_REQUIRED; + bool srv_sign_enabled = server->sec_mode & SECMODE_SIGN_ENABLED; + bool mnt_sign_enabled = global_secflags & CIFSSEC_MAY_SIGN; + + /* + * Is signing required by mnt options? If not then check + * global_secflags to see if it is there. + */ + if (!mnt_sign_required) + mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) == + CIFSSEC_MUST_SIGN); + + /* + * If signing is required then it's automatically enabled too, + * otherwise, check to see if the secflags allow it. + */ + mnt_sign_enabled = mnt_sign_required ? mnt_sign_required : + (global_secflags & CIFSSEC_MAY_SIGN); + + /* If server requires signing, does client allow it? */ + if (srv_sign_required) { + if (!mnt_sign_enabled) { + cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!"); + return -ENOTSUPP; } - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) { - /* signing required */ - cifs_dbg(FYI, "Must sign - secFlags 0x%x\n", secFlags); - if ((server->sec_mode & - (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) { - cifs_dbg(VFS, "signing required but server lacks support\n"); - return -EOPNOTSUPP; - } else - server->sec_mode |= SECMODE_SIGN_REQUIRED; - } else { - /* signing optional ie CIFSSEC_MAY_SIGN */ - if ((server->sec_mode & SECMODE_SIGN_REQUIRED) == 0) - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); + server->sign = true; + } + + /* If client requires signing, does server allow it? */ + if (mnt_sign_required) { + if (!srv_sign_enabled) { + cifs_dbg(VFS, "Server does not support signing!"); + return -ENOTSUPP; + } + server->sign = true; } return 0; @@ -685,7 +696,7 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) signing_check: if (!rc) - rc = cifs_enable_signing(server, secFlags); + rc = cifs_enable_signing(server, ses->sign); neg_err_exit: cifs_buf_release(pSMB); @@ -810,9 +821,8 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses) pSMB->hdr.Mid = get_next_mid(ses->server); - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + if (ses->server->sign) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->hdr.Uid = ses->Suid; @@ -1573,8 +1583,7 @@ cifs_readv_callback(struct mid_q_entry *mid) switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: /* result already set, check signature */ - if (server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (server->sign) { int rc = 0; rc = cifs_verify_signature(&rqst, server, @@ -4827,11 +4836,8 @@ getDFSRetry: strncpy(pSMB->RequestFileName, search_name, name_len); } - if (ses->server) { - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - } + if (ses->server && ses->server->sign) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->hdr.Uid = ses->Suid; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f638b5e1a2d..acbb255352a 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2037,13 +2037,8 @@ match_security(struct TCP_Server_Info *server, struct smb_vol *vol) } /* now check if signing mode is acceptable */ - if ((secFlags & CIFSSEC_MAY_SIGN) == 0 && - (server->sec_mode & SECMODE_SIGN_REQUIRED)) - return false; - else if (((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) && - (server->sec_mode & - (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED)) == 0) - return false; + if (vol->sign && !server->sign) + return false; return true; } @@ -3704,8 +3699,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, } } - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->server->sign) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_STATUS32) { diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 1bec014779f..f7d4b2285ef 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -267,8 +267,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , if (treeCon->nocase) buffer->Flags |= SMBFLG_CASELESS; if ((treeCon->ses) && (treeCon->ses->server)) - if (treeCon->ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (treeCon->ses->server->sign) buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; } diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 0d0fe38f66a..82b784a62c1 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -138,8 +138,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB) capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->server->sign) pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_UNICODE) { @@ -427,8 +426,7 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC; - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (ses->server->sign) { flags |= NTLMSSP_NEGOTIATE_SIGN; if (!ses->server->session_estab) flags |= NTLMSSP_NEGOTIATE_KEY_XCH; @@ -466,8 +464,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC; - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (ses->server->sign) { flags |= NTLMSSP_NEGOTIATE_SIGN; if (!ses->server->session_estab) flags |= NTLMSSP_NEGOTIATE_KEY_XCH; diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 7d1c78bce4a..b28aabd33ed 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -449,8 +449,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) * WRITEX header, not including the 4 byte RFC1001 length. */ if (!(server->capabilities & CAP_LARGE_WRITE_X) || - (!(server->capabilities & CAP_UNIX) && - (server->sec_mode & (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED)))) + (!(server->capabilities & CAP_UNIX) && server->sign)) wsize = min_t(unsigned int, wsize, server->maxBuf - sizeof(WRITE_REQ) + 4); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 1609699e7be..ad8ef10de0b 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -119,8 +119,7 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , /* BB how does SMB2 do case sensitive? */ /* if (tcon->nocase) hdr->Flags |= SMBFLG_CASELESS; */ - if (tcon->ses && tcon->ses->server && - (tcon->ses->server->sec_mode & SECMODE_SIGN_REQUIRED)) + if (tcon->ses && tcon->ses->server && tcon->ses->server->sign) hdr->Flags |= SMB2_FLAGS_SIGNED; out: pdu->StructureSize2 = cpu_to_le16(parmsize); @@ -330,7 +329,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) int resp_buftype; struct TCP_Server_Info *server = ses->server; unsigned int sec_flags; - u16 temp = 0; int blob_offset, blob_length; char *security_blob; int flags = CIFS_NEG_OP; @@ -362,12 +360,12 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) inc_rfc1001_len(req, 2); /* only one of SMB2 signing flags may be set in SMB2 request */ - if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) - temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; - else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */ - temp = SMB2_NEGOTIATE_SIGNING_ENABLED; - - req->SecurityMode = cpu_to_le16(temp); + if (ses->sign) + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; + else if (global_secflags & CIFSSEC_MAY_SIGN) + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; + else + req->SecurityMode = 0; req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities); @@ -424,8 +422,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) goto neg_exit; } - cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags); - rc = cifs_enable_signing(server, sec_flags); + rc = cifs_enable_signing(server, ses->sign); #ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */ if (rc) goto neg_exit; @@ -457,7 +454,6 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ struct TCP_Server_Info *server = ses->server; unsigned int sec_flags; - u8 temp = 0; u16 blob_length = 0; char *security_blob; char *ntlmssp_blob = NULL; @@ -502,14 +498,13 @@ ssetup_ntlmssp_authenticate: req->hdr.CreditRequest = cpu_to_le16(3); /* only one of SMB2 signing flags may be set in SMB2 request */ - if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) - temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; - else if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) - temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; - else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */ - temp = SMB2_NEGOTIATE_SIGNING_ENABLED; - - req->SecurityMode = temp; + if (server->sign) + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; + else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; + else + req->SecurityMode = 0; + req->Capabilities = 0; req->Channel = 0; /* MBZ */ @@ -652,7 +647,7 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) /* since no tcon, smb2_init can not do this, so do here */ req->hdr.SessionId = ses->Suid; - if (server->sec_mode & SECMODE_SIGN_REQUIRED) + if (server->sign) req->hdr.Flags |= SMB2_FLAGS_SIGNED; rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0); @@ -1357,8 +1352,7 @@ smb2_readv_callback(struct mid_q_entry *mid) case MID_RESPONSE_RECEIVED: credits_received = le16_to_cpu(buf->CreditRequest); /* result already set, check signature */ - if (server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (server->sign) { int rc; rc = smb2_verify_signature(&rqst, server); diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 01f0ac80078..c802ecfa770 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -275,8 +275,7 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, dump_smb(mid->resp_buf, min_t(u32, 80, len)); /* convert the length into a more usable form */ - if ((len > 24) && - (server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) { + if (len > 24 && server->sign) { int rc; rc = smb2_verify_signature(&rqst, server); diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index bfbf4700d16..1996d6ceb83 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -463,7 +463,7 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) struct mid_q_entry *mid; /* enable signing if server requires it */ - if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (server->sign) hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; mid = AllocMidQEntry(hdr, server); @@ -612,7 +612,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, dump_smb(mid->resp_buf, min_t(u32, 92, len)); /* convert the length into a more usable form */ - if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (server->sign) { struct kvec iov; int rc = 0; struct smb_rqst rqst = { .rq_iov = &iov, -- cgit v1.2.3-18-g5258 From 3f618223dc0bdcbc8d510350e78ee2195ff93768 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 12 Jun 2013 19:52:14 -0500 Subject: move sectype to the cifs_ses instead of TCP_Server_Info Now that we track what sort of NEGOTIATE response was received, stop mandating that every session on a socket use the same type of auth. Push that decision out into the session setup code, and make the sectype a per-session property. This should allow us to mix multiple sectypes on a socket as long as they are compatible with the NEGOTIATE response. With this too, we can now eliminate the ses->secFlg field since that info is redundant and harder to work with than a securityEnum. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 4 +- fs/cifs/cifsglob.h | 2 - fs/cifs/cifsproto.h | 2 + fs/cifs/cifssmb.c | 92 +++++++++------------------------------ fs/cifs/connect.c | 117 ++++++++++++++------------------------------------ fs/cifs/sess.c | 57 +++++++++++++++++++++++- fs/cifs/smb2pdu.c | 21 +-------- 7 files changed, 115 insertions(+), 180 deletions(-) diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index a85a83d1d00..30bea6bd302 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -535,7 +535,7 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) return rc; } - if (ses->server->secType == RawNTLMSSP) + if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) memcpy(ses->auth_key.response + offset, ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); else @@ -567,7 +567,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) char ntlmv2_hash[16]; unsigned char *tiblob = NULL; /* target info blob */ - if (ses->server->secType == RawNTLMSSP) { + if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) { if (!ses->domainName) { rc = find_domain_name(ses, nls_cp); if (rc) { diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 49020ae460c..ad3408042ff 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -401,7 +401,6 @@ struct smb_vol { kgid_t backupgid; umode_t file_mode; umode_t dir_mode; - unsigned secFlg; enum securityEnum sectype; /* sectype requested via mnt opts */ bool sign; /* was signing requested via mnt opts? */ bool retry:1; @@ -519,7 +518,6 @@ struct TCP_Server_Info { bool echoes:1; /* enable echoes */ #endif u16 dialect; /* dialect index that server chose */ - enum securityEnum secType; bool oplocks:1; /* enable oplocks */ unsigned int maxReq; /* Clients should submit no more */ /* than maxReq distinct unanswered SMBs to the server when using */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ede010fd046..a82b3c09888 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -118,6 +118,8 @@ extern void header_assemble(struct smb_hdr *, char /* command */ , extern int small_smb_init_no_tc(const int smb_cmd, const int wct, struct cifs_ses *ses, void **request_buf); +extern enum securityEnum select_sectype(struct TCP_Server_Info *server, + enum securityEnum requested); extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *nls_cp); extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index dd7e2f61f60..a35aad2060c 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -368,11 +368,12 @@ vt2_err: } static int -decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) +decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr) { int rc = 0; u16 count; char *guid = pSMBr->u.extended_response.GUID; + struct TCP_Server_Info *server = ses->server; count = get_bcc(&pSMBr->hdr); if (count < SMB1_CLIENT_GUID_SIZE) @@ -391,27 +392,13 @@ decode_ext_sec_blob(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) } if (count == SMB1_CLIENT_GUID_SIZE) { - server->secType = RawNTLMSSP; + server->sec_ntlmssp = true; } else { count -= SMB1_CLIENT_GUID_SIZE; rc = decode_negTokenInit( pSMBr->u.extended_response.SecurityBlob, count, server); if (rc != 1) return -EINVAL; - - /* Make sure server supports what we want to use */ - switch(server->secType) { - case Kerberos: - if (!server->sec_kerberos && !server->sec_mskerberos) - return -EOPNOTSUPP; - break; - case RawNTLMSSP: - if (!server->sec_ntlmssp) - return -EOPNOTSUPP; - break; - default: - return -EOPNOTSUPP; - } } return 0; @@ -462,8 +449,7 @@ cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) #ifdef CONFIG_CIFS_WEAK_PW_HASH static int -decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, - unsigned int secFlags) +decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) { __s16 tmp; struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr; @@ -471,12 +457,6 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT) return -EOPNOTSUPP; - if ((secFlags & CIFSSEC_MAY_LANMAN) || (secFlags & CIFSSEC_MAY_PLNTXT)) - server->secType = LANMAN; - else { - cifs_dbg(VFS, "mount failed weak security disabled in /proc/fs/cifs/SecurityFlags\n"); - return -EOPNOTSUPP; - } server->sec_mode = le16_to_cpu(rsp->SecurityMode); server->maxReq = min_t(unsigned int, le16_to_cpu(rsp->MaxMpxCount), @@ -542,8 +522,7 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, } #else static inline int -decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, - unsigned int secFlags) +decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) { cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n"); return -EOPNOTSUPP; @@ -551,17 +530,20 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr, #endif static bool -should_set_ext_sec_flag(unsigned int secFlags) +should_set_ext_sec_flag(enum securityEnum sectype) { - if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) - return true; - else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) - return true; - else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) - return true; - else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) + switch (sectype) { + case RawNTLMSSP: + case Kerberos: return true; - return false; + case Unspecified: + if (global_secflags & + (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)) + return true; + /* Fallthrough */ + default: + return false; + } } int @@ -574,7 +556,6 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) int i; struct TCP_Server_Info *server = ses->server; u16 count; - unsigned int secFlags; if (!server) { WARN(1, "%s: server is NULL!\n", __func__); @@ -586,18 +567,10 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) if (rc) return rc; - /* if any of auth flags (ie not sign or seal) are overriden use them */ - if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) - secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */ - else /* if override flags set only sign/seal OR them with global auth */ - secFlags = global_secflags | ses->overrideSecFlg; - - cifs_dbg(FYI, "secFlags 0x%x\n", secFlags); - pSMB->hdr.Mid = get_next_mid(server); pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS); - if (should_set_ext_sec_flag(secFlags)) { + if (should_set_ext_sec_flag(ses->sectype)) { cifs_dbg(FYI, "Requesting extended security."); pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; } @@ -627,7 +600,7 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) goto neg_err_exit; } else if (pSMBr->hdr.WordCount == 13) { server->negflavor = CIFS_NEGFLAVOR_LANMAN; - rc = decode_lanman_negprot_rsp(server, pSMBr, secFlags); + rc = decode_lanman_negprot_rsp(server, pSMBr); goto signing_check; } else if (pSMBr->hdr.WordCount != 17) { /* unknown wct */ @@ -640,31 +613,6 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) if ((server->sec_mode & SECMODE_USER) == 0) cifs_dbg(FYI, "share mode security\n"); - if ((server->sec_mode & SECMODE_PW_ENCRYPT) == 0) -#ifdef CONFIG_CIFS_WEAK_PW_HASH - if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0) -#endif /* CIFS_WEAK_PW_HASH */ - cifs_dbg(VFS, "Server requests plain text password but client support disabled\n"); - - if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) - server->secType = NTLMv2; - else if (secFlags & CIFSSEC_MAY_NTLM) - server->secType = NTLM; - else if (secFlags & CIFSSEC_MAY_NTLMV2) - server->secType = NTLMv2; - else if (secFlags & CIFSSEC_MAY_KRB5) - server->secType = Kerberos; - else if (secFlags & CIFSSEC_MAY_NTLMSSP) - server->secType = RawNTLMSSP; - else if (secFlags & CIFSSEC_MAY_LANMAN) - server->secType = LANMAN; - else { - rc = -EOPNOTSUPP; - cifs_dbg(VFS, "Invalid security type\n"); - goto neg_err_exit; - } - /* else ... any others ...? */ - /* one byte, so no need to convert this or EncryptionKeyLen from little endian */ server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), @@ -686,7 +634,7 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) server->capabilities & CAP_EXTENDED_SECURITY) && (pSMBr->EncryptionKeyLength == 0)) { server->negflavor = CIFS_NEGFLAVOR_EXTENDED; - rc = decode_ext_sec_blob(server, pSMBr); + rc = decode_ext_sec_blob(ses, pSMBr); } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { rc = -EIO; /* no crypt key only if plain text pwd */ } else { diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index acbb255352a..c4c6aa99ee1 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1033,56 +1033,40 @@ static int cifs_parse_security_flavors(char *value, vol->sign = false; switch (match_token(value, cifs_secflavor_tokens, args)) { + case Opt_sec_krb5p: + cifs_dbg(VFS, "sec=krb5p is not supported!\n"); + return 1; + case Opt_sec_krb5i: + vol->sign = true; + /* Fallthrough */ case Opt_sec_krb5: vol->sectype = Kerberos; - vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_SIGN; break; - case Opt_sec_krb5i: - vol->sectype = Kerberos; + case Opt_sec_ntlmsspi: vol->sign = true; - vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN; - break; - case Opt_sec_krb5p: - /* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */ - cifs_dbg(VFS, "Krb5 cifs privacy not supported\n"); - break; + /* Fallthrough */ case Opt_sec_ntlmssp: vol->sectype = RawNTLMSSP; - vol->secFlg |= CIFSSEC_MAY_NTLMSSP; break; - case Opt_sec_ntlmsspi: - vol->sectype = RawNTLMSSP; + case Opt_sec_ntlmi: vol->sign = true; - vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN; - break; + /* Fallthrough */ case Opt_ntlm: - /* ntlm is default so can be turned off too */ vol->sectype = NTLM; - vol->secFlg |= CIFSSEC_MAY_NTLM; break; - case Opt_sec_ntlmi: - vol->sectype = NTLM; + case Opt_sec_ntlmv2i: vol->sign = true; - vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN; - break; + /* Fallthrough */ case Opt_sec_ntlmv2: vol->sectype = NTLMv2; - vol->secFlg |= CIFSSEC_MAY_NTLMV2; - break; - case Opt_sec_ntlmv2i: - vol->sectype = NTLMv2; - vol->sign = true; - vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN; break; #ifdef CONFIG_CIFS_WEAK_PW_HASH case Opt_sec_lanman: vol->sectype = LANMAN; - vol->secFlg |= CIFSSEC_MAY_LANMAN; break; #endif case Opt_sec_none: vol->nullauth = 1; - vol->secFlg |= CIFSSEC_MAY_NTLM; break; default: cifs_dbg(VFS, "bad security option: %s\n", value); @@ -1445,7 +1429,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, vol->local_lease = 1; break; case Opt_sign: - vol->secFlg |= CIFSSEC_MUST_SIGN; vol->sign = true; break; case Opt_seal: @@ -2003,40 +1986,19 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr, static bool match_security(struct TCP_Server_Info *server, struct smb_vol *vol) { - unsigned int secFlags; - - if (vol->secFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) - secFlags = vol->secFlg; - else - secFlags = global_secflags | vol->secFlg; - - switch (server->secType) { - case LANMAN: - if (!(secFlags & (CIFSSEC_MAY_LANMAN|CIFSSEC_MAY_PLNTXT))) - return false; - break; - case NTLMv2: - if (!(secFlags & CIFSSEC_MAY_NTLMV2)) - return false; - break; - case NTLM: - if (!(secFlags & CIFSSEC_MAY_NTLM)) - return false; - break; - case Kerberos: - if (!(secFlags & CIFSSEC_MAY_KRB5)) - return false; - break; - case RawNTLMSSP: - if (!(secFlags & CIFSSEC_MAY_NTLMSSP)) - return false; - break; - default: - /* shouldn't happen */ + /* + * The select_sectype function should either return the vol->sectype + * that was specified, or "Unspecified" if that sectype was not + * compatible with the given NEGOTIATE request. + */ + if (select_sectype(server, vol->sectype) == Unspecified) return false; - } - /* now check if signing mode is acceptable */ + /* + * Now check if signing mode is acceptable. No need to check + * global_secflags at this point since if MUST_SIGN is set then + * the server->sign had better be too. + */ if (vol->sign && !server->sign) return false; @@ -2239,7 +2201,11 @@ out_err: static int match_session(struct cifs_ses *ses, struct smb_vol *vol) { - switch (ses->server->secType) { + if (vol->sectype != Unspecified && + vol->sectype != ses->sectype) + return 0; + + switch (ses->sectype) { case Kerberos: if (!uid_eq(vol->cred_uid, ses->cred_uid)) return 0; @@ -2516,7 +2482,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) ses->cred_uid = volume_info->cred_uid; ses->linux_uid = volume_info->linux_uid; - ses->overrideSecFlg = volume_info->secFlg; ses->sectype = volume_info->sectype; ses->sign = volume_info->sign; @@ -3681,7 +3646,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, NTLMv2 password here) */ #ifdef CONFIG_CIFS_WEAK_PW_HASH if ((global_secflags & CIFSSEC_MAY_LANMAN) && - (ses->server->secType == LANMAN)) + (ses->sectype == LANMAN)) calc_lanman_hash(tcon->password, ses->server->cryptkey, ses->server->sec_mode & SECMODE_PW_ENCRYPT ? true : false, @@ -3893,27 +3858,11 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, static int cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) { - switch (ses->server->secType) { - case Kerberos: - vol->secFlg = CIFSSEC_MUST_KRB5; + vol->sectype = ses->sectype; + + /* krb5 is special, since we don't need username or pw */ + if (vol->sectype == Kerberos) return 0; - case NTLMv2: - vol->secFlg = CIFSSEC_MUST_NTLMV2; - break; - case NTLM: - vol->secFlg = CIFSSEC_MUST_NTLM; - break; - case RawNTLMSSP: - vol->secFlg = CIFSSEC_MUST_NTLMSSP; - break; - case LANMAN: - vol->secFlg = CIFSSEC_MUST_LANMAN; - break; - default: - /* should never happen */ - vol->secFlg = 0; - break; - } return cifs_set_cifscreds(vol, ses); } diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 82b784a62c1..79358e341fd 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -550,6 +550,56 @@ setup_ntlmv2_ret: return rc; } +enum securityEnum +select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) +{ + switch (server->negflavor) { + case CIFS_NEGFLAVOR_EXTENDED: + switch (requested) { + case Kerberos: + case RawNTLMSSP: + return requested; + case Unspecified: + if (server->sec_ntlmssp && + (global_secflags & CIFSSEC_MAY_NTLMSSP)) + return RawNTLMSSP; + if ((server->sec_kerberos || server->sec_mskerberos) && + (global_secflags & CIFSSEC_MAY_KRB5)) + return Kerberos; + /* Fallthrough */ + default: + return Unspecified; + } + case CIFS_NEGFLAVOR_UNENCAP: + switch (requested) { + case NTLM: + case NTLMv2: + return requested; + case Unspecified: + if (global_secflags & CIFSSEC_MAY_NTLMV2) + return NTLMv2; + if (global_secflags & CIFSSEC_MAY_NTLM) + return NTLM; + /* Fallthrough */ + default: + return Unspecified; + } + case CIFS_NEGFLAVOR_LANMAN: + switch (requested) { + case LANMAN: + return requested; + case Unspecified: + if (global_secflags & CIFSSEC_MAY_LANMAN) + return LANMAN; + /* Fallthrough */ + default: + return Unspecified; + } + default: + return Unspecified; + } +} + int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *nls_cp) @@ -576,8 +626,13 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, return -EINVAL; } - type = ses->server->secType; + type = select_sectype(ses->server, ses->sectype); cifs_dbg(FYI, "sess setup type %d\n", type); + if (type == Unspecified) { + cifs_dbg(VFS, "Unable to select appropriate authentication method!"); + return -EINVAL; + } + if (type == RawNTLMSSP) { /* if memory allocation is successful, caller of this function * frees it. diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index ad8ef10de0b..fd2ea427128 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -328,7 +328,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) int rc = 0; int resp_buftype; struct TCP_Server_Info *server = ses->server; - unsigned int sec_flags; int blob_offset, blob_length; char *security_blob; int flags = CIFS_NEG_OP; @@ -344,14 +343,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) if (rc) return rc; - /* if any of auth flags (ie not sign or seal) are overriden use them */ - if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) - sec_flags = ses->overrideSecFlg; /* BB FIXME fix sign flags?*/ - else /* if override flags set only sign/seal OR them with global auth */ - sec_flags = global_secflags | ses->overrideSecFlg; - - cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags); - req->hdr.SessionId = 0; req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id); @@ -453,7 +444,6 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, int resp_buftype; __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ struct TCP_Server_Info *server = ses->server; - unsigned int sec_flags; u16 blob_length = 0; char *security_blob; char *ntlmssp_blob = NULL; @@ -474,7 +464,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, if (!ses->ntlmssp) return -ENOMEM; - ses->server->secType = RawNTLMSSP; + /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */ + ses->sectype = RawNTLMSSP; ssetup_ntlmssp_authenticate: if (phase == NtLmChallenge) @@ -484,14 +475,6 @@ ssetup_ntlmssp_authenticate: if (rc) return rc; - /* if any of auth flags (ie not sign or seal) are overriden use them */ - if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) - sec_flags = ses->overrideSecFlg; /* BB FIXME fix sign flags?*/ - else /* if override flags set only sign/seal OR them with global auth */ - sec_flags = global_secflags | ses->overrideSecFlg; - - cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags); - req->hdr.SessionId = 0; /* First session, not a reauthenticate */ req->VcNumber = 0; /* MBZ */ /* to enable echos and oplocks */ -- cgit v1.2.3-18-g5258 From 896a8fc25bd31a81afb35e65468484f34f1c15d6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:01:01 -0400 Subject: cifs: update the default global_secflags to include "raw" NTLMv2 Before this patchset, the global_secflags could only offer up a single sectype. With the new set though we have the ability to allow different sectypes since we sort out the one to use after talking to the server. Change the global_secflags to allow NTLMSSP or NTLMv2 by default. If the server sets the extended security bit in the Negotiate response, then we'll use NTLMSSP. If it doesn't then we'll use raw NTLMv2. Mounting a LANMAN server will still require a sec= option by default. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index ad3408042ff..c3162c2f955 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1340,7 +1340,7 @@ require use of the stronger protocol */ #define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ #define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */ -#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMSSP) +#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP) #define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2) #define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP) /* -- cgit v1.2.3-18-g5258 From 7715dad8e10c4115ec85471300b452c9194146b5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:01:01 -0400 Subject: cifs: clean up the SecurityFlags write handler The SecurityFlags handler uses an obsolete simple_strtoul() call, and doesn't really handle the bounds checking well. Fix it to use kstrtouint() instead. Clean up the error messages as well and fix a bogus check for an unsigned int to be less than 0. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index d5974834602..856f8f5fc29 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -598,6 +598,7 @@ static int cifs_security_flags_proc_open(struct inode *inode, struct file *file) static ssize_t cifs_security_flags_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { + int rc; unsigned int flags; char flags_string[12]; char c; @@ -620,26 +621,33 @@ static ssize_t cifs_security_flags_proc_write(struct file *file, global_secflags = CIFSSEC_MAX; return count; } else if (!isdigit(c)) { - cifs_dbg(VFS, "invalid flag %c\n", c); + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", + flags_string); return -EINVAL; } } - /* else we have a number */ - flags = simple_strtoul(flags_string, NULL, 0); + /* else we have a number */ + rc = kstrtouint(flags_string, 0, &flags); + if (rc) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", + flags_string); + return rc; + } cifs_dbg(FYI, "sec flags 0x%x\n", flags); - if (flags <= 0) { - cifs_dbg(VFS, "invalid security flags %s\n", flags_string); + if (flags == 0) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string); return -EINVAL; } if (flags & ~CIFSSEC_MASK) { - cifs_dbg(VFS, "attempt to set unsupported security flags 0x%x\n", + cifs_dbg(VFS, "Unsupported security flags: 0x%x\n", flags & ~CIFSSEC_MASK); return -EINVAL; } + /* flags look ok - update the global security flags for cifs module */ global_secflags = flags; if (global_secflags & CIFSSEC_MUST_SIGN) { -- cgit v1.2.3-18-g5258 From 9cd2e62c4952a00543685c6ee21cd2bf69b621e9 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 12 Jun 2013 19:59:03 -0500 Subject: Fix endian error in SMB2 protocol negotiation Fix minor endian error in Jeff's auth rewrite Reviewed-by: Jeff Laytonn Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index fd2ea427128..a20a8a7e147 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -352,9 +352,9 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) /* only one of SMB2 signing flags may be set in SMB2 request */ if (ses->sign) - req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; + req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); else if (global_secflags & CIFSSEC_MAY_SIGN) - req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; + req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); else req->SecurityMode = 0; -- cgit v1.2.3-18-g5258 From 20b6d8b42e7e7c9af5046fe525d6709e10d14992 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 12 Jun 2013 22:48:41 -0500 Subject: Add SMB3.02 dialect support The new Windows update supports SMB3.02 dialect, a minor update to SMB3. This patch adds support for mounting with vers=3.02 Signed-off-by: Steve French Reviewed-by: Jeff Layton --- fs/cifs/cifsglob.h | 4 ++++ fs/cifs/connect.c | 5 +++++ fs/cifs/smb2ops.c | 18 ++++++++++++++++++ fs/cifs/smb2pdu.c | 2 ++ fs/cifs/smb2pdu.h | 1 + 5 files changed, 30 insertions(+) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c3162c2f955..f13cbbeaf09 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -175,6 +175,7 @@ enum smb_version { Smb_20, Smb_21, Smb_30, + Smb_302, }; struct mid_q_entry; @@ -1486,4 +1487,7 @@ extern struct smb_version_values smb21_values; #define SMB30_VERSION_STRING "3.0" extern struct smb_version_operations smb30_operations; extern struct smb_version_values smb30_values; +#define SMB302_VERSION_STRING "3.02" +/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ +extern struct smb_version_values smb302_values; #endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c4c6aa99ee1..d5f866afe56 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -276,6 +276,7 @@ static const match_table_t cifs_smb_version_tokens = { { Smb_20, SMB20_VERSION_STRING}, { Smb_21, SMB21_VERSION_STRING }, { Smb_30, SMB30_VERSION_STRING }, + { Smb_302, SMB302_VERSION_STRING }, }; static int ip_connect(struct TCP_Server_Info *server); @@ -1124,6 +1125,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol) vol->ops = &smb30_operations; vol->vals = &smb30_values; break; + case Smb_302: + vol->ops = &smb30_operations; /* currently identical with 3.0 */ + vol->vals = &smb302_values; + break; #endif default: cifs_dbg(VFS, "Unknown vers= option specified: %s\n", value); diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index f2e76f3b0c6..14539c71e6e 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -746,3 +746,21 @@ struct smb_version_values smb30_values = { .cap_large_files = SMB2_LARGE_FILES, .oplock_read = SMB2_OPLOCK_LEVEL_II, }; + +struct smb_version_values smb302_values = { + .version_string = SMB302_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .oplock_read = SMB2_OPLOCK_LEVEL_II, +}; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index a20a8a7e147..cb155bfb341 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -386,6 +386,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) + cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); else { cifs_dbg(VFS, "Illegal dialect returned by server %d\n", le16_to_cpu(rsp->DialectRevision)); diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 4cb4ced258c..3da33da8123 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -170,6 +170,7 @@ struct smb2_negotiate_req { #define SMB20_PROT_ID 0x0202 #define SMB21_PROT_ID 0x0210 #define SMB30_PROT_ID 0x0300 +#define SMB302_PROT_ID 0x0302 #define BAD_PROT_ID 0xFFFF /* SecurityMode flags */ -- cgit v1.2.3-18-g5258 From 2b5dc286da3917435106da6431e8d06209b01953 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 13 Jun 2013 10:51:10 -0500 Subject: Add some missing SMB3 and SMB3.02 flags A few missing flags from SMB3.0 dialect, one missing from 2.1, and the new #define flags for SMB3.02 Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/smb2pdu.h | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 3da33da8123..e27ad39f2bd 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -287,7 +287,11 @@ struct smb2_tree_connect_rsp { #define SHI1005_FLAGS_ENABLE_HASH 0x00002000 /* Possible share capabilities */ -#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) +#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */ +#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */ +#define SMB2_SHARE_CAP_SCALEOUT cpu_to_le32(0x00000020) /* 3.0 */ +#define SMB2_SHARE_CAP_CLUSTER cpu_to_le32(0x00000040) /* 3.0 */ +#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */ struct smb2_tree_disconnect_req { struct smb2_hdr hdr; @@ -518,17 +522,25 @@ struct smb2_flush_rsp { __le16 Reserved; } __packed; +/* For read request Flags field below, following flag is defined for SMB3.02 */ +#define SMB2_READFLAG_READ_UNBUFFERED 0x01 + +/* Channel field for read and write: exactly one of following flags can be set*/ +#define SMB2_CHANNEL_NONE 0x00000000 +#define SMB2_CHANNEL_RDMA_V1 0x00000001 /* SMB3 or later */ +#define SMB2_CHANNEL_RDMA_V1_INVALIDATE 0x00000001 /* SMB3.02 or later */ + struct smb2_read_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 49 */ __u8 Padding; /* offset from start of SMB2 header to place read */ - __u8 Reserved; + __u8 Flags; /* MBZ unless SMB3.02 or later */ __le32 Length; __le64 Offset; __u64 PersistentFileId; /* opaque endianness */ __u64 VolatileFileId; /* opaque endianness */ __le32 MinimumCount; - __le32 Channel; /* Reserved MBZ */ + __le32 Channel; /* MBZ except for SMB3 or later */ __le32 RemainingBytes; __le16 ReadChannelInfoOffset; /* Reserved MBZ */ __le16 ReadChannelInfoLength; /* Reserved MBZ */ @@ -546,8 +558,9 @@ struct smb2_read_rsp { __u8 Buffer[1]; } __packed; -/* For write request Flags field below the following flag is defined: */ -#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 +/* For write request Flags field below the following flags are defined: */ +#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 /* SMB2.1 or later */ +#define SMB2_WRITEFLAG_WRITE_UNBUFFERED 0x00000002 /* SMB3.02 or later */ struct smb2_write_req { struct smb2_hdr hdr; -- cgit v1.2.3-18-g5258 From 769ee6a4024434d1960acafd7adde38538bbe3da Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 19 Jun 2013 14:15:30 -0500 Subject: Add ability to dipslay SMB3 share flags and capabilities for debugging SMB3 protocol adds various optional per-share capabilities (and SMB3.02 adds one more beyond that). Add ability to dump (/proc/fs/cifs/DebugData) the share capabilities and share flags to improve debugging. Signed-off-by: Steve French Acked-by: Jeff Layton --- fs/cifs/cifs_debug.c | 4 +++- fs/cifs/cifsglob.h | 3 ++- fs/cifs/smb2ops.c | 20 ++++++++++++++++++++ fs/cifs/smb2pdu.c | 1 + 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 856f8f5fc29..0315824bbf0 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -213,7 +213,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) tcon->nativeFileSystem); } seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x" - "\nPathComponentMax: %d Status: 0x%d", + "\n\tPathComponentMax: %d Status: 0x%d", le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), le32_to_cpu(tcon->fsAttrInfo.Attributes), le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), @@ -224,6 +224,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) seq_puts(m, " type: CDROM "); else seq_printf(m, " type: %d ", dev_type); + if (server->ops->dump_share_caps) + server->ops->dump_share_caps(m, tcon); if (tcon->need_reconnect) seq_puts(m, "\tDISCONNECTED "); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index f13cbbeaf09..9a1e37aad3b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -223,6 +223,7 @@ struct smb_version_operations { void (*dump_detail)(void *); void (*clear_stats)(struct cifs_tcon *); void (*print_stats)(struct seq_file *m, struct cifs_tcon *); + void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *); /* verify the message */ int (*check_message)(char *, unsigned int); bool (*is_oplock_break)(char *, struct TCP_Server_Info *); @@ -809,7 +810,7 @@ struct cifs_tcon { #ifdef CONFIG_CIFS_SMB2 bool print:1; /* set if connection to printer share */ bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */ - __u32 capabilities; + __le32 capabilities; __u32 share_flags; __u32 maximal_access; __u32 vol_serial_number; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 14539c71e6e..76df656e4b2 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -280,6 +280,25 @@ smb2_clear_stats(struct cifs_tcon *tcon) #endif } +static void +smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) +{ + seq_puts(m, "\n\tShare Capabilities:"); + if (tcon->capabilities & SMB2_SHARE_CAP_DFS) + seq_puts(m, " DFS,"); + if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) + seq_puts(m, " CONTINUOUS AVAILABILITY,"); + if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT) + seq_puts(m, " SCALEOUT,"); + if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) + seq_puts(m, " CLUSTER,"); + if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC) + seq_puts(m, " ASYMMETRIC,"); + if (tcon->capabilities == 0) + seq_puts(m, " None"); + seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); +} + static void smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) { @@ -645,6 +664,7 @@ struct smb_version_operations smb30_operations = { .dump_detail = smb2_dump_detail, .clear_stats = smb2_clear_stats, .print_stats = smb2_print_stats, + .dump_share_caps = smb2_dump_share_caps, .is_oplock_break = smb2_is_valid_oplock_break, .need_neg = smb2_need_neg, .negotiate = smb2_negotiate, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index cb155bfb341..f7422a68b16 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -741,6 +741,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, } tcon->share_flags = le32_to_cpu(rsp->ShareFlags); + tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); tcon->tidStatus = CifsGood; tcon->need_reconnect = false; -- cgit v1.2.3-18-g5258 From f43a033d44c3f2f6b153c9c63fff0132f4314f24 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 19 Jun 2013 16:58:39 -0500 Subject: Update cifs version number More than 160 fixes since we last bumped the version number of cifs.ko. Update to version 2.01 so it is easier in modinfo to tell that fixes are in. Signed-off-by: Steve French --- fs/cifs/cifsfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 0e32c3446ce..59aa8db3472 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -132,5 +132,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION "2.0" +#define CIFS_VERSION "2.01" #endif /* _CIFSFS_H */ -- cgit v1.2.3-18-g5258 From be7457d388d25e5d2ebba4c7f216b47b5e3d1eef Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 19 Jun 2013 17:41:10 -0500 Subject: Update headers to update various SMB3 ioctl definitions MS-SMB2 Section 2.2.31 lists fsctls. Update our list of valid cifs/smb2/smb3 fsctls and some related structs based on more recent version of docs. Additional detail on less common ones can be found in MS-FSCC section 2.3. CopyChunk (server side copy, ie refcopy) will depend on a few of these Signed-off-by: Steve French --- fs/cifs/cifspdu.h | 8 ++++++++ fs/cifs/smb2pdu.h | 48 +++++++++++++++++++++++++++++++++++++++++++++++- fs/cifs/smbfsctl.h | 27 +++++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 4e6135a39fd..7e8523c5c18 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -1315,6 +1315,14 @@ typedef struct smb_com_ntransact_rsp { /* parms and data follow */ } __attribute__((packed)) NTRANSACT_RSP; +/* See MS-SMB 2.2.7.2.1.1 */ +struct srv_copychunk { + __le64 SourceOffset; + __le64 DestinationOffset; + __le32 CopyLength; + __u32 Reserved; +} __packed; + typedef struct smb_com_transaction_ioctl_req { struct smb_hdr hdr; /* wct = 23 */ __u8 MaxSetupCount; diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index e27ad39f2bd..d351377d251 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -1,7 +1,7 @@ /* * fs/cifs/smb2pdu.h * - * Copyright (c) International Business Machines Corp., 2009, 2010 + * Copyright (c) International Business Machines Corp., 2009, 2013 * Etersoft, 2012 * Author(s): Steve French (sfrench@us.ibm.com) * Pavel Shilovsky (pshilovsky@samba.org) 2012 @@ -482,6 +482,52 @@ struct create_lease { struct lease_context lcontext; } __packed; +/* this goes in the ioctl buffer when doing a copychunk request */ +struct copychunk_ioctl { + char SourceKey[24]; + __le32 ChunkCount; /* we are only sending 1 */ + __le32 Reserved; + /* array will only be one chunk long for us */ + __le64 SourceOffset; + __le64 TargetOffset; + __u32 Length; /* how many bytes to copy */ + __u32 Reserved2; +} __packed; + +struct smb2_ioctl_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u16 Reserved; + __le32 CtlCode; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __le32 InputOffset; + __le32 InputCount; + __le32 MaxInputResponse; + __le32 OutputOffset; + __le32 OutputCount; + __le32 MaxOutputResponse; + __le32 Flags; + __u32 Reserved2; + char Buffer[0]; +} __packed; + +struct smb2_ioctl_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u16 Reserved; + __le32 CtlCode; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __le32 InputOffset; + __le32 InputCount; + __le32 OutputOffset; + __le32 OutputCount; + __le32 Flags; + __u32 Reserved2; + /* char * buffer[] */ +} __packed; + /* Currently defined values for close flags */ #define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) struct smb2_close_req { diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h index 7056b891e08..d952ee48f4d 100644 --- a/fs/cifs/smbfsctl.h +++ b/fs/cifs/smbfsctl.h @@ -1,7 +1,7 @@ /* * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions * - * Copyright (c) International Business Machines Corp., 2002,2009 + * Copyright (c) International Business Machines Corp., 2002,2013 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ /* IOCTL information */ /* * List of ioctl/fsctl function codes that are or could be useful in the - * future to remote clients like cifs or SMB2 client. There is probably + * future to remote clients like cifs or SMB2/SMB3 client. This is probably * a slightly larger set of fsctls that NTFS local filesystem could handle, * including the seven below that we do not have struct definitions for. * Even with protocol definitions for most of these now available, we still @@ -30,7 +30,13 @@ * remotely. Some of the following, such as the encryption/compression ones * could be invoked from tools via a specialized hook into the VFS rather * than via the standard vfs entry points + * + * See MS-SMB2 Section 2.2.31 (last checked June 2013, all of that list are + * below). Additional detail on less common ones can be found in MS-FSCC + * section 2.3. */ +#define FSCTL_DFS_GET_REFERRALS 0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 #define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 #define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 #define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 @@ -71,14 +77,31 @@ #define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ #define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ #define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */ #define FSCTL_SIS_LINK_FILES 0x0009C104 #define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ #define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ /* strange that the number for this op is not sequential with previous op */ #define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ +/* Enumerate previous versions of a file */ +#define FSCTL_SRV_ENUMERATE_SNAPSHOTS 0x00144064 +/* Retrieve an opaque file reference for server-side data movement ie copy */ +#define FSCTL_SRV_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* 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_VALIDATE_NEGOTIATE_INFO 0x00140204 /* BB add struct */ +/* Perform server-side data movement */ +#define FSCTL_SRV_COPYCHUNK 0x001440F2 +#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */ +#define FSCTL_SRV_READ_HASH 0x001441BB /* BB add struct */ #define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 #define IO_REPARSE_TAG_HSM 0xC0000004 #define IO_REPARSE_TAG_SIS 0x80000007 + +/* fsctl flags */ +/* If Flags is set to this value, the request is an FSCTL not ioctl request */ +#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 + -- cgit v1.2.3-18-g5258 From 46b51d0835ef1e75dd48fe10c90f9c775301d9ee Mon Sep 17 00:00:00 2001 From: Zhao Hongjiang Date: Mon, 24 Jun 2013 01:57:47 -0500 Subject: cifs: using strlcpy instead of strncpy for NUL terminated string, need alway set '\0' in the end. Signed-off-by: Zhao Hongjiang Signed-off-by: Steve French --- fs/cifs/connect.c | 2 +- fs/cifs/smb2pdu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index d5f866afe56..53a1780cd07 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3732,7 +3732,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, } bcc_ptr += length + 1; bytes_left -= (length + 1); - strncpy(tcon->treeName, tree, MAX_TREE_SIZE); + strlcpy(tcon->treeName, tree, sizeof(tcon->treeName)); /* mostly informational -- no need to fail on error here */ kfree(tcon->nativeFileSystem); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index f7422a68b16..92fd6c59c12 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -746,7 +746,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, tcon->tidStatus = CifsGood; tcon->need_reconnect = false; tcon->tid = rsp->hdr.TreeId; - strncpy(tcon->treeName, tree, MAX_TREE_SIZE); + strlcpy(tcon->treeName, tree, sizeof(tcon->treeName)); if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) -- cgit v1.2.3-18-g5258 From c8664730bb5c79d0f9a1845b83e38d20c7575d7d Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 21 Jun 2013 15:35:45 -0500 Subject: Some missing share flags Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/smb2pdu.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index d351377d251..0ef06ec24a0 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -284,7 +284,10 @@ struct smb2_tree_connect_rsp { #define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 #define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 #define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 -#define SHI1005_FLAGS_ENABLE_HASH 0x00002000 +#define SHI1005_FLAGS_ENABLE_HASH_V1 0x00002000 +#define SHI1005_FLAGS_ENABLE_HASH_V2 0x00004000 +#define SHI1005_FLAGS_ENCRYPT_DATA 0x00008000 +#define SHI1005_FLAGS_ALL 0x0000FF33 /* Possible share capabilities */ #define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */ @@ -490,7 +493,7 @@ struct copychunk_ioctl { /* array will only be one chunk long for us */ __le64 SourceOffset; __le64 TargetOffset; - __u32 Length; /* how many bytes to copy */ + __le32 Length; /* how many bytes to copy */ __u32 Reserved2; } __packed; -- cgit v1.2.3-18-g5258 From 7f6538585eccdd7e663df3c6186486ef528d9c90 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 22 Jun 2013 01:48:06 -0500 Subject: Remove typo Cut and paste likely introduced accidentally inserted spurious #define in d60622eb5a23904facf4a4efac60f5bfa810d7d4 causes no harm but looks weird Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 76df656e4b2..ed399308dbc 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -311,7 +311,6 @@ smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) seq_printf(m, "\nSessionSetups: %d sent %d failed", atomic_read(&sent[SMB2_SESSION_SETUP_HE]), atomic_read(&failed[SMB2_SESSION_SETUP_HE])); -#define SMB2LOGOFF 0x0002 /* trivial request/resp */ seq_printf(m, "\nLogoffs: %d sent %d failed", atomic_read(&sent[SMB2_LOGOFF_HE]), atomic_read(&failed[SMB2_LOGOFF_HE])); -- cgit v1.2.3-18-g5258 From 2b80d049eb6dd08431f63fc0c5ce78567648a033 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 23 Jun 2013 18:43:37 -0500 Subject: Charge at least one credit, if server says that it supports multicredit In SMB2.1 and later the server will usually set the large MTU flag, and we need to charge at least one credit, if server says that since it supports multicredit. Windows seems to let us get away with putting a zero there, but they confirmed that it is wrong and the spec says to put one there (if the request is under 64K and the CAP_LARGE_MTU was returned during protocol negotiation by the server. CC: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 92fd6c59c12..0de6a82e246 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1,7 +1,7 @@ /* * fs/cifs/smb2pdu.c * - * Copyright (C) International Business Machines Corp., 2009, 2012 + * Copyright (C) International Business Machines Corp., 2009, 2013 * Etersoft, 2012 * Author(s): Steve French (sfrench@us.ibm.com) * Pavel Shilovsky (pshilovsky@samba.org) 2012 @@ -108,6 +108,13 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , if (!tcon) goto out; + /* BB FIXME when we do write > 64K add +1 for every 64K in req or rsp */ + /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ + /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ + if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) + hdr->CreditCharge = cpu_to_le16(1); + /* else CreditCharge MBZ */ + hdr->TreeId = tcon->tid; /* Uid is not converted */ if (tcon->ses) -- cgit v1.2.3-18-g5258 From 4a72dafa19ba77a2fb77ae676f8e3a0d6077c37c Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 25 Jun 2013 00:20:49 -0500 Subject: SMB2 FSCTL and IOCTL worker function This worker function is needed to send SMB2 fsctl (and ioctl) requests including: validating negotiation info (secure negotiate) querying the servers network interfaces copy offload (refcopy) Followon patches for the above three will use this. This patch also does general validation of the response. In the future, as David Disseldorp notes, for the copychunk ioctl case, we will want to enhance the response processing to allow returning the chunk request limits to the caller (even though the server returns an error, in that case we would return data that the caller could use - see 2.2.32.1). See MS-SMB2 Section 2.2.31 for more details on format of fsctl. Acked-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/smb2misc.c | 4 ++ fs/cifs/smb2pdu.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2pdu.h | 23 +++++++++++ fs/cifs/smb2proto.h | 4 ++ 4 files changed, 147 insertions(+) diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 10383d8c015..b0c43345cd9 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -266,6 +266,10 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) ((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength); break; case SMB2_IOCTL: + *off = le32_to_cpu( + ((struct smb2_ioctl_rsp *)hdr)->OutputOffset); + *len = le32_to_cpu(((struct smb2_ioctl_rsp *)hdr)->OutputCount); + break; case SMB2_CHANGE_NOTIFY: default: /* BB FIXME for unimplemented cases above */ diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 0de6a82e246..c0d102615d0 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -997,6 +997,122 @@ creat_exit: return rc; } +/* + * SMB2 IOCTL is used for both IOCTLs and FSCTLs + */ +int +SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid, u32 opcode, bool is_fsctl, char *in_data, + u32 indatalen, char **out_data, u32 *plen /* returned data len */) +{ + struct smb2_ioctl_req *req; + struct smb2_ioctl_rsp *rsp; + struct TCP_Server_Info *server; + struct cifs_ses *ses = tcon->ses; + struct kvec iov[2]; + int resp_buftype; + int num_iovecs; + int rc = 0; + + cifs_dbg(FYI, "SMB2 IOCTL\n"); + + /* zero out returned data len, in case of error */ + if (plen) + *plen = 0; + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + rc = small_smb2_init(SMB2_IOCTL, tcon, (void **) &req); + if (rc) + return rc; + + req->CtlCode = cpu_to_le32(opcode); + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + + if (indatalen) { + req->InputCount = cpu_to_le32(indatalen); + /* do not set InputOffset if no input data */ + req->InputOffset = + cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer) - 4); + iov[1].iov_base = in_data; + iov[1].iov_len = indatalen; + num_iovecs = 2; + } else + num_iovecs = 1; + + req->OutputOffset = 0; + req->OutputCount = 0; /* MBZ */ + + /* + * Could increase MaxOutputResponse, but that would require more + * than one credit. Windows typically sets this smaller, but for some + * ioctls it may be useful to allow server to send more. No point + * limiting what the server can send as long as fits in one credit + */ + req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */ + + if (is_fsctl) + req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); + else + req->Flags = 0; + + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field */ + iov[0].iov_len = get_rfc1002_length(req) + 4; + + if (indatalen) + inc_rfc1001_len(req, indatalen); + + rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); + rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; + + if (rc != 0) { + if (tcon) + cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); + goto ioctl_exit; + } + + /* check if caller wants to look at return data or just return rc */ + if ((plen == NULL) || (out_data == NULL)) + goto ioctl_exit; + + *plen = le32_to_cpu(rsp->OutputCount); + + /* We check for obvious errors in the output buffer length and offset */ + if (*plen == 0) + goto ioctl_exit; /* server returned no data */ + else if (*plen > 0xFF00) { + cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); + *plen = 0; + rc = -EIO; + goto ioctl_exit; + } + + if (get_rfc1002_length(rsp) < le32_to_cpu(rsp->OutputOffset) + *plen) { + cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, + le32_to_cpu(rsp->OutputOffset)); + *plen = 0; + rc = -EIO; + goto ioctl_exit; + } + + *out_data = kmalloc(*plen, GFP_KERNEL); + if (*out_data == NULL) { + rc = -ENOMEM; + goto ioctl_exit; + } + + memcpy(*out_data, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset), + *plen); +ioctl_exit: + free_rsp_buf(resp_buftype, rsp); + return rc; +} + int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 0ef06ec24a0..f31043b26bd 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -497,6 +497,29 @@ struct copychunk_ioctl { __u32 Reserved2; } __packed; +/* Response and Request are the same format */ +struct validate_negotiate_info { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 DialectCount; + __le16 Dialect[1]; +} __packed; + +#define RSS_CAPABLE 0x00000001 +#define RDMA_CAPABLE 0x00000002 + +struct network_interface_info_ioctl_rsp { + __le32 Next; /* next interface. zero if this is last one */ + __le32 IfIndex; + __le32 Capability; /* RSS or RDMA Capable */ + __le32 Reserved; + __le64 LinkSpeed; + char SockAddr_Storage[128]; +} __packed; + +#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ + struct smb2_ioctl_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 57 */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 2aa3535e38c..d4e1eb80745 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -111,6 +111,10 @@ extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __u32 desired_access, __u32 create_disposition, __u32 file_attributes, __u32 create_options, __u8 *oplock, struct smb2_file_all_info *buf); +extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 opcode, + bool is_fsctl, char *in_data, u32 indatalen, + char **out_data, u32 *plen /* returned data len */); extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id); extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, -- cgit v1.2.3-18-g5258 From 2a2c41c07c710f2c1afe3748bdde40db9ea9d9e6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 25 Jun 2013 01:32:17 -0500 Subject: revalidate directories instiantiated via FIND_* in order to handle DFS referrals We've had a long-standing problem with DFS referral points. CIFS servers generally try to make them look like directories in FIND_FIRST/NEXT responses. When you go to try to do a FIND_FIRST on them though, the server will then (correctly) return STATUS_PATH_NOT_COVERED. Mostly this manifests as spurious EREMOTE errors back to userland. This patch attempts to fix this by marking directories that are discovered via FIND_FIRST/NEXT for revaldiation. When the lookup code runs across them again, we'll reissue a QPathInfo against them and that will make it chase the referral properly. There is some performance penalty involved here and no I haven't measured it -- it'll be highly dependent upon the workload and contents of the mounted share. To try and mitigate that though, the code only marks the inode for revalidation when it's possible to run across a DFS referral. i.e.: when the kernel has DFS support built in and the share is "in DFS" [At the Microsoft plugfest we noted that usually the DFS links had the REPARSE attribute tag enabled - DFS junctions are reparse points after all - so I just added a check for that flag too so the performance impact should be smaller - Steve] Signed-off-by: Jeff Layton Reviewed-by: Sachin Prabhu Signed-off-by: Steve French --- fs/cifs/readdir.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 770d5a9781c..94d62019820 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -126,6 +126,22 @@ out: dput(dentry); } +/* + * Is it possible that this directory might turn out to be a DFS referral + * once we go to try and use it? + */ +static bool +cifs_dfs_is_possible(struct cifs_sb_info *cifs_sb) +{ +#ifdef CONFIG_CIFS_DFS_UPCALL + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + + if (tcon->Flags & SMB_SHARE_IS_IN_DFS) + return true; +#endif + return false; +} + static void cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) { @@ -135,6 +151,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; fattr->cf_dtype = DT_DIR; + /* + * Windows CIFS servers generally make DFS referrals look + * like directories in FIND_* responses with the reparse + * attribute flag also set (since DFS junctions are + * reparse points). We must revalidate at least these + * directory inodes before trying to use them (if + * they are DFS we will get PATH_NOT_COVERED back + * when queried directly and can then try to connect + * to the DFS target) + */ + if (cifs_dfs_is_possible(cifs_sb) && + (fattr->cf_cifsattrs & ATTR_REPARSE)) + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; } else { fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; fattr->cf_dtype = DT_REG; -- cgit v1.2.3-18-g5258 From fdf96a907c1fbb93c633e2b7ede3b8df26d6a4c0 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 25 Jun 2013 14:03:16 -0500 Subject: Handle big endianness in NTLM (ntlmv2) authentication This is RH bug 970891 Uppercasing of username during calculation of ntlmv2 hash fails because UniStrupr function does not handle big endian wchars. Also fix a comment in the same code to reflect its correct usage. [To make it easier for stable (rather than require 2nd patch) fixed this patch of Shirish's to remove endian warning generated by sparse -- steve f.] Reported-by: steve Signed-off-by: Shirish Pargaonkar Cc: Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_unicode.h | 8 ++++---- fs/cifs/cifsencrypt.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 4fb097468e2..fe8d6276410 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -327,14 +327,14 @@ UniToupper(register wchar_t uc) /* * UniStrupr: Upper case a unicode string */ -static inline wchar_t * -UniStrupr(register wchar_t *upin) +static inline __le16 * +UniStrupr(register __le16 *upin) { - register wchar_t *up; + register __le16 *up; up = upin; while (*up) { /* For all characters */ - *up = UniToupper(*up); + *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); up++; } return upin; /* Return input pointer */ diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 30bea6bd302..330875948f1 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -413,7 +413,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, int rc = 0; int len; char nt_hash[CIFS_NTHASH_SIZE]; - wchar_t *user; + __le16 *user; wchar_t *domain; wchar_t *server; @@ -438,7 +438,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, return rc; } - /* convert ses->user_name to unicode and uppercase */ + /* convert ses->user_name to unicode */ len = ses->user_name ? strlen(ses->user_name) : 0; user = kmalloc(2 + (len * 2), GFP_KERNEL); if (user == NULL) { @@ -447,7 +447,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, } if (len) { - len = cifs_strtoUTF16((__le16 *)user, ses->user_name, len, nls_cp); + len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp); UniStrupr(user); } else { memset(user, '\0', 2); -- cgit v1.2.3-18-g5258 From 5d875cc928aa7c95c8c1e89497a9a644f32213d4 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 25 Jun 2013 15:33:41 -0500 Subject: When server doesn't provide SecurityBuffer on SMB2Negotiate pick default According to MS-SMB2 section 2.2.4: if no blob, client picks default which for us will be ses->sectype = RawNTLMSSP; but for time being this is also our only auth choice so doesn't matter as long as we include this fix (which does not treat the empty SecurityBuffer as an error as the code had been doing). We just found a server which sets blob length to zero expecting raw so this fixes negotiation with that server. Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index c0d102615d0..f9b74daf962 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -416,18 +416,22 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, &rsp->hdr); - if (blob_length == 0) { - cifs_dbg(VFS, "missing security blob on negprot\n"); - rc = -EIO; - goto neg_exit; - } + /* + * See MS-SMB2 section 2.2.4: if no blob, client picks default which + * for us will be + * ses->sectype = RawNTLMSSP; + * but for time being this is our only auth choice so doesn't matter. + * We just found a server which sets blob length to zero expecting raw. + */ + if (blob_length == 0) + cifs_dbg(FYI, "missing security blob on negprot\n"); rc = cifs_enable_signing(server, ses->sign); #ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */ if (rc) goto neg_exit; - - rc = decode_neg_token_init(security_blob, blob_length, + if (blob_length) + rc = decode_neg_token_init(security_blob, blob_length, &server->sec_type); if (rc == 1) rc = 0; -- cgit v1.2.3-18-g5258 From 52dfb446dbc1915e1df89f8ea9cae1fee7ab3d5e Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sun, 26 May 2013 07:01:02 -0400 Subject: cifs: try to handle the MUST SecurityFlags sanely The cifs.ko SecurityFlags interface wins my award for worst-designed interface ever, but we're sort of stuck with it since it's documented and people do use it (even if it doesn't work correctly). Case in point -- you can specify multiple sets of "MUST" flags. It makes absolutely no sense, but you can do it. What should the effect be in such a case? No one knows or seems to have considered this so far, so let's define it now. If you try to specify multiple MUST flags, clear any other MAY or MUST bits except for the ones that involve signing. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 0315824bbf0..f3ac4154cbb 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -597,6 +597,32 @@ static int cifs_security_flags_proc_open(struct inode *inode, struct file *file) return single_open(file, cifs_security_flags_proc_show, NULL); } +/* + * Ensure that if someone sets a MUST flag, that we disable all other MAY + * flags except for the ones corresponding to the given MUST flag. If there are + * multiple MUST flags, then try to prefer more secure ones. + */ +static void +cifs_security_flags_handle_must_flags(unsigned int *flags) +{ + unsigned int signflags = *flags & CIFSSEC_MUST_SIGN; + + if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) + *flags = CIFSSEC_MUST_KRB5; + else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) + *flags = CIFSSEC_MUST_NTLMSSP; + else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) + *flags = CIFSSEC_MUST_NTLMV2; + else if ((*flags & CIFSSEC_MUST_NTLM) == CIFSSEC_MUST_NTLM) + *flags = CIFSSEC_MUST_NTLM; + else if ((*flags & CIFSSEC_MUST_LANMAN) == CIFSSEC_MUST_LANMAN) + *flags = CIFSSEC_MUST_LANMAN; + else if ((*flags & CIFSSEC_MUST_PLNTXT) == CIFSSEC_MUST_PLNTXT) + *flags = CIFSSEC_MUST_PLNTXT; + + *flags |= signflags; +} + static ssize_t cifs_security_flags_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { @@ -650,6 +676,8 @@ static ssize_t cifs_security_flags_proc_write(struct file *file, return -EINVAL; } + cifs_security_flags_handle_must_flags(&flags); + /* flags look ok - update the global security flags for cifs module */ global_secflags = flags; if (global_secflags & CIFSSEC_MUST_SIGN) { -- cgit v1.2.3-18-g5258 From 84ceeb9626655ce7c2745f724571ca8008b4695e Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 26 Jun 2013 17:52:17 -0500 Subject: [CIFS] fix static checker warning Dan Carpenter wrote: The patch 7f420cee8bd6: "[CIFS] Charge at least one credit, if server says that it supports multicredit" from Jun 23, 2013, leads to the following Smatch complaint: fs/cifs/smb2pdu.c:120 smb2_hdr_assemble() warn: variable dereferenced before check 'tcon->ses' (see line 115) CC: Dan Carpenter Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index f9b74daf962..53275bf1e0a 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -111,7 +111,8 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , /* BB FIXME when we do write > 64K add +1 for every 64K in req or rsp */ /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ - if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) + if ((tcon->ses) && + (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) hdr->CreditCharge = cpu_to_le16(1); /* else CreditCharge MBZ */ -- cgit v1.2.3-18-g5258 From f87ab88b4065a9ef00620224c4fafadc201a430c Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 26 Jun 2013 19:14:55 -0500 Subject: [CIFS] Do not set DFS flag on SMB2 open If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have to pass the path on the Open SMB prefixed by \\server\share. Not sure when we would need to do the augmented path (if ever) and setting this flag breaks the SMB2 open operation since it is illegal to send an empty path name (without \\server\share prefix) when the DFS flag is set in the SMB open header. We could consider setting the flag on all operations other than open but it is safer to net set it for now. Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 53275bf1e0a..2b312e4eeaa 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -120,13 +120,20 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , /* Uid is not converted */ if (tcon->ses) hdr->SessionId = tcon->ses->Suid; - /* BB check following DFS flags BB */ - /* BB do we have to add check for SHI1005_FLAGS_DFS_ROOT too? */ - if (tcon->share_flags & SHI1005_FLAGS_DFS) - hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; - /* BB how does SMB2 do case sensitive? */ - /* if (tcon->nocase) - hdr->Flags |= SMBFLG_CASELESS; */ + + /* + * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have + * to pass the path on the Open SMB prefixed by \\server\share. + * Not sure when we would need to do the augmented path (if ever) and + * setting this flag breaks the SMB2 open operation since it is + * illegal to send an empty path name (without \\server\share prefix) + * when the DFS flag is set in the SMB open header. We could + * consider setting the flag on all operations other than open + * but it is safer to net set it for now. + */ +/* if (tcon->share_flags & SHI1005_FLAGS_DFS) + hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ + if (tcon->ses && tcon->ses->server && tcon->ses->server->sign) hdr->Flags |= SMB2_FLAGS_SIGNED; out: -- cgit v1.2.3-18-g5258 From 429b46f4fdaf9c9007b7c0fc371b94e40c3764b2 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 26 Jun 2013 23:45:05 -0500 Subject: [CIFS] SMB3 Signing enablement SMB3 uses a much faster method of signing (which is also better in other ways), AES-CMAC. With the kernel now supporting AES-CMAC since last release, we are overdue to allow SMB3 signing (today only CIFS and SMB2 and SMB2.1, but not SMB3 and SMB3.1 can sign) - and we need this also for checking secure negotation and also per-share encryption (two other new SMB3 features which we need to implement). This patch needs some work in a few areas - for example we need to move signing for SMB2/SMB3 from per-socket to per-user (we may be able to use the "nosharesock" mount option in the interim for the multiuser case), and Shirish found a bug in the earlier authentication overhaul (setting signing flags properly) - but those can be done in followon patches. Signed-off-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/Kconfig | 1 + fs/cifs/cifsencrypt.c | 29 ++++++++++ fs/cifs/cifsglob.h | 3 + fs/cifs/cifspdu.h | 5 ++ fs/cifs/cifsproto.h | 1 + fs/cifs/connect.c | 1 + fs/cifs/smb2glob.h | 2 + fs/cifs/smb2transport.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 188 insertions(+), 2 deletions(-) diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 2906ee27640..603f18a65c1 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -10,6 +10,7 @@ config CIFS select CRYPTO_ECB select CRYPTO_DES select CRYPTO_SHA256 + select CRYPTO_CMAC help This is the client VFS module for the Common Internet File System (CIFS) protocol which is the successor to the Server Message Block diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 330875948f1..3d8bf941d12 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -705,6 +705,9 @@ calc_seckey(struct cifs_ses *ses) void cifs_crypto_shash_release(struct TCP_Server_Info *server) { + if (server->secmech.cmacaes) + crypto_free_shash(server->secmech.cmacaes); + if (server->secmech.hmacsha256) crypto_free_shash(server->secmech.hmacsha256); @@ -714,6 +717,8 @@ cifs_crypto_shash_release(struct TCP_Server_Info *server) if (server->secmech.hmacmd5) crypto_free_shash(server->secmech.hmacmd5); + kfree(server->secmech.sdesccmacaes); + kfree(server->secmech.sdeschmacsha256); kfree(server->secmech.sdeschmacmd5); @@ -747,6 +752,13 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server) goto crypto_allocate_hmacsha256_fail; } + server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0); + if (IS_ERR(server->secmech.cmacaes)) { + cifs_dbg(VFS, "could not allocate crypto cmac-aes"); + rc = PTR_ERR(server->secmech.cmacaes); + goto crypto_allocate_cmacaes_fail; + } + size = sizeof(struct shash_desc) + crypto_shash_descsize(server->secmech.hmacmd5); server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL); @@ -777,8 +789,22 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server) server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256; server->secmech.sdeschmacsha256->shash.flags = 0x0; + size = sizeof(struct shash_desc) + + crypto_shash_descsize(server->secmech.cmacaes); + server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL); + if (!server->secmech.sdesccmacaes) { + cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__); + rc = -ENOMEM; + goto crypto_allocate_cmacaes_sdesc_fail; + } + server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes; + server->secmech.sdesccmacaes->shash.flags = 0x0; + return 0; +crypto_allocate_cmacaes_sdesc_fail: + kfree(server->secmech.sdeschmacsha256); + crypto_allocate_hmacsha256_sdesc_fail: kfree(server->secmech.sdescmd5); @@ -786,6 +812,9 @@ crypto_allocate_md5_sdesc_fail: kfree(server->secmech.sdeschmacmd5); crypto_allocate_hmacmd5_sdesc_fail: + crypto_free_shash(server->secmech.cmacaes); + +crypto_allocate_cmacaes_fail: crypto_free_shash(server->secmech.hmacsha256); crypto_allocate_hmacsha256_fail: diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9a1e37aad3b..2d0f524ebee 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -125,9 +125,11 @@ struct cifs_secmech { struct crypto_shash *hmacmd5; /* hmac-md5 hash function */ struct crypto_shash *md5; /* md5 hash function */ struct crypto_shash *hmacsha256; /* hmac-sha256 hash function */ + struct crypto_shash *cmacaes; /* block-cipher based MAC function */ struct sdesc *sdeschmacmd5; /* ctxt to generate ntlmv2 hash, CR1 */ struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */ struct sdesc *sdeschmacsha256; /* ctxt to generate smb2 signature */ + struct sdesc *sdesccmacaes; /* ctxt to generate smb3 signature */ }; /* per smb session structure/fields */ @@ -538,6 +540,7 @@ struct TCP_Server_Info { int timeAdj; /* Adjust for difference in server time zone in sec */ __u64 CurrentMid; /* multiplex id - rotating counter */ char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ + char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */ /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* for signing, protected by srv_mutex */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 7e8523c5c18..11ca24a8e05 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -142,6 +142,11 @@ */ #define CIFS_SESS_KEY_SIZE (16) +/* + * Size of the smb3 signing key + */ +#define SMB3_SIGN_KEY_SIZE (16) + #define CIFS_CLIENT_CHALLENGE_SIZE (8) #define CIFS_SERVER_CHALLENGE_SIZE (8) #define CIFS_HMAC_MD5_HASH_SIZE (16) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index a82b3c09888..ff669e7c585 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -436,6 +436,7 @@ extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *); extern void cifs_crypto_shash_release(struct TCP_Server_Info *); extern int calc_seckey(struct cifs_ses *); +extern int generate_smb3signingkey(struct TCP_Server_Info *); #ifdef CONFIG_CIFS_WEAK_PW_HASH extern int calc_lanman_hash(const char *password, const char *cryptkey, diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 53a1780cd07..354ea7782db 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3841,6 +3841,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, server->sequence_number = 0x2; server->session_estab = true; ses->auth_key.response = NULL; + generate_smb3signingkey(server); } mutex_unlock(&server->srv_mutex); diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h index 7c0e2143e77..c38350851b0 100644 --- a/fs/cifs/smb2glob.h +++ b/fs/cifs/smb2glob.h @@ -54,5 +54,7 @@ #define SMB2_SIGNATURE_SIZE (16) #define SMB2_NTLMV2_SESSKEY_SIZE (16) #define SMB2_HMACSHA256_SIZE (32) +#define SMB2_CMACAES_SIZE (16) +#define SMB3_SIGNKEY_SIZE (16) #endif /* _SMB2_GLOB_H */ diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index c802ecfa770..87563ee58d3 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -116,11 +116,155 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) return rc; } +int +generate_smb3signingkey(struct TCP_Server_Info *server) +{ + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; + __u8 L[4] = {0, 0, 0, 128}; + int rc = 0; + unsigned char prfhash[SMB2_HMACSHA256_SIZE]; + unsigned char *hashptr = prfhash; + + memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); + memset(server->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE); + + rc = crypto_shash_setkey(server->secmech.hmacsha256, + server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set with session key\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash); + if (rc) { + cifs_dbg(VFS, "%s: Could not init sign hmac\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + i, 4); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with n\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + "SMB2AESCMAC", 12); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with label\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + &zero, 1); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with zero\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + "SmbSign", 8); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with context\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + L, 4); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with L\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash, + hashptr); + if (rc) { + cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); + goto smb3signkey_ret; + } + + memcpy(server->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE); + +smb3signkey_ret: + return rc; +} + int smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) { - cifs_dbg(FYI, "smb3 signatures not supported yet\n"); - return -EOPNOTSUPP; + int i, rc; + unsigned char smb3_signature[SMB2_CMACAES_SIZE]; + unsigned char *sigptr = smb3_signature; + struct kvec *iov = rqst->rq_iov; + int n_vec = rqst->rq_nvec; + struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; + + memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); + memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE); + + rc = crypto_shash_setkey(server->secmech.cmacaes, + server->smb3signingkey, SMB2_CMACAES_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); + return rc; + } + + rc = crypto_shash_init(&server->secmech.sdesccmacaes->shash); + if (rc) { + cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__); + return rc; + } + + for (i = 0; i < n_vec; i++) { + if (iov[i].iov_len == 0) + continue; + if (iov[i].iov_base == NULL) { + cifs_dbg(VFS, "null iovec entry"); + return -EIO; + } + /* + * The first entry includes a length field (which does not get + * signed that occupies the first 4 bytes before the header). + */ + if (i == 0) { + if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ + break; /* nothing to sign or corrupt header */ + rc = + crypto_shash_update( + &server->secmech.sdesccmacaes->shash, + iov[i].iov_base + 4, iov[i].iov_len - 4); + } else { + rc = + crypto_shash_update( + &server->secmech.sdesccmacaes->shash, + iov[i].iov_base, iov[i].iov_len); + } + if (rc) { + cifs_dbg(VFS, "%s: Couldn't update cmac aes with payload\n", + __func__); + return rc; + } + } + + /* now hash over the rq_pages array */ + for (i = 0; i < rqst->rq_npages; i++) { + struct kvec p_iov; + + cifs_rqst_page_to_kvec(rqst, i, &p_iov); + crypto_shash_update(&server->secmech.sdesccmacaes->shash, + p_iov.iov_base, p_iov.iov_len); + kunmap(rqst->rq_pages[i]); + } + + rc = crypto_shash_final(&server->secmech.sdesccmacaes->shash, + sigptr); + if (rc) + cifs_dbg(VFS, "%s: Could not generate cmac aes\n", __func__); + + memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE); + + return rc; } /* must be called with server->srv_mutex held */ -- cgit v1.2.3-18-g5258 From e65a5cb41718e0eb17a470bc3acf2c3b2f00f1d0 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 27 Jun 2013 01:06:50 -0500 Subject: [CIFS] Fix build warning Fix build warning in Shirish's recent SMB3 signing patch which occurs when SMB2 support is disabled in Kconfig. fs/built-in.o: In function `cifs_setup_session': >> (.text+0xa1767): undefined reference to `generate_smb3signingkey' Pointed out by: automated 0-DAY kernel build testing backend Intel Open Source Technology Center CC: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/cifsproto.h | 2 +- fs/cifs/connect.c | 3 ++- fs/cifs/smb2ops.c | 1 + fs/cifs/smb2transport.c | 4 ++-- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 2d0f524ebee..b0f077ebb59 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -365,6 +365,8 @@ struct smb_version_operations { void (*set_lease_key)(struct inode *, struct cifs_fid *fid); /* generate new lease key */ void (*new_lease_key)(struct cifs_fid *fid); + /* The next two functions will need to be changed to per smb session */ + void (*generate_signingkey)(struct TCP_Server_Info *server); int (*calc_signature)(struct smb_rqst *rqst, struct TCP_Server_Info *server); }; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ff669e7c585..c8ff018fae6 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -436,7 +436,7 @@ extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *); extern void cifs_crypto_shash_release(struct TCP_Server_Info *); extern int calc_seckey(struct cifs_ses *); -extern int generate_smb3signingkey(struct TCP_Server_Info *); +extern void generate_smb3signingkey(struct TCP_Server_Info *); #ifdef CONFIG_CIFS_WEAK_PW_HASH extern int calc_lanman_hash(const char *password, const char *cryptkey, diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 354ea7782db..afcb8a1a33b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3841,7 +3841,8 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, server->sequence_number = 0x2; server->session_estab = true; ses->auth_key.response = NULL; - generate_smb3signingkey(server); + if (server->ops->generate_signingkey) + server->ops->generate_signingkey(server); } mutex_unlock(&server->srv_mutex); diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index ed399308dbc..48fe7c4dab7 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -709,6 +709,7 @@ struct smb_version_operations smb30_operations = { .get_lease_key = smb2_get_lease_key, .set_lease_key = smb2_set_lease_key, .new_lease_key = smb2_new_lease_key, + .generate_signingkey = generate_smb3signingkey, .calc_signature = smb3_calc_signature, }; diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 87563ee58d3..09b4fbaadeb 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -116,7 +116,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) return rc; } -int +void generate_smb3signingkey(struct TCP_Server_Info *server) { unsigned char zero = 0x0; @@ -187,7 +187,7 @@ generate_smb3signingkey(struct TCP_Server_Info *server) memcpy(server->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE); smb3signkey_ret: - return rc; + return; } int -- cgit v1.2.3-18-g5258 From 50285882fdd919e2b9617fc844b4816b7833f115 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 27 Jun 2013 12:45:00 -0400 Subject: cifs: fix SMB2 signing enablement in cifs_enable_signing Commit 9ddec56131 (cifs: move handling of signed connections into separate function) broke signing on SMB2/3 connections. While the code to enable signing on the connections was very similar between the two, the bits that get set in the sec_mode are different. Declare a couple of new smb_version_values fields and set them appropriately for SMB1 and SMB2/3. Then change cifs_enable_signing to use those instead. Reported-by: Shirish Pargaonkar Signed-off-by: Jeff Layton Tested-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/cifssmb.c | 4 ++-- fs/cifs/smb1ops.c | 2 ++ fs/cifs/smb2ops.c | 8 ++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index b0f077ebb59..e66b0888254 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -387,6 +387,8 @@ struct smb_version_values { unsigned int cap_nt_find; unsigned int cap_large_files; unsigned int oplock_read; + __u16 signing_enabled; + __u16 signing_required; }; #define HEADER_SIZE(server) (server->vals->header_size) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a35aad2060c..bc7dfa85738 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -407,8 +407,8 @@ decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr) int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) { - bool srv_sign_required = server->sec_mode & SECMODE_SIGN_REQUIRED; - bool srv_sign_enabled = server->sec_mode & SECMODE_SIGN_ENABLED; + bool srv_sign_required = server->sec_mode & server->vals->signing_required; + bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled; bool mnt_sign_enabled = global_secflags & CIFSSEC_MAY_SIGN; /* diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index b28aabd33ed..e813f04511d 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -957,4 +957,6 @@ struct smb_version_values smb1_values = { .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, .cap_large_files = CAP_LARGE_FILES, .oplock_read = OPLOCK_READ, + .signing_enabled = SECMODE_SIGN_ENABLED, + .signing_required = SECMODE_SIGN_REQUIRED, }; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 48fe7c4dab7..6d15cab95b9 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -729,6 +729,8 @@ struct smb_version_values smb20_values = { .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, .oplock_read = SMB2_OPLOCK_LEVEL_II, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, }; struct smb_version_values smb21_values = { @@ -747,6 +749,8 @@ struct smb_version_values smb21_values = { .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, .oplock_read = SMB2_OPLOCK_LEVEL_II, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, }; struct smb_version_values smb30_values = { @@ -765,6 +769,8 @@ struct smb_version_values smb30_values = { .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, .oplock_read = SMB2_OPLOCK_LEVEL_II, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, }; struct smb_version_values smb302_values = { @@ -783,4 +789,6 @@ struct smb_version_values smb302_values = { .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, .oplock_read = SMB2_OPLOCK_LEVEL_II, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, }; -- cgit v1.2.3-18-g5258 From 7ac0febb81259fb9e0c447e0b90d0f05f409a02b Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 28 Jun 2013 11:47:33 +0200 Subject: cifs: fill TRANS2_QUERY_FILE_INFO ByteCount fields Currently the trans2 ByteCount field is incorrectly left zero in TRANS2_QUERY_FILE_INFO info_level=SMB_QUERY_FILE_ALL_INFO and info_level=SMB_QUERY_FILE_UNIX_BASIC requests. The field should properly reflect the FID, information_level and padding bytes carried in these requests. Leaving this field zero causes such requests to fail against Novell CIFS servers. Other SMB servers (e.g. Samba) use the parameter count fields for data length calculations instead, so do not suffer the same fate. Signed-off-by: David Disseldorp Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index bc7dfa85738..a89c4cb4e6c 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3930,6 +3930,7 @@ QFileInfoRetry: pSMB->Pad = 0; pSMB->Fid = netfid; inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); @@ -4098,6 +4099,7 @@ UnixQFileInfoRetry: pSMB->Pad = 0; pSMB->Fid = netfid; inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -- cgit v1.2.3-18-g5258