FreeBSD/Linux Kernel Cross Reference
sys/netsmb/smb_smb.c
1 /* $NetBSD: smb_smb.c,v 1.27 2006/05/10 21:53:19 mrg 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.27 2006/05/10 21:53:19 mrg 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_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 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 *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
274 again:
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;
390 bad:
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
401 int
402 smb_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
430 static const char *
431 smb_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
457 int
458 smb_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
469 again:
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;
550 bad:
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
563 int
564 smb_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
585 static inline int
586 smb_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 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
598 if (error)
599 return error;
600 smb_rq_getrequest(rqp, &mbp);
601 smb_rq_wstart(rqp);
602 mb_put_uint8(mbp, 0xff); /* no secondary command */
603 mb_put_uint8(mbp, 0); /* MBZ */
604 mb_put_uint16le(mbp, 0); /* offset to secondary */
605 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
606 mb_put_uint32le(mbp, uio->uio_offset);
607 *len = min(SSTOVC(ssp)->vc_rxmax, *len);
608 mb_put_uint16le(mbp, *len); /* MaxCount */
609 mb_put_uint16le(mbp, *len); /* MinCount (only indicates blocking) */
610 mb_put_uint32le(mbp, *len >> 16); /* MaxCountHigh */
611 mb_put_uint16le(mbp, *len); /* Remaining ("obsolete") */
612 mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */
613 smb_rq_wend(rqp);
614 smb_rq_bstart(rqp);
615 smb_rq_bend(rqp);
616 do {
617 error = smb_rq_simple(rqp);
618 if (error)
619 break;
620 smb_rq_getreply(rqp, &mdp);
621 off = SMB_HDRLEN;
622 md_get_uint8(mdp, &wc);
623 off++;
624 if (wc != 12) {
625 error = EBADRPC;
626 break;
627 }
628 md_get_uint8(mdp, NULL);
629 off++;
630 md_get_uint8(mdp, NULL);
631 off++;
632 md_get_uint16(mdp, NULL);
633 off += 2;
634 md_get_uint16(mdp, NULL);
635 off += 2;
636 md_get_uint16(mdp, NULL); /* data compaction mode */
637 off += 2;
638 md_get_uint16(mdp, NULL);
639 off += 2;
640 md_get_uint16le(mdp, &residlo);
641 off += 2;
642 md_get_uint16le(mdp, &doff); /* data offset */
643 off += 2;
644 md_get_uint16le(mdp, &residhi);
645 off += 2;
646 resid = (residhi << 16) | residlo;
647 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
648 off += 4*2;
649 md_get_uint16(mdp, NULL); /* ByteCount */
650 off += 2;
651 if (doff > off) /* pad byte(s)? */
652 md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
653 if (resid == 0) {
654 *rresid = resid;
655 break;
656 }
657 error = md_get_uio(mdp, uio, resid);
658 if (error)
659 break;
660 *rresid = resid;
661 } while(0);
662 smb_rq_done(rqp);
663 return (error);
664 }
665
666 static inline int
667 smb_smb_writex(struct smb_share *ssp, u_int16_t fid, size_t *len, size_t *rresid,
668 struct uio *uio, struct smb_cred *scred)
669 {
670 struct smb_rq *rqp;
671 struct mbchain *mbp;
672 struct mdchain *mdp;
673 int error;
674 u_int8_t wc;
675 u_int16_t resid;
676
677 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
678 if (error)
679 return (error);
680 smb_rq_getrequest(rqp, &mbp);
681 smb_rq_wstart(rqp);
682 mb_put_uint8(mbp, 0xff); /* no secondary command */
683 mb_put_uint8(mbp, 0); /* MBZ */
684 mb_put_uint16le(mbp, 0); /* offset to secondary */
685 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
686 mb_put_uint32le(mbp, uio->uio_offset);
687 mb_put_uint32le(mbp, 0); /* MBZ (timeout) */
688 mb_put_uint16le(mbp, 0); /* !write-thru */
689 mb_put_uint16le(mbp, 0);
690 *len = min(SSTOVC(ssp)->vc_wxmax, *len);
691 mb_put_uint16le(mbp, *len >> 16);
692 mb_put_uint16le(mbp, *len);
693 mb_put_uint16le(mbp, 64); /* data offset from header start */
694 mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */
695 smb_rq_wend(rqp);
696 smb_rq_bstart(rqp);
697 do {
698 mb_put_uint8(mbp, 0xee); /* mimic xp pad byte! */
699 error = mb_put_uio(mbp, uio, *len);
700 if (error)
701 break;
702 smb_rq_bend(rqp);
703 error = smb_rq_simple(rqp);
704 if (error)
705 break;
706 smb_rq_getreply(rqp, &mdp);
707 md_get_uint8(mdp, &wc);
708 if (wc != 6) {
709 error = EBADRPC;
710 break;
711 }
712 md_get_uint8(mdp, NULL);
713 md_get_uint8(mdp, NULL);
714 md_get_uint16(mdp, NULL);
715 md_get_uint16le(mdp, &resid);
716 *rresid = resid;
717 } while(0);
718
719 smb_rq_done(rqp);
720 return (error);
721 }
722
723 static inline int
724 smb_smb_read(struct smb_share *ssp, u_int16_t fid,
725 size_t *len, size_t *rresid, struct uio *uio, struct smb_cred *scred)
726 {
727 struct smb_rq *rqp;
728 struct mbchain *mbp;
729 struct mdchain *mdp;
730 u_int16_t resid, bc;
731 u_int8_t wc;
732 int error, rlen, blksz;
733
734 /* Cannot read at/beyond 4G */
735 if (uio->uio_offset >= (1LL << 32))
736 return (EFBIG);
737
738 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
739 if (error)
740 return error;
741
742 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
743 rlen = *len = min(blksz, *len);
744
745 smb_rq_getrequest(rqp, &mbp);
746 smb_rq_wstart(rqp);
747 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
748 mb_put_uint16le(mbp, rlen);
749 mb_put_uint32le(mbp, uio->uio_offset);
750 mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
751 smb_rq_wend(rqp);
752 smb_rq_bstart(rqp);
753 smb_rq_bend(rqp);
754 do {
755 error = smb_rq_simple(rqp);
756 if (error)
757 break;
758 smb_rq_getreply(rqp, &mdp);
759 md_get_uint8(mdp, &wc);
760 if (wc != 5) {
761 error = EBADRPC;
762 break;
763 }
764 md_get_uint16le(mdp, &resid);
765 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
766 md_get_uint16le(mdp, &bc);
767 md_get_uint8(mdp, NULL); /* ignore buffer type */
768 md_get_uint16le(mdp, &resid);
769 if (resid == 0) {
770 *rresid = resid;
771 break;
772 }
773 error = md_get_uio(mdp, uio, resid);
774 if (error)
775 break;
776 *rresid = resid;
777 } while(0);
778 smb_rq_done(rqp);
779 return error;
780 }
781
782 int
783 smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
784 struct smb_cred *scred)
785 {
786 size_t tsize, len, resid;
787 int error = 0;
788 int rx = (SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_READX);
789
790 resid = 0; /* XXX gcc */
791
792 tsize = uio->uio_resid;
793 while (tsize > 0) {
794 len = tsize;
795 if (rx)
796 error = smb_smb_readx(ssp, fid, &len, &resid, uio, scred);
797 else
798 error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
799 if (error)
800 break;
801 tsize -= resid;
802 if (resid < len)
803 break;
804 }
805 return error;
806 }
807
808 static inline int
809 smb_smb_write(struct smb_share *ssp, u_int16_t fid, size_t *len, size_t *rresid,
810 struct uio *uio, struct smb_cred *scred)
811 {
812 struct smb_rq *rqp;
813 struct mbchain *mbp;
814 struct mdchain *mdp;
815 u_int16_t resid;
816 u_int8_t wc;
817 int error, blksz;
818
819 /* Cannot write at/beyond 4G */
820 if (uio->uio_offset >= (1LL << 32))
821 return (EFBIG);
822
823 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
824 if (blksz > 0xffff)
825 blksz = 0xffff;
826
827 resid = *len = min(blksz, *len);
828
829 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
830 if (error)
831 return error;
832 smb_rq_getrequest(rqp, &mbp);
833 smb_rq_wstart(rqp);
834 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
835 mb_put_uint16le(mbp, resid);
836 mb_put_uint32le(mbp, uio->uio_offset);
837 mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
838 smb_rq_wend(rqp);
839 smb_rq_bstart(rqp);
840 mb_put_uint8(mbp, SMB_DT_DATA);
841 mb_put_uint16le(mbp, resid);
842 do {
843 error = mb_put_uio(mbp, uio, resid);
844 if (error)
845 break;
846 smb_rq_bend(rqp);
847 error = smb_rq_simple(rqp);
848 if (error)
849 break;
850 smb_rq_getreply(rqp, &mdp);
851 md_get_uint8(mdp, &wc);
852 if (wc != 1) {
853 error = EBADRPC;
854 break;
855 }
856 md_get_uint16le(mdp, &resid);
857 *rresid = resid;
858 } while(0);
859 smb_rq_done(rqp);
860 return error;
861 }
862
863 int
864 smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
865 struct smb_cred *scred)
866 {
867 int error = 0;
868 size_t len, tsize, resid;
869 int wx = (SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_WRITEX);
870
871 resid = 0; /* XXX gcc */
872
873 tsize = uio->uio_resid;
874 while (tsize > 0) {
875 len = tsize;
876 if (wx)
877 error = smb_smb_writex(ssp, fid, &len, &resid, uio, scred);
878 else
879 error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
880 if (error)
881 break;
882 if (resid < len) {
883 error = EIO;
884 break;
885 }
886 tsize -= resid;
887 }
888 return error;
889 }
890
891 #if 0
892 int
893 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
894 {
895 struct smb_rq *rqp;
896 struct mbchain *mbp;
897 int error;
898
899 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
900 if (error)
901 return error;
902 mbp = &rqp->sr_rq;
903 smb_rq_wstart(rqp);
904 mb_put_uint16le(mbp, 1);
905 smb_rq_wend(rqp);
906 smb_rq_bstart(rqp);
907 mb_put_uint32le(mbp, 0);
908 smb_rq_bend(rqp);
909 error = smb_rq_simple(rqp);
910 SMBSDEBUG("%d\n", error);
911 smb_rq_done(rqp);
912 return error;
913 }
914 #endif
Cache object: 6c2aa3a130ffe2178d2bf14453507bd3
|