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