162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *   SMB/CIFS session setup handling routines
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *   Copyright (c) International Business Machines  Corp., 2006, 2009
762306a36Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "cifspdu.h"
1262306a36Sopenharmony_ci#include "cifsglob.h"
1362306a36Sopenharmony_ci#include "cifsproto.h"
1462306a36Sopenharmony_ci#include "cifs_unicode.h"
1562306a36Sopenharmony_ci#include "cifs_debug.h"
1662306a36Sopenharmony_ci#include "ntlmssp.h"
1762306a36Sopenharmony_ci#include "nterr.h"
1862306a36Sopenharmony_ci#include <linux/utsname.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/version.h>
2162306a36Sopenharmony_ci#include "cifsfs.h"
2262306a36Sopenharmony_ci#include "cifs_spnego.h"
2362306a36Sopenharmony_ci#include "smb2proto.h"
2462306a36Sopenharmony_ci#include "fs_context.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int
2762306a36Sopenharmony_cicifs_ses_add_channel(struct cifs_ses *ses,
2862306a36Sopenharmony_ci		     struct cifs_server_iface *iface);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cibool
3162306a36Sopenharmony_ciis_server_using_iface(struct TCP_Server_Info *server,
3262306a36Sopenharmony_ci		      struct cifs_server_iface *iface)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr;
3562306a36Sopenharmony_ci	struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr;
3662306a36Sopenharmony_ci	struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr;
3762306a36Sopenharmony_ci	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (server->dstaddr.ss_family != iface->sockaddr.ss_family)
4062306a36Sopenharmony_ci		return false;
4162306a36Sopenharmony_ci	if (server->dstaddr.ss_family == AF_INET) {
4262306a36Sopenharmony_ci		if (s4->sin_addr.s_addr != i4->sin_addr.s_addr)
4362306a36Sopenharmony_ci			return false;
4462306a36Sopenharmony_ci	} else if (server->dstaddr.ss_family == AF_INET6) {
4562306a36Sopenharmony_ci		if (memcmp(&s6->sin6_addr, &i6->sin6_addr,
4662306a36Sopenharmony_ci			   sizeof(i6->sin6_addr)) != 0)
4762306a36Sopenharmony_ci			return false;
4862306a36Sopenharmony_ci	} else {
4962306a36Sopenharmony_ci		/* unknown family.. */
5062306a36Sopenharmony_ci		return false;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci	return true;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cibool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	int i;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
6062306a36Sopenharmony_ci	for (i = 0; i < ses->chan_count; i++) {
6162306a36Sopenharmony_ci		if (ses->chans[i].iface == iface) {
6262306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
6362306a36Sopenharmony_ci			return true;
6462306a36Sopenharmony_ci		}
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
6762306a36Sopenharmony_ci	return false;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* channel helper functions. assumed that chan_lock is held by caller. */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciint
7362306a36Sopenharmony_cicifs_ses_get_chan_index(struct cifs_ses *ses,
7462306a36Sopenharmony_ci			struct TCP_Server_Info *server)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	unsigned int i;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* if the channel is waiting for termination */
7962306a36Sopenharmony_ci	if (server && server->terminate)
8062306a36Sopenharmony_ci		return CIFS_INVAL_CHAN_INDEX;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	for (i = 0; i < ses->chan_count; i++) {
8362306a36Sopenharmony_ci		if (ses->chans[i].server == server)
8462306a36Sopenharmony_ci			return i;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* If we didn't find the channel, it is likely a bug */
8862306a36Sopenharmony_ci	if (server)
8962306a36Sopenharmony_ci		cifs_dbg(VFS, "unable to get chan index for server: 0x%llx",
9062306a36Sopenharmony_ci			 server->conn_id);
9162306a36Sopenharmony_ci	return CIFS_INVAL_CHAN_INDEX;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_civoid
9562306a36Sopenharmony_cicifs_chan_set_in_reconnect(struct cifs_ses *ses,
9662306a36Sopenharmony_ci			     struct TCP_Server_Info *server)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	int chan_index = cifs_ses_get_chan_index(ses, server);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (chan_index == CIFS_INVAL_CHAN_INDEX)
10162306a36Sopenharmony_ci		return;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	ses->chans[chan_index].in_reconnect = true;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_civoid
10762306a36Sopenharmony_cicifs_chan_clear_in_reconnect(struct cifs_ses *ses,
10862306a36Sopenharmony_ci			     struct TCP_Server_Info *server)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
11162306a36Sopenharmony_ci	if (chan_index == CIFS_INVAL_CHAN_INDEX)
11262306a36Sopenharmony_ci		return;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	ses->chans[chan_index].in_reconnect = false;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cibool
11862306a36Sopenharmony_cicifs_chan_in_reconnect(struct cifs_ses *ses,
11962306a36Sopenharmony_ci			  struct TCP_Server_Info *server)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
12262306a36Sopenharmony_ci	if (chan_index == CIFS_INVAL_CHAN_INDEX)
12362306a36Sopenharmony_ci		return true;	/* err on the safer side */
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return CIFS_CHAN_IN_RECONNECT(ses, chan_index);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_civoid
12962306a36Sopenharmony_cicifs_chan_set_need_reconnect(struct cifs_ses *ses,
13062306a36Sopenharmony_ci			     struct TCP_Server_Info *server)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
13362306a36Sopenharmony_ci	if (chan_index == CIFS_INVAL_CHAN_INDEX)
13462306a36Sopenharmony_ci		return;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	set_bit(chan_index, &ses->chans_need_reconnect);
13762306a36Sopenharmony_ci	cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
13862306a36Sopenharmony_ci		 chan_index, ses->chans_need_reconnect);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_civoid
14262306a36Sopenharmony_cicifs_chan_clear_need_reconnect(struct cifs_ses *ses,
14362306a36Sopenharmony_ci			       struct TCP_Server_Info *server)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
14662306a36Sopenharmony_ci	if (chan_index == CIFS_INVAL_CHAN_INDEX)
14762306a36Sopenharmony_ci		return;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	clear_bit(chan_index, &ses->chans_need_reconnect);
15062306a36Sopenharmony_ci	cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
15162306a36Sopenharmony_ci		 chan_index, ses->chans_need_reconnect);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cibool
15562306a36Sopenharmony_cicifs_chan_needs_reconnect(struct cifs_ses *ses,
15662306a36Sopenharmony_ci			  struct TCP_Server_Info *server)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
15962306a36Sopenharmony_ci	if (chan_index == CIFS_INVAL_CHAN_INDEX)
16062306a36Sopenharmony_ci		return true;	/* err on the safer side */
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cibool
16662306a36Sopenharmony_cicifs_chan_is_iface_active(struct cifs_ses *ses,
16762306a36Sopenharmony_ci			  struct TCP_Server_Info *server)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
17062306a36Sopenharmony_ci	if (chan_index == CIFS_INVAL_CHAN_INDEX)
17162306a36Sopenharmony_ci		return true;	/* err on the safer side */
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return ses->chans[chan_index].iface &&
17462306a36Sopenharmony_ci		ses->chans[chan_index].iface->is_active;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/* returns number of channels added */
17862306a36Sopenharmony_ciint cifs_try_adding_channels(struct cifs_ses *ses)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct TCP_Server_Info *server = ses->server;
18162306a36Sopenharmony_ci	int old_chan_count, new_chan_count;
18262306a36Sopenharmony_ci	int left;
18362306a36Sopenharmony_ci	int rc = 0;
18462306a36Sopenharmony_ci	int tries = 0;
18562306a36Sopenharmony_ci	size_t iface_weight = 0, iface_min_speed = 0;
18662306a36Sopenharmony_ci	struct cifs_server_iface *iface = NULL, *niface = NULL;
18762306a36Sopenharmony_ci	struct cifs_server_iface *last_iface = NULL;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	new_chan_count = old_chan_count = ses->chan_count;
19262306a36Sopenharmony_ci	left = ses->chan_max - ses->chan_count;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (left <= 0) {
19562306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
19662306a36Sopenharmony_ci		cifs_dbg(FYI,
19762306a36Sopenharmony_ci			 "ses already at max_channels (%zu), nothing to open\n",
19862306a36Sopenharmony_ci			 ses->chan_max);
19962306a36Sopenharmony_ci		return 0;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (server->dialect < SMB30_PROT_ID) {
20362306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
20462306a36Sopenharmony_ci		cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
20562306a36Sopenharmony_ci		return 0;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
20962306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
21062306a36Sopenharmony_ci		cifs_server_dbg(VFS, "no multichannel support\n");
21162306a36Sopenharmony_ci		return 0;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	while (left > 0) {
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		tries++;
21862306a36Sopenharmony_ci		if (tries > 3*ses->chan_max) {
21962306a36Sopenharmony_ci			cifs_dbg(VFS, "too many channel open attempts (%d channels left to open)\n",
22062306a36Sopenharmony_ci				 left);
22162306a36Sopenharmony_ci			break;
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		spin_lock(&ses->iface_lock);
22562306a36Sopenharmony_ci		if (!ses->iface_count) {
22662306a36Sopenharmony_ci			spin_unlock(&ses->iface_lock);
22762306a36Sopenharmony_ci			cifs_dbg(VFS, "server %s does not advertise interfaces\n",
22862306a36Sopenharmony_ci				      ses->server->hostname);
22962306a36Sopenharmony_ci			break;
23062306a36Sopenharmony_ci		}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		if (!iface)
23362306a36Sopenharmony_ci			iface = list_first_entry(&ses->iface_list, struct cifs_server_iface,
23462306a36Sopenharmony_ci						 iface_head);
23562306a36Sopenharmony_ci		last_iface = list_last_entry(&ses->iface_list, struct cifs_server_iface,
23662306a36Sopenharmony_ci					     iface_head);
23762306a36Sopenharmony_ci		iface_min_speed = last_iface->speed;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		list_for_each_entry_safe_from(iface, niface, &ses->iface_list,
24062306a36Sopenharmony_ci				    iface_head) {
24162306a36Sopenharmony_ci			/* do not mix rdma and non-rdma interfaces */
24262306a36Sopenharmony_ci			if (iface->rdma_capable != ses->server->rdma)
24362306a36Sopenharmony_ci				continue;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci			/* skip ifaces that are unusable */
24662306a36Sopenharmony_ci			if (!iface->is_active ||
24762306a36Sopenharmony_ci			    (is_ses_using_iface(ses, iface) &&
24862306a36Sopenharmony_ci			     !iface->rss_capable))
24962306a36Sopenharmony_ci				continue;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci			/* check if we already allocated enough channels */
25262306a36Sopenharmony_ci			iface_weight = iface->speed / iface_min_speed;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci			if (iface->weight_fulfilled >= iface_weight)
25562306a36Sopenharmony_ci				continue;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci			/* take ref before unlock */
25862306a36Sopenharmony_ci			kref_get(&iface->refcount);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci			spin_unlock(&ses->iface_lock);
26162306a36Sopenharmony_ci			rc = cifs_ses_add_channel(ses, iface);
26262306a36Sopenharmony_ci			spin_lock(&ses->iface_lock);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci			if (rc) {
26562306a36Sopenharmony_ci				cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n",
26662306a36Sopenharmony_ci					 &iface->sockaddr,
26762306a36Sopenharmony_ci					 rc);
26862306a36Sopenharmony_ci				kref_put(&iface->refcount, release_iface);
26962306a36Sopenharmony_ci				/* failure to add chan should increase weight */
27062306a36Sopenharmony_ci				iface->weight_fulfilled++;
27162306a36Sopenharmony_ci				continue;
27262306a36Sopenharmony_ci			}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci			iface->num_channels++;
27562306a36Sopenharmony_ci			iface->weight_fulfilled++;
27662306a36Sopenharmony_ci			cifs_dbg(VFS, "successfully opened new channel on iface:%pIS\n",
27762306a36Sopenharmony_ci				 &iface->sockaddr);
27862306a36Sopenharmony_ci			break;
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		/* reached end of list. reset weight_fulfilled and start over */
28262306a36Sopenharmony_ci		if (list_entry_is_head(iface, &ses->iface_list, iface_head)) {
28362306a36Sopenharmony_ci			list_for_each_entry(iface, &ses->iface_list, iface_head)
28462306a36Sopenharmony_ci				iface->weight_fulfilled = 0;
28562306a36Sopenharmony_ci			spin_unlock(&ses->iface_lock);
28662306a36Sopenharmony_ci			iface = NULL;
28762306a36Sopenharmony_ci			continue;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci		spin_unlock(&ses->iface_lock);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		left--;
29262306a36Sopenharmony_ci		new_chan_count++;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return new_chan_count - old_chan_count;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci/*
29962306a36Sopenharmony_ci * called when multichannel is disabled by the server.
30062306a36Sopenharmony_ci * this always gets called from smb2_reconnect
30162306a36Sopenharmony_ci * and cannot get called in parallel threads.
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_civoid
30462306a36Sopenharmony_cicifs_disable_secondary_channels(struct cifs_ses *ses)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	int i, chan_count;
30762306a36Sopenharmony_ci	struct TCP_Server_Info *server;
30862306a36Sopenharmony_ci	struct cifs_server_iface *iface;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
31162306a36Sopenharmony_ci	chan_count = ses->chan_count;
31262306a36Sopenharmony_ci	if (chan_count == 1)
31362306a36Sopenharmony_ci		goto done;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	ses->chan_count = 1;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* for all secondary channels reset the need reconnect bit */
31862306a36Sopenharmony_ci	ses->chans_need_reconnect &= 1;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	for (i = 1; i < chan_count; i++) {
32162306a36Sopenharmony_ci		iface = ses->chans[i].iface;
32262306a36Sopenharmony_ci		server = ses->chans[i].server;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		/*
32562306a36Sopenharmony_ci		 * remove these references first, since we need to unlock
32662306a36Sopenharmony_ci		 * the chan_lock here, since iface_lock is a higher lock
32762306a36Sopenharmony_ci		 */
32862306a36Sopenharmony_ci		ses->chans[i].iface = NULL;
32962306a36Sopenharmony_ci		ses->chans[i].server = NULL;
33062306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		if (iface) {
33362306a36Sopenharmony_ci			spin_lock(&ses->iface_lock);
33462306a36Sopenharmony_ci			kref_put(&iface->refcount, release_iface);
33562306a36Sopenharmony_ci			iface->num_channels--;
33662306a36Sopenharmony_ci			if (iface->weight_fulfilled)
33762306a36Sopenharmony_ci				iface->weight_fulfilled--;
33862306a36Sopenharmony_ci			spin_unlock(&ses->iface_lock);
33962306a36Sopenharmony_ci		}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		if (server) {
34262306a36Sopenharmony_ci			if (!server->terminate) {
34362306a36Sopenharmony_ci				server->terminate = true;
34462306a36Sopenharmony_ci				cifs_signal_cifsd_for_reconnect(server, false);
34562306a36Sopenharmony_ci			}
34662306a36Sopenharmony_ci			cifs_put_tcp_session(server, false);
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cidone:
35362306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/*
35762306a36Sopenharmony_ci * update the iface for the channel if necessary.
35862306a36Sopenharmony_ci * will return 0 when iface is updated, 1 if removed, 2 otherwise
35962306a36Sopenharmony_ci * Must be called with chan_lock held.
36062306a36Sopenharmony_ci */
36162306a36Sopenharmony_ciint
36262306a36Sopenharmony_cicifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	unsigned int chan_index;
36562306a36Sopenharmony_ci	size_t iface_weight = 0, iface_min_speed = 0;
36662306a36Sopenharmony_ci	struct cifs_server_iface *iface = NULL;
36762306a36Sopenharmony_ci	struct cifs_server_iface *old_iface = NULL;
36862306a36Sopenharmony_ci	struct cifs_server_iface *last_iface = NULL;
36962306a36Sopenharmony_ci	struct sockaddr_storage ss;
37062306a36Sopenharmony_ci	int rc = 0;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
37362306a36Sopenharmony_ci	chan_index = cifs_ses_get_chan_index(ses, server);
37462306a36Sopenharmony_ci	if (chan_index == CIFS_INVAL_CHAN_INDEX) {
37562306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
37662306a36Sopenharmony_ci		return 0;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (ses->chans[chan_index].iface) {
38062306a36Sopenharmony_ci		old_iface = ses->chans[chan_index].iface;
38162306a36Sopenharmony_ci		if (old_iface->is_active) {
38262306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
38362306a36Sopenharmony_ci			return 1;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
38962306a36Sopenharmony_ci	ss = server->dstaddr;
39062306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	spin_lock(&ses->iface_lock);
39362306a36Sopenharmony_ci	if (!ses->iface_count) {
39462306a36Sopenharmony_ci		spin_unlock(&ses->iface_lock);
39562306a36Sopenharmony_ci		cifs_dbg(VFS, "server %s does not advertise interfaces\n", ses->server->hostname);
39662306a36Sopenharmony_ci		return 0;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	last_iface = list_last_entry(&ses->iface_list, struct cifs_server_iface,
40062306a36Sopenharmony_ci				     iface_head);
40162306a36Sopenharmony_ci	iface_min_speed = last_iface->speed;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* then look for a new one */
40462306a36Sopenharmony_ci	list_for_each_entry(iface, &ses->iface_list, iface_head) {
40562306a36Sopenharmony_ci		if (!chan_index) {
40662306a36Sopenharmony_ci			/* if we're trying to get the updated iface for primary channel */
40762306a36Sopenharmony_ci			if (!cifs_match_ipaddr((struct sockaddr *) &ss,
40862306a36Sopenharmony_ci					       (struct sockaddr *) &iface->sockaddr))
40962306a36Sopenharmony_ci				continue;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci			kref_get(&iface->refcount);
41262306a36Sopenharmony_ci			break;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		/* do not mix rdma and non-rdma interfaces */
41662306a36Sopenharmony_ci		if (iface->rdma_capable != server->rdma)
41762306a36Sopenharmony_ci			continue;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		if (!iface->is_active ||
42062306a36Sopenharmony_ci		    (is_ses_using_iface(ses, iface) &&
42162306a36Sopenharmony_ci		     !iface->rss_capable)) {
42262306a36Sopenharmony_ci			continue;
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		/* check if we already allocated enough channels */
42662306a36Sopenharmony_ci		iface_weight = iface->speed / iface_min_speed;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		if (iface->weight_fulfilled >= iface_weight)
42962306a36Sopenharmony_ci			continue;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		kref_get(&iface->refcount);
43262306a36Sopenharmony_ci		break;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (list_entry_is_head(iface, &ses->iface_list, iface_head)) {
43662306a36Sopenharmony_ci		rc = 1;
43762306a36Sopenharmony_ci		iface = NULL;
43862306a36Sopenharmony_ci		cifs_dbg(FYI, "unable to find a suitable iface\n");
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (!chan_index && !iface) {
44262306a36Sopenharmony_ci		cifs_dbg(FYI, "unable to get the interface matching: %pIS\n",
44362306a36Sopenharmony_ci			 &ss);
44462306a36Sopenharmony_ci		spin_unlock(&ses->iface_lock);
44562306a36Sopenharmony_ci		return 0;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* now drop the ref to the current iface */
44962306a36Sopenharmony_ci	if (old_iface && iface) {
45062306a36Sopenharmony_ci		cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n",
45162306a36Sopenharmony_ci			 &old_iface->sockaddr,
45262306a36Sopenharmony_ci			 &iface->sockaddr);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		old_iface->num_channels--;
45562306a36Sopenharmony_ci		if (old_iface->weight_fulfilled)
45662306a36Sopenharmony_ci			old_iface->weight_fulfilled--;
45762306a36Sopenharmony_ci		iface->num_channels++;
45862306a36Sopenharmony_ci		iface->weight_fulfilled++;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		kref_put(&old_iface->refcount, release_iface);
46162306a36Sopenharmony_ci	} else if (old_iface) {
46262306a36Sopenharmony_ci		cifs_dbg(FYI, "releasing ref to iface: %pIS\n",
46362306a36Sopenharmony_ci			 &old_iface->sockaddr);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		old_iface->num_channels--;
46662306a36Sopenharmony_ci		if (old_iface->weight_fulfilled)
46762306a36Sopenharmony_ci			old_iface->weight_fulfilled--;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		kref_put(&old_iface->refcount, release_iface);
47062306a36Sopenharmony_ci	} else if (!chan_index) {
47162306a36Sopenharmony_ci		/* special case: update interface for primary channel */
47262306a36Sopenharmony_ci		cifs_dbg(FYI, "referencing primary channel iface: %pIS\n",
47362306a36Sopenharmony_ci			 &iface->sockaddr);
47462306a36Sopenharmony_ci		iface->num_channels++;
47562306a36Sopenharmony_ci		iface->weight_fulfilled++;
47662306a36Sopenharmony_ci	} else {
47762306a36Sopenharmony_ci		WARN_ON(!iface);
47862306a36Sopenharmony_ci		cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr);
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	spin_unlock(&ses->iface_lock);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
48362306a36Sopenharmony_ci	chan_index = cifs_ses_get_chan_index(ses, server);
48462306a36Sopenharmony_ci	if (chan_index == CIFS_INVAL_CHAN_INDEX) {
48562306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
48662306a36Sopenharmony_ci		return 0;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	ses->chans[chan_index].iface = iface;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	/* No iface is found. if secondary chan, drop connection */
49262306a36Sopenharmony_ci	if (!iface && SERVER_IS_CHAN(server))
49362306a36Sopenharmony_ci		ses->chans[chan_index].server = NULL;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (!iface && SERVER_IS_CHAN(server))
49862306a36Sopenharmony_ci		cifs_put_tcp_session(server, false);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return rc;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci/*
50462306a36Sopenharmony_ci * If server is a channel of ses, return the corresponding enclosing
50562306a36Sopenharmony_ci * cifs_chan otherwise return NULL.
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_cistruct cifs_chan *
50862306a36Sopenharmony_cicifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	int i;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
51362306a36Sopenharmony_ci	for (i = 0; i < ses->chan_count; i++) {
51462306a36Sopenharmony_ci		if (ses->chans[i].server == server) {
51562306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
51662306a36Sopenharmony_ci			return &ses->chans[i];
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
52062306a36Sopenharmony_ci	return NULL;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int
52462306a36Sopenharmony_cicifs_ses_add_channel(struct cifs_ses *ses,
52562306a36Sopenharmony_ci		     struct cifs_server_iface *iface)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct TCP_Server_Info *chan_server;
52862306a36Sopenharmony_ci	struct cifs_chan *chan;
52962306a36Sopenharmony_ci	struct smb3_fs_context *ctx;
53062306a36Sopenharmony_ci	static const char unc_fmt[] = "\\%s\\foo";
53162306a36Sopenharmony_ci	struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
53262306a36Sopenharmony_ci	struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
53362306a36Sopenharmony_ci	size_t len;
53462306a36Sopenharmony_ci	int rc;
53562306a36Sopenharmony_ci	unsigned int xid = get_xid();
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (iface->sockaddr.ss_family == AF_INET)
53862306a36Sopenharmony_ci		cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI4)\n",
53962306a36Sopenharmony_ci			 ses, iface->speed, iface->rdma_capable ? "yes" : "no",
54062306a36Sopenharmony_ci			 &ipv4->sin_addr);
54162306a36Sopenharmony_ci	else
54262306a36Sopenharmony_ci		cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI6)\n",
54362306a36Sopenharmony_ci			 ses, iface->speed, iface->rdma_capable ? "yes" : "no",
54462306a36Sopenharmony_ci			 &ipv6->sin6_addr);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/*
54762306a36Sopenharmony_ci	 * Setup a ctx with mostly the same info as the existing
54862306a36Sopenharmony_ci	 * session and overwrite it with the requested iface data.
54962306a36Sopenharmony_ci	 *
55062306a36Sopenharmony_ci	 * We need to setup at least the fields used for negprot and
55162306a36Sopenharmony_ci	 * sesssetup.
55262306a36Sopenharmony_ci	 *
55362306a36Sopenharmony_ci	 * We only need the ctx here, so we can reuse memory from
55462306a36Sopenharmony_ci	 * the session and server without caring about memory
55562306a36Sopenharmony_ci	 * management.
55662306a36Sopenharmony_ci	 */
55762306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
55862306a36Sopenharmony_ci	if (!ctx) {
55962306a36Sopenharmony_ci		rc = -ENOMEM;
56062306a36Sopenharmony_ci		goto out_free_xid;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	/* Always make new connection for now (TODO?) */
56462306a36Sopenharmony_ci	ctx->nosharesock = true;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/* Auth */
56762306a36Sopenharmony_ci	ctx->domainauto = ses->domainAuto;
56862306a36Sopenharmony_ci	ctx->domainname = ses->domainName;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* no hostname for extra channels */
57162306a36Sopenharmony_ci	ctx->server_hostname = "";
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	ctx->username = ses->user_name;
57462306a36Sopenharmony_ci	ctx->password = ses->password;
57562306a36Sopenharmony_ci	ctx->sectype = ses->sectype;
57662306a36Sopenharmony_ci	ctx->sign = ses->sign;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/* UNC and paths */
57962306a36Sopenharmony_ci	/* XXX: Use ses->server->hostname? */
58062306a36Sopenharmony_ci	len = sizeof(unc_fmt) + SERVER_NAME_LEN_WITH_NULL;
58162306a36Sopenharmony_ci	ctx->UNC = kzalloc(len, GFP_KERNEL);
58262306a36Sopenharmony_ci	if (!ctx->UNC) {
58362306a36Sopenharmony_ci		rc = -ENOMEM;
58462306a36Sopenharmony_ci		goto out_free_ctx;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci	scnprintf(ctx->UNC, len, unc_fmt, ses->ip_addr);
58762306a36Sopenharmony_ci	ctx->prepath = "";
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* Reuse same version as master connection */
59062306a36Sopenharmony_ci	ctx->vals = ses->server->vals;
59162306a36Sopenharmony_ci	ctx->ops = ses->server->ops;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	ctx->noblocksnd = ses->server->noblocksnd;
59462306a36Sopenharmony_ci	ctx->noautotune = ses->server->noautotune;
59562306a36Sopenharmony_ci	ctx->sockopt_tcp_nodelay = ses->server->tcp_nodelay;
59662306a36Sopenharmony_ci	ctx->echo_interval = ses->server->echo_interval / HZ;
59762306a36Sopenharmony_ci	ctx->max_credits = ses->server->max_credits;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	/*
60062306a36Sopenharmony_ci	 * This will be used for encoding/decoding user/domain/pw
60162306a36Sopenharmony_ci	 * during sess setup auth.
60262306a36Sopenharmony_ci	 */
60362306a36Sopenharmony_ci	ctx->local_nls = ses->local_nls;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	/* Use RDMA if possible */
60662306a36Sopenharmony_ci	ctx->rdma = iface->rdma_capable;
60762306a36Sopenharmony_ci	memcpy(&ctx->dstaddr, &iface->sockaddr, sizeof(ctx->dstaddr));
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	/* reuse master con client guid */
61062306a36Sopenharmony_ci	memcpy(&ctx->client_guid, ses->server->client_guid,
61162306a36Sopenharmony_ci	       sizeof(ctx->client_guid));
61262306a36Sopenharmony_ci	ctx->use_client_guid = true;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	chan_server = cifs_get_tcp_session(ctx, ses->server);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
61762306a36Sopenharmony_ci	chan = &ses->chans[ses->chan_count];
61862306a36Sopenharmony_ci	chan->server = chan_server;
61962306a36Sopenharmony_ci	if (IS_ERR(chan->server)) {
62062306a36Sopenharmony_ci		rc = PTR_ERR(chan->server);
62162306a36Sopenharmony_ci		chan->server = NULL;
62262306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
62362306a36Sopenharmony_ci		goto out;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci	chan->iface = iface;
62662306a36Sopenharmony_ci	ses->chan_count++;
62762306a36Sopenharmony_ci	atomic_set(&ses->chan_seq, 0);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Mark this channel as needing connect/setup */
63062306a36Sopenharmony_ci	cifs_chan_set_need_reconnect(ses, chan->server);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	mutex_lock(&ses->session_mutex);
63562306a36Sopenharmony_ci	/*
63662306a36Sopenharmony_ci	 * We need to allocate the server crypto now as we will need
63762306a36Sopenharmony_ci	 * to sign packets before we generate the channel signing key
63862306a36Sopenharmony_ci	 * (we sign with the session key)
63962306a36Sopenharmony_ci	 */
64062306a36Sopenharmony_ci	rc = smb311_crypto_shash_allocate(chan->server);
64162306a36Sopenharmony_ci	if (rc) {
64262306a36Sopenharmony_ci		cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
64362306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
64462306a36Sopenharmony_ci		goto out;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	rc = cifs_negotiate_protocol(xid, ses, chan->server);
64862306a36Sopenharmony_ci	if (!rc)
64962306a36Sopenharmony_ci		rc = cifs_setup_session(xid, ses, chan->server, ses->local_nls);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	mutex_unlock(&ses->session_mutex);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ciout:
65462306a36Sopenharmony_ci	if (rc && chan->server) {
65562306a36Sopenharmony_ci		cifs_put_tcp_session(chan->server, 0);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		/* we rely on all bits beyond chan_count to be clear */
66062306a36Sopenharmony_ci		cifs_chan_clear_need_reconnect(ses, chan->server);
66162306a36Sopenharmony_ci		ses->chan_count--;
66262306a36Sopenharmony_ci		/*
66362306a36Sopenharmony_ci		 * chan_count should never reach 0 as at least the primary
66462306a36Sopenharmony_ci		 * channel is always allocated
66562306a36Sopenharmony_ci		 */
66662306a36Sopenharmony_ci		WARN_ON(ses->chan_count < 1);
66762306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	kfree(ctx->UNC);
67162306a36Sopenharmony_ciout_free_ctx:
67262306a36Sopenharmony_ci	kfree(ctx);
67362306a36Sopenharmony_ciout_free_xid:
67462306a36Sopenharmony_ci	free_xid(xid);
67562306a36Sopenharmony_ci	return rc;
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
67962306a36Sopenharmony_cistatic __u32 cifs_ssetup_hdr(struct cifs_ses *ses,
68062306a36Sopenharmony_ci			     struct TCP_Server_Info *server,
68162306a36Sopenharmony_ci			     SESSION_SETUP_ANDX *pSMB)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	__u32 capabilities = 0;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* init fields common to all four types of SessSetup */
68662306a36Sopenharmony_ci	/* Note that offsets for first seven fields in req struct are same  */
68762306a36Sopenharmony_ci	/*	in CIFS Specs so does not matter which of 3 forms of struct */
68862306a36Sopenharmony_ci	/*	that we use in next few lines                               */
68962306a36Sopenharmony_ci	/* Note that header is initialized to zero in header_assemble */
69062306a36Sopenharmony_ci	pSMB->req.AndXCommand = 0xFF;
69162306a36Sopenharmony_ci	pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32,
69262306a36Sopenharmony_ci					CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4,
69362306a36Sopenharmony_ci					USHRT_MAX));
69462306a36Sopenharmony_ci	pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq);
69562306a36Sopenharmony_ci	pSMB->req.VcNumber = cpu_to_le16(1);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* BB verify whether signing required on neg or just on auth frame
70062306a36Sopenharmony_ci	   (and NTLM case) */
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
70362306a36Sopenharmony_ci			CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (server->sign)
70662306a36Sopenharmony_ci		pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (ses->capabilities & CAP_UNICODE) {
70962306a36Sopenharmony_ci		pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE;
71062306a36Sopenharmony_ci		capabilities |= CAP_UNICODE;
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci	if (ses->capabilities & CAP_STATUS32) {
71362306a36Sopenharmony_ci		pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS;
71462306a36Sopenharmony_ci		capabilities |= CAP_STATUS32;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci	if (ses->capabilities & CAP_DFS) {
71762306a36Sopenharmony_ci		pSMB->req.hdr.Flags2 |= SMBFLG2_DFS;
71862306a36Sopenharmony_ci		capabilities |= CAP_DFS;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci	if (ses->capabilities & CAP_UNIX)
72162306a36Sopenharmony_ci		capabilities |= CAP_UNIX;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	return capabilities;
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic void
72762306a36Sopenharmony_ciunicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	char *bcc_ptr = *pbcc_area;
73062306a36Sopenharmony_ci	int bytes_ret = 0;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/* Copy OS version */
73362306a36Sopenharmony_ci	bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32,
73462306a36Sopenharmony_ci				    nls_cp);
73562306a36Sopenharmony_ci	bcc_ptr += 2 * bytes_ret;
73662306a36Sopenharmony_ci	bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release,
73762306a36Sopenharmony_ci				    32, nls_cp);
73862306a36Sopenharmony_ci	bcc_ptr += 2 * bytes_ret;
73962306a36Sopenharmony_ci	bcc_ptr += 2; /* trailing null */
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
74262306a36Sopenharmony_ci				    32, nls_cp);
74362306a36Sopenharmony_ci	bcc_ptr += 2 * bytes_ret;
74462306a36Sopenharmony_ci	bcc_ptr += 2; /* trailing null */
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	*pbcc_area = bcc_ptr;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses,
75062306a36Sopenharmony_ci				   const struct nls_table *nls_cp)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	char *bcc_ptr = *pbcc_area;
75362306a36Sopenharmony_ci	int bytes_ret = 0;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* copy domain */
75662306a36Sopenharmony_ci	if (ses->domainName == NULL) {
75762306a36Sopenharmony_ci		/* Sending null domain better than using a bogus domain name (as
75862306a36Sopenharmony_ci		we did briefly in 2.6.18) since server will use its default */
75962306a36Sopenharmony_ci		*bcc_ptr = 0;
76062306a36Sopenharmony_ci		*(bcc_ptr+1) = 0;
76162306a36Sopenharmony_ci		bytes_ret = 0;
76262306a36Sopenharmony_ci	} else
76362306a36Sopenharmony_ci		bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName,
76462306a36Sopenharmony_ci					    CIFS_MAX_DOMAINNAME_LEN, nls_cp);
76562306a36Sopenharmony_ci	bcc_ptr += 2 * bytes_ret;
76662306a36Sopenharmony_ci	bcc_ptr += 2;  /* account for null terminator */
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	*pbcc_area = bcc_ptr;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
77262306a36Sopenharmony_ci				   const struct nls_table *nls_cp)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	char *bcc_ptr = *pbcc_area;
77562306a36Sopenharmony_ci	int bytes_ret = 0;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* BB FIXME add check that strings total less
77862306a36Sopenharmony_ci	than 335 or will need to send them as arrays */
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/* copy user */
78162306a36Sopenharmony_ci	if (ses->user_name == NULL) {
78262306a36Sopenharmony_ci		/* null user mount */
78362306a36Sopenharmony_ci		*bcc_ptr = 0;
78462306a36Sopenharmony_ci		*(bcc_ptr+1) = 0;
78562306a36Sopenharmony_ci	} else {
78662306a36Sopenharmony_ci		bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name,
78762306a36Sopenharmony_ci					    CIFS_MAX_USERNAME_LEN, nls_cp);
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci	bcc_ptr += 2 * bytes_ret;
79062306a36Sopenharmony_ci	bcc_ptr += 2; /* account for null termination */
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	unicode_domain_string(&bcc_ptr, ses, nls_cp);
79362306a36Sopenharmony_ci	unicode_oslm_strings(&bcc_ptr, nls_cp);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	*pbcc_area = bcc_ptr;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
79962306a36Sopenharmony_ci				 const struct nls_table *nls_cp)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	char *bcc_ptr = *pbcc_area;
80262306a36Sopenharmony_ci	int len;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	/* copy user */
80562306a36Sopenharmony_ci	/* BB what about null user mounts - check that we do this BB */
80662306a36Sopenharmony_ci	/* copy user */
80762306a36Sopenharmony_ci	if (ses->user_name != NULL) {
80862306a36Sopenharmony_ci		len = strscpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN);
80962306a36Sopenharmony_ci		if (WARN_ON_ONCE(len < 0))
81062306a36Sopenharmony_ci			len = CIFS_MAX_USERNAME_LEN - 1;
81162306a36Sopenharmony_ci		bcc_ptr += len;
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci	/* else null user mount */
81462306a36Sopenharmony_ci	*bcc_ptr = 0;
81562306a36Sopenharmony_ci	bcc_ptr++; /* account for null termination */
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	/* copy domain */
81862306a36Sopenharmony_ci	if (ses->domainName != NULL) {
81962306a36Sopenharmony_ci		len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
82062306a36Sopenharmony_ci		if (WARN_ON_ONCE(len < 0))
82162306a36Sopenharmony_ci			len = CIFS_MAX_DOMAINNAME_LEN - 1;
82262306a36Sopenharmony_ci		bcc_ptr += len;
82362306a36Sopenharmony_ci	} /* else we will send a null domain name
82462306a36Sopenharmony_ci	     so the server will default to its own domain */
82562306a36Sopenharmony_ci	*bcc_ptr = 0;
82662306a36Sopenharmony_ci	bcc_ptr++;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	/* BB check for overflow here */
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	strcpy(bcc_ptr, "Linux version ");
83162306a36Sopenharmony_ci	bcc_ptr += strlen("Linux version ");
83262306a36Sopenharmony_ci	strcpy(bcc_ptr, init_utsname()->release);
83362306a36Sopenharmony_ci	bcc_ptr += strlen(init_utsname()->release) + 1;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
83662306a36Sopenharmony_ci	bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	*pbcc_area = bcc_ptr;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic void
84262306a36Sopenharmony_cidecode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses,
84362306a36Sopenharmony_ci		      const struct nls_table *nls_cp)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	int len;
84662306a36Sopenharmony_ci	char *data = *pbcc_area;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	cifs_dbg(FYI, "bleft %d\n", bleft);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	kfree(ses->serverOS);
85162306a36Sopenharmony_ci	ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
85262306a36Sopenharmony_ci	cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS);
85362306a36Sopenharmony_ci	len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;
85462306a36Sopenharmony_ci	data += len;
85562306a36Sopenharmony_ci	bleft -= len;
85662306a36Sopenharmony_ci	if (bleft <= 0)
85762306a36Sopenharmony_ci		return;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	kfree(ses->serverNOS);
86062306a36Sopenharmony_ci	ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
86162306a36Sopenharmony_ci	cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS);
86262306a36Sopenharmony_ci	len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;
86362306a36Sopenharmony_ci	data += len;
86462306a36Sopenharmony_ci	bleft -= len;
86562306a36Sopenharmony_ci	if (bleft <= 0)
86662306a36Sopenharmony_ci		return;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	kfree(ses->serverDomain);
86962306a36Sopenharmony_ci	ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
87062306a36Sopenharmony_ci	cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	return;
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic void decode_ascii_ssetup(char **pbcc_area, __u16 bleft,
87662306a36Sopenharmony_ci				struct cifs_ses *ses,
87762306a36Sopenharmony_ci				const struct nls_table *nls_cp)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	int len;
88062306a36Sopenharmony_ci	char *bcc_ptr = *pbcc_area;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	len = strnlen(bcc_ptr, bleft);
88562306a36Sopenharmony_ci	if (len >= bleft)
88662306a36Sopenharmony_ci		return;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	kfree(ses->serverOS);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	ses->serverOS = kmalloc(len + 1, GFP_KERNEL);
89162306a36Sopenharmony_ci	if (ses->serverOS) {
89262306a36Sopenharmony_ci		memcpy(ses->serverOS, bcc_ptr, len);
89362306a36Sopenharmony_ci		ses->serverOS[len] = 0;
89462306a36Sopenharmony_ci		if (strncmp(ses->serverOS, "OS/2", 4) == 0)
89562306a36Sopenharmony_ci			cifs_dbg(FYI, "OS/2 server\n");
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	bcc_ptr += len + 1;
89962306a36Sopenharmony_ci	bleft -= len + 1;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	len = strnlen(bcc_ptr, bleft);
90262306a36Sopenharmony_ci	if (len >= bleft)
90362306a36Sopenharmony_ci		return;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	kfree(ses->serverNOS);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	ses->serverNOS = kmalloc(len + 1, GFP_KERNEL);
90862306a36Sopenharmony_ci	if (ses->serverNOS) {
90962306a36Sopenharmony_ci		memcpy(ses->serverNOS, bcc_ptr, len);
91062306a36Sopenharmony_ci		ses->serverNOS[len] = 0;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	bcc_ptr += len + 1;
91462306a36Sopenharmony_ci	bleft -= len + 1;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	len = strnlen(bcc_ptr, bleft);
91762306a36Sopenharmony_ci	if (len > bleft)
91862306a36Sopenharmony_ci		return;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	/* No domain field in LANMAN case. Domain is
92162306a36Sopenharmony_ci	   returned by old servers in the SMB negprot response */
92262306a36Sopenharmony_ci	/* BB For newer servers which do not support Unicode,
92362306a36Sopenharmony_ci	   but thus do return domain here we could add parsing
92462306a36Sopenharmony_ci	   for it later, but it is not very important */
92562306a36Sopenharmony_ci	cifs_dbg(FYI, "ascii: bytes left %d\n", bleft);
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ciint decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
93062306a36Sopenharmony_ci				    struct cifs_ses *ses)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	unsigned int tioffset; /* challenge message target info area */
93362306a36Sopenharmony_ci	unsigned int tilen; /* challenge message target info area length  */
93462306a36Sopenharmony_ci	CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr;
93562306a36Sopenharmony_ci	__u32 server_flags;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	if (blob_len < sizeof(CHALLENGE_MESSAGE)) {
93862306a36Sopenharmony_ci		cifs_dbg(VFS, "challenge blob len %d too small\n", blob_len);
93962306a36Sopenharmony_ci		return -EINVAL;
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (memcmp(pblob->Signature, "NTLMSSP", 8)) {
94362306a36Sopenharmony_ci		cifs_dbg(VFS, "blob signature incorrect %s\n",
94462306a36Sopenharmony_ci			 pblob->Signature);
94562306a36Sopenharmony_ci		return -EINVAL;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci	if (pblob->MessageType != NtLmChallenge) {
94862306a36Sopenharmony_ci		cifs_dbg(VFS, "Incorrect message type %d\n",
94962306a36Sopenharmony_ci			 pblob->MessageType);
95062306a36Sopenharmony_ci		return -EINVAL;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	server_flags = le32_to_cpu(pblob->NegotiateFlags);
95462306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: negotiate=0x%08x challenge=0x%08x\n", __func__,
95562306a36Sopenharmony_ci		 ses->ntlmssp->client_flags, server_flags);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	if ((ses->ntlmssp->client_flags & (NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN)) &&
95862306a36Sopenharmony_ci	    (!(server_flags & NTLMSSP_NEGOTIATE_56) && !(server_flags & NTLMSSP_NEGOTIATE_128))) {
95962306a36Sopenharmony_ci		cifs_dbg(VFS, "%s: requested signing/encryption but server did not return either 56-bit or 128-bit session key size\n",
96062306a36Sopenharmony_ci			 __func__);
96162306a36Sopenharmony_ci		return -EINVAL;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci	if (!(server_flags & NTLMSSP_NEGOTIATE_NTLM) && !(server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) {
96462306a36Sopenharmony_ci		cifs_dbg(VFS, "%s: server does not seem to support either NTLMv1 or NTLMv2\n", __func__);
96562306a36Sopenharmony_ci		return -EINVAL;
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci	if (ses->server->sign && !(server_flags & NTLMSSP_NEGOTIATE_SIGN)) {
96862306a36Sopenharmony_ci		cifs_dbg(VFS, "%s: forced packet signing but server does not seem to support it\n",
96962306a36Sopenharmony_ci			 __func__);
97062306a36Sopenharmony_ci		return -EOPNOTSUPP;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci	if ((ses->ntlmssp->client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) &&
97362306a36Sopenharmony_ci	    !(server_flags & NTLMSSP_NEGOTIATE_KEY_XCH))
97462306a36Sopenharmony_ci		pr_warn_once("%s: authentication has been weakened as server does not support key exchange\n",
97562306a36Sopenharmony_ci			     __func__);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	ses->ntlmssp->server_flags = server_flags;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE);
98062306a36Sopenharmony_ci	/* In particular we can examine sign flags */
98162306a36Sopenharmony_ci	/* BB spec says that if AvId field of MsvAvTimestamp is populated then
98262306a36Sopenharmony_ci		we must set the MIC field of the AUTHENTICATE_MESSAGE */
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset);
98562306a36Sopenharmony_ci	tilen = le16_to_cpu(pblob->TargetInfoArray.Length);
98662306a36Sopenharmony_ci	if (tioffset > blob_len || tioffset + tilen > blob_len) {
98762306a36Sopenharmony_ci		cifs_dbg(VFS, "tioffset + tilen too high %u + %u\n",
98862306a36Sopenharmony_ci			 tioffset, tilen);
98962306a36Sopenharmony_ci		return -EINVAL;
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci	if (tilen) {
99262306a36Sopenharmony_ci		kfree_sensitive(ses->auth_key.response);
99362306a36Sopenharmony_ci		ses->auth_key.response = kmemdup(bcc_ptr + tioffset, tilen,
99462306a36Sopenharmony_ci						 GFP_KERNEL);
99562306a36Sopenharmony_ci		if (!ses->auth_key.response) {
99662306a36Sopenharmony_ci			cifs_dbg(VFS, "Challenge target info alloc failure\n");
99762306a36Sopenharmony_ci			return -ENOMEM;
99862306a36Sopenharmony_ci		}
99962306a36Sopenharmony_ci		ses->auth_key.len = tilen;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	return 0;
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_cistatic int size_of_ntlmssp_blob(struct cifs_ses *ses, int base_size)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	int sz = base_size + ses->auth_key.len
100862306a36Sopenharmony_ci		- CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (ses->domainName)
101162306a36Sopenharmony_ci		sz += sizeof(__le16) * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
101262306a36Sopenharmony_ci	else
101362306a36Sopenharmony_ci		sz += sizeof(__le16);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (ses->user_name)
101662306a36Sopenharmony_ci		sz += sizeof(__le16) * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
101762306a36Sopenharmony_ci	else
101862306a36Sopenharmony_ci		sz += sizeof(__le16);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	if (ses->workstation_name[0])
102162306a36Sopenharmony_ci		sz += sizeof(__le16) * strnlen(ses->workstation_name,
102262306a36Sopenharmony_ci					       ntlmssp_workstation_name_size(ses));
102362306a36Sopenharmony_ci	else
102462306a36Sopenharmony_ci		sz += sizeof(__le16);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	return sz;
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_cistatic inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf,
103062306a36Sopenharmony_ci						 char *str_value,
103162306a36Sopenharmony_ci						 int str_length,
103262306a36Sopenharmony_ci						 unsigned char *pstart,
103362306a36Sopenharmony_ci						 unsigned char **pcur,
103462306a36Sopenharmony_ci						 const struct nls_table *nls_cp)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	unsigned char *tmp = pstart;
103762306a36Sopenharmony_ci	int len;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (!pbuf)
104062306a36Sopenharmony_ci		return;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	if (!pcur)
104362306a36Sopenharmony_ci		pcur = &tmp;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (!str_value) {
104662306a36Sopenharmony_ci		pbuf->BufferOffset = cpu_to_le32(*pcur - pstart);
104762306a36Sopenharmony_ci		pbuf->Length = 0;
104862306a36Sopenharmony_ci		pbuf->MaximumLength = 0;
104962306a36Sopenharmony_ci		*pcur += sizeof(__le16);
105062306a36Sopenharmony_ci	} else {
105162306a36Sopenharmony_ci		len = cifs_strtoUTF16((__le16 *)*pcur,
105262306a36Sopenharmony_ci				      str_value,
105362306a36Sopenharmony_ci				      str_length,
105462306a36Sopenharmony_ci				      nls_cp);
105562306a36Sopenharmony_ci		len *= sizeof(__le16);
105662306a36Sopenharmony_ci		pbuf->BufferOffset = cpu_to_le32(*pcur - pstart);
105762306a36Sopenharmony_ci		pbuf->Length = cpu_to_le16(len);
105862306a36Sopenharmony_ci		pbuf->MaximumLength = cpu_to_le16(len);
105962306a36Sopenharmony_ci		*pcur += len;
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci/* BB Move to ntlmssp.c eventually */
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ciint build_ntlmssp_negotiate_blob(unsigned char **pbuffer,
106662306a36Sopenharmony_ci				 u16 *buflen,
106762306a36Sopenharmony_ci				 struct cifs_ses *ses,
106862306a36Sopenharmony_ci				 struct TCP_Server_Info *server,
106962306a36Sopenharmony_ci				 const struct nls_table *nls_cp)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	int rc = 0;
107262306a36Sopenharmony_ci	NEGOTIATE_MESSAGE *sec_blob;
107362306a36Sopenharmony_ci	__u32 flags;
107462306a36Sopenharmony_ci	unsigned char *tmp;
107562306a36Sopenharmony_ci	int len;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	len = size_of_ntlmssp_blob(ses, sizeof(NEGOTIATE_MESSAGE));
107862306a36Sopenharmony_ci	*pbuffer = kmalloc(len, GFP_KERNEL);
107962306a36Sopenharmony_ci	if (!*pbuffer) {
108062306a36Sopenharmony_ci		rc = -ENOMEM;
108162306a36Sopenharmony_ci		cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc);
108262306a36Sopenharmony_ci		*buflen = 0;
108362306a36Sopenharmony_ci		goto setup_ntlm_neg_ret;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci	sec_blob = (NEGOTIATE_MESSAGE *)*pbuffer;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	memset(*pbuffer, 0, sizeof(NEGOTIATE_MESSAGE));
108862306a36Sopenharmony_ci	memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
108962306a36Sopenharmony_ci	sec_blob->MessageType = NtLmNegotiate;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	/* BB is NTLMV2 session security format easier to use here? */
109262306a36Sopenharmony_ci	flags = NTLMSSP_NEGOTIATE_56 |	NTLMSSP_REQUEST_TARGET |
109362306a36Sopenharmony_ci		NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
109462306a36Sopenharmony_ci		NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
109562306a36Sopenharmony_ci		NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL |
109662306a36Sopenharmony_ci		NTLMSSP_NEGOTIATE_SIGN;
109762306a36Sopenharmony_ci	if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
109862306a36Sopenharmony_ci		flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	tmp = *pbuffer + sizeof(NEGOTIATE_MESSAGE);
110162306a36Sopenharmony_ci	ses->ntlmssp->client_flags = flags;
110262306a36Sopenharmony_ci	sec_blob->NegotiateFlags = cpu_to_le32(flags);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */
110562306a36Sopenharmony_ci	cifs_security_buffer_from_str(&sec_blob->DomainName,
110662306a36Sopenharmony_ci				      NULL,
110762306a36Sopenharmony_ci				      CIFS_MAX_DOMAINNAME_LEN,
110862306a36Sopenharmony_ci				      *pbuffer, &tmp,
110962306a36Sopenharmony_ci				      nls_cp);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	cifs_security_buffer_from_str(&sec_blob->WorkstationName,
111262306a36Sopenharmony_ci				      NULL,
111362306a36Sopenharmony_ci				      CIFS_MAX_WORKSTATION_LEN,
111462306a36Sopenharmony_ci				      *pbuffer, &tmp,
111562306a36Sopenharmony_ci				      nls_cp);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	*buflen = tmp - *pbuffer;
111862306a36Sopenharmony_cisetup_ntlm_neg_ret:
111962306a36Sopenharmony_ci	return rc;
112062306a36Sopenharmony_ci}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci/*
112362306a36Sopenharmony_ci * Build ntlmssp blob with additional fields, such as version,
112462306a36Sopenharmony_ci * supported by modern servers. For safety limit to SMB3 or later
112562306a36Sopenharmony_ci * See notes in MS-NLMP Section 2.2.2.1 e.g.
112662306a36Sopenharmony_ci */
112762306a36Sopenharmony_ciint build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer,
112862306a36Sopenharmony_ci				 u16 *buflen,
112962306a36Sopenharmony_ci				 struct cifs_ses *ses,
113062306a36Sopenharmony_ci				 struct TCP_Server_Info *server,
113162306a36Sopenharmony_ci				 const struct nls_table *nls_cp)
113262306a36Sopenharmony_ci{
113362306a36Sopenharmony_ci	int rc = 0;
113462306a36Sopenharmony_ci	struct negotiate_message *sec_blob;
113562306a36Sopenharmony_ci	__u32 flags;
113662306a36Sopenharmony_ci	unsigned char *tmp;
113762306a36Sopenharmony_ci	int len;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	len = size_of_ntlmssp_blob(ses, sizeof(struct negotiate_message));
114062306a36Sopenharmony_ci	*pbuffer = kmalloc(len, GFP_KERNEL);
114162306a36Sopenharmony_ci	if (!*pbuffer) {
114262306a36Sopenharmony_ci		rc = -ENOMEM;
114362306a36Sopenharmony_ci		cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc);
114462306a36Sopenharmony_ci		*buflen = 0;
114562306a36Sopenharmony_ci		goto setup_ntlm_smb3_neg_ret;
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci	sec_blob = (struct negotiate_message *)*pbuffer;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	memset(*pbuffer, 0, sizeof(struct negotiate_message));
115062306a36Sopenharmony_ci	memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
115162306a36Sopenharmony_ci	sec_blob->MessageType = NtLmNegotiate;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	/* BB is NTLMV2 session security format easier to use here? */
115462306a36Sopenharmony_ci	flags = NTLMSSP_NEGOTIATE_56 |	NTLMSSP_REQUEST_TARGET |
115562306a36Sopenharmony_ci		NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
115662306a36Sopenharmony_ci		NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
115762306a36Sopenharmony_ci		NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL |
115862306a36Sopenharmony_ci		NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_VERSION;
115962306a36Sopenharmony_ci	if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
116062306a36Sopenharmony_ci		flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR;
116362306a36Sopenharmony_ci	sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL;
116462306a36Sopenharmony_ci	sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD);
116562306a36Sopenharmony_ci	sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	tmp = *pbuffer + sizeof(struct negotiate_message);
116862306a36Sopenharmony_ci	ses->ntlmssp->client_flags = flags;
116962306a36Sopenharmony_ci	sec_blob->NegotiateFlags = cpu_to_le32(flags);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */
117262306a36Sopenharmony_ci	cifs_security_buffer_from_str(&sec_blob->DomainName,
117362306a36Sopenharmony_ci				      NULL,
117462306a36Sopenharmony_ci				      CIFS_MAX_DOMAINNAME_LEN,
117562306a36Sopenharmony_ci				      *pbuffer, &tmp,
117662306a36Sopenharmony_ci				      nls_cp);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	cifs_security_buffer_from_str(&sec_blob->WorkstationName,
117962306a36Sopenharmony_ci				      NULL,
118062306a36Sopenharmony_ci				      CIFS_MAX_WORKSTATION_LEN,
118162306a36Sopenharmony_ci				      *pbuffer, &tmp,
118262306a36Sopenharmony_ci				      nls_cp);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	*buflen = tmp - *pbuffer;
118562306a36Sopenharmony_cisetup_ntlm_smb3_neg_ret:
118662306a36Sopenharmony_ci	return rc;
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci/* See MS-NLMP 2.2.1.3 */
119162306a36Sopenharmony_ciint build_ntlmssp_auth_blob(unsigned char **pbuffer,
119262306a36Sopenharmony_ci					u16 *buflen,
119362306a36Sopenharmony_ci				   struct cifs_ses *ses,
119462306a36Sopenharmony_ci				   struct TCP_Server_Info *server,
119562306a36Sopenharmony_ci				   const struct nls_table *nls_cp)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	int rc;
119862306a36Sopenharmony_ci	AUTHENTICATE_MESSAGE *sec_blob;
119962306a36Sopenharmony_ci	__u32 flags;
120062306a36Sopenharmony_ci	unsigned char *tmp;
120162306a36Sopenharmony_ci	int len;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	rc = setup_ntlmv2_rsp(ses, nls_cp);
120462306a36Sopenharmony_ci	if (rc) {
120562306a36Sopenharmony_ci		cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
120662306a36Sopenharmony_ci		*buflen = 0;
120762306a36Sopenharmony_ci		goto setup_ntlmv2_ret;
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	len = size_of_ntlmssp_blob(ses, sizeof(AUTHENTICATE_MESSAGE));
121162306a36Sopenharmony_ci	*pbuffer = kmalloc(len, GFP_KERNEL);
121262306a36Sopenharmony_ci	if (!*pbuffer) {
121362306a36Sopenharmony_ci		rc = -ENOMEM;
121462306a36Sopenharmony_ci		cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc);
121562306a36Sopenharmony_ci		*buflen = 0;
121662306a36Sopenharmony_ci		goto setup_ntlmv2_ret;
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci	sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
122162306a36Sopenharmony_ci	sec_blob->MessageType = NtLmAuthenticate;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET |
122462306a36Sopenharmony_ci		NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
122562306a36Sopenharmony_ci	/* we only send version information in ntlmssp negotiate, so do not set this flag */
122662306a36Sopenharmony_ci	flags = flags & ~NTLMSSP_NEGOTIATE_VERSION;
122762306a36Sopenharmony_ci	tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
122862306a36Sopenharmony_ci	sec_blob->NegotiateFlags = cpu_to_le32(flags);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	sec_blob->LmChallengeResponse.BufferOffset =
123162306a36Sopenharmony_ci				cpu_to_le32(sizeof(AUTHENTICATE_MESSAGE));
123262306a36Sopenharmony_ci	sec_blob->LmChallengeResponse.Length = 0;
123362306a36Sopenharmony_ci	sec_blob->LmChallengeResponse.MaximumLength = 0;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	sec_blob->NtChallengeResponse.BufferOffset =
123662306a36Sopenharmony_ci				cpu_to_le32(tmp - *pbuffer);
123762306a36Sopenharmony_ci	if (ses->user_name != NULL) {
123862306a36Sopenharmony_ci		memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
123962306a36Sopenharmony_ci				ses->auth_key.len - CIFS_SESS_KEY_SIZE);
124062306a36Sopenharmony_ci		tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci		sec_blob->NtChallengeResponse.Length =
124362306a36Sopenharmony_ci				cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
124462306a36Sopenharmony_ci		sec_blob->NtChallengeResponse.MaximumLength =
124562306a36Sopenharmony_ci				cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
124662306a36Sopenharmony_ci	} else {
124762306a36Sopenharmony_ci		/*
124862306a36Sopenharmony_ci		 * don't send an NT Response for anonymous access
124962306a36Sopenharmony_ci		 */
125062306a36Sopenharmony_ci		sec_blob->NtChallengeResponse.Length = 0;
125162306a36Sopenharmony_ci		sec_blob->NtChallengeResponse.MaximumLength = 0;
125262306a36Sopenharmony_ci	}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	cifs_security_buffer_from_str(&sec_blob->DomainName,
125562306a36Sopenharmony_ci				      ses->domainName,
125662306a36Sopenharmony_ci				      CIFS_MAX_DOMAINNAME_LEN,
125762306a36Sopenharmony_ci				      *pbuffer, &tmp,
125862306a36Sopenharmony_ci				      nls_cp);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	cifs_security_buffer_from_str(&sec_blob->UserName,
126162306a36Sopenharmony_ci				      ses->user_name,
126262306a36Sopenharmony_ci				      CIFS_MAX_USERNAME_LEN,
126362306a36Sopenharmony_ci				      *pbuffer, &tmp,
126462306a36Sopenharmony_ci				      nls_cp);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	cifs_security_buffer_from_str(&sec_blob->WorkstationName,
126762306a36Sopenharmony_ci				      ses->workstation_name,
126862306a36Sopenharmony_ci				      ntlmssp_workstation_name_size(ses),
126962306a36Sopenharmony_ci				      *pbuffer, &tmp,
127062306a36Sopenharmony_ci				      nls_cp);
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	if ((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) &&
127362306a36Sopenharmony_ci	    (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess) &&
127462306a36Sopenharmony_ci	    !calc_seckey(ses)) {
127562306a36Sopenharmony_ci		memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
127662306a36Sopenharmony_ci		sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
127762306a36Sopenharmony_ci		sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE);
127862306a36Sopenharmony_ci		sec_blob->SessionKey.MaximumLength =
127962306a36Sopenharmony_ci				cpu_to_le16(CIFS_CPHTXT_SIZE);
128062306a36Sopenharmony_ci		tmp += CIFS_CPHTXT_SIZE;
128162306a36Sopenharmony_ci	} else {
128262306a36Sopenharmony_ci		sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
128362306a36Sopenharmony_ci		sec_blob->SessionKey.Length = 0;
128462306a36Sopenharmony_ci		sec_blob->SessionKey.MaximumLength = 0;
128562306a36Sopenharmony_ci	}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	*buflen = tmp - *pbuffer;
128862306a36Sopenharmony_cisetup_ntlmv2_ret:
128962306a36Sopenharmony_ci	return rc;
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cienum securityEnum
129362306a36Sopenharmony_cicifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
129462306a36Sopenharmony_ci{
129562306a36Sopenharmony_ci	switch (server->negflavor) {
129662306a36Sopenharmony_ci	case CIFS_NEGFLAVOR_EXTENDED:
129762306a36Sopenharmony_ci		switch (requested) {
129862306a36Sopenharmony_ci		case Kerberos:
129962306a36Sopenharmony_ci		case RawNTLMSSP:
130062306a36Sopenharmony_ci			return requested;
130162306a36Sopenharmony_ci		case Unspecified:
130262306a36Sopenharmony_ci			if (server->sec_ntlmssp &&
130362306a36Sopenharmony_ci			    (global_secflags & CIFSSEC_MAY_NTLMSSP))
130462306a36Sopenharmony_ci				return RawNTLMSSP;
130562306a36Sopenharmony_ci			if ((server->sec_kerberos || server->sec_mskerberos) &&
130662306a36Sopenharmony_ci			    (global_secflags & CIFSSEC_MAY_KRB5))
130762306a36Sopenharmony_ci				return Kerberos;
130862306a36Sopenharmony_ci			fallthrough;
130962306a36Sopenharmony_ci		default:
131062306a36Sopenharmony_ci			return Unspecified;
131162306a36Sopenharmony_ci		}
131262306a36Sopenharmony_ci	case CIFS_NEGFLAVOR_UNENCAP:
131362306a36Sopenharmony_ci		switch (requested) {
131462306a36Sopenharmony_ci		case NTLMv2:
131562306a36Sopenharmony_ci			return requested;
131662306a36Sopenharmony_ci		case Unspecified:
131762306a36Sopenharmony_ci			if (global_secflags & CIFSSEC_MAY_NTLMV2)
131862306a36Sopenharmony_ci				return NTLMv2;
131962306a36Sopenharmony_ci			break;
132062306a36Sopenharmony_ci		default:
132162306a36Sopenharmony_ci			break;
132262306a36Sopenharmony_ci		}
132362306a36Sopenharmony_ci		fallthrough;
132462306a36Sopenharmony_ci	default:
132562306a36Sopenharmony_ci		return Unspecified;
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cistruct sess_data {
133062306a36Sopenharmony_ci	unsigned int xid;
133162306a36Sopenharmony_ci	struct cifs_ses *ses;
133262306a36Sopenharmony_ci	struct TCP_Server_Info *server;
133362306a36Sopenharmony_ci	struct nls_table *nls_cp;
133462306a36Sopenharmony_ci	void (*func)(struct sess_data *);
133562306a36Sopenharmony_ci	int result;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	/* we will send the SMB in three pieces:
133862306a36Sopenharmony_ci	 * a fixed length beginning part, an optional
133962306a36Sopenharmony_ci	 * SPNEGO blob (which can be zero length), and a
134062306a36Sopenharmony_ci	 * last part which will include the strings
134162306a36Sopenharmony_ci	 * and rest of bcc area. This allows us to avoid
134262306a36Sopenharmony_ci	 * a large buffer 17K allocation
134362306a36Sopenharmony_ci	 */
134462306a36Sopenharmony_ci	int buf0_type;
134562306a36Sopenharmony_ci	struct kvec iov[3];
134662306a36Sopenharmony_ci};
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
134962306a36Sopenharmony_cistatic int
135062306a36Sopenharmony_cisess_alloc_buffer(struct sess_data *sess_data, int wct)
135162306a36Sopenharmony_ci{
135262306a36Sopenharmony_ci	int rc;
135362306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
135462306a36Sopenharmony_ci	struct smb_hdr *smb_buf;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
135762306a36Sopenharmony_ci				  (void **)&smb_buf);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	if (rc)
136062306a36Sopenharmony_ci		return rc;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	sess_data->iov[0].iov_base = (char *)smb_buf;
136362306a36Sopenharmony_ci	sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
136462306a36Sopenharmony_ci	/*
136562306a36Sopenharmony_ci	 * This variable will be used to clear the buffer
136662306a36Sopenharmony_ci	 * allocated above in case of any error in the calling function.
136762306a36Sopenharmony_ci	 */
136862306a36Sopenharmony_ci	sess_data->buf0_type = CIFS_SMALL_BUFFER;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	/* 2000 big enough to fit max user, domain, NOS name etc. */
137162306a36Sopenharmony_ci	sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL);
137262306a36Sopenharmony_ci	if (!sess_data->iov[2].iov_base) {
137362306a36Sopenharmony_ci		rc = -ENOMEM;
137462306a36Sopenharmony_ci		goto out_free_smb_buf;
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	return 0;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ciout_free_smb_buf:
138062306a36Sopenharmony_ci	cifs_small_buf_release(smb_buf);
138162306a36Sopenharmony_ci	sess_data->iov[0].iov_base = NULL;
138262306a36Sopenharmony_ci	sess_data->iov[0].iov_len = 0;
138362306a36Sopenharmony_ci	sess_data->buf0_type = CIFS_NO_BUFFER;
138462306a36Sopenharmony_ci	return rc;
138562306a36Sopenharmony_ci}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_cistatic void
138862306a36Sopenharmony_cisess_free_buffer(struct sess_data *sess_data)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	struct kvec *iov = sess_data->iov;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	/*
139362306a36Sopenharmony_ci	 * Zero the session data before freeing, as it might contain sensitive info (keys, etc).
139462306a36Sopenharmony_ci	 * Note that iov[1] is already freed by caller.
139562306a36Sopenharmony_ci	 */
139662306a36Sopenharmony_ci	if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base)
139762306a36Sopenharmony_ci		memzero_explicit(iov[0].iov_base, iov[0].iov_len);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	free_rsp_buf(sess_data->buf0_type, iov[0].iov_base);
140062306a36Sopenharmony_ci	sess_data->buf0_type = CIFS_NO_BUFFER;
140162306a36Sopenharmony_ci	kfree_sensitive(iov[2].iov_base);
140262306a36Sopenharmony_ci}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_cistatic int
140562306a36Sopenharmony_cisess_establish_session(struct sess_data *sess_data)
140662306a36Sopenharmony_ci{
140762306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
140862306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	cifs_server_lock(server);
141162306a36Sopenharmony_ci	if (!server->session_estab) {
141262306a36Sopenharmony_ci		if (server->sign) {
141362306a36Sopenharmony_ci			server->session_key.response =
141462306a36Sopenharmony_ci				kmemdup(ses->auth_key.response,
141562306a36Sopenharmony_ci				ses->auth_key.len, GFP_KERNEL);
141662306a36Sopenharmony_ci			if (!server->session_key.response) {
141762306a36Sopenharmony_ci				cifs_server_unlock(server);
141862306a36Sopenharmony_ci				return -ENOMEM;
141962306a36Sopenharmony_ci			}
142062306a36Sopenharmony_ci			server->session_key.len =
142162306a36Sopenharmony_ci						ses->auth_key.len;
142262306a36Sopenharmony_ci		}
142362306a36Sopenharmony_ci		server->sequence_number = 0x2;
142462306a36Sopenharmony_ci		server->session_estab = true;
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci	cifs_server_unlock(server);
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	cifs_dbg(FYI, "CIFS session established successfully\n");
142962306a36Sopenharmony_ci	return 0;
143062306a36Sopenharmony_ci}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_cistatic int
143362306a36Sopenharmony_cisess_sendreceive(struct sess_data *sess_data)
143462306a36Sopenharmony_ci{
143562306a36Sopenharmony_ci	int rc;
143662306a36Sopenharmony_ci	struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base;
143762306a36Sopenharmony_ci	__u16 count;
143862306a36Sopenharmony_ci	struct kvec rsp_iov = { NULL, 0 };
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
144162306a36Sopenharmony_ci	be32_add_cpu(&smb_buf->smb_buf_length, count);
144262306a36Sopenharmony_ci	put_bcc(count, smb_buf);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	rc = SendReceive2(sess_data->xid, sess_data->ses,
144562306a36Sopenharmony_ci			  sess_data->iov, 3 /* num_iovecs */,
144662306a36Sopenharmony_ci			  &sess_data->buf0_type,
144762306a36Sopenharmony_ci			  CIFS_LOG_ERROR, &rsp_iov);
144862306a36Sopenharmony_ci	cifs_small_buf_release(sess_data->iov[0].iov_base);
144962306a36Sopenharmony_ci	memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec));
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	return rc;
145262306a36Sopenharmony_ci}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_cistatic void
145562306a36Sopenharmony_cisess_auth_ntlmv2(struct sess_data *sess_data)
145662306a36Sopenharmony_ci{
145762306a36Sopenharmony_ci	int rc = 0;
145862306a36Sopenharmony_ci	struct smb_hdr *smb_buf;
145962306a36Sopenharmony_ci	SESSION_SETUP_ANDX *pSMB;
146062306a36Sopenharmony_ci	char *bcc_ptr;
146162306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
146262306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
146362306a36Sopenharmony_ci	__u32 capabilities;
146462306a36Sopenharmony_ci	__u16 bytes_remaining;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	/* old style NTLM sessionsetup */
146762306a36Sopenharmony_ci	/* wct = 13 */
146862306a36Sopenharmony_ci	rc = sess_alloc_buffer(sess_data, 13);
146962306a36Sopenharmony_ci	if (rc)
147062306a36Sopenharmony_ci		goto out;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
147362306a36Sopenharmony_ci	bcc_ptr = sess_data->iov[2].iov_base;
147462306a36Sopenharmony_ci	capabilities = cifs_ssetup_hdr(ses, server, pSMB);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	/* LM2 password would be here if we supported it */
147962306a36Sopenharmony_ci	pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	if (ses->user_name != NULL) {
148262306a36Sopenharmony_ci		/* calculate nlmv2 response and session key */
148362306a36Sopenharmony_ci		rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp);
148462306a36Sopenharmony_ci		if (rc) {
148562306a36Sopenharmony_ci			cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc);
148662306a36Sopenharmony_ci			goto out;
148762306a36Sopenharmony_ci		}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
149062306a36Sopenharmony_ci				ses->auth_key.len - CIFS_SESS_KEY_SIZE);
149162306a36Sopenharmony_ci		bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci		/* set case sensitive password length after tilen may get
149462306a36Sopenharmony_ci		 * assigned, tilen is 0 otherwise.
149562306a36Sopenharmony_ci		 */
149662306a36Sopenharmony_ci		pSMB->req_no_secext.CaseSensitivePasswordLength =
149762306a36Sopenharmony_ci			cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
149862306a36Sopenharmony_ci	} else {
149962306a36Sopenharmony_ci		pSMB->req_no_secext.CaseSensitivePasswordLength = 0;
150062306a36Sopenharmony_ci	}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	if (ses->capabilities & CAP_UNICODE) {
150362306a36Sopenharmony_ci		if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) {
150462306a36Sopenharmony_ci			*bcc_ptr = 0;
150562306a36Sopenharmony_ci			bcc_ptr++;
150662306a36Sopenharmony_ci		}
150762306a36Sopenharmony_ci		unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
150862306a36Sopenharmony_ci	} else {
150962306a36Sopenharmony_ci		ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	sess_data->iov[2].iov_len = (long) bcc_ptr -
151462306a36Sopenharmony_ci			(long) sess_data->iov[2].iov_base;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	rc = sess_sendreceive(sess_data);
151762306a36Sopenharmony_ci	if (rc)
151862306a36Sopenharmony_ci		goto out;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
152162306a36Sopenharmony_ci	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (smb_buf->WordCount != 3) {
152462306a36Sopenharmony_ci		rc = -EIO;
152562306a36Sopenharmony_ci		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
152662306a36Sopenharmony_ci		goto out;
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
153062306a36Sopenharmony_ci		cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
153362306a36Sopenharmony_ci	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	bytes_remaining = get_bcc(smb_buf);
153662306a36Sopenharmony_ci	bcc_ptr = pByteArea(smb_buf);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	/* BB check if Unicode and decode strings */
153962306a36Sopenharmony_ci	if (bytes_remaining == 0) {
154062306a36Sopenharmony_ci		/* no string area to decode, do nothing */
154162306a36Sopenharmony_ci	} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
154262306a36Sopenharmony_ci		/* unicode string area must be word-aligned */
154362306a36Sopenharmony_ci		if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
154462306a36Sopenharmony_ci			++bcc_ptr;
154562306a36Sopenharmony_ci			--bytes_remaining;
154662306a36Sopenharmony_ci		}
154762306a36Sopenharmony_ci		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
154862306a36Sopenharmony_ci				      sess_data->nls_cp);
154962306a36Sopenharmony_ci	} else {
155062306a36Sopenharmony_ci		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
155162306a36Sopenharmony_ci				    sess_data->nls_cp);
155262306a36Sopenharmony_ci	}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	rc = sess_establish_session(sess_data);
155562306a36Sopenharmony_ciout:
155662306a36Sopenharmony_ci	sess_data->result = rc;
155762306a36Sopenharmony_ci	sess_data->func = NULL;
155862306a36Sopenharmony_ci	sess_free_buffer(sess_data);
155962306a36Sopenharmony_ci	kfree_sensitive(ses->auth_key.response);
156062306a36Sopenharmony_ci	ses->auth_key.response = NULL;
156162306a36Sopenharmony_ci}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_UPCALL
156462306a36Sopenharmony_cistatic void
156562306a36Sopenharmony_cisess_auth_kerberos(struct sess_data *sess_data)
156662306a36Sopenharmony_ci{
156762306a36Sopenharmony_ci	int rc = 0;
156862306a36Sopenharmony_ci	struct smb_hdr *smb_buf;
156962306a36Sopenharmony_ci	SESSION_SETUP_ANDX *pSMB;
157062306a36Sopenharmony_ci	char *bcc_ptr;
157162306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
157262306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
157362306a36Sopenharmony_ci	__u32 capabilities;
157462306a36Sopenharmony_ci	__u16 bytes_remaining;
157562306a36Sopenharmony_ci	struct key *spnego_key = NULL;
157662306a36Sopenharmony_ci	struct cifs_spnego_msg *msg;
157762306a36Sopenharmony_ci	u16 blob_len;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	/* extended security */
158062306a36Sopenharmony_ci	/* wct = 12 */
158162306a36Sopenharmony_ci	rc = sess_alloc_buffer(sess_data, 12);
158262306a36Sopenharmony_ci	if (rc)
158362306a36Sopenharmony_ci		goto out;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
158662306a36Sopenharmony_ci	bcc_ptr = sess_data->iov[2].iov_base;
158762306a36Sopenharmony_ci	capabilities = cifs_ssetup_hdr(ses, server, pSMB);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	spnego_key = cifs_get_spnego_key(ses, server);
159062306a36Sopenharmony_ci	if (IS_ERR(spnego_key)) {
159162306a36Sopenharmony_ci		rc = PTR_ERR(spnego_key);
159262306a36Sopenharmony_ci		spnego_key = NULL;
159362306a36Sopenharmony_ci		goto out;
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	msg = spnego_key->payload.data[0];
159762306a36Sopenharmony_ci	/*
159862306a36Sopenharmony_ci	 * check version field to make sure that cifs.upcall is
159962306a36Sopenharmony_ci	 * sending us a response in an expected form
160062306a36Sopenharmony_ci	 */
160162306a36Sopenharmony_ci	if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
160262306a36Sopenharmony_ci		cifs_dbg(VFS, "incorrect version of cifs.upcall (expected %d but got %d)\n",
160362306a36Sopenharmony_ci			 CIFS_SPNEGO_UPCALL_VERSION, msg->version);
160462306a36Sopenharmony_ci		rc = -EKEYREJECTED;
160562306a36Sopenharmony_ci		goto out_put_spnego_key;
160662306a36Sopenharmony_ci	}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	kfree_sensitive(ses->auth_key.response);
160962306a36Sopenharmony_ci	ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
161062306a36Sopenharmony_ci					 GFP_KERNEL);
161162306a36Sopenharmony_ci	if (!ses->auth_key.response) {
161262306a36Sopenharmony_ci		cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n",
161362306a36Sopenharmony_ci			 msg->sesskey_len);
161462306a36Sopenharmony_ci		rc = -ENOMEM;
161562306a36Sopenharmony_ci		goto out_put_spnego_key;
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci	ses->auth_key.len = msg->sesskey_len;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
162062306a36Sopenharmony_ci	capabilities |= CAP_EXTENDED_SECURITY;
162162306a36Sopenharmony_ci	pSMB->req.Capabilities = cpu_to_le32(capabilities);
162262306a36Sopenharmony_ci	sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
162362306a36Sopenharmony_ci	sess_data->iov[1].iov_len = msg->secblob_len;
162462306a36Sopenharmony_ci	pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	if (ses->capabilities & CAP_UNICODE) {
162762306a36Sopenharmony_ci		/* unicode strings must be word aligned */
162862306a36Sopenharmony_ci		if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
162962306a36Sopenharmony_ci			*bcc_ptr = 0;
163062306a36Sopenharmony_ci			bcc_ptr++;
163162306a36Sopenharmony_ci		}
163262306a36Sopenharmony_ci		unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
163362306a36Sopenharmony_ci		unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp);
163462306a36Sopenharmony_ci	} else {
163562306a36Sopenharmony_ci		/* BB: is this right? */
163662306a36Sopenharmony_ci		ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	sess_data->iov[2].iov_len = (long) bcc_ptr -
164062306a36Sopenharmony_ci			(long) sess_data->iov[2].iov_base;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	rc = sess_sendreceive(sess_data);
164362306a36Sopenharmony_ci	if (rc)
164462306a36Sopenharmony_ci		goto out_put_spnego_key;
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
164762306a36Sopenharmony_ci	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	if (smb_buf->WordCount != 4) {
165062306a36Sopenharmony_ci		rc = -EIO;
165162306a36Sopenharmony_ci		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
165262306a36Sopenharmony_ci		goto out_put_spnego_key;
165362306a36Sopenharmony_ci	}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
165662306a36Sopenharmony_ci		cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
165962306a36Sopenharmony_ci	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	bytes_remaining = get_bcc(smb_buf);
166262306a36Sopenharmony_ci	bcc_ptr = pByteArea(smb_buf);
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
166562306a36Sopenharmony_ci	if (blob_len > bytes_remaining) {
166662306a36Sopenharmony_ci		cifs_dbg(VFS, "bad security blob length %d\n",
166762306a36Sopenharmony_ci				blob_len);
166862306a36Sopenharmony_ci		rc = -EINVAL;
166962306a36Sopenharmony_ci		goto out_put_spnego_key;
167062306a36Sopenharmony_ci	}
167162306a36Sopenharmony_ci	bcc_ptr += blob_len;
167262306a36Sopenharmony_ci	bytes_remaining -= blob_len;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	/* BB check if Unicode and decode strings */
167562306a36Sopenharmony_ci	if (bytes_remaining == 0) {
167662306a36Sopenharmony_ci		/* no string area to decode, do nothing */
167762306a36Sopenharmony_ci	} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
167862306a36Sopenharmony_ci		/* unicode string area must be word-aligned */
167962306a36Sopenharmony_ci		if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
168062306a36Sopenharmony_ci			++bcc_ptr;
168162306a36Sopenharmony_ci			--bytes_remaining;
168262306a36Sopenharmony_ci		}
168362306a36Sopenharmony_ci		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
168462306a36Sopenharmony_ci				      sess_data->nls_cp);
168562306a36Sopenharmony_ci	} else {
168662306a36Sopenharmony_ci		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
168762306a36Sopenharmony_ci				    sess_data->nls_cp);
168862306a36Sopenharmony_ci	}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	rc = sess_establish_session(sess_data);
169162306a36Sopenharmony_ciout_put_spnego_key:
169262306a36Sopenharmony_ci	key_invalidate(spnego_key);
169362306a36Sopenharmony_ci	key_put(spnego_key);
169462306a36Sopenharmony_ciout:
169562306a36Sopenharmony_ci	sess_data->result = rc;
169662306a36Sopenharmony_ci	sess_data->func = NULL;
169762306a36Sopenharmony_ci	sess_free_buffer(sess_data);
169862306a36Sopenharmony_ci	kfree_sensitive(ses->auth_key.response);
169962306a36Sopenharmony_ci	ses->auth_key.response = NULL;
170062306a36Sopenharmony_ci}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci#endif /* ! CONFIG_CIFS_UPCALL */
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci/*
170562306a36Sopenharmony_ci * The required kvec buffers have to be allocated before calling this
170662306a36Sopenharmony_ci * function.
170762306a36Sopenharmony_ci */
170862306a36Sopenharmony_cistatic int
170962306a36Sopenharmony_ci_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	SESSION_SETUP_ANDX *pSMB;
171262306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
171362306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
171462306a36Sopenharmony_ci	__u32 capabilities;
171562306a36Sopenharmony_ci	char *bcc_ptr;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	capabilities = cifs_ssetup_hdr(ses, server, pSMB);
172062306a36Sopenharmony_ci	if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
172162306a36Sopenharmony_ci		cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
172262306a36Sopenharmony_ci		return -ENOSYS;
172362306a36Sopenharmony_ci	}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
172662306a36Sopenharmony_ci	capabilities |= CAP_EXTENDED_SECURITY;
172762306a36Sopenharmony_ci	pSMB->req.Capabilities |= cpu_to_le32(capabilities);
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	bcc_ptr = sess_data->iov[2].iov_base;
173062306a36Sopenharmony_ci	/* unicode strings must be word aligned */
173162306a36Sopenharmony_ci	if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
173262306a36Sopenharmony_ci		*bcc_ptr = 0;
173362306a36Sopenharmony_ci		bcc_ptr++;
173462306a36Sopenharmony_ci	}
173562306a36Sopenharmony_ci	unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	sess_data->iov[2].iov_len = (long) bcc_ptr -
173862306a36Sopenharmony_ci					(long) sess_data->iov[2].iov_base;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	return 0;
174162306a36Sopenharmony_ci}
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_cistatic void
174462306a36Sopenharmony_cisess_auth_rawntlmssp_authenticate(struct sess_data *sess_data);
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic void
174762306a36Sopenharmony_cisess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	int rc;
175062306a36Sopenharmony_ci	struct smb_hdr *smb_buf;
175162306a36Sopenharmony_ci	SESSION_SETUP_ANDX *pSMB;
175262306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
175362306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
175462306a36Sopenharmony_ci	__u16 bytes_remaining;
175562306a36Sopenharmony_ci	char *bcc_ptr;
175662306a36Sopenharmony_ci	unsigned char *ntlmsspblob = NULL;
175762306a36Sopenharmony_ci	u16 blob_len;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n");
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	/*
176262306a36Sopenharmony_ci	 * if memory allocation is successful, caller of this function
176362306a36Sopenharmony_ci	 * frees it.
176462306a36Sopenharmony_ci	 */
176562306a36Sopenharmony_ci	ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
176662306a36Sopenharmony_ci	if (!ses->ntlmssp) {
176762306a36Sopenharmony_ci		rc = -ENOMEM;
176862306a36Sopenharmony_ci		goto out;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci	ses->ntlmssp->sesskey_per_smbsess = false;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	/* wct = 12 */
177362306a36Sopenharmony_ci	rc = sess_alloc_buffer(sess_data, 12);
177462306a36Sopenharmony_ci	if (rc)
177562306a36Sopenharmony_ci		goto out;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	/* Build security blob before we assemble the request */
178062306a36Sopenharmony_ci	rc = build_ntlmssp_negotiate_blob(&ntlmsspblob,
178162306a36Sopenharmony_ci				     &blob_len, ses, server,
178262306a36Sopenharmony_ci				     sess_data->nls_cp);
178362306a36Sopenharmony_ci	if (rc)
178462306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	sess_data->iov[1].iov_len = blob_len;
178762306a36Sopenharmony_ci	sess_data->iov[1].iov_base = ntlmsspblob;
178862306a36Sopenharmony_ci	pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
179162306a36Sopenharmony_ci	if (rc)
179262306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	rc = sess_sendreceive(sess_data);
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
179762306a36Sopenharmony_ci	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	/* If true, rc here is expected and not an error */
180062306a36Sopenharmony_ci	if (sess_data->buf0_type != CIFS_NO_BUFFER &&
180162306a36Sopenharmony_ci	    smb_buf->Status.CifsError ==
180262306a36Sopenharmony_ci			cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
180362306a36Sopenharmony_ci		rc = 0;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	if (rc)
180662306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	if (smb_buf->WordCount != 4) {
181162306a36Sopenharmony_ci		rc = -EIO;
181262306a36Sopenharmony_ci		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
181362306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
181462306a36Sopenharmony_ci	}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
181762306a36Sopenharmony_ci	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	bytes_remaining = get_bcc(smb_buf);
182062306a36Sopenharmony_ci	bcc_ptr = pByteArea(smb_buf);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
182362306a36Sopenharmony_ci	if (blob_len > bytes_remaining) {
182462306a36Sopenharmony_ci		cifs_dbg(VFS, "bad security blob length %d\n",
182562306a36Sopenharmony_ci				blob_len);
182662306a36Sopenharmony_ci		rc = -EINVAL;
182762306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ciout_free_ntlmsspblob:
183362306a36Sopenharmony_ci	kfree_sensitive(ntlmsspblob);
183462306a36Sopenharmony_ciout:
183562306a36Sopenharmony_ci	sess_free_buffer(sess_data);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	if (!rc) {
183862306a36Sopenharmony_ci		sess_data->func = sess_auth_rawntlmssp_authenticate;
183962306a36Sopenharmony_ci		return;
184062306a36Sopenharmony_ci	}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	/* Else error. Cleanup */
184362306a36Sopenharmony_ci	kfree_sensitive(ses->auth_key.response);
184462306a36Sopenharmony_ci	ses->auth_key.response = NULL;
184562306a36Sopenharmony_ci	kfree_sensitive(ses->ntlmssp);
184662306a36Sopenharmony_ci	ses->ntlmssp = NULL;
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	sess_data->func = NULL;
184962306a36Sopenharmony_ci	sess_data->result = rc;
185062306a36Sopenharmony_ci}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_cistatic void
185362306a36Sopenharmony_cisess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
185462306a36Sopenharmony_ci{
185562306a36Sopenharmony_ci	int rc;
185662306a36Sopenharmony_ci	struct smb_hdr *smb_buf;
185762306a36Sopenharmony_ci	SESSION_SETUP_ANDX *pSMB;
185862306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
185962306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
186062306a36Sopenharmony_ci	__u16 bytes_remaining;
186162306a36Sopenharmony_ci	char *bcc_ptr;
186262306a36Sopenharmony_ci	unsigned char *ntlmsspblob = NULL;
186362306a36Sopenharmony_ci	u16 blob_len;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	/* wct = 12 */
186862306a36Sopenharmony_ci	rc = sess_alloc_buffer(sess_data, 12);
186962306a36Sopenharmony_ci	if (rc)
187062306a36Sopenharmony_ci		goto out;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	/* Build security blob before we assemble the request */
187362306a36Sopenharmony_ci	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
187462306a36Sopenharmony_ci	smb_buf = (struct smb_hdr *)pSMB;
187562306a36Sopenharmony_ci	rc = build_ntlmssp_auth_blob(&ntlmsspblob,
187662306a36Sopenharmony_ci					&blob_len, ses, server,
187762306a36Sopenharmony_ci					sess_data->nls_cp);
187862306a36Sopenharmony_ci	if (rc)
187962306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
188062306a36Sopenharmony_ci	sess_data->iov[1].iov_len = blob_len;
188162306a36Sopenharmony_ci	sess_data->iov[1].iov_base = ntlmsspblob;
188262306a36Sopenharmony_ci	pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
188362306a36Sopenharmony_ci	/*
188462306a36Sopenharmony_ci	 * Make sure that we tell the server that we are using
188562306a36Sopenharmony_ci	 * the uid that it just gave us back on the response
188662306a36Sopenharmony_ci	 * (challenge)
188762306a36Sopenharmony_ci	 */
188862306a36Sopenharmony_ci	smb_buf->Uid = ses->Suid;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
189162306a36Sopenharmony_ci	if (rc)
189262306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	rc = sess_sendreceive(sess_data);
189562306a36Sopenharmony_ci	if (rc)
189662306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
189962306a36Sopenharmony_ci	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
190062306a36Sopenharmony_ci	if (smb_buf->WordCount != 4) {
190162306a36Sopenharmony_ci		rc = -EIO;
190262306a36Sopenharmony_ci		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
190362306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
190462306a36Sopenharmony_ci	}
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
190762306a36Sopenharmony_ci		cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	if (ses->Suid != smb_buf->Uid) {
191062306a36Sopenharmony_ci		ses->Suid = smb_buf->Uid;
191162306a36Sopenharmony_ci		cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid);
191262306a36Sopenharmony_ci	}
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	bytes_remaining = get_bcc(smb_buf);
191562306a36Sopenharmony_ci	bcc_ptr = pByteArea(smb_buf);
191662306a36Sopenharmony_ci	blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
191762306a36Sopenharmony_ci	if (blob_len > bytes_remaining) {
191862306a36Sopenharmony_ci		cifs_dbg(VFS, "bad security blob length %d\n",
191962306a36Sopenharmony_ci				blob_len);
192062306a36Sopenharmony_ci		rc = -EINVAL;
192162306a36Sopenharmony_ci		goto out_free_ntlmsspblob;
192262306a36Sopenharmony_ci	}
192362306a36Sopenharmony_ci	bcc_ptr += blob_len;
192462306a36Sopenharmony_ci	bytes_remaining -= blob_len;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	/* BB check if Unicode and decode strings */
192862306a36Sopenharmony_ci	if (bytes_remaining == 0) {
192962306a36Sopenharmony_ci		/* no string area to decode, do nothing */
193062306a36Sopenharmony_ci	} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
193162306a36Sopenharmony_ci		/* unicode string area must be word-aligned */
193262306a36Sopenharmony_ci		if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
193362306a36Sopenharmony_ci			++bcc_ptr;
193462306a36Sopenharmony_ci			--bytes_remaining;
193562306a36Sopenharmony_ci		}
193662306a36Sopenharmony_ci		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
193762306a36Sopenharmony_ci				      sess_data->nls_cp);
193862306a36Sopenharmony_ci	} else {
193962306a36Sopenharmony_ci		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
194062306a36Sopenharmony_ci				    sess_data->nls_cp);
194162306a36Sopenharmony_ci	}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ciout_free_ntlmsspblob:
194462306a36Sopenharmony_ci	kfree_sensitive(ntlmsspblob);
194562306a36Sopenharmony_ciout:
194662306a36Sopenharmony_ci	sess_free_buffer(sess_data);
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	if (!rc)
194962306a36Sopenharmony_ci		rc = sess_establish_session(sess_data);
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	/* Cleanup */
195262306a36Sopenharmony_ci	kfree_sensitive(ses->auth_key.response);
195362306a36Sopenharmony_ci	ses->auth_key.response = NULL;
195462306a36Sopenharmony_ci	kfree_sensitive(ses->ntlmssp);
195562306a36Sopenharmony_ci	ses->ntlmssp = NULL;
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	sess_data->func = NULL;
195862306a36Sopenharmony_ci	sess_data->result = rc;
195962306a36Sopenharmony_ci}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_cistatic int select_sec(struct sess_data *sess_data)
196262306a36Sopenharmony_ci{
196362306a36Sopenharmony_ci	int type;
196462306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
196562306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	type = cifs_select_sectype(server, ses->sectype);
196862306a36Sopenharmony_ci	cifs_dbg(FYI, "sess setup type %d\n", type);
196962306a36Sopenharmony_ci	if (type == Unspecified) {
197062306a36Sopenharmony_ci		cifs_dbg(VFS, "Unable to select appropriate authentication method!\n");
197162306a36Sopenharmony_ci		return -EINVAL;
197262306a36Sopenharmony_ci	}
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	switch (type) {
197562306a36Sopenharmony_ci	case NTLMv2:
197662306a36Sopenharmony_ci		sess_data->func = sess_auth_ntlmv2;
197762306a36Sopenharmony_ci		break;
197862306a36Sopenharmony_ci	case Kerberos:
197962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_UPCALL
198062306a36Sopenharmony_ci		sess_data->func = sess_auth_kerberos;
198162306a36Sopenharmony_ci		break;
198262306a36Sopenharmony_ci#else
198362306a36Sopenharmony_ci		cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
198462306a36Sopenharmony_ci		return -ENOSYS;
198562306a36Sopenharmony_ci#endif /* CONFIG_CIFS_UPCALL */
198662306a36Sopenharmony_ci	case RawNTLMSSP:
198762306a36Sopenharmony_ci		sess_data->func = sess_auth_rawntlmssp_negotiate;
198862306a36Sopenharmony_ci		break;
198962306a36Sopenharmony_ci	default:
199062306a36Sopenharmony_ci		cifs_dbg(VFS, "secType %d not supported!\n", type);
199162306a36Sopenharmony_ci		return -ENOSYS;
199262306a36Sopenharmony_ci	}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	return 0;
199562306a36Sopenharmony_ci}
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ciint CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
199862306a36Sopenharmony_ci		   struct TCP_Server_Info *server,
199962306a36Sopenharmony_ci		   const struct nls_table *nls_cp)
200062306a36Sopenharmony_ci{
200162306a36Sopenharmony_ci	int rc = 0;
200262306a36Sopenharmony_ci	struct sess_data *sess_data;
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	if (ses == NULL) {
200562306a36Sopenharmony_ci		WARN(1, "%s: ses == NULL!", __func__);
200662306a36Sopenharmony_ci		return -EINVAL;
200762306a36Sopenharmony_ci	}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL);
201062306a36Sopenharmony_ci	if (!sess_data)
201162306a36Sopenharmony_ci		return -ENOMEM;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	sess_data->xid = xid;
201462306a36Sopenharmony_ci	sess_data->ses = ses;
201562306a36Sopenharmony_ci	sess_data->server = server;
201662306a36Sopenharmony_ci	sess_data->buf0_type = CIFS_NO_BUFFER;
201762306a36Sopenharmony_ci	sess_data->nls_cp = (struct nls_table *) nls_cp;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	rc = select_sec(sess_data);
202062306a36Sopenharmony_ci	if (rc)
202162306a36Sopenharmony_ci		goto out;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	while (sess_data->func)
202462306a36Sopenharmony_ci		sess_data->func(sess_data);
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	/* Store result before we free sess_data */
202762306a36Sopenharmony_ci	rc = sess_data->result;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ciout:
203062306a36Sopenharmony_ci	kfree_sensitive(sess_data);
203162306a36Sopenharmony_ci	return rc;
203262306a36Sopenharmony_ci}
203362306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
2034