xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb2_rq.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
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 
51 static const uint8_t SMB2_SIGNATURE[4] = SMB2_PROTOCOL_ID;
52 
53 static int  smb2_rq_enqueue(struct smb_rq *rqp);
54 static 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  */
62 void
63 smb2_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 
116 int
117 smb2_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  */
125 int
126 smb2_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 
142 static int
143 smb2_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 	 */
192 ok_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  */
205 int
206 smb2_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  */
267 static int
268 smb2_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  */
327 int
328 smb2_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