summaryrefslogtreecommitdiff
path: root/fs/cifs/sess.c
diff options
context:
space:
mode:
authorSteve French <stfrench@microsoft.com>2023-05-22 04:46:30 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2023-06-28 12:12:40 +0300
commit29429a1f5871dbe54ee0da81bb12db8567f15379 (patch)
tree2ca619330d1d853a878075d14244aece3031b751 /fs/cifs/sess.c
parent9d3e4bca4b92bb64491ce7b83482494fe70455bf (diff)
downloadlinux-29429a1f5871dbe54ee0da81bb12db8567f15379.tar.xz
smb: move client and server files to common directory fs/smb
commit 38c8a9a52082579090e34c033d439ed2cd1a462d upstream. Move CIFS/SMB3 related client and server files (cifs.ko and ksmbd.ko and helper modules) to new fs/smb subdirectory: fs/cifs --> fs/smb/client fs/ksmbd --> fs/smb/server fs/smbfs_common --> fs/smb/common Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> [ added to stable trees to handle the directory change to handle the future stable patches due to the constant churn in this filesystem at the moment - gregkh ] Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/cifs/sess.c')
-rw-r--r--fs/cifs/sess.c1858
1 files changed, 0 insertions, 1858 deletions
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
deleted file mode 100644
index 81be17845072..000000000000
--- a/fs/cifs/sess.c
+++ /dev/null
@@ -1,1858 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1
-/*
- *
- * SMB/CIFS session setup handling routines
- *
- * Copyright (c) International Business Machines Corp., 2006, 2009
- * Author(s): Steve French (sfrench@us.ibm.com)
- *
- */
-
-#include "cifspdu.h"
-#include "cifsglob.h"
-#include "cifsproto.h"
-#include "cifs_unicode.h"
-#include "cifs_debug.h"
-#include "ntlmssp.h"
-#include "nterr.h"
-#include <linux/utsname.h>
-#include <linux/slab.h>
-#include <linux/version.h>
-#include "cifsfs.h"
-#include "cifs_spnego.h"
-#include "smb2proto.h"
-#include "fs_context.h"
-
-static int
-cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
- struct cifs_server_iface *iface);
-
-bool
-is_server_using_iface(struct TCP_Server_Info *server,
- struct cifs_server_iface *iface)
-{
- struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr;
- struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr;
- struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr;
- struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr;
-
- if (server->dstaddr.ss_family != iface->sockaddr.ss_family)
- return false;
- if (server->dstaddr.ss_family == AF_INET) {
- if (s4->sin_addr.s_addr != i4->sin_addr.s_addr)
- return false;
- } else if (server->dstaddr.ss_family == AF_INET6) {
- if (memcmp(&s6->sin6_addr, &i6->sin6_addr,
- sizeof(i6->sin6_addr)) != 0)
- return false;
- } else {
- /* unknown family.. */
- return false;
- }
- return true;
-}
-
-bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
-{
- int i;
-
- spin_lock(&ses->chan_lock);
- for (i = 0; i < ses->chan_count; i++) {
- if (ses->chans[i].iface == iface) {
- spin_unlock(&ses->chan_lock);
- return true;
- }
- }
- spin_unlock(&ses->chan_lock);
- return false;
-}
-
-/* channel helper functions. assumed that chan_lock is held by caller. */
-
-unsigned int
-cifs_ses_get_chan_index(struct cifs_ses *ses,
- struct TCP_Server_Info *server)
-{
- unsigned int i;
-
- for (i = 0; i < ses->chan_count; i++) {
- if (ses->chans[i].server == server)
- return i;
- }
-
- /* If we didn't find the channel, it is likely a bug */
- if (server)
- cifs_dbg(VFS, "unable to get chan index for server: 0x%llx",
- server->conn_id);
- WARN_ON(1);
- return 0;
-}
-
-void
-cifs_chan_set_in_reconnect(struct cifs_ses *ses,
- struct TCP_Server_Info *server)
-{
- unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
-
- ses->chans[chan_index].in_reconnect = true;
-}
-
-void
-cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
- struct TCP_Server_Info *server)
-{
- unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
-
- ses->chans[chan_index].in_reconnect = false;
-}
-
-bool
-cifs_chan_in_reconnect(struct cifs_ses *ses,
- struct TCP_Server_Info *server)
-{
- unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
-
- return CIFS_CHAN_IN_RECONNECT(ses, chan_index);
-}
-
-void
-cifs_chan_set_need_reconnect(struct cifs_ses *ses,
- struct TCP_Server_Info *server)
-{
- unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
-
- set_bit(chan_index, &ses->chans_need_reconnect);
- cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
- chan_index, ses->chans_need_reconnect);
-}
-
-void
-cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
- struct TCP_Server_Info *server)
-{
- unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
-
- clear_bit(chan_index, &ses->chans_need_reconnect);
- cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
- chan_index, ses->chans_need_reconnect);
-}
-
-bool
-cifs_chan_needs_reconnect(struct cifs_ses *ses,
- struct TCP_Server_Info *server)
-{
- unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
-
- return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
-}
-
-bool
-cifs_chan_is_iface_active(struct cifs_ses *ses,
- struct TCP_Server_Info *server)
-{
- unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
-
- return ses->chans[chan_index].iface &&
- ses->chans[chan_index].iface->is_active;
-}
-
-/* returns number of channels added */
-int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
-{
- struct TCP_Server_Info *server = ses->server;
- int old_chan_count, new_chan_count;
- int left;
- int rc = 0;
- int tries = 0;
- struct cifs_server_iface *iface = NULL, *niface = NULL;
-
- spin_lock(&ses->chan_lock);
-
- new_chan_count = old_chan_count = ses->chan_count;
- left = ses->chan_max - ses->chan_count;
-
- if (left <= 0) {
- spin_unlock(&ses->chan_lock);
- cifs_dbg(FYI,
- "ses already at max_channels (%zu), nothing to open\n",
- ses->chan_max);
- return 0;
- }
-
- if (server->dialect < SMB30_PROT_ID) {
- spin_unlock(&ses->chan_lock);
- cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
- return 0;
- }
-
- if (!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
- ses->chan_max = 1;
- spin_unlock(&ses->chan_lock);
- cifs_server_dbg(VFS, "no multichannel support\n");
- return 0;
- }
- spin_unlock(&ses->chan_lock);
-
- /*
- * Keep connecting to same, fastest, iface for all channels as
- * long as its RSS. Try next fastest one if not RSS or channel
- * creation fails.
- */
- spin_lock(&ses->iface_lock);
- iface = list_first_entry(&ses->iface_list, struct cifs_server_iface,
- iface_head);
- spin_unlock(&ses->iface_lock);
-
- while (left > 0) {
-
- tries++;
- if (tries > 3*ses->chan_max) {
- cifs_dbg(FYI, "too many channel open attempts (%d channels left to open)\n",
- left);
- break;
- }
-
- spin_lock(&ses->iface_lock);
- if (!ses->iface_count) {
- spin_unlock(&ses->iface_lock);
- break;
- }
-
- list_for_each_entry_safe_from(iface, niface, &ses->iface_list,
- iface_head) {
- /* skip ifaces that are unusable */
- if (!iface->is_active ||
- (is_ses_using_iface(ses, iface) &&
- !iface->rss_capable)) {
- continue;
- }
-
- /* take ref before unlock */
- kref_get(&iface->refcount);
-
- spin_unlock(&ses->iface_lock);
- rc = cifs_ses_add_channel(cifs_sb, ses, iface);
- spin_lock(&ses->iface_lock);
-
- if (rc) {
- cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n",
- &iface->sockaddr,
- rc);
- kref_put(&iface->refcount, release_iface);
- continue;
- }
-
- cifs_dbg(FYI, "successfully opened new channel on iface:%pIS\n",
- &iface->sockaddr);
- break;
- }
- spin_unlock(&ses->iface_lock);
-
- left--;
- new_chan_count++;
- }
-
- return new_chan_count - old_chan_count;
-}
-
-/*
- * update the iface for the channel if necessary.
- * will return 0 when iface is updated, 1 if removed, 2 otherwise
- * Must be called with chan_lock held.
- */
-int
-cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
-{
- unsigned int chan_index;
- struct cifs_server_iface *iface = NULL;
- struct cifs_server_iface *old_iface = NULL;
- int rc = 0;
-
- spin_lock(&ses->chan_lock);
- chan_index = cifs_ses_get_chan_index(ses, server);
- if (!chan_index) {
- spin_unlock(&ses->chan_lock);
- return 0;
- }
-
- if (ses->chans[chan_index].iface) {
- old_iface = ses->chans[chan_index].iface;
- if (old_iface->is_active) {
- spin_unlock(&ses->chan_lock);
- return 1;
- }
- }
- spin_unlock(&ses->chan_lock);
-
- spin_lock(&ses->iface_lock);
- /* then look for a new one */
- list_for_each_entry(iface, &ses->iface_list, iface_head) {
- if (!iface->is_active ||
- (is_ses_using_iface(ses, iface) &&
- !iface->rss_capable)) {
- continue;
- }
- kref_get(&iface->refcount);
- break;
- }
-
- if (list_entry_is_head(iface, &ses->iface_list, iface_head)) {
- rc = 1;
- iface = NULL;
- cifs_dbg(FYI, "unable to find a suitable iface\n");
- }
-
- /* now drop the ref to the current iface */
- if (old_iface && iface) {
- cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n",
- &old_iface->sockaddr,
- &iface->sockaddr);
- kref_put(&old_iface->refcount, release_iface);
- } else if (old_iface) {
- cifs_dbg(FYI, "releasing ref to iface: %pIS\n",
- &old_iface->sockaddr);
- kref_put(&old_iface->refcount, release_iface);
- } else {
- WARN_ON(!iface);
- cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr);
- }
- spin_unlock(&ses->iface_lock);
-
- spin_lock(&ses->chan_lock);
- chan_index = cifs_ses_get_chan_index(ses, server);
- ses->chans[chan_index].iface = iface;
-
- /* No iface is found. if secondary chan, drop connection */
- if (!iface && CIFS_SERVER_IS_CHAN(server))
- ses->chans[chan_index].server = NULL;
-
- spin_unlock(&ses->chan_lock);
-
- if (!iface && CIFS_SERVER_IS_CHAN(server))
- cifs_put_tcp_session(server, false);
-
- return rc;
-}
-
-/*
- * If server is a channel of ses, return the corresponding enclosing
- * cifs_chan otherwise return NULL.
- */
-struct cifs_chan *
-cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server)
-{
- int i;
-
- spin_lock(&ses->chan_lock);
- for (i = 0; i < ses->chan_count; i++) {
- if (ses->chans[i].server == server) {
- spin_unlock(&ses->chan_lock);
- return &ses->chans[i];
- }
- }
- spin_unlock(&ses->chan_lock);
- return NULL;
-}
-
-static int
-cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
- struct cifs_server_iface *iface)
-{
- struct TCP_Server_Info *chan_server;
- struct cifs_chan *chan;
- struct smb3_fs_context ctx = {NULL};
- static const char unc_fmt[] = "\\%s\\foo";
- char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0};
- struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
- struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
- int rc;
- unsigned int xid = get_xid();
-
- if (iface->sockaddr.ss_family == AF_INET)
- cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI4)\n",
- ses, iface->speed, iface->rdma_capable ? "yes" : "no",
- &ipv4->sin_addr);
- else
- cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI6)\n",
- ses, iface->speed, iface->rdma_capable ? "yes" : "no",
- &ipv6->sin6_addr);
-
- /*
- * Setup a ctx with mostly the same info as the existing
- * session and overwrite it with the requested iface data.
- *
- * We need to setup at least the fields used for negprot and
- * sesssetup.
- *
- * We only need the ctx here, so we can reuse memory from
- * the session and server without caring about memory
- * management.
- */
-
- /* Always make new connection for now (TODO?) */
- ctx.nosharesock = true;
-
- /* Auth */
- ctx.domainauto = ses->domainAuto;
- ctx.domainname = ses->domainName;
-
- /* no hostname for extra channels */
- ctx.server_hostname = "";
-
- ctx.username = ses->user_name;
- ctx.password = ses->password;
- ctx.sectype = ses->sectype;
- ctx.sign = ses->sign;
-
- /* UNC and paths */
- /* XXX: Use ses->server->hostname? */
- sprintf(unc, unc_fmt, ses->ip_addr);
- ctx.UNC = unc;
- ctx.prepath = "";
-
- /* Reuse same version as master connection */
- ctx.vals = ses->server->vals;
- ctx.ops = ses->server->ops;
-
- ctx.noblocksnd = ses->server->noblocksnd;
- ctx.noautotune = ses->server->noautotune;
- ctx.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
- ctx.echo_interval = ses->server->echo_interval / HZ;
- ctx.max_credits = ses->server->max_credits;
-
- /*
- * This will be used for encoding/decoding user/domain/pw
- * during sess setup auth.
- */
- ctx.local_nls = cifs_sb->local_nls;
-
- /* Use RDMA if possible */
- ctx.rdma = iface->rdma_capable;
- memcpy(&ctx.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage));
-
- /* reuse master con client guid */
- memcpy(&ctx.client_guid, ses->server->client_guid,
- SMB2_CLIENT_GUID_SIZE);
- ctx.use_client_guid = true;
-
- chan_server = cifs_get_tcp_session(&ctx, ses->server);
-
- spin_lock(&ses->chan_lock);
- chan = &ses->chans[ses->chan_count];
- chan->server = chan_server;
- if (IS_ERR(chan->server)) {
- rc = PTR_ERR(chan->server);
- chan->server = NULL;
- spin_unlock(&ses->chan_lock);
- goto out;
- }
- chan->iface = iface;
- ses->chan_count++;
- atomic_set(&ses->chan_seq, 0);
-
- /* Mark this channel as needing connect/setup */
- cifs_chan_set_need_reconnect(ses, chan->server);
-
- spin_unlock(&ses->chan_lock);
-
- mutex_lock(&ses->session_mutex);
- /*
- * We need to allocate the server crypto now as we will need
- * to sign packets before we generate the channel signing key
- * (we sign with the session key)
- */
- rc = smb311_crypto_shash_allocate(chan->server);
- if (rc) {
- cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
- mutex_unlock(&ses->session_mutex);
- goto out;
- }
-
- rc = cifs_negotiate_protocol(xid, ses, chan->server);
- if (!rc)
- rc = cifs_setup_session(xid, ses, chan->server, cifs_sb->local_nls);
-
- mutex_unlock(&ses->session_mutex);
-
-out:
- if (rc && chan->server) {
- /*
- * we should avoid race with these delayed works before we
- * remove this channel
- */
- cancel_delayed_work_sync(&chan->server->echo);
- cancel_delayed_work_sync(&chan->server->resolve);
- cancel_delayed_work_sync(&chan->server->reconnect);
-
- spin_lock(&ses->chan_lock);
- /* we rely on all bits beyond chan_count to be clear */
- cifs_chan_clear_need_reconnect(ses, chan->server);
- ses->chan_count--;
- /*
- * chan_count should never reach 0 as at least the primary
- * channel is always allocated
- */
- WARN_ON(ses->chan_count < 1);
- spin_unlock(&ses->chan_lock);
-
- cifs_put_tcp_session(chan->server, 0);
- }
-
- free_xid(xid);
- return rc;
-}
-
-#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
-static __u32 cifs_ssetup_hdr(struct cifs_ses *ses,
- struct TCP_Server_Info *server,
- SESSION_SETUP_ANDX *pSMB)
-{
- __u32 capabilities = 0;
-
- /* init fields common to all four types of SessSetup */
- /* Note that offsets for first seven fields in req struct are same */
- /* in CIFS Specs so does not matter which of 3 forms of struct */
- /* that we use in next few lines */
- /* Note that header is initialized to zero in header_assemble */
- pSMB->req.AndXCommand = 0xFF;
- pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32,
- CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4,
- USHRT_MAX));
- pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq);
- pSMB->req.VcNumber = cpu_to_le16(1);
-
- /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
-
- /* BB verify whether signing required on neg or just on auth frame
- (and NTLM case) */
-
- capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
- CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
-
- if (server->sign)
- pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
-
- if (ses->capabilities & CAP_UNICODE) {
- pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE;
- capabilities |= CAP_UNICODE;
- }
- if (ses->capabilities & CAP_STATUS32) {
- pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS;
- capabilities |= CAP_STATUS32;
- }
- if (ses->capabilities & CAP_DFS) {
- pSMB->req.hdr.Flags2 |= SMBFLG2_DFS;
- capabilities |= CAP_DFS;
- }
- if (ses->capabilities & CAP_UNIX)
- capabilities |= CAP_UNIX;
-
- return capabilities;
-}
-
-static void
-unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp)
-{
- char *bcc_ptr = *pbcc_area;
- int bytes_ret = 0;
-
- /* Copy OS version */
- bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32,
- nls_cp);
- bcc_ptr += 2 * bytes_ret;
- bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release,
- 32, nls_cp);
- bcc_ptr += 2 * bytes_ret;
- bcc_ptr += 2; /* trailing null */
-
- bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
- 32, nls_cp);
- bcc_ptr += 2 * bytes_ret;
- bcc_ptr += 2; /* trailing null */
-
- *pbcc_area = bcc_ptr;
-}
-
-static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses,
- const struct nls_table *nls_cp)
-{
- char *bcc_ptr = *pbcc_area;
- int bytes_ret = 0;
-
- /* copy domain */
- if (ses->domainName == NULL) {
- /* Sending null domain better than using a bogus domain name (as
- we did briefly in 2.6.18) since server will use its default */
- *bcc_ptr = 0;
- *(bcc_ptr+1) = 0;
- bytes_ret = 0;
- } else
- bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName,
- CIFS_MAX_DOMAINNAME_LEN, nls_cp);
- bcc_ptr += 2 * bytes_ret;
- bcc_ptr += 2; /* account for null terminator */
-
- *pbcc_area = bcc_ptr;
-}
-
-static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
- const struct nls_table *nls_cp)
-{
- char *bcc_ptr = *pbcc_area;
- int bytes_ret = 0;
-
- /* BB FIXME add check that strings total less
- than 335 or will need to send them as arrays */
-
- /* copy user */
- if (ses->user_name == NULL) {
- /* null user mount */
- *bcc_ptr = 0;
- *(bcc_ptr+1) = 0;
- } else {
- bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name,
- CIFS_MAX_USERNAME_LEN, nls_cp);
- }
- bcc_ptr += 2 * bytes_ret;
- bcc_ptr += 2; /* account for null termination */
-
- unicode_domain_string(&bcc_ptr, ses, nls_cp);
- unicode_oslm_strings(&bcc_ptr, nls_cp);
-
- *pbcc_area = bcc_ptr;
-}
-
-static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
- const struct nls_table *nls_cp)
-{
- char *bcc_ptr = *pbcc_area;
- int len;
-
- /* copy user */
- /* BB what about null user mounts - check that we do this BB */
- /* copy user */
- if (ses->user_name != NULL) {
- len = strscpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN);
- if (WARN_ON_ONCE(len < 0))
- len = CIFS_MAX_USERNAME_LEN - 1;
- bcc_ptr += len;
- }
- /* else null user mount */
- *bcc_ptr = 0;
- bcc_ptr++; /* account for null termination */
-
- /* copy domain */
- if (ses->domainName != NULL) {
- len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
- if (WARN_ON_ONCE(len < 0))
- len = CIFS_MAX_DOMAINNAME_LEN - 1;
- bcc_ptr += len;
- } /* else we will send a null domain name
- so the server will default to its own domain */
- *bcc_ptr = 0;
- bcc_ptr++;
-
- /* BB check for overflow here */
-
- strcpy(bcc_ptr, "Linux version ");
- bcc_ptr += strlen("Linux version ");
- strcpy(bcc_ptr, init_utsname()->release);
- bcc_ptr += strlen(init_utsname()->release) + 1;
-
- strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
- bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
-
- *pbcc_area = bcc_ptr;
-}
-
-static void
-decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses,
- const struct nls_table *nls_cp)
-{
- int len;
- char *data = *pbcc_area;
-
- cifs_dbg(FYI, "bleft %d\n", bleft);
-
- kfree(ses->serverOS);
- ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
- cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS);
- len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;
- data += len;
- bleft -= len;
- if (bleft <= 0)
- return;
-
- kfree(ses->serverNOS);
- ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
- cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS);
- len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;
- data += len;
- bleft -= len;
- if (bleft <= 0)
- return;
-
- kfree(ses->serverDomain);
- ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
- cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain);
-
- return;
-}
-
-static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft,
- struct cifs_ses *ses,
- const struct nls_table *nls_cp)
-{
- int len;
- char *bcc_ptr = *pbcc_area;
-
- cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft);
-
- len = strnlen(bcc_ptr, bleft);
- if (len >= bleft)
- return;
-
- kfree(ses->serverOS);
-
- ses->serverOS = kmalloc(len + 1, GFP_KERNEL);
- if (ses->serverOS) {
- memcpy(ses->serverOS, bcc_ptr, len);
- ses->serverOS[len] = 0;
- if (strncmp(ses->serverOS, "OS/2", 4) == 0)
- cifs_dbg(FYI, "OS/2 server\n");
- }
-
- bcc_ptr += len + 1;
- bleft -= len + 1;
-
- len = strnlen(bcc_ptr, bleft);
- if (len >= bleft)
- return;
-
- kfree(ses->serverNOS);
-
- ses->serverNOS = kmalloc(len + 1, GFP_KERNEL);
- if (ses->serverNOS) {
- memcpy(ses->serverNOS, bcc_ptr, len);
- ses->serverNOS[len] = 0;
- }
-
- bcc_ptr += len + 1;
- bleft -= len + 1;
-
- len = strnlen(bcc_ptr, bleft);
- if (len > bleft)
- return;
-
- /* No domain field in LANMAN case. Domain is
- returned by old servers in the SMB negprot response */
- /* BB For newer servers which do not support Unicode,
- 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);
-}
-#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
-
-int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
- struct cifs_ses *ses)
-{
- unsigned int tioffset; /* challenge message target info area */
- unsigned int tilen; /* challenge message target info area length */
- CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr;
- __u32 server_flags;
-
- if (blob_len < sizeof(CHALLENGE_MESSAGE)) {
- cifs_dbg(VFS, "challenge blob len %d too small\n", blob_len);
- return -EINVAL;
- }
-
- if (memcmp(pblob->Signature, "NTLMSSP", 8)) {
- cifs_dbg(VFS, "blob signature incorrect %s\n",
- pblob->Signature);
- return -EINVAL;
- }
- if (pblob->MessageType != NtLmChallenge) {
- cifs_dbg(VFS, "Incorrect message type %d\n",
- pblob->MessageType);
- return -EINVAL;
- }
-
- server_flags = le32_to_cpu(pblob->NegotiateFlags);
- cifs_dbg(FYI, "%s: negotiate=0x%08x challenge=0x%08x\n", __func__,
- ses->ntlmssp->client_flags, server_flags);
-
- if ((ses->ntlmssp->client_flags & (NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN)) &&
- (!(server_flags & NTLMSSP_NEGOTIATE_56) && !(server_flags & NTLMSSP_NEGOTIATE_128))) {
- cifs_dbg(VFS, "%s: requested signing/encryption but server did not return either 56-bit or 128-bit session key size\n",
- __func__);
- return -EINVAL;
- }
- if (!(server_flags & NTLMSSP_NEGOTIATE_NTLM) && !(server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) {
- cifs_dbg(VFS, "%s: server does not seem to support either NTLMv1 or NTLMv2\n", __func__);
- return -EINVAL;
- }
- if (ses->server->sign && !(server_flags & NTLMSSP_NEGOTIATE_SIGN)) {
- cifs_dbg(VFS, "%s: forced packet signing but server does not seem to support it\n",
- __func__);
- return -EOPNOTSUPP;
- }
- if ((ses->ntlmssp->client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) &&
- !(server_flags & NTLMSSP_NEGOTIATE_KEY_XCH))
- pr_warn_once("%s: authentication has been weakened as server does not support key exchange\n",
- __func__);
-
- ses->ntlmssp->server_flags = server_flags;
-
- memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE);
- /* In particular we can examine sign flags */
- /* BB spec says that if AvId field of MsvAvTimestamp is populated then
- we must set the MIC field of the AUTHENTICATE_MESSAGE */
-
- tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset);
- tilen = le16_to_cpu(pblob->TargetInfoArray.Length);
- if (tioffset > blob_len || tioffset + tilen > blob_len) {
- cifs_dbg(VFS, "tioffset + tilen too high %u + %u\n",
- tioffset, tilen);
- return -EINVAL;
- }
- if (tilen) {
- kfree_sensitive(ses->auth_key.response);
- ses->auth_key.response = kmemdup(bcc_ptr + tioffset, tilen,
- GFP_KERNEL);
- if (!ses->auth_key.response) {
- cifs_dbg(VFS, "Challenge target info alloc failure\n");
- return -ENOMEM;
- }
- ses->auth_key.len = tilen;
- }
-
- 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);
-
- if (ses->workstation_name[0])
- sz += sizeof(__le16) * strnlen(ses->workstation_name,
- ntlmssp_workstation_name_size(ses));
- else
- sz += sizeof(__le16);
-
- 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 */
-
-int build_ntlmssp_negotiate_blob(unsigned char **pbuffer,
- u16 *buflen,
- struct cifs_ses *ses,
- struct TCP_Server_Info *server,
- const struct nls_table *nls_cp)
-{
- int rc = 0;
- 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));
- memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
- sec_blob->MessageType = NtLmNegotiate;
-
- /* BB is NTLMV2 session security format easier to use here? */
- flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET |
- NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
- NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
- NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL |
- NTLMSSP_NEGOTIATE_SIGN;
- if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
- flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
-
- tmp = *pbuffer + sizeof(NEGOTIATE_MESSAGE);
- ses->ntlmssp->client_flags = flags;
- sec_blob->NegotiateFlags = cpu_to_le32(flags);
-
- /* 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);
-
- cifs_security_buffer_from_str(&sec_blob->WorkstationName,
- NULL,
- CIFS_MAX_WORKSTATION_LEN,
- *pbuffer, &tmp,
- nls_cp);
-
- *buflen = tmp - *pbuffer;
-setup_ntlm_neg_ret:
- return rc;
-}
-
-/*
- * Build ntlmssp blob with additional fields, such as version,
- * supported by modern servers. For safety limit to SMB3 or later
- * See notes in MS-NLMP Section 2.2.2.1 e.g.
- */
-int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer,
- u16 *buflen,
- struct cifs_ses *ses,
- struct TCP_Server_Info *server,
- const struct nls_table *nls_cp)
-{
- int rc = 0;
- struct negotiate_message *sec_blob;
- __u32 flags;
- unsigned char *tmp;
- int len;
-
- len = size_of_ntlmssp_blob(ses, sizeof(struct 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_smb3_neg_ret;
- }
- sec_blob = (struct negotiate_message *)*pbuffer;
-
- memset(*pbuffer, 0, sizeof(struct negotiate_message));
- memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
- sec_blob->MessageType = NtLmNegotiate;
-
- /* BB is NTLMV2 session security format easier to use here? */
- flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET |
- NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
- NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
- NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL |
- NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_VERSION;
- if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
- flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
-
- sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR;
- sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL;
- sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD);
- sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;
-
- tmp = *pbuffer + sizeof(struct negotiate_message);
- ses->ntlmssp->client_flags = flags;
- sec_blob->NegotiateFlags = cpu_to_le32(flags);
-
- /* 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);
-
- cifs_security_buffer_from_str(&sec_blob->WorkstationName,
- NULL,
- CIFS_MAX_WORKSTATION_LEN,
- *pbuffer, &tmp,
- nls_cp);
-
- *buflen = tmp - *pbuffer;
-setup_ntlm_smb3_neg_ret:
- return rc;
-}
-
-
-int build_ntlmssp_auth_blob(unsigned char **pbuffer,
- u16 *buflen,
- struct cifs_ses *ses,
- struct TCP_Server_Info *server,
- const struct nls_table *nls_cp)
-{
- int rc;
- AUTHENTICATE_MESSAGE *sec_blob;
- __u32 flags;
- unsigned char *tmp;
- int len;
-
- rc = setup_ntlmv2_rsp(ses, nls_cp);
- if (rc) {
- cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
- *buflen = 0;
- goto setup_ntlmv2_ret;
- }
-
- 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);
- *buflen = 0;
- goto setup_ntlmv2_ret;
- }
- sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer;
-
- memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
- sec_blob->MessageType = NtLmAuthenticate;
-
- flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET |
- NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
-
- tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
- sec_blob->NegotiateFlags = cpu_to_le32(flags);
-
- sec_blob->LmChallengeResponse.BufferOffset =
- cpu_to_le32(sizeof(AUTHENTICATE_MESSAGE));
- sec_blob->LmChallengeResponse.Length = 0;
- sec_blob->LmChallengeResponse.MaximumLength = 0;
-
- sec_blob->NtChallengeResponse.BufferOffset =
- cpu_to_le32(tmp - *pbuffer);
- if (ses->user_name != NULL) {
- memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
- ses->auth_key.len - CIFS_SESS_KEY_SIZE);
- tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
-
- sec_blob->NtChallengeResponse.Length =
- cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
- sec_blob->NtChallengeResponse.MaximumLength =
- cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
- } else {
- /*
- * don't send an NT Response for anonymous access
- */
- sec_blob->NtChallengeResponse.Length = 0;
- sec_blob->NtChallengeResponse.MaximumLength = 0;
- }
-
- cifs_security_buffer_from_str(&sec_blob->DomainName,
- ses->domainName,
- CIFS_MAX_DOMAINNAME_LEN,
- *pbuffer, &tmp,
- nls_cp);
-
- cifs_security_buffer_from_str(&sec_blob->UserName,
- ses->user_name,
- CIFS_MAX_USERNAME_LEN,
- *pbuffer, &tmp,
- nls_cp);
-
- cifs_security_buffer_from_str(&sec_blob->WorkstationName,
- ses->workstation_name,
- ntlmssp_workstation_name_size(ses),
- *pbuffer, &tmp,
- nls_cp);
-
- if ((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) &&
- (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess) &&
- !calc_seckey(ses)) {
- memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
- sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
- sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE);
- sec_blob->SessionKey.MaximumLength =
- cpu_to_le16(CIFS_CPHTXT_SIZE);
- tmp += CIFS_CPHTXT_SIZE;
- } else {
- sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
- sec_blob->SessionKey.Length = 0;
- sec_blob->SessionKey.MaximumLength = 0;
- }
-
- *buflen = tmp - *pbuffer;
-setup_ntlmv2_ret:
- return rc;
-}
-
-enum securityEnum
-cifs_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 NTLMv2:
- return requested;
- case Unspecified:
- if (global_secflags & CIFSSEC_MAY_NTLMV2)
- return NTLMv2;
- break;
- default:
- break;
- }
- fallthrough;
- default:
- return Unspecified;
- }
-}
-
-struct sess_data {
- unsigned int xid;
- struct cifs_ses *ses;
- struct TCP_Server_Info *server;
- struct nls_table *nls_cp;
- void (*func)(struct sess_data *);
- int result;
-
- /* we will send the SMB in three pieces:
- * a fixed length beginning part, an optional
- * SPNEGO blob (which can be zero length), and a
- * last part which will include the strings
- * and rest of bcc area. This allows us to avoid
- * a large buffer 17K allocation
- */
- int buf0_type;
- struct kvec iov[3];
-};
-
-#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
-static int
-sess_alloc_buffer(struct sess_data *sess_data, int wct)
-{
- int rc;
- struct cifs_ses *ses = sess_data->ses;
- struct smb_hdr *smb_buf;
-
- rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
- (void **)&smb_buf);
-
- if (rc)
- return rc;
-
- sess_data->iov[0].iov_base = (char *)smb_buf;
- sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
- /*
- * This variable will be used to clear the buffer
- * allocated above in case of any error in the calling function.
- */
- sess_data->buf0_type = CIFS_SMALL_BUFFER;
-
- /* 2000 big enough to fit max user, domain, NOS name etc. */
- sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL);
- if (!sess_data->iov[2].iov_base) {
- rc = -ENOMEM;
- goto out_free_smb_buf;
- }
-
- return 0;
-
-out_free_smb_buf:
- cifs_small_buf_release(smb_buf);
- sess_data->iov[0].iov_base = NULL;
- sess_data->iov[0].iov_len = 0;
- sess_data->buf0_type = CIFS_NO_BUFFER;
- return rc;
-}
-
-static void
-sess_free_buffer(struct sess_data *sess_data)
-{
- struct kvec *iov = sess_data->iov;
-
- /*
- * Zero the session data before freeing, as it might contain sensitive info (keys, etc).
- * Note that iov[1] is already freed by caller.
- */
- if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base)
- memzero_explicit(iov[0].iov_base, iov[0].iov_len);
-
- free_rsp_buf(sess_data->buf0_type, iov[0].iov_base);
- sess_data->buf0_type = CIFS_NO_BUFFER;
- kfree_sensitive(iov[2].iov_base);
-}
-
-static int
-sess_establish_session(struct sess_data *sess_data)
-{
- struct cifs_ses *ses = sess_data->ses;
- struct TCP_Server_Info *server = sess_data->server;
-
- cifs_server_lock(server);
- if (!server->session_estab) {
- if (server->sign) {
- server->session_key.response =
- kmemdup(ses->auth_key.response,
- ses->auth_key.len, GFP_KERNEL);
- if (!server->session_key.response) {
- cifs_server_unlock(server);
- return -ENOMEM;
- }
- server->session_key.len =
- ses->auth_key.len;
- }
- server->sequence_number = 0x2;
- server->session_estab = true;
- }
- cifs_server_unlock(server);
-
- cifs_dbg(FYI, "CIFS session established successfully\n");
- return 0;
-}
-
-static int
-sess_sendreceive(struct sess_data *sess_data)
-{
- int rc;
- struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base;
- __u16 count;
- struct kvec rsp_iov = { NULL, 0 };
-
- count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
- be32_add_cpu(&smb_buf->smb_buf_length, count);
- put_bcc(count, smb_buf);
-
- rc = SendReceive2(sess_data->xid, sess_data->ses,
- sess_data->iov, 3 /* num_iovecs */,
- &sess_data->buf0_type,
- CIFS_LOG_ERROR, &rsp_iov);
- cifs_small_buf_release(sess_data->iov[0].iov_base);
- memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec));
-
- return rc;
-}
-
-static void
-sess_auth_ntlmv2(struct sess_data *sess_data)
-{
- int rc = 0;
- struct smb_hdr *smb_buf;
- SESSION_SETUP_ANDX *pSMB;
- char *bcc_ptr;
- struct cifs_ses *ses = sess_data->ses;
- struct TCP_Server_Info *server = sess_data->server;
- __u32 capabilities;
- __u16 bytes_remaining;
-
- /* old style NTLM sessionsetup */
- /* wct = 13 */
- rc = sess_alloc_buffer(sess_data, 13);
- if (rc)
- goto out;
-
- pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
- bcc_ptr = sess_data->iov[2].iov_base;
- capabilities = cifs_ssetup_hdr(ses, server, pSMB);
-
- pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
-
- /* LM2 password would be here if we supported it */
- pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
-
- if (ses->user_name != NULL) {
- /* calculate nlmv2 response and session key */
- rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp);
- if (rc) {
- cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc);
- goto out;
- }
-
- memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
- ses->auth_key.len - CIFS_SESS_KEY_SIZE);
- bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
-
- /* set case sensitive password length after tilen may get
- * assigned, tilen is 0 otherwise.
- */
- pSMB->req_no_secext.CaseSensitivePasswordLength =
- cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
- } else {
- pSMB->req_no_secext.CaseSensitivePasswordLength = 0;
- }
-
- if (ses->capabilities & CAP_UNICODE) {
- if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) {
- *bcc_ptr = 0;
- bcc_ptr++;
- }
- unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
- } else {
- ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
- }
-
-
- sess_data->iov[2].iov_len = (long) bcc_ptr -
- (long) sess_data->iov[2].iov_base;
-
- rc = sess_sendreceive(sess_data);
- if (rc)
- goto out;
-
- pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
- smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
-
- if (smb_buf->WordCount != 3) {
- rc = -EIO;
- cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
- goto out;
- }
-
- if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
- cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
-
- ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
- cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
-
- bytes_remaining = get_bcc(smb_buf);
- bcc_ptr = pByteArea(smb_buf);
-
- /* BB check if Unicode and decode strings */
- if (bytes_remaining == 0) {
- /* no string area to decode, do nothing */
- } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
- /* unicode string area must be word-aligned */
- if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
- ++bcc_ptr;
- --bytes_remaining;
- }
- decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
- sess_data->nls_cp);
- } else {
- decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
- sess_data->nls_cp);
- }
-
- rc = sess_establish_session(sess_data);
-out:
- sess_data->result = rc;
- sess_data->func = NULL;
- sess_free_buffer(sess_data);
- kfree_sensitive(ses->auth_key.response);
- ses->auth_key.response = NULL;
-}
-
-#ifdef CONFIG_CIFS_UPCALL
-static void
-sess_auth_kerberos(struct sess_data *sess_data)
-{
- int rc = 0;
- struct smb_hdr *smb_buf;
- SESSION_SETUP_ANDX *pSMB;
- char *bcc_ptr;
- struct cifs_ses *ses = sess_data->ses;
- struct TCP_Server_Info *server = sess_data->server;
- __u32 capabilities;
- __u16 bytes_remaining;
- struct key *spnego_key = NULL;
- struct cifs_spnego_msg *msg;
- u16 blob_len;
-
- /* extended security */
- /* wct = 12 */
- rc = sess_alloc_buffer(sess_data, 12);
- if (rc)
- goto out;
-
- pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
- bcc_ptr = sess_data->iov[2].iov_base;
- capabilities = cifs_ssetup_hdr(ses, server, pSMB);
-
- spnego_key = cifs_get_spnego_key(ses, server);
- if (IS_ERR(spnego_key)) {
- rc = PTR_ERR(spnego_key);
- spnego_key = NULL;
- goto out;
- }
-
- msg = spnego_key->payload.data[0];
- /*
- * check version field to make sure that cifs.upcall is
- * sending us a response in an expected form
- */
- if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
- cifs_dbg(VFS, "incorrect version of cifs.upcall (expected %d but got %d)\n",
- CIFS_SPNEGO_UPCALL_VERSION, msg->version);
- rc = -EKEYREJECTED;
- goto out_put_spnego_key;
- }
-
- kfree_sensitive(ses->auth_key.response);
- ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
- GFP_KERNEL);
- if (!ses->auth_key.response) {
- cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n",
- msg->sesskey_len);
- rc = -ENOMEM;
- goto out_put_spnego_key;
- }
- ses->auth_key.len = msg->sesskey_len;
-
- pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
- capabilities |= CAP_EXTENDED_SECURITY;
- pSMB->req.Capabilities = cpu_to_le32(capabilities);
- sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
- sess_data->iov[1].iov_len = msg->secblob_len;
- pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len);
-
- if (ses->capabilities & CAP_UNICODE) {
- /* unicode strings must be word aligned */
- if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
- *bcc_ptr = 0;
- bcc_ptr++;
- }
- unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
- unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp);
- } else {
- /* BB: is this right? */
- ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
- }
-
- sess_data->iov[2].iov_len = (long) bcc_ptr -
- (long) sess_data->iov[2].iov_base;
-
- rc = sess_sendreceive(sess_data);
- if (rc)
- goto out_put_spnego_key;
-
- pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
- smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
-
- if (smb_buf->WordCount != 4) {
- rc = -EIO;
- cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
- goto out_put_spnego_key;
- }
-
- if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
- cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
-
- ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
- cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
-
- bytes_remaining = get_bcc(smb_buf);
- bcc_ptr = pByteArea(smb_buf);
-
- blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
- if (blob_len > bytes_remaining) {
- cifs_dbg(VFS, "bad security blob length %d\n",
- blob_len);
- rc = -EINVAL;
- goto out_put_spnego_key;
- }
- bcc_ptr += blob_len;
- bytes_remaining -= blob_len;
-
- /* BB check if Unicode and decode strings */
- if (bytes_remaining == 0) {
- /* no string area to decode, do nothing */
- } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
- /* unicode string area must be word-aligned */
- if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
- ++bcc_ptr;
- --bytes_remaining;
- }
- decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
- sess_data->nls_cp);
- } else {
- decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
- sess_data->nls_cp);
- }
-
- rc = sess_establish_session(sess_data);
-out_put_spnego_key:
- key_invalidate(spnego_key);
- key_put(spnego_key);
-out:
- sess_data->result = rc;
- sess_data->func = NULL;
- sess_free_buffer(sess_data);
- kfree_sensitive(ses->auth_key.response);
- ses->auth_key.response = NULL;
-}
-
-#endif /* ! CONFIG_CIFS_UPCALL */
-
-/*
- * The required kvec buffers have to be allocated before calling this
- * function.
- */
-static int
-_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
-{
- SESSION_SETUP_ANDX *pSMB;
- struct cifs_ses *ses = sess_data->ses;
- struct TCP_Server_Info *server = sess_data->server;
- __u32 capabilities;
- char *bcc_ptr;
-
- pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
-
- capabilities = cifs_ssetup_hdr(ses, server, pSMB);
- if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
- cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
- return -ENOSYS;
- }
-
- pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
- capabilities |= CAP_EXTENDED_SECURITY;
- pSMB->req.Capabilities |= cpu_to_le32(capabilities);
-
- bcc_ptr = sess_data->iov[2].iov_base;
- /* unicode strings must be word aligned */
- if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
- *bcc_ptr = 0;
- bcc_ptr++;
- }
- unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
-
- sess_data->iov[2].iov_len = (long) bcc_ptr -
- (long) sess_data->iov[2].iov_base;
-
- return 0;
-}
-
-static void
-sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data);
-
-static void
-sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
-{
- int rc;
- struct smb_hdr *smb_buf;
- SESSION_SETUP_ANDX *pSMB;
- struct cifs_ses *ses = sess_data->ses;
- struct TCP_Server_Info *server = sess_data->server;
- __u16 bytes_remaining;
- char *bcc_ptr;
- unsigned char *ntlmsspblob = NULL;
- u16 blob_len;
-
- cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n");
-
- /*
- * if memory allocation is successful, caller of this function
- * frees it.
- */
- ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
- if (!ses->ntlmssp) {
- rc = -ENOMEM;
- goto out;
- }
- ses->ntlmssp->sesskey_per_smbsess = false;
-
- /* wct = 12 */
- rc = sess_alloc_buffer(sess_data, 12);
- if (rc)
- goto out;
-
- pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
-
- /* Build security blob before we assemble the request */
- rc = build_ntlmssp_negotiate_blob(&ntlmsspblob,
- &blob_len, ses, server,
- sess_data->nls_cp);
- if (rc)
- goto out_free_ntlmsspblob;
-
- 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)
- goto out_free_ntlmsspblob;
-
- rc = sess_sendreceive(sess_data);
-
- pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
- smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
-
- /* If true, rc here is expected and not an error */
- if (sess_data->buf0_type != CIFS_NO_BUFFER &&
- smb_buf->Status.CifsError ==
- cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
- rc = 0;
-
- if (rc)
- goto out_free_ntlmsspblob;
-
- cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
-
- if (smb_buf->WordCount != 4) {
- rc = -EIO;
- cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
- goto out_free_ntlmsspblob;
- }
-
- ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
- cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
-
- bytes_remaining = get_bcc(smb_buf);
- bcc_ptr = pByteArea(smb_buf);
-
- blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
- if (blob_len > bytes_remaining) {
- cifs_dbg(VFS, "bad security blob length %d\n",
- blob_len);
- rc = -EINVAL;
- goto out_free_ntlmsspblob;
- }
-
- rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
-
-out_free_ntlmsspblob:
- kfree_sensitive(ntlmsspblob);
-out:
- sess_free_buffer(sess_data);
-
- if (!rc) {
- sess_data->func = sess_auth_rawntlmssp_authenticate;
- return;
- }
-
- /* Else error. Cleanup */
- kfree_sensitive(ses->auth_key.response);
- ses->auth_key.response = NULL;
- kfree_sensitive(ses->ntlmssp);
- ses->ntlmssp = NULL;
-
- sess_data->func = NULL;
- sess_data->result = rc;
-}
-
-static void
-sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
-{
- int rc;
- struct smb_hdr *smb_buf;
- SESSION_SETUP_ANDX *pSMB;
- struct cifs_ses *ses = sess_data->ses;
- struct TCP_Server_Info *server = sess_data->server;
- __u16 bytes_remaining;
- char *bcc_ptr;
- unsigned char *ntlmsspblob = NULL;
- u16 blob_len;
-
- cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
-
- /* wct = 12 */
- rc = sess_alloc_buffer(sess_data, 12);
- if (rc)
- goto out;
-
- /* Build security blob before we assemble the request */
- pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
- smb_buf = (struct smb_hdr *)pSMB;
- rc = build_ntlmssp_auth_blob(&ntlmsspblob,
- &blob_len, ses, server,
- sess_data->nls_cp);
- if (rc)
- goto out_free_ntlmsspblob;
- sess_data->iov[1].iov_len = blob_len;
- sess_data->iov[1].iov_base = ntlmsspblob;
- pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
- /*
- * Make sure that we tell the server that we are using
- * the uid that it just gave us back on the response
- * (challenge)
- */
- smb_buf->Uid = ses->Suid;
-
- rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
- if (rc)
- goto out_free_ntlmsspblob;
-
- rc = sess_sendreceive(sess_data);
- if (rc)
- goto out_free_ntlmsspblob;
-
- pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
- smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
- if (smb_buf->WordCount != 4) {
- rc = -EIO;
- cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
- goto out_free_ntlmsspblob;
- }
-
- if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
- cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
-
- if (ses->Suid != smb_buf->Uid) {
- ses->Suid = smb_buf->Uid;
- cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid);
- }
-
- bytes_remaining = get_bcc(smb_buf);
- bcc_ptr = pByteArea(smb_buf);
- blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
- if (blob_len > bytes_remaining) {
- cifs_dbg(VFS, "bad security blob length %d\n",
- blob_len);
- rc = -EINVAL;
- goto out_free_ntlmsspblob;
- }
- bcc_ptr += blob_len;
- bytes_remaining -= blob_len;
-
-
- /* BB check if Unicode and decode strings */
- if (bytes_remaining == 0) {
- /* no string area to decode, do nothing */
- } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
- /* unicode string area must be word-aligned */
- if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
- ++bcc_ptr;
- --bytes_remaining;
- }
- decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
- sess_data->nls_cp);
- } else {
- decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
- sess_data->nls_cp);
- }
-
-out_free_ntlmsspblob:
- kfree_sensitive(ntlmsspblob);
-out:
- sess_free_buffer(sess_data);
-
- if (!rc)
- rc = sess_establish_session(sess_data);
-
- /* Cleanup */
- kfree_sensitive(ses->auth_key.response);
- ses->auth_key.response = NULL;
- kfree_sensitive(ses->ntlmssp);
- ses->ntlmssp = NULL;
-
- sess_data->func = NULL;
- sess_data->result = rc;
-}
-
-static int select_sec(struct sess_data *sess_data)
-{
- int type;
- struct cifs_ses *ses = sess_data->ses;
- struct TCP_Server_Info *server = sess_data->server;
-
- type = cifs_select_sectype(server, ses->sectype);
- cifs_dbg(FYI, "sess setup type %d\n", type);
- if (type == Unspecified) {
- cifs_dbg(VFS, "Unable to select appropriate authentication method!\n");
- return -EINVAL;
- }
-
- switch (type) {
- case NTLMv2:
- sess_data->func = sess_auth_ntlmv2;
- break;
- case Kerberos:
-#ifdef CONFIG_CIFS_UPCALL
- sess_data->func = sess_auth_kerberos;
- break;
-#else
- cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
- return -ENOSYS;
-#endif /* CONFIG_CIFS_UPCALL */
- case RawNTLMSSP:
- sess_data->func = sess_auth_rawntlmssp_negotiate;
- break;
- default:
- cifs_dbg(VFS, "secType %d not supported!\n", type);
- return -ENOSYS;
- }
-
- return 0;
-}
-
-int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
- struct TCP_Server_Info *server,
- const struct nls_table *nls_cp)
-{
- int rc = 0;
- struct sess_data *sess_data;
-
- if (ses == NULL) {
- WARN(1, "%s: ses == NULL!", __func__);
- return -EINVAL;
- }
-
- sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL);
- if (!sess_data)
- return -ENOMEM;
-
- sess_data->xid = xid;
- sess_data->ses = ses;
- sess_data->server = server;
- sess_data->buf0_type = CIFS_NO_BUFFER;
- sess_data->nls_cp = (struct nls_table *) nls_cp;
-
- rc = select_sec(sess_data);
- if (rc)
- goto out;
-
- while (sess_data->func)
- sess_data->func(sess_data);
-
- /* Store result before we free sess_data */
- rc = sess_data->result;
-
-out:
- kfree_sensitive(sess_data);
- return rc;
-}
-#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */