FreeBSD/Linux Kernel Cross Reference
sys/netsmb/smb_smb.c
1 /* $NetBSD: smb_smb.c,v 1.23 2005/02/26 22:39:50 perry 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.23 2005/02/26 22:39:50 perry 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
61 struct smb_dialect {
62 int d_id;
63 const char *d_name;
64 };
65
66 static 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
78 static u_int32_t
79 smb_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
91 static u_int32_t
92 smb_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
104 int
105 smb_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_p == vcp->vc_iod->iod_p);
117
118 vcp->vc_hflags = 0;
119 vcp->vc_hflags2 = 0;
120 vcp->obj.co_flags &= ~(SMBV_ENCRYPT);
121 sp = &vcp->vc_sopt;
122 bzero(sp, 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_uint16(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_uint16(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 }
254 bad:
255 smb_rq_done(rqp);
256 return error;
257 }
258
259 int
260 smb_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 *pp, *up, *pbuf, *encpass;
267 int error, plen, uniplen, ulen, upper;
268
269 KASSERT(scred->scr_p == vcp->vc_iod->iod_p);
270
271 upper = 0;
272
273 again:
274
275 vcp->vc_smbuid = SMB_UID_UNKNOWN;
276
277 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
278 if (error)
279 return error;
280 pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
281 encpass = malloc(24, M_SMBTEMP, M_WAITOK);
282 if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
283 /*
284 * We try w/o uppercasing first so Samba mixed case
285 * passwords work. If that fails we come back and try
286 * uppercasing to satisfy OS/2 and Windows for Workgroups.
287 */
288 if (upper) {
289 iconv_convstr(vcp->vc_toupper, pbuf,
290 smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN + 1);
291 } else {
292 strlcpy(pbuf, smb_vc_getpass(vcp),
293 SMB_MAXPASSWORDLEN + 1);
294 }
295 if (!SMB_UNICODE_STRINGS(vcp))
296 iconv_convstr(vcp->vc_toserver, pbuf, pbuf,
297 SMB_MAXPASSWORDLEN + 1);
298
299 if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
300 uniplen = plen = 24;
301 smb_encrypt(pbuf, vcp->vc_ch, encpass);
302 ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
303 if (SMB_UNICODE_STRINGS(vcp)) {
304 strlcpy(pbuf, smb_vc_getpass(vcp),
305 SMB_MAXPASSWORDLEN + 1);
306 } else
307 iconv_convstr(vcp->vc_toserver, pbuf,
308 smb_vc_getpass(vcp),
309 SMB_MAXPASSWORDLEN + 1);
310 smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass);
311 pp = encpass;
312 unipp = ntencpass;
313 } else {
314 plen = strlen(pbuf) + 1;
315 pp = pbuf;
316 uniplen = plen * 2;
317 ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
318 smb_strtouni(ntencpass, smb_vc_getpass(vcp));
319 plen--;
320
321 /*
322 * The uniplen is zeroed because Samba cannot deal
323 * with this 2nd cleartext password. This Samba
324 * "bug" is actually a workaround for problems in
325 * Microsoft clients.
326 */
327 uniplen = 0/*-= 2*/;
328 unipp = ntencpass;
329 }
330 } else {
331 /*
332 * In the share security mode password will be used
333 * only in the tree authentication
334 */
335 pp = "";
336 plen = 1;
337 unipp = &smb_unieol;
338 uniplen = 0;
339 }
340 smb_rq_wstart(rqp);
341 mbp = &rqp->sr_rq;
342 up = vcp->vc_username;
343 ulen = strlen(up) + 1;
344 /*
345 * If userid is null we are attempting anonymous browse login
346 * so passwords must be zero length.
347 */
348 if (ulen == 1)
349 plen = uniplen = 0;
350 mb_put_uint8(mbp, 0xff);
351 mb_put_uint8(mbp, 0);
352 mb_put_uint16le(mbp, 0);
353 mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
354 mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
355 mb_put_uint16le(mbp, vcp->vc_number);
356 mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
357 mb_put_uint16le(mbp, plen);
358 if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
359 mb_put_uint32le(mbp, 0);
360 smb_rq_wend(rqp);
361 smb_rq_bstart(rqp);
362 mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
363 smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);
364 } else {
365 mb_put_uint16le(mbp, uniplen);
366 mb_put_uint32le(mbp, 0); /* reserved */
367 mb_put_uint32le(mbp, vcp->obj.co_flags & SMBV_UNICODE ?
368 SMB_CAP_UNICODE : 0);
369 smb_rq_wend(rqp);
370 smb_rq_bstart(rqp);
371 mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
372 mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
373 smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* AccountName */
374 smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE); /* PrimaryDomain */
375 smb_put_dstring(mbp, vcp, "NetBSD", SMB_CS_NONE); /* Client's OS */
376 smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE); /* Client name */
377 }
378 smb_rq_bend(rqp);
379 if (ntencpass)
380 free(ntencpass, M_SMBTEMP);
381 error = smb_rq_simple(rqp);
382 SMBSDEBUG("%d\n", error);
383 if (error) {
384 if (error == EACCES)
385 error = EAUTH;
386 goto bad;
387 }
388 vcp->vc_smbuid = rqp->sr_rpuid;
389 bad:
390 free(encpass, M_SMBTEMP);
391 free(pbuf, M_SMBTEMP);
392 smb_rq_done(rqp);
393 if (error && !upper && vcp->vc_sopt.sv_sm & SMB_SM_USER) {
394 upper = 1;
395 goto again;
396 }
397 return error;
398 }
399
400 int
401 smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
402 {
403 struct smb_rq *rqp;
404 struct mbchain *mbp;
405 int error;
406
407 KASSERT(scred->scr_p == vcp->vc_iod->iod_p);
408
409 if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
410 return 0;
411
412 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
413 if (error)
414 return error;
415 mbp = &rqp->sr_rq;
416 smb_rq_wstart(rqp);
417 mb_put_uint8(mbp, 0xff);
418 mb_put_uint8(mbp, 0);
419 mb_put_uint16le(mbp, 0);
420 smb_rq_wend(rqp);
421 smb_rq_bstart(rqp);
422 smb_rq_bend(rqp);
423 error = smb_rq_simple(rqp);
424 SMBSDEBUG("%d\n", error);
425 smb_rq_done(rqp);
426 return error;
427 }
428
429 static const char *
430 smb_share_typename(int stype)
431 {
432 static const char smb_any_share[] = "?????";
433 const char *pp;
434
435 switch (stype) {
436 case SMB_ST_DISK:
437 pp = "A:";
438 break;
439 case SMB_ST_PRINTER:
440 pp = smb_any_share; /* can't use LPT: here... */
441 break;
442 case SMB_ST_PIPE:
443 pp = "IPC";
444 break;
445 case SMB_ST_COMM:
446 pp = "COMM";
447 break;
448 case SMB_ST_ANY:
449 default:
450 pp = smb_any_share;
451 break;
452 }
453 return pp;
454 }
455
456 int
457 smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
458 {
459 struct smb_vc *vcp;
460 struct smb_rq rq, *rqp = &rq;
461 struct mbchain *mbp;
462 const char *pp;
463 char *pbuf, *encpass;
464 int error, plen, caseopt, upper;
465
466 upper = 0;
467
468 again:
469
470 #if 0
471 /* Disable Unicode for SMB_COM_TREE_CONNECT_ANDX requests */
472 if (SSTOVC(ssp)->vc_hflags2 & SMB_FLAGS2_UNICODE) {
473 vcp = SSTOVC(ssp);
474 if (vcp->vc_toserver) {
475 iconv_close(vcp->vc_toserver);
476 /* Use NULL until UTF-8 -> ASCII works */
477 vcp->vc_toserver = NULL;
478 }
479 if (vcp->vc_tolocal) {
480 iconv_close(vcp->vc_tolocal);
481 /* Use NULL until ASCII -> UTF-8 works*/
482 vcp->vc_tolocal = NULL;
483 }
484 vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE;
485 }
486 #endif
487
488 ssp->ss_tid = SMB_TID_UNKNOWN;
489 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp);
490 if (error)
491 return error;
492 vcp = rqp->sr_vc;
493 caseopt = SMB_CS_NONE;
494 if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
495 plen = 1;
496 pp = "";
497 pbuf = NULL;
498 encpass = NULL;
499 } else {
500 pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
501 encpass = malloc(24, M_SMBTEMP, M_WAITOK);
502 /*
503 * We try w/o uppercasing first so Samba mixed case
504 * passwords work. If that fails we come back and try
505 * uppercasing to satisfy OS/2 and Windows for Workgroups.
506 */
507 if (upper) {
508 iconv_convstr(vcp->vc_toupper, pbuf,
509 smb_share_getpass(ssp), SMB_MAXPASSWORDLEN + 1);
510 } else {
511 strlcpy(pbuf, smb_share_getpass(ssp),
512 SMB_MAXPASSWORDLEN + 1);
513 }
514 if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
515 plen = 24;
516 smb_encrypt(pbuf, vcp->vc_ch, encpass);
517 pp = encpass;
518 } else {
519 plen = strlen(pbuf) + 1;
520 pp = pbuf;
521 }
522 }
523 mbp = &rqp->sr_rq;
524 smb_rq_wstart(rqp);
525 mb_put_uint8(mbp, 0xff);
526 mb_put_uint8(mbp, 0);
527 mb_put_uint16le(mbp, 0);
528 mb_put_uint16le(mbp, 0); /* Flags */
529 mb_put_uint16le(mbp, plen);
530 smb_rq_wend(rqp);
531 smb_rq_bstart(rqp);
532 mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
533 smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
534 pp = vcp->vc_srvname;
535 smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
536 smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
537 pp = ssp->ss_name;
538 smb_put_dstring(mbp, vcp, pp, caseopt);
539 pp = smb_share_typename(ssp->ss_type);
540 smb_put_dstring(mbp, vcp, pp, caseopt);
541 smb_rq_bend(rqp);
542 error = smb_rq_simple(rqp);
543 SMBSDEBUG("%d\n", error);
544 if (error)
545 goto bad;
546 ssp->ss_tid = rqp->sr_rptid;
547 ssp->ss_vcgenid = vcp->vc_genid;
548 ssp->ss_flags |= SMBS_CONNECTED;
549 bad:
550 if (encpass)
551 free(encpass, M_SMBTEMP);
552 if (pbuf)
553 free(pbuf, M_SMBTEMP);
554 smb_rq_done(rqp);
555 if (error && !upper) {
556 upper = 1;
557 goto again;
558 }
559 return error;
560 }
561
562 int
563 smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
564 {
565 struct smb_rq *rqp;
566 int error;
567
568 if (ssp->ss_tid == SMB_TID_UNKNOWN)
569 return 0;
570 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
571 if (error)
572 return error;
573 smb_rq_wstart(rqp);
574 smb_rq_wend(rqp);
575 smb_rq_bstart(rqp);
576 smb_rq_bend(rqp);
577 error = smb_rq_simple(rqp);
578 SMBSDEBUG("%d\n", error);
579 smb_rq_done(rqp);
580 ssp->ss_tid = SMB_TID_UNKNOWN;
581 return error;
582 }
583
584 static __inline int
585 smb_smb_readx(struct smb_share *ssp, u_int16_t fid, size_t *len, size_t *rresid,
586 struct uio *uio, struct smb_cred *scred)
587 {
588 struct smb_rq *rqp;
589 struct mbchain *mbp;
590 struct mdchain *mdp;
591 u_int8_t wc;
592 int error;
593 u_int16_t residhi, residlo, off, doff;
594 u_int32_t resid;
595
596 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
597 if (error)
598 return error;
599 smb_rq_getrequest(rqp, &mbp);
600 smb_rq_wstart(rqp);
601 mb_put_uint8(mbp, 0xff); /* no secondary command */
602 mb_put_uint8(mbp, 0); /* MBZ */
603 mb_put_uint16le(mbp, 0); /* offset to secondary */
604 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
605 mb_put_uint32le(mbp, uio->uio_offset);
606 *len = min(SSTOVC(ssp)->vc_rxmax, *len);
607 mb_put_uint16le(mbp, *len); /* MaxCount */
608 mb_put_uint16le(mbp, *len); /* MinCount (only indicates blocking) */
609 mb_put_uint32le(mbp, *len >> 16); /* MaxCountHigh */
610 mb_put_uint16le(mbp, *len); /* Remaining ("obsolete") */
611 mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */
612 smb_rq_wend(rqp);
613 smb_rq_bstart(rqp);
614 smb_rq_bend(rqp);
615 do {
616 error = smb_rq_simple(rqp);
617 if (error)
618 break;
619 smb_rq_getreply(rqp, &mdp);
620 off = SMB_HDRLEN;
621 md_get_uint8(mdp, &wc);
622 off++;
623 if (wc != 12) {
624 error = EBADRPC;
625 break;
626 }
627 md_get_uint8(mdp, NULL);
628 off++;
629 md_get_uint8(mdp, NULL);
630 off++;
631 md_get_uint16(mdp, NULL);
632 off += 2;
633 md_get_uint16(mdp, NULL);
634 off += 2;
635 md_get_uint16(mdp, NULL); /* data compaction mode */
636 off += 2;
637 md_get_uint16(mdp, NULL);
638 off += 2;
639 md_get_uint16le(mdp, &residlo);
640 off += 2;
641 md_get_uint16le(mdp, &doff); /* data offset */
642 off += 2;
643 md_get_uint16le(mdp, &residhi);
644 off += 2;
645 resid = (residhi << 16) | residlo;
646 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
647 off += 4*2;
648 md_get_uint16(mdp, NULL); /* ByteCount */
649 off += 2;
650 if (doff > off) /* pad byte(s)? */
651 md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
652 if (resid == 0) {
653 *rresid = resid;
654 break;
655 }
656 error = md_get_uio(mdp, uio, resid);
657 if (error)
658 break;
659 *rresid = resid;
660 } while(0);
661 smb_rq_done(rqp);
662 return (error);
663 }
664
665 static __inline int
666 smb_smb_writex(struct smb_share *ssp, u_int16_t fid, size_t *len, size_t *rresid,
667 struct uio *uio, struct smb_cred *scred)
668 {
669 struct smb_rq *rqp;
670 struct mbchain *mbp;
671 struct mdchain *mdp;
672 int error;
673 u_int8_t wc;
674 u_int16_t resid;
675
676 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
677 if (error)
678 return (error);
679 smb_rq_getrequest(rqp, &mbp);
680 smb_rq_wstart(rqp);
681 mb_put_uint8(mbp, 0xff); /* no secondary command */
682 mb_put_uint8(mbp, 0); /* MBZ */
683 mb_put_uint16le(mbp, 0); /* offset to secondary */
684 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
685 mb_put_uint32le(mbp, uio->uio_offset);
686 mb_put_uint32le(mbp, 0); /* MBZ (timeout) */
687 mb_put_uint16le(mbp, 0); /* !write-thru */
688 mb_put_uint16le(mbp, 0);
689 *len = min(SSTOVC(ssp)->vc_wxmax, *len);
690 mb_put_uint16le(mbp, *len >> 16);
691 mb_put_uint16le(mbp, *len);
692 mb_put_uint16le(mbp, 64); /* data offset from header start */
693 mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */
694 smb_rq_wend(rqp);
695 smb_rq_bstart(rqp);
696 do {
697 mb_put_uint8(mbp, 0xee); /* mimic xp pad byte! */
698 error = mb_put_uio(mbp, uio, *len);
699 if (error)
700 break;
701 smb_rq_bend(rqp);
702 error = smb_rq_simple(rqp);
703 if (error)
704 break;
705 smb_rq_getreply(rqp, &mdp);
706 md_get_uint8(mdp, &wc);
707 if (wc != 6) {
708 error = EBADRPC;
709 break;
710 }
711 md_get_uint8(mdp, NULL);
712 md_get_uint8(mdp, NULL);
713 md_get_uint16(mdp, NULL);
714 md_get_uint16le(mdp, &resid);
715 *rresid = resid;
716 } while(0);
717
718 smb_rq_done(rqp);
719 return (error);
720 }
721
722 static __inline int
723 smb_smb_read(struct smb_share *ssp, u_int16_t fid,
724 size_t *len, size_t *rresid, struct uio *uio, struct smb_cred *scred)
725 {
726 struct smb_rq *rqp;
727 struct mbchain *mbp;
728 struct mdchain *mdp;
729 u_int16_t resid, bc;
730 u_int8_t wc;
731 int error, rlen, blksz;
732
733 /* Cannot read at/beyond 4G */
734 if (uio->uio_offset >= (1LL << 32))
735 return (EFBIG);
736
737 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
738 if (error)
739 return error;
740
741 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
742 rlen = *len = min(blksz, *len);
743
744 smb_rq_getrequest(rqp, &mbp);
745 smb_rq_wstart(rqp);
746 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
747 mb_put_uint16le(mbp, rlen);
748 mb_put_uint32le(mbp, uio->uio_offset);
749 mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
750 smb_rq_wend(rqp);
751 smb_rq_bstart(rqp);
752 smb_rq_bend(rqp);
753 do {
754 error = smb_rq_simple(rqp);
755 if (error)
756 break;
757 smb_rq_getreply(rqp, &mdp);
758 md_get_uint8(mdp, &wc);
759 if (wc != 5) {
760 error = EBADRPC;
761 break;
762 }
763 md_get_uint16le(mdp, &resid);
764 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
765 md_get_uint16le(mdp, &bc);
766 md_get_uint8(mdp, NULL); /* ignore buffer type */
767 md_get_uint16le(mdp, &resid);
768 if (resid == 0) {
769 *rresid = resid;
770 break;
771 }
772 error = md_get_uio(mdp, uio, resid);
773 if (error)
774 break;
775 *rresid = resid;
776 } while(0);
777 smb_rq_done(rqp);
778 return error;
779 }
780
781 int
782 smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
783 struct smb_cred *scred)
784 {
785 size_t tsize, len, resid;
786 int error = 0;
787 int rx = (SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_READX);
788
789 tsize = uio->uio_resid;
790 while (tsize > 0) {
791 len = tsize;
792 if (rx)
793 error = smb_smb_readx(ssp, fid, &len, &resid, uio, scred);
794 else
795 error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
796 if (error)
797 break;
798 tsize -= resid;
799 if (resid < len)
800 break;
801 }
802 return error;
803 }
804
805 static __inline int
806 smb_smb_write(struct smb_share *ssp, u_int16_t fid, size_t *len, size_t *rresid,
807 struct uio *uio, struct smb_cred *scred)
808 {
809 struct smb_rq *rqp;
810 struct mbchain *mbp;
811 struct mdchain *mdp;
812 u_int16_t resid;
813 u_int8_t wc;
814 int error, blksz;
815
816 /* Cannot write at/beyond 4G */
817 if (uio->uio_offset >= (1LL << 32))
818 return (EFBIG);
819
820 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
821 if (blksz > 0xffff)
822 blksz = 0xffff;
823
824 resid = *len = min(blksz, *len);
825
826 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
827 if (error)
828 return error;
829 smb_rq_getrequest(rqp, &mbp);
830 smb_rq_wstart(rqp);
831 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
832 mb_put_uint16le(mbp, resid);
833 mb_put_uint32le(mbp, uio->uio_offset);
834 mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
835 smb_rq_wend(rqp);
836 smb_rq_bstart(rqp);
837 mb_put_uint8(mbp, SMB_DT_DATA);
838 mb_put_uint16le(mbp, resid);
839 do {
840 error = mb_put_uio(mbp, uio, resid);
841 if (error)
842 break;
843 smb_rq_bend(rqp);
844 error = smb_rq_simple(rqp);
845 if (error)
846 break;
847 smb_rq_getreply(rqp, &mdp);
848 md_get_uint8(mdp, &wc);
849 if (wc != 1) {
850 error = EBADRPC;
851 break;
852 }
853 md_get_uint16le(mdp, &resid);
854 *rresid = resid;
855 } while(0);
856 smb_rq_done(rqp);
857 return error;
858 }
859
860 int
861 smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
862 struct smb_cred *scred)
863 {
864 int error = 0;
865 size_t len, tsize, resid;
866 int wx = (SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_WRITEX);
867
868 tsize = uio->uio_resid;
869 while (tsize > 0) {
870 len = tsize;
871 if (wx)
872 error = smb_smb_writex(ssp, fid, &len, &resid, uio, scred);
873 else
874 error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
875 if (error)
876 break;
877 if (resid < len) {
878 error = EIO;
879 break;
880 }
881 tsize -= resid;
882 }
883 return error;
884 }
885
886 #if 0
887 int
888 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
889 {
890 struct smb_rq *rqp;
891 struct mbchain *mbp;
892 int error;
893
894 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
895 if (error)
896 return error;
897 mbp = &rqp->sr_rq;
898 smb_rq_wstart(rqp);
899 mb_put_uint16le(mbp, 1);
900 smb_rq_wend(rqp);
901 smb_rq_bstart(rqp);
902 mb_put_uint32le(mbp, 0);
903 smb_rq_bend(rqp);
904 error = smb_rq_simple(rqp);
905 SMBSDEBUG("%d\n", error);
906 smb_rq_done(rqp);
907 return error;
908 }
909 #endif
Cache object: 2e10486148e346e88a6049d37d1f2ecb
|