xref: /dragonfly/sys/netproto/smb/smb_smb.c (revision a563ca70)
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  * $FreeBSD: src/sys/netsmb/smb_smb.c,v 1.1.2.3 2002/12/14 14:44:19 fjoe Exp $
33  * $DragonFly: src/sys/netproto/smb/smb_smb.c,v 1.7 2007/08/21 17:26:47 dillon Exp $
34  */
35 /*
36  * various SMB requests. Most of the routines merely packs data into mbufs.
37  */
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/proc.h>
43 #include <sys/lock.h>
44 #include <sys/sysctl.h>
45 #include <sys/socket.h>
46 #include <sys/uio.h>
47 
48 #include <sys/iconv.h>
49 #include <machine/limits.h>
50 
51 #include "smb.h"
52 #include "smb_subr.h"
53 #include "smb_rq.h"
54 #include "smb_conn.h"
55 #include "smb_tran.h"
56 
57 struct smb_dialect {
58 	int		d_id;
59 	const char *	d_name;
60 };
61 
62 static struct smb_dialect smb_dialects[] = {
63 	{SMB_DIALECT_CORE,	"PC NETWORK PROGRAM 1.0"},
64 	{SMB_DIALECT_COREPLUS,	"MICROSOFT NETWORKS 1.03"},
65 	{SMB_DIALECT_LANMAN1_0,	"MICROSOFT NETWORKS 3.0"},
66 	{SMB_DIALECT_LANMAN1_0,	"LANMAN1.0"},
67 	{SMB_DIALECT_LANMAN2_0,	"LM1.2X002"},
68 	{SMB_DIALECT_LANMAN2_0,	"Samba"},
69 	{SMB_DIALECT_NTLM0_12,	"NT LANMAN 1.0"},
70 	{SMB_DIALECT_NTLM0_12,	"NT LM 0.12"},
71 	{-1,			NULL}
72 };
73 
74 #define	SMB_DIALECT_MAX	(sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2)
75 
76 static int
77 smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
78 {
79 	if (scred->scr_td == vcp->vc_iod->iod_td)
80 		return 0;
81 	SMBERROR("wrong function called(%s)\n", name);
82 	return EINVAL;
83 }
84 
85 int
86 smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
87 {
88 	struct smb_dialect *dp;
89 	struct smb_sopt *sp = NULL;
90 	struct smb_rq *rqp;
91 	struct mbchain *mbp;
92 	struct mdchain *mdp;
93 	u_int8_t wc, stime[8], sblen;
94 	u_int16_t dindex, tw, tw1, swlen, bc;
95 	int error, maxqsz;
96 
97 	if (smb_smb_nomux(vcp, scred, __func__) != 0)
98 		return EINVAL;
99 	vcp->vc_hflags = 0;
100 	vcp->vc_hflags2 = 0;
101 	vcp->obj.co_flags &= ~(SMBV_ENCRYPT);
102 	sp = &vcp->vc_sopt;
103 	bzero(sp, sizeof(struct smb_sopt));
104 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
105 	if (error)
106 		return error;
107 	smb_rq_getrequest(rqp, &mbp);
108 	smb_rq_wstart(rqp);
109 	smb_rq_wend(rqp);
110 	smb_rq_bstart(rqp);
111 	for(dp = smb_dialects; dp->d_id != -1; dp++) {
112 		mb_put_uint8(mbp, SMB_DT_DIALECT);
113 		smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE);
114 	}
115 	smb_rq_bend(rqp);
116 	error = smb_rq_simple(rqp);
117 	SMBSDEBUG("%d\n", error);
118 	if (error)
119 		goto bad;
120 	smb_rq_getreply(rqp, &mdp);
121 	do {
122 		error = md_get_uint8(mdp, &wc);
123 		if (error)
124 			break;
125 		error = md_get_uint16le(mdp, &dindex);
126 		if (error)
127 			break;
128 		if (dindex > 7) {
129 			SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex);
130 			error = EBADRPC;
131 			break;
132 		}
133 		dp = smb_dialects + dindex;
134 		sp->sv_proto = dp->d_id;
135 		SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc);
136 		error = EBADRPC;
137 		if (dp->d_id >= SMB_DIALECT_NTLM0_12) {
138 			if (wc != 17)
139 				break;
140 			md_get_uint8(mdp, &sp->sv_sm);
141 			md_get_uint16le(mdp, &sp->sv_maxmux);
142 			md_get_uint16le(mdp, &sp->sv_maxvcs);
143 			md_get_uint32le(mdp, &sp->sv_maxtx);
144 			md_get_uint32le(mdp, &sp->sv_maxraw);
145 			md_get_uint32le(mdp, &sp->sv_skey);
146 			md_get_uint32le(mdp, &sp->sv_caps);
147 			md_get_mem(mdp, stime, 8, MB_MSYSTEM);
148 			md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
149 			md_get_uint8(mdp, &sblen);
150 			if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
151 				if (sblen != SMB_MAXCHALLENGELEN) {
152 					SMBERROR("Unexpected length of security blob (%d)\n", sblen);
153 					break;
154 				}
155 				error = md_get_uint16(mdp, &bc);
156 				if (error)
157 					break;
158 				if (sp->sv_caps & SMB_CAP_EXT_SECURITY)
159 					md_get_mem(mdp, NULL, 16, MB_MSYSTEM);
160 				error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM);
161 				if (error)
162 					break;
163 				vcp->vc_chlen = sblen;
164 				vcp->obj.co_flags |= SMBV_ENCRYPT;
165 			}
166 			vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
167 			if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
168 			    sp->sv_maxtx < 4096 &&
169 			    (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) {
170 				vcp->obj.co_flags |= SMBV_WIN95;
171 				SMBSDEBUG("Win95 detected\n");
172 			}
173 		} else if (dp->d_id > SMB_DIALECT_CORE) {
174 			md_get_uint16le(mdp, &tw);
175 			sp->sv_sm = tw;
176 			md_get_uint16le(mdp, &tw);
177 			sp->sv_maxtx = tw;
178 			md_get_uint16le(mdp, &sp->sv_maxmux);
179 			md_get_uint16le(mdp, &sp->sv_maxvcs);
180 			md_get_uint16le(mdp, &tw);	/* rawmode */
181 			md_get_uint32le(mdp, &sp->sv_skey);
182 			if (wc == 13) {		/* >= LANMAN1 */
183 				md_get_uint16(mdp, &tw);		/* time */
184 				md_get_uint16(mdp, &tw1);		/* date */
185 				md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
186 				md_get_uint16le(mdp, &swlen);
187 				if (swlen > SMB_MAXCHALLENGELEN)
188 					break;
189 				md_get_uint16(mdp, NULL);	/* mbz */
190 				if (md_get_uint16(mdp, &bc) != 0)
191 					break;
192 				if (bc < swlen)
193 					break;
194 				if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
195 					error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM);
196 					if (error)
197 						break;
198 					vcp->vc_chlen = swlen;
199 					vcp->obj.co_flags |= SMBV_ENCRYPT;
200 				}
201 			}
202 			vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
203 		} else {	/* an old CORE protocol */
204 			sp->sv_maxmux = 1;
205 		}
206 		error = 0;
207 	} while (0);
208 	if (error == 0) {
209 		vcp->vc_maxvcs = sp->sv_maxvcs;
210 		if (vcp->vc_maxvcs <= 1) {
211 			if (vcp->vc_maxvcs == 0)
212 				vcp->vc_maxvcs = 1;
213 		}
214 		if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff)
215 			sp->sv_maxtx = 1024;
216 		SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz);
217 		vcp->vc_txmax = min(sp->sv_maxtx, maxqsz);
218 		SMBSDEBUG("TZ = %d\n", sp->sv_tz);
219 		SMBSDEBUG("CAPS = %x\n", sp->sv_caps);
220 		SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux);
221 		SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs);
222 		SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw);
223 		SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx);
224 	}
225 bad:
226 	smb_rq_done(rqp);
227 	return error;
228 }
229 
230 int
231 smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
232 {
233 	struct smb_rq *rqp;
234 	struct mbchain *mbp;
235 /*	u_int8_t wc;
236 	u_int16_t tw, tw1;*/
237 	smb_uniptr unipp, ntencpass = NULL;
238 	char *pp, *up, *pbuf, *encpass;
239 	int error, plen, uniplen, ulen;
240 
241 	vcp->vc_smbuid = SMB_UID_UNKNOWN;
242 
243 	if (smb_smb_nomux(vcp, scred, __func__) != 0)
244 		return EINVAL;
245 
246 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
247 	if (error)
248 		return error;
249 	pbuf = kmalloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
250 	encpass = kmalloc(24, M_SMBTEMP, M_WAITOK);
251 	if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
252 		iconv_convstr(vcp->vc_toupper, pbuf, smb_vc_getpass(vcp));
253 		iconv_convstr(vcp->vc_toserver, pbuf, pbuf);
254 		if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
255 			uniplen = plen = 24;
256 			smb_encrypt(pbuf, vcp->vc_ch, encpass);
257 			ntencpass = kmalloc(uniplen, M_SMBTEMP, M_WAITOK);
258 			iconv_convstr(vcp->vc_toserver, pbuf, smb_vc_getpass(vcp));
259 			smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass);
260 			pp = encpass;
261 			unipp = ntencpass;
262 		} else {
263 			plen = strlen(pbuf) + 1;
264 			pp = pbuf;
265 			uniplen = plen * 2;
266 			ntencpass = kmalloc(uniplen, M_SMBTEMP, M_WAITOK);
267 			smb_strtouni(ntencpass, smb_vc_getpass(vcp));
268 			plen--;
269 
270 			/*
271 			 * The uniplen is zeroed because Samba cannot deal
272 			 * with this 2nd cleartext password.  This Samba
273 			 * "bug" is actually a workaround for problems in
274 			 * Microsoft clients.
275 			 */
276 			uniplen = 0/*-= 2*/;
277 			unipp = ntencpass;
278 		}
279 	} else {
280 		/*
281 		 * In the share security mode password will be used
282 		 * only in the tree authentication
283 		 */
284 		 pp = "";
285 		 plen = 1;
286 		 unipp = &smb_unieol;
287 		 uniplen = 0 /* sizeof(smb_unieol) */;
288 	}
289 	smb_rq_wstart(rqp);
290 	mbp = &rqp->sr_rq;
291 	up = vcp->vc_username;
292 	ulen = strlen(up) + 1;
293 	mb_put_uint8(mbp, 0xff);
294 	mb_put_uint8(mbp, 0);
295 	mb_put_uint16le(mbp, 0);
296 	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
297 	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
298 	mb_put_uint16le(mbp, vcp->vc_number);
299 	mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
300 	mb_put_uint16le(mbp, plen);
301 	if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
302 		mb_put_uint32le(mbp, 0);
303 		smb_rq_wend(rqp);
304 		smb_rq_bstart(rqp);
305 		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
306 		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);
307 	} else {
308 		mb_put_uint16le(mbp, uniplen);
309 		mb_put_uint32le(mbp, 0);		/* reserved */
310 		mb_put_uint32le(mbp, vcp->obj.co_flags & SMBV_UNICODE ?
311 				     SMB_CAP_UNICODE : 0);
312 		smb_rq_wend(rqp);
313 		smb_rq_bstart(rqp);
314 		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
315 		mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
316 		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);		/* AccountName */
317 		smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE);	/* PrimaryDomain */
318 		smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE);	/* Client's OS */
319 		smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE);		/* Client name */
320 	}
321 	smb_rq_bend(rqp);
322 	if (ntencpass)
323 		kfree(ntencpass, M_SMBTEMP);
324 	error = smb_rq_simple(rqp);
325 	SMBSDEBUG("%d\n", error);
326 	if (error) {
327 		if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess)
328 			error = EAUTH;
329 		goto bad;
330 	}
331 	vcp->vc_smbuid = rqp->sr_rpuid;
332 bad:
333 	kfree(encpass, M_SMBTEMP);
334 	kfree(pbuf, M_SMBTEMP);
335 	smb_rq_done(rqp);
336 	return error;
337 }
338 
339 int
340 smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
341 {
342 	struct smb_rq *rqp;
343 	struct mbchain *mbp;
344 	int error;
345 
346 	if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
347 		return 0;
348 
349 	if (smb_smb_nomux(vcp, scred, __func__) != 0)
350 		return EINVAL;
351 
352 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
353 	if (error)
354 		return error;
355 	mbp = &rqp->sr_rq;
356 	smb_rq_wstart(rqp);
357 	mb_put_uint8(mbp, 0xff);
358 	mb_put_uint8(mbp, 0);
359 	mb_put_uint16le(mbp, 0);
360 	smb_rq_wend(rqp);
361 	smb_rq_bstart(rqp);
362 	smb_rq_bend(rqp);
363 	error = smb_rq_simple(rqp);
364 	SMBSDEBUG("%d\n", error);
365 	smb_rq_done(rqp);
366 	return error;
367 }
368 
369 static char smb_any_share[] = "?????";
370 
371 static char *
372 smb_share_typename(int stype)
373 {
374 	char *pp;
375 
376 	switch (stype) {
377 	    case SMB_ST_DISK:
378 		pp = "A:";
379 		break;
380 	    case SMB_ST_PRINTER:
381 		pp = smb_any_share;		/* can't use LPT: here... */
382 		break;
383 	    case SMB_ST_PIPE:
384 		pp = "IPC";
385 		break;
386 	    case SMB_ST_COMM:
387 		pp = "COMM";
388 		break;
389 	    case SMB_ST_ANY:
390 	    default:
391 		pp = smb_any_share;
392 		break;
393 	}
394 	return pp;
395 }
396 
397 int
398 smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
399 {
400 	struct smb_vc *vcp;
401 	struct smb_rq rq, *rqp = &rq;
402 	struct mbchain *mbp;
403 	char *pp, *pbuf, *encpass;
404 	int error, plen, caseopt;
405 
406 	ssp->ss_tid = SMB_TID_UNKNOWN;
407 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp);
408 	if (error)
409 		return error;
410 	vcp = rqp->sr_vc;
411 	caseopt = SMB_CS_NONE;
412 	if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
413 		plen = 1;
414 		pp = "";
415 		pbuf = NULL;
416 		encpass = NULL;
417 	} else {
418 		pbuf = kmalloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
419 		encpass = kmalloc(24, M_SMBTEMP, M_WAITOK);
420 		iconv_convstr(vcp->vc_toupper, pbuf, smb_share_getpass(ssp));
421 		iconv_convstr(vcp->vc_toserver, pbuf, pbuf);
422 		if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
423 			plen = 24;
424 			smb_encrypt(pbuf, vcp->vc_ch, encpass);
425 			pp = encpass;
426 		} else {
427 			plen = strlen(pbuf) + 1;
428 			pp = pbuf;
429 		}
430 	}
431 	mbp = &rqp->sr_rq;
432 	smb_rq_wstart(rqp);
433 	mb_put_uint8(mbp, 0xff);
434 	mb_put_uint8(mbp, 0);
435 	mb_put_uint16le(mbp, 0);
436 	mb_put_uint16le(mbp, 0);		/* Flags */
437 	mb_put_uint16le(mbp, plen);
438 	smb_rq_wend(rqp);
439 	smb_rq_bstart(rqp);
440 	mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
441 	smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
442 	pp = vcp->vc_srvname;
443 	smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
444 	smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
445 	pp = ssp->ss_name;
446 	smb_put_dstring(mbp, vcp, pp, caseopt);
447 	pp = smb_share_typename(ssp->ss_type);
448 	smb_put_dstring(mbp, vcp, pp, caseopt);
449 	smb_rq_bend(rqp);
450 	error = smb_rq_simple(rqp);
451 	SMBSDEBUG("%d\n", error);
452 	if (error)
453 		goto bad;
454 	ssp->ss_tid = rqp->sr_rptid;
455 	ssp->ss_vcgenid = vcp->vc_genid;
456 	ssp->ss_flags |= SMBS_CONNECTED;
457 bad:
458 	if (encpass)
459 		kfree(encpass, M_SMBTEMP);
460 	if (pbuf)
461 		kfree(pbuf, M_SMBTEMP);
462 	smb_rq_done(rqp);
463 	return error;
464 }
465 
466 int
467 smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
468 {
469 	struct smb_rq *rqp;
470 	struct mbchain *mbp;
471 	int error;
472 
473 	if (ssp->ss_tid == SMB_TID_UNKNOWN)
474 		return 0;
475 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
476 	if (error)
477 		return error;
478 	mbp = &rqp->sr_rq;
479 	smb_rq_wstart(rqp);
480 	smb_rq_wend(rqp);
481 	smb_rq_bstart(rqp);
482 	smb_rq_bend(rqp);
483 	error = smb_rq_simple(rqp);
484 	SMBSDEBUG("%d\n", error);
485 	smb_rq_done(rqp);
486 	ssp->ss_tid = SMB_TID_UNKNOWN;
487 	return error;
488 }
489 
490 static __inline int
491 smb_smb_read(struct smb_share *ssp, u_int16_t fid,
492 	int *len, int *rresid, struct uio *uio, struct smb_cred *scred)
493 {
494 	struct smb_rq *rqp;
495 	struct mbchain *mbp;
496 	struct mdchain *mdp;
497 	u_int16_t resid, bc;
498 	u_int8_t wc;
499 	int error, rlen, blksz;
500 
501 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
502 	if (error)
503 		return error;
504 
505 	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
506 	rlen = *len = min(blksz, *len);
507 
508 	smb_rq_getrequest(rqp, &mbp);
509 	smb_rq_wstart(rqp);
510 	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
511 	mb_put_uint16le(mbp, rlen);
512 	mb_put_uint32le(mbp, uio->uio_offset);
513 	mb_put_uint16le(mbp, (unsigned short)szmin(uio->uio_resid, 0xffff));
514 	smb_rq_wend(rqp);
515 	smb_rq_bstart(rqp);
516 	smb_rq_bend(rqp);
517 	do {
518 		error = smb_rq_simple(rqp);
519 		if (error)
520 			break;
521 		smb_rq_getreply(rqp, &mdp);
522 		md_get_uint8(mdp, &wc);
523 		if (wc != 5) {
524 			error = EBADRPC;
525 			break;
526 		}
527 		md_get_uint16le(mdp, &resid);
528 		md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
529 		md_get_uint16le(mdp, &bc);
530 		md_get_uint8(mdp, NULL);		/* ignore buffer type */
531 		md_get_uint16le(mdp, &resid);
532 		if (resid == 0) {
533 			*rresid = resid;
534 			break;
535 		}
536 		error = md_get_uio(mdp, uio, resid);
537 		if (error)
538 			break;
539 		*rresid = resid;
540 	} while(0);
541 	smb_rq_done(rqp);
542 	return error;
543 }
544 
545 int
546 smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
547 	struct smb_cred *scred)
548 {
549 	int len, resid;
550 	int error = 0;
551 
552 	while (uio->uio_resid > 0) {
553 		if (uio->uio_resid > INT_MAX)
554 			len = INT_MAX;
555 		else
556 			len = (int)uio->uio_resid;
557 		error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
558 		if (error)
559 			break;
560 		if (resid < len)
561 			break;
562 	}
563 	return error;
564 }
565 
566 static __inline int
567 smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
568 	struct uio *uio, struct smb_cred *scred)
569 {
570 	struct smb_rq *rqp;
571 	struct mbchain *mbp;
572 	struct mdchain *mdp;
573 	u_int16_t resid;
574 	u_int8_t wc;
575 	int error, blksz;
576 
577 	/* write data must be real */
578 	KKASSERT(uio->uio_segflg != UIO_NOCOPY);
579 
580 	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
581 	if (blksz > 0xffff)
582 		blksz = 0xffff;
583 
584 	resid = *len = min(blksz, *len);
585 
586 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
587 	if (error)
588 		return error;
589 	smb_rq_getrequest(rqp, &mbp);
590 	smb_rq_wstart(rqp);
591 	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
592 	mb_put_uint16le(mbp, resid);
593 	mb_put_uint32le(mbp, uio->uio_offset);
594 	mb_put_uint16le(mbp, (unsigned short)szmin(uio->uio_resid, 0xffff));
595 	smb_rq_wend(rqp);
596 	smb_rq_bstart(rqp);
597 	mb_put_uint8(mbp, SMB_DT_DATA);
598 	mb_put_uint16le(mbp, resid);
599 	do {
600 		error = mb_put_uio(mbp, uio, resid);
601 		if (error)
602 			break;
603 		smb_rq_bend(rqp);
604 		error = smb_rq_simple(rqp);
605 		if (error)
606 			break;
607 		smb_rq_getreply(rqp, &mdp);
608 		md_get_uint8(mdp, &wc);
609 		if (wc != 1) {
610 			error = EBADRPC;
611 			break;
612 		}
613 		md_get_uint16le(mdp, &resid);
614 		*rresid = resid;
615 	} while(0);
616 	smb_rq_done(rqp);
617 	return error;
618 }
619 
620 int
621 smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
622 	struct smb_cred *scred)
623 {
624 	int error = 0, len, resid;
625 	struct uio olduio;
626 
627 	olduio = *uio;
628 	while (uio->uio_resid > 0) {
629 		if (uio->uio_resid > INT_MAX)
630 			len = INT_MAX;
631 		else
632 			len = (int)uio->uio_resid;
633 		error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
634 		if (error)
635 			break;
636 		if (resid < len) {
637 			error = EIO;
638 			break;
639 		}
640 	}
641 	if (error) {
642 		/*
643 		 * Errors can happen on the copyin, the rpc, etc.  So they
644 		 * imply resid is unreliable.  The only safe thing is
645 		 * to pretend zero bytes made it.  We needn't restore the
646 		 * iovs because callers don't depend on them in error
647 		 * paths - uio_resid and uio_offset are what matter.
648 		 */
649 		*uio = olduio;
650 	}
651 	return error;
652 }
653 
654 int
655 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
656 {
657 	struct smb_rq *rqp;
658 	struct mbchain *mbp;
659 	int error;
660 
661 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
662 	if (error)
663 		return error;
664 	mbp = &rqp->sr_rq;
665 	smb_rq_wstart(rqp);
666 	mb_put_uint16le(mbp, 1);
667 	smb_rq_wend(rqp);
668 	smb_rq_bstart(rqp);
669 	mb_put_uint32le(mbp, 0);
670 	smb_rq_bend(rqp);
671 	error = smb_rq_simple(rqp);
672 	SMBSDEBUG("%d\n", error);
673 	smb_rq_done(rqp);
674 	return error;
675 }
676