OSDN Git Service

cifs: send workstation name during ntlmssp session setup
authorShyam Prasad N <sprasad@microsoft.com>
Fri, 5 Nov 2021 19:03:57 +0000 (19:03 +0000)
committerSteve French <stfrench@microsoft.com>
Mon, 8 Nov 2021 19:07:56 +0000 (13:07 -0600)
During the ntlmssp session setup (authenticate phases)
send the client workstation info. This can make debugging easier on
servers.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Reviewed-by: Enzo Matsumiya <ematsumiya@suse.de>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifsglob.h
fs/cifs/connect.c
fs/cifs/fs_context.c
fs/cifs/fs_context.h
fs/cifs/misc.c
fs/cifs/ntlmssp.h
fs/cifs/sess.c
fs/cifs/smb2pdu.c

index eeea520..41e97df 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/mempool.h>
 #include <linux/workqueue.h>
+#include <linux/utsname.h>
 #include "cifs_fs_sb.h"
 #include "cifsacl.h"
 #include <crypto/internal/hash.h>
 #define XATTR_DOS_ATTRIB "user.DOSATTRIB"
 #endif
 
+#define CIFS_MAX_WORKSTATION_LEN  (__NEW_UTS_LEN + 1)  /* reasonable max for client */
+
 /*
  * CIFS vfs client Status information (based on what we know.)
  */
@@ -909,6 +912,7 @@ struct cifs_ses {
                                   and after mount option parsing we fill it */
        char *domainName;
        char *password;
+       char *workstation_name;
        struct session_key auth_key;
        struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
        enum securityEnum sectype; /* what security flavor was specified? */
index 24742b7..c45fed4 100644 (file)
@@ -1948,6 +1948,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
                if (!ses->domainName)
                        goto get_ses_fail;
        }
+       if (ctx->workstation_name) {
+               ses->workstation_name = kstrdup(ctx->workstation_name,
+                                               GFP_KERNEL);
+               if (!ses->workstation_name)
+                       goto get_ses_fail;
+       }
        if (ctx->domainauto)
                ses->domainAuto = ctx->domainauto;
        ses->cred_uid = ctx->cred_uid;
index 38d96a4..9b29b63 100644 (file)
@@ -309,6 +309,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
        new_ctx->username = NULL;
        new_ctx->password = NULL;
        new_ctx->domainname = NULL;
+       new_ctx->workstation_name = NULL;
        new_ctx->UNC = NULL;
        new_ctx->source = NULL;
        new_ctx->iocharset = NULL;
@@ -323,6 +324,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
        DUP_CTX_STR(UNC);
        DUP_CTX_STR(source);
        DUP_CTX_STR(domainname);
+       DUP_CTX_STR(workstation_name);
        DUP_CTX_STR(nodename);
        DUP_CTX_STR(iocharset);
 
@@ -720,6 +722,11 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc,
                cifs_errorf(fc, "can not change domainname during remount\n");
                return -EINVAL;
        }
