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