1139823Simp/*-
275374Sbp * Copyright (c) 2000-2001 Boris Popov
375374Sbp * All rights reserved.
475374Sbp *
575374Sbp * Redistribution and use in source and binary forms, with or without
675374Sbp * modification, are permitted provided that the following conditions
775374Sbp * are met:
875374Sbp * 1. Redistributions of source code must retain the above copyright
975374Sbp *    notice, this list of conditions and the following disclaimer.
1075374Sbp * 2. Redistributions in binary form must reproduce the above copyright
1175374Sbp *    notice, this list of conditions and the following disclaimer in the
1275374Sbp *    documentation and/or other materials provided with the distribution.
1375374Sbp *
1475374Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1575374Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1675374Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1775374Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1875374Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1975374Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2075374Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2175374Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2275374Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2375374Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2475374Sbp * SUCH DAMAGE.
2575374Sbp */
26116189Sobrien
2775374Sbp/*
2875374Sbp * various SMB requests. Most of the routines merely packs data into mbufs.
2975374Sbp */
30116189Sobrien
31116189Sobrien#include <sys/cdefs.h>
32116189Sobrien__FBSDID("$FreeBSD: releng/11.0/sys/netsmb/smb_smb.c 298375 2016-04-20 21:13:24Z pfg $");
33116189Sobrien
3475374Sbp#include <sys/param.h>
3575374Sbp#include <sys/systm.h>
3675374Sbp#include <sys/kernel.h>
3775374Sbp#include <sys/malloc.h>
3875374Sbp#include <sys/proc.h>
3975374Sbp#include <sys/lock.h>
4075374Sbp#include <sys/sysctl.h>
4175374Sbp#include <sys/socket.h>
4275374Sbp#include <sys/uio.h>
4375374Sbp
4475374Sbp#include <sys/iconv.h>
4575374Sbp
4675374Sbp#include <netsmb/smb.h>
4775374Sbp#include <netsmb/smb_subr.h>
4875374Sbp#include <netsmb/smb_rq.h>
4975374Sbp#include <netsmb/smb_conn.h>
5075374Sbp#include <netsmb/smb_tran.h>
5175374Sbp
52124087Stjr#include "opt_netsmb.h"
53124087Stjr
5475374Sbpstruct smb_dialect {
5575374Sbp	int		d_id;
5675374Sbp	const char *	d_name;
5775374Sbp};
5875374Sbp
5975374Sbpstatic struct smb_dialect smb_dialects[] = {
6075374Sbp	{SMB_DIALECT_CORE,	"PC NETWORK PROGRAM 1.0"},
6175374Sbp	{SMB_DIALECT_COREPLUS,	"MICROSOFT NETWORKS 1.03"},
6275374Sbp	{SMB_DIALECT_LANMAN1_0,	"MICROSOFT NETWORKS 3.0"},
6375374Sbp	{SMB_DIALECT_LANMAN1_0,	"LANMAN1.0"},
6475374Sbp	{SMB_DIALECT_LANMAN2_0,	"LM1.2X002"},
6575374Sbp	{SMB_DIALECT_LANMAN2_0,	"Samba"},
6675374Sbp	{SMB_DIALECT_NTLM0_12,	"NT LANMAN 1.0"},
6775374Sbp	{SMB_DIALECT_NTLM0_12,	"NT LM 0.12"},
6875374Sbp	{-1,			NULL}
6975374Sbp};
7075374Sbp
71103395Sbpstatic u_int32_t
72103395Sbpsmb_vc_maxread(struct smb_vc *vcp)
73103395Sbp{
74103395Sbp	/*
75103395Sbp	 * Specs say up to 64k data bytes, but Windows traffic
76103395Sbp	 * uses 60k... no doubt for some good reason.
77124087Stjr	 *
78124087Stjr	 * Don't exceed the server's buffer size if signatures
79124087Stjr	 * are enabled otherwise Windows 2003 chokes. Allow space
80124087Stjr	 * for the SMB header & a little bit extra.
81103395Sbp	 */
82124087Stjr	if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) &&
83124087Stjr	    (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0)
84103395Sbp		return (60*1024);
85103395Sbp	else
86124087Stjr		return (vcp->vc_sopt.sv_maxtx - SMB_HDRLEN - 64);
87103395Sbp}
88103395Sbp
89103395Sbpstatic u_int32_t
90103395Sbpsmb_vc_maxwrite(struct smb_vc *vcp)
91103395Sbp{
92103395Sbp	/*
93124087Stjr	 * See comment above.
94103395Sbp	 */
95124087Stjr	if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) &&
96124087Stjr	    (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0)
97103395Sbp		return (60*1024);
98103395Sbp	else
99124087Stjr		return (vcp->vc_sopt.sv_maxtx - SMB_HDRLEN - 64);
100103395Sbp}
101103395Sbp
10275374Sbpstatic int
10375374Sbpsmb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
10475374Sbp{
10587192Sbp	if (scred->scr_td->td_proc == vcp->vc_iod->iod_p)
10675374Sbp		return 0;
10775374Sbp	SMBERROR("wrong function called(%s)\n", name);
10875374Sbp	return EINVAL;
10975374Sbp}
11075374Sbp
11175374Sbpint
11275374Sbpsmb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
11375374Sbp{
11475374Sbp	struct smb_dialect *dp;
11575374Sbp	struct smb_sopt *sp = NULL;
11675374Sbp	struct smb_rq *rqp;
11775374Sbp	struct mbchain *mbp;
11875374Sbp	struct mdchain *mdp;
11975374Sbp	u_int8_t wc, stime[8], sblen;
12075374Sbp	u_int16_t dindex, tw, tw1, swlen, bc;
12175374Sbp	int error, maxqsz;
122227650Skevlo	int unicode = SMB_UNICODE_STRINGS(vcp);
123227650Skevlo	void * servercharset = vcp->vc_toserver;
124227650Skevlo	void * localcharset = vcp->vc_tolocal;
12575374Sbp
12687599Sobrien	if (smb_smb_nomux(vcp, scred, __func__) != 0)
12775374Sbp		return EINVAL;
128227650Skevlo	/* Disable Unicode for SMB_COM_NEGOTIATE requests */
129227650Skevlo	if (unicode) {
130227650Skevlo		vcp->vc_toserver = vcp->vc_cp_toserver;
131227650Skevlo		vcp->vc_tolocal  = vcp->vc_cp_tolocal;
132227650Skevlo	}
13375374Sbp	vcp->vc_hflags = 0;
13475374Sbp	vcp->vc_hflags2 = 0;
13575374Sbp	vcp->obj.co_flags &= ~(SMBV_ENCRYPT);
13675374Sbp	sp = &vcp->vc_sopt;
13775374Sbp	bzero(sp, sizeof(struct smb_sopt));
13875374Sbp	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
13975374Sbp	if (error)
14075374Sbp		return error;
14175374Sbp	smb_rq_getrequest(rqp, &mbp);
14275374Sbp	smb_rq_wstart(rqp);
14375374Sbp	smb_rq_wend(rqp);
14475374Sbp	smb_rq_bstart(rqp);
14575374Sbp	for(dp = smb_dialects; dp->d_id != -1; dp++) {
14675374Sbp		mb_put_uint8(mbp, SMB_DT_DIALECT);
14775374Sbp		smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE);
14875374Sbp	}
14975374Sbp	smb_rq_bend(rqp);
15075374Sbp	error = smb_rq_simple(rqp);
15175374Sbp	SMBSDEBUG("%d\n", error);
15275374Sbp	if (error)
15375374Sbp		goto bad;
15475374Sbp	smb_rq_getreply(rqp, &mdp);
15575374Sbp	do {
15675374Sbp		error = md_get_uint8(mdp, &wc);
15775374Sbp		if (error)
15875374Sbp			break;
15975374Sbp		error = md_get_uint16le(mdp, &dindex);
16075374Sbp		if (error)
16175374Sbp			break;
16275374Sbp		if (dindex > 7) {
16375374Sbp			SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex);
16475374Sbp			error = EBADRPC;
16575374Sbp			break;
16675374Sbp		}
16775374Sbp		dp = smb_dialects + dindex;
16875374Sbp		sp->sv_proto = dp->d_id;
16975374Sbp		SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc);
17075374Sbp		error = EBADRPC;
17175374Sbp		if (dp->d_id >= SMB_DIALECT_NTLM0_12) {
17275374Sbp			if (wc != 17)
17375374Sbp				break;
17475374Sbp			md_get_uint8(mdp, &sp->sv_sm);
17575374Sbp			md_get_uint16le(mdp, &sp->sv_maxmux);
17675374Sbp			md_get_uint16le(mdp, &sp->sv_maxvcs);
17775374Sbp			md_get_uint32le(mdp, &sp->sv_maxtx);
17875374Sbp			md_get_uint32le(mdp, &sp->sv_maxraw);
17975374Sbp			md_get_uint32le(mdp, &sp->sv_skey);
18075374Sbp			md_get_uint32le(mdp, &sp->sv_caps);
18175374Sbp			md_get_mem(mdp, stime, 8, MB_MSYSTEM);
18275374Sbp			md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
18375374Sbp			md_get_uint8(mdp, &sblen);
18475374Sbp			if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
18575374Sbp				if (sblen != SMB_MAXCHALLENGELEN) {
18675374Sbp					SMBERROR("Unexpected length of security blob (%d)\n", sblen);
18775374Sbp					break;
18875374Sbp				}
189227650Skevlo				error = md_get_uint16le(mdp, &bc);
19075374Sbp				if (error)
19175374Sbp					break;
19275374Sbp				if (sp->sv_caps & SMB_CAP_EXT_SECURITY)
19375374Sbp					md_get_mem(mdp, NULL, 16, MB_MSYSTEM);
19475374Sbp				error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM);
19575374Sbp				if (error)
19675374Sbp					break;
19775374Sbp				vcp->vc_chlen = sblen;
19875374Sbp				vcp->obj.co_flags |= SMBV_ENCRYPT;
19975374Sbp			}
200124087Stjr			if (sp->sv_sm & SMB_SM_SIGS_REQUIRE)
201124087Stjr				vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
202227650Skevlo			if (vcp->vc_ucs_toserver &&
203227650Skevlo				sp->sv_caps & SMB_CAP_UNICODE) {
204227650Skevlo				/*
205227650Skevlo				* They do Unicode.
206227650Skevlo				*/
207227650Skevlo				vcp->obj.co_flags |= SMBV_UNICODE;
208227650Skevlo			}
20975374Sbp			vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
21075374Sbp			if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
21175374Sbp			    sp->sv_maxtx < 4096 &&
21275374Sbp			    (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) {
21375374Sbp				vcp->obj.co_flags |= SMBV_WIN95;
21475374Sbp				SMBSDEBUG("Win95 detected\n");
21575374Sbp			}
216227650Skevlo			error = 0;
217227650Skevlo			break;
218227650Skevlo		}
219227650Skevlo		vcp->vc_hflags2 &= ~(SMB_FLAGS2_EXT_SEC|SMB_FLAGS2_DFS|
220227650Skevlo				     SMB_FLAGS2_ERR_STATUS|SMB_FLAGS2_UNICODE);
221227650Skevlo		unicode = 0;
222227650Skevlo		if (dp->d_id > SMB_DIALECT_CORE) {
22375374Sbp			md_get_uint16le(mdp, &tw);
22475374Sbp			sp->sv_sm = tw;
22575374Sbp			md_get_uint16le(mdp, &tw);
22675374Sbp			sp->sv_maxtx = tw;
22775374Sbp			md_get_uint16le(mdp, &sp->sv_maxmux);
22875374Sbp			md_get_uint16le(mdp, &sp->sv_maxvcs);
22975374Sbp			md_get_uint16le(mdp, &tw);	/* rawmode */
23075374Sbp			md_get_uint32le(mdp, &sp->sv_skey);
23175374Sbp			if (wc == 13) {		/* >= LANMAN1 */
23275374Sbp				md_get_uint16(mdp, &tw);		/* time */
23375374Sbp				md_get_uint16(mdp, &tw1);		/* date */
23475374Sbp				md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
23575374Sbp				md_get_uint16le(mdp, &swlen);
23675374Sbp				if (swlen > SMB_MAXCHALLENGELEN)
23775374Sbp					break;
23875374Sbp				md_get_uint16(mdp, NULL);	/* mbz */
239227650Skevlo				if (md_get_uint16le(mdp, &bc) != 0)
24075374Sbp					break;
24175374Sbp				if (bc < swlen)
24275374Sbp					break;
24375374Sbp				if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
24475374Sbp					error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM);
24575374Sbp					if (error)
24675374Sbp						break;
24775374Sbp					vcp->vc_chlen = swlen;
24875374Sbp					vcp->obj.co_flags |= SMBV_ENCRYPT;
24975374Sbp				}
25075374Sbp			}
25175374Sbp			vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
25275374Sbp		} else {	/* an old CORE protocol */
25375374Sbp			sp->sv_maxmux = 1;
25475374Sbp		}
25575374Sbp		error = 0;
25675374Sbp	} while (0);
25775374Sbp	if (error == 0) {
25875374Sbp		vcp->vc_maxvcs = sp->sv_maxvcs;
25975374Sbp		if (vcp->vc_maxvcs <= 1) {
26075374Sbp			if (vcp->vc_maxvcs == 0)
26175374Sbp				vcp->vc_maxvcs = 1;
26275374Sbp		}
26375374Sbp		if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff)
26475374Sbp			sp->sv_maxtx = 1024;
265103395Sbp		else
266103395Sbp			sp->sv_maxtx = min(sp->sv_maxtx,
267103395Sbp					   63*1024 + SMB_HDRLEN + 16);
268103395Sbp		SMB_TRAN_GETPARAM(vcp, SMBTP_RCVSZ, &maxqsz);
269103395Sbp		vcp->vc_rxmax = min(smb_vc_maxread(vcp), maxqsz - 1024);
27075374Sbp		SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz);
271103395Sbp		vcp->vc_wxmax = min(smb_vc_maxwrite(vcp), maxqsz - 1024);
27275374Sbp		vcp->vc_txmax = min(sp->sv_maxtx, maxqsz);
27375374Sbp		SMBSDEBUG("TZ = %d\n", sp->sv_tz);
27475374Sbp		SMBSDEBUG("CAPS = %x\n", sp->sv_caps);
27575374Sbp		SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux);
27675374Sbp		SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs);
27775374Sbp		SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw);
27875374Sbp		SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx);
27975374Sbp	}
28075374Sbpbad:
281227650Skevlo	/* Restore Unicode conversion state */
282227650Skevlo	if (unicode) {
283227650Skevlo		vcp->vc_toserver = servercharset;
284227650Skevlo		vcp->vc_tolocal  = localcharset;
285227650Skevlo		vcp->vc_hflags2 |= SMB_FLAGS2_UNICODE;
286227650Skevlo	}
28775374Sbp	smb_rq_done(rqp);
28875374Sbp	return error;
28975374Sbp}
29075374Sbp
29175374Sbpint
29275374Sbpsmb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
29375374Sbp{
29475374Sbp	struct smb_rq *rqp;
29575374Sbp	struct mbchain *mbp;
29675374Sbp/*	u_int8_t wc;
29775374Sbp	u_int16_t tw, tw1;*/
29875374Sbp	smb_uniptr unipp, ntencpass = NULL;
29975374Sbp	char *pp, *up, *pbuf, *encpass;
300103396Sbp	int error, plen, uniplen, ulen, upper;
301227650Skevlo	u_int32_t caps = 0;
30275374Sbp
303103396Sbp	upper = 0;
304103396Sbp
305227650Skevlo	if (vcp->obj.co_flags & SMBV_UNICODE)
306227650Skevlo		caps |= SMB_CAP_UNICODE;
307227650Skevlo
308103396Sbpagain:
309103396Sbp
31075374Sbp	vcp->vc_smbuid = SMB_UID_UNKNOWN;
31175374Sbp
31287599Sobrien	if (smb_smb_nomux(vcp, scred, __func__) != 0)
31375374Sbp		return EINVAL;
31475374Sbp
31575374Sbp	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
31675374Sbp	if (error)
31775374Sbp		return error;
318111119Simp	pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
319111119Simp	encpass = malloc(24, M_SMBTEMP, M_WAITOK);
32075374Sbp	if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
321103396Sbp		/*
322103396Sbp		 * We try w/o uppercasing first so Samba mixed case
323103396Sbp		 * passwords work.  If that fails we come back and try
324103396Sbp		 * uppercasing to satisfy OS/2 and Windows for Workgroups.
325103396Sbp		 */
326103396Sbp		if (upper++) {
327103396Sbp			iconv_convstr(vcp->vc_toupper, pbuf,
328103396Sbp				      smb_vc_getpass(vcp)/*, SMB_MAXPASSWORDLEN*/);
329103396Sbp		} else {
330103396Sbp			strncpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN);
331103396Sbp			pbuf[SMB_MAXPASSWORDLEN] = '\0';
332103396Sbp		}
333103396Sbp		if (!SMB_UNICODE_STRINGS(vcp))
334103396Sbp			iconv_convstr(vcp->vc_toserver, pbuf, pbuf/*,
335103396Sbp				      SMB_MAXPASSWORDLEN*/);
336103396Sbp
33775374Sbp		if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
33875374Sbp			uniplen = plen = 24;
33975374Sbp			smb_encrypt(pbuf, vcp->vc_ch, encpass);
340111119Simp			ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
341103396Sbp			if (SMB_UNICODE_STRINGS(vcp)) {
342103396Sbp				strncpy(pbuf, smb_vc_getpass(vcp),
343103396Sbp					SMB_MAXPASSWORDLEN);
344103396Sbp				pbuf[SMB_MAXPASSWORDLEN] = '\0';
345103396Sbp			} else
346103396Sbp				iconv_convstr(vcp->vc_toserver, pbuf,
347103396Sbp					      smb_vc_getpass(vcp)/*,
348103396Sbp					      SMB_MAXPASSWORDLEN*/);
34975374Sbp			smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass);
35075374Sbp			pp = encpass;
35175374Sbp			unipp = ntencpass;
35275374Sbp		} else {
35375374Sbp			plen = strlen(pbuf) + 1;
35475374Sbp			pp = pbuf;
35575374Sbp			uniplen = plen * 2;
356111119Simp			ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
35775374Sbp			smb_strtouni(ntencpass, smb_vc_getpass(vcp));
35875374Sbp			plen--;
35991022Sbp
36091022Sbp			/*
36191022Sbp			 * The uniplen is zeroed because Samba cannot deal
36291022Sbp			 * with this 2nd cleartext password.  This Samba
36391022Sbp			 * "bug" is actually a workaround for problems in
36491022Sbp			 * Microsoft clients.
36591022Sbp			 */
36675374Sbp			uniplen = 0/*-= 2*/;
36775374Sbp			unipp = ntencpass;
36875374Sbp		}
36975374Sbp	} else {
37075374Sbp		/*
37175374Sbp		 * In the share security mode password will be used
37275374Sbp		 * only in the tree authentication
37375374Sbp		 */
37475374Sbp		 pp = "";
37575374Sbp		 plen = 1;
37675374Sbp		 unipp = &smb_unieol;
377107666Sfjoe		 uniplen = 0 /* sizeof(smb_unieol) */;
37875374Sbp	}
37975374Sbp	smb_rq_wstart(rqp);
38075374Sbp	mbp = &rqp->sr_rq;
38175374Sbp	up = vcp->vc_username;
38275374Sbp	ulen = strlen(up) + 1;
383103396Sbp	/*
384103396Sbp	 * If userid is null we are attempting anonymous browse login
385103396Sbp	 * so passwords must be zero length.
386103396Sbp	 */
387103396Sbp	if (ulen == 1)
388103396Sbp		plen = uniplen = 0;
38975374Sbp	mb_put_uint8(mbp, 0xff);
39075374Sbp	mb_put_uint8(mbp, 0);
39175374Sbp	mb_put_uint16le(mbp, 0);
39275374Sbp	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
39375374Sbp	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
39475374Sbp	mb_put_uint16le(mbp, vcp->vc_number);
39575374Sbp	mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
39675374Sbp	mb_put_uint16le(mbp, plen);
39775374Sbp	if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
39875374Sbp		mb_put_uint32le(mbp, 0);
39975374Sbp		smb_rq_wend(rqp);
40075374Sbp		smb_rq_bstart(rqp);
40175374Sbp		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
40275374Sbp		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);
40375374Sbp	} else {
40475374Sbp		mb_put_uint16le(mbp, uniplen);
40575374Sbp		mb_put_uint32le(mbp, 0);		/* reserved */
406227650Skevlo		mb_put_uint32le(mbp, caps);
40775374Sbp		smb_rq_wend(rqp);
40875374Sbp		smb_rq_bstart(rqp);
40975374Sbp		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
41075374Sbp		mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
41175374Sbp		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);		/* AccountName */
41275374Sbp		smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE);	/* PrimaryDomain */
41375374Sbp		smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE);	/* Client's OS */
41475374Sbp		smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE);		/* Client name */
41575374Sbp	}
41675374Sbp	smb_rq_bend(rqp);
41775374Sbp	if (ntencpass)
41875374Sbp		free(ntencpass, M_SMBTEMP);
419124087Stjr	if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)
420124087Stjr		smb_calcmackey(vcp);
42175374Sbp	error = smb_rq_simple(rqp);
42275374Sbp	SMBSDEBUG("%d\n", error);
42375374Sbp	if (error) {
42475374Sbp		if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess)
42575374Sbp			error = EAUTH;
42675374Sbp		goto bad;
42775374Sbp	}
42875374Sbp	vcp->vc_smbuid = rqp->sr_rpuid;
42975374Sbpbad:
43075374Sbp	free(encpass, M_SMBTEMP);
43175374Sbp	free(pbuf, M_SMBTEMP);
43275374Sbp	smb_rq_done(rqp);
433103396Sbp	if (error && upper == 1 && vcp->vc_sopt.sv_sm & SMB_SM_USER)
434103396Sbp		goto again;
43575374Sbp	return error;
43675374Sbp}
43775374Sbp
43875374Sbpint
43975374Sbpsmb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
44075374Sbp{
44175374Sbp	struct smb_rq *rqp;
44275374Sbp	struct mbchain *mbp;
44375374Sbp	int error;
44475374Sbp
44575374Sbp	if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
44675374Sbp		return 0;
44775374Sbp
44887599Sobrien	if (smb_smb_nomux(vcp, scred, __func__) != 0)
44975374Sbp		return EINVAL;
45075374Sbp
45175374Sbp	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
45275374Sbp	if (error)
45375374Sbp		return error;
45475374Sbp	mbp = &rqp->sr_rq;
45575374Sbp	smb_rq_wstart(rqp);
45675374Sbp	mb_put_uint8(mbp, 0xff);
45775374Sbp	mb_put_uint8(mbp, 0);
45875374Sbp	mb_put_uint16le(mbp, 0);
45975374Sbp	smb_rq_wend(rqp);
46075374Sbp	smb_rq_bstart(rqp);
46175374Sbp	smb_rq_bend(rqp);
46275374Sbp	error = smb_rq_simple(rqp);
46375374Sbp	SMBSDEBUG("%d\n", error);
46475374Sbp	smb_rq_done(rqp);
46575374Sbp	return error;
46675374Sbp}
46775374Sbp
46875374Sbpstatic char smb_any_share[] = "?????";
46975374Sbp
47075374Sbpstatic char *
47175374Sbpsmb_share_typename(int stype)
47275374Sbp{
47375374Sbp	char *pp;
47475374Sbp
47575374Sbp	switch (stype) {
47675374Sbp	    case SMB_ST_DISK:
47775374Sbp		pp = "A:";
47875374Sbp		break;
47975374Sbp	    case SMB_ST_PRINTER:
48075374Sbp		pp = smb_any_share;		/* can't use LPT: here... */
48175374Sbp		break;
48275374Sbp	    case SMB_ST_PIPE:
48375374Sbp		pp = "IPC";
48475374Sbp		break;
48575374Sbp	    case SMB_ST_COMM:
48675374Sbp		pp = "COMM";
48775374Sbp		break;
48875374Sbp	    case SMB_ST_ANY:
48975374Sbp	    default:
49075374Sbp		pp = smb_any_share;
49175374Sbp		break;
49275374Sbp	}
49375374Sbp	return pp;
49475374Sbp}
49575374Sbp
49675374Sbpint
49775374Sbpsmb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
49875374Sbp{
49975374Sbp	struct smb_vc *vcp;
50075374Sbp	struct smb_rq rq, *rqp = &rq;
50175374Sbp	struct mbchain *mbp;
50275374Sbp	char *pp, *pbuf, *encpass;
503103396Sbp	int error, plen, caseopt, upper;
50475374Sbp
505103396Sbp	upper = 0;
506103396Sbp
507103396Sbpagain:
508103396Sbp	/* Disable Unicode for SMB_COM_TREE_CONNECT_ANDX requests */
509103396Sbp	if (SSTOVC(ssp)->vc_hflags2 & SMB_FLAGS2_UNICODE) {
510103396Sbp		vcp = SSTOVC(ssp);
511227650Skevlo		vcp->vc_toserver = vcp->vc_cp_toserver;
512227650Skevlo		vcp->vc_tolocal = vcp->vc_cp_tolocal;
513103396Sbp		vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE;
514103396Sbp	}
515103396Sbp
51675374Sbp	ssp->ss_tid = SMB_TID_UNKNOWN;
51775374Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp);
51875374Sbp	if (error)
51975374Sbp		return error;
52075374Sbp	vcp = rqp->sr_vc;
52175374Sbp	caseopt = SMB_CS_NONE;
52275374Sbp	if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
52375374Sbp		plen = 1;
52475374Sbp		pp = "";
52575374Sbp		pbuf = NULL;
52675374Sbp		encpass = NULL;
52775374Sbp	} else {
528111119Simp		pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
529111119Simp		encpass = malloc(24, M_SMBTEMP, M_WAITOK);
530103396Sbp		/*
531103396Sbp		 * We try w/o uppercasing first so Samba mixed case
532103396Sbp		 * passwords work.  If that fails we come back and try
533103396Sbp		 * uppercasing to satisfy OS/2 and Windows for Workgroups.
534103396Sbp		 */
535103396Sbp		if (upper++) {
536103396Sbp			iconv_convstr(vcp->vc_toupper, pbuf,
537103396Sbp				      smb_share_getpass(ssp)/*,
538103396Sbp				      SMB_MAXPASSWORDLEN*/);
539103396Sbp		} else {
540103396Sbp			strncpy(pbuf, smb_share_getpass(ssp),
541103396Sbp				SMB_MAXPASSWORDLEN);
542103396Sbp			pbuf[SMB_MAXPASSWORDLEN] = '\0';
543103396Sbp		}
54475374Sbp		if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
54575374Sbp			plen = 24;
54675374Sbp			smb_encrypt(pbuf, vcp->vc_ch, encpass);
54775374Sbp			pp = encpass;
54875374Sbp		} else {
54975374Sbp			plen = strlen(pbuf) + 1;
55075374Sbp			pp = pbuf;
55175374Sbp		}
55275374Sbp	}
55375374Sbp	mbp = &rqp->sr_rq;
55475374Sbp	smb_rq_wstart(rqp);
55575374Sbp	mb_put_uint8(mbp, 0xff);
55675374Sbp	mb_put_uint8(mbp, 0);
55775374Sbp	mb_put_uint16le(mbp, 0);
55875374Sbp	mb_put_uint16le(mbp, 0);		/* Flags */
55975374Sbp	mb_put_uint16le(mbp, plen);
56075374Sbp	smb_rq_wend(rqp);
56175374Sbp	smb_rq_bstart(rqp);
56275374Sbp	mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
56375374Sbp	smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
56475374Sbp	pp = vcp->vc_srvname;
56575374Sbp	smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
56675374Sbp	smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
56775374Sbp	pp = ssp->ss_name;
56875374Sbp	smb_put_dstring(mbp, vcp, pp, caseopt);
56975374Sbp	pp = smb_share_typename(ssp->ss_type);
57075374Sbp	smb_put_dstring(mbp, vcp, pp, caseopt);
57175374Sbp	smb_rq_bend(rqp);
57275374Sbp	error = smb_rq_simple(rqp);
57375374Sbp	SMBSDEBUG("%d\n", error);
57475374Sbp	if (error)
57575374Sbp		goto bad;
57675374Sbp	ssp->ss_tid = rqp->sr_rptid;
57775374Sbp	ssp->ss_vcgenid = vcp->vc_genid;
57875374Sbp	ssp->ss_flags |= SMBS_CONNECTED;
579227650Skevlo	/*
580227650Skevlo	 * If the server can speak Unicode then switch
581227650Skevlo	 * our converters to do Unicode <--> Local
582227650Skevlo	 */
583227650Skevlo	if (vcp->obj.co_flags & SMBV_UNICODE) {
584227650Skevlo		vcp->vc_toserver = vcp->vc_ucs_toserver;
585227650Skevlo		vcp->vc_tolocal = vcp->vc_ucs_tolocal;
586227650Skevlo		vcp->vc_hflags2 |= SMB_FLAGS2_UNICODE;
587227650Skevlo	}
58875374Sbpbad:
58975374Sbp	if (encpass)
59075374Sbp		free(encpass, M_SMBTEMP);
59175374Sbp	if (pbuf)
59275374Sbp		free(pbuf, M_SMBTEMP);
59375374Sbp	smb_rq_done(rqp);
594103396Sbp	if (error && upper == 1)
595103396Sbp		goto again;
59675374Sbp	return error;
59775374Sbp}
59875374Sbp
59975374Sbpint
60075374Sbpsmb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
60175374Sbp{
60275374Sbp	struct smb_rq *rqp;
60375374Sbp	struct mbchain *mbp;
60475374Sbp	int error;
60575374Sbp
60675374Sbp	if (ssp->ss_tid == SMB_TID_UNKNOWN)
60775374Sbp		return 0;
60875374Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
60975374Sbp	if (error)
61075374Sbp		return error;
61175374Sbp	mbp = &rqp->sr_rq;
61275374Sbp	smb_rq_wstart(rqp);
61375374Sbp	smb_rq_wend(rqp);
61475374Sbp	smb_rq_bstart(rqp);
61575374Sbp	smb_rq_bend(rqp);
61675374Sbp	error = smb_rq_simple(rqp);
61775374Sbp	SMBSDEBUG("%d\n", error);
61875374Sbp	smb_rq_done(rqp);
61975374Sbp	ssp->ss_tid = SMB_TID_UNKNOWN;
62075374Sbp	return error;
62175374Sbp}
62275374Sbp
62375374Sbpstatic __inline int
624103395Sbpsmb_smb_readx(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
625103395Sbp	      struct uio *uio, struct smb_cred *scred)
626103395Sbp{
627103395Sbp	struct smb_rq *rqp;
628103395Sbp	struct mbchain *mbp;
629103395Sbp	struct mdchain *mdp;
630103395Sbp	u_int8_t wc;
631103395Sbp	int error;
632103395Sbp	u_int16_t residhi, residlo, off, doff;
633103395Sbp	u_int32_t resid;
634103395Sbp
635103395Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
636103395Sbp	if (error)
637103395Sbp		return error;
638103395Sbp	smb_rq_getrequest(rqp, &mbp);
639103395Sbp	smb_rq_wstart(rqp);
640103395Sbp	mb_put_uint8(mbp, 0xff);	/* no secondary command */
641103395Sbp	mb_put_uint8(mbp, 0);		/* MBZ */
642103395Sbp	mb_put_uint16le(mbp, 0);	/* offset to secondary */
643103395Sbp	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
644103395Sbp	mb_put_uint32le(mbp, uio->uio_offset);
645103395Sbp	*len = min(SSTOVC(ssp)->vc_rxmax, *len);
646103395Sbp	mb_put_uint16le(mbp, *len);	/* MaxCount */
647103395Sbp	mb_put_uint16le(mbp, *len);	/* MinCount (only indicates blocking) */
648103395Sbp	mb_put_uint32le(mbp, (unsigned)*len >> 16);	/* MaxCountHigh */
649103395Sbp	mb_put_uint16le(mbp, *len);	/* Remaining ("obsolete") */
650103395Sbp	mb_put_uint32le(mbp, uio->uio_offset >> 32);	/* OffsetHigh */
651103395Sbp	smb_rq_wend(rqp);
652103395Sbp	smb_rq_bstart(rqp);
653103395Sbp	smb_rq_bend(rqp);
654103395Sbp	do {
655103395Sbp		error = smb_rq_simple(rqp);
656103395Sbp		if (error)
657103395Sbp			break;
658103395Sbp		smb_rq_getreply(rqp, &mdp);
659103395Sbp		off = SMB_HDRLEN;
660103395Sbp		md_get_uint8(mdp, &wc);
661103395Sbp		off++;
662103395Sbp		if (wc != 12) {
663103395Sbp			error = EBADRPC;
664103395Sbp			break;
665103395Sbp		}
666103395Sbp		md_get_uint8(mdp, NULL);
667103395Sbp		off++;
668103395Sbp		md_get_uint8(mdp, NULL);
669103395Sbp		off++;
670103395Sbp		md_get_uint16le(mdp, NULL);
671103395Sbp		off += 2;
672103395Sbp		md_get_uint16le(mdp, NULL);
673103395Sbp		off += 2;
674103395Sbp		md_get_uint16le(mdp, NULL);	/* data compaction mode */
675103395Sbp		off += 2;
676103395Sbp		md_get_uint16le(mdp, NULL);
677103395Sbp		off += 2;
678103395Sbp		md_get_uint16le(mdp, &residlo);
679103395Sbp		off += 2;
680103395Sbp		md_get_uint16le(mdp, &doff);	/* data offset */
681103395Sbp		off += 2;
682103395Sbp		md_get_uint16le(mdp, &residhi);
683103395Sbp		off += 2;
684103395Sbp		resid = (residhi << 16) | residlo;
685103395Sbp		md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
686103395Sbp		off += 4*2;
687103395Sbp		md_get_uint16le(mdp, NULL);	/* ByteCount */
688103395Sbp		off += 2;
689103395Sbp		if (doff > off)	/* pad byte(s)? */
690103395Sbp			md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
691103395Sbp		if (resid == 0) {
692103395Sbp			*rresid = resid;
693103395Sbp			break;
694103395Sbp		}
695103395Sbp		error = md_get_uio(mdp, uio, resid);
696103395Sbp		if (error)
697103395Sbp			break;
698103395Sbp		*rresid = resid;
699103395Sbp	} while(0);
700103395Sbp	smb_rq_done(rqp);
701103395Sbp	return (error);
702103395Sbp}
703103395Sbp
704103395Sbpstatic __inline int
705103395Sbpsmb_smb_writex(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
706103395Sbp	struct uio *uio, struct smb_cred *scred)
707103395Sbp{
708103395Sbp	struct smb_rq *rqp;
709103395Sbp	struct mbchain *mbp;
710103395Sbp	struct mdchain *mdp;
711103395Sbp	int error;
712103395Sbp	u_int8_t wc;
713103395Sbp	u_int16_t resid;
714103395Sbp
715103395Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
716103395Sbp	if (error)
717103395Sbp		return (error);
718103395Sbp	smb_rq_getrequest(rqp, &mbp);
719103395Sbp	smb_rq_wstart(rqp);
720103395Sbp	mb_put_uint8(mbp, 0xff);	/* no secondary command */
721103395Sbp	mb_put_uint8(mbp, 0);		/* MBZ */
722103395Sbp	mb_put_uint16le(mbp, 0);	/* offset to secondary */
723103395Sbp	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
724103395Sbp	mb_put_uint32le(mbp, uio->uio_offset);
725103395Sbp	mb_put_uint32le(mbp, 0);	/* MBZ (timeout) */
726103395Sbp	mb_put_uint16le(mbp, 0);	/* !write-thru */
727103395Sbp	mb_put_uint16le(mbp, 0);
728103395Sbp	*len = min(SSTOVC(ssp)->vc_wxmax, *len);
729103395Sbp	mb_put_uint16le(mbp, (unsigned)*len >> 16);
730103395Sbp	mb_put_uint16le(mbp, *len);
731103395Sbp	mb_put_uint16le(mbp, 64);	/* data offset from header start */
732103395Sbp	mb_put_uint32le(mbp, uio->uio_offset >> 32);	/* OffsetHigh */
733103395Sbp	smb_rq_wend(rqp);
734103395Sbp	smb_rq_bstart(rqp);
735103395Sbp	do {
736103395Sbp		mb_put_uint8(mbp, 0xee);	/* mimic xp pad byte! */
737103395Sbp		error = mb_put_uio(mbp, uio, *len);
738103395Sbp		if (error)
739103395Sbp			break;
740103395Sbp		smb_rq_bend(rqp);
741103395Sbp		error = smb_rq_simple(rqp);
742103395Sbp		if (error)
743103395Sbp			break;
744103395Sbp		smb_rq_getreply(rqp, &mdp);
745103395Sbp		md_get_uint8(mdp, &wc);
746103395Sbp		if (wc != 6) {
747103395Sbp			error = EBADRPC;
748103395Sbp			break;
749103395Sbp		}
750103395Sbp		md_get_uint8(mdp, NULL);
751103395Sbp		md_get_uint8(mdp, NULL);
752103395Sbp		md_get_uint16le(mdp, NULL);
753103395Sbp		md_get_uint16le(mdp, &resid);
754103395Sbp		*rresid = resid;
755103395Sbp	} while(0);
756103395Sbp
757103395Sbp	smb_rq_done(rqp);
758103395Sbp	return (error);
759103395Sbp}
760103395Sbp
761103395Sbpstatic __inline int
76275374Sbpsmb_smb_read(struct smb_share *ssp, u_int16_t fid,
76375374Sbp	int *len, int *rresid, struct uio *uio, struct smb_cred *scred)
76475374Sbp{
76575374Sbp	struct smb_rq *rqp;
76675374Sbp	struct mbchain *mbp;
76775374Sbp	struct mdchain *mdp;
76875374Sbp	u_int16_t resid, bc;
76975374Sbp	u_int8_t wc;
77075374Sbp	int error, rlen, blksz;
77175374Sbp
772103395Sbp	if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_READX)
773103395Sbp		return (smb_smb_readx(ssp, fid, len, rresid, uio, scred));
774103395Sbp
77575374Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
77675374Sbp	if (error)
77775374Sbp		return error;
77875374Sbp
77975374Sbp	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
78075374Sbp	rlen = *len = min(blksz, *len);
78175374Sbp
78275374Sbp	smb_rq_getrequest(rqp, &mbp);
78375374Sbp	smb_rq_wstart(rqp);
78475374Sbp	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
78575374Sbp	mb_put_uint16le(mbp, rlen);
78675374Sbp	mb_put_uint32le(mbp, uio->uio_offset);
78775374Sbp	mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
78875374Sbp	smb_rq_wend(rqp);
78975374Sbp	smb_rq_bstart(rqp);
79075374Sbp	smb_rq_bend(rqp);
79175374Sbp	do {
79275374Sbp		error = smb_rq_simple(rqp);
79375374Sbp		if (error)
79475374Sbp			break;
79575374Sbp		smb_rq_getreply(rqp, &mdp);
79675374Sbp		md_get_uint8(mdp, &wc);
79775374Sbp		if (wc != 5) {
79875374Sbp			error = EBADRPC;
79975374Sbp			break;
80075374Sbp		}
80175374Sbp		md_get_uint16le(mdp, &resid);
80275374Sbp		md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
80375374Sbp		md_get_uint16le(mdp, &bc);
80475374Sbp		md_get_uint8(mdp, NULL);		/* ignore buffer type */
80575374Sbp		md_get_uint16le(mdp, &resid);
80675374Sbp		if (resid == 0) {
80775374Sbp			*rresid = resid;
80875374Sbp			break;
80975374Sbp		}
81075374Sbp		error = md_get_uio(mdp, uio, resid);
81175374Sbp		if (error)
81275374Sbp			break;
81375374Sbp		*rresid = resid;
81475374Sbp	} while(0);
81575374Sbp	smb_rq_done(rqp);
81675374Sbp	return error;
81775374Sbp}
81875374Sbp
81975374Sbpint
82075374Sbpsmb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
82175374Sbp	struct smb_cred *scred)
82275374Sbp{
82375374Sbp	int tsize, len, resid;
82475374Sbp	int error = 0;
82575374Sbp
82675374Sbp	tsize = uio->uio_resid;
82775374Sbp	while (tsize > 0) {
828170804Smjacob		resid = 0;
82975374Sbp		len = tsize;
83075374Sbp		error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
83175374Sbp		if (error)
83275374Sbp			break;
83375374Sbp		tsize -= resid;
83475374Sbp		if (resid < len)
83575374Sbp			break;
83675374Sbp	}
83775374Sbp	return error;
83875374Sbp}
83975374Sbp
84075374Sbpstatic __inline int
84175374Sbpsmb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
84275374Sbp	struct uio *uio, struct smb_cred *scred)
84375374Sbp{
84475374Sbp	struct smb_rq *rqp;
84575374Sbp	struct mbchain *mbp;
84675374Sbp	struct mdchain *mdp;
84775374Sbp	u_int16_t resid;
84875374Sbp	u_int8_t wc;
84975374Sbp	int error, blksz;
85075374Sbp
851103395Sbp	if (*len && SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)
852103395Sbp		return (smb_smb_writex(ssp, fid, len, rresid, uio, scred));
853103395Sbp
85475374Sbp	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
85575374Sbp	if (blksz > 0xffff)
85675374Sbp		blksz = 0xffff;
85775374Sbp
85875374Sbp	resid = *len = min(blksz, *len);
85975374Sbp
86075374Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
86175374Sbp	if (error)
86275374Sbp		return error;
86375374Sbp	smb_rq_getrequest(rqp, &mbp);
86475374Sbp	smb_rq_wstart(rqp);
86575374Sbp	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
86675374Sbp	mb_put_uint16le(mbp, resid);
86775374Sbp	mb_put_uint32le(mbp, uio->uio_offset);
86875374Sbp	mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
86975374Sbp	smb_rq_wend(rqp);
87075374Sbp	smb_rq_bstart(rqp);
87175374Sbp	mb_put_uint8(mbp, SMB_DT_DATA);
87275374Sbp	mb_put_uint16le(mbp, resid);
87375374Sbp	do {
87475374Sbp		error = mb_put_uio(mbp, uio, resid);
87575374Sbp		if (error)
87675374Sbp			break;
87775374Sbp		smb_rq_bend(rqp);
87875374Sbp		error = smb_rq_simple(rqp);
87975374Sbp		if (error)
88075374Sbp			break;
88175374Sbp		smb_rq_getreply(rqp, &mdp);
88275374Sbp		md_get_uint8(mdp, &wc);
88375374Sbp		if (wc != 1) {
88475374Sbp			error = EBADRPC;
88575374Sbp			break;
88675374Sbp		}
88775374Sbp		md_get_uint16le(mdp, &resid);
88875374Sbp		*rresid = resid;
88975374Sbp	} while(0);
89075374Sbp	smb_rq_done(rqp);
89175374Sbp	return error;
89275374Sbp}
89375374Sbp
89475374Sbpint
89575374Sbpsmb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
89675374Sbp	struct smb_cred *scred)
89775374Sbp{
89875374Sbp	int error = 0, len, tsize, resid;
89975374Sbp	struct uio olduio;
90075374Sbp
90175374Sbp	tsize = uio->uio_resid;
90275374Sbp	olduio = *uio;
90375374Sbp	while (tsize > 0) {
904170804Smjacob		resid = 0;
90575374Sbp		len = tsize;
90675374Sbp		error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
90775374Sbp		if (error)
90875374Sbp			break;
90975374Sbp		if (resid < len) {
91075374Sbp			error = EIO;
91175374Sbp			break;
91275374Sbp		}
91375374Sbp		tsize -= resid;
91475374Sbp	}
91575374Sbp	if (error) {
91691023Sbp		/*
91791023Sbp		 * Errors can happen on the copyin, the rpc, etc.  So they
91891023Sbp		 * imply resid is unreliable.  The only safe thing is
91991023Sbp		 * to pretend zero bytes made it.  We needn't restore the
92091023Sbp		 * iovs because callers don't depend on them in error
92191023Sbp		 * paths - uio_resid and uio_offset are what matter.
92291023Sbp		 */
92375374Sbp		*uio = olduio;
92475374Sbp	}
92575374Sbp	return error;
92675374Sbp}
92775374Sbp
92875374Sbpint
92975374Sbpsmb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
93075374Sbp{
93175374Sbp	struct smb_rq *rqp;
93275374Sbp	struct mbchain *mbp;
93375374Sbp	int error;
93475374Sbp
93575374Sbp	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
93675374Sbp	if (error)
93775374Sbp		return error;
93875374Sbp	mbp = &rqp->sr_rq;
93975374Sbp	smb_rq_wstart(rqp);
94075374Sbp	mb_put_uint16le(mbp, 1);
94175374Sbp	smb_rq_wend(rqp);
94275374Sbp	smb_rq_bstart(rqp);
94375374Sbp	mb_put_uint32le(mbp, 0);
94475374Sbp	smb_rq_bend(rqp);
94575374Sbp	error = smb_rq_simple(rqp);
94675374Sbp	SMBSDEBUG("%d\n", error);
94775374Sbp	smb_rq_done(rqp);
94875374Sbp	return error;
94975374Sbp}
950