FreeBSD/Linux Kernel Cross Reference
sys/netsmb/smb_smb.c
1 /* $NetBSD: smb_smb.c,v 1.21 2003/10/30 01:43:10 simonb 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.21 2003/10/30 01:43:10 simonb 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*/);
291 } else {
292 strncpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN);
293 pbuf[SMB_MAXPASSWORDLEN] = '\0';
294 }
295 if (!SMB_UNICODE_STRINGS(vcp))
296 iconv_convstr(vcp->vc_toserver, pbuf, pbuf/*,
297 SMB_MAXPASSWORDLEN*/);
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 strncpy(pbuf, smb_vc_getpass(vcp),
305 SMB_MAXPASSWORDLEN);
306 pbuf[SMB_MAXPASSWORDLEN] = '\0';
307 } else
308 iconv_convstr(vcp->vc_toserver, pbuf,
309 smb_vc_getpass(vcp)/*,
310 SMB_MAXPASSWORDLEN*/);
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, (caddr_t)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_p == vcp->vc_iod->iod_p);
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)/*,
511 SMB_MAXPASSWORDLEN*/);
512 } else {
513 strncpy(pbuf, smb_share_getpass(ssp),
514 SMB_MAXPASSWORDLEN);
515 pbuf[SMB_MAXPASSWORDLEN] = '\0';
516 }
517 if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
518 plen = 24;
519 smb_encrypt(pbuf, vcp->vc_ch, encpass);
520 pp = encpass;
521 } else {
522 plen = strlen(pbuf) + 1;
523 pp = pbuf;
524 }
525 }
526 mbp = &rqp->sr_rq;
527 smb_rq_wstart(rqp);
528 mb_put_uint8(mbp, 0xff);
529 mb_put_uint8(mbp, 0);
530 mb_put_uint16le(mbp, 0);
531 mb_put_uint16le(mbp, 0); /* Flags */
532 mb_put_uint16le(mbp, plen);
533 smb_rq_wend(rqp);
534 smb_rq_bstart(rqp);
535 mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
536 smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
537 pp = vcp->vc_srvname;
538 smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
539 smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
540 pp = ssp->ss_name;
541 smb_put_dstring(mbp, vcp, pp, caseopt);
542 pp = smb_share_typename(ssp->ss_type);
543 smb_put_dstring(mbp, vcp, pp, caseopt);
544 smb_rq_bend(rqp);
545 error = smb_rq_simple(rqp);
546 SMBSDEBUG("%d\n", error);
547 if (error)
548 goto bad;
549 ssp->ss_tid = rqp->sr_rptid;
550 ssp->ss_vcgenid = vcp->vc_genid;
551 ssp->ss_flags |= SMBS_CONNECTED;
552 bad:
553 if (encpass)
554 free(encpass, M_SMBTEMP);
555 if (pbuf)
556 free(pbuf, M_SMBTEMP);
557 smb_rq_done(rqp);
558 if (error && !upper) {
559 upper = 1;
560 goto again;
561 }
562 return error;
563 }
564
565 int
566 smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
567 {
568 struct smb_rq *rqp;
569 int error;
570
571 if (ssp->ss_tid == SMB_TID_UNKNOWN)
572 return 0;
573 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
574 if (error)
575 return error;
576 smb_rq_wstart(rqp);
577 smb_rq_wend(rqp);
578 smb_rq_bstart(rqp);
579 smb_rq_bend(rqp);
580 error = smb_rq_simple(rqp);
581 SMBSDEBUG("%d\n", error);
582 smb_rq_done(rqp);
583 ssp->ss_tid = SMB_TID_UNKNOWN;
584 return error;
585 }
586
587 static __inline int
588 smb_smb_readx(struct smb_share *ssp, u_int16_t fid, size_t *len, size_t *rresid,
589 struct uio *uio, struct smb_cred *scred)
590 {
591 struct smb_rq *rqp;
592 struct mbchain *mbp;
593 struct mdchain *mdp;
594 u_int8_t wc;
595 int error;
596 u_int16_t residhi, residlo, off, doff;
597 u_int32_t resid;
598
599 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
600 if (error)
601 return error;
602 smb_rq_getrequest(rqp, &mbp);
603 smb_rq_wstart(rqp);
604 mb_put_uint8(mbp, 0xff); /* no secondary command */
605 mb_put_uint8(mbp, 0); /* MBZ */
606 mb_put_uint16le(mbp, 0); /* offset to secondary */
607 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
608 mb_put_uint32le(mbp, uio->uio_offset);
609 *len = min(SSTOVC(ssp)->vc_rxmax, *len);
610 mb_put_uint16le(mbp, *len); /* MaxCount */
611 mb_put_uint16le(mbp, *len); /* MinCount (only indicates blocking) */
612 mb_put_uint32le(mbp, *len >> 16); /* MaxCountHigh */
613 mb_put_uint16le(mbp, *len); /* Remaining ("obsolete") */
614 mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */
615 smb_rq_wend(rqp);
616 smb_rq_bstart(rqp);
617 smb_rq_bend(rqp);
618 do {
619 error = smb_rq_simple(rqp);
620 if (error)
621 break;
622 smb_rq_getreply(rqp, &mdp);
623 off = SMB_HDRLEN;
624 md_get_uint8(mdp, &wc);
625 off++;
626 if (wc != 12) {
627 error = EBADRPC;
628 break;
629 }
630 md_get_uint8(mdp, NULL);
631 off++;
632 md_get_uint8(mdp, NULL);
633 off++;
634 md_get_uint16(mdp, NULL);
635 off += 2;
636 md_get_uint16(mdp, NULL);
637 off += 2;
638 md_get_uint16(mdp, NULL); /* data compaction mode */
639 off += 2;
640 md_get_uint16(mdp, NULL);
641 off += 2;
642 md_get_uint16le(mdp, &residlo);
643 off += 2;
644 md_get_uint16le(mdp, &doff); /* data offset */
645 off += 2;
646 md_get_uint16le(mdp, &residhi);
647 off += 2;
648 resid = (residhi << 16) | residlo;
649 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
650 off += 4*2;
651 md_get_uint16(mdp, NULL); /* ByteCount */
652 off += 2;
653 if (doff > off) /* pad byte(s)? */
654 md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
655 if (resid == 0) {
656 *rresid = resid;
657 break;
658 }
659 error = md_get_uio(mdp, uio, resid);
660 if (error)
661 break;
662 *rresid = resid;
663 } while(0);
664 smb_rq_done(rqp);
665 return (error);
666 }
667
668 static __inline int
669 smb_smb_writex(struct smb_share *ssp, u_int16_t fid, size_t *len, size_t *rresid,
670 struct uio *uio, struct smb_cred *scred)
671 {
672 struct smb_rq *rqp;
673 struct mbchain *mbp;
674 struct mdchain *mdp;
675 int error;
676 u_int8_t wc;
677 u_int16_t resid;
678
679 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
680 if (error)
681 return (error);
682 smb_rq_getrequest(rqp, &mbp);
683 smb_rq_wstart(rqp);
684 mb_put_uint8(mbp, 0xff); /* no secondary command */
685 mb_put_uint8(mbp, 0); /* MBZ */
686 mb_put_uint16le(mbp, 0); /* offset to secondary */
687 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
688 mb_put_uint32le(mbp, uio->uio_offset);
689 mb_put_uint32le(mbp, 0); /* MBZ (timeout) */
690 mb_put_uint16le(mbp, 0); /* !write-thru */
691 mb_put_uint16le(mbp, 0);
692 *len = min(SSTOVC(ssp)->vc_wxmax, *len);
693 mb_put_uint16le(mbp, *len >> 16);
694 mb_put_uint16le(mbp, *len);
695 mb_put_uint16le(mbp, 64); /* data offset from header start */
696 mb_put_uint32le(mbp, uio->uio_offset >> 32); /* OffsetHigh */
697 smb_rq_wend(rqp);
698 smb_rq_bstart(rqp);
699 do {
700 mb_put_uint8(mbp, 0xee); /* mimic xp pad byte! */
701 error = mb_put_uio(mbp, uio, *len);
702 if (error)
703 break;
704 smb_rq_bend(rqp);
705 error = smb_rq_simple(rqp);
706 if (error)
707 break;
708 smb_rq_getreply(rqp, &mdp);
709 md_get_uint8(mdp, &wc);
710 if (wc != 6) {
711 error = EBADRPC;
712 break;
713 }
714 md_get_uint8(mdp, NULL);
715 md_get_uint8(mdp, NULL);
716 md_get_uint16(mdp, NULL);
717 md_get_uint16le(mdp, &resid);
718 *rresid = resid;
719 } while(0);
720
721 smb_rq_done(rqp);
722 return (error);
723 }
724
725 static __inline int
726 smb_smb_read(struct smb_share *ssp, u_int16_t fid,
727 size_t *len, size_t *rresid, struct uio *uio, struct smb_cred *scred)
728 {
729 struct smb_rq *rqp;
730 struct mbchain *mbp;
731 struct mdchain *mdp;
732 u_int16_t resid, bc;
733 u_int8_t wc;
734 int error, rlen, blksz;
735
736 /* Cannot read at/beyond 4G */
737 if (uio->uio_offset >= (1LL << 32))
738 return (EFBIG);
739
740 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
741 if (error)
742 return error;
743
744 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
745 rlen = *len = min(blksz, *len);
746
747 smb_rq_getrequest(rqp, &mbp);
748 smb_rq_wstart(rqp);
749 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
750 mb_put_uint16le(mbp, rlen);
751 mb_put_uint32le(mbp, uio->uio_offset);
752 mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
753 smb_rq_wend(rqp);
754 smb_rq_bstart(rqp);
755 smb_rq_bend(rqp);
756 do {
757 error = smb_rq_simple(rqp);
758 if (error)
759 break;
760 smb_rq_getreply(rqp, &mdp);
761 md_get_uint8(mdp, &wc);
762 if (wc != 5) {
763 error = EBADRPC;
764 break;
765 }
766 md_get_uint16le(mdp, &resid);
767 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
768 md_get_uint16le(mdp, &bc);
769 md_get_uint8(mdp, NULL); /* ignore buffer type */
770 md_get_uint16le(mdp, &resid);
771 if (resid == 0) {
772 *rresid = resid;
773 break;
774 }
775 error = md_get_uio(mdp, uio, resid);
776 if (error)
777 break;
778 *rresid = resid;
779 } while(0);
780 smb_rq_done(rqp);
781 return error;
782 }
783
784 int
785 smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
786 struct smb_cred *scred)
787 {
788 size_t tsize, len, resid;
789 int error = 0;
790 int rx = (SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_READX);
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 tsize = uio->uio_resid;
872 while (tsize > 0) {
873 len = tsize;
874 if (wx)
875 error = smb_smb_writex(ssp, fid, &len, &resid, uio, scred);
876 else
877 error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
878 if (error)
879 break;
880 if (resid < len) {
881 error = EIO;
882 break;
883 }
884 tsize -= resid;
885 }
886 return error;
887 }
888
889 #if 0
890 int
891 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
892 {
893 struct smb_rq *rqp;
894 struct mbchain *mbp;
895 int error;
896
897 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
898 if (error)
899 return error;
900 mbp = &rqp->sr_rq;
901 smb_rq_wstart(rqp);
902 mb_put_uint16le(mbp, 1);
903 smb_rq_wend(rqp);
904 smb_rq_bstart(rqp);
905 mb_put_uint32le(mbp, 0);
906 smb_rq_bend(rqp);
907 error = smb_rq_simple(rqp);
908 SMBSDEBUG("%d\n", error);
909 smb_rq_done(rqp);
910 return error;
911 }
912 #endif
Cache object: b94ba9b2ec0d8772e3b38e79590fa10d
|