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