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$");
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
7175374Sbp#define	SMB_DIALECT_MAX	(sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2)
7275374Sbp
73103395Sbpstatic u_int32_t
74103395Sbpsmb_vc_maxread(struct smb_vc *vcp)
75103395Sbp{
76103395Sbp	/*
77103395Sbp	 * Specs say up to 64k data bytes, but Windows traffic
78103395Sbp	 * uses 60k... no doubt for some good reason.
79124087Stjr	 *
80124087Stjr	 * Don't exceed the server's buffer size if signatures
81124087Stjr	 * are enabled otherwise Windows 2003 chokes. Allow space
82124087Stjr	 * for the SMB header & a little bit extra.
83103395Sbp	 */
84124087Stjr	if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) &&
85124087Stjr	    (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0)
86103395Sbp		return (60*1024);
87103395Sbp	else
88124087Stjr		return (vcp->vc_sopt.sv_maxtx - SMB_HDRLEN - 64);
89103395Sbp}
90103395Sbp
91103395Sbpstatic u_int32_t
92103395Sbpsmb_vc_maxwrite(struct smb_vc *vcp)
93103395Sbp{
94103395Sbp	/*
95124087Stjr	 * See comment above.
96103395Sbp	 */
97124087Stjr	if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) &&
98124087Stjr	    (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0)
99103395Sbp		return (60*1024);
100103395Sbp	else
101124087Stjr		return (vcp->vc_sopt.sv_maxtx - SMB_HDRLEN - 64);
102103395Sbp}
103103395Sbp
10475374Sbpstatic int
10575374Sbpsmb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
10675374Sbp{
10787192Sbp	if (scred->scr_td->td_proc == vcp->vc_iod->iod_p)
10875374Sbp		return 0;
10975374Sbp	SMBERROR("wrong function called(%s)\n", name);
11075374Sbp	return EINVAL;
11175374Sbp}
11275374Sbp
11375374Sbpint
11475374Sbpsmb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
11575374Sbp{
11675374Sbp	struct smb_dialect *dp;
11775374Sbp	struct smb_sopt *sp = NULL;
11875374Sbp	struct smb_rq *rqp;
11975374Sbp	struct mbchain *mbp;
12075374Sbp	struct mdchain *mdp;
12175374Sbp	u_int8_t wc, stime[8], sblen;
12275374Sbp	u_int16_t dindex, tw, tw1, swlen, bc;
12375374Sbp	int error, maxqsz;
124227650Skevlo	int unicode = SMB_UNICODE_STRINGS(vcp);
125227650Skevlo	void * servercharset = vcp->vc_toserver;
126227650Skevlo	void * localcharset = vcp->vc_tolocal;
12775374Sbp
12887599Sobrien	if (smb_smb_nomux(vcp, scred, __func__) != 0)
12975374Sbp		return EINVAL;
130227650Skevlo	/* Disable Unicode for SMB_COM_NEGOTIATE requests */
131227650Skevlo	if (unicode) {
132227650Skevlo		vcp->vc_toserver = vcp->vc_cp_toserver;
133227650Skevlo		vcp->vc_tolocal  = vcp->vc_cp_tolocal;
134227650Skevlo	}
13575374Sbp	vcp->vc_hflags = 0;
13675374Sbp	vcp->vc_hflags2 = 0;
13775374Sbp	vcp->obj.co_flags &= ~(SMBV_ENCRYPT);
13875374Sbp	sp = &vcp->vc_sopt;
13975374Sbp	bzero(sp, sizeof(struct smb_sopt));
14075374Sbp	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
14175374Sbp	if (error)
14275374Sbp		return error;
14375374Sbp	smb_rq_getrequest(rqp, &mbp);
14475374Sbp	smb_rq_wstart(rqp);
14575374Sbp	smb_rq_wend(rqp);
14675374Sbp	smb_rq_bstart(rqp);
14775374Sbp	for(dp = smb_dialects; dp->d_id != -1; dp++) {
14875374Sbp		mb_put_uint8(mbp, SMB_DT_DIALECT);
14975374Sbp		smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE);
15075374Sbp	}
15175374Sbp	smb_rq_bend(rqp);
15275374Sbp	error = smb_rq_simple(rqp);
15375374Sbp	SMBSDEBUG("%d\n", error);
15475374Sbp	if (error)
15575374Sbp		goto bad;
15675374Sbp	smb_rq_getreply(rqp, &mdp);
15775374Sbp	do {
15875374Sbp		error = md_get_uint8(mdp, &wc);
15975374Sbp		if (error)
16075374Sbp			break;
16175374Sbp		error = md_get_uint16le(mdp, &dindex);
16275374Sbp		if (error)
16375374Sbp			break;
16475374Sbp		if (dindex > 7) {
16575374Sbp			SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex);
16675374Sbp			error = EBADRPC;
16775374Sbp			break;
16875374Sbp		}
16975374Sbp		dp = smb_dialects + dindex;
17075374Sbp		sp->sv_proto = dp->d_id;
17175374Sbp		SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc);
17275374Sbp		error = EBADRPC;
17375374Sbp		if (dp->d_id >= SMB_DIALECT_NTLM0_12) {
17475374Sbp			if (wc != 17)
17575374Sbp				break;
17675374Sbp			md_get_uint8(mdp, &sp->sv_sm);
17775374Sbp			md_get_uint16le(mdp, &sp->sv_maxmux);
17875374Sbp			md_get_uint16le(mdp, &sp->sv_maxvcs);
17975374Sbp			md_get_uint32le(mdp, &sp->sv_maxtx);
18075374Sbp			md_get_uint32le(mdp, &sp->sv_maxraw);
18175374Sbp			md_get_uint32le(mdp, &sp->sv_skey);
18275374Sbp			md_get_uint32le(mdp, &sp->sv_caps);
18375374Sbp			md_get_mem(mdp, stime, 8, MB_MSYSTEM);
18475374Sbp			md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
18575374Sbp			md_get_uint8(mdp, &sblen);
18675374Sbp			if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
18775374Sbp				if (sblen != SMB_MAXCHALLENGELEN) {
18875374Sbp					SMBERROR("Unexpected length of security blob (%d)\n", sblen);
18975374Sbp					break;
19075374Sbp				}
191227650Skevlo				error = md_get_uint16le(mdp, &bc);
19275374Sbp				if (error)
19375374Sbp					break;
19475374Sbp				if (sp->sv_caps & SMB_CAP_EXT_SECURITY)
19575374Sbp					md_get_mem(mdp, NULL, 16, MB_MSYSTEM);
19675374Sbp				error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM);
19775374Sbp				if (error)
19875374Sbp					break;
19975374Sbp				vcp->vc_chlen = sblen;
20075374Sbp				vcp->obj.co_flags |= SMBV_ENCRYPT;
20175374Sbp			}
202124087Stjr			if (sp->sv_sm & SMB_SM_SIGS_REQUIRE)
203124087Stjr				vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
204227650Skevlo			if (vcp->vc_ucs_toserver &&
205227650Skevlo				sp->sv_caps & SMB_CAP_UNICODE) {
206227650Skevlo				/*
207227650Skevlo				* They do Unicode.
208227650Skevlo				*/
209227650Skevlo				vcp->obj.co_flags |= SMBV_UNICODE;
210227650Skevlo			}
21175374Sbp			vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
21275374Sbp			if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
21375374Sbp			    sp->sv_maxtx < 4096 &&
21475374Sbp			    (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) {
21575374Sbp				vcp->obj.co_flags |= SMBV_WIN95;
21675374Sbp				SMBSDEBUG("Win95 detected\n");
21775374Sbp			}
218227650Skevlo			error = 0;
219227650Skevlo			break;
220227650Skevlo		}
221227650Skevlo		vcp->vc_hflags2 &= ~(SMB_FLAGS2_EXT_SEC|SMB_FLAGS2_DFS|
222227650Skevlo				     SMB_FLAGS2_ERR_STATUS|SMB_FLAGS2_UNICODE);
223227650Skevlo		unicode = 0;
224227650Skevlo		if (dp->d_id > SMB_DIALECT_CORE) {
22575374Sbp			md_get_uint16le(mdp, &tw);
22675374Sbp			sp->sv_sm = tw;
22775374Sbp			md_get_uint16le(mdp, &tw);
22875374Sbp			sp->sv_maxtx = tw;
22975374Sbp			md_get_uint16le(mdp, &sp->sv_maxmux);
23075374Sbp			md_get_uint16le(mdp, &sp->sv_maxvcs);
23175374Sbp			md_get_uint16le(mdp, &tw);	/* rawmode */
23275374Sbp			md_get_uint32le(mdp, &sp->sv_skey);
23375374Sbp			if (wc == 13) {		/* >= LANMAN1 */
23475374Sbp				md_get_uint16(mdp, &tw);		/* time */
23575374Sbp				md_get_uint16(mdp, &tw1);		/* date */
23675374Sbp				md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
23775374Sbp				md_get_uint16le(mdp, &swlen);
23875374Sbp				if (swlen > SMB_MAXCHALLENGELEN)
23975374Sbp					break;
24075374Sbp				md_get_uint16(mdp, NULL);	/* mbz */
241227650Skevlo				if (md_get_uint16le(mdp, &bc) != 0)
24275374Sbp					break;
24375374Sbp				if (bc < swlen)
24475374Sbp					break;
24575374Sbp				if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
24675374Sbp					error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM);
24775374Sbp					if (error)
24875374Sbp						break;
24975374Sbp					vcp->vc_chlen = swlen;
25075374Sbp					vcp->obj.co_flags |= SMBV_ENCRYPT;
25175374Sbp				}
25275374Sbp			}
25375374Sbp			vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
25475374Sbp		} else {	/* an old CORE protocol */
25575374Sbp			sp->sv_maxmux = 1;
25675374Sbp		}
25775374Sbp		error = 0;
25875374Sbp	} while (0);
25975374Sbp	if (error == 0) {
26075374Sbp		vcp->vc_maxvcs = sp->sv_maxvcs;
26175374Sbp		if (vcp->vc_maxvcs <= 1) {
26275374Sbp			if (vcp->vc_maxvcs == 0)
26375374Sbp				vcp->vc_maxvcs = 1;
26475374Sbp		}
26575374Sbp		if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff)
26675374Sbp			sp->sv_maxtx = 1024;
267103395Sbp		else
268103395Sbp			sp->sv_maxtx = min(sp->sv_maxtx,
269103395Sbp					   63*1024 + SMB_HDRLEN + 16);
270103395Sbp		SMB_TRAN_GETPARAM(vcp, SMBTP_RCVSZ, &maxqsz);
271103395Sbp		vcp->vc_rxmax = min(smb_vc_maxread(vcp), maxqsz - 1024);
27275374Sbp		SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz);
273103395Sbp		vcp->vc_wxmax = min(smb_vc_maxwrite(vcp), maxqsz - 1024);
27475374Sbp		vcp->vc_txmax = min(sp->sv_maxtx, maxqsz);
27575374Sbp		SMBSDEBUG("TZ = %d\n", sp->sv_tz);
27675374Sbp		SMBSDEBUG("CAPS = %x\n", sp->sv_caps);
27775374Sbp		SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux);
27875374Sbp		SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs);
27975374Sbp		SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw);
28075374Sbp		SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx);
28175374Sbp	}
28275374Sbpbad:
283227650Skevlo	/* Restore Unicode conversion state */
284227650Skevlo	if (unicode) {
285227650Skevlo		vcp->vc_toserver = servercharset;
286227650Skevlo		vcp->vc_tolocal  = localcharset;
287227650Skevlo		vcp->vc_hflags2 |= SMB_FLAGS2_UNICODE;
288227650Skevlo	}
28975374Sbp	smb_rq_done(rqp);
29075374Sbp	return error;
29175374Sbp}
29275374Sbp
29375374Sbpint
29475374Sbpsmb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
29575374Sbp{
29675374Sbp	struct smb_rq *rqp;
29775374Sbp	struct mbchain *mbp;
29875374Sbp/*	u_int8_t wc;
29975374Sbp	u_int16_t tw, tw1;*/
30075374Sbp	smb_uniptr unipp, ntencpass = NULL;
30175374Sbp	char *pp, *up, *pbuf, *encpass;
302103396Sbp	int error, plen, uniplen, ulen, upper;
303227650Skevlo	u_int32_t caps = 0;
30475374Sbp
305103396Sbp	upper = 0;
306103396Sbp
307227650Skevlo	if (vcp->obj.co_flags & SMBV_UNICODE)
308227650Skevlo		caps |= SMB_CAP_UNICODE;
309227650Skevlo
310103396Sbpagain:
311103396Sbp
31275374Sbp	vcp->vc_smbuid = SMB_UID_UNKNOWN;
31375374Sbp
31487599Sobrien	if (smb_smb_nomux(vcp, scred, __func__) != 0)
31575374Sbp		return EINVAL;
31675374Sbp
31775374Sbp	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
31875374Sbp	if (error)
31975374Sbp		return error;
320111119Simp	pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
321111119Simp	encpass = malloc(24, M_SMBTEMP, M_WAITOK);
32275374Sbp	if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
323103396Sbp		/*
324103396Sbp		 * We try w/o uppercasing first so Samba mixed case
325103396Sbp		 * passwords work.  If that fails we come back and try
326103396Sbp		 * uppercasing to satisfy OS/2 and Windows for Workgroups.
327103396Sbp		 */
328103396Sbp		if (upper++) {
329103396Sbp			iconv_convstr(vcp->vc_toupper, pbuf,
330103396Sbp				      smb_vc_getpass(vcp)/*, SMB_MAXPASSWORDLEN*/);
331103396Sbp		} else {
332103396Sbp			strncpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN);
333103396Sbp			pbuf[SMB_MAXPASSWORDLEN] = '\0';
334103396Sbp		}
335103396Sbp		if (!SMB_UNICODE_STRINGS(vcp))
336103396Sbp			iconv_convstr(vcp->vc_toserver, pbuf, pbuf/*,
337103396Sbp				      SMB_MAXPASSWORDLEN*/);
338103396Sbp
33975374Sbp		if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
34075374Sbp			uniplen = plen = 24;
34175374Sbp			smb_encrypt(pbuf, vcp->vc_ch, encpass);
342111119Simp			ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
343103396Sbp			if (SMB_UNICODE_STRINGS(vcp)) {
344103396Sbp				strncpy(pbuf, smb_vc_getpass(vcp),
345103396Sbp					SMB_MAXPASSWORDLEN);
346103396Sbp				pbuf[SMB_MAXPASSWORDLEN] = '\0';
347103396Sbp			} else
348103396Sbp				iconv_convstr(vcp->vc_toserver, pbuf,
349103396Sbp					      smb_vc_getpass(vcp)/*,
350103396Sbp					      SMB_MAXPASSWORDLEN*/);
35175374Sbp			smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass);
35275374Sbp			pp = encpass;
35375374Sbp			unipp = ntencpass;
35475374Sbp		} else {
35575374Sbp			plen = strlen(pbuf) + 1;
35675374Sbp			pp = pbuf;
35775374Sbp			uniplen = plen * 2;
358111119Simp			ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
35975374Sbp			smb_strtouni(ntencpass, smb_vc_getpass(vcp));
36075374Sbp			plen--;
36191022Sbp
36291022Sbp			/*
36391022Sbp			 * The uniplen is zeroed because Samba cannot deal
36491022Sbp			 * with this 2nd cleartext password.  This Samba
36591022Sbp			 * "bug" is actually a workaround for problems in
36691022Sbp			 * Microsoft clients.
36791022Sbp			 */
36875374Sbp			uniplen = 0/*-= 2*/;
36975374Sbp			unipp = ntencpass;
37075374Sbp		}
37175374Sbp	} else {
37275374Sbp		/*
37375374Sbp		 * In the share security mode password will be used
37475374Sbp		 * only in the tree authentication
37575374Sbp		 */
37675374Sbp		 pp = "";
37775374Sbp		 plen = 1;
37875374Sbp		 unipp = &smb_unieol;
379107666Sfjoe		 uniplen = 0 /* sizeof(smb_unieol) */;
38075374Sbp	}
38175374Sbp	smb_rq_wstart(rqp);
38275374Sbp	mbp = &rqp->sr_rq;
38375374Sbp	up = vcp->vc_username;
38475374Sbp	ulen = strlen(up) + 1;
385103396Sbp	/*
386103396Sbp	 * If userid is null we are attempting anonymous browse login
387103396Sbp	 * so passwords must be zero length.
388103396Sbp	 */
389103396Sbp	if (ulen == 1)
390103396Sbp		plen = uniplen = 0;
39175374Sbp	mb_put_uint8(mbp, 0xff);
39275374Sbp	mb_put_uint8(mbp, 0);
39375374Sbp	mb_put_uint16le(mbp, 0);
39475374Sbp	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
39575374Sbp	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
39675374Sbp	mb_put_uint16le(mbp, vcp->vc_number);
39775374Sbp	mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
39875374Sbp	mb_put_uint16le(mbp, plen);
39975374Sbp	if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
40075374Sbp		mb_put_uint32le(mbp, 0);
40175374Sbp		smb_rq_wend(rqp);
40275374Sbp		smb_rq_bstart(rqp);
40375374Sbp		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
40475374Sbp		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);
40575374Sbp	} else {
40675374Sbp		mb_put_uint16le(mbp, uniplen);
40775374Sbp		mb_put_uint32le(mbp, 0);		/* reserved */
408227650Skevlo		mb_put_uint32le(mbp, caps);
40975374Sbp		smb_rq_wend(rqp);
41075374Sbp		smb_rq_bstart(rqp);
41175374Sbp		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
41275374Sbp		mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
41375374Sbp		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);		/* AccountName */
41475374Sbp		smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE);	/* PrimaryDomain */
41575374Sbp		smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE);	/* Client's OS */
41675374Sbp		smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE);		/* Client name */
41775374Sbp	}
41875374Sbp	smb_rq_bend(rqp);
41975374Sbp	if (ntencpass)
42075374Sbp		free(ntencpass, M_SMBTEMP);
421124087Stjr	if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)
422124087Stjr		smb_calcmackey(vcp);
42375374Sbp	error = smb_rq_simple(rqp);
42475374Sbp	SMBSDEBUG("%d\n", error);
42575374Sbp	if (error) {
42675374Sbp		if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess)
42775374Sbp			error = EAUTH;
42875374Sbp		goto bad;
42975374Sbp	}
43075374Sbp	vcp->vc_smbuid = rqp->sr_rpuid;
43175374Sbpbad:
43275374Sbp	free(encpass, M_SMBTEMP);
43375374Sbp	free(pbuf, M_SMBTEMP);
43475374Sbp	smb_rq_done(rqp);
435103396Sbp	if (error && upper == 1 && vcp->vc_sopt.sv_sm & SMB_SM_USER)
436103396Sbp		goto again;
43775374Sbp	return error;
43875374Sbp}
43975374Sbp
44075374Sbpint
44175374Sbpsmb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
44275374Sbp{
44375374Sbp	struct smb_rq *rqp;
44475374Sbp	struct mbchain *mbp;
44575374Sbp	int error;
44675374Sbp
44775374Sbp	if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
44875374Sbp		return 0;
44975374Sbp
45087599Sobrien	if (smb_smb_nomux(vcp, scred, __func__) != 0)
45175374Sbp		return EINVAL;
45275374Sbp
45375374Sbp	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
45475374Sbp	if (error)
45575374Sbp		return error;
45675374Sbp	mbp = &rqp->sr_rq;
45775374Sbp	smb_rq_wstart(rqp);
45875374Sbp	mb_put_uint8(mbp, 0xff);
45975374Sbp	mb_put_uint8(mbp, 0);
46075374Sbp	mb_put_uint16le(mbp, 0);
46175374Sbp	smb_rq_wend(rqp);
46275374Sbp	smb_rq_bstart(rqp);
46375374Sbp	smb_rq_bend(rqp);
46475374Sbp	error = smb_rq_simple(rqp);
46575374Sbp	SMBSDEBUG("%d\n", error);
46675374Sbp	smb_rq_done(rqp);
46775374Sbp	return error;
46875374Sbp}
46975374Sbp
47075374Sbpstatic char smb_any_share[] = "?????";
47175374Sbp
47275374Sbpstatic char *
47375374Sbpsmb_share_typename(int stype)
47475374Sbp{
47575374Sbp	char *pp;
47675374Sbp
47775374Sbp	switch (stype) {
47875374Sbp	    case SMB_ST_DISK:
47975374Sbp		pp = "A:";
48075374Sbp		break;
48175374Sbp	    case SMB_ST_PRINTER:
48275374Sbp		pp = smb_any_share;		/* can't use LPT: here... */
48375374Sbp		break;
48475374Sbp	    case SMB_ST_PIPE:
48575374Sbp		pp = "IPC";
48675374Sbp		break;
48775374Sbp	    case SMB_ST_COMM:
48875374Sbp		pp = "COMM";
48975374Sbp		break;
49075374Sbp	    case SMB_ST_ANY:
49175374Sbp	    default:
49275374Sbp		pp = smb_any_share;
49375374Sbp		break;
49475374Sbp	}
49575374Sbp	return pp;
49675374Sbp}
49775374Sbp
49875374Sbpint
49975374Sbpsmb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
50075374Sbp{
50175374Sbp	struct smb_vc *vcp;
50275374Sbp	struct smb_rq rq, *rqp = &rq;
50375374Sbp	struct mbchain *mbp;
50475374Sbp	char *pp, *pbuf, *encpass;
505103396Sbp	int error, plen, caseopt, upper;
50675374Sbp
507103396Sbp	upper = 0;
508103396Sbp
509103396Sbpagain:
510103396Sbp	/* Disable Unicode for SMB_COM_TREE_CONNECT_ANDX requests */
511103396Sbp	if (SSTOVC(ssp)->vc_hflags2 & SMB_FLAGS2_UNICODE) {
512103396Sbp		vcp = SSTOVC(ssp);
513227650Skevlo		vcp->vc_toserver = vcp->vc_cp_toserver;
514227650Skevlo		vcp->vc_tolocal = vcp->vc_cp_tolocal;
515103396Sbp		vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE;
516103396Sbp	}
517103396Sbp
51875374Sbp	ssp->ss_tid = SMB_TID_UNKNOWN;
51975374Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp);
52075374Sbp	if (error)
52175374Sbp		return error;
52275374Sbp	vcp = rqp->sr_vc;
52375374Sbp	caseopt = SMB_CS_NONE;
52475374Sbp	if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
52575374Sbp		plen = 1;
52675374Sbp		pp = "";
52775374Sbp		pbuf = NULL;
52875374Sbp		encpass = NULL;
52975374Sbp	} else {
530111119Simp		pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
531111119Simp		encpass = malloc(24, M_SMBTEMP, M_WAITOK);
532103396Sbp		/*
533103396Sbp		 * We try w/o uppercasing first so Samba mixed case
534103396Sbp		 * passwords work.  If that fails we come back and try
535103396Sbp		 * uppercasing to satisfy OS/2 and Windows for Workgroups.
536103396Sbp		 */
537103396Sbp		if (upper++) {
538103396Sbp			iconv_convstr(vcp->vc_toupper, pbuf,
539103396Sbp				      smb_share_getpass(ssp)/*,
540103396Sbp				      SMB_MAXPASSWORDLEN*/);
541103396Sbp		} else {
542103396Sbp			strncpy(pbuf, smb_share_getpass(ssp),
543103396Sbp				SMB_MAXPASSWORDLEN);
544103396Sbp			pbuf[SMB_MAXPASSWORDLEN] = '\0';
545103396Sbp		}
54675374Sbp		if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
54775374Sbp			plen = 24;
54875374Sbp			smb_encrypt(pbuf, vcp->vc_ch, encpass);
54975374Sbp			pp = encpass;
55075374Sbp		} else {
55175374Sbp			plen = strlen(pbuf) + 1;
55275374Sbp			pp = pbuf;
55375374Sbp		}
55475374Sbp	}
55575374Sbp	mbp = &rqp->sr_rq;
55675374Sbp	smb_rq_wstart(rqp);
55775374Sbp	mb_put_uint8(mbp, 0xff);
55875374Sbp	mb_put_uint8(mbp, 0);
55975374Sbp	mb_put_uint16le(mbp, 0);
56075374Sbp	mb_put_uint16le(mbp, 0);		/* Flags */
56175374Sbp	mb_put_uint16le(mbp, plen);
56275374Sbp	smb_rq_wend(rqp);
56375374Sbp	smb_rq_bstart(rqp);
56475374Sbp	mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
56575374Sbp	smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
56675374Sbp	pp = vcp->vc_srvname;
56775374Sbp	smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
56875374Sbp	smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
56975374Sbp	pp = ssp->ss_name;
57075374Sbp	smb_put_dstring(mbp, vcp, pp, caseopt);
57175374Sbp	pp = smb_share_typename(ssp->ss_type);
57275374Sbp	smb_put_dstring(mbp, vcp, pp, caseopt);
57375374Sbp	smb_rq_bend(rqp);
57475374Sbp	error = smb_rq_simple(rqp);
57575374Sbp	SMBSDEBUG("%d\n", error);
57675374Sbp	if (error)
57775374Sbp		goto bad;
57875374Sbp	ssp->ss_tid = rqp->sr_rptid;
57975374Sbp	ssp->ss_vcgenid = vcp->vc_genid;
58075374Sbp	ssp->ss_flags |= SMBS_CONNECTED;
581227650Skevlo	/*
582227650Skevlo	 * If the server can speak Unicode then switch
583227650Skevlo	 * our converters to do Unicode <--> Local
584227650Skevlo	 */
585227650Skevlo	if (vcp->obj.co_flags & SMBV_UNICODE) {
586227650Skevlo		vcp->vc_toserver = vcp->vc_ucs_toserver;
587227650Skevlo		vcp->vc_tolocal = vcp->vc_ucs_tolocal;
588227650Skevlo		vcp->vc_hflags2 |= SMB_FLAGS2_UNICODE;
589227650Skevlo	}
59075374Sbpbad:
59175374Sbp	if (encpass)
59275374Sbp		free(encpass, M_SMBTEMP);
59375374Sbp	if (pbuf)
59475374Sbp		free(pbuf, M_SMBTEMP);
59575374Sbp	smb_rq_done(rqp);
596103396Sbp	if (error && upper == 1)
597103396Sbp		goto again;
59875374Sbp	return error;
59975374Sbp}
60075374Sbp
60175374Sbpint
60275374Sbpsmb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
60375374Sbp{
60475374Sbp	struct smb_rq *rqp;
60575374Sbp	struct mbchain *mbp;
60675374Sbp	int error;
60775374Sbp
60875374Sbp	if (ssp->ss_tid == SMB_TID_UNKNOWN)
60975374Sbp		return 0;
61075374Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
61175374Sbp	if (error)
61275374Sbp		return error;
61375374Sbp	mbp = &rqp->sr_rq;
61475374Sbp	smb_rq_wstart(rqp);
61575374Sbp	smb_rq_wend(rqp);
61675374Sbp	smb_rq_bstart(rqp);
61775374Sbp	smb_rq_bend(rqp);
61875374Sbp	error = smb_rq_simple(rqp);
61975374Sbp	SMBSDEBUG("%d\n", error);
62075374Sbp	smb_rq_done(rqp);
62175374Sbp	ssp->ss_tid = SMB_TID_UNKNOWN;
62275374Sbp	return error;
62375374Sbp}
62475374Sbp
62575374Sbpstatic __inline int
626103395Sbpsmb_smb_readx(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
627103395Sbp	      struct uio *uio, struct smb_cred *scred)
628103395Sbp{
629103395Sbp	struct smb_rq *rqp;
630103395Sbp	struct mbchain *mbp;
631103395Sbp	struct mdchain *mdp;
632103395Sbp	u_int8_t wc;
633103395Sbp	int error;
634103395Sbp	u_int16_t residhi, residlo, off, doff;
635103395Sbp	u_int32_t resid;
636103395Sbp
637103395Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
638103395Sbp	if (error)
639103395Sbp		return error;
640103395Sbp	smb_rq_getrequest(rqp, &mbp);
641103395Sbp	smb_rq_wstart(rqp);
642103395Sbp	mb_put_uint8(mbp, 0xff);	/* no secondary command */
643103395Sbp	mb_put_uint8(mbp, 0);		/* MBZ */
644103395Sbp	mb_put_uint16le(mbp, 0);	/* offset to secondary */
645103395Sbp	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
646103395Sbp	mb_put_uint32le(mbp, uio->uio_offset);
647103395Sbp	*len = min(SSTOVC(ssp)->vc_rxmax, *len);
648103395Sbp	mb_put_uint16le(mbp, *len);	/* MaxCount */
649103395Sbp	mb_put_uint16le(mbp, *len);	/* MinCount (only indicates blocking) */
650103395Sbp	mb_put_uint32le(mbp, (unsigned)*len >> 16);	/* MaxCountHigh */
651103395Sbp	mb_put_uint16le(mbp, *len);	/* Remaining ("obsolete") */
652103395Sbp	mb_put_uint32le(mbp, uio->uio_offset >> 32);	/* OffsetHigh */
653103395Sbp	smb_rq_wend(rqp);
654103395Sbp	smb_rq_bstart(rqp);
655103395Sbp	smb_rq_bend(rqp);
656103395Sbp	do {
657103395Sbp		error = smb_rq_simple(rqp);
658103395Sbp		if (error)
659103395Sbp			break;
660103395Sbp		smb_rq_getreply(rqp, &mdp);
661103395Sbp		off = SMB_HDRLEN;
662103395Sbp		md_get_uint8(mdp, &wc);
663103395Sbp		off++;
664103395Sbp		if (wc != 12) {
665103395Sbp			error = EBADRPC;
666103395Sbp			break;
667103395Sbp		}
668103395Sbp		md_get_uint8(mdp, NULL);
669103395Sbp		off++;
670103395Sbp		md_get_uint8(mdp, NULL);
671103395Sbp		off++;
672103395Sbp		md_get_uint16le(mdp, NULL);
673103395Sbp		off += 2;
674103395Sbp		md_get_uint16le(mdp, NULL);
675103395Sbp		off += 2;
676103395Sbp		md_get_uint16le(mdp, NULL);	/* data compaction mode */
677103395Sbp		off += 2;
678103395Sbp		md_get_uint16le(mdp, NULL);
679103395Sbp		off += 2;
680103395Sbp		md_get_uint16le(mdp, &residlo);
681103395Sbp		off += 2;
682103395Sbp		md_get_uint16le(mdp, &doff);	/* data offset */
683103395Sbp		off += 2;
684103395Sbp		md_get_uint16le(mdp, &residhi);
685103395Sbp		off += 2;
686103395Sbp		resid = (residhi << 16) | residlo;
687103395Sbp		md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
688103395Sbp		off += 4*2;
689103395Sbp		md_get_uint16le(mdp, NULL);	/* ByteCount */
690103395Sbp		off += 2;
691103395Sbp		if (doff > off)	/* pad byte(s)? */
692103395Sbp			md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
693103395Sbp		if (resid == 0) {
694103395Sbp			*rresid = resid;
695103395Sbp			break;
696103395Sbp		}
697103395Sbp		error = md_get_uio(mdp, uio, resid);
698103395Sbp		if (error)
699103395Sbp			break;
700103395Sbp		*rresid = resid;
701103395Sbp	} while(0);
702103395Sbp	smb_rq_done(rqp);
703103395Sbp	return (error);
704103395Sbp}
705103395Sbp
706103395Sbpstatic __inline int
707103395Sbpsmb_smb_writex(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
708103395Sbp	struct uio *uio, struct smb_cred *scred)
709103395Sbp{
710103395Sbp	struct smb_rq *rqp;
711103395Sbp	struct mbchain *mbp;
712103395Sbp	struct mdchain *mdp;
713103395Sbp	int error;
714103395Sbp	u_int8_t wc;
715103395Sbp	u_int16_t resid;
716103395Sbp
717103395Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
718103395Sbp	if (error)
719103395Sbp		return (error);
720103395Sbp	smb_rq_getrequest(rqp, &mbp);
721103395Sbp	smb_rq_wstart(rqp);
722103395Sbp	mb_put_uint8(mbp, 0xff);	/* no secondary command */
723103395Sbp	mb_put_uint8(mbp, 0);		/* MBZ */
724103395Sbp	mb_put_uint16le(mbp, 0);	/* offset to secondary */
725103395Sbp	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
726103395Sbp	mb_put_uint32le(mbp, uio->uio_offset);
727103395Sbp	mb_put_uint32le(mbp, 0);	/* MBZ (timeout) */
728103395Sbp	mb_put_uint16le(mbp, 0);	/* !write-thru */
729103395Sbp	mb_put_uint16le(mbp, 0);
730103395Sbp	*len = min(SSTOVC(ssp)->vc_wxmax, *len);
731103395Sbp	mb_put_uint16le(mbp, (unsigned)*len >> 16);
732103395Sbp	mb_put_uint16le(mbp, *len);
733103395Sbp	mb_put_uint16le(mbp, 64);	/* data offset from header start */
734103395Sbp	mb_put_uint32le(mbp, uio->uio_offset >> 32);	/* OffsetHigh */
735103395Sbp	smb_rq_wend(rqp);
736103395Sbp	smb_rq_bstart(rqp);
737103395Sbp	do {
738103395Sbp		mb_put_uint8(mbp, 0xee);	/* mimic xp pad byte! */
739103395Sbp		error = mb_put_uio(mbp, uio, *len);
740103395Sbp		if (error)
741103395Sbp			break;
742103395Sbp		smb_rq_bend(rqp);
743103395Sbp		error = smb_rq_simple(rqp);
744103395Sbp		if (error)
745103395Sbp			break;
746103395Sbp		smb_rq_getreply(rqp, &mdp);
747103395Sbp		md_get_uint8(mdp, &wc);
748103395Sbp		if (wc != 6) {
749103395Sbp			error = EBADRPC;
750103395Sbp			break;
751103395Sbp		}
752103395Sbp		md_get_uint8(mdp, NULL);
753103395Sbp		md_get_uint8(mdp, NULL);
754103395Sbp		md_get_uint16le(mdp, NULL);
755103395Sbp		md_get_uint16le(mdp, &resid);
756103395Sbp		*rresid = resid;
757103395Sbp	} while(0);
758103395Sbp
759103395Sbp	smb_rq_done(rqp);
760103395Sbp	return (error);
761103395Sbp}
762103395Sbp
763103395Sbpstatic __inline int
76475374Sbpsmb_smb_read(struct smb_share *ssp, u_int16_t fid,
76575374Sbp	int *len, int *rresid, struct uio *uio, struct smb_cred *scred)
76675374Sbp{
76775374Sbp	struct smb_rq *rqp;
76875374Sbp	struct mbchain *mbp;
76975374Sbp	struct mdchain *mdp;
77075374Sbp	u_int16_t resid, bc;
77175374Sbp	u_int8_t wc;
77275374Sbp	int error, rlen, blksz;
77375374Sbp
774103395Sbp	if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_READX)
775103395Sbp		return (smb_smb_readx(ssp, fid, len, rresid, uio, scred));
776103395Sbp
77775374Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
77875374Sbp	if (error)
77975374Sbp		return error;
78075374Sbp
78175374Sbp	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
78275374Sbp	rlen = *len = min(blksz, *len);
78375374Sbp
78475374Sbp	smb_rq_getrequest(rqp, &mbp);
78575374Sbp	smb_rq_wstart(rqp);
78675374Sbp	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
78775374Sbp	mb_put_uint16le(mbp, rlen);
78875374Sbp	mb_put_uint32le(mbp, uio->uio_offset);
78975374Sbp	mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
79075374Sbp	smb_rq_wend(rqp);
79175374Sbp	smb_rq_bstart(rqp);
79275374Sbp	smb_rq_bend(rqp);
79375374Sbp	do {
79475374Sbp		error = smb_rq_simple(rqp);
79575374Sbp		if (error)
79675374Sbp			break;
79775374Sbp		smb_rq_getreply(rqp, &mdp);
79875374Sbp		md_get_uint8(mdp, &wc);
79975374Sbp		if (wc != 5) {
80075374Sbp			error = EBADRPC;
80175374Sbp			break;
80275374Sbp		}
80375374Sbp		md_get_uint16le(mdp, &resid);
80475374Sbp		md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
80575374Sbp		md_get_uint16le(mdp, &bc);
80675374Sbp		md_get_uint8(mdp, NULL);		/* ignore buffer type */
80775374Sbp		md_get_uint16le(mdp, &resid);
80875374Sbp		if (resid == 0) {
80975374Sbp			*rresid = resid;
81075374Sbp			break;
81175374Sbp		}
81275374Sbp		error = md_get_uio(mdp, uio, resid);
81375374Sbp		if (error)
81475374Sbp			break;
81575374Sbp		*rresid = resid;
81675374Sbp	} while(0);
81775374Sbp	smb_rq_done(rqp);
81875374Sbp	return error;
81975374Sbp}
82075374Sbp
82175374Sbpint
82275374Sbpsmb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
82375374Sbp	struct smb_cred *scred)
82475374Sbp{
82575374Sbp	int tsize, len, resid;
82675374Sbp	int error = 0;
82775374Sbp
82875374Sbp	tsize = uio->uio_resid;
82975374Sbp	while (tsize > 0) {
830170804Smjacob		resid = 0;
83175374Sbp		len = tsize;
83275374Sbp		error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
83375374Sbp		if (error)
83475374Sbp			break;
83575374Sbp		tsize -= resid;
83675374Sbp		if (resid < len)
83775374Sbp			break;
83875374Sbp	}
83975374Sbp	return error;
84075374Sbp}
84175374Sbp
84275374Sbpstatic __inline int
84375374Sbpsmb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
84475374Sbp	struct uio *uio, struct smb_cred *scred)
84575374Sbp{
84675374Sbp	struct smb_rq *rqp;
84775374Sbp	struct mbchain *mbp;
84875374Sbp	struct mdchain *mdp;
84975374Sbp	u_int16_t resid;
85075374Sbp	u_int8_t wc;
85175374Sbp	int error, blksz;
85275374Sbp
853103395Sbp	if (*len && SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)
854103395Sbp		return (smb_smb_writex(ssp, fid, len, rresid, uio, scred));
855103395Sbp
85675374Sbp	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
85775374Sbp	if (blksz > 0xffff)
85875374Sbp		blksz = 0xffff;
85975374Sbp
86075374Sbp	resid = *len = min(blksz, *len);
86175374Sbp
86275374Sbp	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
86375374Sbp	if (error)
86475374Sbp		return error;
86575374Sbp	smb_rq_getrequest(rqp, &mbp);
86675374Sbp	smb_rq_wstart(rqp);
86775374Sbp	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
86875374Sbp	mb_put_uint16le(mbp, resid);
86975374Sbp	mb_put_uint32le(mbp, uio->uio_offset);
87075374Sbp	mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
87175374Sbp	smb_rq_wend(rqp);
87275374Sbp	smb_rq_bstart(rqp);
87375374Sbp	mb_put_uint8(mbp, SMB_DT_DATA);
87475374Sbp	mb_put_uint16le(mbp, resid);
87575374Sbp	do {
87675374Sbp		error = mb_put_uio(mbp, uio, resid);
87775374Sbp		if (error)
87875374Sbp			break;
87975374Sbp		smb_rq_bend(rqp);
88075374Sbp		error = smb_rq_simple(rqp);
88175374Sbp		if (error)
88275374Sbp			break;
88375374Sbp		smb_rq_getreply(rqp, &mdp);
88475374Sbp		md_get_uint8(mdp, &wc);
88575374Sbp		if (wc != 1) {
88675374Sbp			error = EBADRPC;
88775374Sbp			break;
88875374Sbp		}
88975374Sbp		md_get_uint16le(mdp, &resid);
89075374Sbp		*rresid = resid;
89175374Sbp	} while(0);
89275374Sbp	smb_rq_done(rqp);
89375374Sbp	return error;
89475374Sbp}
89575374Sbp
89675374Sbpint
89775374Sbpsmb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
89875374Sbp	struct smb_cred *scred)
89975374Sbp{
90075374Sbp	int error = 0, len, tsize, resid;
90175374Sbp	struct uio olduio;
90275374Sbp
90375374Sbp	tsize = uio->uio_resid;
90475374Sbp	olduio = *uio;
90575374Sbp	while (tsize > 0) {
906170804Smjacob		resid = 0;
90775374Sbp		len = tsize;
90875374Sbp		error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
90975374Sbp		if (error)
91075374Sbp			break;
91175374Sbp		if (resid < len) {
91275374Sbp			error = EIO;
91375374Sbp			break;
91475374Sbp		}
91575374Sbp		tsize -= resid;
91675374Sbp	}
91775374Sbp	if (error) {
91891023Sbp		/*
91991023Sbp		 * Errors can happen on the copyin, the rpc, etc.  So they
92091023Sbp		 * imply resid is unreliable.  The only safe thing is
92191023Sbp		 * to pretend zero bytes made it.  We needn't restore the
92291023Sbp		 * iovs because callers don't depend on them in error
92391023Sbp		 * paths - uio_resid and uio_offset are what matter.
92491023Sbp		 */
92575374Sbp		*uio = olduio;
92675374Sbp	}
92775374Sbp	return error;
92875374Sbp}
92975374Sbp
93075374Sbpint
93175374Sbpsmb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
93275374Sbp{
93375374Sbp	struct smb_rq *rqp;
93475374Sbp	struct mbchain *mbp;
93575374Sbp	int error;
93675374Sbp
93775374Sbp	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
93875374Sbp	if (error)
93975374Sbp		return error;
94075374Sbp	mbp = &rqp->sr_rq;
94175374Sbp	smb_rq_wstart(rqp);
94275374Sbp	mb_put_uint16le(mbp, 1);
94375374Sbp	smb_rq_wend(rqp);
94475374Sbp	smb_rq_bstart(rqp);
94575374Sbp	mb_put_uint32le(mbp, 0);
94675374Sbp	smb_rq_bend(rqp);
94775374Sbp	error = smb_rq_simple(rqp);
94875374Sbp	SMBSDEBUG("%d\n", error);
94975374Sbp	smb_rq_done(rqp);
95075374Sbp	return error;
95175374Sbp}
952