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 * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $ 33 */ 34 35 /* 36 * Copyright 2011 Nexenta Systems, Inc. All rights reserved. 37 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 38 * Use is subject to license terms. 39 */ 40 41 #include <sys/param.h> 42 #include <sys/kmem.h> 43 #include <sys/systm.h> 44 #include <sys/policy.h> 45 #include <sys/conf.h> 46 #include <sys/proc.h> 47 #include <sys/fcntl.h> 48 #include <sys/file.h> 49 #include <sys/socket.h> 50 #include <sys/sunddi.h> 51 #include <sys/cmn_err.h> 52 53 #include <netsmb/smb_osdep.h> 54 55 #include <netsmb/smb.h> 56 #include <netsmb/smb_conn.h> 57 #include <netsmb/smb_rq.h> 58 #include <netsmb/smb_subr.h> 59 #include <netsmb/smb_dev.h> 60 61 static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg); 62 63 /* 64 * Ioctl function for SMBIOC_FLAGS2 65 */ 66 int 67 smb_usr_get_flags2(smb_dev_t *sdp, intptr_t arg, int flags) 68 { 69 struct smb_vc *vcp = NULL; 70 71 /* This ioctl requires a session. */ 72 if ((vcp = sdp->sd_vc) == NULL) 73 return (ENOTCONN); 74 75 /* 76 * Return the flags2 value. 77 */ 78 if (ddi_copyout(&vcp->vc_hflags2, (void *)arg, 79 sizeof (u_int16_t), flags)) 80 return (EFAULT); 81 82 return (0); 83 } 84 85 /* 86 * Ioctl function for SMBIOC_GETSSNKEY 87 * Size copied out is SMBIOC_HASH_SZ. 88 * 89 * The RPC library needs this for encrypting things 90 * like "set password" requests. This is called 91 * with an active RPC binding, so the connection 92 * will already be active (but this checks). 93 */ 94 int 95 smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags) 96 { 97 struct smb_vc *vcp = NULL; 98 99 /* This ioctl requires an active session. */ 100 if ((vcp = sdp->sd_vc) == NULL) 101 return (ENOTCONN); 102 if (vcp->vc_state != SMBIOD_ST_VCACTIVE) 103 return (ENOTCONN); 104 105 /* 106 * Return the session key. 107 */ 108 if (ddi_copyout(vcp->vc_ssn_key, (void *)arg, 109 SMBIOC_HASH_SZ, flags)) 110 return (EFAULT); 111 112 return (0); 113 } 114 115 /* 116 * Ioctl function for SMBIOC_REQUEST 117 */ 118 int 119 smb_usr_simplerq(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) 120 { 121 struct smb_cred scred; 122 struct smb_share *ssp; 123 smbioc_rq_t *ioc = NULL; 124 struct smb_rq *rqp = NULL; 125 struct mbchain *mbp; 126 struct mdchain *mdp; 127 uint32_t rsz; 128 int err, mbseg; 129 130 /* This ioctl requires a share. */ 131 if ((ssp = sdp->sd_share) == NULL) 132 return (ENOTCONN); 133 134 smb_credinit(&scred, cr); 135 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); 136 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { 137 err = EFAULT; 138 goto out; 139 } 140 141 /* See ddi_copyin, ddi_copyout */ 142 mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER; 143 144 /* 145 * Lots of SMB commands could be safe, but 146 * these are the only ones used by libsmbfs. 147 */ 148 switch (ioc->ioc_cmd) { 149 /* These are OK */ 150 case SMB_COM_CLOSE: 151 case SMB_COM_FLUSH: 152 case SMB_COM_NT_CREATE_ANDX: 153 case SMB_COM_OPEN_PRINT_FILE: 154 case SMB_COM_CLOSE_PRINT_FILE: 155 break; 156 157 default: 158 err = EPERM; 159 goto out; 160 } 161 162 err = smb_rq_alloc(SSTOCP(ssp), ioc->ioc_cmd, &scred, &rqp); 163 if (err) 164 goto out; 165 166 mbp = &rqp->sr_rq; 167 err = mb_put_mem(mbp, ioc->ioc_tbuf, ioc->ioc_tbufsz, mbseg); 168 169 err = smb_rq_simple(rqp); 170 if (err == 0) { 171 /* 172 * This may have been an open, so save the 173 * generation ID of the share, which we 174 * check before trying read or write. 175 */ 176 sdp->sd_vcgenid = ssp->ss_vcgenid; 177 178 /* 179 * Have reply data. to copyout. 180 * SMB header already parsed. 181 */ 182 mdp = &rqp->sr_rp; 183 rsz = msgdsize(mdp->md_top) - SMB_HDRLEN; 184 if (ioc->ioc_rbufsz < rsz) { 185 err = EOVERFLOW; 186 goto out; 187 } 188 ioc->ioc_rbufsz = rsz; 189 err = md_get_mem(mdp, ioc->ioc_rbuf, rsz, mbseg); 190 if (err) 191 goto out; 192 193 } 194 195 ioc->ioc_errclass = rqp->sr_errclass; 196 ioc->ioc_serror = rqp->sr_serror; 197 ioc->ioc_error = rqp->sr_error; 198 (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); 199 200 out: 201 if (rqp != NULL) 202 smb_rq_done(rqp); /* free rqp */ 203 kmem_free(ioc, sizeof (*ioc)); 204 smb_credrele(&scred); 205 206 return (err); 207 208 } 209 210 /* 211 * Ioctl function for SMBIOC_T2RQ 212 */ 213 int 214 smb_usr_t2request(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) 215 { 216 struct smb_cred scred; 217 struct smb_share *ssp; 218 smbioc_t2rq_t *ioc = NULL; 219 struct smb_t2rq *t2p = NULL; 220 struct mdchain *mdp; 221 int err, len, mbseg; 222 223 /* This ioctl requires a share. */ 224 if ((ssp = sdp->sd_share) == NULL) 225 return (ENOTCONN); 226 227 smb_credinit(&scred, cr); 228 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); 229 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { 230 err = EFAULT; 231 goto out; 232 } 233 234 /* See ddi_copyin, ddi_copyout */ 235 mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER; 236 237 if (ioc->ioc_setupcnt > SMBIOC_T2RQ_MAXSETUP) { 238 err = EINVAL; 239 goto out; 240 } 241 242 /* 243 * Fill in the FID for libsmbfs transact named pipe. 244 */ 245 if (ioc->ioc_setupcnt > 1 && ioc->ioc_setup[1] == 0xFFFF) { 246 if (sdp->sd_vcgenid != ssp->ss_vcgenid) { 247 err = ESTALE; 248 goto out; 249 } 250 ioc->ioc_setup[1] = (uint16_t)sdp->sd_smbfid; 251 } 252 253 t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP); 254 err = smb_t2_init(t2p, SSTOCP(ssp), 255 ioc->ioc_setup, ioc->ioc_setupcnt, &scred); 256 if (err) 257 goto out; 258 t2p->t2_setupcount = ioc->ioc_setupcnt; 259 t2p->t2_setupdata = ioc->ioc_setup; 260 261 /* This ioc member is a fixed-size array. */ 262 if (ioc->ioc_name[0]) { 263 /* Get the name length - carefully! */ 264 ioc->ioc_name[SMBIOC_T2RQ_MAXNAME-1] = '\0'; 265 t2p->t_name_len = strlen(ioc->ioc_name); 266 t2p->t_name = ioc->ioc_name; 267 } 268 t2p->t2_maxscount = 0; 269 t2p->t2_maxpcount = ioc->ioc_rparamcnt; 270 t2p->t2_maxdcount = ioc->ioc_rdatacnt; 271 272 /* Transmit parameters */ 273 err = smb_cpdatain(&t2p->t2_tparam, 274 ioc->ioc_tparamcnt, ioc->ioc_tparam, mbseg); 275 if (err) 276 goto out; 277 278 /* Transmit data */ 279 err = smb_cpdatain(&t2p->t2_tdata, 280 ioc->ioc_tdatacnt, ioc->ioc_tdata, mbseg); 281 if (err) 282 goto out; 283 284 err = smb_t2_request(t2p); 285 286 /* Copyout returned parameters. */ 287 mdp = &t2p->t2_rparam; 288 if (err == 0 && mdp->md_top != NULL) { 289 /* User's buffer large enough? */ 290 len = m_fixhdr(mdp->md_top); 291 if (len > ioc->ioc_rparamcnt) { 292 err = EMSGSIZE; 293 goto out; 294 } 295 ioc->ioc_rparamcnt = (ushort_t)len; 296 err = md_get_mem(mdp, ioc->ioc_rparam, len, mbseg); 297 if (err) 298 goto out; 299 } else 300 ioc->ioc_rparamcnt = 0; 301 302 /* Copyout returned data. */ 303 mdp = &t2p->t2_rdata; 304 if (err == 0 && mdp->md_top != NULL) { 305 /* User's buffer large enough? */ 306 len = m_fixhdr(mdp->md_top); 307 if (len > ioc->ioc_rdatacnt) { 308 err = EMSGSIZE; 309 goto out; 310 } 311 ioc->ioc_rdatacnt = (ushort_t)len; 312 err = md_get_mem(mdp, ioc->ioc_rdata, len, mbseg); 313 if (err) 314 goto out; 315 } else 316 ioc->ioc_rdatacnt = 0; 317 318 ioc->ioc_errclass = t2p->t2_sr_errclass; 319 ioc->ioc_serror = t2p->t2_sr_serror; 320 ioc->ioc_error = t2p->t2_sr_error; 321 ioc->ioc_rpflags2 = t2p->t2_sr_rpflags2; 322 323 (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); 324 325 326 out: 327 if (t2p != NULL) { 328 /* Note: t2p->t_name no longer allocated */ 329 smb_t2_done(t2p); 330 kmem_free(t2p, sizeof (*t2p)); 331 } 332 kmem_free(ioc, sizeof (*ioc)); 333 smb_credrele(&scred); 334 335 return (err); 336 } 337 338 /* helper for _t2request */ 339 static int 340 smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg) 341 { 342 int error; 343 344 if (len == 0) 345 return (0); 346 error = mb_init(mbp); 347 if (error) 348 return (error); 349 return (mb_put_mem(mbp, data, len, mbseg)); 350 } 351 352 /* 353 * Helper for nsmb_ioctl cases 354 * SMBIOC_READ, SMBIOC_WRITE 355 */ 356 int 357 smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) 358 { 359 struct smb_cred scred; 360 struct smb_share *ssp; 361 smbioc_rw_t *ioc = NULL; 362 struct iovec aiov[1]; 363 struct uio auio; 364 uint16_t fh; 365 int err; 366 uio_rw_t rw; 367 368 /* This ioctl requires a share. */ 369 if ((ssp = sdp->sd_share) == NULL) 370 return (ENOTCONN); 371 372 /* After reconnect, force close+reopen */ 373 if (sdp->sd_vcgenid != ssp->ss_vcgenid) 374 return (ESTALE); 375 376 smb_credinit(&scred, cr); 377 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); 378 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { 379 err = EFAULT; 380 goto out; 381 } 382 383 switch (cmd) { 384 case SMBIOC_READ: 385 rw = UIO_READ; 386 break; 387 case SMBIOC_WRITE: 388 rw = UIO_WRITE; 389 break; 390 default: 391 err = ENODEV; 392 goto out; 393 } 394 395 /* 396 * If caller passes -1 in ioc_fh, then 397 * use the FID from SMBIOC_NTCREATE. 398 */ 399 if (ioc->ioc_fh == -1) 400 fh = (uint16_t)sdp->sd_smbfid; 401 else 402 fh = (uint16_t)ioc->ioc_fh; 403 404 aiov[0].iov_base = ioc->ioc_base; 405 aiov[0].iov_len = (size_t)ioc->ioc_cnt; 406 407 auio.uio_iov = aiov; 408 auio.uio_iovcnt = 1; 409 auio.uio_loffset = ioc->ioc_offset; 410 auio.uio_segflg = (flags & FKIOCTL) ? 411 UIO_SYSSPACE : UIO_USERSPACE; 412 auio.uio_fmode = 0; 413 auio.uio_resid = (size_t)ioc->ioc_cnt; 414 415 err = smb_rwuio(ssp, fh, rw, &auio, &scred, 0); 416 417 /* 418 * On return ioc_cnt holds the 419 * number of bytes transferred. 420 */ 421 ioc->ioc_cnt -= auio.uio_resid; 422 423 (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); 424 425 out: 426 kmem_free(ioc, sizeof (*ioc)); 427 smb_credrele(&scred); 428 429 return (err); 430 } 431 432 /* 433 * Helper for nsmb_ioctl case 434 * SMBIOC_NTCREATE 435 */ 436 int 437 smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) 438 { 439 struct smb_cred scred; 440 struct mbchain name_mb; 441 struct smb_share *ssp; 442 smbioc_ntcreate_t *ioc = NULL; 443 uint16_t fid; 444 int err, nmlen; 445 446 /* This ioctl requires a share. */ 447 if ((ssp = sdp->sd_share) == NULL) 448 return (ENOTCONN); 449 450 /* Must not be already open. */ 451 if (sdp->sd_smbfid != -1) 452 return (EINVAL); 453 454 mb_init(&name_mb); 455 smb_credinit(&scred, cr); 456 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); 457 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { 458 err = EFAULT; 459 goto out; 460 } 461 462 /* Build name_mb */ 463 ioc->ioc_name[SMBIOC_MAX_NAME-1] = '\0'; 464 nmlen = strnlen(ioc->ioc_name, SMBIOC_MAX_NAME-1); 465 err = smb_put_dmem(&name_mb, SSTOVC(ssp), 466 ioc->ioc_name, nmlen, 467 SMB_CS_NONE, NULL); 468 if (err != 0) 469 goto out; 470 471 /* Do the OtW open, save the FID. */ 472 err = smb_smb_ntcreate(ssp, &name_mb, 473 0, /* create flags */ 474 ioc->ioc_req_acc, 475 ioc->ioc_efattr, 476 ioc->ioc_share_acc, 477 ioc->ioc_open_disp, 478 ioc->ioc_creat_opts, 479 NTCREATEX_IMPERSONATION_IMPERSONATION, 480 &scred, 481 &fid, 482 NULL, 483 NULL); 484 if (err != 0) 485 goto out; 486 487 sdp->sd_smbfid = fid; 488 sdp->sd_vcgenid = ssp->ss_vcgenid; 489 490 out: 491 kmem_free(ioc, sizeof (*ioc)); 492 smb_credrele(&scred); 493 mb_done(&name_mb); 494 495 return (err); 496 } 497 498 /* 499 * Helper for nsmb_ioctl case 500 * SMBIOC_PRINTJOB 501 */ 502 int 503 smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) 504 { 505 struct smb_cred scred; 506 struct smb_share *ssp; 507 smbioc_printjob_t *ioc = NULL; 508 uint16_t fid; 509 int err; 510 511 /* This ioctl requires a share. */ 512 if ((ssp = sdp->sd_share) == NULL) 513 return (ENOTCONN); 514 515 /* The share must be a print queue. */ 516 if (ssp->ss_type != STYPE_PRINTQ) 517 return (EINVAL); 518 519 /* Must not be already open. */ 520 if (sdp->sd_smbfid != -1) 521 return (EINVAL); 522 523 smb_credinit(&scred, cr); 524 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); 525 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { 526 err = EFAULT; 527 goto out; 528 } 529 ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0'; 530 531 /* Do the OtW open, save the FID. */ 532 err = smb_smb_open_prjob(ssp, ioc->ioc_title, 533 ioc->ioc_setuplen, ioc->ioc_prmode, 534 &scred, &fid); 535 if (err != 0) 536 goto out; 537 538 sdp->sd_smbfid = fid; 539 sdp->sd_vcgenid = ssp->ss_vcgenid; 540 541 out: 542 kmem_free(ioc, sizeof (*ioc)); 543 smb_credrele(&scred); 544 545 return (err); 546 } 547 548 /* 549 * Helper for nsmb_ioctl case 550 * SMBIOC_CLOSEFH 551 */ 552 int 553 smb_usr_closefh(smb_dev_t *sdp, cred_t *cr) 554 { 555 struct smb_cred scred; 556 struct smb_share *ssp; 557 uint16_t fid; 558 int err; 559 560 /* This ioctl requires a share. */ 561 if ((ssp = sdp->sd_share) == NULL) 562 return (ENOTCONN); 563 564 if (sdp->sd_smbfid == -1) 565 return (0); 566 fid = (uint16_t)sdp->sd_smbfid; 567 sdp->sd_smbfid = -1; 568 569 smb_credinit(&scred, cr); 570 if (ssp->ss_type == STYPE_PRINTQ) 571 err = smb_smb_close_prjob(ssp, fid, &scred); 572 else 573 err = smb_smb_close(ssp, fid, NULL, &scred); 574 smb_credrele(&scred); 575 576 return (err); 577 } 578 579 /* 580 * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE 581 * Find or create a session (a.k.a. "VC" in here) 582 */ 583 int 584 smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) 585 { 586 struct smb_cred scred; 587 smbioc_ossn_t *ossn = NULL; 588 struct smb_vc *vcp = NULL; 589 int error = 0; 590 uid_t realuid; 591 592 /* Should be no VC */ 593 if (sdp->sd_vc != NULL) 594 return (EISCONN); 595 596 smb_credinit(&scred, cr); 597 ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP); 598 if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) { 599 error = EFAULT; 600 goto out; 601 } 602 603 /* 604 * Only superuser can specify a UID or GID. 605 */ 606 realuid = crgetruid(cr); 607 if (ossn->ssn_owner == SMBM_ANY_OWNER) 608 ossn->ssn_owner = realuid; 609 else { 610 /* 611 * Do we have the privilege to create with the 612 * specified uid? (does uid == cr->cr_uid, etc.) 613 */ 614 if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) { 615 error = EPERM; 616 goto out; 617 } 618 /* ossn->ssn_owner is OK */ 619 } 620 621 /* 622 * Make sure the strings are null terminated. 623 */ 624 ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0'; 625 ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0'; 626 ossn->ssn_id.id_user[ SMBIOC_MAX_NAME-1] = '\0'; 627 628 if (cmd == SMBIOC_SSN_CREATE) 629 ossn->ssn_vopt |= SMBVOPT_CREATE; 630 else /* FIND */ 631 ossn->ssn_vopt &= ~SMBVOPT_CREATE; 632 633 error = smb_vc_findcreate(ossn, &scred, &vcp); 634 if (error) 635 goto out; 636 ASSERT(vcp != NULL); 637 638 /* 639 * We have a VC, held, but not locked. 640 * If we're creating, mark this instance as 641 * an open from IOD so close can do cleanup. 642 * 643 * XXX: Would be nice to have a back pointer 644 * from the VC to this (IOD) sdp instance. 645 */ 646 if (cmd == SMBIOC_SSN_CREATE) { 647 if (vcp->iod_thr != NULL) { 648 error = EEXIST; 649 goto out; 650 } 651 sdp->sd_flags |= NSMBFL_IOD; 652 } else { 653 /* 654 * Wait for it to finish connecting 655 * (or reconnect) if necessary. 656 */ 657 if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { 658 error = smb_iod_reconnect(vcp); 659 if (error != 0) 660 goto out; 661 } 662 } 663 664 /* 665 * The VC has a hold from _findvc 666 * which we keep until _SSN_RELE 667 * or nsmb_close(). 668 */ 669 sdp->sd_level = SMBL_VC; 670 sdp->sd_vc = vcp; 671 vcp = NULL; 672 (void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags); 673 674 out: 675 if (vcp) { 676 /* Error path: rele hold from _findcreate */ 677 smb_vc_rele(vcp); 678 } 679 kmem_free(ossn, sizeof (*ossn)); 680 smb_credrele(&scred); 681 682 return (error); 683 } 684 685 /* 686 * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL 687 * Release or kill the current session. 688 */ 689 int 690 smb_usr_drop_ssn(smb_dev_t *sdp, int cmd) 691 { 692 struct smb_vc *vcp = NULL; 693 694 /* Must have a VC. */ 695 if ((vcp = sdp->sd_vc) == NULL) 696 return (ENOTCONN); 697 698 /* If we have a share ref, drop it too. */ 699 if (sdp->sd_share) { 700 smb_share_rele(sdp->sd_share); 701 sdp->sd_share = NULL; 702 sdp->sd_level = SMBL_VC; 703 } 704 705 if (cmd == SMBIOC_SSN_KILL) 706 smb_vc_kill(vcp); 707 708 /* Drop the VC ref. */ 709 smb_vc_rele(vcp); 710 sdp->sd_vc = NULL; 711 sdp->sd_level = 0; 712 713 return (0); 714 } 715 716 /* 717 * Find or create a tree (connected share) 718 */ 719 int 720 smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) 721 { 722 struct smb_cred scred; 723 smbioc_tcon_t *tcon = NULL; 724 struct smb_vc *vcp = NULL; 725 struct smb_share *ssp = NULL; 726 int error = 0; 727 728 /* Must have a VC. */ 729 if ((vcp = sdp->sd_vc) == NULL) 730 return (ENOTCONN); 731 /* Should not have a share. */ 732 if (sdp->sd_share != NULL) 733 return (EISCONN); 734 735 smb_credinit(&scred, cr); 736 tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP); 737 if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) { 738 error = EFAULT; 739 goto out; 740 } 741 742 /* 743 * Make sure the strings are null terminated. 744 */ 745 tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0'; 746 tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0'; 747 748 if (cmd == SMBIOC_TREE_CONNECT) 749 tcon->tc_opt |= SMBSOPT_CREATE; 750 else /* FIND */ 751 tcon->tc_opt &= ~SMBSOPT_CREATE; 752 753 error = smb_share_findcreate(tcon, vcp, &ssp, &scred); 754 if (error) 755 goto out; 756 ASSERT(ssp != NULL); 757 758 /* 759 * We have a share, held, but not locked. 760 * If we're creating, do tree connect now, 761 * otherwise let that wait for a request. 762 */ 763 if (cmd == SMBIOC_TREE_CONNECT) { 764 error = smb_share_tcon(ssp, &scred); 765 if (error) 766 goto out; 767 } 768 769 /* 770 * Give caller the real share type from 771 * the tree connect response, so they can 772 * see if they got the requested type. 773 */ 774 tcon->tc_sh.sh_type = ssp->ss_type; 775 776 /* 777 * The share has a hold from _tcon 778 * which we keep until nsmb_close() 779 * or the SMBIOC_TDIS below. 780 */ 781 sdp->sd_level = SMBL_SHARE; 782 sdp->sd_share = ssp; 783 ssp = NULL; 784 (void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags); 785 786 out: 787 if (ssp) { 788 /* Error path: rele hold from _findcreate */ 789 smb_share_rele(ssp); 790 } 791 /* 792 * This structure may contain a 793 * cleartext password, so zap it. 794 */ 795 bzero(tcon, sizeof (*tcon)); 796 kmem_free(tcon, sizeof (*tcon)); 797 smb_credrele(&scred); 798 799 return (error); 800 } 801 802 /* 803 * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL 804 * Release or kill the current tree 805 */ 806 int 807 smb_usr_drop_tree(smb_dev_t *sdp, int cmd) 808 { 809 struct smb_share *ssp = NULL; 810 811 /* Must have a VC and a share. */ 812 if (sdp->sd_vc == NULL) 813 return (ENOTCONN); 814 if ((ssp = sdp->sd_share) == NULL) 815 return (ENOTCONN); 816 817 if (cmd == SMBIOC_TREE_KILL) 818 smb_share_kill(ssp); 819 820 /* Drop the share ref. */ 821 smb_share_rele(sdp->sd_share); 822 sdp->sd_share = NULL; 823 sdp->sd_level = SMBL_VC; 824 825 return (0); 826 } 827 828 829 /* 830 * Ioctl function: SMBIOC_IOD_WORK 831 * 832 * Become the reader (IOD) thread, until either the connection is 833 * reset by the server, or until the connection is idle longer than 834 * some max time. (max idle time not yet implemented) 835 */ 836 int 837 smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) 838 { 839 struct smb_vc *vcp = NULL; 840 int err = 0; 841 842 /* Must have a valid session. */ 843 if ((vcp = sdp->sd_vc) == NULL) 844 return (EINVAL); 845 if (vcp->vc_flags & SMBV_GONE) 846 return (EINVAL); 847 848 /* 849 * Is there already an IOD for this VC? 850 * (Should never happen.) 851 */ 852 SMB_VC_LOCK(vcp); 853 if (vcp->iod_thr == NULL) 854 vcp->iod_thr = curthread; 855 else 856 err = EEXIST; 857 SMB_VC_UNLOCK(vcp); 858 if (err) 859 return (err); 860 861 /* 862 * Copy the "work" state, etc. into the VC 863 * The MAC key is copied separately. 864 */ 865 if (ddi_copyin((void *)arg, &vcp->vc_work, 866 sizeof (smbioc_ssn_work_t), flags)) { 867 err = EFAULT; 868 goto out; 869 } 870 if (vcp->vc_u_maclen) { 871 vcp->vc_mackeylen = vcp->vc_u_maclen; 872 vcp->vc_mackey = kmem_alloc(vcp->vc_mackeylen, KM_SLEEP); 873 if (ddi_copyin(vcp->vc_u_mackey.lp_ptr, vcp->vc_mackey, 874 vcp->vc_mackeylen, flags)) { 875 err = EFAULT; 876 goto out; 877 } 878 } 879 880 err = smb_iod_vc_work(vcp, cr); 881 882 /* Caller wants state here. */ 883 vcp->vc_work.wk_out_state = vcp->vc_state; 884 885 (void) ddi_copyout(&vcp->vc_work, (void *)arg, 886 sizeof (smbioc_ssn_work_t), flags); 887 888 out: 889 if (vcp->vc_mackey) { 890 kmem_free(vcp->vc_mackey, vcp->vc_mackeylen); 891 vcp->vc_mackey = NULL; 892 vcp->vc_mackeylen = 0; 893 } 894 895 /* 896 * The IOD thread is leaving the driver. Clear iod_thr, 897 * and wake up anybody waiting for us to quit. 898 */ 899 SMB_VC_LOCK(vcp); 900 vcp->iod_thr = NULL; 901 cv_broadcast(&vcp->vc_statechg); 902 SMB_VC_UNLOCK(vcp); 903 904 return (err); 905 } 906 907 /* 908 * Ioctl functions: SMBIOC_IOD_IDLE, SMBIOC_IOD_RCFAIL 909 * 910 * Wait for user-level requests to be enqueued on this session, 911 * and then return to the user-space helper, which will then 912 * initiate a reconnect, etc. 913 */ 914 int 915 smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags) 916 { 917 struct smb_vc *vcp = NULL; 918 int err = 0; 919 920 /* Must have a valid session. */ 921 if ((vcp = sdp->sd_vc) == NULL) 922 return (EINVAL); 923 if (vcp->vc_flags & SMBV_GONE) 924 return (EINVAL); 925 926 /* 927 * Is there already an IOD for this VC? 928 * (Should never happen.) 929 */ 930 SMB_VC_LOCK(vcp); 931 if (vcp->iod_thr == NULL) 932 vcp->iod_thr = curthread; 933 else 934 err = EEXIST; 935 SMB_VC_UNLOCK(vcp); 936 if (err) 937 return (err); 938 939 /* nothing to copyin */ 940 941 switch (cmd) { 942 case SMBIOC_IOD_IDLE: 943 err = smb_iod_vc_idle(vcp); 944 break; 945 946 case SMBIOC_IOD_RCFAIL: 947 err = smb_iod_vc_rcfail(vcp); 948 break; 949 950 default: 951 err = ENOTTY; 952 goto out; 953 } 954 955 /* Both of these ioctls copy out the new state. */ 956 (void) ddi_copyout(&vcp->vc_state, (void *)arg, 957 sizeof (int), flags); 958 959 out: 960 /* 961 * The IOD thread is leaving the driver. Clear iod_thr, 962 * and wake up anybody waiting for us to quit. 963 */ 964 SMB_VC_LOCK(vcp); 965 vcp->iod_thr = NULL; 966 cv_broadcast(&vcp->vc_statechg); 967 SMB_VC_UNLOCK(vcp); 968 969 return (err); 970 } 971