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