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
smb2_rq_fillhdr(struct smb_rq * rqp)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
smb2_rq_simple(struct smb_rq * rqp)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
smb2_rq_simple_timed(struct smb_rq * rqp,int timeout)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
smb2_rq_enqueue(struct smb_rq * rqp)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
smb2_rq_internal(struct smb_rq * rqp,int timeout)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
smb2_rq_reply(struct smb_rq * rqp)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
smb2_rq_parsehdr(struct smb_rq * rqp)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