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