smb_smb.c revision 116189
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
33/*
34 * various SMB requests. Most of the routines merely packs data into mbufs.
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/sys/netsmb/smb_smb.c 116189 2003-06-11 05:37:42Z obrien $");
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/malloc.h>
44#include <sys/proc.h>
45#include <sys/lock.h>
46#include <sys/sysctl.h>
47#include <sys/socket.h>
48#include <sys/uio.h>
49
50#include <sys/iconv.h>
51
52#include <netsmb/smb.h>
53#include <netsmb/smb_subr.h>
54#include <netsmb/smb_rq.h>
55#include <netsmb/smb_conn.h>
56#include <netsmb/smb_tran.h>
57
58struct smb_dialect {
59	int		d_id;
60	const char *	d_name;
61};
62
63static struct smb_dialect smb_dialects[] = {
64	{SMB_DIALECT_CORE,	"PC NETWORK PROGRAM 1.0"},
65	{SMB_DIALECT_COREPLUS,	"MICROSOFT NETWORKS 1.03"},
66	{SMB_DIALECT_LANMAN1_0,	"MICROSOFT NETWORKS 3.0"},
67	{SMB_DIALECT_LANMAN1_0,	"LANMAN1.0"},
68	{SMB_DIALECT_LANMAN2_0,	"LM1.2X002"},
69	{SMB_DIALECT_LANMAN2_0,	"Samba"},
70	{SMB_DIALECT_NTLM0_12,	"NT LANMAN 1.0"},
71	{SMB_DIALECT_NTLM0_12,	"NT LM 0.12"},
72	{-1,			NULL}
73};
74
75#define	SMB_DIALECT_MAX	(sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2)
76
77static u_int32_t
78smb_vc_maxread(struct smb_vc *vcp)
79{
80	/*
81	 * Specs say up to 64k data bytes, but Windows traffic
82	 * uses 60k... no doubt for some good reason.
83	 */
84	if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX)
85		return (60*1024);
86	else
87		return (vcp->vc_sopt.sv_maxtx);
88}
89
90static u_int32_t
91smb_vc_maxwrite(struct smb_vc *vcp)
92{
93	/*
94	 * Specs say up to 64k data bytes, but Windows traffic
95	 * uses 60k... probably for some good reason.
96	 */
97	if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)
98		return (60*1024);
99	else
100		return (vcp->vc_sopt.sv_maxtx);
101}
102
103static int
104smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
105{
106	if (scred->scr_td->td_proc == vcp->vc_iod->iod_p)
107		return 0;
108	SMBERROR("wrong function called(%s)\n", name);
109	return EINVAL;
110}
111
112int
113smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
114{
115	struct smb_dialect *dp;
116	struct smb_sopt *sp = NULL;
117	struct smb_rq *rqp;
118	struct mbchain *mbp;
119	struct mdchain *mdp;
120	u_int8_t wc, stime[8], sblen;
121	u_int16_t dindex, tw, tw1, swlen, bc;
122	int error, maxqsz;
123
124	if (smb_smb_nomux(vcp, scred, __func__) != 0)
125		return EINVAL;
126	vcp->vc_hflags = 0;
127	vcp->vc_hflags2 = 0;
128	vcp->obj.co_flags &= ~(SMBV_ENCRYPT);
129	sp = &vcp->vc_sopt;
130	bzero(sp, sizeof(struct smb_sopt));
131	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
132	if (error)
133		return error;
134	smb_rq_getrequest(rqp, &mbp);
135	smb_rq_wstart(rqp);
136	smb_rq_wend(rqp);
137	smb_rq_bstart(rqp);
138	for(dp = smb_dialects; dp->d_id != -1; dp++) {
139		mb_put_uint8(mbp, SMB_DT_DIALECT);
140		smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE);
141	}
142	smb_rq_bend(rqp);
143	error = smb_rq_simple(rqp);
144	SMBSDEBUG("%d\n", error);
145	if (error)
146		goto bad;
147	smb_rq_getreply(rqp, &mdp);
148	do {
149		error = md_get_uint8(mdp, &wc);
150		if (error)
151			break;
152		error = md_get_uint16le(mdp, &dindex);
153		if (error)
154			break;
155		if (dindex > 7) {
156			SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex);
157			error = EBADRPC;
158			break;
159		}
160		dp = smb_dialects + dindex;
161		sp->sv_proto = dp->d_id;
162		SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc);
163		error = EBADRPC;
164		if (dp->d_id >= SMB_DIALECT_NTLM0_12) {
165			if (wc != 17)
166				break;
167			md_get_uint8(mdp, &sp->sv_sm);
168			md_get_uint16le(mdp, &sp->sv_maxmux);
169			md_get_uint16le(mdp, &sp->sv_maxvcs);
170			md_get_uint32le(mdp, &sp->sv_maxtx);
171			md_get_uint32le(mdp, &sp->sv_maxraw);
172			md_get_uint32le(mdp, &sp->sv_skey);
173			md_get_uint32le(mdp, &sp->sv_caps);
174			md_get_mem(mdp, stime, 8, MB_MSYSTEM);
175			md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
176			md_get_uint8(mdp, &sblen);
177			if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
178				if (sblen != SMB_MAXCHALLENGELEN) {
179					SMBERROR("Unexpected length of security blob (%d)\n", sblen);
180					break;
181				}
182				error = md_get_uint16(mdp, &bc);
183				if (error)
184					break;
185				if (sp->sv_caps & SMB_CAP_EXT_SECURITY)
186					md_get_mem(mdp, NULL, 16, MB_MSYSTEM);
187				error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM);
188				if (error)
189					break;
190				vcp->vc_chlen = sblen;
191				vcp->obj.co_flags |= SMBV_ENCRYPT;
192			}
193			vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
194			if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
195			    sp->sv_maxtx < 4096 &&
196			    (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) {
197				vcp->obj.co_flags |= SMBV_WIN95;
198				SMBSDEBUG("Win95 detected\n");
199			}
200		} else if (dp->d_id > SMB_DIALECT_CORE) {
201			md_get_uint16le(mdp, &tw);
202			sp->sv_sm = tw;
203			md_get_uint16le(mdp, &tw);
204			sp->sv_maxtx = tw;
205			md_get_uint16le(mdp, &sp->sv_maxmux);
206			md_get_uint16le(mdp, &sp->sv_maxvcs);
207			md_get_uint16le(mdp, &tw);	/* rawmode */
208			md_get_uint32le(mdp, &sp->sv_skey);
209			if (wc == 13) {		/* >= LANMAN1 */
210				md_get_uint16(mdp, &tw);		/* time */
211				md_get_uint16(mdp, &tw1);		/* date */
212				md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
213				md_get_uint16le(mdp, &swlen);
214				if (swlen > SMB_MAXCHALLENGELEN)
215					break;
216				md_get_uint16(mdp, NULL);	/* mbz */
217				if (md_get_uint16(mdp, &bc) != 0)
218					break;
219				if (bc < swlen)
220					break;
221				if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
222					error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM);
223					if (error)
224						break;
225					vcp->vc_chlen = swlen;
226					vcp->obj.co_flags |= SMBV_ENCRYPT;
227				}
228			}
229			vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
230		} else {	/* an old CORE protocol */
231			sp->sv_maxmux = 1;
232		}
233		error = 0;
234	} while (0);
235	if (error == 0) {
236		vcp->vc_maxvcs = sp->sv_maxvcs;
237		if (vcp->vc_maxvcs <= 1) {
238			if (vcp->vc_maxvcs == 0)
239				vcp->vc_maxvcs = 1;
240		}
241		if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff)
242			sp->sv_maxtx = 1024;
243		else
244			sp->sv_maxtx = min(sp->sv_maxtx,
245					   63*1024 + SMB_HDRLEN + 16);
246		SMB_TRAN_GETPARAM(vcp, SMBTP_RCVSZ, &maxqsz);
247		vcp->vc_rxmax = min(smb_vc_maxread(vcp), maxqsz - 1024);
248		SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz);
249		vcp->vc_wxmax = min(smb_vc_maxwrite(vcp), maxqsz - 1024);
250		vcp->vc_txmax = min(sp->sv_maxtx, maxqsz);
251		SMBSDEBUG("TZ = %d\n", sp->sv_tz);
252		SMBSDEBUG("CAPS = %x\n", sp->sv_caps);
253		SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux);
254		SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs);
255		SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw);
256		SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx);
257	}
258bad:
259	smb_rq_done(rqp);
260	return error;
261}
262
263int
264smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
265{
266	struct smb_rq *rqp;
267	struct mbchain *mbp;
268/*	u_int8_t wc;
269	u_int16_t tw, tw1;*/
270	smb_uniptr unipp, ntencpass = NULL;
271	char *pp, *up, *pbuf, *encpass;
272	int error, plen, uniplen, ulen, upper;
273
274	upper = 0;
275
276again:
277
278	vcp->vc_smbuid = SMB_UID_UNKNOWN;
279
280	if (smb_smb_nomux(vcp, scred, __func__) != 0)
281		return EINVAL;
282
283	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
284	if (error)
285		return error;
286	pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
287	encpass = malloc(24, M_SMBTEMP, M_WAITOK);
288	if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
289		/*
290		 * We try w/o uppercasing first so Samba mixed case
291		 * passwords work.  If that fails we come back and try
292		 * uppercasing to satisfy OS/2 and Windows for Workgroups.
293		 */
294		if (upper++) {
295			iconv_convstr(vcp->vc_toupper, pbuf,
296				      smb_vc_getpass(vcp)/*, SMB_MAXPASSWORDLEN*/);
297		} else {
298			strncpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN);
299			pbuf[SMB_MAXPASSWORDLEN] = '\0';
300		}
301		if (!SMB_UNICODE_STRINGS(vcp))
302			iconv_convstr(vcp->vc_toserver, pbuf, pbuf/*,
303				      SMB_MAXPASSWORDLEN*/);
304
305		if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
306			uniplen = plen = 24;
307			smb_encrypt(pbuf, vcp->vc_ch, encpass);
308			ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
309			if (SMB_UNICODE_STRINGS(vcp)) {
310				strncpy(pbuf, smb_vc_getpass(vcp),
311					SMB_MAXPASSWORDLEN);
312				pbuf[SMB_MAXPASSWORDLEN] = '\0';
313			} else
314				iconv_convstr(vcp->vc_toserver, pbuf,
315					      smb_vc_getpass(vcp)/*,
316					      SMB_MAXPASSWORDLEN*/);
317			smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass);
318			pp = encpass;
319			unipp = ntencpass;
320		} else {
321			plen = strlen(pbuf) + 1;
322			pp = pbuf;
323			uniplen = plen * 2;
324			ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
325			smb_strtouni(ntencpass, smb_vc_getpass(vcp));
326			plen--;
327
328			/*
329			 * The uniplen is zeroed because Samba cannot deal
330			 * with this 2nd cleartext password.  This Samba
331			 * "bug" is actually a workaround for problems in
332			 * Microsoft clients.
333			 */
334			uniplen = 0/*-= 2*/;
335			unipp = ntencpass;
336		}
337	} else {
338		/*
339		 * In the share security mode password will be used
340		 * only in the tree authentication
341		 */
342		 pp = "";
343		 plen = 1;
344		 unipp = &smb_unieol;
345		 uniplen = 0 /* sizeof(smb_unieol) */;
346	}
347	smb_rq_wstart(rqp);
348	mbp = &rqp->sr_rq;
349	up = vcp->vc_username;
350	ulen = strlen(up) + 1;
351	/*
352	 * If userid is null we are attempting anonymous browse login
353	 * so passwords must be zero length.
354	 */
355	if (ulen == 1)
356		plen = uniplen = 0;
357	mb_put_uint8(mbp, 0xff);
358	mb_put_uint8(mbp, 0);
359	mb_put_uint16le(mbp, 0);
360	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
361	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
362	mb_put_uint16le(mbp, vcp->vc_number);
363	mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
364	mb_put_uint16le(mbp, plen);
365	if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
366		mb_put_uint32le(mbp, 0);
367		smb_rq_wend(rqp);
368		smb_rq_bstart(rqp);
369		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
370		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);
371	} else {
372		mb_put_uint16le(mbp, uniplen);
373		mb_put_uint32le(mbp, 0);		/* reserved */
374		mb_put_uint32le(mbp, vcp->obj.co_flags & SMBV_UNICODE ?
375				     SMB_CAP_UNICODE : 0);
376		smb_rq_wend(rqp);
377		smb_rq_bstart(rqp);
378		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
379		mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
380		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);		/* AccountName */
381		smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE);	/* PrimaryDomain */
382		smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE);	/* Client's OS */
383		smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE);		/* Client name */
384	}
385	smb_rq_bend(rqp);
386	if (ntencpass)
387		free(ntencpass, M_SMBTEMP);
388	error = smb_rq_simple(rqp);
389	SMBSDEBUG("%d\n", error);
390	if (error) {
391		if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess)
392			error = EAUTH;
393		goto bad;
394	}
395	vcp->vc_smbuid = rqp->sr_rpuid;
396bad:
397	free(encpass, M_SMBTEMP);
398	free(pbuf, M_SMBTEMP);
399	smb_rq_done(rqp);
400	if (error && upper == 1 && vcp->vc_sopt.sv_sm & SMB_SM_USER)
401		goto again;
402	return error;
403}
404
405int
406smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
407{
408	struct smb_rq *rqp;
409	struct mbchain *mbp;
410	int error;
411
412	if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
413		return 0;
414
415	if (smb_smb_nomux(vcp, scred, __func__) != 0)
416		return EINVAL;
417
418	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
419	if (error)
420		return error;
421	mbp = &rqp->sr_rq;
422	smb_rq_wstart(rqp);
423	mb_put_uint8(mbp, 0xff);
424	mb_put_uint8(mbp, 0);
425	mb_put_uint16le(mbp, 0);
426	smb_rq_wend(rqp);
427	smb_rq_bstart(rqp);
428	smb_rq_bend(rqp);
429	error = smb_rq_simple(rqp);
430	SMBSDEBUG("%d\n", error);
431	smb_rq_done(rqp);
432	return error;
433}
434
435static char smb_any_share[] = "?????";
436
437static char *
438smb_share_typename(int stype)
439{
440	char *pp;
441
442	switch (stype) {
443	    case SMB_ST_DISK:
444		pp = "A:";
445		break;
446	    case SMB_ST_PRINTER:
447		pp = smb_any_share;		/* can't use LPT: here... */
448		break;
449	    case SMB_ST_PIPE:
450		pp = "IPC";
451		break;
452	    case SMB_ST_COMM:
453		pp = "COMM";
454		break;
455	    case SMB_ST_ANY:
456	    default:
457		pp = smb_any_share;
458		break;
459	}
460	return pp;
461}
462
463int
464smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
465{
466	struct smb_vc *vcp;
467	struct smb_rq rq, *rqp = &rq;
468	struct mbchain *mbp;
469	char *pp, *pbuf, *encpass;
470	int error, plen, caseopt, upper;
471
472	upper = 0;
473
474again:
475
476#if 0
477	/* Disable Unicode for SMB_COM_TREE_CONNECT_ANDX requests */
478	if (SSTOVC(ssp)->vc_hflags2 & SMB_FLAGS2_UNICODE) {
479		vcp = SSTOVC(ssp);
480		if (vcp->vc_toserver) {
481			iconv_close(vcp->vc_toserver);
482			/* Use NULL until UTF-8 -> ASCII works */
483			vcp->vc_toserver = NULL;
484		}
485		if (vcp->vc_tolocal) {
486			iconv_close(vcp->vc_tolocal);
487			/* Use NULL until ASCII -> UTF-8 works*/
488			vcp->vc_tolocal = NULL;
489		}
490		vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE;
491	}
492#endif
493
494	ssp->ss_tid = SMB_TID_UNKNOWN;
495	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp);
496	if (error)
497		return error;
498	vcp = rqp->sr_vc;
499	caseopt = SMB_CS_NONE;
500	if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
501		plen = 1;
502		pp = "";
503		pbuf = NULL;
504		encpass = NULL;
505	} else {
506		pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
507		encpass = malloc(24, M_SMBTEMP, M_WAITOK);
508		/*
509		 * We try w/o uppercasing first so Samba mixed case
510		 * passwords work.  If that fails we come back and try
511		 * uppercasing to satisfy OS/2 and Windows for Workgroups.
512		 */
513		if (upper++) {
514			iconv_convstr(vcp->vc_toupper, pbuf,
515				      smb_share_getpass(ssp)/*,
516				      SMB_MAXPASSWORDLEN*/);
517		} else {
518			strncpy(pbuf, smb_share_getpass(ssp),
519				SMB_MAXPASSWORDLEN);
520			pbuf[SMB_MAXPASSWORDLEN] = '\0';
521		}
522		if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
523			plen = 24;
524			smb_encrypt(pbuf, vcp->vc_ch, encpass);
525			pp = encpass;
526		} else {
527			plen = strlen(pbuf) + 1;
528			pp = pbuf;
529		}
530	}
531	mbp = &rqp->sr_rq;
532	smb_rq_wstart(rqp);
533	mb_put_uint8(mbp, 0xff);
534	mb_put_uint8(mbp, 0);
535	mb_put_uint16le(mbp, 0);
536	mb_put_uint16le(mbp, 0);		/* Flags */
537	mb_put_uint16le(mbp, plen);
538	smb_rq_wend(rqp);
539	smb_rq_bstart(rqp);
540	mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
541	smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
542	pp = vcp->vc_srvname;
543	smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
544	smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
545	pp = ssp->ss_name;
546	smb_put_dstring(mbp, vcp, pp, caseopt);
547	pp = smb_share_typename(ssp->ss_type);
548	smb_put_dstring(mbp, vcp, pp, caseopt);
549	smb_rq_bend(rqp);
550	error = smb_rq_simple(rqp);
551	SMBSDEBUG("%d\n", error);
552	if (error)
553		goto bad;
554	ssp->ss_tid = rqp->sr_rptid;
555	ssp->ss_vcgenid = vcp->vc_genid;
556	ssp->ss_flags |= SMBS_CONNECTED;
557bad:
558	if (encpass)
559		free(encpass, M_SMBTEMP);
560	if (pbuf)
561		free(pbuf, M_SMBTEMP);
562	smb_rq_done(rqp);
563	if (error && upper == 1)
564		goto again;
565	return error;
566}
567
568int
569smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
570{
571	struct smb_rq *rqp;
572	struct mbchain *mbp;
573	int error;
574
575	if (ssp->ss_tid == SMB_TID_UNKNOWN)
576		return 0;
577	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
578	if (error)
579		return error;
580	mbp = &rqp->sr_rq;
581	smb_rq_wstart(rqp);
582	smb_rq_wend(rqp);
583	smb_rq_bstart(rqp);
584	smb_rq_bend(rqp);
585	error = smb_rq_simple(rqp);
586	SMBSDEBUG("%d\n", error);
587	smb_rq_done(rqp);
588	ssp->ss_tid = SMB_TID_UNKNOWN;
589	return error;
590}
591
592static __inline int
593smb_smb_readx(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
594	      struct uio *uio, struct smb_cred *scred)
595{
596	struct smb_rq *rqp;
597	struct mbchain *mbp;
598	struct mdchain *mdp;
599	u_int8_t wc;
600	int error;
601	u_int16_t residhi, residlo, off, doff;
602	u_int32_t resid;
603
604	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
605	if (error)
606		return error;
607	smb_rq_getrequest(rqp, &mbp);
608	smb_rq_wstart(rqp);
609	mb_put_uint8(mbp, 0xff);	/* no secondary command */
610	mb_put_uint8(mbp, 0);		/* MBZ */
611	mb_put_uint16le(mbp, 0);	/* offset to secondary */
612	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
613	mb_put_uint32le(mbp, uio->uio_offset);
614	*len = min(SSTOVC(ssp)->vc_rxmax, *len);
615	mb_put_uint16le(mbp, *len);	/* MaxCount */
616	mb_put_uint16le(mbp, *len);	/* MinCount (only indicates blocking) */
617	mb_put_uint32le(mbp, (unsigned)*len >> 16);	/* MaxCountHigh */
618	mb_put_uint16le(mbp, *len);	/* Remaining ("obsolete") */
619	mb_put_uint32le(mbp, uio->uio_offset >> 32);	/* OffsetHigh */
620	smb_rq_wend(rqp);
621	smb_rq_bstart(rqp);
622	smb_rq_bend(rqp);
623	do {
624		error = smb_rq_simple(rqp);
625		if (error)
626			break;
627		smb_rq_getreply(rqp, &mdp);
628		off = SMB_HDRLEN;
629		md_get_uint8(mdp, &wc);
630		off++;
631		if (wc != 12) {
632			error = EBADRPC;
633			break;
634		}
635		md_get_uint8(mdp, NULL);
636		off++;
637		md_get_uint8(mdp, NULL);
638		off++;
639		md_get_uint16le(mdp, NULL);
640		off += 2;
641		md_get_uint16le(mdp, NULL);
642		off += 2;
643		md_get_uint16le(mdp, NULL);	/* data compaction mode */
644		off += 2;
645		md_get_uint16le(mdp, NULL);
646		off += 2;
647		md_get_uint16le(mdp, &residlo);
648		off += 2;
649		md_get_uint16le(mdp, &doff);	/* data offset */
650		off += 2;
651		md_get_uint16le(mdp, &residhi);
652		off += 2;
653		resid = (residhi << 16) | residlo;
654		md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
655		off += 4*2;
656		md_get_uint16le(mdp, NULL);	/* ByteCount */
657		off += 2;
658		if (doff > off)	/* pad byte(s)? */
659			md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
660		if (resid == 0) {
661			*rresid = resid;
662			break;
663		}
664		error = md_get_uio(mdp, uio, resid);
665		if (error)
666			break;
667		*rresid = resid;
668	} while(0);
669	smb_rq_done(rqp);
670	return (error);
671}
672
673static __inline int
674smb_smb_writex(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
675	struct uio *uio, struct smb_cred *scred)
676{
677	struct smb_rq *rqp;
678	struct mbchain *mbp;
679	struct mdchain *mdp;
680	int error;
681	u_int8_t wc;
682	u_int16_t resid;
683
684	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
685	if (error)
686		return (error);
687	smb_rq_getrequest(rqp, &mbp);
688	smb_rq_wstart(rqp);
689	mb_put_uint8(mbp, 0xff);	/* no secondary command */
690	mb_put_uint8(mbp, 0);		/* MBZ */
691	mb_put_uint16le(mbp, 0);	/* offset to secondary */
692	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
693	mb_put_uint32le(mbp, uio->uio_offset);
694	mb_put_uint32le(mbp, 0);	/* MBZ (timeout) */
695	mb_put_uint16le(mbp, 0);	/* !write-thru */
696	mb_put_uint16le(mbp, 0);
697	*len = min(SSTOVC(ssp)->vc_wxmax, *len);
698	mb_put_uint16le(mbp, (unsigned)*len >> 16);
699	mb_put_uint16le(mbp, *len);
700	mb_put_uint16le(mbp, 64);	/* data offset from header start */
701	mb_put_uint32le(mbp, uio->uio_offset >> 32);	/* OffsetHigh */
702	smb_rq_wend(rqp);
703	smb_rq_bstart(rqp);
704	do {
705		mb_put_uint8(mbp, 0xee);	/* mimic xp pad byte! */
706		error = mb_put_uio(mbp, uio, *len);
707		if (error)
708			break;
709		smb_rq_bend(rqp);
710		error = smb_rq_simple(rqp);
711		if (error)
712			break;
713		smb_rq_getreply(rqp, &mdp);
714		md_get_uint8(mdp, &wc);
715		if (wc != 6) {
716			error = EBADRPC;
717			break;
718		}
719		md_get_uint8(mdp, NULL);
720		md_get_uint8(mdp, NULL);
721		md_get_uint16le(mdp, NULL);
722		md_get_uint16le(mdp, &resid);
723		*rresid = resid;
724	} while(0);
725
726	smb_rq_done(rqp);
727	return (error);
728}
729
730static __inline int
731smb_smb_read(struct smb_share *ssp, u_int16_t fid,
732	int *len, int *rresid, struct uio *uio, struct smb_cred *scred)
733{
734	struct smb_rq *rqp;
735	struct mbchain *mbp;
736	struct mdchain *mdp;
737	u_int16_t resid, bc;
738	u_int8_t wc;
739	int error, rlen, blksz;
740
741	if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_READX)
742		return (smb_smb_readx(ssp, fid, len, rresid, uio, scred));
743
744	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
745	if (error)
746		return error;
747
748	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
749	rlen = *len = min(blksz, *len);
750
751	smb_rq_getrequest(rqp, &mbp);
752	smb_rq_wstart(rqp);
753	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
754	mb_put_uint16le(mbp, rlen);
755	mb_put_uint32le(mbp, uio->uio_offset);
756	mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
757	smb_rq_wend(rqp);
758	smb_rq_bstart(rqp);
759	smb_rq_bend(rqp);
760	do {
761		error = smb_rq_simple(rqp);
762		if (error)
763			break;
764		smb_rq_getreply(rqp, &mdp);
765		md_get_uint8(mdp, &wc);
766		if (wc != 5) {
767			error = EBADRPC;
768			break;
769		}
770		md_get_uint16le(mdp, &resid);
771		md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
772		md_get_uint16le(mdp, &bc);
773		md_get_uint8(mdp, NULL);		/* ignore buffer type */
774		md_get_uint16le(mdp, &resid);
775		if (resid == 0) {
776			*rresid = resid;
777			break;
778		}
779		error = md_get_uio(mdp, uio, resid);
780		if (error)
781			break;
782		*rresid = resid;
783	} while(0);
784	smb_rq_done(rqp);
785	return error;
786}
787
788int
789smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
790	struct smb_cred *scred)
791{
792	int tsize, len, resid;
793	int error = 0;
794
795	tsize = uio->uio_resid;
796	while (tsize > 0) {
797		len = tsize;
798		error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
799		if (error)
800			break;
801		tsize -= resid;
802		if (resid < len)
803			break;
804	}
805	return error;
806}
807
808static __inline int
809smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
810	struct uio *uio, struct smb_cred *scred)
811{
812	struct smb_rq *rqp;
813	struct mbchain *mbp;
814	struct mdchain *mdp;
815	u_int16_t resid;
816	u_int8_t wc;
817	int error, blksz;
818
819	if (*len && SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)
820		return (smb_smb_writex(ssp, fid, len, rresid, uio, scred));
821
822	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
823	if (blksz > 0xffff)
824		blksz = 0xffff;
825
826	resid = *len = min(blksz, *len);
827
828	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
829	if (error)
830		return error;
831	smb_rq_getrequest(rqp, &mbp);
832	smb_rq_wstart(rqp);
833	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
834	mb_put_uint16le(mbp, resid);
835	mb_put_uint32le(mbp, uio->uio_offset);
836	mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
837	smb_rq_wend(rqp);
838	smb_rq_bstart(rqp);
839	mb_put_uint8(mbp, SMB_DT_DATA);
840	mb_put_uint16le(mbp, resid);
841	do {
842		error = mb_put_uio(mbp, uio, resid);
843		if (error)
844			break;
845		smb_rq_bend(rqp);
846		error = smb_rq_simple(rqp);
847		if (error)
848			break;
849		smb_rq_getreply(rqp, &mdp);
850		md_get_uint8(mdp, &wc);
851		if (wc != 1) {
852			error = EBADRPC;
853			break;
854		}
855		md_get_uint16le(mdp, &resid);
856		*rresid = resid;
857	} while(0);
858	smb_rq_done(rqp);
859	return error;
860}
861
862int
863smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
864	struct smb_cred *scred)
865{
866	int error = 0, len, tsize, resid;
867	struct uio olduio;
868
869	tsize = uio->uio_resid;
870	olduio = *uio;
871	while (tsize > 0) {
872		len = tsize;
873		error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
874		if (error)
875			break;
876		if (resid < len) {
877			error = EIO;
878			break;
879		}
880		tsize -= resid;
881	}
882	if (error) {
883		/*
884		 * Errors can happen on the copyin, the rpc, etc.  So they
885		 * imply resid is unreliable.  The only safe thing is
886		 * to pretend zero bytes made it.  We needn't restore the
887		 * iovs because callers don't depend on them in error
888		 * paths - uio_resid and uio_offset are what matter.
889		 */
890		*uio = olduio;
891	}
892	return error;
893}
894
895int
896smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
897{
898	struct smb_rq *rqp;
899	struct mbchain *mbp;
900	int error;
901
902	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
903	if (error)
904		return error;
905	mbp = &rqp->sr_rq;
906	smb_rq_wstart(rqp);
907	mb_put_uint16le(mbp, 1);
908	smb_rq_wend(rqp);
909	smb_rq_bstart(rqp);
910	mb_put_uint32le(mbp, 0);
911	smb_rq_bend(rqp);
912	error = smb_rq_simple(rqp);
913	SMBSDEBUG("%d\n", error);
914	smb_rq_done(rqp);
915	return error;
916}
917