xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb2_smb.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * Copyright (c) 2011 - 2013 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/kmem.h>
31 #include <sys/proc.h>
32 #include <sys/lock.h>
33 #include <sys/socket.h>
34 #include <sys/uio.h>
35 #include <sys/random.h>
36 #include <sys/note.h>
37 #include <sys/errno.h>
38 #include <sys/cmn_err.h>
39 
40 #include <smb/ntaccess.h>
41 #include <smb/winioctl.h>
42 
43 #include <netsmb/smb_osdep.h>
44 
45 #include <netsmb/smb.h>
46 #include <netsmb/smb2.h>
47 #include <netsmb/smb_conn.h>
48 #include <netsmb/smb_subr.h>
49 #include <netsmb/smb_tran.h>
50 #include <netsmb/smb_rq.h>
51 #include <netsmb/smb2_rq.h>
52 
53 /*
54  * Supported dialects.  Keep sorted by number because of how the
55  * vc_maxver check below may truncate this list.
56  */
57 #define	NDIALECTS	3
58 static const uint16_t smb2_dialects[NDIALECTS] = {
59 	SMB2_DIALECT_0210,
60 	SMB2_DIALECT_0300,
61 	SMB2_DIALECT_0302,
62 };
63 
64 /* Optional capabilities we advertise (none yet). */
65 uint32_t smb2_clnt_caps = 0;
66 
67 /* How many credits to ask for during ssn. setup. */
68 uint16_t smb2_ss_req_credits = 64;
69 
70 /*
71  * Default timeout values, all in seconds.
72  * Make these tunable (only via mdb for now).
73  */
74 int smb2_timo_notice = 15;
75 int smb2_timo_default = 30;
76 int smb2_timo_logon = 45;
77 int smb2_timo_open = 45;
78 int smb2_timo_read = 45;
79 int smb2_timo_write = 60;
80 int smb2_timo_append = 90;
81 
82 /*
83  * This is a special handler for the odd SMB1-to-SMB2 negotiate
84  * response, where an SMB1 request gets an SMB2 response.
85  *
86  * Unlike most parse functions here, this needs to parse both
87  * the SMB2 header and the nego. response body.  Note that
88  * the only "SMB2" dialect our SMB1 negotiate offered was
89  * { SMB_DIALECT_SMB2_FF, "SMB 2.???"} so the only valid
90  * SMB2 dialect we should get is: SMB2_DIALECT_02ff
91  */
92 int
93 smb2_parse_smb1nego_resp(struct smb_rq *rqp)
94 {
95 	struct smb_vc *vcp = rqp->sr_vc;
96 	struct smb_sopt *sp = &vcp->vc_sopt;
97 	struct mdchain *mdp;
98 	uint16_t length = 0;
99 	int error;
100 
101 	/* Get pointer to response data */
102 	smb_rq_getreply(rqp, &mdp);
103 
104 	error = smb2_rq_parsehdr(rqp);
105 	if (error != 0)
106 		return (error);
107 
108 	/*
109 	 * Parse SMB 2/3 Negotiate Response
110 	 * We are already pointing to begining of Response data
111 	 */
112 
113 	/* Check structure size is 65 */
114 	md_get_uint16le(mdp, &length);
115 	if (length != 65)
116 		return (EBADRPC);
117 
118 	/* Get Security Mode */
119 	md_get_uint16le(mdp, &sp->sv2_security_mode);
120 
121 	/* Get Dialect. */
122 	error = md_get_uint16le(mdp, &sp->sv_proto);
123 	if (error != 0)
124 		return (error);
125 
126 	/* What dialect did we get? */
127 	if (sp->sv_proto != SMB2_DIALECT_02ff) {
128 		SMBERROR("Unknown dialect 0x%x\n", sp->sv_proto);
129 		return (EINVAL);
130 	}
131 	/* Set our (internal) SMB1 dialect also. */
132 	sp->sv_proto = SMB_DIALECT_SMB2_FF;
133 
134 	/*
135 	 * This request did not go through smb2_iod_addrq and
136 	 * smb2_iod_process() so the SMB2 message ID state is
137 	 * behind what we need it to be.  Fix that.
138 	 */
139 	vcp->vc2_next_message_id = 1;
140 	vcp->vc2_limit_message_id = 2;
141 
142 	/*
143 	 * Skip parsing the rest.  We'll get a normal
144 	 * SMB2 negotiate next and do negotiate then.
145 	 */
146 	return (0);
147 }
148 
149 int
150 smb2_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
151 {
152 	smb_sopt_t *sp = &vcp->vc_sopt;
153 	smbioc_ssn_work_t *wk = &vcp->vc_work;
154 	struct smb_rq *rqp = NULL;
155 	struct mbchain *mbp = NULL;
156 	struct mdchain *mdp = NULL;
157 	uint16_t *ndialects_p;
158 	uint16_t ndialects = NDIALECTS;
159 	boolean_t will_sign = B_FALSE;
160 	uint16_t length = 0;
161 	uint16_t security_mode;
162 	uint16_t sec_buf_off;
163 	uint16_t sec_buf_len;
164 	int err, i;
165 
166 	/*
167 	 * Compute security mode
168 	 */
169 	if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) {
170 		security_mode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
171 	} else {
172 		security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
173 	}
174 
175 	err = smb_rq_alloc(VCTOCP(vcp), SMB2_NEGOTIATE, scred, &rqp);
176 	if (err)
177 		return (err);
178 
179 	/*
180 	 * Build the SMB2 negotiate request.
181 	 */
182 	smb_rq_getrequest(rqp, &mbp);
183 	mb_put_uint16le(mbp, 36);		/* Struct Size */
184 	ndialects_p = mb_reserve(mbp, 2);	/* Dialect Count */
185 	mb_put_uint16le(mbp, security_mode);
186 	mb_put_uint16le(mbp, 0);		/*  Reserved */
187 	mb_put_uint32le(mbp, smb2_clnt_caps);
188 	mb_put_mem(mbp, vcp->vc_cl_guid, 16, MB_MSYSTEM);
189 	mb_put_uint64le(mbp, 0);		/* Start Time */
190 	for (i = 0; i < ndialects; i++) {	/* Dialects */
191 		if (smb2_dialects[i] > vcp->vc_maxver)
192 			break;
193 		mb_put_uint16le(mbp, smb2_dialects[i]);
194 	}
195 	*ndialects_p = htoles(i);
196 
197 	/*
198 	 * Do the OTW call.
199 	 */
200 	err = smb2_rq_internal(rqp, smb2_timo_default);
201 	if (err) {
202 		goto errout;
203 	}
204 	/* Should only get status success. */
205 	if (rqp->sr_error != NT_STATUS_SUCCESS) {
206 		err = ENOTSUP;
207 		goto errout;
208 	}
209 
210 	/*
211 	 * Decode the negotiate response
212 	 */
213 	smb_rq_getreply(rqp, &mdp);
214 
215 	md_get_uint16le(mdp, &length);	/* Struct size */
216 	if (length != 65) {
217 		err = EBADRPC;
218 		goto errout;
219 	}
220 
221 	md_get_uint16le(mdp, &sp->sv2_security_mode);
222 	md_get_uint16le(mdp, &sp->sv_proto); /* dialect */
223 	md_get_uint16le(mdp, NULL);	/* reserved */
224 	md_get_mem(mdp, sp->sv2_guid, 16, MB_MSYSTEM);
225 	md_get_uint32le(mdp, &sp->sv2_capabilities);
226 	md_get_uint32le(mdp, &sp->sv2_maxtransact);
227 	md_get_uint32le(mdp, &sp->sv2_maxread);
228 	md_get_uint32le(mdp, &sp->sv2_maxwrite);
229 	md_get_uint64le(mdp, NULL);	/* curr_time */
230 	md_get_uint64le(mdp, NULL);	/* boot_time */
231 
232 	/* Get Security Blob offset and length */
233 	md_get_uint16le(mdp, &sec_buf_off);
234 	err = md_get_uint16le(mdp, &sec_buf_len);
235 	if (err != 0)
236 		goto errout;
237 	md_get_uint32le(mdp, NULL);	/* reserved */
238 
239 	/*
240 	 * Security buffer offset is from the beginning of SMB 2 Header
241 	 * Calculate how much further we have to go to get to it.
242 	 * Current offset is: SMB2_HDRLEN + 64
243 	 */
244 	if (sec_buf_len != 0) {
245 		int skip = (int)sec_buf_off - (SMB2_HDRLEN + 64);
246 		if (skip < 0) {
247 			err = EBADRPC;
248 			goto errout;
249 		}
250 		if (skip > 0) {
251 			md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
252 		}
253 
254 		/*
255 		 * Copy the security blob out to user space.
256 		 * Buffer addr,size in vc_auth_rbuf,rlen
257 		 */
258 		if (wk->wk_u_auth_rlen < sec_buf_len) {
259 			SMBSDEBUG("vc_auth_rbuf too small");
260 			/* Give caller required size. */
261 			wk->wk_u_auth_rlen = sec_buf_len;
262 			err = EMSGSIZE;
263 			goto errout;
264 		}
265 		wk->wk_u_auth_rlen = sec_buf_len;
266 		err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr,
267 		    sec_buf_len, MB_MUSER);
268 		if (err) {
269 			goto errout;
270 		}
271 	}
272 
273 	/*
274 	 * Decoded everything.  Now decisions.
275 	 */
276 
277 	/*
278 	 * Turn on signing if either Server or client requires it,
279 	 * except: anonymous sessions can't sign.
280 	 */
281 	if ((sp->sv2_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) ||
282 	    (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED))
283 		will_sign = B_TRUE;
284 	if (vcp->vc_vopt & SMBVOPT_ANONYMOUS)
285 		will_sign = B_FALSE;
286 	SMBSDEBUG("Security signatures: %d", (int)will_sign);
287 	if (will_sign)
288 		vcp->vc_flags |= SMBV_SIGNING;
289 
290 	/*
291 	 * ToDo - too many places are looking at sv_caps, so for now
292 	 * set the SMB1 capabilities too.  Later we should use the
293 	 * sv2_capabilities for SMB 2+.
294 	 */
295 	sp->sv_caps =  (SMB_CAP_UNICODE |
296 			SMB_CAP_LARGE_FILES |
297 			SMB_CAP_STATUS32 |
298 			SMB_CAP_LARGE_READX |
299 			SMB_CAP_LARGE_WRITEX |
300 			SMB_CAP_EXT_SECURITY);
301 	if (sp->sv2_capabilities & SMB2_CAP_DFS)
302 		sp->sv_caps |= SMB_CAP_DFS;
303 
304 	/*
305 	 * A few sanity checks on what we received,
306 	 * becuse we will send these in ssnsetup.
307 	 *
308 	 * Maximum outstanding requests (we care),
309 	 * and Max. VCs (we only use one).  Also,
310 	 * MaxBufferSize lower limit per spec.
311 	 */
312 	if (sp->sv2_maxread < 0x8000) {
313 		SMBSDEBUG("maxread too small\n");
314 		err = ENOTSUP;
315 		goto errout;
316 	}
317 	if (sp->sv2_maxwrite < 0x8000) {
318 		SMBSDEBUG("maxwrite too small\n");
319 		err = ENOTSUP;
320 		goto errout;
321 	}
322 	if (sp->sv2_maxtransact < 0x4000) {
323 		SMBSDEBUG("maxtransact too small\n");
324 		err = ENOTSUP;
325 		goto errout;
326 	}
327 
328 	/* Here too, fill SMB1 fields */
329 	vcp->vc_rxmax = sp->sv2_maxread;
330 	vcp->vc_wxmax = sp->sv2_maxwrite;
331 	vcp->vc_txmax = sp->sv2_maxtransact;
332 
333 	smb_rq_done(rqp);
334 	return (0);
335 
336 errout:
337 	smb_rq_done(rqp);
338 	if (err == 0)
339 		err = EBADRPC;
340 	return (err);
341 }
342 
343 int
344 smb2_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
345 {
346 	// smb_sopt_t *sv = &vcp->vc_sopt;
347 	smbioc_ssn_work_t *wk = &vcp->vc_work;
348 	struct smb_rq *rqp = NULL;
349 	struct mbchain *mbp = NULL;
350 	struct mdchain *mdp = NULL;
351 	char *sb;
352 	int err, ret;
353 	uint16_t sblen;
354 	uint16_t length = 0;
355 	uint16_t session_flags;
356 	uint16_t sec_buf_off;
357 	uint16_t sec_buf_len;
358 	uint8_t security_mode;
359 
360 	/*
361 	 * Compute security mode
362 	 */
363 	if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) {
364 		security_mode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
365 	} else {
366 		security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
367 	}
368 
369 	sb = wk->wk_u_auth_wbuf.lp_ptr;
370 	sblen = (uint16_t)wk->wk_u_auth_wlen;
371 
372 	err = smb_rq_alloc(VCTOCP(vcp), SMB2_SESSION_SETUP, scred, &rqp);
373 	if (err != 0) {
374 		ret = err;
375 		goto out;
376 	}
377 
378 	/*
379 	 * Always ask for some credits. The server usually will
380 	 * only grant these credits once we've authenticated.
381 	 */
382 	rqp->sr2_creditsrequested = smb2_ss_req_credits;
383 
384 	/*
385 	 * Build the SMB Session Setup request.
386 	 */
387 	smb_rq_getrequest(rqp, &mbp);
388 
389 	mb_put_uint16le(mbp, 25);	/* Struct size */
390 	mb_put_uint8(mbp, 0);		/* VcNumber */
391 	mb_put_uint8(mbp, security_mode);
392 	mb_put_uint32le(mbp, smb2_clnt_caps);	/* Capabilities */
393 	mb_put_uint32le(mbp, 0);	/* Channel - always 0 */
394 
395 	/*
396 	 * Security buffer offset and length.  Normally would use
397 	 * ptr = mb_reserve() and fill in later, but since only a
398 	 * small amount of fixed-size stuff follows (12 bytes)
399 	 * we can just compute the offset now.
400 	 */
401 	mb_put_uint16le(mbp, mbp->mb_count + 12);
402 	mb_put_uint16le(mbp, sblen);
403 	mb_put_uint64le(mbp, vcp->vc2_prev_session_id);
404 	err = mb_put_mem(mbp, sb, sblen, MB_MUSER);
405 	if (err != 0) {
406 		ret = err;
407 		goto out;
408 	}
409 
410 	/*
411 	 * Run the request.  The return value here should be the
412 	 * return from this function, unless we fail decoding.
413 	 * Note: NT_STATUS_MORE_PROCESSING_REQUIRED is OK, and
414 	 * the caller expects EINPROGRESS for that case.
415 	 */
416 	ret = smb2_rq_internal(rqp, smb2_timo_logon);
417 	if (ret != 0)
418 		goto out;
419 	switch (rqp->sr_error) {
420 	case NT_STATUS_SUCCESS:
421 		break;
422 	case NT_STATUS_MORE_PROCESSING_REQUIRED:
423 		/* Keep going, but return... */
424 		ret = EINPROGRESS;
425 		break;
426 	default:
427 		ret = EAUTH;
428 		goto out;
429 	}
430 
431 	/*
432 	 * After the first Session Setup Response,
433 	 * save the session ID.
434 	 */
435 	if (vcp->vc2_session_id == 0)
436 		vcp->vc2_session_id = rqp->sr2_rspsessionid;
437 
438 	/*
439 	 * Decode the session setup response
440 	 */
441 	smb_rq_getreply(rqp, &mdp);
442 
443 	md_get_uint16le(mdp, &length);	/* Struct size */
444 	if (length != 9) {
445 		ret = EBADRPC;
446 		goto out;
447 	}
448 
449 	md_get_uint16le(mdp, &session_flags);
450 	md_get_uint16le(mdp, &sec_buf_off);
451 	err = md_get_uint16le(mdp, &sec_buf_len);
452 	if (err != 0) {
453 		ret = err;
454 		goto out;
455 	}
456 
457 	/*
458 	 * Security buffer offset is from the beginning of SMB 2 Header
459 	 * Calculate how much further we have to go to get to it.
460 	 * Current offset is: SMB2_HDRLEN + 8
461 	 */
462 	if (sec_buf_len != 0) {
463 		int skip = (int)sec_buf_off - (SMB2_HDRLEN + 8);
464 		if (skip < 0) {
465 			ret = EBADRPC;
466 			goto out;
467 		}
468 		if (skip > 0) {
469 			md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
470 		}
471 
472 		/*
473 		 * Copy the security blob out to user space.
474 		 * Buffer addr,size in vc_auth_rbuf,rlen
475 		 */
476 		if (wk->wk_u_auth_rlen < sec_buf_len) {
477 			SMBSDEBUG("vc_auth_rbuf too small");
478 			/* Give caller required size. */
479 			wk->wk_u_auth_rlen = sec_buf_len;
480 			ret = EMSGSIZE;
481 			goto out;
482 		}
483 		wk->wk_u_auth_rlen = sec_buf_len;
484 		err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr,
485 		    sec_buf_len, MB_MUSER);
486 		if (err != 0) {
487 			ret = err;
488 			goto out;
489 		}
490 	}
491 
492 out:
493 	if (err != 0 && err != EINPROGRESS) {
494 		/* Session ID no longer valid. */
495 		vcp->vc2_session_id = 0;
496 	}
497 	if (rqp)
498 		smb_rq_done(rqp);
499 
500 	return (ret);
501 }
502 
503 int
504 smb2_smb_logoff(struct smb_vc *vcp, struct smb_cred *scred)
505 {
506 	struct smb_rq *rqp;
507 	struct mbchain *mbp;
508 	int error;
509 
510 	if (vcp->vc2_session_id == 0)
511 		return (0);
512 
513 	error = smb_rq_alloc(VCTOCP(vcp), SMB2_LOGOFF, scred, &rqp);
514 	if (error)
515 		return (error);
516 
517 	/*
518 	 * Fill in Logoff part
519 	 */
520 	smb_rq_getrequest(rqp, &mbp);
521 	mb_put_uint16le(mbp, 4);	/* Struct size */
522 	mb_put_uint16le(mbp, 0);	/* Reserved */
523 
524 	/*
525 	 * Run this with a relatively short timeout. (5 sec.)
526 	 * We don't really care about the result here.
527 	 * Also, don't reconnect for this, of course!
528 	 */
529 	rqp->sr_flags |= SMBR_NORECONNECT;
530 	error = smb2_rq_internal(rqp, 5);
531 	smb_rq_done(rqp);
532 	return (error);
533 }
534 
535 int
536 smb2_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
537 {
538 	struct smb_vc *vcp;
539 	struct smb_rq *rqp = NULL;
540 	struct mbchain *mbp;
541 	struct mdchain *mdp;
542 	char *unc_name = NULL;
543 	int error, unc_len;
544 	uint16_t plen, *plenp;
545 	uint16_t options = 0;
546 	uint_t cnt0;
547 	uint32_t net_stype;
548 	uint16_t structure_size = 0;
549 	uint8_t smb2stype;
550 
551 	vcp = SSTOVC(ssp);
552 
553 	/*
554 	 * Make this a "VC-level" request, so it will have
555 	 * rqp->sr_share == NULL, and smb_iod_sendrq()
556 	 * will send it with TID = SMB_TID_UNKNOWN
557 	 *
558 	 * This also serves to bypass the wait for
559 	 * share state changes, which this call is
560 	 * trying to carry out.
561 	 */
562 	error = smb_rq_alloc(VCTOCP(vcp), SMB2_TREE_CONNECT, scred, &rqp);
563 	if (error)
564 		return (error);
565 
566 	/*
567 	 * Build the UNC name, i.e. "//server/share"
568 	 * but with backslashes of course.
569 	 * size math: three slashes, one null.
570 	 */
571 	unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name);
572 	unc_name = kmem_alloc(unc_len, KM_SLEEP);
573 	(void) snprintf(unc_name, unc_len, "\\\\%s\\%s",
574 	    vcp->vc_srvname, ssp->ss_name);
575 	SMBSDEBUG("unc_name: \"%s\"", unc_name);
576 
577 	/*
578 	 * Build the request.
579 	 */
580 	mbp = &rqp->sr_rq;
581 
582 	mb_put_uint16le(mbp, 9);	/* Struct size */
583 	mb_put_uint16le(mbp, 0);	/* Reserved */
584 	mb_put_uint16le(mbp, 72);	/* Path Offset */
585 
586 	/*
587 	 * Fill in path length after we put the string, so we know
588 	 * the length after conversion from UTF-8 to UCS-2.
589 	 */
590 	plenp = mb_reserve(mbp, 2);
591 	cnt0 = mbp->mb_count;
592 
593 	/* UNC resource name (without the null) */
594 	error = smb_put_dmem(mbp, vcp, unc_name, unc_len - 1,
595 	    SMB_CS_NONE, NULL);
596 	if (error)
597 		goto out;
598 
599 	/* Now go back and fill in the path length. */
600 	plen = (uint16_t)(mbp->mb_count - cnt0);
601 	*plenp = htoles(plen);
602 
603 	/*
604 	 * Run the request.
605 	 *
606 	 * Using NOINTR_RECV because we don't want to risk
607 	 * missing a successful tree connect response,
608 	 * which would "leak" Tree IDs.
609 	 */
610 	rqp->sr_flags |= SMBR_NOINTR_RECV;
611 	error = smb2_rq_simple(rqp);
612 	SMBSDEBUG("%d\n", error);
613 	if (error) {
614 		/*
615 		 * If we get the server name wrong, i.e. due to
616 		 * mis-configured name services, this will be
617 		 * NT_STATUS_DUPLICATE_NAME.  Log this error.
618 		 */
619 		SMBERROR("(%s) failed, status=0x%x",
620 		    unc_name, rqp->sr_error);
621 		goto out;
622 	}
623 
624 	/*
625 	 * Parse the tree connect response
626 	 */
627 	smb_rq_getreply(rqp, &mdp);
628 
629 	/* Check structure size is 16 */
630 	md_get_uint16le(mdp, &structure_size);
631 	if (structure_size != 16) {
632 		error = EBADRPC;
633 		goto out;
634 	}
635 
636 	md_get_uint8(mdp, &smb2stype);
637 	md_get_uint8(mdp, NULL);	/* reserved */
638 	md_get_uint32le(mdp, &ssp->ss2_share_flags);
639 	md_get_uint32le(mdp, &ssp->ss2_share_caps);
640 	error = md_get_uint32le(mdp, NULL);	/* maxAccessRights */
641 	if (error)
642 		goto out;
643 
644 	/*
645 	 * Convert SMB2 share type to NetShareEnum share type
646 	 */
647 	switch (smb2stype) {
648 	case SMB2_SHARE_TYPE_DISK:
649 		net_stype = STYPE_DISKTREE;
650 		break;
651 	case SMB2_SHARE_TYPE_PIPE:
652 		net_stype = STYPE_IPC;
653 		break;
654 	case SMB2_SHARE_TYPE_PRINT:
655 		net_stype = STYPE_PRINTQ;
656 		break;
657 	default:
658 		net_stype = STYPE_UNKNOWN;
659 		break;
660 	}
661 	ssp->ss_type = net_stype;
662 
663 	/*
664 	 * Map SMB 2/3 capabilities to SMB 1 options,
665 	 * for common code that looks there.
666 	 */
667 	if (ssp->ss2_share_caps & SMB2_SHARE_CAP_DFS)
668 		options |= SMB_SHARE_IS_IN_DFS;
669 
670 	/* Update share state */
671 	SMB_SS_LOCK(ssp);
672 	ssp->ss2_tree_id = rqp->sr2_rsptreeid;
673 	ssp->ss_vcgenid = vcp->vc_genid;
674 	ssp->ss_options = options;
675 	ssp->ss_flags |= SMBS_CONNECTED;
676 	SMB_SS_UNLOCK(ssp);
677 
678 out:
679 	if (unc_name)
680 		kmem_free(unc_name, unc_len);
681 	smb_rq_done(rqp);
682 	return (error);
683 }
684 
685 int
686 smb2_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
687 {
688 	struct smb_vc *vcp;
689 	struct smb_rq *rqp;
690 	struct mbchain *mbp;
691 	int error;
692 
693 	if (ssp->ss2_tree_id == SMB2_TID_UNKNOWN)
694 		return (0);
695 
696 	/*
697 	 * Build this as a "VC-level" request, so it will
698 	 * avoid testing the _GONE flag on the share,
699 	 * which has already been set at this point.
700 	 * Add the share pointer "by hand" below, so
701 	 * smb_iod_sendrq will plug in the TID.
702 	 */
703 	vcp = SSTOVC(ssp);
704 	error = smb_rq_alloc(VCTOCP(vcp), SMB2_TREE_DISCONNECT, scred, &rqp);
705 	if (error)
706 		return (error);
707 	rqp->sr_share = ssp; /* See "by hand" above. */
708 
709 	/*
710 	 * Fill in SMB2 Tree Disconnect part
711 	 */
712 	smb_rq_getrequest(rqp, &mbp);
713 	mb_put_uint16le(mbp, 4);	/* Struct size */
714 	mb_put_uint16le(mbp, 0);	/* Reserved */
715 
716 	/*
717 	 * Run this with a relatively short timeout. (5 sec.)
718 	 * We don't really care about the result here, but we
719 	 * do need to make sure we send this out, or we could
720 	 * "leak" active tree IDs on interrupt or timeout.
721 	 * The NOINTR_SEND flag makes this request immune to
722 	 * interrupt or timeout until the send is done.
723 	 * Also, don't reconnect for this, of course!
724 	 */
725 	rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
726 	error = smb2_rq_simple_timed(rqp, 5);
727 
728 	smb_rq_done(rqp);
729 
730 	/* Whether we get an error or not... */
731 	ssp->ss2_tree_id = SMB2_TID_UNKNOWN;
732 
733 	return (error);
734 }
735 
736 /*
737  * Put the name, first skipping a leading slash.
738  */
739 static int
740 put_name_skip_slash(struct mbchain *mbp, struct mbchain *name_mbp)
741 {
742 	mblk_t *m;
743 
744 	if (name_mbp == NULL)
745 		return (0);
746 	m = name_mbp->mb_top;
747 	if (m == NULL)
748 		return (0);
749 
750 	/* Use a dup of the message to leave the passed one untouched. */
751 	m = dupmsg(m);
752 	if (m == NULL)
753 		return (ENOSR);
754 
755 	if (MBLKL(m) >= 2 &&
756 	    m->b_rptr[0] == '\\' &&
757 	    m->b_rptr[1] == '\0')
758 		m->b_rptr += 2;
759 
760 	return (mb_put_mbuf(mbp, m));
761 }
762 
763 /*
764  * Modern create/open of file or directory.
765  *
766  * The passed name is a full path relative to the share root.
767  * Callers prepare paths with a leading slash (backslash)
768  * because that's what SMB1 expected.  SMB2 does not allow the
769  * leading slash here.  To make life simpler for callers skip a
770  * leading slash here.  That allows callers use use common logic
771  * for building paths without needing to know if the connection
772  * is using SMB1 or SMB2 (just build paths with a leading slash).
773  */
774 int
775 smb2_smb_ntcreate(
776 	struct smb_share *ssp,
777 	struct mbchain	*name_mb,
778 	struct mbchain	*cctx_in,
779 	struct mdchain	*cctx_out,
780 	uint32_t cr_flags,	/* create flags */
781 	uint32_t req_acc,	/* requested access */
782 	uint32_t efa,		/* ext. file attrs (DOS attr +) */
783 	uint32_t share_acc,
784 	uint32_t open_disp,	/* open disposition */
785 	uint32_t createopt,	/* NTCREATEX_OPTIONS_ */
786 	uint32_t impersonate,	/* NTCREATEX_IMPERSONATION_... */
787 	struct smb_cred *scrp,
788 	smb2fid_t *fidp,	/* returned FID */
789 	uint32_t *cr_act_p,	/* optional create action */
790 	struct smbfattr *fap)	/* optional attributes */
791 {
792 	struct smbfattr fa;
793 	struct smb_rq *rqp;
794 	struct mbchain *mbp;
795 	struct mdchain *mdp;
796 	uint16_t *name_offp;
797 	uint16_t *name_lenp;
798 	uint32_t *cctx_offp;
799 	uint32_t *cctx_lenp;
800 	uint32_t rcc_off, rcc_len;
801 	smb2fid_t smb2_fid;
802 	uint64_t llongint;
803 	uint32_t longint, createact;
804 	uint_t off, len;
805 	int error;
806 	uint16_t StructSize = 57;	// [MS-SMB2]
807 
808 	bzero(&fa, sizeof (fa));
809 
810 	error = smb_rq_alloc(SSTOCP(ssp), SMB2_CREATE, scrp, &rqp);
811 	if (error)
812 		return (error);
813 
814 	/*
815 	 * Todo: Assemble creat contexts (if needed)
816 	 * into an mbchain.
817 	 */
818 
819 	/*
820 	 * Build the SMB 2/3 Create Request
821 	 */
822 	smb_rq_getrequest(rqp, &mbp);
823 	mb_put_uint16le(mbp, StructSize);
824 	mb_put_uint8(mbp, 0);				/* Security flags */
825 	mb_put_uint8(mbp, SMB2_OPLOCK_LEVEL_NONE);	/* Oplock level */
826 	mb_put_uint32le(mbp, impersonate);	/* Impersonation Level */
827 	mb_put_uint64le(mbp, cr_flags);
828 	mb_put_uint64le(mbp, 0);			/* Reserved */
829 	mb_put_uint32le(mbp, req_acc);
830 	mb_put_uint32le(mbp, efa);			/* File attributes */
831 	mb_put_uint32le(mbp, share_acc);		/* Share access */
832 	mb_put_uint32le(mbp, open_disp);		/* Create disposition */
833 	mb_put_uint32le(mbp, createopt);		/* Create options */
834 
835 	name_offp = mb_reserve(mbp, 2);			/* Name offset */
836 	name_lenp = mb_reserve(mbp, 2);			/* Name len */
837 
838 	cctx_offp = mb_reserve(mbp, 4);			/* Context offset */
839 	cctx_lenp = mb_reserve(mbp, 4);			/* Context len */
840 
841 	/*
842 	 * Put the file name, which is provided in an mbchain.
843 	 * If there's a leading slash, skip it (see above).
844 	 */
845 	off = mbp->mb_count;
846 	*name_offp = htoles((uint16_t)off);
847 	error = put_name_skip_slash(mbp, name_mb);
848 	if (error)
849 		goto out;
850 	len = mbp->mb_count - off;
851 	*name_lenp = htoles((uint16_t)len);
852 
853 	/*
854 	 * Now the create contexts (if provided)
855 	 */
856 	if (cctx_in != NULL) {
857 		off = mbp->mb_count;
858 		*cctx_offp = htolel((uint32_t)off);
859 		mb_put_mbchain(mbp, cctx_in);
860 		len = mbp->mb_count - off;
861 		*cctx_lenp = htolel((uint32_t)len);
862 	} else {
863 		*cctx_offp = 0;
864 		*cctx_lenp = 0;
865 	}
866 
867 	/*
868 	 * If we didn't put any variable-sized data, we'll have
869 	 * put exactly 56 bytes of data, and we need to pad out
870 	 * this request to the 57 bytes StructSize indicated.
871 	 */
872 	if (mbp->mb_count < (StructSize + SMB2_HDRLEN))
873 		mb_put_uint8(mbp, 0);
874 
875 	/*
876 	 * Don't want to risk missing a successful
877 	 * open response, or we could "leak" FIDs.
878 	 */
879 	rqp->sr_flags |= SMBR_NOINTR_RECV;
880 	error = smb2_rq_simple_timed(rqp, smb2_timo_open);
881 	if (error)
882 		goto out;
883 
884 	/*
885 	 * Parse SMB 2/3 Create Response
886 	 */
887 	smb_rq_getreply(rqp, &mdp);
888 
889 	/* Check structure size is 89 */
890 	error = md_get_uint16le(mdp, &StructSize);
891 	if (StructSize != 89) {
892 		error = EBADRPC;
893 		goto out;
894 	}
895 
896 	md_get_uint8(mdp, NULL);		/* oplock lvl granted */
897 	md_get_uint8(mdp, NULL);		/* mbz */
898 	md_get_uint32le(mdp, &createact);	/* create_action */
899 	md_get_uint64le(mdp, &llongint);	/* creation time */
900 	smb_time_NT2local(llongint, &fa.fa_createtime);
901 	md_get_uint64le(mdp, &llongint);	/* access time */
902 	smb_time_NT2local(llongint, &fa.fa_atime);
903 	md_get_uint64le(mdp, &llongint);	/* write time */
904 	smb_time_NT2local(llongint, &fa.fa_mtime);
905 	md_get_uint64le(mdp, &llongint);	/* change time */
906 	smb_time_NT2local(llongint, &fa.fa_ctime);
907 	md_get_uint64le(mdp, &llongint);	/* allocation size */
908 	fa.fa_allocsz = llongint;
909 	md_get_uint64le(mdp, &llongint);	/* EOF position */
910 	fa.fa_size = llongint;
911 	md_get_uint32le(mdp, &longint);		/* attributes */
912 	fa.fa_attr = longint;
913 	md_get_uint32le(mdp, NULL);		/* reserved */
914 
915 	/* Get SMB 2/3 File ID and create user fid to return */
916 	md_get_uint64le(mdp, &smb2_fid.fid_persistent);
917 	error = md_get_uint64le(mdp, &smb2_fid.fid_volatile);
918 	if (error)
919 		goto out;
920 
921 	/* Get Context Offset */
922 	error = md_get_uint32le(mdp, &rcc_off);
923 	if (error)
924 		goto out;
925 	/* Get Context Length */
926 	error = md_get_uint32le(mdp, &rcc_len);
927 	if (error)
928 		goto out;
929 
930 	/*
931 	 * If the caller wants the returned create contexts, parse.
932 	 * Context offset is from the beginning of SMB 2/3 Header
933 	 * Calculate how much further we have to go to get to it.
934 	 * Current offset is: SMB2_HDRLEN + 88
935 	 */
936 	if (rcc_len != 0) {
937 		int skip = (int)rcc_off - (SMB2_HDRLEN + 88);
938 		if (skip < 0) {
939 			error = EBADRPC;
940 			goto out;
941 		}
942 		if (skip > 0) {
943 			md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
944 		}
945 		if (cctx_out != NULL) {
946 			mblk_t *m = NULL;
947 			error = md_get_mbuf(mdp, rcc_len, &m);
948 			if (error)
949 				goto out;
950 			md_initm(cctx_out, m);
951 		}
952 	}
953 
954 out:
955 	smb_rq_done(rqp);
956 	if (error)
957 		return (error);
958 
959 	*fidp = smb2_fid;
960 	if (cr_act_p)
961 		*cr_act_p = createact;
962 	if (fap)
963 		*fap = fa; /* struct copy */
964 
965 	return (0);
966 }
967 
968 int
969 smb2_smb_close(struct smb_share *ssp, smb2fid_t *fid, struct smb_cred *scrp)
970 {
971 	struct smb_rq *rqp;
972 	struct mbchain *mbp;
973 	int error;
974 
975 	error = smb_rq_alloc(SSTOCP(ssp), SMB2_CLOSE, scrp, &rqp);
976 	if (error)
977 		return (error);
978 
979 	/*
980 	 * Build the SMB 2/3 Close Request
981 	 */
982 	smb_rq_getrequest(rqp, &mbp);
983 	mb_put_uint16le(mbp, 24);		/* Struct size */
984 	mb_put_uint16le(mbp, 0);		/* Flags */
985 	mb_put_uint32le(mbp, 0);		/* Reserved */
986 
987 	mb_put_uint64le(mbp, fid->fid_persistent);
988 	mb_put_uint64le(mbp, fid->fid_volatile);
989 
990 	/* Make sure we send, but only if already connected */
991 	rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
992 	error = smb2_rq_simple(rqp);
993 	smb_rq_done(rqp);
994 	return (error);
995 }
996 
997 int
998 smb2_smb_ioctl(
999 	struct smb_share *ssp,
1000 	smb2fid_t *fid,
1001 	struct mbchain	*data_in,
1002 	struct mdchain	*data_out,
1003 	uint32_t *data_out_sz,	/* max / returned */
1004 	uint32_t ctl_code,
1005 	struct smb_cred *scrp)
1006 {
1007 	struct smb_rq *rqp;
1008 	struct mbchain *mbp;
1009 	struct mdchain *mdp;
1010 	uint32_t *data_in_offp;
1011 	uint32_t *data_in_lenp;
1012 	uint32_t data_out_off;
1013 	uint32_t data_out_len;
1014 	uint16_t length = 0;
1015 	uint_t off, len;
1016 	int error;
1017 
1018 	error = smb_rq_alloc(SSTOCP(ssp), SMB2_IOCTL, scrp, &rqp);
1019 	if (error)
1020 		return (error);
1021 
1022 	/*
1023 	 * Build the SMB 2 IOCTL Request
1024 	 */
1025 	smb_rq_getrequest(rqp, &mbp);
1026 	mb_put_uint16le(mbp, 57);		/* Struct size */
1027 	mb_put_uint16le(mbp, 0);		/* Reserved */
1028 	mb_put_uint32le(mbp, ctl_code);
1029 
1030 	mb_put_uint64le(mbp, fid->fid_persistent);
1031 	mb_put_uint64le(mbp, fid->fid_volatile);
1032 
1033 	data_in_offp = mb_reserve(mbp, 4);
1034 	data_in_lenp = mb_reserve(mbp, 4);
1035 	mb_put_uint32le(mbp, 0);		/* Max input resp */
1036 
1037 	mb_put_uint32le(mbp, 0);		/* Output offset */
1038 	mb_put_uint32le(mbp, 0);		/* Output count */
1039 	mb_put_uint32le(mbp, *data_out_sz);
1040 
1041 	mb_put_uint32le(mbp, SMB2_IOCTL_IS_FSCTL); /* Flags */
1042 	mb_put_uint32le(mbp, 0);		/* Reserved2 */
1043 
1044 	/*
1045 	 * Now data_in (if provided)
1046 	 */
1047 	if (data_in != NULL) {
1048 		off = mbp->mb_count;
1049 		*data_in_offp = htolel((uint32_t)off);
1050 		mb_put_mbchain(mbp, data_in);
1051 		len = mbp->mb_count - off;
1052 		*data_in_lenp = htolel((uint32_t)len);
1053 	} else {
1054 		*data_in_offp = 0;
1055 		*data_in_lenp = 0;
1056 	}
1057 
1058 	/*
1059 	 * Run the request
1060 	 */
1061 	error = smb2_rq_simple_timed(rqp, smb2_timo_default);
1062 	if (error)
1063 		goto out;
1064 
1065 	/*
1066 	 * Parse SMB 2 Ioctl Response
1067 	 */
1068 	smb_rq_getreply(rqp, &mdp);
1069 
1070 	/* Check structure size is 49 */
1071 	md_get_uint16le(mdp, &length);
1072 	if (length != 49) {
1073 		error = EBADRPC;
1074 		goto out;
1075 	}
1076 	md_get_uint16le(mdp, NULL);	/* reserved */
1077 	md_get_uint32le(mdp, NULL);	/* Get CtlCode */
1078 	md_get_uint64le(mdp, NULL);	/* fid_persistent */
1079 	md_get_uint64le(mdp, NULL);	/* fid_volatile */
1080 	md_get_uint32le(mdp, NULL);	/* Get Input offset */
1081 	md_get_uint32le(mdp, NULL);	/* Get Input count */
1082 
1083 	error = md_get_uint32le(mdp, &data_out_off);
1084 	if (error)
1085 		goto out;
1086 	error = md_get_uint32le(mdp, &data_out_len);
1087 	if (error)
1088 		goto out;
1089 
1090 	md_get_uint32le(mdp, NULL);	/* Flags */
1091 	md_get_uint32le(mdp, NULL);	/* reserved */
1092 
1093 	/*
1094 	 * If the caller wants the ioctl output data, parse.
1095 	 * Current offset is: SMB2_HDRLEN + 48
1096 	 * Always return the received length.
1097 	 */
1098 	*data_out_sz = data_out_len;
1099 	if (data_out_len != 0) {
1100 		int skip = (int)data_out_off - (SMB2_HDRLEN + 48);
1101 		if (skip < 0) {
1102 			error = EBADRPC;
1103 			goto out;
1104 		}
1105 		if (skip > 0) {
1106 			md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
1107 		}
1108 		if (data_out != NULL) {
1109 			mblk_t *m = NULL;
1110 			error = md_get_mbuf(mdp, data_out_len, &m);
1111 			if (error)
1112 				goto out;
1113 			md_initm(data_out, m);
1114 		}
1115 	}
1116 
1117 out:
1118 	smb_rq_done(rqp);
1119 
1120 	return (error);
1121 }
1122 
1123 int
1124 smb2_smb_read(smb_fh_t *fhp, uint32_t *lenp,
1125 	uio_t *uiop, smb_cred_t *scred, int timo)
1126 {
1127 	struct smb_share *ssp = FHTOSS(fhp);
1128 	struct smb_rq *rqp;
1129 	struct mbchain *mbp;
1130 	struct mdchain *mdp;
1131 	int error;
1132 	uint64_t off64 = uiop->uio_loffset;
1133 	uint32_t rlen;
1134 	uint16_t length = 0;
1135 	uint8_t data_offset;
1136 
1137 	error = smb_rq_alloc(SSTOCP(ssp), SMB2_READ, scred, &rqp);
1138 	if (error)
1139 		return (error);
1140 
1141 	/*
1142 	 * Build the SMB 2 Read Request
1143 	 */
1144 	smb_rq_getrequest(rqp, &mbp);
1145 	mb_put_uint16le(mbp, 49);		/* Struct size */
1146 	mb_put_uint16le(mbp, 0);		/* Padding and Reserved */
1147 
1148 	mb_put_uint32le(mbp, *lenp);		/* Length of read */
1149 	mb_put_uint64le(mbp, off64);		/* Offset */
1150 
1151 	mb_put_uint64le(mbp, fhp->fh_fid2.fid_persistent);
1152 	mb_put_uint64le(mbp, fhp->fh_fid2.fid_volatile);
1153 
1154 	mb_put_uint32le(mbp, 1);	/* MinCount */
1155 					/* (only indicates blocking) */
1156 
1157 	mb_put_uint32le(mbp, 0);	/* Channel */
1158 	mb_put_uint32le(mbp, 0);	/* Remaining */
1159 	mb_put_uint32le(mbp, 0);	/* Channel offset/len */
1160 	mb_put_uint8(mbp, 0);		/* data "blob" (pad) */
1161 
1162 	if (timo == 0)
1163 		timo = smb2_timo_read;
1164 	error = smb2_rq_simple_timed(rqp, timo);
1165 	if (error)
1166 		goto out;
1167 
1168 	/*
1169 	 * Parse SMB 2 Read Response
1170 	 */
1171 	smb_rq_getreply(rqp, &mdp);
1172 
1173 	/* Check structure size is 17 */
1174 	md_get_uint16le(mdp, &length);
1175 	if (length != 17) {
1176 		error = EBADRPC;
1177 		goto out;
1178 	}
1179 	md_get_uint8(mdp, &data_offset);
1180 	md_get_uint8(mdp, NULL);		/* reserved */
1181 
1182 	/* Get Data Length read */
1183 	error = md_get_uint32le(mdp, &rlen);
1184 	if (error)
1185 		goto out;
1186 
1187 	md_get_uint32le(mdp, NULL);	/* Data Remaining (always 0) */
1188 	md_get_uint32le(mdp, NULL);	/* Get Reserved2 (always 0) */
1189 
1190 	/*
1191 	 * Data offset is from the beginning of SMB 2/3 Header
1192 	 * Calculate how much further we have to go to get to it.
1193 	 */
1194 	if (data_offset < (SMB2_HDRLEN + 16)) {
1195 		error = EBADRPC;
1196 		goto out;
1197 	}
1198 	if (data_offset > (SMB2_HDRLEN + 16)) {
1199 		int skip = data_offset - (SMB2_HDRLEN + 16);
1200 		md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
1201 	}
1202 
1203 	/*
1204 	 * Get the data
1205 	 */
1206 	if (rlen == 0) {
1207 		*lenp = rlen;
1208 		goto out;
1209 	}
1210 	/* paranoid */
1211 	if (rlen > *lenp) {
1212 		SMBSDEBUG("bad server! rlen %d, len %d\n",
1213 		    rlen, *lenp);
1214 		rlen = *lenp;
1215 	}
1216 
1217 	error = md_get_uio(mdp, uiop, rlen);
1218 	if (error)
1219 		goto out;
1220 
1221 	/* Success */
1222 	*lenp = rlen;
1223 
1224 out:
1225 	smb_rq_done(rqp);
1226 	return (error);
1227 }
1228 
1229 int
1230 smb2_smb_write(smb_fh_t *fhp, uint32_t *lenp,
1231 	uio_t *uiop, smb_cred_t *scred, int timo)
1232 {
1233 	struct smb_share *ssp = FHTOSS(fhp);
1234 	struct smb_rq *rqp;
1235 	struct mbchain *mbp;
1236 	struct mdchain *mdp;
1237 	int error;
1238 	uint64_t off64 = uiop->uio_loffset;
1239 	uint32_t rlen;
1240 	uint16_t data_offset;
1241 	uint16_t length = 0;
1242 
1243 	error = smb_rq_alloc(SSTOCP(ssp), SMB2_WRITE, scred, &rqp);
1244 	if (error)
1245 		return (error);
1246 
1247 	/*
1248 	 * Build the SMB 2 Write Request
1249 	 */
1250 	smb_rq_getrequest(rqp, &mbp);
1251 	mb_put_uint16le(mbp, 49);		/* Struct size */
1252 	data_offset = SMB2_HDRLEN + 48;
1253 	mb_put_uint16le(mbp, data_offset);	/* Data Offset */
1254 	mb_put_uint32le(mbp, *lenp);		/* Length of write */
1255 	mb_put_uint64le(mbp, off64);		/* Offset */
1256 
1257 	mb_put_uint64le(mbp, fhp->fh_fid2.fid_persistent);
1258 	mb_put_uint64le(mbp, fhp->fh_fid2.fid_volatile);
1259 
1260 	mb_put_uint32le(mbp, 0);		/* Channel */
1261 	mb_put_uint32le(mbp, 0);		/* Remaining */
1262 	mb_put_uint32le(mbp, 0);		/* Channel offset/len */
1263 	mb_put_uint32le(mbp, 0);		/* Write flags */
1264 
1265 	error = mb_put_uio(mbp, uiop, *lenp);
1266 	if (error)
1267 		goto out;
1268 
1269 	if (timo == 0)
1270 		timo = smb2_timo_write;
1271 	error = smb2_rq_simple_timed(rqp, timo);
1272 	if (error)
1273 		goto out;
1274 
1275 	/*
1276 	 * Parse SMB 2/3 Write Response
1277 	 */
1278 	smb_rq_getreply(rqp, &mdp);
1279 
1280 	/* Check structure size is 17 */
1281 	md_get_uint16le(mdp, &length);
1282 	if (length != 17) {
1283 		error = EBADRPC;
1284 		goto out;
1285 	}
1286 
1287 	md_get_uint16le(mdp, NULL);    /* Get Reserved */
1288 
1289 	/* Get Data Length written */
1290 	error = md_get_uint32le(mdp, &rlen);
1291 	if (error)
1292 		goto out;
1293 
1294 	/* Get Data Remaining (always 0) */
1295 	md_get_uint32le(mdp, NULL);
1296 
1297 	/* Get Reserved2 (always 0) */
1298 	md_get_uint32le(mdp, NULL);
1299 
1300 	/* Success */
1301 	*lenp = rlen;
1302 
1303 out:
1304 	smb_rq_done(rqp);
1305 	return (error);
1306 }
1307 
1308 /*
1309  * Note: the IOD calls this, so this request must not wait for
1310  * connection state changes, etc. (uses smb2_rq_internal)
1311  */
1312 int
1313 smb2_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo)
1314 {
1315 	struct smb_rq *rqp;
1316 	struct mbchain *mbp;
1317 	int error;
1318 
1319 	error = smb_rq_alloc(VCTOCP(vcp), SMB2_ECHO, scred, &rqp);
1320 	if (error)
1321 		return (error);
1322 
1323 	/*
1324 	 * Build the SMB 2 Echo Request
1325 	 */
1326 	smb_rq_getrequest(rqp, &mbp);
1327 	mb_put_uint16le(mbp, 4);		/* Struct size */
1328 	mb_put_uint16le(mbp, 0);		/* Reserved */
1329 
1330 	rqp->sr_flags |= SMBR_NORECONNECT;
1331 	error = smb2_rq_internal(rqp, timo);
1332 
1333 	smb_rq_done(rqp);
1334 	return (error);
1335 }
1336