1/*
2 * Copyright (c) 2011  Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
26 */
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/time.h>
31#include <sys/kmem.h>
32#include <sys/proc.h>
33#include <sys/lock.h>
34#include <sys/socket.h>
35#include <sys/mount.h>
36#include <sys/sunddi.h>
37#include <sys/cmn_err.h>
38#include <sys/atomic.h>
39#include <sys/sdt.h>
40
41#include <netsmb/smb_osdep.h>
42
43#include <netsmb/smb.h>
44#include <netsmb/smb2.h>
45#include <netsmb/smb_conn.h>
46#include <netsmb/smb_subr.h>
47#include <netsmb/smb_tran.h>
48#include <netsmb/smb_rq.h>
49#include <netsmb/smb2_rq.h>
50
51static const uint8_t SMB2_SIGNATURE[4] = SMB2_PROTOCOL_ID;
52
53static int  smb2_rq_enqueue(struct smb_rq *rqp);
54static int  smb2_rq_reply(struct smb_rq *rqp);
55
56/*
57 * Given a request with it's body already composed,
58 * rewind to the start and fill in the SMB2 header.
59 * This is called when the request is enqueued,
60 * so we have the final message ID etc.
61 */
62void
63smb2_rq_fillhdr(struct smb_rq *rqp)
64{
65	struct mbchain mbtmp, *mbp = &mbtmp;
66	uint16_t creditcharge, creditrequest;
67	size_t len;
68	mblk_t *m;
69
70	ASSERT((rqp->sr2_nextcmd & 7) == 0);
71	if (rqp->sr2_nextcmd != 0) {
72		len = msgdsize(rqp->sr_rq.mb_top);
73		ASSERT((len & 7) == 0);
74	}
75
76	/*
77	 * When sending negotiate, we don't technically know yet
78	 * if the server handles SMB 2.1 or later and credits.
79	 * Negotiate is supposed to set these to zero.
80	 */
81	if (rqp->sr2_command == SMB2_NEGOTIATE) {
82		creditcharge = creditrequest = 0;
83	} else {
84		creditcharge = rqp->sr2_creditcharge;
85		creditrequest = rqp->sr2_creditsrequested;
86	}
87
88	/*
89	 * Fill in the SMB2 header using a dup of the first mblk,
90	 * which points at the same data but has its own wptr,
91	 * so we can rewind without trashing the message.
92	 */
93	m = dupb(rqp->sr_rq.mb_top);
94	m->b_wptr = m->b_rptr;	/* rewind */
95	mb_initm(mbp, m);
96
97	mb_put_mem(mbp, SMB2_SIGNATURE, 4, MB_MSYSTEM);
98	mb_put_uint16le(mbp, SMB2_HDR_SIZE);		/* Struct Size */
99	mb_put_uint16le(mbp, creditcharge);
100	mb_put_uint32le(mbp, 0);	/* Status */
101	mb_put_uint16le(mbp, rqp->sr2_command);
102	mb_put_uint16le(mbp, creditrequest);
103	mb_put_uint32le(mbp, rqp->sr2_rqflags);
104	mb_put_uint32le(mbp, rqp->sr2_nextcmd);
105	mb_put_uint64le(mbp, rqp->sr2_messageid);
106
107	mb_put_uint32le(mbp, rqp->sr_pid);		/* Process ID */
108	mb_put_uint32le(mbp, rqp->sr2_rqtreeid);	/* Tree ID */
109	mb_put_uint64le(mbp, rqp->sr2_rqsessionid);	/* Session ID */
110	/* The MAC signature is filled in by smb2_vc_sign() */
111
112	/* This will free the mblk from dupb. */
113	mb_done(mbp);
114}
115
116int
117smb2_rq_simple(struct smb_rq *rqp)
118{
119	return (smb2_rq_simple_timed(rqp, smb2_timo_default));
120}
121
122/*
123 * Simple request-reply exchange
124 */
125int
126smb2_rq_simple_timed(struct smb_rq *rqp, int timeout)
127{
128	int error;
129
130	rqp->sr_flags &= ~SMBR_RESTART;
131	rqp->sr_timo = timeout;	/* in seconds */
132	rqp->sr_state = SMBRQ_NOTSENT;
133
134	error = smb2_rq_enqueue(rqp);
135	if (error == 0)
136		error = smb2_rq_reply(rqp);
137
138	return (error);
139}
140
141
142static int
143smb2_rq_enqueue(struct smb_rq *rqp)
144{
145	struct smb_vc *vcp = rqp->sr_vc;
146	struct smb_share *ssp = rqp->sr_share;
147	int error = 0;
148
149	ASSERT((vcp->vc_flags & SMBV_SMB2) != 0);
150
151	/*
152	 * Normal requests may initiate a reconnect,
153	 * and/or wait for state changes to finish.
154	 * Some requests set the NORECONNECT flag
155	 * to avoid all that (i.e. tree discon)
156	 */
157	if (rqp->sr_flags & SMBR_NORECONNECT) {
158		if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
159			SMBSDEBUG("bad vc_state=%d\n", vcp->vc_state);
160			return (ENOTCONN);
161		}
162		if (ssp != NULL &&
163		    ((ssp->ss_flags & SMBS_CONNECTED) == 0))
164			return (ENOTCONN);
165		goto ok_out;
166	}
167
168	/*
169	 * If we're not connected, initiate a reconnect
170	 * and/or wait for an existing one to finish.
171	 */
172	if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
173		error = smb_iod_reconnect(vcp);
174		if (error != 0)
175			return (error);
176	}
177
178	/*
179	 * If this request has a "share" object
180	 * that needs a tree connect, do it now.
181	 */
182	if (ssp != NULL && (ssp->ss_flags & SMBS_CONNECTED) == 0) {
183		error = smb_share_tcon(ssp, rqp->sr_cred);
184		if (error)
185			return (error);
186	}
187
188	/*
189	 * We now know what UID + TID to use.
190	 * Store them in the request.
191	 */
192ok_out:
193	rqp->sr2_rqsessionid = vcp->vc2_session_id;
194	rqp->sr2_rqtreeid = ssp ? ssp->ss2_tree_id : SMB2_TID_UNKNOWN;
195	error = smb2_iod_addrq(rqp);
196
197	return (error);
198}
199
200/*
201 * Used by the IOD thread during connection setup,
202 * and for smb2_echo after network timeouts.  Note that
203 * unlike smb2_rq_simple, callers must check sr_error.
204 */
205int
206smb2_rq_internal(struct smb_rq *rqp, int timeout)
207{
208	struct smb_vc *vcp = rqp->sr_vc;
209	int error;
210
211	ASSERT((vcp->vc_flags & SMBV_SMB2) != 0);
212
213	rqp->sr_flags &= ~SMBR_RESTART;
214	rqp->sr_timo = timeout;	/* in seconds */
215	rqp->sr_state = SMBRQ_NOTSENT;
216
217	/*
218	 * In-line smb2_rq_enqueue(rqp) here, as we don't want it
219	 * trying to reconnect etc. for an internal request.
220	 */
221	rqp->sr2_rqsessionid = vcp->vc2_session_id;
222	rqp->sr2_rqtreeid = SMB2_TID_UNKNOWN;
223	rqp->sr_flags |= SMBR_INTERNAL;
224	error = smb2_iod_addrq(rqp);
225	if (error != 0)
226		return (error);
227
228	/*
229	 * In-line a variant of smb2_rq_reply(rqp) here as we may
230	 * need to do custom parsing for SMB1-to-SMB2 negotiate.
231	 */
232	if (rqp->sr_timo == SMBNOREPLYWAIT) {
233		smb_iod_removerq(rqp);
234		return (0);
235	}
236
237	error = smb_iod_waitrq_int(rqp);
238	if (error)
239		return (error);
240
241	/*
242	 * If the request was signed, validate the
243	 * signature on the response.
244	 */
245	if (rqp->sr2_rqflags & SMB2_FLAGS_SIGNED) {
246		error = smb2_rq_verify(rqp);
247		if (error)
248			return (error);
249	}
250
251	/*
252	 * Parse the SMB2 header.
253	 */
254	error = smb2_rq_parsehdr(rqp);
255
256	/*
257	 * Skip the error translation smb2_rq_reply does.
258	 * Callers of this expect "raw" NT status.
259	 */
260
261	return (error);
262}
263
264/*
265 * Wait for a reply to this request, then parse it.
266 */
267static int
268smb2_rq_reply(struct smb_rq *rqp)
269{
270	int error;
271
272	if (rqp->sr_timo == SMBNOREPLYWAIT) {
273		smb_iod_removerq(rqp);
274		return (0);
275	}
276
277	error = smb_iod_waitrq(rqp);
278	if (error)
279		return (error);
280
281	/*
282	 * If the request was signed, validate the
283	 * signature on the response.
284	 */
285	if (rqp->sr2_rqflags & SMB2_FLAGS_SIGNED) {
286		error = smb2_rq_verify(rqp);
287		if (error)
288			return (error);
289	}
290
291	/*
292	 * Parse the SMB2 header
293	 */
294	error = smb2_rq_parsehdr(rqp);
295	if (error != 0)
296		return (error);
297
298	if (rqp->sr_error != 0) {
299		error = smb_maperr32(rqp->sr_error);
300	}
301
302	if (error != 0) {
303		/*
304		 * Do a special check for STATUS_BUFFER_OVERFLOW;
305		 * it's not an error.
306		 */
307		if (rqp->sr_error == NT_STATUS_BUFFER_OVERFLOW) {
308			/*
309			 * Don't report it as an error to our caller;
310			 * they can look at rqp->sr_error if they
311			 * need to know whether we got a
312			 * STATUS_BUFFER_OVERFLOW.
313			 */
314			rqp->sr_flags |= SMBR_MOREDATA;
315			error = 0;
316		}
317	} else {
318		rqp->sr_flags &= ~SMBR_MOREDATA;
319	}
320
321	return (error);
322}
323
324/*
325 * Parse the SMB 2+ Header
326 */
327int
328smb2_rq_parsehdr(struct smb_rq *rqp)
329{
330	struct mdchain *mdp = &rqp->sr_rp;
331	uint32_t protocol_id;
332	uint16_t length = 0;
333	uint16_t credit_charge;
334	uint16_t command;
335	uint64_t message_id = 0;
336	int error = 0;
337
338	/* Get Protocol ID */
339	md_get_uint32le(mdp, &protocol_id);
340
341	/* Get/Check structure size is 64 */
342	md_get_uint16le(mdp, &length);
343	if (length != 64)
344		return (EBADRPC);
345
346	md_get_uint16le(mdp, &credit_charge);
347	md_get_uint32le(mdp, &rqp->sr_error);
348	md_get_uint16le(mdp, &command);
349	md_get_uint16le(mdp, &rqp->sr2_rspcreditsgranted);
350	md_get_uint32le(mdp, &rqp->sr2_rspflags);
351	md_get_uint32le(mdp, &rqp->sr2_rspnextcmd);
352	md_get_uint64le(mdp, &message_id);
353
354	if ((rqp->sr2_rspflags & SMB2_FLAGS_ASYNC_COMMAND) == 0) {
355		/*
356		 * Sync Header
357		 */
358
359		/* Get Process ID */
360		md_get_uint32le(mdp, &rqp->sr2_rsppid);
361
362		/* Get Tree ID */
363		md_get_uint32le(mdp, &rqp->sr2_rsptreeid);
364	} else {
365		/*
366		 * Async Header
367		 */
368
369		/* Get Async ID */
370		md_get_uint64le(mdp, &rqp->sr2_rspasyncid);
371	}
372
373	/* Get Session ID */
374	error = md_get_uint64le(mdp, &rqp->sr2_rspsessionid);
375	if (error)
376		return (error);
377
378	/* Skip MAC Signature */
379	error = md_get_mem(mdp, NULL, 16, MB_MSYSTEM);
380
381	return (error);
382}
383