1 /* 2 * Copyright (c) 2000-2001 Boris Popov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Boris Popov. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $FreeBSD: src/sys/netsmb/smb_smb.c,v 1.1.2.3 2002/12/14 14:44:19 fjoe Exp $ 33 */ 34 /* 35 * various SMB requests. Most of the routines merely packs data into mbufs. 36 */ 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/malloc.h> 41 #include <sys/proc.h> 42 #include <sys/lock.h> 43 #include <sys/sysctl.h> 44 #include <sys/socket.h> 45 #include <sys/uio.h> 46 47 #include <sys/iconv.h> 48 #include <machine/limits.h> 49 50 #include "smb.h" 51 #include "smb_subr.h" 52 #include "smb_rq.h" 53 #include "smb_conn.h" 54 #include "smb_tran.h" 55 56 struct smb_dialect { 57 int d_id; 58 const char * d_name; 59 }; 60 61 static struct smb_dialect smb_dialects[] = { 62 {SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"}, 63 {SMB_DIALECT_COREPLUS, "MICROSOFT NETWORKS 1.03"}, 64 {SMB_DIALECT_LANMAN1_0, "MICROSOFT NETWORKS 3.0"}, 65 {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"}, 66 {SMB_DIALECT_LANMAN2_0, "LM1.2X002"}, 67 {SMB_DIALECT_LANMAN2_0, "Samba"}, 68 {SMB_DIALECT_NTLM0_12, "NT LANMAN 1.0"}, 69 {SMB_DIALECT_NTLM0_12, "NT LM 0.12"}, 70 {-1, NULL} 71 }; 72 73 #define SMB_DIALECT_MAX (NELEM(smb_dialects) - 2) 74 75 static int 76 smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name) 77 { 78 if (scred->scr_td == vcp->vc_iod->iod_td) 79 return 0; 80 SMBERROR("wrong function called(%s)\n", name); 81 return EINVAL; 82 } 83 84 int 85 smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) 86 { 87 struct smb_dialect *dp; 88 struct smb_sopt *sp = NULL; 89 struct smb_rq *rqp; 90 struct mbchain *mbp; 91 struct mdchain *mdp; 92 u_int8_t wc, stime[8], sblen; 93 u_int16_t dindex, tw, tw1, swlen, bc; 94 int error, maxqsz; 95 96 if (smb_smb_nomux(vcp, scred, __func__) != 0) 97 return EINVAL; 98 vcp->vc_hflags = 0; 99 vcp->vc_hflags2 = 0; 100 vcp->obj.co_flags &= ~(SMBV_ENCRYPT); 101 sp = &vcp->vc_sopt; 102 bzero(sp, sizeof(struct smb_sopt)); 103 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp); 104 if (error) 105 return error; 106 smb_rq_getrequest(rqp, &mbp); 107 smb_rq_wstart(rqp); 108 smb_rq_wend(rqp); 109 smb_rq_bstart(rqp); 110 for(dp = smb_dialects; dp->d_id != -1; dp++) { 111 mb_put_uint8(mbp, SMB_DT_DIALECT); 112 smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE); 113 } 114 smb_rq_bend(rqp); 115 error = smb_rq_simple(rqp); 116 SMBSDEBUG("%d\n", error); 117 if (error) 118 goto bad; 119 smb_rq_getreply(rqp, &mdp); 120 do { 121 error = md_get_uint8(mdp, &wc); 122 if (error) 123 break; 124 error = md_get_uint16le(mdp, &dindex); 125 if (error) 126 break; 127 if (dindex > 7) { 128 SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex); 129 error = EBADRPC; 130 break; 131 } 132 dp = smb_dialects + dindex; 133 sp->sv_proto = dp->d_id; 134 SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc); 135 error = EBADRPC; 136 if (dp->d_id >= SMB_DIALECT_NTLM0_12) { 137 if (wc != 17) 138 break; 139 md_get_uint8(mdp, &sp->sv_sm); 140 md_get_uint16le(mdp, &sp->sv_maxmux); 141 md_get_uint16le(mdp, &sp->sv_maxvcs); 142 md_get_uint32le(mdp, &sp->sv_maxtx); 143 md_get_uint32le(mdp, &sp->sv_maxraw); 144 md_get_uint32le(mdp, &sp->sv_skey); 145 md_get_uint32le(mdp, &sp->sv_caps); 146 md_get_mem(mdp, stime, 8, MB_MSYSTEM); 147 md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); 148 md_get_uint8(mdp, &sblen); 149 if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) { 150 if (sblen != SMB_MAXCHALLENGELEN) { 151 SMBERROR("Unexpected length of security blob (%d)\n", sblen); 152 break; 153 } 154 error = md_get_uint16(mdp, &bc); 155 if (error) 156 break; 157 if (sp->sv_caps & SMB_CAP_EXT_SECURITY) 158 md_get_mem(mdp, NULL, 16, MB_MSYSTEM); 159 error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM); 160 if (error) 161 break; 162 vcp->vc_chlen = sblen; 163 vcp->obj.co_flags |= SMBV_ENCRYPT; 164 } 165 vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; 166 if (dp->d_id == SMB_DIALECT_NTLM0_12 && 167 sp->sv_maxtx < 4096 && 168 (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) { 169 vcp->obj.co_flags |= SMBV_WIN95; 170 SMBSDEBUG("Win95 detected\n"); 171 } 172 } else if (dp->d_id > SMB_DIALECT_CORE) { 173 md_get_uint16le(mdp, &tw); 174 sp->sv_sm = tw; 175 md_get_uint16le(mdp, &tw); 176 sp->sv_maxtx = tw; 177 md_get_uint16le(mdp, &sp->sv_maxmux); 178 md_get_uint16le(mdp, &sp->sv_maxvcs); 179 md_get_uint16le(mdp, &tw); /* rawmode */ 180 md_get_uint32le(mdp, &sp->sv_skey); 181 if (wc == 13) { /* >= LANMAN1 */ 182 md_get_uint16(mdp, &tw); /* time */ 183 md_get_uint16(mdp, &tw1); /* date */ 184 md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); 185 md_get_uint16le(mdp, &swlen); 186 if (swlen > SMB_MAXCHALLENGELEN) 187 break; 188 md_get_uint16(mdp, NULL); /* mbz */ 189 if (md_get_uint16(mdp, &bc) != 0) 190 break; 191 if (bc < swlen) 192 break; 193 if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) { 194 error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM); 195 if (error) 196 break; 197 vcp->vc_chlen = swlen; 198 vcp->obj.co_flags |= SMBV_ENCRYPT; 199 } 200 } 201 vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; 202 } else { /* an old CORE protocol */ 203 sp->sv_maxmux = 1; 204 } 205 error = 0; 206 } while (0); 207 if (error == 0) { 208 vcp->vc_maxvcs = sp->sv_maxvcs; 209 if (vcp->vc_maxvcs <= 1) { 210 if (vcp->vc_maxvcs == 0) 211 vcp->vc_maxvcs = 1; 212 } 213 if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff) 214 sp->sv_maxtx = 1024; 215 SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz); 216 vcp->vc_txmax = min(sp->sv_maxtx, maxqsz); 217 SMBSDEBUG("TZ = %d\n", sp->sv_tz); 218 SMBSDEBUG("CAPS = %x\n", sp->sv_caps); 219 SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux); 220 SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs); 221 SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw); 222 SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx); 223 } 224 bad: 225 smb_rq_done(rqp); 226 return error; 227 } 228 229 int 230 smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) 231 { 232 struct smb_rq *rqp; 233 struct mbchain *mbp; 234 /* u_int8_t wc; 235 u_int16_t tw, tw1;*/ 236 smb_uniptr unipp, ntencpass = NULL; 237 char *pp, *up, *pbuf, *encpass; 238 int error, plen, uniplen, ulen; 239 240 vcp->vc_smbuid = SMB_UID_UNKNOWN; 241 242 if (smb_smb_nomux(vcp, scred, __func__) != 0) 243 return EINVAL; 244 245 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp); 246 if (error) 247 return error; 248 pbuf = kmalloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK); 249 encpass = kmalloc(24, M_SMBTEMP, M_WAITOK); 250 if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { 251 iconv_convstr(vcp->vc_toupper, pbuf, smb_vc_getpass(vcp)); 252 iconv_convstr(vcp->vc_toserver, pbuf, pbuf); 253 if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { 254 uniplen = plen = 24; 255 smb_encrypt(pbuf, vcp->vc_ch, encpass); 256 ntencpass = kmalloc(uniplen, M_SMBTEMP, M_WAITOK); 257 iconv_convstr(vcp->vc_toserver, pbuf, smb_vc_getpass(vcp)); 258 smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass); 259 pp = encpass; 260 unipp = ntencpass; 261 } else { 262 plen = strlen(pbuf) + 1; 263 pp = pbuf; 264 uniplen = plen * 2; 265 ntencpass = kmalloc(uniplen, M_SMBTEMP, M_WAITOK); 266 smb_strtouni(ntencpass, smb_vc_getpass(vcp)); 267 plen--; 268 269 /* 270 * The uniplen is zeroed because Samba cannot deal 271 * with this 2nd cleartext password. This Samba 272 * "bug" is actually a workaround for problems in 273 * Microsoft clients. 274 */ 275 uniplen = 0/*-= 2*/; 276 unipp = ntencpass; 277 } 278 } else { 279 /* 280 * In the share security mode password will be used 281 * only in the tree authentication 282 */ 283 pp = ""; 284 plen = 1; 285 unipp = &smb_unieol; 286 uniplen = 0 /* sizeof(smb_unieol) */; 287 } 288 smb_rq_wstart(rqp); 289 mbp = &rqp->sr_rq; 290 up = vcp->vc_username; 291 ulen = strlen(up) + 1; 292 mb_put_uint8(mbp, 0xff); 293 mb_put_uint8(mbp, 0); 294 mb_put_uint16le(mbp, 0); 295 mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx); 296 mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux); 297 mb_put_uint16le(mbp, vcp->vc_number); 298 mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey); 299 mb_put_uint16le(mbp, plen); 300 if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) { 301 mb_put_uint32le(mbp, 0); 302 smb_rq_wend(rqp); 303 smb_rq_bstart(rqp); 304 mb_put_mem(mbp, pp, plen, MB_MSYSTEM); 305 smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); 306 } else { 307 mb_put_uint16le(mbp, uniplen); 308 mb_put_uint32le(mbp, 0); /* reserved */ 309 mb_put_uint32le(mbp, vcp->obj.co_flags & SMBV_UNICODE ? 310 SMB_CAP_UNICODE : 0); 311 smb_rq_wend(rqp); 312 smb_rq_bstart(rqp); 313 mb_put_mem(mbp, pp, plen, MB_MSYSTEM); 314 mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM); 315 smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* AccountName */ 316 smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE); /* PrimaryDomain */ 317 smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE); /* Client's OS */ 318 smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE); /* Client name */ 319 } 320 smb_rq_bend(rqp); 321 if (ntencpass) 322 kfree(ntencpass, M_SMBTEMP); 323 error = smb_rq_simple(rqp); 324 SMBSDEBUG("%d\n", error); 325 if (error) { 326 if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess) 327 error = EAUTH; 328 goto bad; 329 } 330 vcp->vc_smbuid = rqp->sr_rpuid; 331 bad: 332 kfree(encpass, M_SMBTEMP); 333 kfree(pbuf, M_SMBTEMP); 334 smb_rq_done(rqp); 335 return error; 336 } 337 338 int 339 smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred) 340 { 341 struct smb_rq *rqp; 342 struct mbchain *mbp; 343 int error; 344 345 if (vcp->vc_smbuid == SMB_UID_UNKNOWN) 346 return 0; 347 348 if (smb_smb_nomux(vcp, scred, __func__) != 0) 349 return EINVAL; 350 351 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp); 352 if (error) 353 return error; 354 mbp = &rqp->sr_rq; 355 smb_rq_wstart(rqp); 356 mb_put_uint8(mbp, 0xff); 357 mb_put_uint8(mbp, 0); 358 mb_put_uint16le(mbp, 0); 359 smb_rq_wend(rqp); 360 smb_rq_bstart(rqp); 361 smb_rq_bend(rqp); 362 error = smb_rq_simple(rqp); 363 SMBSDEBUG("%d\n", error); 364 smb_rq_done(rqp); 365 return error; 366 } 367 368 static char smb_any_share[] = "?????"; 369 370 static char * 371 smb_share_typename(int stype) 372 { 373 char *pp; 374 375 switch (stype) { 376 case SMB_ST_DISK: 377 pp = "A:"; 378 break; 379 case SMB_ST_PRINTER: 380 pp = smb_any_share; /* can't use LPT: here... */ 381 break; 382 case SMB_ST_PIPE: 383 pp = "IPC"; 384 break; 385 case SMB_ST_COMM: 386 pp = "COMM"; 387 break; 388 case SMB_ST_ANY: 389 default: 390 pp = smb_any_share; 391 break; 392 } 393 return pp; 394 } 395 396 int 397 smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) 398 { 399 struct smb_vc *vcp; 400 struct smb_rq rq, *rqp = &rq; 401 struct mbchain *mbp; 402 char *pp, *pbuf, *encpass; 403 int error, plen, caseopt; 404 405 ssp->ss_tid = SMB_TID_UNKNOWN; 406 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp); 407 if (error) 408 return error; 409 vcp = rqp->sr_vc; 410 caseopt = SMB_CS_NONE; 411 if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { 412 plen = 1; 413 pp = ""; 414 pbuf = NULL; 415 encpass = NULL; 416 } else { 417 pbuf = kmalloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK); 418 encpass = kmalloc(24, M_SMBTEMP, M_WAITOK); 419 iconv_convstr(vcp->vc_toupper, pbuf, smb_share_getpass(ssp)); 420 iconv_convstr(vcp->vc_toserver, pbuf, pbuf); 421 if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { 422 plen = 24; 423 smb_encrypt(pbuf, vcp->vc_ch, encpass); 424 pp = encpass; 425 } else { 426 plen = strlen(pbuf) + 1; 427 pp = pbuf; 428 } 429 } 430 mbp = &rqp->sr_rq; 431 smb_rq_wstart(rqp); 432 mb_put_uint8(mbp, 0xff); 433 mb_put_uint8(mbp, 0); 434 mb_put_uint16le(mbp, 0); 435 mb_put_uint16le(mbp, 0); /* Flags */ 436 mb_put_uint16le(mbp, plen); 437 smb_rq_wend(rqp); 438 smb_rq_bstart(rqp); 439 mb_put_mem(mbp, pp, plen, MB_MSYSTEM); 440 smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt); 441 pp = vcp->vc_srvname; 442 smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt); 443 smb_put_dmem(mbp, vcp, "\\", 1, caseopt); 444 pp = ssp->ss_name; 445 smb_put_dstring(mbp, vcp, pp, caseopt); 446 pp = smb_share_typename(ssp->ss_type); 447 smb_put_dstring(mbp, vcp, pp, caseopt); 448 smb_rq_bend(rqp); 449 error = smb_rq_simple(rqp); 450 SMBSDEBUG("%d\n", error); 451 if (error) 452 goto bad; 453 ssp->ss_tid = rqp->sr_rptid; 454 ssp->ss_vcgenid = vcp->vc_genid; 455 ssp->ss_flags |= SMBS_CONNECTED; 456 bad: 457 if (encpass) 458 kfree(encpass, M_SMBTEMP); 459 if (pbuf) 460 kfree(pbuf, M_SMBTEMP); 461 smb_rq_done(rqp); 462 return error; 463 } 464 465 int 466 smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) 467 { 468 struct smb_rq *rqp; 469 struct mbchain *mbp; 470 int error; 471 472 if (ssp->ss_tid == SMB_TID_UNKNOWN) 473 return 0; 474 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp); 475 if (error) 476 return error; 477 mbp = &rqp->sr_rq; 478 smb_rq_wstart(rqp); 479 smb_rq_wend(rqp); 480 smb_rq_bstart(rqp); 481 smb_rq_bend(rqp); 482 error = smb_rq_simple(rqp); 483 SMBSDEBUG("%d\n", error); 484 smb_rq_done(rqp); 485 ssp->ss_tid = SMB_TID_UNKNOWN; 486 return error; 487 } 488 489 static __inline int 490 smb_smb_read(struct smb_share *ssp, u_int16_t fid, 491 int *len, int *rresid, struct uio *uio, struct smb_cred *scred) 492 { 493 struct smb_rq *rqp; 494 struct mbchain *mbp; 495 struct mdchain *mdp; 496 u_int16_t resid, bc; 497 u_int8_t wc; 498 int error, rlen, blksz; 499 500 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp); 501 if (error) 502 return error; 503 504 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16; 505 rlen = *len = min(blksz, *len); 506 507 smb_rq_getrequest(rqp, &mbp); 508 smb_rq_wstart(rqp); 509 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); 510 mb_put_uint16le(mbp, rlen); 511 mb_put_uint32le(mbp, uio->uio_offset); 512 mb_put_uint16le(mbp, (unsigned short)szmin(uio->uio_resid, 0xffff)); 513 smb_rq_wend(rqp); 514 smb_rq_bstart(rqp); 515 smb_rq_bend(rqp); 516 do { 517 error = smb_rq_simple(rqp); 518 if (error) 519 break; 520 smb_rq_getreply(rqp, &mdp); 521 md_get_uint8(mdp, &wc); 522 if (wc != 5) { 523 error = EBADRPC; 524 break; 525 } 526 md_get_uint16le(mdp, &resid); 527 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); 528 md_get_uint16le(mdp, &bc); 529 md_get_uint8(mdp, NULL); /* ignore buffer type */ 530 md_get_uint16le(mdp, &resid); 531 if (resid == 0) { 532 *rresid = resid; 533 break; 534 } 535 error = md_get_uio(mdp, uio, resid); 536 if (error) 537 break; 538 *rresid = resid; 539 } while(0); 540 smb_rq_done(rqp); 541 return error; 542 } 543 544 int 545 smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio, 546 struct smb_cred *scred) 547 { 548 int len, resid; 549 int error = 0; 550 551 while (uio->uio_resid > 0) { 552 if (uio->uio_resid > INT_MAX) 553 len = INT_MAX; 554 else 555 len = (int)uio->uio_resid; 556 error = smb_smb_read(ssp, fid, &len, &resid, uio, scred); 557 if (error) 558 break; 559 if (resid < len) 560 break; 561 } 562 return error; 563 } 564 565 static __inline int 566 smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, 567 struct uio *uio, struct smb_cred *scred) 568 { 569 struct smb_rq *rqp; 570 struct mbchain *mbp; 571 struct mdchain *mdp; 572 u_int16_t resid; 573 u_int8_t wc; 574 int error, blksz; 575 576 /* write data must be real */ 577 KKASSERT(uio->uio_segflg != UIO_NOCOPY); 578 579 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16; 580 if (blksz > 0xffff) 581 blksz = 0xffff; 582 583 resid = *len = min(blksz, *len); 584 585 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp); 586 if (error) 587 return error; 588 smb_rq_getrequest(rqp, &mbp); 589 smb_rq_wstart(rqp); 590 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM); 591 mb_put_uint16le(mbp, resid); 592 mb_put_uint32le(mbp, uio->uio_offset); 593 mb_put_uint16le(mbp, (unsigned short)szmin(uio->uio_resid, 0xffff)); 594 smb_rq_wend(rqp); 595 smb_rq_bstart(rqp); 596 mb_put_uint8(mbp, SMB_DT_DATA); 597 mb_put_uint16le(mbp, resid); 598 do { 599 error = mb_put_uio(mbp, uio, resid); 600 if (error) 601 break; 602 smb_rq_bend(rqp); 603 error = smb_rq_simple(rqp); 604 if (error) 605 break; 606 smb_rq_getreply(rqp, &mdp); 607 md_get_uint8(mdp, &wc); 608 if (wc != 1) { 609 error = EBADRPC; 610 break; 611 } 612 md_get_uint16le(mdp, &resid); 613 *rresid = resid; 614 } while(0); 615 smb_rq_done(rqp); 616 return error; 617 } 618 619 int 620 smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio, 621 struct smb_cred *scred) 622 { 623 int error = 0, len, resid; 624 struct uio olduio; 625 626 olduio = *uio; 627 while (uio->uio_resid > 0) { 628 if (uio->uio_resid > INT_MAX) 629 len = INT_MAX; 630 else 631 len = (int)uio->uio_resid; 632 error = smb_smb_write(ssp, fid, &len, &resid, uio, scred); 633 if (error) 634 break; 635 if (resid < len) { 636 error = EIO; 637 break; 638 } 639 } 640 if (error) { 641 /* 642 * Errors can happen on the copyin, the rpc, etc. So they 643 * imply resid is unreliable. The only safe thing is 644 * to pretend zero bytes made it. We needn't restore the 645 * iovs because callers don't depend on them in error 646 * paths - uio_resid and uio_offset are what matter. 647 */ 648 *uio = olduio; 649 } 650 return error; 651 } 652 653 int 654 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred) 655 { 656 struct smb_rq *rqp; 657 struct mbchain *mbp; 658 int error; 659 660 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp); 661 if (error) 662 return error; 663 mbp = &rqp->sr_rq; 664 smb_rq_wstart(rqp); 665 mb_put_uint16le(mbp, 1); 666 smb_rq_wend(rqp); 667 smb_rq_bstart(rqp); 668 mb_put_uint32le(mbp, 0); 669 smb_rq_bend(rqp); 670 error = smb_rq_simple(rqp); 671 SMBSDEBUG("%d\n", error); 672 smb_rq_done(rqp); 673 return error; 674 } 675