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