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