xref: /dragonfly/sys/netproto/smb/smb_smb.c (revision d555fba4f8e2c05de8ee1a5ecc7362dff369e037)
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
smb_smb_nomux(struct smb_vc * vcp,struct smb_cred * scred,const char * name)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
smb_smb_negotiate(struct smb_vc * vcp,struct smb_cred * scred)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
smb_smb_ssnsetup(struct smb_vc * vcp,struct smb_cred * scred)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
smb_smb_ssnclose(struct smb_vc * vcp,struct smb_cred * scred)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 *
smb_share_typename(int stype)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
smb_smb_treeconnect(struct smb_share * ssp,struct smb_cred * scred)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
smb_smb_treedisconnect(struct smb_share * ssp,struct smb_cred * scred)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
smb_smb_read(struct smb_share * ssp,u_int16_t fid,int * len,int * rresid,struct uio * uio,struct smb_cred * scred)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
smb_read(struct smb_share * ssp,u_int16_t fid,struct uio * uio,struct smb_cred * scred)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
smb_smb_write(struct smb_share * ssp,u_int16_t fid,int * len,int * rresid,struct uio * uio,struct smb_cred * scred)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
smb_write(struct smb_share * ssp,u_int16_t fid,struct uio * uio,struct smb_cred * scred)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
smb_smb_echo(struct smb_vc * vcp,struct smb_cred * scred)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 }
678