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