xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c (revision adee678425979226b2b55d1a0b39ce4c989382e9)
1 /*
2  * Copyright (c) 2000-2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: smb_smb.c,v 1.35.100.2 2005/06/02 00:55:39 lindak Exp $
33  */
34 
35 /*
36  * Portions Copyright (C) 2001 - 2014 Apple Inc. All rights reserved.
37  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
38  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
39  */
40 
41 /*
42  * various SMB requests. Most of the routines merely packs data into mbufs.
43  */
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kmem.h>
47 #include <sys/proc.h>
48 #include <sys/lock.h>
49 #include <sys/socket.h>
50 #include <sys/uio.h>
51 #include <sys/random.h>
52 #include <sys/note.h>
53 #include <sys/errno.h>
54 #include <sys/cmn_err.h>
55 
56 #include <netsmb/smb_osdep.h>
57 
58 #include <netsmb/smb.h>
59 #include <netsmb/smb_conn.h>
60 #include <netsmb/smb_rq.h>
61 #include <netsmb/smb_subr.h>
62 #include <netsmb/smb_tran.h>
63 
64 #define	STYPE_LEN	8	/* share type strings */
65 
66 struct smb_dialect {
67 	int		d_id;
68 	const char	*d_name;
69 };
70 
71 static struct smb_dialect smb_dialects[3] = {
72 	{SMB_DIALECT_NTLM0_12,	"NT LANMAN 1.0"},
73 	{SMB_DIALECT_NTLM0_12,	"NT LM 0.12"},
74 #define	NDIALECT_SMB1	2
75 	{SMB_DIALECT_SMB2_FF,	"SMB 2.???"},
76 #define	NDIALECT_SMB2	3
77 };
78 
79 static const uint32_t smb_clnt_caps_mask =
80     SMB_CAP_UNICODE |
81     SMB_CAP_LARGE_FILES |
82     SMB_CAP_NT_SMBS |
83     SMB_CAP_STATUS32 |
84     SMB_CAP_EXT_SECURITY;
85 
86 /*
87  * Default timeout values, all in seconds.
88  * Make these tunable (only via mdb for now).
89  */
90 int smb_timo_notice = 15;
91 int smb_timo_default = 30;	/* was SMB_DEFRQTIMO */
92 int smb_timo_logon = 45;
93 int smb_timo_open = 45;
94 int smb_timo_read = 45;
95 int smb_timo_write = 60;	/* was SMBWRTTIMO */
96 int smb_timo_append = 90;
97 
98 int
smb_smb_negotiate(struct smb_vc * vcp,struct smb_cred * scred)99 smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
100 {
101 	smb_sopt_t *sv = &vcp->vc_sopt;
102 	smbioc_ssn_work_t *wk = &vcp->vc_work;
103 	struct smb_rq *rqp = NULL;
104 	struct mbchain *mbp = NULL;
105 	struct mdchain *mdp = NULL;
106 	struct smb_dialect *dp;
107 	int err, sblen, tlen;
108 	uint8_t wc, eklen;
109 	uint16_t dindex, bc;
110 	uint16_t ndialects;
111 	boolean_t will_sign = B_FALSE;
112 
113 	/*
114 	 * Initialize: vc_hflags and vc_hflags2.
115 	 * Note: vcp->vc_hflags* are copied into the
116 	 * (per request) rqp->rq_hflags* by smb_rq_init.
117 	 *
118 	 * Like Windows, set FLAGS2_UNICODE in our first request,
119 	 * even though technically we don't yet know whether the
120 	 * server supports Unicode.  Will clear this flag below
121 	 * if we find out it doesn't.  Need to do this because
122 	 * some servers reject all non-Unicode requests.
123 	 */
124 	vcp->vc_hflags =
125 	    SMB_FLAGS_CASELESS |
126 	    SMB_FLAGS_CANONICAL_PATHNAMES;
127 	vcp->vc_hflags2 =
128 	    SMB_FLAGS2_KNOWS_LONG_NAMES |
129 	    SMB_FLAGS2_KNOWS_EAS |
130 	    SMB_FLAGS2_IS_LONG_NAME |
131 	    SMB_FLAGS2_EXT_SEC |
132 	    SMB_FLAGS2_ERR_STATUS |
133 	    SMB_FLAGS2_UNICODE;
134 
135 	/*
136 	 * The initial UID needs to be zero,
137 	 */
138 	vcp->vc_smbuid = 0;
139 
140 	/*
141 	 * (Re)init negotiated values
142 	 */
143 	bzero(sv, sizeof (*sv));
144 	sv->sv_maxmux = 1;
145 	sv->sv_maxvcs = 1;
146 	sv->sv_maxtx = 1024;
147 
148 	/*
149 	 * Should we offer the magic SMB2 dialect?
150 	 */
151 	if (vcp->vc_ssn.ssn_maxver >= SMB2_DIALECT_BASE)
152 		ndialects = NDIALECT_SMB2;
153 	else
154 		ndialects = NDIALECT_SMB1;
155 
156 	err = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
157 	if (err)
158 		return (err);
159 
160 	/*
161 	 * Build the SMB request.
162 	 */
163 	smb_rq_getrequest(rqp, &mbp);
164 	smb_rq_wstart(rqp);
165 	smb_rq_wend(rqp);
166 	smb_rq_bstart(rqp);
167 	for (dindex = 0; dindex < ndialects; dindex++) {
168 		dp = &smb_dialects[dindex];
169 		mb_put_uint8(mbp, SMB_DT_DIALECT);
170 		tlen = strlen(dp->d_name) + 1;
171 		mb_put_mem(mbp, dp->d_name, tlen, MB_MSYSTEM);
172 	}
173 	smb_rq_bend(rqp);
174 
175 	/*
176 	 * Do the OTW call.
177 	 */
178 	err = smb_rq_internal(rqp, smb_timo_default);
179 	/*
180 	 * If it's an SMB1-to-SMB2 negotiate response,
181 	 * call the special handler and then skip the
182 	 * whole rest of this function.
183 	 */
184 	if (err == EPROTO) {
185 		err = smb2_parse_smb1nego_resp(rqp);
186 		smb_rq_done(rqp);
187 		return (err);
188 	}
189 	if (err) {
190 		SMBSDEBUG("smb_rq_internal, err %d", err);
191 		goto errout;
192 	}
193 	/* Should only get status success. */
194 	if (rqp->sr_error != NT_STATUS_SUCCESS) {
195 		err = ENOTSUP;
196 		goto errout;
197 	}
198 
199 	/*
200 	 * Decode the response
201 	 *
202 	 * Comments to right show names as described in
203 	 * The Microsoft SMB Protocol spec. [MS-SMB]
204 	 * section 2.2.3
205 	 */
206 	smb_rq_getreply(rqp, &mdp);
207 	(void) md_get_uint8(mdp, &wc);
208 	err = md_get_uint16le(mdp, &dindex);
209 	if (err != 0)
210 		goto errout;
211 	if (dindex >= ndialects) {
212 		SMBERROR("Invalid dialect index from server: %s\n",
213 		    vcp->vc_srvname);
214 		err = EBADRPC;
215 		goto errout;
216 	}
217 	dp = smb_dialects + dindex;
218 	sv->sv_proto = dp->d_id;
219 	SMBSDEBUG("Dialect %s", dp->d_name);
220 	if (dp->d_id < SMB_DIALECT_NTLM0_12) {
221 		SMBSDEBUG("old dialect %s", dp->d_name);
222 		goto errout;
223 	}
224 	if (wc != 17) {
225 		SMBSDEBUG("bad wc %d", (int)wc);
226 		goto errout;
227 	}
228 	md_get_uint8(mdp, &sv->sv_sm);		/* SecurityMode */
229 	md_get_uint16le(mdp, &sv->sv_maxmux);	/* MaxMpxCount */
230 	md_get_uint16le(mdp, &sv->sv_maxvcs);	/* MaxCountVCs */
231 	md_get_uint32le(mdp, &sv->sv_maxtx);	/* MaxBufferSize */
232 	md_get_uint32le(mdp, &sv->sv_maxraw);	/* MaxRawSize */
233 	md_get_uint32le(mdp, &sv->sv_skey);	/* SessionKey */
234 	md_get_uint32le(mdp, &sv->sv_caps);	/* Capabilities */
235 	md_get_mem(mdp, NULL, 8, MB_MSYSTEM);	/* SystemTime(s) */
236 	md_get_uint16le(mdp, (uint16_t *)&sv->sv_tz);
237 	md_get_uint8(mdp, &eklen);	/* EncryptionKeyLength */
238 	err = md_get_uint16le(mdp, &bc);	/* ByteCount */
239 	if (err)
240 		goto errout;
241 
242 	/* BEGIN CSTYLED */
243 	/*
244 	 * Will we do SMB signing?  Or block the connection?
245 	 * The table below describes this logic.  References:
246 	 * [Windows Server Protocols: MS-SMB, sec. 3.2.4.2.3]
247 	 * http://msdn.microsoft.com/en-us/library/cc212511.aspx
248 	 * http://msdn.microsoft.com/en-us/library/cc212929.aspx
249 	 *
250 	 * Srv/Cli     | Required | Enabled    | If Required | Disabled
251 	 * ------------+----------+------------+-------------+-----------
252 	 * Required    | Signed   | Signed     | Signed      | Blocked [1]
253 	 * ------------+----------+------------+-------------+-----------
254 	 * Enabled     | Signed   | Signed     | Not Signed  | Not Signed
255 	 * ------------+----------+------------+-------------+-----------
256 	 * If Required | Signed   | Not Signed | Not Signed  | Not Signed
257 	 * ------------+----------+------------+-------------+-----------
258 	 * Disabled    | Blocked  | Not Signed | Not Signed  | Not Signed
259 	 *
260 	 * [1] Like Windows 2003 and later, we don't really implement
261 	 * the "Disabled" setting.  Instead we implement "If Required",
262 	 * so we always sign if the server requires signing.
263 	 */
264 	/* END CSTYLED */
265 
266 	if (sv->sv_sm & SMB_SM_SIGS_REQUIRE) {
267 		/*
268 		 * Server requires signing.  We will sign,
269 		 * even if local setting is "disabled".
270 		 */
271 		will_sign = B_TRUE;
272 	} else if (sv->sv_sm & SMB_SM_SIGS) {
273 		/*
274 		 * Server enables signing (client's option).
275 		 * If enabled locally, do signing.
276 		 */
277 		if (vcp->vc_vopt & SMBVOPT_SIGNING_ENABLED)
278 			will_sign = B_TRUE;
279 		/* else not signing. */
280 	} else {
281 		/*
282 		 * Server does not support signing.
283 		 * If we "require" it, bail now.
284 		 */
285 		if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) {
286 			SMBERROR("Client requires signing "
287 			    "but server has it disabled.");
288 			err = EBADRPC;
289 			goto errout;
290 		}
291 	}
292 
293 	/*
294 	 * Anonymous sessions can't sign.
295 	 */
296 	if (vcp->vc_vopt & SMBVOPT_ANONYMOUS) {
297 		will_sign = B_FALSE;
298 	}
299 
300 	SMBSDEBUG("Security signatures: %d", (int)will_sign);
301 	if (will_sign) {
302 		vcp->vc_flags |= SMBV_SIGNING;
303 		vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
304 
305 		/*
306 		 * MS-SMB 2.2.4.5 says that when SMB signing is enabled,
307 		 * we should NOT use "large read/write" even though the
308 		 * server might offer those capabilities.
309 		 */
310 		sv->sv_caps &= ~(SMB_CAP_LARGE_READX | SMB_CAP_LARGE_WRITEX);
311 	}
312 
313 	/* See comment above re. FLAGS2_UNICODE */
314 	if ((sv->sv_caps & SMB_CAP_UNICODE) != 0)
315 		vcp->vc_flags |= SMBV_UNICODE;
316 	else
317 		vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE;
318 
319 	if ((sv->sv_caps & SMB_CAP_STATUS32) == 0) {
320 		/* They don't do NT error codes. */
321 		vcp->vc_hflags2 &= ~SMB_FLAGS2_ERR_STATUS;
322 	}
323 
324 	/*
325 	 * Warn if they don't support SMB_CAP_NT_SMBS
326 	 * (We'll try to use NtCreate anyway)
327 	 */
328 	if ((sv->sv_caps & SMB_CAP_NT_SMBS) == 0) {
329 		cmn_err(CE_NOTE, "%s does not support SMB_CAP_NT_SMBS",
330 		    vcp->vc_srvname);
331 	}
332 
333 	/*
334 	 * The rest of the message varies depending on
335 	 * whether we've negotiated "extended security".
336 	 *
337 	 * With extended security, we have:
338 	 *	Server_GUID	(length 16)
339 	 *	Security_BLOB
340 	 * Otherwise we have:
341 	 *	EncryptionKey (length is eklen)
342 	 *	PrimaryDomain
343 	 */
344 	if (sv->sv_caps & SMB_CAP_EXT_SECURITY) {
345 		SMBSDEBUG("Ext.Security: yes");
346 
347 		/*
348 		 * Skip the server GUID.
349 		 */
350 		err = md_get_mem(mdp, NULL, SMB_GUIDLEN, MB_MSYSTEM);
351 		if (err)
352 			goto errout;
353 		/*
354 		 * Remainder is the security blob.
355 		 * Note: eklen "must be ignored" [MS-SMB]
356 		 */
357 		sblen = (int)bc - SMB_GUIDLEN;
358 		if (sblen < 0)
359 			goto errout;
360 		/* Security blob (hint) is next */
361 	} else {
362 		SMBSDEBUG("Ext.Security: no");
363 		err = ENOTSUP;
364 		goto errout;
365 	}
366 
367 	/*
368 	 * Copy the security blob out to user space.
369 	 * Buffer addr,size in vc_auth_rbuf,rlen
370 	 */
371 	if (wk->wk_u_auth_rlen < sblen) {
372 		SMBSDEBUG("vc_auth_rbuf too small");
373 		/* Give caller required size. */
374 		wk->wk_u_auth_rlen = sblen;
375 		err = EMSGSIZE;
376 		goto errout;
377 	}
378 	wk->wk_u_auth_rlen = sblen;
379 	err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr, sblen, MB_MUSER);
380 	if (err)
381 		goto errout;
382 
383 	/*
384 	 * A few sanity checks on what we received,
385 	 * becuse we will send these in ssnsetup.
386 	 *
387 	 * Maximum outstanding requests (we care),
388 	 * and Max. VCs (we only use one).  Also,
389 	 * MaxBufferSize lower limit per spec.
390 	 */
391 	if (sv->sv_maxmux < 1)
392 		sv->sv_maxmux = 1;
393 	if (sv->sv_maxvcs < 1)
394 		sv->sv_maxvcs = 1;
395 	if (sv->sv_maxtx < 1024)
396 		sv->sv_maxtx = 1024;
397 
398 	/*
399 	 * Maximum transfer size.
400 	 * Sanity checks:
401 	 *
402 	 * Let's be conservative about an upper limit here.
403 	 * Win2k uses 16644 (and others) so 32k should be a
404 	 * reasonable sanity limit for this value.
405 	 *
406 	 * Note that this limit does NOT affect READX/WRITEX
407 	 * with CAP_LARGE_..., which we nearly always use.
408 	 */
409 	vcp->vc_txmax = sv->sv_maxtx;
410 	if (vcp->vc_txmax > 0x8000)
411 		vcp->vc_txmax = 0x8000;
412 
413 	/*
414 	 * Max read/write sizes, WITHOUT overhead.
415 	 * This is just the payload size, so we must
416 	 * leave room for the SMB headers, etc.
417 	 * This is just the ct_txmax value, but
418 	 * reduced and rounded down.  Tricky bit:
419 	 *
420 	 * Servers typically give us a value that's
421 	 * some nice "round" number, i.e 0x4000 plus
422 	 * some overhead, i.e. Win2k: 16644==0x4104
423 	 * Subtract for the SMB header (32) and the
424 	 * SMB command word and byte vectors (34?),
425 	 * then round down to a 512 byte multiple.
426 	 */
427 	tlen = vcp->vc_txmax - 68;
428 	tlen &= 0xFE00;
429 
430 	vcp->vc_rwmax = tlen;
431 	vcp->vc_rxmax = tlen;
432 	vcp->vc_wxmax = tlen;
433 
434 	/*
435 	 * Most of the "capability" bits we offer in session setup
436 	 * are just copied from those offered by the server.
437 	 */
438 	sv->sv_caps &= smb_clnt_caps_mask;
439 
440 	smb_rq_done(rqp);
441 	return (0);
442 
443 errout:
444 	smb_rq_done(rqp);
445 	if (err == 0)
446 		err = EBADRPC;
447 	return (err);
448 }
449 
450 static const char NativeOS[] = "illumos";
451 static const char LanMan[] = "NETSMB";
452 
453 int
smb_smb_ssnsetup(struct smb_vc * vcp,struct smb_cred * scred)454 smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
455 {
456 	smb_sopt_t *sv = &vcp->vc_sopt;
457 	smbioc_ssn_work_t *wk = &vcp->vc_work;
458 	struct smb_rq *rqp = NULL;
459 	struct mbchain *mbp = NULL;
460 	struct mdchain *mdp = NULL;
461 	char *sb;
462 	int err, ret;
463 	uint32_t caps;
464 	uint16_t action, bc, sblen;
465 	uint8_t wc;
466 
467 	caps = sv->sv_caps;
468 	sb = wk->wk_u_auth_wbuf.lp_ptr;
469 	sblen = (uint16_t)wk->wk_u_auth_wlen;
470 
471 	err = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX,
472 	    scred, &rqp);
473 	if (err != 0) {
474 		ret = err;
475 		goto out;
476 	}
477 
478 	/*
479 	 * Build the SMB Session Setup request.
480 	 * Always extended security form.
481 	 */
482 	mbp = &rqp->sr_rq;
483 	smb_rq_wstart(rqp);
484 	mb_put_uint16le(mbp, 0xff);		/* 0: AndXCommand */
485 	mb_put_uint16le(mbp, 0);		/* 1: AndXOffset */
486 	mb_put_uint16le(mbp, sv->sv_maxtx);	/* 2: MaxBufferSize */
487 	mb_put_uint16le(mbp, sv->sv_maxmux);	/* 3: MaxMpxCount */
488 	mb_put_uint16le(mbp, 1);		/* 4: VcNumber */
489 	mb_put_uint32le(mbp, sv->sv_skey);	/* 5,6: Session Key */
490 	mb_put_uint16le(mbp, sblen);	/* 7: Sec. Blob Len */
491 	mb_put_uint32le(mbp, 0);	/* 8,9: reserved */
492 	mb_put_uint32le(mbp, caps);	/* 10,11: Capabilities */
493 	smb_rq_wend(rqp);		/* 12: Byte Count */
494 	smb_rq_bstart(rqp);
495 	err = mb_put_mem(mbp, sb, sblen, MB_MUSER);
496 	if (err != 0) {
497 		ret = err;
498 		goto out;
499 	}
500 	(void) smb_put_dstring(mbp, vcp, NativeOS, SMB_CS_NONE);
501 	(void) smb_put_dstring(mbp, vcp, LanMan, SMB_CS_NONE);
502 	smb_rq_bend(rqp);
503 
504 	/*
505 	 * Run the request.  The return value here should be the
506 	 * return from this function, unless we fail decoding.
507 	 * Note: NT_STATUS_MORE_PROCESSING_REQUIRED is OK, and
508 	 * the caller expects EINPROGRESS for that case.
509 	 */
510 	ret = smb_rq_internal(rqp, smb_timo_logon);
511 	if (ret != 0)
512 		goto out;
513 	switch (rqp->sr_error) {
514 	case NT_STATUS_SUCCESS:
515 		break;
516 	case NT_STATUS_MORE_PROCESSING_REQUIRED:
517 		/* Keep going, but return... */
518 		ret = EINPROGRESS;
519 		break;
520 	default:
521 		ret = EAUTH;
522 		goto out;
523 	}
524 
525 	if (vcp->vc_smbuid == 0)
526 		vcp->vc_smbuid = rqp->sr_rpuid;
527 
528 	/*
529 	 * Parse the reply
530 	 */
531 	smb_rq_getreply(rqp, &mdp);
532 
533 	err = md_get_uint8(mdp, &wc);
534 	if (err != 0)
535 		wc = 0;
536 	if (wc != 4) {
537 		ret = EBADRPC;
538 		goto out;
539 	}
540 	md_get_uint16le(mdp, NULL);	/* secondary cmd */
541 	md_get_uint16le(mdp, NULL);	/* andxoffset */
542 	md_get_uint16le(mdp, &action);	/* action XXX */
543 	md_get_uint16le(mdp, &sblen);	/* sec. blob len */
544 	md_get_uint16le(mdp, &bc);	/* byte count */
545 	/*
546 	 * Get the security blob, after
547 	 * sanity-checking the length.
548 	 */
549 	if (sblen == 0 || sblen > bc) {
550 		ret = EBADRPC;
551 		goto out;
552 	}
553 	if (sblen > wk->wk_u_auth_rlen) {
554 		ret = EBADRPC;
555 		goto out;
556 	}
557 	sb = wk->wk_u_auth_rbuf.lp_ptr;
558 	err = md_get_mem(mdp, sb, sblen, MB_MUSER);
559 	if (err) {
560 		ret = EBADRPC;
561 		goto out;
562 	}
563 
564 	/*
565 	 * Native OS, LANMGR, & Domain follow here.
566 	 * We don't need them and don't parse them.
567 	 */
568 
569 out:
570 	if (err != 0 && err != EINPROGRESS) {
571 		/* UID no longer valid. */
572 		vcp->vc_smbuid = 0;
573 	}
574 	if (rqp)
575 		smb_rq_done(rqp);
576 
577 	return (ret);
578 }
579 
580 int
smb_smb_logoff(struct smb_vc * vcp,struct smb_cred * scred)581 smb_smb_logoff(struct smb_vc *vcp, struct smb_cred *scred)
582 {
583 	struct smb_rq *rqp;
584 	struct mbchain *mbp;
585 	int error;
586 
587 	if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
588 		return (0);
589 
590 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
591 	if (error)
592 		return (error);
593 	mbp = &rqp->sr_rq;
594 	smb_rq_wstart(rqp);
595 	mb_put_uint8(mbp, 0xff);
596 	mb_put_uint8(mbp, 0);
597 	mb_put_uint16le(mbp, 0);
598 	smb_rq_wend(rqp);
599 	smb_rq_bstart(rqp);
600 	smb_rq_bend(rqp);
601 
602 	/*
603 	 * Run this with a relatively short timeout. (5 sec.)
604 	 * We don't really care about the result here.
605 	 * Also, don't reconnect for this, of course!
606 	 */
607 	rqp->sr_flags |= SMBR_NORECONNECT;
608 	error = smb_rq_internal(rqp, 5);
609 	smb_rq_done(rqp);
610 	return (error);
611 }
612 
613 /*
614  * Get the string representation of a share "use" type,
615  * as needed for the "service" in tree connect.
616  */
617 static const char *
smb_share_typename(uint32_t stype)618 smb_share_typename(uint32_t stype)
619 {
620 	const char *p;
621 
622 	switch (stype) {
623 	case STYPE_DISKTREE:
624 		p = "A:";
625 		break;
626 	case STYPE_PRINTQ:
627 		p = "LPT1:";
628 		break;
629 	case STYPE_DEVICE:
630 		p = "COMM";
631 		break;
632 	case STYPE_IPC:
633 		p = "IPC";
634 		break;
635 	case STYPE_UNKNOWN:
636 	default:
637 		p = "?????";
638 		break;
639 	}
640 	return (p);
641 }
642 
643 /*
644  * Parse a share type name (inverse of above)
645  */
646 static uint32_t
smb_share_parsetype(char * name)647 smb_share_parsetype(char *name)
648 {
649 	int stype;
650 
651 	switch (*name) {
652 	case 'A':	/* A: */
653 		stype = STYPE_DISKTREE;
654 		break;
655 	case 'C':	/* COMM */
656 		stype = STYPE_DEVICE;
657 		break;
658 	case 'I':	/* IPC */
659 		stype = STYPE_IPC;
660 		break;
661 	case 'L':	/* LPT: */
662 		stype = STYPE_PRINTQ;
663 		break;
664 	default:
665 		stype = STYPE_UNKNOWN;
666 		break;
667 	}
668 	return (stype);
669 }
670 
671 int
smb_smb_treeconnect(struct smb_share * ssp,struct smb_cred * scred)672 smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
673 {
674 	struct smb_vc *vcp;
675 	struct smb_rq *rqp = NULL;
676 	struct mbchain *mbp;
677 	struct mdchain *mdp;
678 	const char *tname;
679 	char *pbuf, *unc_name = NULL;
680 	int error, tlen, plen, unc_len;
681 	uint16_t bcnt, options;
682 	uint8_t wc;
683 	char stype_str[STYPE_LEN];
684 
685 	vcp = SSTOVC(ssp);
686 
687 	/*
688 	 * Make this a "VC-level" request, so it will have
689 	 * rqp->sr_share == NULL, and smb_iod_sendrq()
690 	 * will send it with TID = SMB_TID_UNKNOWN
691 	 *
692 	 * This also serves to bypass the wait for
693 	 * share state changes, which this call is
694 	 * trying to carry out.
695 	 */
696 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_CONNECT_ANDX,
697 	    scred, &rqp);
698 	if (error)
699 		return (error);
700 
701 	/*
702 	 * Build the UNC name, i.e. "//server/share"
703 	 * but with backslashes of course.
704 	 * size math: three slashes, one null.
705 	 */
706 	unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name);
707 	unc_name = kmem_alloc(unc_len, KM_SLEEP);
708 	(void) snprintf(unc_name, unc_len, "\\\\%s\\%s",
709 	    vcp->vc_srvname, ssp->ss_name);
710 	SMBSDEBUG("unc_name: \"%s\"", unc_name);
711 
712 
713 	/*
714 	 * Share-level password (pre-computed in user-space)
715 	 * MS-SMB 2.2.6 says this should be null terminated,
716 	 * and the pw length includes the null.
717 	 */
718 	pbuf = ssp->ss_pass;
719 	plen = strlen(pbuf) + 1;
720 
721 	/*
722 	 * Build the request.
723 	 */
724 	mbp = &rqp->sr_rq;
725 	smb_rq_wstart(rqp);
726 	mb_put_uint8(mbp, 0xff);
727 	mb_put_uint8(mbp, 0);
728 	mb_put_uint16le(mbp, 0);
729 	mb_put_uint16le(mbp, 0);		/* Flags */
730 	mb_put_uint16le(mbp, plen);
731 	smb_rq_wend(rqp);
732 	smb_rq_bstart(rqp);
733 
734 	/* Tree connect password, if any */
735 	error = mb_put_mem(mbp, pbuf, plen, MB_MSYSTEM);
736 	if (error)
737 		goto out;
738 
739 	/* UNC resource name */
740 	error = smb_put_dstring(mbp, vcp, unc_name, SMB_CS_NONE);
741 	if (error)
742 		goto out;
743 
744 	/*
745 	 * Put the type string (always ASCII),
746 	 * including the null.
747 	 */
748 	tname = smb_share_typename(ssp->ss_use);
749 	tlen = strlen(tname) + 1;
750 	error = mb_put_mem(mbp, tname, tlen, MB_MSYSTEM);
751 	if (error)
752 		goto out;
753 
754 	smb_rq_bend(rqp);
755 
756 	/*
757 	 * Run the request.
758 	 *
759 	 * Using NOINTR_RECV because we don't want to risk
760 	 * missing a successful tree connect response,
761 	 * which would "leak" Tree IDs.
762 	 */
763 	rqp->sr_flags |= SMBR_NOINTR_RECV;
764 	error = smb_rq_simple(rqp);
765 	SMBSDEBUG("%d\n", error);
766 	if (error) {
767 		/*
768 		 * If we get the server name wrong, i.e. due to
769 		 * mis-configured name services, this will be
770 		 * NT_STATUS_DUPLICATE_NAME.  Log this error.
771 		 */
772 		SMBERROR("(%s) failed, status=0x%x",
773 		    unc_name, rqp->sr_error);
774 		goto out;
775 	}
776 
777 	/*
778 	 * Parse the TCON response
779 	 */
780 	smb_rq_getreply(rqp, &mdp);
781 	md_get_uint8(mdp, &wc);
782 	if (wc != 3 && wc != 7) {
783 		error = EBADRPC;
784 		goto out;
785 	}
786 	md_get_uint16le(mdp, NULL);		/* AndX cmd */
787 	md_get_uint16le(mdp, NULL);		/* AndX off */
788 	md_get_uint16le(mdp, &options);		/* option bits (DFS, search) */
789 	if (wc == 7) {
790 		md_get_uint32le(mdp, NULL);	/* MaximalShareAccessRights */
791 		md_get_uint32le(mdp, NULL);	/* GuestMaximalShareAcc... */
792 	}
793 	error = md_get_uint16le(mdp, &bcnt);	/* byte count */
794 	if (error)
795 		goto out;
796 
797 	/*
798 	 * Get the returned share type string, i.e. "IPC" or whatever.
799 	 * (See smb_share_typename, smb_share_parsetype).  If we get
800 	 * an error reading the type, just say STYPE_UNKNOWN.
801 	 */
802 	tlen = STYPE_LEN;
803 	bzero(stype_str, tlen--);
804 	if (tlen > bcnt)
805 		tlen = bcnt;
806 	md_get_mem(mdp, stype_str, tlen, MB_MSYSTEM);
807 	stype_str[tlen] = '\0';
808 	ssp->ss_type = smb_share_parsetype(stype_str);
809 
810 	/* Success! */
811 	SMB_SS_LOCK(ssp);
812 	ssp->ss_tid = rqp->sr_rptid;
813 	ssp->ss_vcgenid = vcp->vc_genid;
814 	ssp->ss_options = options;
815 	ssp->ss_flags |= SMBS_CONNECTED;
816 	SMB_SS_UNLOCK(ssp);
817 
818 out:
819 	if (unc_name)
820 		kmem_free(unc_name, unc_len);
821 	smb_rq_done(rqp);
822 	return (error);
823 }
824 
825 int
smb_smb_treedisconnect(struct smb_share * ssp,struct smb_cred * scred)826 smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
827 {
828 	struct smb_vc *vcp;
829 	struct smb_rq *rqp;
830 	int error;
831 
832 	if (ssp->ss_tid == SMB_TID_UNKNOWN)
833 		return (0);
834 
835 	/*
836 	 * Build this as a "VC-level" request, so it will
837 	 * avoid testing the _GONE flag on the share,
838 	 * which has already been set at this point.
839 	 * Add the share pointer "by hand" below, so
840 	 * smb_iod_sendrq will plug in the TID.
841 	 */
842 	vcp = SSTOVC(ssp);
843 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
844 	if (error)
845 		return (error);
846 	rqp->sr_share = ssp; /* by hand */
847 
848 	smb_rq_wstart(rqp);
849 	smb_rq_wend(rqp);
850 	smb_rq_bstart(rqp);
851 	smb_rq_bend(rqp);
852 
853 	/*
854 	 * Run this with a relatively short timeout. (5 sec.)
855 	 * We don't really care about the result here, but we
856 	 * do need to make sure we send this out, or we could
857 	 * "leak" active tree IDs on interrupt or timeout.
858 	 * The NOINTR_SEND flag makes this request immune to
859 	 * interrupt or timeout until the send is done.
860 	 * Also, don't reconnect for this, of course!
861 	 */
862 	rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
863 	error = smb_rq_simple_timed(rqp, 5);
864 	SMBSDEBUG("%d\n", error);
865 	smb_rq_done(rqp);
866 	ssp->ss_tid = SMB_TID_UNKNOWN;
867 	return (error);
868 }
869 
870 /*
871  * Modern create/open of file or directory.
872  */
873 int
smb1_smb_ntcreate(struct smb_share * ssp,struct mbchain * name_mb,uint32_t cr_flags,uint32_t req_acc,uint32_t efa,uint32_t share_acc,uint32_t open_disp,uint32_t createopt,uint32_t impersonate,struct smb_cred * scrp,uint16_t * fidp,uint32_t * cr_act_p,struct smbfattr * fap)874 smb1_smb_ntcreate(
875 	struct smb_share *ssp,
876 	struct mbchain	*name_mb,
877 	uint32_t cr_flags,	/* create flags */
878 	uint32_t req_acc,	/* requested access */
879 	uint32_t efa,		/* ext. file attrs (DOS attr +) */
880 	uint32_t share_acc,
881 	uint32_t open_disp,	/* open disposition */
882 	uint32_t createopt,	/* NTCREATEX_OPTIONS_ */
883 	uint32_t impersonate,	/* NTCREATEX_IMPERSONATION_... */
884 	struct smb_cred *scrp,
885 	uint16_t *fidp,		/* returned FID */
886 	uint32_t *cr_act_p,	/* optional create action */
887 	struct smbfattr *fap)	/* optional attributes */
888 {
889 	struct smb_rq rq, *rqp = &rq;
890 	struct smb_vc *vcp = SSTOVC(ssp);
891 	struct mbchain *mbp;
892 	struct mdchain *mdp;
893 	struct smbfattr fa;
894 	uint64_t llongint;
895 	uint32_t longint, createact;
896 	uint16_t fid;
897 	uint8_t wc;
898 	int error;
899 
900 	bzero(&fa, sizeof (fa));
901 	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_NT_CREATE_ANDX, scrp);
902 	if (error)
903 		return (error);
904 	smb_rq_getrequest(rqp, &mbp);
905 
906 	/* Word parameters */
907 	smb_rq_wstart(rqp);
908 	mb_put_uint8(mbp, 0xff);	/* secondary command */
909 	mb_put_uint8(mbp, 0);		/* MBZ */
910 	mb_put_uint16le(mbp, 0);	/* offset to next command (none) */
911 	mb_put_uint8(mbp, 0);		/* MBZ */
912 	mb_put_uint16le(mbp, name_mb->mb_count);
913 	mb_put_uint32le(mbp, cr_flags);	/* NTCREATEX_FLAGS_* */
914 	mb_put_uint32le(mbp, 0);	/* FID - basis for path if not root */
915 	mb_put_uint32le(mbp, req_acc);
916 	mb_put_uint64le(mbp, 0);	/* "initial allocation size" */
917 	mb_put_uint32le(mbp, efa);
918 	mb_put_uint32le(mbp, share_acc);
919 	mb_put_uint32le(mbp, open_disp);
920 	mb_put_uint32le(mbp, createopt);
921 	mb_put_uint32le(mbp, impersonate);
922 	mb_put_uint8(mbp, 0);   /* security flags (?) */
923 	smb_rq_wend(rqp);
924 
925 	/*
926 	 * Byte parameters: Just the path name, aligned.
927 	 * Note: mb_put_mbuf consumes mb_top, so clear it.
928 	 */
929 	smb_rq_bstart(rqp);
930 	if (SMB_UNICODE_STRINGS(vcp))
931 		mb_put_padbyte(mbp);
932 	mb_put_mbuf(mbp, name_mb->mb_top);
933 	bzero(name_mb, sizeof (*name_mb));
934 	smb_rq_bend(rqp);
935 
936 	/*
937 	 * Don't want to risk missing a successful
938 	 * open response, or we could "leak" FIDs.
939 	 */
940 	rqp->sr_flags |= SMBR_NOINTR_RECV;
941 	error = smb_rq_simple_timed(rqp, smb_timo_open);
942 	if (error)
943 		goto done;
944 	smb_rq_getreply(rqp, &mdp);
945 	/*
946 	 * spec says 26 for word count, but 34 words are defined
947 	 * and observed from win2000
948 	 */
949 	error = md_get_uint8(mdp, &wc);
950 	if (error)
951 		goto done;
952 	if (wc != 26 && wc < 34) {
953 		error = EBADRPC;
954 		goto done;
955 	}
956 	md_get_uint8(mdp, NULL);		/* secondary cmd */
957 	md_get_uint8(mdp, NULL);		/* mbz */
958 	md_get_uint16le(mdp, NULL);		/* andxoffset */
959 	md_get_uint8(mdp, NULL);		/* oplock lvl granted */
960 	md_get_uint16le(mdp, &fid);		/* file ID */
961 	md_get_uint32le(mdp, &createact);	/* create_action */
962 
963 	md_get_uint64le(mdp, &llongint);	/* creation time */
964 	smb_time_NT2local(llongint, &fa.fa_createtime);
965 	md_get_uint64le(mdp, &llongint);	/* access time */
966 	smb_time_NT2local(llongint, &fa.fa_atime);
967 	md_get_uint64le(mdp, &llongint);	/* write time */
968 	smb_time_NT2local(llongint, &fa.fa_mtime);
969 	md_get_uint64le(mdp, &llongint);	/* change time */
970 	smb_time_NT2local(llongint, &fa.fa_ctime);
971 
972 	md_get_uint32le(mdp, &longint);		/* attributes */
973 	fa.fa_attr = longint;
974 	md_get_uint64le(mdp, &llongint);	/* allocation size */
975 	fa.fa_allocsz = llongint;
976 	md_get_uint64le(mdp, &llongint);	/* EOF position */
977 	fa.fa_size = llongint;
978 
979 	error = md_get_uint16le(mdp, NULL);	/* file type */
980 	/* other stuff we don't care about */
981 
982 done:
983 	smb_rq_done(rqp);
984 	if (error)
985 		return (error);
986 
987 	*fidp = fid;
988 	if (cr_act_p)
989 		*cr_act_p = createact;
990 	if (fap)
991 		*fap = fa; /* struct copy */
992 
993 	return (0);
994 }
995 
996 int
smb1_smb_close(struct smb_share * ssp,uint16_t fid,struct timespec * mtime,struct smb_cred * scrp)997 smb1_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime,
998 	struct smb_cred *scrp)
999 {
1000 	struct smb_rq rq, *rqp = &rq;
1001 	struct mbchain *mbp;
1002 	long time;
1003 	int error;
1004 
1005 	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scrp);
1006 	if (error)
1007 		return (error);
1008 	smb_rq_getrequest(rqp, &mbp);
1009 	smb_rq_wstart(rqp);
1010 	mb_put_uint16le(mbp, fid);
1011 	if (mtime) {
1012 		int sv_tz = SSTOVC(ssp)->vc_sopt.sv_tz;
1013 		smb_time_local2server(mtime, sv_tz, &time);
1014 	} else {
1015 		time = 0;
1016 	}
1017 	mb_put_uint32le(mbp, time);
1018 	smb_rq_wend(rqp);
1019 	smb_rq_bstart(rqp);
1020 	smb_rq_bend(rqp);
1021 
1022 	/* Make sure we send, but only if already connected */
1023 	rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
1024 	error = smb_rq_simple(rqp);
1025 	smb_rq_done(rqp);
1026 	return (error);
1027 }
1028 
1029 int
smb_smb_open_prjob(struct smb_share * ssp,char * title,uint16_t setuplen,uint16_t mode,struct smb_cred * scrp,uint16_t * fidp)1030 smb_smb_open_prjob(
1031 	struct smb_share *ssp,
1032 	char	*title,
1033 	uint16_t setuplen,
1034 	uint16_t mode,
1035 	struct smb_cred *scrp,
1036 	uint16_t *fidp)
1037 {
1038 	struct smb_rq rq, *rqp = &rq;
1039 	struct smb_vc *vcp = SSTOVC(ssp);
1040 	struct mbchain *mbp;
1041 	struct mdchain *mdp;
1042 	uint16_t fid;
1043 	uint8_t wc;
1044 	int error;
1045 
1046 	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN_PRINT_FILE, scrp);
1047 	if (error)
1048 		return (error);
1049 	smb_rq_getrequest(rqp, &mbp);
1050 
1051 	/* Word parameters */
1052 	smb_rq_wstart(rqp);
1053 	mb_put_uint16le(mbp, setuplen);
1054 	mb_put_uint16le(mbp, mode);
1055 	smb_rq_wend(rqp);
1056 
1057 	/*
1058 	 * Byte parameters: Just the title
1059 	 */
1060 	smb_rq_bstart(rqp);
1061 	mb_put_uint8(mbp, SMB_DT_ASCII);
1062 	error = smb_put_dstring(mbp, vcp, title, SMB_CS_NONE);
1063 	smb_rq_bend(rqp);
1064 	if (error)
1065 		goto done;
1066 
1067 	/*
1068 	 * Don't want to risk missing a successful
1069 	 * open response, or we could "leak" FIDs.
1070 	 */
1071 	rqp->sr_flags |= SMBR_NOINTR_RECV;
1072 	error = smb_rq_simple_timed(rqp, smb_timo_open);
1073 	if (error)
1074 		goto done;
1075 
1076 	smb_rq_getreply(rqp, &mdp);
1077 	error = md_get_uint8(mdp, &wc);
1078 	if (error || wc < 1) {
1079 		error = EBADRPC;
1080 		goto done;
1081 	}
1082 	error = md_get_uint16le(mdp, &fid);
1083 
1084 done:
1085 	smb_rq_done(rqp);
1086 	if (error)
1087 		return (error);
1088 
1089 	*fidp = fid;
1090 	return (0);
1091 }
1092 
1093 /*
1094  * Like smb_smb_close, but for print shares.
1095  */
1096 int
smb_smb_close_prjob(struct smb_share * ssp,uint16_t fid,struct smb_cred * scrp)1097 smb_smb_close_prjob(struct smb_share *ssp, uint16_t fid,
1098 	struct smb_cred *scrp)
1099 {
1100 	struct smb_rq rq, *rqp = &rq;
1101 	struct mbchain *mbp;
1102 	int error;
1103 
1104 	error = smb_rq_init(rqp, SSTOCP(ssp),
1105 	    SMB_COM_CLOSE_PRINT_FILE, scrp);
1106 	if (error)
1107 		return (error);
1108 	smb_rq_getrequest(rqp, &mbp);
1109 	smb_rq_wstart(rqp);
1110 	mb_put_uint16le(mbp, fid);
1111 	smb_rq_wend(rqp);
1112 	smb_rq_bstart(rqp);
1113 	smb_rq_bend(rqp);
1114 
1115 	/* Make sure we send but only if already connected */
1116 	rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
1117 	error = smb_rq_simple(rqp);
1118 	smb_rq_done(rqp);
1119 	return (error);
1120 }
1121 
1122 int
smb_smb_readx(smb_fh_t * fhp,uint32_t * lenp,uio_t * uiop,smb_cred_t * scred,int timo)1123 smb_smb_readx(smb_fh_t *fhp, uint32_t *lenp,
1124 	uio_t *uiop, smb_cred_t *scred, int timo)
1125 {
1126 	struct smb_share *ssp = FHTOSS(fhp);
1127 	struct smb_rq *rqp;
1128 	struct mbchain *mbp;
1129 	struct mdchain *mdp;
1130 	int error;
1131 	uint32_t offlo, offhi, rlen;
1132 	uint16_t lenhi, lenlo, off, doff;
1133 	uint8_t wc;
1134 
1135 	lenhi = (uint16_t)(*lenp >> 16);
1136 	lenlo = (uint16_t)*lenp;
1137 	offhi = (uint32_t)(uiop->uio_loffset >> 32);
1138 	offlo = (uint32_t)uiop->uio_loffset;
1139 
1140 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
1141 	if (error)
1142 		return (error);
1143 	smb_rq_getrequest(rqp, &mbp);
1144 	smb_rq_wstart(rqp);
1145 	mb_put_uint8(mbp, 0xff);	/* no secondary command */
1146 	mb_put_uint8(mbp, 0);		/* MBZ */
1147 	mb_put_uint16le(mbp, 0);	/* offset to secondary */
1148 	mb_put_uint16le(mbp, fhp->fh_fid1);
1149 	mb_put_uint32le(mbp, offlo);	/* offset (low part) */
1150 	mb_put_uint16le(mbp, lenlo);	/* MaxCount */
1151 	mb_put_uint16le(mbp, 1);	/* MinCount */
1152 					/* (only indicates blocking) */
1153 	mb_put_uint32le(mbp, lenhi);	/* MaxCountHigh */
1154 	mb_put_uint16le(mbp, lenlo);	/* Remaining ("obsolete") */
1155 	mb_put_uint32le(mbp, offhi);	/* offset (high part) */
1156 	smb_rq_wend(rqp);
1157 	smb_rq_bstart(rqp);
1158 	smb_rq_bend(rqp);
1159 
1160 	if (timo == 0)
1161 		timo = smb_timo_read;
1162 	error = smb_rq_simple_timed(rqp, timo);
1163 	if (error)
1164 		goto out;
1165 
1166 	smb_rq_getreply(rqp, &mdp);
1167 	error = md_get_uint8(mdp, &wc);
1168 	if (error)
1169 		goto out;
1170 	if (wc != 12) {
1171 		error = EBADRPC;
1172 		goto out;
1173 	}
1174 	md_get_uint8(mdp, NULL);
1175 	md_get_uint8(mdp, NULL);
1176 	md_get_uint16le(mdp, NULL);
1177 	md_get_uint16le(mdp, NULL);
1178 	md_get_uint16le(mdp, NULL);	/* data compaction mode */
1179 	md_get_uint16le(mdp, NULL);
1180 	md_get_uint16le(mdp, &lenlo);	/* data len ret. */
1181 	md_get_uint16le(mdp, &doff);	/* data offset */
1182 	md_get_uint16le(mdp, &lenhi);
1183 	rlen = (lenhi << 16) | lenlo;
1184 	md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
1185 	error = md_get_uint16le(mdp, NULL);	/* ByteCount */
1186 	if (error)
1187 		goto out;
1188 	/*
1189 	 * Does the data offset indicate padding?
1190 	 * The current offset is a constant, found
1191 	 * by counting the md_get_ calls above.
1192 	 */
1193 	off = SMB_HDRLEN + 3 + (12 * 2); /* =59 */
1194 	if (doff > off)	/* pad byte(s)? */
1195 		md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
1196 	if (rlen == 0) {
1197 		*lenp = rlen;
1198 		goto out;
1199 	}
1200 	/* paranoid */
1201 	if (rlen > *lenp) {
1202 		SMBSDEBUG("bad server! rlen %d, len %d\n",
1203 		    rlen, *lenp);
1204 		rlen = *lenp;
1205 	}
1206 	error = md_get_uio(mdp, uiop, rlen);
1207 	if (error)
1208 		goto out;
1209 
1210 	/* Success */
1211 	*lenp = rlen;
1212 
1213 out:
1214 	smb_rq_done(rqp);
1215 	return (error);
1216 }
1217 
1218 int
smb_smb_writex(smb_fh_t * fhp,uint32_t * lenp,uio_t * uiop,smb_cred_t * scred,int timo)1219 smb_smb_writex(smb_fh_t *fhp, uint32_t *lenp,
1220 	uio_t *uiop, smb_cred_t *scred, int timo)
1221 {
1222 	struct smb_share *ssp = FHTOSS(fhp);
1223 	struct smb_rq *rqp;
1224 	struct mbchain *mbp;
1225 	struct mdchain *mdp;
1226 	int error;
1227 	uint32_t offlo, offhi, rlen;
1228 	uint16_t lenhi, lenlo;
1229 	uint8_t wc;
1230 
1231 	lenhi = (uint16_t)(*lenp >> 16);
1232 	lenlo = (uint16_t)*lenp;
1233 	offhi = (uint32_t)(uiop->uio_loffset >> 32);
1234 	offlo = (uint32_t)uiop->uio_loffset;
1235 
1236 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
1237 	if (error)
1238 		return (error);
1239 	smb_rq_getrequest(rqp, &mbp);
1240 	smb_rq_wstart(rqp);
1241 	mb_put_uint8(mbp, 0xff);	/* no secondary command */
1242 	mb_put_uint8(mbp, 0);		/* MBZ */
1243 	mb_put_uint16le(mbp, 0);	/* offset to secondary */
1244 	mb_put_uint16le(mbp, fhp->fh_fid1);
1245 	mb_put_uint32le(mbp, offlo);	/* offset (low part) */
1246 	mb_put_uint32le(mbp, 0);	/* MBZ (timeout) */
1247 	mb_put_uint16le(mbp, 0);	/* !write-thru */
1248 	mb_put_uint16le(mbp, 0);
1249 	mb_put_uint16le(mbp, lenhi);
1250 	mb_put_uint16le(mbp, lenlo);
1251 	mb_put_uint16le(mbp, 64);	/* data offset from header start */
1252 	mb_put_uint32le(mbp, offhi);	/* offset (high part) */
1253 	smb_rq_wend(rqp);
1254 	smb_rq_bstart(rqp);
1255 
1256 	mb_put_uint8(mbp, 0);	/* pad byte */
1257 	error = mb_put_uio(mbp, uiop, *lenp);
1258 	if (error)
1259 		goto out;
1260 	smb_rq_bend(rqp);
1261 	if (timo == 0)
1262 		timo = smb_timo_write;
1263 	error = smb_rq_simple_timed(rqp, timo);
1264 	if (error)
1265 		goto out;
1266 	smb_rq_getreply(rqp, &mdp);
1267 	error = md_get_uint8(mdp, &wc);
1268 	if (error)
1269 		goto out;
1270 	if (wc != 6) {
1271 		error = EBADRPC;
1272 		goto out;
1273 	}
1274 	md_get_uint8(mdp, NULL);	/* andx cmd */
1275 	md_get_uint8(mdp, NULL);	/* reserved */
1276 	md_get_uint16le(mdp, NULL);	/* andx offset */
1277 	md_get_uint16le(mdp, &lenlo);	/* data len ret. */
1278 	md_get_uint16le(mdp, NULL);	/* remaining */
1279 	error = md_get_uint16le(mdp, &lenhi);
1280 	if (error)
1281 		goto out;
1282 
1283 	/* Success */
1284 	rlen = (lenhi << 16) | lenlo;
1285 	*lenp = rlen;
1286 
1287 out:
1288 	smb_rq_done(rqp);
1289 	return (error);
1290 }
1291 
1292 
1293 static u_int32_t	smbechoes = 0;
1294 
1295 /*
1296  * Note: the IOD calls this, so this request must not wait for
1297  * connection state changes, etc. (uses smb_rq_internal)
1298  */
1299 int
smb_smb_echo(struct smb_vc * vcp,struct smb_cred * scred,int timo)1300 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo)
1301 {
1302 	struct smb_rq *rqp;
1303 	struct mbchain *mbp;
1304 	int error;
1305 
1306 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
1307 	if (error)
1308 		return (error);
1309 	mbp = &rqp->sr_rq;
1310 	smb_rq_wstart(rqp);
1311 	mb_put_uint16le(mbp, 1); /* echo count */
1312 	smb_rq_wend(rqp);
1313 	smb_rq_bstart(rqp);
1314 	mb_put_uint32le(mbp, atomic_inc_32_nv(&smbechoes));
1315 	smb_rq_bend(rqp);
1316 	rqp->sr_flags |= SMBR_NORECONNECT;
1317 	error = smb_rq_internal(rqp, timo);
1318 	SMBSDEBUG("%d\n", error);
1319 	smb_rq_done(rqp);
1320 	return (error);
1321 }
1322