1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * Connection engine. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/kernel.h> 36 #include <sys/malloc.h> 37 #include <sys/priv.h> 38 #include <sys/proc.h> 39 #include <sys/lock.h> 40 #include <sys/sysctl.h> 41 #include <sys/socketvar.h> 42 43 #include <sys/iconv.h> 44 45 #include <netsmb/smb.h> 46 #include <netsmb/smb_subr.h> 47 #include <netsmb/smb_conn.h> 48 #include <netsmb/smb_tran.h> 49 #include <netsmb/smb_trantcp.h> 50 51 static struct smb_connobj smb_vclist; 52 static int smb_vcnext = 1; /* next unique id for VC */ 53 54 SYSCTL_NODE(_net, OID_AUTO, smb, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 55 "SMB protocol"); 56 57 static MALLOC_DEFINE(M_SMBCONN, "smb_conn", "SMB connection"); 58 59 static void smb_co_init(struct smb_connobj *cp, int level, char *ilockname, 60 char *lockname); 61 static void smb_co_done(struct smb_connobj *cp); 62 static int smb_vc_disconnect(struct smb_vc *vcp); 63 static void smb_vc_free(struct smb_connobj *cp); 64 static void smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred); 65 static smb_co_free_t smb_share_free; 66 static smb_co_gone_t smb_share_gone; 67 68 static int smb_sysctl_treedump(SYSCTL_HANDLER_ARGS); 69 70 SYSCTL_PROC(_net_smb, OID_AUTO, treedump, 71 CTLFLAG_RD | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE, 72 NULL, 0, smb_sysctl_treedump, "S,treedump", 73 "Requester tree"); 74 75 int 76 smb_sm_init(void) 77 { 78 79 smb_co_init(&smb_vclist, SMBL_SM, "smbsm ilock", "smbsm"); 80 sx_xlock(&smb_vclist.co_interlock); 81 smb_co_unlock(&smb_vclist); 82 sx_unlock(&smb_vclist.co_interlock); 83 return 0; 84 } 85 86 int 87 smb_sm_done(void) 88 { 89 90 /* XXX: hold the mutex */ 91 if (smb_vclist.co_usecount > 1) { 92 SMBERROR("%d connections still active\n", smb_vclist.co_usecount - 1); 93 return EBUSY; 94 } 95 smb_co_done(&smb_vclist); 96 return 0; 97 } 98 99 static int 100 smb_sm_lockvclist(void) 101 { 102 int error; 103 104 sx_xlock(&smb_vclist.co_interlock); 105 error = smb_co_lock(&smb_vclist); 106 sx_unlock(&smb_vclist.co_interlock); 107 108 return error; 109 } 110 111 static void 112 smb_sm_unlockvclist(void) 113 { 114 115 sx_xlock(&smb_vclist.co_interlock); 116 smb_co_unlock(&smb_vclist); 117 sx_unlock(&smb_vclist.co_interlock); 118 } 119 120 static int 121 smb_sm_lookupint(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, 122 struct smb_cred *scred, struct smb_vc **vcpp) 123 { 124 struct smb_connobj *scp; 125 struct smb_vc *vcp; 126 int exact = 1; 127 int error; 128 129 vcspec->shspec = shspec; 130 error = ENOENT; 131 vcp = NULL; 132 SMBCO_FOREACH(scp, &smb_vclist) { 133 vcp = (struct smb_vc *)scp; 134 error = smb_vc_lock(vcp); 135 if (error) 136 continue; 137 138 if ((vcp->obj.co_flags & SMBV_PRIVATE) || 139 !CONNADDREQ(vcp->vc_paddr, vcspec->sap) || 140 strcmp(vcp->vc_username, vcspec->username) != 0) 141 goto err1; 142 if (vcspec->owner != SMBM_ANY_OWNER) { 143 if (vcp->vc_uid != vcspec->owner) 144 goto err1; 145 } else 146 exact = 0; 147 if (vcspec->group != SMBM_ANY_GROUP) { 148 if (vcp->vc_grp != vcspec->group) 149 goto err1; 150 } else 151 exact = 0; 152 if (vcspec->mode & SMBM_EXACT) { 153 if (!exact || (vcspec->mode & SMBM_MASK) != 154 vcp->vc_mode) 155 goto err1; 156 } 157 if (smb_vc_access(vcp, scred, vcspec->mode) != 0) 158 goto err1; 159 vcspec->ssp = NULL; 160 if (shspec) { 161 error = (int)smb_vc_lookupshare(vcp, shspec, scred, 162 &vcspec->ssp); 163 if (error) 164 goto fail; 165 } 166 error = 0; 167 break; 168 err1: 169 error = 1; 170 fail: 171 smb_vc_unlock(vcp); 172 } 173 if (vcp) { 174 smb_vc_ref(vcp); 175 *vcpp = vcp; 176 } 177 return (error); 178 } 179 180 int 181 smb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, 182 struct smb_cred *scred, struct smb_vc **vcpp) 183 { 184 struct smb_vc *vcp; 185 struct smb_share *ssp = NULL; 186 int error; 187 188 *vcpp = vcp = NULL; 189 190 error = smb_sm_lockvclist(); 191 if (error) 192 return error; 193 error = smb_sm_lookupint(vcspec, shspec, scred, vcpp); 194 if (error == 0 || (vcspec->flags & SMBV_CREATE) == 0) { 195 smb_sm_unlockvclist(); 196 return error; 197 } 198 error = smb_sm_lookupint(vcspec, NULL, scred, &vcp); 199 if (error) { 200 error = smb_vc_create(vcspec, scred, &vcp); 201 if (error) 202 goto out; 203 error = smb_vc_connect(vcp, scred); 204 if (error) 205 goto out; 206 } 207 if (shspec == NULL) 208 goto out; 209 error = smb_share_create(vcp, shspec, scred, &ssp); 210 if (error) 211 goto out; 212 error = smb_smb_treeconnect(ssp, scred); 213 if (error == 0) 214 vcspec->ssp = ssp; 215 else 216 smb_share_put(ssp, scred); 217 out: 218 smb_sm_unlockvclist(); 219 if (error == 0) 220 *vcpp = vcp; 221 else if (vcp) { 222 smb_vc_lock(vcp); 223 smb_vc_put(vcp, scred); 224 } 225 return error; 226 } 227 228 /* 229 * Common code for connection object 230 */ 231 static void 232 smb_co_init(struct smb_connobj *cp, int level, char *ilockname, char *lockname) 233 { 234 SLIST_INIT(&cp->co_children); 235 sx_init_flags(&cp->co_interlock, ilockname, SX_RECURSE); 236 cv_init(&cp->co_lock, "smblock"); 237 cp->co_lockcnt = 0; 238 cp->co_locker = NULL; 239 cp->co_level = level; 240 cp->co_usecount = 1; 241 sx_xlock(&cp->co_interlock); 242 smb_co_lock(cp); 243 sx_unlock(&cp->co_interlock); 244 } 245 246 static void 247 smb_co_done(struct smb_connobj *cp) 248 { 249 250 sx_destroy(&cp->co_interlock); 251 cv_destroy(&cp->co_lock); 252 cp->co_locker = NULL; 253 cp->co_flags = 0; 254 cp->co_lockcnt = 0; 255 } 256 257 static void 258 smb_co_gone(struct smb_connobj *cp, struct smb_cred *scred) 259 { 260 struct smb_connobj *parent; 261 262 if (cp->co_gone) 263 cp->co_gone(cp, scred); 264 parent = cp->co_parent; 265 if (parent) { 266 sx_xlock(&parent->co_interlock); 267 smb_co_lock(parent); 268 sx_unlock(&parent->co_interlock); 269 SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next); 270 smb_co_put(parent, scred); 271 } 272 if (cp->co_free) 273 cp->co_free(cp); 274 } 275 276 void 277 smb_co_ref(struct smb_connobj *cp) 278 { 279 280 sx_xlock(&cp->co_interlock); 281 cp->co_usecount++; 282 sx_unlock(&cp->co_interlock); 283 } 284 285 void 286 smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred) 287 { 288 289 sx_xlock(&cp->co_interlock); 290 smb_co_unlock(cp); 291 if (cp->co_usecount > 1) { 292 cp->co_usecount--; 293 sx_unlock(&cp->co_interlock); 294 return; 295 } 296 if (cp->co_usecount == 0) { 297 SMBERROR("negative use_count for object %d", cp->co_level); 298 sx_unlock(&cp->co_interlock); 299 return; 300 } 301 cp->co_usecount--; 302 cp->co_flags |= SMBO_GONE; 303 sx_unlock(&cp->co_interlock); 304 smb_co_gone(cp, scred); 305 } 306 307 int 308 smb_co_get(struct smb_connobj *cp, struct smb_cred *scred) 309 { 310 int error; 311 312 MPASS(sx_xholder(&cp->co_interlock) == curthread); 313 cp->co_usecount++; 314 error = smb_co_lock(cp); 315 if (error) 316 cp->co_usecount--; 317 return error; 318 } 319 320 void 321 smb_co_put(struct smb_connobj *cp, struct smb_cred *scred) 322 { 323 324 sx_xlock(&cp->co_interlock); 325 if (cp->co_usecount > 1) { 326 cp->co_usecount--; 327 } else if (cp->co_usecount == 1) { 328 cp->co_usecount--; 329 cp->co_flags |= SMBO_GONE; 330 } else { 331 SMBERROR("negative usecount"); 332 } 333 smb_co_unlock(cp); 334 sx_unlock(&cp->co_interlock); 335 if ((cp->co_flags & SMBO_GONE) == 0) 336 return; 337 smb_co_gone(cp, scred); 338 } 339 340 int 341 smb_co_lock(struct smb_connobj *cp) 342 { 343 344 MPASS(sx_xholder(&cp->co_interlock) == curthread); 345 for (;;) { 346 if (cp->co_flags & SMBO_GONE) 347 return EINVAL; 348 if (cp->co_locker == NULL) { 349 cp->co_locker = curthread; 350 return 0; 351 } 352 if (cp->co_locker == curthread) { 353 cp->co_lockcnt++; 354 return 0; 355 } 356 cv_wait(&cp->co_lock, &cp->co_interlock); 357 } 358 } 359 360 void 361 smb_co_unlock(struct smb_connobj *cp) 362 { 363 364 MPASS(sx_xholder(&cp->co_interlock) == curthread); 365 MPASS(cp->co_locker == curthread); 366 if (cp->co_lockcnt != 0) { 367 cp->co_lockcnt--; 368 return; 369 } 370 cp->co_locker = NULL; 371 cv_signal(&cp->co_lock); 372 } 373 374 static void 375 smb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child) 376 { 377 378 smb_co_ref(parent); 379 SLIST_INSERT_HEAD(&parent->co_children, child, co_next); 380 child->co_parent = parent; 381 } 382 383 /* 384 * Session implementation 385 */ 386 387 int 388 smb_vc_create(struct smb_vcspec *vcspec, 389 struct smb_cred *scred, struct smb_vc **vcpp) 390 { 391 struct smb_vc *vcp; 392 struct ucred *cred = scred->scr_cred; 393 uid_t uid = vcspec->owner; 394 gid_t gid = vcspec->group; 395 uid_t realuid = cred->cr_uid; 396 char *domain = vcspec->domain; 397 int error, isroot; 398 399 isroot = smb_suser(cred) == 0; 400 /* 401 * Only superuser can create VCs with different uid and gid 402 */ 403 if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) 404 return EPERM; 405 if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) 406 return EPERM; 407 408 vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK); 409 smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc ilock", "smb_vc"); 410 vcp->obj.co_free = smb_vc_free; 411 vcp->obj.co_gone = smb_vc_gone; 412 vcp->vc_number = smb_vcnext++; 413 vcp->vc_timo = SMB_DEFRQTIMO; 414 vcp->vc_smbuid = SMB_UID_UNKNOWN; 415 vcp->vc_mode = vcspec->rights & SMBM_MASK; 416 vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE); 417 vcp->vc_tdesc = &smb_tran_nbtcp_desc; 418 vcp->vc_seqno = 0; 419 vcp->vc_mackey = NULL; 420 vcp->vc_mackeylen = 0; 421 422 if (uid == SMBM_ANY_OWNER) 423 uid = realuid; 424 if (gid == SMBM_ANY_GROUP) 425 gid = cred->cr_groups[0]; 426 vcp->vc_uid = uid; 427 vcp->vc_grp = gid; 428 429 smb_sl_init(&vcp->vc_stlock, "vcstlock"); 430 error = ENOMEM; 431 432 vcp->vc_paddr = sodupsockaddr(vcspec->sap, M_WAITOK); 433 if (vcp->vc_paddr == NULL) 434 goto fail; 435 vcp->vc_laddr = sodupsockaddr(vcspec->lap, M_WAITOK); 436 if (vcp->vc_laddr == NULL) 437 goto fail; 438 vcp->vc_pass = smb_strdup(vcspec->pass); 439 if (vcp->vc_pass == NULL) 440 goto fail; 441 vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain : 442 "NODOMAIN"); 443 if (vcp->vc_domain == NULL) 444 goto fail; 445 vcp->vc_srvname = smb_strdup(vcspec->srvname); 446 if (vcp->vc_srvname == NULL) 447 goto fail; 448 vcp->vc_username = smb_strdup(vcspec->username); 449 if (vcp->vc_username == NULL) 450 goto fail; 451 error = (int)iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower); 452 if (error) 453 goto fail; 454 error = (int)iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper); 455 if (error) 456 goto fail; 457 if (vcspec->servercs[0]) { 458 error = (int)iconv_open(vcspec->servercs, vcspec->localcs, 459 &vcp->vc_cp_toserver); 460 if (error) 461 goto fail; 462 error = (int)iconv_open(vcspec->localcs, vcspec->servercs, 463 &vcp->vc_cp_tolocal); 464 if (error) 465 goto fail; 466 vcp->vc_toserver = vcp->vc_cp_toserver; 467 vcp->vc_tolocal = vcp->vc_cp_tolocal; 468 iconv_add(ENCODING_UNICODE, ENCODING_UNICODE, SMB_UNICODE_NAME); 469 iconv_add(ENCODING_UNICODE, SMB_UNICODE_NAME, ENCODING_UNICODE); 470 error = (int)iconv_open(SMB_UNICODE_NAME, vcspec->localcs, 471 &vcp->vc_ucs_toserver); 472 if (!error) { 473 error = (int)iconv_open(vcspec->localcs, SMB_UNICODE_NAME, 474 &vcp->vc_ucs_tolocal); 475 } 476 if (error) { 477 if (vcp->vc_ucs_toserver) 478 iconv_close(vcp->vc_ucs_toserver); 479 vcp->vc_ucs_toserver = NULL; 480 vcp->vc_ucs_tolocal = NULL; 481 } 482 } 483 error = (int)smb_iod_create(vcp); 484 if (error) 485 goto fail; 486 *vcpp = vcp; 487 smb_co_addchild(&smb_vclist, VCTOCP(vcp)); 488 return (0); 489 490 fail: 491 smb_vc_put(vcp, scred); 492 return (error); 493 } 494 495 static void 496 smb_vc_free(struct smb_connobj *cp) 497 { 498 struct smb_vc *vcp = CPTOVC(cp); 499 500 if (vcp->vc_iod) 501 smb_iod_destroy(vcp->vc_iod); 502 SMB_STRFREE(vcp->vc_username); 503 SMB_STRFREE(vcp->vc_srvname); 504 SMB_STRFREE(vcp->vc_pass); 505 SMB_STRFREE(vcp->vc_domain); 506 if (vcp->vc_mackey) 507 free(vcp->vc_mackey, M_SMBTEMP); 508 if (vcp->vc_paddr) 509 free(vcp->vc_paddr, M_SONAME); 510 if (vcp->vc_laddr) 511 free(vcp->vc_laddr, M_SONAME); 512 if (vcp->vc_tolower) 513 iconv_close(vcp->vc_tolower); 514 if (vcp->vc_toupper) 515 iconv_close(vcp->vc_toupper); 516 if (vcp->vc_tolocal) 517 vcp->vc_tolocal = NULL; 518 if (vcp->vc_toserver) 519 vcp->vc_toserver = NULL; 520 if (vcp->vc_cp_tolocal) 521 iconv_close(vcp->vc_cp_tolocal); 522 if (vcp->vc_cp_toserver) 523 iconv_close(vcp->vc_cp_toserver); 524 if (vcp->vc_ucs_tolocal) 525 iconv_close(vcp->vc_ucs_tolocal); 526 if (vcp->vc_ucs_toserver) 527 iconv_close(vcp->vc_ucs_toserver); 528 smb_co_done(VCTOCP(vcp)); 529 smb_sl_destroy(&vcp->vc_stlock); 530 free(vcp, M_SMBCONN); 531 } 532 533 /* 534 * Called when use count of VC dropped to zero. 535 */ 536 static void 537 smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred) 538 { 539 struct smb_vc *vcp = CPTOVC(cp); 540 541 smb_vc_disconnect(vcp); 542 } 543 544 void 545 smb_vc_ref(struct smb_vc *vcp) 546 { 547 smb_co_ref(VCTOCP(vcp)); 548 } 549 550 void 551 smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred) 552 { 553 smb_co_rele(VCTOCP(vcp), scred); 554 } 555 556 int 557 smb_vc_get(struct smb_vc *vcp, struct smb_cred *scred) 558 { 559 struct smb_connobj *cp; 560 int error; 561 562 cp = VCTOCP(vcp); 563 sx_xlock(&cp->co_interlock); 564 error = smb_co_get(cp, scred); 565 sx_unlock(&cp->co_interlock); 566 return error; 567 } 568 569 void 570 smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred) 571 { 572 smb_co_put(VCTOCP(vcp), scred); 573 } 574 575 int 576 smb_vc_lock(struct smb_vc *vcp) 577 { 578 struct smb_connobj *cp; 579 int error; 580 581 cp = VCTOCP(vcp); 582 sx_xlock(&cp->co_interlock); 583 error = smb_co_lock(cp); 584 sx_unlock(&cp->co_interlock); 585 return error; 586 } 587 588 void 589 smb_vc_unlock(struct smb_vc *vcp) 590 { 591 592 struct smb_connobj *cp; 593 594 cp = VCTOCP(vcp); 595 sx_xlock(&cp->co_interlock); 596 smb_co_unlock(cp); 597 sx_unlock(&cp->co_interlock); 598 } 599 600 int 601 smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode) 602 { 603 struct ucred *cred = scred->scr_cred; 604 605 if (smb_suser(cred) == 0 || cred->cr_uid == vcp->vc_uid) 606 return 0; 607 mode >>= 3; 608 if (!groupmember(vcp->vc_grp, cred)) 609 mode >>= 3; 610 return (vcp->vc_mode & mode) == mode ? 0 : EACCES; 611 } 612 613 static int 614 smb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp) 615 { 616 int exact = 1; 617 618 if (strcmp(ssp->ss_name, dp->name) != 0) 619 return 1; 620 if (dp->owner != SMBM_ANY_OWNER) { 621 if (ssp->ss_uid != dp->owner) 622 return 1; 623 } else 624 exact = 0; 625 if (dp->group != SMBM_ANY_GROUP) { 626 if (ssp->ss_grp != dp->group) 627 return 1; 628 } else 629 exact = 0; 630 631 if (dp->mode & SMBM_EXACT) { 632 if (!exact) 633 return 1; 634 return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1; 635 } 636 if (smb_share_access(ssp, dp->scred, dp->mode) != 0) 637 return 1; 638 return 0; 639 } 640 641 /* 642 * Lookup share in the given VC. Share referenced and locked on return. 643 * VC expected to be locked on entry and will be left locked on exit. 644 */ 645 int 646 smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp, 647 struct smb_cred *scred, struct smb_share **sspp) 648 { 649 struct smb_connobj *scp = NULL; 650 struct smb_share *ssp = NULL; 651 int error; 652 653 *sspp = NULL; 654 dp->scred = scred; 655 SMBCO_FOREACH(scp, VCTOCP(vcp)) { 656 ssp = (struct smb_share *)scp; 657 error = smb_share_lock(ssp); 658 if (error) 659 continue; 660 if (smb_vc_cmpshare(ssp, dp) == 0) 661 break; 662 smb_share_unlock(ssp); 663 } 664 if (ssp) { 665 smb_share_ref(ssp); 666 *sspp = ssp; 667 error = 0; 668 } else 669 error = ENOENT; 670 return error; 671 } 672 673 int 674 smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred) 675 { 676 677 return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL); 678 } 679 680 /* 681 * Destroy VC to server, invalidate shares linked with it. 682 * Transport should be locked on entry. 683 */ 684 int 685 smb_vc_disconnect(struct smb_vc *vcp) 686 { 687 688 if (vcp->vc_iod != NULL) 689 smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | 690 SMBIOD_EV_SYNC, NULL); 691 return 0; 692 } 693 694 static char smb_emptypass[] = ""; 695 696 const char * 697 smb_vc_getpass(struct smb_vc *vcp) 698 { 699 if (vcp->vc_pass) 700 return vcp->vc_pass; 701 return smb_emptypass; 702 } 703 704 static int 705 smb_vc_getinfo(struct smb_vc *vcp, struct smb_vc_info *vip) 706 { 707 bzero(vip, sizeof(struct smb_vc_info)); 708 vip->itype = SMB_INFO_VC; 709 vip->usecount = vcp->obj.co_usecount; 710 vip->uid = vcp->vc_uid; 711 vip->gid = vcp->vc_grp; 712 vip->mode = vcp->vc_mode; 713 vip->flags = vcp->obj.co_flags; 714 vip->sopt = vcp->vc_sopt; 715 vip->iodstate = vcp->vc_iod->iod_state; 716 bzero(&vip->sopt.sv_skey, sizeof(vip->sopt.sv_skey)); 717 snprintf(vip->srvname, sizeof(vip->srvname), "%s", vcp->vc_srvname); 718 snprintf(vip->vcname, sizeof(vip->vcname), "%s", vcp->vc_username); 719 return 0; 720 } 721 722 u_short 723 smb_vc_nextmid(struct smb_vc *vcp) 724 { 725 u_short r; 726 727 sx_xlock(&vcp->obj.co_interlock); 728 r = vcp->vc_mid++; 729 sx_unlock(&vcp->obj.co_interlock); 730 return r; 731 } 732 733 /* 734 * Share implementation 735 */ 736 /* 737 * Allocate share structure and attach it to the given VC 738 * Connection expected to be locked on entry. Share will be returned 739 * in locked state. 740 */ 741 int 742 smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, 743 struct smb_cred *scred, struct smb_share **sspp) 744 { 745 struct smb_share *ssp; 746 struct ucred *cred = scred->scr_cred; 747 uid_t realuid = cred->cr_uid; 748 uid_t uid = shspec->owner; 749 gid_t gid = shspec->group; 750 int error, isroot; 751 752 isroot = smb_suser(cred) == 0; 753 /* 754 * Only superuser can create shares with different uid and gid 755 */ 756 if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot) 757 return EPERM; 758 if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot) 759 return EPERM; 760 error = smb_vc_lookupshare(vcp, shspec, scred, &ssp); 761 if (!error) { 762 smb_share_put(ssp, scred); 763 return EEXIST; 764 } 765 if (uid == SMBM_ANY_OWNER) 766 uid = realuid; 767 if (gid == SMBM_ANY_GROUP) 768 gid = cred->cr_groups[0]; 769 ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK); 770 smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss ilock", "smbss"); 771 ssp->obj.co_free = smb_share_free; 772 ssp->obj.co_gone = smb_share_gone; 773 smb_sl_init(&ssp->ss_stlock, "ssstlock"); 774 ssp->ss_name = smb_strdup(shspec->name); 775 if (shspec->pass && shspec->pass[0]) 776 ssp->ss_pass = smb_strdup(shspec->pass); 777 ssp->ss_type = shspec->stype; 778 ssp->ss_tid = SMB_TID_UNKNOWN; 779 ssp->ss_uid = uid; 780 ssp->ss_grp = gid; 781 ssp->ss_mode = shspec->rights & SMBM_MASK; 782 smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); 783 *sspp = ssp; 784 return 0; 785 } 786 787 static void 788 smb_share_free(struct smb_connobj *cp) 789 { 790 struct smb_share *ssp = CPTOSS(cp); 791 792 SMB_STRFREE(ssp->ss_name); 793 SMB_STRFREE(ssp->ss_pass); 794 smb_sl_destroy(&ssp->ss_stlock); 795 smb_co_done(SSTOCP(ssp)); 796 free(ssp, M_SMBCONN); 797 } 798 799 static void 800 smb_share_gone(struct smb_connobj *cp, struct smb_cred *scred) 801 { 802 struct smb_share *ssp = CPTOSS(cp); 803 804 smb_smb_treedisconnect(ssp, scred); 805 } 806 807 void 808 smb_share_ref(struct smb_share *ssp) 809 { 810 smb_co_ref(SSTOCP(ssp)); 811 } 812 813 void 814 smb_share_rele(struct smb_share *ssp, struct smb_cred *scred) 815 { 816 smb_co_rele(SSTOCP(ssp), scred); 817 } 818 819 int 820 smb_share_get(struct smb_share *ssp, struct smb_cred *scred) 821 { 822 struct smb_connobj *cp = SSTOCP(ssp); 823 int error; 824 825 sx_xlock(&cp->co_interlock); 826 error = smb_co_get(cp, scred); 827 sx_unlock(&cp->co_interlock); 828 return error; 829 } 830 831 void 832 smb_share_put(struct smb_share *ssp, struct smb_cred *scred) 833 { 834 835 smb_co_put(SSTOCP(ssp), scred); 836 } 837 838 int 839 smb_share_lock(struct smb_share *ssp) 840 { 841 struct smb_connobj *cp; 842 int error; 843 844 cp = SSTOCP(ssp); 845 sx_xlock(&cp->co_interlock); 846 error = smb_co_lock(cp); 847 sx_unlock(&cp->co_interlock); 848 return error; 849 } 850 851 void 852 smb_share_unlock(struct smb_share *ssp) 853 { 854 struct smb_connobj *cp; 855 856 cp = SSTOCP(ssp); 857 sx_xlock(&cp->co_interlock); 858 smb_co_unlock(cp); 859 sx_unlock(&cp->co_interlock); 860 } 861 862 int 863 smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode) 864 { 865 struct ucred *cred = scred->scr_cred; 866 867 if (smb_suser(cred) == 0 || cred->cr_uid == ssp->ss_uid) 868 return 0; 869 mode >>= 3; 870 if (!groupmember(ssp->ss_grp, cred)) 871 mode >>= 3; 872 return (ssp->ss_mode & mode) == mode ? 0 : EACCES; 873 } 874 875 void 876 smb_share_invalidate(struct smb_share *ssp) 877 { 878 ssp->ss_tid = SMB_TID_UNKNOWN; 879 } 880 881 int 882 smb_share_valid(struct smb_share *ssp) 883 { 884 return ssp->ss_tid != SMB_TID_UNKNOWN && 885 ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid; 886 } 887 888 const char* 889 smb_share_getpass(struct smb_share *ssp) 890 { 891 struct smb_vc *vcp; 892 893 if (ssp->ss_pass) 894 return ssp->ss_pass; 895 vcp = SSTOVC(ssp); 896 if (vcp->vc_pass) 897 return vcp->vc_pass; 898 return smb_emptypass; 899 } 900 901 static int 902 smb_share_getinfo(struct smb_share *ssp, struct smb_share_info *sip) 903 { 904 bzero(sip, sizeof(struct smb_share_info)); 905 sip->itype = SMB_INFO_SHARE; 906 sip->usecount = ssp->obj.co_usecount; 907 sip->tid = ssp->ss_tid; 908 sip->type= ssp->ss_type; 909 sip->uid = ssp->ss_uid; 910 sip->gid = ssp->ss_grp; 911 sip->mode= ssp->ss_mode; 912 sip->flags = ssp->obj.co_flags; 913 snprintf(sip->sname, sizeof(sip->sname), "%s", ssp->ss_name); 914 return 0; 915 } 916 917 /* 918 * Dump an entire tree into sysctl call 919 */ 920 static int 921 smb_sysctl_treedump(SYSCTL_HANDLER_ARGS) 922 { 923 struct smb_connobj *scp1, *scp2; 924 struct smb_vc *vcp; 925 struct smb_share *ssp; 926 struct smb_vc_info vci; 927 struct smb_share_info ssi; 928 int error, itype; 929 930 error = sysctl_wire_old_buffer(req, 0); 931 if (error) 932 return (error); 933 error = smb_sm_lockvclist(); 934 if (error) 935 return error; 936 SMBCO_FOREACH(scp1, &smb_vclist) { 937 vcp = (struct smb_vc *)scp1; 938 error = smb_vc_lock(vcp); 939 if (error) 940 continue; 941 smb_vc_getinfo(vcp, &vci); 942 error = SYSCTL_OUT(req, &vci, sizeof(struct smb_vc_info)); 943 if (error) { 944 smb_vc_unlock(vcp); 945 break; 946 } 947 SMBCO_FOREACH(scp2, VCTOCP(vcp)) { 948 ssp = (struct smb_share *)scp2; 949 error = smb_share_lock(ssp); 950 if (error) { 951 error = 0; 952 continue; 953 } 954 smb_share_getinfo(ssp, &ssi); 955 smb_share_unlock(ssp); 956 error = SYSCTL_OUT(req, &ssi, sizeof(struct smb_share_info)); 957 if (error) 958 break; 959 } 960 smb_vc_unlock(vcp); 961 if (error) 962 break; 963 } 964 if (!error) { 965 itype = SMB_INFO_NONE; 966 error = SYSCTL_OUT(req, &itype, sizeof(itype)); 967 } 968 smb_sm_unlockvclist(); 969 return error; 970 } 971