+       if (new_ctx->workstation_name &&
+           (!old_ctx->workstation_name || strcmp(new_ctx->workstation_name, old_ctx->workstation_name))) {
+               cifs_errorf(fc, "can not change workstation_name during remount\n");
+               return -EINVAL;
+       }
        if (new_ctx->nodename &&
            (!old_ctx->nodename || strcmp(new_ctx->nodename, old_ctx->nodename))) {
                cifs_errorf(fc, "can not change nodename during remount\n");
@@ -753,7 +760,8 @@ static int smb3_reconfigure(struct fs_context *fc)
                return rc;
 
        /*
-        * We can not change UNC/username/password/domainname/nodename/iocharset
+        * We can not change UNC/username/password/domainname/
+        * workstation_name/nodename/iocharset
         * during reconnect so ignore what we have in the new context and
         * just use what we already have in cifs_sb->ctx.
         */
@@ -762,6 +770,7 @@ static int smb3_reconfigure(struct fs_context *fc)
        STEAL_STRING(cifs_sb, ctx, username);
        STEAL_STRING(cifs_sb, ctx, password);
        STEAL_STRING(cifs_sb, ctx, domainname);
+       STEAL_STRING(cifs_sb, ctx, workstation_name);
        STEAL_STRING(cifs_sb, ctx, nodename);
        STEAL_STRING(cifs_sb, ctx, iocharset);
 
@@ -1414,13 +1423,22 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
 
 int smb3_init_fs_context(struct fs_context *fc)
 {
+       int rc;
        struct smb3_fs_context *ctx;
        char *nodename = utsname()->nodename;
        int i;
 
        ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL);
-       if (unlikely(!ctx))
-               return -ENOMEM;
+       if (unlikely(!ctx)) {
+               rc = -ENOMEM;
+               goto err_exit;
+       }
+
+       ctx->workstation_name = kstrdup(nodename, GFP_KERNEL);
+       if (unlikely(!ctx->workstation_name)) {
+               rc = -ENOMEM;
+               goto err_exit;
+       }
 
        /*
         * does not have to be perfect mapping since field is
@@ -1493,6 +1511,14 @@ int smb3_init_fs_context(struct fs_context *fc)
        fc->fs_private = ctx;
        fc->ops = &smb3_fs_context_ops;
        return 0;
+
+err_exit:
+       if (ctx) {
+               kfree(ctx->workstation_name);
+               kfree(ctx);
+       }
+
+       return rc;
 }
 
 void
@@ -1518,6 +1544,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx)
        ctx->source = NULL;
        kfree(ctx->domainname);
        ctx->domainname = NULL;
+       kfree(ctx->workstation_name);
+       ctx->workstation_name = NULL;
        kfree(ctx->nodename);
        ctx->nodename = NULL;
        kfree(ctx->iocharset);
index b2d22cf..e54090d 100644 (file)
@@ -170,6 +170,7 @@ struct smb3_fs_context {
        char *server_hostname;
        char *UNC;
        char *nodename;
+       char *workstation_name;
        char *iocharset;  /* local code page for mapping to and from Unicode */
        char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */
        char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */
index ba2c3e8..fb76040 100644 (file)
@@ -94,6 +94,7 @@ sesInfoFree(struct cifs_ses *buf_to_free)
        kfree_sensitive(buf_to_free->password);
        kfree(buf_to_free->user_name);
        kfree(buf_to_free->domainName);
+       kfree(buf_to_free->workstation_name);
        kfree_sensitive(buf_to_free->auth_key.response);
        kfree(buf_to_free->iface_list);
        kfree_sensitive(buf_to_free);
index 25a2b8e..fe707f4 100644 (file)
@@ -119,7 +119,9 @@ typedef struct _AUTHENTICATE_MESSAGE {
  */
 
 int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
-void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses);
+int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen,
+                                struct cifs_ses *ses,
+                                const struct nls_table *nls_cp);
 int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
                        struct cifs_ses *ses,
                        const struct nls_table *nls_cp);
index 23e02db..93a1619 100644 (file)
@@ -599,18 +599,85 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
        return 0;
 }
 
+static int size_of_ntlmssp_blob(struct cifs_ses *ses, int base_size)
+{
+       int sz = base_size + ses->auth_key.len
+               - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2;
+
+       if (ses->domainName)
+               sz += sizeof(__le16) * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
+       else
+               sz += sizeof(__le16);
+
+       if (ses->user_name)
+               sz += sizeof(__le16) * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
+       else
+               sz += sizeof(__le16);
+
+       sz += sizeof(__le16) * strnlen(ses->workstation_name, CIFS_MAX_WORKSTATION_LEN);
+
+       return sz;
+}
+
+static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf,
+                                                char *str_value,
+                                                int str_length,
+                                                unsigned char *pstart,
+                                                unsigned char **pcur,
+                                                const struct nls_table *nls_cp)
+{
+       unsigned char *tmp = pstart;
+       int len;
+
+       if (!pbuf)
+               return;
+
+       if (!pcur)
+               pcur = &tmp;
+
+       if (!str_value) {
+               pbuf->BufferOffset = cpu_to_le32(*pcur - pstart);
+               pbuf->Length = 0;
+               pbuf->MaximumLength = 0;
+               *pcur += sizeof(__le16);
+       } else {
+               len = cifs_strtoUTF16((__le16 *)*pcur,
+                                     str_value,
+                                     str_length,
+                                     nls_cp);
+               len *= sizeof(__le16);
+               pbuf->BufferOffset = cpu_to_le32(*pcur - pstart);
+               pbuf->Length = cpu_to_le16(len);
+               pbuf->MaximumLength = cpu_to_le16(len);
+               *pcur += len;
+       }
+}
+
 /* BB Move to ntlmssp.c eventually */
 
