xref: /linux/fs/smb/client/smb1session.c (revision bf4afc53b77aeaa48b5409da5c8da6bb4eff7f43)
1dec5a519SDavid Howells // SPDX-License-Identifier: LGPL-2.1
2dec5a519SDavid Howells /*
3dec5a519SDavid Howells  *
4dec5a519SDavid Howells  *   SMB/CIFS session setup handling routines
5dec5a519SDavid Howells  *
6dec5a519SDavid Howells  *   Copyright (c) International Business Machines  Corp., 2006, 2009
7dec5a519SDavid Howells  *   Author(s): Steve French (sfrench@us.ibm.com)
8dec5a519SDavid Howells  *
9dec5a519SDavid Howells  */
10dec5a519SDavid Howells 
11dec5a519SDavid Howells #include "cifsproto.h"
12dec5a519SDavid Howells #include "smb1proto.h"
13dec5a519SDavid Howells #include "ntlmssp.h"
14dec5a519SDavid Howells #include "nterr.h"
15dec5a519SDavid Howells #include "cifs_spnego.h"
16dec5a519SDavid Howells #include "cifs_unicode.h"
17dec5a519SDavid Howells #include "cifs_debug.h"
18dec5a519SDavid Howells 
19dec5a519SDavid Howells struct sess_data {
20dec5a519SDavid Howells 	unsigned int xid;
21dec5a519SDavid Howells 	struct cifs_ses *ses;
22dec5a519SDavid Howells 	struct TCP_Server_Info *server;
23dec5a519SDavid Howells 	struct nls_table *nls_cp;
24dec5a519SDavid Howells 	void (*func)(struct sess_data *);
25dec5a519SDavid Howells 	int result;
26dec5a519SDavid Howells 	unsigned int in_len;
27dec5a519SDavid Howells 
28dec5a519SDavid Howells 	/* we will send the SMB in three pieces:
29dec5a519SDavid Howells 	 * a fixed length beginning part, an optional
30dec5a519SDavid Howells 	 * SPNEGO blob (which can be zero length), and a
31dec5a519SDavid Howells 	 * last part which will include the strings
32dec5a519SDavid Howells 	 * and rest of bcc area. This allows us to avoid
33dec5a519SDavid Howells 	 * a large buffer 17K allocation
34dec5a519SDavid Howells 	 */
35dec5a519SDavid Howells 	int buf0_type;
36dec5a519SDavid Howells 	struct kvec iov[3];
37dec5a519SDavid Howells };
38dec5a519SDavid Howells 
cifs_ssetup_hdr(struct cifs_ses * ses,struct TCP_Server_Info * server,SESSION_SETUP_ANDX * pSMB)39dec5a519SDavid Howells static __u32 cifs_ssetup_hdr(struct cifs_ses *ses,
40dec5a519SDavid Howells 			     struct TCP_Server_Info *server,
41dec5a519SDavid Howells 			     SESSION_SETUP_ANDX *pSMB)
42dec5a519SDavid Howells {
43dec5a519SDavid Howells 	__u32 capabilities = 0;
44dec5a519SDavid Howells 
45dec5a519SDavid Howells 	/* init fields common to all four types of SessSetup */
46dec5a519SDavid Howells 	/* Note that offsets for first seven fields in req struct are same  */
47dec5a519SDavid Howells 	/*	in CIFS Specs so does not matter which of 3 forms of struct */
48dec5a519SDavid Howells 	/*	that we use in next few lines                               */
49dec5a519SDavid Howells 	/* Note that header is initialized to zero in header_assemble */
50dec5a519SDavid Howells 	pSMB->req.AndXCommand = 0xFF;
51dec5a519SDavid Howells 	pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32,
52dec5a519SDavid Howells 					CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4,
53dec5a519SDavid Howells 					USHRT_MAX));
54dec5a519SDavid Howells 	pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq);
55dec5a519SDavid Howells 	pSMB->req.VcNumber = cpu_to_le16(1);
56dec5a519SDavid Howells 	pSMB->req.SessionKey = server->session_key_id;
57dec5a519SDavid Howells 
58dec5a519SDavid Howells 	/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
59dec5a519SDavid Howells 
60dec5a519SDavid Howells 	/* BB verify whether signing required on neg or just auth frame (and NTLM case) */
61dec5a519SDavid Howells 
62dec5a519SDavid Howells 	capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
63dec5a519SDavid Howells 			CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
64dec5a519SDavid Howells 
65dec5a519SDavid Howells 	if (server->sign)
66dec5a519SDavid Howells 		pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
67dec5a519SDavid Howells 
68dec5a519SDavid Howells 	if (ses->capabilities & CAP_UNICODE) {
69dec5a519SDavid Howells 		pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE;
70dec5a519SDavid Howells 		capabilities |= CAP_UNICODE;
71dec5a519SDavid Howells 	}
72dec5a519SDavid Howells 	if (ses->capabilities & CAP_STATUS32) {
73dec5a519SDavid Howells 		pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS;
74dec5a519SDavid Howells 		capabilities |= CAP_STATUS32;
75dec5a519SDavid Howells 	}
76dec5a519SDavid Howells 	if (ses->capabilities & CAP_DFS) {
77dec5a519SDavid Howells 		pSMB->req.hdr.Flags2 |= SMBFLG2_DFS;
78dec5a519SDavid Howells 		capabilities |= CAP_DFS;
79dec5a519SDavid Howells 	}
80dec5a519SDavid Howells 	if (ses->capabilities & CAP_UNIX)
81dec5a519SDavid Howells 		capabilities |= CAP_UNIX;
82dec5a519SDavid Howells 
83dec5a519SDavid Howells 	return capabilities;
84dec5a519SDavid Howells }
85dec5a519SDavid Howells 
86dec5a519SDavid Howells static void
unicode_oslm_strings(char ** pbcc_area,const struct nls_table * nls_cp)87dec5a519SDavid Howells unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp)
88dec5a519SDavid Howells {
89dec5a519SDavid Howells 	char *bcc_ptr = *pbcc_area;
90dec5a519SDavid Howells 	int bytes_ret = 0;
91dec5a519SDavid Howells 
92dec5a519SDavid Howells 	/* Copy OS version */
93dec5a519SDavid Howells 	bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32,
94dec5a519SDavid Howells 				    nls_cp);
95dec5a519SDavid Howells 	bcc_ptr += 2 * bytes_ret;
96dec5a519SDavid Howells 	bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release,
97dec5a519SDavid Howells 				    32, nls_cp);
98dec5a519SDavid Howells 	bcc_ptr += 2 * bytes_ret;
99dec5a519SDavid Howells 	bcc_ptr += 2; /* trailing null */
100dec5a519SDavid Howells 
101dec5a519SDavid Howells 	bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
102dec5a519SDavid Howells 				    32, nls_cp);
103dec5a519SDavid Howells 	bcc_ptr += 2 * bytes_ret;
104dec5a519SDavid Howells 	bcc_ptr += 2; /* trailing null */
105dec5a519SDavid Howells 
106dec5a519SDavid Howells 	*pbcc_area = bcc_ptr;
107dec5a519SDavid Howells }
108dec5a519SDavid Howells 
109dec5a519SDavid Howells static void
ascii_oslm_strings(char ** pbcc_area,const struct nls_table * nls_cp)110dec5a519SDavid Howells ascii_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp)
111dec5a519SDavid Howells {
112dec5a519SDavid Howells 	char *bcc_ptr = *pbcc_area;
113dec5a519SDavid Howells 
114dec5a519SDavid Howells 	strcpy(bcc_ptr, "Linux version ");
115dec5a519SDavid Howells 	bcc_ptr += strlen("Linux version ");
116dec5a519SDavid Howells 	strcpy(bcc_ptr, init_utsname()->release);
117dec5a519SDavid Howells 	bcc_ptr += strlen(init_utsname()->release) + 1;
118dec5a519SDavid Howells 
119dec5a519SDavid Howells 	strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
120dec5a519SDavid Howells 	bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
121dec5a519SDavid Howells 
122dec5a519SDavid Howells 	*pbcc_area = bcc_ptr;
123dec5a519SDavid Howells }
124dec5a519SDavid Howells 
unicode_domain_string(char ** pbcc_area,struct cifs_ses * ses,const struct nls_table * nls_cp)125dec5a519SDavid Howells static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses,
126dec5a519SDavid Howells 				   const struct nls_table *nls_cp)
127dec5a519SDavid Howells {
128dec5a519SDavid Howells 	char *bcc_ptr = *pbcc_area;
129dec5a519SDavid Howells 	int bytes_ret = 0;
130dec5a519SDavid Howells 
131dec5a519SDavid Howells 	/* copy domain */
132dec5a519SDavid Howells 	if (ses->domainName == NULL) {
133dec5a519SDavid Howells 		/*
134dec5a519SDavid Howells 		 * Sending null domain better than using a bogus domain name (as
135dec5a519SDavid Howells 		 * we did briefly in 2.6.18) since server will use its default
136dec5a519SDavid Howells 		 */
137dec5a519SDavid Howells 		*bcc_ptr = 0;
138dec5a519SDavid Howells 		*(bcc_ptr+1) = 0;
139dec5a519SDavid Howells 		bytes_ret = 0;
140dec5a519SDavid Howells 	} else
141dec5a519SDavid Howells 		bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName,
142dec5a519SDavid Howells 					    CIFS_MAX_DOMAINNAME_LEN, nls_cp);
143dec5a519SDavid Howells 	bcc_ptr += 2 * bytes_ret;
144dec5a519SDavid Howells 	bcc_ptr += 2;  /* account for null terminator */
145dec5a519SDavid Howells 
146dec5a519SDavid Howells 	*pbcc_area = bcc_ptr;
147dec5a519SDavid Howells }
148dec5a519SDavid Howells 
ascii_domain_string(char ** pbcc_area,struct cifs_ses * ses,const struct nls_table * nls_cp)149dec5a519SDavid Howells static void ascii_domain_string(char **pbcc_area, struct cifs_ses *ses,
150dec5a519SDavid Howells 				const struct nls_table *nls_cp)
151dec5a519SDavid Howells {
152dec5a519SDavid Howells 	char *bcc_ptr = *pbcc_area;
153dec5a519SDavid Howells 	int len;
154dec5a519SDavid Howells 
155dec5a519SDavid Howells 	/* copy domain */
156dec5a519SDavid Howells 	if (ses->domainName != NULL) {
157dec5a519SDavid Howells 		len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
158dec5a519SDavid Howells 		if (WARN_ON_ONCE(len < 0))
159dec5a519SDavid Howells 			len = CIFS_MAX_DOMAINNAME_LEN - 1;
160dec5a519SDavid Howells 		bcc_ptr += len;
161dec5a519SDavid Howells 	} /* else we send a null domain name so server will default to its own domain */
162dec5a519SDavid Howells 	*bcc_ptr = 0;
163dec5a519SDavid Howells 	bcc_ptr++;
164dec5a519SDavid Howells 
165dec5a519SDavid Howells 	*pbcc_area = bcc_ptr;
166dec5a519SDavid Howells }
167dec5a519SDavid Howells 
unicode_ssetup_strings(char ** pbcc_area,struct cifs_ses * ses,const struct nls_table * nls_cp)168dec5a519SDavid Howells static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
169dec5a519SDavid Howells 				   const struct nls_table *nls_cp)
170dec5a519SDavid Howells {
171dec5a519SDavid Howells 	char *bcc_ptr = *pbcc_area;
172dec5a519SDavid Howells 	int bytes_ret = 0;
173dec5a519SDavid Howells 
174dec5a519SDavid Howells 	/* BB FIXME add check that strings less than 335 or will need to send as arrays */
175dec5a519SDavid Howells 
176dec5a519SDavid Howells 	/* copy user */
177dec5a519SDavid Howells 	if (ses->user_name == NULL) {
178dec5a519SDavid Howells 		/* null user mount */
179dec5a519SDavid Howells 		*bcc_ptr = 0;
180dec5a519SDavid Howells 		*(bcc_ptr+1) = 0;
181dec5a519SDavid Howells 	} else {
182dec5a519SDavid Howells 		bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name,
183dec5a519SDavid Howells 					    CIFS_MAX_USERNAME_LEN, nls_cp);
184dec5a519SDavid Howells 	}
185dec5a519SDavid Howells 	bcc_ptr += 2 * bytes_ret;
186dec5a519SDavid Howells 	bcc_ptr += 2; /* account for null termination */
187dec5a519SDavid Howells 
188dec5a519SDavid Howells 	unicode_domain_string(&bcc_ptr, ses, nls_cp);
189dec5a519SDavid Howells 	unicode_oslm_strings(&bcc_ptr, nls_cp);
190dec5a519SDavid Howells 
191dec5a519SDavid Howells 	*pbcc_area = bcc_ptr;
192dec5a519SDavid Howells }
193dec5a519SDavid Howells 
ascii_ssetup_strings(char ** pbcc_area,struct cifs_ses * ses,const struct nls_table * nls_cp)194dec5a519SDavid Howells static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
195dec5a519SDavid Howells 				 const struct nls_table *nls_cp)
196dec5a519SDavid Howells {
197dec5a519SDavid Howells 	char *bcc_ptr = *pbcc_area;
198dec5a519SDavid Howells 	int len;
199dec5a519SDavid Howells 
200dec5a519SDavid Howells 	/* copy user */
201dec5a519SDavid Howells 	/* BB what about null user mounts - check that we do this BB */
202dec5a519SDavid Howells 	/* copy user */
203dec5a519SDavid Howells 	if (ses->user_name != NULL) {
204dec5a519SDavid Howells 		len = strscpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN);
205dec5a519SDavid Howells 		if (WARN_ON_ONCE(len < 0))
206dec5a519SDavid Howells 			len = CIFS_MAX_USERNAME_LEN - 1;
207dec5a519SDavid Howells 		bcc_ptr += len;
208dec5a519SDavid Howells 	}
209dec5a519SDavid Howells 	/* else null user mount */
210dec5a519SDavid Howells 	*bcc_ptr = 0;
211dec5a519SDavid Howells 	bcc_ptr++; /* account for null termination */
212dec5a519SDavid Howells 
213dec5a519SDavid Howells 	/* BB check for overflow here */
214dec5a519SDavid Howells 
215dec5a519SDavid Howells 	ascii_domain_string(&bcc_ptr, ses, nls_cp);
216dec5a519SDavid Howells 	ascii_oslm_strings(&bcc_ptr, nls_cp);
217dec5a519SDavid Howells 
218dec5a519SDavid Howells 	*pbcc_area = bcc_ptr;
219dec5a519SDavid Howells }
220dec5a519SDavid Howells 
221dec5a519SDavid Howells static void
decode_unicode_ssetup(char ** pbcc_area,int bleft,struct cifs_ses * ses,const struct nls_table * nls_cp)222dec5a519SDavid Howells decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses,
223dec5a519SDavid Howells 		      const struct nls_table *nls_cp)
224dec5a519SDavid Howells {
225dec5a519SDavid Howells 	int len;
226dec5a519SDavid Howells 	char *data = *pbcc_area;
227dec5a519SDavid Howells 
228dec5a519SDavid Howells 	cifs_dbg(FYI, "bleft %d\n", bleft);
229dec5a519SDavid Howells 
230dec5a519SDavid Howells 	kfree(ses->serverOS);
231dec5a519SDavid Howells 	ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
232dec5a519SDavid Howells 	cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS);
233dec5a519SDavid Howells 	len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;
234dec5a519SDavid Howells 	data += len;
235dec5a519SDavid Howells 	bleft -= len;
236dec5a519SDavid Howells 	if (bleft <= 0)
237dec5a519SDavid Howells 		return;
238dec5a519SDavid Howells 
239dec5a519SDavid Howells 	kfree(ses->serverNOS);
240dec5a519SDavid Howells 	ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
241dec5a519SDavid Howells 	cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS);
242dec5a519SDavid Howells 	len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;
243dec5a519SDavid Howells 	data += len;
244dec5a519SDavid Howells 	bleft -= len;
245dec5a519SDavid Howells 	if (bleft <= 0)
246dec5a519SDavid Howells 		return;
247dec5a519SDavid Howells 
248dec5a519SDavid Howells 	kfree(ses->serverDomain);
249dec5a519SDavid Howells 	ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
250dec5a519SDavid Howells 	cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain);
251dec5a519SDavid Howells 
252dec5a519SDavid Howells 	return;
253dec5a519SDavid Howells }
254dec5a519SDavid Howells 
decode_ascii_ssetup(char ** pbcc_area,__u16 bleft,struct cifs_ses * ses,const struct nls_table * nls_cp)255dec5a519SDavid Howells static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft,
256dec5a519SDavid Howells 				struct cifs_ses *ses,
257dec5a519SDavid Howells 				const struct nls_table *nls_cp)
258dec5a519SDavid Howells {
259dec5a519SDavid Howells 	int len;
260dec5a519SDavid Howells 	char *bcc_ptr = *pbcc_area;
261dec5a519SDavid Howells 
262dec5a519SDavid Howells 	cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft);
263dec5a519SDavid Howells 
264dec5a519SDavid Howells 	len = strnlen(bcc_ptr, bleft);
265dec5a519SDavid Howells 	if (len >= bleft)
266dec5a519SDavid Howells 		return;
267dec5a519SDavid Howells 
268dec5a519SDavid Howells 	kfree(ses->serverOS);
269dec5a519SDavid Howells 
270dec5a519SDavid Howells 	ses->serverOS = kmalloc(len + 1, GFP_KERNEL);
271dec5a519SDavid Howells 	if (ses->serverOS) {
272dec5a519SDavid Howells 		memcpy(ses->serverOS, bcc_ptr, len);
273dec5a519SDavid Howells 		ses->serverOS[len] = 0;
274dec5a519SDavid Howells 		if (strncmp(ses->serverOS, "OS/2", 4) == 0)
275dec5a519SDavid Howells 			cifs_dbg(FYI, "OS/2 server\n");
276dec5a519SDavid Howells 	}
277dec5a519SDavid Howells 
278dec5a519SDavid Howells 	bcc_ptr += len + 1;
279dec5a519SDavid Howells 	bleft -= len + 1;
280dec5a519SDavid Howells 
281dec5a519SDavid Howells 	len = strnlen(bcc_ptr, bleft);
282dec5a519SDavid Howells 	if (len >= bleft)
283dec5a519SDavid Howells 		return;
284dec5a519SDavid Howells 
285dec5a519SDavid Howells 	kfree(ses->serverNOS);
286dec5a519SDavid Howells 
287dec5a519SDavid Howells 	ses->serverNOS = kmalloc(len + 1, GFP_KERNEL);
288dec5a519SDavid Howells 	if (ses->serverNOS) {
289dec5a519SDavid Howells 		memcpy(ses->serverNOS, bcc_ptr, len);
290dec5a519SDavid Howells 		ses->serverNOS[len] = 0;
291dec5a519SDavid Howells 	}
292dec5a519SDavid Howells 
293dec5a519SDavid Howells 	bcc_ptr += len + 1;
294dec5a519SDavid Howells 	bleft -= len + 1;
295dec5a519SDavid Howells 
296dec5a519SDavid Howells 	len = strnlen(bcc_ptr, bleft);
297dec5a519SDavid Howells 	if (len > bleft)
298dec5a519SDavid Howells 		return;
299dec5a519SDavid Howells 
300dec5a519SDavid Howells 	/*
301dec5a519SDavid Howells 	 * No domain field in LANMAN case. Domain is
302dec5a519SDavid Howells 	 * returned by old servers in the SMB negprot response
303dec5a519SDavid Howells 	 *
304dec5a519SDavid Howells 	 * BB For newer servers which do not support Unicode,
305dec5a519SDavid Howells 	 * but thus do return domain here, we could add parsing
306dec5a519SDavid Howells 	 * for it later, but it is not very important
307dec5a519SDavid Howells 	 */
308dec5a519SDavid Howells 	cifs_dbg(FYI, "ascii: bytes left %d\n", bleft);
309dec5a519SDavid Howells }
310dec5a519SDavid Howells 
311dec5a519SDavid Howells static int
sess_alloc_buffer(struct sess_data * sess_data,int wct)312dec5a519SDavid Howells sess_alloc_buffer(struct sess_data *sess_data, int wct)
313dec5a519SDavid Howells {
314dec5a519SDavid Howells 	int rc;
315dec5a519SDavid Howells 	struct cifs_ses *ses = sess_data->ses;
316dec5a519SDavid Howells 	struct smb_hdr *smb_buf;
317dec5a519SDavid Howells 
318dec5a519SDavid Howells 	rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
319dec5a519SDavid Howells 				  (void **)&smb_buf);
320dec5a519SDavid Howells 
321dec5a519SDavid Howells 	if (rc < 0)
322dec5a519SDavid Howells 		return rc;
323dec5a519SDavid Howells 
324dec5a519SDavid Howells 	sess_data->in_len = rc;
325dec5a519SDavid Howells 	sess_data->iov[0].iov_base = (char *)smb_buf;
326dec5a519SDavid Howells 	sess_data->iov[0].iov_len = sess_data->in_len;
327dec5a519SDavid Howells 	/*
328dec5a519SDavid Howells 	 * This variable will be used to clear the buffer
329dec5a519SDavid Howells 	 * allocated above in case of any error in the calling function.
330dec5a519SDavid Howells 	 */
331dec5a519SDavid Howells 	sess_data->buf0_type = CIFS_SMALL_BUFFER;
332dec5a519SDavid Howells 
333dec5a519SDavid Howells 	/* 2000 big enough to fit max user, domain, NOS name etc. */
334dec5a519SDavid Howells 	sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL);
335dec5a519SDavid Howells 	if (!sess_data->iov[2].iov_base) {
336dec5a519SDavid Howells 		rc = -ENOMEM;
337dec5a519SDavid Howells 		goto out_free_smb_buf;
338dec5a519SDavid Howells 	}
339dec5a519SDavid Howells 
340dec5a519SDavid Howells 	return 0;
341dec5a519SDavid Howells 
342dec5a519SDavid Howells out_free_smb_buf:
343dec5a519SDavid Howells 	cifs_small_buf_release(smb_buf);
344dec5a519SDavid Howells 	sess_data->iov[0].iov_base = NULL;
345dec5a519SDavid Howells 	sess_data->iov[0].iov_len = 0;
346dec5a519SDavid Howells 	sess_data->buf0_type = CIFS_NO_BUFFER;
347dec5a519SDavid Howells 	return rc;
348dec5a519SDavid Howells }
349dec5a519SDavid Howells 
350dec5a519SDavid Howells static void
sess_free_buffer(struct sess_data * sess_data)351dec5a519SDavid Howells sess_free_buffer(struct sess_data *sess_data)
352dec5a519SDavid Howells {
353dec5a519SDavid Howells 	struct kvec *iov = sess_data->iov;
354dec5a519SDavid Howells 
355dec5a519SDavid Howells 	/*
356dec5a519SDavid Howells 	 * Zero the session data before freeing, as it might contain sensitive info (keys, etc).
357dec5a519SDavid Howells 	 * Note that iov[1] is already freed by caller.
358dec5a519SDavid Howells 	 */
359dec5a519SDavid Howells 	if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base)
360dec5a519SDavid Howells 		memzero_explicit(iov[0].iov_base, iov[0].iov_len);
361dec5a519SDavid Howells 
362dec5a519SDavid Howells 	free_rsp_buf(sess_data->buf0_type, iov[0].iov_base);
363dec5a519SDavid Howells 	sess_data->buf0_type = CIFS_NO_BUFFER;
364dec5a519SDavid Howells 	kfree_sensitive(iov[2].iov_base);
365dec5a519SDavid Howells }
366dec5a519SDavid Howells 
367dec5a519SDavid Howells static int
sess_establish_session(struct sess_data * sess_data)368dec5a519SDavid Howells sess_establish_session(struct sess_data *sess_data)
369dec5a519SDavid Howells {
370dec5a519SDavid Howells 	struct cifs_ses *ses = sess_data->ses;
371dec5a519SDavid Howells 	struct TCP_Server_Info *server = sess_data->server;
372dec5a519SDavid Howells 
373dec5a519SDavid Howells 	cifs_server_lock(server);
374dec5a519SDavid Howells 	if (!server->session_estab) {
375dec5a519SDavid Howells 		if (server->sign) {
376dec5a519SDavid Howells 			server->session_key.response =
377dec5a519SDavid Howells 				kmemdup(ses->auth_key.response,
378dec5a519SDavid Howells 				ses->auth_key.len, GFP_KERNEL);
379dec5a519SDavid Howells 			if (!server->session_key.response) {
380dec5a519SDavid Howells 				cifs_server_unlock(server);
381dec5a519SDavid Howells 				return -ENOMEM;
382dec5a519SDavid Howells 			}
383dec5a519SDavid Howells 			server->session_key.len =
384dec5a519SDavid Howells 						ses->auth_key.len;
385dec5a519SDavid Howells 		}
386dec5a519SDavid Howells 		server->sequence_number = 0x2;
387dec5a519SDavid Howells 		server->session_estab = true;
388dec5a519SDavid Howells 	}
389dec5a519SDavid Howells 	cifs_server_unlock(server);
390dec5a519SDavid Howells 
391dec5a519SDavid Howells 	cifs_dbg(FYI, "CIFS session established successfully\n");
392dec5a519SDavid Howells 	return 0;
393dec5a519SDavid Howells }
394dec5a519SDavid Howells 
395dec5a519SDavid Howells static int
sess_sendreceive(struct sess_data * sess_data)396dec5a519SDavid Howells sess_sendreceive(struct sess_data *sess_data)
397dec5a519SDavid Howells {
398dec5a519SDavid Howells 	int rc;
399dec5a519SDavid Howells 	struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base;
400dec5a519SDavid Howells 	__u16 count;
401dec5a519SDavid Howells 	struct kvec rsp_iov = { NULL, 0 };
402dec5a519SDavid Howells 
403dec5a519SDavid Howells 	count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
404dec5a519SDavid Howells 	sess_data->in_len += count;
405dec5a519SDavid Howells 	put_bcc(count, smb_buf);
406dec5a519SDavid Howells 
407dec5a519SDavid Howells 	rc = SendReceive2(sess_data->xid, sess_data->ses,
408dec5a519SDavid Howells 			  sess_data->iov, 3 /* num_iovecs */,
409dec5a519SDavid Howells 			  &sess_data->buf0_type,
410dec5a519SDavid Howells 			  CIFS_LOG_ERROR, &rsp_iov);
411dec5a519SDavid Howells 	cifs_small_buf_release(sess_data->iov[0].iov_base);
412dec5a519SDavid Howells 	memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec));
413dec5a519SDavid Howells 
414dec5a519SDavid Howells 	return rc;
415dec5a519SDavid Howells }
416dec5a519SDavid Howells 
417dec5a519SDavid Howells static void
sess_auth_ntlmv2(struct sess_data * sess_data)418dec5a519SDavid Howells sess_auth_ntlmv2(struct sess_data *sess_data)
419dec5a519SDavid Howells {
420dec5a519SDavid Howells 	int rc = 0;
421dec5a519SDavid Howells 	struct smb_hdr *smb_buf;
422dec5a519SDavid Howells 	SESSION_SETUP_ANDX *pSMB;
423dec5a519SDavid Howells 	char *bcc_ptr;
424dec5a519SDavid Howells 	struct cifs_ses *ses = sess_data->ses;
425dec5a519SDavid Howells 	struct TCP_Server_Info *server = sess_data->server;
426dec5a519SDavid Howells 	__u32 capabilities;
427dec5a519SDavid Howells 	__u16 bytes_remaining;
428dec5a519SDavid Howells 
429dec5a519SDavid Howells 	/* old style NTLM sessionsetup */
430dec5a519SDavid Howells 	/* wct = 13 */
431dec5a519SDavid Howells 	rc = sess_alloc_buffer(sess_data, 13);
432dec5a519SDavid Howells 	if (rc)
433dec5a519SDavid Howells 		goto out;
434dec5a519SDavid Howells 
435dec5a519SDavid Howells 	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
436dec5a519SDavid Howells 	bcc_ptr = sess_data->iov[2].iov_base;
437dec5a519SDavid Howells 	capabilities = cifs_ssetup_hdr(ses, server, pSMB);
438dec5a519SDavid Howells 
439dec5a519SDavid Howells 	pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
440dec5a519SDavid Howells 
441dec5a519SDavid Howells 	/* LM2 password would be here if we supported it */
442dec5a519SDavid Howells 	pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
443dec5a519SDavid Howells 
444dec5a519SDavid Howells 	if (ses->user_name != NULL) {
445dec5a519SDavid Howells 		/* calculate nlmv2 response and session key */
446dec5a519SDavid Howells 		rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp);
447dec5a519SDavid Howells 		if (rc) {
448dec5a519SDavid Howells 			cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc);
449dec5a519SDavid Howells 			goto out;
450dec5a519SDavid Howells 		}
451dec5a519SDavid Howells 
452dec5a519SDavid Howells 		memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
453dec5a519SDavid Howells 				ses->auth_key.len - CIFS_SESS_KEY_SIZE);
454dec5a519SDavid Howells 		bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
455dec5a519SDavid Howells 
456dec5a519SDavid Howells 		/* set case sensitive password length after tilen may get
457dec5a519SDavid Howells 		 * assigned, tilen is 0 otherwise.
458dec5a519SDavid Howells 		 */
459dec5a519SDavid Howells 		pSMB->req_no_secext.CaseSensitivePasswordLength =
460dec5a519SDavid Howells 			cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
461dec5a519SDavid Howells 	} else {
462dec5a519SDavid Howells 		pSMB->req_no_secext.CaseSensitivePasswordLength = 0;
463dec5a519SDavid Howells 	}
464dec5a519SDavid Howells 
465dec5a519SDavid Howells 	if (ses->capabilities & CAP_UNICODE) {
466dec5a519SDavid Howells 		if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) {
467dec5a519SDavid Howells 			*bcc_ptr = 0;
468dec5a519SDavid Howells 			bcc_ptr++;
469dec5a519SDavid Howells 		}
470dec5a519SDavid Howells 		unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
471dec5a519SDavid Howells 	} else {
472dec5a519SDavid Howells 		ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
473dec5a519SDavid Howells 	}
474dec5a519SDavid Howells 
475dec5a519SDavid Howells 
476dec5a519SDavid Howells 	sess_data->iov[2].iov_len = (long) bcc_ptr -
477dec5a519SDavid Howells 			(long) sess_data->iov[2].iov_base;
478dec5a519SDavid Howells 
479dec5a519SDavid Howells 	rc = sess_sendreceive(sess_data);
480dec5a519SDavid Howells 	if (rc)
481dec5a519SDavid Howells 		goto out;
482dec5a519SDavid Howells 
483dec5a519SDavid Howells 	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
484dec5a519SDavid Howells 	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
485dec5a519SDavid Howells 
486dec5a519SDavid Howells 	if (smb_buf->WordCount != 3) {
487dec5a519SDavid Howells 		rc = smb_EIO1(smb_eio_trace_sess_nl2_wcc, smb_buf->WordCount);
488dec5a519SDavid Howells 		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
489dec5a519SDavid Howells 		goto out;
490dec5a519SDavid Howells 	}
491dec5a519SDavid Howells 
492dec5a519SDavid Howells 	if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
493dec5a519SDavid Howells 		cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
494dec5a519SDavid Howells 
495dec5a519SDavid Howells 	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
496dec5a519SDavid Howells 	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
497dec5a519SDavid Howells 
498dec5a519SDavid Howells 	bytes_remaining = get_bcc(smb_buf);
499dec5a519SDavid Howells 	bcc_ptr = pByteArea(smb_buf);
500dec5a519SDavid Howells 
501dec5a519SDavid Howells 	/* BB check if Unicode and decode strings */
502dec5a519SDavid Howells 	if (bytes_remaining == 0) {
503dec5a519SDavid Howells 		/* no string area to decode, do nothing */
504dec5a519SDavid Howells 	} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
505dec5a519SDavid Howells 		/* unicode string area must be word-aligned */
506dec5a519SDavid Howells 		if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
507dec5a519SDavid Howells 			++bcc_ptr;
508dec5a519SDavid Howells 			--bytes_remaining;
509dec5a519SDavid Howells 		}
510dec5a519SDavid Howells 		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
511dec5a519SDavid Howells 				      sess_data->nls_cp);
512dec5a519SDavid Howells 	} else {
513dec5a519SDavid Howells 		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
514dec5a519SDavid Howells 				    sess_data->nls_cp);
515dec5a519SDavid Howells 	}
516dec5a519SDavid Howells 
517dec5a519SDavid Howells 	rc = sess_establish_session(sess_data);
518dec5a519SDavid Howells out:
519dec5a519SDavid Howells 	sess_data->result = rc;
520dec5a519SDavid Howells 	sess_data->func = NULL;
521dec5a519SDavid Howells 	sess_free_buffer(sess_data);
522dec5a519SDavid Howells 	kfree_sensitive(ses->auth_key.response);
523dec5a519SDavid Howells 	ses->auth_key.response = NULL;
524dec5a519SDavid Howells }
525dec5a519SDavid Howells 
526dec5a519SDavid Howells #ifdef CONFIG_CIFS_UPCALL
527dec5a519SDavid Howells static void
sess_auth_kerberos(struct sess_data * sess_data)528dec5a519SDavid Howells sess_auth_kerberos(struct sess_data *sess_data)
529dec5a519SDavid Howells {
530dec5a519SDavid Howells 	int rc = 0;
531dec5a519SDavid Howells 	struct smb_hdr *smb_buf;
532dec5a519SDavid Howells 	SESSION_SETUP_ANDX *pSMB;
533dec5a519SDavid Howells 	char *bcc_ptr;
534dec5a519SDavid Howells 	struct cifs_ses *ses = sess_data->ses;
535dec5a519SDavid Howells 	struct TCP_Server_Info *server = sess_data->server;
536dec5a519SDavid Howells 	__u32 capabilities;
537dec5a519SDavid Howells 	__u16 bytes_remaining;
538dec5a519SDavid Howells 	struct key *spnego_key = NULL;
539dec5a519SDavid Howells 	struct cifs_spnego_msg *msg;
540dec5a519SDavid Howells 	u16 blob_len;
541dec5a519SDavid Howells 
542dec5a519SDavid Howells 	/* extended security */
543dec5a519SDavid Howells 	/* wct = 12 */
544dec5a519SDavid Howells 	rc = sess_alloc_buffer(sess_data, 12);
545dec5a519SDavid Howells 	if (rc)
546dec5a519SDavid Howells 		goto out;
547dec5a519SDavid Howells 
548dec5a519SDavid Howells 	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
549dec5a519SDavid Howells 	bcc_ptr = sess_data->iov[2].iov_base;
550dec5a519SDavid Howells 	capabilities = cifs_ssetup_hdr(ses, server, pSMB);
551dec5a519SDavid Howells 
552dec5a519SDavid Howells 	spnego_key = cifs_get_spnego_key(ses, server);
553dec5a519SDavid Howells 	if (IS_ERR(spnego_key)) {
554dec5a519SDavid Howells 		rc = PTR_ERR(spnego_key);
555dec5a519SDavid Howells 		spnego_key = NULL;
556dec5a519SDavid Howells 		goto out;
557dec5a519SDavid Howells 	}
558dec5a519SDavid Howells 
559dec5a519SDavid Howells 	msg = spnego_key->payload.data[0];
560dec5a519SDavid Howells 	/*
561dec5a519SDavid Howells 	 * check version field to make sure that cifs.upcall is
562dec5a519SDavid Howells 	 * sending us a response in an expected form
563dec5a519SDavid Howells 	 */
564dec5a519SDavid Howells 	if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
565dec5a519SDavid Howells 		cifs_dbg(VFS, "incorrect version of cifs.upcall (expected %d but got %d)\n",
566dec5a519SDavid Howells 			 CIFS_SPNEGO_UPCALL_VERSION, msg->version);
567dec5a519SDavid Howells 		rc = -EKEYREJECTED;
568dec5a519SDavid Howells 		goto out_put_spnego_key;
569dec5a519SDavid Howells 	}
570dec5a519SDavid Howells 
571dec5a519SDavid Howells 	kfree_sensitive(ses->auth_key.response);
572dec5a519SDavid Howells 	ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
573dec5a519SDavid Howells 					 GFP_KERNEL);
574dec5a519SDavid Howells 	if (!ses->auth_key.response) {
575dec5a519SDavid Howells 		cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n",
576dec5a519SDavid Howells 			 msg->sesskey_len);
577dec5a519SDavid Howells 		rc = -ENOMEM;
578dec5a519SDavid Howells 		goto out_put_spnego_key;
579dec5a519SDavid Howells 	}
580dec5a519SDavid Howells 	ses->auth_key.len = msg->sesskey_len;
581dec5a519SDavid Howells 
582dec5a519SDavid Howells 	pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
583dec5a519SDavid Howells 	capabilities |= CAP_EXTENDED_SECURITY;
584dec5a519SDavid Howells 	pSMB->req.Capabilities = cpu_to_le32(capabilities);
585dec5a519SDavid Howells 	sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
586dec5a519SDavid Howells 	sess_data->iov[1].iov_len = msg->secblob_len;
587dec5a519SDavid Howells 	pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len);
588dec5a519SDavid Howells 
589dec5a519SDavid Howells 	if (pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) {
590dec5a519SDavid Howells 		/* unicode strings must be word aligned */
591dec5a519SDavid Howells 		if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
592dec5a519SDavid Howells 			*bcc_ptr = 0;
593dec5a519SDavid Howells 			bcc_ptr++;
594dec5a519SDavid Howells 		}
595dec5a519SDavid Howells 		unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
596dec5a519SDavid Howells 		unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp);
597dec5a519SDavid Howells 	} else {
598dec5a519SDavid Howells 		ascii_oslm_strings(&bcc_ptr, sess_data->nls_cp);
599dec5a519SDavid Howells 		ascii_domain_string(&bcc_ptr, ses, sess_data->nls_cp);
600dec5a519SDavid Howells 	}
601dec5a519SDavid Howells 
602dec5a519SDavid Howells 	sess_data->iov[2].iov_len = (long) bcc_ptr -
603dec5a519SDavid Howells 			(long) sess_data->iov[2].iov_base;
604dec5a519SDavid Howells 
605dec5a519SDavid Howells 	rc = sess_sendreceive(sess_data);
606dec5a519SDavid Howells 	if (rc)
607dec5a519SDavid Howells 		goto out_put_spnego_key;
608dec5a519SDavid Howells 
609dec5a519SDavid Howells 	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
610dec5a519SDavid Howells 	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
611dec5a519SDavid Howells 
612dec5a519SDavid Howells 	if (smb_buf->WordCount != 4) {
613dec5a519SDavid Howells 		rc = smb_EIO1(smb_eio_trace_sess_krb_wcc, smb_buf->WordCount);
614dec5a519SDavid Howells 		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
615dec5a519SDavid Howells 		goto out_put_spnego_key;
616dec5a519SDavid Howells 	}
617dec5a519SDavid Howells 
618dec5a519SDavid Howells 	if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
619dec5a519SDavid Howells 		cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
620dec5a519SDavid Howells 
621dec5a519SDavid Howells 	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
622dec5a519SDavid Howells 	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
623dec5a519SDavid Howells 
624dec5a519SDavid Howells 	bytes_remaining = get_bcc(smb_buf);
625dec5a519SDavid Howells 	bcc_ptr = pByteArea(smb_buf);
626dec5a519SDavid Howells 
627dec5a519SDavid Howells 	blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
628dec5a519SDavid Howells 	if (blob_len > bytes_remaining) {
629dec5a519SDavid Howells 		cifs_dbg(VFS, "bad security blob length %d\n",
630dec5a519SDavid Howells 				blob_len);
631dec5a519SDavid Howells 		rc = -EINVAL;
632dec5a519SDavid Howells 		goto out_put_spnego_key;
633dec5a519SDavid Howells 	}
634dec5a519SDavid Howells 	bcc_ptr += blob_len;
635dec5a519SDavid Howells 	bytes_remaining -= blob_len;
636dec5a519SDavid Howells 
637dec5a519SDavid Howells 	/* BB check if Unicode and decode strings */
638dec5a519SDavid Howells 	if (bytes_remaining == 0) {
639dec5a519SDavid Howells 		/* no string area to decode, do nothing */
640dec5a519SDavid Howells 	} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
641dec5a519SDavid Howells 		/* unicode string area must be word-aligned */
642dec5a519SDavid Howells 		if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
643dec5a519SDavid Howells 			++bcc_ptr;
644dec5a519SDavid Howells 			--bytes_remaining;
645dec5a519SDavid Howells 		}
646dec5a519SDavid Howells 		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
647dec5a519SDavid Howells 				      sess_data->nls_cp);
648dec5a519SDavid Howells 	} else {
649dec5a519SDavid Howells 		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
650dec5a519SDavid Howells 				    sess_data->nls_cp);
651dec5a519SDavid Howells 	}
652dec5a519SDavid Howells 
653dec5a519SDavid Howells 	rc = sess_establish_session(sess_data);
654dec5a519SDavid Howells out_put_spnego_key:
655dec5a519SDavid Howells 	key_invalidate(spnego_key);
656dec5a519SDavid Howells 	key_put(spnego_key);
657dec5a519SDavid Howells out:
658dec5a519SDavid Howells 	sess_data->result = rc;
659dec5a519SDavid Howells 	sess_data->func = NULL;
660dec5a519SDavid Howells 	sess_free_buffer(sess_data);
661dec5a519SDavid Howells 	kfree_sensitive(ses->auth_key.response);
662dec5a519SDavid Howells 	ses->auth_key.response = NULL;
663dec5a519SDavid Howells }
664dec5a519SDavid Howells 
665dec5a519SDavid Howells #endif /* ! CONFIG_CIFS_UPCALL */
666dec5a519SDavid Howells 
667dec5a519SDavid Howells /*
668dec5a519SDavid Howells  * The required kvec buffers have to be allocated before calling this
669dec5a519SDavid Howells  * function.
670dec5a519SDavid Howells  */
671dec5a519SDavid Howells static int
_sess_auth_rawntlmssp_assemble_req(struct sess_data * sess_data)672dec5a519SDavid Howells _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
673dec5a519SDavid Howells {
674dec5a519SDavid Howells 	SESSION_SETUP_ANDX *pSMB;
675dec5a519SDavid Howells 	struct cifs_ses *ses = sess_data->ses;
676dec5a519SDavid Howells 	struct TCP_Server_Info *server = sess_data->server;
677dec5a519SDavid Howells 	__u32 capabilities;
678dec5a519SDavid Howells 	char *bcc_ptr;
679dec5a519SDavid Howells 
680dec5a519SDavid Howells 	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
681dec5a519SDavid Howells 
682dec5a519SDavid Howells 	capabilities = cifs_ssetup_hdr(ses, server, pSMB);
683dec5a519SDavid Howells 	pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
684dec5a519SDavid Howells 	capabilities |= CAP_EXTENDED_SECURITY;
685dec5a519SDavid Howells 	pSMB->req.Capabilities |= cpu_to_le32(capabilities);
686dec5a519SDavid Howells 
687dec5a519SDavid Howells 	bcc_ptr = sess_data->iov[2].iov_base;
688dec5a519SDavid Howells 
689dec5a519SDavid Howells 	if (pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) {
690dec5a519SDavid Howells 		/* unicode strings must be word aligned */
691dec5a519SDavid Howells 		if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
692dec5a519SDavid Howells 			*bcc_ptr = 0;
693dec5a519SDavid Howells 			bcc_ptr++;
694dec5a519SDavid Howells 		}
695dec5a519SDavid Howells 		unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
696dec5a519SDavid Howells 	} else {
697dec5a519SDavid Howells 		ascii_oslm_strings(&bcc_ptr, sess_data->nls_cp);
698dec5a519SDavid Howells 	}
699dec5a519SDavid Howells 
700dec5a519SDavid Howells 	sess_data->iov[2].iov_len = (long) bcc_ptr -
701dec5a519SDavid Howells 					(long) sess_data->iov[2].iov_base;
702dec5a519SDavid Howells 
703dec5a519SDavid Howells 	return 0;
704dec5a519SDavid Howells }
705dec5a519SDavid Howells 
706dec5a519SDavid Howells static void
707dec5a519SDavid Howells sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data);
708dec5a519SDavid Howells 
709dec5a519SDavid Howells static void
sess_auth_rawntlmssp_negotiate(struct sess_data * sess_data)710dec5a519SDavid Howells sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
711dec5a519SDavid Howells {
712dec5a519SDavid Howells 	int rc;
713dec5a519SDavid Howells 	struct smb_hdr *smb_buf;
714dec5a519SDavid Howells 	SESSION_SETUP_ANDX *pSMB;
715dec5a519SDavid Howells 	struct cifs_ses *ses = sess_data->ses;
716dec5a519SDavid Howells 	struct TCP_Server_Info *server = sess_data->server;
717dec5a519SDavid Howells 	__u16 bytes_remaining;
718dec5a519SDavid Howells 	char *bcc_ptr;
719dec5a519SDavid Howells 	unsigned char *ntlmsspblob = NULL;
720dec5a519SDavid Howells 	u16 blob_len;
721dec5a519SDavid Howells 
722dec5a519SDavid Howells 	cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n");
723dec5a519SDavid Howells 
724dec5a519SDavid Howells 	/*
725dec5a519SDavid Howells 	 * if memory allocation is successful, caller of this function
726dec5a519SDavid Howells 	 * frees it.
727dec5a519SDavid Howells 	 */
728*bf4afc53SLinus Torvalds 	ses->ntlmssp = kmalloc_obj(struct ntlmssp_auth);
729dec5a519SDavid Howells 	if (!ses->ntlmssp) {
730dec5a519SDavid Howells 		rc = -ENOMEM;
731dec5a519SDavid Howells 		goto out;
732dec5a519SDavid Howells 	}
733dec5a519SDavid Howells 	ses->ntlmssp->sesskey_per_smbsess = false;
734dec5a519SDavid Howells 
735dec5a519SDavid Howells 	/* wct = 12 */
736dec5a519SDavid Howells 	rc = sess_alloc_buffer(sess_data, 12);
737dec5a519SDavid Howells 	if (rc)
738dec5a519SDavid Howells 		goto out;
739dec5a519SDavid Howells 
740dec5a519SDavid Howells 	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
741dec5a519SDavid Howells 
742dec5a519SDavid Howells 	/* Build security blob before we assemble the request */
743dec5a519SDavid Howells 	rc = build_ntlmssp_negotiate_blob(&ntlmsspblob,
744dec5a519SDavid Howells 				     &blob_len, ses, server,
745dec5a519SDavid Howells 				     sess_data->nls_cp);
746dec5a519SDavid Howells 	if (rc)
747dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
748dec5a519SDavid Howells 
749dec5a519SDavid Howells 	sess_data->iov[1].iov_len = blob_len;
750dec5a519SDavid Howells 	sess_data->iov[1].iov_base = ntlmsspblob;
751dec5a519SDavid Howells 	pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
752dec5a519SDavid Howells 
753dec5a519SDavid Howells 	rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
754dec5a519SDavid Howells 	if (rc)
755dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
756dec5a519SDavid Howells 
757dec5a519SDavid Howells 	rc = sess_sendreceive(sess_data);
758dec5a519SDavid Howells 
759dec5a519SDavid Howells 	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
760dec5a519SDavid Howells 	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
761dec5a519SDavid Howells 
762dec5a519SDavid Howells 	/* If true, rc here is expected and not an error */
763dec5a519SDavid Howells 	if (sess_data->buf0_type != CIFS_NO_BUFFER &&
764dec5a519SDavid Howells 	    smb_buf->Status.CifsError ==
765dec5a519SDavid Howells 			cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
766dec5a519SDavid Howells 		rc = 0;
767dec5a519SDavid Howells 
768dec5a519SDavid Howells 	if (rc)
769dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
770dec5a519SDavid Howells 
771dec5a519SDavid Howells 	cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
772dec5a519SDavid Howells 
773dec5a519SDavid Howells 	if (smb_buf->WordCount != 4) {
774dec5a519SDavid Howells 		rc = smb_EIO1(smb_eio_trace_sess_rawnl_neg_wcc, smb_buf->WordCount);
775dec5a519SDavid Howells 		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
776dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
777dec5a519SDavid Howells 	}
778dec5a519SDavid Howells 
779dec5a519SDavid Howells 	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */
780dec5a519SDavid Howells 	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
781dec5a519SDavid Howells 
782dec5a519SDavid Howells 	bytes_remaining = get_bcc(smb_buf);
783dec5a519SDavid Howells 	bcc_ptr = pByteArea(smb_buf);
784dec5a519SDavid Howells 
785dec5a519SDavid Howells 	blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
786dec5a519SDavid Howells 	if (blob_len > bytes_remaining) {
787dec5a519SDavid Howells 		cifs_dbg(VFS, "bad security blob length %d\n",
788dec5a519SDavid Howells 				blob_len);
789dec5a519SDavid Howells 		rc = -EINVAL;
790dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
791dec5a519SDavid Howells 	}
792dec5a519SDavid Howells 
793dec5a519SDavid Howells 	rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
794dec5a519SDavid Howells 
795dec5a519SDavid Howells out_free_ntlmsspblob:
796dec5a519SDavid Howells 	kfree_sensitive(ntlmsspblob);
797dec5a519SDavid Howells out:
798dec5a519SDavid Howells 	sess_free_buffer(sess_data);
799dec5a519SDavid Howells 
800dec5a519SDavid Howells 	if (!rc) {
801dec5a519SDavid Howells 		sess_data->func = sess_auth_rawntlmssp_authenticate;
802dec5a519SDavid Howells 		return;
803dec5a519SDavid Howells 	}
804dec5a519SDavid Howells 
805dec5a519SDavid Howells 	/* Else error. Cleanup */
806dec5a519SDavid Howells 	kfree_sensitive(ses->auth_key.response);
807dec5a519SDavid Howells 	ses->auth_key.response = NULL;
808dec5a519SDavid Howells 	kfree_sensitive(ses->ntlmssp);
809dec5a519SDavid Howells 	ses->ntlmssp = NULL;
810dec5a519SDavid Howells 
811dec5a519SDavid Howells 	sess_data->func = NULL;
812dec5a519SDavid Howells 	sess_data->result = rc;
813dec5a519SDavid Howells }
814dec5a519SDavid Howells 
815dec5a519SDavid Howells static void
sess_auth_rawntlmssp_authenticate(struct sess_data * sess_data)816dec5a519SDavid Howells sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
817dec5a519SDavid Howells {
818dec5a519SDavid Howells 	int rc;
819dec5a519SDavid Howells 	struct smb_hdr *smb_buf;
820dec5a519SDavid Howells 	SESSION_SETUP_ANDX *pSMB;
821dec5a519SDavid Howells 	struct cifs_ses *ses = sess_data->ses;
822dec5a519SDavid Howells 	struct TCP_Server_Info *server = sess_data->server;
823dec5a519SDavid Howells 	__u16 bytes_remaining;
824dec5a519SDavid Howells 	char *bcc_ptr;
825dec5a519SDavid Howells 	unsigned char *ntlmsspblob = NULL;
826dec5a519SDavid Howells 	u16 blob_len;
827dec5a519SDavid Howells 
828dec5a519SDavid Howells 	cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
829dec5a519SDavid Howells 
830dec5a519SDavid Howells 	/* wct = 12 */
831dec5a519SDavid Howells 	rc = sess_alloc_buffer(sess_data, 12);
832dec5a519SDavid Howells 	if (rc)
833dec5a519SDavid Howells 		goto out;
834dec5a519SDavid Howells 
835dec5a519SDavid Howells 	/* Build security blob before we assemble the request */
836dec5a519SDavid Howells 	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
837dec5a519SDavid Howells 	smb_buf = (struct smb_hdr *)pSMB;
838dec5a519SDavid Howells 	rc = build_ntlmssp_auth_blob(&ntlmsspblob,
839dec5a519SDavid Howells 					&blob_len, ses, server,
840dec5a519SDavid Howells 					sess_data->nls_cp);
841dec5a519SDavid Howells 	if (rc)
842dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
843dec5a519SDavid Howells 	sess_data->iov[1].iov_len = blob_len;
844dec5a519SDavid Howells 	sess_data->iov[1].iov_base = ntlmsspblob;
845dec5a519SDavid Howells 	pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
846dec5a519SDavid Howells 	/*
847dec5a519SDavid Howells 	 * Make sure that we tell the server that we are using
848dec5a519SDavid Howells 	 * the uid that it just gave us back on the response
849dec5a519SDavid Howells 	 * (challenge)
850dec5a519SDavid Howells 	 */
851dec5a519SDavid Howells 	smb_buf->Uid = ses->Suid;
852dec5a519SDavid Howells 
853dec5a519SDavid Howells 	rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
854dec5a519SDavid Howells 	if (rc)
855dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
856dec5a519SDavid Howells 
857dec5a519SDavid Howells 	rc = sess_sendreceive(sess_data);
858dec5a519SDavid Howells 	if (rc)
859dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
860dec5a519SDavid Howells 
861dec5a519SDavid Howells 	pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
862dec5a519SDavid Howells 	smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
863dec5a519SDavid Howells 	if (smb_buf->WordCount != 4) {
864dec5a519SDavid Howells 		rc = smb_EIO1(smb_eio_trace_sess_rawnl_auth_wcc, smb_buf->WordCount);
865dec5a519SDavid Howells 		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
866dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
867dec5a519SDavid Howells 	}
868dec5a519SDavid Howells 
869dec5a519SDavid Howells 	if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
870dec5a519SDavid Howells 		cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
871dec5a519SDavid Howells 
872dec5a519SDavid Howells 	if (ses->Suid != smb_buf->Uid) {
873dec5a519SDavid Howells 		ses->Suid = smb_buf->Uid;
874dec5a519SDavid Howells 		cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid);
875dec5a519SDavid Howells 	}
876dec5a519SDavid Howells 
877dec5a519SDavid Howells 	bytes_remaining = get_bcc(smb_buf);
878dec5a519SDavid Howells 	bcc_ptr = pByteArea(smb_buf);
879dec5a519SDavid Howells 	blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
880dec5a519SDavid Howells 	if (blob_len > bytes_remaining) {
881dec5a519SDavid Howells 		cifs_dbg(VFS, "bad security blob length %d\n",
882dec5a519SDavid Howells 				blob_len);
883dec5a519SDavid Howells 		rc = -EINVAL;
884dec5a519SDavid Howells 		goto out_free_ntlmsspblob;
885dec5a519SDavid Howells 	}
886dec5a519SDavid Howells 	bcc_ptr += blob_len;
887dec5a519SDavid Howells 	bytes_remaining -= blob_len;
888dec5a519SDavid Howells 
889dec5a519SDavid Howells 
890dec5a519SDavid Howells 	/* BB check if Unicode and decode strings */
891dec5a519SDavid Howells 	if (bytes_remaining == 0) {
892dec5a519SDavid Howells 		/* no string area to decode, do nothing */
893dec5a519SDavid Howells 	} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
894dec5a519SDavid Howells 		/* unicode string area must be word-aligned */
895dec5a519SDavid Howells 		if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
896dec5a519SDavid Howells 			++bcc_ptr;
897dec5a519SDavid Howells 			--bytes_remaining;
898dec5a519SDavid Howells 		}
899dec5a519SDavid Howells 		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
900dec5a519SDavid Howells 				      sess_data->nls_cp);
901dec5a519SDavid Howells 	} else {
902dec5a519SDavid Howells 		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
903dec5a519SDavid Howells 				    sess_data->nls_cp);
904dec5a519SDavid Howells 	}
905dec5a519SDavid Howells 
906dec5a519SDavid Howells out_free_ntlmsspblob:
907dec5a519SDavid Howells 	kfree_sensitive(ntlmsspblob);
908dec5a519SDavid Howells out:
909dec5a519SDavid Howells 	sess_free_buffer(sess_data);
910dec5a519SDavid Howells 
911dec5a519SDavid Howells 	if (!rc)
912dec5a519SDavid Howells 		rc = sess_establish_session(sess_data);
913dec5a519SDavid Howells 
914dec5a519SDavid Howells 	/* Cleanup */
915dec5a519SDavid Howells 	kfree_sensitive(ses->auth_key.response);
916dec5a519SDavid Howells 	ses->auth_key.response = NULL;
917dec5a519SDavid Howells 	kfree_sensitive(ses->ntlmssp);
918dec5a519SDavid Howells 	ses->ntlmssp = NULL;
919dec5a519SDavid Howells 
920dec5a519SDavid Howells 	sess_data->func = NULL;
921dec5a519SDavid Howells 	sess_data->result = rc;
922dec5a519SDavid Howells }
923dec5a519SDavid Howells 
select_sec(struct sess_data * sess_data)924dec5a519SDavid Howells static int select_sec(struct sess_data *sess_data)
925dec5a519SDavid Howells {
926dec5a519SDavid Howells 	int type;
927dec5a519SDavid Howells 	struct cifs_ses *ses = sess_data->ses;
928dec5a519SDavid Howells 	struct TCP_Server_Info *server = sess_data->server;
929dec5a519SDavid Howells 
930dec5a519SDavid Howells 	type = cifs_select_sectype(server, ses->sectype);
931dec5a519SDavid Howells 	cifs_dbg(FYI, "sess setup type %d\n", type);
932dec5a519SDavid Howells 	if (type == Unspecified) {
933dec5a519SDavid Howells 		cifs_dbg(VFS, "Unable to select appropriate authentication method!\n");
934dec5a519SDavid Howells 		return -EINVAL;
935dec5a519SDavid Howells 	}
936dec5a519SDavid Howells 
937dec5a519SDavid Howells 	switch (type) {
938dec5a519SDavid Howells 	case NTLMv2:
939dec5a519SDavid Howells 		sess_data->func = sess_auth_ntlmv2;
940dec5a519SDavid Howells 		break;
941dec5a519SDavid Howells 	case Kerberos:
942dec5a519SDavid Howells #ifdef CONFIG_CIFS_UPCALL
943dec5a519SDavid Howells 		sess_data->func = sess_auth_kerberos;
944dec5a519SDavid Howells 		break;
945dec5a519SDavid Howells #else
946dec5a519SDavid Howells 		cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
947dec5a519SDavid Howells 		return -ENOSYS;
948dec5a519SDavid Howells #endif /* CONFIG_CIFS_UPCALL */
949dec5a519SDavid Howells 	case RawNTLMSSP:
950dec5a519SDavid Howells 		sess_data->func = sess_auth_rawntlmssp_negotiate;
951dec5a519SDavid Howells 		break;
952dec5a519SDavid Howells 	default:
953dec5a519SDavid Howells 		cifs_dbg(VFS, "secType %d not supported!\n", type);
954dec5a519SDavid Howells 		return -ENOSYS;
955dec5a519SDavid Howells 	}
956dec5a519SDavid Howells 
957dec5a519SDavid Howells 	return 0;
958dec5a519SDavid Howells }
959dec5a519SDavid Howells 
CIFS_SessSetup(const unsigned int xid,struct cifs_ses * ses,struct TCP_Server_Info * server,const struct nls_table * nls_cp)960dec5a519SDavid Howells int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
961dec5a519SDavid Howells 		   struct TCP_Server_Info *server,
962dec5a519SDavid Howells 		   const struct nls_table *nls_cp)
963dec5a519SDavid Howells {
964dec5a519SDavid Howells 	int rc = 0;
965dec5a519SDavid Howells 	struct sess_data *sess_data;
966dec5a519SDavid Howells 
967dec5a519SDavid Howells 	if (ses == NULL) {
968dec5a519SDavid Howells 		WARN(1, "%s: ses == NULL!", __func__);
969dec5a519SDavid Howells 		return -EINVAL;
970dec5a519SDavid Howells 	}
971dec5a519SDavid Howells 
972*bf4afc53SLinus Torvalds 	sess_data = kzalloc_obj(struct sess_data);
973dec5a519SDavid Howells 	if (!sess_data)
974dec5a519SDavid Howells 		return -ENOMEM;
975dec5a519SDavid Howells 
976dec5a519SDavid Howells 	sess_data->xid = xid;
977dec5a519SDavid Howells 	sess_data->ses = ses;
978dec5a519SDavid Howells 	sess_data->server = server;
979dec5a519SDavid Howells 	sess_data->buf0_type = CIFS_NO_BUFFER;
980dec5a519SDavid Howells 	sess_data->nls_cp = (struct nls_table *) nls_cp;
981dec5a519SDavid Howells 
982dec5a519SDavid Howells 	rc = select_sec(sess_data);
983dec5a519SDavid Howells 	if (rc)
984dec5a519SDavid Howells 		goto out;
985dec5a519SDavid Howells 
986dec5a519SDavid Howells 	while (sess_data->func)
987dec5a519SDavid Howells 		sess_data->func(sess_data);
988dec5a519SDavid Howells 
989dec5a519SDavid Howells 	/* Store result before we free sess_data */
990dec5a519SDavid Howells 	rc = sess_data->result;
991dec5a519SDavid Howells 
992dec5a519SDavid Howells out:
993dec5a519SDavid Howells 	kfree_sensitive(sess_data);
994dec5a519SDavid Howells 	return rc;
995dec5a519SDavid Howells }
996