-/* We do not malloc the blob, it is passed in pbuffer, because
-   it is fixed size, and small, making this approach cleaner */
-void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
-                                        struct cifs_ses *ses)
+int build_ntlmssp_negotiate_blob(unsigned char **pbuffer,
+                                u16 *buflen,
+                                struct cifs_ses *ses,
+                                const struct nls_table *nls_cp)
 {
+       int rc = 0;
        struct TCP_Server_Info *server = cifs_ses_server(ses);
-       NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;
+       NEGOTIATE_MESSAGE *sec_blob;
        __u32 flags;
+       unsigned char *tmp;
+       int len;
+
+       len = size_of_ntlmssp_blob(ses, sizeof(NEGOTIATE_MESSAGE));
+       *pbuffer = kmalloc(len, GFP_KERNEL);
+       if (!*pbuffer) {
+               rc = -ENOMEM;
+               cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc);
+               *buflen = 0;
+               goto setup_ntlm_neg_ret;
+       }
+       sec_blob = (NEGOTIATE_MESSAGE *)*pbuffer;
 
-       memset(pbuffer, 0, sizeof(NEGOTIATE_MESSAGE));
+       memset(*pbuffer, 0, sizeof(NEGOTIATE_MESSAGE));
        memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
        sec_blob->MessageType = NtLmNegotiate;
 
@@ -624,34 +691,25 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
        if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
                flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
 
+       tmp = *pbuffer + sizeof(NEGOTIATE_MESSAGE);
        sec_blob->NegotiateFlags = cpu_to_le32(flags);
 
-       sec_blob->WorkstationName.BufferOffset = 0;
-       sec_blob->WorkstationName.Length = 0;
-       sec_blob->WorkstationName.MaximumLength = 0;
-
-       /* Domain name is sent on the Challenge not Negotiate NTLMSSP request */
-       sec_blob->DomainName.BufferOffset = 0;
-       sec_blob->DomainName.Length = 0;
-       sec_blob->DomainName.MaximumLength = 0;
-}
-
-static int size_of_ntlmssp_blob(struct cifs_ses *ses)
-{
-       int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len
-               - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2;
-
-       if (ses->domainName)
-               sz += 2 * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
-       else
-               sz += 2;
+       /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */
+       cifs_security_buffer_from_str(&sec_blob->DomainName,
+                                     NULL,
+                                     CIFS_MAX_DOMAINNAME_LEN,
+                                     *pbuffer, &tmp,
+                                     nls_cp);
 
-       if (ses->user_name)
-               sz += 2 * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
-       else
-               sz += 2;
+       cifs_security_buffer_from_str(&sec_blob->WorkstationName,
+                                     NULL,
+                                     CIFS_MAX_WORKSTATION_LEN,
+                                     *pbuffer, &tmp,
+                                     nls_cp);
 
-       return sz;
+       *buflen = tmp - *pbuffer;
+setup_ntlm_neg_ret:
+       return rc;
 }
 
 int build_ntlmssp_auth_blob(unsigned char **pbuffer,
@@ -663,6 +721,7 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
        AUTHENTICATE_MESSAGE *sec_blob;
        __u32 flags;
        unsigned char *tmp;
+       int len;
 
        rc = setup_ntlmv2_rsp(ses, nls_cp);
        if (rc) {
@@ -670,7 +729,9 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
                *buflen = 0;
                goto setup_ntlmv2_ret;
        }
-       *pbuffer = kmalloc(size_of_ntlmssp_blob(ses), GFP_KERNEL);
+
+       len = size_of_ntlmssp_blob(ses, sizeof(AUTHENTICATE_MESSAGE));
+       *pbuffer = kmalloc(len, GFP_KERNEL);
        if (!*pbuffer) {
                rc = -ENOMEM;
                cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc);
@@ -686,7 +747,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 |
-               NTLMSSP_NEGOTIATE_SEAL;
+               NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
        if (ses->server->sign)
                flags |= NTLMSSP_NEGOTIATE_SIGN;
        if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
@@ -719,42 +780,23 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
                sec_blob->NtChallengeResponse.MaximumLength = 0;
        }
 
-       if (ses->domainName == NULL) {
-               sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
-               sec_blob->DomainName.Length = 0;
-               sec_blob->DomainName.MaximumLength = 0;
-               tmp += 2;
-       } else {
-               int len;
-               len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName,
-                                     CIFS_MAX_DOMAINNAME_LEN, nls_cp);
-               len *= 2; /* unicode is 2 bytes each */
-               sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
-               sec_blob->DomainName.Length = cpu_to_le16(len);
-               sec_blob->DomainName.MaximumLength = cpu_to_le16(len);
-               tmp += len;
-       }
+       cifs_security_buffer_from_str(&sec_blob->DomainName,
+                                     ses->domainName,
+                                     CIFS_MAX_DOMAINNAME_LEN,
+                                     *pbuffer, &tmp,
+                                     nls_cp);
 
-       if (ses->user_name == NULL) {
-               sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
-               sec_blob->UserName.Length = 0;
-               sec_blob->UserName.MaximumLength = 0;
-               tmp += 2;
-       } else {
-               int len;
-               len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name,
-                                     CIFS_MAX_USERNAME_LEN, nls_cp);
-               len *= 2; /* unicode is 2 bytes each */
-               sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
-               sec_blob->UserName.Length = cpu_to_le16(len);
-               sec_blob->UserName.MaximumLength = cpu_to_le16(len);
-               tmp += len;
-       }
+       cifs_security_buffer_from_str(&sec_blob->UserName,
+                                     ses->user_name,
+                                     CIFS_MAX_USERNAME_LEN,
+                                     *pbuffer, &tmp,
+                                     nls_cp);
 
-       sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
-       sec_blob->WorkstationName.Length = 0;
-       sec_blob->WorkstationName.MaximumLength = 0;
-       tmp += 2;
+       cifs_security_buffer_from_str(&sec_blob->WorkstationName,
+                                     ses->workstation_name,
+                                     CIFS_MAX_WORKSTATION_LEN,
+                                     *pbuffer, &tmp,
+                                     nls_cp);
 
        if (((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) ||
                (ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC))
@@ -1230,6 +1272,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
        struct cifs_ses *ses = sess_data->ses;
        __u16 bytes_remaining;
        char *bcc_ptr;
+       unsigned char *ntlmsspblob = NULL;
        u16 blob_len;
 
        cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n");
@@ -1253,10 +1296,15 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
        pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
 
        /* Build security blob before we assemble the request */
-       build_ntlmssp_negotiate_blob(pSMB->req.SecurityBlob, ses);
-       sess_data->iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
-       sess_data->iov[1].iov_base = pSMB->req.SecurityBlob;
-       pSMB->req.SecurityBlobLength = cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
+       rc = build_ntlmssp_negotiate_blob(&ntlmsspblob,
+                                    &blob_len, ses,
+                                    sess_data->nls_cp);
+       if (rc)
+               goto out;
+
+       sess_data->iov[1].iov_len = blob_len;
+       sess_data->iov[1].iov_base = ntlmsspblob;
+       pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
 
        rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
        if (rc)
index d2ecb2e..5034e53 100644 (file)
@@ -1456,7 +1456,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
        int rc;
        struct cifs_ses *ses = sess_data->ses;
        struct smb2_sess_setup_rsp *rsp = NULL;
-       char *ntlmssp_blob = NULL;
+       unsigned char *ntlmssp_blob = NULL;
        bool use_spnego = false; /* else use raw ntlmssp */
        u16 blob_length = 0;
 
@@ -1475,22 +1475,17 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
        if (rc)
                goto out_err;
 
-       ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
-                              GFP_KERNEL);
-       if (ntlmssp_blob == NULL) {
-               rc = -ENOMEM;
-               goto out;
-       }
+       rc = build_ntlmssp_negotiate_blob(&ntlmssp_blob,
+                                         &blob_length, ses,
+                                         sess_data->nls_cp);
+       if (rc)
+               goto out_err;
 
-       build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
        if (use_spnego) {
                /* BB eventually need to add this */
                cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
                rc = -EOPNOTSUPP;
                goto out;
-       } else {
-               blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
-               /* with raw NTLMSSP we don't encapsulate in SPNEGO */
        }
        sess_data->iov[1].iov_base = ntlmssp_blob;
        sess_data->iov[1].iov_len = blob_length;